From 10a7fe8d65e8f0ce9c1fc24ba6080afedcc1a76a Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 17 Jul 2015 19:16:08 +0800 Subject: First commit --- COPYING | 674 +++++++++++++++++++++++++++++++++++++++++++++++++++ README | 12 + exwm-floating.el | 431 +++++++++++++++++++++++++++++++++ exwm-input.el | 451 ++++++++++++++++++++++++++++++++++ exwm-layout.el | 194 +++++++++++++++ exwm-manage.el | 333 +++++++++++++++++++++++++ exwm-workspace.el | 230 ++++++++++++++++++ exwm.el | 706 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 3031 insertions(+) create mode 100644 COPYING create mode 100644 README create mode 100644 exwm-floating.el create mode 100644 exwm-input.el create mode 100644 exwm-layout.el create mode 100644 exwm-manage.el create mode 100644 exwm-workspace.el create mode 100644 exwm.el diff --git a/COPYING b/COPYING new file mode 100644 index 0000000000..94a9ed024d --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README b/README new file mode 100644 index 0000000000..a0340f3e88 --- /dev/null +++ b/README @@ -0,0 +1,12 @@ +Emacs X Window Manager +====================== + +EXWM (Emacs X Window Manager) turns Emacs into a full-featured tiling X window +manager. Please refer to "exwm.el" for more details. + +EXWM is built on top of XELB and its utility libraris: + + XELB: https://github.com/ch11ng/xelb + + XELB utilility libraries: https://github.com/ch11ng/xelb-util + +You may also be intrested in an input method server designed for EXWM: + + EXIM: https://github.com/ch11ng/exim diff --git a/exwm-floating.el b/exwm-floating.el new file mode 100644 index 0000000000..9e57c5ca7d --- /dev/null +++ b/exwm-floating.el @@ -0,0 +1,431 @@ +;;; exwm-floating.el --- Floating Module for EXWM -*- lexical-binding: t -*- + +;; Copyright (C) 2015 Chris Feng + +;; Author: Chris Feng +;; Keywords: unix + +;; This file is not part of GNU Emacs. + +;; This file is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This file is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this file. If not, see . + +;;; Commentary: + +;; This module deals with the conversion between floating and non-floating +;; states and implements moving/resizing operations on floating windows. + +;; Todo: +;; + move/resize with keyboard. + +;;; Code: + +(require 'xcb-cursor) + +(defvar exwm-floating-border-width 1 "Border width of the floating window.") +(defvar exwm-floating-border-color "blue" + "Border color of the floating window.") + +(defun exwm-floating--set-floating (id) + "Make window ID floating." + (interactive) + (setq exwm-input--focus-lock t) + (when (get-buffer-window (exwm--id->buffer id)) ;window in non-floating state + (set-window-buffer (selected-window) (other-buffer))) ;hide it first + (let* ((original-frame + (with-current-buffer (exwm--id->buffer id) + (if (and exwm-transient-for (exwm--id->buffer exwm-transient-for)) + ;; Place a modal in the same workspace with its leading window + (with-current-buffer (exwm--id->buffer exwm-transient-for) + exwm--frame) + ;; Fallback to current workspace + exwm-workspace--current))) + (original-id (frame-parameter original-frame 'exwm-window-id)) + ;; Create new frame + (frame (with-current-buffer "*scratch*" + (make-frame `((minibuffer . nil) ;use the one on workspace + (background-color + . ,exwm-floating-border-color) + (internal-border-width + . ,exwm-floating-border-width) + (unsplittable . t))))) ;and fix the size later + (frame-id (string-to-int (frame-parameter frame 'window-id))) + (outer-id (string-to-int (frame-parameter frame 'outer-window-id))) + (window (frame-first-window frame)) ;and it's the only window + (x (slot-value exwm--geometry 'x)) + (y (slot-value exwm--geometry 'y)) + (width (slot-value exwm--geometry 'width)) + (height (slot-value exwm--geometry 'height))) + ;; Save window IDs + (set-frame-parameter frame 'exwm-window-id frame-id) + (set-frame-parameter frame 'exwm-outer-id outer-id) + ;; Set urgency flag if it's not appear in the active workspace + (let ((idx (cl-position original-frame exwm-workspace--list))) + (when (/= idx exwm-workspace-current-index) + (set-frame-parameter original-frame 'exwm--urgency t) + (exwm-workspace--update-switch-history))) + ;; Fix illegal parameters + ;; FIXME: check normal hints restrictions + (let* ((display-width (x-display-pixel-width)) + (display-height (- (x-display-pixel-height) + (window-pixel-height (minibuffer-window + original-frame)) + (* 2 (window-mode-line-height)) + (window-header-line-height window) + (* 2 exwm-floating-border-width))) + (display-height (* 2 (/ display-height 2)))) ;round to even + (if (> width display-width) + ;; Too wide + (progn (setq x 0 + width display-width)) + ;; Invalid width + (when (= 0 width) (setq width (/ display-width 2))) + ;; Completely outsize + (when (or (> x display-width) (> 0 (+ x display-width))) + (setq x (/ (- display-width width) 2)))) + (if (> height display-height) + ;; Too tall + (setq y 0 + height display-height) + ;; Invalid height + (when (= 0 height) (setq height (/ display-height 2))) + ;; Completely outside + (when (or (> y display-height) (> 0 (+ y display-height))) + (setq y (/ (- display-height height) 2))))) + ;; Set OverrideRedirect on this frame + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window outer-id :value-mask xcb:CW:OverrideRedirect + :override-redirect 1)) + ;; Set event mask + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window frame-id :value-mask xcb:CW:EventMask + :event-mask xcb:EventMask:SubstructureRedirect)) + ;; Reparent this frame to the original one + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window outer-id :parent original-id + :x (- x exwm-floating-border-width) + :y (- y exwm-floating-border-width))) + ;; Save the geometry + ;; Rationale: the frame will not be ready for some time, thus we cannot + ;; infer the correct window size from its geometry. + (with-current-buffer (exwm--id->buffer id) + (setq exwm--floating-edges + (vector exwm-floating-border-width exwm-floating-border-width + (+ width exwm-floating-border-width) + (+ height exwm-floating-border-width)))) + ;; Fit frame to client + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window outer-id + :value-mask (logior xcb:ConfigWindow:Width + xcb:ConfigWindow:Height + xcb:ConfigWindow:StackMode) + :width (+ width (* 2 exwm-floating-border-width)) + :height (+ height (* 2 exwm-floating-border-width) + (window-mode-line-height) + (window-header-line-height)) + :stack-mode xcb:StackMode:Above)) ;top-most + ;; Reparent window to this frame + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window id :value-mask xcb:CW:EventMask + :event-mask xcb:EventMask:NoEvent)) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window id :parent frame-id + :x exwm-floating-border-width + :y exwm-floating-border-width)) + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window id :value-mask xcb:CW:EventMask + :event-mask exwm--client-event-mask)) + (xcb:flush exwm--connection) + ;; Set window/buffer + (with-current-buffer (exwm--id->buffer id) + (setq window-size-fixed t ;make frame fixed size + exwm--frame original-frame + exwm--floating-frame frame) + (set-window-buffer window (current-buffer)) ;this changes current buffer + (set-window-dedicated-p window t)) + (with-current-buffer (exwm--id->buffer id) + ;; Some window should not get input focus on creation + ;; FIXME: other conditions? + (unless (memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY exwm-window-type) + (x-focus-frame exwm--floating-frame) + (exwm-input--set-focus id))) + (setq exwm-input--focus-lock nil))) + +(defun exwm-floating--unset-floating (id) + "Make window ID non-floating." + (interactive) + (setq exwm-input--focus-lock t) + (let ((buffer (exwm--id->buffer id))) + ;; Reparent to workspace frame + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window id :value-mask xcb:CW:EventMask + :event-mask xcb:EventMask:NoEvent)) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window id + :parent (frame-parameter exwm-workspace--current + 'exwm-window-id) + :x 0 :y 0)) ;temporary position + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window id :value-mask xcb:CW:EventMask + :event-mask exwm--client-event-mask)) + (xcb:flush exwm--connection) + (with-current-buffer buffer + (when exwm--floating-frame ;from floating to non-floating + (setq exwm--floating-edges nil) ;invalid by now + (set-window-dedicated-p (frame-first-window exwm--floating-frame) nil) + (delete-frame exwm--floating-frame))) ;remove the floating frame + (with-current-buffer buffer + (setq exwm--floating-frame nil + exwm--frame exwm-workspace--current)) + (select-frame exwm-workspace--current t) + (set-window-buffer nil buffer) + (exwm-layout--show id) + (exwm-input--set-focus id)) + (setq exwm-input--focus-lock nil)) + +(defun exwm-floating-toggle-floating () + "Toggle the current window between floating and non-floating states." + (interactive) + (with-current-buffer (window-buffer) + (if exwm--floating-frame + (exwm-floating--unset-floating exwm--id) + (exwm-floating--set-floating exwm--id)))) + +(defvar exwm-floating--moveresize-id nil) +(defvar exwm-floating--moveresize-type nil) +(defvar exwm-floating--moveresize-delta nil) + +(defun exwm-floating--start-moveresize (id &optional type) + "Start move/resize." + (let ((buffer (exwm--id->buffer id)) + frame frame-id cursor) + (when (and buffer + (setq frame (with-current-buffer buffer exwm--floating-frame)) + (setq frame-id (frame-parameter frame 'exwm-outer-id)) + ;; Test if the pointer can be grabbed + (= xcb:GrabStatus:Success + (slot-value + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GrabPointer + :owner-events 0 :grab-window frame-id + :event-mask xcb:EventMask:NoEvent + :pointer-mode xcb:GrabMode:Async + :keyboard-mode xcb:GrabMode:Async + :confine-to xcb:Window:None + :cursor xcb:Cursor:None + :time xcb:Time:CurrentTime)) + 'status))) + (setq exwm--floating-edges nil) ;invalid by now + (with-slots (root-x root-y win-x win-y) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:QueryPointer :window id)) + (select-frame-set-input-focus frame) ;raise and focus it + (setq width (frame-pixel-width frame) + height (frame-pixel-height frame)) + (unless type + ;; Determine the resize type according to the pointer position + ;; Clicking the center 1/3 part to resize has not effect + (setq x (/ (* 3 win-x) (float width)) + y (/ (* 3 win-y) (float height)) + type (cond ((and (< x 1) (< y 1)) + xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPLEFT) + ((and (> x 2) (< y 1)) + xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPRIGHT) + ((and (> x 2) (> y 2)) + xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT) + ((and (< x 1) (> y 2)) + xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT) + ((< y 1) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOP) + ((> x 2) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_RIGHT) + ((> y 2) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOM) + ((< x 1) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_LEFT)))) + (when type + (cond ((= type xcb:ewmh:_NET_WM_MOVERESIZE_MOVE) + (setq exwm-floating--moveresize-delta (list win-x win-y 0 0) + cursor exwm-floating--cursor-move)) + ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPLEFT) + (setq exwm-floating--moveresize-delta + (list win-x win-y (+ root-x width) (+ root-y height)) + cursor exwm-floating--cursor-top-left)) + ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOP) + (setq exwm-floating--moveresize-delta + (list 0 win-y 0 (+ root-y height)) + cursor exwm-floating--cursor-top)) + ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPRIGHT) + (setq exwm-floating--moveresize-delta + (list 0 win-y (- root-x width) (+ root-y height)) + cursor exwm-floating--cursor-top-right)) + ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_RIGHT) + (setq exwm-floating--moveresize-delta + (list 0 0 (- root-x width) 0) + cursor exwm-floating--cursor-right)) + ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT) + (setq exwm-floating--moveresize-delta + (list 0 0 (- root-x width) (- root-y height)) + cursor exwm-floating--cursor-bottom-right)) + ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOM) + (setq exwm-floating--moveresize-delta + (list 0 0 0 (- root-y height)) + cursor exwm-floating--cursor-bottom)) + ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT) + (setq exwm-floating--moveresize-delta + (list win-x 0 (+ root-x width) (- root-y height)) + cursor exwm-floating--cursor-bottom-left)) + ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_LEFT) + (setq exwm-floating--moveresize-delta + (list win-x 0 (+ root-x width) 0) + cursor exwm-floating--cursor-left))) + ;; Select events and change cursor (should always succeed) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GrabPointer + :owner-events 0 :grab-window frame-id + :event-mask (logior xcb:EventMask:ButtonRelease + xcb:EventMask:ButtonMotion) + :pointer-mode xcb:GrabMode:Async + :keyboard-mode xcb:GrabMode:Async + :confine-to xcb:Window:None + :cursor cursor + :time xcb:Time:CurrentTime)) + (setq exwm-floating--moveresize-id frame-id + exwm-floating--moveresize-type type)))))) + +(defun exwm-floating--stop-moveresize (&rest args) + "Stop move/resize." + (xcb:+request exwm--connection + (make-instance 'xcb:UngrabPointer :time xcb:Time:CurrentTime)) + (xcb:flush exwm--connection) + (setq exwm-floating--moveresize-id nil + exwm-floating--moveresize-type nil + exwm-floating--moveresize-delta nil)) + +(defun exwm-floating--do-moveresize (data synthetic) + "Perform move/resize." + (let ((mask 0) (x 0) (y 0) (width 0) (height 0) + (delta exwm-floating--moveresize-delta) + obj root-x root-y) + (when (and exwm-floating--moveresize-id exwm-floating--moveresize-type) + (setq obj (make-instance 'xcb:MotionNotify)) + (xcb:unmarshal obj data) + (setq root-x (slot-value obj 'root-x) + root-y (slot-value obj 'root-y)) + ;; Perform move/resize according to the previously set type + (cond ((= exwm-floating--moveresize-type + xcb:ewmh:_NET_WM_MOVERESIZE_MOVE) + (setq mask (logior xcb:ConfigWindow:X xcb:ConfigWindow:Y) + x (- root-x (elt delta 0)) + y (- root-y (elt delta 1)))) + ((= exwm-floating--moveresize-type + xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPLEFT) + (setq mask + (logior xcb:ConfigWindow:X xcb:ConfigWindow:Y + xcb:ConfigWindow:Width xcb:ConfigWindow:Height) + x (- root-x (elt delta 0)) + y (- root-y (elt delta 1)) + width (- (elt delta 2) root-x) + height (- (elt delta 3) root-y))) + ((= exwm-floating--moveresize-type + xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOP) + (setq mask (logior xcb:ConfigWindow:Y xcb:ConfigWindow:Height) + y (- root-y (elt delta 1)) + height (- (elt delta 3) root-y))) + ((= exwm-floating--moveresize-type + xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPRIGHT) + (setq mask + (logior xcb:ConfigWindow:Y + xcb:ConfigWindow:Width xcb:ConfigWindow:Height) + y (- root-y (elt delta 1)) + width (- root-x (elt delta 2)) + height (- (elt delta 3) root-y))) + ((= exwm-floating--moveresize-type + xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_RIGHT) + (setq mask (logior xcb:ConfigWindow:Width) + width (- root-x (elt delta 2)))) + ((= exwm-floating--moveresize-type + xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT) + (setq mask + (logior xcb:ConfigWindow:Width xcb:ConfigWindow:Height) + width (- root-x (elt delta 2)) + height (- root-y (elt delta 3)))) + ((= exwm-floating--moveresize-type + xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOM) + (setq mask (logior xcb:ConfigWindow:Height) + height (- root-y (elt delta 3)))) + ((= exwm-floating--moveresize-type + xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT) + (setq mask + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Width xcb:ConfigWindow:Height) + x (- root-x (elt delta 0)) + width (- (elt delta 2) root-x) + height (- root-y (elt delta 3)))) + ((= exwm-floating--moveresize-type + xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_LEFT) + (setq mask + (logior xcb:ConfigWindow:X xcb:ConfigWindow:Width) + x (- root-x (elt delta 0)) + width (- (elt delta 2) root-x)))) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window exwm-floating--moveresize-id :value-mask mask + :x x :y y :width width :height height)) + (xcb:flush exwm--connection)))) + +;; Cursors for moving/resizing a window +(defvar exwm-floating--cursor-move nil) +(defvar exwm-floating--cursor-top-left nil) +(defvar exwm-floating--cursor-top nil) +(defvar exwm-floating--cursor-top-right nil) +(defvar exwm-floating--cursor-right nil) +(defvar exwm-floating--cursor-bottom-right nil) +(defvar exwm-floating--cursor-bottom nil) +(defvar exwm-floating--cursor-bottom-left nil) +(defvar exwm-floating--cursor-left nil) + +(defun exwm-floating--init () + "Initialize floating module." + ;; Initialize cursors for moving/resizing a window + (xcb:cursor:init exwm--connection) + (setq exwm-floating--cursor-move + (xcb:cursor:load-cursor exwm--connection "fleur") + exwm-floating--cursor-top-left + (xcb:cursor:load-cursor exwm--connection "top_left_corner") + exwm-floating--cursor-top + (xcb:cursor:load-cursor exwm--connection "top_side") + exwm-floating--cursor-top-right + (xcb:cursor:load-cursor exwm--connection "top_right_corner") + exwm-floating--cursor-right + (xcb:cursor:load-cursor exwm--connection "right_side") + exwm-floating--cursor-bottom-right + (xcb:cursor:load-cursor exwm--connection "bottom_right_corner") + exwm-floating--cursor-bottom + (xcb:cursor:load-cursor exwm--connection "bottom_side") + exwm-floating--cursor-bottom-left + (xcb:cursor:load-cursor exwm--connection "bottom_left_corner") + exwm-floating--cursor-left + (xcb:cursor:load-cursor exwm--connection "left_side"))) + + + +(provide 'exwm-floating) + +;;; exwm-floating.el ends here diff --git a/exwm-input.el b/exwm-input.el new file mode 100644 index 0000000000..107df0599e --- /dev/null +++ b/exwm-input.el @@ -0,0 +1,451 @@ +;;; exwm-input.el --- Input Module for EXWM -*- lexical-binding: t -*- + +;; Copyright (C) 2015 Chris Feng + +;; Author: Chris Feng +;; Keywords: unix + +;; This file is not part of GNU Emacs. + +;; This file is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This file is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this file. If not, see . + +;;; Commentary: + +;; This module deals with key/mouse matters, including: +;; + Input focus, +;; + Key/Button event handling, +;; + Key events filtering and simulation. + +;; Todo: +;; + Pointer simulation mode (e.g. 'C-c 1'/'C-c 2' for single/double click, +;; move with arrow keys). +;; + Demonstrate how to add a local key binding (add global prefix key and +;; modify `exwm-mode-map'). +;; + Simulation keys to mimic Emacs key bindings for text edit (redo, select, +;; cancel, clear, etc). Some of them are not present on common keyboard +;; (keycode = 0). May need to use XKB extension. + +;;; Code: + +(require 'xcb-keysyms) +(require 'exwm-floating) + +(defvar exwm-input-move-event 's-down-mouse-1 + "Emacs event to start moving a window.") +(defvar exwm-input-resize-event 's-down-mouse-3 + "Emacs event to start resizing a window.") + +(defvar exwm-input--move-keysym nil) +(defvar exwm-input--move-mask nil) +(defvar exwm-input--resize-keysym nil) +(defvar exwm-input--resize-mask nil) + +(defvar exwm-input--timestamp xcb:Time:CurrentTime + "A recent timestamp received from X server. + +It's updated in several occasions, and only used by `exwm-input--set-focus'.") + +(defun exwm-input--set-focus (id) + "Set input focus to window ID in a proper way." + (exwm--with-current-id id + (setq exwm-input--focus-id id) + (if (and (not exwm--hints-input) + (memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols)) + (xcb:+request exwm--connection + (make-instance 'xcb:icccm:SendEvent + :destination id + :event (xcb:marshal + (make-instance 'xcb:icccm:WM_TAKE_FOCUS + :window id + :time exwm-input--timestamp) + exwm--connection))) + (xcb:+request exwm--connection + (make-instance 'xcb:SetInputFocus + :revert-to xcb:InputFocus:Parent :focus id + :time xcb:Time:CurrentTime))) + (xcb:flush exwm--connection))) + +(defvar exwm-input--focus-id xcb:Window:None + "The window that is theoretically focused.") +(defvar exwm-input--focus-lock nil + "Non-nil when input focus should stay unchanged.") + +(defun exwm-input--update-focus () + "Update input focus." + (unless exwm-input--focus-lock + (setq exwm-input--focus-lock t) + (when (eq (current-buffer) (window-buffer)) ;e.g. with-temp-buffer + (if (eq major-mode 'exwm-mode) + (progn (setq exwm-input--focus-id exwm--id) + (when exwm--floating-frame + (if (eq (selected-frame) exwm--floating-frame) + ;; Cancel the possible input focus redirection + (redirect-frame-focus exwm--floating-frame nil) + ;; Focus the floating frame + (x-focus-frame exwm--floating-frame))) + ;; Finally focus the window + (exwm-input--set-focus exwm-input--focus-id)) + (exwm--with-current-id exwm-input--focus-id + (setq exwm-input--focus-id xcb:Window:None) + (let ((frame (selected-frame))) + (if exwm--floating-frame + (unless (or (eq frame exwm--floating-frame) + (active-minibuffer-window)) + ;; Redirect input focus to the workspace frame + (redirect-frame-focus exwm--floating-frame frame)) + ;; Focus the workspace frame + (x-focus-frame frame)))))) + (setq exwm-input--focus-lock nil))) + +(defun exwm-input--finish-key-sequence () + "Mark the end of a key sequence (with the aid of `pre-command-hook')." + (setq exwm-input--during-key-sequence nil) + (when exwm-input--temp-line-mode + (setq exwm-input--temp-line-mode nil) + (exwm-input--release-keyboard))) + +(defun exwm-input--on-MappingNotify (data synthetic) + "Handle MappingNotify event." + (let ((obj (make-instance 'xcb:MappingNotify))) + (xcb:unmarshal obj data) + (with-slots (request first-keycode count) obj + (cond ((= request xcb:Mapping:Modifier) + ;; Modifier keys changed + (xcb:keysyms:update-modifier-mapping exwm--connection)) + ((= request xcb:Mapping:Keyboard) + ;; Only updated changed keys + (xcb:keysyms:update-keyboard-mapping exwm--connection + first-keycode count)))))) + +(defun exwm-input--on-ButtonPress (data synthetic) + "Handle ButtonPress event." + (let ((obj (make-instance 'xcb:ButtonPress)) + (mode xcb:Allow:SyncPointer)) + (xcb:unmarshal obj data) + (with-slots (detail time event state) obj + (setq exwm-input--timestamp time) + (cond ((and (= state exwm-input--move-mask) + (= detail exwm-input--move-keysym)) + ;; Move + (exwm-floating--start-moveresize event + xcb:ewmh:_NET_WM_MOVERESIZE_MOVE)) + ((and (= state exwm-input--resize-mask) + (= detail exwm-input--resize-keysym)) + ;; Resize + (exwm-floating--start-moveresize event)) + (t + ;; Click to focus + (unless (and (boundp 'exwm--id) (= event exwm--id)) + (exwm--with-current-id event + (raise-frame (or exwm--floating-frame exwm--frame)) + (select-window (get-buffer-window nil 'visible)))) + ;; The event should be replayed + (setq mode xcb:Allow:ReplayPointer)))) + (xcb:+request exwm--connection + (make-instance 'xcb:AllowEvents :mode mode :time xcb:Time:CurrentTime)) + (xcb:flush exwm--connection))) + +(defun exwm-input--on-KeyPress (data synthetic) + "Handle KeyPress event." + (let ((obj (make-instance 'xcb:KeyPress))) + (xcb:unmarshal obj data) + (setq exwm-input--timestamp (slot-value obj 'time)) + (funcall 'exwm-input--handle-KeyPress obj))) + +(defvar exwm-input--global-keys nil "Global key bindings.") +(defvar exwm-input--global-prefix-keys nil + "List of prefix keys of global key bindings.") + +(defun exwm-input--update-global-prefix-keys () + "Update `exwm-input--global-prefix-keys'." + (when exwm--connection + (let ((original exwm-input--global-prefix-keys) + keysym) + (setq exwm-input--global-prefix-keys nil) + (dolist (i exwm-input--global-keys) + (cl-pushnew (elt i 0) exwm-input--global-prefix-keys)) + (unless (equal original exwm-input--global-prefix-keys) + ;; Grab global keys on root window + (if (xcb:+request-checked+request-check exwm--connection + (make-instance 'xcb:UngrabKey + :key xcb:Grab:Any :grab-window exwm--root + :modifiers xcb:ModMask:Any)) + (exwm--log "Failed to ungrab keys") + (dolist (i exwm-input--global-prefix-keys) + (setq keysym (xcb:keysyms:event->keysym i)) + (when (or (not keysym) + (xcb:+request-checked+request-check exwm--connection + (make-instance 'xcb:GrabKey + :owner-events 0 + :grab-window exwm--root + :modifiers (cadr keysym) + :key (xcb:keysyms:keysym->keycode + exwm--connection (car keysym)) + :pointer-mode xcb:GrabMode:Async + :keyboard-mode xcb:GrabMode:Async))) + (user-error "[EXWM] Failed to grab key: %s" i)))))))) + +(defun exwm-input-set-key (key command) + "Set a global key binding." + (global-set-key key command) + (cl-pushnew key exwm-input--global-keys)) + +(defvar exwm-input--during-key-sequence nil + "Non-nil indicates Emacs is waiting for more keys to form a key sequence.") +(defvar exwm-input--temp-line-mode nil + "Non-nil indicates it's in temporary line-mode for char-mode.") + +;; ;; This implementation has a problem that it also releases queued keys after +;; ;; requesting AllowEvent. The client window will capture unexpected key events +;; ;; in this case. +;; ;; P.S.; to use this implementation, comment out the KeyRelease listener +;; ;; together with this one and make GrabKey in Sync mode. +;; (cl-defmethod exwm-input--handle-KeyPress-line-mode ((obj xcb:KeyPress)) +;; "Parse X KeyPress event to Emacs key event and then feed the command loop." +;; (with-slots (detail state) obj +;; (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) +;; event window mode) +;; (when (and keysym +;; (setq event (xcb:keysyms:keysym->event keysym state)) +;; (or exwm-input--during-key-sequence +;; (= exwm-input--focus-id xcb:Window:None) +;; (setq window (active-minibuffer-window)) +;; (eq event ?\C-c) ;mode-specific key +;; (memq event exwm-input--global-prefix-keys) +;; (memq event exwm-input-prefix-keys) +;; (memq event +;; exwm-input--simulation-prefix-keys))) +;; (setq mode xcb:Allow:SyncKeyboard) +;; (unless window (setq exwm-input--during-key-sequence t)) +;; (push event unread-command-events)) +;; (xcb:+request exwm--connection +;; (make-instance 'xcb:AllowEvents +;; :mode (or mode xcb:Allow:ReplayKeyboard) +;; :time xcb:Time:CurrentTime)) +;; (xcb:flush exwm--connection)))) + +;; This implementation has a drawback that some (legacy) applications +;; (e.g. xterm) ignore the synthetic key events, making it only viable for EXWM +;; to work in char-mode in such case. +(cl-defmethod exwm-input--handle-KeyPress-line-mode ((obj xcb:KeyPress)) + "Parse X KeyPress event to Emacs key event and then feed the command loop." + (with-slots (detail state) obj + (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) + event minibuffer-window) + (if (and keysym + (setq event (xcb:keysyms:keysym->event keysym state)) + (or exwm-input--during-key-sequence + (= exwm-input--focus-id xcb:Window:None) + (setq minibuffer-window (active-minibuffer-window)) + (eq event ?\C-c) ;mode-specific key + (memq event exwm-input--global-prefix-keys) + (memq event exwm-input-prefix-keys) + (memq event exwm-input--simulation-prefix-keys))) + ;; Forward key to Emacs frame + (progn (unless minibuffer-window + (setq exwm-input--during-key-sequence t)) + (push event unread-command-events)) + ;; Forward key to window + (xcb:+request exwm--connection + (make-instance 'xcb:SendEvent + :propagate 0 :destination (slot-value obj 'event) + :event-mask xcb:EventMask:NoEvent + :event (xcb:marshal obj exwm--connection))) + (xcb:flush exwm--connection))))) + +(cl-defmethod exwm-input--handle-KeyPress-char-mode ((obj xcb:KeyPress)) + "Handle KeyPress event in char-mode." + (with-slots (detail state) obj + (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) + event) + (when (and keysym (setq event (xcb:keysyms:keysym->event keysym state))) + (setq exwm-input--temp-line-mode t + exwm-input--during-key-sequence t) + (push event unread-command-events) + (exwm-input--grab-keyboard))))) ;grab keyboard temporarily + +(defalias 'exwm-input--handle-KeyPress 'exwm-input--handle-KeyPress-line-mode + "Generic function for handling KeyPress event.") + +(defun exwm-input--grab-keyboard (&optional id) + "Grab all key events on window ID." + (unless id (setq id (exwm--buffer->id (window-buffer)))) + (when (xcb:+request-checked+request-check exwm--connection + (make-instance 'xcb:GrabKey + :owner-events 0 :grab-window id + :modifiers xcb:ModMask:Any + :key xcb:Grab:Any + :pointer-mode xcb:GrabMode:Async + :keyboard-mode xcb:GrabMode:Async)) + (exwm--log "Failed to grab keyboard for #x%x" id)) + (defalias 'exwm-input--handle-KeyPress + 'exwm-input--handle-KeyPress-line-mode)) + +(defun exwm-input--release-keyboard (&optional id) + "Ungrab all key events on window ID." + (unless id (setq id (exwm--buffer->id (window-buffer)))) + (when (xcb:+request-checked+request-check exwm--connection + (make-instance 'xcb:UngrabKey + :key xcb:Grab:Any :grab-window id + :modifiers xcb:ModMask:Any)) + (exwm--log "Failed to release keyboard for #x%x" id)) + (defalias 'exwm-input--handle-KeyPress + 'exwm-input--handle-KeyPress-char-mode)) + +(defun exwm-input-grab-keyboard (&optional id) + "Switch to line-mode." + (interactive) + (exwm-input--grab-keyboard id) + (setq mode-line-process + '(": " + (:propertize "line" + help-echo "mouse-1: Switch to char-mode" + mouse-face mode-line-highlight + local-map + (keymap + (mode-line + keymap + (down-mouse-1 . exwm-input-release-keyboard)))))) + (force-mode-line-update)) + +(defun exwm-input-release-keyboard (&optional id) + "Switch to char-mode." + (interactive) + (exwm-input--release-keyboard id) + (setq mode-line-process + '(": " + (:propertize "char" + help-echo "mouse-1: Switch to line-mode" + mouse-face mode-line-highlight + local-map + (keymap + (mode-line + keymap + (down-mouse-1 . exwm-input-grab-keyboard)))))) + (force-mode-line-update)) + +(defun exwm-input--fake-key (event) + "Fake a key event equivalent to Emacs event EVENT." + (let* ((keysym (xcb:keysyms:event->keysym event)) + (keycode (xcb:keysyms:keysym->keycode exwm--connection (car keysym))) + (id (exwm--buffer->id (window-buffer (selected-window))))) + (when keycode + (xcb:+request exwm--connection + (make-instance 'xcb:SendEvent + :propagate 0 :destination id + :event-mask xcb:EventMask:NoEvent + :event (xcb:marshal + (make-instance 'xcb:KeyPress + :detail keycode + :time xcb:Time:CurrentTime + :root exwm--root :event id + :child 0 + :root-x 0 :root-y 0 + :event-x 0 :event-y 0 + :state (cadr keysym) + :same-screen 1) + exwm--connection)))) + (xcb:flush exwm--connection))) + +(defun exwm-input-send-next-key (times) + "Send next key to client window." + (interactive "p") + (when (> times 12) (setq times 12)) + (let (key keys) + (dotimes (i times) + ;; Skip events not from keyboard + (setq exwm-input--during-key-sequence t) + (catch 'break + (while t + (setq key (read-key (format "Send key: %s (%d/%d)" + (key-description keys) + (1+ i) times))) + (unless (listp key) (throw 'break nil)))) + (setq exwm-input--during-key-sequence nil) + (setq keys (vconcat keys (vector key))) + (exwm-input--fake-key key)))) + +;; (defun exwm-input-send-last-key () +;; (interactive) +;; (unless (listp last-input-event) ;not a key event +;; (exwm-input--fake-key last-input-event))) + +(defvar exwm-input-prefix-keys + '(?\C-x ?\C-u ?\C-h ?\M-x ?\M-` ?\M-\S-! ?\M-\S-& ?\M-\S-:) + "List of prefix keys EXWM should forward to Emacs when in line-mode.") + +(defvar exwm-input--simulation-keys nil "Simulation keys in line-mode.") +(defvar exwm-input--simulation-prefix-keys nil + "List of prefix keys of simulation keys in line-mode.") + +(defun exwm-input--update-simulation-prefix-keys () + "Update the list of prefix keys of simulation keys." + (setq exwm-input--simulation-prefix-keys nil) + (dolist (i exwm-input--simulation-keys) + (define-key exwm-mode-map (car i) 'exwm-input-send-simulation-key) + (cl-pushnew (elt (car i) 0) exwm-input--simulation-prefix-keys))) + +(defun exwm-input-set-simulation-keys (simulation-keys) + "Set simulation keys. + +SIMULATION-KEYS is a list of alist (key-sequence1 . key-sequence2)." + (setq exwm-input--simulation-keys nil) + (dolist (i simulation-keys) + (cl-pushnew `(,(vconcat (car i)) . ,(cdr i)) exwm-input--simulation-keys)) + (exwm-input--update-simulation-prefix-keys)) + +(defun exwm-input-send-simulation-key () + "Fake a key event according to last input key sequence." + (interactive) + (let ((pair (assoc (this-command-keys-vector) ;FIXME: pefix + exwm-input--simulation-keys)) + key) + (when pair + (setq pair (cdr pair)) + (unless (listp pair) + (setq pair (list pair))) + (dolist (i pair) + (exwm-input--fake-key i))))) + +(defun exwm-input--init () + "Initialize the keyboard module." + ;; Refresh keyboard mapping + (xcb:keysyms:init exwm--connection) + ;; Convert move/resize buttons + (let ((move-key (xcb:keysyms:event->keysym exwm-input-move-event)) + (resize-key (xcb:keysyms:event->keysym exwm-input-resize-event))) + (setq exwm-input--move-keysym (car move-key) + exwm-input--move-mask (cadr move-key) + exwm-input--resize-keysym (car resize-key) + exwm-input--resize-mask (cadr resize-key))) + ;; Attach event listeners + (xcb:+event exwm--connection 'xcb:MappingNotify + 'exwm-input--on-MappingNotify) + (xcb:+event exwm--connection 'xcb:KeyPress 'exwm-input--on-KeyPress) + (xcb:+event exwm--connection 'xcb:ButtonPress 'exwm-input--on-ButtonPress) + (xcb:+event exwm--connection 'xcb:ButtonRelease + 'exwm-floating--stop-moveresize) + (xcb:+event exwm--connection 'xcb:MotionNotify 'exwm-floating--do-moveresize) + ;; `pre-command-hook' marks the end of a key sequence (existing or not) + (add-hook 'pre-command-hook 'exwm-input--finish-key-sequence) + ;; Update focus when buffer list updates + (add-hook 'buffer-list-update-hook 'exwm-input--update-focus) + ;; Update prefix keys for global keys + (exwm-input--update-global-prefix-keys)) + + + +(provide 'exwm-input) + +;;; exwm-input.el ends here diff --git a/exwm-layout.el b/exwm-layout.el new file mode 100644 index 0000000000..7630b688e2 --- /dev/null +++ b/exwm-layout.el @@ -0,0 +1,194 @@ +;;; exwm-layout.el --- Layout Module for EXWM -*- lexical-binding: t -*- + +;; Copyright (C) 2015 Chris Feng + +;; Author: Chris Feng +;; Keywords: unix + +;; This file is not part of GNU Emacs. + +;; This file is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This file is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this file. If not, see . + +;;; Commentary: + +;; This module is responsible for keeping X client window properly displayed. + +;;; Code: + +(defun exwm-layout--show (id &optional window) + "Show window ID exactly fit in the Emacs window WINDOW." + (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window id)) + (xcb:+request exwm--connection + (make-instance 'xcb:icccm:set-WM_STATE + :window id :state xcb:icccm:WM_STATE:NormalState + :icon xcb:Window:None)) + (let* ((edges (or (exwm--with-current-id id exwm--floating-edges) + (window-inside-pixel-edges window))) + (x (elt edges 0)) + (y (elt edges 1)) + (width (- (elt edges 2) (elt edges 0))) + (height (- (elt edges 3) (elt edges 1)))) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window id + :value-mask (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height + xcb:ConfigWindow:StackMode) + :x x :y y :width width :height height + ;; In order to put non-floating window at bottom + :stack-mode xcb:StackMode:Below)) + (xcb:+request exwm--connection + (make-instance 'xcb:SendEvent + :propagate 0 :destination id + :event-mask xcb:EventMask:StructureNotify + :event (xcb:marshal + (make-instance 'xcb:ConfigureNotify + :event id :window id + :above-sibling xcb:Window:None + :x x :y y + :width width :height height + :border-width 0 + :override-redirect 0) + exwm--connection)))) + (xcb:flush exwm--connection)) + +(defun exwm-layout--hide (id) + "Hide window ID." + (unless (eq xcb:icccm:WM_STATE:IconicState ;already hidden + (with-current-buffer (exwm--id->buffer id) exwm-state)) + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window id :value-mask xcb:CW:EventMask + :event-mask xcb:EventMask:NoEvent)) + (xcb:+request exwm--connection (make-instance 'xcb:UnmapWindow :window id)) + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window id :value-mask xcb:CW:EventMask + :event-mask exwm--client-event-mask)) + (xcb:+request exwm--connection + (make-instance 'xcb:icccm:set-WM_STATE + :window id + :state xcb:icccm:WM_STATE:IconicState + :icon xcb:Window:None)) + (xcb:flush exwm--connection))) + +(defun exwm-layout-set-fullscreen (&optional id) + "Make window ID fullscreen." + (interactive) + (with-current-buffer (if id (exwm--id->buffer id) (window-buffer)) + ;; Set the floating frame fullscreen first when the client is floating + (when exwm--floating-frame + (let* ((outer-id (frame-parameter exwm--floating-frame 'exwm-outer-id)) + (geometry (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetGeometry + :drawable outer-id)))) + (setq exwm--floating-frame-geometry + (vector (slot-value geometry 'x) (slot-value geometry 'y) + (slot-value geometry 'width) + (slot-value geometry 'height))) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window outer-id + :value-mask (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height) + :x 0 :y 0 + :width (x-display-pixel-width) + :height (x-display-pixel-height)))) + (xcb:flush exwm--connection)) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window exwm--id + :value-mask (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height) + :x 0 :y 0 + :width (x-display-pixel-width) + :height (x-display-pixel-height))) + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_STATE + :window exwm--id + :data (vector xcb:Atom:_NET_WM_STATE_FULLSCREEN))) + (xcb:flush exwm--connection) + (cl-assert (not exwm--fullscreen)) + (setq exwm--fullscreen t) + (exwm-input-release-keyboard))) + +(defun exwm-layout-unset-fullscreen (&optional id) + "Restore window from fullscreen state." + (interactive) + (with-current-buffer (if id (exwm--id->buffer id) (window-buffer)) + ;; Restore the floating frame if the client is floating + (when exwm--floating-frame + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter exwm--floating-frame + 'exwm-outer-id) + :value-mask (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height) + :x (elt exwm--floating-frame-geometry 0) + :y (elt exwm--floating-frame-geometry 1) + :width (elt exwm--floating-frame-geometry 2) + :height (elt exwm--floating-frame-geometry 3)))) + (exwm-layout--show exwm--id) + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_STATE :window exwm--id :data [])) + (xcb:flush exwm--connection) + (cl-assert exwm--fullscreen) + (setq exwm--fullscreen nil) + (exwm-input-grab-keyboard))) + +(defvar exwm-layout--window-configuration (current-window-configuration) + "Last saved window configuration, for avoiding unnecessary refreshes.") + +(defun exwm-layout--refresh () + "Refresh layout." + (unless (compare-window-configurations exwm-layout--window-configuration + (current-window-configuration)) + (setq exwm-layout--window-configuration (current-window-configuration)) + (let ((frame (selected-frame)) + windows) + (if (not (memq frame exwm-workspace--list)) + ;; Refresh a floating frame + (when (eq major-mode 'exwm-mode) + (with-current-buffer (window-buffer (frame-first-window frame)) + (exwm-layout--show exwm--id (frame-first-window frame)))) + ;; Refresh the whole workspace + (dolist (pair exwm--id-buffer-alist) + (with-current-buffer (cdr pair) + ;; Exclude windows on other workspaces and floating frames + (when (and (eq frame exwm--frame) (not exwm--floating-frame)) + (setq windows (get-buffer-window-list (current-buffer) 0)) + (if (not windows) + (exwm-layout--hide exwm--id) + (exwm-layout--show exwm--id (car windows)) + (dolist (i (cdr windows)) + (set-window-buffer i "*scratch*")))))))))) + +(defun exwm-layout--init () + "Initialize layout module." + ;; Auto refresh layout + (add-hook 'window-configuration-change-hook 'exwm-layout--refresh)) + + + +(provide 'exwm-layout) + +;;; exwm-layout.el ends here diff --git a/exwm-manage.el b/exwm-manage.el new file mode 100644 index 0000000000..afab18e924 --- /dev/null +++ b/exwm-manage.el @@ -0,0 +1,333 @@ +;;; exwm-manage.el --- Window Management Module for -*- lexical-binding: t -*- +;;; EXWM + +;; Copyright (C) 2015 Chris Feng + +;; Author: Chris Feng +;; Keywords: unix + +;; This file is not part of GNU Emacs. + +;; This file is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This file is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this file. If not, see . + +;;; Commentary: + +;; This is the fundamental module of EXWM that deals with window management. + +;;; Code: + +(require 'exwm-input) + +(defvar exwm-manage-finish-hook nil + "Normal hook run after a window is just managed, in the context of the +corresponding buffer.") + +(defun exwm-manage--update-geometry (id &optional force) + "Update window geometry." + (exwm--with-current-id id + (unless (and exwm--geometry (not force)) + (let ((reply (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetGeometry :drawable id)))) + (when reply ;nil when destroyed + (setq exwm--geometry reply)))))) + +(defun exwm-manage--manage-window (id) + "Manage window ID." + (setq exwm-input--focus-lock t) + (catch 'return + ;; Ensure it's not managed + (when (assoc id exwm--id-buffer-alist) + (throw 'return 'managed)) + ;; Ensure it's alive + (when (xcb:+request-checked+request-check exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window id :value-mask xcb:CW:EventMask + :event-mask exwm--client-event-mask)) + (throw 'return 'dead)) + (with-current-buffer (generate-new-buffer "*EXWM*") + (push `(,id . ,(current-buffer)) exwm--id-buffer-alist) + (exwm-mode) + (setq exwm--id id) + (exwm--update-window-type id) + (exwm--update-class id) + (exwm-manage--update-geometry id) + ;; No need to manage (please check OverrideRedirect outside) + (when (or + (not + (or (not exwm-window-type) + (memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY exwm-window-type) + (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG exwm-window-type) + (memq xcb:Atom:_NET_WM_WINDOW_TYPE_NORMAL exwm-window-type))) + ;; For Java applications + (and exwm-instance-name + (string-prefix-p "sun-awt-X11-" exwm-instance-name) + (not (string-suffix-p "XFramePeer" exwm-instance-name)))) + ;; Remove all events + (xcb:+request-checked+request-check exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window id :value-mask xcb:CW:EventMask + :event-mask xcb:EventMask:NoEvent)) + ;; The window needs to be mapped + (xcb:+request exwm--connection + (make-instance xcb:MapWindow :window id)) + (with-slots (x y width height) exwm--geometry + ;; Reparent to virtual root (essential) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window id + :parent (frame-parameter exwm-workspace--current + 'exwm-window-id) + :x x :y y)) + ;; Center window of type _NET_WM_WINDOW_TYPE_SPLASH + (when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_SPLASH exwm-window-type) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window id + :value-mask (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y) + :x (/ (- (x-display-pixel-width) width) 2) + :y (/ (- (x-display-pixel-height) height) 2))))) + (xcb:flush exwm--connection) + (setq kill-buffer-query-functions nil) + (setq exwm--id-buffer-alist (assq-delete-all id exwm--id-buffer-alist)) + (kill-buffer (current-buffer)) + (throw 'return 'ignored)) + ;; Manage the window + (xcb:+request exwm--connection ;remove border + (make-instance 'xcb:ConfigureWindow + :window id :value-mask xcb:ConfigWindow:BorderWidth + :border-width 0)) + (xcb:+request exwm--connection ;grab buttons for set focus/move/resize + (make-instance 'xcb:GrabButton + :owner-events 0 :grab-window id + :event-mask xcb:EventMask:ButtonPress + :pointer-mode xcb:GrabMode:Sync + :keyboard-mode xcb:GrabMode:Async + :confine-to xcb:Window:None + :cursor xcb:Cursor:None + :button xcb:ButtonIndex:Any + :modifiers xcb:ModMask:Any)) + (xcb:flush exwm--connection) + (exwm--update-title id) + (exwm--update-transient-for id) + (exwm--update-normal-hints id) + (exwm--update-hints id) + (exwm--update-protocols id) + (exwm--update-state id) + (if (or exwm-transient-for exwm--fixed-size + (memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY exwm-window-type) + (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG exwm-window-type)) + (exwm-floating--set-floating id) + (exwm-floating--unset-floating id)) + (exwm-input-grab-keyboard id) + (exwm-workspace--update-switch-history) + (setq exwm-input--focus-lock nil) ;unlocked in advance + (with-current-buffer (exwm--id->buffer id) + (run-hooks 'exwm-manage-finish-hook)))) + (setq exwm-input--focus-lock nil)) + +(defun exwm-manage--unmanage-window (id &optional withdraw-only) + "Unmanage window ID." + (let ((buffer (exwm--id->buffer id))) + (setq exwm--id-buffer-alist (assq-delete-all id exwm--id-buffer-alist)) + (when (buffer-live-p buffer) + (with-current-buffer buffer + (exwm-workspace--update-switch-history) + ;; + (when withdraw-only + ;; Reparent back to root + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window id :value-mask xcb:CW:EventMask + :event-mask xcb:EventMask:NoEvent)) + (let (x y geometry geometry-parent) + (if (not exwm--floating-frame) + (setq x 0 y 0) ;the position does not matter + (setq geometry-parent + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetGeometry + :drawable + (frame-parameter exwm--floating-frame + 'exwm-outer-id))) + geometry (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetGeometry + :drawable id))) + (if (not (and geometry-parent geometry)) + (setq x 0 y 0) ;e.g. have been destroyed + (setq x (+ (slot-value geometry-parent 'x) + (slot-value geometry 'x)) + y (+ (slot-value geometry-parent 'y) + (slot-value geometry 'y))))) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window id :parent exwm--root :x x :y y))) + ;; Delete WM_STATE property + (xcb:+request exwm--connection + (make-instance 'xcb:DeleteProperty + :window id :property xcb:Atom:WM_STATE)) + (xcb:flush exwm--connection)) + (setq kill-buffer-query-functions nil) + (kill-buffer) + (select-frame-set-input-focus exwm-workspace--current))))) + +(defun exwm-manage--scan () + "Search for existing windows and try to manage them." + (let* ((tree (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:QueryTree :window exwm--root)))) + (dolist (i (slot-value tree 'children)) + (with-slots (override-redirect map-state) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetWindowAttributes :window i)) + (when (and (= 0 override-redirect) (= xcb:MapState:Viewable map-state)) + (exwm-manage--manage-window i)))))) + +(defvar exwm-manage--ping-lock nil + "Non-nil indicates EXWM is pinging a window.") +(defvar exwm-manage-ping-timeout 3 "Seconds to wait before killing a client.") + +(defun exwm-manage--close-window (id &optional buffer) + "Close window ID in a proper way." + (catch 'return + (unless buffer (setq buffer (exwm--id->buffer id))) + ;; Destroy the client window if it does not support WM_DELETE_WINDOW + (unless (and (buffer-live-p buffer) + (with-current-buffer buffer + (memq xcb:Atom:WM_DELETE_WINDOW exwm--protocols))) + (xcb:+request exwm--connection + (make-instance 'xcb:DestroyWindow :window id)) + (xcb:flush exwm--connection) + (throw 'return nil)) + ;; Try to close the window with WM_DELETE_WINDOW client message + (xcb:+request exwm--connection + (make-instance 'xcb:icccm:SendEvent + :destination id + :event (xcb:marshal + (make-instance 'xcb:icccm:WM_DELETE_WINDOW + :window id) + exwm--connection))) + (xcb:flush exwm--connection) + ;; Try to determine if the client stop responding + ;; FIXME: check + (with-current-buffer buffer + (when (memq xcb:Atom:_NET_WM_PING exwm--protocols) + (setq exwm-manage--ping-lock t) + (xcb:+request exwm--connection + (make-instance 'xcb:SendEvent + :propagate 0 :destination id + :event-mask xcb:EventMask:NoEvent + :event (xcb:marshal + (make-instance 'xcb:ewmh:_NET_WM_PING + :window id :timestamp 0 + :client-window id) + exwm--connection))) + (xcb:flush exwm--connection) + (with-timeout (exwm-manage-ping-timeout + (if (yes-or-no-p (format "\ +`%s' is not responding. Would you like to kill it? " (buffer-name buffer))) + (progn (exwm-manage--kill-client id) + (throw 'return nil)) + (throw 'return nil))) + (while (and exwm-manage--ping-lock + (exwm--id->buffer id)) ;may have be destroyed + (accept-process-output nil 0.1))) + (throw 'return nil))) + (throw 'return nil))) + +(defun exwm-manage--kill-client (&optional id) + "Kill an X client." + (interactive) + (unless id (setq id (exwm--buffer->id (current-buffer)))) + (let ((pid (slot-value + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:ewmh:get-_NET_WM_PID :window id)) + 'value))) + (if pid + (signal-process pid 'SIGKILL) + (xcb:+request exwm--connection + (make-instance 'xcb:KillClient :resource id)) + (xcb:flush exwm--connection)))) + +(defun exwm-manage--on-ConfigureRequest (data synthetic) + "Handle ConfigureRequest event." + (let ((obj (make-instance 'xcb:ConfigureRequest)) + buffer edges) + (xcb:unmarshal obj data) + (with-slots (window x y width height border-width) obj + (if (setq buffer (exwm--id->buffer window)) + ;; Send client message for managed windows + (progn + (setq edges (or (with-current-buffer buffer exwm--floating-edges) + (window-inside-absolute-pixel-edges + (get-buffer-window buffer)))) + (xcb:+request exwm--connection + (make-instance 'xcb:SendEvent + :propagate 0 :destination window + :event-mask xcb:EventMask:StructureNotify + :event (xcb:marshal + (make-instance + 'xcb:ConfigureNotify + :event window :window window + :above-sibling xcb:Window:None + :x (elt edges 0) :y (elt edges 1) + :width (- (elt edges 2) (elt edges 0)) + :height (- (elt edges 3) (elt edges 1)) + :border-width 0 :override-redirect 0) + exwm--connection)))) + ;; Configure unmanaged windows + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window window + :value-mask (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height + xcb:ConfigWindow:BorderWidth) + :x x :y y :width width :height height + :border-width border-width))))) + (xcb:flush exwm--connection)) + +(defun exwm-manage--on-MapRequest (data synthetic) + "Handle MapRequest event." + (let ((obj (make-instance 'xcb:MapRequest))) + (xcb:unmarshal obj data) + (exwm-manage--manage-window (slot-value obj 'window)))) + +(defun exwm-manage--on-UnmapNotify (data synthetic) + "Handle UnmapNotify event." + (unless synthetic + (let ((obj (make-instance 'xcb:UnmapNotify))) + (xcb:unmarshal obj data) + (exwm-manage--unmanage-window (slot-value obj 'window) t)))) + +(defun exwm-manage--on-DestroyNotify (data synthetic) + "Handle DestroyNotify event." + (unless synthetic + (let ((obj (make-instance 'xcb:DestroyNotify))) + (xcb:unmarshal obj data) + (exwm-manage--unmanage-window (slot-value obj 'window))))) + +(defun exwm-manage--init () + "Initialize manage module." + (xcb:+event exwm--connection 'xcb:ConfigureRequest + 'exwm-manage--on-ConfigureRequest) + (xcb:+event exwm--connection 'xcb:MapRequest 'exwm-manage--on-MapRequest) + (xcb:+event exwm--connection 'xcb:UnmapNotify 'exwm-manage--on-UnmapNotify) + (xcb:+event exwm--connection 'xcb:DestroyNotify + 'exwm-manage--on-DestroyNotify)) + + + +(provide 'exwm-manage) + +;;; exwm-manage.el ends here diff --git a/exwm-workspace.el b/exwm-workspace.el new file mode 100644 index 0000000000..c225c434dd --- /dev/null +++ b/exwm-workspace.el @@ -0,0 +1,230 @@ +;;; exwm-workspace.el --- Workspace Module for EXWM -*- lexical-binding: t -*- + +;; Copyright (C) 2015 Chris Feng + +;; Author: Chris Feng +;; Keywords: unix + +;; This file is not part of GNU Emacs. + +;; This file is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This file is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this file. If not, see . + +;;; Commentary: + +;; This module adds workspace support for EXWM. + +;; Todo: +;; + prevent from deleting frames of Emacs client (`frame-delete-functions') + +;;; Code: + +(defvar exwm-workspace-number 4 "Number of workspaces (1 ~ 10).") +(defvar exwm-workspace--list nil "List of all workspaces (Emacs frames).") +(defvar exwm-workspace--switch-map + (let ((map (make-sparse-keymap))) + (define-key map [t] (lambda () (interactive))) + (dotimes (i 10) + (define-key map (int-to-string i) + `(lambda () + (interactive) + (when (< ,i exwm-workspace-number) + (goto-history-element ,(1+ i)) + (exit-minibuffer))))) + (define-key map "\C-a" (lambda () (interactive) (goto-history-element 1))) + (define-key map "\C-e" (lambda () + (interactive) + (goto-history-element exwm-workspace-number))) + (define-key map "\C-g" 'abort-recursive-edit) + (define-key map "\C-]" 'abort-recursive-edit) + (define-key map "\C-j" 'exit-minibuffer) + ;; (define-key map "\C-m" 'exit-minibuffer) ;not working + (define-key map [return] 'exit-minibuffer) + (define-key map " " 'exit-minibuffer) + (define-key map "\C-f" 'previous-history-element) + (define-key map "\C-b" 'next-history-element) + ;; Alternative keys + (define-key map [right] 'previous-history-element) + (define-key map [left] 'next-history-element) + map) + "Keymap used for interactively switch workspace.") + +(defvar exwm-workspace--switch-history nil + "History for `read-from-minibuffer' to interactively switch workspace.") + +(defun exwm-workspace--update-switch-history () + "Update the history for switching workspace to reflect the latest status." + (let ((sequence (number-sequence 0 (1- exwm-workspace-number))) + (not-empty (make-vector exwm-workspace-number nil))) + (dolist (i exwm--id-buffer-alist) + (with-current-buffer (cdr i) + (setf (elt not-empty (cl-position exwm--frame exwm-workspace--list)) + t))) + (setq exwm-workspace--switch-history + (mapcar + (lambda (i) + (mapconcat + (lambda (j) + (format (if (= i j) "[%s]" " %s ") + (propertize + (int-to-string j) + 'face + (cond ((frame-parameter (elt exwm-workspace--list j) + 'exwm--urgency) + '(:foreground "orange")) + ((elt not-empty j) '(:foreground "green")) + (t nil))))) + sequence "")) + sequence)))) + +(defvar exwm-workspace--current nil "Current active workspace.") +(defvar exwm-workspace-current-index 0 "Index of current active workspace.") + +(defun exwm-workspace-switch (index &optional force) + "Switch to workspace INDEX. Query for INDEX if it's not specified. + +The optional FORCE option is for internal use only " + (interactive + (list + (let* ((history-add-new-input nil) ;prevent modifying history + (idx (read-from-minibuffer + "Workspace: " (elt exwm-workspace--switch-history + exwm-workspace-current-index) + exwm-workspace--switch-map nil + `(exwm-workspace--switch-history + . ,(1+ exwm-workspace-current-index))))) + (cl-position idx exwm-workspace--switch-history :test 'equal)))) + (unless (and (<= 0 index) (< index exwm-workspace-number)) + (user-error "[EXWM] Workspace index out of range: %d" index)) + (when (or force (/= exwm-workspace-current-index index)) + (select-frame-set-input-focus (elt exwm-workspace--list index)) + ;; Hide all workspaces but the selected one + (dotimes (i exwm-workspace-number) + (unless (= i index) (make-frame-invisible (elt exwm-workspace--list i)))) + (setq exwm-workspace--current (elt exwm-workspace--list index) + exwm-workspace-current-index index) + (setq default-minibuffer-frame (selected-frame)) + ;; Hide windows in other workspaces by preprending a space + (dolist (i exwm--id-buffer-alist) + (with-current-buffer (cdr i) + (let ((name (replace-regexp-in-string "^\\s-*" "" (buffer-name)))) + (rename-buffer (if (eq (selected-frame) exwm--frame) + name + (concat " " name)))))) + ;; Update demands attention flag + (set-frame-parameter (selected-frame) 'exwm--urgency nil) + ;; Update switch workspace history + (exwm-workspace--update-switch-history) + ;; Update _NET_CURRENT_DESKTOP + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_CURRENT_DESKTOP + :window exwm--root :data index)) + (xcb:flush exwm--connection))) + +(defun exwm-workspace-move-window (index &optional id) + "Move window ID to workspace INDEX." + (interactive + (list + (let* ((history-add-new-input nil) ;prevent modifying history + (idx (read-from-minibuffer + "Workspace: " (elt exwm-workspace--switch-history + exwm-workspace-current-index) + exwm-workspace--switch-map nil + `(exwm-workspace--switch-history + . ,(1+ exwm-workspace-current-index))))) + (cl-position idx exwm-workspace--switch-history :test 'equal)))) + (unless id (setq id (exwm--buffer->id (window-buffer)))) + (unless (and (<= 0 index) (< index exwm-workspace-number)) + (user-error "[EXWM] Workspace index out of range: %d" index)) + (when (/= exwm-workspace-current-index index) + (set-window-buffer (get-buffer-window (exwm--id->buffer id)) + (other-buffer)) + (let ((frame (elt exwm-workspace--list index))) + (with-current-buffer (exwm--id->buffer id) + (setq exwm--frame frame) + (rename-buffer + (concat " " (replace-regexp-in-string "^\\s-*" "" (buffer-name)))) + (if exwm--floating-frame + ;; Move the floating frame is enough + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window (frame-parameter exwm--floating-frame + 'exwm-outer-id) + :parent (frame-parameter frame 'exwm-window-id) + :x 0 :y 0)) + ;; Move the window itself + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window id :value-mask xcb:CW:EventMask + :event-mask xcb:EventMask:NoEvent)) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window id + :parent (frame-parameter frame 'exwm-window-id) + :x 0 :y 0)) + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window id :value-mask xcb:CW:EventMask + :event-mask exwm--client-event-mask))))) + (xcb:flush exwm--connection) + (exwm-workspace--update-switch-history))) + +(defun exwm-workspace--init () + "Initialize workspace module." + (cl-assert (and (< 0 exwm-workspace-number) (>= 10 exwm-workspace-number))) + ;; Prevent unexpected exit + (setq confirm-kill-emacs + (lambda (prompt) + (pcase (length exwm--id-buffer-alist) + (0 (y-or-n-p prompt)) + (x (yes-or-no-p (format "[EXWM] %d window%s currently alive. %s" + x (if (= x 1) "" "s") prompt)))))) + ;; Initialize workspaces + (setq exwm-workspace--list (frame-list)) + (when (< 1 (length exwm-workspace--list)) + ;; Emacs client creates an extra (but unusable) frame + (dolist (i exwm-workspace--list) + (unless (frame-parameter i 'window-id) + (setq exwm-workspace--list (delq i exwm-workspace--list))))) + (cl-assert (= 1 (length exwm-workspace--list))) + ;; Configure the existing frame + (set-frame-parameter (car exwm-workspace--list) 'fullscreen 'fullboth) + ;; Create remaining frames + (dotimes (i (1- exwm-workspace-number)) + (nconc exwm-workspace--list + (list (make-frame '((window-system . x) (fullscreen . fullboth)))))) + ;; Configure workspaces + (dolist (i exwm-workspace--list) + (let ((window-id (string-to-int (frame-parameter i 'window-id))) + (outer-id (string-to-int (frame-parameter i 'outer-window-id)))) + ;; Save window IDs + (set-frame-parameter i 'exwm-window-id window-id) + (set-frame-parameter i 'exwm-outer-id outer-id) + ;; Set OverrideRedirect on all frames + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window outer-id :value-mask xcb:CW:OverrideRedirect + :override-redirect 1)) + ;; Select events on all virtual roots + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window window-id :value-mask xcb:CW:EventMask + :event-mask xcb:EventMask:SubstructureRedirect)))) + ;; Switch to the first workspace + (exwm-workspace-switch 0 t)) + + + +(provide 'exwm-workspace) + +;;; exwm-workspace.el ends here diff --git a/exwm.el b/exwm.el new file mode 100644 index 0000000000..49c4380405 --- /dev/null +++ b/exwm.el @@ -0,0 +1,706 @@ +;;; exwm.el --- Emacs X Window Manager -*- lexical-binding: t -*- + +;; Copyright (C) 2015 Chris Feng + +;; Author: Chris Feng +;; Keywords: unix + +;; This file is not part of GNU Emacs. + +;; This file is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This file is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this file. If not, see . + +;;; Commentary: + +;; Overview +;; -------- +;; EXWM (Emacs X Window Manager) turns Emacs into a full-featured tiling X +;; window manager. It's built on top of XELB, thus purely consists of Elisp +;; codes. Some features include: +;; + It's fully keyboard-driven. +;; - You have full access to all key bindings in Emacs. +;; - It allows you to bind additional key sequences with `exwm-input-set-key' +;; (just like `global-set-key'). +;; - It supports simulation keys (i.e., map one key sequence to another). +;; + Workspace support. +;; - EXWM support up to 10 workspaces. +;; + ICCCM/EWMH compliance. +;; - Note that the support for EWMH can never be complete since EXWM is not a +;; conventional window manager. + +;; How it works +;; ------------ +;; Emacs itself is an tiling window manager, though unfortunately not for +;; managing X things. EXWM is therefore created to overcome this limitation by +;; relating X concepts to Emacs ones as shown in the following table. +;; +;; +=============+=========+ +;; | X Window | Emacs | +;; +=============+=========+ +;; | window | buffer | +;; +-------------+---------+ +;; | container* | window | +;; +-------------+---------+ +;; | workspace / | frame** | +;; | decoration | | +;; +=============+=========+ +;; * Here a container means the parent of an X client window created by window +;; manager for layout management. +;; ** In EXWM, A frame usually acts as a workspace. But for a floating window, +;; it's the decoration around the top-level window. +;; +;; Each X client window corresponds to a dedicated buffer in EXWM mode. When +;; such a buffer is buried or unburied, the attached X client window is hide or +;; shown accordingly. The position and size of the X client window is then +;; determined by the Emacs window its corresponding buffer displayed in. +;; +;; A buffer in EXWM mode also records which workspace it belongs to, and its +;; attached X client window is made a child (in the sense of X) of the +;; workspace frame. The switch between workspaces is simply done by switching +;; corresponding Emacs frames. + +;; Installation & configuration +;; ---------------------------- +;; Here are the minimal steps to get EXWM working: +;; 0. Install xelb and xelb-util first. +;; 1. Put EXWM somewhere on your disk and make sure it's in `load-path'. +;; 2. In your Emacs init file, add following lines (modify accordingly): +;; +;; (require 'exwm) +;; ;; We always need a way to go back from char-mode to line-mode +;; (exwm-input-set-key (kbd "s-r") 'exwm-reset) +;; ;; Bind a key to switch workspace interactively +;; (exwm-input-set-key (kbd "s-w") 'exwm-workspace-switch) +;; ;; Use class name to name an EXWM buffer +;; (add-hook 'exwm-update-class-hook +;; (lambda () (rename-buffer exwm-class-name t))) +;; ;; Enable EXWM +;; (exwm-enable) +;; +;; 3. Make a file '~/.xinitrc' with the content +;; +;; exec emacs +;; +;; 4. Launch EXWM in a console with +;; +;; xinit +;; +;; You should refer to other resources on how to further configure '~/.xinitrc' +;; and other init scripts to improve the working experience. Besides, you +;; should hide the menu-bar, tool-bar, etc to increase the usable space. + +;; Interactive modes +;; ----------------- +;; There are two modes in EXWM to interact with an X client window: line mode +;; and char mode. They are analogous to those concepts in `ansi-term'. EXWM +;; buffers are created in line mode by default. +;; +;; In line mode, all key events are intercepted and then forwarded to the X +;; client window except +;; + it forms a mode-specific key sequence (which begins with 'C-c'), or +;; + it forms a key sequence bound with `exwm-input-set-key', or +;; + it forms a key sequence starting with a line mode prefix key, or +;; + it forms a key sequence in line mode simulation keys. +;; You can use 'C-c q' (bound to `exwm-input-send-next-key', can be 'C-u' +;; prefixed) to send these keys to the client. +;; +;; In char mode however, no key event is intercepted except those bound with +;; `exwm-input-set-key'. Therefore you will almost always need to +;; 'exwm-input-set-key' a key sequence to go from char mode to line mode. + +;; Related projects +;; ---------------- +;; EXWM is not the first attempt to make Emacs an X window manger; there is +;; another ancient project called XWEM (http://www.nongnu.org/xwem/) for +;; XEmacs, though it seems nobody have ever got it working on GNU Emacs. + +;; Todo: +;; + Add RandR support. +;; + Investigate DnD support (e.g. drag a chromium tab to another window). +;; + Auto hide minibuffer, or allow users to place it elsewhere. +;; + Add system tray support. + +;; References: +;; + dwm (http://dwm.suckless.org/) +;; + i3 wm (https://i3wm.org/) +;; + Also see references within each required library. + +;;; Code: + +(require 'xcb) +(require 'xcb-icccm) +(require 'xcb-ewmh) +(require 'exwm-workspace) +(require 'exwm-layout) +(require 'exwm-floating) +(require 'exwm-manage) +(require 'exwm-input) + +(defvar exwm-debug-on nil "Non-nil to turn on debug for EXWM.") + +(defmacro exwm--log (format-string &rest args) + "Print debug message." + (when exwm-debug-on + `(message (concat "[EXWM] " ,format-string) ,@args))) + +(defconst exwm--client-event-mask + (logior xcb:EventMask:StructureNotify xcb:EventMask:PropertyChange) + "Event mask set on all managed windows.") + +(defvar exwm--connection nil "X connection.") +(defvar exwm--root nil "Root window.") +(defvar exwm--id-buffer-alist nil "Alist of ( . )") + +(defsubst exwm--id->buffer (id) + "X window ID => Emacs buffer." + (cdr (assoc id exwm--id-buffer-alist))) + +(defsubst exwm--buffer->id (buffer) + "Emacs buffer => X window ID." + (car (rassoc buffer exwm--id-buffer-alist))) + +(defun exwm--lock (&rest args) ;args are for abnormal hook + "Lock (disable all events)." + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window exwm--root + :value-mask xcb:CW:EventMask + :event-mask xcb:EventMask:NoEvent)) + (xcb:flush exwm--connection)) + +(defun exwm--unlock (&rest args) ;args are for abnormal hook + "Unlock (enable all events)." + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window exwm--root + :value-mask xcb:CW:EventMask + :event-mask (logior xcb:EventMask:StructureNotify + xcb:EventMask:SubstructureRedirect))) + (xcb:flush exwm--connection)) + +(defun exwm-reset () + "Reset window to standard state: non-fullscreen, line-mode." + (interactive) + (with-current-buffer (window-buffer (selected-window)) + (when exwm--fullscreen (exwm-layout-unset-fullscreen)) + (exwm-input-grab-keyboard))) + +(defmacro exwm--with-current-id (id &rest body) + "Evaluate BODY in the context of the buffer corresponding to window ID." + (declare (indent 1)) + `(when ,id + (let ((buffer (exwm--id->buffer ,id))) + (when buffer + (with-current-buffer buffer ,@body))))) + +(defun exwm--update-window-type (id &optional force) + "Update _NET_WM_WINDOW_TYPE." + (exwm--with-current-id id + (unless (and exwm-window-type (not force)) + (let ((reply (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:ewmh:get-_NET_WM_WINDOW_TYPE + :window id)))) + (when reply ;nil when destroyed + (setq exwm-window-type (append (slot-value reply 'value) nil))))))) + +(defvar exwm-update-class-hook nil + "Normal hook run when window class is updated.") + +(defun exwm--update-class (id &optional force) + "Update WM_CLASS." + (exwm--with-current-id id + (unless (and exwm-instance-name exwm-class-name (not force)) + (let ((reply (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:icccm:get-WM_CLASS :window id)))) + (when reply ;nil when destroyed + (setq exwm-instance-name (slot-value reply 'instance-name) + exwm-class-name (slot-value reply 'class-name)) + (when (and exwm-instance-name exwm-class-name) + (run-hooks 'exwm-update-class-hook))))))) + +(defvar exwm-update-title-hook nil + "Normal hook run when window title is updated.") + +(defun exwm--update-utf8-title (id &optional force) + "Update _NET_WM_NAME." + (exwm--with-current-id id + (when (or force (not exwm-title)) + (let ((reply (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:ewmh:get-_NET_WM_NAME :window id)))) + (when reply ;nil when destroyed + (setq exwm-title (slot-value reply 'value)) + (when exwm-title + (setq exwm--title-is-utf8 t) + (run-hooks 'exwm-update-title-hook))))))) + +(defun exwm--update-ctext-title (id &optional force) + "Update WM_NAME." + (exwm--with-current-id id + (unless (or exwm--title-is-utf8 + (and exwm-title (not force))) + (let ((reply (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:icccm:get-WM_NAME :window id)))) + (when reply ;nil when destroyed + (setq exwm-title (slot-value reply 'value)) + (when exwm-title + (run-hooks 'exwm-update-title-hook))))))) + +(defun exwm--update-title (id) + "Update _NET_WM_NAME or WM_NAME." + (exwm--update-utf8-title id) + (exwm--update-ctext-title id)) + +(defun exwm--update-transient-for (id &optional force) + "Update WM_TRANSIENT_FOR." + (exwm--with-current-id id + (unless (and exwm-transient-for (not force)) + (let ((reply (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:icccm:get-WM_TRANSIENT_FOR + :window id)))) + (when reply ;nil when destroyed + (setq exwm-transient-for (slot-value reply 'value))))))) + +(defun exwm--update-normal-hints (id &optional force) + "Update WM_NORMAL_HINTS." + (exwm--with-current-id id + (unless (and (not force) + (or exwm--normal-hints-x exwm--normal-hints-y + exwm--normal-hints-width exwm--normal-hints-height + exwm--normal-hints-min-width exwm--normal-hints-min-height + exwm--normal-hints-max-width exwm--normal-hints-max-height + ;; FIXME: other fields + )) + (let ((reply (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:icccm:get-WM_NORMAL_HINTS + :window id)))) + (when (and reply (slot-value reply 'flags)) ;nil when destroyed + (with-slots (flags x y width height min-width min-height max-width + max-height base-width base-height ;; win-gravity + ) + reply + (unless (= 0 (logand flags xcb:icccm:WM_SIZE_HINTS:USPosition)) + (setq exwm--normal-hints-x x exwm--normal-hints-y y)) + (unless (= 0 (logand flags xcb:icccm:WM_SIZE_HINTS:USSize)) + (setq exwm--normal-hints-width width + exwm--normal-hints-height height)) + (unless (= 0 (logand flags xcb:icccm:WM_SIZE_HINTS:PMinSize)) + (setq exwm--normal-hints-min-width min-width + exwm--normal-hints-min-height min-height)) + (unless (= 0 (logand flags xcb:icccm:WM_SIZE_HINTS:PMaxSize)) + (setq exwm--normal-hints-max-width max-width + exwm--normal-hints-max-height max-height)) + (unless (or exwm--normal-hints-min-width + (= 0 (logand flags xcb:icccm:WM_SIZE_HINTS:PBaseSize))) + (setq exwm--normal-hints-min-width base-width + exwm--normal-hints-min-height base-height)) + ;; (unless (= 0 (logand flags xcb:icccm:WM_SIZE_HINTS:PWinGravity)) + ;; (setq exwm--normal-hints-win-gravity win-gravity)) + (setq exwm--fixed-size + (and exwm--normal-hints-min-width + exwm--normal-hints-min-height + exwm--normal-hints-max-width + exwm--normal-hints-max-height + (/= 0 exwm--normal-hints-min-width) + (/= 0 exwm--normal-hints-min-height) + (= exwm--normal-hints-min-width + exwm--normal-hints-max-width) + (= exwm--normal-hints-min-height + exwm--normal-hints-max-height))))))))) + +(defun exwm--update-hints (id &optional force) + "Update WM_HINTS." + (exwm--with-current-id id + (unless (and (not force) exwm--hints-input exwm--hints-urgency) + (let ((reply (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:icccm:get-WM_HINTS :window id)))) + (when (and reply (slot-value reply 'flags)) ;nil when destroyed + (with-slots (flags input) reply + (when flags + (unless (= 0 (logand flags xcb:icccm:WM_HINTS:InputHint)) + (setq exwm--hints-input (when input (= 1 input)))) + (unless (= 0 (logand flags xcb:icccm:WM_HINTS:UrgencyHint)) + (setq exwm--hints-urgency t)))) + (when (and exwm--hints-urgency + (not (eq exwm--frame exwm-workspace--current))) + (unless (frame-parameter exwm--frame 'exwm--urgency) + (set-frame-parameter exwm--frame 'exwm--urgency t) + (exwm-workspace--update-switch-history)))))))) + +(defun exwm--update-protocols (id &optional force) + "Update WM_PROTOCOLS." + (exwm--with-current-id id + (unless (and exwm--protocols (not force)) + (let ((reply (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:icccm:get-WM_PROTOCOLS + :window id)))) + (when reply ;nil when destroyed + (setq exwm--protocols (append (slot-value reply 'value) nil))))))) + +(defun exwm--update-state (id &optional force) + "Update WM_STATE." + (exwm--with-current-id id + (unless (and exwm-state (not force)) + (let ((reply (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:icccm:get-WM_STATE :window id)))) + (when reply ;nil when destroyed + (setq exwm-state (or (slot-value reply 'state) + ;; Default to normal state + xcb:icccm:WM_STATE:NormalState))))))) + +(defun exwm--on-PropertyNotify (data synthetic) + "Handle PropertyNotify event." + (let ((obj (make-instance 'xcb:PropertyNotify)) + atom window state + buffer) + (xcb:unmarshal obj data) + (setq id (slot-value obj 'window) + atom (slot-value obj 'atom) + exwm-input--timestamp (slot-value obj 'time) + state (slot-value obj 'state)) + (setq buffer (exwm--id->buffer id)) + (when (buffer-live-p buffer) + (with-current-buffer buffer + (cond ((= atom xcb:Atom:_NET_WM_WINDOW_TYPE) + (exwm--update-window-type id t)) + ((= atom xcb:Atom:WM_CLASS) + (exwm--update-class id t)) + ((= atom xcb:Atom:_NET_WM_NAME) + (exwm--update-utf8-title id t)) + ((= atom xcb:Atom:WM_NAME) + (exwm--update-ctext-title id t)) + ((= atom xcb:Atom:WM_TRANSIENT_FOR) + (exwm--update-transient-for id t)) + ((= atom xcb:Atom:WM_NORMAL_HINTS) + (exwm--update-normal-hints id t)) + ((= atom xcb:Atom:WM_HINTS) + (exwm--update-hints id t)) + ((= atom xcb:Atom:WM_PROTOCOLS) + (exwm--update-protocols id t)) + ((= atom xcb:Atom:WM_STATE) + (exwm--update-state id t)) + (t (exwm--log "Unhandled PropertyNotify: %s(%d)" + (x-get-atom-name atom) atom))))))) + +(defun exwm--on-ClientMessage (raw-data synthetic) + "Handle ClientMessage event." + (let ((obj (make-instance 'xcb:ClientMessage)) + type id data) + (xcb:unmarshal obj raw-data) + (setq type (slot-value obj 'type) + id (slot-value obj 'window) + data (slot-value (slot-value obj 'data) 'data32)) + (cond + ;; _NET_WM_MOVERESIZE + ((= type xcb:Atom:_NET_WM_MOVERESIZE) + (let ((direction (elt data 2)) + (buffer (exwm--id->buffer id))) + (unless (and buffer (with-current-buffer buffer + (not exwm--floating-frame))) + (cond ((= direction + xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_KEYBOARD) + ;; FIXME + ) + ((= direction + xcb:ewmh:_NET_WM_MOVERESIZE_MOVE_KEYBOARD) + ;; FIXME + ) + ((= direction xcb:ewmh:_NET_WM_MOVERESIZE_CANCEL) + (exwm-floating--stop-moveresize)) + (t (exwm-floating--start-moveresize id direction)))))) + ;; _NET_REQUEST_FRAME_EXTENTS + ((= type xcb:Atom:_NET_REQUEST_FRAME_EXTENTS) + (let ((buffer (exwm--id->buffer id)) + left right top bottom) + (if (or (not buffer) + (with-current-buffer buffer + (not exwm--floating-frame))) + (setq left 0 right 0 top 0 bottom 0) + (setq left exwm-floating-border-width + right exwm-floating-border-width + top (+ exwm-floating-border-width (window-header-line-height)) + bottom (+ exwm-floating-border-width + (window-mode-line-height)))) + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_FRAME_EXTENTS + :window id :left left :right right + :top top :bottom bottom))) + (xcb:flush exwm--connection)) + ;; _NET_WM_STATE + ((= type xcb:Atom:_NET_WM_STATE) + (let ((action (elt data 0)) + (props (list (elt data 1) (elt data 2))) + (buffer (exwm--id->buffer id)) + props-new) + (when buffer ;ensure it's managed + (with-current-buffer buffer + ;; _NET_WM_STATE_MODAL + (when (memq xcb:Atom:_NET_WM_STATE_MODAL props) + (cond ((= action xcb:ewmh:_NET_WM_STATE_ADD) + (unless exwm--floating-frame + (exwm-floating--set-floating id)) + (push xcb:Atom:_NET_WM_STATE_MODAL props-new)) + ((= action xcb:ewmh:_NET_WM_STATE_REMOVE) + (when exwm--floating-frame + (exwm-floating--unset-floating id))) + ((= action xcb:ewmh:_NET_WM_STATE_TOGGLE) + (if exwm--floating-frame + (exwm-floating--unset-floating id) + (exwm-floating--set-floating id) + (push xcb:Atom:_NET_WM_STATE_MODAL props-new))))) + ;; _NET_WM_STATE_FULLSCREEN + (when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN props) + (cond ((= action xcb:ewmh:_NET_WM_STATE_ADD) + (unless exwm--fullscreen (exwm-layout-set-fullscreen id)) + (push xcb:Atom:_NET_WM_STATE_FULLSCREEN props-new)) + ((= action xcb:ewmh:_NET_WM_STATE_REMOVE) + (when exwm--fullscreen (exwm-layout-unset-fullscreen id))) + ((= action xcb:ewmh:_NET_WM_STATE_TOGGLE) + (if exwm--fullscreen + (exwm-layout-unset-fullscreen id) + (exwm-layout-set-fullscreen id) + (push xcb:Atom:_NET_WM_STATE_FULLSCREEN props-new))))) + ;; _NET_WM_STATE_DEMANDS_ATTENTION + ;; FIXME: check (may require other properties set) + (when (memq xcb:Atom:_NET_WM_STATE_DEMANDS_ATTENTION props) + (when (= action xcb:ewmh:_NET_WM_STATE_ADD) + (let ((idx (cl-position exwm--frame exwm-workspace--list))) + (unless (= idx exwm-workspace-current-index) + (set-frame-parameter exwm--frame 'exwm--urgency t) + (exwm-workspace--update-switch-history)))) + ;; xcb:ewmh:_NET_WM_STATE_REMOVE? + ;; xcb:ewmh:_NET_WM_STATE_TOGGLE? + ) + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_STATE + :window id :data (vconcat props-new))) + (xcb:flush exwm--connection))))) + ((= type xcb:Atom:WM_PROTOCOLS) + (let ((type (elt data 0))) + (cond ((= type xcb:Atom:_NET_WM_PING) + (setq exwm--ping-lock nil)) + (t (exwm--log "Unhandled WM_PROTOCOLS of type: %d" type))))) + (t (exwm--log "Unhandled client message: %s" obj))))) + +(defun exwm--init-icccm-ewmh () + "Initialize ICCCM/EWMH support." + ;; Handle PropertyNotify event + (xcb:+event exwm--connection 'xcb:PropertyNotify 'exwm--on-PropertyNotify) + ;; Handle relevant client messages + ;; FIXME: WM_STATE client messages (normal => iconic) + ;; WM_COLORMAP_NOTIFY + (xcb:+event exwm--connection 'xcb:ClientMessage 'exwm--on-ClientMessage) + ;; Set _NET_SUPPORTED + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_SUPPORTED + :window exwm--root + :data (vector xcb:Atom:_NET_SUPPORTED + xcb:Atom:_NET_NUMBER_OF_DESKTOPS + xcb:Atom:_NET_DESKTOP_VIEWPORT + xcb:Atom:_NET_CURRENT_DESKTOP + xcb:Atom:_NET_WORKAREA + xcb:Atom:_NET_SUPPORTING_WM_CHECK + xcb:Atom:_NET_VIRTUAL_ROOTS + xcb:Atom:_NET_WM_MOVERESIZE + xcb:Atom:_NET_REQUEST_FRAME_EXTENTS + xcb:Atom:_NET_FRAME_EXTENTS + xcb:Atom:_NET_WM_NAME + ;; + xcb:Atom:_NET_WM_WINDOW_TYPE + xcb:Atom:_NET_WM_WINDOW_TYPE_TOOLBAR + xcb:Atom:_NET_WM_WINDOW_TYPE_MENU + xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY + xcb:Atom:_NET_WM_WINDOW_TYPE_SPLASH + xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG + xcb:Atom:_NET_WM_WINDOW_TYPE_DROPDOWN_MENU + xcb:Atom:_NET_WM_WINDOW_TYPE_POPUP_MENU + xcb:Atom:_NET_WM_WINDOW_TYPE_TOOLTIP + xcb:Atom:_NET_WM_WINDOW_TYPE_NOTIFICATION + xcb:Atom:_NET_WM_WINDOW_TYPE_COMBO + xcb:Atom:_NET_WM_WINDOW_TYPE_DND + xcb:Atom:_NET_WM_WINDOW_TYPE_NORMAL + ;; + xcb:Atom:_NET_WM_STATE + xcb:Atom:_NET_WM_STATE_MODAL + xcb:Atom:_NET_WM_STATE_FULLSCREEN + xcb:Atom:_NET_WM_STATE_DEMANDS_ATTENTION + ;; FIXME: more? + ))) + ;; Create a child window for setting _NET_SUPPORTING_WM_CHECK + (let ((new-id (xcb:generate-id exwm--connection))) + (xcb:+request exwm--connection + (make-instance 'xcb:CreateWindow + :depth 0 :wid new-id :parent exwm--root + :x -1 :y -1 :width 1 :height 1 + :border-width 0 :class xcb:WindowClass:CopyFromParent + :visual 0 :value-mask xcb:CW:OverrideRedirect + :override-redirect 1)) + (dolist (i (list exwm--root new-id)) + ;; Set _NET_SUPPORTING_WM_CHECK + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_SUPPORTING_WM_CHECK + :window i :data new-id)) + ;; Set _NET_WM_NAME + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_NAME + :window i :data "EXWM")))) + ;; Set _NET_NUMBER_OF_DESKTOPS + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_NUMBER_OF_DESKTOPS + :window exwm--root :data exwm-workspace-number)) + ;; Set _NET_DESKTOP_VIEWPORT + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT + :window exwm--root + :data (make-vector (* 2 exwm-workspace-number) 0))) + ;; Set _NET_WORKAREA (with minibuffer and bottom mode-line excluded) + (let* ((workareas + (vconcat (window-absolute-pixel-edges (get-largest-window t)))) + (workareas (mapconcat (lambda (i) workareas) + (make-list exwm-workspace-number 0) []))) + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WORKAREA + :window exwm--root :data workareas))) + ;; Set _NET_VIRTUAL_ROOTS + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_VIRTUAL_ROOTS + :window exwm--root + :data (vconcat (mapcar + (lambda (i) + (frame-parameter i 'exwm-window-id)) + exwm-workspace--list)))) + (xcb:flush exwm--connection)) + +(defvar exwm-init-hook nil + "Normal hook run when EXWM has just finished initialization.") + +(defun exwm-init (&optional frame) + "Initialize EXWM." + (if (not (eq 'x (framep (or frame (selected-frame))))) + (exwm--log "Not running under X environment") + (unless exwm--connection + (setq exwm--connection (xcb:connect-to-socket)) + (set-process-query-on-exit-flag (slot-value exwm--connection 'process) + nil) ;prevent query message on exit + (setq exwm--root + (slot-value (car (slot-value + (xcb:get-setup exwm--connection) 'roots)) + 'root)) + (if (xcb:+request-checked+request-check exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window exwm--root :value-mask xcb:CW:EventMask + :event-mask xcb:EventMask:SubstructureRedirect)) + ;; Other window manager is running + (progn (xcb:disconnect exwm--connection) + (setq exwm--connection nil) + (exwm-enable 'undo) + (exwm--log "Other window manager detected")) + ;; Initialize ICCCM/EWMH support + ;; (xcb:icccm:init exwm--connection) + (xcb:ewmh:init exwm--connection) + (exwm--lock) + (exwm-workspace--init) + (exwm--init-icccm-ewmh) + (exwm-layout--init) + (exwm-floating--init) + (exwm-manage--init) + (exwm-input--init) + (exwm--unlock) + ;; Disable events during new frame creation + (add-hook 'before-make-frame-hook 'exwm--lock) + (add-hook 'after-make-frame-functions 'exwm--unlock) + ;; Manage exiting windows + (exwm-manage--scan) + (run-hooks 'exwm-init-hook))))) + +(defvar exwm-mode-map + (let ((map (make-sparse-keymap))) + (define-key map "\C-ck" 'exwm-input-release-keyboard) + (define-key map "\C-cf" 'exwm-layout-set-fullscreen) + (define-key map "\C-cm" 'exwm-floating-toggle-floating) + (define-key map "\C-cq" 'exwm-input-send-next-key) + (define-key map "\C-cv" 'exwm-workspace-move-window) + map) + "Keymap for `exwm-mode'.") + +(define-derived-mode exwm-mode nil "EXWM" + "Major mode for managing X windows. + +\\{exwm-mode-map}" + ;; Internal variables + (make-local-variable 'exwm--id) ;window id + (set (make-local-variable 'exwm--frame) nil) ;workspace frame + (set (make-local-variable 'exwm--floating-frame) nil) ;floating frame + (set (make-local-variable 'exwm--floating-edges) nil) ;four edges + (set (make-local-variable 'exwm--fullscreen) nil) ;used in fullscreen + (set (make-local-variable 'exwm--floating-frame-geometry) nil) ;in fullscreen + (set (make-local-variable 'exwm--fixed-size) nil) ;fixed size + ;; Properties + (set (make-local-variable 'exwm-window-type) nil) + (set (make-local-variable 'exwm--geometry) nil) + (set (make-local-variable 'exwm-class-name) nil) + (set (make-local-variable 'exwm-instance-name) nil) + (set (make-local-variable 'exwm-title) nil) + (set (make-local-variable 'exwm--title-is-utf8) nil) + (set (make-local-variable 'exwm-transient-for) nil) + ;; _NET_WM_NORMAL_HINTS + (set (make-local-variable 'exwm--normal-hints-x) nil) + (set (make-local-variable 'exwm--normal-hints-y) nil) + (set (make-local-variable 'exwm--normal-hints-width) nil) + (set (make-local-variable 'exwm--normal-hints-height) nil) + (set (make-local-variable 'exwm--normal-hints-min-width) nil) + (set (make-local-variable 'exwm--normal-hints-min-height) nil) + (set (make-local-variable 'exwm--normal-hints-max-width) nil) + (set (make-local-variable 'exwm--normal-hints-max-height) nil) + ;; (set (make-local-variable 'exwm--normal-hints-win-gravity) nil) + ;; WM_HINTS + (set (make-local-variable 'exwm--hints-input) nil) ;FIXME + (set (make-local-variable 'exwm--hints-urgency) nil) + ;; + (set (make-local-variable 'exwm--protocols) nil) + (set (make-local-variable 'exwm-state) nil) + ;; Change major-mode is not allowed + (set (make-local-variable 'change-major-mode-hook) 'kill-buffer) + ;; + (setq mode-name + '(:eval (propertize "EXWM" 'face + (when (cl-some (lambda (i) + (frame-parameter i + 'exwm--urgency)) + exwm-workspace--list) + 'font-lock-warning-face)))) + ;; Kill buffer -> close window + (set (make-local-variable 'kill-buffer-query-functions) + (list (lambda () + (exwm-manage--close-window exwm--id (current-buffer)) + nil))) + (setq buffer-read-only t + left-margin-width nil + right-margin-width nil + left-fringe-width 0 + right-fringe-width 0 + vertical-scroll-bar nil)) + +(defun exwm-enable (&optional undo) + "Enable/Disable EXWM" + (setq frame-resize-pixelwise t) ;mandatory; before init + (if (eq undo 'undo) + (progn (remove-hook 'window-setup-hook 'exwm-init) + (remove-hook 'after-make-frame-functions 'exwm-init)) + (add-hook 'window-setup-hook 'exwm-init t) ;for Emacs + (add-hook 'after-make-frame-functions 'exwm-init t))) ;for Emacs Client + + + +(provide 'exwm) + +;;; exwm.el ends here -- cgit 1.4.1 From 7892ed36f1cd1c77fa71a6581aef17d19ffa4fda Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 18 Jul 2015 10:23:24 +0800 Subject: Improve move/resize The type of move/resize is consistent during one complete operation. So there is no need to judge it on every mouse motion. --- exwm-floating.el | 183 +++++++++++++++++++++++++------------------------------ 1 file changed, 83 insertions(+), 100 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 9e57c5ca7d..f302bc076f 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -211,9 +211,8 @@ (exwm-floating--unset-floating exwm--id) (exwm-floating--set-floating exwm--id)))) -(defvar exwm-floating--moveresize-id nil) -(defvar exwm-floating--moveresize-type nil) -(defvar exwm-floating--moveresize-delta nil) +(defvar exwm-floating--moveresize-calculate nil + "Calculate move/resize parameters [frame-id event-mask x y width height].") (defun exwm-floating--start-moveresize (id &optional type) "Start move/resize." @@ -261,40 +260,85 @@ ((< x 1) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_LEFT)))) (when type (cond ((= type xcb:ewmh:_NET_WM_MOVERESIZE_MOVE) - (setq exwm-floating--moveresize-delta (list win-x win-y 0 0) - cursor exwm-floating--cursor-move)) + (setq cursor exwm-floating--cursor-move + exwm-floating--moveresize-calculate + `(lambda (x y) + (vector ,frame-id + ,(logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y) + (- x ,win-x) (- y ,win-y) 0 0)))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPLEFT) - (setq exwm-floating--moveresize-delta - (list win-x win-y (+ root-x width) (+ root-y height)) - cursor exwm-floating--cursor-top-left)) + (setq cursor exwm-floating--cursor-top-left + exwm-floating--moveresize-calculate + `(lambda (x y) + (vector ,frame-id + ,(logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height) + (- x ,win-x) (- y ,win-y) + (- ,(+ root-x width) x) + (- ,(+ root-y height) y))))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOP) - (setq exwm-floating--moveresize-delta - (list 0 win-y 0 (+ root-y height)) - cursor exwm-floating--cursor-top)) + (setq cursor exwm-floating--cursor-top + exwm-floating--moveresize-calculate + `(lambda (x y) + (vector ,frame-id + ,(logior xcb:ConfigWindow:Y + xcb:ConfigWindow:Height) + 0 (- y ,win-y) 0 (- ,(+ root-y height) y))))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPRIGHT) - (setq exwm-floating--moveresize-delta - (list 0 win-y (- root-x width) (+ root-y height)) - cursor exwm-floating--cursor-top-right)) + (setq cursor exwm-floating--cursor-top-right + exwm-floating--moveresize-calculate + `(lambda (x y) + (vector ,frame-id + ,(logior xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height) + 0 (- y ,win-y) (- x ,(- root-x width)) + (- ,(+ root-y height) y))))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_RIGHT) - (setq exwm-floating--moveresize-delta - (list 0 0 (- root-x width) 0) - cursor exwm-floating--cursor-right)) + (setq cursor exwm-floating--cursor-right + exwm-floating--moveresize-calculate + `(lambda (x y) + (vector ,frame-id ,xcb:ConfigWindow:Width + 0 0 (- x ,(- root-x width)) 0)))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT) - (setq exwm-floating--moveresize-delta - (list 0 0 (- root-x width) (- root-y height)) - cursor exwm-floating--cursor-bottom-right)) + (setq cursor exwm-floating--cursor-bottom-right + exwm-floating--moveresize-calculate + `(lambda (x y) + (vector ,frame-id + ,(logior xcb:ConfigWindow:Width + xcb:ConfigWindow:Height) + 0 0 (- x ,(- root-x width)) + (- y ,(- root-y height)))))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOM) - (setq exwm-floating--moveresize-delta - (list 0 0 0 (- root-y height)) - cursor exwm-floating--cursor-bottom)) + (setq cursor exwm-floating--cursor-bottom + exwm-floating--moveresize-calculate + `(lambda (x y) + (vector ,frame-id + ,xcb:ConfigWindow:Height + 0 0 0 (- y ,(- root-y height)))))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT) - (setq exwm-floating--moveresize-delta - (list win-x 0 (+ root-x width) (- root-y height)) - cursor exwm-floating--cursor-bottom-left)) + (setq cursor exwm-floating--cursor-bottom-left + exwm-floating--moveresize-calculate + `(lambda (x y) + (vector ,frame-id + ,(logior xcb:ConfigWindow:X + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height) + (- x ,win-x) + 0 + (- ,(+ root-x width) x) + (- y ,(- root-y height)))))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_LEFT) - (setq exwm-floating--moveresize-delta - (list win-x 0 (+ root-x width) 0) - cursor exwm-floating--cursor-left))) + (setq cursor exwm-floating--cursor-left + exwm-floating--moveresize-calculate + `(lambda (x y) + (vector ,frame-id + ,(logior xcb:ConfigWindow:X + xcb:ConfigWindow:Width) + (- x ,win-x) 0 (- ,(+ root-x width) x) 0))))) ;; Select events and change cursor (should always succeed) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GrabPointer @@ -305,89 +349,28 @@ :keyboard-mode xcb:GrabMode:Async :confine-to xcb:Window:None :cursor cursor - :time xcb:Time:CurrentTime)) - (setq exwm-floating--moveresize-id frame-id - exwm-floating--moveresize-type type)))))) + :time xcb:Time:CurrentTime))))))) (defun exwm-floating--stop-moveresize (&rest args) "Stop move/resize." (xcb:+request exwm--connection (make-instance 'xcb:UngrabPointer :time xcb:Time:CurrentTime)) (xcb:flush exwm--connection) - (setq exwm-floating--moveresize-id nil - exwm-floating--moveresize-type nil - exwm-floating--moveresize-delta nil)) + (setq exwm-floating--moveresize-calculate nil)) (defun exwm-floating--do-moveresize (data synthetic) "Perform move/resize." - (let ((mask 0) (x 0) (y 0) (width 0) (height 0) - (delta exwm-floating--moveresize-delta) - obj root-x root-y) - (when (and exwm-floating--moveresize-id exwm-floating--moveresize-type) - (setq obj (make-instance 'xcb:MotionNotify)) + (when exwm-floating--moveresize-calculate + (let ((obj (make-instance 'xcb:MotionNotify)) + result) (xcb:unmarshal obj data) - (setq root-x (slot-value obj 'root-x) - root-y (slot-value obj 'root-y)) - ;; Perform move/resize according to the previously set type - (cond ((= exwm-floating--moveresize-type - xcb:ewmh:_NET_WM_MOVERESIZE_MOVE) - (setq mask (logior xcb:ConfigWindow:X xcb:ConfigWindow:Y) - x (- root-x (elt delta 0)) - y (- root-y (elt delta 1)))) - ((= exwm-floating--moveresize-type - xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPLEFT) - (setq mask - (logior xcb:ConfigWindow:X xcb:ConfigWindow:Y - xcb:ConfigWindow:Width xcb:ConfigWindow:Height) - x (- root-x (elt delta 0)) - y (- root-y (elt delta 1)) - width (- (elt delta 2) root-x) - height (- (elt delta 3) root-y))) - ((= exwm-floating--moveresize-type - xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOP) - (setq mask (logior xcb:ConfigWindow:Y xcb:ConfigWindow:Height) - y (- root-y (elt delta 1)) - height (- (elt delta 3) root-y))) - ((= exwm-floating--moveresize-type - xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPRIGHT) - (setq mask - (logior xcb:ConfigWindow:Y - xcb:ConfigWindow:Width xcb:ConfigWindow:Height) - y (- root-y (elt delta 1)) - width (- root-x (elt delta 2)) - height (- (elt delta 3) root-y))) - ((= exwm-floating--moveresize-type - xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_RIGHT) - (setq mask (logior xcb:ConfigWindow:Width) - width (- root-x (elt delta 2)))) - ((= exwm-floating--moveresize-type - xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT) - (setq mask - (logior xcb:ConfigWindow:Width xcb:ConfigWindow:Height) - width (- root-x (elt delta 2)) - height (- root-y (elt delta 3)))) - ((= exwm-floating--moveresize-type - xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOM) - (setq mask (logior xcb:ConfigWindow:Height) - height (- root-y (elt delta 3)))) - ((= exwm-floating--moveresize-type - xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT) - (setq mask - (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Width xcb:ConfigWindow:Height) - x (- root-x (elt delta 0)) - width (- (elt delta 2) root-x) - height (- root-y (elt delta 3)))) - ((= exwm-floating--moveresize-type - xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_LEFT) - (setq mask - (logior xcb:ConfigWindow:X xcb:ConfigWindow:Width) - x (- root-x (elt delta 0)) - width (- (elt delta 2) root-x)))) + (setq result (funcall exwm-floating--moveresize-calculate + (slot-value obj 'root-x) (slot-value obj 'root-y))) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow - :window exwm-floating--moveresize-id :value-mask mask - :x x :y y :width width :height height)) + :window (elt result 0) :value-mask (elt result 1) + :x (elt result 2) :y (elt result 3) + :width (elt result 4) :height (elt result 5))) (xcb:flush exwm--connection)))) ;; Cursors for moving/resizing a window -- cgit 1.4.1 From cf87bb55ed2b7070aceaea70783a578563734b7d Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 18 Jul 2015 18:15:40 +0800 Subject: Add wiki link to README --- README | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README b/README index a0340f3e88..a2a2fdc6d3 100644 --- a/README +++ b/README @@ -2,7 +2,8 @@ Emacs X Window Manager ====================== EXWM (Emacs X Window Manager) turns Emacs into a full-featured tiling X window -manager. Please refer to "exwm.el" for more details. +manager. Please check the wiki for more details. + + Wiki: https://github.com/ch11ng/exwm/wiki EXWM is built on top of XELB and its utility libraris: + XELB: https://github.com/ch11ng/xelb -- cgit 1.4.1 From 968d0b48825e9d4b7f0c918f33337f34c1db7487 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 18 Jul 2015 21:16:14 +0800 Subject: Various fixes for workspace Fixes for full screen, move window, etc. --- exwm-input.el | 2 -- exwm-workspace.el | 11 +++++++---- exwm.el | 4 ++++ 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 107df0599e..1f8e4f4de6 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -30,8 +30,6 @@ ;; Todo: ;; + Pointer simulation mode (e.g. 'C-c 1'/'C-c 2' for single/double click, ;; move with arrow keys). -;; + Demonstrate how to add a local key binding (add global prefix key and -;; modify `exwm-mode-map'). ;; + Simulation keys to mimic Emacs key bindings for text edit (redo, select, ;; cancel, clear, etc). Some of them are not present on common keyboard ;; (keycode = 0). May need to use XKB extension. diff --git a/exwm-workspace.el b/exwm-workspace.el index c225c434dd..94d0e75eaf 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -68,8 +68,9 @@ (not-empty (make-vector exwm-workspace-number nil))) (dolist (i exwm--id-buffer-alist) (with-current-buffer (cdr i) - (setf (elt not-empty (cl-position exwm--frame exwm-workspace--list)) - t))) + (when exwm--frame + (setf (elt not-empty (cl-position exwm--frame exwm-workspace--list)) + t)))) (setq exwm-workspace--switch-history (mapcar (lambda (i) @@ -147,8 +148,6 @@ The optional FORCE option is for internal use only " (unless (and (<= 0 index) (< index exwm-workspace-number)) (user-error "[EXWM] Workspace index out of range: %d" index)) (when (/= exwm-workspace-current-index index) - (set-window-buffer (get-buffer-window (exwm--id->buffer id)) - (other-buffer)) (let ((frame (elt exwm-workspace--list index))) (with-current-buffer (exwm--id->buffer id) (setq exwm--frame frame) @@ -163,6 +162,8 @@ The optional FORCE option is for internal use only " :parent (frame-parameter frame 'exwm-window-id) :x 0 :y 0)) ;; Move the window itself + (set-window-buffer (get-buffer-window (exwm--id->buffer id)) + (other-buffer)) (xcb:+request exwm--connection (make-instance 'xcb:ChangeWindowAttributes :window id :value-mask xcb:CW:EventMask @@ -197,6 +198,7 @@ The optional FORCE option is for internal use only " (unless (frame-parameter i 'window-id) (setq exwm-workspace--list (delq i exwm-workspace--list))))) (cl-assert (= 1 (length exwm-workspace--list))) + (exwm--make-emacs-idle-for 0.1) ;wait for the frame ready ;; Configure the existing frame (set-frame-parameter (car exwm-workspace--list) 'fullscreen 'fullboth) ;; Create remaining frames @@ -220,6 +222,7 @@ The optional FORCE option is for internal use only " (make-instance 'xcb:ChangeWindowAttributes :window window-id :value-mask xcb:CW:EventMask :event-mask xcb:EventMask:SubstructureRedirect)))) + (xcb:flush exwm--connection) ;; Switch to the first workspace (exwm-workspace-switch 0 t)) diff --git a/exwm.el b/exwm.el index 49c4380405..c6e005976f 100644 --- a/exwm.el +++ b/exwm.el @@ -188,6 +188,10 @@ xcb:EventMask:SubstructureRedirect))) (xcb:flush exwm--connection)) +(defun exwm--make-emacs-idle-for (seconds) + "Put Emacs in idle state for SECONDS seconds." + (with-timeout (seconds) (read-event))) + (defun exwm-reset () "Reset window to standard state: non-fullscreen, line-mode." (interactive) -- cgit 1.4.1 From 36e8361b9bd581e89d593b32002404bbf4811b62 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 19 Jul 2015 09:15:53 +0800 Subject: Various fixes Remove wrong shift modifiers. Hide a Window when it's moved to another workspace. --- exwm-input.el | 2 +- exwm-workspace.el | 14 +++----------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 1f8e4f4de6..e185b1af14 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -380,7 +380,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") ;; (exwm-input--fake-key last-input-event))) (defvar exwm-input-prefix-keys - '(?\C-x ?\C-u ?\C-h ?\M-x ?\M-` ?\M-\S-! ?\M-\S-& ?\M-\S-:) + '(?\C-x ?\C-u ?\C-h ?\M-x ?\M-` ?\M-! ?\M-& ?\M-:) "List of prefix keys EXWM should forward to Emacs when in line-mode.") (defvar exwm-input--simulation-keys nil "Simulation keys in line-mode.") diff --git a/exwm-workspace.el b/exwm-workspace.el index 94d0e75eaf..bab2612f4f 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -162,21 +162,13 @@ The optional FORCE option is for internal use only " :parent (frame-parameter frame 'exwm-window-id) :x 0 :y 0)) ;; Move the window itself - (set-window-buffer (get-buffer-window (exwm--id->buffer id)) - (other-buffer)) - (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window id :value-mask xcb:CW:EventMask - :event-mask xcb:EventMask:NoEvent)) + (bury-buffer) + (exwm-layout--hide id) (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow :window id :parent (frame-parameter frame 'exwm-window-id) - :x 0 :y 0)) - (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window id :value-mask xcb:CW:EventMask - :event-mask exwm--client-event-mask))))) + :x 0 :y 0))))) (xcb:flush exwm--connection) (exwm-workspace--update-switch-history))) -- cgit 1.4.1 From f4416a10e3f87ceddc70ae02f065f14c5e6dc3fd Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 26 Jul 2015 16:04:21 +0800 Subject: Fix potential naming conflicts Buffers may share a same name (without the possible leading space) when created in different workspaces. --- exwm-workspace.el | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index bab2612f4f..b46b2e8d99 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -119,9 +119,9 @@ The optional FORCE option is for internal use only " (dolist (i exwm--id-buffer-alist) (with-current-buffer (cdr i) (let ((name (replace-regexp-in-string "^\\s-*" "" (buffer-name)))) - (rename-buffer (if (eq (selected-frame) exwm--frame) - name - (concat " " name)))))) + (exwm-workspace-rename-buffer (if (eq (selected-frame) exwm--frame) + name + (concat " " name)))))) ;; Update demands attention flag (set-frame-parameter (selected-frame) 'exwm--urgency nil) ;; Update switch workspace history @@ -151,7 +151,7 @@ The optional FORCE option is for internal use only " (let ((frame (elt exwm-workspace--list index))) (with-current-buffer (exwm--id->buffer id) (setq exwm--frame frame) - (rename-buffer + (exwm-workspace-rename-buffer (concat " " (replace-regexp-in-string "^\\s-*" "" (buffer-name)))) (if exwm--floating-frame ;; Move the floating frame is enough @@ -172,6 +172,19 @@ The optional FORCE option is for internal use only " (xcb:flush exwm--connection) (exwm-workspace--update-switch-history))) +(defun exwm-workspace-rename-buffer (newname) + "Rename a buffer." + (if (/= ? (aref newname 0)) + (rename-buffer newname t) + ;; If a buffer name is prefixed with a space, Emacs append a random + ;; number before renaming it. This is not desired behavior. + (let ((name (replace-regexp-in-string "<[0-9]+>$" "" newname)) + (counter 1)) + (while (and (get-buffer newname) + (not (eq (get-buffer newname) (current-buffer)))) + (setq newname (format "%s<%d>" name (cl-incf counter))))) + (rename-buffer newname))) + (defun exwm-workspace--init () "Initialize workspace module." (cl-assert (and (< 0 exwm-workspace-number) (>= 10 exwm-workspace-number))) -- cgit 1.4.1 From 86764d27a333be53336cf2e38d237c7bfa218e7b Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 3 Aug 2015 20:26:53 +0800 Subject: Add basic RandR support This implementation is analogous to that in i3-wm, which requires external tools to properly configure RandR first. --- exwm-randr.el | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ exwm-workspace.el | 78 +++++++++++++++++++++----------- exwm.el | 3 +- 3 files changed, 184 insertions(+), 28 deletions(-) create mode 100644 exwm-randr.el diff --git a/exwm-randr.el b/exwm-randr.el new file mode 100644 index 0000000000..c9ad1ce6ae --- /dev/null +++ b/exwm-randr.el @@ -0,0 +1,131 @@ +;;; exwm-randr.el --- RandR Module for EXWM -*- lexical-binding: t -*- + +;; Copyright (C) 2015 Chris Feng + +;; Author: Chris Feng +;; Keywords: unix + +;; This file is not part of GNU Emacs. + +;; This file is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This file is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this file. If not, see . + +;;; Commentary: + +;; This module adds RandR support for EXWM. Currently it requires external +;; tools such as xrandr(1) to properly configure RandR first. This dependency +;; may be removed in the future, but more work is needed before that. + +;; To use this module, first set `exwm-randr-workspace-output-plist': +;; (setq exwm-randr-workspace-output-plist '(0 "VGA1")) +;; Then configure RandR with 'xrandr': +;; $ xrandr --output VGA1 --left-of LVDS1 --auto +;; With above lines, workspace 0 should be assigned to the output named "VGA1", +;; staying at the left of other workspaces on the output "LVDS1". + +;; Todo: +;; + Update EWMH hints. + +;; References: +;; + RandR (http://www.x.org/archive/X11R7.7/doc/randrproto/randrproto.txt) + +;;; Code: + +(require 'xcb-randr) + +(defvar exwm-randr-workspace-output-plist nil) + +(defun exwm-randr--refresh () + "Refresh workspaces according to the updated RandR info." + (let (output-plist default-geometry) + ;; Query all outputs + (with-slots (config-timestamp outputs) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:randr:GetScreenResources + :window exwm--root)) + (dolist (output outputs) + (with-slots (crtc connection name) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:randr:GetOutputInfo + :output output + :config-timestamp config-timestamp)) + (setq name ;UTF-8 encoded + (decode-coding-string (apply 'unibyte-string name) 'utf-8)) + (if (or (/= connection xcb:randr:Connection:Connected) + (= 0 crtc)) ;FIXME + (plist-put output-plist name nil) + (with-slots (x y width height) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:randr:GetCrtcInfo + :crtc crtc + :config-timestamp config-timestamp)) + (setq output-plist (plist-put output-plist name + (vector x y width height))) + (unless default-geometry ;assume the first output as primary + (setq default-geometry (vector x y width height)))))))) + (cl-assert (<= 2 (length output-plist))) + (dotimes (i exwm-workspace-number) + (let* ((output (plist-get exwm-randr-workspace-output-plist i)) + (geometry (lax-plist-get output-plist output)) + (frame (elt exwm-workspace--list i))) + (unless geometry + (setq geometry default-geometry + output nil)) + (set-frame-parameter frame 'exwm-randr-output output) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter frame 'exwm-outer-id) + :value-mask (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height) + :x (elt geometry 0) :y (elt geometry 1) + :width (elt geometry 2) :height (elt geometry 3))))) + (xcb:flush exwm--connection))) + +(defun exwm-randr--init () + "Initialize RandR extension and EXWM RandR module." + (if (= 0 (slot-value (xcb:get-extension-data exwm--connection 'xcb:randr) + 'present)) + (error "[EXWM] RandR extension is not supported by the server") + (with-slots (major-version minor-version) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:randr:QueryVersion + :major-version 1 :minor-version 2)) + (if (or (/= major-version 1) (< minor-version 2)) + (error "[EXWM] The server only support RandR version up to %d.%d" + major-version minor-version) + (exwm-randr--refresh) + (xcb:+event exwm--connection 'xcb:randr:ScreenChangeNotify + (lambda (data synthetic) + (exwm-randr--refresh))) + ;; (xcb:+event exwm--connection 'xcb:randr:Notify + ;; (lambda (data synthetic) + ;; (exwm-randr--refresh))) + (xcb:+request exwm--connection + (make-instance 'xcb:randr:SelectInput + :window exwm--root + :enable xcb:randr:NotifyMask:ScreenChange + ;; :enable (logior + ;; xcb:randr:NotifyMask:ScreenChange + ;; xcb:randr:NotifyMask:OutputChange + ;; xcb:randr:NotifyMask:OutputProperty + ;; xcb:randr:NotifyMask:CrtcChange) + )) + (xcb:flush exwm--connection))))) + + + +(provide 'exwm-randr) + +;;; exwm-randr.el ends here diff --git a/exwm-workspace.el b/exwm-workspace.el index b46b2e8d99..160055f81d 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -90,11 +90,12 @@ (defvar exwm-workspace--current nil "Current active workspace.") (defvar exwm-workspace-current-index 0 "Index of current active workspace.") +(defvar exwm-workspace--switch-lock nil "Non-nil to prevent workspace switch.") (defun exwm-workspace-switch (index &optional force) "Switch to workspace INDEX. Query for INDEX if it's not specified. -The optional FORCE option is for internal use only " +The optional FORCE option is for internal use only." (interactive (list (let* ((history-add-new-input nil) ;prevent modifying history @@ -105,32 +106,53 @@ The optional FORCE option is for internal use only " `(exwm-workspace--switch-history . ,(1+ exwm-workspace-current-index))))) (cl-position idx exwm-workspace--switch-history :test 'equal)))) - (unless (and (<= 0 index) (< index exwm-workspace-number)) - (user-error "[EXWM] Workspace index out of range: %d" index)) - (when (or force (/= exwm-workspace-current-index index)) - (select-frame-set-input-focus (elt exwm-workspace--list index)) - ;; Hide all workspaces but the selected one - (dotimes (i exwm-workspace-number) - (unless (= i index) (make-frame-invisible (elt exwm-workspace--list i)))) - (setq exwm-workspace--current (elt exwm-workspace--list index) - exwm-workspace-current-index index) - (setq default-minibuffer-frame (selected-frame)) - ;; Hide windows in other workspaces by preprending a space - (dolist (i exwm--id-buffer-alist) - (with-current-buffer (cdr i) - (let ((name (replace-regexp-in-string "^\\s-*" "" (buffer-name)))) - (exwm-workspace-rename-buffer (if (eq (selected-frame) exwm--frame) - name - (concat " " name)))))) - ;; Update demands attention flag - (set-frame-parameter (selected-frame) 'exwm--urgency nil) - ;; Update switch workspace history - (exwm-workspace--update-switch-history) - ;; Update _NET_CURRENT_DESKTOP - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_CURRENT_DESKTOP - :window exwm--root :data index)) - (xcb:flush exwm--connection))) + (unless exwm-workspace--switch-lock + (setq exwm-workspace--switch-lock t) + (unless (and (<= 0 index) (< index exwm-workspace-number)) + (user-error "[EXWM] Workspace index out of range: %d" index)) + (when (or force (/= exwm-workspace-current-index index)) + (let ((frame (elt exwm-workspace--list index))) + (setq exwm-workspace--current frame + exwm-workspace-current-index index) + (select-frame-set-input-focus frame) + (exwm--make-emacs-idle-for 0.1) ;FIXME + ;; Move mouse when necessary + (let ((position (mouse-pixel-position)) + x y w h) + (unless (eq frame (car position)) + (setq x (cadr position) + y (cddr position) + w (frame-pixel-width frame) + h (frame-pixel-height frame)) + (when (or (> x w) (> y h)) + (setq x (/ w 2) + y (/ h 2))) + (set-mouse-pixel-position frame x y))) + (setq default-minibuffer-frame frame) + ;; Hide windows in other workspaces by preprending a space + (dolist (i exwm--id-buffer-alist) + (with-current-buffer (cdr i) + (let ((name (replace-regexp-in-string "^\\s-*" "" (buffer-name)))) + (exwm-workspace-rename-buffer (if (eq frame exwm--frame) + name + (concat " " name)))))) + ;; Update demands attention flag + (set-frame-parameter frame 'exwm--urgency nil) + ;; Update switch workspace history + (exwm-workspace--update-switch-history) + ;; Update _NET_CURRENT_DESKTOP + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_CURRENT_DESKTOP + :window exwm--root :data index)) + (xcb:flush exwm--connection))) + (setq exwm-workspace--switch-lock nil))) + +(defun exwm-workspace--on-focus-in () + "Fix unexpected frame switch." + (unless exwm-workspace--switch-lock + (let ((index (cl-position (selected-frame) exwm-workspace--list))) + (when (and index (/= index exwm-workspace-current-index)) + (exwm-workspace-switch index))))) (defun exwm-workspace-move-window (index &optional id) "Move window ID to workspace INDEX." @@ -228,6 +250,8 @@ The optional FORCE option is for internal use only " :window window-id :value-mask xcb:CW:EventMask :event-mask xcb:EventMask:SubstructureRedirect)))) (xcb:flush exwm--connection) + ;; Handle unexpected frame switch + (add-hook 'focus-in-hook 'exwm-workspace--on-focus-in) ;; Switch to the first workspace (exwm-workspace-switch 0 t)) diff --git a/exwm.el b/exwm.el index c6e005976f..a38744fa1d 100644 --- a/exwm.el +++ b/exwm.el @@ -125,7 +125,6 @@ ;; XEmacs, though it seems nobody have ever got it working on GNU Emacs. ;; Todo: -;; + Add RandR support. ;; + Investigate DnD support (e.g. drag a chromium tab to another window). ;; + Auto hide minibuffer, or allow users to place it elsewhere. ;; + Add system tray support. @@ -145,6 +144,7 @@ (require 'exwm-floating) (require 'exwm-manage) (require 'exwm-input) +(require 'exwm-randr) (defvar exwm-debug-on nil "Non-nil to turn on debug for EXWM.") @@ -618,6 +618,7 @@ (exwm-floating--init) (exwm-manage--init) (exwm-input--init) + (exwm-randr--init) (exwm--unlock) ;; Disable events during new frame creation (add-hook 'before-make-frame-hook 'exwm--lock) -- cgit 1.4.1 From ea7c6c7035ee412fa126a430e38f2694c351a2f6 Mon Sep 17 00:00:00 2001 From: "Markus S." Date: Wed, 5 Aug 2015 02:57:50 +0200 Subject: Fix typos --- exwm.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/exwm.el b/exwm.el index a38744fa1d..3f38b66bdc 100644 --- a/exwm.el +++ b/exwm.el @@ -40,9 +40,9 @@ ;; How it works ;; ------------ -;; Emacs itself is an tiling window manager, though unfortunately not for -;; managing X things. EXWM is therefore created to overcome this limitation by -;; relating X concepts to Emacs ones as shown in the following table. +;; Emacs itself is a tiling window manager, though unfortunately not for +;; managing X things. EXWM has therefore been created to overcome this limitation +;; by relating X concepts to Emacs ones as shown in the following table. ;; ;; +=============+=========+ ;; | X Window | Emacs | -- cgit 1.4.1 From 3fc0cb2bf7e97a525862e95052aaffca70b2089e Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 5 Aug 2015 14:10:44 +0800 Subject: Various fixes * Prevent marking the end of a key sequence with a single `C-u'. * Enable `C-u' prefix for key simulation since it's not possible for users to define simulation keys starting with `C-u'. * Make Emacs idle only after the visual parts are updated to prevent from disturbing users. * Should use '?\s' instead of '? '. --- exwm-input.el | 21 ++++++++++++--------- exwm-workspace.el | 4 ++-- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index e185b1af14..6d72cc26a0 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -108,10 +108,12 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (defun exwm-input--finish-key-sequence () "Mark the end of a key sequence (with the aid of `pre-command-hook')." - (setq exwm-input--during-key-sequence nil) - (when exwm-input--temp-line-mode - (setq exwm-input--temp-line-mode nil) - (exwm-input--release-keyboard))) + (when (and exwm-input--during-key-sequence + (not (equal [?\C-u] (this-single-command-keys)))) + (setq exwm-input--during-key-sequence nil) + (when exwm-input--temp-line-mode + (setq exwm-input--temp-line-mode nil) + (exwm-input--release-keyboard)))) (defun exwm-input--on-MappingNotify (data synthetic) "Handle MappingNotify event." @@ -403,18 +405,19 @@ SIMULATION-KEYS is a list of alist (key-sequence1 . key-sequence2)." (cl-pushnew `(,(vconcat (car i)) . ,(cdr i)) exwm-input--simulation-keys)) (exwm-input--update-simulation-prefix-keys)) -(defun exwm-input-send-simulation-key () +(defun exwm-input-send-simulation-key (times) "Fake a key event according to last input key sequence." - (interactive) - (let ((pair (assoc (this-command-keys-vector) ;FIXME: pefix + (interactive "p") + (let ((pair (assoc (this-single-command-keys) exwm-input--simulation-keys)) key) (when pair (setq pair (cdr pair)) (unless (listp pair) (setq pair (list pair))) - (dolist (i pair) - (exwm-input--fake-key i))))) + (dotimes (i times) + (dolist (j pair) + (exwm-input--fake-key j)))))) (defun exwm-input--init () "Initialize the keyboard module." diff --git a/exwm-workspace.el b/exwm-workspace.el index 160055f81d..3add2121ff 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -115,7 +115,6 @@ The optional FORCE option is for internal use only." (setq exwm-workspace--current frame exwm-workspace-current-index index) (select-frame-set-input-focus frame) - (exwm--make-emacs-idle-for 0.1) ;FIXME ;; Move mouse when necessary (let ((position (mouse-pixel-position)) x y w h) @@ -140,6 +139,7 @@ The optional FORCE option is for internal use only." (set-frame-parameter frame 'exwm--urgency nil) ;; Update switch workspace history (exwm-workspace--update-switch-history) + (exwm--make-emacs-idle-for 0.1) ;FIXME ;; Update _NET_CURRENT_DESKTOP (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_CURRENT_DESKTOP @@ -196,7 +196,7 @@ The optional FORCE option is for internal use only." (defun exwm-workspace-rename-buffer (newname) "Rename a buffer." - (if (/= ? (aref newname 0)) + (if (/= ?\s (aref newname 0)) (rename-buffer newname t) ;; If a buffer name is prefixed with a space, Emacs append a random ;; number before renaming it. This is not desired behavior. -- cgit 1.4.1 From caf2feec639e26506b787a07889aaf28ee9c3c3f Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 5 Aug 2015 20:53:17 +0800 Subject: Drop intro to EXIM --- README | 3 --- 1 file changed, 3 deletions(-) diff --git a/README b/README index a2a2fdc6d3..a8b9fa7117 100644 --- a/README +++ b/README @@ -8,6 +8,3 @@ manager. Please check the wiki for more details. EXWM is built on top of XELB and its utility libraris: + XELB: https://github.com/ch11ng/xelb + XELB utilility libraries: https://github.com/ch11ng/xelb-util - -You may also be intrested in an input method server designed for EXWM: - + EXIM: https://github.com/ch11ng/exim -- cgit 1.4.1 From 3fb90b9eaac752760fc32404cbd1574b8ef61669 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 6 Aug 2015 12:32:14 +0800 Subject: Fix fullscreen issues * Correct ConfigureNotify events sent to fullscreen windows. * Exit fullscreen mode before switching workspace. * Temporarily treat `xcb:Atom:_NET_WM_STATE_ABOVE` as `xcb:Atom:_NET_WM_STATE_FULLSCREEN` since a) "plugin-container" (Flash Player) seems only set this, and b) it's not normally used by applications. This makes fullscreen videos working in e.g. iceweasel. --- exwm-manage.el | 14 +++++++++----- exwm-workspace.el | 5 +++++ exwm.el | 3 ++- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index afab18e924..0d2c74f4f1 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -133,7 +133,7 @@ corresponding buffer.") (exwm-input-grab-keyboard id) (exwm-workspace--update-switch-history) (setq exwm-input--focus-lock nil) ;unlocked in advance - (with-current-buffer (exwm--id->buffer id) + (exwm--with-current-id id (run-hooks 'exwm-manage-finish-hook)))) (setq exwm-input--focus-lock nil)) @@ -266,10 +266,14 @@ corresponding buffer.") (with-slots (window x y width height border-width) obj (if (setq buffer (exwm--id->buffer window)) ;; Send client message for managed windows - (progn - (setq edges (or (with-current-buffer buffer exwm--floating-edges) - (window-inside-absolute-pixel-edges - (get-buffer-window buffer)))) + (with-current-buffer buffer + (setq edges + (if exwm--fullscreen + (list 0 0 + (x-display-pixel-width) (x-display-pixel-height)) + (or exwm--floating-edges + (window-inside-absolute-pixel-edges + (get-buffer-window))))) (xcb:+request exwm--connection (make-instance 'xcb:SendEvent :propagate 0 :destination window diff --git a/exwm-workspace.el b/exwm-workspace.el index 3add2121ff..ed06f64ad2 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -111,6 +111,11 @@ The optional FORCE option is for internal use only." (unless (and (<= 0 index) (< index exwm-workspace-number)) (user-error "[EXWM] Workspace index out of range: %d" index)) (when (or force (/= exwm-workspace-current-index index)) + ;; Exit fullscreen mode + (with-current-buffer (window-buffer) + (when (and (eq major-mode 'exwm-mode) exwm--fullscreen) + (exwm-layout-unset-fullscreen) + (exwm-input-grab-keyboard))) (let ((frame (elt exwm-workspace--list index))) (setq exwm-workspace--current frame exwm-workspace-current-index index) diff --git a/exwm.el b/exwm.el index 3f38b66bdc..c8f10aff24 100644 --- a/exwm.el +++ b/exwm.el @@ -462,7 +462,8 @@ (exwm-floating--set-floating id) (push xcb:Atom:_NET_WM_STATE_MODAL props-new))))) ;; _NET_WM_STATE_FULLSCREEN - (when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN props) + (when (or (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN props) + (memq xcb:Atom:_NET_WM_STATE_ABOVE props)) (cond ((= action xcb:ewmh:_NET_WM_STATE_ADD) (unless exwm--fullscreen (exwm-layout-set-fullscreen id)) (push xcb:Atom:_NET_WM_STATE_FULLSCREEN props-new)) -- cgit 1.4.1 From 1e36a22b3f5fd3867f29eaf3e1891422a3bb952c Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 6 Aug 2015 14:41:28 +0800 Subject: Check buffer mode in exwm-reset This should prevent users from misoperation. --- exwm-workspace.el | 6 +----- exwm.el | 7 ++++--- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index ed06f64ad2..d700f41a14 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -111,11 +111,7 @@ The optional FORCE option is for internal use only." (unless (and (<= 0 index) (< index exwm-workspace-number)) (user-error "[EXWM] Workspace index out of range: %d" index)) (when (or force (/= exwm-workspace-current-index index)) - ;; Exit fullscreen mode - (with-current-buffer (window-buffer) - (when (and (eq major-mode 'exwm-mode) exwm--fullscreen) - (exwm-layout-unset-fullscreen) - (exwm-input-grab-keyboard))) + (exwm-reset) ;exit full screen (let ((frame (elt exwm-workspace--list index))) (setq exwm-workspace--current frame exwm-workspace-current-index index) diff --git a/exwm.el b/exwm.el index c8f10aff24..18d8ddad8f 100644 --- a/exwm.el +++ b/exwm.el @@ -195,9 +195,10 @@ (defun exwm-reset () "Reset window to standard state: non-fullscreen, line-mode." (interactive) - (with-current-buffer (window-buffer (selected-window)) - (when exwm--fullscreen (exwm-layout-unset-fullscreen)) - (exwm-input-grab-keyboard))) + (with-current-buffer (window-buffer) + (when (and (eq major-mode 'exwm-mode) exwm--fullscreen) + (exwm-layout-unset-fullscreen) + (exwm-input-grab-keyboard)))) (defmacro exwm--with-current-id (id &rest body) "Evaluate BODY in the context of the buffer corresponding to window ID." -- cgit 1.4.1 From 2ad1a89db0c9e3704c79294620c5ed4925b143eb Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 7 Aug 2015 12:41:15 +0800 Subject: Various input fixes * Fix `exwm-reset` * Make input mode buffer local * Allow window to stay in `char-mode` while setting input focus to other window or switching to other workspace --- exwm-input.el | 30 +++++++++++++++--------------- exwm-workspace.el | 20 ++++++++++---------- exwm.el | 6 ++++-- 3 files changed, 29 insertions(+), 27 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 6d72cc26a0..f94cf50f24 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -161,7 +161,9 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (let ((obj (make-instance 'xcb:KeyPress))) (xcb:unmarshal obj data) (setq exwm-input--timestamp (slot-value obj 'time)) - (funcall 'exwm-input--handle-KeyPress obj))) + (if (eq major-mode 'exwm-mode) + (funcall exwm--on-KeyPress obj) + (exwm-input--on-KeyPress-char-mode obj)))) (defvar exwm-input--global-keys nil "Global key bindings.") (defvar exwm-input--global-prefix-keys nil @@ -211,7 +213,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") ;; ;; in this case. ;; ;; P.S.; to use this implementation, comment out the KeyRelease listener ;; ;; together with this one and make GrabKey in Sync mode. -;; (cl-defmethod exwm-input--handle-KeyPress-line-mode ((obj xcb:KeyPress)) +;; (cl-defmethod exwm-input--on-KeyPress-line-mode ((obj xcb:KeyPress)) ;; "Parse X KeyPress event to Emacs key event and then feed the command loop." ;; (with-slots (detail state) obj ;; (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) @@ -238,7 +240,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") ;; This implementation has a drawback that some (legacy) applications ;; (e.g. xterm) ignore the synthetic key events, making it only viable for EXWM ;; to work in char-mode in such case. -(cl-defmethod exwm-input--handle-KeyPress-line-mode ((obj xcb:KeyPress)) +(cl-defmethod exwm-input--on-KeyPress-line-mode ((obj xcb:KeyPress)) "Parse X KeyPress event to Emacs key event and then feed the command loop." (with-slots (detail state) obj (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) @@ -264,23 +266,22 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") :event (xcb:marshal obj exwm--connection))) (xcb:flush exwm--connection))))) -(cl-defmethod exwm-input--handle-KeyPress-char-mode ((obj xcb:KeyPress)) +(cl-defmethod exwm-input--on-KeyPress-char-mode ((obj xcb:KeyPress)) "Handle KeyPress event in char-mode." (with-slots (detail state) obj (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) event) (when (and keysym (setq event (xcb:keysyms:keysym->event keysym state))) - (setq exwm-input--temp-line-mode t - exwm-input--during-key-sequence t) - (push event unread-command-events) - (exwm-input--grab-keyboard))))) ;grab keyboard temporarily - -(defalias 'exwm-input--handle-KeyPress 'exwm-input--handle-KeyPress-line-mode - "Generic function for handling KeyPress event.") + (when (eq major-mode 'exwm-mode) + (setq exwm-input--temp-line-mode t + exwm-input--during-key-sequence t) + (exwm-input--grab-keyboard)) ;grab keyboard temporarily + (push event unread-command-events))))) (defun exwm-input--grab-keyboard (&optional id) "Grab all key events on window ID." (unless id (setq id (exwm--buffer->id (window-buffer)))) + (cl-assert id) (when (xcb:+request-checked+request-check exwm--connection (make-instance 'xcb:GrabKey :owner-events 0 :grab-window id @@ -289,19 +290,18 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") :pointer-mode xcb:GrabMode:Async :keyboard-mode xcb:GrabMode:Async)) (exwm--log "Failed to grab keyboard for #x%x" id)) - (defalias 'exwm-input--handle-KeyPress - 'exwm-input--handle-KeyPress-line-mode)) + (setq exwm--on-KeyPress 'exwm-input--on-KeyPress-line-mode)) (defun exwm-input--release-keyboard (&optional id) "Ungrab all key events on window ID." (unless id (setq id (exwm--buffer->id (window-buffer)))) + (cl-assert id) (when (xcb:+request-checked+request-check exwm--connection (make-instance 'xcb:UngrabKey :key xcb:Grab:Any :grab-window id :modifiers xcb:ModMask:Any)) (exwm--log "Failed to release keyboard for #x%x" id)) - (defalias 'exwm-input--handle-KeyPress - 'exwm-input--handle-KeyPress-char-mode)) + (setq exwm--on-KeyPress 'exwm-input--on-KeyPress-char-mode)) (defun exwm-input-grab-keyboard (&optional id) "Switch to line-mode." diff --git a/exwm-workspace.el b/exwm-workspace.el index d700f41a14..b1755435ef 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -98,20 +98,20 @@ The optional FORCE option is for internal use only." (interactive (list - (let* ((history-add-new-input nil) ;prevent modifying history - (idx (read-from-minibuffer - "Workspace: " (elt exwm-workspace--switch-history - exwm-workspace-current-index) - exwm-workspace--switch-map nil - `(exwm-workspace--switch-history - . ,(1+ exwm-workspace-current-index))))) - (cl-position idx exwm-workspace--switch-history :test 'equal)))) - (unless exwm-workspace--switch-lock + (unless (and (eq major-mode 'exwm-mode) exwm--fullscreen) ;it's invisible + (let* ((history-add-new-input nil) ;prevent modifying history + (idx (read-from-minibuffer + "Workspace: " (elt exwm-workspace--switch-history + exwm-workspace-current-index) + exwm-workspace--switch-map nil + `(exwm-workspace--switch-history + . ,(1+ exwm-workspace-current-index))))) + (cl-position idx exwm-workspace--switch-history :test 'equal))))) + (unless (or exwm-workspace--switch-lock (not index)) (setq exwm-workspace--switch-lock t) (unless (and (<= 0 index) (< index exwm-workspace-number)) (user-error "[EXWM] Workspace index out of range: %d" index)) (when (or force (/= exwm-workspace-current-index index)) - (exwm-reset) ;exit full screen (let ((frame (elt exwm-workspace--list index))) (setq exwm-workspace--current frame exwm-workspace-current-index index) diff --git a/exwm.el b/exwm.el index 18d8ddad8f..88d767469b 100644 --- a/exwm.el +++ b/exwm.el @@ -196,8 +196,8 @@ "Reset window to standard state: non-fullscreen, line-mode." (interactive) (with-current-buffer (window-buffer) - (when (and (eq major-mode 'exwm-mode) exwm--fullscreen) - (exwm-layout-unset-fullscreen) + (when (eq major-mode 'exwm-mode) + (when exwm--fullscreen (exwm-layout-unset-fullscreen)) (exwm-input-grab-keyboard)))) (defmacro exwm--with-current-id (id &rest body) @@ -651,6 +651,8 @@ (set (make-local-variable 'exwm--fullscreen) nil) ;used in fullscreen (set (make-local-variable 'exwm--floating-frame-geometry) nil) ;in fullscreen (set (make-local-variable 'exwm--fixed-size) nil) ;fixed size + (set (make-local-variable 'exwm--on-KeyPress) ;KeyPress event handler + 'exwm-input--on-KeyPress-line-mode) ;; Properties (set (make-local-variable 'exwm-window-type) nil) (set (make-local-variable 'exwm--geometry) nil) -- cgit 1.4.1 From 84f0f0328b173af82c873e395353bd9a6c3bf1f6 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 7 Aug 2015 20:22:12 +0800 Subject: Fix input focus lost after closing window Also insert some debug messages. --- exwm-input.el | 35 +++++++++++++++++++++++++---------- exwm-layout.el | 3 +++ exwm-manage.el | 6 ++++-- exwm-workspace.el | 2 ++ 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index f94cf50f24..c312ff762b 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -57,17 +57,22 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (defun exwm-input--set-focus (id) "Set input focus to window ID in a proper way." (exwm--with-current-id id + (exwm--log "Set focus ID to #x%x" id) (setq exwm-input--focus-id id) (if (and (not exwm--hints-input) (memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols)) - (xcb:+request exwm--connection - (make-instance 'xcb:icccm:SendEvent - :destination id - :event (xcb:marshal - (make-instance 'xcb:icccm:WM_TAKE_FOCUS - :window id - :time exwm-input--timestamp) - exwm--connection))) + (progn + (exwm--log "Focus on #x%x with WM_TAKE_FOCUS" id) + (xcb:+request exwm--connection + (make-instance 'xcb:icccm:SendEvent + :destination id + :event (xcb:marshal + (make-instance 'xcb:icccm:WM_TAKE_FOCUS + :window id + :time + exwm-input--timestamp) + exwm--connection)))) + (exwm--log "Focus on #x%x with SetInputFocus" id) (xcb:+request exwm--connection (make-instance 'xcb:SetInputFocus :revert-to xcb:InputFocus:Parent :focus id @@ -85,24 +90,34 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (setq exwm-input--focus-lock t) (when (eq (current-buffer) (window-buffer)) ;e.g. with-temp-buffer (if (eq major-mode 'exwm-mode) - (progn (setq exwm-input--focus-id exwm--id) + (progn (exwm--log "Set focus ID to #x%x" exwm--id) + (setq exwm-input--focus-id exwm--id) (when exwm--floating-frame (if (eq (selected-frame) exwm--floating-frame) ;; Cancel the possible input focus redirection - (redirect-frame-focus exwm--floating-frame nil) + (progn + (exwm--log "Cancel input focus redirection on %s" + exwm--floating-frame) + (redirect-frame-focus exwm--floating-frame nil)) ;; Focus the floating frame + (exwm--log "Focus on floating frame %s" + exwm--floating-frame) (x-focus-frame exwm--floating-frame))) ;; Finally focus the window (exwm-input--set-focus exwm-input--focus-id)) (exwm--with-current-id exwm-input--focus-id + (exwm--log "Set focus ID to #x%x" xcb:Window:None) (setq exwm-input--focus-id xcb:Window:None) (let ((frame (selected-frame))) (if exwm--floating-frame (unless (or (eq frame exwm--floating-frame) (active-minibuffer-window)) ;; Redirect input focus to the workspace frame + (exwm--log "Redirect input focus (%s => %s)" + exwm--floating-frame frame) (redirect-frame-focus exwm--floating-frame frame)) ;; Focus the workspace frame + (exwm--log "Focus on workspace %s" frame) (x-focus-frame frame)))))) (setq exwm-input--focus-lock nil))) diff --git a/exwm-layout.el b/exwm-layout.el index 7630b688e2..1dd8333bb4 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -28,6 +28,7 @@ (defun exwm-layout--show (id &optional window) "Show window ID exactly fit in the Emacs window WINDOW." + (exwm--log "Show #x%x in %s" id window) (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window id)) (xcb:+request exwm--connection (make-instance 'xcb:icccm:set-WM_STATE @@ -69,6 +70,7 @@ "Hide window ID." (unless (eq xcb:icccm:WM_STATE:IconicState ;already hidden (with-current-buffer (exwm--id->buffer id) exwm-state)) + (exwm--log "Hide #x%x" id) (xcb:+request exwm--connection (make-instance 'xcb:ChangeWindowAttributes :window id :value-mask xcb:CW:EventMask @@ -162,6 +164,7 @@ "Refresh layout." (unless (compare-window-configurations exwm-layout--window-configuration (current-window-configuration)) + (exwm--log "Refresh layout") (setq exwm-layout--window-configuration (current-window-configuration)) (let ((frame (selected-frame)) windows) diff --git a/exwm-manage.el b/exwm-manage.el index 0d2c74f4f1..7c82696fb7 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -178,8 +178,10 @@ corresponding buffer.") :window id :property xcb:Atom:WM_STATE)) (xcb:flush exwm--connection)) (setq kill-buffer-query-functions nil) - (kill-buffer) - (select-frame-set-input-focus exwm-workspace--current))))) + (let ((floating exwm--floating-frame)) + (kill-buffer) + (when floating + (select-frame-set-input-focus exwm-workspace--current))))))) (defun exwm-manage--scan () "Search for existing windows and try to manage them." diff --git a/exwm-workspace.el b/exwm-workspace.el index b1755435ef..da3f9ce4bc 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -152,7 +152,9 @@ The optional FORCE option is for internal use only." "Fix unexpected frame switch." (unless exwm-workspace--switch-lock (let ((index (cl-position (selected-frame) exwm-workspace--list))) + (exwm--log "Focus on workspace %s" index) (when (and index (/= index exwm-workspace-current-index)) + (exwm--log "Workspace was switched unexpectedly") (exwm-workspace-switch index))))) (defun exwm-workspace-move-window (index &optional id) -- cgit 1.4.1 From 14628a940c12051f2538556b97a2ef4531c75201 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 7 Aug 2015 21:52:34 +0800 Subject: Fix race conditions when managing a window Since it takes some time for EXWM to create a buffer for a window (to do some checking for example), the window may send several MapRequest events before it's mapped. This commit should fix such issue. --- exwm-manage.el | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/exwm-manage.el b/exwm-manage.el index 7c82696fb7..8594066a71 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -42,19 +42,28 @@ corresponding buffer.") (when reply ;nil when destroyed (setq exwm--geometry reply)))))) +(defvar exwm-manage--manage-window-queue nil + "List of window IDs to prevent race conditions.") + (defun exwm-manage--manage-window (id) "Manage window ID." + (exwm--log "Try to manage #x%x" id) (setq exwm-input--focus-lock t) (catch 'return ;; Ensure it's not managed - (when (assoc id exwm--id-buffer-alist) + (when (or (assoc id exwm--id-buffer-alist) + (memq id exwm-manage--manage-window-queue)) + (exwm--log "#x%x is already managed" id) (throw 'return 'managed)) + (push id exwm-manage--manage-window-queue) ;prevent reentering ;; Ensure it's alive (when (xcb:+request-checked+request-check exwm--connection (make-instance 'xcb:ChangeWindowAttributes :window id :value-mask xcb:CW:EventMask :event-mask exwm--client-event-mask)) + (delq id exwm-manage--manage-window-queue) ;cleanup (throw 'return 'dead)) + (delq id exwm-manage--manage-window-queue) ;cleanup (late enough) (with-current-buffer (generate-new-buffer "*EXWM*") (push `(,id . ,(current-buffer)) exwm--id-buffer-alist) (exwm-mode) @@ -73,6 +82,7 @@ corresponding buffer.") (and exwm-instance-name (string-prefix-p "sun-awt-X11-" exwm-instance-name) (not (string-suffix-p "XFramePeer" exwm-instance-name)))) + (exwm--log "No need to manage #x%x" id) ;; Remove all events (xcb:+request-checked+request-check exwm--connection (make-instance 'xcb:ChangeWindowAttributes @@ -104,6 +114,7 @@ corresponding buffer.") (kill-buffer (current-buffer)) (throw 'return 'ignored)) ;; Manage the window + (exwm--log "Manage #x%x" id) (xcb:+request exwm--connection ;remove border (make-instance 'xcb:ConfigureWindow :window id :value-mask xcb:ConfigWindow:BorderWidth @@ -140,6 +151,7 @@ corresponding buffer.") (defun exwm-manage--unmanage-window (id &optional withdraw-only) "Unmanage window ID." (let ((buffer (exwm--id->buffer id))) + (exwm--log "Unmanage #x%x (buffer: %s)" id buffer) (setq exwm--id-buffer-alist (assq-delete-all id exwm--id-buffer-alist)) (when (buffer-live-p buffer) (with-current-buffer buffer -- cgit 1.4.1 From 3b9d0dd9217add078415ecda644b5cd19632f1fc Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 8 Aug 2015 08:29:57 +0800 Subject: Fixes for manage/unmanage window * Make sure `exwm-manage--manage-window-queue` is cleaned * Improve input focus handling after unmanaging a window * Remove a redundant call to `exwm-layout--show` --- exwm-floating.el | 1 - exwm-manage.el | 16 +++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index f302bc076f..3e9cade15a 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -199,7 +199,6 @@ exwm--frame exwm-workspace--current)) (select-frame exwm-workspace--current t) (set-window-buffer nil buffer) - (exwm-layout--show id) (exwm-input--set-focus id)) (setq exwm-input--focus-lock nil)) diff --git a/exwm-manage.el b/exwm-manage.el index 8594066a71..8bf118e2f0 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -61,9 +61,11 @@ corresponding buffer.") (make-instance 'xcb:ChangeWindowAttributes :window id :value-mask xcb:CW:EventMask :event-mask exwm--client-event-mask)) - (delq id exwm-manage--manage-window-queue) ;cleanup + (setq exwm-manage--manage-window-queue + (delq id exwm-manage--manage-window-queue)) ;cleanup (throw 'return 'dead)) - (delq id exwm-manage--manage-window-queue) ;cleanup (late enough) + (setq exwm-manage--manage-window-queue + (delq id exwm-manage--manage-window-queue)) ;cleanup (late enough) (with-current-buffer (generate-new-buffer "*EXWM*") (push `(,id . ,(current-buffer)) exwm--id-buffer-alist) (exwm-mode) @@ -193,7 +195,15 @@ corresponding buffer.") (let ((floating exwm--floating-frame)) (kill-buffer) (when floating - (select-frame-set-input-focus exwm-workspace--current))))))) + (if (eq 'exwm-mode + (with-current-buffer + (window-buffer + (frame-first-window exwm-workspace--current)) + major-mode)) + ;; Input focus is to be set on a window + (x-focus-frame exwm-workspace--current) + ;; Set input focus on a frame + (select-frame-set-input-focus exwm-workspace--current)))))))) (defun exwm-manage--scan () "Search for existing windows and try to manage them." -- cgit 1.4.1 From 52984898ec8d59fe42e86a311c7e7dbeec95c611 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 8 Aug 2015 11:16:17 +0800 Subject: Fix some input focus issues * Fix input focus lost after moving window to another workspace * Enhance `exwm-reset` to provide user a way to reset input focus when it's lost unexpectedly --- exwm-workspace.el | 13 ++++++++----- exwm.el | 3 +++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index da3f9ce4bc..efecf571db 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -181,14 +181,17 @@ The optional FORCE option is for internal use only." (if exwm--floating-frame ;; Move the floating frame is enough (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window (frame-parameter exwm--floating-frame - 'exwm-outer-id) - :parent (frame-parameter frame 'exwm-window-id) - :x 0 :y 0)) + (make-instance 'xcb:ReparentWindow + :window (frame-parameter exwm--floating-frame + 'exwm-outer-id) + :parent (frame-parameter frame 'exwm-window-id) + :x 0 :y 0)) ;; Move the window itself (bury-buffer) (exwm-layout--hide id) + ;; Force update input focus + (setq exwm-input--focus-id xcb:Window:None) + (exwm-input--update-focus) (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow :window id diff --git a/exwm.el b/exwm.el index 88d767469b..984f8eee5e 100644 --- a/exwm.el +++ b/exwm.el @@ -198,6 +198,9 @@ (with-current-buffer (window-buffer) (when (eq major-mode 'exwm-mode) (when exwm--fullscreen (exwm-layout-unset-fullscreen)) + ;; Force update input focus + (setq exwm-input--focus-id xcb:Window:None) + (exwm-input--update-focus) (exwm-input-grab-keyboard)))) (defmacro exwm--with-current-id (id &rest body) -- cgit 1.4.1 From 63402b0efc19a50e89d868fad3f91b134170aab6 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 8 Aug 2015 20:12:07 +0800 Subject: Correct layout refresh problems * Relax the conditions to refresh layout; this may introduce some overheads though * Fix the problem when `*scratch*` buffer is killed; close #12 * Enhance `exwm-reset` by forcing layout refresh in it. This should allow users to overcome some layout bugs --- exwm-layout.el | 34 ++++++++++++++++++---------------- exwm.el | 3 +++ 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index 1dd8333bb4..906357ed34 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -157,23 +157,25 @@ (setq exwm--fullscreen nil) (exwm-input-grab-keyboard))) -(defvar exwm-layout--window-configuration (current-window-configuration) - "Last saved window configuration, for avoiding unnecessary refreshes.") - (defun exwm-layout--refresh () "Refresh layout." - (unless (compare-window-configurations exwm-layout--window-configuration - (current-window-configuration)) - (exwm--log "Refresh layout") - (setq exwm-layout--window-configuration (current-window-configuration)) - (let ((frame (selected-frame)) - windows) - (if (not (memq frame exwm-workspace--list)) - ;; Refresh a floating frame - (when (eq major-mode 'exwm-mode) - (with-current-buffer (window-buffer (frame-first-window frame)) - (exwm-layout--show exwm--id (frame-first-window frame)))) - ;; Refresh the whole workspace + (let ((frame (selected-frame)) + windows placeholder) + (if (not (memq frame exwm-workspace--list)) + ;; Refresh a floating frame + (progn + (cl-assert (eq major-mode 'exwm-mode)) + (let ((window (frame-first-window frame))) + (with-current-buffer (window-buffer window) + (exwm--log "Refresh floating window #x%x" exwm--id) + (exwm-layout--show exwm--id window)))) + ;; Refresh the whole workspace + ;; Workspaces other than the active one can also be refreshed (RandR) + (exwm--log "Refresh workspace %s" frame) + (let ((placeholder (get-buffer "*scratch*"))) + (unless placeholder ;create the *scratch* buffer if it's killed + (setq placeholder (get-buffer-create "*scratch*")) + (set-buffer-major-mode placeholder)) (dolist (pair exwm--id-buffer-alist) (with-current-buffer (cdr pair) ;; Exclude windows on other workspaces and floating frames @@ -183,7 +185,7 @@ (exwm-layout--hide exwm--id) (exwm-layout--show exwm--id (car windows)) (dolist (i (cdr windows)) - (set-window-buffer i "*scratch*")))))))))) + (set-window-buffer i placeholder)))))))))) (defun exwm-layout--init () "Initialize layout module." diff --git a/exwm.el b/exwm.el index 984f8eee5e..7c9fa59410 100644 --- a/exwm.el +++ b/exwm.el @@ -201,6 +201,8 @@ ;; Force update input focus (setq exwm-input--focus-id xcb:Window:None) (exwm-input--update-focus) + ;; Force refresh + (exwm-layout--refresh) (exwm-input-grab-keyboard)))) (defmacro exwm--with-current-id (id &rest body) @@ -396,6 +398,7 @@ (exwm--update-protocols id t)) ((= atom xcb:Atom:WM_STATE) (exwm--update-state id t)) + ((= atom xcb:Atom:_NET_WM_USER_TIME)) ;ignored (t (exwm--log "Unhandled PropertyNotify: %s(%d)" (x-get-atom-name atom) atom))))))) -- cgit 1.4.1 From 42f4ec6db50fc0d62792847e63349c46b056ad8f Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 8 Aug 2015 20:39:05 +0800 Subject: Show moved window by default --- exwm-workspace.el | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index efecf571db..148ff36c59 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -180,12 +180,15 @@ The optional FORCE option is for internal use only." (concat " " (replace-regexp-in-string "^\\s-*" "" (buffer-name)))) (if exwm--floating-frame ;; Move the floating frame is enough - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window (frame-parameter exwm--floating-frame - 'exwm-outer-id) - :parent (frame-parameter frame 'exwm-window-id) - :x 0 :y 0)) + (progn + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window (frame-parameter exwm--floating-frame + 'exwm-outer-id) + :parent (frame-parameter frame + 'exwm-window-id) + :x 0 :y 0)) + (xcb:flush exwm--connection)) ;; Move the window itself (bury-buffer) (exwm-layout--hide id) @@ -196,8 +199,10 @@ The optional FORCE option is for internal use only." (make-instance 'xcb:ReparentWindow :window id :parent (frame-parameter frame 'exwm-window-id) - :x 0 :y 0))))) - (xcb:flush exwm--connection) + :x 0 :y 0)) + (xcb:flush exwm--connection) + (set-window-buffer (frame-first-window frame) + (exwm--id->buffer id))))) (exwm-workspace--update-switch-history))) (defun exwm-workspace-rename-buffer (newname) -- cgit 1.4.1 From 36bb5793c2026230a2a660d443dde1c3b1a98de6 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 9 Aug 2015 11:27:48 +0800 Subject: Refresh when minibuffer grows (fix #10) The expansion of echo area is not handled however. --- exwm-layout.el | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/exwm-layout.el b/exwm-layout.el index 906357ed34..414aa37cbc 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -187,10 +187,21 @@ (dolist (i (cdr windows)) (set-window-buffer i placeholder)))))))))) +(defun exwm-layout--on-minibuffer-setup () + "Refresh layout when minibuffer grows." + (run-with-idle-timer 0.01 nil ;FIXME + (lambda () + (when (and (< 1 (window-height (minibuffer-window))) + (not (and (eq major-mode 'exwm-mode) + exwm--floating-frame))) + (exwm-layout--refresh))))) + (defun exwm-layout--init () "Initialize layout module." ;; Auto refresh layout - (add-hook 'window-configuration-change-hook 'exwm-layout--refresh)) + (add-hook 'window-configuration-change-hook 'exwm-layout--refresh) + ;; Refresh when minibuffer grows + (add-hook 'minibuffer-setup-hook 'exwm-layout--on-minibuffer-setup t)) -- cgit 1.4.1 From edc70eb6616b818463c94b5ab8c9e3f1dfd177c9 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 9 Aug 2015 18:05:13 +0800 Subject: Remove redundant code caused by the concurrency of events With the introduction of ch11ng/xelb@6a7bccc, many weird behaviors should disappear. These include by not limit to * race conditions when managing a window (a workaround is provided in @14628a9) * race conditions when unmanaging a window This commit removes some corresponding code. --- exwm-manage.el | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index 8bf118e2f0..6c229481d2 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -42,30 +42,21 @@ corresponding buffer.") (when reply ;nil when destroyed (setq exwm--geometry reply)))))) -(defvar exwm-manage--manage-window-queue nil - "List of window IDs to prevent race conditions.") - (defun exwm-manage--manage-window (id) "Manage window ID." (exwm--log "Try to manage #x%x" id) (setq exwm-input--focus-lock t) (catch 'return ;; Ensure it's not managed - (when (or (assoc id exwm--id-buffer-alist) - (memq id exwm-manage--manage-window-queue)) + (when (assoc id exwm--id-buffer-alist) (exwm--log "#x%x is already managed" id) (throw 'return 'managed)) - (push id exwm-manage--manage-window-queue) ;prevent reentering ;; Ensure it's alive (when (xcb:+request-checked+request-check exwm--connection (make-instance 'xcb:ChangeWindowAttributes :window id :value-mask xcb:CW:EventMask :event-mask exwm--client-event-mask)) - (setq exwm-manage--manage-window-queue - (delq id exwm-manage--manage-window-queue)) ;cleanup (throw 'return 'dead)) - (setq exwm-manage--manage-window-queue - (delq id exwm-manage--manage-window-queue)) ;cleanup (late enough) (with-current-buffer (generate-new-buffer "*EXWM*") (push `(,id . ,(current-buffer)) exwm--id-buffer-alist) (exwm-mode) -- cgit 1.4.1 From 2d4104a0eceb7d043ed1cd6bdd1bda1db4f91a73 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 10 Aug 2015 10:55:28 +0800 Subject: Fix emacsclient bugs `emacsclient` started with `-c` or `-t` argument create a new frame that shall not be used to manage X windows. Also fix some related input focus issues (with some remaining unfixed). Close #17. --- exwm-floating.el | 14 ++++++++------ exwm-input.el | 6 ++++-- exwm-layout.el | 53 ++++++++++++++++++++++++++++++++--------------------- exwm.el | 9 +++++---- 4 files changed, 49 insertions(+), 33 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 3e9cade15a..bc7ee33846 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -53,12 +53,14 @@ (original-id (frame-parameter original-frame 'exwm-window-id)) ;; Create new frame (frame (with-current-buffer "*scratch*" - (make-frame `((minibuffer . nil) ;use the one on workspace - (background-color - . ,exwm-floating-border-color) - (internal-border-width - . ,exwm-floating-border-width) - (unsplittable . t))))) ;and fix the size later + (prog2 + (exwm--lock) + (make-frame + `((minibuffer . nil) ;use the one on workspace + (background-color . ,exwm-floating-border-color) + (internal-border-width . ,exwm-floating-border-width) + (unsplittable . t))) ;and fix the size later + (exwm--unlock)))) (frame-id (string-to-int (frame-parameter frame 'window-id))) (outer-id (string-to-int (frame-parameter frame 'outer-window-id))) (window (frame-first-window frame)) ;and it's the only window diff --git a/exwm-input.el b/exwm-input.el index c312ff762b..97dfd53e36 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -88,7 +88,8 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") "Update input focus." (unless exwm-input--focus-lock (setq exwm-input--focus-lock t) - (when (eq (current-buffer) (window-buffer)) ;e.g. with-temp-buffer + (when (and (frame-parameter nil 'exwm-window-id) ;e.g. emacsclient frame + (eq (current-buffer) (window-buffer))) ;e.g. `with-temp-buffer' (if (eq major-mode 'exwm-mode) (progn (exwm--log "Set focus ID to #x%x" exwm--id) (setq exwm-input--focus-id exwm--id) @@ -163,7 +164,8 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") ;; Click to focus (unless (and (boundp 'exwm--id) (= event exwm--id)) (exwm--with-current-id event - (raise-frame (or exwm--floating-frame exwm--frame)) + (select-frame-set-input-focus (or exwm--floating-frame + exwm--frame)) (select-window (get-buffer-window nil 'visible)))) ;; The event should be replayed (setq mode xcb:Allow:ReplayPointer)))) diff --git a/exwm-layout.el b/exwm-layout.el index 414aa37cbc..4ae4859a22 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -160,32 +160,43 @@ (defun exwm-layout--refresh () "Refresh layout." (let ((frame (selected-frame)) - windows placeholder) + (placeholder (get-buffer "*scratch*")) + windows) (if (not (memq frame exwm-workspace--list)) - ;; Refresh a floating frame - (progn - (cl-assert (eq major-mode 'exwm-mode)) - (let ((window (frame-first-window frame))) + (if (frame-parameter frame 'exwm-window-id) + ;; Refresh a floating frame + (progn + (cl-assert (eq major-mode 'exwm-mode)) + (let ((window (frame-first-window frame))) + (with-current-buffer (window-buffer window) + (exwm--log "Refresh floating window #x%x" exwm--id) + (exwm-layout--show exwm--id window)))) + ;; Other frames (e.g. terminal/graphical frame of emacsclient) + ;; We shall bury all `exwm-mode' buffers in this case + (unless placeholder ;create the *scratch* buffer if it's killed + (setq placeholder (get-buffer-create "*scratch*")) + (set-buffer-major-mode placeholder)) + (setq windows (window-list frame 0)) ;exclude minibuffer + (dolist (window windows) (with-current-buffer (window-buffer window) - (exwm--log "Refresh floating window #x%x" exwm--id) - (exwm-layout--show exwm--id window)))) + (when (eq major-mode 'exwm-mode) + (set-window-buffer window placeholder))))) ;; Refresh the whole workspace ;; Workspaces other than the active one can also be refreshed (RandR) (exwm--log "Refresh workspace %s" frame) - (let ((placeholder (get-buffer "*scratch*"))) - (unless placeholder ;create the *scratch* buffer if it's killed - (setq placeholder (get-buffer-create "*scratch*")) - (set-buffer-major-mode placeholder)) - (dolist (pair exwm--id-buffer-alist) - (with-current-buffer (cdr pair) - ;; Exclude windows on other workspaces and floating frames - (when (and (eq frame exwm--frame) (not exwm--floating-frame)) - (setq windows (get-buffer-window-list (current-buffer) 0)) - (if (not windows) - (exwm-layout--hide exwm--id) - (exwm-layout--show exwm--id (car windows)) - (dolist (i (cdr windows)) - (set-window-buffer i placeholder)))))))))) + (unless placeholder ;create the *scratch* buffer if it's killed + (setq placeholder (get-buffer-create "*scratch*")) + (set-buffer-major-mode placeholder)) + (dolist (pair exwm--id-buffer-alist) + (with-current-buffer (cdr pair) + ;; Exclude windows on other workspaces and floating frames + (when (and (eq frame exwm--frame) (not exwm--floating-frame)) + (setq windows (get-buffer-window-list (current-buffer) 0)) + (if (not windows) + (exwm-layout--hide exwm--id) + (exwm-layout--show exwm--id (car windows)) + (dolist (i (cdr windows)) + (set-window-buffer i placeholder))))))))) (defun exwm-layout--on-minibuffer-setup () "Refresh layout when minibuffer grows." diff --git a/exwm.el b/exwm.el index 7c9fa59410..4c4d4aff9c 100644 --- a/exwm.el +++ b/exwm.el @@ -195,6 +195,9 @@ (defun exwm-reset () "Reset window to standard state: non-fullscreen, line-mode." (interactive) + (unless (frame-parameter nil 'exwm-window-id) + ;; Move focus away form a non-EXWM frame + (x-focus-frame exwm-workspace--current)) (with-current-buffer (window-buffer) (when (eq major-mode 'exwm-mode) (when exwm--fullscreen (exwm-layout-unset-fullscreen)) @@ -400,7 +403,8 @@ (exwm--update-state id t)) ((= atom xcb:Atom:_NET_WM_USER_TIME)) ;ignored (t (exwm--log "Unhandled PropertyNotify: %s(%d)" - (x-get-atom-name atom) atom))))))) + (x-get-atom-name atom exwm-workspace--current) + atom))))))) (defun exwm--on-ClientMessage (raw-data synthetic) "Handle ClientMessage event." @@ -628,9 +632,6 @@ (exwm-input--init) (exwm-randr--init) (exwm--unlock) - ;; Disable events during new frame creation - (add-hook 'before-make-frame-hook 'exwm--lock) - (add-hook 'after-make-frame-functions 'exwm--unlock) ;; Manage exiting windows (exwm-manage--scan) (run-hooks 'exwm-init-hook))))) -- cgit 1.4.1 From 048994c7948528630b5c13f56dd22a9b2972e09c Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 10 Aug 2015 14:23:37 +0800 Subject: Remove redundant code caused by the concurrency of events (continued) Remove `exwm--with-current-id`, which was introduced to as a wrapper to `with-current-buffer` to do extra checks. Note that in functions run as hooks, the validation of window ID is still required as they are not synchronized with events. --- exwm-input.el | 37 ++++++++++++++++++++----------------- exwm-layout.el | 4 +++- exwm-manage.el | 4 ++-- exwm.el | 26 +++++++++----------------- 4 files changed, 34 insertions(+), 37 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 97dfd53e36..24aa94abda 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -56,7 +56,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (defun exwm-input--set-focus (id) "Set input focus to window ID in a proper way." - (exwm--with-current-id id + (with-current-buffer (exwm--id->buffer id) (exwm--log "Set focus ID to #x%x" id) (setq exwm-input--focus-id id) (if (and (not exwm--hints-input) @@ -105,21 +105,24 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") exwm--floating-frame) (x-focus-frame exwm--floating-frame))) ;; Finally focus the window - (exwm-input--set-focus exwm-input--focus-id)) - (exwm--with-current-id exwm-input--focus-id - (exwm--log "Set focus ID to #x%x" xcb:Window:None) - (setq exwm-input--focus-id xcb:Window:None) - (let ((frame (selected-frame))) - (if exwm--floating-frame - (unless (or (eq frame exwm--floating-frame) - (active-minibuffer-window)) - ;; Redirect input focus to the workspace frame - (exwm--log "Redirect input focus (%s => %s)" - exwm--floating-frame frame) - (redirect-frame-focus exwm--floating-frame frame)) - ;; Focus the workspace frame - (exwm--log "Focus on workspace %s" frame) - (x-focus-frame frame)))))) + (when (exwm--id->buffer exwm-input--focus-id) + (exwm-input--set-focus exwm-input--focus-id))) + (let ((buffer (exwm--id->buffer exwm-input--focus-id))) + (when buffer + (with-current-buffer buffer + (exwm--log "Set focus ID to #x%x" xcb:Window:None) + (setq exwm-input--focus-id xcb:Window:None) + (let ((frame (selected-frame))) + (if exwm--floating-frame + (unless (or (eq frame exwm--floating-frame) + (active-minibuffer-window)) + ;; Redirect input focus to the workspace frame + (exwm--log "Redirect input focus (%s => %s)" + exwm--floating-frame frame) + (redirect-frame-focus exwm--floating-frame frame)) + ;; Focus the workspace frame + (exwm--log "Focus on workspace %s" frame) + (x-focus-frame frame)))))))) (setq exwm-input--focus-lock nil))) (defun exwm-input--finish-key-sequence () @@ -163,7 +166,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (t ;; Click to focus (unless (and (boundp 'exwm--id) (= event exwm--id)) - (exwm--with-current-id event + (with-current-buffer (exwm--id->buffer event) (select-frame-set-input-focus (or exwm--floating-frame exwm--frame)) (select-window (get-buffer-window nil 'visible)))) diff --git a/exwm-layout.el b/exwm-layout.el index 4ae4859a22..5d056afeb5 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -34,7 +34,9 @@ (make-instance 'xcb:icccm:set-WM_STATE :window id :state xcb:icccm:WM_STATE:NormalState :icon xcb:Window:None)) - (let* ((edges (or (exwm--with-current-id id exwm--floating-edges) + (let* ((buffer (exwm--id->buffer id)) + (edges (or (and buffer + (with-current-buffer buffer exwm--floating-edges)) (window-inside-pixel-edges window))) (x (elt edges 0)) (y (elt edges 1)) diff --git a/exwm-manage.el b/exwm-manage.el index 6c229481d2..9d98607898 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -35,7 +35,7 @@ corresponding buffer.") (defun exwm-manage--update-geometry (id &optional force) "Update window geometry." - (exwm--with-current-id id + (with-current-buffer (exwm--id->buffer id) (unless (and exwm--geometry (not force)) (let ((reply (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GetGeometry :drawable id)))) @@ -137,7 +137,7 @@ corresponding buffer.") (exwm-input-grab-keyboard id) (exwm-workspace--update-switch-history) (setq exwm-input--focus-lock nil) ;unlocked in advance - (exwm--with-current-id id + (with-current-buffer (exwm--id->buffer id) (run-hooks 'exwm-manage-finish-hook)))) (setq exwm-input--focus-lock nil)) diff --git a/exwm.el b/exwm.el index 4c4d4aff9c..95b674243c 100644 --- a/exwm.el +++ b/exwm.el @@ -208,17 +208,9 @@ (exwm-layout--refresh) (exwm-input-grab-keyboard)))) -(defmacro exwm--with-current-id (id &rest body) - "Evaluate BODY in the context of the buffer corresponding to window ID." - (declare (indent 1)) - `(when ,id - (let ((buffer (exwm--id->buffer ,id))) - (when buffer - (with-current-buffer buffer ,@body))))) - (defun exwm--update-window-type (id &optional force) "Update _NET_WM_WINDOW_TYPE." - (exwm--with-current-id id + (with-current-buffer (exwm--id->buffer id) (unless (and exwm-window-type (not force)) (let ((reply (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:ewmh:get-_NET_WM_WINDOW_TYPE @@ -231,7 +223,7 @@ (defun exwm--update-class (id &optional force) "Update WM_CLASS." - (exwm--with-current-id id + (with-current-buffer (exwm--id->buffer id) (unless (and exwm-instance-name exwm-class-name (not force)) (let ((reply (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:icccm:get-WM_CLASS :window id)))) @@ -246,7 +238,7 @@ (defun exwm--update-utf8-title (id &optional force) "Update _NET_WM_NAME." - (exwm--with-current-id id + (with-current-buffer (exwm--id->buffer id) (when (or force (not exwm-title)) (let ((reply (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:ewmh:get-_NET_WM_NAME :window id)))) @@ -258,7 +250,7 @@ (defun exwm--update-ctext-title (id &optional force) "Update WM_NAME." - (exwm--with-current-id id + (with-current-buffer (exwm--id->buffer id) (unless (or exwm--title-is-utf8 (and exwm-title (not force))) (let ((reply (xcb:+request-unchecked+reply exwm--connection @@ -275,7 +267,7 @@ (defun exwm--update-transient-for (id &optional force) "Update WM_TRANSIENT_FOR." - (exwm--with-current-id id + (with-current-buffer (exwm--id->buffer id) (unless (and exwm-transient-for (not force)) (let ((reply (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:icccm:get-WM_TRANSIENT_FOR @@ -285,7 +277,7 @@ (defun exwm--update-normal-hints (id &optional force) "Update WM_NORMAL_HINTS." - (exwm--with-current-id id + (with-current-buffer (exwm--id->buffer id) (unless (and (not force) (or exwm--normal-hints-x exwm--normal-hints-y exwm--normal-hints-width exwm--normal-hints-height @@ -332,7 +324,7 @@ (defun exwm--update-hints (id &optional force) "Update WM_HINTS." - (exwm--with-current-id id + (with-current-buffer (exwm--id->buffer id) (unless (and (not force) exwm--hints-input exwm--hints-urgency) (let ((reply (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:icccm:get-WM_HINTS :window id)))) @@ -351,7 +343,7 @@ (defun exwm--update-protocols (id &optional force) "Update WM_PROTOCOLS." - (exwm--with-current-id id + (with-current-buffer (exwm--id->buffer id) (unless (and exwm--protocols (not force)) (let ((reply (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:icccm:get-WM_PROTOCOLS @@ -361,7 +353,7 @@ (defun exwm--update-state (id &optional force) "Update WM_STATE." - (exwm--with-current-id id + (with-current-buffer (exwm--id->buffer id) (unless (and exwm-state (not force)) (let ((reply (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:icccm:get-WM_STATE :window id)))) -- cgit 1.4.1 From 24b964bb4af100b959a33215cc91b9c896c9359e Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 10 Aug 2015 21:01:55 +0800 Subject: Ignore repeated `MappingNotify` events For some reason, `MappingNotify` events are generated quite frequently and greatly impact the performance. This commit disables the complete refresh of keyboard mapping. --- exwm-input.el | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 24aa94abda..293fd67047 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -139,13 +139,24 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (let ((obj (make-instance 'xcb:MappingNotify))) (xcb:unmarshal obj data) (with-slots (request first-keycode count) obj - (cond ((= request xcb:Mapping:Modifier) - ;; Modifier keys changed - (xcb:keysyms:update-modifier-mapping exwm--connection)) - ((= request xcb:Mapping:Keyboard) - ;; Only updated changed keys - (xcb:keysyms:update-keyboard-mapping exwm--connection - first-keycode count)))))) + (cond + ((= request xcb:Mapping:Modifier) + ;; Modifier keys changed + (exwm--log "Update modifier mapping") + (xcb:keysyms:update-modifier-mapping exwm--connection) + ) + ((= request xcb:Mapping:Keyboard) + ;; Only update changed keys + (with-slots (min-keycode max-keycode) + (xcb:get-setup exwm--connection) + ;; Since this operation is quite time-consuming, a complete refresh + ;; is forbidden as it's unlikely to bring any useful information + (unless (and (= min-keycode first-keycode) + (= max-keycode (+ first-keycode count -1))) + (exwm--log "Update keyboard mapping: %d ~ %d" + first-keycode (+ first-keycode count)) + (xcb:keysyms:update-keyboard-mapping exwm--connection + first-keycode count)))))))) (defun exwm-input--on-ButtonPress (data synthetic) "Handle ButtonPress event." -- cgit 1.4.1 From b755296f547938d0f97d6aa49b8cb0d726de9eb9 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 11 Aug 2015 09:18:21 +0800 Subject: Remove redundant code caused by the concurrency of events (contd, 2) * Remove locks that are no longer required * Also fix #20 (inactive workspace frame steals input focus) --- exwm-floating.el | 8 ++---- exwm-input.el | 75 ++++++++++++++++++++++++++----------------------------- exwm-manage.el | 5 +--- exwm-workspace.el | 18 ++++++------- 4 files changed, 45 insertions(+), 61 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index bc7ee33846..3971fa4812 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -39,7 +39,6 @@ (defun exwm-floating--set-floating (id) "Make window ID floating." (interactive) - (setq exwm-input--focus-lock t) (when (get-buffer-window (exwm--id->buffer id)) ;window in non-floating state (set-window-buffer (selected-window) (other-buffer))) ;hide it first (let* ((original-frame @@ -167,13 +166,11 @@ ;; FIXME: other conditions? (unless (memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY exwm-window-type) (x-focus-frame exwm--floating-frame) - (exwm-input--set-focus id))) - (setq exwm-input--focus-lock nil))) + (exwm-input--set-focus id))))) (defun exwm-floating--unset-floating (id) "Make window ID non-floating." (interactive) - (setq exwm-input--focus-lock t) (let ((buffer (exwm--id->buffer id))) ;; Reparent to workspace frame (xcb:+request exwm--connection @@ -201,8 +198,7 @@ exwm--frame exwm-workspace--current)) (select-frame exwm-workspace--current t) (set-window-buffer nil buffer) - (exwm-input--set-focus id)) - (setq exwm-input--focus-lock nil)) + (exwm-input--set-focus id))) (defun exwm-floating-toggle-floating () "Toggle the current window between floating and non-floating states." diff --git a/exwm-input.el b/exwm-input.el index 293fd67047..a19331431d 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -81,49 +81,44 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (defvar exwm-input--focus-id xcb:Window:None "The window that is theoretically focused.") -(defvar exwm-input--focus-lock nil - "Non-nil when input focus should stay unchanged.") (defun exwm-input--update-focus () "Update input focus." - (unless exwm-input--focus-lock - (setq exwm-input--focus-lock t) - (when (and (frame-parameter nil 'exwm-window-id) ;e.g. emacsclient frame - (eq (current-buffer) (window-buffer))) ;e.g. `with-temp-buffer' - (if (eq major-mode 'exwm-mode) - (progn (exwm--log "Set focus ID to #x%x" exwm--id) - (setq exwm-input--focus-id exwm--id) - (when exwm--floating-frame - (if (eq (selected-frame) exwm--floating-frame) - ;; Cancel the possible input focus redirection - (progn - (exwm--log "Cancel input focus redirection on %s" - exwm--floating-frame) - (redirect-frame-focus exwm--floating-frame nil)) - ;; Focus the floating frame - (exwm--log "Focus on floating frame %s" - exwm--floating-frame) - (x-focus-frame exwm--floating-frame))) - ;; Finally focus the window - (when (exwm--id->buffer exwm-input--focus-id) - (exwm-input--set-focus exwm-input--focus-id))) - (let ((buffer (exwm--id->buffer exwm-input--focus-id))) - (when buffer - (with-current-buffer buffer - (exwm--log "Set focus ID to #x%x" xcb:Window:None) - (setq exwm-input--focus-id xcb:Window:None) - (let ((frame (selected-frame))) - (if exwm--floating-frame - (unless (or (eq frame exwm--floating-frame) - (active-minibuffer-window)) - ;; Redirect input focus to the workspace frame - (exwm--log "Redirect input focus (%s => %s)" - exwm--floating-frame frame) - (redirect-frame-focus exwm--floating-frame frame)) - ;; Focus the workspace frame - (exwm--log "Focus on workspace %s" frame) - (x-focus-frame frame)))))))) - (setq exwm-input--focus-lock nil))) + (when (and (frame-parameter nil 'exwm-window-id) ;e.g. emacsclient frame + (eq (current-buffer) (window-buffer))) ;e.g. `with-temp-buffer' + (exwm--log "EXWM-INPUT--UPDATE-FOCUS") + (if (eq major-mode 'exwm-mode) + (progn (exwm--log "Set focus ID to #x%x" exwm--id) + (setq exwm-input--focus-id exwm--id) + (when exwm--floating-frame + (if (eq (selected-frame) exwm--floating-frame) + ;; Cancel the possible input focus redirection + (progn + (exwm--log "Cancel input focus redirection on %s" + exwm--floating-frame) + (redirect-frame-focus exwm--floating-frame nil)) + ;; Focus the floating frame + (exwm--log "Focus on floating frame %s" + exwm--floating-frame) + (x-focus-frame exwm--floating-frame))) + ;; Finally focus the window + (when (exwm--id->buffer exwm-input--focus-id) + (exwm-input--set-focus exwm-input--focus-id))) + (let ((buffer (exwm--id->buffer exwm-input--focus-id))) + (when (and buffer (eq (selected-frame) exwm-workspace--current)) + (with-current-buffer buffer + (exwm--log "Set focus ID to #x%x" xcb:Window:None) + (setq exwm-input--focus-id xcb:Window:None) + (if exwm--floating-frame + (unless (active-minibuffer-window) + ;; Redirect input focus to the workspace frame + (exwm--log "Redirect input focus (%s => %s)" + exwm--floating-frame exwm-workspace--current) + (redirect-frame-focus exwm--floating-frame + exwm-workspace--current)) + ;; Focus the workspace frame + (exwm--log "Focus on workspace %s" exwm-workspace--current) + (x-focus-frame exwm-workspace--current)))))))) (defun exwm-input--finish-key-sequence () "Mark the end of a key sequence (with the aid of `pre-command-hook')." diff --git a/exwm-manage.el b/exwm-manage.el index 9d98607898..79cff221e0 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -45,7 +45,6 @@ corresponding buffer.") (defun exwm-manage--manage-window (id) "Manage window ID." (exwm--log "Try to manage #x%x" id) - (setq exwm-input--focus-lock t) (catch 'return ;; Ensure it's not managed (when (assoc id exwm--id-buffer-alist) @@ -136,10 +135,8 @@ corresponding buffer.") (exwm-floating--unset-floating id)) (exwm-input-grab-keyboard id) (exwm-workspace--update-switch-history) - (setq exwm-input--focus-lock nil) ;unlocked in advance (with-current-buffer (exwm--id->buffer id) - (run-hooks 'exwm-manage-finish-hook)))) - (setq exwm-input--focus-lock nil)) + (run-hooks 'exwm-manage-finish-hook))))) (defun exwm-manage--unmanage-window (id &optional withdraw-only) "Unmanage window ID." diff --git a/exwm-workspace.el b/exwm-workspace.el index 148ff36c59..bcac917023 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -90,7 +90,6 @@ (defvar exwm-workspace--current nil "Current active workspace.") (defvar exwm-workspace-current-index 0 "Index of current active workspace.") -(defvar exwm-workspace--switch-lock nil "Non-nil to prevent workspace switch.") (defun exwm-workspace-switch (index &optional force) "Switch to workspace INDEX. Query for INDEX if it's not specified. @@ -107,8 +106,7 @@ The optional FORCE option is for internal use only." `(exwm-workspace--switch-history . ,(1+ exwm-workspace-current-index))))) (cl-position idx exwm-workspace--switch-history :test 'equal))))) - (unless (or exwm-workspace--switch-lock (not index)) - (setq exwm-workspace--switch-lock t) + (when index (unless (and (<= 0 index) (< index exwm-workspace-number)) (user-error "[EXWM] Workspace index out of range: %d" index)) (when (or force (/= exwm-workspace-current-index index)) @@ -145,17 +143,15 @@ The optional FORCE option is for internal use only." (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_CURRENT_DESKTOP :window exwm--root :data index)) - (xcb:flush exwm--connection))) - (setq exwm-workspace--switch-lock nil))) + (xcb:flush exwm--connection))))) (defun exwm-workspace--on-focus-in () "Fix unexpected frame switch." - (unless exwm-workspace--switch-lock - (let ((index (cl-position (selected-frame) exwm-workspace--list))) - (exwm--log "Focus on workspace %s" index) - (when (and index (/= index exwm-workspace-current-index)) - (exwm--log "Workspace was switched unexpectedly") - (exwm-workspace-switch index))))) + (let ((index (cl-position (selected-frame) exwm-workspace--list))) + (exwm--log "Focus on workspace %s" index) + (when (and index (/= index exwm-workspace-current-index)) + (exwm--log "Workspace was switched unexpectedly") + (exwm-workspace-switch index)))) (defun exwm-workspace-move-window (index &optional id) "Move window ID to workspace INDEX." -- cgit 1.4.1 From 801185c7cc2c257ac2e8ed9a3aa8e8119c56ad1f Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 11 Aug 2015 11:54:38 +0800 Subject: Fix buffer switch problems * Prevent switching to floating windows or windows on other workspaces * Provide a workaround for `ido-mode` (can be enabled with `(exwm-enable-ido-workaround)`) --- exwm-layout.el | 8 +++++++- exwm.el | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/exwm-layout.el b/exwm-layout.el index 5d056afeb5..f2d4eb0cbe 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -198,7 +198,13 @@ (exwm-layout--hide exwm--id) (exwm-layout--show exwm--id (car windows)) (dolist (i (cdr windows)) - (set-window-buffer i placeholder))))))))) + (set-window-buffer i placeholder)))))) + ;; Make sure windows floating / on other workspaces are excluded + (dolist (window (window-list frame 0)) + (with-current-buffer (window-buffer window) + (when (and (eq major-mode 'exwm-mode) + (or exwm--floating-frame (not (eq frame exwm--frame)))) + (set-window-buffer window placeholder))))))) (defun exwm-layout--on-minibuffer-setup () "Refresh layout when minibuffer grows." diff --git a/exwm.el b/exwm.el index 95b674243c..7e909cdef6 100644 --- a/exwm.el +++ b/exwm.el @@ -707,6 +707,30 @@ (add-hook 'window-setup-hook 'exwm-init t) ;for Emacs (add-hook 'after-make-frame-functions 'exwm-init t))) ;for Emacs Client +(defun exwm--ido-buffer-window-other-frame (orig-fun buffer) + "Wrapper for `ido-buffer-window-other-frame' to exclude invisible windows." + (let* ((window (funcall orig-fun buffer)) + (frame (window-frame window))) + ;; Exclude windows on other workspaces + (unless (and (memq frame exwm-workspace--list) + (not (eq frame exwm-workspace--current))) + window))) + +(defun exwm--fix-ido-buffer-window-other-frame () + "Fix `ido-buffer-window-other-frame'." + (advice-add 'ido-buffer-window-other-frame :around + 'exwm--ido-buffer-window-other-frame)) + +(defun exwm-enable-ido-workaround () + "Enable workarounds for `ido-mode'." + (add-hook 'exwm-init-hook 'exwm--fix-ido-buffer-window-other-frame)) + +(defun exwm-disable-ido-workaround () + "Disable workarounds for `ido-mode'." + (remove-hook 'exwm-init-hook 'exwm--fix-ido-buffer-window-other-frame) + (advice-remove 'ido-buffer-window-other-frame + 'exwm--ido-buffer-window-other-frame)) + (provide 'exwm) -- cgit 1.4.1 From 1ce18afd0559bacc3a7c43f5a73342814a5b176f Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 11 Aug 2015 15:06:11 +0800 Subject: Center floating windows by default This commit makes a floating window centered to its leading window if it has a valid WM_TRANSIENT_FOR property set. Other it's placed at the center of the screen. --- exwm-floating.el | 20 +++++++++++++++++++- exwm-input.el | 1 - exwm-manage.el | 7 +++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 3971fa4812..7888fd2d0b 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -67,6 +67,7 @@ (y (slot-value exwm--geometry 'y)) (width (slot-value exwm--geometry 'width)) (height (slot-value exwm--geometry 'height))) + (exwm--log "Floating geometry (original): %dx%d%+d%+d" width height x y) ;; Save window IDs (set-frame-parameter frame 'exwm-window-id frame-id) (set-frame-parameter frame 'exwm-outer-id outer-id) @@ -102,7 +103,24 @@ (when (= 0 height) (setq height (/ display-height 2))) ;; Completely outside (when (or (> y display-height) (> 0 (+ y display-height))) - (setq y (/ (- display-height height) 2))))) + (setq y (/ (- display-height height) 2)))) + ;; Center floating windows + (when (and (= x 0) (= y 0)) + (let ((buffer (exwm--id->buffer exwm-transient-for)) + window edges) + (when (and buffer (setq window (get-buffer-window buffer))) + (setq edges (window-inside-absolute-pixel-edges window)) + (unless (and (<= width (- (elt edges 2) (elt edges 0))) + (<= height (- (elt edges 3) (elt edges 1)))) + (setq edges nil))) + (if edges + ;; Put at the center of leading window + (setq x (/ (- (+ (elt edges 2) (elt edges 0)) width) 2) + y (/ (- (+ (elt edges 3) (elt edges 1)) height) 2)) + ;; Put at the center of screen + (setq x (/ (- display-width width) 2) + y (/ (- display-height height) 2)))))) + (exwm--log "Floating geometry (corrected): %dx%d%+d%+d" width height x y) ;; Set OverrideRedirect on this frame (xcb:+request exwm--connection (make-instance 'xcb:ChangeWindowAttributes diff --git a/exwm-input.el b/exwm-input.el index a19331431d..4acacdf572 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -86,7 +86,6 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") "Update input focus." (when (and (frame-parameter nil 'exwm-window-id) ;e.g. emacsclient frame (eq (current-buffer) (window-buffer))) ;e.g. `with-temp-buffer' - (exwm--log "EXWM-INPUT--UPDATE-FOCUS") (if (eq major-mode 'exwm-mode) (progn (exwm--log "Set focus ID to #x%x" exwm--id) (setq exwm-input--focus-id exwm--id) diff --git a/exwm-manage.el b/exwm-manage.el index 79cff221e0..fc61e50744 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -276,6 +276,8 @@ corresponding buffer.") buffer edges) (xcb:unmarshal obj data) (with-slots (window x y width height border-width) obj + (exwm--log "ConfigureRequest from #x%x @%dx%d%+d%+d, border: %d" + window width height x y border-width) (if (setq buffer (exwm--id->buffer window)) ;; Send client message for managed windows (with-current-buffer buffer @@ -286,6 +288,7 @@ corresponding buffer.") (or exwm--floating-edges (window-inside-absolute-pixel-edges (get-buffer-window))))) + (exwm--log "Reply with ConfigureNotify (edges): %s" edges) (xcb:+request exwm--connection (make-instance 'xcb:SendEvent :propagate 0 :destination window @@ -300,6 +303,7 @@ corresponding buffer.") :height (- (elt edges 3) (elt edges 1)) :border-width 0 :override-redirect 0) exwm--connection)))) + (exwm--log "ConfigureWindow (preserve geometry)") ;; Configure unmanaged windows (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow @@ -317,6 +321,7 @@ corresponding buffer.") "Handle MapRequest event." (let ((obj (make-instance 'xcb:MapRequest))) (xcb:unmarshal obj data) + (exwm--log "MapRequest from #x%x" (slot-value obj 'window)) (exwm-manage--manage-window (slot-value obj 'window)))) (defun exwm-manage--on-UnmapNotify (data synthetic) @@ -324,6 +329,7 @@ corresponding buffer.") (unless synthetic (let ((obj (make-instance 'xcb:UnmapNotify))) (xcb:unmarshal obj data) + (exwm--log "UnmapNotify from #x%x" (slot-value obj 'window)) (exwm-manage--unmanage-window (slot-value obj 'window) t)))) (defun exwm-manage--on-DestroyNotify (data synthetic) @@ -331,6 +337,7 @@ corresponding buffer.") (unless synthetic (let ((obj (make-instance 'xcb:DestroyNotify))) (xcb:unmarshal obj data) + (exwm--log "DestroyNotify from #x%x" (slot-value obj 'window)) (exwm-manage--unmanage-window (slot-value obj 'window))))) (defun exwm-manage--init () -- cgit 1.4.1 From 04e426961736c67046fa3809fc14f1ac027dae77 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 12 Aug 2015 18:09:35 +0800 Subject: Improve input focus switch mechanism This commit should fix most input focus bugs (especially those related to floating windows). The actual settings of input focus are delayed to exclude redundant event. Dead code since this commit is removed. This commit also fixes a bug for non-floating windows converted form floating state. The workaround for `ido-mode` is also improved to properly handle `exwm-mode` buffers. --- exwm-floating.el | 16 ++++------ exwm-input.el | 95 ++++++++++++++++++++++++++++--------------------------- exwm-manage.el | 11 ++----- exwm-workspace.el | 3 -- exwm.el | 18 ++++------- 5 files changed, 62 insertions(+), 81 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 7888fd2d0b..e1d8738270 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -179,12 +179,7 @@ exwm--floating-frame frame) (set-window-buffer window (current-buffer)) ;this changes current buffer (set-window-dedicated-p window t)) - (with-current-buffer (exwm--id->buffer id) - ;; Some window should not get input focus on creation - ;; FIXME: other conditions? - (unless (memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY exwm-window-type) - (x-focus-frame exwm--floating-frame) - (exwm-input--set-focus id))))) + (select-window window))) (defun exwm-floating--unset-floating (id) "Make window ID non-floating." @@ -212,11 +207,12 @@ (set-window-dedicated-p (frame-first-window exwm--floating-frame) nil) (delete-frame exwm--floating-frame))) ;remove the floating frame (with-current-buffer buffer - (setq exwm--floating-frame nil + (setq window-size-fixed nil + exwm--floating-frame nil exwm--frame exwm-workspace--current)) - (select-frame exwm-workspace--current t) - (set-window-buffer nil buffer) - (exwm-input--set-focus id))) + (let ((window (frame-selected-window exwm-workspace--current))) + (set-window-buffer window buffer) + (select-window window)))) (defun exwm-floating-toggle-floating () "Toggle the current window between floating and non-floating states." diff --git a/exwm-input.el b/exwm-input.el index 4acacdf572..454521f094 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -57,8 +57,6 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (defun exwm-input--set-focus (id) "Set input focus to window ID in a proper way." (with-current-buffer (exwm--id->buffer id) - (exwm--log "Set focus ID to #x%x" id) - (setq exwm-input--focus-id id) (if (and (not exwm--hints-input) (memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols)) (progn @@ -79,45 +77,53 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") :time xcb:Time:CurrentTime))) (xcb:flush exwm--connection))) -(defvar exwm-input--focus-id xcb:Window:None - "The window that is theoretically focused.") +(defvar exwm-input--focus-buffer nil "The buffer to be focused.") +(defvar exwm-input--redirected nil + "Indicate next update on buffer list is actually a result of redirection.") +(defvar exwm-input--timer nil "Currently running timer.") + +(defun exwm-input--on-buffer-list-update () + "Run in buffer-list-update-hook to track input focus." + (let ((frame (selected-frame)) + (buffer (current-buffer))) + (when (and (not (minibufferp buffer)) + (frame-parameter frame 'exwm-window-id) ;e.g. emacsclient frame + (eq buffer (window-buffer))) ;e.g. `with-temp-buffer' + (unless (and exwm-input--redirected + exwm-input--focus-buffer + (with-current-buffer exwm-input--focus-buffer + exwm--floating-frame)) + (setq exwm-input--focus-buffer buffer) + (when exwm-input--timer (cancel-timer exwm-input--timer)) + (setq exwm-input--timer + (run-with-timer 0.01 nil 'exwm-input--update-focus))) + (setq exwm-input--redirected nil)))) + +(defun exwm-input--on-focus-in () + "Run in focus-in-hook to remove redirected focus on frame." + (let ((frame (selected-frame))) + (when (and (frame-parameter frame 'exwm-window-id) + (not (memq frame exwm-workspace--list))) + (setq exwm-input--redirected t)))) (defun exwm-input--update-focus () "Update input focus." - (when (and (frame-parameter nil 'exwm-window-id) ;e.g. emacsclient frame - (eq (current-buffer) (window-buffer))) ;e.g. `with-temp-buffer' - (if (eq major-mode 'exwm-mode) - (progn (exwm--log "Set focus ID to #x%x" exwm--id) - (setq exwm-input--focus-id exwm--id) - (when exwm--floating-frame - (if (eq (selected-frame) exwm--floating-frame) - ;; Cancel the possible input focus redirection - (progn - (exwm--log "Cancel input focus redirection on %s" - exwm--floating-frame) - (redirect-frame-focus exwm--floating-frame nil)) - ;; Focus the floating frame - (exwm--log "Focus on floating frame %s" - exwm--floating-frame) - (x-focus-frame exwm--floating-frame))) - ;; Finally focus the window - (when (exwm--id->buffer exwm-input--focus-id) - (exwm-input--set-focus exwm-input--focus-id))) - (let ((buffer (exwm--id->buffer exwm-input--focus-id))) - (when (and buffer (eq (selected-frame) exwm-workspace--current)) - (with-current-buffer buffer - (exwm--log "Set focus ID to #x%x" xcb:Window:None) - (setq exwm-input--focus-id xcb:Window:None) - (if exwm--floating-frame - (unless (active-minibuffer-window) - ;; Redirect input focus to the workspace frame - (exwm--log "Redirect input focus (%s => %s)" - exwm--floating-frame exwm-workspace--current) - (redirect-frame-focus exwm--floating-frame - exwm-workspace--current)) - ;; Focus the workspace frame - (exwm--log "Focus on workspace %s" exwm-workspace--current) - (x-focus-frame exwm-workspace--current)))))))) + (when exwm-input--focus-buffer + (with-current-buffer exwm-input--focus-buffer + (exwm--log "Set focus on %s" exwm-input--focus-buffer) + (setq exwm-input--focus-buffer nil) + (if (eq major-mode 'exwm-mode) + (progn + (when exwm--floating-frame + (redirect-frame-focus exwm--floating-frame nil) + (select-frame-set-input-focus exwm--floating-frame t)) + (exwm-input--set-focus exwm--id)) + (select-frame-set-input-focus exwm-workspace--current t) + (dolist (pair exwm--id-buffer-alist) + (with-current-buffer (cdr pair) + (when (and exwm--floating-frame + (eq exwm--frame exwm-workspace--current)) + (redirect-frame-focus exwm--floating-frame exwm--frame)))))))) (defun exwm-input--finish-key-sequence () "Mark the end of a key sequence (with the aid of `pre-command-hook')." @@ -169,12 +175,8 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") ;; Resize (exwm-floating--start-moveresize event)) (t - ;; Click to focus - (unless (and (boundp 'exwm--id) (= event exwm--id)) - (with-current-buffer (exwm--id->buffer event) - (select-frame-set-input-focus (or exwm--floating-frame - exwm--frame)) - (select-window (get-buffer-window nil 'visible)))) + (select-window (get-buffer-window (exwm--id->buffer event) + 'visible)) ;; The event should be replayed (setq mode xcb:Allow:ReplayPointer)))) (xcb:+request exwm--connection @@ -246,7 +248,6 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") ;; (when (and keysym ;; (setq event (xcb:keysyms:keysym->event keysym state)) ;; (or exwm-input--during-key-sequence -;; (= exwm-input--focus-id xcb:Window:None) ;; (setq window (active-minibuffer-window)) ;; (eq event ?\C-c) ;mode-specific key ;; (memq event exwm-input--global-prefix-keys) @@ -273,7 +274,6 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (if (and keysym (setq event (xcb:keysyms:keysym->event keysym state)) (or exwm-input--during-key-sequence - (= exwm-input--focus-id xcb:Window:None) (setq minibuffer-window (active-minibuffer-window)) (eq event ?\C-c) ;mode-specific key (memq event exwm-input--global-prefix-keys) @@ -466,7 +466,8 @@ SIMULATION-KEYS is a list of alist (key-sequence1 . key-sequence2)." ;; `pre-command-hook' marks the end of a key sequence (existing or not) (add-hook 'pre-command-hook 'exwm-input--finish-key-sequence) ;; Update focus when buffer list updates - (add-hook 'buffer-list-update-hook 'exwm-input--update-focus) + (add-hook 'buffer-list-update-hook 'exwm-input--on-buffer-list-update) + (add-hook 'focus-in-hook 'exwm-input--on-focus-in) ;; Update prefix keys for global keys (exwm-input--update-global-prefix-keys)) diff --git a/exwm-manage.el b/exwm-manage.el index fc61e50744..d1736ff9a4 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -183,15 +183,8 @@ corresponding buffer.") (let ((floating exwm--floating-frame)) (kill-buffer) (when floating - (if (eq 'exwm-mode - (with-current-buffer - (window-buffer - (frame-first-window exwm-workspace--current)) - major-mode)) - ;; Input focus is to be set on a window - (x-focus-frame exwm-workspace--current) - ;; Set input focus on a frame - (select-frame-set-input-focus exwm-workspace--current)))))))) + (select-window + (frame-selected-window exwm-workspace--current)))))))) (defun exwm-manage--scan () "Search for existing windows and try to manage them." diff --git a/exwm-workspace.el b/exwm-workspace.el index bcac917023..09112d2d65 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -188,9 +188,6 @@ The optional FORCE option is for internal use only." ;; Move the window itself (bury-buffer) (exwm-layout--hide id) - ;; Force update input focus - (setq exwm-input--focus-id xcb:Window:None) - (exwm-input--update-focus) (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow :window id diff --git a/exwm.el b/exwm.el index 7e909cdef6..989bf88156 100644 --- a/exwm.el +++ b/exwm.el @@ -195,15 +195,9 @@ (defun exwm-reset () "Reset window to standard state: non-fullscreen, line-mode." (interactive) - (unless (frame-parameter nil 'exwm-window-id) - ;; Move focus away form a non-EXWM frame - (x-focus-frame exwm-workspace--current)) (with-current-buffer (window-buffer) (when (eq major-mode 'exwm-mode) (when exwm--fullscreen (exwm-layout-unset-fullscreen)) - ;; Force update input focus - (setq exwm-input--focus-id xcb:Window:None) - (exwm-input--update-focus) ;; Force refresh (exwm-layout--refresh) (exwm-input-grab-keyboard)))) @@ -709,12 +703,12 @@ (defun exwm--ido-buffer-window-other-frame (orig-fun buffer) "Wrapper for `ido-buffer-window-other-frame' to exclude invisible windows." - (let* ((window (funcall orig-fun buffer)) - (frame (window-frame window))) - ;; Exclude windows on other workspaces - (unless (and (memq frame exwm-workspace--list) - (not (eq frame exwm-workspace--current))) - window))) + (with-current-buffer buffer + (if (eq major-mode 'exwm-mode) + ;; `ido-mode' works well with `exwm-mode' buffers + (funcall orig-fun buffer) + ;; Other buffers should be selected within the same workspace + (get-buffer-window buffer exwm-workspace--current)))) (defun exwm--fix-ido-buffer-window-other-frame () "Fix `ido-buffer-window-other-frame'." -- cgit 1.4.1 From 35560a49d6c03f96c1f62bfee72ee667cffadb9e Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 13 Aug 2015 07:54:19 +0800 Subject: Minor fixes for emacsclient --- exwm-input.el | 4 ++-- exwm.el | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 454521f094..b9a5f206e7 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -175,8 +175,8 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") ;; Resize (exwm-floating--start-moveresize event)) (t - (select-window (get-buffer-window (exwm--id->buffer event) - 'visible)) + ;; Click to focus + (select-window (get-buffer-window (exwm--id->buffer event) t)) ;; The event should be replayed (setq mode xcb:Allow:ReplayPointer)))) (xcb:+request exwm--connection diff --git a/exwm.el b/exwm.el index 989bf88156..d33ab26190 100644 --- a/exwm.el +++ b/exwm.el @@ -590,6 +590,7 @@ (if (not (eq 'x (framep (or frame (selected-frame))))) (exwm--log "Not running under X environment") (unless exwm--connection + (exwm-enable 'undo) ;never initialize again (setq exwm--connection (xcb:connect-to-socket)) (set-process-query-on-exit-flag (slot-value exwm--connection 'process) nil) ;prevent query message on exit @@ -604,7 +605,6 @@ ;; Other window manager is running (progn (xcb:disconnect exwm--connection) (setq exwm--connection nil) - (exwm-enable 'undo) (exwm--log "Other window manager detected")) ;; Initialize ICCCM/EWMH support ;; (xcb:icccm:init exwm--connection) @@ -694,11 +694,11 @@ (defun exwm-enable (&optional undo) "Enable/Disable EXWM" - (setq frame-resize-pixelwise t) ;mandatory; before init (if (eq undo 'undo) (progn (remove-hook 'window-setup-hook 'exwm-init) (remove-hook 'after-make-frame-functions 'exwm-init)) - (add-hook 'window-setup-hook 'exwm-init t) ;for Emacs + (setq frame-resize-pixelwise t) ;mandatory; before init + (add-hook 'window-setup-hook 'exwm-init t) ;for Emacs (add-hook 'after-make-frame-functions 'exwm-init t))) ;for Emacs Client (defun exwm--ido-buffer-window-other-frame (orig-fun buffer) -- cgit 1.4.1 From 07e59e0429c4b13a5036e9a207e37fc02135f599 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 13 Aug 2015 12:02:44 +0800 Subject: Fix multi-screen bugs * RandR module is now made optional; users can enable it with `exwm-randr-enable`. * Correct the calculation of sizes/coordinates at various places. * Input focus is now tracked with (Emacs) window instead of buffer since the latter can be ambiguous in multi-screen settings. --- exwm-floating.el | 14 +++++++++----- exwm-input.el | 23 +++++++++++++---------- exwm-layout.el | 19 ++++++++++--------- exwm-manage.el | 13 ++++++++++--- exwm-randr.el | 13 ++++++++++++- exwm.el | 2 -- 6 files changed, 54 insertions(+), 30 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index e1d8738270..06d6fa505c 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -39,8 +39,9 @@ (defun exwm-floating--set-floating (id) "Make window ID floating." (interactive) - (when (get-buffer-window (exwm--id->buffer id)) ;window in non-floating state - (set-window-buffer (selected-window) (other-buffer))) ;hide it first + (let ((window (get-buffer-window (exwm--id->buffer id)))) + (when window ;window in non-floating state + (set-window-buffer window (other-buffer)))) ;hide it first (let* ((original-frame (with-current-buffer (exwm--id->buffer id) (if (and exwm-transient-for (exwm--id->buffer exwm-transient-for)) @@ -78,8 +79,8 @@ (exwm-workspace--update-switch-history))) ;; Fix illegal parameters ;; FIXME: check normal hints restrictions - (let* ((display-width (x-display-pixel-width)) - (display-height (- (x-display-pixel-height) + (let* ((display-width (frame-pixel-width original-frame)) + (display-height (- (frame-pixel-height original-frame) (window-pixel-height (minibuffer-window original-frame)) (* 2 (window-mode-line-height)) @@ -373,6 +374,8 @@ "Perform move/resize." (when exwm-floating--moveresize-calculate (let ((obj (make-instance 'xcb:MotionNotify)) + (frame-x (or (frame-parameter exwm-workspace--current 'exwm-x) 0)) + (frame-y (or (frame-parameter exwm-workspace--current 'exwm-y) 0)) result) (xcb:unmarshal obj data) (setq result (funcall exwm-floating--moveresize-calculate @@ -380,7 +383,8 @@ (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window (elt result 0) :value-mask (elt result 1) - :x (elt result 2) :y (elt result 3) + :x (- (elt result 2) frame-x) + :y (- (elt result 3) frame-y) :width (elt result 4) :height (elt result 5))) (xcb:flush exwm--connection)))) diff --git a/exwm-input.el b/exwm-input.el index b9a5f206e7..0ef72325bb 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -77,7 +77,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") :time xcb:Time:CurrentTime))) (xcb:flush exwm--connection))) -(defvar exwm-input--focus-buffer nil "The buffer to be focused.") +(defvar exwm-input--focus-window nil "The (Emacs) window to be focused.") (defvar exwm-input--redirected nil "Indicate next update on buffer list is actually a result of redirection.") (defvar exwm-input--timer nil "Currently running timer.") @@ -85,15 +85,17 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (defun exwm-input--on-buffer-list-update () "Run in buffer-list-update-hook to track input focus." (let ((frame (selected-frame)) + (window (selected-window)) (buffer (current-buffer))) (when (and (not (minibufferp buffer)) (frame-parameter frame 'exwm-window-id) ;e.g. emacsclient frame (eq buffer (window-buffer))) ;e.g. `with-temp-buffer' (unless (and exwm-input--redirected - exwm-input--focus-buffer - (with-current-buffer exwm-input--focus-buffer + exwm-input--focus-window + (with-current-buffer (window-buffer + exwm-input--focus-window) exwm--floating-frame)) - (setq exwm-input--focus-buffer buffer) + (setq exwm-input--focus-window window) (when exwm-input--timer (cancel-timer exwm-input--timer)) (setq exwm-input--timer (run-with-timer 0.01 nil 'exwm-input--update-focus))) @@ -108,22 +110,23 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (defun exwm-input--update-focus () "Update input focus." - (when exwm-input--focus-buffer - (with-current-buffer exwm-input--focus-buffer - (exwm--log "Set focus on %s" exwm-input--focus-buffer) - (setq exwm-input--focus-buffer nil) + (when exwm-input--focus-window + (with-current-buffer (window-buffer exwm-input--focus-window) + (exwm--log "Set focus on %s" exwm-input--focus-window) (if (eq major-mode 'exwm-mode) (progn (when exwm--floating-frame (redirect-frame-focus exwm--floating-frame nil) (select-frame-set-input-focus exwm--floating-frame t)) (exwm-input--set-focus exwm--id)) - (select-frame-set-input-focus exwm-workspace--current t) + (select-frame-set-input-focus (window-frame exwm-input--focus-window) + t) (dolist (pair exwm--id-buffer-alist) (with-current-buffer (cdr pair) (when (and exwm--floating-frame (eq exwm--frame exwm-workspace--current)) - (redirect-frame-focus exwm--floating-frame exwm--frame)))))))) + (redirect-frame-focus exwm--floating-frame exwm--frame))))) + (setq exwm-input--focus-window nil)))) (defun exwm-input--finish-key-sequence () "Mark the end of a key sequence (with the aid of `pre-command-hook')." diff --git a/exwm-layout.el b/exwm-layout.el index f2d4eb0cbe..a6b450f7da 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -111,8 +111,9 @@ xcb:ConfigWindow:Width xcb:ConfigWindow:Height) :x 0 :y 0 - :width (x-display-pixel-width) - :height (x-display-pixel-height)))) + :width (frame-pixel-width exwm-workspace--current) + :height (frame-pixel-height + exwm-workspace--current)))) (xcb:flush exwm--connection)) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow @@ -122,8 +123,8 @@ xcb:ConfigWindow:Width xcb:ConfigWindow:Height) :x 0 :y 0 - :width (x-display-pixel-width) - :height (x-display-pixel-height))) + :width (frame-pixel-width exwm-workspace--current) + :height (frame-pixel-height exwm-workspace--current))) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_STATE :window exwm--id @@ -168,11 +169,11 @@ (if (frame-parameter frame 'exwm-window-id) ;; Refresh a floating frame (progn - (cl-assert (eq major-mode 'exwm-mode)) - (let ((window (frame-first-window frame))) - (with-current-buffer (window-buffer window) - (exwm--log "Refresh floating window #x%x" exwm--id) - (exwm-layout--show exwm--id window)))) + (when (eq major-mode 'exwm-mode) + (let ((window (frame-first-window frame))) + (with-current-buffer (window-buffer window) + (exwm--log "Refresh floating window #x%x" exwm--id) + (exwm-layout--show exwm--id window))))) ;; Other frames (e.g. terminal/graphical frame of emacsclient) ;; We shall bury all `exwm-mode' buffers in this case (unless placeholder ;create the *scratch* buffer if it's killed diff --git a/exwm-manage.el b/exwm-manage.el index d1736ff9a4..4f11874d3a 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -98,8 +98,14 @@ corresponding buffer.") :window id :value-mask (logior xcb:ConfigWindow:X xcb:ConfigWindow:Y) - :x (/ (- (x-display-pixel-width) width) 2) - :y (/ (- (x-display-pixel-height) height) 2))))) + :x (/ (- (frame-pixel-width + exwm-workspace--current) + width) + 2) + :y (/ (- (frame-pixel-height + exwm-workspace--current) + height) + 2))))) (xcb:flush exwm--connection) (setq kill-buffer-query-functions nil) (setq exwm--id-buffer-alist (assq-delete-all id exwm--id-buffer-alist)) @@ -277,7 +283,8 @@ corresponding buffer.") (setq edges (if exwm--fullscreen (list 0 0 - (x-display-pixel-width) (x-display-pixel-height)) + (frame-pixel-width exwm-workspace--current) + (frame-pixel-height exwm-workspace--current)) (or exwm--floating-edges (window-inside-absolute-pixel-edges (get-buffer-window))))) diff --git a/exwm-randr.el b/exwm-randr.el index c9ad1ce6ae..cd40fb43bd 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -26,8 +26,11 @@ ;; tools such as xrandr(1) to properly configure RandR first. This dependency ;; may be removed in the future, but more work is needed before that. -;; To use this module, first set `exwm-randr-workspace-output-plist': +;; To use this module, first load/enable it and properly configure the variable +;; `exwm-randr-workspace-output-plist': +;; (require 'exwm-randr) ;; (setq exwm-randr-workspace-output-plist '(0 "VGA1")) +;; (exwm-randr-enable) ;; Then configure RandR with 'xrandr': ;; $ xrandr --output VGA1 --left-of LVDS1 --auto ;; With above lines, workspace 0 should be assigned to the output named "VGA1", @@ -82,6 +85,8 @@ (setq geometry default-geometry output nil)) (set-frame-parameter frame 'exwm-randr-output output) + (set-frame-parameter frame 'exwm-x (elt geometry 0)) + (set-frame-parameter frame 'exwm-y (elt geometry 1)) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window (frame-parameter frame 'exwm-outer-id) @@ -108,9 +113,11 @@ (exwm-randr--refresh) (xcb:+event exwm--connection 'xcb:randr:ScreenChangeNotify (lambda (data synthetic) + (exwm--log "(RandR) ScreenChangeNotify") (exwm-randr--refresh))) ;; (xcb:+event exwm--connection 'xcb:randr:Notify ;; (lambda (data synthetic) + ;; (exwm--log "(RandR) Notify") ;; (exwm-randr--refresh))) (xcb:+request exwm--connection (make-instance 'xcb:randr:SelectInput @@ -124,6 +131,10 @@ )) (xcb:flush exwm--connection))))) +(defun exwm-randr-enable () + "Enable RandR support for EXWM." + (add-hook 'exwm-init-hook 'exwm-randr--init)) + (provide 'exwm-randr) diff --git a/exwm.el b/exwm.el index d33ab26190..7dffa87aec 100644 --- a/exwm.el +++ b/exwm.el @@ -144,7 +144,6 @@ (require 'exwm-floating) (require 'exwm-manage) (require 'exwm-input) -(require 'exwm-randr) (defvar exwm-debug-on nil "Non-nil to turn on debug for EXWM.") @@ -616,7 +615,6 @@ (exwm-floating--init) (exwm-manage--init) (exwm-input--init) - (exwm-randr--init) (exwm--unlock) ;; Manage exiting windows (exwm-manage--scan) -- cgit 1.4.1 From dba43b018d791f59f0462b56a46a4eced2d8a3c1 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 13 Aug 2015 15:33:02 +0800 Subject: Fix input focus stealing This was fixed in b755296 but broken by 04e4269. --- exwm-input.el | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 0ef72325bb..6b41fff5c7 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -112,20 +112,22 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") "Update input focus." (when exwm-input--focus-window (with-current-buffer (window-buffer exwm-input--focus-window) - (exwm--log "Set focus on %s" exwm-input--focus-window) (if (eq major-mode 'exwm-mode) (progn (when exwm--floating-frame (redirect-frame-focus exwm--floating-frame nil) (select-frame-set-input-focus exwm--floating-frame t)) + (exwm--log "Set focus on #x%x" exwm--id) (exwm-input--set-focus exwm--id)) - (select-frame-set-input-focus (window-frame exwm-input--focus-window) - t) - (dolist (pair exwm--id-buffer-alist) - (with-current-buffer (cdr pair) - (when (and exwm--floating-frame - (eq exwm--frame exwm-workspace--current)) - (redirect-frame-focus exwm--floating-frame exwm--frame))))) + (when (eq (selected-window) exwm-input--focus-window) + (exwm--log "Focus on %s" exwm-input--focus-window) + (select-frame-set-input-focus (window-frame exwm-input--focus-window) + t) + (dolist (pair exwm--id-buffer-alist) + (with-current-buffer (cdr pair) + (when (and exwm--floating-frame + (eq exwm--frame exwm-workspace--current)) + (redirect-frame-focus exwm--floating-frame exwm--frame)))))) (setq exwm-input--focus-window nil)))) (defun exwm-input--finish-key-sequence () -- cgit 1.4.1 From 7bfd429d5252e8df43d1b19b836a6c4970290fa0 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 14 Aug 2015 17:46:43 +0800 Subject: Honor `value-mask` field in ConfigureRequest event Some applications (e.g. JNLP) don't set correct values for fields not mentioned in `value-mask`. This commit corrects this bug together with another Java AWT specific problem. --- exwm-manage.el | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index 4f11874d3a..ffe21e23e7 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -71,7 +71,8 @@ corresponding buffer.") (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG exwm-window-type) (memq xcb:Atom:_NET_WM_WINDOW_TYPE_NORMAL exwm-window-type))) ;; For Java applications - (and exwm-instance-name + (and (memq xcb:Atom:_NET_WM_WINDOW_TYPE_NORMAL exwm-window-type) + exwm-instance-name (string-prefix-p "sun-awt-X11-" exwm-instance-name) (not (string-suffix-p "XFramePeer" exwm-instance-name)))) (exwm--log "No need to manage #x%x" id) @@ -274,9 +275,11 @@ corresponding buffer.") (let ((obj (make-instance 'xcb:ConfigureRequest)) buffer edges) (xcb:unmarshal obj data) - (with-slots (window x y width height border-width) obj - (exwm--log "ConfigureRequest from #x%x @%dx%d%+d%+d, border: %d" - window width height x y border-width) + (with-slots (stack-mode window sibling x y width height border-width + value-mask) + obj + (exwm--log "ConfigureRequest from #x%x (#x%x) @%dx%d%+d%+d, border: %d" + value-mask window width height x y border-width) (if (setq buffer (exwm--id->buffer window)) ;; Send client message for managed windows (with-current-buffer buffer @@ -308,13 +311,10 @@ corresponding buffer.") (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window window - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height - xcb:ConfigWindow:BorderWidth) + :value-mask value-mask :x x :y y :width width :height height - :border-width border-width))))) + :border-width border-width + :sibling sibling :stack-mode stack-mode))))) (xcb:flush exwm--connection)) (defun exwm-manage--on-MapRequest (data synthetic) -- cgit 1.4.1 From d998b42b89bc9e248b6d2250c56ddd17ec4b17cf Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 16 Aug 2015 19:00:42 +0800 Subject: Provide hooks run when the floating state of a window changes (close #28) --- exwm-floating.el | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 06d6fa505c..bc6b1a246a 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -36,6 +36,11 @@ (defvar exwm-floating-border-color "blue" "Border color of the floating window.") +(defvar exwm-floating-setup-hook nil + "Normal hook run when a window has been made floating.") +(defvar exwm-floating-exit-hook nil + "Normal hook run when a window has exited floating state.") + (defun exwm-floating--set-floating (id) "Make window ID floating." (interactive) @@ -180,7 +185,8 @@ exwm--floating-frame frame) (set-window-buffer window (current-buffer)) ;this changes current buffer (set-window-dedicated-p window t)) - (select-window window))) + (select-window window)) + (run-hooks 'exwm-floating-setup-hook)) (defun exwm-floating--unset-floating (id) "Make window ID non-floating." @@ -213,7 +219,8 @@ exwm--frame exwm-workspace--current)) (let ((window (frame-selected-window exwm-workspace--current))) (set-window-buffer window buffer) - (select-window window)))) + (select-window window))) + (run-hooks 'exwm-floating-exit-hook)) (defun exwm-floating-toggle-floating () "Toggle the current window between floating and non-floating states." -- cgit 1.4.1 From 15ad591d978836b753839014f3c7ce67111763c1 Mon Sep 17 00:00:00 2001 From: Philip Date: Sun, 16 Aug 2015 18:05:47 +0000 Subject: Fix bug when moving a window to the current workspace * exwm-workspace.el (exwm-workspace-move-window): Run reparenting code when moving a window to the current workspace. --- exwm-workspace.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 09112d2d65..00f73261ec 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -168,9 +168,9 @@ The optional FORCE option is for internal use only." (unless id (setq id (exwm--buffer->id (window-buffer)))) (unless (and (<= 0 index) (< index exwm-workspace-number)) (user-error "[EXWM] Workspace index out of range: %d" index)) - (when (/= exwm-workspace-current-index index) + (with-current-buffer (exwm--id->buffer id) (let ((frame (elt exwm-workspace--list index))) - (with-current-buffer (exwm--id->buffer id) + (when (not (equal exwm--frame frame)) (setq exwm--frame frame) (exwm-workspace-rename-buffer (concat " " (replace-regexp-in-string "^\\s-*" "" (buffer-name)))) -- cgit 1.4.1 From 43b00d2f0fefd39ceddb62be792634aabc9e23ca Mon Sep 17 00:00:00 2001 From: Philip Date: Mon, 17 Aug 2015 06:40:43 +0000 Subject: fix buffer renaming per https://github.com/ch11ng/exwm/pull/30 --- exwm-workspace.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 00f73261ec..7ac1fecb75 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -171,9 +171,11 @@ The optional FORCE option is for internal use only." (with-current-buffer (exwm--id->buffer id) (let ((frame (elt exwm-workspace--list index))) (when (not (equal exwm--frame frame)) + (let ((name (replace-regexp-in-string "^\\s-*" "" (buffer-name)))) + (exwm-workspace-rename-buffer (if (= index exwm-workspace-current-index) + name + (concat " " name)))) (setq exwm--frame frame) - (exwm-workspace-rename-buffer - (concat " " (replace-regexp-in-string "^\\s-*" "" (buffer-name)))) (if exwm--floating-frame ;; Move the floating frame is enough (progn -- cgit 1.4.1 From d1806e91888cc2d56e252299d07cb6af05a3e9d0 Mon Sep 17 00:00:00 2001 From: Philip Date: Sun, 16 Aug 2015 18:53:04 +0000 Subject: Improve code robustness. * exwm-layout.el (exwm-layout-unset-fullscreen) (exwm-layout-set-fullscreen): Use `user-error' rather than `cl-assert'. * exwm-input.el (exwm-input--set-focus): Silently accept unknown ids. (exwm-input--grab-keyboard) (exwm-input--release-keyboard): Silently ignore calls for windows that have no buffer. * exwm-manage.el (exwm-manage--kill-client): Don't throw error when trying to kill a vanished window. --- exwm-input.el | 75 +++++++++++++++++++++++++++++----------------------------- exwm-layout.el | 6 +++-- exwm-manage.el | 7 +++--- 3 files changed, 45 insertions(+), 43 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 6b41fff5c7..760e5c96dc 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -56,26 +56,27 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (defun exwm-input--set-focus (id) "Set input focus to window ID in a proper way." - (with-current-buffer (exwm--id->buffer id) - (if (and (not exwm--hints-input) - (memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols)) - (progn - (exwm--log "Focus on #x%x with WM_TAKE_FOCUS" id) - (xcb:+request exwm--connection - (make-instance 'xcb:icccm:SendEvent - :destination id - :event (xcb:marshal - (make-instance 'xcb:icccm:WM_TAKE_FOCUS - :window id - :time - exwm-input--timestamp) - exwm--connection)))) - (exwm--log "Focus on #x%x with SetInputFocus" id) - (xcb:+request exwm--connection - (make-instance 'xcb:SetInputFocus - :revert-to xcb:InputFocus:Parent :focus id - :time xcb:Time:CurrentTime))) - (xcb:flush exwm--connection))) + (when (exwm--id->buffer id) + (with-current-buffer (exwm--id->buffer id) + (if (and (not exwm--hints-input) + (memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols)) + (progn + (exwm--log "Focus on #x%x with WM_TAKE_FOCUS" id) + (xcb:+request exwm--connection + (make-instance 'xcb:icccm:SendEvent + :destination id + :event (xcb:marshal + (make-instance 'xcb:icccm:WM_TAKE_FOCUS + :window id + :time + exwm-input--timestamp) + exwm--connection)))) + (exwm--log "Focus on #x%x with SetInputFocus" id) + (xcb:+request exwm--connection + (make-instance 'xcb:SetInputFocus + :revert-to xcb:InputFocus:Parent :focus id + :time xcb:Time:CurrentTime))) + (xcb:flush exwm--connection)))) (defvar exwm-input--focus-window nil "The (Emacs) window to be focused.") (defvar exwm-input--redirected nil @@ -311,27 +312,27 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (defun exwm-input--grab-keyboard (&optional id) "Grab all key events on window ID." (unless id (setq id (exwm--buffer->id (window-buffer)))) - (cl-assert id) - (when (xcb:+request-checked+request-check exwm--connection - (make-instance 'xcb:GrabKey - :owner-events 0 :grab-window id - :modifiers xcb:ModMask:Any - :key xcb:Grab:Any - :pointer-mode xcb:GrabMode:Async - :keyboard-mode xcb:GrabMode:Async)) - (exwm--log "Failed to grab keyboard for #x%x" id)) - (setq exwm--on-KeyPress 'exwm-input--on-KeyPress-line-mode)) + (when id + (when (xcb:+request-checked+request-check exwm--connection + (make-instance 'xcb:GrabKey + :owner-events 0 :grab-window id + :modifiers xcb:ModMask:Any + :key xcb:Grab:Any + :pointer-mode xcb:GrabMode:Async + :keyboard-mode xcb:GrabMode:Async)) + (exwm--log "Failed to grab keyboard for #x%x" id)) + (setq exwm--on-KeyPress 'exwm-input--on-KeyPress-line-mode))) (defun exwm-input--release-keyboard (&optional id) "Ungrab all key events on window ID." (unless id (setq id (exwm--buffer->id (window-buffer)))) - (cl-assert id) - (when (xcb:+request-checked+request-check exwm--connection - (make-instance 'xcb:UngrabKey - :key xcb:Grab:Any :grab-window id - :modifiers xcb:ModMask:Any)) - (exwm--log "Failed to release keyboard for #x%x" id)) - (setq exwm--on-KeyPress 'exwm-input--on-KeyPress-char-mode)) + (when id + (when (xcb:+request-checked+request-check exwm--connection + (make-instance 'xcb:UngrabKey + :key xcb:Grab:Any :grab-window id + :modifiers xcb:ModMask:Any)) + (exwm--log "Failed to release keyboard for #x%x" id)) + (setq exwm--on-KeyPress 'exwm-input--on-KeyPress-char-mode))) (defun exwm-input-grab-keyboard (&optional id) "Switch to line-mode." diff --git a/exwm-layout.el b/exwm-layout.el index a6b450f7da..1ffb3b93fc 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -93,6 +93,8 @@ "Make window ID fullscreen." (interactive) (with-current-buffer (if id (exwm--id->buffer id) (window-buffer)) + (when exwm--fullscreen + (user-error "Already in full-screen mode.")) ;; Set the floating frame fullscreen first when the client is floating (when exwm--floating-frame (let* ((outer-id (frame-parameter exwm--floating-frame 'exwm-outer-id)) @@ -130,7 +132,6 @@ :window exwm--id :data (vector xcb:Atom:_NET_WM_STATE_FULLSCREEN))) (xcb:flush exwm--connection) - (cl-assert (not exwm--fullscreen)) (setq exwm--fullscreen t) (exwm-input-release-keyboard))) @@ -138,6 +139,8 @@ "Restore window from fullscreen state." (interactive) (with-current-buffer (if id (exwm--id->buffer id) (window-buffer)) + (unless exwm--fullscreen + (user-error "Not in full-screen mode.")) ;; Restore the floating frame if the client is floating (when exwm--floating-frame (xcb:+request exwm--connection @@ -156,7 +159,6 @@ (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_STATE :window exwm--id :data [])) (xcb:flush exwm--connection) - (cl-assert exwm--fullscreen) (setq exwm--fullscreen nil) (exwm-input-grab-keyboard))) diff --git a/exwm-manage.el b/exwm-manage.el index ffe21e23e7..3274d65c17 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -260,10 +260,9 @@ corresponding buffer.") "Kill an X client." (interactive) (unless id (setq id (exwm--buffer->id (current-buffer)))) - (let ((pid (slot-value - (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:ewmh:get-_NET_WM_PID :window id)) - 'value))) + (let* ((response (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:ewmh:get-_NET_WM_PID :window id))) + (pid (and response (slot-value response 'value)))) (if pid (signal-process pid 'SIGKILL) (xcb:+request exwm--connection -- cgit 1.4.1 From 5210e13e7c95d0d8f94c657efa13e71fad4ef818 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 24 Aug 2015 19:01:14 +0800 Subject: Only manage windows mapped as the direct children of root window (close #38) Sometimes Emacs create child windows of virtual roots. This commit ensures EXWM will not manage them. --- exwm-manage.el | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index 3274d65c17..0fde4d4061 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -278,7 +278,7 @@ corresponding buffer.") value-mask) obj (exwm--log "ConfigureRequest from #x%x (#x%x) @%dx%d%+d%+d, border: %d" - value-mask window width height x y border-width) + window value-mask width height x y border-width) (if (setq buffer (exwm--id->buffer window)) ;; Send client message for managed windows (with-current-buffer buffer @@ -320,8 +320,13 @@ corresponding buffer.") "Handle MapRequest event." (let ((obj (make-instance 'xcb:MapRequest))) (xcb:unmarshal obj data) - (exwm--log "MapRequest from #x%x" (slot-value obj 'window)) - (exwm-manage--manage-window (slot-value obj 'window)))) + (with-slots (parent window) obj + (if (/= exwm--root parent) + (progn (xcb:+request exwm--connection + (make-instance xcb:MapWindow :window window)) + (xcb:flush exwm--connection)) + (exwm--log "MapRequest from #x%x" window) + (exwm-manage--manage-window window))))) (defun exwm-manage--on-UnmapNotify (data synthetic) "Handle UnmapNotify event." -- cgit 1.4.1 From 94bdbfc0da7c8ef14acdc8aa4e73dc1c8fee9700 Mon Sep 17 00:00:00 2001 From: Philip Date: Mon, 24 Aug 2015 19:09:42 +0000 Subject: Avoid using the "no window manager" code in Emacs * exwm.el (exwm--on-ClientMessage): Handle fullscreen requests for frames. (exwm-init): Initialize workspaces after unlocking events. * exwm-workspace.el (exwm-workspace--init): Create frames as invisible, then make them visible only once their OverrideRedirect property has been set. * exwm-randr.el (exwm-randr--refresh): New frame parameter `exwm-geometry'. * exwm-layout.el (exwm-layout--set-frame-fullscreen): New function. The Emacs code is buggy, see https://github.com/ch11ng/exwm/issues/39 https://github.com/ch11ng/exwm/pull/42 --- exwm-layout.el | 28 ++++++++++++++++++++++++++++ exwm-randr.el | 6 ++++++ exwm-workspace.el | 14 ++++++++++---- exwm.el | 18 ++++++++++++++++-- 4 files changed, 60 insertions(+), 6 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index 1ffb3b93fc..735b156e16 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -162,6 +162,34 @@ (setq exwm--fullscreen nil) (exwm-input-grab-keyboard))) +;; This function is superficially similar to `exwm-layout-set-fullscreen', but +;; they do very different things: `exwm-layout--set-frame-fullscreen' resizes a +;; frame to the actual monitor size, `exwm-layout-set-fullscreen' resizes an X +;; window to the frame size. +(defun exwm-layout--set-frame-fullscreen (frame) + "Make frame FRAME fullscreen, with regard to its XRandR output if applicable." + (let ((geometry (or (frame-parameter frame 'exwm-geometry) + (xcb:+request-unchecked+reply + exwm--connection + (make-instance 'xcb:GetGeometry + :drawable exwm--root)) + (make-instance 'xcb:RECTANGLE :x 0 :y 0 + :width (x-display-width) + :height (x-display-height)))) + (id (frame-parameter frame 'exwm-outer-id))) + (with-slots (x y width height) geometry + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window id + :value-mask (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height) + :x x :y y + :width width + :height height)) + (xcb:flush exwm--connection)))) + (defun exwm-layout--refresh () "Refresh layout." (let ((frame (selected-frame)) diff --git a/exwm-randr.el b/exwm-randr.el index cd40fb43bd..6bddb00139 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -85,6 +85,12 @@ (setq geometry default-geometry output nil)) (set-frame-parameter frame 'exwm-randr-output output) + (set-frame-parameter frame 'exwm-geometry + (make-instance 'xcb:RECTANGLE + :x (elt geometry 0) + :y (elt geometry 1) + :width (elt geometry 2) + :height (elt geometry 3))) (set-frame-parameter frame 'exwm-x (elt geometry 0)) (set-frame-parameter frame 'exwm-y (elt geometry 1)) (xcb:+request exwm--connection diff --git a/exwm-workspace.el b/exwm-workspace.el index 7ac1fecb75..8e3839f024 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -231,13 +231,11 @@ The optional FORCE option is for internal use only." (unless (frame-parameter i 'window-id) (setq exwm-workspace--list (delq i exwm-workspace--list))))) (cl-assert (= 1 (length exwm-workspace--list))) - (exwm--make-emacs-idle-for 0.1) ;wait for the frame ready - ;; Configure the existing frame - (set-frame-parameter (car exwm-workspace--list) 'fullscreen 'fullboth) ;; Create remaining frames (dotimes (i (1- exwm-workspace-number)) (nconc exwm-workspace--list - (list (make-frame '((window-system . x) (fullscreen . fullboth)))))) + (list (make-frame '((window-system . x) + (visibility . nil)))))) ;; Configure workspaces (dolist (i exwm-workspace--list) (let ((window-id (string-to-int (frame-parameter i 'window-id))) @@ -256,6 +254,14 @@ The optional FORCE option is for internal use only." :window window-id :value-mask xcb:CW:EventMask :event-mask xcb:EventMask:SubstructureRedirect)))) (xcb:flush exwm--connection) + ;; We have to delay making the frame visible until the + ;; override-redirect flag has been set. + (select-frame-set-input-focus (car exwm-workspace--list)) + (dolist (i exwm-workspace--list) + (set-frame-parameter i 'visibility t) + (lower-frame i) + (set-frame-parameter i 'fullscreen 'fullboth)) + (raise-frame (car exwm-workspace--list)) ;; Handle unexpected frame switch (add-hook 'focus-in-hook 'exwm-workspace--on-focus-in) ;; Switch to the first workspace diff --git a/exwm.el b/exwm.el index 7dffa87aec..a52e11419d 100644 --- a/exwm.el +++ b/exwm.el @@ -441,6 +441,20 @@ (props (list (elt data 1) (elt data 2))) (buffer (exwm--id->buffer id)) props-new) + ;; only support _NET_WM_STATE_FULLSCREEN / _NET_WM_STATE_ADD for frames + (when (and (not buffer) + (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN props) + (= action xcb:ewmh:_NET_WM_STATE_ADD)) + (dolist (f exwm-workspace--list) + (when (equal (frame-parameter f 'exwm-outer-id) id) + (exwm-layout--set-frame-fullscreen f) + (xcb:+request + exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_STATE + :window id + :data (vector + xcb:Atom:_NET_WM_STATE_FULLSCREEN))) + (xcb:flush exwm--connection)))) (when buffer ;ensure it's managed (with-current-buffer buffer ;; _NET_WM_STATE_MODAL @@ -609,14 +623,14 @@ ;; (xcb:icccm:init exwm--connection) (xcb:ewmh:init exwm--connection) (exwm--lock) - (exwm-workspace--init) (exwm--init-icccm-ewmh) (exwm-layout--init) (exwm-floating--init) (exwm-manage--init) (exwm-input--init) (exwm--unlock) - ;; Manage exiting windows + (exwm-workspace--init) + ;; Manage existing windows (exwm-manage--scan) (run-hooks 'exwm-init-hook))))) -- cgit 1.4.1 From 981293f06af320f2929fd0d209eb97e63b4d8e3e Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 26 Aug 2015 15:27:31 +0800 Subject: Replay KeyPress events instead of fake them in line-mode X windows in line-mode receive KeyPress events faked with SendEvent requests previously. This causes many problems including: * Some applications (e.g. xterm) ignore synthetic events completely * KeyPress and KeyRelease evnets arrive disorderly This commit makes EXWM exploiting AllowEvents requests (in ReplayKeyboard mode) to forward KeyPress events to X windows instead. --- exwm-input.el | 77 +++++++++++++++++++---------------------------------------- 1 file changed, 24 insertions(+), 53 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 760e5c96dc..ede635c54e 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -241,61 +241,27 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (defvar exwm-input--temp-line-mode nil "Non-nil indicates it's in temporary line-mode for char-mode.") -;; ;; This implementation has a problem that it also releases queued keys after -;; ;; requesting AllowEvent. The client window will capture unexpected key events -;; ;; in this case. -;; ;; P.S.; to use this implementation, comment out the KeyRelease listener -;; ;; together with this one and make GrabKey in Sync mode. -;; (cl-defmethod exwm-input--on-KeyPress-line-mode ((obj xcb:KeyPress)) -;; "Parse X KeyPress event to Emacs key event and then feed the command loop." -;; (with-slots (detail state) obj -;; (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) -;; event window mode) -;; (when (and keysym -;; (setq event (xcb:keysyms:keysym->event keysym state)) -;; (or exwm-input--during-key-sequence -;; (setq window (active-minibuffer-window)) -;; (eq event ?\C-c) ;mode-specific key -;; (memq event exwm-input--global-prefix-keys) -;; (memq event exwm-input-prefix-keys) -;; (memq event -;; exwm-input--simulation-prefix-keys))) -;; (setq mode xcb:Allow:SyncKeyboard) -;; (unless window (setq exwm-input--during-key-sequence t)) -;; (push event unread-command-events)) -;; (xcb:+request exwm--connection -;; (make-instance 'xcb:AllowEvents -;; :mode (or mode xcb:Allow:ReplayKeyboard) -;; :time xcb:Time:CurrentTime)) -;; (xcb:flush exwm--connection)))) - -;; This implementation has a drawback that some (legacy) applications -;; (e.g. xterm) ignore the synthetic key events, making it only viable for EXWM -;; to work in char-mode in such case. (cl-defmethod exwm-input--on-KeyPress-line-mode ((obj xcb:KeyPress)) "Parse X KeyPress event to Emacs key event and then feed the command loop." (with-slots (detail state) obj (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) - event minibuffer-window) - (if (and keysym - (setq event (xcb:keysyms:keysym->event keysym state)) - (or exwm-input--during-key-sequence - (setq minibuffer-window (active-minibuffer-window)) - (eq event ?\C-c) ;mode-specific key - (memq event exwm-input--global-prefix-keys) - (memq event exwm-input-prefix-keys) - (memq event exwm-input--simulation-prefix-keys))) - ;; Forward key to Emacs frame - (progn (unless minibuffer-window - (setq exwm-input--during-key-sequence t)) - (push event unread-command-events)) - ;; Forward key to window - (xcb:+request exwm--connection - (make-instance 'xcb:SendEvent - :propagate 0 :destination (slot-value obj 'event) - :event-mask xcb:EventMask:NoEvent - :event (xcb:marshal obj exwm--connection))) - (xcb:flush exwm--connection))))) + event minibuffer-window mode) + (when (and keysym + (setq event (xcb:keysyms:keysym->event keysym state)) + (or exwm-input--during-key-sequence + (setq minibuffer-window (active-minibuffer-window)) + (eq event ?\C-c) ;mode-specific key + (memq event exwm-input--global-prefix-keys) + (memq event exwm-input-prefix-keys) + (memq event exwm-input--simulation-prefix-keys))) + (setq mode xcb:Allow:AsyncKeyboard) + (unless minibuffer-window (setq exwm-input--during-key-sequence t)) + (push event unread-command-events)) + (xcb:+request exwm--connection + (make-instance 'xcb:AllowEvents + :mode (or mode xcb:Allow:ReplayKeyboard) + :time xcb:Time:CurrentTime)) + (xcb:flush exwm--connection)))) (cl-defmethod exwm-input--on-KeyPress-char-mode ((obj xcb:KeyPress)) "Handle KeyPress event in char-mode." @@ -307,7 +273,12 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (setq exwm-input--temp-line-mode t exwm-input--during-key-sequence t) (exwm-input--grab-keyboard)) ;grab keyboard temporarily - (push event unread-command-events))))) + (push event unread-command-events)))) + (xcb:+request exwm--connection + (make-instance 'xcb:AllowEvents + :mode xcb:Allow:AsyncKeyboard + :time xcb:Time:CurrentTime)) + (xcb:flush exwm--connection)) (defun exwm-input--grab-keyboard (&optional id) "Grab all key events on window ID." @@ -319,7 +290,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") :modifiers xcb:ModMask:Any :key xcb:Grab:Any :pointer-mode xcb:GrabMode:Async - :keyboard-mode xcb:GrabMode:Async)) + :keyboard-mode xcb:GrabMode:Sync)) (exwm--log "Failed to grab keyboard for #x%x" id)) (setq exwm--on-KeyPress 'exwm-input--on-KeyPress-line-mode))) -- cgit 1.4.1 From b50a6e6dd9f5a34e91fd544e3ead0c81a7217777 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 26 Aug 2015 17:25:21 +0800 Subject: Correct several EWMH properties The following EWMH properties on the root window are corrected in this commit: _NET_VIRTUAL_ROOTS, _NET_WORKAREA and _NET_DESKTOP_VIEWPORT. --- exwm-floating.el | 8 ++++++-- exwm-layout.el | 33 ++++++++++++++++----------------- exwm-randr.el | 51 +++++++++++++++++++++++++++------------------------ exwm-workspace.el | 8 ++++++++ exwm.el | 10 +--------- 5 files changed, 58 insertions(+), 52 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index bc6b1a246a..8c2a8f4998 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -381,9 +381,13 @@ "Perform move/resize." (when exwm-floating--moveresize-calculate (let ((obj (make-instance 'xcb:MotionNotify)) - (frame-x (or (frame-parameter exwm-workspace--current 'exwm-x) 0)) - (frame-y (or (frame-parameter exwm-workspace--current 'exwm-y) 0)) + (geometry (frame-parameter exwm-workspace--current 'exwm-geometry)) + (frame-x 0) + (frame-y 0) result) + (when geometry + (setq frame-x (slot-value geometry 'x) + frame-y (slot-value geometry 'y))) (xcb:unmarshal obj data) (setq result (funcall exwm-floating--moveresize-calculate (slot-value obj 'root-x) (slot-value obj 'root-y))) diff --git a/exwm-layout.el b/exwm-layout.el index 735b156e16..fe1645f0fb 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -167,28 +167,27 @@ ;; frame to the actual monitor size, `exwm-layout-set-fullscreen' resizes an X ;; window to the frame size. (defun exwm-layout--set-frame-fullscreen (frame) - "Make frame FRAME fullscreen, with regard to its XRandR output if applicable." + "Make frame FRAME fullscreen, with regard to its RandR output if applicable." (let ((geometry (or (frame-parameter frame 'exwm-geometry) - (xcb:+request-unchecked+reply - exwm--connection + (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GetGeometry :drawable exwm--root)) (make-instance 'xcb:RECTANGLE :x 0 :y 0 - :width (x-display-width) - :height (x-display-height)))) - (id (frame-parameter frame 'exwm-outer-id))) + :width (x-display-pixel-width) + :height (x-display-pixel-height)))) + (id (frame-parameter frame 'exwm-outer-id))) (with-slots (x y width height) geometry - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window id - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) - :x x :y y - :width width - :height height)) - (xcb:flush exwm--connection)))) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window id + :value-mask (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height) + :x x :y y + :width width + :height height)) + (xcb:flush exwm--connection)))) (defun exwm-layout--refresh () "Refresh layout." diff --git a/exwm-randr.el b/exwm-randr.el index 6bddb00139..57ad569e22 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -36,9 +36,6 @@ ;; With above lines, workspace 0 should be assigned to the output named "VGA1", ;; staying at the left of other workspaces on the output "LVDS1". -;; Todo: -;; + Update EWMH hints. - ;; References: ;; + RandR (http://www.x.org/archive/X11R7.7/doc/randrproto/randrproto.txt) @@ -50,7 +47,7 @@ (defun exwm-randr--refresh () "Refresh workspaces according to the updated RandR info." - (let (output-plist default-geometry) + (let (geometry output-plist default-geometry workareas viewports) ;; Query all outputs (with-slots (config-timestamp outputs) (xcb:+request-unchecked+reply exwm--connection @@ -72,10 +69,12 @@ (make-instance 'xcb:randr:GetCrtcInfo :crtc crtc :config-timestamp config-timestamp)) - (setq output-plist (plist-put output-plist name - (vector x y width height))) + (setq geometry (make-instance 'xcb:RECTANGLE + :x x :y y + :width width :height height) + output-plist (plist-put output-plist name geometry)) (unless default-geometry ;assume the first output as primary - (setq default-geometry (vector x y width height)))))))) + (setq default-geometry geometry))))))) (cl-assert (<= 2 (length output-plist))) (dotimes (i exwm-workspace-number) (let* ((output (plist-get exwm-randr-workspace-output-plist i)) @@ -85,23 +84,27 @@ (setq geometry default-geometry output nil)) (set-frame-parameter frame 'exwm-randr-output output) - (set-frame-parameter frame 'exwm-geometry - (make-instance 'xcb:RECTANGLE - :x (elt geometry 0) - :y (elt geometry 1) - :width (elt geometry 2) - :height (elt geometry 3))) - (set-frame-parameter frame 'exwm-x (elt geometry 0)) - (set-frame-parameter frame 'exwm-y (elt geometry 1)) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window (frame-parameter frame 'exwm-outer-id) - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) - :x (elt geometry 0) :y (elt geometry 1) - :width (elt geometry 2) :height (elt geometry 3))))) + (set-frame-parameter frame 'exwm-geometry geometry) + (with-slots (x y width height) geometry + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter frame 'exwm-outer-id) + :value-mask (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height) + :x x :y y :width width :height height)) + (setq workareas (nconc workareas (list x y width height)) + viewports (nconc viewports (list x y)))))) + ;; Update _NET_WORKAREA + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WORKAREA + :window exwm--root :data (vconcat workareas))) + ;; Update _NET_DESKTOP_VIEWPORT + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT + :window exwm--root + :data (vconcat viewports))) (xcb:flush exwm--connection))) (defun exwm-randr--init () diff --git a/exwm-workspace.el b/exwm-workspace.el index 8e3839f024..cf8caa8691 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -264,6 +264,14 @@ The optional FORCE option is for internal use only." (raise-frame (car exwm-workspace--list)) ;; Handle unexpected frame switch (add-hook 'focus-in-hook 'exwm-workspace--on-focus-in) + ;; Set _NET_VIRTUAL_ROOTS + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_VIRTUAL_ROOTS + :window exwm--root + :data (vconcat (mapcar + (lambda (i) + (frame-parameter i 'exwm-window-id)) + exwm-workspace--list)))) ;; Switch to the first workspace (exwm-workspace-switch 0 t)) diff --git a/exwm.el b/exwm.el index a52e11419d..3b2705a163 100644 --- a/exwm.el +++ b/exwm.el @@ -579,20 +579,12 @@ :data (make-vector (* 2 exwm-workspace-number) 0))) ;; Set _NET_WORKAREA (with minibuffer and bottom mode-line excluded) (let* ((workareas - (vconcat (window-absolute-pixel-edges (get-largest-window t)))) + (vector 0 0 (x-display-pixel-width) (x-display-pixel-height))) (workareas (mapconcat (lambda (i) workareas) (make-list exwm-workspace-number 0) []))) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WORKAREA :window exwm--root :data workareas))) - ;; Set _NET_VIRTUAL_ROOTS - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_VIRTUAL_ROOTS - :window exwm--root - :data (vconcat (mapcar - (lambda (i) - (frame-parameter i 'exwm-window-id)) - exwm-workspace--list)))) (xcb:flush exwm--connection)) (defvar exwm-init-hook nil -- cgit 1.4.1 From bb4ebde55b3bdd903cfc3bcaba3001957763c83d Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 26 Aug 2015 19:35:19 +0800 Subject: Fix `exwm-workspace-rename-buffer` Ensure buffer names are unique. --- exwm-workspace.el | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index cf8caa8691..27e3179566 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -202,16 +202,17 @@ The optional FORCE option is for internal use only." (defun exwm-workspace-rename-buffer (newname) "Rename a buffer." - (if (/= ?\s (aref newname 0)) - (rename-buffer newname t) - ;; If a buffer name is prefixed with a space, Emacs append a random - ;; number before renaming it. This is not desired behavior. - (let ((name (replace-regexp-in-string "<[0-9]+>$" "" newname)) - (counter 1)) - (while (and (get-buffer newname) - (not (eq (get-buffer newname) (current-buffer)))) - (setq newname (format "%s<%d>" name (cl-incf counter))))) - (rename-buffer newname))) + (let ((hidden (= ?\s (aref newname 0))) + (basename (replace-regexp-in-string "<[0-9]+>$" "" newname)) + (counter 1) + tmp) + (when hidden (setq basename (substring basename 1))) + (setq newname basename) + (while (and (setq tmp (or (get-buffer newname) + (get-buffer (concat " " newname)))) + (not (eq tmp (current-buffer)))) + (setq newname (format "%s<%d>" basename (cl-incf counter)))) + (rename-buffer (concat (and hidden " ") newname)))) (defun exwm-workspace--init () "Initialize workspace module." -- cgit 1.4.1 From 5f9f6737912f2a18f7adfff8c6f3ef8162ca52f1 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 27 Aug 2015 11:03:27 +0800 Subject: Fix window size calculations * When sending the synthetic ConfigureNotify event, make sure we are dealing with the correct Emacs window * When managing a floating window, ensure it can be easily pick up by the user --- exwm-floating.el | 9 +++++---- exwm-manage.el | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 8c2a8f4998..0b4f327f8b 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -98,8 +98,8 @@ width display-width)) ;; Invalid width (when (= 0 width) (setq width (/ display-width 2))) - ;; Completely outsize - (when (or (> x display-width) (> 0 (+ x display-width))) + ;; Make sure at least half of the window is visible + (when (or (> (+ x (/ width 2)) display-width) (> 0 (+ x (/ width 2)))) (setq x (/ (- display-width width) 2)))) (if (> height display-height) ;; Too tall @@ -107,8 +107,9 @@ height display-height) ;; Invalid height (when (= 0 height) (setq height (/ display-height 2))) - ;; Completely outside - (when (or (> y display-height) (> 0 (+ y display-height))) + ;; Make sure at least half of the window is visible + (when (or (> (+ y (/ height 2)) display-height) + (> 0 (+ y (/ height 2)))) (setq y (/ (- display-height height) 2)))) ;; Center floating windows (when (and (= x 0) (= y 0)) diff --git a/exwm-manage.el b/exwm-manage.el index 0fde4d4061..e7134fe671 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -289,7 +289,7 @@ corresponding buffer.") (frame-pixel-height exwm-workspace--current)) (or exwm--floating-edges (window-inside-absolute-pixel-edges - (get-buffer-window))))) + (get-buffer-window buffer t))))) (exwm--log "Reply with ConfigureNotify (edges): %s" edges) (xcb:+request exwm--connection (make-instance 'xcb:SendEvent -- cgit 1.4.1 From 7032ee70030273e9a32d3aa444b1247406d15552 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 27 Aug 2015 11:13:22 +0800 Subject: Re-enable complete refresh of keyboard mapping Since the performance of `xcb:keysyms:update-keyboard-mapping` is no longer an problem, we allow every possible refresh of keyboard mapping again. --- exwm-input.el | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index ede635c54e..eb53b41b8c 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -149,20 +149,13 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") ((= request xcb:Mapping:Modifier) ;; Modifier keys changed (exwm--log "Update modifier mapping") - (xcb:keysyms:update-modifier-mapping exwm--connection) - ) + (xcb:keysyms:update-modifier-mapping exwm--connection)) ((= request xcb:Mapping:Keyboard) ;; Only update changed keys - (with-slots (min-keycode max-keycode) - (xcb:get-setup exwm--connection) - ;; Since this operation is quite time-consuming, a complete refresh - ;; is forbidden as it's unlikely to bring any useful information - (unless (and (= min-keycode first-keycode) - (= max-keycode (+ first-keycode count -1))) - (exwm--log "Update keyboard mapping: %d ~ %d" - first-keycode (+ first-keycode count)) - (xcb:keysyms:update-keyboard-mapping exwm--connection - first-keycode count)))))))) + (exwm--log "Update keyboard mapping: %d ~ %d" + first-keycode (+ first-keycode count)) + (xcb:keysyms:update-keyboard-mapping exwm--connection + first-keycode count)))))) (defun exwm-input--on-ButtonPress (data synthetic) "Handle ButtonPress event." -- cgit 1.4.1 From 5222dc17d66375f20485d65573043e5a8286ae9d Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 27 Aug 2015 13:05:05 +0800 Subject: Avoid deleting the frame created by Emacs client by accident When Emacs is invoked as `emacsclient -a '' -c`, it creates a frame that can be deleted without any prompt. This commit removes the `client` parameter from that frame to avoid such inconvenience. --- exwm-workspace.el | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 27e3179566..f4e1dd1950 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -24,9 +24,6 @@ ;; This module adds workspace support for EXWM. -;; Todo: -;; + prevent from deleting frames of Emacs client (`frame-delete-functions') - ;;; Code: (defvar exwm-workspace-number 4 "Number of workspaces (1 ~ 10).") @@ -230,8 +227,10 @@ The optional FORCE option is for internal use only." ;; Emacs client creates an extra (but unusable) frame (dolist (i exwm-workspace--list) (unless (frame-parameter i 'window-id) - (setq exwm-workspace--list (delq i exwm-workspace--list))))) - (cl-assert (= 1 (length exwm-workspace--list))) + (setq exwm-workspace--list (delq i exwm-workspace--list)))) + (cl-assert (= 1 (length exwm-workspace--list))) + ;; Prevent user from deleting this frame by accident + (set-frame-parameter (car exwm-workspace--list) 'client nil)) ;; Create remaining frames (dotimes (i (1- exwm-workspace-number)) (nconc exwm-workspace--list -- cgit 1.4.1 From 76f48490b175e30d520c2dbf05f18807b03b10de Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 28 Aug 2015 18:33:02 +0800 Subject: Drop intro to xelb-util --- README | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README b/README index a8b9fa7117..5e2bedd493 100644 --- a/README +++ b/README @@ -2,9 +2,7 @@ Emacs X Window Manager ====================== EXWM (Emacs X Window Manager) turns Emacs into a full-featured tiling X window -manager. Please check the wiki for more details. - + Wiki: https://github.com/ch11ng/exwm/wiki +manager. Please check the wiki (https://github.com/ch11ng/exwm/wiki) for more +details. -EXWM is built on top of XELB and its utility libraris: - + XELB: https://github.com/ch11ng/xelb - + XELB utilility libraries: https://github.com/ch11ng/xelb-util +EXWM is built on top of XELB (https://github.com/ch11ng/xelb). -- cgit 1.4.1 From f7bec7a97729848f201d474b2984c71a8a981466 Mon Sep 17 00:00:00 2001 From: Philip Date: Fri, 28 Aug 2015 18:14:04 +0000 Subject: Minor fix --- exwm-manage.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index e7134fe671..648b42b8fe 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -83,7 +83,7 @@ corresponding buffer.") :event-mask xcb:EventMask:NoEvent)) ;; The window needs to be mapped (xcb:+request exwm--connection - (make-instance xcb:MapWindow :window id)) + (make-instance 'xcb:MapWindow :window id)) (with-slots (x y width height) exwm--geometry ;; Reparent to virtual root (essential) (xcb:+request exwm--connection @@ -323,7 +323,7 @@ corresponding buffer.") (with-slots (parent window) obj (if (/= exwm--root parent) (progn (xcb:+request exwm--connection - (make-instance xcb:MapWindow :window window)) + (make-instance 'xcb:MapWindow :window window)) (xcb:flush exwm--connection)) (exwm--log "MapRequest from #x%x" window) (exwm-manage--manage-window window))))) -- cgit 1.4.1 From f1d37b9a8c8d5dbe223b9099c09b2b25f0236c64 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 3 Sep 2015 18:56:36 +0800 Subject: Ungrab pointer timely when resizing is aborted * exwm-floating.el (exwm-floating--start-moveresize): when resizing type cannot be decided, ungrab the previously grabbed pointer. --- exwm-floating.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exwm-floating.el b/exwm-floating.el index 0b4f327f8b..851a586dec 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -278,7 +278,8 @@ ((> x 2) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_RIGHT) ((> y 2) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOM) ((< x 1) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_LEFT)))) - (when type + (if (not type) + (exwm-floating--stop-moveresize) (cond ((= type xcb:ewmh:_NET_WM_MOVERESIZE_MOVE) (setq cursor exwm-floating--cursor-move exwm-floating--moveresize-calculate -- cgit 1.4.1 From 6d63c712808bcb12ce7190999d17c4a0d6959cdb Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 4 Sep 2015 09:09:59 +0800 Subject: Prepare for GNU ELPA release * Transfer copyright to Free Software Foundation * Add packaging components (e.g. headers) * Coding style fixes: + Quote functions with "#'" + Wrap long lines + Fix doc strings / comments * Replace `string-to-int' with `string-to-number' * Fix compiling errors / eliminate compiling warnings + Add exwm-core.el to hold common variables, functions and macros * Remove the redundant COPYING file * Add .gitignore * Rename README to README.md --- .gitignore | 1 + COPYING | 674 ------------------------------------------------------ README | 8 - README.md | 13 ++ exwm-core.el | 155 +++++++++++++ exwm-floating.el | 51 +++-- exwm-input.el | 94 ++++---- exwm-layout.el | 21 +- exwm-manage.el | 27 +-- exwm-randr.el | 27 +-- exwm-workspace.el | 58 ++--- exwm.el | 298 +++++------------------- 12 files changed, 382 insertions(+), 1045 deletions(-) create mode 100644 .gitignore delete mode 100644 COPYING delete mode 100644 README create mode 100644 README.md create mode 100644 exwm-core.el diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..c531d9867f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.elc diff --git a/COPYING b/COPYING deleted file mode 100644 index 94a9ed024d..0000000000 --- a/COPYING +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/README b/README deleted file mode 100644 index 5e2bedd493..0000000000 --- a/README +++ /dev/null @@ -1,8 +0,0 @@ -Emacs X Window Manager -====================== - -EXWM (Emacs X Window Manager) turns Emacs into a full-featured tiling X window -manager. Please check the wiki (https://github.com/ch11ng/exwm/wiki) for more -details. - -EXWM is built on top of XELB (https://github.com/ch11ng/xelb). diff --git a/README.md b/README.md new file mode 100644 index 0000000000..5d18fa6c85 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +Emacs X Window Manager +====================== + +EXWM (Emacs X Window Manager) is a full-featured tiling X window manager for +Emacs built on top of [XELB](https://github.com/ch11ng/xelb). +It features: ++ Fully keyboard-driven operation ++ Hybrid layout modes (tiling & stacking) ++ Workspace support ++ ICCCM/EWMH compliance ++ Basic RandR support (optional) + +Please check the [wiki](https://github.com/ch11ng/exwm/wiki) for more details. diff --git a/exwm-core.el b/exwm-core.el new file mode 100644 index 0000000000..9810de4f74 --- /dev/null +++ b/exwm-core.el @@ -0,0 +1,155 @@ +;;; exwm-core.el --- Core definitions -*- lexical-binding: t -*- + +;; Copyright (C) 2015 Free Software Foundation, Inc. + +;; Author: Chris Feng + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Commentary: + +;; This module includes core definitions of variables, macros, functions, etc +;; shared by various other modules. + +;;; Code: + +(require 'xcb) +(require 'xcb-icccm) +(require 'xcb-ewmh) + +(eval-and-compile + (defvar exwm-debug-on nil "Non-nil to turn on debug for EXWM.")) + +(defmacro exwm--log (format-string &rest args) + "Print debug message." + (when exwm-debug-on + `(message (concat "[EXWM] " ,format-string) ,@args))) + +(defun exwm--make-emacs-idle-for (seconds) + "Put Emacs in idle state for SECONDS seconds." + (with-timeout (seconds) (read-event))) + +(defvar exwm--connection nil "X connection.") +(defvar exwm--root nil "Root window.") +(defvar exwm--id-buffer-alist nil "Alist of ( . ).") + +(defsubst exwm--id->buffer (id) + "X window ID => Emacs buffer." + (cdr (assoc id exwm--id-buffer-alist))) + +(defsubst exwm--buffer->id (buffer) + "Emacs buffer BUFFER => X window ID." + (car (rassoc buffer exwm--id-buffer-alist))) + +(defun exwm--lock (&rest _args) + "Lock (disable all events)." + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window exwm--root + :value-mask xcb:CW:EventMask + :event-mask xcb:EventMask:NoEvent)) + (xcb:flush exwm--connection)) + +(defun exwm--unlock (&rest _args) + "Unlock (enable all events)." + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window exwm--root + :value-mask xcb:CW:EventMask + :event-mask (logior xcb:EventMask:StructureNotify + xcb:EventMask:SubstructureRedirect))) + (xcb:flush exwm--connection)) + +(defconst exwm--client-event-mask + (logior xcb:EventMask:StructureNotify xcb:EventMask:PropertyChange) + "Event mask set on all managed windows.") + +;; Internal variables +(defvar-local exwm--id nil) ;window ID +(defvar-local exwm--frame nil) ;workspace frame +(defvar-local exwm--floating-frame nil) ;floating frame +(defvar-local exwm--floating-edges nil) ;four edges +(defvar-local exwm--fullscreen nil) ;used in fullscreen +(defvar-local exwm--floating-frame-geometry nil) ;in fullscreen +(defvar-local exwm--fixed-size nil) ;fixed size +(defvar-local exwm--on-KeyPress ;KeyPress event handler + #'exwm-input--on-KeyPress-line-mode) +;; Properties +(defvar-local exwm-window-type nil "_NET_WM_WINDOW_TYPE.") +(defvar-local exwm--geometry nil) +(defvar-local exwm-class-name nil "Class name in WM_CLASS.") +(defvar-local exwm-instance-name nil "Instance name in WM_CLASS.") +(defvar-local exwm-title nil "Window title (either _NET_WM_NAME or WM_NAME)") +(defvar-local exwm--title-is-utf8 nil) +(defvar-local exwm-transient-for nil "WM_TRANSIENT_FOR.") +(defvar-local exwm--protocols nil) +(defvar-local exwm-state nil "WM_STATE.") +;; _NET_WM_NORMAL_HINTS +(defvar-local exwm--normal-hints-x nil) +(defvar-local exwm--normal-hints-y nil) +(defvar-local exwm--normal-hints-width nil) +(defvar-local exwm--normal-hints-height nil) +(defvar-local exwm--normal-hints-min-width nil) +(defvar-local exwm--normal-hints-min-height nil) +(defvar-local exwm--normal-hints-max-width nil) +(defvar-local exwm--normal-hints-max-height nil) +;; (defvar-local exwm--normal-hints-win-gravity nil) +;; WM_HINTS +(defvar-local exwm--hints-input nil) ;FIXME +(defvar-local exwm--hints-urgency nil) + +(defvar exwm-mode-map + (let ((map (make-sparse-keymap))) + (define-key map "\C-ck" #'exwm-input-release-keyboard) + (define-key map "\C-cf" #'exwm-layout-set-fullscreen) + (define-key map "\C-cm" #'exwm-floating-toggle-floating) + (define-key map "\C-cq" #'exwm-input-send-next-key) + (define-key map "\C-cv" #'exwm-workspace-move-window) + map) + "Keymap for `exwm-mode'.") + +(define-derived-mode exwm-mode nil "EXWM" + "Major mode for managing X windows. + +\\{exwm-mode-map}" + ;; + (setq mode-name + '(:eval (propertize "EXWM" 'face + (when (cl-some (lambda (i) + (frame-parameter i + 'exwm--urgency)) + exwm-workspace--list) + 'font-lock-warning-face)))) + ;; Change major-mode is not allowed + (add-hook 'change-major-mode-hook #'kill-buffer nil t) + ;; Kill buffer -> close window + (add-hook 'kill-buffer-query-functions + (lambda () + (exwm-manage--close-window exwm--id (current-buffer)) + nil) + nil t) + (setq buffer-read-only t + left-margin-width nil + right-margin-width nil + left-fringe-width 0 + right-fringe-width 0 + vertical-scroll-bar nil)) + + + +(provide 'exwm-core) + +;;; exwm-core.el ends here diff --git a/exwm-floating.el b/exwm-floating.el index 851a586dec..6bb635a565 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -1,24 +1,23 @@ ;;; exwm-floating.el --- Floating Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015 Chris Feng +;; Copyright (C) 2015 Free Software Foundation, Inc. ;; Author: Chris Feng -;; Keywords: unix -;; This file is not part of GNU Emacs. +;; This file is part of GNU Emacs. -;; This file is free software: you can redistribute it and/or modify +;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. -;; This file is distributed in the hope that it will be useful, +;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with this file. If not, see . +;; along with GNU Emacs. If not, see . ;;; Commentary: @@ -31,6 +30,8 @@ ;;; Code: (require 'xcb-cursor) +(require 'exwm-core) +(eval-when-compile (require 'exwm-workspace)) (defvar exwm-floating-border-width 1 "Border width of the floating window.") (defvar exwm-floating-border-color "blue" @@ -41,6 +42,18 @@ (defvar exwm-floating-exit-hook nil "Normal hook run when a window has exited floating state.") +;; Cursors for moving/resizing a window +(defvar exwm-floating--cursor-move nil) +(defvar exwm-floating--cursor-top-left nil) +(defvar exwm-floating--cursor-top nil) +(defvar exwm-floating--cursor-top-right nil) +(defvar exwm-floating--cursor-right nil) +(defvar exwm-floating--cursor-bottom-right nil) +(defvar exwm-floating--cursor-bottom nil) +(defvar exwm-floating--cursor-bottom-left nil) +(defvar exwm-floating--cursor-left nil) + +;;;###autoload (defun exwm-floating--set-floating (id) "Make window ID floating." (interactive) @@ -66,8 +79,8 @@ (internal-border-width . ,exwm-floating-border-width) (unsplittable . t))) ;and fix the size later (exwm--unlock)))) - (frame-id (string-to-int (frame-parameter frame 'window-id))) - (outer-id (string-to-int (frame-parameter frame 'outer-window-id))) + (frame-id (string-to-number (frame-parameter frame 'window-id))) + (outer-id (string-to-number (frame-parameter frame 'outer-window-id))) (window (frame-first-window frame)) ;and it's the only window (x (slot-value exwm--geometry 'x)) (y (slot-value exwm--geometry 'y)) @@ -189,6 +202,7 @@ (select-window window)) (run-hooks 'exwm-floating-setup-hook)) +;;;###autoload (defun exwm-floating--unset-floating (id) "Make window ID non-floating." (interactive) @@ -223,6 +237,7 @@ (select-window window))) (run-hooks 'exwm-floating-exit-hook)) +;;;###autoload (defun exwm-floating-toggle-floating () "Toggle the current window between floating and non-floating states." (interactive) @@ -234,10 +249,11 @@ (defvar exwm-floating--moveresize-calculate nil "Calculate move/resize parameters [frame-id event-mask x y width height].") +;;;###autoload (defun exwm-floating--start-moveresize (id &optional type) "Start move/resize." (let ((buffer (exwm--id->buffer id)) - frame frame-id cursor) + frame frame-id x y width height cursor) (when (and buffer (setq frame (with-current-buffer buffer exwm--floating-frame)) (setq frame-id (frame-parameter frame 'exwm-outer-id)) @@ -372,14 +388,16 @@ :cursor cursor :time xcb:Time:CurrentTime))))))) -(defun exwm-floating--stop-moveresize (&rest args) +;;;###autoload +(defun exwm-floating--stop-moveresize (&rest _args) "Stop move/resize." (xcb:+request exwm--connection (make-instance 'xcb:UngrabPointer :time xcb:Time:CurrentTime)) (xcb:flush exwm--connection) (setq exwm-floating--moveresize-calculate nil)) -(defun exwm-floating--do-moveresize (data synthetic) +;;;###autoload +(defun exwm-floating--do-moveresize (data _synthetic) "Perform move/resize." (when exwm-floating--moveresize-calculate (let ((obj (make-instance 'xcb:MotionNotify)) @@ -401,17 +419,6 @@ :width (elt result 4) :height (elt result 5))) (xcb:flush exwm--connection)))) -;; Cursors for moving/resizing a window -(defvar exwm-floating--cursor-move nil) -(defvar exwm-floating--cursor-top-left nil) -(defvar exwm-floating--cursor-top nil) -(defvar exwm-floating--cursor-top-right nil) -(defvar exwm-floating--cursor-right nil) -(defvar exwm-floating--cursor-bottom-right nil) -(defvar exwm-floating--cursor-bottom nil) -(defvar exwm-floating--cursor-bottom-left nil) -(defvar exwm-floating--cursor-left nil) - (defun exwm-floating--init () "Initialize floating module." ;; Initialize cursors for moving/resizing a window diff --git a/exwm-input.el b/exwm-input.el index eb53b41b8c..69d2aff8e2 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -1,24 +1,23 @@ ;;; exwm-input.el --- Input Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015 Chris Feng +;; Copyright (C) 2015 Free Software Foundation, Inc. ;; Author: Chris Feng -;; Keywords: unix -;; This file is not part of GNU Emacs. +;; This file is part of GNU Emacs. -;; This file is free software: you can redistribute it and/or modify +;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. -;; This file is distributed in the hope that it will be useful, +;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with this file. If not, see . +;; along with GNU Emacs. If not, see . ;;; Commentary: @@ -33,11 +32,13 @@ ;; + Simulation keys to mimic Emacs key bindings for text edit (redo, select, ;; cancel, clear, etc). Some of them are not present on common keyboard ;; (keycode = 0). May need to use XKB extension. +;; + Investigate DnD support (e.g. drag a chromium tab to another window). ;;; Code: (require 'xcb-keysyms) -(require 'exwm-floating) +(require 'exwm-core) +(eval-when-compile (require 'exwm-workspace)) (defvar exwm-input-move-event 's-down-mouse-1 "Emacs event to start moving a window.") @@ -99,7 +100,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (setq exwm-input--focus-window window) (when exwm-input--timer (cancel-timer exwm-input--timer)) (setq exwm-input--timer - (run-with-timer 0.01 nil 'exwm-input--update-focus))) + (run-with-timer 0.01 nil #'exwm-input--update-focus))) (setq exwm-input--redirected nil)))) (defun exwm-input--on-focus-in () @@ -131,6 +132,11 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (redirect-frame-focus exwm--floating-frame exwm--frame)))))) (setq exwm-input--focus-window nil)))) +(defvar exwm-input--during-key-sequence nil + "Non-nil indicates Emacs is waiting for more keys to form a key sequence.") +(defvar exwm-input--temp-line-mode nil + "Non-nil indicates it's in temporary line-mode for char-mode.") + (defun exwm-input--finish-key-sequence () "Mark the end of a key sequence (with the aid of `pre-command-hook')." (when (and exwm-input--during-key-sequence @@ -140,7 +146,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (setq exwm-input--temp-line-mode nil) (exwm-input--release-keyboard)))) -(defun exwm-input--on-MappingNotify (data synthetic) +(defun exwm-input--on-MappingNotify (data _synthetic) "Handle MappingNotify event." (let ((obj (make-instance 'xcb:MappingNotify))) (xcb:unmarshal obj data) @@ -157,7 +163,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (xcb:keysyms:update-keyboard-mapping exwm--connection first-keycode count)))))) -(defun exwm-input--on-ButtonPress (data synthetic) +(defun exwm-input--on-ButtonPress (data _synthetic) "Handle ButtonPress event." (let ((obj (make-instance 'xcb:ButtonPress)) (mode xcb:Allow:SyncPointer)) @@ -167,8 +173,8 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (cond ((and (= state exwm-input--move-mask) (= detail exwm-input--move-keysym)) ;; Move - (exwm-floating--start-moveresize event - xcb:ewmh:_NET_WM_MOVERESIZE_MOVE)) + (exwm-floating--start-moveresize + event xcb:ewmh:_NET_WM_MOVERESIZE_MOVE)) ((and (= state exwm-input--resize-mask) (= detail exwm-input--resize-keysym)) ;; Resize @@ -182,7 +188,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (make-instance 'xcb:AllowEvents :mode mode :time xcb:Time:CurrentTime)) (xcb:flush exwm--connection))) -(defun exwm-input--on-KeyPress (data synthetic) +(defun exwm-input--on-KeyPress (data _synthetic) "Handle KeyPress event." (let ((obj (make-instance 'xcb:KeyPress))) (xcb:unmarshal obj data) @@ -194,6 +200,12 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (defvar exwm-input--global-keys nil "Global key bindings.") (defvar exwm-input--global-prefix-keys nil "List of prefix keys of global key bindings.") +(defvar exwm-input-prefix-keys + '(?\C-x ?\C-u ?\C-h ?\M-x ?\M-` ?\M-! ?\M-& ?\M-:) + "List of prefix keys EXWM should forward to Emacs when in line-mode.") +(defvar exwm-input--simulation-keys nil "Simulation keys in line-mode.") +(defvar exwm-input--simulation-prefix-keys nil + "List of prefix keys of simulation keys in line-mode.") (defun exwm-input--update-global-prefix-keys () "Update `exwm-input--global-prefix-keys'." @@ -229,14 +241,10 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (global-set-key key command) (cl-pushnew key exwm-input--global-keys)) -(defvar exwm-input--during-key-sequence nil - "Non-nil indicates Emacs is waiting for more keys to form a key sequence.") -(defvar exwm-input--temp-line-mode nil - "Non-nil indicates it's in temporary line-mode for char-mode.") - -(cl-defmethod exwm-input--on-KeyPress-line-mode ((obj xcb:KeyPress)) +;;;###autoload +(defun exwm-input--on-KeyPress-line-mode (key-press) "Parse X KeyPress event to Emacs key event and then feed the command loop." - (with-slots (detail state) obj + (with-slots (detail state) key-press (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) event minibuffer-window mode) (when (and keysym @@ -256,16 +264,16 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") :time xcb:Time:CurrentTime)) (xcb:flush exwm--connection)))) -(cl-defmethod exwm-input--on-KeyPress-char-mode ((obj xcb:KeyPress)) +(defun exwm-input--on-KeyPress-char-mode (key-press) "Handle KeyPress event in char-mode." - (with-slots (detail state) obj + (with-slots (detail state) key-press (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) event) (when (and keysym (setq event (xcb:keysyms:keysym->event keysym state))) (when (eq major-mode 'exwm-mode) (setq exwm-input--temp-line-mode t exwm-input--during-key-sequence t) - (exwm-input--grab-keyboard)) ;grab keyboard temporarily + (exwm-input--grab-keyboard)) ;grab keyboard temporarily (push event unread-command-events)))) (xcb:+request exwm--connection (make-instance 'xcb:AllowEvents @@ -285,7 +293,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") :pointer-mode xcb:GrabMode:Async :keyboard-mode xcb:GrabMode:Sync)) (exwm--log "Failed to grab keyboard for #x%x" id)) - (setq exwm--on-KeyPress 'exwm-input--on-KeyPress-line-mode))) + (setq exwm--on-KeyPress #'exwm-input--on-KeyPress-line-mode))) (defun exwm-input--release-keyboard (&optional id) "Ungrab all key events on window ID." @@ -296,8 +304,9 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") :key xcb:Grab:Any :grab-window id :modifiers xcb:ModMask:Any)) (exwm--log "Failed to release keyboard for #x%x" id)) - (setq exwm--on-KeyPress 'exwm-input--on-KeyPress-char-mode))) + (setq exwm--on-KeyPress #'exwm-input--on-KeyPress-char-mode))) +;;;###autoload (defun exwm-input-grab-keyboard (&optional id) "Switch to line-mode." (interactive) @@ -314,6 +323,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (down-mouse-1 . exwm-input-release-keyboard)))))) (force-mode-line-update)) +;;;###autoload (defun exwm-input-release-keyboard (&optional id) "Switch to char-mode." (interactive) @@ -353,6 +363,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") exwm--connection)))) (xcb:flush exwm--connection))) +;;;###autoload (defun exwm-input-send-next-key (times) "Send next key to client window." (interactive "p") @@ -376,19 +387,11 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") ;; (unless (listp last-input-event) ;not a key event ;; (exwm-input--fake-key last-input-event))) -(defvar exwm-input-prefix-keys - '(?\C-x ?\C-u ?\C-h ?\M-x ?\M-` ?\M-! ?\M-& ?\M-:) - "List of prefix keys EXWM should forward to Emacs when in line-mode.") - -(defvar exwm-input--simulation-keys nil "Simulation keys in line-mode.") -(defvar exwm-input--simulation-prefix-keys nil - "List of prefix keys of simulation keys in line-mode.") - (defun exwm-input--update-simulation-prefix-keys () "Update the list of prefix keys of simulation keys." (setq exwm-input--simulation-prefix-keys nil) (dolist (i exwm-input--simulation-keys) - (define-key exwm-mode-map (car i) 'exwm-input-send-simulation-key) + (define-key exwm-mode-map (car i) #'exwm-input-send-simulation-key) (cl-pushnew (elt (car i) 0) exwm-input--simulation-prefix-keys))) (defun exwm-input-set-simulation-keys (simulation-keys) @@ -403,14 +406,12 @@ SIMULATION-KEYS is a list of alist (key-sequence1 . key-sequence2)." (defun exwm-input-send-simulation-key (times) "Fake a key event according to last input key sequence." (interactive "p") - (let ((pair (assoc (this-single-command-keys) - exwm-input--simulation-keys)) - key) + (let ((pair (assoc (this-single-command-keys) exwm-input--simulation-keys))) (when pair (setq pair (cdr pair)) (unless (listp pair) (setq pair (list pair))) - (dotimes (i times) + (dotimes (_ times) (dolist (j pair) (exwm-input--fake-key j)))))) @@ -427,17 +428,18 @@ SIMULATION-KEYS is a list of alist (key-sequence1 . key-sequence2)." exwm-input--resize-mask (cadr resize-key))) ;; Attach event listeners (xcb:+event exwm--connection 'xcb:MappingNotify - 'exwm-input--on-MappingNotify) - (xcb:+event exwm--connection 'xcb:KeyPress 'exwm-input--on-KeyPress) - (xcb:+event exwm--connection 'xcb:ButtonPress 'exwm-input--on-ButtonPress) + #'exwm-input--on-MappingNotify) + (xcb:+event exwm--connection 'xcb:KeyPress #'exwm-input--on-KeyPress) + (xcb:+event exwm--connection 'xcb:ButtonPress #'exwm-input--on-ButtonPress) (xcb:+event exwm--connection 'xcb:ButtonRelease - 'exwm-floating--stop-moveresize) - (xcb:+event exwm--connection 'xcb:MotionNotify 'exwm-floating--do-moveresize) + #'exwm-floating--stop-moveresize) + (xcb:+event exwm--connection 'xcb:MotionNotify + #'exwm-floating--do-moveresize) ;; `pre-command-hook' marks the end of a key sequence (existing or not) - (add-hook 'pre-command-hook 'exwm-input--finish-key-sequence) + (add-hook 'pre-command-hook #'exwm-input--finish-key-sequence) ;; Update focus when buffer list updates - (add-hook 'buffer-list-update-hook 'exwm-input--on-buffer-list-update) - (add-hook 'focus-in-hook 'exwm-input--on-focus-in) + (add-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update) + (add-hook 'focus-in-hook #'exwm-input--on-focus-in) ;; Update prefix keys for global keys (exwm-input--update-global-prefix-keys)) diff --git a/exwm-layout.el b/exwm-layout.el index fe1645f0fb..a14651a46d 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -1,24 +1,23 @@ ;;; exwm-layout.el --- Layout Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015 Chris Feng +;; Copyright (C) 2015 Free Software Foundation, Inc. ;; Author: Chris Feng -;; Keywords: unix -;; This file is not part of GNU Emacs. +;; This file is part of GNU Emacs. -;; This file is free software: you can redistribute it and/or modify +;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. -;; This file is distributed in the hope that it will be useful, +;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with this file. If not, see . +;; along with GNU Emacs. If not, see . ;;; Commentary: @@ -26,6 +25,10 @@ ;;; Code: +(require 'exwm-core) +(eval-when-compile (require 'exwm-workspace)) + +;;;###autoload (defun exwm-layout--show (id &optional window) "Show window ID exactly fit in the Emacs window WINDOW." (exwm--log "Show #x%x in %s" id window) @@ -68,6 +71,7 @@ exwm--connection)))) (xcb:flush exwm--connection)) +;;;###autoload (defun exwm-layout--hide (id) "Hide window ID." (unless (eq xcb:icccm:WM_STATE:IconicState ;already hidden @@ -89,6 +93,7 @@ :icon xcb:Window:None)) (xcb:flush exwm--connection))) +;;;###autoload (defun exwm-layout-set-fullscreen (&optional id) "Make window ID fullscreen." (interactive) @@ -248,9 +253,9 @@ (defun exwm-layout--init () "Initialize layout module." ;; Auto refresh layout - (add-hook 'window-configuration-change-hook 'exwm-layout--refresh) + (add-hook 'window-configuration-change-hook #'exwm-layout--refresh) ;; Refresh when minibuffer grows - (add-hook 'minibuffer-setup-hook 'exwm-layout--on-minibuffer-setup t)) + (add-hook 'minibuffer-setup-hook #'exwm-layout--on-minibuffer-setup t)) diff --git a/exwm-manage.el b/exwm-manage.el index 648b42b8fe..a8d0e592a8 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -1,25 +1,24 @@ ;;; exwm-manage.el --- Window Management Module for -*- lexical-binding: t -*- ;;; EXWM -;; Copyright (C) 2015 Chris Feng +;; Copyright (C) 2015 Free Software Foundation, Inc. ;; Author: Chris Feng -;; Keywords: unix -;; This file is not part of GNU Emacs. +;; This file is part of GNU Emacs. -;; This file is free software: you can redistribute it and/or modify +;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. -;; This file is distributed in the hope that it will be useful, +;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with this file. If not, see . +;; along with GNU Emacs. If not, see . ;;; Commentary: @@ -27,7 +26,8 @@ ;;; Code: -(require 'exwm-input) +(require 'exwm-core) +(eval-when-compile (require 'exwm-workspace)) (defvar exwm-manage-finish-hook nil "Normal hook run after a window is just managed, in the context of the @@ -208,6 +208,7 @@ corresponding buffer.") "Non-nil indicates EXWM is pinging a window.") (defvar exwm-manage-ping-timeout 3 "Seconds to wait before killing a client.") +;;;###autoload (defun exwm-manage--close-window (id &optional buffer) "Close window ID in a proper way." (catch 'return @@ -269,7 +270,7 @@ corresponding buffer.") (make-instance 'xcb:KillClient :resource id)) (xcb:flush exwm--connection)))) -(defun exwm-manage--on-ConfigureRequest (data synthetic) +(defun exwm-manage--on-ConfigureRequest (data _synthetic) "Handle ConfigureRequest event." (let ((obj (make-instance 'xcb:ConfigureRequest)) buffer edges) @@ -316,7 +317,7 @@ corresponding buffer.") :sibling sibling :stack-mode stack-mode))))) (xcb:flush exwm--connection)) -(defun exwm-manage--on-MapRequest (data synthetic) +(defun exwm-manage--on-MapRequest (data _synthetic) "Handle MapRequest event." (let ((obj (make-instance 'xcb:MapRequest))) (xcb:unmarshal obj data) @@ -347,11 +348,11 @@ corresponding buffer.") (defun exwm-manage--init () "Initialize manage module." (xcb:+event exwm--connection 'xcb:ConfigureRequest - 'exwm-manage--on-ConfigureRequest) - (xcb:+event exwm--connection 'xcb:MapRequest 'exwm-manage--on-MapRequest) - (xcb:+event exwm--connection 'xcb:UnmapNotify 'exwm-manage--on-UnmapNotify) + #'exwm-manage--on-ConfigureRequest) + (xcb:+event exwm--connection 'xcb:MapRequest #'exwm-manage--on-MapRequest) + (xcb:+event exwm--connection 'xcb:UnmapNotify #'exwm-manage--on-UnmapNotify) (xcb:+event exwm--connection 'xcb:DestroyNotify - 'exwm-manage--on-DestroyNotify)) + #'exwm-manage--on-DestroyNotify)) diff --git a/exwm-randr.el b/exwm-randr.el index 57ad569e22..d14e0974c7 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -1,24 +1,23 @@ ;;; exwm-randr.el --- RandR Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015 Chris Feng +;; Copyright (C) 2015 Free Software Foundation, Inc. ;; Author: Chris Feng -;; Keywords: unix -;; This file is not part of GNU Emacs. +;; This file is part of GNU Emacs. -;; This file is free software: you can redistribute it and/or modify +;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. -;; This file is distributed in the hope that it will be useful, +;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with this file. If not, see . +;; along with GNU Emacs. If not, see . ;;; Commentary: @@ -42,6 +41,8 @@ ;;; Code: (require 'xcb-randr) +(require 'exwm-core) +(eval-when-compile (require 'exwm-workspace)) (defvar exwm-randr-workspace-output-plist nil) @@ -60,7 +61,7 @@ :output output :config-timestamp config-timestamp)) (setq name ;UTF-8 encoded - (decode-coding-string (apply 'unibyte-string name) 'utf-8)) + (decode-coding-string (apply #'unibyte-string name) 'utf-8)) (if (or (/= connection xcb:randr:Connection:Connected) (= 0 crtc)) ;FIXME (plist-put output-plist name nil) @@ -102,9 +103,9 @@ :window exwm--root :data (vconcat workareas))) ;; Update _NET_DESKTOP_VIEWPORT (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT - :window exwm--root - :data (vconcat viewports))) + (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT + :window exwm--root + :data (vconcat viewports))) (xcb:flush exwm--connection))) (defun exwm-randr--init () @@ -121,11 +122,11 @@ major-version minor-version) (exwm-randr--refresh) (xcb:+event exwm--connection 'xcb:randr:ScreenChangeNotify - (lambda (data synthetic) + (lambda (_data _synthetic) (exwm--log "(RandR) ScreenChangeNotify") (exwm-randr--refresh))) ;; (xcb:+event exwm--connection 'xcb:randr:Notify - ;; (lambda (data synthetic) + ;; (lambda (_data _synthetic) ;; (exwm--log "(RandR) Notify") ;; (exwm-randr--refresh))) (xcb:+request exwm--connection @@ -142,7 +143,7 @@ (defun exwm-randr-enable () "Enable RandR support for EXWM." - (add-hook 'exwm-init-hook 'exwm-randr--init)) + (add-hook 'exwm-init-hook #'exwm-randr--init)) diff --git a/exwm-workspace.el b/exwm-workspace.el index f4e1dd1950..9cf1ff0509 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1,31 +1,36 @@ ;;; exwm-workspace.el --- Workspace Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015 Chris Feng +;; Copyright (C) 2015 Free Software Foundation, Inc. ;; Author: Chris Feng -;; Keywords: unix -;; This file is not part of GNU Emacs. +;; This file is part of GNU Emacs. -;; This file is free software: you can redistribute it and/or modify +;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. -;; This file is distributed in the hope that it will be useful, +;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with this file. If not, see . +;; along with GNU Emacs. If not, see . ;;; Commentary: ;; This module adds workspace support for EXWM. +;; Todo: +;; + Auto hide minibuffer, or allow users to place it elsewhere. +;; + Add system tray support. + ;;; Code: +(require 'exwm-core) + (defvar exwm-workspace-number 4 "Number of workspaces (1 ~ 10).") (defvar exwm-workspace--list nil "List of all workspaces (Emacs frames).") (defvar exwm-workspace--switch-map @@ -42,23 +47,24 @@ (define-key map "\C-e" (lambda () (interactive) (goto-history-element exwm-workspace-number))) - (define-key map "\C-g" 'abort-recursive-edit) - (define-key map "\C-]" 'abort-recursive-edit) - (define-key map "\C-j" 'exit-minibuffer) - ;; (define-key map "\C-m" 'exit-minibuffer) ;not working - (define-key map [return] 'exit-minibuffer) - (define-key map " " 'exit-minibuffer) - (define-key map "\C-f" 'previous-history-element) - (define-key map "\C-b" 'next-history-element) + (define-key map "\C-g" #'abort-recursive-edit) + (define-key map "\C-]" #'abort-recursive-edit) + (define-key map "\C-j" #'exit-minibuffer) + ;; (define-key map "\C-m" #'exit-minibuffer) ;not working + (define-key map [return] #'exit-minibuffer) + (define-key map " " #'exit-minibuffer) + (define-key map "\C-f" #'previous-history-element) + (define-key map "\C-b" #'next-history-element) ;; Alternative keys - (define-key map [right] 'previous-history-element) - (define-key map [left] 'next-history-element) + (define-key map [right] #'previous-history-element) + (define-key map [left] #'next-history-element) map) "Keymap used for interactively switch workspace.") (defvar exwm-workspace--switch-history nil "History for `read-from-minibuffer' to interactively switch workspace.") +;;;###autoload (defun exwm-workspace--update-switch-history () "Update the history for switching workspace to reflect the latest status." (let ((sequence (number-sequence 0 (1- exwm-workspace-number))) @@ -95,14 +101,14 @@ The optional FORCE option is for internal use only." (interactive (list (unless (and (eq major-mode 'exwm-mode) exwm--fullscreen) ;it's invisible - (let* ((history-add-new-input nil) ;prevent modifying history + (let* ((history-add-new-input nil) ;prevent modifying history (idx (read-from-minibuffer "Workspace: " (elt exwm-workspace--switch-history exwm-workspace-current-index) exwm-workspace--switch-map nil `(exwm-workspace--switch-history . ,(1+ exwm-workspace-current-index))))) - (cl-position idx exwm-workspace--switch-history :test 'equal))))) + (cl-position idx exwm-workspace--switch-history :test #'equal))))) (when index (unless (and (<= 0 index) (< index exwm-workspace-number)) (user-error "[EXWM] Workspace index out of range: %d" index)) @@ -150,6 +156,7 @@ The optional FORCE option is for internal use only." (exwm--log "Workspace was switched unexpectedly") (exwm-workspace-switch index)))) +;;;###autoload (defun exwm-workspace-move-window (index &optional id) "Move window ID to workspace INDEX." (interactive @@ -161,7 +168,7 @@ The optional FORCE option is for internal use only." exwm-workspace--switch-map nil `(exwm-workspace--switch-history . ,(1+ exwm-workspace-current-index))))) - (cl-position idx exwm-workspace--switch-history :test 'equal)))) + (cl-position idx exwm-workspace--switch-history :test #'equal)))) (unless id (setq id (exwm--buffer->id (window-buffer)))) (unless (and (<= 0 index) (< index exwm-workspace-number)) (user-error "[EXWM] Workspace index out of range: %d" index)) @@ -169,9 +176,8 @@ The optional FORCE option is for internal use only." (let ((frame (elt exwm-workspace--list index))) (when (not (equal exwm--frame frame)) (let ((name (replace-regexp-in-string "^\\s-*" "" (buffer-name)))) - (exwm-workspace-rename-buffer (if (= index exwm-workspace-current-index) - name - (concat " " name)))) + (exwm-workspace-rename-buffer + (if (= index exwm-workspace-current-index) name (concat " " name)))) (setq exwm--frame frame) (if exwm--floating-frame ;; Move the floating frame is enough @@ -232,14 +238,14 @@ The optional FORCE option is for internal use only." ;; Prevent user from deleting this frame by accident (set-frame-parameter (car exwm-workspace--list) 'client nil)) ;; Create remaining frames - (dotimes (i (1- exwm-workspace-number)) + (dotimes (_ (1- exwm-workspace-number)) (nconc exwm-workspace--list (list (make-frame '((window-system . x) (visibility . nil)))))) ;; Configure workspaces (dolist (i exwm-workspace--list) - (let ((window-id (string-to-int (frame-parameter i 'window-id))) - (outer-id (string-to-int (frame-parameter i 'outer-window-id)))) + (let ((window-id (string-to-number (frame-parameter i 'window-id))) + (outer-id (string-to-number (frame-parameter i 'outer-window-id)))) ;; Save window IDs (set-frame-parameter i 'exwm-window-id window-id) (set-frame-parameter i 'exwm-outer-id outer-id) @@ -263,7 +269,7 @@ The optional FORCE option is for internal use only." (set-frame-parameter i 'fullscreen 'fullboth)) (raise-frame (car exwm-workspace--list)) ;; Handle unexpected frame switch - (add-hook 'focus-in-hook 'exwm-workspace--on-focus-in) + (add-hook 'focus-in-hook #'exwm-workspace--on-focus-in) ;; Set _NET_VIRTUAL_ROOTS (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_VIRTUAL_ROOTS diff --git a/exwm.el b/exwm.el index 3b2705a163..84fbc15255 100644 --- a/exwm.el +++ b/exwm.el @@ -1,80 +1,46 @@ ;;; exwm.el --- Emacs X Window Manager -*- lexical-binding: t -*- -;; Copyright (C) 2015 Chris Feng +;; Copyright (C) 2015 Free Software Foundation, Inc. ;; Author: Chris Feng +;; Maintainer: Chris Feng +;; Version: 0 +;; Package-Requires: ((xelb "0.1")) ;; Keywords: unix +;; URL: https://github.com/ch11ng/exwm -;; This file is not part of GNU Emacs. +;; This file is part of GNU Emacs. -;; This file is free software: you can redistribute it and/or modify +;; GNU Emacs is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. -;; This file is distributed in the hope that it will be useful, +;; GNU Emacs is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with this file. If not, see . +;; along with GNU Emacs. If not, see . ;;; Commentary: ;; Overview ;; -------- -;; EXWM (Emacs X Window Manager) turns Emacs into a full-featured tiling X -;; window manager. It's built on top of XELB, thus purely consists of Elisp -;; codes. Some features include: -;; + It's fully keyboard-driven. -;; - You have full access to all key bindings in Emacs. -;; - It allows you to bind additional key sequences with `exwm-input-set-key' -;; (just like `global-set-key'). -;; - It supports simulation keys (i.e., map one key sequence to another). -;; + Workspace support. -;; - EXWM support up to 10 workspaces. -;; + ICCCM/EWMH compliance. -;; - Note that the support for EWMH can never be complete since EXWM is not a -;; conventional window manager. - -;; How it works -;; ------------ -;; Emacs itself is a tiling window manager, though unfortunately not for -;; managing X things. EXWM has therefore been created to overcome this limitation -;; by relating X concepts to Emacs ones as shown in the following table. -;; -;; +=============+=========+ -;; | X Window | Emacs | -;; +=============+=========+ -;; | window | buffer | -;; +-------------+---------+ -;; | container* | window | -;; +-------------+---------+ -;; | workspace / | frame** | -;; | decoration | | -;; +=============+=========+ -;; * Here a container means the parent of an X client window created by window -;; manager for layout management. -;; ** In EXWM, A frame usually acts as a workspace. But for a floating window, -;; it's the decoration around the top-level window. -;; -;; Each X client window corresponds to a dedicated buffer in EXWM mode. When -;; such a buffer is buried or unburied, the attached X client window is hide or -;; shown accordingly. The position and size of the X client window is then -;; determined by the Emacs window its corresponding buffer displayed in. -;; -;; A buffer in EXWM mode also records which workspace it belongs to, and its -;; attached X client window is made a child (in the sense of X) of the -;; workspace frame. The switch between workspaces is simply done by switching -;; corresponding Emacs frames. +;; EXWM (Emacs X Window Manager) is a full-featured tiling X window manager for +;; Emacs built on top of XELB. It features: +;; + Fully keyboard-driven operation +;; + Hybrid layout modes (tiling & stacking) +;; + Workspace support +;; + ICCCM/EWMH compliance +;; + Basic RandR support (optional) ;; Installation & configuration ;; ---------------------------- ;; Here are the minimal steps to get EXWM working: -;; 0. Install xelb and xelb-util first. -;; 1. Put EXWM somewhere on your disk and make sure it's in `load-path'. -;; 2. In your Emacs init file, add following lines (modify accordingly): +;; 1. Install XELB and EXWM, and make sure they are in `load-path'. +;; 2. In '~/.emacs', add following lines (please modify accordingly): ;; ;; (require 'exwm) ;; ;; We always need a way to go back from char-mode to line-mode @@ -83,51 +49,23 @@ ;; (exwm-input-set-key (kbd "s-w") 'exwm-workspace-switch) ;; ;; Use class name to name an EXWM buffer ;; (add-hook 'exwm-update-class-hook -;; (lambda () (rename-buffer exwm-class-name t))) +;; (lambda () (exwm-workspace-rename-buffer exwm-class-name t))) ;; ;; Enable EXWM ;; (exwm-enable) ;; -;; 3. Make a file '~/.xinitrc' with the content +;; 3. Make a file '~/.xinitrc' with the following lines: ;; +;; # You may need to comment out the next line to disable access control +;; #xhost + ;; exec emacs ;; -;; 4. Launch EXWM in a console with +;; 4. Launch EXWM in a console (e.g. tty1) with ;; -;; xinit +;; xinit -- vt01 ;; -;; You should refer to other resources on how to further configure '~/.xinitrc' -;; and other init scripts to improve the working experience. Besides, you -;; should hide the menu-bar, tool-bar, etc to increase the usable space. - -;; Interactive modes -;; ----------------- -;; There are two modes in EXWM to interact with an X client window: line mode -;; and char mode. They are analogous to those concepts in `ansi-term'. EXWM -;; buffers are created in line mode by default. -;; -;; In line mode, all key events are intercepted and then forwarded to the X -;; client window except -;; + it forms a mode-specific key sequence (which begins with 'C-c'), or -;; + it forms a key sequence bound with `exwm-input-set-key', or -;; + it forms a key sequence starting with a line mode prefix key, or -;; + it forms a key sequence in line mode simulation keys. -;; You can use 'C-c q' (bound to `exwm-input-send-next-key', can be 'C-u' -;; prefixed) to send these keys to the client. -;; -;; In char mode however, no key event is intercepted except those bound with -;; `exwm-input-set-key'. Therefore you will almost always need to -;; 'exwm-input-set-key' a key sequence to go from char mode to line mode. - -;; Related projects -;; ---------------- -;; EXWM is not the first attempt to make Emacs an X window manger; there is -;; another ancient project called XWEM (http://www.nongnu.org/xwem/) for -;; XEmacs, though it seems nobody have ever got it working on GNU Emacs. - -;; Todo: -;; + Investigate DnD support (e.g. drag a chromium tab to another window). -;; + Auto hide minibuffer, or allow users to place it elsewhere. -;; + Add system tray support. +;; You should additionally hide the menu-bar, tool-bar, etc to increase the +;; usable space. Please check the wiki (https://github.com/ch11ng/exwm/wiki) +;; for more detailed instructions on installation, configuration, usage, etc. ;; References: ;; + dwm (http://dwm.suckless.org/) @@ -136,61 +74,13 @@ ;;; Code: -(require 'xcb) -(require 'xcb-icccm) -(require 'xcb-ewmh) +(require 'exwm-core) (require 'exwm-workspace) (require 'exwm-layout) (require 'exwm-floating) (require 'exwm-manage) (require 'exwm-input) -(defvar exwm-debug-on nil "Non-nil to turn on debug for EXWM.") - -(defmacro exwm--log (format-string &rest args) - "Print debug message." - (when exwm-debug-on - `(message (concat "[EXWM] " ,format-string) ,@args))) - -(defconst exwm--client-event-mask - (logior xcb:EventMask:StructureNotify xcb:EventMask:PropertyChange) - "Event mask set on all managed windows.") - -(defvar exwm--connection nil "X connection.") -(defvar exwm--root nil "Root window.") -(defvar exwm--id-buffer-alist nil "Alist of ( . )") - -(defsubst exwm--id->buffer (id) - "X window ID => Emacs buffer." - (cdr (assoc id exwm--id-buffer-alist))) - -(defsubst exwm--buffer->id (buffer) - "Emacs buffer => X window ID." - (car (rassoc buffer exwm--id-buffer-alist))) - -(defun exwm--lock (&rest args) ;args are for abnormal hook - "Lock (disable all events)." - (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window exwm--root - :value-mask xcb:CW:EventMask - :event-mask xcb:EventMask:NoEvent)) - (xcb:flush exwm--connection)) - -(defun exwm--unlock (&rest args) ;args are for abnormal hook - "Unlock (enable all events)." - (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window exwm--root - :value-mask xcb:CW:EventMask - :event-mask (logior xcb:EventMask:StructureNotify - xcb:EventMask:SubstructureRedirect))) - (xcb:flush exwm--connection)) - -(defun exwm--make-emacs-idle-for (seconds) - "Put Emacs in idle state for SECONDS seconds." - (with-timeout (seconds) (read-event))) - (defun exwm-reset () "Reset window to standard state: non-fullscreen, line-mode." (interactive) @@ -201,6 +91,7 @@ (exwm-layout--refresh) (exwm-input-grab-keyboard)))) +;;;###autoload (defun exwm--update-window-type (id &optional force) "Update _NET_WM_WINDOW_TYPE." (with-current-buffer (exwm--id->buffer id) @@ -214,6 +105,7 @@ (defvar exwm-update-class-hook nil "Normal hook run when window class is updated.") +;;;###autoload (defun exwm--update-class (id &optional force) "Update WM_CLASS." (with-current-buffer (exwm--id->buffer id) @@ -229,6 +121,7 @@ (defvar exwm-update-title-hook nil "Normal hook run when window title is updated.") +;;;###autoload (defun exwm--update-utf8-title (id &optional force) "Update _NET_WM_NAME." (with-current-buffer (exwm--id->buffer id) @@ -241,6 +134,7 @@ (setq exwm--title-is-utf8 t) (run-hooks 'exwm-update-title-hook))))))) +;;;###autoload (defun exwm--update-ctext-title (id &optional force) "Update WM_NAME." (with-current-buffer (exwm--id->buffer id) @@ -253,11 +147,13 @@ (when exwm-title (run-hooks 'exwm-update-title-hook))))))) +;;;###autoload (defun exwm--update-title (id) "Update _NET_WM_NAME or WM_NAME." (exwm--update-utf8-title id) (exwm--update-ctext-title id)) +;;;###autoload (defun exwm--update-transient-for (id &optional force) "Update WM_TRANSIENT_FOR." (with-current-buffer (exwm--id->buffer id) @@ -268,6 +164,7 @@ (when reply ;nil when destroyed (setq exwm-transient-for (slot-value reply 'value))))))) +;;;###autoload (defun exwm--update-normal-hints (id &optional force) "Update WM_NORMAL_HINTS." (with-current-buffer (exwm--id->buffer id) @@ -315,6 +212,7 @@ (= exwm--normal-hints-min-height exwm--normal-hints-max-height))))))))) +;;;###autoload (defun exwm--update-hints (id &optional force) "Update WM_HINTS." (with-current-buffer (exwm--id->buffer id) @@ -334,6 +232,7 @@ (set-frame-parameter exwm--frame 'exwm--urgency t) (exwm-workspace--update-switch-history)))))))) +;;;###autoload (defun exwm--update-protocols (id &optional force) "Update WM_PROTOCOLS." (with-current-buffer (exwm--id->buffer id) @@ -344,6 +243,7 @@ (when reply ;nil when destroyed (setq exwm--protocols (append (slot-value reply 'value) nil))))))) +;;;###autoload (defun exwm--update-state (id &optional force) "Update WM_STATE." (with-current-buffer (exwm--id->buffer id) @@ -355,16 +255,14 @@ ;; Default to normal state xcb:icccm:WM_STATE:NormalState))))))) -(defun exwm--on-PropertyNotify (data synthetic) +(defun exwm--on-PropertyNotify (data _synthetic) "Handle PropertyNotify event." (let ((obj (make-instance 'xcb:PropertyNotify)) - atom window state - buffer) + atom id buffer) (xcb:unmarshal obj data) (setq id (slot-value obj 'window) atom (slot-value obj 'atom) - exwm-input--timestamp (slot-value obj 'time) - state (slot-value obj 'state)) + exwm-input--timestamp (slot-value obj 'time)) (setq buffer (exwm--id->buffer id)) (when (buffer-live-p buffer) (with-current-buffer buffer @@ -391,7 +289,7 @@ (x-get-atom-name atom exwm-workspace--current) atom))))))) -(defun exwm--on-ClientMessage (raw-data synthetic) +(defun exwm--on-ClientMessage (raw-data _synthetic) "Handle ClientMessage event." (let ((obj (make-instance 'xcb:ClientMessage)) type id data) @@ -420,20 +318,20 @@ ;; _NET_REQUEST_FRAME_EXTENTS ((= type xcb:Atom:_NET_REQUEST_FRAME_EXTENTS) (let ((buffer (exwm--id->buffer id)) - left right top bottom) + left right top btm) (if (or (not buffer) (with-current-buffer buffer (not exwm--floating-frame))) - (setq left 0 right 0 top 0 bottom 0) + (setq left 0 right 0 top 0 btm 0) (setq left exwm-floating-border-width right exwm-floating-border-width top (+ exwm-floating-border-width (window-header-line-height)) - bottom (+ exwm-floating-border-width - (window-mode-line-height)))) + btm (+ exwm-floating-border-width + (window-mode-line-height)))) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_FRAME_EXTENTS :window id :left left :right right - :top top :bottom bottom))) + :top top :bottom btm))) (xcb:flush exwm--connection)) ;; _NET_WM_STATE ((= type xcb:Atom:_NET_WM_STATE) @@ -502,18 +400,18 @@ ((= type xcb:Atom:WM_PROTOCOLS) (let ((type (elt data 0))) (cond ((= type xcb:Atom:_NET_WM_PING) - (setq exwm--ping-lock nil)) + (setq exwm-manage--ping-lock nil)) (t (exwm--log "Unhandled WM_PROTOCOLS of type: %d" type))))) (t (exwm--log "Unhandled client message: %s" obj))))) (defun exwm--init-icccm-ewmh () "Initialize ICCCM/EWMH support." ;; Handle PropertyNotify event - (xcb:+event exwm--connection 'xcb:PropertyNotify 'exwm--on-PropertyNotify) + (xcb:+event exwm--connection 'xcb:PropertyNotify #'exwm--on-PropertyNotify) ;; Handle relevant client messages ;; FIXME: WM_STATE client messages (normal => iconic) ;; WM_COLORMAP_NOTIFY - (xcb:+event exwm--connection 'xcb:ClientMessage 'exwm--on-ClientMessage) + (xcb:+event exwm--connection 'xcb:ClientMessage #'exwm--on-ClientMessage) ;; Set _NET_SUPPORTED (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_SUPPORTED @@ -580,7 +478,7 @@ ;; Set _NET_WORKAREA (with minibuffer and bottom mode-line excluded) (let* ((workareas (vector 0 0 (x-display-pixel-width) (x-display-pixel-height))) - (workareas (mapconcat (lambda (i) workareas) + (workareas (mapconcat (lambda (_) workareas) (make-list exwm-workspace-number 0) []))) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WORKAREA @@ -626,84 +524,14 @@ (exwm-manage--scan) (run-hooks 'exwm-init-hook))))) -(defvar exwm-mode-map - (let ((map (make-sparse-keymap))) - (define-key map "\C-ck" 'exwm-input-release-keyboard) - (define-key map "\C-cf" 'exwm-layout-set-fullscreen) - (define-key map "\C-cm" 'exwm-floating-toggle-floating) - (define-key map "\C-cq" 'exwm-input-send-next-key) - (define-key map "\C-cv" 'exwm-workspace-move-window) - map) - "Keymap for `exwm-mode'.") - -(define-derived-mode exwm-mode nil "EXWM" - "Major mode for managing X windows. - -\\{exwm-mode-map}" - ;; Internal variables - (make-local-variable 'exwm--id) ;window id - (set (make-local-variable 'exwm--frame) nil) ;workspace frame - (set (make-local-variable 'exwm--floating-frame) nil) ;floating frame - (set (make-local-variable 'exwm--floating-edges) nil) ;four edges - (set (make-local-variable 'exwm--fullscreen) nil) ;used in fullscreen - (set (make-local-variable 'exwm--floating-frame-geometry) nil) ;in fullscreen - (set (make-local-variable 'exwm--fixed-size) nil) ;fixed size - (set (make-local-variable 'exwm--on-KeyPress) ;KeyPress event handler - 'exwm-input--on-KeyPress-line-mode) - ;; Properties - (set (make-local-variable 'exwm-window-type) nil) - (set (make-local-variable 'exwm--geometry) nil) - (set (make-local-variable 'exwm-class-name) nil) - (set (make-local-variable 'exwm-instance-name) nil) - (set (make-local-variable 'exwm-title) nil) - (set (make-local-variable 'exwm--title-is-utf8) nil) - (set (make-local-variable 'exwm-transient-for) nil) - ;; _NET_WM_NORMAL_HINTS - (set (make-local-variable 'exwm--normal-hints-x) nil) - (set (make-local-variable 'exwm--normal-hints-y) nil) - (set (make-local-variable 'exwm--normal-hints-width) nil) - (set (make-local-variable 'exwm--normal-hints-height) nil) - (set (make-local-variable 'exwm--normal-hints-min-width) nil) - (set (make-local-variable 'exwm--normal-hints-min-height) nil) - (set (make-local-variable 'exwm--normal-hints-max-width) nil) - (set (make-local-variable 'exwm--normal-hints-max-height) nil) - ;; (set (make-local-variable 'exwm--normal-hints-win-gravity) nil) - ;; WM_HINTS - (set (make-local-variable 'exwm--hints-input) nil) ;FIXME - (set (make-local-variable 'exwm--hints-urgency) nil) - ;; - (set (make-local-variable 'exwm--protocols) nil) - (set (make-local-variable 'exwm-state) nil) - ;; Change major-mode is not allowed - (set (make-local-variable 'change-major-mode-hook) 'kill-buffer) - ;; - (setq mode-name - '(:eval (propertize "EXWM" 'face - (when (cl-some (lambda (i) - (frame-parameter i - 'exwm--urgency)) - exwm-workspace--list) - 'font-lock-warning-face)))) - ;; Kill buffer -> close window - (set (make-local-variable 'kill-buffer-query-functions) - (list (lambda () - (exwm-manage--close-window exwm--id (current-buffer)) - nil))) - (setq buffer-read-only t - left-margin-width nil - right-margin-width nil - left-fringe-width 0 - right-fringe-width 0 - vertical-scroll-bar nil)) - (defun exwm-enable (&optional undo) - "Enable/Disable EXWM" + "Enable/Disable EXWM." (if (eq undo 'undo) - (progn (remove-hook 'window-setup-hook 'exwm-init) - (remove-hook 'after-make-frame-functions 'exwm-init)) - (setq frame-resize-pixelwise t) ;mandatory; before init - (add-hook 'window-setup-hook 'exwm-init t) ;for Emacs - (add-hook 'after-make-frame-functions 'exwm-init t))) ;for Emacs Client + (progn (remove-hook 'window-setup-hook #'exwm-init) + (remove-hook 'after-make-frame-functions #'exwm-init)) + (setq frame-resize-pixelwise t) ;mandatory; before init + (add-hook 'window-setup-hook #'exwm-init t) ;for Emacs + (add-hook 'after-make-frame-functions #'exwm-init t))) ;for Emacs Client (defun exwm--ido-buffer-window-other-frame (orig-fun buffer) "Wrapper for `ido-buffer-window-other-frame' to exclude invisible windows." @@ -717,17 +545,17 @@ (defun exwm--fix-ido-buffer-window-other-frame () "Fix `ido-buffer-window-other-frame'." (advice-add 'ido-buffer-window-other-frame :around - 'exwm--ido-buffer-window-other-frame)) + #'exwm--ido-buffer-window-other-frame)) (defun exwm-enable-ido-workaround () - "Enable workarounds for `ido-mode'." - (add-hook 'exwm-init-hook 'exwm--fix-ido-buffer-window-other-frame)) + "Enable workarounds for 'ido-mode'." + (add-hook 'exwm-init-hook #'exwm--fix-ido-buffer-window-other-frame)) (defun exwm-disable-ido-workaround () - "Disable workarounds for `ido-mode'." - (remove-hook 'exwm-init-hook 'exwm--fix-ido-buffer-window-other-frame) + "Disable workarounds for 'ido-mode'." + (remove-hook 'exwm-init-hook #'exwm--fix-ido-buffer-window-other-frame) (advice-remove 'ido-buffer-window-other-frame - 'exwm--ido-buffer-window-other-frame)) + #'exwm--ido-buffer-window-other-frame)) -- cgit 1.4.1 From 637ac157192dcae0785cc27a613bc59626950592 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 6 Sep 2015 17:18:48 +0800 Subject: Code cleanups * exwm-workspace.el (exwm-workspace--update-switch-history): use `aref' instead of `elt' to index vectors * .elpaignore: ignore README.md --- .elpaignore | 1 + exwm-workspace.el | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 .elpaignore diff --git a/.elpaignore b/.elpaignore new file mode 100644 index 0000000000..b43bf86b50 --- /dev/null +++ b/.elpaignore @@ -0,0 +1 @@ +README.md diff --git a/exwm-workspace.el b/exwm-workspace.el index 9cf1ff0509..2ea8d1fb16 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -72,7 +72,7 @@ (dolist (i exwm--id-buffer-alist) (with-current-buffer (cdr i) (when exwm--frame - (setf (elt not-empty (cl-position exwm--frame exwm-workspace--list)) + (setf (aref not-empty (cl-position exwm--frame exwm-workspace--list)) t)))) (setq exwm-workspace--switch-history (mapcar @@ -86,7 +86,7 @@ (cond ((frame-parameter (elt exwm-workspace--list j) 'exwm--urgency) '(:foreground "orange")) - ((elt not-empty j) '(:foreground "green")) + ((aref not-empty j) '(:foreground "green")) (t nil))))) sequence "")) sequence)))) -- cgit 1.4.1 From eafd031c556ecfe986c656ca72fcde7e06ac819a Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 7 Sep 2015 17:33:22 +0800 Subject: Allow hide/show mode-line on floating frames * exwm-core.el: new buffer-local variable exwm--floating-mode-line-format for saving mode-line-format when mode-line is hidden * exwm-floating.el (exwm-floating--fit-frame-to-window) (exwm-floating-hide-mode-line, exwm-floating-show-mode-line): new functions for resizing frames, hiding/showing mode-line respectively; (exwm-floating--set-floating): use exwm-floating--fit-frame-to-window to resize frames --- exwm-core.el | 15 +++++++------ exwm-floating.el | 65 ++++++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 62 insertions(+), 18 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 9810de4f74..74eb94fc49 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -78,13 +78,14 @@ "Event mask set on all managed windows.") ;; Internal variables -(defvar-local exwm--id nil) ;window ID -(defvar-local exwm--frame nil) ;workspace frame -(defvar-local exwm--floating-frame nil) ;floating frame -(defvar-local exwm--floating-edges nil) ;four edges -(defvar-local exwm--fullscreen nil) ;used in fullscreen -(defvar-local exwm--floating-frame-geometry nil) ;in fullscreen -(defvar-local exwm--fixed-size nil) ;fixed size +(defvar-local exwm--id nil) ;window ID +(defvar-local exwm--frame nil) ;workspace frame +(defvar-local exwm--floating-frame nil) ;floating frame +(defvar-local exwm--floating-edges nil) ;four edges +(defvar-local exwm--floating-mode-line-format nil) ;save mode-line-format +(defvar-local exwm--fullscreen nil) ;used in fullscreen +(defvar-local exwm--floating-frame-geometry nil) ;in fullscreen +(defvar-local exwm--fixed-size nil) ;fixed size (defvar-local exwm--on-KeyPress ;KeyPress event handler #'exwm-input--on-KeyPress-line-mode) ;; Properties diff --git a/exwm-floating.el b/exwm-floating.el index 6bb635a565..4ea495d0d2 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -166,17 +166,7 @@ (+ width exwm-floating-border-width) (+ height exwm-floating-border-width)))) ;; Fit frame to client - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window outer-id - :value-mask (logior xcb:ConfigWindow:Width - xcb:ConfigWindow:Height - xcb:ConfigWindow:StackMode) - :width (+ width (* 2 exwm-floating-border-width)) - :height (+ height (* 2 exwm-floating-border-width) - (window-mode-line-height) - (window-header-line-height)) - :stack-mode xcb:StackMode:Above)) ;top-most + (exwm-floating--fit-frame-to-window outer-id width height) ;; Reparent window to this frame (xcb:+request exwm--connection (make-instance 'xcb:ChangeWindowAttributes @@ -246,6 +236,59 @@ (exwm-floating--unset-floating exwm--id) (exwm-floating--set-floating exwm--id)))) +(defun exwm-floating--fit-frame-to-window (&optional frame-outer-id + width height) + "Resize a floating frame to make it fit the size of the window. + +Default to resize `exwm--floating-frame' unless FRAME-OUTER-ID is non-nil. +This function will issue an `xcb:GetGeometry' request unless WIDTH and HEIGHT +are provided. You should call `xcb:flush' and assign `window-size-fixed' a +non-nil value afterwards." + (setq window-size-fixed nil) + (unless (and width height) + (let ((geometry (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetGeometry :drawable exwm--id)))) + (setq width (slot-value geometry 'width) + height (slot-value geometry 'height)))) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (or frame-outer-id + (frame-parameter exwm--floating-frame + 'exwm-outer-id)) + :value-mask (logior xcb:ConfigWindow:Width + xcb:ConfigWindow:Height + xcb:ConfigWindow:StackMode) + :width (+ width (* 2 exwm-floating-border-width)) + :height (+ height (* 2 exwm-floating-border-width) + (window-mode-line-height) + (window-header-line-height)) + :stack-mode xcb:StackMode:Above))) ;top-most + +(defun exwm-floating-hide-mode-line () + "Hide mode-line of a floating frame." + (interactive) + (unless (eq major-mode 'exwm-mode) + (user-error "[EXWM] Please use this command with EXWM buffers")) + (when (and exwm--floating-frame mode-line-format) + (setq exwm--floating-mode-line-format mode-line-format + mode-line-format nil) + (exwm-floating--fit-frame-to-window) + (xcb:flush exwm--connection) + (setq window-size-fixed t))) + +(defun exwm-floating-show-mode-line () + "Show mode-line of a floating frame." + (interactive) + (unless (eq major-mode 'exwm-mode) + (user-error "[EXWM] Please use this command with EXWM buffers")) + (when (and exwm--floating-frame (not mode-line-format)) + (setq mode-line-format exwm--floating-mode-line-format + exwm--floating-mode-line-format nil) + (exwm-floating--fit-frame-to-window) + (exwm-input-grab-keyboard) ;mode-line-format may be outdated + (xcb:flush exwm--connection) + (setq window-size-fixed t))) + (defvar exwm-floating--moveresize-calculate nil "Calculate move/resize parameters [frame-id event-mask x y width height].") -- cgit 1.4.1 From 5373c1df1a8432f6a54e8ded8c9a145842c067e2 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 9 Sep 2015 11:26:17 +0800 Subject: Add support for xcb:Atom:_NET_CLIENT_LIST_STACKING etc The _NET_CLIENT_LIST_STACKING EWMH property is essential for e.g. the tabbar of chromium to work correctly. * exwm-input.el: Remove invalid TODO item. * exwm.el (exwm--init-icccm-ewmh): Add xcb:Atom:_NET_CLIENT_LIST and xcb:Atom:_NET_CLIENT_LIST_STACKING to _NET_SUPPORTED. * exwm-layout.el (exwm-layout--refresh): Update _NET_CLIENT_LIST_STACKING. * exwm-manage.el (exwm-manage--manage-window, exwm-manage--unmanage-window): Update _NET_CLIENT_LIST. --- exwm-input.el | 1 - exwm-layout.el | 15 ++++++++++++++- exwm-manage.el | 9 +++++++++ exwm.el | 2 ++ 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 69d2aff8e2..afa619b873 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -32,7 +32,6 @@ ;; + Simulation keys to mimic Emacs key bindings for text edit (redo, select, ;; cancel, clear, etc). Some of them are not present on common keyboard ;; (keycode = 0). May need to use XKB extension. -;; + Investigate DnD support (e.g. drag a chromium tab to another window). ;;; Code: diff --git a/exwm-layout.el b/exwm-layout.el index a14651a46d..ae1f48de8f 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -239,7 +239,20 @@ (with-current-buffer (window-buffer window) (when (and (eq major-mode 'exwm-mode) (or exwm--floating-frame (not (eq frame exwm--frame)))) - (set-window-buffer window placeholder))))))) + (set-window-buffer window placeholder)))) + ;; Update _NET_CLIENT_LIST_STACKING + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_CLIENT_LIST_STACKING + :window exwm--root + :data (vconcat + (delq nil + (mapcar + (lambda (buffer) + (with-current-buffer buffer + (when (eq major-mode 'exwm-mode) + exwm--id))) + (buffer-list)))))) + (xcb:flush exwm--connection)))) (defun exwm-layout--on-minibuffer-setup () "Refresh layout when minibuffer grows." diff --git a/exwm-manage.el b/exwm-manage.el index a8d0e592a8..2636c016b8 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -128,6 +128,10 @@ corresponding buffer.") :cursor xcb:Cursor:None :button xcb:ButtonIndex:Any :modifiers xcb:ModMask:Any)) + (xcb:+request exwm--connection ;update _NET_CLIENT_LIST + (make-instance 'xcb:ewmh:set-_NET_CLIENT_LIST + :window exwm--root + :data (vconcat (mapcar #'car exwm--id-buffer-alist)))) (xcb:flush exwm--connection) (exwm--update-title id) (exwm--update-transient-for id) @@ -150,6 +154,11 @@ corresponding buffer.") (let ((buffer (exwm--id->buffer id))) (exwm--log "Unmanage #x%x (buffer: %s)" id buffer) (setq exwm--id-buffer-alist (assq-delete-all id exwm--id-buffer-alist)) + (xcb:+request exwm--connection ;update _NET_CLIENT_LIST + (make-instance 'xcb:ewmh:set-_NET_CLIENT_LIST + :window exwm--root + :data (vconcat (mapcar #'car exwm--id-buffer-alist)))) + (xcb:flush exwm--connection) (when (buffer-live-p buffer) (with-current-buffer buffer (exwm-workspace--update-switch-history) diff --git a/exwm.el b/exwm.el index 84fbc15255..bf45e40c8b 100644 --- a/exwm.el +++ b/exwm.el @@ -417,6 +417,8 @@ (make-instance 'xcb:ewmh:set-_NET_SUPPORTED :window exwm--root :data (vector xcb:Atom:_NET_SUPPORTED + xcb:Atom:_NET_CLIENT_LIST + xcb:Atom:_NET_CLIENT_LIST_STACKING xcb:Atom:_NET_NUMBER_OF_DESKTOPS xcb:Atom:_NET_DESKTOP_VIEWPORT xcb:Atom:_NET_CURRENT_DESKTOP -- cgit 1.4.1 From 3f2f84456997a46e047adbf45c4c44b628ae1377 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 9 Sep 2015 19:08:51 +0800 Subject: Add a command to interactively move X window to the current workspace * exwm-workspace.el (exwm-workspace-move-window): Hide buffer on the original Emacs window when moving an X window to the current workspace. * exwm-workspace.el: New function exwm-workspace-switch-to-window for interactively moving an X window on another workspace to the current one. --- exwm-workspace.el | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 2ea8d1fb16..5fae713f57 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -174,7 +174,7 @@ The optional FORCE option is for internal use only." (user-error "[EXWM] Workspace index out of range: %d" index)) (with-current-buffer (exwm--id->buffer id) (let ((frame (elt exwm-workspace--list index))) - (when (not (equal exwm--frame frame)) + (unless (eq exwm--frame frame) (let ((name (replace-regexp-in-string "^\\s-*" "" (buffer-name)))) (exwm-workspace-rename-buffer (if (= index exwm-workspace-current-index) name (concat " " name)))) @@ -191,7 +191,12 @@ The optional FORCE option is for internal use only." :x 0 :y 0)) (xcb:flush exwm--connection)) ;; Move the window itself - (bury-buffer) + (if (/= index exwm-workspace-current-index) + (bury-buffer) + (set-window-buffer (get-buffer-window (current-buffer) t) + (or (get-buffer "*scratch*") + (prog1 (get-buffer-create "*scratch*") + (set-buffer-major-mode "*scratch*"))))) (exwm-layout--hide id) (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow @@ -203,6 +208,28 @@ The optional FORCE option is for internal use only." (exwm--id->buffer id))))) (exwm-workspace--update-switch-history))) +(defun exwm-workspace-switch-to-window () + "Make the current Emacs window display another X window." + (interactive) + ;; Show all buffers + (dolist (pair exwm--id-buffer-alist) + (with-current-buffer (cdr pair) + (when (= ?\s (aref (buffer-name) 0)) + (rename-buffer (substring (buffer-name) 1))))) + (let ((buffer (read-buffer "Switch to window: " nil t))) + (when buffer + (with-current-buffer buffer + (when (and (eq major-mode 'exwm-mode) + (not (eq exwm--frame exwm-workspace--current))) + (exwm-workspace-move-window exwm-workspace-current-index + exwm--id))))) + ;; Hide buffers on other workspaces + (dolist (pair exwm--id-buffer-alist) + (with-current-buffer (cdr pair) + (unless (or (eq exwm--frame exwm-workspace--current) + (= ?\s (aref (buffer-name) 0))) + (rename-buffer (concat " " (buffer-name))))))) + (defun exwm-workspace-rename-buffer (newname) "Rename a buffer." (let ((hidden (= ?\s (aref newname 0))) -- cgit 1.4.1 From 1d435157d3c9b3f807aca55bb4e6d16d5cb6c5df Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 11 Sep 2015 08:07:53 +0800 Subject: Allow switch to normal buffers in exwm-workspace-switch-to-window * exwm-workspace.el (exwm-workspace-switch-to-window): Allow switch to normal buffers. --- exwm-workspace.el | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 5fae713f57..d2619eed94 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -219,10 +219,11 @@ The optional FORCE option is for internal use only." (let ((buffer (read-buffer "Switch to window: " nil t))) (when buffer (with-current-buffer buffer - (when (and (eq major-mode 'exwm-mode) - (not (eq exwm--frame exwm-workspace--current))) - (exwm-workspace-move-window exwm-workspace-current-index - exwm--id))))) + (if (and (eq major-mode 'exwm-mode) + (not (eq exwm--frame exwm-workspace--current))) + (exwm-workspace-move-window exwm-workspace-current-index + exwm--id) + (switch-to-buffer buffer))))) ;; Hide buffers on other workspaces (dolist (pair exwm--id-buffer-alist) (with-current-buffer (cdr pair) -- cgit 1.4.1 From dbcabe7946592c0af550c31b9a47a9856fd2d501 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 11 Sep 2015 17:13:43 +0800 Subject: Implement move/resize with keyboard * exwm-floating.el: Remove an invalid TODO item. * exwm-floating.el (exwm-floating--set-floating) (exwm-floating-hide-mode-line, exwm-floating-show-mode-line): Set window-size-fixed only for fixed-size floating windows. * exwm-floating.el (exwm-floating-move): New function for moving a floating window. * exwm-layout.el (exwm-layout-enlarge-window) (exwm-layout-enlarge-window-horizontally, exwm-layout-shrink-window) (exwm-layout-shrink-window-horizontally): New commands for interactively resizing a floating window. --- exwm-floating.el | 34 +++++++++++++++----- exwm-layout.el | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 8 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 4ea495d0d2..04493764bd 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -24,9 +24,6 @@ ;; This module deals with the conversion between floating and non-floating ;; states and implements moving/resizing operations on floating windows. -;; Todo: -;; + move/resize with keyboard. - ;;; Code: (require 'xcb-cursor) @@ -184,7 +181,7 @@ (xcb:flush exwm--connection) ;; Set window/buffer (with-current-buffer (exwm--id->buffer id) - (setq window-size-fixed t ;make frame fixed size + (setq window-size-fixed exwm--fixed-size exwm--frame original-frame exwm--floating-frame frame) (set-window-buffer window (current-buffer)) ;this changes current buffer @@ -242,8 +239,8 @@ Default to resize `exwm--floating-frame' unless FRAME-OUTER-ID is non-nil. This function will issue an `xcb:GetGeometry' request unless WIDTH and HEIGHT -are provided. You should call `xcb:flush' and assign `window-size-fixed' a -non-nil value afterwards." +are provided. You should call `xcb:flush' and restore the value of +`window-size-fixed' afterwards." (setq window-size-fixed nil) (unless (and width height) (let ((geometry (xcb:+request-unchecked+reply exwm--connection @@ -274,7 +271,7 @@ non-nil value afterwards." mode-line-format nil) (exwm-floating--fit-frame-to-window) (xcb:flush exwm--connection) - (setq window-size-fixed t))) + (setq window-size-fixed exwm--fixed-size))) (defun exwm-floating-show-mode-line () "Show mode-line of a floating frame." @@ -287,7 +284,7 @@ non-nil value afterwards." (exwm-floating--fit-frame-to-window) (exwm-input-grab-keyboard) ;mode-line-format may be outdated (xcb:flush exwm--connection) - (setq window-size-fixed t))) + (setq window-size-fixed exwm--fixed-size))) (defvar exwm-floating--moveresize-calculate nil "Calculate move/resize parameters [frame-id event-mask x y width height].") @@ -462,6 +459,27 @@ non-nil value afterwards." :width (elt result 4) :height (elt result 5))) (xcb:flush exwm--connection)))) +(defun exwm-floating-move (&optional delta-x delta-y) + "Move a floating window right by DELTA-X pixels and down by DELTA-Y pixels. + +Both DELTA-X and DELTA-Y default to 1. This command should be bound locally." + (unless (and (eq major-mode 'exwm-mode) exwm--floating-frame) + (user-error "[EXWM] `exwm-floating-move' is only for floating X windows")) + (unless delta-x (setq delta-x 1)) + (unless delta-y (setq delta-y 1)) + (unless (and (= 0 delta-x) (= 0 delta-y)) + (let* ((id (frame-parameter exwm--floating-frame 'exwm-outer-id)) + (geometry (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetGeometry :drawable id)))) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window id + :value-mask (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y) + :x (+ (slot-value geometry 'x) delta-x) + :y (+ (slot-value geometry 'y) delta-y)))) + (xcb:flush exwm--connection))) + (defun exwm-floating--init () "Initialize floating module." ;; Initialize cursors for moving/resizing a window diff --git a/exwm-layout.el b/exwm-layout.el index ae1f48de8f..27533a5ebd 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -263,6 +263,103 @@ exwm--floating-frame))) (exwm-layout--refresh))))) +(defun exwm-layout-enlarge-window (delta &optional horizontal) + "Make the selected window DELTA pixels taller. + +If no argument is given, make the selected window one pixel taller. If the +optional argument HORIZONTAL is non-nil, make selected window DELTA pixels +wider. If DELTA is negative, shrink selected window by -DELTA pixels. + +Normal hints are checked and regarded if the selected window is displaying an +`exwm-mode' buffer. However, this may violate the normal hints set on other X +windows." + (interactive "p") + (cond + ((zerop delta)) ;no operation + ((window-minibuffer-p)) ;avoid resize minibuffer-window + ((not (and (eq major-mode 'exwm-mode) exwm--floating-frame)) + ;; Resize on tiling layout + (unless (= 0 (window-resizable nil delta horizontal nil t)) ;not resizable + (let ((window-resize-pixelwise t)) + (window-resize nil delta horizontal nil t)))) + ;; Resize on floating layout + (exwm--fixed-size) ;fixed size + (horizontal + (let* ((width (frame-pixel-width)) + (edges (window-inside-pixel-edges)) + (inner-width (- (elt edges 2) (elt edges 0))) + (margin (- width inner-width))) + (if (> delta 0) + (if (not exwm--normal-hints-max-width) + (cl-incf width delta) + (if (>= inner-width exwm--normal-hints-max-width) + (setq width nil) + (setq width (min (+ exwm--normal-hints-max-width margin) + (+ width delta))))) + (if (not exwm--normal-hints-min-width) + (cl-incf width delta) + (if (<= inner-width exwm--normal-hints-min-width) + (setq width nil) + (setq width (max (+ exwm--normal-hints-min-width margin) + (+ width delta)))))) + (when width + (setq exwm--floating-edges nil) ;invalid from now on + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter exwm--floating-frame + 'exwm-outer-id) + :value-mask xcb:ConfigWindow:Width + :width width)) + (xcb:flush exwm--connection)))) + (t + (let* ((height (frame-pixel-height)) + (edges (window-inside-pixel-edges)) + (inner-height (- (elt edges 3) (elt edges 1))) + (margin (- height inner-height))) + (if (> delta 0) + (if (not exwm--normal-hints-max-height) + (cl-incf height delta) + (if (>= inner-height exwm--normal-hints-max-height) + (setq height nil) + (setq height (min (+ exwm--normal-hints-max-height margin) + (+ height delta))))) + (if (not exwm--normal-hints-min-height) + (cl-incf height delta) + (if (<= inner-height exwm--normal-hints-min-height) + (setq height nil) + (setq height (max (+ exwm--normal-hints-min-height margin) + (+ height delta)))))) + (when height + (setq exwm--floating-edges nil) ;invalid from now on + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter exwm--floating-frame + 'exwm-outer-id) + :value-mask xcb:ConfigWindow:Height + :height height)) + (xcb:flush exwm--connection)))))) + +(defun exwm-layout-enlarge-window-horizontally (delta) + "Make the selected window DELTA pixels wider. + +See also `exwm-layout-enlarge-window'." + (interactive "p") + (exwm-layout-enlarge-window delta t)) + +(defun exwm-layout-shrink-window (delta) + "Make the selected window DELTA pixels lower. + +See also `exwm-layout-enlarge-window'." + (interactive "p") + (exwm-layout-enlarge-window (- delta))) + +(defun exwm-layout-shrink-window-horizontally (delta) + "Make the selected window DELTA pixels narrower. + +See also `exwm-layout-enlarge-window'." + (interactive "p") + (exwm-layout-enlarge-window (- delta) t)) + (defun exwm-layout--init () "Initialize layout module." ;; Auto refresh layout -- cgit 1.4.1 From cfbd9a5a451cf57149215accbecc75ff7b4a8c3d Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 11 Sep 2015 17:34:06 +0800 Subject: Minor fix for window move * exwm-workspace.el (exwm-workspace-move-window): Move to selected instead of the first window of a frame (workspace). --- exwm-workspace.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index d2619eed94..40dd57ca69 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -204,7 +204,7 @@ The optional FORCE option is for internal use only." :parent (frame-parameter frame 'exwm-window-id) :x 0 :y 0)) (xcb:flush exwm--connection) - (set-window-buffer (frame-first-window frame) + (set-window-buffer (frame-selected-window frame) (exwm--id->buffer id))))) (exwm-workspace--update-switch-history))) -- cgit 1.4.1 From 576a676f1f0895bd473d54c2713ee9e2423023e6 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 11 Sep 2015 21:00:27 +0800 Subject: Fix with-slots * exwm-randr.el (exwm-randr--refresh): Could not set the name slot in xcb:randr:GetOutputInfo~reply, turn to another variable. --- exwm-randr.el | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/exwm-randr.el b/exwm-randr.el index d14e0974c7..fc8477d2aa 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -48,7 +48,7 @@ (defun exwm-randr--refresh () "Refresh workspaces according to the updated RandR info." - (let (geometry output-plist default-geometry workareas viewports) + (let (output-name geometry output-plist default-geometry workareas viewports) ;; Query all outputs (with-slots (config-timestamp outputs) (xcb:+request-unchecked+reply exwm--connection @@ -60,11 +60,11 @@ (make-instance 'xcb:randr:GetOutputInfo :output output :config-timestamp config-timestamp)) - (setq name ;UTF-8 encoded + (setf output-name ;UTF-8 encoded (decode-coding-string (apply #'unibyte-string name) 'utf-8)) (if (or (/= connection xcb:randr:Connection:Connected) (= 0 crtc)) ;FIXME - (plist-put output-plist name nil) + (plist-put output-plist output-name nil) (with-slots (x y width height) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:randr:GetCrtcInfo @@ -73,7 +73,7 @@ (setq geometry (make-instance 'xcb:RECTANGLE :x x :y y :width width :height height) - output-plist (plist-put output-plist name geometry)) + output-plist (plist-put output-plist output-name geometry)) (unless default-geometry ;assume the first output as primary (setq default-geometry geometry))))))) (cl-assert (<= 2 (length output-plist))) -- cgit 1.4.1 From b458d5ac30afed348df4788721bb48be94e97c60 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 16 Sep 2015 21:32:38 +0800 Subject: Allow showing buffers on other workspaces and moving an X window by switching to its buffer * exwm-workspace.el (exwm-workspace-show-all-buffers, exwm-workspace-switch) (exwm-workspace-move-window, exwm-workspace-switch-to-buffer): Show buffers on other workspaces if `exwm-workspace-show-all-buffers' is non-nil. * exwm-layout.el (exwm-layout-show-all-buffers, exwm-layout--refresh): Allow moving an X window by switch to its corresponding buffer from another workspace when `exwm-layout-show-all-buffers' is non-nil. * exwm.el (exwm--ido-buffer-window-other-frame): Handle the case when `exwm-layout-show-all-buffers' is non-nil. * exwm-floating.el (exwm-floating--set-floating): Handle the case when *scratch* buffer is killed. * exwm-workspace.el (exwm-workspace-switch-to-buffer): Renamed from `exwm-workspace-switch-to-window' to better reflect its role. --- exwm-floating.el | 5 ++++- exwm-layout.el | 31 +++++++++++++++++++++---------- exwm-workspace.el | 51 ++++++++++++++++++++++++++++++--------------------- exwm.el | 4 +++- 4 files changed, 58 insertions(+), 33 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 04493764bd..12390b0ea2 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -67,7 +67,10 @@ exwm-workspace--current))) (original-id (frame-parameter original-frame 'exwm-window-id)) ;; Create new frame - (frame (with-current-buffer "*scratch*" + (frame (with-current-buffer + (or (get-buffer "*scratch*") + (prog1 (get-buffer-create "*scratch*") + (set-buffer-major-mode "*scratch*"))) (prog2 (exwm--lock) (make-frame diff --git a/exwm-layout.el b/exwm-layout.el index 27533a5ebd..2c9ab750b4 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -194,10 +194,15 @@ :height height)) (xcb:flush exwm--connection)))) +(defvar exwm-layout-show-all-buffers nil + "Non-nil to allow switching to buffers on other workspaces.") + (defun exwm-layout--refresh () "Refresh layout." (let ((frame (selected-frame)) - (placeholder (get-buffer "*scratch*")) + (placeholder (or (get-buffer "*scratch*") + (prog1 (get-buffer-create "*scratch*") + (set-buffer-major-mode "*scratch*")))) windows) (if (not (memq frame exwm-workspace--list)) (if (frame-parameter frame 'exwm-window-id) @@ -221,19 +226,25 @@ ;; Refresh the whole workspace ;; Workspaces other than the active one can also be refreshed (RandR) (exwm--log "Refresh workspace %s" frame) - (unless placeholder ;create the *scratch* buffer if it's killed - (setq placeholder (get-buffer-create "*scratch*")) - (set-buffer-major-mode placeholder)) (dolist (pair exwm--id-buffer-alist) (with-current-buffer (cdr pair) - ;; Exclude windows on other workspaces and floating frames - (when (and (eq frame exwm--frame) (not exwm--floating-frame)) + (when (and (not exwm--floating-frame) ;exclude floating X windows + (or exwm-layout-show-all-buffers + ;; Exclude X windows on other workspaces + (eq frame exwm--frame))) (setq windows (get-buffer-window-list (current-buffer) 0)) (if (not windows) - (exwm-layout--hide exwm--id) - (exwm-layout--show exwm--id (car windows)) - (dolist (i (cdr windows)) - (set-window-buffer i placeholder)))))) + (when (eq frame exwm--frame) ;for exwm-layout-show-all-buffers + (exwm-layout--hide exwm--id)) + (if (eq frame exwm--frame) + (exwm-layout--show exwm--id (car windows)) + (exwm-workspace-move-window + (cl-position frame exwm-workspace--list) exwm--id)) + (let ((window (car windows))) + ;; Make sure this buffer is not displayed elsewhere + (dolist (i (get-buffer-window-list (current-buffer) 0 t)) + (unless (eq i window) + (set-window-buffer i placeholder)))))))) ;; Make sure windows floating / on other workspaces are excluded (dolist (window (window-list frame 0)) (with-current-buffer (window-buffer window) diff --git a/exwm-workspace.el b/exwm-workspace.el index 40dd57ca69..fcc42e2733 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -93,6 +93,8 @@ (defvar exwm-workspace--current nil "Current active workspace.") (defvar exwm-workspace-current-index 0 "Index of current active workspace.") +(defvar exwm-workspace-show-all-buffers nil + "Non-nil to show buffers on other workspaces.") (defun exwm-workspace-switch (index &optional force) "Switch to workspace INDEX. Query for INDEX if it's not specified. @@ -131,12 +133,14 @@ The optional FORCE option is for internal use only." (set-mouse-pixel-position frame x y))) (setq default-minibuffer-frame frame) ;; Hide windows in other workspaces by preprending a space - (dolist (i exwm--id-buffer-alist) - (with-current-buffer (cdr i) - (let ((name (replace-regexp-in-string "^\\s-*" "" (buffer-name)))) - (exwm-workspace-rename-buffer (if (eq frame exwm--frame) - name - (concat " " name)))))) + (unless exwm-workspace-show-all-buffers + (dolist (i exwm--id-buffer-alist) + (with-current-buffer (cdr i) + (let ((name (replace-regexp-in-string "^\\s-*" "" + (buffer-name)))) + (exwm-workspace-rename-buffer (if (eq frame exwm--frame) + name + (concat " " name))))))) ;; Update demands attention flag (set-frame-parameter frame 'exwm--urgency nil) ;; Update switch workspace history @@ -175,9 +179,12 @@ The optional FORCE option is for internal use only." (with-current-buffer (exwm--id->buffer id) (let ((frame (elt exwm-workspace--list index))) (unless (eq exwm--frame frame) - (let ((name (replace-regexp-in-string "^\\s-*" "" (buffer-name)))) - (exwm-workspace-rename-buffer - (if (= index exwm-workspace-current-index) name (concat " " name)))) + (unless exwm-workspace-show-all-buffers + (let ((name (replace-regexp-in-string "^\\s-*" "" (buffer-name)))) + (exwm-workspace-rename-buffer + (if (= index exwm-workspace-current-index) + name + (concat " " name))))) (setq exwm--frame frame) (if exwm--floating-frame ;; Move the floating frame is enough @@ -208,15 +215,16 @@ The optional FORCE option is for internal use only." (exwm--id->buffer id))))) (exwm-workspace--update-switch-history))) -(defun exwm-workspace-switch-to-window () - "Make the current Emacs window display another X window." +(defun exwm-workspace-switch-to-buffer () + "Make the current Emacs window display another buffer." (interactive) ;; Show all buffers - (dolist (pair exwm--id-buffer-alist) - (with-current-buffer (cdr pair) - (when (= ?\s (aref (buffer-name) 0)) - (rename-buffer (substring (buffer-name) 1))))) - (let ((buffer (read-buffer "Switch to window: " nil t))) + (unless exwm-workspace-show-all-buffers + (dolist (pair exwm--id-buffer-alist) + (with-current-buffer (cdr pair) + (when (= ?\s (aref (buffer-name) 0)) + (rename-buffer (substring (buffer-name) 1)))))) + (let ((buffer (read-buffer "Switch to buffer: " nil t))) (when buffer (with-current-buffer buffer (if (and (eq major-mode 'exwm-mode) @@ -225,11 +233,12 @@ The optional FORCE option is for internal use only." exwm--id) (switch-to-buffer buffer))))) ;; Hide buffers on other workspaces - (dolist (pair exwm--id-buffer-alist) - (with-current-buffer (cdr pair) - (unless (or (eq exwm--frame exwm-workspace--current) - (= ?\s (aref (buffer-name) 0))) - (rename-buffer (concat " " (buffer-name))))))) + (unless exwm-workspace-show-all-buffers + (dolist (pair exwm--id-buffer-alist) + (with-current-buffer (cdr pair) + (unless (or (eq exwm--frame exwm-workspace--current) + (= ?\s (aref (buffer-name) 0))) + (rename-buffer (concat " " (buffer-name)))))))) (defun exwm-workspace-rename-buffer (newname) "Rename a buffer." diff --git a/exwm.el b/exwm.el index bf45e40c8b..9311af1c8d 100644 --- a/exwm.el +++ b/exwm.el @@ -538,7 +538,9 @@ (defun exwm--ido-buffer-window-other-frame (orig-fun buffer) "Wrapper for `ido-buffer-window-other-frame' to exclude invisible windows." (with-current-buffer buffer - (if (eq major-mode 'exwm-mode) + (if (and (eq major-mode 'exwm-mode) + (or exwm--floating-frame + (not exwm-layout-show-all-buffers))) ;; `ido-mode' works well with `exwm-mode' buffers (funcall orig-fun buffer) ;; Other buffers should be selected within the same workspace -- cgit 1.4.1 From 4255f078a690751a20377372be1352698e041ad0 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 17 Sep 2015 19:48:50 +0800 Subject: On-demand update exwm-workspace--switch-history * exwm-workspace.el (exwm-workspace--switch-history-outdated) (exwm-workspace--update-switch-history, exwm-workspace-switch) (exwm-workspace-move-window): * exwm.el (exwm--update-hints, exwm--on-ClientMessage): * exwm-floating.el (exwm-floating--set-floating): * exwm-manage.el (exwm-manage--manage-window, exwm-manage--unmanage-window): Update exwm-workspace--switch-history only when it's used. --- exwm-floating.el | 2 +- exwm-manage.el | 4 +-- exwm-workspace.el | 75 +++++++++++++++++++++++++++++++------------------------ exwm.el | 4 +-- 4 files changed, 47 insertions(+), 38 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 12390b0ea2..346f02e8ca 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -94,7 +94,7 @@ (let ((idx (cl-position original-frame exwm-workspace--list))) (when (/= idx exwm-workspace-current-index) (set-frame-parameter original-frame 'exwm--urgency t) - (exwm-workspace--update-switch-history))) + (setq exwm-workspace--switch-history-outdated t))) ;; Fix illegal parameters ;; FIXME: check normal hints restrictions (let* ((display-width (frame-pixel-width original-frame)) diff --git a/exwm-manage.el b/exwm-manage.el index 2636c016b8..3e3c848a56 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -145,7 +145,7 @@ corresponding buffer.") (exwm-floating--set-floating id) (exwm-floating--unset-floating id)) (exwm-input-grab-keyboard id) - (exwm-workspace--update-switch-history) + (setq exwm-workspace--switch-history-outdated t) (with-current-buffer (exwm--id->buffer id) (run-hooks 'exwm-manage-finish-hook))))) @@ -161,7 +161,7 @@ corresponding buffer.") (xcb:flush exwm--connection) (when (buffer-live-p buffer) (with-current-buffer buffer - (exwm-workspace--update-switch-history) + (setq exwm-workspace--switch-history-outdated t) ;; (when withdraw-only ;; Reparent back to root diff --git a/exwm-workspace.el b/exwm-workspace.el index fcc42e2733..a6f21b2794 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -63,33 +63,39 @@ (defvar exwm-workspace--switch-history nil "History for `read-from-minibuffer' to interactively switch workspace.") +;;;###autoload +(defvar exwm-workspace--switch-history-outdated nil + "Non-nil to indicate `exwm-workspace--switch-history' is outdated.") ;;;###autoload (defun exwm-workspace--update-switch-history () "Update the history for switching workspace to reflect the latest status." - (let ((sequence (number-sequence 0 (1- exwm-workspace-number))) - (not-empty (make-vector exwm-workspace-number nil))) - (dolist (i exwm--id-buffer-alist) - (with-current-buffer (cdr i) - (when exwm--frame - (setf (aref not-empty (cl-position exwm--frame exwm-workspace--list)) - t)))) - (setq exwm-workspace--switch-history - (mapcar - (lambda (i) - (mapconcat - (lambda (j) - (format (if (= i j) "[%s]" " %s ") - (propertize - (int-to-string j) - 'face - (cond ((frame-parameter (elt exwm-workspace--list j) - 'exwm--urgency) - '(:foreground "orange")) - ((aref not-empty j) '(:foreground "green")) - (t nil))))) - sequence "")) - sequence)))) + (when exwm-workspace--switch-history-outdated + (setq exwm-workspace--switch-history-outdated nil) + (let ((sequence (number-sequence 0 (1- exwm-workspace-number))) + (not-empty (make-vector exwm-workspace-number nil))) + (dolist (i exwm--id-buffer-alist) + (with-current-buffer (cdr i) + (when exwm--frame + (setf (aref not-empty + (cl-position exwm--frame exwm-workspace--list)) + t)))) + (setq exwm-workspace--switch-history + (mapcar + (lambda (i) + (mapconcat + (lambda (j) + (format (if (= i j) "[%s]" " %s ") + (propertize + (int-to-string j) + 'face + (cond ((frame-parameter (elt exwm-workspace--list j) + 'exwm--urgency) + '(:foreground "orange")) + ((aref not-empty j) '(:foreground "green")) + (t nil))))) + sequence "")) + sequence))))) (defvar exwm-workspace--current nil "Current active workspace.") (defvar exwm-workspace-current-index 0 "Index of current active workspace.") @@ -103,6 +109,7 @@ The optional FORCE option is for internal use only." (interactive (list (unless (and (eq major-mode 'exwm-mode) exwm--fullscreen) ;it's invisible + (exwm-workspace--update-switch-history) (let* ((history-add-new-input nil) ;prevent modifying history (idx (read-from-minibuffer "Workspace: " (elt exwm-workspace--switch-history @@ -144,7 +151,7 @@ The optional FORCE option is for internal use only." ;; Update demands attention flag (set-frame-parameter frame 'exwm--urgency nil) ;; Update switch workspace history - (exwm-workspace--update-switch-history) + (setq exwm-workspace--switch-history-outdated t) (exwm--make-emacs-idle-for 0.1) ;FIXME ;; Update _NET_CURRENT_DESKTOP (xcb:+request exwm--connection @@ -165,14 +172,16 @@ The optional FORCE option is for internal use only." "Move window ID to workspace INDEX." (interactive (list - (let* ((history-add-new-input nil) ;prevent modifying history - (idx (read-from-minibuffer - "Workspace: " (elt exwm-workspace--switch-history - exwm-workspace-current-index) - exwm-workspace--switch-map nil - `(exwm-workspace--switch-history - . ,(1+ exwm-workspace-current-index))))) - (cl-position idx exwm-workspace--switch-history :test #'equal)))) + (progn + (exwm-workspace--update-switch-history) + (let* ((history-add-new-input nil) ;prevent modifying history + (idx (read-from-minibuffer + "Workspace: " (elt exwm-workspace--switch-history + exwm-workspace-current-index) + exwm-workspace--switch-map nil + `(exwm-workspace--switch-history + . ,(1+ exwm-workspace-current-index))))) + (cl-position idx exwm-workspace--switch-history :test #'equal))))) (unless id (setq id (exwm--buffer->id (window-buffer)))) (unless (and (<= 0 index) (< index exwm-workspace-number)) (user-error "[EXWM] Workspace index out of range: %d" index)) @@ -213,7 +222,7 @@ The optional FORCE option is for internal use only." (xcb:flush exwm--connection) (set-window-buffer (frame-selected-window frame) (exwm--id->buffer id))))) - (exwm-workspace--update-switch-history))) + (setq exwm-workspace--switch-history-outdated t))) (defun exwm-workspace-switch-to-buffer () "Make the current Emacs window display another buffer." diff --git a/exwm.el b/exwm.el index 9311af1c8d..d2a0ecf144 100644 --- a/exwm.el +++ b/exwm.el @@ -230,7 +230,7 @@ (not (eq exwm--frame exwm-workspace--current))) (unless (frame-parameter exwm--frame 'exwm--urgency) (set-frame-parameter exwm--frame 'exwm--urgency t) - (exwm-workspace--update-switch-history)))))))) + (setq exwm-workspace--switch-history-outdated t)))))))) ;;;###autoload (defun exwm--update-protocols (id &optional force) @@ -389,7 +389,7 @@ (let ((idx (cl-position exwm--frame exwm-workspace--list))) (unless (= idx exwm-workspace-current-index) (set-frame-parameter exwm--frame 'exwm--urgency t) - (exwm-workspace--update-switch-history)))) + (setq exwm-workspace--switch-history-outdated t)))) ;; xcb:ewmh:_NET_WM_STATE_REMOVE? ;; xcb:ewmh:_NET_WM_STATE_TOGGLE? ) -- cgit 1.4.1 From 412d2a52bde98bb72fff9a848d5d28b6c5dff4c4 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 18 Sep 2015 14:17:52 +0800 Subject: Fix wrong/missing ConfigureNotify events * exwm-floating.el (exwm-floating--set-floating): Set exwm--floating-edges as absolute edges (the relative edges can be easily determined). * exwm-layout.el (exwm-layout--show): Send correct absolute positions to floating X windows. * exwm-floating.el (exwm-floating--stop-moveresize, exwm-floating-move): Send ConfigureNotify events after moving floating X windows. --- exwm-floating.el | 51 +++++++++++++++++++++++++++++++++++++++++++++------ exwm-layout.el | 17 ++++++++++++----- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 346f02e8ca..36a658a767 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -161,10 +161,7 @@ ;; Rationale: the frame will not be ready for some time, thus we cannot ;; infer the correct window size from its geometry. (with-current-buffer (exwm--id->buffer id) - (setq exwm--floating-edges - (vector exwm-floating-border-width exwm-floating-border-width - (+ width exwm-floating-border-width) - (+ height exwm-floating-border-width)))) + (setq exwm--floating-edges (vector x y (+ width x) (+ height y)))) ;; Fit frame to client (exwm-floating--fit-frame-to-window outer-id width height) ;; Reparent window to this frame @@ -436,6 +433,28 @@ are provided. You should call `xcb:flush' and restore the value of "Stop move/resize." (xcb:+request exwm--connection (make-instance 'xcb:UngrabPointer :time xcb:Time:CurrentTime)) + ;; Inform the X window that its absolute position is changed + (when exwm-floating--moveresize-calculate + (let ((edges (window-inside-absolute-pixel-edges (frame-selected-window))) + (id (with-current-buffer (window-buffer (frame-selected-window)) + exwm--id))) + (xcb:+request exwm--connection + (make-instance 'xcb:SendEvent + :propagate 0 :destination id + :event-mask xcb:EventMask:StructureNotify + :event (xcb:marshal + (make-instance 'xcb:ConfigureNotify + :event id :window id + :above-sibling xcb:Window:None + :x (elt edges 0) + :y (elt edges 1) + :width (- (elt edges 2) + (elt edges 0)) + :height (- (elt edges 3) + (elt edges 1)) + :border-width 0 + :override-redirect 0) + exwm--connection))))) (xcb:flush exwm--connection) (setq exwm-floating--moveresize-calculate nil)) @@ -473,14 +492,34 @@ Both DELTA-X and DELTA-Y default to 1. This command should be bound locally." (unless (and (= 0 delta-x) (= 0 delta-y)) (let* ((id (frame-parameter exwm--floating-frame 'exwm-outer-id)) (geometry (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:GetGeometry :drawable id)))) + (make-instance 'xcb:GetGeometry :drawable id))) + (edges (window-inside-absolute-pixel-edges))) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window id :value-mask (logior xcb:ConfigWindow:X xcb:ConfigWindow:Y) :x (+ (slot-value geometry 'x) delta-x) - :y (+ (slot-value geometry 'y) delta-y)))) + :y (+ (slot-value geometry 'y) delta-y))) + ;; Inform the X window that its absolute position is changed + (xcb:+request exwm--connection + (make-instance 'xcb:SendEvent + :propagate 0 :destination exwm--id + :event-mask xcb:EventMask:StructureNotify + :event (xcb:marshal + (make-instance 'xcb:ConfigureNotify + :event exwm--id + :window exwm--id + :above-sibling xcb:Window:None + :x (+ (elt edges 0) delta-x) + :y (+ (elt edges 1) delta-y) + :width (- (elt edges 2) + (elt edges 0)) + :height (- (elt edges 3) + (elt edges 1)) + :border-width 0 + :override-redirect 0) + exwm--connection)))) (xcb:flush exwm--connection))) (defun exwm-floating--init () diff --git a/exwm-layout.el b/exwm-layout.el index 2c9ab750b4..13bd085326 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -40,11 +40,17 @@ (let* ((buffer (exwm--id->buffer id)) (edges (or (and buffer (with-current-buffer buffer exwm--floating-edges)) - (window-inside-pixel-edges window))) - (x (elt edges 0)) - (y (elt edges 1)) + (window-inside-absolute-pixel-edges window))) (width (- (elt edges 2) (elt edges 0))) - (height (- (elt edges 3) (elt edges 1)))) + (height (- (elt edges 3) (elt edges 1))) + x y) + (if exwm--floating-edges + ;; The relative position of a floating X window is determinate + (setq x exwm-floating-border-width + y exwm-floating-border-width) + (let ((relative-edges (window-inside-pixel-edges window))) + (setq x (elt relative-edges 0) + y (elt relative-edges 1)))) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window id @@ -64,7 +70,8 @@ (make-instance 'xcb:ConfigureNotify :event id :window id :above-sibling xcb:Window:None - :x x :y y + :x (elt edges 0) + :y (elt edges 1) :width width :height height :border-width 0 :override-redirect 0) -- cgit 1.4.1 From 26ebc32e605249284f1ded69555006f10891cbba Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 18 Sep 2015 14:38:05 +0800 Subject: Fix a compilation warning * exwm-floating.el: Autoload exwm-floating-border-width. --- exwm-floating.el | 1 + 1 file changed, 1 insertion(+) diff --git a/exwm-floating.el b/exwm-floating.el index 36a658a767..f8891befe0 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -30,6 +30,7 @@ (require 'exwm-core) (eval-when-compile (require 'exwm-workspace)) +;;;###autoload (defvar exwm-floating-border-width 1 "Border width of the floating window.") (defvar exwm-floating-border-color "blue" "Border color of the floating window.") -- cgit 1.4.1 From c75a8b82ec2747a6fe7fc4b3a0dfaf1adca7ea64 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 18 Sep 2015 22:15:45 +0800 Subject: Prevent Emacs cursor style change when pointer is in an X window * exwm-input.el (exwm-input--on-KeyPress-char-mode): Compensate FocusOut event by sending a synthetic FocusIn event to prevent the change of cursor style (e.g. box to hollow) when pointer is in an X window. --- exwm-input.el | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/exwm-input.el b/exwm-input.el index afa619b873..042467b961 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -268,6 +268,21 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (with-slots (detail state) key-press (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) event) + ;; Compensate FocusOut event (prevent cursor style change) + (unless (eq major-mode 'exwm-mode) + (let ((id (frame-parameter nil 'exwm-window-id))) + (xcb:+request exwm--connection + (make-instance 'xcb:SendEvent + :propagate 0 + :destination id + :event-mask xcb:EventMask:StructureNotify + :event + (xcb:marshal + (make-instance 'xcb:FocusIn + :detail xcb:NotifyDetail:Inferior + :event id + :mode xcb:NotifyMode:Normal) + exwm--connection))))) (when (and keysym (setq event (xcb:keysyms:keysym->event keysym state))) (when (eq major-mode 'exwm-mode) (setq exwm-input--temp-line-mode t -- cgit 1.4.1 From 921e6e5be989b94b3b62d6db72d31fb16c24ee30 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 19 Sep 2015 12:34:30 +0800 Subject: Avoid autoloading variables * exwm-floating.el: * exwm-layout.el: Avoid autoloading exwm-floating-border-width. * exwm-workspace.el: Avoid autoloading exwm-workspace--switch-history-outdated. --- exwm-floating.el | 1 - exwm-layout.el | 2 ++ exwm-workspace.el | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index f8891befe0..36a658a767 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -30,7 +30,6 @@ (require 'exwm-core) (eval-when-compile (require 'exwm-workspace)) -;;;###autoload (defvar exwm-floating-border-width 1 "Border width of the floating window.") (defvar exwm-floating-border-color "blue" "Border color of the floating window.") diff --git a/exwm-layout.el b/exwm-layout.el index 13bd085326..4ccaef7450 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -28,6 +28,8 @@ (require 'exwm-core) (eval-when-compile (require 'exwm-workspace)) +(defvar exwm-floating-border-width) + ;;;###autoload (defun exwm-layout--show (id &optional window) "Show window ID exactly fit in the Emacs window WINDOW." diff --git a/exwm-workspace.el b/exwm-workspace.el index a6f21b2794..6f76d2061a 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -63,7 +63,6 @@ (defvar exwm-workspace--switch-history nil "History for `read-from-minibuffer' to interactively switch workspace.") -;;;###autoload (defvar exwm-workspace--switch-history-outdated nil "Non-nil to indicate `exwm-workspace--switch-history' is outdated.") -- cgit 1.4.1 From b35430429e96ce6b2bb46a4d155d86acdba8d768 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 19 Sep 2015 16:46:08 +0800 Subject: Check _MOTIF_WM_HINTS when attempting to manage an X window * exwm-core.el: New buffer-local variable exwm--mwm-hints. * exwm-manage.el: New variable exwm--atom-_MOTIF_WM_HINTS for holding the value of _MOTIF_WM_HINTS atom; new function exwm--update-mwm-hints for updating the _MOTIF_WM_HINTS property of an X window. * exwm-manage.el (exwm-manage--init): Intern the _MOTIF_WM_HINTS atom. * exwm-manage.el (exwm-manage--manage-window): Avoid managing windows without decoration (implied by _MOTIF_WM_HINTS). --- exwm-core.el | 2 ++ exwm-manage.el | 38 +++++++++++++++++++++++++++++++++----- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 74eb94fc49..0f39c2d344 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -111,6 +111,8 @@ ;; WM_HINTS (defvar-local exwm--hints-input nil) ;FIXME (defvar-local exwm--hints-urgency nil) +;; _MOTIF_WM_HINTS +(defvar-local exwm--mwm-hints nil) (defvar exwm-mode-map (let ((map (make-sparse-keymap))) diff --git a/exwm-manage.el b/exwm-manage.el index 3e3c848a56..b2eda4b8fc 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -42,6 +42,23 @@ corresponding buffer.") (when reply ;nil when destroyed (setq exwm--geometry reply)))))) +;; The _MOTIF_WM_HINTS atom (see for more details) +;; It's currently only used in 'exwm-manage' module +(defvar exwm-manage--_MOTIF_WM_HINTS nil "_MOTIF_WM_HINTS atom.") + +(defun exwm-manage--update-mwm-hints (id &optional force) + "Update _MOTIF_WM_HINTS." + (with-current-buffer (exwm--id->buffer id) + (unless (and exwm--mwm-hints (not force)) + (let ((reply (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:icccm:-GetProperty + :window id + :property exwm-manage--_MOTIF_WM_HINTS + :type exwm-manage--_MOTIF_WM_HINTS + :long-length 5)))) + (when reply + (setq exwm--mwm-hints (append (slot-value reply 'value) nil))))))) + (defun exwm-manage--manage-window (id) "Manage window ID." (exwm--log "Try to manage #x%x" id) @@ -63,6 +80,7 @@ corresponding buffer.") (exwm--update-window-type id) (exwm--update-class id) (exwm-manage--update-geometry id) + (exwm-manage--update-mwm-hints id) ;; No need to manage (please check OverrideRedirect outside) (when (or (not @@ -70,11 +88,12 @@ corresponding buffer.") (memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY exwm-window-type) (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG exwm-window-type) (memq xcb:Atom:_NET_WM_WINDOW_TYPE_NORMAL exwm-window-type))) - ;; For Java applications - (and (memq xcb:Atom:_NET_WM_WINDOW_TYPE_NORMAL exwm-window-type) - exwm-instance-name - (string-prefix-p "sun-awt-X11-" exwm-instance-name) - (not (string-suffix-p "XFramePeer" exwm-instance-name)))) + ;; Check _MOTIF_WM_HINTS (mainly for Java applications) + ;; See for the definitions of these fields + (and exwm--mwm-hints + (/= 0 (logand (elt exwm--mwm-hints 0) ;MotifWmHints.flags + 2)) ;MWM_HINTS_DECORATIONS + (= 0 (elt exwm--mwm-hints 2)))) ;MotifWmHints.decorations (exwm--log "No need to manage #x%x" id) ;; Remove all events (xcb:+request-checked+request-check exwm--connection @@ -356,6 +375,15 @@ corresponding buffer.") (defun exwm-manage--init () "Initialize manage module." + ;; Intern _MOTIF_WM_HINTS + (let ((atom-name "_MOTIF_WM_HINTS")) + (setq exwm-manage--_MOTIF_WM_HINTS + (slot-value (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:InternAtom + :only-if-exists 0 + :name-len (length atom-name) + :name atom-name)) + 'atom))) (xcb:+event exwm--connection 'xcb:ConfigureRequest #'exwm-manage--on-ConfigureRequest) (xcb:+event exwm--connection 'xcb:MapRequest #'exwm-manage--on-MapRequest) -- cgit 1.4.1 From 81478de9e6676ebe3c67d4673a7d4e56e7ad2265 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 19 Sep 2015 17:52:04 +0800 Subject: Manage non-floating windows without decorations * exwm-manage.el (exwm-manage--manage-window): Should manage non-floating windows without decorations. --- exwm-manage.el | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index b2eda4b8fc..e7682cda88 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -79,6 +79,8 @@ corresponding buffer.") (setq exwm--id id) (exwm--update-window-type id) (exwm--update-class id) + (exwm--update-transient-for id) + (exwm--update-normal-hints id) (exwm-manage--update-geometry id) (exwm-manage--update-mwm-hints id) ;; No need to manage (please check OverrideRedirect outside) @@ -93,7 +95,13 @@ corresponding buffer.") (and exwm--mwm-hints (/= 0 (logand (elt exwm--mwm-hints 0) ;MotifWmHints.flags 2)) ;MWM_HINTS_DECORATIONS - (= 0 (elt exwm--mwm-hints 2)))) ;MotifWmHints.decorations + (= 0 (elt exwm--mwm-hints 2)) ;MotifWmHints.decorations + ;; Floating windows only + (or exwm-transient-for exwm--fixed-size + (memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY + exwm-window-type) + (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG + exwm-window-type)))) (exwm--log "No need to manage #x%x" id) ;; Remove all events (xcb:+request-checked+request-check exwm--connection @@ -153,8 +161,6 @@ corresponding buffer.") :data (vconcat (mapcar #'car exwm--id-buffer-alist)))) (xcb:flush exwm--connection) (exwm--update-title id) - (exwm--update-transient-for id) - (exwm--update-normal-hints id) (exwm--update-hints id) (exwm--update-protocols id) (exwm--update-state id) -- cgit 1.4.1 From 9f30672b15abd84e5755ed36f505830e80739b13 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 20 Sep 2015 10:51:58 +0800 Subject: Fix input & input focus issues * exwm-manage.el (exwm-manage--manage-window): Only grab left/middle/right buttons. * exwm-input.el (exwm-input--on-ButtonPress): Only perform click-to-focus on unfocused X windows. * exwm-input.el (exwm-input--update-focus): Do not focus an X window on another workspace, but instead keep focusing on the current one and set exwm--urgency parameter on that frame. * exwm-input.el (exwm-input--fake-key): Send KeyRelease event (some applications reply on it). --- exwm-input.el | 44 +++++++++++++++++++++++++++----------------- exwm-manage.el | 20 ++++++++++---------- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 042467b961..eb287925a5 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -114,7 +114,14 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (when exwm-input--focus-window (with-current-buffer (window-buffer exwm-input--focus-window) (if (eq major-mode 'exwm-mode) - (progn + (if (not (eq exwm--frame exwm-workspace--current)) + ;; Do not focus X windows on other workspace + (progn + (set-frame-parameter exwm--frame 'exwm--urgency t) + (setq exwm-workspace--switch-history-outdated t) + (force-mode-line-update) + ;; The application may have changed its input focus + (exwm-workspace-switch exwm-workspace-current-index t)) (when exwm--floating-frame (redirect-frame-focus exwm--floating-frame nil) (select-frame-set-input-focus exwm--floating-frame t)) @@ -180,7 +187,9 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (exwm-floating--start-moveresize event)) (t ;; Click to focus - (select-window (get-buffer-window (exwm--id->buffer event) t)) + (let ((window (get-buffer-window (exwm--id->buffer event) t))) + (unless (eq window (selected-window)) + (select-window window))) ;; The event should be replayed (setq mode xcb:Allow:ReplayPointer)))) (xcb:+request exwm--connection @@ -360,21 +369,22 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (keycode (xcb:keysyms:keysym->keycode exwm--connection (car keysym))) (id (exwm--buffer->id (window-buffer (selected-window))))) (when keycode - (xcb:+request exwm--connection - (make-instance 'xcb:SendEvent - :propagate 0 :destination id - :event-mask xcb:EventMask:NoEvent - :event (xcb:marshal - (make-instance 'xcb:KeyPress - :detail keycode - :time xcb:Time:CurrentTime - :root exwm--root :event id - :child 0 - :root-x 0 :root-y 0 - :event-x 0 :event-y 0 - :state (cadr keysym) - :same-screen 1) - exwm--connection)))) + (dolist (class '(xcb:KeyPress xcb:KeyRelease)) + (xcb:+request exwm--connection + (make-instance 'xcb:SendEvent + :propagate 0 :destination id + :event-mask xcb:EventMask:NoEvent + :event (xcb:marshal + (make-instance class + :detail keycode + :time xcb:Time:CurrentTime + :root exwm--root :event id + :child 0 + :root-x 0 :root-y 0 + :event-x 0 :event-y 0 + :state (cadr keysym) + :same-screen 1) + exwm--connection))))) (xcb:flush exwm--connection))) ;;;###autoload diff --git a/exwm-manage.el b/exwm-manage.el index e7682cda88..d83fc447d6 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -145,16 +145,16 @@ corresponding buffer.") (make-instance 'xcb:ConfigureWindow :window id :value-mask xcb:ConfigWindow:BorderWidth :border-width 0)) - (xcb:+request exwm--connection ;grab buttons for set focus/move/resize - (make-instance 'xcb:GrabButton - :owner-events 0 :grab-window id - :event-mask xcb:EventMask:ButtonPress - :pointer-mode xcb:GrabMode:Sync - :keyboard-mode xcb:GrabMode:Async - :confine-to xcb:Window:None - :cursor xcb:Cursor:None - :button xcb:ButtonIndex:Any - :modifiers xcb:ModMask:Any)) + (dolist (button ;grab buttons to set focus / move / resize + (list xcb:ButtonIndex:1 xcb:ButtonIndex:2 xcb:ButtonIndex:3)) + (xcb:+request-checked+request-check exwm--connection + (make-instance 'xcb:GrabButton + :owner-events 0 :grab-window id + :event-mask xcb:EventMask:ButtonPress + :pointer-mode xcb:GrabMode:Sync + :keyboard-mode xcb:GrabMode:Async + :confine-to xcb:Window:None :cursor xcb:Cursor:None + :button button :modifiers xcb:ModMask:Any))) (xcb:+request exwm--connection ;update _NET_CLIENT_LIST (make-instance 'xcb:ewmh:set-_NET_CLIENT_LIST :window exwm--root -- cgit 1.4.1 From 623db8b378949b0dd863bd8ee069417973b69bad Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 21 Sep 2015 13:31:57 +0800 Subject: Minor fixes for packaging * .gitignore: Add ELPA files. * exwm-workspace.el: Autoload exwm-workspace-switch. * README.org: Renamed from README.md; add an installation note. --- .gitignore | 2 ++ README.md | 13 ------------- README.org | 15 +++++++++++++++ exwm-workspace.el | 1 + 4 files changed, 18 insertions(+), 13 deletions(-) delete mode 100644 README.md create mode 100644 README.org diff --git a/.gitignore b/.gitignore index c531d9867f..9e4b0ee5b4 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ *.elc +*-pkg.el +*-autoloads.el diff --git a/README.md b/README.md deleted file mode 100644 index 5d18fa6c85..0000000000 --- a/README.md +++ /dev/null @@ -1,13 +0,0 @@ -Emacs X Window Manager -====================== - -EXWM (Emacs X Window Manager) is a full-featured tiling X window manager for -Emacs built on top of [XELB](https://github.com/ch11ng/xelb). -It features: -+ Fully keyboard-driven operation -+ Hybrid layout modes (tiling & stacking) -+ Workspace support -+ ICCCM/EWMH compliance -+ Basic RandR support (optional) - -Please check the [wiki](https://github.com/ch11ng/exwm/wiki) for more details. diff --git a/README.org b/README.org new file mode 100644 index 0000000000..49ba04b33a --- /dev/null +++ b/README.org @@ -0,0 +1,15 @@ +#+TITLE: Emacs X Window Manager + +EXWM (Emacs X Window Manager) is a full-featured tiling X window manager for +Emacs built on top of [[https://github.com/ch11ng/xelb][XELB]]. +It features: ++ Fully keyboard-driven operation ++ Hybrid layout modes (tiling & stacking) ++ Workspace support ++ ICCCM/EWMH compliance ++ Basic RandR support (optional) + +Please check the [[https://github.com/ch11ng/exwm/wiki][User Guide]] for more details. + +*Note*: If you install EXWM from source, you need to manually install XELB +(either from source or GNU ELPA). diff --git a/exwm-workspace.el b/exwm-workspace.el index 6f76d2061a..03972b4078 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -101,6 +101,7 @@ (defvar exwm-workspace-show-all-buffers nil "Non-nil to show buffers on other workspaces.") +;;;###autoload (defun exwm-workspace-switch (index &optional force) "Switch to workspace INDEX. Query for INDEX if it's not specified. -- cgit 1.4.1 From 22ee3e6c5ffdc73f231e7125f48827dc1d44237f Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 23 Sep 2015 12:53:08 +0800 Subject: Adjust default prefix keys; advice x-create-frame * exwm-input.el (exwm-input--on-KeyPress-line-mode, exwm-input-prefix-keys): Allow users to disable 'C-c' prefixed keys; Add 'C-c' to / remove 'M-!' from the default prefix keys. * exwm-workspace.el (exwm-workspace--x-create-frame, exwm-workspace--init): Advice `x-create-frame' to prevent it from hanging EXWM, making e.g. speedbar working. * exwm-floating.el (exwm-floating--set-floating): Remove the now unnecessary request that sets override-redirect on floating frames. --- exwm-floating.el | 5 ----- exwm-input.el | 3 +-- exwm-workspace.el | 14 ++++++++++++++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 36a658a767..924f7cffca 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -141,11 +141,6 @@ (setq x (/ (- display-width width) 2) y (/ (- display-height height) 2)))))) (exwm--log "Floating geometry (corrected): %dx%d%+d%+d" width height x y) - ;; Set OverrideRedirect on this frame - (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window outer-id :value-mask xcb:CW:OverrideRedirect - :override-redirect 1)) ;; Set event mask (xcb:+request exwm--connection (make-instance 'xcb:ChangeWindowAttributes diff --git a/exwm-input.el b/exwm-input.el index eb287925a5..9645182d09 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -209,7 +209,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (defvar exwm-input--global-prefix-keys nil "List of prefix keys of global key bindings.") (defvar exwm-input-prefix-keys - '(?\C-x ?\C-u ?\C-h ?\M-x ?\M-` ?\M-! ?\M-& ?\M-:) + '(?\C-c ?\C-x ?\C-u ?\C-h ?\M-x ?\M-` ?\M-& ?\M-:) "List of prefix keys EXWM should forward to Emacs when in line-mode.") (defvar exwm-input--simulation-keys nil "Simulation keys in line-mode.") (defvar exwm-input--simulation-prefix-keys nil @@ -259,7 +259,6 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (setq event (xcb:keysyms:keysym->event keysym state)) (or exwm-input--during-key-sequence (setq minibuffer-window (active-minibuffer-window)) - (eq event ?\C-c) ;mode-specific key (memq event exwm-input--global-prefix-keys) (memq event exwm-input-prefix-keys) (memq event exwm-input--simulation-prefix-keys))) diff --git a/exwm-workspace.el b/exwm-workspace.el index 03972b4078..566ea539b6 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -263,6 +263,18 @@ The optional FORCE option is for internal use only." (setq newname (format "%s<%d>" basename (cl-incf counter)))) (rename-buffer (concat (and hidden " ") newname)))) +(defun exwm-workspace--x-create-frame (orig-fun params) + "Set override-redirect on the frame created by `x-create-frame'." + (let ((frame (funcall orig-fun params))) + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window (string-to-number + (frame-parameter frame 'outer-window-id)) + :value-mask xcb:CW:OverrideRedirect + :override-redirect 1)) + (xcb:flush exwm--connection) + frame)) + (defun exwm-workspace--init () "Initialize workspace module." (cl-assert (and (< 0 exwm-workspace-number) (>= 10 exwm-workspace-number))) @@ -306,6 +318,8 @@ The optional FORCE option is for internal use only." :window window-id :value-mask xcb:CW:EventMask :event-mask xcb:EventMask:SubstructureRedirect)))) (xcb:flush exwm--connection) + ;; We have to advice `x-create-frame' or every call to it would hang EXWM + (advice-add 'x-create-frame :around #'exwm-workspace--x-create-frame) ;; We have to delay making the frame visible until the ;; override-redirect flag has been set. (select-frame-set-input-focus (car exwm-workspace--list)) -- cgit 1.4.1 From 4ad76b879d2f3807828de5345d5da4cac301415d Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 24 Sep 2015 20:03:57 +0800 Subject: Fix a calculation error of the position of a floating frame * exwm-floating.el (exwm-floating--set-floating): The position of a floating frame should be relative to its workspace. --- exwm-floating.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 924f7cffca..0994cb9914 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -135,8 +135,8 @@ (setq edges nil))) (if edges ;; Put at the center of leading window - (setq x (/ (- (+ (elt edges 2) (elt edges 0)) width) 2) - y (/ (- (+ (elt edges 3) (elt edges 1)) height) 2)) + (setq x (/ (- (elt edges 2) (elt edges 0) width) 2) + y (/ (- (elt edges 3) (elt edges 1) height) 2)) ;; Put at the center of screen (setq x (/ (- display-width width) 2) y (/ (- display-height height) 2)))))) -- cgit 1.4.1 From f685de12d464b334ba7efdfe67e989dd63a96fa0 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 25 Sep 2015 13:45:30 +0800 Subject: Fix click-to-focus on multi-monitor settings * exwm-input.el (exwm-input--on-ButtonPress): [click-to-focus] Switch to the corresponding workspace if necessary. --- exwm-input.el | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/exwm-input.el b/exwm-input.el index 9645182d09..9def8ca565 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -187,8 +187,22 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (exwm-floating--start-moveresize event)) (t ;; Click to focus - (let ((window (get-buffer-window (exwm--id->buffer event) t))) + (let ((window (get-buffer-window (exwm--id->buffer event) t)) + frame) (unless (eq window (selected-window)) + (setq frame (window-frame window)) + (unless (eq frame exwm-workspace--current) + (if (memq frame exwm-workspace--list) + ;; The X window is on another workspace + (exwm-workspace-switch + (cl-position frame exwm-workspace--list)) + (with-current-buffer (window-buffer window) + (when (and (eq major-mode 'exwm-mode) + (not (eq exwm--frame + exwm-workspace--current))) + ;; The floating X window is on another workspace + (exwm-workspace-switch + (cl-position exwm--frame exwm-workspace--list)))))) (select-window window))) ;; The event should be replayed (setq mode xcb:Allow:ReplayPointer)))) -- cgit 1.4.1 From 5184f0d7c1b540a6241904528d068dce288a911e Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 27 Sep 2015 19:31:00 +0800 Subject: Work around subrs that block EXWM; other minor fixes Some subrs (e.g. x-file-dialog) create X windows and block the execution of EXWM, so they won't work normally. This commit partly fixes this issue by invoking them in a subordinate Emacs instance and trying to fetch the result back. * exwm.el (exwm-blocking-subrs): New variable for specify such subrs. * exwm.el (exwm-enable, exwm--server-name, exwm--server-stop) (exwm--server-eval-at): The implementation. * exwm-core.el: * exwm-floating.el: * exwm-layout.el: * exwm-manage.el: * exwm-randr.el: Evaluate constants at compile-time. * README.md: Renamed from README.org to make the 'Commentary:' section used by GNU ELPA instead. * exwm.el: Depends on XELB version 0.3. --- README.md | 16 ++++++++++++ README.org | 15 ----------- exwm-core.el | 8 +++--- exwm-floating.el | 62 +++++++++++++++++++++++++------------------- exwm-layout.el | 47 ++++++++++++++++++--------------- exwm-manage.el | 5 ++-- exwm-randr.el | 20 +++++++------- exwm.el | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 8 files changed, 169 insertions(+), 83 deletions(-) create mode 100644 README.md delete mode 100644 README.org diff --git a/README.md b/README.md new file mode 100644 index 0000000000..09fe470d39 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# Emacs X Window Manager + +EXWM (Emacs X Window Manager) is a full-featured tiling X window manager for +Emacs built on top of [XELB](https://github.com/ch11ng/xelb). +It features: ++ Fully keyboard-driven operation ++ Hybrid layout modes (tiling & stacking) ++ Workspace support ++ ICCCM/EWMH compliance ++ Basic RandR support (optional) + +Please check the [User Guide](https://github.com/ch11ng/exwm/wiki) +for more details. + +**Note**: If you install EXWM from source, you need to manually install XELB +(either from source or GNU ELPA). diff --git a/README.org b/README.org deleted file mode 100644 index 49ba04b33a..0000000000 --- a/README.org +++ /dev/null @@ -1,15 +0,0 @@ -#+TITLE: Emacs X Window Manager - -EXWM (Emacs X Window Manager) is a full-featured tiling X window manager for -Emacs built on top of [[https://github.com/ch11ng/xelb][XELB]]. -It features: -+ Fully keyboard-driven operation -+ Hybrid layout modes (tiling & stacking) -+ Workspace support -+ ICCCM/EWMH compliance -+ Basic RandR support (optional) - -Please check the [[https://github.com/ch11ng/exwm/wiki][User Guide]] for more details. - -*Note*: If you install EXWM from source, you need to manually install XELB -(either from source or GNU ELPA). diff --git a/exwm-core.el b/exwm-core.el index 0f39c2d344..1ac8678659 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -69,12 +69,14 @@ (make-instance 'xcb:ChangeWindowAttributes :window exwm--root :value-mask xcb:CW:EventMask - :event-mask (logior xcb:EventMask:StructureNotify - xcb:EventMask:SubstructureRedirect))) + :event-mask (eval-when-compile + (logior xcb:EventMask:SubstructureRedirect + xcb:EventMask:StructureNotify)))) (xcb:flush exwm--connection)) (defconst exwm--client-event-mask - (logior xcb:EventMask:StructureNotify xcb:EventMask:PropertyChange) + (eval-when-compile + (logior xcb:EventMask:StructureNotify xcb:EventMask:PropertyChange)) "Event mask set on all managed windows.") ;; Internal variables diff --git a/exwm-floating.el b/exwm-floating.el index 0994cb9914..59b4e4432d 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -247,9 +247,10 @@ are provided. You should call `xcb:flush' and restore the value of :window (or frame-outer-id (frame-parameter exwm--floating-frame 'exwm-outer-id)) - :value-mask (logior xcb:ConfigWindow:Width - xcb:ConfigWindow:Height - xcb:ConfigWindow:StackMode) + :value-mask (eval-when-compile + (logior xcb:ConfigWindow:Width + xcb:ConfigWindow:Height + xcb:ConfigWindow:StackMode)) :width (+ width (* 2 exwm-floating-border-width)) :height (+ height (* 2 exwm-floating-border-width) (window-mode-line-height) @@ -336,18 +337,20 @@ are provided. You should call `xcb:flush' and restore the value of exwm-floating--moveresize-calculate `(lambda (x y) (vector ,frame-id - ,(logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y) + ,(eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y)) (- x ,win-x) (- y ,win-y) 0 0)))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPLEFT) (setq cursor exwm-floating--cursor-top-left exwm-floating--moveresize-calculate `(lambda (x y) (vector ,frame-id - ,(logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) + ,(eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height)) (- x ,win-x) (- y ,win-y) (- ,(+ root-x width) x) (- ,(+ root-y height) y))))) @@ -356,17 +359,19 @@ are provided. You should call `xcb:flush' and restore the value of exwm-floating--moveresize-calculate `(lambda (x y) (vector ,frame-id - ,(logior xcb:ConfigWindow:Y - xcb:ConfigWindow:Height) + ,(eval-when-compile + (logior xcb:ConfigWindow:Y + xcb:ConfigWindow:Height)) 0 (- y ,win-y) 0 (- ,(+ root-y height) y))))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPRIGHT) (setq cursor exwm-floating--cursor-top-right exwm-floating--moveresize-calculate `(lambda (x y) (vector ,frame-id - ,(logior xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) + ,(eval-when-compile + (logior xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height)) 0 (- y ,win-y) (- x ,(- root-x width)) (- ,(+ root-y height) y))))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_RIGHT) @@ -380,8 +385,9 @@ are provided. You should call `xcb:flush' and restore the value of exwm-floating--moveresize-calculate `(lambda (x y) (vector ,frame-id - ,(logior xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) + ,(eval-when-compile + (logior xcb:ConfigWindow:Width + xcb:ConfigWindow:Height)) 0 0 (- x ,(- root-x width)) (- y ,(- root-y height)))))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOM) @@ -396,9 +402,10 @@ are provided. You should call `xcb:flush' and restore the value of exwm-floating--moveresize-calculate `(lambda (x y) (vector ,frame-id - ,(logior xcb:ConfigWindow:X - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) + ,(eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height)) (- x ,win-x) 0 (- ,(+ root-x width) x) @@ -408,15 +415,17 @@ are provided. You should call `xcb:flush' and restore the value of exwm-floating--moveresize-calculate `(lambda (x y) (vector ,frame-id - ,(logior xcb:ConfigWindow:X - xcb:ConfigWindow:Width) + ,(eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Width)) (- x ,win-x) 0 (- ,(+ root-x width) x) 0))))) ;; Select events and change cursor (should always succeed) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GrabPointer :owner-events 0 :grab-window frame-id - :event-mask (logior xcb:EventMask:ButtonRelease - xcb:EventMask:ButtonMotion) + :event-mask (eval-when-compile + (logior xcb:EventMask:ButtonRelease + xcb:EventMask:ButtonMotion)) :pointer-mode xcb:GrabMode:Async :keyboard-mode xcb:GrabMode:Async :confine-to xcb:Window:None @@ -472,7 +481,7 @@ are provided. You should call `xcb:flush' and restore the value of (make-instance 'xcb:ConfigureWindow :window (elt result 0) :value-mask (elt result 1) :x (- (elt result 2) frame-x) - :y (- (elt result 3) frame-y) + :y (- (elt result 3) frame-y) :width (elt result 4) :height (elt result 5))) (xcb:flush exwm--connection)))) @@ -492,8 +501,9 @@ Both DELTA-X and DELTA-Y default to 1. This command should be bound locally." (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window id - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y) + :value-mask (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y)) :x (+ (slot-value geometry 'x) delta-x) :y (+ (slot-value geometry 'y) delta-y))) ;; Inform the X window that its absolute position is changed diff --git a/exwm-layout.el b/exwm-layout.el index 4ccaef7450..1d11dbcdca 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -56,11 +56,12 @@ (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window id - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height - xcb:ConfigWindow:StackMode) + :value-mask (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height + xcb:ConfigWindow:StackMode)) :x x :y y :width width :height height ;; In order to put non-floating window at bottom :stack-mode xcb:StackMode:Below)) @@ -122,10 +123,11 @@ (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window outer-id - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) + :value-mask (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height)) :x 0 :y 0 :width (frame-pixel-width exwm-workspace--current) :height (frame-pixel-height @@ -134,10 +136,11 @@ (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window exwm--id - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) + :value-mask (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height)) :x 0 :y 0 :width (frame-pixel-width exwm-workspace--current) :height (frame-pixel-height exwm-workspace--current))) @@ -161,10 +164,11 @@ (make-instance 'xcb:ConfigureWindow :window (frame-parameter exwm--floating-frame 'exwm-outer-id) - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) + :value-mask (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height)) :x (elt exwm--floating-frame-geometry 0) :y (elt exwm--floating-frame-geometry 1) :width (elt exwm--floating-frame-geometry 2) @@ -194,10 +198,11 @@ (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window id - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) + :value-mask (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height)) :x x :y y :width width :height height)) diff --git a/exwm-manage.el b/exwm-manage.el index d83fc447d6..ae707f73ee 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -124,8 +124,9 @@ corresponding buffer.") (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window id - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y) + :value-mask (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y)) :x (/ (- (frame-pixel-width exwm-workspace--current) width) diff --git a/exwm-randr.el b/exwm-randr.el index fc8477d2aa..26d15dcb7d 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -90,10 +90,11 @@ (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window (frame-parameter frame 'exwm-outer-id) - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) + :value-mask (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height)) :x x :y y :width width :height height)) (setq workareas (nconc workareas (list x y width height)) viewports (nconc viewports (list x y)))))) @@ -133,11 +134,12 @@ (make-instance 'xcb:randr:SelectInput :window exwm--root :enable xcb:randr:NotifyMask:ScreenChange - ;; :enable (logior - ;; xcb:randr:NotifyMask:ScreenChange - ;; xcb:randr:NotifyMask:OutputChange - ;; xcb:randr:NotifyMask:OutputProperty - ;; xcb:randr:NotifyMask:CrtcChange) + ;; :enable (eval-when-compile + ;; (logior + ;; xcb:randr:NotifyMask:ScreenChange + ;; xcb:randr:NotifyMask:OutputChange + ;; xcb:randr:NotifyMask:OutputProperty + ;; xcb:randr:NotifyMask:CrtcChange)) )) (xcb:flush exwm--connection))))) diff --git a/exwm.el b/exwm.el index d2a0ecf144..b5d246ed40 100644 --- a/exwm.el +++ b/exwm.el @@ -5,7 +5,7 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng ;; Version: 0 -;; Package-Requires: ((xelb "0.1")) +;; Package-Requires: ((xelb "0.3")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm @@ -74,6 +74,7 @@ ;;; Code: +(require 'server) (require 'exwm-core) (require 'exwm-workspace) (require 'exwm-layout) @@ -526,14 +527,78 @@ (exwm-manage--scan) (run-hooks 'exwm-init-hook))))) +(defvar exwm-blocking-subrs '(x-file-dialog x-popup-dialog x-select-font) + "Subrs (primitives) that would normally block EXWM.") + (defun exwm-enable (&optional undo) "Enable/Disable EXWM." - (if (eq undo 'undo) - (progn (remove-hook 'window-setup-hook #'exwm-init) - (remove-hook 'after-make-frame-functions #'exwm-init)) - (setq frame-resize-pixelwise t) ;mandatory; before init - (add-hook 'window-setup-hook #'exwm-init t) ;for Emacs - (add-hook 'after-make-frame-functions #'exwm-init t))) ;for Emacs Client + (pcase undo + (`undo ;prevent reinitialization + (remove-hook 'window-setup-hook #'exwm-init) + (remove-hook 'after-make-frame-functions #'exwm-init)) + (`undo-all ;attempt to revert everything + (remove-hook 'window-setup-hook #'exwm-init) + (remove-hook 'after-make-frame-functions #'exwm-init) + (remove-hook 'kill-emacs-hook #'exwm--server-stop) + (dolist (i exwm-blocking-subrs) + (advice-remove i #'exwm--server-eval-at))) + (_ ;enable EXWM + (setq frame-resize-pixelwise t) ;mandatory; before init + (add-hook 'window-setup-hook #'exwm-init t) ;for Emacs + (add-hook 'after-make-frame-functions #'exwm-init t) ;for Emacs Client + (add-hook 'kill-emacs-hook #'exwm--server-stop) + (dolist (i exwm-blocking-subrs) + (advice-add i :around #'exwm--server-eval-at))))) + +(defconst exwm--server-name "server-exwm" + "Name of the subordinate Emacs server.") +(defvar exwm--server-process nil "Process of the subordinate Emacs server.") + +(defun exwm--server-stop () + "Stop the subordinate Emacs server." + (server-force-delete exwm--server-name) + (when exwm--server-process + (delete-process exwm--server-process) + (setq exwm--server-process nil))) + +(defun exwm--server-eval-at (&rest args) + "Wrapper of `server-eval-at' used to advice subrs." + ;; Start the subordinate Emacs server if it's not alive + (unless (server-running-p exwm--server-name) + (when exwm--server-process (delete-process exwm--server-process)) + (setq exwm--server-process + (start-process exwm--server-name + nil + (car command-line-args) ;The executable file + "-d" x-display-name + "-Q" + (concat "--daemon=" exwm--server-name) + "--eval" + ;; Create an invisible frame + "(make-frame '((window-system . x) (visibility)))")) + (while (not (server-running-p exwm--server-name)) + (sit-for 0.001))) + (server-eval-at + exwm--server-name + `(progn (select-frame (car (frame-list))) + (let ((result ,(nconc (list (make-symbol (subr-name (car args)))) + (cdr args)))) + (pcase (type-of result) + ;; Return the name of a buffer + (`buffer (buffer-name result)) + ;; We blindly convert all font objects to their XLFD names. This + ;; might cause problems of course, but it still has a chance to + ;; work (whereas directly passing font objects would merely + ;; raise errors). + ((or `font-entity `font-object `font-spec) + (font-xlfd-name result)) + ;; Passing following types makes little sense + ((or `compiled-function `finalizer `frame `hash-table `marker + `overlay `process `window `window-configuration)) + ;; Passing the name of a subr + (`subr (make-symbol (subr-name result))) + ;; For other types, return the value as-is. + (t result)))))) (defun exwm--ido-buffer-window-other-frame (orig-fun buffer) "Wrapper for `ido-buffer-window-other-frame' to exclude invisible windows." -- cgit 1.4.1 From 3b518eec23e221aa2480970dd35c11c68653869a Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 1 Oct 2015 09:30:14 +0800 Subject: Fix a position calculation error on multi-monitor settings * exwm-floating.el (exwm-floating--set-floating): Always use relative positions. --- exwm-floating.el | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 59b4e4432d..f408cc16b6 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -85,8 +85,15 @@ (x (slot-value exwm--geometry 'x)) (y (slot-value exwm--geometry 'y)) (width (slot-value exwm--geometry 'width)) - (height (slot-value exwm--geometry 'height))) - (exwm--log "Floating geometry (original): %dx%d%+d%+d" width height x y) + (height (slot-value exwm--geometry 'height)) + (frame-geometry (frame-parameter original-frame 'exwm-geometry))) + (exwm--log "Floating geometry (original, absolute): %dx%d%+d%+d" + width height x y) + (when frame-geometry + (setq x (- x (slot-value frame-geometry 'x)) + y (- y (slot-value frame-geometry 'y)))) + (exwm--log "Floating geometry (original, relative): %dx%d%+d%+d" + width height x y) ;; Save window IDs (set-frame-parameter frame 'exwm-window-id frame-id) (set-frame-parameter frame 'exwm-outer-id outer-id) -- cgit 1.4.1 From 1aeec4abf4de594841b05310753ae7fab527eb72 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 10 Oct 2015 20:56:55 +0800 Subject: Restrict the check of _MOTIF_WM_HINTS * exwm-manage.el (exwm-manage--manage-window): Restrict the check of _MOTIF_WM_HINTS to only Java applications (since some other applications like Evince would also set it). --- exwm-manage.el | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index ae707f73ee..fb6ca1672b 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -90,12 +90,15 @@ corresponding buffer.") (memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY exwm-window-type) (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG exwm-window-type) (memq xcb:Atom:_NET_WM_WINDOW_TYPE_NORMAL exwm-window-type))) - ;; Check _MOTIF_WM_HINTS (mainly for Java applications) + ;; Check _MOTIF_WM_HINTS for Java applications ;; See for the definitions of these fields (and exwm--mwm-hints + exwm-instance-name (/= 0 (logand (elt exwm--mwm-hints 0) ;MotifWmHints.flags - 2)) ;MWM_HINTS_DECORATIONS + 2)) ;MWM_HINTS_DECORATIONS (= 0 (elt exwm--mwm-hints 2)) ;MotifWmHints.decorations + ;; Java applications only + (string-prefix-p "sun-awt-X11-" exwm-instance-name) ;; Floating windows only (or exwm-transient-for exwm--fixed-size (memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY -- cgit 1.4.1 From 46461d994b32da1813d3e531b02127c7b9281947 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 11 Oct 2015 11:39:10 +0800 Subject: Improve robustness * exwm-input.el (exwm-input--update-focus, exwm-input--on-ButtonPress): Make sure Emacs windows are alive before manipulating them. --- exwm-input.el | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 9def8ca565..6c34853143 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -111,7 +111,9 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (defun exwm-input--update-focus () "Update input focus." - (when exwm-input--focus-window + (when (and exwm-input--focus-window + ;; The Emacs window may have been deleted + (window-buffer exwm-input--focus-window)) (with-current-buffer (window-buffer exwm-input--focus-window) (if (eq major-mode 'exwm-mode) (if (not (eq exwm--frame exwm-workspace--current)) @@ -203,7 +205,12 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") ;; The floating X window is on another workspace (exwm-workspace-switch (cl-position exwm--frame exwm-workspace--list)))))) - (select-window window))) + ;; It has been reported that the `window' may have be deleted + (if (window-live-p window) + (select-window window) + (setq window + (get-buffer-window (exwm--id->buffer event) t)) + (when window (select-window window))))) ;; The event should be replayed (setq mode xcb:Allow:ReplayPointer)))) (xcb:+request exwm--connection -- cgit 1.4.1 From e70c6fad56d0290890d75322c2f5335173624549 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 17 Oct 2015 11:02:32 +0800 Subject: Allow certain commands to receive key events in line-mode * exwm-input.el (exwm-input-command-whitelist): New variable. (exwm-input--on-KeyPress-line-mode): Allow certain commands which receive inputs without using the minibuffer to work in line-mode. --- exwm-input.el | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/exwm-input.el b/exwm-input.el index 6c34853143..f0bf6aa8b6 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -270,6 +270,13 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (global-set-key key command) (cl-pushnew key exwm-input--global-keys)) +;; These commands usually call something like `read-char' without using the +;; minibuffer, so they will not get inputs after invoked. It'd be better if we +;; can determine whether there's a command waiting for input so that this +;; variable can be removed. +(defvar exwm-input-command-whitelist nil + "A list of commands that when active all keys should be forwarded to Emacs.") + ;;;###autoload (defun exwm-input--on-KeyPress-line-mode (key-press) "Parse X KeyPress event to Emacs key event and then feed the command loop." @@ -280,6 +287,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (setq event (xcb:keysyms:keysym->event keysym state)) (or exwm-input--during-key-sequence (setq minibuffer-window (active-minibuffer-window)) + (memq real-this-command exwm-input-command-whitelist) (memq event exwm-input--global-prefix-keys) (memq event exwm-input-prefix-keys) (memq event exwm-input--simulation-prefix-keys))) -- cgit 1.4.1 From 463d0da41139ccf48c9c0c9522c93041769dfcdb Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 19 Oct 2015 14:59:58 +0800 Subject: Close the (possible) active minibuffer when switching workspace * exwm-workspace.el (exwm-workspace-switch): An active minibuffer on another workspace might cause problems for input. Closing it should be sufficient. --- exwm-workspace.el | 2 ++ 1 file changed, 2 insertions(+) diff --git a/exwm-workspace.el b/exwm-workspace.el index 566ea539b6..0813c89219 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -138,6 +138,8 @@ The optional FORCE option is for internal use only." (setq x (/ w 2) y (/ h 2))) (set-mouse-pixel-position frame x y))) + ;; Close the (possible) active minibuffer + (when (active-minibuffer-window) (abort-recursive-edit)) (setq default-minibuffer-frame frame) ;; Hide windows in other workspaces by preprending a space (unless exwm-workspace-show-all-buffers -- cgit 1.4.1 From e8bc51280dba18dc394e15dd544e70ad27176f82 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 25 Oct 2015 14:51:06 +0800 Subject: Fix workspace switch issues * exwm-core.el (exwm--make-emacs-idle-for): Removed. * exwm-workspace.el (exwm-workspace--switch-count): New variable. (exwm-workspace-switch): Increase exwm-workspace--switch-count when necessary; Remove the call to exwm--make-emacs-idle-for. (exwm-workspace--on-focus-in): Consume exwm-workspace--switch-count. * exwm-workspace.el (exwm-workspace--on-focus-in): Close active minibuffer. --- exwm-core.el | 4 ---- exwm-workspace.el | 12 ++++++++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 1ac8678659..ec7034737a 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -38,10 +38,6 @@ (when exwm-debug-on `(message (concat "[EXWM] " ,format-string) ,@args))) -(defun exwm--make-emacs-idle-for (seconds) - "Put Emacs in idle state for SECONDS seconds." - (with-timeout (seconds) (read-event))) - (defvar exwm--connection nil "X connection.") (defvar exwm--root nil "Root window.") (defvar exwm--id-buffer-alist nil "Alist of ( . ).") diff --git a/exwm-workspace.el b/exwm-workspace.el index 0813c89219..641ba7e680 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -100,6 +100,10 @@ (defvar exwm-workspace-current-index 0 "Index of current active workspace.") (defvar exwm-workspace-show-all-buffers nil "Non-nil to show buffers on other workspaces.") +(defvar exwm-workspace--switch-count 0 + "`exwm-workspace-switch' execution counts. + +Consumed by `exwm-workspace--on-focus-in.'") ;;;###autoload (defun exwm-workspace-switch (index &optional force) @@ -125,6 +129,7 @@ The optional FORCE option is for internal use only." (let ((frame (elt exwm-workspace--list index))) (setq exwm-workspace--current frame exwm-workspace-current-index index) + (unless force (cl-incf exwm-workspace--switch-count)) (select-frame-set-input-focus frame) ;; Move mouse when necessary (let ((position (mouse-pixel-position)) @@ -154,7 +159,6 @@ The optional FORCE option is for internal use only." (set-frame-parameter frame 'exwm--urgency nil) ;; Update switch workspace history (setq exwm-workspace--switch-history-outdated t) - (exwm--make-emacs-idle-for 0.1) ;FIXME ;; Update _NET_CURRENT_DESKTOP (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_CURRENT_DESKTOP @@ -165,9 +169,13 @@ The optional FORCE option is for internal use only." "Fix unexpected frame switch." (let ((index (cl-position (selected-frame) exwm-workspace--list))) (exwm--log "Focus on workspace %s" index) + ;; Close the (possible) active minibuffer + (when (active-minibuffer-window) (abort-recursive-edit)) (when (and index (/= index exwm-workspace-current-index)) (exwm--log "Workspace was switched unexpectedly") - (exwm-workspace-switch index)))) + (if (< 0 exwm-workspace--switch-count) + (cl-decf exwm-workspace--switch-count) + (exwm-workspace-switch index))))) ;;;###autoload (defun exwm-workspace-move-window (index &optional id) -- cgit 1.4.1 From d05df51b0563b385da46384901c73daa073c28b9 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 26 Oct 2015 12:38:20 +0800 Subject: Fix problems introduced/exposed by last commit * exwm-workspace.el (exwm-workspace-switch, exwm-workspace--on-focus-in): Use handle-switch-frame instead of exwm-workspace--switch-count to filter out events. * exwm-workspace.el (exwm-workspace--init): Delay making workspaces fullscreen. * exwm-workspace.el (exwm-workspace-move-window): * exwm-floating.el (exwm-floating--set-floating): * exwm-layout.el (exwm-layout--refresh): `set-buffer-major-mode` does not accept buffer names. --- exwm-floating.el | 6 ++++-- exwm-input.el | 4 +--- exwm-layout.el | 17 +++++++++-------- exwm-workspace.el | 35 ++++++++++++++++++----------------- 4 files changed, 32 insertions(+), 30 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index f408cc16b6..408cf372dc 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -69,8 +69,10 @@ ;; Create new frame (frame (with-current-buffer (or (get-buffer "*scratch*") - (prog1 (get-buffer-create "*scratch*") - (set-buffer-major-mode "*scratch*"))) + (progn + (set-buffer-major-mode + (get-buffer-create "*scratch*")) + (get-buffer "*scratch*"))) (prog2 (exwm--lock) (make-frame diff --git a/exwm-input.el b/exwm-input.el index f0bf6aa8b6..8a9d089e2f 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -111,9 +111,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (defun exwm-input--update-focus () "Update input focus." - (when (and exwm-input--focus-window - ;; The Emacs window may have been deleted - (window-buffer exwm-input--focus-window)) + (when (window-live-p exwm-input--focus-window) (with-current-buffer (window-buffer exwm-input--focus-window) (if (eq major-mode 'exwm-mode) (if (not (eq exwm--frame exwm-workspace--current)) diff --git a/exwm-layout.el b/exwm-layout.el index 1d11dbcdca..ffe1f1d0c4 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -215,18 +215,19 @@ "Refresh layout." (let ((frame (selected-frame)) (placeholder (or (get-buffer "*scratch*") - (prog1 (get-buffer-create "*scratch*") - (set-buffer-major-mode "*scratch*")))) + (progn + (set-buffer-major-mode + (get-buffer-create "*scratch*")) + (get-buffer "*scratch*")))) windows) (if (not (memq frame exwm-workspace--list)) (if (frame-parameter frame 'exwm-window-id) ;; Refresh a floating frame - (progn - (when (eq major-mode 'exwm-mode) - (let ((window (frame-first-window frame))) - (with-current-buffer (window-buffer window) - (exwm--log "Refresh floating window #x%x" exwm--id) - (exwm-layout--show exwm--id window))))) + (when (eq major-mode 'exwm-mode) + (let ((window (frame-first-window frame))) + (with-current-buffer (window-buffer window) + (exwm--log "Refresh floating window #x%x" exwm--id) + (exwm-layout--show exwm--id window)))) ;; Other frames (e.g. terminal/graphical frame of emacsclient) ;; We shall bury all `exwm-mode' buffers in this case (unless placeholder ;create the *scratch* buffer if it's killed diff --git a/exwm-workspace.el b/exwm-workspace.el index 641ba7e680..8000b0ba7f 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -100,10 +100,6 @@ (defvar exwm-workspace-current-index 0 "Index of current active workspace.") (defvar exwm-workspace-show-all-buffers nil "Non-nil to show buffers on other workspaces.") -(defvar exwm-workspace--switch-count 0 - "`exwm-workspace-switch' execution counts. - -Consumed by `exwm-workspace--on-focus-in.'") ;;;###autoload (defun exwm-workspace-switch (index &optional force) @@ -129,7 +125,6 @@ The optional FORCE option is for internal use only." (let ((frame (elt exwm-workspace--list index))) (setq exwm-workspace--current frame exwm-workspace-current-index index) - (unless force (cl-incf exwm-workspace--switch-count)) (select-frame-set-input-focus frame) ;; Move mouse when necessary (let ((position (mouse-pixel-position)) @@ -167,14 +162,14 @@ The optional FORCE option is for internal use only." (defun exwm-workspace--on-focus-in () "Fix unexpected frame switch." - (let ((index (cl-position (selected-frame) exwm-workspace--list))) - (exwm--log "Focus on workspace %s" index) - ;; Close the (possible) active minibuffer - (when (active-minibuffer-window) (abort-recursive-edit)) - (when (and index (/= index exwm-workspace-current-index)) - (exwm--log "Workspace was switched unexpectedly") - (if (< 0 exwm-workspace--switch-count) - (cl-decf exwm-workspace--switch-count) + ;; `focus-in-hook' is run by `handle-switch-frame' + (unless (eq this-command 'handle-switch-frame) + (let ((index (cl-position (selected-frame) exwm-workspace--list))) + (exwm--log "Focus on workspace %s" index) + (when (and index (/= index exwm-workspace-current-index)) + (exwm--log "Workspace was switched unexpectedly") + ;; Close the (possible) active minibuffer + (when (active-minibuffer-window) (abort-recursive-edit)) (exwm-workspace-switch index))))) ;;;###autoload @@ -221,8 +216,10 @@ The optional FORCE option is for internal use only." (bury-buffer) (set-window-buffer (get-buffer-window (current-buffer) t) (or (get-buffer "*scratch*") - (prog1 (get-buffer-create "*scratch*") - (set-buffer-major-mode "*scratch*"))))) + (progn + (set-buffer-major-mode + (get-buffer-create "*scratch*")) + (get-buffer "*scratch*"))))) (exwm-layout--hide id) (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow @@ -335,8 +332,12 @@ The optional FORCE option is for internal use only." (select-frame-set-input-focus (car exwm-workspace--list)) (dolist (i exwm-workspace--list) (set-frame-parameter i 'visibility t) - (lower-frame i) - (set-frame-parameter i 'fullscreen 'fullboth)) + (lower-frame i)) + ;; Delay making the workspaces fullscreen until Emacs becomes idle + (run-with-idle-timer 0 nil + (lambda () + (dolist (i exwm-workspace--list) + (set-frame-parameter i 'fullscreen 'fullboth)))) (raise-frame (car exwm-workspace--list)) ;; Handle unexpected frame switch (add-hook 'focus-in-hook #'exwm-workspace--on-focus-in) -- cgit 1.4.1 From ba536bb6e354577cd69926865aee0e6f90f7306a Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 27 Oct 2015 20:24:28 +0800 Subject: Delay closing minibuffer * exwm-workspace.el (exwm-workspace-switch, exwm-workspace--on-focus-in): Delay closing minibuffer until Emacs is idle. This prevent nonlocal exits from interrupting remaining code. --- exwm-workspace.el | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 8000b0ba7f..d1fe6cf381 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -139,7 +139,8 @@ The optional FORCE option is for internal use only." y (/ h 2))) (set-mouse-pixel-position frame x y))) ;; Close the (possible) active minibuffer - (when (active-minibuffer-window) (abort-recursive-edit)) + (when (active-minibuffer-window) + (run-with-idle-timer 0 nil (lambda () (abort-recursive-edit)))) (setq default-minibuffer-frame frame) ;; Hide windows in other workspaces by preprending a space (unless exwm-workspace-show-all-buffers @@ -168,8 +169,6 @@ The optional FORCE option is for internal use only." (exwm--log "Focus on workspace %s" index) (when (and index (/= index exwm-workspace-current-index)) (exwm--log "Workspace was switched unexpectedly") - ;; Close the (possible) active minibuffer - (when (active-minibuffer-window) (abort-recursive-edit)) (exwm-workspace-switch index))))) ;;;###autoload -- cgit 1.4.1 From 11cc84d77e83b81fce8d3032df29d798546648e7 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 28 Oct 2015 10:37:18 +0800 Subject: Minor input focus fix * exwm-input.el (exwm-input--on-buffer-list-update): Schedule input focus switch with idle timer. --- exwm-input.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm-input.el b/exwm-input.el index 8a9d089e2f..f54c630710 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -99,7 +99,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (setq exwm-input--focus-window window) (when exwm-input--timer (cancel-timer exwm-input--timer)) (setq exwm-input--timer - (run-with-timer 0.01 nil #'exwm-input--update-focus))) + (run-with-idle-timer 0.01 nil #'exwm-input--update-focus))) (setq exwm-input--redirected nil)))) (defun exwm-input--on-focus-in () -- cgit 1.4.1 From 2779d2e418bb07adf0408dc8fdaa0fb6e88bf13a Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 28 Oct 2015 14:04:41 +0800 Subject: Make the creation/destruction of floating frames more silent * exwm-floating.el (exwm-floating--set-floating): Reparent floating frames as late as possible. * exwm-manage.el (exwm-manage--unmanage-window): Hide floating frames first on unmanagement. --- exwm-floating.el | 16 +++++++++------- exwm-manage.el | 11 +++++++---- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 408cf372dc..8fbb2bc26c 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -31,7 +31,7 @@ (eval-when-compile (require 'exwm-workspace)) (defvar exwm-floating-border-width 1 "Border width of the floating window.") -(defvar exwm-floating-border-color "blue" +(defvar exwm-floating-border-color "navy" "Border color of the floating window.") (defvar exwm-floating-setup-hook nil @@ -79,6 +79,8 @@ `((minibuffer . nil) ;use the one on workspace (background-color . ,exwm-floating-border-color) (internal-border-width . ,exwm-floating-border-width) + (left . 10000) + (top . 10000) (unsplittable . t))) ;and fix the size later (exwm--unlock)))) (frame-id (string-to-number (frame-parameter frame 'window-id))) @@ -155,12 +157,6 @@ (make-instance 'xcb:ChangeWindowAttributes :window frame-id :value-mask xcb:CW:EventMask :event-mask xcb:EventMask:SubstructureRedirect)) - ;; Reparent this frame to the original one - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window outer-id :parent original-id - :x (- x exwm-floating-border-width) - :y (- y exwm-floating-border-width))) ;; Save the geometry ;; Rationale: the frame will not be ready for some time, thus we cannot ;; infer the correct window size from its geometry. @@ -182,6 +178,12 @@ (make-instance 'xcb:ChangeWindowAttributes :window id :value-mask xcb:CW:EventMask :event-mask exwm--client-event-mask)) + ;; Reparent this frame to the original one + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window outer-id :parent original-id + :x (- x exwm-floating-border-width) + :y (- y exwm-floating-border-width))) (xcb:flush exwm--connection) ;; Set window/buffer (with-current-buffer (exwm--id->buffer id) diff --git a/exwm-manage.el b/exwm-manage.el index fb6ca1672b..0eed8b9fd7 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -139,9 +139,9 @@ corresponding buffer.") height) 2))))) (xcb:flush exwm--connection) - (setq kill-buffer-query-functions nil) (setq exwm--id-buffer-alist (assq-delete-all id exwm--id-buffer-alist)) - (kill-buffer (current-buffer)) + (let ((kill-buffer-query-functions nil)) + (kill-buffer (current-buffer))) (throw 'return 'ignored)) ;; Manage the window (exwm--log "Manage #x%x" id) @@ -190,6 +190,9 @@ corresponding buffer.") (xcb:flush exwm--connection) (when (buffer-live-p buffer) (with-current-buffer buffer + (when exwm--floating-frame + (make-frame-invisible exwm--floating-frame) + (redisplay)) (setq exwm-workspace--switch-history-outdated t) ;; (when withdraw-only @@ -224,8 +227,8 @@ corresponding buffer.") (make-instance 'xcb:DeleteProperty :window id :property xcb:Atom:WM_STATE)) (xcb:flush exwm--connection)) - (setq kill-buffer-query-functions nil) - (let ((floating exwm--floating-frame)) + (let ((kill-buffer-query-functions nil) + (floating exwm--floating-frame)) (kill-buffer) (when floating (select-window -- cgit 1.4.1 From 34d588599d2d7ef46db6a810c1549199f78cf8ba Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 28 Oct 2015 18:55:49 +0800 Subject: Add demo configurations * exwm-config.el: Demo EXWM configurations. * xinitrc: Demo xinitrc file. * exwm.el (exwm-enable-ido-workaround, exwm-disable-ido-workaround): Partly moved to exwm-config.el. --- exwm-config.el | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ exwm.el | 45 ++++--------------------- xinitrc | 14 ++++++++ 3 files changed, 122 insertions(+), 38 deletions(-) create mode 100644 exwm-config.el create mode 100644 xinitrc diff --git a/exwm-config.el b/exwm-config.el new file mode 100644 index 0000000000..7f02303e23 --- /dev/null +++ b/exwm-config.el @@ -0,0 +1,101 @@ +;;; exwm-config.el --- Predefined configurations -*- lexical-binding: t -*- + +;; Copyright (C) 2015 Free Software Foundation, Inc. + +;; Author: Chris Feng + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Commentary: + +;; This module contains typical (yet minimal) configurations of EXWM. + +;;; Code: + +(require 'exwm) + +(defun exwm-config-default () + "Default configuration of EXWM." + ;; Make class name the buffer name + (add-hook 'exwm-update-class-hook + (lambda () + (exwm-workspace-rename-buffer exwm-class-name))) + ;; 's-r': Reset + (exwm-input-set-key (kbd "s-r") #'exwm-reset) + ;; 's-w': Switch workspace + (exwm-input-set-key (kbd "s-w") #'exwm-workspace-switch) + ;; 's-N': Switch to certain workspace + (dotimes (i exwm-workspace-number) + (exwm-input-set-key (kbd (format "s-%d" i)) + `(lambda () (interactive) (exwm-workspace-switch ,i)))) + ;; 's-&': Launch application + (exwm-input-set-key (kbd "s-&") + (lambda (command) + (interactive (list (read-shell-command "$ "))) + (start-process-shell-command command nil command))) + ;; Line-editing shortcuts + (exwm-input-set-simulation-keys + '(([?\C-b] . left) + ([?\C-f] . right) + ([?\C-p] . up) + ([?\C-n] . down) + ([?\C-a] . home) + ([?\C-e] . end) + ([?\M-v] . prior) + ([?\C-v] . next))) + ;; Enable EXWM + (exwm-enable) + ;; Configure Ido + (exwm-config-ido) + ;; Other configurations + (exwm-config-misc)) + +(defun exwm-config--ido-buffer-window-other-frame (orig-fun buffer) + "Wrapper for `ido-buffer-window-other-frame' to exclude invisible windows." + (with-current-buffer buffer + (if (and (eq major-mode 'exwm-mode) + (or exwm--floating-frame + (not exwm-layout-show-all-buffers))) + ;; `ido-mode' works well with `exwm-mode' buffers + (funcall orig-fun buffer) + ;; Other buffers should be selected within the same workspace + (get-buffer-window buffer exwm-workspace--current)))) + +(defun exwm-config--fix/ido-buffer-window-other-frame () + "Fix `ido-buffer-window-other-frame'." + (advice-add 'ido-buffer-window-other-frame :around + #'exwm-config--ido-buffer-window-other-frame)) + +(defun exwm-config-ido () + "Configure Ido to work with EXWM." + (ido-mode 1) + (add-hook 'exwm-init-hook #'exwm-config--fix/ido-buffer-window-other-frame)) + +(defun exwm-config-misc () + "Other configurations." + ;; Make more room + (menu-bar-mode -1) + (tool-bar-mode -1) + (scroll-bar-mode -1) + (fringe-mode 1) + ;; Disable dialog boxes + (setq use-dialog-box nil)) + + + +(provide 'exwm-config) + +;; exwm-config.el ends here diff --git a/exwm.el b/exwm.el index b5d246ed40..ba71e32ab6 100644 --- a/exwm.el +++ b/exwm.el @@ -43,22 +43,10 @@ ;; 2. In '~/.emacs', add following lines (please modify accordingly): ;; ;; (require 'exwm) -;; ;; We always need a way to go back from char-mode to line-mode -;; (exwm-input-set-key (kbd "s-r") 'exwm-reset) -;; ;; Bind a key to switch workspace interactively -;; (exwm-input-set-key (kbd "s-w") 'exwm-workspace-switch) -;; ;; Use class name to name an EXWM buffer -;; (add-hook 'exwm-update-class-hook -;; (lambda () (exwm-workspace-rename-buffer exwm-class-name t))) -;; ;; Enable EXWM -;; (exwm-enable) -;; -;; 3. Make a file '~/.xinitrc' with the following lines: -;; -;; # You may need to comment out the next line to disable access control -;; #xhost + -;; exec emacs +;; (require 'exwm-config) +;; (exwm-config-default) ;; +;; 3. Link or copy the file 'xinitrc' to '~/.xinitrc'. ;; 4. Launch EXWM in a console (e.g. tty1) with ;; ;; xinit -- vt01 @@ -600,31 +588,12 @@ ;; For other types, return the value as-is. (t result)))))) -(defun exwm--ido-buffer-window-other-frame (orig-fun buffer) - "Wrapper for `ido-buffer-window-other-frame' to exclude invisible windows." - (with-current-buffer buffer - (if (and (eq major-mode 'exwm-mode) - (or exwm--floating-frame - (not exwm-layout-show-all-buffers))) - ;; `ido-mode' works well with `exwm-mode' buffers - (funcall orig-fun buffer) - ;; Other buffers should be selected within the same workspace - (get-buffer-window buffer exwm-workspace--current)))) - -(defun exwm--fix-ido-buffer-window-other-frame () - "Fix `ido-buffer-window-other-frame'." - (advice-add 'ido-buffer-window-other-frame :around - #'exwm--ido-buffer-window-other-frame)) - -(defun exwm-enable-ido-workaround () - "Enable workarounds for 'ido-mode'." - (add-hook 'exwm-init-hook #'exwm--fix-ido-buffer-window-other-frame)) +(define-obsolete-function-alias 'exwm-enable-ido-workaround 'exwm-config-ido + "25.1" "Enable workarounds for Ido.") (defun exwm-disable-ido-workaround () - "Disable workarounds for 'ido-mode'." - (remove-hook 'exwm-init-hook #'exwm--fix-ido-buffer-window-other-frame) - (advice-remove 'ido-buffer-window-other-frame - #'exwm--ido-buffer-window-other-frame)) + "This function does nothing actually." + (declare (obsolete nil "25.1"))) diff --git a/xinitrc b/xinitrc new file mode 100644 index 0000000000..3de5b0b51b --- /dev/null +++ b/xinitrc @@ -0,0 +1,14 @@ +# Disable access control +xhost + + +# Themes, etc +gnome-settings-daemon & + +# Fallback cursor +xsetroot -cursor_name left_ptr + +# Keyboard repeat rate +xset r rate 200 60 + +# Start Emacs +exec dbus-launch --exit-with-session emacs -- cgit 1.4.1 From 51f5e35aa6a85516bcd00b3bf874f3f3261ebfdc Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 28 Oct 2015 20:38:06 +0800 Subject: Check KEYSYMs converted from events * exwm-input.el (exwm-input--update-global-prefix-keys) (exwm-input--fake-key, exwm-input--init): Check the return value of xcb:keysyms:event->keysym. --- exwm-input.el | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index f54c630710..5cf5f95c1e 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -261,7 +261,8 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") exwm--connection (car keysym)) :pointer-mode xcb:GrabMode:Async :keyboard-mode xcb:GrabMode:Async))) - (user-error "[EXWM] Failed to grab key: %s" i)))))))) + (user-error "[EXWM] Failed to grab key: %s" + (single-key-description i))))))))) (defun exwm-input-set-key (key command) "Set a global key binding." @@ -392,9 +393,13 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (defun exwm-input--fake-key (event) "Fake a key event equivalent to Emacs event EVENT." (let* ((keysym (xcb:keysyms:event->keysym event)) - (keycode (xcb:keysyms:keysym->keycode exwm--connection (car keysym))) - (id (exwm--buffer->id (window-buffer (selected-window))))) + keycode id) + (unless keysym + (user-error "[EXWM] Invalid key: %s" (single-key-description event))) + (setq keycode (xcb:keysyms:keysym->keycode exwm--connection + (car keysym))) (when keycode + (setq id (exwm--buffer->id (window-buffer (selected-window)))) (dolist (class '(xcb:KeyPress xcb:KeyRelease)) (xcb:+request exwm--connection (make-instance 'xcb:SendEvent @@ -472,6 +477,12 @@ SIMULATION-KEYS is a list of alist (key-sequence1 . key-sequence2)." ;; Convert move/resize buttons (let ((move-key (xcb:keysyms:event->keysym exwm-input-move-event)) (resize-key (xcb:keysyms:event->keysym exwm-input-resize-event))) + (unless move-key + (user-error "[EXWM] Invalid key: %s" + (single-key-description exwm-input-move-event))) + (unless resize-key + (user-error "[EXWM] Invalid key: %s" + (single-key-description exwm-input-resize-event))) (setq exwm-input--move-keysym (car move-key) exwm-input--move-mask (cadr move-key) exwm-input--resize-keysym (car resize-key) -- cgit 1.4.1 From d9b4ba0265fc1674b1205a11e838f86bce434b13 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 29 Oct 2015 12:22:00 +0800 Subject: Avoid mapping managed X windows on MapRequest * exwm-manage.el (exwm-manage--manage-window, exwm-manage--on-MapRequest): Check managed X windows in exwm-manage--on-MapRequest instead. --- exwm-manage.el | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index 0eed8b9fd7..bf285ab944 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -63,10 +63,6 @@ corresponding buffer.") "Manage window ID." (exwm--log "Try to manage #x%x" id) (catch 'return - ;; Ensure it's not managed - (when (assoc id exwm--id-buffer-alist) - (exwm--log "#x%x is already managed" id) - (throw 'return 'managed)) ;; Ensure it's alive (when (xcb:+request-checked+request-check exwm--connection (make-instance 'xcb:ChangeWindowAttributes @@ -363,12 +359,14 @@ corresponding buffer.") (let ((obj (make-instance 'xcb:MapRequest))) (xcb:unmarshal obj data) (with-slots (parent window) obj - (if (/= exwm--root parent) - (progn (xcb:+request exwm--connection - (make-instance 'xcb:MapWindow :window window)) - (xcb:flush exwm--connection)) - (exwm--log "MapRequest from #x%x" window) - (exwm-manage--manage-window window))))) + (if (assoc window exwm--id-buffer-alist) + (exwm--log "#x%x is already managed" id) + (if (/= exwm--root parent) + (progn (xcb:+request exwm--connection + (make-instance 'xcb:MapWindow :window window)) + (xcb:flush exwm--connection)) + (exwm--log "MapRequest from #x%x" window) + (exwm-manage--manage-window window)))))) (defun exwm-manage--on-UnmapNotify (data synthetic) "Handle UnmapNotify event." -- cgit 1.4.1 From b2ee10205177361a5f14dd11c4206b5ffd1cb98c Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 29 Oct 2015 18:31:16 +0800 Subject: Improve robustness of killing buffers * exwm-core.el (exwm-mode): Use the return value of exwm-manage--close-window. * exwm-manage.el (exwm-manage--close-window): Kill empty buffers; Handle X windows that does not support _NET_WM_PING. (exwm-manage--kill-client): Kill X windows supporting _NET_WM_PID with both SIGKILL and KillClient. --- exwm-core.el | 3 +-- exwm-manage.el | 69 +++++++++++++++++++++++++++++++++------------------------- 2 files changed, 40 insertions(+), 32 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index ec7034737a..aaa98e394f 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -139,8 +139,7 @@ ;; Kill buffer -> close window (add-hook 'kill-buffer-query-functions (lambda () - (exwm-manage--close-window exwm--id (current-buffer)) - nil) + (exwm-manage--close-window exwm--id (current-buffer))) nil t) (setq buffer-read-only t left-margin-width nil diff --git a/exwm-manage.el b/exwm-manage.el index bf285ab944..aa484d657b 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -249,6 +249,8 @@ corresponding buffer.") (defun exwm-manage--close-window (id &optional buffer) "Close window ID in a proper way." (catch 'return + (unless (exwm--id->buffer id) + (throw 'return t)) (unless buffer (setq buffer (exwm--id->buffer id))) ;; Destroy the client window if it does not support WM_DELETE_WINDOW (unless (and (buffer-live-p buffer) @@ -268,31 +270,33 @@ corresponding buffer.") exwm--connection))) (xcb:flush exwm--connection) ;; Try to determine if the client stop responding - ;; FIXME: check (with-current-buffer buffer - (when (memq xcb:Atom:_NET_WM_PING exwm--protocols) - (setq exwm-manage--ping-lock t) - (xcb:+request exwm--connection - (make-instance 'xcb:SendEvent - :propagate 0 :destination id - :event-mask xcb:EventMask:NoEvent - :event (xcb:marshal - (make-instance 'xcb:ewmh:_NET_WM_PING - :window id :timestamp 0 - :client-window id) - exwm--connection))) - (xcb:flush exwm--connection) - (with-timeout (exwm-manage-ping-timeout - (if (yes-or-no-p (format "\ -`%s' is not responding. Would you like to kill it? " (buffer-name buffer))) - (progn (exwm-manage--kill-client id) - (throw 'return nil)) - (throw 'return nil))) - (while (and exwm-manage--ping-lock - (exwm--id->buffer id)) ;may have be destroyed - (accept-process-output nil 0.1))) - (throw 'return nil))) - (throw 'return nil))) + (unless (memq xcb:Atom:_NET_WM_PING exwm--protocols) + ;; Ensure it's dead + (run-with-timer exwm-manage-ping-timeout nil + `(lambda () (exwm-manage--kill-client ,id))) + (throw 'return nil)) + (setq exwm-manage--ping-lock t) + (xcb:+request exwm--connection + (make-instance 'xcb:SendEvent + :propagate 0 :destination id + :event-mask xcb:EventMask:NoEvent + :event (xcb:marshal + (make-instance 'xcb:ewmh:_NET_WM_PING + :window id :timestamp 0 + :client-window id) + exwm--connection))) + (xcb:flush exwm--connection) + (with-timeout (exwm-manage-ping-timeout + (if (yes-or-no-p (format "`%s' is not responding. \ +Would you like to kill it? " + (buffer-name buffer))) + (progn (exwm-manage--kill-client id) + (throw 'return nil)) + (throw 'return nil))) + (while (and exwm-manage--ping-lock + (exwm--id->buffer id)) ;may have been destroyed + (accept-process-output nil 0.1)))))) (defun exwm-manage--kill-client (&optional id) "Kill an X client." @@ -300,12 +304,17 @@ corresponding buffer.") (unless id (setq id (exwm--buffer->id (current-buffer)))) (let* ((response (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:ewmh:get-_NET_WM_PID :window id))) - (pid (and response (slot-value response 'value)))) - (if pid - (signal-process pid 'SIGKILL) - (xcb:+request exwm--connection - (make-instance 'xcb:KillClient :resource id)) - (xcb:flush exwm--connection)))) + (pid (and response (slot-value response 'value))) + (request (make-instance 'xcb:KillClient :resource id))) + (if (not pid) + (xcb:+request exwm--connection request) + ;; What if the PID is fake/wrong? + (signal-process pid 'SIGKILL) + ;; Ensure it's dead + (run-with-timer exwm-manage-ping-timeout nil + `(lambda () + (xcb:+request exwm--connection ,request)))) + (xcb:flush exwm--connection))) (defun exwm-manage--on-ConfigureRequest (data _synthetic) "Handle ConfigureRequest event." -- cgit 1.4.1 From 45c24eac7a1f9eab5e4a5244e2d14f9f35aa69fa Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 30 Oct 2015 17:23:02 +0800 Subject: Use new calling conventions of xcb:keysyms * exwm-input.el: Use new calling conventions of xcb:keysyms --- exwm-input.el | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 5cf5f95c1e..98324b8d68 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -238,7 +238,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") "Update `exwm-input--global-prefix-keys'." (when exwm--connection (let ((original exwm-input--global-prefix-keys) - keysym) + keysym keycode) (setq exwm-input--global-prefix-keys nil) (dolist (i exwm-input--global-keys) (cl-pushnew (elt i 0) exwm-input--global-prefix-keys)) @@ -250,15 +250,16 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") :modifiers xcb:ModMask:Any)) (exwm--log "Failed to ungrab keys") (dolist (i exwm-input--global-prefix-keys) - (setq keysym (xcb:keysyms:event->keysym i)) + (setq keysym (xcb:keysyms:event->keysym exwm--connection i)) (when (or (not keysym) + (not (setq keycode (xcb:keysyms:keysym->keycode + exwm--connection (car keysym)))) (xcb:+request-checked+request-check exwm--connection (make-instance 'xcb:GrabKey :owner-events 0 :grab-window exwm--root :modifiers (cadr keysym) - :key (xcb:keysyms:keysym->keycode - exwm--connection (car keysym)) + :key keycode :pointer-mode xcb:GrabMode:Async :keyboard-mode xcb:GrabMode:Async))) (user-error "[EXWM] Failed to grab key: %s" @@ -283,7 +284,8 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) event minibuffer-window mode) (when (and keysym - (setq event (xcb:keysyms:keysym->event keysym state)) + (setq event (xcb:keysyms:keysym->event exwm--connection + keysym state)) (or exwm-input--during-key-sequence (setq minibuffer-window (active-minibuffer-window)) (memq real-this-command exwm-input-command-whitelist) @@ -319,7 +321,9 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") :event id :mode xcb:NotifyMode:Normal) exwm--connection))))) - (when (and keysym (setq event (xcb:keysyms:keysym->event keysym state))) + (when (and keysym + (setq event (xcb:keysyms:keysym->event exwm--connection + keysym state))) (when (eq major-mode 'exwm-mode) (setq exwm-input--temp-line-mode t exwm-input--during-key-sequence t) @@ -392,7 +396,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (defun exwm-input--fake-key (event) "Fake a key event equivalent to Emacs event EVENT." - (let* ((keysym (xcb:keysyms:event->keysym event)) + (let* ((keysym (xcb:keysyms:event->keysym exwm--connection event)) keycode id) (unless keysym (user-error "[EXWM] Invalid key: %s" (single-key-description event))) @@ -475,8 +479,10 @@ SIMULATION-KEYS is a list of alist (key-sequence1 . key-sequence2)." ;; Refresh keyboard mapping (xcb:keysyms:init exwm--connection) ;; Convert move/resize buttons - (let ((move-key (xcb:keysyms:event->keysym exwm-input-move-event)) - (resize-key (xcb:keysyms:event->keysym exwm-input-resize-event))) + (let ((move-key (xcb:keysyms:event->keysym exwm--connection + exwm-input-move-event)) + (resize-key (xcb:keysyms:event->keysym exwm--connection + exwm-input-resize-event))) (unless move-key (user-error "[EXWM] Invalid key: %s" (single-key-description exwm-input-move-event))) -- cgit 1.4.1 From 9ee1c9bb175b164c7146cdf51ab91642d08a3ed9 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 1 Nov 2015 10:54:20 +0800 Subject: Add hook run when screen changes * exwm-randr.el (exwm-randr-screen-change-hook): New hook. (exwm-randr--init): Run the new hook in the event handler of ScreenChangeNotify. --- exwm-randr.el | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/exwm-randr.el b/exwm-randr.el index 26d15dcb7d..1ac8f29e1e 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -25,15 +25,21 @@ ;; tools such as xrandr(1) to properly configure RandR first. This dependency ;; may be removed in the future, but more work is needed before that. -;; To use this module, first load/enable it and properly configure the variable -;; `exwm-randr-workspace-output-plist': +;; To use this module, load, enable it and configure +;; `exwm-randr-workspace-output-plist' and `exwm-randr-screen-change-hook' +;; as follows: +;; ;; (require 'exwm-randr) ;; (setq exwm-randr-workspace-output-plist '(0 "VGA1")) +;; (add-hook 'exwm-randr-screen-change-hook +;; (lambda () +;; (start-process-shell-command +;; "xrandr" nil "xrandr --output VGA1 --left-of LVDS1 --auto"))) ;; (exwm-randr-enable) -;; Then configure RandR with 'xrandr': -;; $ xrandr --output VGA1 --left-of LVDS1 --auto +;; ;; With above lines, workspace 0 should be assigned to the output named "VGA1", -;; staying at the left of other workspaces on the output "LVDS1". +;; staying at the left of other workspaces on the output "LVDS1". Please refer +;; to xrandr(1) for the configuration of RandR. ;; References: ;; + RandR (http://www.x.org/archive/X11R7.7/doc/randrproto/randrproto.txt) @@ -109,6 +115,9 @@ :data (vconcat viewports))) (xcb:flush exwm--connection))) +(defvar exwm-randr-screen-change-hook nil + "Normal hook run when screen changes.") + (defun exwm-randr--init () "Initialize RandR extension and EXWM RandR module." (if (= 0 (slot-value (xcb:get-extension-data exwm--connection 'xcb:randr) @@ -125,6 +134,7 @@ (xcb:+event exwm--connection 'xcb:randr:ScreenChangeNotify (lambda (_data _synthetic) (exwm--log "(RandR) ScreenChangeNotify") + (run-hooks 'exwm-randr-screen-change-hook) (exwm-randr--refresh))) ;; (xcb:+event exwm--connection 'xcb:randr:Notify ;; (lambda (_data _synthetic) -- cgit 1.4.1 From 7ee6d48a358b6d00cba77ad934dc431bbdd833c8 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 2 Nov 2015 11:19:59 +0800 Subject: Disable some incompatible features * exwm.el (exwm-init): exwm-config.el (exwm-config-misc): Disable dialog boxes and hourglass pointer by default. --- exwm-config.el | 4 +--- exwm.el | 3 +++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/exwm-config.el b/exwm-config.el index 7f02303e23..bf3fd2ffbb 100644 --- a/exwm-config.el +++ b/exwm-config.el @@ -90,9 +90,7 @@ (menu-bar-mode -1) (tool-bar-mode -1) (scroll-bar-mode -1) - (fringe-mode 1) - ;; Disable dialog boxes - (setq use-dialog-box nil)) + (fringe-mode 1)) diff --git a/exwm.el b/exwm.el index ba71e32ab6..fb4e47040b 100644 --- a/exwm.el +++ b/exwm.el @@ -500,6 +500,9 @@ (progn (xcb:disconnect exwm--connection) (setq exwm--connection nil) (exwm--log "Other window manager detected")) + ;; Disable some features not working well with EXWM + (setq use-dialog-box nil + display-hourglass nil) ;; Initialize ICCCM/EWMH support ;; (xcb:icccm:init exwm--connection) (xcb:ewmh:init exwm--connection) -- cgit 1.4.1 From 5c5c7cf76b5914d8dfe18856a1e301ad4cd48ce4 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 3 Nov 2015 09:08:43 +0800 Subject: Bump version to 0.1 --- exwm.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm.el b/exwm.el index fb4e47040b..96413c9931 100644 --- a/exwm.el +++ b/exwm.el @@ -4,7 +4,7 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0 +;; Version: 0.1 ;; Package-Requires: ((xelb "0.3")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From a295a15ff646cbfa174ee1f78f9f8e27e1a7d587 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 10 Nov 2015 19:10:16 +0800 Subject: Fix a typo * exwm-manage.el (exwm-manage--on-MapRequest): Correct a wrong variable name. --- exwm-manage.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm-manage.el b/exwm-manage.el index aa484d657b..58908a2ffa 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -369,7 +369,7 @@ Would you like to kill it? " (xcb:unmarshal obj data) (with-slots (parent window) obj (if (assoc window exwm--id-buffer-alist) - (exwm--log "#x%x is already managed" id) + (exwm--log "#x%x is already managed" window) (if (/= exwm--root parent) (progn (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window window)) -- cgit 1.4.1 From 3b19dad16269dc18d65919fe7adea12dbedc6550 Mon Sep 17 00:00:00 2001 From: "W. Greenhouse" Date: Sun, 15 Nov 2015 03:35:54 +0000 Subject: Limit X host-based auth permissions. We don't need more than the currently logged in user to have access to the X session, so don't grant X host access to other users. --- xinitrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xinitrc b/xinitrc index 3de5b0b51b..873265fb64 100644 --- a/xinitrc +++ b/xinitrc @@ -1,5 +1,5 @@ # Disable access control -xhost + +xhost +SI:localuser:$USER # Themes, etc gnome-settings-daemon & -- cgit 1.4.1 From 4d5dd85dcc49c8ee9f0c496b439b420eaaeae5af Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 11 Dec 2015 12:11:24 +0800 Subject: Add support for desktop and dock * exwm-manage.el (exwm-manage--manage-window): Add support for _NET_WM_WINDOW_TYPE_DESKTOP and _NET_WM_WINDOW_TYPE_DOCK (they are not reparented). --- exwm-manage.el | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index 58908a2ffa..5b562ed52f 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -111,13 +111,17 @@ corresponding buffer.") (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window id)) (with-slots (x y width height) exwm--geometry - ;; Reparent to virtual root (essential) - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window id - :parent (frame-parameter exwm-workspace--current - 'exwm-window-id) - :x x :y y)) + ;; Reparent to virtual root + (unless (or (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DESKTOP + exwm-window-type) + (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DOCK + exwm-window-type)) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window id + :parent (frame-parameter exwm-workspace--current + 'exwm-window-id) + :x x :y y))) ;; Center window of type _NET_WM_WINDOW_TYPE_SPLASH (when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_SPLASH exwm-window-type) (xcb:+request exwm--connection -- cgit 1.4.1 From 07921a3731c3b951d7d5ecc35b808c40d1d15bd4 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 13 Dec 2015 16:57:04 +0800 Subject: Remove the MappingNotify event listener * exwm-input.el (exwm-input--on-MappingNotify, exwm-input--init): Remove the event listener for MappingNotify event (it should be handled in the underling library instead). --- exwm-input.el | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 98324b8d68..99a4b8fa62 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -152,23 +152,6 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (setq exwm-input--temp-line-mode nil) (exwm-input--release-keyboard)))) -(defun exwm-input--on-MappingNotify (data _synthetic) - "Handle MappingNotify event." - (let ((obj (make-instance 'xcb:MappingNotify))) - (xcb:unmarshal obj data) - (with-slots (request first-keycode count) obj - (cond - ((= request xcb:Mapping:Modifier) - ;; Modifier keys changed - (exwm--log "Update modifier mapping") - (xcb:keysyms:update-modifier-mapping exwm--connection)) - ((= request xcb:Mapping:Keyboard) - ;; Only update changed keys - (exwm--log "Update keyboard mapping: %d ~ %d" - first-keycode (+ first-keycode count)) - (xcb:keysyms:update-keyboard-mapping exwm--connection - first-keycode count)))))) - (defun exwm-input--on-ButtonPress (data _synthetic) "Handle ButtonPress event." (let ((obj (make-instance 'xcb:ButtonPress)) @@ -494,8 +477,6 @@ SIMULATION-KEYS is a list of alist (key-sequence1 . key-sequence2)." exwm-input--resize-keysym (car resize-key) exwm-input--resize-mask (cadr resize-key))) ;; Attach event listeners - (xcb:+event exwm--connection 'xcb:MappingNotify - #'exwm-input--on-MappingNotify) (xcb:+event exwm--connection 'xcb:KeyPress #'exwm-input--on-KeyPress) (xcb:+event exwm--connection 'xcb:ButtonPress #'exwm-input--on-ButtonPress) (xcb:+event exwm--connection 'xcb:ButtonRelease -- cgit 1.4.1 From 0db666b4fbbf0ce4446e5e5205fa70822cd93fd6 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 2 Feb 2016 22:33:58 +0800 Subject: Update copyright year to 2016 --- exwm-config.el | 2 +- exwm-core.el | 2 +- exwm-floating.el | 2 +- exwm-input.el | 2 +- exwm-layout.el | 2 +- exwm-manage.el | 2 +- exwm-randr.el | 2 +- exwm-workspace.el | 2 +- exwm.el | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/exwm-config.el b/exwm-config.el index bf3fd2ffbb..512abfc0df 100644 --- a/exwm-config.el +++ b/exwm-config.el @@ -1,6 +1,6 @@ ;;; exwm-config.el --- Predefined configurations -*- lexical-binding: t -*- -;; Copyright (C) 2015 Free Software Foundation, Inc. +;; Copyright (C) 2015-2016 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-core.el b/exwm-core.el index aaa98e394f..744c4ac69e 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -1,6 +1,6 @@ ;;; exwm-core.el --- Core definitions -*- lexical-binding: t -*- -;; Copyright (C) 2015 Free Software Foundation, Inc. +;; Copyright (C) 2015-2016 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-floating.el b/exwm-floating.el index 8fbb2bc26c..a4c9b17c98 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -1,6 +1,6 @@ ;;; exwm-floating.el --- Floating Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015 Free Software Foundation, Inc. +;; Copyright (C) 2015-2016 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-input.el b/exwm-input.el index 99a4b8fa62..d399fbbd8f 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -1,6 +1,6 @@ ;;; exwm-input.el --- Input Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015 Free Software Foundation, Inc. +;; Copyright (C) 2015-2016 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-layout.el b/exwm-layout.el index ffe1f1d0c4..2374823fea 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -1,6 +1,6 @@ ;;; exwm-layout.el --- Layout Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015 Free Software Foundation, Inc. +;; Copyright (C) 2015-2016 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-manage.el b/exwm-manage.el index 5b562ed52f..e5a717932c 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -1,7 +1,7 @@ ;;; exwm-manage.el --- Window Management Module for -*- lexical-binding: t -*- ;;; EXWM -;; Copyright (C) 2015 Free Software Foundation, Inc. +;; Copyright (C) 2015-2016 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-randr.el b/exwm-randr.el index 1ac8f29e1e..8c8f94d93b 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -1,6 +1,6 @@ ;;; exwm-randr.el --- RandR Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015 Free Software Foundation, Inc. +;; Copyright (C) 2015-2016 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-workspace.el b/exwm-workspace.el index d1fe6cf381..8510059a08 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1,6 +1,6 @@ ;;; exwm-workspace.el --- Workspace Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015 Free Software Foundation, Inc. +;; Copyright (C) 2015-2016 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm.el b/exwm.el index 96413c9931..719ec89578 100644 --- a/exwm.el +++ b/exwm.el @@ -1,6 +1,6 @@ ;;; exwm.el --- Emacs X Window Manager -*- lexical-binding: t -*- -;; Copyright (C) 2015 Free Software Foundation, Inc. +;; Copyright (C) 2015-2016 Free Software Foundation, Inc. ;; Author: Chris Feng ;; Maintainer: Chris Feng -- cgit 1.4.1 From 9c95c03e18f6d5cf78bcd54bf00f8055a3863336 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 3 Feb 2016 12:12:24 +0800 Subject: Rework the X windows hierarchy model This commit add workspace and X window containers support to avoid using Emacs frames as the parents of X windows. This should make it easier to set input focus. * exwm-core.el (exwm--container, exwm--floating-frame-position): New file local variables. (exwm--floating-frame-geometry): Removed file local variable. * exwm-floating.el (exwm-floating--set-floating) (exwm-floating--unset-floating, exwm-floating--do-moveresize) (exwm-floating-move): Use container. (exwm-floating--fit-frame-to-window): No longer adjust stacking order. (exwm-floating--fit-frame-to-window): The first member is changed to buffer. (exwm-floating--start-moveresize): Use container. Correctly set input focus. * exwm-input.el (exwm-input--redirected, exwm-input--on-focus-in): Removed. (exwm-input--on-buffer-list-update): Remove the restriction on floating frames which is no longer valid. (exwm-input--update-focus): Adjust stacking order. (exwm-input--on-minibuffer-setup): New function for setting focus on the Emacs frame when entering minibuffer. (exwm-input--on-KeyPress-line-mode): No longer compensate FocusOut event. (exwm-input--grab-keyboard, exwm-input--release-keyboard): Local keys are now grabbed on the X window container. (exwm-input--init): Add `exwm-input--on-minibuffer-setup' to `minibuffer-setup-hook'. * exwm-layout.el (exwm-layout--resize-container): New function to resize/reposition both the X window and its container. (exwm-layout--show, exwm-layout--hide): Use container. (exwm-layout-set-fullscreen): Use container. No longer save width and height. (exwm-layout-unset-fullscreen, exwm-layout--set-frame-fullscreen): Use container. (exwm-layout--refresh): Update a frame parameter. Remove dead code. * exwm-manage.el (exwm-manage--manage-window): Reparent unmanaged X windows to the workspace. Create X window container as the parent of the X window. (exwm-manage--unmanage-window): Unmap/destroy container when appropriate. Use the position of container. (exwm-manage--unmanage-window): Destroy the container. * exwm-randr.el (exwm-randr--refresh): Resize workspace using container. * exwm-workspace.el (exwm-workspace-switch): Raise workspace. Correctly set input focus. (exwm-workspace--on-focus-in): Removed. (exwm-workspace-move-window): Reparent to workspace container. (exwm-workspace--init): Create workspace frames as visible. Create workspace containers. Remove exwm-workspace--on-focus-in from focus-in-hook. Update _NET_VIRTUAL_ROOTS. * exwm.el (exwm-init): No longer disable hourglass window. Initialize workspace module before input. * exwm-core.el (exwm--debug): New macro for setting debug forms. * exwm-floating.el (exwm-floating--set-floating): No longer do `exwm--lock' and `exwm--unlock' since `make-frame' is already adviced to take care of everything. Correctly set input focus to the newly created floating X window. * exwm-core.el (exwm--floating-edges): Removed file local variable. * exwm-floating.el (exwm-floating--set-floating) (exwm-floating--unset-floating): * exwm-layout.el (exwm-layout--show, exwm-layout-enlarge-window): * exwm-manage.el (exwm-manage--on-ConfigureRequest): No longer use floating geometry. * exwm-input.el (exwm-input--update-global-prefix-keys): Grab global keys on workspaces containers instead of the root window (or input focus would transfer to the workspace containing the pointer when the grab is active). * exwm-workspace.el (exwm-workspace-switch): No longer move mouse. --- exwm-core.el | 7 ++- exwm-floating.el | 154 +++++++++++++++++++++++----------------------------- exwm-input.el | 140 ++++++++++++++++++++++++------------------------ exwm-layout.el | 157 ++++++++++++++++++++++++++---------------------------- exwm-manage.el | 140 ++++++++++++++++++++++++++++++------------------ exwm-randr.el | 14 ++--- exwm-workspace.el | 93 +++++++++++++++----------------- exwm.el | 5 +- 8 files changed, 356 insertions(+), 354 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index aaa98e394f..55801a9d2d 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -38,6 +38,9 @@ (when exwm-debug-on `(message (concat "[EXWM] " ,format-string) ,@args))) +(defmacro exwm--debug (&rest forms) + (when exwm-debug-on `(progn ,@forms))) + (defvar exwm--connection nil "X connection.") (defvar exwm--root nil "Root window.") (defvar exwm--id-buffer-alist nil "Alist of ( . ).") @@ -77,12 +80,12 @@ ;; Internal variables (defvar-local exwm--id nil) ;window ID +(defvar-local exwm--container nil) ;container (defvar-local exwm--frame nil) ;workspace frame (defvar-local exwm--floating-frame nil) ;floating frame -(defvar-local exwm--floating-edges nil) ;four edges (defvar-local exwm--floating-mode-line-format nil) ;save mode-line-format (defvar-local exwm--fullscreen nil) ;used in fullscreen -(defvar-local exwm--floating-frame-geometry nil) ;in fullscreen +(defvar-local exwm--floating-frame-position nil) ;used in fullscreen (defvar-local exwm--fixed-size nil) ;fixed size (defvar-local exwm--on-KeyPress ;KeyPress event handler #'exwm-input--on-KeyPress-line-mode) diff --git a/exwm-floating.el b/exwm-floating.el index 8fbb2bc26c..251856c254 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -65,7 +65,6 @@ exwm--frame) ;; Fallback to current workspace exwm-workspace--current))) - (original-id (frame-parameter original-frame 'exwm-window-id)) ;; Create new frame (frame (with-current-buffer (or (get-buffer "*scratch*") @@ -73,18 +72,16 @@ (set-buffer-major-mode (get-buffer-create "*scratch*")) (get-buffer "*scratch*"))) - (prog2 - (exwm--lock) - (make-frame - `((minibuffer . nil) ;use the one on workspace - (background-color . ,exwm-floating-border-color) - (internal-border-width . ,exwm-floating-border-width) - (left . 10000) - (top . 10000) - (unsplittable . t))) ;and fix the size later - (exwm--unlock)))) - (frame-id (string-to-number (frame-parameter frame 'window-id))) + (make-frame + `((minibuffer . nil) ;use the one on workspace + (background-color . ,exwm-floating-border-color) + (internal-border-width . ,exwm-floating-border-width) + (left . 10000) + (top . 10000) + (unsplittable . t))))) ;and fix the size later (outer-id (string-to-number (frame-parameter frame 'outer-window-id))) + (container (with-current-buffer (exwm--id->buffer id) + exwm--container)) (window (frame-first-window frame)) ;and it's the only window (x (slot-value exwm--geometry 'x)) (y (slot-value exwm--geometry 'y)) @@ -99,7 +96,6 @@ (exwm--log "Floating geometry (original, relative): %dx%d%+d%+d" width height x y) ;; Save window IDs - (set-frame-parameter frame 'exwm-window-id frame-id) (set-frame-parameter frame 'exwm-outer-id outer-id) ;; Set urgency flag if it's not appear in the active workspace (let ((idx (cl-position original-frame exwm-workspace--list))) @@ -152,36 +148,19 @@ (setq x (/ (- display-width width) 2) y (/ (- display-height height) 2)))))) (exwm--log "Floating geometry (corrected): %dx%d%+d%+d" width height x y) - ;; Set event mask - (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window frame-id :value-mask xcb:CW:EventMask - :event-mask xcb:EventMask:SubstructureRedirect)) - ;; Save the geometry - ;; Rationale: the frame will not be ready for some time, thus we cannot - ;; infer the correct window size from its geometry. - (with-current-buffer (exwm--id->buffer id) - (setq exwm--floating-edges (vector x y (+ width x) (+ height y)))) ;; Fit frame to client (exwm-floating--fit-frame-to-window outer-id width height) - ;; Reparent window to this frame - (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window id :value-mask xcb:CW:EventMask - :event-mask xcb:EventMask:NoEvent)) + ;; Reparent this frame to the container (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow - :window id :parent frame-id - :x exwm-floating-border-width - :y exwm-floating-border-width)) - (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window id :value-mask xcb:CW:EventMask - :event-mask exwm--client-event-mask)) - ;; Reparent this frame to the original one + :window outer-id :parent container :x 0 :y 0)) + ;; Place the container (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window outer-id :parent original-id + (make-instance 'xcb:ConfigureWindow + :window container + :value-mask (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y)) :x (- x exwm-floating-border-width) :y (- y exwm-floating-border-width))) (xcb:flush exwm--connection) @@ -192,7 +171,7 @@ exwm--floating-frame frame) (set-window-buffer window (current-buffer)) ;this changes current buffer (set-window-dedicated-p window t)) - (select-window window)) + (select-frame-set-input-focus frame)) (run-hooks 'exwm-floating-setup-hook)) ;;;###autoload @@ -200,25 +179,16 @@ "Make window ID non-floating." (interactive) (let ((buffer (exwm--id->buffer id))) - ;; Reparent to workspace frame - (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window id :value-mask xcb:CW:EventMask - :event-mask xcb:EventMask:NoEvent)) - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window id - :parent (frame-parameter exwm-workspace--current - 'exwm-window-id) - :x 0 :y 0)) ;temporary position - (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window id :value-mask xcb:CW:EventMask - :event-mask exwm--client-event-mask)) + (with-current-buffer buffer + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window exwm--container + :parent (frame-parameter exwm-workspace--current + 'exwm-workspace) + :x 0 :y 0))) ;temporary position (xcb:flush exwm--connection) (with-current-buffer buffer (when exwm--floating-frame ;from floating to non-floating - (setq exwm--floating-edges nil) ;invalid by now (set-window-dedicated-p (frame-first-window exwm--floating-frame) nil) (delete-frame exwm--floating-frame))) ;remove the floating frame (with-current-buffer buffer @@ -260,13 +230,11 @@ are provided. You should call `xcb:flush' and restore the value of 'exwm-outer-id)) :value-mask (eval-when-compile (logior xcb:ConfigWindow:Width - xcb:ConfigWindow:Height - xcb:ConfigWindow:StackMode)) + xcb:ConfigWindow:Height)) :width (+ width (* 2 exwm-floating-border-width)) :height (+ height (* 2 exwm-floating-border-width) (window-mode-line-height) - (window-header-line-height)) - :stack-mode xcb:StackMode:Above))) ;top-most + (window-header-line-height))))) (defun exwm-floating-hide-mode-line () "Hide mode-line of a floating frame." @@ -294,22 +262,23 @@ are provided. You should call `xcb:flush' and restore the value of (setq window-size-fixed exwm--fixed-size))) (defvar exwm-floating--moveresize-calculate nil - "Calculate move/resize parameters [frame-id event-mask x y width height].") + "Calculate move/resize parameters [buffer event-mask x y width height].") ;;;###autoload (defun exwm-floating--start-moveresize (id &optional type) "Start move/resize." (let ((buffer (exwm--id->buffer id)) - frame frame-id x y width height cursor) + frame container x y width height cursor) (when (and buffer - (setq frame (with-current-buffer buffer exwm--floating-frame)) - (setq frame-id (frame-parameter frame 'exwm-outer-id)) + (with-current-buffer buffer + (setq frame exwm--floating-frame + container exwm--container)) ;; Test if the pointer can be grabbed (= xcb:GrabStatus:Success (slot-value (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GrabPointer - :owner-events 0 :grab-window frame-id + :owner-events 0 :grab-window container :event-mask xcb:EventMask:NoEvent :pointer-mode xcb:GrabMode:Async :keyboard-mode xcb:GrabMode:Async @@ -317,11 +286,10 @@ are provided. You should call `xcb:flush' and restore the value of :cursor xcb:Cursor:None :time xcb:Time:CurrentTime)) 'status))) - (setq exwm--floating-edges nil) ;invalid by now (with-slots (root-x root-y win-x win-y) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:QueryPointer :window id)) - (select-frame-set-input-focus frame) ;raise and focus it + (select-window (frame-first-window frame)) ;transfer input focus (setq width (frame-pixel-width frame) height (frame-pixel-height frame)) (unless type @@ -347,7 +315,7 @@ are provided. You should call `xcb:flush' and restore the value of (setq cursor exwm-floating--cursor-move exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,frame-id + (vector ,buffer ,(eval-when-compile (logior xcb:ConfigWindow:X xcb:ConfigWindow:Y)) @@ -356,7 +324,7 @@ are provided. You should call `xcb:flush' and restore the value of (setq cursor exwm-floating--cursor-top-left exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,frame-id + (vector ,buffer ,(eval-when-compile (logior xcb:ConfigWindow:X xcb:ConfigWindow:Y @@ -369,7 +337,7 @@ are provided. You should call `xcb:flush' and restore the value of (setq cursor exwm-floating--cursor-top exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,frame-id + (vector ,buffer ,(eval-when-compile (logior xcb:ConfigWindow:Y xcb:ConfigWindow:Height)) @@ -378,7 +346,7 @@ are provided. You should call `xcb:flush' and restore the value of (setq cursor exwm-floating--cursor-top-right exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,frame-id + (vector ,buffer ,(eval-when-compile (logior xcb:ConfigWindow:Y xcb:ConfigWindow:Width @@ -389,13 +357,13 @@ are provided. You should call `xcb:flush' and restore the value of (setq cursor exwm-floating--cursor-right exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,frame-id ,xcb:ConfigWindow:Width + (vector ,buffer ,xcb:ConfigWindow:Width 0 0 (- x ,(- root-x width)) 0)))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT) (setq cursor exwm-floating--cursor-bottom-right exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,frame-id + (vector ,buffer ,(eval-when-compile (logior xcb:ConfigWindow:Width xcb:ConfigWindow:Height)) @@ -405,14 +373,14 @@ are provided. You should call `xcb:flush' and restore the value of (setq cursor exwm-floating--cursor-bottom exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,frame-id + (vector ,buffer ,xcb:ConfigWindow:Height 0 0 0 (- y ,(- root-y height)))))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT) (setq cursor exwm-floating--cursor-bottom-left exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,frame-id + (vector ,buffer ,(eval-when-compile (logior xcb:ConfigWindow:X xcb:ConfigWindow:Width @@ -425,7 +393,7 @@ are provided. You should call `xcb:flush' and restore the value of (setq cursor exwm-floating--cursor-left exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,frame-id + (vector ,buffer ,(eval-when-compile (logior xcb:ConfigWindow:X xcb:ConfigWindow:Width)) @@ -433,7 +401,7 @@ are provided. You should call `xcb:flush' and restore the value of ;; Select events and change cursor (should always succeed) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GrabPointer - :owner-events 0 :grab-window frame-id + :owner-events 0 :grab-window container :event-mask (eval-when-compile (logior xcb:EventMask:ButtonRelease xcb:EventMask:ButtonMotion)) @@ -488,12 +456,26 @@ are provided. You should call `xcb:flush' and restore the value of (xcb:unmarshal obj data) (setq result (funcall exwm-floating--moveresize-calculate (slot-value obj 'root-x) (slot-value obj 'root-y))) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window (elt result 0) :value-mask (elt result 1) - :x (- (elt result 2) frame-x) - :y (- (elt result 3) frame-y) - :width (elt result 4) :height (elt result 5))) + (with-current-buffer (aref result 0) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window exwm--container + :value-mask (logand (aref result 1) + (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y))) + :x (- (aref result 2) frame-x) + :y (- (aref result 3) frame-y))) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter exwm--floating-frame + 'exwm-outer-id) + :value-mask + (logand (aref result 1) + (eval-when-compile + (logior xcb:ConfigWindow:Width + xcb:ConfigWindow:Height))) + :width (aref result 4) :height (aref result 5)))) (xcb:flush exwm--connection)))) (defun exwm-floating-move (&optional delta-x delta-y) @@ -505,13 +487,13 @@ Both DELTA-X and DELTA-Y default to 1. This command should be bound locally." (unless delta-x (setq delta-x 1)) (unless delta-y (setq delta-y 1)) (unless (and (= 0 delta-x) (= 0 delta-y)) - (let* ((id (frame-parameter exwm--floating-frame 'exwm-outer-id)) - (geometry (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:GetGeometry :drawable id))) + (let* ((geometry (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetGeometry + :drawable exwm--container))) (edges (window-inside-absolute-pixel-edges))) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow - :window id + :window exwm--container :value-mask (eval-when-compile (logior xcb:ConfigWindow:X xcb:ConfigWindow:Y)) diff --git a/exwm-input.el b/exwm-input.el index 99a4b8fa62..156399c744 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -79,8 +79,6 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (xcb:flush exwm--connection)))) (defvar exwm-input--focus-window nil "The (Emacs) window to be focused.") -(defvar exwm-input--redirected nil - "Indicate next update on buffer list is actually a result of redirection.") (defvar exwm-input--timer nil "Currently running timer.") (defun exwm-input--on-buffer-list-update () @@ -89,25 +87,12 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (window (selected-window)) (buffer (current-buffer))) (when (and (not (minibufferp buffer)) - (frame-parameter frame 'exwm-window-id) ;e.g. emacsclient frame + (frame-parameter frame 'exwm-outer-id) ;e.g. emacsclient frame (eq buffer (window-buffer))) ;e.g. `with-temp-buffer' - (unless (and exwm-input--redirected - exwm-input--focus-window - (with-current-buffer (window-buffer - exwm-input--focus-window) - exwm--floating-frame)) - (setq exwm-input--focus-window window) - (when exwm-input--timer (cancel-timer exwm-input--timer)) - (setq exwm-input--timer - (run-with-idle-timer 0.01 nil #'exwm-input--update-focus))) - (setq exwm-input--redirected nil)))) - -(defun exwm-input--on-focus-in () - "Run in focus-in-hook to remove redirected focus on frame." - (let ((frame (selected-frame))) - (when (and (frame-parameter frame 'exwm-window-id) - (not (memq frame exwm-workspace--list))) - (setq exwm-input--redirected t)))) + (when exwm-input--timer (cancel-timer exwm-input--timer)) + (setq exwm-input--focus-window window + exwm-input--timer + (run-with-idle-timer 0.01 nil #'exwm-input--update-focus))))) (defun exwm-input--update-focus () "Update input focus." @@ -122,22 +107,42 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (force-mode-line-update) ;; The application may have changed its input focus (exwm-workspace-switch exwm-workspace-current-index t)) - (when exwm--floating-frame - (redirect-frame-focus exwm--floating-frame nil) - (select-frame-set-input-focus exwm--floating-frame t)) (exwm--log "Set focus on #x%x" exwm--id) - (exwm-input--set-focus exwm--id)) + (exwm-input--set-focus exwm--id) + ;; Adjust stacking orders + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window exwm--container + :value-mask xcb:ConfigWindow:StackMode + :stack-mode (if exwm--floating-frame + xcb:StackMode:Above + xcb:StackMode:Below))) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter + (or exwm--floating-frame exwm--frame) + 'exwm-outer-id) + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Below)) + (xcb:flush exwm--connection)) (when (eq (selected-window) exwm-input--focus-window) (exwm--log "Focus on %s" exwm-input--focus-window) (select-frame-set-input-focus (window-frame exwm-input--focus-window) t) - (dolist (pair exwm--id-buffer-alist) - (with-current-buffer (cdr pair) - (when (and exwm--floating-frame - (eq exwm--frame exwm-workspace--current)) - (redirect-frame-focus exwm--floating-frame exwm--frame)))))) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter + (window-frame exwm-input--focus-window) + 'exwm-outer-id) + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Below)) + (xcb:flush exwm--connection))) (setq exwm-input--focus-window nil)))) +(defun exwm-input--on-minibuffer-setup () + "Run in minibuffer-setup-hook to set input focus to the frame." + (x-focus-frame (selected-frame))) + (defvar exwm-input--during-key-sequence nil "Non-nil indicates Emacs is waiting for more keys to form a key sequence.") (defvar exwm-input--temp-line-mode nil @@ -221,32 +226,38 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") "Update `exwm-input--global-prefix-keys'." (when exwm--connection (let ((original exwm-input--global-prefix-keys) - keysym keycode) + keysym keycode ungrab-key grab-key workspace) (setq exwm-input--global-prefix-keys nil) (dolist (i exwm-input--global-keys) (cl-pushnew (elt i 0) exwm-input--global-prefix-keys)) (unless (equal original exwm-input--global-prefix-keys) - ;; Grab global keys on root window - (if (xcb:+request-checked+request-check exwm--connection - (make-instance 'xcb:UngrabKey - :key xcb:Grab:Any :grab-window exwm--root - :modifiers xcb:ModMask:Any)) - (exwm--log "Failed to ungrab keys") - (dolist (i exwm-input--global-prefix-keys) - (setq keysym (xcb:keysyms:event->keysym exwm--connection i)) - (when (or (not keysym) - (not (setq keycode (xcb:keysyms:keysym->keycode - exwm--connection (car keysym)))) - (xcb:+request-checked+request-check exwm--connection - (make-instance 'xcb:GrabKey - :owner-events 0 - :grab-window exwm--root - :modifiers (cadr keysym) - :key keycode - :pointer-mode xcb:GrabMode:Async - :keyboard-mode xcb:GrabMode:Async))) - (user-error "[EXWM] Failed to grab key: %s" - (single-key-description i))))))))) + (setq ungrab-key (make-instance 'xcb:UngrabKey + :key xcb:Grab:Any :grab-window nil + :modifiers xcb:ModMask:Any) + grab-key (make-instance 'xcb:GrabKey + :owner-events 0 + :grab-window nil + :modifiers nil + :key nil + :pointer-mode xcb:GrabMode:Async + :keyboard-mode xcb:GrabMode:Async)) + (dolist (w exwm-workspace--list) + (setq workspace (frame-parameter w 'exwm-workspace)) + (setf (slot-value ungrab-key 'grab-window) workspace) + (if (xcb:+request-checked+request-check exwm--connection ungrab-key) + (exwm--log "Failed to ungrab keys") + (dolist (k exwm-input--global-prefix-keys) + (setq keysym (xcb:keysyms:event->keysym exwm--connection k) + keycode (xcb:keysyms:keysym->keycode exwm--connection + (car keysym))) + (setf (slot-value grab-key 'grab-window) workspace + (slot-value grab-key 'modifiers) (cadr keysym) + (slot-value grab-key 'key) keycode) + (when (or (not keycode) + (xcb:+request-checked+request-check exwm--connection + grab-key)) + (user-error "[EXWM] Failed to grab key: %s" + (single-key-description k)))))))))) (defun exwm-input-set-key (key command) "Set a global key binding." @@ -289,21 +300,6 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (with-slots (detail state) key-press (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) event) - ;; Compensate FocusOut event (prevent cursor style change) - (unless (eq major-mode 'exwm-mode) - (let ((id (frame-parameter nil 'exwm-window-id))) - (xcb:+request exwm--connection - (make-instance 'xcb:SendEvent - :propagate 0 - :destination id - :event-mask xcb:EventMask:StructureNotify - :event - (xcb:marshal - (make-instance 'xcb:FocusIn - :detail xcb:NotifyDetail:Inferior - :event id - :mode xcb:NotifyMode:Normal) - exwm--connection))))) (when (and keysym (setq event (xcb:keysyms:keysym->event exwm--connection keysym state))) @@ -324,7 +320,10 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (when id (when (xcb:+request-checked+request-check exwm--connection (make-instance 'xcb:GrabKey - :owner-events 0 :grab-window id + :owner-events 0 + :grab-window + (with-current-buffer (exwm--id->buffer id) + exwm--container) :modifiers xcb:ModMask:Any :key xcb:Grab:Any :pointer-mode xcb:GrabMode:Async @@ -338,7 +337,10 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (when id (when (xcb:+request-checked+request-check exwm--connection (make-instance 'xcb:UngrabKey - :key xcb:Grab:Any :grab-window id + :key xcb:Grab:Any + :grab-window + (with-current-buffer (exwm--id->buffer id) + exwm--container) :modifiers xcb:ModMask:Any)) (exwm--log "Failed to release keyboard for #x%x" id)) (setq exwm--on-KeyPress #'exwm-input--on-KeyPress-char-mode))) @@ -487,7 +489,7 @@ SIMULATION-KEYS is a list of alist (key-sequence1 . key-sequence2)." (add-hook 'pre-command-hook #'exwm-input--finish-key-sequence) ;; Update focus when buffer list updates (add-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update) - (add-hook 'focus-in-hook #'exwm-input--on-focus-in) + (add-hook 'minibuffer-setup-hook #'exwm-input--on-minibuffer-setup) ;; Update prefix keys for global keys (exwm-input--update-global-prefix-keys)) diff --git a/exwm-layout.el b/exwm-layout.el index ffe1f1d0c4..c3bfcf8ef9 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -30,41 +30,72 @@ (defvar exwm-floating-border-width) +(defun exwm-layout--resize-container (id container x y width height + &optional container-only) + "Resize a container (and its content unless CONTAINER-ONLY is non-nil)." + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window container + :value-mask (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height)) + :x x :y y :width width :height height)) + (unless container-only + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window id + :value-mask (eval-when-compile + (logior xcb:ConfigWindow:Width + xcb:ConfigWindow:Height)) + :width width :height height)))) + ;;;###autoload (defun exwm-layout--show (id &optional window) "Show window ID exactly fit in the Emacs window WINDOW." (exwm--log "Show #x%x in %s" id window) (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window id)) + (with-current-buffer (exwm--id->buffer id) + (xcb:+request exwm--connection + (make-instance 'xcb:MapWindow :window exwm--container))) (xcb:+request exwm--connection (make-instance 'xcb:icccm:set-WM_STATE :window id :state xcb:icccm:WM_STATE:NormalState :icon xcb:Window:None)) - (let* ((buffer (exwm--id->buffer id)) - (edges (or (and buffer - (with-current-buffer buffer exwm--floating-edges)) - (window-inside-absolute-pixel-edges window))) + (let* ((edges (window-inside-absolute-pixel-edges window)) (width (- (elt edges 2) (elt edges 0))) - (height (- (elt edges 3) (elt edges 1))) - x y) - (if exwm--floating-edges - ;; The relative position of a floating X window is determinate - (setq x exwm-floating-border-width - y exwm-floating-border-width) - (let ((relative-edges (window-inside-pixel-edges window))) - (setq x (elt relative-edges 0) - y (elt relative-edges 1)))) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window id - :value-mask (eval-when-compile - (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height - xcb:ConfigWindow:StackMode)) - :x x :y y :width width :height height - ;; In order to put non-floating window at bottom - :stack-mode xcb:StackMode:Below)) + (height (- (elt edges 3) (elt edges 1)))) + (with-current-buffer (exwm--id->buffer id) + (if exwm--floating-frame + ;; A floating X window is of the same size as the Emacs window, + ;; whereas its container is of the same size as the Emacs frame. + (progn + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window exwm--container + :value-mask (logior xcb:ConfigWindow:Width + xcb:ConfigWindow:Height) + :width (frame-pixel-width exwm--floating-frame) + :height (frame-pixel-height + exwm--floating-frame))) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window exwm--id + :value-mask (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height) + :x exwm-floating-border-width + :y exwm-floating-border-width + :width width + :height height))) + (let ((relative-edges (window-inside-pixel-edges window))) + (exwm-layout--resize-container id exwm--container + (elt relative-edges 0) + (elt relative-edges 1) + width height + (active-minibuffer-window))))) (xcb:+request exwm--connection (make-instance 'xcb:SendEvent :propagate 0 :destination id @@ -96,6 +127,9 @@ (make-instance 'xcb:ChangeWindowAttributes :window id :value-mask xcb:CW:EventMask :event-mask exwm--client-event-mask)) + (with-current-buffer (exwm--id->buffer id) + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow :window exwm--container))) (xcb:+request exwm--connection (make-instance 'xcb:icccm:set-WM_STATE :window id @@ -112,38 +146,16 @@ (user-error "Already in full-screen mode.")) ;; Set the floating frame fullscreen first when the client is floating (when exwm--floating-frame - (let* ((outer-id (frame-parameter exwm--floating-frame 'exwm-outer-id)) - (geometry (xcb:+request-unchecked+reply exwm--connection + (let* ((geometry (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GetGeometry - :drawable outer-id)))) - (setq exwm--floating-frame-geometry - (vector (slot-value geometry 'x) (slot-value geometry 'y) - (slot-value geometry 'width) - (slot-value geometry 'height))) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window outer-id - :value-mask (eval-when-compile - (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height)) - :x 0 :y 0 - :width (frame-pixel-width exwm-workspace--current) - :height (frame-pixel-height - exwm-workspace--current)))) + :drawable exwm--container)))) + (setq exwm--floating-frame-position + (vector (slot-value geometry 'x) (slot-value geometry 'y)))) (xcb:flush exwm--connection)) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window exwm--id - :value-mask (eval-when-compile - (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height)) - :x 0 :y 0 - :width (frame-pixel-width exwm-workspace--current) - :height (frame-pixel-height exwm-workspace--current))) + (exwm-layout--resize-container exwm--id exwm--container 0 0 + (frame-pixel-width exwm-workspace--current) + (frame-pixel-height + exwm-workspace--current)) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_STATE :window exwm--id @@ -162,17 +174,12 @@ (when exwm--floating-frame (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow - :window (frame-parameter exwm--floating-frame - 'exwm-outer-id) + :window exwm--container :value-mask (eval-when-compile (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height)) - :x (elt exwm--floating-frame-geometry 0) - :y (elt exwm--floating-frame-geometry 1) - :width (elt exwm--floating-frame-geometry 2) - :height (elt exwm--floating-frame-geometry 3)))) + xcb:ConfigWindow:Y)) + :x (elt exwm--floating-frame-position 0) + :y (elt exwm--floating-frame-position 1)))) (exwm-layout--show exwm--id) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_STATE :window exwm--id :data [])) @@ -193,19 +200,10 @@ (make-instance 'xcb:RECTANGLE :x 0 :y 0 :width (x-display-pixel-width) :height (x-display-pixel-height)))) - (id (frame-parameter frame 'exwm-outer-id))) + (id (frame-parameter frame 'exwm-outer-id)) + (workspace (frame-parameter frame 'exwm-workspace))) (with-slots (x y width height) geometry - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window id - :value-mask (eval-when-compile - (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height)) - :x x :y y - :width width - :height height)) + (exwm-layout--resize-container id workspace x y width height) (xcb:flush exwm--connection)))) (defvar exwm-layout-show-all-buffers nil @@ -221,7 +219,7 @@ (get-buffer "*scratch*")))) windows) (if (not (memq frame exwm-workspace--list)) - (if (frame-parameter frame 'exwm-window-id) + (if (frame-parameter frame 'exwm-outer-id) ;; Refresh a floating frame (when (eq major-mode 'exwm-mode) (let ((window (frame-first-window frame))) @@ -230,9 +228,6 @@ (exwm-layout--show exwm--id window)))) ;; Other frames (e.g. terminal/graphical frame of emacsclient) ;; We shall bury all `exwm-mode' buffers in this case - (unless placeholder ;create the *scratch* buffer if it's killed - (setq placeholder (get-buffer-create "*scratch*")) - (set-buffer-major-mode placeholder)) (setq windows (window-list frame 0)) ;exclude minibuffer (dolist (window windows) (with-current-buffer (window-buffer window) @@ -329,7 +324,6 @@ windows." (setq width (max (+ exwm--normal-hints-min-width margin) (+ width delta)))))) (when width - (setq exwm--floating-edges nil) ;invalid from now on (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window (frame-parameter exwm--floating-frame @@ -356,7 +350,6 @@ windows." (setq height (max (+ exwm--normal-hints-min-height margin) (+ height delta)))))) (when height - (setq exwm--floating-edges nil) ;invalid from now on (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window (frame-parameter exwm--floating-frame diff --git a/exwm-manage.el b/exwm-manage.el index 5b562ed52f..d8f91f3517 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -120,7 +120,7 @@ corresponding buffer.") (make-instance 'xcb:ReparentWindow :window id :parent (frame-parameter exwm-workspace--current - 'exwm-window-id) + 'exwm-workspace) :x x :y y))) ;; Center window of type _NET_WM_WINDOW_TYPE_SPLASH (when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_SPLASH exwm-window-type) @@ -145,10 +145,34 @@ corresponding buffer.") (throw 'return 'ignored)) ;; Manage the window (exwm--log "Manage #x%x" id) + ;; Create a new container as the parent of this X window + (setq exwm--container (xcb:generate-id exwm--connection)) + (xcb:+request exwm--connection + (make-instance 'xcb:CreateWindow + :depth 0 :wid exwm--container + :parent (frame-parameter exwm-workspace--current + 'exwm-workspace) + :x 0 :y 0 :width 1 :height 1 :border-width 0 + :class xcb:WindowClass:CopyFromParent + :visual 0 ;CopyFromParent + :value-mask (logior xcb:CW:OverrideRedirect + xcb:CW:EventMask) + :override-redirect 1 + :event-mask xcb:EventMask:SubstructureRedirect)) + (exwm--debug + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_NAME + :window exwm--container + :data (format "EXWM container for 0x%x" id)))) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window id :parent exwm--container :x 0 :y 0)) (xcb:+request exwm--connection ;remove border (make-instance 'xcb:ConfigureWindow :window id :value-mask xcb:ConfigWindow:BorderWidth :border-width 0)) + ;; (xcb:+request exwm--connection ;map the window + ;; (make-instance 'xcb:MapWindow :window id)) (dolist (button ;grab buttons to set focus / move / resize (list xcb:ButtonIndex:1 xcb:ButtonIndex:2 xcb:ButtonIndex:3)) (xcb:+request-checked+request-check exwm--connection @@ -190,9 +214,8 @@ corresponding buffer.") (xcb:flush exwm--connection) (when (buffer-live-p buffer) (with-current-buffer buffer - (when exwm--floating-frame - (make-frame-invisible exwm--floating-frame) - (redisplay)) + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow :window exwm--container)) (setq exwm-workspace--switch-history-outdated t) ;; (when withdraw-only @@ -207,9 +230,7 @@ corresponding buffer.") (setq geometry-parent (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GetGeometry - :drawable - (frame-parameter exwm--floating-frame - 'exwm-outer-id))) + :drawable exwm--container)) geometry (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GetGeometry :drawable id))) @@ -227,6 +248,13 @@ corresponding buffer.") (make-instance 'xcb:DeleteProperty :window id :property xcb:Atom:WM_STATE)) (xcb:flush exwm--connection)) + ;; Destroy the container (it seems it has to be delayed). + (run-with-idle-timer 0 nil + `(lambda () + (xcb:+request exwm--connection + ,(make-instance 'xcb:DestroyWindow + :window exwm--container)) + (xcb:flush exwm--connection))) (let ((kill-buffer-query-functions nil) (floating exwm--floating-frame)) (kill-buffer) @@ -252,55 +280,62 @@ corresponding buffer.") ;;;###autoload (defun exwm-manage--close-window (id &optional buffer) "Close window ID in a proper way." - (catch 'return - (unless (exwm--id->buffer id) - (throw 'return t)) - (unless buffer (setq buffer (exwm--id->buffer id))) - ;; Destroy the client window if it does not support WM_DELETE_WINDOW - (unless (and (buffer-live-p buffer) - (with-current-buffer buffer - (memq xcb:Atom:WM_DELETE_WINDOW exwm--protocols))) - (xcb:+request exwm--connection - (make-instance 'xcb:DestroyWindow :window id)) - (xcb:flush exwm--connection) - (throw 'return nil)) - ;; Try to close the window with WM_DELETE_WINDOW client message - (xcb:+request exwm--connection - (make-instance 'xcb:icccm:SendEvent - :destination id - :event (xcb:marshal - (make-instance 'xcb:icccm:WM_DELETE_WINDOW - :window id) - exwm--connection))) - (xcb:flush exwm--connection) - ;; Try to determine if the client stop responding - (with-current-buffer buffer - (unless (memq xcb:Atom:_NET_WM_PING exwm--protocols) - ;; Ensure it's dead - (run-with-timer exwm-manage-ping-timeout nil - `(lambda () (exwm-manage--kill-client ,id))) + (let (container) + (catch 'return + (unless (exwm--id->buffer id) + (throw 'return t)) + (unless buffer (setq buffer (exwm--id->buffer id))) + (when (buffer-live-p buffer) + (setq container exwm--container)) + ;; Destroy the client window if it does not support WM_DELETE_WINDOW + (unless (and (buffer-live-p buffer) + (with-current-buffer buffer + (memq xcb:Atom:WM_DELETE_WINDOW exwm--protocols))) + (xcb:+request exwm--connection + (make-instance 'xcb:DestroyWindow :window id)) + (xcb:flush exwm--connection) (throw 'return nil)) - (setq exwm-manage--ping-lock t) + ;; Try to close the window with WM_DELETE_WINDOW client message (xcb:+request exwm--connection - (make-instance 'xcb:SendEvent - :propagate 0 :destination id - :event-mask xcb:EventMask:NoEvent + (make-instance 'xcb:icccm:SendEvent + :destination id :event (xcb:marshal - (make-instance 'xcb:ewmh:_NET_WM_PING - :window id :timestamp 0 - :client-window id) + (make-instance 'xcb:icccm:WM_DELETE_WINDOW + :window id) exwm--connection))) (xcb:flush exwm--connection) - (with-timeout (exwm-manage-ping-timeout - (if (yes-or-no-p (format "`%s' is not responding. \ + ;; Try to determine if the client stop responding + (with-current-buffer buffer + (unless (memq xcb:Atom:_NET_WM_PING exwm--protocols) + ;; Ensure it's dead + (run-with-timer exwm-manage-ping-timeout nil + `(lambda () (exwm-manage--kill-client ,id))) + (throw 'return nil)) + (setq exwm-manage--ping-lock t) + (xcb:+request exwm--connection + (make-instance 'xcb:SendEvent + :propagate 0 :destination id + :event-mask xcb:EventMask:NoEvent + :event (xcb:marshal + (make-instance 'xcb:ewmh:_NET_WM_PING + :window id :timestamp 0 + :client-window id) + exwm--connection))) + (xcb:flush exwm--connection) + (with-timeout (exwm-manage-ping-timeout + (if (yes-or-no-p (format "`%s' is not responding. \ Would you like to kill it? " - (buffer-name buffer))) - (progn (exwm-manage--kill-client id) - (throw 'return nil)) - (throw 'return nil))) - (while (and exwm-manage--ping-lock - (exwm--id->buffer id)) ;may have been destroyed - (accept-process-output nil 0.1)))))) + (buffer-name buffer))) + (progn (exwm-manage--kill-client id) + (throw 'return nil)) + (throw 'return nil))) + (while (and exwm-manage--ping-lock + (exwm--id->buffer id)) ;may have been destroyed + (accept-process-output nil 0.1))))) + ;; Finally destroy the container + (xcb:+request exwm--connection + (make-instance 'xcb:DestroyWindow :window container)) + (xcb:flush exwm--connection))) (defun exwm-manage--kill-client (&optional id) "Kill an X client." @@ -338,9 +373,8 @@ Would you like to kill it? " (list 0 0 (frame-pixel-width exwm-workspace--current) (frame-pixel-height exwm-workspace--current)) - (or exwm--floating-edges - (window-inside-absolute-pixel-edges - (get-buffer-window buffer t))))) + (window-inside-absolute-pixel-edges + (get-buffer-window buffer t)))) (exwm--log "Reply with ConfigureNotify (edges): %s" edges) (xcb:+request exwm--connection (make-instance 'xcb:SendEvent diff --git a/exwm-randr.el b/exwm-randr.el index 1ac8f29e1e..7d3609f0fc 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -48,6 +48,7 @@ (require 'xcb-randr) (require 'exwm-core) +(require 'exwm-layout) (eval-when-compile (require 'exwm-workspace)) (defvar exwm-randr-workspace-output-plist nil) @@ -93,15 +94,10 @@ (set-frame-parameter frame 'exwm-randr-output output) (set-frame-parameter frame 'exwm-geometry geometry) (with-slots (x y width height) geometry - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window (frame-parameter frame 'exwm-outer-id) - :value-mask (eval-when-compile - (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height)) - :x x :y y :width width :height height)) + (exwm-layout--resize-container (frame-parameter frame 'exwm-outer-id) + (frame-parameter frame + 'exwm-workspace) + x y width height) (setq workareas (nconc workareas (list x y width height)) viewports (nconc viewports (list x y)))))) ;; Update _NET_WORKAREA diff --git a/exwm-workspace.el b/exwm-workspace.el index d1fe6cf381..eb7b2466bd 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -122,22 +122,16 @@ The optional FORCE option is for internal use only." (unless (and (<= 0 index) (< index exwm-workspace-number)) (user-error "[EXWM] Workspace index out of range: %d" index)) (when (or force (/= exwm-workspace-current-index index)) - (let ((frame (elt exwm-workspace--list index))) + (let* ((frame (elt exwm-workspace--list index)) + (workspace (frame-parameter frame 'exwm-workspace))) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window workspace + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Above)) (setq exwm-workspace--current frame exwm-workspace-current-index index) - (select-frame-set-input-focus frame) - ;; Move mouse when necessary - (let ((position (mouse-pixel-position)) - x y w h) - (unless (eq frame (car position)) - (setq x (cadr position) - y (cddr position) - w (frame-pixel-width frame) - h (frame-pixel-height frame)) - (when (or (> x w) (> y h)) - (setq x (/ w 2) - y (/ h 2))) - (set-mouse-pixel-position frame x y))) + (select-window (frame-selected-window frame)) ;; Close the (possible) active minibuffer (when (active-minibuffer-window) (run-with-idle-timer 0 nil (lambda () (abort-recursive-edit)))) @@ -161,16 +155,6 @@ The optional FORCE option is for internal use only." :window exwm--root :data index)) (xcb:flush exwm--connection))))) -(defun exwm-workspace--on-focus-in () - "Fix unexpected frame switch." - ;; `focus-in-hook' is run by `handle-switch-frame' - (unless (eq this-command 'handle-switch-frame) - (let ((index (cl-position (selected-frame) exwm-workspace--list))) - (exwm--log "Focus on workspace %s" index) - (when (and index (/= index exwm-workspace-current-index)) - (exwm--log "Workspace was switched unexpectedly") - (exwm-workspace-switch index))))) - ;;;###autoload (defun exwm-workspace-move-window (index &optional id) "Move window ID to workspace INDEX." @@ -204,10 +188,9 @@ The optional FORCE option is for internal use only." (progn (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow - :window (frame-parameter exwm--floating-frame - 'exwm-outer-id) - :parent (frame-parameter frame - 'exwm-window-id) + :window exwm--container + :parent + (frame-parameter frame 'exwm-workspace) :x 0 :y 0)) (xcb:flush exwm--connection)) ;; Move the window itself @@ -222,8 +205,10 @@ The optional FORCE option is for internal use only." (exwm-layout--hide id) (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow - :window id - :parent (frame-parameter frame 'exwm-window-id) + ;; (current-buffer) is changed. + :window (with-current-buffer (exwm--id->buffer id) + exwm--container) + :parent (frame-parameter frame 'exwm-workspace) :x 0 :y 0)) (xcb:flush exwm--connection) (set-window-buffer (frame-selected-window frame) @@ -303,50 +288,58 @@ The optional FORCE option is for internal use only." (set-frame-parameter (car exwm-workspace--list) 'client nil)) ;; Create remaining frames (dotimes (_ (1- exwm-workspace-number)) - (nconc exwm-workspace--list - (list (make-frame '((window-system . x) - (visibility . nil)))))) + (nconc exwm-workspace--list (list (make-frame '((window-system . x)))))) ;; Configure workspaces (dolist (i exwm-workspace--list) - (let ((window-id (string-to-number (frame-parameter i 'window-id))) - (outer-id (string-to-number (frame-parameter i 'outer-window-id)))) + (let ((outer-id (string-to-number (frame-parameter i 'outer-window-id))) + (workspace (xcb:generate-id exwm--connection))) ;; Save window IDs - (set-frame-parameter i 'exwm-window-id window-id) (set-frame-parameter i 'exwm-outer-id outer-id) + (set-frame-parameter i 'exwm-workspace workspace) ;; Set OverrideRedirect on all frames (xcb:+request exwm--connection (make-instance 'xcb:ChangeWindowAttributes :window outer-id :value-mask xcb:CW:OverrideRedirect :override-redirect 1)) - ;; Select events on all virtual roots (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window window-id :value-mask xcb:CW:EventMask - :event-mask xcb:EventMask:SubstructureRedirect)))) + (make-instance 'xcb:CreateWindow + :depth 0 :wid workspace :parent exwm--root + :x 0 :y 0 + :width (x-display-pixel-width) + :height (x-display-pixel-height) + :border-width 0 :class xcb:WindowClass:CopyFromParent + :visual 0 ;CopyFromParent + :value-mask (logior xcb:CW:OverrideRedirect + xcb:CW:EventMask) + :override-redirect 1 + :event-mask xcb:EventMask:SubstructureRedirect)) + (exwm--debug + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_NAME + :window workspace + :data + (format "EXWM workspace %d" + (cl-position i exwm-workspace--list))))) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window outer-id :parent workspace :x 0 :y 0)) + (xcb:+request exwm--connection + (make-instance 'xcb:MapWindow :window workspace)))) (xcb:flush exwm--connection) ;; We have to advice `x-create-frame' or every call to it would hang EXWM (advice-add 'x-create-frame :around #'exwm-workspace--x-create-frame) - ;; We have to delay making the frame visible until the - ;; override-redirect flag has been set. - (select-frame-set-input-focus (car exwm-workspace--list)) - (dolist (i exwm-workspace--list) - (set-frame-parameter i 'visibility t) - (lower-frame i)) ;; Delay making the workspaces fullscreen until Emacs becomes idle (run-with-idle-timer 0 nil (lambda () (dolist (i exwm-workspace--list) (set-frame-parameter i 'fullscreen 'fullboth)))) - (raise-frame (car exwm-workspace--list)) - ;; Handle unexpected frame switch - (add-hook 'focus-in-hook #'exwm-workspace--on-focus-in) ;; Set _NET_VIRTUAL_ROOTS (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_VIRTUAL_ROOTS :window exwm--root :data (vconcat (mapcar (lambda (i) - (frame-parameter i 'exwm-window-id)) + (frame-parameter i 'exwm-workspace)) exwm-workspace--list)))) ;; Switch to the first workspace (exwm-workspace-switch 0 t)) diff --git a/exwm.el b/exwm.el index 96413c9931..6af5446c6b 100644 --- a/exwm.el +++ b/exwm.el @@ -501,8 +501,7 @@ (setq exwm--connection nil) (exwm--log "Other window manager detected")) ;; Disable some features not working well with EXWM - (setq use-dialog-box nil - display-hourglass nil) + (setq use-dialog-box nil) ;; Initialize ICCCM/EWMH support ;; (xcb:icccm:init exwm--connection) (xcb:ewmh:init exwm--connection) @@ -511,9 +510,9 @@ (exwm-layout--init) (exwm-floating--init) (exwm-manage--init) + (exwm-workspace--init) (exwm-input--init) (exwm--unlock) - (exwm-workspace--init) ;; Manage existing windows (exwm-manage--scan) (run-hooks 'exwm-init-hook))))) -- cgit 1.4.1 From d8281abca4bc5182040a7866560a1806c59176d4 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 3 Feb 2016 13:23:55 +0800 Subject: Avoid unnecessary changes of stacking order * exwm-input.el (exwm-input--update-focus): Only restack a tiling X window when it's not the last but one sibling. This should reduce flickering. --- exwm-input.el | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 156399c744..f84b5fce69 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -110,20 +110,34 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (exwm--log "Set focus on #x%x" exwm--id) (exwm-input--set-focus exwm--id) ;; Adjust stacking orders - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window exwm--container - :value-mask xcb:ConfigWindow:StackMode - :stack-mode (if exwm--floating-frame - xcb:StackMode:Above - xcb:StackMode:Below))) + (if exwm--floating-frame + ;; Put this floating X window at top. + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window exwm--container + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:TopIf)) + ;; This should be the last X window but one in the siblings. + (with-slots (children) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:QueryTree + :window + (frame-parameter exwm--frame + 'exwm-workspace))) + (unless (eq (cadr children) exwm--container) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window exwm--container + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Below))))) + ;; Make sure Emacs frames are at bottom. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window (frame-parameter (or exwm--floating-frame exwm--frame) 'exwm-outer-id) :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:Below)) + :stack-mode xcb:StackMode:BottomIf)) (xcb:flush exwm--connection)) (when (eq (selected-window) exwm-input--focus-window) (exwm--log "Focus on %s" exwm-input--focus-window) -- cgit 1.4.1 From 0e4055d3392537cd4138181b9c615fa03053add8 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 6 Feb 2016 12:59:33 +0800 Subject: Add auto-hiding minibuffer support * exwm-floating.el (exwm-floating--set-floating): Take auto-hiding minibuffer into account when calculating available height. (exwm-floating--unset-floating): Restack the container to avoid further restacking. * exwm-input.el (exwm-input--update-focus): Use more accurate restacking. (exwm-input--on-minibuffer-setup): Replaced by `exwm-layout--on-minibuffer-setup' and `exwm-workspace--on-minibuffer-setup'. (exwm-input-command-whitelist, exwm-input--during-command) (exwm-input--on-KeyPress-line-mode): The functionality of `exwm-input-command-whitelist' is replaced by `exwm-input--during-command', which can automatically tell whether functions like `read-event' are being called. (exwm-input--init): Add/remove corresponding hooks. * exwm-layout.el (exwm-layout--on-minibuffer-setup): Also set input focus. (exwm-layout--on-echo-area-change): New function for refreshing layout when the size of echo area changes. (exwm-layout--init): Track size changes for fixed minibuffer and echo area. * exwm-manage.el (exwm-manage--on-ConfigureRequest): Never grant restacking requests initiated by other clients. * exwm-workspace.el (exwm-workspace--minibuffer): New variable for the auto-hiding minibuffer. (exwm-workspace-minibuffer-position): For setting the position of the auto-hiding minibuffer. (exwm-workspace-display-echo-area-timeout): Seconds before echo area auto-hides. (exwm-workspace--display-echo-area-timer): The corresponding timer. (exwm-workspace-switch): Configure the auto-hiding minibuffer when switching workspace. (exwm-workspace--update-minibuffer): New function for adjusting the height of the auto-hiding minibuffer. (exwm-workspace--on-ConfigureNotify): New function for configuring the container of the auto-hiding minibuffer. (exwm-workspace--display-buffer): New function for forcing `minibuffer-completion-help' to use the workspace frame. (exwm-workspace--show-minibuffer, exwm-workspace--hide-minibuffer): New functions for showing/hiding the auto-hiding minibuffer (container). (exwm-workspace--on-minibuffer-setup, exwm-workspace--on-minibuffer-exit): New functions called when the auto-hiding minibuffer entered/exists. (exwm-workspace--on-echo-area-dirty, exwm-workspace--on-echo-area-clear): New functions when the auto-hiding echo area is ready to show/hide. (exwm-workspace--init): Set up the auto-hiding minibuffer and workspace frames. Track sizes changes for auto-hiding minibuffer and echo area. No need to set OverrideRedirect on workspace frames. * exwm.el (exwm--init-icccm-ewmh): Correct the value of _NET_WORKAREA. --- exwm-floating.el | 18 +++- exwm-input.el | 59 +++++------- exwm-layout.el | 25 +++-- exwm-manage.el | 13 +-- exwm-workspace.el | 268 ++++++++++++++++++++++++++++++++++++++++++++++++++---- exwm.el | 8 +- 6 files changed, 320 insertions(+), 71 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 88fa2077e7..ff584c03ee 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -106,8 +106,10 @@ ;; FIXME: check normal hints restrictions (let* ((display-width (frame-pixel-width original-frame)) (display-height (- (frame-pixel-height original-frame) - (window-pixel-height (minibuffer-window - original-frame)) + (if exwm-workspace-minibuffer-position + 0 + (window-pixel-height (minibuffer-window + original-frame))) (* 2 (window-mode-line-height)) (window-header-line-height window) (* 2 exwm-floating-border-width))) @@ -180,12 +182,22 @@ (interactive) (let ((buffer (exwm--id->buffer id))) (with-current-buffer buffer + ;; Reparent the container to the workspace (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow :window exwm--container :parent (frame-parameter exwm-workspace--current 'exwm-workspace) - :x 0 :y 0))) ;temporary position + :x 0 :y 0)) ;temporary position + ;; Put the container just above the Emacs frame + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window exwm--container + :value-mask (logior xcb:ConfigWindow:Sibling + xcb:ConfigWindow:StackMode) + :sibling (frame-parameter exwm-workspace--current + 'exwm-outer-id) + :stack-mode xcb:StackMode:Above))) (xcb:flush exwm--connection) (with-current-buffer buffer (when exwm--floating-frame ;from floating to non-floating diff --git a/exwm-input.el b/exwm-input.el index a5899d5c4e..757efb7566 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -110,26 +110,25 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (exwm--log "Set focus on #x%x" exwm--id) (exwm-input--set-focus exwm--id) ;; Adjust stacking orders - (if exwm--floating-frame + (when exwm--floating-frame + (if (memq exwm-workspace-minibuffer-position '(top bottom)) + ;; Put this floating X window just below the minibuffer. + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window exwm--container + :value-mask + (logior xcb:ConfigWindow:Sibling + xcb:ConfigWindow:StackMode) + :sibling (frame-parameter + exwm-workspace--minibuffer + 'exwm-container) + :stack-mode xcb:StackMode:Below)) ;; Put this floating X window at top. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window exwm--container :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:TopIf)) - ;; This should be the last X window but one in the siblings. - (with-slots (children) - (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:QueryTree - :window - (frame-parameter exwm--frame - 'exwm-workspace))) - (unless (eq (cadr children) exwm--container) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window exwm--container - :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:Below))))) + :stack-mode xcb:StackMode:Above)))) ;; Make sure Emacs frames are at bottom. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow @@ -142,21 +141,9 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (when (eq (selected-window) exwm-input--focus-window) (exwm--log "Focus on %s" exwm-input--focus-window) (select-frame-set-input-focus (window-frame exwm-input--focus-window) - t) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window (frame-parameter - (window-frame exwm-input--focus-window) - 'exwm-outer-id) - :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:Below)) - (xcb:flush exwm--connection))) + t))) (setq exwm-input--focus-window nil)))) -(defun exwm-input--on-minibuffer-setup () - "Run in minibuffer-setup-hook to set input focus to the frame." - (x-focus-frame (selected-frame))) - (defvar exwm-input--during-key-sequence nil "Non-nil indicates Emacs is waiting for more keys to form a key sequence.") (defvar exwm-input--temp-line-mode nil @@ -278,12 +265,13 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (global-set-key key command) (cl-pushnew key exwm-input--global-keys)) -;; These commands usually call something like `read-char' without using the -;; minibuffer, so they will not get inputs after invoked. It'd be better if we -;; can determine whether there's a command waiting for input so that this -;; variable can be removed. (defvar exwm-input-command-whitelist nil "A list of commands that when active all keys should be forwarded to Emacs.") +(make-obsolete-variable 'exwm-input-command-whitelist + "This variable can be safely removed." "25.1") + +(defvar exwm-input--during-command nil + "Indicate whether between `pre-command-hook' and `post-command-hook'.") ;;;###autoload (defun exwm-input--on-KeyPress-line-mode (key-press) @@ -295,8 +283,8 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (setq event (xcb:keysyms:keysym->event exwm--connection keysym state)) (or exwm-input--during-key-sequence + exwm-input--during-command (setq minibuffer-window (active-minibuffer-window)) - (memq real-this-command exwm-input-command-whitelist) (memq event exwm-input--global-prefix-keys) (memq event exwm-input-prefix-keys) (memq event exwm-input--simulation-prefix-keys))) @@ -501,9 +489,12 @@ SIMULATION-KEYS is a list of alist (key-sequence1 . key-sequence2)." #'exwm-floating--do-moveresize) ;; `pre-command-hook' marks the end of a key sequence (existing or not) (add-hook 'pre-command-hook #'exwm-input--finish-key-sequence) + ;; Control `exwm-input--during-command' + (add-hook 'pre-command-hook (lambda () (setq exwm-input--during-command t))) + (add-hook 'post-command-hook + (lambda () (setq exwm-input--during-command nil))) ;; Update focus when buffer list updates (add-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update) - (add-hook 'minibuffer-setup-hook #'exwm-input--on-minibuffer-setup) ;; Update prefix keys for global keys (exwm-input--update-global-prefix-keys)) diff --git a/exwm-layout.el b/exwm-layout.el index a290876bb3..316bf62a74 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -279,10 +279,20 @@ "Refresh layout when minibuffer grows." (run-with-idle-timer 0.01 nil ;FIXME (lambda () - (when (and (< 1 (window-height (minibuffer-window))) - (not (and (eq major-mode 'exwm-mode) - exwm--floating-frame))) - (exwm-layout--refresh))))) + (when (< 1 (window-height (minibuffer-window))) + (exwm-layout--refresh)))) + ;; Set input focus on the Emacs frame + (x-focus-frame (window-frame (minibuffer-selected-window)))) + +(defun exwm-layout--on-echo-area-change (&optional dirty) + "Run when message arrives or in `echo-area-clear-hook' to refresh layout." + (when (and (current-message) + (or (cl-position ?\n (current-message)) + (> (length (current-message)) + (frame-width exwm-workspace--current)))) + (if dirty + (exwm-layout--refresh) + (run-with-idle-timer 0.01 nil #'exwm-layout--refresh)))) ;FIXME (defun exwm-layout-enlarge-window (delta &optional horizontal) "Make the selected window DELTA pixels taller. @@ -383,8 +393,11 @@ See also `exwm-layout-enlarge-window'." "Initialize layout module." ;; Auto refresh layout (add-hook 'window-configuration-change-hook #'exwm-layout--refresh) - ;; Refresh when minibuffer grows - (add-hook 'minibuffer-setup-hook #'exwm-layout--on-minibuffer-setup t)) + (unless (memq exwm-workspace-minibuffer-position '(top bottom)) + ;; Refresh when minibuffer grows + (add-hook 'minibuffer-setup-hook #'exwm-layout--on-minibuffer-setup t) + (run-with-idle-timer 0 t #'exwm-layout--on-echo-area-change t) + (add-hook 'echo-area-clear-hook #'exwm-layout--on-echo-area-change))) diff --git a/exwm-manage.el b/exwm-manage.el index 2f35b1475f..b32d677ea9 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -360,8 +360,7 @@ Would you like to kill it? " (let ((obj (make-instance 'xcb:ConfigureRequest)) buffer edges) (xcb:unmarshal obj data) - (with-slots (stack-mode window sibling x y width height border-width - value-mask) + (with-slots (window x y width height border-width value-mask) obj (exwm--log "ConfigureRequest from #x%x (#x%x) @%dx%d%+d%+d, border: %d" window value-mask width height x y border-width) @@ -391,14 +390,16 @@ Would you like to kill it? " :border-width 0 :override-redirect 0) exwm--connection)))) (exwm--log "ConfigureWindow (preserve geometry)") - ;; Configure unmanaged windows + ;; Configure the unmanaged window without changing the stacking order. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window window - :value-mask value-mask + :value-mask + (logand value-mask + (lognot xcb:ConfigWindow:Sibling) + (lognot xcb:ConfigWindow:StackMode)) :x x :y y :width width :height height - :border-width border-width - :sibling sibling :stack-mode stack-mode))))) + :border-width border-width))))) (xcb:flush exwm--connection)) (defun exwm-manage--on-MapRequest (data _synthetic) diff --git a/exwm-workspace.el b/exwm-workspace.el index 8bdcc63450..9669428c85 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -24,7 +24,6 @@ ;; This module adds workspace support for EXWM. ;; Todo: -;; + Auto hide minibuffer, or allow users to place it elsewhere. ;; + Add system tray support. ;;; Code: @@ -100,6 +99,17 @@ (defvar exwm-workspace-current-index 0 "Index of current active workspace.") (defvar exwm-workspace-show-all-buffers nil "Non-nil to show buffers on other workspaces.") +(defvar exwm-workspace--minibuffer nil + "The minibuffer frame shared among all frames.") +(defvar exwm-workspace-minibuffer-position nil + "Position of the minibuffer frame. + +Value nil means to use the default position which is fixed at bottom, while +'top and 'bottom mean to use an auto-hiding minibuffer.") +(defvar exwm-workspace-display-echo-area-timeout 1 + "Timeout for displaying echo area.") +(defvar exwm-workspace--display-echo-area-timer nil + "Timer for auto-hiding echo area.") ;;;###autoload (defun exwm-workspace-switch (index &optional force) @@ -135,7 +145,30 @@ The optional FORCE option is for internal use only." ;; Close the (possible) active minibuffer (when (active-minibuffer-window) (run-with-idle-timer 0 nil (lambda () (abort-recursive-edit)))) - (setq default-minibuffer-frame frame) + (if (not (memq exwm-workspace-minibuffer-position '(top bottom))) + (setq default-minibuffer-frame frame) + ;; Resize/reposition the minibuffer frame + (let ((x 0) + (y (if (eq exwm-workspace-minibuffer-position 'top) + 0 + (- (frame-pixel-height frame) + (frame-pixel-height exwm-workspace--minibuffer)))) + (width (x-display-pixel-width)) + (container (frame-parameter exwm-workspace--minibuffer + 'exwm-container))) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window container + :parent (frame-parameter frame 'exwm-workspace) + :x x :y y)) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window container + :value-mask (logior xcb:ConfigWindow:Width + xcb:ConfigWindow:StackMode) + :width width + :stack-mode xcb:StackMode:Above)) + (set-frame-width exwm-workspace--minibuffer width nil t))) ;; Hide windows in other workspaces by preprending a space (unless exwm-workspace-show-all-buffers (dolist (i exwm--id-buffer-alist) @@ -266,6 +299,146 @@ The optional FORCE option is for internal use only." (xcb:flush exwm--connection) frame)) +(defun exwm-workspace--update-minibuffer (&optional echo-area) + "Update the minibuffer frame." + (let ((height + (with-current-buffer + (window-buffer (minibuffer-window exwm-workspace--minibuffer)) + (max 1 + (if echo-area + (let ((width (frame-width exwm-workspace--minibuffer)) + (result 0)) + (mapc (lambda (i) + (setq result + (+ result + (ceiling (1+ (length i)) width)))) + (split-string (current-message) "\n")) + result) + (count-screen-lines)))))) + (when (and (integerp max-mini-window-height) + (> height max-mini-window-height)) + (setq height max-mini-window-height)) + (set-frame-height exwm-workspace--minibuffer height))) + +(defun exwm-workspace--on-ConfigureNotify (data _synthetic) + "Adjust the container to fit the minibuffer frame." + (let ((obj (make-instance 'xcb:ConfigureNotify)) + value-mask y) + (xcb:unmarshal obj data) + (with-slots (window height) obj + (when (eq (frame-parameter exwm-workspace--minibuffer 'exwm-outer-id) + window) + (when (and (floatp max-mini-window-height) + (> height (* max-mini-window-height + (frame-pixel-height exwm-workspace--current)))) + (setq height (floor + (* max-mini-window-height + (frame-pixel-height exwm-workspace--current)))) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window window + :value-mask xcb:ConfigWindow:Height + :height height))) + (if (eq exwm-workspace-minibuffer-position 'top) + (setq value-mask xcb:ConfigWindow:Height + y 0) + (setq value-mask (logior xcb:ConfigWindow:Y xcb:ConfigWindow:Height) + y (- (frame-pixel-height exwm-workspace--current) height))) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter exwm-workspace--minibuffer + 'exwm-container) + :value-mask value-mask + :y y + :height height)) + (xcb:flush exwm--connection))))) + +(defun exwm-workspace--display-buffer (buffer alist) + "Display buffer in the current workspace frame. + +This functions is modified from `display-buffer-reuse-window' and +`display-buffer-pop-up-window'." + (when (eq (selected-frame) exwm-workspace--minibuffer) + (setq buffer (get-buffer buffer)) ;convert string to buffer. + (let (window) + (if (setq window (get-buffer-window buffer exwm-workspace--current)) + (window--display-buffer buffer window 'reuse alist) + (when (setq window (or (window--try-to-split-window + (get-largest-window exwm-workspace--current t) + alist) + (window--try-to-split-window + (get-lru-window exwm-workspace--current t) + alist))) + (window--display-buffer + buffer window 'window alist display-buffer-mark-dedicated)))))) + +(defun exwm-workspace--show-minibuffer () + "Show the minibuffer frame." + ;; Cancel pending timer. + (when exwm-workspace--display-echo-area-timer + (cancel-timer exwm-workspace--display-echo-area-timer) + (setq exwm-workspace--display-echo-area-timer nil)) + ;; Show the minibuffer frame. + (xcb:+request exwm--connection + (make-instance 'xcb:MapWindow + :window (frame-parameter exwm-workspace--minibuffer + 'exwm-container))) + (xcb:flush exwm--connection) + ;; Unfortunately we need the following lines to workaround a cursor + ;; flickering issue for line-mode floating X windows. They just make the + ;; minibuffer appear to be focused. + (with-current-buffer (window-buffer (minibuffer-window + exwm-workspace--minibuffer)) + (setq cursor-in-non-selected-windows + (frame-parameter exwm-workspace--minibuffer 'cursor-type)))) + +(defun exwm-workspace--hide-minibuffer () + "Hide the minibuffer frame." + ;; Hide the minibuffer frame. + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow + :window (frame-parameter exwm-workspace--minibuffer + 'exwm-container))) + (xcb:flush exwm--connection)) + +(defun exwm-workspace--on-minibuffer-setup () + "Run in minibuffer-setup-hook to show the minibuffer and its container." + (unless (> -1 (minibuffer-depth)) + (add-hook 'post-command-hook #'exwm-workspace--update-minibuffer) + (exwm-workspace--show-minibuffer) + ;; Set input focus on the Emacs frame + (x-focus-frame (window-frame (minibuffer-selected-window))))) + +(defun exwm-workspace--on-minibuffer-exit () + "Run in minibuffer-exit-hook to hide the minibuffer container." + (unless (> -1 (minibuffer-depth)) + (remove-hook 'post-command-hook #'exwm-workspace--update-minibuffer) + (exwm-workspace--hide-minibuffer))) + +(defvar exwm-input--during-command) + +(defun exwm-workspace--on-echo-area-dirty () + "Run when new message arrives to show the echo area and its container." + (when (and (not (active-minibuffer-window)) + (or (current-message) + cursor-in-echo-area)) + (exwm-workspace--update-minibuffer t) + (exwm-workspace--show-minibuffer) + (unless (or (not exwm-workspace-display-echo-area-timeout) + exwm-input--during-command ;e.g. read-event + input-method-use-echo-area) + (setq exwm-workspace--display-echo-area-timer + (run-with-timer exwm-workspace-display-echo-area-timeout nil + #'exwm-workspace--on-echo-area-clear))))) + +(defun exwm-workspace--on-echo-area-clear () + "Run in echo-area-clear-hook to hide echo area container." + (unless (active-minibuffer-window) + (exwm-workspace--hide-minibuffer)) + (when exwm-workspace--display-echo-area-timer + (cancel-timer exwm-workspace--display-echo-area-timer) + (setq exwm-workspace--display-echo-area-timer nil))) + (defun exwm-workspace--init () "Initialize workspace module." (cl-assert (and (< 0 exwm-workspace-number) (>= 10 exwm-workspace-number))) @@ -276,19 +449,79 @@ The optional FORCE option is for internal use only." (0 (y-or-n-p prompt)) (x (yes-or-no-p (format "[EXWM] %d window%s currently alive. %s" x (if (= x 1) "" "s") prompt)))))) - ;; Initialize workspaces - (setq exwm-workspace--list (frame-list)) - (when (< 1 (length exwm-workspace--list)) - ;; Emacs client creates an extra (but unusable) frame - (dolist (i exwm-workspace--list) - (unless (frame-parameter i 'window-id) - (setq exwm-workspace--list (delq i exwm-workspace--list)))) - (cl-assert (= 1 (length exwm-workspace--list))) - ;; Prevent user from deleting this frame by accident - (set-frame-parameter (car exwm-workspace--list) 'client nil)) - ;; Create remaining frames - (dotimes (_ (1- exwm-workspace-number)) - (nconc exwm-workspace--list (list (make-frame '((window-system . x)))))) + (if (not (memq exwm-workspace-minibuffer-position '(top bottom))) + ;; Initialize workspaces with minibuffers. + (progn + (setq exwm-workspace--list (frame-list)) + (when (< 1 (length exwm-workspace--list)) + ;; Emacs client creates an extra (but unusable) frame. + (dolist (i exwm-workspace--list) + (unless (frame-parameter i 'window-id) + (setq exwm-workspace--list (delq i exwm-workspace--list)))) + (cl-assert (= 1 (length exwm-workspace--list))) + ;; Prevent user from deleting this frame by accident. + (set-frame-parameter (car exwm-workspace--list) 'client nil)) + ;; Create remaining frames. + (dotimes (_ (1- exwm-workspace-number)) + (nconc exwm-workspace--list + (list (make-frame '((window-system . x))))))) + ;; Initialize workspaces without minibuffers. + (let ((old-frames (frame-list))) + (setq exwm-workspace--minibuffer + (make-frame '((window-system . x) (minibuffer . only) + (left . 10000) (right . 10000) + (width . 0) (height . 0))) + default-minibuffer-frame exwm-workspace--minibuffer) + ;; Remove/hide existing frames. + (dolist (f old-frames) + (if (frame-parameter f 'client) + (make-frame-invisible f) + (delete-frame f)))) + (let ((outer-id (string-to-number + (frame-parameter exwm-workspace--minibuffer + 'outer-window-id))) + (container (xcb:generate-id exwm--connection))) + (set-frame-parameter exwm-workspace--minibuffer 'exwm-outer-id outer-id) + (set-frame-parameter exwm-workspace--minibuffer 'exwm-container + container) + (xcb:+request exwm--connection + (make-instance 'xcb:CreateWindow + :depth 0 :wid container :parent exwm--root + :x -1 :y -1 :width 1 :height 1 + :border-width 0 :class xcb:WindowClass:CopyFromParent + :visual 0 ;CopyFromParent + :value-mask xcb:CW:OverrideRedirect + :override-redirect 1)) + (exwm--debug + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_NAME + :window container + :data "Minibuffer container"))) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window outer-id :parent container :x 0 :y 0)) + ;; Attach event listener for monitoring the frame + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window outer-id + :value-mask xcb:CW:EventMask + :event-mask xcb:EventMask:StructureNotify)) + (xcb:+event exwm--connection 'xcb:ConfigureNotify + #'exwm-workspace--on-ConfigureNotify)) + ;; Show/hide minibuffer / echo area when they're active/inactive. + (add-hook 'minibuffer-setup-hook #'exwm-workspace--on-minibuffer-setup) + (add-hook 'minibuffer-exit-hook #'exwm-workspace--on-minibuffer-exit) + (run-with-idle-timer 0 t #'exwm-workspace--on-echo-area-dirty) + (add-hook 'echo-area-clear-hook #'exwm-workspace--on-echo-area-clear) + ;; Create workspace frames. + (dotimes (_ exwm-workspace-number) + (push (make-frame `((window-system . x) + (minibuffer . ,(minibuffer-window + exwm-workspace--minibuffer)))) + exwm-workspace--list)) + ;; The default behavior of `display-buffer' (indirectly called by + ;; `minibuffer-completion-help') is not correct here. + (cl-pushnew '(exwm-workspace--display-buffer) display-buffer-alist)) ;; Configure workspaces (dolist (i exwm-workspace--list) (let ((outer-id (string-to-number (frame-parameter i 'outer-window-id))) @@ -296,11 +529,6 @@ The optional FORCE option is for internal use only." ;; Save window IDs (set-frame-parameter i 'exwm-outer-id outer-id) (set-frame-parameter i 'exwm-workspace workspace) - ;; Set OverrideRedirect on all frames - (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window outer-id :value-mask xcb:CW:OverrideRedirect - :override-redirect 1)) (xcb:+request exwm--connection (make-instance 'xcb:CreateWindow :depth 0 :wid workspace :parent exwm--root diff --git a/exwm.el b/exwm.el index 02d4d9cbe2..720274fdcb 100644 --- a/exwm.el +++ b/exwm.el @@ -466,9 +466,13 @@ (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT :window exwm--root :data (make-vector (* 2 exwm-workspace-number) 0))) - ;; Set _NET_WORKAREA (with minibuffer and bottom mode-line excluded) + ;; Set _NET_WORKAREA (with minibuffer excluded) (let* ((workareas - (vector 0 0 (x-display-pixel-width) (x-display-pixel-height))) + (vector 0 0 (x-display-pixel-width) + (- (x-display-pixel-height) + (if exwm-workspace-minibuffer-position + 0 + (window-pixel-height (minibuffer-window)))))) (workareas (mapconcat (lambda (_) workareas) (make-list exwm-workspace-number 0) []))) (xcb:+request exwm--connection -- cgit 1.4.1 From bc80eefe3ff015f645b5ca2ff4a8838a48a72b96 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 6 Feb 2016 16:47:30 +0800 Subject: Fix multi-monitor/fullscreen issues * exwm-randr.el (exwm-randr--refresh): Correct the _NET_WORKAREA property. Reconfigure the current workspace when screen changes. (exwm-randr--init): Run `exwm-randr-screen-change-hook' to take into account already attached monitor(s), * exwm.el (exwm--on-ClientMessage): Reconfigure the current workspace when it's fullscreen. --- exwm-randr.el | 17 ++++++++++++++--- exwm.el | 4 +++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/exwm-randr.el b/exwm-randr.el index 3ed64ed285..566f4eeaad 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -55,7 +55,8 @@ (defun exwm-randr--refresh () "Refresh workspaces according to the updated RandR info." - (let (output-name geometry output-plist default-geometry workareas viewports) + (let (output-name geometry output-plist default-geometry workareas + workarea-offset viewports) ;; Query all outputs (with-slots (config-timestamp outputs) (xcb:+request-unchecked+reply exwm--connection @@ -84,6 +85,10 @@ (unless default-geometry ;assume the first output as primary (setq default-geometry geometry))))))) (cl-assert (<= 2 (length output-plist))) + (exwm--log "(randr) outputs: %s" output-plist) + (setq workarea-offset (if exwm-workspace-minibuffer-position + 0 + (window-pixel-height (minibuffer-window)))) (dotimes (i exwm-workspace-number) (let* ((output (plist-get exwm-randr-workspace-output-plist i)) (geometry (lax-plist-get output-plist output)) @@ -98,7 +103,9 @@ (frame-parameter frame 'exwm-workspace) x y width height) - (setq workareas (nconc workareas (list x y width height)) + (setq workareas + (nconc workareas (list x y width (- height + workarea-offset))) viewports (nconc viewports (list x y)))))) ;; Update _NET_WORKAREA (xcb:+request exwm--connection @@ -109,7 +116,9 @@ (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT :window exwm--root :data (vconcat viewports))) - (xcb:flush exwm--connection))) + (xcb:flush exwm--connection)) + ;; Force update workspace settings. + (exwm-workspace-switch exwm-workspace-current-index t)) (defvar exwm-randr-screen-change-hook nil "Normal hook run when screen changes.") @@ -126,6 +135,8 @@ (if (or (/= major-version 1) (< minor-version 2)) (error "[EXWM] The server only support RandR version up to %d.%d" major-version minor-version) + ;; External monitor(s) may already be connected. + (run-hooks 'exwm-randr-screen-change-hook) (exwm-randr--refresh) (xcb:+event exwm--connection 'xcb:randr:ScreenChangeNotify (lambda (_data _synthetic) diff --git a/exwm.el b/exwm.el index 720274fdcb..4335c814cf 100644 --- a/exwm.el +++ b/exwm.el @@ -341,7 +341,9 @@ :window id :data (vector xcb:Atom:_NET_WM_STATE_FULLSCREEN))) - (xcb:flush exwm--connection)))) + (xcb:flush exwm--connection))) + ;; Force update workspace settings. + (exwm-workspace-switch exwm-workspace-current-index t)) (when buffer ;ensure it's managed (with-current-buffer buffer ;; _NET_WM_STATE_MODAL -- cgit 1.4.1 From 97daba20ad22f05f2e4c1347ff86d6c957142a0a Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 6 Feb 2016 20:33:56 +0800 Subject: Improve bc80eefe * exwm-layout.el (exwm-layout--set-frame-fullscreen): * exwm-randr.el (exwm-randr--refresh): * exwm-workspace.el (exwm-workspace--resize-minibuffer): (exwm-workspace-switch): * exwm.el (exwm--on-ClientMessage): Calling `exwm-workspace-switch' in bc80eefe does not work correctly sometimes. This commit improves it by directly specify the geometry info rather than getting it from Emacs frame. --- exwm-layout.el | 2 ++ exwm-randr.el | 6 +++--- exwm-workspace.el | 52 +++++++++++++++++++++++++++++++--------------------- exwm.el | 4 +--- 4 files changed, 37 insertions(+), 27 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index 316bf62a74..0dc5e1ac02 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -203,6 +203,8 @@ (id (frame-parameter frame 'exwm-outer-id)) (workspace (frame-parameter frame 'exwm-workspace))) (with-slots (x y width height) geometry + (when (eq frame exwm-workspace--current) + (exwm-workspace--resize-minibuffer width height)) (exwm-layout--resize-container id workspace x y width height) (xcb:flush exwm--connection)))) diff --git a/exwm-randr.el b/exwm-randr.el index 566f4eeaad..7e0d9bab64 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -103,6 +103,8 @@ (frame-parameter frame 'exwm-workspace) x y width height) + (when (eq frame exwm-workspace--current) + (exwm-workspace--resize-minibuffer width height)) (setq workareas (nconc workareas (list x y width (- height workarea-offset))) @@ -116,9 +118,7 @@ (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT :window exwm--root :data (vconcat viewports))) - (xcb:flush exwm--connection)) - ;; Force update workspace settings. - (exwm-workspace-switch exwm-workspace-current-index t)) + (xcb:flush exwm--connection))) (defvar exwm-randr-screen-change-hook nil "Normal hook run when screen changes.") diff --git a/exwm-workspace.el b/exwm-workspace.el index 9669428c85..1c23ca0229 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -111,6 +111,29 @@ Value nil means to use the default position which is fixed at bottom, while (defvar exwm-workspace--display-echo-area-timer nil "Timer for auto-hiding echo area.") +(defun exwm-workspace--resize-minibuffer (&optional width height) + "Resize minibuffer (and its container) to fit the size of workspace. + +If WIDTH and HEIGHT of the workspace is not specified, they're get from the +workspace frame." + (let ((y (if (eq exwm-workspace-minibuffer-position 'top) + 0 + (- (or height (frame-pixel-height exwm-workspace--current)) + (frame-pixel-height exwm-workspace--minibuffer)))) + (width (or width (frame-pixel-width exwm-workspace--current))) + (container (frame-parameter exwm-workspace--minibuffer + 'exwm-container))) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window container + :value-mask (logior xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:StackMode) + :y y + :width width + :stack-mode xcb:StackMode:Above)) + (set-frame-width exwm-workspace--minibuffer width nil t))) + ;;;###autoload (defun exwm-workspace-switch (index &optional force) "Switch to workspace INDEX. Query for INDEX if it's not specified. @@ -148,27 +171,14 @@ The optional FORCE option is for internal use only." (if (not (memq exwm-workspace-minibuffer-position '(top bottom))) (setq default-minibuffer-frame frame) ;; Resize/reposition the minibuffer frame - (let ((x 0) - (y (if (eq exwm-workspace-minibuffer-position 'top) - 0 - (- (frame-pixel-height frame) - (frame-pixel-height exwm-workspace--minibuffer)))) - (width (x-display-pixel-width)) - (container (frame-parameter exwm-workspace--minibuffer - 'exwm-container))) - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window container - :parent (frame-parameter frame 'exwm-workspace) - :x x :y y)) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window container - :value-mask (logior xcb:ConfigWindow:Width - xcb:ConfigWindow:StackMode) - :width width - :stack-mode xcb:StackMode:Above)) - (set-frame-width exwm-workspace--minibuffer width nil t))) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window + (frame-parameter exwm-workspace--minibuffer + 'exwm-container) + :parent (frame-parameter frame 'exwm-workspace) + :x 0 :y 0)) + (exwm-workspace--resize-minibuffer)) ;; Hide windows in other workspaces by preprending a space (unless exwm-workspace-show-all-buffers (dolist (i exwm--id-buffer-alist) diff --git a/exwm.el b/exwm.el index 4335c814cf..720274fdcb 100644 --- a/exwm.el +++ b/exwm.el @@ -341,9 +341,7 @@ :window id :data (vector xcb:Atom:_NET_WM_STATE_FULLSCREEN))) - (xcb:flush exwm--connection))) - ;; Force update workspace settings. - (exwm-workspace-switch exwm-workspace-current-index t)) + (xcb:flush exwm--connection)))) (when buffer ;ensure it's managed (with-current-buffer buffer ;; _NET_WM_STATE_MODAL -- cgit 1.4.1 From 00065234749054940563f59a0027a53bc5ebffad Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Sat, 6 Feb 2016 22:43:32 +0100 Subject: Resize minibuffer only when it's in its own frame 0e4055d3392 introduced a few calls to exwm-workspace--resize-minibuffer in various places. This function only works when the minibuffer is displayed in its own frame but was called unconditionally in some cases. Fix it by wrapping all calls in an appropriate conditional and add an assertion. Also rename the function so it is clearer that it resizes a frame, which might prevent calling it unconditionally in the future. --- exwm-input.el | 2 +- exwm-layout.el | 7 ++++--- exwm-randr.el | 5 +++-- exwm-workspace.el | 13 +++++++++---- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 757efb7566..85be1efb20 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -111,7 +111,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (exwm-input--set-focus exwm--id) ;; Adjust stacking orders (when exwm--floating-frame - (if (memq exwm-workspace-minibuffer-position '(top bottom)) + (if (exwm-workspace--minibuffer-own-frame-p) ;; Put this floating X window just below the minibuffer. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow diff --git a/exwm-layout.el b/exwm-layout.el index 0dc5e1ac02..871438f9f6 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -203,8 +203,9 @@ (id (frame-parameter frame 'exwm-outer-id)) (workspace (frame-parameter frame 'exwm-workspace))) (with-slots (x y width height) geometry - (when (eq frame exwm-workspace--current) - (exwm-workspace--resize-minibuffer width height)) + (when (and (eq frame exwm-workspace--current) + (exwm-workspace--minibuffer-own-frame-p)) + (exwm-workspace--resize-minibuffer-frame width height)) (exwm-layout--resize-container id workspace x y width height) (xcb:flush exwm--connection)))) @@ -395,7 +396,7 @@ See also `exwm-layout-enlarge-window'." "Initialize layout module." ;; Auto refresh layout (add-hook 'window-configuration-change-hook #'exwm-layout--refresh) - (unless (memq exwm-workspace-minibuffer-position '(top bottom)) + (unless (exwm-workspace--minibuffer-own-frame-p) ;; Refresh when minibuffer grows (add-hook 'minibuffer-setup-hook #'exwm-layout--on-minibuffer-setup t) (run-with-idle-timer 0 t #'exwm-layout--on-echo-area-change t) diff --git a/exwm-randr.el b/exwm-randr.el index 7e0d9bab64..51161a455d 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -103,8 +103,9 @@ (frame-parameter frame 'exwm-workspace) x y width height) - (when (eq frame exwm-workspace--current) - (exwm-workspace--resize-minibuffer width height)) + (when (and (eq frame exwm-workspace--current) + (exwm-workspace--minibuffer-own-frame-p)) + (exwm-workspace--resize-minibuffer-frame width height)) (setq workareas (nconc workareas (list x y width (- height workarea-offset))) diff --git a/exwm-workspace.el b/exwm-workspace.el index 1c23ca0229..b166733ac6 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -111,11 +111,16 @@ Value nil means to use the default position which is fixed at bottom, while (defvar exwm-workspace--display-echo-area-timer nil "Timer for auto-hiding echo area.") -(defun exwm-workspace--resize-minibuffer (&optional width height) +(defun exwm-workspace--minibuffer-own-frame-p () + "Reports whether the minibuffer is displayed in its own frame." + (memq exwm-workspace-minibuffer-position '(top bottom))) + +(defun exwm-workspace--resize-minibuffer-frame (&optional width height) "Resize minibuffer (and its container) to fit the size of workspace. If WIDTH and HEIGHT of the workspace is not specified, they're get from the workspace frame." + (cl-assert (exwm-workspace--minibuffer-own-frame-p)) (let ((y (if (eq exwm-workspace-minibuffer-position 'top) 0 (- (or height (frame-pixel-height exwm-workspace--current)) @@ -168,7 +173,7 @@ The optional FORCE option is for internal use only." ;; Close the (possible) active minibuffer (when (active-minibuffer-window) (run-with-idle-timer 0 nil (lambda () (abort-recursive-edit)))) - (if (not (memq exwm-workspace-minibuffer-position '(top bottom))) + (if (not (exwm-workspace--minibuffer-own-frame-p)) (setq default-minibuffer-frame frame) ;; Resize/reposition the minibuffer frame (xcb:+request exwm--connection @@ -178,7 +183,7 @@ The optional FORCE option is for internal use only." 'exwm-container) :parent (frame-parameter frame 'exwm-workspace) :x 0 :y 0)) - (exwm-workspace--resize-minibuffer)) + (exwm-workspace--resize-minibuffer-frame)) ;; Hide windows in other workspaces by preprending a space (unless exwm-workspace-show-all-buffers (dolist (i exwm--id-buffer-alist) @@ -459,7 +464,7 @@ This functions is modified from `display-buffer-reuse-window' and (0 (y-or-n-p prompt)) (x (yes-or-no-p (format "[EXWM] %d window%s currently alive. %s" x (if (= x 1) "" "s") prompt)))))) - (if (not (memq exwm-workspace-minibuffer-position '(top bottom))) + (if (not (exwm-workspace--minibuffer-own-frame-p)) ;; Initialize workspaces with minibuffers. (progn (setq exwm-workspace--list (frame-list)) -- cgit 1.4.1 From 3d643fafbe58d9b3e2dc9e864cbf05a2d911e1b2 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 7 Feb 2016 10:22:33 +0800 Subject: Remove an assertion * exwm-randr.el (exwm-randr--refresh): There can be no valid output sometimes. --- exwm-randr.el | 71 ++++++++++++++++++++++++++++++----------------------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/exwm-randr.el b/exwm-randr.el index 51161a455d..716d5218e2 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -84,42 +84,43 @@ output-plist (plist-put output-plist output-name geometry)) (unless default-geometry ;assume the first output as primary (setq default-geometry geometry))))))) - (cl-assert (<= 2 (length output-plist))) (exwm--log "(randr) outputs: %s" output-plist) - (setq workarea-offset (if exwm-workspace-minibuffer-position - 0 - (window-pixel-height (minibuffer-window)))) - (dotimes (i exwm-workspace-number) - (let* ((output (plist-get exwm-randr-workspace-output-plist i)) - (geometry (lax-plist-get output-plist output)) - (frame (elt exwm-workspace--list i))) - (unless geometry - (setq geometry default-geometry - output nil)) - (set-frame-parameter frame 'exwm-randr-output output) - (set-frame-parameter frame 'exwm-geometry geometry) - (with-slots (x y width height) geometry - (exwm-layout--resize-container (frame-parameter frame 'exwm-outer-id) - (frame-parameter frame - 'exwm-workspace) - x y width height) - (when (and (eq frame exwm-workspace--current) - (exwm-workspace--minibuffer-own-frame-p)) - (exwm-workspace--resize-minibuffer-frame width height)) - (setq workareas - (nconc workareas (list x y width (- height - workarea-offset))) - viewports (nconc viewports (list x y)))))) - ;; Update _NET_WORKAREA - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_WORKAREA - :window exwm--root :data (vconcat workareas))) - ;; Update _NET_DESKTOP_VIEWPORT - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT - :window exwm--root - :data (vconcat viewports))) - (xcb:flush exwm--connection))) + (when output-plist + (setq workarea-offset (if exwm-workspace-minibuffer-position + 0 + (window-pixel-height (minibuffer-window)))) + (dotimes (i exwm-workspace-number) + (let* ((output (plist-get exwm-randr-workspace-output-plist i)) + (geometry (lax-plist-get output-plist output)) + (frame (elt exwm-workspace--list i))) + (unless geometry + (setq geometry default-geometry + output nil)) + (set-frame-parameter frame 'exwm-randr-output output) + (set-frame-parameter frame 'exwm-geometry geometry) + (with-slots (x y width height) geometry + (exwm-layout--resize-container (frame-parameter frame + 'exwm-outer-id) + (frame-parameter frame + 'exwm-workspace) + x y width height) + (when (and (eq frame exwm-workspace--current) + (exwm-workspace--minibuffer-own-frame-p)) + (exwm-workspace--resize-minibuffer-frame width height)) + (setq workareas + (nconc workareas (list x y width (- height + workarea-offset))) + viewports (nconc viewports (list x y)))))) + ;; Update _NET_WORKAREA + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WORKAREA + :window exwm--root :data (vconcat workareas))) + ;; Update _NET_DESKTOP_VIEWPORT + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT + :window exwm--root + :data (vconcat viewports))) + (xcb:flush exwm--connection)))) (defvar exwm-randr-screen-change-hook nil "Normal hook run when screen changes.") -- cgit 1.4.1 From e2edf81271e1a7dd148aa7386b730ae2838c2860 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 7 Feb 2016 10:45:59 +0800 Subject: Add some useful key bindings * exwm-core.el (exwm--floating-mode-line-format, exwm--mode-line-format) (exwm-mode-map): * exwm-floating.el (exwm-floating-hide-mode-line) (exwm-floating-show-mode-line): * exwm-layout.el (exwm-layout-hide-mode-line, exwm-layout-show-mode-line) (exwm-layout-toggle-mode-line): Allow hide/show mode-line for all `exwm-mode' buffers with 'C-c M'. * exwm-config.el (exwm-config-default): Add simulation keys for 'C-d' and 'C-k'. --- exwm-config.el | 4 +++- exwm-core.el | 17 +++++++++-------- exwm-floating.el | 28 ++++------------------------ exwm-layout.el | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 33 deletions(-) diff --git a/exwm-config.el b/exwm-config.el index 512abfc0df..db8c68f445 100644 --- a/exwm-config.el +++ b/exwm-config.el @@ -55,7 +55,9 @@ ([?\C-a] . home) ([?\C-e] . end) ([?\M-v] . prior) - ([?\C-v] . next))) + ([?\C-v] . next) + ([?\C-d] . delete) + ([?\C-k] . (S-end delete)))) ;; Enable EXWM (exwm-enable) ;; Configure Ido diff --git a/exwm-core.el b/exwm-core.el index 61633c9775..9430b43548 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -79,14 +79,14 @@ "Event mask set on all managed windows.") ;; Internal variables -(defvar-local exwm--id nil) ;window ID -(defvar-local exwm--container nil) ;container -(defvar-local exwm--frame nil) ;workspace frame -(defvar-local exwm--floating-frame nil) ;floating frame -(defvar-local exwm--floating-mode-line-format nil) ;save mode-line-format -(defvar-local exwm--fullscreen nil) ;used in fullscreen -(defvar-local exwm--floating-frame-position nil) ;used in fullscreen -(defvar-local exwm--fixed-size nil) ;fixed size +(defvar-local exwm--id nil) ;window ID +(defvar-local exwm--container nil) ;container +(defvar-local exwm--frame nil) ;workspace frame +(defvar-local exwm--floating-frame nil) ;floating frame +(defvar-local exwm--mode-line-format nil) ;save mode-line-format +(defvar-local exwm--fullscreen nil) ;used in fullscreen +(defvar-local exwm--floating-frame-position nil) ;used in fullscreen +(defvar-local exwm--fixed-size nil) ;fixed size (defvar-local exwm--on-KeyPress ;KeyPress event handler #'exwm-input--on-KeyPress-line-mode) ;; Properties @@ -122,6 +122,7 @@ (define-key map "\C-cm" #'exwm-floating-toggle-floating) (define-key map "\C-cq" #'exwm-input-send-next-key) (define-key map "\C-cv" #'exwm-workspace-move-window) + (define-key map "\C-cM" #'exwm-layout-toggle-mode-line) map) "Keymap for `exwm-mode'.") diff --git a/exwm-floating.el b/exwm-floating.el index ff584c03ee..34e9f947e7 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -248,30 +248,10 @@ are provided. You should call `xcb:flush' and restore the value of (window-mode-line-height) (window-header-line-height))))) -(defun exwm-floating-hide-mode-line () - "Hide mode-line of a floating frame." - (interactive) - (unless (eq major-mode 'exwm-mode) - (user-error "[EXWM] Please use this command with EXWM buffers")) - (when (and exwm--floating-frame mode-line-format) - (setq exwm--floating-mode-line-format mode-line-format - mode-line-format nil) - (exwm-floating--fit-frame-to-window) - (xcb:flush exwm--connection) - (setq window-size-fixed exwm--fixed-size))) - -(defun exwm-floating-show-mode-line () - "Show mode-line of a floating frame." - (interactive) - (unless (eq major-mode 'exwm-mode) - (user-error "[EXWM] Please use this command with EXWM buffers")) - (when (and exwm--floating-frame (not mode-line-format)) - (setq mode-line-format exwm--floating-mode-line-format - exwm--floating-mode-line-format nil) - (exwm-floating--fit-frame-to-window) - (exwm-input-grab-keyboard) ;mode-line-format may be outdated - (xcb:flush exwm--connection) - (setq window-size-fixed exwm--fixed-size))) +(define-obsolete-function-alias 'exwm-floating-hide-mode-line + 'exwm-layout-hide-mode-line "25.1" "Hide mode-line of a floating frame.") +(define-obsolete-function-alias 'exwm-floating-show-mode-line + 'exwm-layout-show-mode-line "25.1" "Show mode-line of a floating frame.") (defvar exwm-floating--moveresize-calculate nil "Calculate move/resize parameters [buffer event-mask x y width height].") diff --git a/exwm-layout.el b/exwm-layout.el index 871438f9f6..df603f709a 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -392,6 +392,39 @@ See also `exwm-layout-enlarge-window'." (interactive "p") (exwm-layout-enlarge-window (- delta) t)) +(defun exwm-layout-hide-mode-line () + "Hide mode-line." + (interactive) + (when (and (eq major-mode 'exwm-mode) mode-line-format) + (setq exwm--mode-line-format mode-line-format + mode-line-format nil) + (if (not exwm--floating-frame) + (exwm-layout--show exwm--id) + (exwm-floating--fit-frame-to-window) + (xcb:flush exwm--connection) + (setq window-size-fixed exwm--fixed-size)))) + +(defun exwm-layout-show-mode-line () + "Show mode-line." + (interactive) + (when (and (eq major-mode 'exwm-mode) (not mode-line-format)) + (setq mode-line-format exwm--mode-line-format + exwm--mode-line-format nil) + (if (not exwm--floating-frame) + (exwm-layout--show exwm--id) + (exwm-floating--fit-frame-to-window) + (exwm-input-grab-keyboard) + (xcb:flush exwm--connection) + (setq window-size-fixed exwm--fixed-size)))) + +(defun exwm-layout-toggle-mode-line () + "Toggle the display of mode-line." + (interactive) + (when (eq major-mode 'exwm-mode) + (if mode-line-format + (exwm-layout-hide-mode-line) + (exwm-layout-show-mode-line)))) + (defun exwm-layout--init () "Initialize layout module." ;; Auto refresh layout -- cgit 1.4.1 From 15cdf8f5897183adf5426ecc280eb61626e2c980 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 7 Feb 2016 11:25:49 +0800 Subject: Unmanage X windows on exit * exwm-workspace.el (exwm-workspace--confirm-kill-emacs): New function called on exit to unmanage X windows. (exwm-workspace--init): Set `confirm-kill-emacs' to `exwm-workspace--confirm-kill-emacs'. * exwm-manage.el (exwm-manage--on-UnmapNotify): Ignore UnmapNotify event generated as a result of parent being resized. --- exwm-manage.el | 6 ++++-- exwm-workspace.el | 20 ++++++++++++++------ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index b32d677ea9..b899afbb85 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -421,8 +421,10 @@ Would you like to kill it? " (unless synthetic (let ((obj (make-instance 'xcb:UnmapNotify))) (xcb:unmarshal obj data) - (exwm--log "UnmapNotify from #x%x" (slot-value obj 'window)) - (exwm-manage--unmanage-window (slot-value obj 'window) t)))) + (with-slots (window from-configure) obj + (unless from-configure ;the parent is being resized + (exwm--log "UnmapNotify from #x%x" window) + (exwm-manage--unmanage-window window t)))))) (defun exwm-manage--on-DestroyNotify (data synthetic) "Handle DestroyNotify event." diff --git a/exwm-workspace.el b/exwm-workspace.el index b166733ac6..f65c557eef 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -454,16 +454,24 @@ This functions is modified from `display-buffer-reuse-window' and (cancel-timer exwm-workspace--display-echo-area-timer) (setq exwm-workspace--display-echo-area-timer nil))) +(defun exwm-workspace--confirm-kill-emacs (prompt) + "Confirm before exiting Emacs." + (when (pcase (length exwm--id-buffer-alist) + (0 (y-or-n-p prompt)) + (x (yes-or-no-p (format "[EXWM] %d window%s currently alive. %s" + x (if (= x 1) "" "s") prompt)))) + (dolist (i exwm--id-buffer-alist) + (exwm-manage--unmanage-window (car i) t) + (xcb:+request exwm--connection + (make-instance 'xcb:MapWindow :window (car i)))) + (xcb:flush exwm--connection) + t)) + (defun exwm-workspace--init () "Initialize workspace module." (cl-assert (and (< 0 exwm-workspace-number) (>= 10 exwm-workspace-number))) ;; Prevent unexpected exit - (setq confirm-kill-emacs - (lambda (prompt) - (pcase (length exwm--id-buffer-alist) - (0 (y-or-n-p prompt)) - (x (yes-or-no-p (format "[EXWM] %d window%s currently alive. %s" - x (if (= x 1) "" "s") prompt)))))) + (setq confirm-kill-emacs #'exwm-workspace--confirm-kill-emacs) (if (not (exwm-workspace--minibuffer-own-frame-p)) ;; Initialize workspaces with minibuffers. (progn -- cgit 1.4.1 From fb9bfd291109d3bebc622f2c33a072fac475ac85 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 7 Feb 2016 11:40:14 +0800 Subject: Eliminate compilation warnings --- exwm-floating.el | 1 + exwm-layout.el | 1 + exwm-manage.el | 1 + exwm-workspace.el | 2 ++ 4 files changed, 5 insertions(+) diff --git a/exwm-floating.el b/exwm-floating.el index 34e9f947e7..b5ab8a8878 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -221,6 +221,7 @@ (exwm-floating--unset-floating exwm--id) (exwm-floating--set-floating exwm--id)))) +;;;###autoload (defun exwm-floating--fit-frame-to-window (&optional frame-outer-id width height) "Resize a floating frame to make it fit the size of the window. diff --git a/exwm-layout.el b/exwm-layout.el index df603f709a..a70cf1b012 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -417,6 +417,7 @@ See also `exwm-layout-enlarge-window'." (xcb:flush exwm--connection) (setq window-size-fixed exwm--fixed-size)))) +;;;###autoload (defun exwm-layout-toggle-mode-line () "Toggle the display of mode-line." (interactive) diff --git a/exwm-manage.el b/exwm-manage.el index b899afbb85..d7edee06da 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -202,6 +202,7 @@ corresponding buffer.") (with-current-buffer (exwm--id->buffer id) (run-hooks 'exwm-manage-finish-hook))))) +;;;###autoload (defun exwm-manage--unmanage-window (id &optional withdraw-only) "Unmanage window ID." (let ((buffer (exwm--id->buffer id))) diff --git a/exwm-workspace.el b/exwm-workspace.el index f65c557eef..feeb2fe028 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -111,10 +111,12 @@ Value nil means to use the default position which is fixed at bottom, while (defvar exwm-workspace--display-echo-area-timer nil "Timer for auto-hiding echo area.") +;;;###autoload (defun exwm-workspace--minibuffer-own-frame-p () "Reports whether the minibuffer is displayed in its own frame." (memq exwm-workspace-minibuffer-position '(top bottom))) +;;;###autoload (defun exwm-workspace--resize-minibuffer-frame (&optional width height) "Resize minibuffer (and its container) to fit the size of workspace. -- cgit 1.4.1 From 35d04c34685a1c1e9f351a2c50b3eea2b2fb5f25 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 9 Feb 2016 13:26:48 +0800 Subject: Fix bugs on managing/unmanaging X windows * exwm-manage.el (exwm-manage--scan): Unmap X windows before managing them. (exwm-manage--on-UnmapNotify): Do not ignore synthetic UnmapNotify events (according to ICCCM). Do not use the `from-configure' slot which was mistakenly introduced due to the bug in `exwm-manage--scan'. * exwm-workspace.el (exwm-workspace--confirm-kill-emacs): Do more cleanups. --- exwm-manage.el | 19 +++++++++---------- exwm-workspace.el | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index d7edee06da..21e18ff9cf 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -171,8 +171,6 @@ corresponding buffer.") (make-instance 'xcb:ConfigureWindow :window id :value-mask xcb:ConfigWindow:BorderWidth :border-width 0)) - ;; (xcb:+request exwm--connection ;map the window - ;; (make-instance 'xcb:MapWindow :window id)) (dolist (button ;grab buttons to set focus / move / resize (list xcb:ButtonIndex:1 xcb:ButtonIndex:2 xcb:ButtonIndex:3)) (xcb:+request-checked+request-check exwm--connection @@ -272,6 +270,9 @@ corresponding buffer.") (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GetWindowAttributes :window i)) (when (and (= 0 override-redirect) (= xcb:MapState:Viewable map-state)) + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow :window i)) + (xcb:flush exwm--connection) (exwm-manage--manage-window i)))))) (defvar exwm-manage--ping-lock nil @@ -417,15 +418,13 @@ Would you like to kill it? " (exwm--log "MapRequest from #x%x" window) (exwm-manage--manage-window window)))))) -(defun exwm-manage--on-UnmapNotify (data synthetic) +(defun exwm-manage--on-UnmapNotify (data _synthetic) "Handle UnmapNotify event." - (unless synthetic - (let ((obj (make-instance 'xcb:UnmapNotify))) - (xcb:unmarshal obj data) - (with-slots (window from-configure) obj - (unless from-configure ;the parent is being resized - (exwm--log "UnmapNotify from #x%x" window) - (exwm-manage--unmanage-window window t)))))) + (let ((obj (make-instance 'xcb:UnmapNotify))) + (xcb:unmarshal obj data) + (with-slots (window) obj + (exwm--log "UnmapNotify from #x%x" window) + (exwm-manage--unmanage-window window t)))) (defun exwm-manage--on-DestroyNotify (data synthetic) "Handle DestroyNotify event." diff --git a/exwm-workspace.el b/exwm-workspace.el index feeb2fe028..3d7f67ecb8 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -462,11 +462,29 @@ This functions is modified from `display-buffer-reuse-window' and (0 (y-or-n-p prompt)) (x (yes-or-no-p (format "[EXWM] %d window%s currently alive. %s" x (if (= x 1) "" "s") prompt)))) + ;; Remove SubstructureRedirect event. + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window exwm--root :value-mask xcb:CW:EventMask + :event-mask 0)) + ;; Remove the _NET_SUPPORTING_WM_CHECK X window. + (with-slots (value) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:ewmh:get-_NET_SUPPORTING_WM_CHECK + :window exwm--root)) + (xcb:+request exwm--connection + (make-instance 'xcb:DeleteProperty + :window exwm--root + :property xcb:Atom:_NET_SUPPORTING_WM_CHECK)) + (xcb:+request exwm--connection + (make-instance 'xcb:DestroyWindow :window value))) + ;; Unmanage all X windows. (dolist (i exwm--id-buffer-alist) (exwm-manage--unmanage-window (car i) t) (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window (car i)))) (xcb:flush exwm--connection) + (xcb:disconnect exwm--connection) t)) (defun exwm-workspace--init () -- cgit 1.4.1 From ea027d424ea71209ca1819094577559d7847203c Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 12 Feb 2016 14:10:11 +0800 Subject: Postpone making workspace frames fullscreen * exwm-workspace.el (exwm-workspace--init, exwm-workspace--post-init): Move work to postpone to the new function `exwm-workspace--post-init'. * exwm.el (exwm-init): Call `exwm-workspace--post-init' after `exwm--unlock`. --- exwm-workspace.el | 13 ++++++++----- exwm.el | 1 + 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 3d7f67ecb8..99a7c7bd2b 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -599,11 +599,6 @@ This functions is modified from `display-buffer-reuse-window' and (xcb:flush exwm--connection) ;; We have to advice `x-create-frame' or every call to it would hang EXWM (advice-add 'x-create-frame :around #'exwm-workspace--x-create-frame) - ;; Delay making the workspaces fullscreen until Emacs becomes idle - (run-with-idle-timer 0 nil - (lambda () - (dolist (i exwm-workspace--list) - (set-frame-parameter i 'fullscreen 'fullboth)))) ;; Set _NET_VIRTUAL_ROOTS (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_VIRTUAL_ROOTS @@ -615,6 +610,14 @@ This functions is modified from `display-buffer-reuse-window' and ;; Switch to the first workspace (exwm-workspace-switch 0 t)) +(defun exwm-workspace--post-init () + "The second stage in the initialization of the workspace module." + ;; Delay making the workspaces fullscreen until Emacs becomes idle + (run-with-idle-timer 0 nil + (lambda () + (dolist (i exwm-workspace--list) + (set-frame-parameter i 'fullscreen 'fullboth))))) + (provide 'exwm-workspace) diff --git a/exwm.el b/exwm.el index 720274fdcb..8f15b4e870 100644 --- a/exwm.el +++ b/exwm.el @@ -517,6 +517,7 @@ (exwm-workspace--init) (exwm-input--init) (exwm--unlock) + (exwm-workspace--post-init) ;; Manage existing windows (exwm-manage--scan) (run-hooks 'exwm-init-hook))))) -- cgit 1.4.1 From 12e2d574c916ec7b93b645c0d0bf2e1fb0ce342a Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 12 Feb 2016 14:12:33 +0800 Subject: Bump version to 0.2 --- exwm.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm.el b/exwm.el index 8f15b4e870..85c905e6ef 100644 --- a/exwm.el +++ b/exwm.el @@ -4,8 +4,8 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0.1 -;; Package-Requires: ((xelb "0.3")) +;; Version: 0.2 +;; Package-Requires: ((xelb "0.5")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From 3f7722079cebd0d998239ce40457899135250a15 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 18 Feb 2016 19:56:01 +0800 Subject: Fix floating X window bugs introduced by 9c95c03e * exwm-floating.el (exwm-floating--set-floating): Make floating frames invisible before resizing them. (exwm-floating--fit-frame-to-window): Removed since unused. * exwm-layout.el (exwm-layout-hide-mode-line, exwm-layout-show-mode-line): Use set frame height instead of exwm-floating--fit-frame-to-window. * exwm-core.el (exwm-mode): Replace `exwm-manage--close-window' with `exwm-manage--kill-buffer-query-function'. * exwm-floating.el (exwm-floating--unset-floating): Reparent out floating frames. * exwm-manage.el (exwm-manage--unmanage-window): Reparent out floating frames. Hide floating frames. (exwm-manage--close-window, exwm-manage--kill-buffer-query-function): Rename `exwm-manage--close-window' since it's only used in `kill-buffer-query-functions'. Reparent out floating frames. --- exwm-core.el | 4 +- exwm-floating.el | 60 ++++++++++----------- exwm-layout.el | 29 ++++++---- exwm-manage.el | 158 +++++++++++++++++++++++++++++++------------------------ 4 files changed, 137 insertions(+), 114 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 9430b43548..b09ca52c0c 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -142,9 +142,7 @@ (add-hook 'change-major-mode-hook #'kill-buffer nil t) ;; Kill buffer -> close window (add-hook 'kill-buffer-query-functions - (lambda () - (exwm-manage--close-window exwm--id (current-buffer))) - nil t) + #'exwm-manage--kill-buffer-query-function nil t) (setq buffer-read-only t left-margin-width nil right-margin-width nil diff --git a/exwm-floating.el b/exwm-floating.el index b5ab8a8878..82b4487f15 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -50,6 +50,8 @@ (defvar exwm-floating--cursor-bottom-left nil) (defvar exwm-floating--cursor-left nil) +(declare-function exwm-layout--refresh "exwm-layout.el") + ;;;###autoload (defun exwm-floating--set-floating (id) "Make window ID floating." @@ -78,6 +80,8 @@ (internal-border-width . ,exwm-floating-border-width) (left . 10000) (top . 10000) + (width . ,window-min-width) + (height . ,window-min-height) (unsplittable . t))))) ;and fix the size later (outer-id (string-to-number (frame-parameter frame 'outer-window-id))) (container (with-current-buffer (exwm--id->buffer id) @@ -151,7 +155,17 @@ y (/ (- display-height height) 2)))))) (exwm--log "Floating geometry (corrected): %dx%d%+d%+d" width height x y) ;; Fit frame to client - (exwm-floating--fit-frame-to-window outer-id width height) + ;; It seems we have to make the frame invisible in order to resize it + ;; timely. + ;; The frame will be made visible by `select-frame-set-input-focus'. + (make-frame-invisible frame) + (let ((edges (window-inside-pixel-edges window))) + (set-frame-size frame + (+ width (- (frame-pixel-width frame) + (- (elt edges 2) (elt edges 0)))) + (+ height (- (frame-pixel-height frame) + (- (elt edges 3) (elt edges 1)))) + t)) ;; Reparent this frame to the container (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow @@ -171,8 +185,12 @@ (setq window-size-fixed exwm--fixed-size exwm--frame original-frame exwm--floating-frame frame) + ;; Do the refresh manually. + (remove-hook 'window-configuration-change-hook #'exwm-layout--refresh) (set-window-buffer window (current-buffer)) ;this changes current buffer - (set-window-dedicated-p window t)) + (add-hook 'window-configuration-change-hook #'exwm-layout--refresh) + (set-window-dedicated-p window t) + (exwm-layout--show id window)) (select-frame-set-input-focus frame)) (run-hooks 'exwm-floating-setup-hook)) @@ -182,6 +200,16 @@ (interactive) (let ((buffer (exwm--id->buffer id))) (with-current-buffer buffer + ;; Reparent the frame back to the root window. + (when exwm--floating-frame + (let ((frame-id (frame-parameter exwm--floating-frame 'exwm-outer-id))) + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow :window frame-id)) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window frame-id + :parent exwm--root + :x 0 :y 0)))) ;; Reparent the container to the workspace (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow @@ -221,34 +249,6 @@ (exwm-floating--unset-floating exwm--id) (exwm-floating--set-floating exwm--id)))) -;;;###autoload -(defun exwm-floating--fit-frame-to-window (&optional frame-outer-id - width height) - "Resize a floating frame to make it fit the size of the window. - -Default to resize `exwm--floating-frame' unless FRAME-OUTER-ID is non-nil. -This function will issue an `xcb:GetGeometry' request unless WIDTH and HEIGHT -are provided. You should call `xcb:flush' and restore the value of -`window-size-fixed' afterwards." - (setq window-size-fixed nil) - (unless (and width height) - (let ((geometry (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:GetGeometry :drawable exwm--id)))) - (setq width (slot-value geometry 'width) - height (slot-value geometry 'height)))) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window (or frame-outer-id - (frame-parameter exwm--floating-frame - 'exwm-outer-id)) - :value-mask (eval-when-compile - (logior xcb:ConfigWindow:Width - xcb:ConfigWindow:Height)) - :width (+ width (* 2 exwm-floating-border-width)) - :height (+ height (* 2 exwm-floating-border-width) - (window-mode-line-height) - (window-header-line-height))))) - (define-obsolete-function-alias 'exwm-floating-hide-mode-line 'exwm-layout-hide-mode-line "25.1" "Hide mode-line of a floating frame.") (define-obsolete-function-alias 'exwm-floating-show-mode-line diff --git a/exwm-layout.el b/exwm-layout.el index a70cf1b012..52a84b0fe1 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -396,13 +396,18 @@ See also `exwm-layout-enlarge-window'." "Hide mode-line." (interactive) (when (and (eq major-mode 'exwm-mode) mode-line-format) - (setq exwm--mode-line-format mode-line-format - mode-line-format nil) - (if (not exwm--floating-frame) - (exwm-layout--show exwm--id) - (exwm-floating--fit-frame-to-window) - (xcb:flush exwm--connection) - (setq window-size-fixed exwm--fixed-size)))) + (let (mode-line-height) + (when exwm--floating-frame + (setq mode-line-height (window-mode-line-height + (frame-root-window exwm--floating-frame)))) + (setq exwm--mode-line-format mode-line-format + mode-line-format nil) + (if (not exwm--floating-frame) + (exwm-layout--show exwm--id) + (set-frame-height exwm--floating-frame + (- (frame-pixel-height exwm--floating-frame) + mode-line-height) + nil t))))) (defun exwm-layout-show-mode-line () "Show mode-line." @@ -412,10 +417,12 @@ See also `exwm-layout-enlarge-window'." exwm--mode-line-format nil) (if (not exwm--floating-frame) (exwm-layout--show exwm--id) - (exwm-floating--fit-frame-to-window) - (exwm-input-grab-keyboard) - (xcb:flush exwm--connection) - (setq window-size-fixed exwm--fixed-size)))) + (set-frame-height exwm--floating-frame + (+ (frame-pixel-height exwm--floating-frame) + (window-mode-line-height (frame-root-window + exwm--floating-frame))) + nil t) + (exwm-input-grab-keyboard)))) ;;;###autoload (defun exwm-layout-toggle-mode-line () diff --git a/exwm-manage.el b/exwm-manage.el index 21e18ff9cf..50784ce301 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -103,7 +103,7 @@ corresponding buffer.") exwm-window-type)))) (exwm--log "No need to manage #x%x" id) ;; Remove all events - (xcb:+request-checked+request-check exwm--connection + (xcb:+request exwm--connection (make-instance 'xcb:ChangeWindowAttributes :window id :value-mask xcb:CW:EventMask :event-mask xcb:EventMask:NoEvent)) @@ -173,7 +173,7 @@ corresponding buffer.") :border-width 0)) (dolist (button ;grab buttons to set focus / move / resize (list xcb:ButtonIndex:1 xcb:ButtonIndex:2 xcb:ButtonIndex:3)) - (xcb:+request-checked+request-check exwm--connection + (xcb:+request exwm--connection (make-instance 'xcb:GrabButton :owner-events 0 :grab-window id :event-mask xcb:EventMask:ButtonPress @@ -206,13 +206,10 @@ corresponding buffer.") (let ((buffer (exwm--id->buffer id))) (exwm--log "Unmanage #x%x (buffer: %s)" id buffer) (setq exwm--id-buffer-alist (assq-delete-all id exwm--id-buffer-alist)) - (xcb:+request exwm--connection ;update _NET_CLIENT_LIST - (make-instance 'xcb:ewmh:set-_NET_CLIENT_LIST - :window exwm--root - :data (vconcat (mapcar #'car exwm--id-buffer-alist)))) - (xcb:flush exwm--connection) (when (buffer-live-p buffer) (with-current-buffer buffer + ;; Hide the floating frame as early as possible. + (when exwm--floating-frame (make-frame-invisible exwm--floating-frame)) (xcb:+request exwm--connection (make-instance 'xcb:UnmapWindow :window exwm--container)) (setq exwm-workspace--switch-history-outdated t) @@ -245,21 +242,29 @@ corresponding buffer.") ;; Delete WM_STATE property (xcb:+request exwm--connection (make-instance 'xcb:DeleteProperty - :window id :property xcb:Atom:WM_STATE)) - (xcb:flush exwm--connection)) + :window id :property xcb:Atom:WM_STATE))) ;; Destroy the container (it seems it has to be delayed). - (run-with-idle-timer 0 nil - `(lambda () - (xcb:+request exwm--connection - ,(make-instance 'xcb:DestroyWindow - :window exwm--container)) - (xcb:flush exwm--connection))) + (when exwm--floating-frame + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window (frame-parameter exwm--floating-frame + 'exwm-outer-id) + :parent exwm--root + :x 0 :y 0))) + (xcb:+request exwm--connection + (make-instance 'xcb:DestroyWindow :window exwm--container)) + (xcb:flush exwm--connection) (let ((kill-buffer-query-functions nil) (floating exwm--floating-frame)) (kill-buffer) (when floating (select-window - (frame-selected-window exwm-workspace--current)))))))) + (frame-selected-window exwm-workspace--current)))))) + (xcb:+request exwm--connection ;update _NET_CLIENT_LIST + (make-instance 'xcb:ewmh:set-_NET_CLIENT_LIST + :window exwm--root + :data (vconcat (mapcar #'car exwm--id-buffer-alist)))) + (xcb:flush exwm--connection))) (defun exwm-manage--scan () "Search for existing windows and try to manage them." @@ -280,64 +285,77 @@ corresponding buffer.") (defvar exwm-manage-ping-timeout 3 "Seconds to wait before killing a client.") ;;;###autoload -(defun exwm-manage--close-window (id &optional buffer) - "Close window ID in a proper way." - (let (container) - (catch 'return - (unless (exwm--id->buffer id) - (throw 'return t)) - (unless buffer (setq buffer (exwm--id->buffer id))) - (when (buffer-live-p buffer) - (setq container exwm--container)) - ;; Destroy the client window if it does not support WM_DELETE_WINDOW - (unless (and (buffer-live-p buffer) - (with-current-buffer buffer - (memq xcb:Atom:WM_DELETE_WINDOW exwm--protocols))) +(defun exwm-manage--kill-buffer-query-function () + "Run in `kill-buffer-query-functions'." + (catch 'return + (when (xcb:+request-checked+request-check exwm--connection + (make-instance 'xcb:MapWindow :window exwm--id)) + ;; The X window is no longer alive so just close the buffer. + ;; Destroy the container. + (when exwm--floating-frame (xcb:+request exwm--connection - (make-instance 'xcb:DestroyWindow :window id)) - (xcb:flush exwm--connection) - (throw 'return nil)) - ;; Try to close the window with WM_DELETE_WINDOW client message + (make-instance 'xcb:ReparentWindow + :window (frame-parameter exwm--floating-frame + 'exwm-outer-id) + :parent exwm--root + :x 0 :y 0))) (xcb:+request exwm--connection - (make-instance 'xcb:icccm:SendEvent - :destination id - :event (xcb:marshal - (make-instance 'xcb:icccm:WM_DELETE_WINDOW - :window id) - exwm--connection))) + (make-instance 'xcb:DestroyWindow :window exwm--container)) (xcb:flush exwm--connection) - ;; Try to determine if the client stop responding - (with-current-buffer buffer - (unless (memq xcb:Atom:_NET_WM_PING exwm--protocols) - ;; Ensure it's dead - (run-with-timer exwm-manage-ping-timeout nil - `(lambda () (exwm-manage--kill-client ,id))) - (throw 'return nil)) - (setq exwm-manage--ping-lock t) - (xcb:+request exwm--connection - (make-instance 'xcb:SendEvent - :propagate 0 :destination id - :event-mask xcb:EventMask:NoEvent - :event (xcb:marshal - (make-instance 'xcb:ewmh:_NET_WM_PING - :window id :timestamp 0 - :client-window id) - exwm--connection))) - (xcb:flush exwm--connection) - (with-timeout (exwm-manage-ping-timeout - (if (yes-or-no-p (format "`%s' is not responding. \ -Would you like to kill it? " - (buffer-name buffer))) - (progn (exwm-manage--kill-client id) - (throw 'return nil)) - (throw 'return nil))) - (while (and exwm-manage--ping-lock - (exwm--id->buffer id)) ;may have been destroyed - (accept-process-output nil 0.1))))) - ;; Finally destroy the container + (throw 'return t)) + (unless (memq xcb:Atom:WM_DELETE_WINDOW exwm--protocols) + ;; The X window does not support WM_DELETE_WINDOW; destroy it. + (xcb:+request exwm--connection + (make-instance 'xcb:DestroyWindow :window exwm--id)) + (xcb:flush exwm--connection) + ;; Wait for DestroyNotify event. + (throw 'return nil)) + ;; Try to close the X window with WM_DELETE_WINDOW client message. (xcb:+request exwm--connection - (make-instance 'xcb:DestroyWindow :window container)) - (xcb:flush exwm--connection))) + (make-instance 'xcb:icccm:SendEvent + :destination exwm--id + :event (xcb:marshal + (make-instance 'xcb:icccm:WM_DELETE_WINDOW + :window exwm--id) + exwm--connection))) + (xcb:flush exwm--connection) + ;; + (unless (memq xcb:Atom:_NET_WM_PING exwm--protocols) + ;; The window does not support _NET_WM_PING. To make sure it'll die, + ;; kill it after the time runs out. + (run-with-timer exwm-manage-ping-timeout nil + `(lambda () (exwm-manage--kill-client ,exwm--id))) + ;; Wait for DestroyNotify event. + (throw 'return nil)) + ;; Try to determine if the X window is dead with _NET_WM_PING. + (setq exwm-manage--ping-lock t) + (xcb:+request exwm--connection + (make-instance 'xcb:SendEvent + :propagate 0 + :destination exwm--id + :event-mask xcb:EventMask:NoEvent + :event (xcb:marshal + (make-instance 'xcb:ewmh:_NET_WM_PING + :window exwm--id + :timestamp 0 + :client-window exwm--id) + exwm--connection))) + (xcb:flush exwm--connection) + (with-timeout (exwm-manage-ping-timeout + (if (yes-or-no-p (format "'%s' is not responding. \ +Would you like to kill it? " + (buffer-name))) + (progn (exwm-manage--kill-client exwm--id) + ;; Kill the unresponsive X window and + ;; wait for DestroyNotify event. + (throw 'return nil)) + ;; Give up. + (throw 'return nil))) + (while (and exwm-manage--ping-lock + (exwm--id->buffer exwm--id)) ;may have been destroyed. + (accept-process-output nil 0.1)) + ;; Give up. + (throw 'return nil)))) (defun exwm-manage--kill-client (&optional id) "Kill an X client." -- cgit 1.4.1 From bfd43feb494a8a7675f3a882ea5ebeaa91fb3f82 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 19 Feb 2016 11:02:37 +0800 Subject: Add system tray support * exwm-systemtray.el: New module adds a simple system tray (using the X11 System Tray protocol). * exwm-workspace.el (exwm-workspace-switch-hook, exwm-workspace-switch): New hook run after switching workspace. --- exwm-systemtray.el | 372 +++++++++++++++++++++++++++++++++++++++++++++++++++++ exwm-workspace.el | 9 +- 2 files changed, 377 insertions(+), 4 deletions(-) create mode 100644 exwm-systemtray.el diff --git a/exwm-systemtray.el b/exwm-systemtray.el new file mode 100644 index 0000000000..c892fcc1a7 --- /dev/null +++ b/exwm-systemtray.el @@ -0,0 +1,372 @@ +;;; exwm-systemtray.el --- System Tray Module for -*- lexical-binding: t -*- +;;; EXWM + +;; Copyright (C) 2016 Free Software Foundation, Inc. + +;; Author: Chris Feng + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Commentary: + +;; This module adds system tray support for EXWM. + +;; To use this module, load and enable it as follows: +;; (require 'exwm-systemtray) +;; (exwm-systemtray-enable) + +;;; Code: + +(require 'xcb-xembed) +(require 'xcb-systemtray) +(require 'exwm-core) +(require 'exwm-workspace) + +(defclass exwm-systemtray--icon () + ((width :initarg :width) + (height :initarg :height) + (visible :initarg :visible)) + :documentation "Attributes of a system tray icon.") + +;; GTK icons require at least 16 pixels to show normally. +(defconst exwm-systemtray--icon-min-size 16 "Minimum icon size.") + +(defvar exwm-systemtray-height (max exwm-systemtray--icon-min-size + (line-pixel-height)) + "System tray height. + +You shall use the default value if using auto-hide minibuffer.") + +(defvar exwm-systemtray-icon-gap 2 "Gap between icons.") + +(defvar exwm-systemtray--connection nil "The X connection.") +(defvar exwm-systemtray--list nil "The icon list.") +(defvar exwm-systemtray--selection-owner-window nil + "The selection owner window.") +(defvar exwm-systemtray--embedder nil "The embedder window.") + +(defun exwm-systemtray--embed (icon) + "Embed an icon." + (exwm--log "(System Tray) Try to embed #x%x" icon) + (let ((info (xcb:+request-unchecked+reply exwm-systemtray--connection + (make-instance 'xcb:xembed:get-_XEMBED_INFO + :window icon))) + width* height* visible) + (when info + (exwm--log "(System Tray) Embed #x%x" icon) + (with-slots (width height) + (xcb:+request-unchecked+reply exwm-systemtray--connection + (make-instance 'xcb:GetGeometry :drawable icon)) + (setq height* exwm-systemtray-height + width* (round (* width (/ (float height*) height)))) + (when (< width* exwm-systemtray--icon-min-size) + (setq width* exwm-systemtray--icon-min-size + height* (round (* height (/ (float width*) width))))) + (exwm--log "(System Tray) Resize from %dx%d to %dx%d" + width height width* height*)) + ;; Reparent to the embedder. + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:ReparentWindow + :window icon + :parent exwm-systemtray--embedder + :x 0 + ;; Vertically centered. + :y (/ (- exwm-systemtray-height height*) 2))) + ;; Resize the icon. + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:ConfigureWindow + :window icon + :value-mask (logior xcb:ConfigWindow:Width + xcb:ConfigWindow:Height + xcb:ConfigWindow:BorderWidth) + :width width* + :height height* + :border-width 0)) + ;; Set event mask. + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:ChangeWindowAttributes + :window icon + :value-mask xcb:CW:EventMask + :event-mask (logior xcb:EventMask:ResizeRedirect + xcb:EventMask:PropertyChange))) + (when (setq visible + (/= 0 (logand (slot-value info 'flags) xcb:xembed:MAPPED))) + (exwm--log "(System Tray) Map the window") + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:MapWindow :window icon))) + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:xembed:SendEvent + :destination icon + :event + (xcb:marshal + (make-instance 'xcb:xembed:EMBEDDED-NOTIFY + :window icon + :time xcb:Time:CurrentTime + :embedder exwm-systemtray--embedder + :version 0) + exwm-systemtray--connection))) + (push `(,icon . ,(make-instance 'exwm-systemtray--icon + :width width* + :height height* + :visible visible)) + exwm-systemtray--list) + (exwm-systemtray--refresh)))) + +(defun exwm-systemtray--unembed (icon) + "Unembed an icon." + (exwm--log "(System Tray) Unembed #x%x" icon) + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:UnmapWindow :window icon)) + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:ReparentWindow + :window icon + :parent exwm--root + :x 0 :y 0)) + (setq exwm-systemtray--list + (assq-delete-all icon exwm-systemtray--list)) + (exwm-systemtray--refresh)) + +(defun exwm-systemtray--refresh () + "Refresh the system tray." + ;; Make sure to redraw the embedder. + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:UnmapWindow :window exwm-systemtray--embedder)) + (let ((x exwm-systemtray-icon-gap) + map) + (dolist (pair exwm-systemtray--list) + (when (slot-value (cdr pair) 'visible) + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:ConfigureWindow + :window (car pair) + :value-mask xcb:ConfigWindow:X + :x x)) + (setq x (+ x (slot-value (cdr pair) 'width) + exwm-systemtray-icon-gap)) + (setq map t))) + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:ConfigureWindow + :window exwm-systemtray--embedder + :value-mask (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Width) + :x (- (frame-pixel-width exwm-workspace--current) x) + :width x)) + (when map + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:MapWindow :window exwm-systemtray--embedder)))) + (xcb:flush exwm-systemtray--connection)) + +(defun exwm-systemtray--on-DestroyNotify (data _synthetic) + "Unembed icons on DestroyNotify." + (let ((obj (make-instance 'xcb:DestroyNotify))) + (xcb:unmarshal obj data) + (with-slots (window) obj + (when (alist-get window exwm-systemtray--list) + (exwm-systemtray--unembed window))))) + +(defun exwm-systemtray--on-ReparentNotify (data _synthetic) + "Unembed icons on ReparentNotify." + (let ((obj (make-instance 'xcb:ReparentNotify))) + (xcb:unmarshal obj data) + (with-slots (window parent) obj + (when (and (/= parent exwm-systemtray--embedder) + (alist-get window exwm-systemtray--list)) + (exwm-systemtray--unembed window))))) + +(defun exwm-systemtray--on-ResizeRequest (data _synthetic) + "Resize the tray icon on ResizeRequest." + (let ((obj (make-instance 'xcb:ResizeRequest)) + attr) + (xcb:unmarshal obj data) + (with-slots (window width height) obj + (when (setq attr (alist-get window exwm-systemtray--list)) + (with-slots ((width* width) + (height* height)) + attr + (setq height* exwm-systemtray-height + width* (round (* width (/ (float height*) height)))) + (when (< width* exwm-systemtray--icon-min-size) + (setq width* exwm-systemtray--icon-min-size + height* (round (* height (/ (float width*) width))))) + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:ConfigureWindow + :window window + :value-mask (logior xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height) + ;; Vertically centered. + :y (/ (- exwm-systemtray-height height*) 2) + :width width* + :height height*))) + (exwm-systemtray--refresh))))) + +(defun exwm-systemtray--on-PropertyNotify (data _synthetic) + "Map/Unmap the tray icon on PropertyNotify." + (let ((obj (make-instance 'xcb:PropertyNotify)) + attr info visible) + (xcb:unmarshal obj data) + (with-slots (window atom state) obj + (when (and (eq state xcb:Property:NewValue) + (eq atom xcb:Atom:_XEMBED_INFO) + (setq attr (alist-get window exwm-systemtray--list))) + (setq info (xcb:+request-unchecked+reply exwm-systemtray--connection + (make-instance 'xcb:xembed:get-_XEMBED_INFO + :window window))) + (when info + (setq visible (/= 0 (logand (slot-value info 'flags) + xcb:xembed:MAPPED))) + (exwm--log "(System Tray) #x%x visible? %s" window visible) + (if visible + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:MapWindow :window window)) + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:UnmapWindow :window window))) + (setf (slot-value attr 'visible) visible) + (exwm-systemtray--refresh)))))) + +(defun exwm-systemtray--on-ClientMessage (data _synthetic) + "Handle client messages." + (let ((obj (make-instance 'xcb:ClientMessage)) + opcode data32) + (xcb:unmarshal obj data) + (with-slots (window type data) obj + (when (eq type xcb:Atom:_NET_SYSTEM_TRAY_OPCODE) + (setq data32 (slot-value data 'data32) + opcode (elt data32 1)) + (cond ((= opcode xcb:systemtray:opcode:REQUEST-DOCK) + (exwm-systemtray--embed (elt data32 2))) + ((= opcode xcb:systemtray:opcode:BEGIN-MESSAGE) + ;; FIXME + ) + ((= opcode xcb:systemtray:opcode:CANCEL-MESSAGE) + ;; FIXME + ) + (t + (exwm--log "(System Tray) Unknown opcode message: %s" obj))))))) + +(defun exwm-systemtray--on-exwm-workspace-switch () + "Reparent the system tray in `exwm-workspace-switch-hook'." + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:ReparentWindow + :window exwm-systemtray--embedder + :parent (string-to-number + (frame-parameter exwm-workspace--current + 'window-id)) + :x 0 + :y (- (frame-pixel-height exwm-workspace--current) + exwm-systemtray-height))) + (exwm-systemtray--refresh)) + +(defvar xcb:Atom:_NET_SYSTEM_TRAY_S0) + +(defun exwm-systemtray--init () + "Initialize system tray module." + (cl-assert (not exwm-systemtray--connection)) + (cl-assert (not exwm-systemtray--list)) + (cl-assert (not exwm-systemtray--selection-owner-window)) + (cl-assert (not exwm-systemtray--embedder)) + ;; Create a new connection. + (setq exwm-systemtray--connection (xcb:connect-to-socket)) + (set-process-query-on-exit-flag (slot-value exwm-systemtray--connection + 'process) + nil) + ;; Initialize XELB modules. + (xcb:xembed:init exwm-systemtray--connection) + (xcb:systemtray:init exwm-systemtray--connection) + ;; Acquire the manager selection _NET_SYSTEM_TRAY_S0. + (with-slots (owner) + (xcb:+request-unchecked+reply exwm-systemtray--connection + (make-instance 'xcb:GetSelectionOwner + :selection xcb:Atom:_NET_SYSTEM_TRAY_S0)) + (when (/= owner xcb:Window:None) + (error "[EXWM] Other system tray detected"))) + (let ((id (xcb:generate-id exwm-systemtray--connection))) + (setq exwm-systemtray--selection-owner-window id) + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:CreateWindow + :depth 0 :wid id :parent exwm--root + :x 0 :y 0 :width 1 :height 1 + :border-width 0 :class xcb:WindowClass:InputOnly + :visual 0 :value-mask xcb:CW:OverrideRedirect + :override-redirect 1)) + ;; Get the selection ownership. + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:SetSelectionOwner + :owner id + :selection xcb:Atom:_NET_SYSTEM_TRAY_S0 + :time xcb:Time:CurrentTime)) + ;; Set _NET_WM_NAME. + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:ewmh:set-_NET_WM_NAME + :window id :data "EXWM system tray selection owner")) + ;; Set the _NET_SYSTEM_TRAY_ORIENTATION property. + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:xembed:set-_NET_SYSTEM_TRAY_ORIENTATION + :window id + :data xcb:systemtray:ORIENTATION:HORZ))) + ;; Create the embedder. + (let ((id (xcb:generate-id exwm-systemtray--connection)) + parent y) + (setq exwm-systemtray--embedder id) + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:CreateWindow + :depth 0 :wid id :parent exwm--root + :x 0 :y 0 :width 1 :height exwm-systemtray-height + :border-width 0 :class xcb:WindowClass:CopyFromParent + :visual 0 :value-mask xcb:CW:EventMask + :event-mask xcb:EventMask:SubstructureNotify)) + (if exwm-workspace-minibuffer-position + (setq parent (frame-parameter exwm-workspace--minibuffer + 'exwm-container) + ;; Vertically centered. + y (/ (- (line-pixel-height) exwm-systemtray-height) 2)) + (setq parent (string-to-number (frame-parameter exwm-workspace--current + 'window-id)) + ;; Bottom aligned. + y (- (frame-pixel-height exwm-workspace--current) + exwm-systemtray-height))) + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:ReparentWindow + :window id :parent parent :x 0 :y y)) + ;; Set _NET_WM_NAME. + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:ewmh:set-_NET_WM_NAME + :window id :data "EXWM system tray embedder"))) + (xcb:flush exwm-systemtray--connection) + ;; Attach event listeners. + (xcb:+event exwm-systemtray--connection 'xcb:DestroyNotify + #'exwm-systemtray--on-DestroyNotify) + (xcb:+event exwm-systemtray--connection 'xcb:ReparentNotify + #'exwm-systemtray--on-ReparentNotify) + (xcb:+event exwm-systemtray--connection 'xcb:ResizeRequest + #'exwm-systemtray--on-ResizeRequest) + (xcb:+event exwm-systemtray--connection 'xcb:PropertyNotify + #'exwm-systemtray--on-PropertyNotify) + (xcb:+event exwm-systemtray--connection 'xcb:ClientMessage + #'exwm-systemtray--on-ClientMessage) + ;; Add hook to reparent the embedder. + (unless exwm-workspace-minibuffer-position + (add-hook 'exwm-workspace-switch-hook + #'exwm-systemtray--on-exwm-workspace-switch))) + +(defun exwm-systemtray-enable () + "Enable system tray support for EXWM." + (add-hook 'exwm-init-hook #'exwm-systemtray--init)) + + + +(provide 'exwm-systemtray) + +;; exwm-systemtray.el ends here diff --git a/exwm-workspace.el b/exwm-workspace.el index 99a7c7bd2b..e9bab305e3 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -23,9 +23,6 @@ ;; This module adds workspace support for EXWM. -;; Todo: -;; + Add system tray support. - ;;; Code: (require 'exwm-core) @@ -141,6 +138,9 @@ workspace frame." :stack-mode xcb:StackMode:Above)) (set-frame-width exwm-workspace--minibuffer width nil t))) +(defvar exwm-workspace-switch-hook nil + "Normal hook run after switching workspace.") + ;;;###autoload (defun exwm-workspace-switch (index &optional force) "Switch to workspace INDEX. Query for INDEX if it's not specified. @@ -203,7 +203,8 @@ The optional FORCE option is for internal use only." (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_CURRENT_DESKTOP :window exwm--root :data index)) - (xcb:flush exwm--connection))))) + (xcb:flush exwm--connection)) + (run-hooks 'exwm-workspace-switch-hook)))) ;;;###autoload (defun exwm-workspace-move-window (index &optional id) -- cgit 1.4.1 From fc589b899b71e88e48931de41ea1df760f9c1edd Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 19 Feb 2016 17:12:43 +0800 Subject: Fix system tray issues after updating workspaces * exwm-workspace.el (exwm-workspace-switch-hook): New hook run by `exwm-workspace-switch'. * exwm-randr.el (exwm-randr-refresh-hook): New hook run by `exwm-randr--refresh'. * exwm-systemtray.el (exwm-systemtray--on-randr-refresh) (exwm-systemtray--on-workspace-switch, exwm-systemtray--init): Update the system tray in `exwm-randr-refresh-hook' and `exwm-workspace-switch-hook'. * exwm-layout.el (exwm-layout--set-frame-fullscreen): * exwm-workspace.el (exwm-workspace--post-init): Wait until all workspace frames are set fullscreen. * exwm-workspace.el (exwm-workspace--current-width) (exwm-workspace--current-height): New functions for retrieving the width and height of the current workspace. * exwm-layout.el (exwm-layout-set-fullscreen): * exwm-manage.el (exwm-manage--manage-window) (exwm-manage--on-ConfigureRequest): * exwm-systemtray.el (exwm-systemtray--refresh, exwm-systemtray--init): * exwm-workspace.le (exwm-workspace--resize-minibuffer-frame) (exwm-workspace--on-ConfigureNotify): Switch to `exwm-workspace--current-width' and `exwm-workspace--current-height'. * exwm-core.el: * exwm-floating.el: * exwm-floating.el: * exwm-input.el: * exwm-layout.el: * exwm-manage.el: * exwm-randr.el: * exwm-systemtray.el: * exwm-workspace.el: * exwm.el: Clean up loading file. Set/Unset some functions as commands. * README.md: Add intro to system tray. --- README.md | 5 +++-- exwm-core.el | 7 ++++++- exwm-floating.el | 17 ++++++++-------- exwm-input.el | 21 ++++++++++++++++--- exwm-layout.el | 24 +++++++++++++++------- exwm-manage.el | 29 +++++++++++++++++---------- exwm-randr.el | 25 +++++++++++++---------- exwm-systemtray.el | 59 +++++++++++++++++++++++++++++++++--------------------- exwm-workspace.el | 46 ++++++++++++++++++++++++++++++++---------- exwm.el | 16 ++++----------- 10 files changed, 159 insertions(+), 90 deletions(-) diff --git a/README.md b/README.md index 09fe470d39..7f918bd297 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,12 @@ EXWM (Emacs X Window Manager) is a full-featured tiling X window manager for Emacs built on top of [XELB](https://github.com/ch11ng/xelb). It features: -+ Fully keyboard-driven operation ++ Fully keyboard-driven operations + Hybrid layout modes (tiling & stacking) + Workspace support + ICCCM/EWMH compliance -+ Basic RandR support (optional) ++ (Optional) RandR (multi-monitor) support ++ (Optional) system tray Please check the [User Guide](https://github.com/ch11ng/exwm/wiki) for more details. diff --git a/exwm-core.el b/exwm-core.el index b09ca52c0c..4d936ed752 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -78,6 +78,9 @@ (logior xcb:EventMask:StructureNotify xcb:EventMask:PropertyChange)) "Event mask set on all managed windows.") +(declare-function exwm-input--on-KeyPress-line-mode "exwm-input.el" + (key-press)) + ;; Internal variables (defvar-local exwm--id nil) ;window ID (defvar-local exwm--container nil) ;container @@ -110,7 +113,7 @@ (defvar-local exwm--normal-hints-max-height nil) ;; (defvar-local exwm--normal-hints-win-gravity nil) ;; WM_HINTS -(defvar-local exwm--hints-input nil) ;FIXME +(defvar-local exwm--hints-input nil) (defvar-local exwm--hints-urgency nil) ;; _MOTIF_WM_HINTS (defvar-local exwm--mwm-hints nil) @@ -126,6 +129,8 @@ map) "Keymap for `exwm-mode'.") +(declare-function exwm-manage--kill-buffer-query-function "exwm-manage.el") + (define-derived-mode exwm-mode nil "EXWM" "Major mode for managing X windows. diff --git a/exwm-floating.el b/exwm-floating.el index 82b4487f15..209539eb4a 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -28,7 +28,6 @@ (require 'xcb-cursor) (require 'exwm-core) -(eval-when-compile (require 'exwm-workspace)) (defvar exwm-floating-border-width 1 "Border width of the floating window.") (defvar exwm-floating-border-color "navy" @@ -50,12 +49,17 @@ (defvar exwm-floating--cursor-bottom-left nil) (defvar exwm-floating--cursor-left nil) +(defvar exwm-workspace--current) +(defvar exwm-workspace--list) +(defvar exwm-workspace-current-index) +(defvar exwm-workspace--switch-history-outdated) +(defvar exwm-workspace-minibuffer-position) + (declare-function exwm-layout--refresh "exwm-layout.el") +(declare-function exwm-layout--show "exwm-layout.el") -;;;###autoload (defun exwm-floating--set-floating (id) "Make window ID floating." - (interactive) (let ((window (get-buffer-window (exwm--id->buffer id)))) (when window ;window in non-floating state (set-window-buffer window (other-buffer)))) ;hide it first @@ -85,7 +89,7 @@ (unsplittable . t))))) ;and fix the size later (outer-id (string-to-number (frame-parameter frame 'outer-window-id))) (container (with-current-buffer (exwm--id->buffer id) - exwm--container)) + exwm--container)) (window (frame-first-window frame)) ;and it's the only window (x (slot-value exwm--geometry 'x)) (y (slot-value exwm--geometry 'y)) @@ -194,10 +198,8 @@ (select-frame-set-input-focus frame)) (run-hooks 'exwm-floating-setup-hook)) -;;;###autoload (defun exwm-floating--unset-floating (id) "Make window ID non-floating." - (interactive) (let ((buffer (exwm--id->buffer id))) (with-current-buffer buffer ;; Reparent the frame back to the root window. @@ -257,7 +259,6 @@ (defvar exwm-floating--moveresize-calculate nil "Calculate move/resize parameters [buffer event-mask x y width height].") -;;;###autoload (defun exwm-floating--start-moveresize (id &optional type) "Start move/resize." (let ((buffer (exwm--id->buffer id)) @@ -404,7 +405,6 @@ :cursor cursor :time xcb:Time:CurrentTime))))))) -;;;###autoload (defun exwm-floating--stop-moveresize (&rest _args) "Stop move/resize." (xcb:+request exwm--connection @@ -434,7 +434,6 @@ (xcb:flush exwm--connection) (setq exwm-floating--moveresize-calculate nil)) -;;;###autoload (defun exwm-floating--do-moveresize (data _synthetic) "Perform move/resize." (when exwm-floating--moveresize-calculate diff --git a/exwm-input.el b/exwm-input.el index 85be1efb20..5e078030c2 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -37,7 +37,6 @@ (require 'xcb-keysyms) (require 'exwm-core) -(eval-when-compile (require 'exwm-workspace)) (defvar exwm-input-move-event 's-down-mouse-1 "Emacs event to start moving a window.") @@ -94,6 +93,11 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") exwm-input--timer (run-with-idle-timer 0.01 nil #'exwm-input--update-focus))))) +(defvar exwm-workspace--current) +(defvar exwm-workspace--switch-history-outdated) +(defvar exwm-workspace-current-index) +(defvar exwm-workspace--minibuffer) + (defun exwm-input--update-focus () "Update input focus." (when (window-live-p exwm-input--focus-window) @@ -158,6 +162,11 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (setq exwm-input--temp-line-mode nil) (exwm-input--release-keyboard)))) +(declare-function exwm-floating--start-moveresize "exwm-floating.el" + (id &optional type)) + +(defvar exwm-workspace--list) + (defun exwm-input--on-ButtonPress (data _synthetic) "Handle ButtonPress event." (let ((obj (make-instance 'xcb:ButtonPress)) @@ -262,6 +271,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (defun exwm-input-set-key (key command) "Set a global key binding." + (interactive "KSet key globally: \nCSet key %s to command: ") (global-set-key key command) (cl-pushnew key exwm-input--global-keys)) @@ -273,7 +283,6 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (defvar exwm-input--during-command nil "Indicate whether between `pre-command-hook' and `post-command-hook'.") -;;;###autoload (defun exwm-input--on-KeyPress-line-mode (key-press) "Parse X KeyPress event to Emacs key event and then feed the command loop." (with-slots (detail state) key-press @@ -443,12 +452,13 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (defun exwm-input-set-simulation-keys (simulation-keys) "Set simulation keys. -SIMULATION-KEYS is a list of alist (key-sequence1 . key-sequence2)." +SIMULATION-KEYS is an alist of the form (original-key . simulated-key)." (setq exwm-input--simulation-keys nil) (dolist (i simulation-keys) (cl-pushnew `(,(vconcat (car i)) . ,(cdr i)) exwm-input--simulation-keys)) (exwm-input--update-simulation-prefix-keys)) +;;;###autoload (defun exwm-input-send-simulation-key (times) "Fake a key event according to last input key sequence." (interactive "p") @@ -461,6 +471,11 @@ SIMULATION-KEYS is a list of alist (key-sequence1 . key-sequence2)." (dolist (j pair) (exwm-input--fake-key j)))))) +(declare-function exwm-floating--stop-moveresize "exwm-floating.el" + (&rest _args)) +(declare-function exwm-floating--do-moveresize "exwm-floating.el" + (data _synthetic)) + (defun exwm-input--init () "Initialize the keyboard module." ;; Refresh keyboard mapping diff --git a/exwm-layout.el b/exwm-layout.el index 52a84b0fe1..c0f3c61472 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -26,7 +26,6 @@ ;;; Code: (require 'exwm-core) -(eval-when-compile (require 'exwm-workspace)) (defvar exwm-floating-border-width) @@ -51,7 +50,6 @@ xcb:ConfigWindow:Height)) :width width :height height)))) -;;;###autoload (defun exwm-layout--show (id &optional window) "Show window ID exactly fit in the Emacs window WINDOW." (exwm--log "Show #x%x in %s" id window) @@ -112,7 +110,6 @@ exwm--connection)))) (xcb:flush exwm--connection)) -;;;###autoload (defun exwm-layout--hide (id) "Hide window ID." (unless (eq xcb:icccm:WM_STATE:IconicState ;already hidden @@ -137,6 +134,9 @@ :icon xcb:Window:None)) (xcb:flush exwm--connection))) +(defvar exwm-workspace--current) +(defvar exwm-workspace--list) + ;;;###autoload (defun exwm-layout-set-fullscreen (&optional id) "Make window ID fullscreen." @@ -153,9 +153,8 @@ (vector (slot-value geometry 'x) (slot-value geometry 'y)))) (xcb:flush exwm--connection)) (exwm-layout--resize-container exwm--id exwm--container 0 0 - (frame-pixel-width exwm-workspace--current) - (frame-pixel-height - exwm-workspace--current)) + (exwm-workspace--current-width) + (exwm-workspace--current-height)) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_STATE :window exwm--id @@ -164,6 +163,7 @@ (setq exwm--fullscreen t) (exwm-input-release-keyboard))) +;;;###autoload (defun exwm-layout-unset-fullscreen (&optional id) "Restore window from fullscreen state." (interactive) @@ -187,6 +187,9 @@ (setq exwm--fullscreen nil) (exwm-input-grab-keyboard))) +(defvar exwm-layout--fullscreen-frame-count 0 + "Count the fullscreen workspace frames.") + ;; This function is superficially similar to `exwm-layout-set-fullscreen', but ;; they do very different things: `exwm-layout--set-frame-fullscreen' resizes a ;; frame to the actual monitor size, `exwm-layout-set-fullscreen' resizes an X @@ -207,7 +210,8 @@ (exwm-workspace--minibuffer-own-frame-p)) (exwm-workspace--resize-minibuffer-frame width height)) (exwm-layout--resize-container id workspace x y width height) - (xcb:flush exwm--connection)))) + (xcb:flush exwm--connection))) + (cl-incf exwm-layout--fullscreen-frame-count)) (defvar exwm-layout-show-all-buffers nil "Non-nil to allow switching to buffers on other workspaces.") @@ -297,6 +301,7 @@ (exwm-layout--refresh) (run-with-idle-timer 0.01 nil #'exwm-layout--refresh)))) ;FIXME +;;;###autoload (defun exwm-layout-enlarge-window (delta &optional horizontal) "Make the selected window DELTA pixels taller. @@ -371,6 +376,7 @@ windows." :height height)) (xcb:flush exwm--connection)))))) +;;;###autoload (defun exwm-layout-enlarge-window-horizontally (delta) "Make the selected window DELTA pixels wider. @@ -378,6 +384,7 @@ See also `exwm-layout-enlarge-window'." (interactive "p") (exwm-layout-enlarge-window delta t)) +;;;###autoload (defun exwm-layout-shrink-window (delta) "Make the selected window DELTA pixels lower. @@ -385,6 +392,7 @@ See also `exwm-layout-enlarge-window'." (interactive "p") (exwm-layout-enlarge-window (- delta))) +;;;###autoload (defun exwm-layout-shrink-window-horizontally (delta) "Make the selected window DELTA pixels narrower. @@ -392,6 +400,7 @@ See also `exwm-layout-enlarge-window'." (interactive "p") (exwm-layout-enlarge-window (- delta) t)) +;;;###autoload (defun exwm-layout-hide-mode-line () "Hide mode-line." (interactive) @@ -409,6 +418,7 @@ See also `exwm-layout-enlarge-window'." mode-line-height) nil t))))) +;;;###autoload (defun exwm-layout-show-mode-line () "Show mode-line." (interactive) diff --git a/exwm-manage.el b/exwm-manage.el index 50784ce301..224ee16aa8 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -27,7 +27,6 @@ ;;; Code: (require 'exwm-core) -(eval-when-compile (require 'exwm-workspace)) (defvar exwm-manage-finish-hook nil "Normal hook run after a window is just managed, in the context of the @@ -59,6 +58,20 @@ corresponding buffer.") (when reply (setq exwm--mwm-hints (append (slot-value reply 'value) nil))))))) +(defvar exwm-workspace--current) +(defvar exwm-workspace--switch-history-outdated) + +(declare-function exwm--update-window-type "exwm.el" (id &optional force)) +(declare-function exwm--update-class "exwm.el" (id &optional force)) +(declare-function exwm--update-transient-for "exwm.el" (id &optional force)) +(declare-function exwm--update-normal-hints "exwm.el" (id &optional force)) +(declare-function exwm--update-title "exwm.el" (id)) +(declare-function exwm--update-hints "exwm.el" (id &optional force)) +(declare-function exwm--update-protocols "exwm.el" (id &optional force)) +(declare-function exwm--update-state "exwm.el" (id &optional force)) +(declare-function exwm-floating--set-floating "exwm-floating.el" (id)) +(declare-function exwm-floating--unset-floating "exwm-floating.el" (id)) + (defun exwm-manage--manage-window (id) "Manage window ID." (exwm--log "Try to manage #x%x" id) @@ -130,12 +143,9 @@ corresponding buffer.") :value-mask (eval-when-compile (logior xcb:ConfigWindow:X xcb:ConfigWindow:Y)) - :x (/ (- (frame-pixel-width - exwm-workspace--current) - width) + :x (/ (- (exwm-workspace--current-width) width) 2) - :y (/ (- (frame-pixel-height - exwm-workspace--current) + :y (/ (- (exwm-workspace--current-height) height) 2))))) (xcb:flush exwm--connection) @@ -200,7 +210,6 @@ corresponding buffer.") (with-current-buffer (exwm--id->buffer id) (run-hooks 'exwm-manage-finish-hook))))) -;;;###autoload (defun exwm-manage--unmanage-window (id &optional withdraw-only) "Unmanage window ID." (let ((buffer (exwm--id->buffer id))) @@ -284,7 +293,6 @@ corresponding buffer.") "Non-nil indicates EXWM is pinging a window.") (defvar exwm-manage-ping-timeout 3 "Seconds to wait before killing a client.") -;;;###autoload (defun exwm-manage--kill-buffer-query-function () "Run in `kill-buffer-query-functions'." (catch 'return @@ -359,7 +367,6 @@ Would you like to kill it? " (defun exwm-manage--kill-client (&optional id) "Kill an X client." - (interactive) (unless id (setq id (exwm--buffer->id (current-buffer)))) (let* ((response (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:ewmh:get-_NET_WM_PID :window id))) @@ -390,8 +397,8 @@ Would you like to kill it? " (setq edges (if exwm--fullscreen (list 0 0 - (frame-pixel-width exwm-workspace--current) - (frame-pixel-height exwm-workspace--current)) + (exwm-workspace--current-width) + (exwm-workspace--current-height)) (window-inside-absolute-pixel-edges (get-buffer-window buffer t)))) (exwm--log "Reply with ConfigureNotify (edges): %s" edges) diff --git a/exwm-randr.el b/exwm-randr.el index 716d5218e2..7f9b443c86 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -48,11 +48,19 @@ (require 'xcb-randr) (require 'exwm-core) -(require 'exwm-layout) -(eval-when-compile (require 'exwm-workspace)) (defvar exwm-randr-workspace-output-plist nil) +(defvar exwm-randr-refresh-hook nil + "Normal hook run when the RandR module just refreshed.") + +(defvar exwm-workspace-minibuffer-position) +(defvar exwm-layout--fullscreen-frame-count) +(defvar exwm-workspace-number) +(defvar exwm-workspace--list) + +(declare-function exwm-layout--set-frame-fullscreen "exwm-layout.el" (frame)) + (defun exwm-randr--refresh () "Refresh workspaces according to the updated RandR info." (let (output-name geometry output-plist default-geometry workareas @@ -89,6 +97,7 @@ (setq workarea-offset (if exwm-workspace-minibuffer-position 0 (window-pixel-height (minibuffer-window)))) + (setq exwm-layout--fullscreen-frame-count 0) (dotimes (i exwm-workspace-number) (let* ((output (plist-get exwm-randr-workspace-output-plist i)) (geometry (lax-plist-get output-plist output)) @@ -98,15 +107,8 @@ output nil)) (set-frame-parameter frame 'exwm-randr-output output) (set-frame-parameter frame 'exwm-geometry geometry) + (exwm-layout--set-frame-fullscreen frame) (with-slots (x y width height) geometry - (exwm-layout--resize-container (frame-parameter frame - 'exwm-outer-id) - (frame-parameter frame - 'exwm-workspace) - x y width height) - (when (and (eq frame exwm-workspace--current) - (exwm-workspace--minibuffer-own-frame-p)) - (exwm-workspace--resize-minibuffer-frame width height)) (setq workareas (nconc workareas (list x y width (- height workarea-offset))) @@ -120,7 +122,8 @@ (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT :window exwm--root :data (vconcat viewports))) - (xcb:flush exwm--connection)))) + (xcb:flush exwm--connection) + (run-hooks 'exwm-randr-refresh-hook)))) (defvar exwm-randr-screen-change-hook nil "Normal hook run when screen changes.") diff --git a/exwm-systemtray.el b/exwm-systemtray.el index c892fcc1a7..11d9be6474 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -33,7 +33,6 @@ (require 'xcb-xembed) (require 'xcb-systemtray) (require 'exwm-core) -(require 'exwm-workspace) (defclass exwm-systemtray--icon () ((width :initarg :width) @@ -161,7 +160,7 @@ You shall use the default value if using auto-hide minibuffer.") :window exwm-systemtray--embedder :value-mask (logior xcb:ConfigWindow:X xcb:ConfigWindow:Width) - :x (- (frame-pixel-width exwm-workspace--current) x) + :x (- (exwm-workspace--current-width) x) :width x)) (when map (xcb:+request exwm-systemtray--connection @@ -173,7 +172,7 @@ You shall use the default value if using auto-hide minibuffer.") (let ((obj (make-instance 'xcb:DestroyNotify))) (xcb:unmarshal obj data) (with-slots (window) obj - (when (alist-get window exwm-systemtray--list) + (when (assoc window exwm-systemtray--list) (exwm-systemtray--unembed window))))) (defun exwm-systemtray--on-ReparentNotify (data _synthetic) @@ -182,7 +181,7 @@ You shall use the default value if using auto-hide minibuffer.") (xcb:unmarshal obj data) (with-slots (window parent) obj (when (and (/= parent exwm-systemtray--embedder) - (alist-get window exwm-systemtray--list)) + (assoc window exwm-systemtray--list)) (exwm-systemtray--unembed window))))) (defun exwm-systemtray--on-ResizeRequest (data _synthetic) @@ -191,7 +190,7 @@ You shall use the default value if using auto-hide minibuffer.") attr) (xcb:unmarshal obj data) (with-slots (window width height) obj - (when (setq attr (alist-get window exwm-systemtray--list)) + (when (setq attr (cdr (assoc window exwm-systemtray--list))) (with-slots ((width* width) (height* height)) attr @@ -220,7 +219,7 @@ You shall use the default value if using auto-hide minibuffer.") (with-slots (window atom state) obj (when (and (eq state xcb:Property:NewValue) (eq atom xcb:Atom:_XEMBED_INFO) - (setq attr (alist-get window exwm-systemtray--list))) + (setq attr (cdr (assoc window exwm-systemtray--list)))) (setq info (xcb:+request-unchecked+reply exwm-systemtray--connection (make-instance 'xcb:xembed:get-_XEMBED_INFO :window window))) @@ -256,20 +255,36 @@ You shall use the default value if using auto-hide minibuffer.") (t (exwm--log "(System Tray) Unknown opcode message: %s" obj))))))) -(defun exwm-systemtray--on-exwm-workspace-switch () - "Reparent the system tray in `exwm-workspace-switch-hook'." - (xcb:+request exwm-systemtray--connection - (make-instance 'xcb:ReparentWindow - :window exwm-systemtray--embedder - :parent (string-to-number - (frame-parameter exwm-workspace--current - 'window-id)) - :x 0 - :y (- (frame-pixel-height exwm-workspace--current) - exwm-systemtray-height))) +(defvar exwm-workspace-minibuffer-position) +(defvar exwm-workspace--current) + +(defun exwm-systemtray--on-workspace-switch () + "Reparent/Refresh the system tray in `exwm-workspace-switch-hook'." + (unless exwm-workspace-minibuffer-position + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:ReparentWindow + :window exwm-systemtray--embedder + :parent (string-to-number + (frame-parameter exwm-workspace--current + 'window-id)) + :x 0 + :y (- (exwm-workspace--current-height) + exwm-systemtray-height)))) + (exwm-systemtray--refresh)) + +(defun exwm-systemtray--on-randr-refresh () + "Reposition/Refresh the system tray in `exwm-randr-refresh-hook'." + (unless exwm-workspace-minibuffer-position + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:ConfigureWindow + :window exwm-systemtray--embedder + :value-mask xcb:ConfigWindow:Y + :y (- (exwm-workspace--current-height) + exwm-systemtray-height)))) (exwm-systemtray--refresh)) (defvar xcb:Atom:_NET_SYSTEM_TRAY_S0) +(defvar exwm-workspace--minibuffer) (defun exwm-systemtray--init () "Initialize system tray module." @@ -335,8 +350,7 @@ You shall use the default value if using auto-hide minibuffer.") (setq parent (string-to-number (frame-parameter exwm-workspace--current 'window-id)) ;; Bottom aligned. - y (- (frame-pixel-height exwm-workspace--current) - exwm-systemtray-height))) + y (- (exwm-workspace--current-height) exwm-systemtray-height))) (xcb:+request exwm-systemtray--connection (make-instance 'xcb:ReparentWindow :window id :parent parent :x 0 :y y)) @@ -356,10 +370,9 @@ You shall use the default value if using auto-hide minibuffer.") #'exwm-systemtray--on-PropertyNotify) (xcb:+event exwm-systemtray--connection 'xcb:ClientMessage #'exwm-systemtray--on-ClientMessage) - ;; Add hook to reparent the embedder. - (unless exwm-workspace-minibuffer-position - (add-hook 'exwm-workspace-switch-hook - #'exwm-systemtray--on-exwm-workspace-switch))) + ;; Add hook to move/reparent the embedder. + (add-hook 'exwm-workspace-switch-hook #'exwm-systemtray--on-workspace-switch) + (add-hook 'exwm-randr-refresh-hook #'exwm-systemtray--on-randr-refresh)) (defun exwm-systemtray-enable () "Enable system tray support for EXWM." diff --git a/exwm-workspace.el b/exwm-workspace.el index e9bab305e3..99e3b5510e 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -62,7 +62,6 @@ (defvar exwm-workspace--switch-history-outdated nil "Non-nil to indicate `exwm-workspace--switch-history' is outdated.") -;;;###autoload (defun exwm-workspace--update-switch-history () "Update the history for switching workspace to reflect the latest status." (when exwm-workspace--switch-history-outdated @@ -108,6 +107,22 @@ Value nil means to use the default position which is fixed at bottom, while (defvar exwm-workspace--display-echo-area-timer nil "Timer for auto-hiding echo area.") +;;;###autoload +(defun exwm-workspace--current-width () + "Return the width of current workspace." + (let ((geometry (frame-parameter exwm-workspace--current 'exwm-geometry))) + (if geometry + (slot-value geometry 'width) + (x-display-pixel-width)))) + +;;;###autoload +(defun exwm-workspace--current-height () + "Return the height of current workspace." + (let ((geometry (frame-parameter exwm-workspace--current 'exwm-geometry))) + (if geometry + (slot-value geometry 'height) + (x-display-pixel-height)))) + ;;;###autoload (defun exwm-workspace--minibuffer-own-frame-p () "Reports whether the minibuffer is displayed in its own frame." @@ -122,9 +137,9 @@ workspace frame." (cl-assert (exwm-workspace--minibuffer-own-frame-p)) (let ((y (if (eq exwm-workspace-minibuffer-position 'top) 0 - (- (or height (frame-pixel-height exwm-workspace--current)) + (- (or height (exwm-workspace--current-height)) (frame-pixel-height exwm-workspace--minibuffer)))) - (width (or width (frame-pixel-width exwm-workspace--current))) + (width (or width (exwm-workspace--current-width))) (container (frame-parameter exwm-workspace--minibuffer 'exwm-container))) (xcb:+request exwm--connection @@ -206,6 +221,8 @@ The optional FORCE option is for internal use only." (xcb:flush exwm--connection)) (run-hooks 'exwm-workspace-switch-hook)))) +(declare-function exwm-layout--hide "exwm-layout.el" (id)) + ;;;###autoload (defun exwm-workspace-move-window (index &optional id) "Move window ID to workspace INDEX." @@ -266,6 +283,7 @@ The optional FORCE option is for internal use only." (exwm--id->buffer id))))) (setq exwm-workspace--switch-history-outdated t))) +;;;###autoload (defun exwm-workspace-switch-to-buffer () "Make the current Emacs window display another buffer." (interactive) @@ -348,10 +366,10 @@ The optional FORCE option is for internal use only." window) (when (and (floatp max-mini-window-height) (> height (* max-mini-window-height - (frame-pixel-height exwm-workspace--current)))) + (exwm-workspace--current-height)))) (setq height (floor (* max-mini-window-height - (frame-pixel-height exwm-workspace--current)))) + (exwm-workspace--current-height)))) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window window @@ -361,7 +379,7 @@ The optional FORCE option is for internal use only." (setq value-mask xcb:ConfigWindow:Height y 0) (setq value-mask (logior xcb:ConfigWindow:Y xcb:ConfigWindow:Height) - y (- (frame-pixel-height exwm-workspace--current) height))) + y (- (exwm-workspace--current-height) height))) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window (frame-parameter exwm-workspace--minibuffer @@ -457,6 +475,8 @@ This functions is modified from `display-buffer-reuse-window' and (cancel-timer exwm-workspace--display-echo-area-timer) (setq exwm-workspace--display-echo-area-timer nil))) +(declare-function exwm-manage--unmanage-window "exwm-manage.el") + (defun exwm-workspace--confirm-kill-emacs (prompt) "Confirm before exiting Emacs." (when (pcase (length exwm--id-buffer-alist) @@ -611,13 +631,17 @@ This functions is modified from `display-buffer-reuse-window' and ;; Switch to the first workspace (exwm-workspace-switch 0 t)) +(defvar exwm-layout--fullscreen-frame-count) + (defun exwm-workspace--post-init () "The second stage in the initialization of the workspace module." - ;; Delay making the workspaces fullscreen until Emacs becomes idle - (run-with-idle-timer 0 nil - (lambda () - (dolist (i exwm-workspace--list) - (set-frame-parameter i 'fullscreen 'fullboth))))) + ;; Make the workspaces fullscreen. + (dolist (i exwm-workspace--list) + (set-frame-parameter i 'fullscreen 'fullboth)) + ;; Wait until all workspace frames are resized. + (with-timeout (1) + (while (< exwm-layout--fullscreen-frame-count exwm-workspace-number) + (accept-process-output nil 0.1)))) diff --git a/exwm.el b/exwm.el index 85c905e6ef..b425acf7b5 100644 --- a/exwm.el +++ b/exwm.el @@ -30,11 +30,12 @@ ;; -------- ;; EXWM (Emacs X Window Manager) is a full-featured tiling X window manager for ;; Emacs built on top of XELB. It features: -;; + Fully keyboard-driven operation +;; + Fully keyboard-driven operations ;; + Hybrid layout modes (tiling & stacking) ;; + Workspace support ;; + ICCCM/EWMH compliance -;; + Basic RandR support (optional) +;; ++ (Optional) RandR (multi-monitor) support +;; ++ (Optional) system tray ;; Installation & configuration ;; ---------------------------- @@ -70,6 +71,7 @@ (require 'exwm-manage) (require 'exwm-input) +;;;###autoload (defun exwm-reset () "Reset window to standard state: non-fullscreen, line-mode." (interactive) @@ -80,7 +82,6 @@ (exwm-layout--refresh) (exwm-input-grab-keyboard)))) -;;;###autoload (defun exwm--update-window-type (id &optional force) "Update _NET_WM_WINDOW_TYPE." (with-current-buffer (exwm--id->buffer id) @@ -94,7 +95,6 @@ (defvar exwm-update-class-hook nil "Normal hook run when window class is updated.") -;;;###autoload (defun exwm--update-class (id &optional force) "Update WM_CLASS." (with-current-buffer (exwm--id->buffer id) @@ -110,7 +110,6 @@ (defvar exwm-update-title-hook nil "Normal hook run when window title is updated.") -;;;###autoload (defun exwm--update-utf8-title (id &optional force) "Update _NET_WM_NAME." (with-current-buffer (exwm--id->buffer id) @@ -123,7 +122,6 @@ (setq exwm--title-is-utf8 t) (run-hooks 'exwm-update-title-hook))))))) -;;;###autoload (defun exwm--update-ctext-title (id &optional force) "Update WM_NAME." (with-current-buffer (exwm--id->buffer id) @@ -136,13 +134,11 @@ (when exwm-title (run-hooks 'exwm-update-title-hook))))))) -;;;###autoload (defun exwm--update-title (id) "Update _NET_WM_NAME or WM_NAME." (exwm--update-utf8-title id) (exwm--update-ctext-title id)) -;;;###autoload (defun exwm--update-transient-for (id &optional force) "Update WM_TRANSIENT_FOR." (with-current-buffer (exwm--id->buffer id) @@ -153,7 +149,6 @@ (when reply ;nil when destroyed (setq exwm-transient-for (slot-value reply 'value))))))) -;;;###autoload (defun exwm--update-normal-hints (id &optional force) "Update WM_NORMAL_HINTS." (with-current-buffer (exwm--id->buffer id) @@ -201,7 +196,6 @@ (= exwm--normal-hints-min-height exwm--normal-hints-max-height))))))))) -;;;###autoload (defun exwm--update-hints (id &optional force) "Update WM_HINTS." (with-current-buffer (exwm--id->buffer id) @@ -221,7 +215,6 @@ (set-frame-parameter exwm--frame 'exwm--urgency t) (setq exwm-workspace--switch-history-outdated t)))))))) -;;;###autoload (defun exwm--update-protocols (id &optional force) "Update WM_PROTOCOLS." (with-current-buffer (exwm--id->buffer id) @@ -232,7 +225,6 @@ (when reply ;nil when destroyed (setq exwm--protocols (append (slot-value reply 'value) nil))))))) -;;;###autoload (defun exwm--update-state (id &optional force) "Update WM_STATE." (with-current-buffer (exwm--id->buffer id) -- cgit 1.4.1 From 08bf970b16405d4f6b48559e517ab61339a956bd Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 19 Feb 2016 20:18:29 +0800 Subject: Minor fixes for system tray * exwm-systemtray.el (exwm-systemtray--embed): Default to visible if the XEMBED_MAPPED flag is not set. (exwm-systemtray--on-ClientMessage): Only embed new icons. Ignore balloon messages. --- exwm-systemtray.el | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/exwm-systemtray.el b/exwm-systemtray.el index 11d9be6474..e9a9745316 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -101,8 +101,13 @@ You shall use the default value if using auto-hide minibuffer.") :value-mask xcb:CW:EventMask :event-mask (logior xcb:EventMask:ResizeRedirect xcb:EventMask:PropertyChange))) - (when (setq visible - (/= 0 (logand (slot-value info 'flags) xcb:xembed:MAPPED))) + (setq visible (slot-value info 'flags)) + (if visible + (setq visible + (/= 0 (logand (slot-value info 'flags) xcb:xembed:MAPPED))) + ;; Default to visible. + (setq visible t)) + (when visible (exwm--log "(System Tray) Map the window") (xcb:+request exwm-systemtray--connection (make-instance 'xcb:MapWindow :window icon))) @@ -245,13 +250,11 @@ You shall use the default value if using auto-hide minibuffer.") (setq data32 (slot-value data 'data32) opcode (elt data32 1)) (cond ((= opcode xcb:systemtray:opcode:REQUEST-DOCK) - (exwm-systemtray--embed (elt data32 2))) - ((= opcode xcb:systemtray:opcode:BEGIN-MESSAGE) - ;; FIXME - ) - ((= opcode xcb:systemtray:opcode:CANCEL-MESSAGE) - ;; FIXME - ) + (unless (assoc (elt data32 2) exwm-systemtray--list) + (exwm-systemtray--embed (elt data32 2)))) + ;; Not implemented (rarely used nowadays). + ((or (= opcode xcb:systemtray:opcode:BEGIN-MESSAGE) + (= opcode xcb:systemtray:opcode:CANCEL-MESSAGE))) (t (exwm--log "(System Tray) Unknown opcode message: %s" obj))))))) -- cgit 1.4.1 From 33254c37df052996d63ff2a55efeb2b6e8799694 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 20 Feb 2016 10:02:11 +0800 Subject: Redefine mode-specific keys * exwm-core.el (exwm-mode-map): Redefine mode-specific keys to comply with the key binding conventions. --- exwm-core.el | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 4d936ed752..5377c6766f 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -120,12 +120,12 @@ (defvar exwm-mode-map (let ((map (make-sparse-keymap))) - (define-key map "\C-ck" #'exwm-input-release-keyboard) - (define-key map "\C-cf" #'exwm-layout-set-fullscreen) - (define-key map "\C-cm" #'exwm-floating-toggle-floating) - (define-key map "\C-cq" #'exwm-input-send-next-key) - (define-key map "\C-cv" #'exwm-workspace-move-window) - (define-key map "\C-cM" #'exwm-layout-toggle-mode-line) + (define-key map "\C-c\C-f" #'exwm-layout-set-fullscreen) + (define-key map "\C-c\C-k" #'exwm-input-release-keyboard) + (define-key map "\C-c\C-m" #'exwm-workspace-move-window) + (define-key map "\C-c\C-q" #'exwm-input-send-next-key) + (define-key map "\C-c\C-t\C-f" #'exwm-floating-toggle-floating) + (define-key map "\C-c\C-t\C-m" #'exwm-layout-toggle-mode-line) map) "Keymap for `exwm-mode'.") -- cgit 1.4.1 From 1c79e1c2384128915357ea629fc2a0503bd36733 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 20 Feb 2016 14:52:53 +0800 Subject: Prevent/Reduce flickering issues with floating X windows * exwm-floating.el (exwm-floating--set-floating) (exwm-floating--unset-floating): Prevent flickering when creating/removing a floating X window. * exwm-layout.el (exwm-layout--show): Show X windows after resizing to prevent flickering. * exwm-manage.el (exwm-manage--unmanage-window): Reduce flickering by hiding the container. (exwm-manage--kill-buffer-query-function): Prevent flickering by hiding the container (except that the X window destroys itself after receiving the WM_DELETE_WINDOW client message). --- exwm-floating.el | 35 ++++++++++++++++++++++++++++++----- exwm-layout.el | 19 ++++++++++--------- exwm-manage.el | 53 ++++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 78 insertions(+), 29 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 209539eb4a..276948801d 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -193,17 +193,42 @@ (remove-hook 'window-configuration-change-hook #'exwm-layout--refresh) (set-window-buffer window (current-buffer)) ;this changes current buffer (add-hook 'window-configuration-change-hook #'exwm-layout--refresh) - (set-window-dedicated-p window t) - (exwm-layout--show id window)) - (select-frame-set-input-focus frame)) - (run-hooks 'exwm-floating-setup-hook)) + (set-window-dedicated-p window t)) + (select-frame-set-input-focus frame) + ;; `x_make_frame_visible' autoraises the frame. Force lowering it. + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window outer-id + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Below)) + ;; Show the X window with its container (and flush). + (exwm-layout--show id window)) + (run-hooks 'exwm-floating-setup-hook) + ;; Redraw the frame. + (redisplay)) (defun exwm-floating--unset-floating (id) "Make window ID non-floating." (let ((buffer (exwm--id->buffer id))) (with-current-buffer buffer - ;; Reparent the frame back to the root window. (when exwm--floating-frame + ;; The X window is already mapped. + ;; Unmap the container to prevent flickering. + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow :window exwm--container)) + (xcb:flush exwm--connection) + ;; Unmap the X window. + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window id :value-mask xcb:CW:EventMask + :event-mask xcb:EventMask:NoEvent)) + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow :window id)) + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window id :value-mask xcb:CW:EventMask + :event-mask exwm--client-event-mask)) + ;; Reparent the floating frame back to the root window. (let ((frame-id (frame-parameter exwm--floating-frame 'exwm-outer-id))) (xcb:+request exwm--connection (make-instance 'xcb:UnmapWindow :window frame-id)) diff --git a/exwm-layout.el b/exwm-layout.el index c0f3c61472..e3c1febc44 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -53,14 +53,6 @@ (defun exwm-layout--show (id &optional window) "Show window ID exactly fit in the Emacs window WINDOW." (exwm--log "Show #x%x in %s" id window) - (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window id)) - (with-current-buffer (exwm--id->buffer id) - (xcb:+request exwm--connection - (make-instance 'xcb:MapWindow :window exwm--container))) - (xcb:+request exwm--connection - (make-instance 'xcb:icccm:set-WM_STATE - :window id :state xcb:icccm:WM_STATE:NormalState - :icon xcb:Window:None)) (let* ((edges (window-inside-absolute-pixel-edges window)) (width (- (elt edges 2) (elt edges 0))) (height (- (elt edges 3) (elt edges 1)))) @@ -93,7 +85,16 @@ (elt relative-edges 0) (elt relative-edges 1) width height - (active-minibuffer-window))))) + (active-minibuffer-window)))) + ;; Make the resizing take effect. + (xcb:flush exwm--connection) + (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window id)) + (xcb:+request exwm--connection + (make-instance 'xcb:MapWindow :window exwm--container)) + (xcb:+request exwm--connection + (make-instance 'xcb:icccm:set-WM_STATE + :window id :state xcb:icccm:WM_STATE:NormalState + :icon xcb:Window:None))) (xcb:+request exwm--connection (make-instance 'xcb:SendEvent :propagate 0 :destination id diff --git a/exwm-manage.el b/exwm-manage.el index 224ee16aa8..c9c6ff9d2c 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -213,14 +213,18 @@ corresponding buffer.") (defun exwm-manage--unmanage-window (id &optional withdraw-only) "Unmanage window ID." (let ((buffer (exwm--id->buffer id))) - (exwm--log "Unmanage #x%x (buffer: %s)" id buffer) + (exwm--log "Unmanage #x%x (buffer: %s, widthdraw: %s)" + id buffer withdraw-only) (setq exwm--id-buffer-alist (assq-delete-all id exwm--id-buffer-alist)) (when (buffer-live-p buffer) (with-current-buffer buffer - ;; Hide the floating frame as early as possible. - (when exwm--floating-frame (make-frame-invisible exwm--floating-frame)) + ;; Flickering seems unavoidable here if the DestroyWindow request is + ;; not initiated by us. + ;; What we can do is to hide the its container ASAP. (xcb:+request exwm--connection (make-instance 'xcb:UnmapWindow :window exwm--container)) + (xcb:flush exwm--connection) + ;; (setq exwm-workspace--switch-history-outdated t) ;; (when withdraw-only @@ -254,12 +258,13 @@ corresponding buffer.") :window id :property xcb:Atom:WM_STATE))) ;; Destroy the container (it seems it has to be delayed). (when exwm--floating-frame - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window (frame-parameter exwm--floating-frame - 'exwm-outer-id) - :parent exwm--root - :x 0 :y 0))) + ;; Unmap the floating frame. + (let ((window (frame-parameter exwm--floating-frame 'exwm-outer-id))) + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow :window window)) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window window :parent exwm--root :x 0 :y 0)))) (xcb:+request exwm--connection (make-instance 'xcb:DestroyWindow :window exwm--container)) (xcb:flush exwm--connection) @@ -268,12 +273,12 @@ corresponding buffer.") (kill-buffer) (when floating (select-window - (frame-selected-window exwm-workspace--current)))))) - (xcb:+request exwm--connection ;update _NET_CLIENT_LIST - (make-instance 'xcb:ewmh:set-_NET_CLIENT_LIST - :window exwm--root - :data (vconcat (mapcar #'car exwm--id-buffer-alist)))) - (xcb:flush exwm--connection))) + (frame-selected-window exwm-workspace--current))))) + (xcb:+request exwm--connection ;update _NET_CLIENT_LIST + (make-instance 'xcb:ewmh:set-_NET_CLIENT_LIST + :window exwm--root + :data (vconcat (mapcar #'car exwm--id-buffer-alist)))) + (xcb:flush exwm--connection)))) (defun exwm-manage--scan () "Search for existing windows and try to manage them." @@ -300,6 +305,10 @@ corresponding buffer.") (make-instance 'xcb:MapWindow :window exwm--id)) ;; The X window is no longer alive so just close the buffer. ;; Destroy the container. + ;; Hide the container to prevent flickering. + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow :window exwm--container)) + (xcb:flush exwm--connection) (when exwm--floating-frame (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow @@ -313,6 +322,9 @@ corresponding buffer.") (throw 'return t)) (unless (memq xcb:Atom:WM_DELETE_WINDOW exwm--protocols) ;; The X window does not support WM_DELETE_WINDOW; destroy it. + ;; Hide the container to prevent flickering. + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow :window exwm--container)) (xcb:+request exwm--connection (make-instance 'xcb:DestroyWindow :window exwm--id)) (xcb:flush exwm--connection) @@ -331,6 +343,10 @@ corresponding buffer.") (unless (memq xcb:Atom:_NET_WM_PING exwm--protocols) ;; The window does not support _NET_WM_PING. To make sure it'll die, ;; kill it after the time runs out. + ;; Hide the container to prevent flickering. + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow :window exwm--container)) + (xcb:flush exwm--connection) (run-with-timer exwm-manage-ping-timeout nil `(lambda () (exwm-manage--kill-client ,exwm--id))) ;; Wait for DestroyNotify event. @@ -368,6 +384,13 @@ Would you like to kill it? " (defun exwm-manage--kill-client (&optional id) "Kill an X client." (unless id (setq id (exwm--buffer->id (current-buffer)))) + ;; Hide the container to prevent flickering. + (let ((buffer (exwm--id->buffer id))) + (when buffer + (with-current-buffer buffer + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow :window exwm--container)) + (xcb:flush exwm--connection)))) (let* ((response (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:ewmh:get-_NET_WM_PID :window id))) (pid (and response (slot-value response 'value))) -- cgit 1.4.1 From e3d33a4aad6b5748e9352501b2c6cb058379025f Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 20 Feb 2016 21:52:07 +0800 Subject: Prevent Emacs frames from restacking themselves Putting Emacs frames (workspace frames, floating frames) into dedicated containers greatly simplifies the stacking order management and totally fixes relevant issues. * exwm-floating.el (exwm-floating--set-floating): Create floating frame container. Remove redundant stacking order modification code. (exwm-floating--unset-floating): Destroy the floating frame container. No need to reparent the X window container. (exwm-floating--do-moveresize): Resize the floating frame container. * exwm-input.el (exwm-input--update-focus): No need to restack frames. * exwm-layout.el (exwm-layout--show, exwm-layout--set-frame-fullscreen) (exwm-layout-enlarge-window): Resize the floating frame container. * exwm-manage.el (exwm-manage--on-ConfigureRequest): Re-enable stacking order modification on ConfigureRequest. * exwm-workspace.el (exwm-workspace--confirm-kill-emacs): Reparent out all frames on exit. No need to remove selected events or created resources. (exwm-workspace--init): Create workspace frame containers. * exwm-layout.el (exwm-layout-set-fullscreen): * exwm-manage.el (exwm-manage--unmanage-window): Remove a redundant call to `xcb:flush'. * exwm-manage.el (exwm-manage--unmanage-window): Force unmap the X window. Unmap the floating frame before reparent it. --- exwm-floating.el | 109 ++++++++++++++++++++++++++++++++++-------------------- exwm-input.el | 14 ++----- exwm-layout.el | 88 ++++++++++++++++++++++++++----------------- exwm-manage.el | 42 ++++++++++++--------- exwm-workspace.el | 60 ++++++++++++++++++++---------- 5 files changed, 192 insertions(+), 121 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 276948801d..1ec54d10fa 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -90,6 +90,7 @@ (outer-id (string-to-number (frame-parameter frame 'outer-window-id))) (container (with-current-buffer (exwm--id->buffer id) exwm--container)) + (frame-container (xcb:generate-id exwm--connection)) (window (frame-first-window frame)) ;and it's the only window (x (slot-value exwm--geometry 'x)) (y (slot-value exwm--geometry 'y)) @@ -103,8 +104,9 @@ y (- y (slot-value frame-geometry 'y)))) (exwm--log "Floating geometry (original, relative): %dx%d%+d%+d" width height x y) - ;; Save window IDs + ;; Save frame parameters. (set-frame-parameter frame 'exwm-outer-id outer-id) + (set-frame-parameter frame 'exwm-container frame-container) ;; Set urgency flag if it's not appear in the active workspace (let ((idx (cl-position original-frame exwm-workspace--list))) (when (/= idx exwm-workspace-current-index) @@ -163,18 +165,43 @@ ;; timely. ;; The frame will be made visible by `select-frame-set-input-focus'. (make-frame-invisible frame) - (let ((edges (window-inside-pixel-edges window))) - (set-frame-size frame - (+ width (- (frame-pixel-width frame) - (- (elt edges 2) (elt edges 0)))) - (+ height (- (frame-pixel-height frame) - (- (elt edges 3) (elt edges 1)))) - t)) - ;; Reparent this frame to the container + (let* ((edges (window-inside-pixel-edges window)) + (frame-width (+ width (- (frame-pixel-width frame) + (- (elt edges 2) (elt edges 0))))) + (frame-height (+ height (- (frame-pixel-height frame) + (- (elt edges 3) (elt edges 1)))))) + (set-frame-size frame frame-width frame-height t) + ;; Create the frame container as the parent of the frame and + ;; a child of the X window container. + (xcb:+request exwm--connection + (make-instance 'xcb:CreateWindow + :depth 0 :wid frame-container + :parent container + :x 0 :y 0 :width width :height height :border-width 0 + :class xcb:WindowClass:CopyFromParent + :visual 0 ;CopyFromParent + :value-mask xcb:CW:OverrideRedirect + :override-redirect 1)) + ;; Put it at bottom. + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window frame-container + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Below)) + ;; Map it. + (xcb:+request exwm--connection + (make-instance 'xcb:MapWindow :window frame-container)) + (exwm--debug + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_NAME + :window frame-container + :data + (format "floating frame container for 0x%x" id))))) + ;; Reparent this frame to its container. (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow - :window outer-id :parent container :x 0 :y 0)) - ;; Place the container + :window outer-id :parent frame-container :x 0 :y 0)) + ;; Place the X window container. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window container @@ -193,16 +220,9 @@ (remove-hook 'window-configuration-change-hook #'exwm-layout--refresh) (set-window-buffer window (current-buffer)) ;this changes current buffer (add-hook 'window-configuration-change-hook #'exwm-layout--refresh) - (set-window-dedicated-p window t)) - (select-frame-set-input-focus frame) - ;; `x_make_frame_visible' autoraises the frame. Force lowering it. - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window outer-id - :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:Below)) - ;; Show the X window with its container (and flush). - (exwm-layout--show id window)) + (set-window-dedicated-p window t) + (exwm-layout--show id window)) + (select-frame-set-input-focus frame)) (run-hooks 'exwm-floating-setup-hook) ;; Redraw the frame. (redisplay)) @@ -229,29 +249,28 @@ :window id :value-mask xcb:CW:EventMask :event-mask exwm--client-event-mask)) ;; Reparent the floating frame back to the root window. - (let ((frame-id (frame-parameter exwm--floating-frame 'exwm-outer-id))) + (let ((frame-id (frame-parameter exwm--floating-frame 'exwm-outer-id)) + (frame-container (frame-parameter exwm--floating-frame + 'exwm-container))) (xcb:+request exwm--connection (make-instance 'xcb:UnmapWindow :window frame-id)) (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow :window frame-id :parent exwm--root - :x 0 :y 0)))) - ;; Reparent the container to the workspace - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window exwm--container - :parent (frame-parameter exwm-workspace--current - 'exwm-workspace) - :x 0 :y 0)) ;temporary position - ;; Put the container just above the Emacs frame + :x 0 :y 0)) + ;; Also destroy its container. + (xcb:+request exwm--connection + (make-instance 'xcb:DestroyWindow :window frame-container)))) + ;; Put the X window container just above the Emacs frame container + ;; (the stacking order won't change from now on). (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window exwm--container :value-mask (logior xcb:ConfigWindow:Sibling xcb:ConfigWindow:StackMode) :sibling (frame-parameter exwm-workspace--current - 'exwm-outer-id) + 'exwm-container) :stack-mode xcb:StackMode:Above))) (xcb:flush exwm--connection) (with-current-buffer buffer @@ -466,13 +485,19 @@ (geometry (frame-parameter exwm-workspace--current 'exwm-geometry)) (frame-x 0) (frame-y 0) - result) + result value-mask width height) (when geometry (setq frame-x (slot-value geometry 'x) frame-y (slot-value geometry 'y))) (xcb:unmarshal obj data) (setq result (funcall exwm-floating--moveresize-calculate - (slot-value obj 'root-x) (slot-value obj 'root-y))) + (slot-value obj 'root-x) (slot-value obj 'root-y)) + value-mask (logand (aref result 1) + (eval-when-compile + (logior xcb:ConfigWindow:Width + xcb:ConfigWindow:Height))) + width (aref result 4) + height (aref result 5)) (with-current-buffer (aref result 0) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow @@ -483,16 +508,20 @@ xcb:ConfigWindow:Y))) :x (- (aref result 2) frame-x) :y (- (aref result 3) frame-y))) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter exwm--floating-frame + 'exwm-container) + :value-mask value-mask + :width width + :height height)) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window (frame-parameter exwm--floating-frame 'exwm-outer-id) - :value-mask - (logand (aref result 1) - (eval-when-compile - (logior xcb:ConfigWindow:Width - xcb:ConfigWindow:Height))) - :width (aref result 4) :height (aref result 5)))) + :value-mask value-mask + :width width + :height height))) (xcb:flush exwm--connection)))) (defun exwm-floating-move (&optional delta-x delta-y) diff --git a/exwm-input.el b/exwm-input.el index 5e078030c2..0a50bef262 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -113,8 +113,8 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (exwm-workspace-switch exwm-workspace-current-index t)) (exwm--log "Set focus on #x%x" exwm--id) (exwm-input--set-focus exwm--id) - ;; Adjust stacking orders (when exwm--floating-frame + ;; Adjust stacking orders of the floating container. (if (exwm-workspace--minibuffer-own-frame-p) ;; Put this floating X window just below the minibuffer. (xcb:+request exwm--connection @@ -132,16 +132,8 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (make-instance 'xcb:ConfigureWindow :window exwm--container :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:Above)))) - ;; Make sure Emacs frames are at bottom. - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window (frame-parameter - (or exwm--floating-frame exwm--frame) - 'exwm-outer-id) - :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:BottomIf)) - (xcb:flush exwm--connection)) + :stack-mode xcb:StackMode:Above))) + (xcb:flush exwm--connection))) (when (eq (selected-window) exwm-input--focus-window) (exwm--log "Focus on %s" exwm-input--focus-window) (select-frame-set-input-focus (window-frame exwm-input--focus-window) diff --git a/exwm-layout.el b/exwm-layout.el index e3c1febc44..c9146de371 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -55,37 +55,46 @@ (exwm--log "Show #x%x in %s" id window) (let* ((edges (window-inside-absolute-pixel-edges window)) (width (- (elt edges 2) (elt edges 0))) - (height (- (elt edges 3) (elt edges 1)))) + (height (- (elt edges 3) (elt edges 1))) + frame-width frame-height) (with-current-buffer (exwm--id->buffer id) - (if exwm--floating-frame - ;; A floating X window is of the same size as the Emacs window, - ;; whereas its container is of the same size as the Emacs frame. - (progn - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window exwm--container - :value-mask (logior xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) - :width (frame-pixel-width exwm--floating-frame) - :height (frame-pixel-height - exwm--floating-frame))) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window exwm--id - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) - :x exwm-floating-border-width - :y exwm-floating-border-width - :width width - :height height))) - (let ((relative-edges (window-inside-pixel-edges window))) - (exwm-layout--resize-container id exwm--container - (elt relative-edges 0) - (elt relative-edges 1) - width height - (active-minibuffer-window)))) + (if (not exwm--floating-frame) + (let ((relative-edges (window-inside-pixel-edges window))) + (exwm-layout--resize-container id exwm--container + (elt relative-edges 0) + (elt relative-edges 1) + width height + (active-minibuffer-window))) + ;; A floating X window is of the same size as the Emacs window, + ;; whereas its container is of the same size as the Emacs frame. + (setq frame-width (frame-pixel-width exwm--floating-frame) + frame-height (frame-pixel-height exwm--floating-frame)) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window exwm--container + :value-mask (logior xcb:ConfigWindow:Width + xcb:ConfigWindow:Height) + :width frame-width + :height frame-height)) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter exwm--floating-frame + 'exwm-container) + :value-mask (logior xcb:ConfigWindow:Width + xcb:ConfigWindow:Height) + :width frame-width + :height frame-height)) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window exwm--id + :value-mask (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height) + :x exwm-floating-border-width + :y exwm-floating-border-width + :width width + :height height))) ;; Make the resizing take effect. (xcb:flush exwm--connection) (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window id)) @@ -151,8 +160,7 @@ (make-instance 'xcb:GetGeometry :drawable exwm--container)))) (setq exwm--floating-frame-position - (vector (slot-value geometry 'x) (slot-value geometry 'y)))) - (xcb:flush exwm--connection)) + (vector (slot-value geometry 'x) (slot-value geometry 'y))))) (exwm-layout--resize-container exwm--id exwm--container 0 0 (exwm-workspace--current-width) (exwm-workspace--current-height)) @@ -205,12 +213,14 @@ :width (x-display-pixel-width) :height (x-display-pixel-height)))) (id (frame-parameter frame 'exwm-outer-id)) + (container (frame-parameter frame 'exwm-container)) (workspace (frame-parameter frame 'exwm-workspace))) (with-slots (x y width height) geometry (when (and (eq frame exwm-workspace--current) (exwm-workspace--minibuffer-own-frame-p)) (exwm-workspace--resize-minibuffer-frame width height)) - (exwm-layout--resize-container id workspace x y width height) + (exwm-layout--resize-container id container 0 0 width height) + (exwm-layout--resize-container nil workspace x y width height t) (xcb:flush exwm--connection))) (cl-incf exwm-layout--fullscreen-frame-count)) @@ -349,6 +359,12 @@ windows." 'exwm-outer-id) :value-mask xcb:ConfigWindow:Width :width width)) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter exwm--floating-frame + 'exwm-container) + :value-mask xcb:ConfigWindow:Width + :width width)) (xcb:flush exwm--connection)))) (t (let* ((height (frame-pixel-height)) @@ -375,6 +391,12 @@ windows." 'exwm-outer-id) :value-mask xcb:ConfigWindow:Height :height height)) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter exwm--floating-frame + 'exwm-container) + :value-mask xcb:ConfigWindow:Height + :height height)) (xcb:flush exwm--connection)))))) ;;;###autoload diff --git a/exwm-manage.el b/exwm-manage.el index c9c6ff9d2c..fd1d64bafd 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -224,6 +224,9 @@ corresponding buffer.") (xcb:+request exwm--connection (make-instance 'xcb:UnmapWindow :window exwm--container)) (xcb:flush exwm--connection) + ;; Unmap the X window. + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow :window id)) ;; (setq exwm-workspace--switch-history-outdated t) ;; @@ -256,18 +259,17 @@ corresponding buffer.") (xcb:+request exwm--connection (make-instance 'xcb:DeleteProperty :window id :property xcb:Atom:WM_STATE))) - ;; Destroy the container (it seems it has to be delayed). (when exwm--floating-frame - ;; Unmap the floating frame. + ;; Unmap the floating frame before destroying the containers. (let ((window (frame-parameter exwm--floating-frame 'exwm-outer-id))) (xcb:+request exwm--connection (make-instance 'xcb:UnmapWindow :window window)) (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow :window window :parent exwm--root :x 0 :y 0)))) + ;; Destroy the X window container (and the frame container if any). (xcb:+request exwm--connection (make-instance 'xcb:DestroyWindow :window exwm--container)) - (xcb:flush exwm--connection) (let ((kill-buffer-query-functions nil) (floating exwm--floating-frame)) (kill-buffer) @@ -310,12 +312,14 @@ corresponding buffer.") (make-instance 'xcb:UnmapWindow :window exwm--container)) (xcb:flush exwm--connection) (when exwm--floating-frame - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window (frame-parameter exwm--floating-frame - 'exwm-outer-id) - :parent exwm--root - :x 0 :y 0))) + (let ((window (frame-parameter exwm--floating-frame 'exwm-outer-id))) + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow :window window)) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window window + :parent exwm--root + :x 0 :y 0)))) (xcb:+request exwm--connection (make-instance 'xcb:DestroyWindow :window exwm--container)) (xcb:flush exwm--connection) @@ -410,10 +414,13 @@ Would you like to kill it? " (let ((obj (make-instance 'xcb:ConfigureRequest)) buffer edges) (xcb:unmarshal obj data) - (with-slots (window x y width height border-width value-mask) + (with-slots (window x y width height + border-width sibling stack-mode value-mask) obj - (exwm--log "ConfigureRequest from #x%x (#x%x) @%dx%d%+d%+d, border: %d" - window value-mask width height x y border-width) + (exwm--log "ConfigureRequest from #x%x (#x%x) @%dx%d%+d%+d; \ +border-width: %d; sibling: #x%x; stack-mode: %d" + window value-mask width height x y + border-width sibling stack-mode) (if (setq buffer (exwm--id->buffer window)) ;; Send client message for managed windows (with-current-buffer buffer @@ -440,16 +447,15 @@ Would you like to kill it? " :border-width 0 :override-redirect 0) exwm--connection)))) (exwm--log "ConfigureWindow (preserve geometry)") - ;; Configure the unmanaged window without changing the stacking order. + ;; Configure the unmanaged window. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window window - :value-mask - (logand value-mask - (lognot xcb:ConfigWindow:Sibling) - (lognot xcb:ConfigWindow:StackMode)) + :value-mask value-mask :x x :y y :width width :height height - :border-width border-width))))) + :border-width border-width + :sibling sibling + :stack-mode stack-mode))))) (xcb:flush exwm--connection)) (defun exwm-manage--on-MapRequest (data _synthetic) diff --git a/exwm-workspace.el b/exwm-workspace.el index 99e3b5510e..42c081820e 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -252,7 +252,7 @@ The optional FORCE option is for internal use only." (concat " " name))))) (setq exwm--frame frame) (if exwm--floating-frame - ;; Move the floating frame is enough + ;; Move the floating container. (progn (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow @@ -261,7 +261,7 @@ The optional FORCE option is for internal use only." (frame-parameter frame 'exwm-workspace) :x 0 :y 0)) (xcb:flush exwm--connection)) - ;; Move the window itself + ;; Move the X window container. (if (/= index exwm-workspace-current-index) (bury-buffer) (set-window-buffer (get-buffer-window (current-buffer) t) @@ -483,28 +483,30 @@ This functions is modified from `display-buffer-reuse-window' and (0 (y-or-n-p prompt)) (x (yes-or-no-p (format "[EXWM] %d window%s currently alive. %s" x (if (= x 1) "" "s") prompt)))) - ;; Remove SubstructureRedirect event. - (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window exwm--root :value-mask xcb:CW:EventMask - :event-mask 0)) - ;; Remove the _NET_SUPPORTING_WM_CHECK X window. - (with-slots (value) - (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:ewmh:get-_NET_SUPPORTING_WM_CHECK - :window exwm--root)) - (xcb:+request exwm--connection - (make-instance 'xcb:DeleteProperty - :window exwm--root - :property xcb:Atom:_NET_SUPPORTING_WM_CHECK)) - (xcb:+request exwm--connection - (make-instance 'xcb:DestroyWindow :window value))) ;; Unmanage all X windows. (dolist (i exwm--id-buffer-alist) (exwm-manage--unmanage-window (car i) t) (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window (car i)))) + ;; Reparent out the minibuffer frame. + (when exwm-workspace-minibuffer-position + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window (frame-parameter exwm-workspace--minibuffer + 'exwm-outer-id) + :parent exwm--root + :x 0 + :y 0))) + ;; Reparent out all workspace frames. + (dolist (f exwm-workspace--list) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window (frame-parameter f 'exwm-outer-id) + :parent exwm--root + :x 0 + :y 0))) (xcb:flush exwm--connection) + ;; Destroy all resources created by this connection. (xcb:disconnect exwm--connection) t)) @@ -589,9 +591,11 @@ This functions is modified from `display-buffer-reuse-window' and ;; Configure workspaces (dolist (i exwm-workspace--list) (let ((outer-id (string-to-number (frame-parameter i 'outer-window-id))) + (container (xcb:generate-id exwm--connection)) (workspace (xcb:generate-id exwm--connection))) ;; Save window IDs (set-frame-parameter i 'exwm-outer-id outer-id) + (set-frame-parameter i 'exwm-container container) (set-frame-parameter i 'exwm-workspace workspace) (xcb:+request exwm--connection (make-instance 'xcb:CreateWindow @@ -605,16 +609,34 @@ This functions is modified from `display-buffer-reuse-window' and xcb:CW:EventMask) :override-redirect 1 :event-mask xcb:EventMask:SubstructureRedirect)) + (xcb:+request exwm--connection + (make-instance 'xcb:CreateWindow + :depth 0 :wid container :parent workspace + :x 0 :y 0 + :width (x-display-pixel-width) + :height (x-display-pixel-height) + :border-width 0 :class xcb:WindowClass:CopyFromParent + :visual 0 ;CopyFromParent + :value-mask xcb:CW:OverrideRedirect + :override-redirect 1)) (exwm--debug (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_NAME :window workspace :data (format "EXWM workspace %d" + (cl-position i exwm-workspace--list)))) + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_NAME + :window container + :data + (format "EXWM workspace %d frame container" (cl-position i exwm-workspace--list))))) (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow - :window outer-id :parent workspace :x 0 :y 0)) + :window outer-id :parent container :x 0 :y 0)) + (xcb:+request exwm--connection + (make-instance 'xcb:MapWindow :window container)) (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window workspace)))) (xcb:flush exwm--connection) -- cgit 1.4.1 From 734b4013063d1bf3c6e34e0aa66e39a92ba49ace Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 21 Feb 2016 16:30:08 +0800 Subject: Fix the advice function for ido-buffer-window-other-frame * exwm-config.el (exwm-config--ido-buffer-window-other-frame) (exwm-config--fix/ido-buffer-window-other-frame): Do not use advice. Fix issues when switching form/to floating frames. --- exwm-config.el | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/exwm-config.el b/exwm-config.el index db8c68f445..d6e10f23c9 100644 --- a/exwm-config.el +++ b/exwm-config.el @@ -26,6 +26,7 @@ ;;; Code: (require 'exwm) +(require 'ido) (defun exwm-config-default () "Default configuration of EXWM." @@ -65,21 +66,37 @@ ;; Other configurations (exwm-config-misc)) -(defun exwm-config--ido-buffer-window-other-frame (orig-fun buffer) - "Wrapper for `ido-buffer-window-other-frame' to exclude invisible windows." - (with-current-buffer buffer - (if (and (eq major-mode 'exwm-mode) - (or exwm--floating-frame - (not exwm-layout-show-all-buffers))) - ;; `ido-mode' works well with `exwm-mode' buffers - (funcall orig-fun buffer) - ;; Other buffers should be selected within the same workspace - (get-buffer-window buffer exwm-workspace--current)))) - (defun exwm-config--fix/ido-buffer-window-other-frame () "Fix `ido-buffer-window-other-frame'." - (advice-add 'ido-buffer-window-other-frame :around - #'exwm-config--ido-buffer-window-other-frame)) + (defalias 'exwm-config-ido-buffer-window-other-frame + (symbol-function #'ido-buffer-window-other-frame)) + (defun ido-buffer-window-other-frame (buffer) + "This is a version redefined by EXWM. + +You can find the original one at `exwm-config-ido-buffer-window-other-frame'." + (with-current-buffer (window-buffer (selected-window)) + (if (and (eq major-mode 'exwm-mode) + exwm--floating-frame) + ;; Switch from a floating frame. + (with-current-buffer buffer + (if (and (eq major-mode 'exwm-mode) + exwm--floating-frame + (eq exwm--frame exwm-workspace--current)) + ;; Switch to another floating frame. + (frame-root-window exwm--floating-frame) + ;; Do not switch if the buffer is not on the current workspace. + (or (get-buffer-window buffer exwm-workspace--current) + (selected-window)))) + (with-current-buffer buffer + (when (eq major-mode 'exwm-mode) + (if (eq exwm--frame exwm-workspace--current) + (when exwm--floating-frame + ;; Switch to a floating frame on the current workspace. + (frame-selected-window exwm--floating-frame)) + ;; Do not switch to exwm-mode buffers on other workspace (which + ;; won't work unless `exwm-layout-show-all-buffers' is set) + (unless exwm-layout-show-all-buffers + (selected-window))))))))) (defun exwm-config-ido () "Configure Ido to work with EXWM." -- cgit 1.4.1 From 7116b01b0c9b7efda105487a17192470c466b9c4 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 21 Feb 2016 16:39:34 +0800 Subject: Various fixes for floating X windows * exwm-floating.el (exwm-floating--set-floating): Always create floating X windows on current workspace. * exwm-workspace.el (exwm-workspace-switch): Restore selected floating frames. * exwm-workspace.el (exwm-workspace-move-window): Restore the position of floating X windows. Recreate floating frames when using fixed minibuffer. Restack tiling X windows. --- exwm-floating.el | 16 ++------- exwm-workspace.el | 100 ++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 91 insertions(+), 25 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 1ec54d10fa..97d673c15b 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -63,14 +63,7 @@ (let ((window (get-buffer-window (exwm--id->buffer id)))) (when window ;window in non-floating state (set-window-buffer window (other-buffer)))) ;hide it first - (let* ((original-frame - (with-current-buffer (exwm--id->buffer id) - (if (and exwm-transient-for (exwm--id->buffer exwm-transient-for)) - ;; Place a modal in the same workspace with its leading window - (with-current-buffer (exwm--id->buffer exwm-transient-for) - exwm--frame) - ;; Fallback to current workspace - exwm-workspace--current))) + (let* ((original-frame exwm-workspace--current) ;; Create new frame (frame (with-current-buffer (or (get-buffer "*scratch*") @@ -79,7 +72,7 @@ (get-buffer-create "*scratch*")) (get-buffer "*scratch*"))) (make-frame - `((minibuffer . nil) ;use the one on workspace + `((minibuffer . nil) ;use the default minibuffer. (background-color . ,exwm-floating-border-color) (internal-border-width . ,exwm-floating-border-width) (left . 10000) @@ -107,11 +100,6 @@ ;; Save frame parameters. (set-frame-parameter frame 'exwm-outer-id outer-id) (set-frame-parameter frame 'exwm-container frame-container) - ;; Set urgency flag if it's not appear in the active workspace - (let ((idx (cl-position original-frame exwm-workspace--list))) - (when (/= idx exwm-workspace-current-index) - (set-frame-parameter original-frame 'exwm--urgency t) - (setq exwm-workspace--switch-history-outdated t))) ;; Fix illegal parameters ;; FIXME: check normal hints restrictions (let* ((display-width (frame-pixel-width original-frame)) diff --git a/exwm-workspace.el b/exwm-workspace.el index 42c081820e..99bc605e5f 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -186,7 +186,14 @@ The optional FORCE option is for internal use only." :stack-mode xcb:StackMode:Above)) (setq exwm-workspace--current frame exwm-workspace-current-index index) - (select-window (frame-selected-window frame)) + (unless (memq (selected-frame) exwm-workspace--list) + ;; Save the floating frame window selected on the previous workspace. + (set-frame-parameter (with-current-buffer (window-buffer) + exwm--frame) + 'exwm-selected-window (selected-window))) + (select-window (or (frame-parameter frame 'exwm-selected-window) + (frame-selected-window frame))) + (set-frame-parameter frame 'exwm-selected-window nil) ;; Close the (possible) active minibuffer (when (active-minibuffer-window) (run-with-idle-timer 0 nil (lambda () (abort-recursive-edit)))) @@ -221,7 +228,11 @@ The optional FORCE option is for internal use only." (xcb:flush exwm--connection)) (run-hooks 'exwm-workspace-switch-hook)))) +(defvar exwm-floating-border-width) +(defvar exwm-floating-border-color) + (declare-function exwm-layout--hide "exwm-layout.el" (id)) +(declare-function exwm-layout--refresh "exwm-layout.el") ;;;###autoload (defun exwm-workspace-move-window (index &optional id) @@ -253,14 +264,72 @@ The optional FORCE option is for internal use only." (setq exwm--frame frame) (if exwm--floating-frame ;; Move the floating container. - (progn + (with-slots (x y) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetGeometry :drawable exwm--container)) (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow :window exwm--container :parent (frame-parameter frame 'exwm-workspace) - :x 0 :y 0)) - (xcb:flush exwm--connection)) + :x x :y y)) + (xcb:flush exwm--connection) + (unless exwm-workspace-minibuffer-position + ;; The frame needs to be recreated since it won't use the + ;; minibuffer on the new workspace. + (let* ((old-frame exwm--floating-frame) + (new-frame + (with-current-buffer + (or (get-buffer "*scratch*") + (progn + (set-buffer-major-mode + (get-buffer-create "*scratch*")) + (get-buffer "*scratch*"))) + (make-frame + `((minibuffer . ,(minibuffer-window frame)) + (background-color . ,exwm-floating-border-color) + (internal-border-width + . ,exwm-floating-border-width) + (left . 10000) + (top . 10000) + (width . ,window-min-width) + (height . ,window-min-height) + (unsplittable . t))))) + (outer-id (string-to-number + (frame-parameter new-frame + 'outer-window-id))) + (frame-container (frame-parameter old-frame + 'exwm-container)) + (window (frame-root-window new-frame))) + (set-frame-parameter new-frame 'exwm-outer-id outer-id) + (set-frame-parameter new-frame 'exwm-container + frame-container) + (make-frame-invisible new-frame) + (set-frame-size new-frame + (frame-pixel-width old-frame) + (frame-pixel-height old-frame) + t) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window outer-id + :parent frame-container + :x 0 :y 0)) + (xcb:flush exwm--connection) + (with-current-buffer (exwm--id->buffer id) + (setq window-size-fixed nil + exwm--frame frame + exwm--floating-frame new-frame) + (set-window-dedicated-p (frame-root-window old-frame) nil) + (remove-hook 'window-configuration-change-hook + #'exwm-layout--refresh) + (set-window-buffer window (current-buffer)) + (add-hook 'window-configuration-change-hook + #'exwm-layout--refresh) + (delete-frame old-frame) + (set-window-dedicated-p window t)) + (make-frame-visible new-frame) + (with-selected-frame new-frame + (exwm-layout--refresh))))) ;; Move the X window container. (if (/= index exwm-workspace-current-index) (bury-buffer) @@ -271,13 +340,22 @@ The optional FORCE option is for internal use only." (get-buffer-create "*scratch*")) (get-buffer "*scratch*"))))) (exwm-layout--hide id) - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - ;; (current-buffer) is changed. - :window (with-current-buffer (exwm--id->buffer id) - exwm--container) - :parent (frame-parameter frame 'exwm-workspace) - :x 0 :y 0)) + ;; (current-buffer) is changed. + (with-current-buffer (exwm--id->buffer id) + ;; Reparent to the destination workspace. + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window exwm--container + :parent (frame-parameter frame 'exwm-workspace) + :x 0 :y 0)) + ;; Place it just above the destination frame container. + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window exwm--container + :value-mask (logior xcb:ConfigWindow:Sibling + xcb:ConfigWindow:StackMode) + :sibling (frame-parameter frame 'exwm-container) + :stack-mode xcb:StackMode:Above))) (xcb:flush exwm--connection) (set-window-buffer (frame-selected-window frame) (exwm--id->buffer id))))) -- cgit 1.4.1 From 55cec760ca3cee21e735bff9a9a5229b222b97ff Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 21 Feb 2016 20:19:45 +0800 Subject: Fix emacsclient related issues * exwm-systemtray.el (exwm-systemtray-height): The value is not available when emacsclient has just loaded the library (and it crashes emacsclient). * exwm-workspace.el (exwm-workspace--init): Set `default-minibuffer-frame' later to prevent it from being modified when using emacsclient. * exwm-floating.el: * exwm-randr.el: * exwm-systemtray.el: * exwm-workspace.el: * exwm.el: Use `exwm-workspace--minibuffer-own-frame-p' instead of the raw variable. --- exwm-floating.el | 3 +-- exwm-randr.el | 3 +-- exwm-systemtray.el | 14 +++++++------- exwm-workspace.el | 9 +++++---- exwm.el | 2 +- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 97d673c15b..b56201914e 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -53,7 +53,6 @@ (defvar exwm-workspace--list) (defvar exwm-workspace-current-index) (defvar exwm-workspace--switch-history-outdated) -(defvar exwm-workspace-minibuffer-position) (declare-function exwm-layout--refresh "exwm-layout.el") (declare-function exwm-layout--show "exwm-layout.el") @@ -104,7 +103,7 @@ ;; FIXME: check normal hints restrictions (let* ((display-width (frame-pixel-width original-frame)) (display-height (- (frame-pixel-height original-frame) - (if exwm-workspace-minibuffer-position + (if (exwm-workspace--minibuffer-own-frame-p) 0 (window-pixel-height (minibuffer-window original-frame))) diff --git a/exwm-randr.el b/exwm-randr.el index 7f9b443c86..e761d8f2e3 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -54,7 +54,6 @@ (defvar exwm-randr-refresh-hook nil "Normal hook run when the RandR module just refreshed.") -(defvar exwm-workspace-minibuffer-position) (defvar exwm-layout--fullscreen-frame-count) (defvar exwm-workspace-number) (defvar exwm-workspace--list) @@ -94,7 +93,7 @@ (setq default-geometry geometry))))))) (exwm--log "(randr) outputs: %s" output-plist) (when output-plist - (setq workarea-offset (if exwm-workspace-minibuffer-position + (setq workarea-offset (if (exwm-workspace--minibuffer-own-frame-p) 0 (window-pixel-height (minibuffer-window)))) (setq exwm-layout--fullscreen-frame-count 0) diff --git a/exwm-systemtray.el b/exwm-systemtray.el index e9a9745316..861304ab7c 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -43,9 +43,7 @@ ;; GTK icons require at least 16 pixels to show normally. (defconst exwm-systemtray--icon-min-size 16 "Minimum icon size.") -(defvar exwm-systemtray-height (max exwm-systemtray--icon-min-size - (line-pixel-height)) - "System tray height. +(defvar exwm-systemtray-height nil "System tray height. You shall use the default value if using auto-hide minibuffer.") @@ -258,12 +256,11 @@ You shall use the default value if using auto-hide minibuffer.") (t (exwm--log "(System Tray) Unknown opcode message: %s" obj))))))) -(defvar exwm-workspace-minibuffer-position) (defvar exwm-workspace--current) (defun exwm-systemtray--on-workspace-switch () "Reparent/Refresh the system tray in `exwm-workspace-switch-hook'." - (unless exwm-workspace-minibuffer-position + (unless (exwm-workspace--minibuffer-own-frame-p) (xcb:+request exwm-systemtray--connection (make-instance 'xcb:ReparentWindow :window exwm-systemtray--embedder @@ -277,7 +274,7 @@ You shall use the default value if using auto-hide minibuffer.") (defun exwm-systemtray--on-randr-refresh () "Reposition/Refresh the system tray in `exwm-randr-refresh-hook'." - (unless exwm-workspace-minibuffer-position + (unless (exwm-workspace--minibuffer-own-frame-p) (xcb:+request exwm-systemtray--connection (make-instance 'xcb:ConfigureWindow :window exwm-systemtray--embedder @@ -295,6 +292,9 @@ You shall use the default value if using auto-hide minibuffer.") (cl-assert (not exwm-systemtray--list)) (cl-assert (not exwm-systemtray--selection-owner-window)) (cl-assert (not exwm-systemtray--embedder)) + (unless exwm-systemtray-height + (setq exwm-systemtray-height (max exwm-systemtray--icon-min-size + (line-pixel-height)))) ;; Create a new connection. (setq exwm-systemtray--connection (xcb:connect-to-socket)) (set-process-query-on-exit-flag (slot-value exwm-systemtray--connection @@ -345,7 +345,7 @@ You shall use the default value if using auto-hide minibuffer.") :border-width 0 :class xcb:WindowClass:CopyFromParent :visual 0 :value-mask xcb:CW:EventMask :event-mask xcb:EventMask:SubstructureNotify)) - (if exwm-workspace-minibuffer-position + (if (exwm-workspace--minibuffer-own-frame-p) (setq parent (frame-parameter exwm-workspace--minibuffer 'exwm-container) ;; Vertically centered. diff --git a/exwm-workspace.el b/exwm-workspace.el index 99bc605e5f..07bfdd9d5e 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -274,7 +274,7 @@ The optional FORCE option is for internal use only." (frame-parameter frame 'exwm-workspace) :x x :y y)) (xcb:flush exwm--connection) - (unless exwm-workspace-minibuffer-position + (unless (exwm-workspace--minibuffer-own-frame-p) ;; The frame needs to be recreated since it won't use the ;; minibuffer on the new workspace. (let* ((old-frame exwm--floating-frame) @@ -567,7 +567,7 @@ This functions is modified from `display-buffer-reuse-window' and (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window (car i)))) ;; Reparent out the minibuffer frame. - (when exwm-workspace-minibuffer-position + (when (exwm-workspace--minibuffer-own-frame-p) (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow :window (frame-parameter exwm-workspace--minibuffer @@ -614,13 +614,14 @@ This functions is modified from `display-buffer-reuse-window' and (setq exwm-workspace--minibuffer (make-frame '((window-system . x) (minibuffer . only) (left . 10000) (right . 10000) - (width . 0) (height . 0))) - default-minibuffer-frame exwm-workspace--minibuffer) + (width . 0) (height . 0)))) ;; Remove/hide existing frames. (dolist (f old-frames) (if (frame-parameter f 'client) (make-frame-invisible f) (delete-frame f)))) + ;; This is the only usable minibuffer frame. + (setq default-minibuffer-frame exwm-workspace--minibuffer) (let ((outer-id (string-to-number (frame-parameter exwm-workspace--minibuffer 'outer-window-id))) diff --git a/exwm.el b/exwm.el index b425acf7b5..d3d2c10255 100644 --- a/exwm.el +++ b/exwm.el @@ -462,7 +462,7 @@ (let* ((workareas (vector 0 0 (x-display-pixel-width) (- (x-display-pixel-height) - (if exwm-workspace-minibuffer-position + (if (exwm-workspace--minibuffer-own-frame-p) 0 (window-pixel-height (minibuffer-window)))))) (workareas (mapconcat (lambda (_) workareas) -- cgit 1.4.1 From 35493faca670738f6078009ec6d9b862721d7ea0 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 22 Feb 2016 12:41:21 +0800 Subject: Bump version to 0.3 --- exwm.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm.el b/exwm.el index d3d2c10255..d967c7eca4 100644 --- a/exwm.el +++ b/exwm.el @@ -4,8 +4,8 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0.2 -;; Package-Requires: ((xelb "0.5")) +;; Version: 0.3 +;; Package-Requires: ((xelb "0.6")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From 4838f2b7fa2a8f3bdd2a8b2f779039a1b397e12b Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 23 Feb 2016 19:04:51 +0800 Subject: Make input focus revert to pointer root * exwm-input.el (exwm-input--set-focus): Input focus should not revert to parent (which was the Emacs frame) any more. --- exwm-input.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exwm-input.el b/exwm-input.el index 0a50bef262..943dd7da83 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -73,7 +73,8 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (exwm--log "Focus on #x%x with SetInputFocus" id) (xcb:+request exwm--connection (make-instance 'xcb:SetInputFocus - :revert-to xcb:InputFocus:Parent :focus id + :revert-to xcb:InputFocus:PointerRoot + :focus id :time xcb:Time:CurrentTime))) (xcb:flush exwm--connection)))) -- cgit 1.4.1 From 84bdc1378a7ebe4537c0cd1cf7cd054cd4e28b78 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 24 Feb 2016 20:55:01 +0800 Subject: Fix issues with moving X windows between workspaces * exwm-workspace.el (exwm-workspace-move-window): Select the moved floating X window. Update the 'exwm-selected-window' frame parameter. (exwm-workspace-switch): Check 'exwm-selected-window' for dead windows. (exwm-workspace-switch-to-buffer): Allow non-interactive call. --- exwm-workspace.el | 94 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 56 insertions(+), 38 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 07bfdd9d5e..4af98fb0ad 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -178,7 +178,8 @@ The optional FORCE option is for internal use only." (user-error "[EXWM] Workspace index out of range: %d" index)) (when (or force (/= exwm-workspace-current-index index)) (let* ((frame (elt exwm-workspace--list index)) - (workspace (frame-parameter frame 'exwm-workspace))) + (workspace (frame-parameter frame 'exwm-workspace)) + (window (frame-parameter frame 'exwm-selected-window))) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window workspace @@ -191,7 +192,7 @@ The optional FORCE option is for internal use only." (set-frame-parameter (with-current-buffer (window-buffer) exwm--frame) 'exwm-selected-window (selected-window))) - (select-window (or (frame-parameter frame 'exwm-selected-window) + (select-window (or (when (window-live-p window) window) (frame-selected-window frame))) (set-frame-parameter frame 'exwm-selected-window nil) ;; Close the (possible) active minibuffer @@ -231,6 +232,7 @@ The optional FORCE option is for internal use only." (defvar exwm-floating-border-width) (defvar exwm-floating-border-color) +(declare-function exwm-layout--show "exwm-layout.el" (id)) (declare-function exwm-layout--hide "exwm-layout.el" (id)) (declare-function exwm-layout--refresh "exwm-layout.el") @@ -274,7 +276,10 @@ The optional FORCE option is for internal use only." (frame-parameter frame 'exwm-workspace) :x x :y y)) (xcb:flush exwm--connection) - (unless (exwm-workspace--minibuffer-own-frame-p) + (if (exwm-workspace--minibuffer-own-frame-p) + (when (= index exwm-workspace-current-index) + (select-frame-set-input-focus exwm--floating-frame) + (exwm-layout--refresh)) ;; The frame needs to be recreated since it won't use the ;; minibuffer on the new workspace. (let* ((old-frame exwm--floating-frame) @@ -326,19 +331,29 @@ The optional FORCE option is for internal use only." (add-hook 'window-configuration-change-hook #'exwm-layout--refresh) (delete-frame old-frame) - (set-window-dedicated-p window t)) - (make-frame-visible new-frame) - (with-selected-frame new-frame - (exwm-layout--refresh))))) + (set-window-dedicated-p window t) + (exwm-layout--show id window)) + (if (/= index exwm-workspace-current-index) + (make-frame-visible new-frame) + (select-frame-set-input-focus new-frame) + (redisplay)))) + ;; Update the 'exwm-selected-window' frame parameter. + (when (/= index exwm-workspace-current-index) + (with-current-buffer (exwm--id->buffer id) + (set-frame-parameter frame 'exwm-selected-window + (frame-root-window + exwm--floating-frame))))) ;; Move the X window container. - (if (/= index exwm-workspace-current-index) - (bury-buffer) - (set-window-buffer (get-buffer-window (current-buffer) t) - (or (get-buffer "*scratch*") - (progn - (set-buffer-major-mode - (get-buffer-create "*scratch*")) - (get-buffer "*scratch*"))))) + (if (= index exwm-workspace-current-index) + (set-window-buffer (get-buffer-window (current-buffer) t) + (or (get-buffer "*scratch*") + (progn + (set-buffer-major-mode + (get-buffer-create "*scratch*")) + (get-buffer "*scratch*")))) + (bury-buffer) + ;; Clear the 'exwm-selected-window' frame parameter. + (set-frame-parameter frame 'exwm-selected-window nil)) (exwm-layout--hide id) ;; (current-buffer) is changed. (with-current-buffer (exwm--id->buffer id) @@ -362,30 +377,33 @@ The optional FORCE option is for internal use only." (setq exwm-workspace--switch-history-outdated t))) ;;;###autoload -(defun exwm-workspace-switch-to-buffer () +(defun exwm-workspace-switch-to-buffer (buffer-or-name) "Make the current Emacs window display another buffer." - (interactive) - ;; Show all buffers - (unless exwm-workspace-show-all-buffers - (dolist (pair exwm--id-buffer-alist) - (with-current-buffer (cdr pair) - (when (= ?\s (aref (buffer-name) 0)) - (rename-buffer (substring (buffer-name) 1)))))) - (let ((buffer (read-buffer "Switch to buffer: " nil t))) - (when buffer - (with-current-buffer buffer - (if (and (eq major-mode 'exwm-mode) - (not (eq exwm--frame exwm-workspace--current))) - (exwm-workspace-move-window exwm-workspace-current-index - exwm--id) - (switch-to-buffer buffer))))) - ;; Hide buffers on other workspaces - (unless exwm-workspace-show-all-buffers - (dolist (pair exwm--id-buffer-alist) - (with-current-buffer (cdr pair) - (unless (or (eq exwm--frame exwm-workspace--current) - (= ?\s (aref (buffer-name) 0))) - (rename-buffer (concat " " (buffer-name)))))))) + (interactive + (let ((inhibit-quit t)) + ;; Show all buffers + (unless exwm-workspace-show-all-buffers + (dolist (pair exwm--id-buffer-alist) + (with-current-buffer (cdr pair) + (when (= ?\s (aref (buffer-name) 0)) + (rename-buffer (substring (buffer-name) 1)))))) + (prog1 + (with-local-quit + (list (get-buffer (read-buffer "Switch to buffer: " nil t)))) + ;; Hide buffers on other workspaces + (unless exwm-workspace-show-all-buffers + (dolist (pair exwm--id-buffer-alist) + (with-current-buffer (cdr pair) + (unless (or (eq exwm--frame exwm-workspace--current) + (= ?\s (aref (buffer-name) 0))) + (rename-buffer (concat " " (buffer-name)))))))))) + (when buffer-or-name + (with-current-buffer buffer-or-name + (if (and (eq major-mode 'exwm-mode) + (not (eq exwm--frame exwm-workspace--current))) + (exwm-workspace-move-window exwm-workspace-current-index + exwm--id) + (switch-to-buffer buffer-or-name))))) (defun exwm-workspace-rename-buffer (newname) "Rename a buffer." -- cgit 1.4.1 From db6d26c662a750b273e1bbc06902a2a46e101e4c Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 24 Feb 2016 21:27:23 +0800 Subject: Refresh the workspace after creating a floating X window * exwm-floating.el (exwm-floating--set-floating): Refresh the workspace (since auto-refresh was disabled). --- exwm-floating.el | 2 ++ 1 file changed, 2 insertions(+) diff --git a/exwm-floating.el b/exwm-floating.el index b56201914e..457705322c 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -209,6 +209,8 @@ (add-hook 'window-configuration-change-hook #'exwm-layout--refresh) (set-window-dedicated-p window t) (exwm-layout--show id window)) + (with-selected-frame exwm-workspace--current + (exwm-layout--refresh)) (select-frame-set-input-focus frame)) (run-hooks 'exwm-floating-setup-hook) ;; Redraw the frame. -- cgit 1.4.1 From 5a39c5c2fad643a656c59c0071f0cbea58eb65a9 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 25 Feb 2016 12:41:35 +0800 Subject: Allow user to hide floating X windows * exwm-core.el (exwm-mode-map): Add a new key to hide floating X windows. * exwm-floating.el (exwm-floating-hide): New command to hide a floating X window. * exwm-workspace.el: Fix a compile warning. --- exwm-core.el | 1 + exwm-floating.el | 16 ++++++++++++++++ exwm-workspace.el | 2 +- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/exwm-core.el b/exwm-core.el index 5377c6766f..e202d4ee47 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -121,6 +121,7 @@ (defvar exwm-mode-map (let ((map (make-sparse-keymap))) (define-key map "\C-c\C-f" #'exwm-layout-set-fullscreen) + (define-key map "\C-c\C-h" #'exwm-floating-hide) (define-key map "\C-c\C-k" #'exwm-input-release-keyboard) (define-key map "\C-c\C-m" #'exwm-workspace-move-window) (define-key map "\C-c\C-q" #'exwm-input-send-next-key) diff --git a/exwm-floating.el b/exwm-floating.el index 457705322c..ba162f67e1 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -284,6 +284,22 @@ (exwm-floating--unset-floating exwm--id) (exwm-floating--set-floating exwm--id)))) +;;;###autoload +(defun exwm-floating-hide () + "Hide the current floating X window (which would show again when selected)." + (interactive) + (when (and (eq major-mode 'exwm-mode) + exwm--floating-frame) + ;; Put this floating X window at bottom. + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window exwm--container + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Below)) + ;; FIXME: Should it be put into iconic state? + (xcb:flush exwm--connection) + (select-frame-set-input-focus exwm-workspace--current))) + (define-obsolete-function-alias 'exwm-floating-hide-mode-line 'exwm-layout-hide-mode-line "25.1" "Hide mode-line of a floating frame.") (define-obsolete-function-alias 'exwm-floating-show-mode-line diff --git a/exwm-workspace.el b/exwm-workspace.el index 4af98fb0ad..80767d52d7 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -232,7 +232,7 @@ The optional FORCE option is for internal use only." (defvar exwm-floating-border-width) (defvar exwm-floating-border-color) -(declare-function exwm-layout--show "exwm-layout.el" (id)) +(declare-function exwm-layout--show "exwm-layout.el" (id &optional window)) (declare-function exwm-layout--hide "exwm-layout.el" (id)) (declare-function exwm-layout--refresh "exwm-layout.el") -- cgit 1.4.1 From b400ae6eba75e61085b46a1671b52290e3ba30a2 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 25 Feb 2016 12:42:25 +0800 Subject: Bump version to 0.4 --- exwm.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm.el b/exwm.el index d967c7eca4..39eb35004d 100644 --- a/exwm.el +++ b/exwm.el @@ -4,7 +4,7 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0.3 +;; Version: 0.4 ;; Package-Requires: ((xelb "0.6")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From 84a9041b20e5477fb2ce2989de9dd2583ef12773 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 25 Feb 2016 18:36:54 +0800 Subject: Fix exwm-workspace-switch-to-buffer * exwm-workspace.el (exwm-workspace-switch-to-buffer): Select the floating frame when switching to a floating X window buffer. --- exwm-workspace.el | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 80767d52d7..8230bd7628 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -399,10 +399,19 @@ The optional FORCE option is for internal use only." (rename-buffer (concat " " (buffer-name)))))))))) (when buffer-or-name (with-current-buffer buffer-or-name - (if (and (eq major-mode 'exwm-mode) - (not (eq exwm--frame exwm-workspace--current))) - (exwm-workspace-move-window exwm-workspace-current-index - exwm--id) + (if (eq major-mode 'exwm-mode) + ;; EXWM buffer. + (if (eq exwm--frame exwm-workspace--current) + ;; On the current workspace. + (if (not exwm--floating-frame) + (switch-to-buffer buffer-or-name) + ;; Select the floating frame. + (select-frame-set-input-focus exwm--floating-frame) + (select-window (frame-root-window exwm--floating-frame))) + ;; On another workspace. + (exwm-workspace-move-window exwm-workspace-current-index + exwm--id)) + ;; Ordinary buffer. (switch-to-buffer buffer-or-name))))) (defun exwm-workspace-rename-buffer (newname) -- cgit 1.4.1 From bc4aafec1683a27209aec33f44c3947e169860c6 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 26 Feb 2016 09:24:15 +0800 Subject: Minor fixes for layout and workspace * exwm-layout.el (exwm-layout-show-mode-line): Force update mode-line. * exwm-workspace.el (exwm-workspace--update-minibuffer): Treat nil as empty string. --- exwm-layout.el | 3 ++- exwm-workspace.el | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index c9146de371..09428da4bb 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -455,7 +455,8 @@ See also `exwm-layout-enlarge-window'." (window-mode-line-height (frame-root-window exwm--floating-frame))) nil t) - (exwm-input-grab-keyboard)))) + (exwm-input-grab-keyboard)) + (force-mode-line-update))) ;;;###autoload (defun exwm-layout-toggle-mode-line () diff --git a/exwm-workspace.el b/exwm-workspace.el index 8230bd7628..283b3849db 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -453,7 +453,7 @@ The optional FORCE option is for internal use only." (setq result (+ result (ceiling (1+ (length i)) width)))) - (split-string (current-message) "\n")) + (split-string (or (current-message) "") "\n")) result) (count-screen-lines)))))) (when (and (integerp max-mini-window-height) -- cgit 1.4.1 From 74fba563b82c1a4d3a589c71041a5bf370709a9e Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 26 Feb 2016 18:18:34 +0800 Subject: Do not wait for WM_STATE property events * exwm-layout.el (exwm-layout--show, exwm-layout--hide): Save the state directly. --- exwm-layout.el | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index 09428da4bb..76721a01a1 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -103,7 +103,8 @@ (xcb:+request exwm--connection (make-instance 'xcb:icccm:set-WM_STATE :window id :state xcb:icccm:WM_STATE:NormalState - :icon xcb:Window:None))) + :icon xcb:Window:None)) + (setq exwm-state xcb:icccm:WM_STATE:NormalState)) (xcb:+request exwm--connection (make-instance 'xcb:SendEvent :propagate 0 :destination id @@ -122,27 +123,27 @@ (defun exwm-layout--hide (id) "Hide window ID." - (unless (eq xcb:icccm:WM_STATE:IconicState ;already hidden - (with-current-buffer (exwm--id->buffer id) exwm-state)) - (exwm--log "Hide #x%x" id) - (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window id :value-mask xcb:CW:EventMask - :event-mask xcb:EventMask:NoEvent)) - (xcb:+request exwm--connection (make-instance 'xcb:UnmapWindow :window id)) - (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window id :value-mask xcb:CW:EventMask - :event-mask exwm--client-event-mask)) - (with-current-buffer (exwm--id->buffer id) + (with-current-buffer (exwm--id->buffer id) + (unless (eq xcb:icccm:WM_STATE:IconicState exwm-state) ;already hidden + (exwm--log "Hide #x%x" id) (xcb:+request exwm--connection - (make-instance 'xcb:UnmapWindow :window exwm--container))) - (xcb:+request exwm--connection - (make-instance 'xcb:icccm:set-WM_STATE - :window id - :state xcb:icccm:WM_STATE:IconicState - :icon xcb:Window:None)) - (xcb:flush exwm--connection))) + (make-instance 'xcb:ChangeWindowAttributes + :window id :value-mask xcb:CW:EventMask + :event-mask xcb:EventMask:NoEvent)) + (xcb:+request exwm--connection (make-instance 'xcb:UnmapWindow :window id)) + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window id :value-mask xcb:CW:EventMask + :event-mask exwm--client-event-mask)) + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow :window exwm--container)) + (xcb:+request exwm--connection + (make-instance 'xcb:icccm:set-WM_STATE + :window id + :state xcb:icccm:WM_STATE:IconicState + :icon xcb:Window:None)) + (setq exwm-state xcb:icccm:WM_STATE:IconicState) + (xcb:flush exwm--connection)))) (defvar exwm-workspace--current) (defvar exwm-workspace--list) -- cgit 1.4.1 From 74555b25c2dccacd550762d339175494d3d78562 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 28 Feb 2016 20:03:56 +0800 Subject: Simplify `exwm-workspace--display-buffer' * exwm-workspace.el (exwm-workspace--display-buffer): Make it simpler and more reliable. --- exwm-workspace.el | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 283b3849db..89bb6940b0 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -495,23 +495,12 @@ The optional FORCE option is for internal use only." (xcb:flush exwm--connection))))) (defun exwm-workspace--display-buffer (buffer alist) - "Display buffer in the current workspace frame. - -This functions is modified from `display-buffer-reuse-window' and -`display-buffer-pop-up-window'." + "Display BUFFER as if the current workspace is selected." + ;; Only when the floating minibuffer frame is selected. + ;; This also protect this functions from being recursively called. (when (eq (selected-frame) exwm-workspace--minibuffer) - (setq buffer (get-buffer buffer)) ;convert string to buffer. - (let (window) - (if (setq window (get-buffer-window buffer exwm-workspace--current)) - (window--display-buffer buffer window 'reuse alist) - (when (setq window (or (window--try-to-split-window - (get-largest-window exwm-workspace--current t) - alist) - (window--try-to-split-window - (get-lru-window exwm-workspace--current t) - alist))) - (window--display-buffer - buffer window 'window alist display-buffer-mark-dedicated)))))) + (with-selected-frame exwm-workspace--current + (display-buffer buffer alist)))) (defun exwm-workspace--show-minibuffer () "Show the minibuffer frame." -- cgit 1.4.1 From 93e42136e3eba95c129c92a928b0c27517f2af7e Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 1 Mar 2016 18:51:09 +0800 Subject: Avoid unnecessary input focus changes in line-mode * exwm-input.el (exwm-input--grab-keyboard, exwm-input--release-keyboard): Grab keys on X windows instead to prevent unexpected input focus changes. * exwm-workspace.el (exwm-workspace--show-minibuffer): The workaround for cursor flickering issue is no more needed. --- exwm-input.el | 8 ++------ exwm-workspace.el | 9 +-------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 943dd7da83..31125a25c6 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -325,9 +325,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (when (xcb:+request-checked+request-check exwm--connection (make-instance 'xcb:GrabKey :owner-events 0 - :grab-window - (with-current-buffer (exwm--id->buffer id) - exwm--container) + :grab-window id :modifiers xcb:ModMask:Any :key xcb:Grab:Any :pointer-mode xcb:GrabMode:Async @@ -342,9 +340,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (when (xcb:+request-checked+request-check exwm--connection (make-instance 'xcb:UngrabKey :key xcb:Grab:Any - :grab-window - (with-current-buffer (exwm--id->buffer id) - exwm--container) + :grab-window id :modifiers xcb:ModMask:Any)) (exwm--log "Failed to release keyboard for #x%x" id)) (setq exwm--on-KeyPress #'exwm-input--on-KeyPress-char-mode))) diff --git a/exwm-workspace.el b/exwm-workspace.el index 89bb6940b0..6fc38d4a32 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -513,14 +513,7 @@ The optional FORCE option is for internal use only." (make-instance 'xcb:MapWindow :window (frame-parameter exwm-workspace--minibuffer 'exwm-container))) - (xcb:flush exwm--connection) - ;; Unfortunately we need the following lines to workaround a cursor - ;; flickering issue for line-mode floating X windows. They just make the - ;; minibuffer appear to be focused. - (with-current-buffer (window-buffer (minibuffer-window - exwm-workspace--minibuffer)) - (setq cursor-in-non-selected-windows - (frame-parameter exwm-workspace--minibuffer 'cursor-type)))) + (xcb:flush exwm--connection)) (defun exwm-workspace--hide-minibuffer () "Hide the minibuffer frame." -- cgit 1.4.1 From fe9be0b3efab284dae9eb1de37e97a70a9e08bd2 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 3 Mar 2016 19:34:17 +0800 Subject: Handle buffer change after a buffer is killed * exwm-manage.el (exwm-manage--kill-buffer-query-function): Handle buffer change. * exwm-workspace.el (exwm-workspace--show-minibuffer): Revert the change made in 93e42136 (the problem has not been fully resolved). --- exwm-manage.el | 95 ++++++++++++++++++++++++++++--------------------------- exwm-workspace.el | 10 +++++- 2 files changed, 57 insertions(+), 48 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index fd1d64bafd..659e4267b7 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -334,56 +334,57 @@ corresponding buffer.") (xcb:flush exwm--connection) ;; Wait for DestroyNotify event. (throw 'return nil)) - ;; Try to close the X window with WM_DELETE_WINDOW client message. - (xcb:+request exwm--connection - (make-instance 'xcb:icccm:SendEvent - :destination exwm--id - :event (xcb:marshal - (make-instance 'xcb:icccm:WM_DELETE_WINDOW - :window exwm--id) - exwm--connection))) - (xcb:flush exwm--connection) - ;; - (unless (memq xcb:Atom:_NET_WM_PING exwm--protocols) - ;; The window does not support _NET_WM_PING. To make sure it'll die, - ;; kill it after the time runs out. - ;; Hide the container to prevent flickering. + (let ((id exwm--id)) + ;; Try to close the X window with WM_DELETE_WINDOW client message. (xcb:+request exwm--connection - (make-instance 'xcb:UnmapWindow :window exwm--container)) + (make-instance 'xcb:icccm:SendEvent + :destination id + :event (xcb:marshal + (make-instance 'xcb:icccm:WM_DELETE_WINDOW + :window id) + exwm--connection))) (xcb:flush exwm--connection) - (run-with-timer exwm-manage-ping-timeout nil - `(lambda () (exwm-manage--kill-client ,exwm--id))) - ;; Wait for DestroyNotify event. - (throw 'return nil)) - ;; Try to determine if the X window is dead with _NET_WM_PING. - (setq exwm-manage--ping-lock t) - (xcb:+request exwm--connection - (make-instance 'xcb:SendEvent - :propagate 0 - :destination exwm--id - :event-mask xcb:EventMask:NoEvent - :event (xcb:marshal - (make-instance 'xcb:ewmh:_NET_WM_PING - :window exwm--id - :timestamp 0 - :client-window exwm--id) - exwm--connection))) - (xcb:flush exwm--connection) - (with-timeout (exwm-manage-ping-timeout - (if (yes-or-no-p (format "'%s' is not responding. \ + ;; + (unless (memq xcb:Atom:_NET_WM_PING exwm--protocols) + ;; The window does not support _NET_WM_PING. To make sure it'll die, + ;; kill it after the time runs out. + ;; Hide the container to prevent flickering. + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow :window exwm--container)) + (xcb:flush exwm--connection) + (run-with-timer exwm-manage-ping-timeout nil + `(lambda () (exwm-manage--kill-client ,id))) + ;; Wait for DestroyNotify event. + (throw 'return nil)) + ;; Try to determine if the X window is dead with _NET_WM_PING. + (setq exwm-manage--ping-lock t) + (xcb:+request exwm--connection + (make-instance 'xcb:SendEvent + :propagate 0 + :destination id + :event-mask xcb:EventMask:NoEvent + :event (xcb:marshal + (make-instance 'xcb:ewmh:_NET_WM_PING + :window id + :timestamp 0 + :client-window id) + exwm--connection))) + (xcb:flush exwm--connection) + (with-timeout (exwm-manage-ping-timeout + (if (yes-or-no-p (format "'%s' is not responding. \ Would you like to kill it? " - (buffer-name))) - (progn (exwm-manage--kill-client exwm--id) - ;; Kill the unresponsive X window and - ;; wait for DestroyNotify event. - (throw 'return nil)) - ;; Give up. - (throw 'return nil))) - (while (and exwm-manage--ping-lock - (exwm--id->buffer exwm--id)) ;may have been destroyed. - (accept-process-output nil 0.1)) - ;; Give up. - (throw 'return nil)))) + (buffer-name))) + (progn (exwm-manage--kill-client id) + ;; Kill the unresponsive X window and + ;; wait for DestroyNotify event. + (throw 'return nil)) + ;; Give up. + (throw 'return nil))) + (while (and exwm-manage--ping-lock + (exwm--id->buffer id)) ;may have been destroyed. + (accept-process-output nil 0.1)) + ;; Give up. + (throw 'return nil))))) (defun exwm-manage--kill-client (&optional id) "Kill an X client." diff --git a/exwm-workspace.el b/exwm-workspace.el index 6fc38d4a32..b2fb648304 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -513,7 +513,15 @@ The optional FORCE option is for internal use only." (make-instance 'xcb:MapWindow :window (frame-parameter exwm-workspace--minibuffer 'exwm-container))) - (xcb:flush exwm--connection)) + (xcb:flush exwm--connection) + ;; Unfortunately we need the following lines to workaround a cursor + ;; flickering issue for line-mode floating X windows. They just make the + ;; minibuffer appear to be focused. + (with-current-buffer (window-buffer (minibuffer-window + exwm-workspace--minibuffer)) + (setq cursor-in-non-selected-windows + (frame-parameter exwm-workspace--minibuffer 'cursor-type)))) + (defun exwm-workspace--hide-minibuffer () "Hide the minibuffer frame." -- cgit 1.4.1 From 8706e490fb07617145a32ba9eaffd4ee37f67d87 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 4 Mar 2016 19:11:10 +0800 Subject: Allow moving/resizing undecorated X windows * exwm-manage.el (exwm-manage--manage-window): Do not manage undecorated floating X windows (set in _MOTIF_WM_HINTS). * exwm-floating.el (exwm-floating--start-moveresize) (exwm-floating--stop-moveresize, exwm-floating--do-moveresize): Allow moving/resizing undecorated X windows with _NET_WM_MOVERESIZE client message. --- exwm-floating.el | 152 ++++++++++++++++++++++++++++++++----------------------- exwm-manage.el | 7 +-- 2 files changed, 91 insertions(+), 68 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index ba162f67e1..cce6de0a7b 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -310,18 +310,23 @@ (defun exwm-floating--start-moveresize (id &optional type) "Start move/resize." - (let ((buffer (exwm--id->buffer id)) - frame container x y width height cursor) - (when (and buffer - (with-current-buffer buffer - (setq frame exwm--floating-frame - container exwm--container)) + (let ((buffer-or-id (or (exwm--id->buffer id) id)) + frame container-or-id x y width height cursor) + (if (bufferp buffer-or-id) + ;; Managed. + (with-current-buffer buffer-or-id + (setq frame exwm--floating-frame + container-or-id exwm--container)) + ;; Unmanaged. + (setq container-or-id id)) + (when (and container-or-id ;; Test if the pointer can be grabbed (= xcb:GrabStatus:Success (slot-value (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GrabPointer - :owner-events 0 :grab-window container + :owner-events 0 + :grab-window container-or-id :event-mask xcb:EventMask:NoEvent :pointer-mode xcb:GrabMode:Async :keyboard-mode xcb:GrabMode:Async @@ -332,33 +337,43 @@ (with-slots (root-x root-y win-x win-y) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:QueryPointer :window id)) - (select-window (frame-first-window frame)) ;transfer input focus - (setq width (frame-pixel-width frame) - height (frame-pixel-height frame)) - (unless type - ;; Determine the resize type according to the pointer position - ;; Clicking the center 1/3 part to resize has not effect - (setq x (/ (* 3 win-x) (float width)) - y (/ (* 3 win-y) (float height)) - type (cond ((and (< x 1) (< y 1)) - xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPLEFT) - ((and (> x 2) (< y 1)) - xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPRIGHT) - ((and (> x 2) (> y 2)) - xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT) - ((and (< x 1) (> y 2)) - xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT) - ((< y 1) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOP) - ((> x 2) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_RIGHT) - ((> y 2) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOM) - ((< x 1) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_LEFT)))) + (if (not (bufferp buffer-or-id)) + ;; Unmanaged. + (unless (eq type xcb:ewmh:_NET_WM_MOVERESIZE_MOVE) + (with-slots ((width* width) + (height* height)) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetGeometry :drawable id)) + (setq width width* + height height*))) + ;; Managed. + (select-window (frame-first-window frame)) ;transfer input focus + (setq width (frame-pixel-width frame) + height (frame-pixel-height frame)) + (unless type + ;; Determine the resize type according to the pointer position + ;; Clicking the center 1/3 part to resize has not effect + (setq x (/ (* 3 win-x) (float width)) + y (/ (* 3 win-y) (float height)) + type (cond ((and (< x 1) (< y 1)) + xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPLEFT) + ((and (> x 2) (< y 1)) + xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPRIGHT) + ((and (> x 2) (> y 2)) + xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT) + ((and (< x 1) (> y 2)) + xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT) + ((> x 2) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_RIGHT) + ((> y 2) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOM) + ((< x 1) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_LEFT) + ((< y 1) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOP))))) (if (not type) (exwm-floating--stop-moveresize) (cond ((= type xcb:ewmh:_NET_WM_MOVERESIZE_MOVE) (setq cursor exwm-floating--cursor-move exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,buffer + (vector ,buffer-or-id ,(eval-when-compile (logior xcb:ConfigWindow:X xcb:ConfigWindow:Y)) @@ -367,7 +382,7 @@ (setq cursor exwm-floating--cursor-top-left exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,buffer + (vector ,buffer-or-id ,(eval-when-compile (logior xcb:ConfigWindow:X xcb:ConfigWindow:Y @@ -380,7 +395,7 @@ (setq cursor exwm-floating--cursor-top exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,buffer + (vector ,buffer-or-id ,(eval-when-compile (logior xcb:ConfigWindow:Y xcb:ConfigWindow:Height)) @@ -389,7 +404,7 @@ (setq cursor exwm-floating--cursor-top-right exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,buffer + (vector ,buffer-or-id ,(eval-when-compile (logior xcb:ConfigWindow:Y xcb:ConfigWindow:Width @@ -400,13 +415,14 @@ (setq cursor exwm-floating--cursor-right exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,buffer ,xcb:ConfigWindow:Width + (vector ,buffer-or-id + ,xcb:ConfigWindow:Width 0 0 (- x ,(- root-x width)) 0)))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT) (setq cursor exwm-floating--cursor-bottom-right exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,buffer + (vector ,buffer-or-id ,(eval-when-compile (logior xcb:ConfigWindow:Width xcb:ConfigWindow:Height)) @@ -416,14 +432,14 @@ (setq cursor exwm-floating--cursor-bottom exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,buffer + (vector ,buffer-or-id ,xcb:ConfigWindow:Height 0 0 0 (- y ,(- root-y height)))))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT) (setq cursor exwm-floating--cursor-bottom-left exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,buffer + (vector ,buffer-or-id ,(eval-when-compile (logior xcb:ConfigWindow:X xcb:ConfigWindow:Width @@ -436,7 +452,7 @@ (setq cursor exwm-floating--cursor-left exwm-floating--moveresize-calculate `(lambda (x y) - (vector ,buffer + (vector ,buffer-or-id ,(eval-when-compile (logior xcb:ConfigWindow:X xcb:ConfigWindow:Width)) @@ -444,7 +460,7 @@ ;; Select events and change cursor (should always succeed) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GrabPointer - :owner-events 0 :grab-window container + :owner-events 0 :grab-window container-or-id :event-mask (eval-when-compile (logior xcb:EventMask:ButtonRelease xcb:EventMask:ButtonMotion)) @@ -459,7 +475,9 @@ (xcb:+request exwm--connection (make-instance 'xcb:UngrabPointer :time xcb:Time:CurrentTime)) ;; Inform the X window that its absolute position is changed - (when exwm-floating--moveresize-calculate + (when (and exwm-floating--moveresize-calculate + ;; Unmanaged. + (eq major-mode 'exwm-mode)) (let ((edges (window-inside-absolute-pixel-edges (frame-selected-window))) (id (with-current-buffer (window-buffer (frame-selected-window)) exwm--id))) @@ -490,7 +508,7 @@ (geometry (frame-parameter exwm-workspace--current 'exwm-geometry)) (frame-x 0) (frame-y 0) - result value-mask width height) + result value-mask width height buffer-or-id container-or-id) (when geometry (setq frame-x (slot-value geometry 'x) frame-y (slot-value geometry 'y))) @@ -503,30 +521,38 @@ xcb:ConfigWindow:Height))) width (aref result 4) height (aref result 5)) - (with-current-buffer (aref result 0) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window exwm--container - :value-mask (logand (aref result 1) - (eval-when-compile - (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y))) - :x (- (aref result 2) frame-x) - :y (- (aref result 3) frame-y))) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window (frame-parameter exwm--floating-frame - 'exwm-container) - :value-mask value-mask - :width width - :height height)) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window (frame-parameter exwm--floating-frame - 'exwm-outer-id) - :value-mask value-mask - :width width - :height height))) + (setq buffer-or-id (aref result 0)) + (setq container-or-id + (if (bufferp buffer-or-id) + ;; Managed. + (with-current-buffer buffer-or-id exwm--container) + ;; Unmanaged. + buffer-or-id)) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window container-or-id + :value-mask (aref result 1) + :x (- (aref result 2) frame-x) + :y (- (aref result 3) frame-y) + :width width + :height height)) + (when (bufferp buffer-or-id) + ;; Managed. + (with-current-buffer buffer-or-id + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter exwm--floating-frame + 'exwm-container) + :value-mask value-mask + :width width + :height height)) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter exwm--floating-frame + 'exwm-outer-id) + :value-mask value-mask + :width width + :height height)))) (xcb:flush exwm--connection)))) (defun exwm-floating-move (&optional delta-x delta-y) diff --git a/exwm-manage.el b/exwm-manage.el index 659e4267b7..9f9c844c86 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -99,15 +99,12 @@ corresponding buffer.") (memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY exwm-window-type) (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG exwm-window-type) (memq xcb:Atom:_NET_WM_WINDOW_TYPE_NORMAL exwm-window-type))) - ;; Check _MOTIF_WM_HINTS for Java applications - ;; See for the definitions of these fields + ;; Check the _MOTIF_WM_HINTS property. (and exwm--mwm-hints - exwm-instance-name + ;; See for fields definitions. (/= 0 (logand (elt exwm--mwm-hints 0) ;MotifWmHints.flags 2)) ;MWM_HINTS_DECORATIONS (= 0 (elt exwm--mwm-hints 2)) ;MotifWmHints.decorations - ;; Java applications only - (string-prefix-p "sun-awt-X11-" exwm-instance-name) ;; Floating windows only (or exwm-transient-for exwm--fixed-size (memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY -- cgit 1.4.1 From 6fe6fe52f619bc2e4648cebf137266363f0e19d4 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 6 Mar 2016 13:45:13 +0800 Subject: Untabify --- exwm-floating.el | 132 +++++++++++++++++++++++++++---------------------------- exwm-manage.el | 80 ++++++++++++++++----------------- exwm.el | 6 +-- 3 files changed, 109 insertions(+), 109 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index cce6de0a7b..a67bc1977e 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -313,10 +313,10 @@ (let ((buffer-or-id (or (exwm--id->buffer id) id)) frame container-or-id x y width height cursor) (if (bufferp buffer-or-id) - ;; Managed. - (with-current-buffer buffer-or-id - (setq frame exwm--floating-frame - container-or-id exwm--container)) + ;; Managed. + (with-current-buffer buffer-or-id + (setq frame exwm--floating-frame + container-or-id exwm--container)) ;; Unmanaged. (setq container-or-id id)) (when (and container-or-id @@ -326,7 +326,7 @@ (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GrabPointer :owner-events 0 - :grab-window container-or-id + :grab-window container-or-id :event-mask xcb:EventMask:NoEvent :pointer-mode xcb:GrabMode:Async :keyboard-mode xcb:GrabMode:Async @@ -337,36 +337,36 @@ (with-slots (root-x root-y win-x win-y) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:QueryPointer :window id)) - (if (not (bufferp buffer-or-id)) - ;; Unmanaged. - (unless (eq type xcb:ewmh:_NET_WM_MOVERESIZE_MOVE) - (with-slots ((width* width) - (height* height)) - (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:GetGeometry :drawable id)) - (setq width width* - height height*))) - ;; Managed. - (select-window (frame-first-window frame)) ;transfer input focus - (setq width (frame-pixel-width frame) - height (frame-pixel-height frame)) - (unless type - ;; Determine the resize type according to the pointer position - ;; Clicking the center 1/3 part to resize has not effect - (setq x (/ (* 3 win-x) (float width)) - y (/ (* 3 win-y) (float height)) - type (cond ((and (< x 1) (< y 1)) - xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPLEFT) - ((and (> x 2) (< y 1)) - xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPRIGHT) - ((and (> x 2) (> y 2)) - xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT) - ((and (< x 1) (> y 2)) - xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT) - ((> x 2) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_RIGHT) - ((> y 2) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOM) - ((< x 1) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_LEFT) - ((< y 1) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOP))))) + (if (not (bufferp buffer-or-id)) + ;; Unmanaged. + (unless (eq type xcb:ewmh:_NET_WM_MOVERESIZE_MOVE) + (with-slots ((width* width) + (height* height)) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetGeometry :drawable id)) + (setq width width* + height height*))) + ;; Managed. + (select-window (frame-first-window frame)) ;transfer input focus + (setq width (frame-pixel-width frame) + height (frame-pixel-height frame)) + (unless type + ;; Determine the resize type according to the pointer position + ;; Clicking the center 1/3 part to resize has not effect + (setq x (/ (* 3 win-x) (float width)) + y (/ (* 3 win-y) (float height)) + type (cond ((and (< x 1) (< y 1)) + xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPLEFT) + ((and (> x 2) (< y 1)) + xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPRIGHT) + ((and (> x 2) (> y 2)) + xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT) + ((and (< x 1) (> y 2)) + xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT) + ((> x 2) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_RIGHT) + ((> y 2) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOM) + ((< x 1) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_LEFT) + ((< y 1) xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOP))))) (if (not type) (exwm-floating--stop-moveresize) (cond ((= type xcb:ewmh:_NET_WM_MOVERESIZE_MOVE) @@ -416,7 +416,7 @@ exwm-floating--moveresize-calculate `(lambda (x y) (vector ,buffer-or-id - ,xcb:ConfigWindow:Width + ,xcb:ConfigWindow:Width 0 0 (- x ,(- root-x width)) 0)))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT) (setq cursor exwm-floating--cursor-bottom-right @@ -476,8 +476,8 @@ (make-instance 'xcb:UngrabPointer :time xcb:Time:CurrentTime)) ;; Inform the X window that its absolute position is changed (when (and exwm-floating--moveresize-calculate - ;; Unmanaged. - (eq major-mode 'exwm-mode)) + ;; Unmanaged. + (eq major-mode 'exwm-mode)) (let ((edges (window-inside-absolute-pixel-edges (frame-selected-window))) (id (with-current-buffer (window-buffer (frame-selected-window)) exwm--id))) @@ -523,36 +523,36 @@ height (aref result 5)) (setq buffer-or-id (aref result 0)) (setq container-or-id - (if (bufferp buffer-or-id) - ;; Managed. - (with-current-buffer buffer-or-id exwm--container) - ;; Unmanaged. - buffer-or-id)) + (if (bufferp buffer-or-id) + ;; Managed. + (with-current-buffer buffer-or-id exwm--container) + ;; Unmanaged. + buffer-or-id)) (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window container-or-id - :value-mask (aref result 1) - :x (- (aref result 2) frame-x) - :y (- (aref result 3) frame-y) - :width width - :height height)) + (make-instance 'xcb:ConfigureWindow + :window container-or-id + :value-mask (aref result 1) + :x (- (aref result 2) frame-x) + :y (- (aref result 3) frame-y) + :width width + :height height)) (when (bufferp buffer-or-id) - ;; Managed. - (with-current-buffer buffer-or-id - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window (frame-parameter exwm--floating-frame - 'exwm-container) - :value-mask value-mask - :width width - :height height)) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window (frame-parameter exwm--floating-frame - 'exwm-outer-id) - :value-mask value-mask - :width width - :height height)))) + ;; Managed. + (with-current-buffer buffer-or-id + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter exwm--floating-frame + 'exwm-container) + :value-mask value-mask + :width width + :height height)) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter exwm--floating-frame + 'exwm-outer-id) + :value-mask value-mask + :width width + :height height)))) (xcb:flush exwm--connection)))) (defun exwm-floating-move (&optional delta-x delta-y) diff --git a/exwm-manage.el b/exwm-manage.el index 9f9c844c86..adc8519664 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -101,7 +101,7 @@ corresponding buffer.") (memq xcb:Atom:_NET_WM_WINDOW_TYPE_NORMAL exwm-window-type))) ;; Check the _MOTIF_WM_HINTS property. (and exwm--mwm-hints - ;; See for fields definitions. + ;; See for fields definitions. (/= 0 (logand (elt exwm--mwm-hints 0) ;MotifWmHints.flags 2)) ;MWM_HINTS_DECORATIONS (= 0 (elt exwm--mwm-hints 2)) ;MotifWmHints.decorations @@ -334,54 +334,54 @@ corresponding buffer.") (let ((id exwm--id)) ;; Try to close the X window with WM_DELETE_WINDOW client message. (xcb:+request exwm--connection - (make-instance 'xcb:icccm:SendEvent - :destination id - :event (xcb:marshal - (make-instance 'xcb:icccm:WM_DELETE_WINDOW - :window id) - exwm--connection))) + (make-instance 'xcb:icccm:SendEvent + :destination id + :event (xcb:marshal + (make-instance 'xcb:icccm:WM_DELETE_WINDOW + :window id) + exwm--connection))) (xcb:flush exwm--connection) ;; (unless (memq xcb:Atom:_NET_WM_PING exwm--protocols) - ;; The window does not support _NET_WM_PING. To make sure it'll die, - ;; kill it after the time runs out. - ;; Hide the container to prevent flickering. - (xcb:+request exwm--connection - (make-instance 'xcb:UnmapWindow :window exwm--container)) - (xcb:flush exwm--connection) - (run-with-timer exwm-manage-ping-timeout nil - `(lambda () (exwm-manage--kill-client ,id))) - ;; Wait for DestroyNotify event. - (throw 'return nil)) + ;; The window does not support _NET_WM_PING. To make sure it'll die, + ;; kill it after the time runs out. + ;; Hide the container to prevent flickering. + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow :window exwm--container)) + (xcb:flush exwm--connection) + (run-with-timer exwm-manage-ping-timeout nil + `(lambda () (exwm-manage--kill-client ,id))) + ;; Wait for DestroyNotify event. + (throw 'return nil)) ;; Try to determine if the X window is dead with _NET_WM_PING. (setq exwm-manage--ping-lock t) (xcb:+request exwm--connection - (make-instance 'xcb:SendEvent - :propagate 0 - :destination id - :event-mask xcb:EventMask:NoEvent - :event (xcb:marshal - (make-instance 'xcb:ewmh:_NET_WM_PING - :window id - :timestamp 0 - :client-window id) - exwm--connection))) + (make-instance 'xcb:SendEvent + :propagate 0 + :destination id + :event-mask xcb:EventMask:NoEvent + :event (xcb:marshal + (make-instance 'xcb:ewmh:_NET_WM_PING + :window id + :timestamp 0 + :client-window id) + exwm--connection))) (xcb:flush exwm--connection) (with-timeout (exwm-manage-ping-timeout - (if (yes-or-no-p (format "'%s' is not responding. \ + (if (yes-or-no-p (format "'%s' is not responding. \ Would you like to kill it? " - (buffer-name))) - (progn (exwm-manage--kill-client id) - ;; Kill the unresponsive X window and - ;; wait for DestroyNotify event. - (throw 'return nil)) - ;; Give up. - (throw 'return nil))) - (while (and exwm-manage--ping-lock - (exwm--id->buffer id)) ;may have been destroyed. - (accept-process-output nil 0.1)) - ;; Give up. - (throw 'return nil))))) + (buffer-name))) + (progn (exwm-manage--kill-client id) + ;; Kill the unresponsive X window and + ;; wait for DestroyNotify event. + (throw 'return nil)) + ;; Give up. + (throw 'return nil))) + (while (and exwm-manage--ping-lock + (exwm--id->buffer id)) ;may have been destroyed. + (accept-process-output nil 0.1)) + ;; Give up. + (throw 'return nil))))) (defun exwm-manage--kill-client (&optional id) "Kill an X client." diff --git a/exwm.el b/exwm.el index 39eb35004d..e60b341bc3 100644 --- a/exwm.el +++ b/exwm.el @@ -326,14 +326,14 @@ (= action xcb:ewmh:_NET_WM_STATE_ADD)) (dolist (f exwm-workspace--list) (when (equal (frame-parameter f 'exwm-outer-id) id) - (exwm-layout--set-frame-fullscreen f) - (xcb:+request + (exwm-layout--set-frame-fullscreen f) + (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_STATE :window id :data (vector xcb:Atom:_NET_WM_STATE_FULLSCREEN))) - (xcb:flush exwm--connection)))) + (xcb:flush exwm--connection)))) (when buffer ;ensure it's managed (with-current-buffer buffer ;; _NET_WM_STATE_MODAL -- cgit 1.4.1 From f0a5425f4c176c487a5c3e94319f0c2b3d3cc22a Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 6 Mar 2016 14:28:42 +0800 Subject: Allow floating X windows to resize themselves * exwm-manage.el (exwm-manage--on-ConfigureRequest): Allow (non-fullscreen) floating X windows to resize themselves. --- exwm-manage.el | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index adc8519664..bb6d5ffe4c 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -419,7 +419,9 @@ Would you like to kill it? " border-width: %d; sibling: #x%x; stack-mode: %d" window value-mask width height x y border-width sibling stack-mode) - (if (setq buffer (exwm--id->buffer window)) + (if (and (setq buffer (exwm--id->buffer window)) + (with-current-buffer buffer + (or exwm--fullscreen (not exwm--floating-frame)))) ;; Send client message for managed windows (with-current-buffer buffer (setq edges @@ -444,16 +446,29 @@ border-width: %d; sibling: #x%x; stack-mode: %d" :height (- (elt edges 3) (elt edges 1)) :border-width 0 :override-redirect 0) exwm--connection)))) - (exwm--log "ConfigureWindow (preserve geometry)") - ;; Configure the unmanaged window. - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window window - :value-mask value-mask - :x x :y y :width width :height height - :border-width border-width - :sibling sibling - :stack-mode stack-mode))))) + (if buffer + (with-current-buffer buffer + (exwm--log "ConfigureWindow (resize floating X window)") + (setq edges + (window-inside-pixel-edges (get-buffer-window buffer t))) + (set-frame-size exwm--floating-frame + (+ width + (- (frame-pixel-width exwm--floating-frame) + (- (elt edges 2) (elt edges 0)))) + (+ height + (- (frame-pixel-height exwm--floating-frame) + (- (elt edges 3) (elt edges 1)))) + t)) + (exwm--log "ConfigureWindow (preserve geometry)") + ;; Configure the unmanaged window. + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window window + :value-mask value-mask + :x x :y y :width width :height height + :border-width border-width + :sibling sibling + :stack-mode stack-mode)))))) (xcb:flush exwm--connection)) (defun exwm-manage--on-MapRequest (data _synthetic) -- cgit 1.4.1 From 1342fe1789184bb6b732d62bdb673139d44b83f0 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Mon, 7 Mar 2016 00:00:00 +0000 Subject: Change input mode of window being clicked When clicking a mode-line of other window to switch the EXWM input mode, `window-buffer' does not return the window whose mode-line has been clicked, but the current one. This change ensures that the right window has its input mode and mode-line updated. * exwm-input.el (exwm-input--update-mode-line): Factor out setting `mode-line-process'. (exwm-input--grab-keyboard, exwm-input--release-keyboard) (exwm-input-grab-keyboard, exwm-input-release-keyboard): Make sure the buffer of the window being clicked has its input mode updated. --- exwm-input.el | 72 ++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 28 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 31125a25c6..50ca3aef22 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -318,6 +318,34 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") :time xcb:Time:CurrentTime)) (xcb:flush exwm--connection)) +(defun exwm-input--update-mode-line (id) + "Update the propertized `mode-line-process' for window ID." + (let (help-echo cmd mode) + (case exwm--on-KeyPress + ((exwm-input--on-KeyPress-line-mode) + (setq mode "line" + help-echo "mouse-1: Switch to char-mode" + cmd `(lambda () + (interactive) + (exwm-input-release-keyboard ,id)))) + ((exwm-input--on-KeyPress-char-mode) + (setq mode "char" + help-echo "mouse-1: Switch to line-mode" + cmd `(lambda () + (interactive) + (exwm-input-grab-keyboard ,id))))) + (with-current-buffer (exwm--id->buffer id) + (setq mode-line-process + `(": " + (:propertize ,mode + help-echo ,help-echo + mouse-face mode-line-highlight + local-map + (keymap + (mode-line + keymap + (down-mouse-1 . ,cmd))))))))) + (defun exwm-input--grab-keyboard (&optional id) "Grab all key events on window ID." (unless id (setq id (exwm--buffer->id (window-buffer)))) @@ -331,7 +359,8 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") :pointer-mode xcb:GrabMode:Async :keyboard-mode xcb:GrabMode:Sync)) (exwm--log "Failed to grab keyboard for #x%x" id)) - (setq exwm--on-KeyPress #'exwm-input--on-KeyPress-line-mode))) + (with-current-buffer (exwm--id->buffer id) + (setq exwm--on-KeyPress #'exwm-input--on-KeyPress-line-mode)))) (defun exwm-input--release-keyboard (&optional id) "Ungrab all key events on window ID." @@ -343,41 +372,28 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") :grab-window id :modifiers xcb:ModMask:Any)) (exwm--log "Failed to release keyboard for #x%x" id)) - (setq exwm--on-KeyPress #'exwm-input--on-KeyPress-char-mode))) + (with-current-buffer (exwm--id->buffer id) + (setq exwm--on-KeyPress #'exwm-input--on-KeyPress-char-mode)))) ;;;###autoload (defun exwm-input-grab-keyboard (&optional id) "Switch to line-mode." - (interactive) - (exwm-input--grab-keyboard id) - (setq mode-line-process - '(": " - (:propertize "line" - help-echo "mouse-1: Switch to char-mode" - mouse-face mode-line-highlight - local-map - (keymap - (mode-line - keymap - (down-mouse-1 . exwm-input-release-keyboard)))))) - (force-mode-line-update)) + (interactive (list (exwm--buffer->id (window-buffer)))) + (when id + (with-current-buffer (exwm--id->buffer id) + (exwm-input--grab-keyboard id) + (exwm-input--update-mode-line id) + (force-mode-line-update)))) ;;;###autoload (defun exwm-input-release-keyboard (&optional id) "Switch to char-mode." - (interactive) - (exwm-input--release-keyboard id) - (setq mode-line-process - '(": " - (:propertize "char" - help-echo "mouse-1: Switch to line-mode" - mouse-face mode-line-highlight - local-map - (keymap - (mode-line - keymap - (down-mouse-1 . exwm-input-grab-keyboard)))))) - (force-mode-line-update)) + (interactive (list (exwm--buffer->id (window-buffer)))) + (when id + (with-current-buffer (exwm--id->buffer id) + (exwm-input--release-keyboard id) + (exwm-input--update-mode-line id) + (force-mode-line-update)))) (defun exwm-input--fake-key (event) "Fake a key event equivalent to Emacs event EVENT." -- cgit 1.4.1 From a50e6bd384d2157cb5070f138eb64d23496cf7ac Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 7 Mar 2016 18:16:04 +0800 Subject: Restack fullscreen X windows * exwm-layout.el (exwm-layout-set-fullscreen) (exwm-layout-unset-fullscreen): Raise and lower fullscreen X windows respectively. --- exwm-layout.el | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index 76721a01a1..309bfb6fb6 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -165,6 +165,12 @@ (exwm-layout--resize-container exwm--id exwm--container 0 0 (exwm-workspace--current-width) (exwm-workspace--current-height)) + ;; Raise the X window. + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window exwm--container + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Above)) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_STATE :window exwm--id @@ -180,16 +186,25 @@ (with-current-buffer (if id (exwm--id->buffer id) (window-buffer)) (unless exwm--fullscreen (user-error "Not in full-screen mode.")) - ;; Restore the floating frame if the client is floating - (when exwm--floating-frame + (if exwm--floating-frame + ;; Restore the floating frame. + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window exwm--container + :value-mask (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y)) + :x (elt exwm--floating-frame-position 0) + :y (elt exwm--floating-frame-position 1))) + ;; Put the X window just above the Emacs frame. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window exwm--container - :value-mask (eval-when-compile - (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y)) - :x (elt exwm--floating-frame-position 0) - :y (elt exwm--floating-frame-position 1)))) + :value-mask (logior xcb:ConfigWindow:Sibling + xcb:ConfigWindow:StackMode) + :sibling (frame-parameter exwm-workspace--current + 'exwm-container) + :stack-mode xcb:StackMode:Above))) (exwm-layout--show exwm--id) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_STATE :window exwm--id :data [])) -- cgit 1.4.1 From ac3f8098c7b8d87225a34e85fcac8996404d1d8d Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 8 Mar 2016 13:01:05 +0800 Subject: Use `cl-case' instead of `case' --- exwm-input.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm-input.el b/exwm-input.el index 50ca3aef22..dc7f2b54ef 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -321,7 +321,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (defun exwm-input--update-mode-line (id) "Update the propertized `mode-line-process' for window ID." (let (help-echo cmd mode) - (case exwm--on-KeyPress + (cl-case exwm--on-KeyPress ((exwm-input--on-KeyPress-line-mode) (setq mode "line" help-echo "mouse-1: Switch to char-mode" -- cgit 1.4.1 From c8c0bc7b60c043d47e153e40c2ae139f513e9358 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 11 Mar 2016 14:12:01 +0800 Subject: Neglect trivial resizing requests * exwm-manage.el (exwm-manage--on-ConfigureRequest): Neglect trivial resizing requests since that cannot be done precisely. --- exwm-manage.el | 52 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index bb6d5ffe4c..b0657b920c 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -407,10 +407,14 @@ Would you like to kill it? " (xcb:+request exwm--connection ,request)))) (xcb:flush exwm--connection))) +;; FIXME: Make the following values as small as possible. +(defconst exwm-manage--width-delta-min 5) +(defconst exwm-manage--height-delta-min 5) + (defun exwm-manage--on-ConfigureRequest (data _synthetic) "Handle ConfigureRequest event." (let ((obj (make-instance 'xcb:ConfigureRequest)) - buffer edges) + buffer edges width-delta height-delta) (xcb:unmarshal obj data) (with-slots (window x y width height border-width sibling stack-mode value-mask) @@ -421,7 +425,29 @@ border-width: %d; sibling: #x%x; stack-mode: %d" border-width sibling stack-mode) (if (and (setq buffer (exwm--id->buffer window)) (with-current-buffer buffer - (or exwm--fullscreen (not exwm--floating-frame)))) + (or exwm--fullscreen + ;; Make sure it's a floating X window wanting to resize + ;; itself. + (or (not exwm--floating-frame) + (progn + (setq edges + (window-inside-pixel-edges + (get-buffer-window buffer t)) + width-delta (- width (- (elt edges 2) + (elt edges 0))) + height-delta (- height (- (elt edges 3) + (elt edges 1)))) + ;; We cannot do resizing precisely for now. + (and (if (= 0 (logand value-mask + xcb:ConfigWindow:Width)) + t + (< (abs width-delta) + exwm-manage--width-delta-min)) + (if (= 0 (logand value-mask + xcb:ConfigWindow:Height)) + t + (< (abs height-delta) + exwm-manage--height-delta-min)))))))) ;; Send client message for managed windows (with-current-buffer buffer (setq edges @@ -449,16 +475,18 @@ border-width: %d; sibling: #x%x; stack-mode: %d" (if buffer (with-current-buffer buffer (exwm--log "ConfigureWindow (resize floating X window)") - (setq edges - (window-inside-pixel-edges (get-buffer-window buffer t))) - (set-frame-size exwm--floating-frame - (+ width - (- (frame-pixel-width exwm--floating-frame) - (- (elt edges 2) (elt edges 0)))) - (+ height - (- (frame-pixel-height exwm--floating-frame) - (- (elt edges 3) (elt edges 1)))) - t)) + (when (and (/= 0 (logand value-mask xcb:ConfigWindow:Width)) + (>= (abs width-delta) exwm-manage--width-delta-min)) + (set-frame-width exwm--floating-frame + (+ (frame-pixel-width exwm--floating-frame) + width-delta) + nil t)) + (when (and (/= 0 (logand value-mask xcb:ConfigWindow:Height)) + (>= (abs height-delta) exwm-manage--height-delta-min)) + (set-frame-height exwm--floating-frame + (+ (frame-pixel-height exwm--floating-frame) + height-delta) + nil t))) (exwm--log "ConfigureWindow (preserve geometry)") ;; Configure the unmanaged window. (xcb:+request exwm--connection -- cgit 1.4.1 From 3cef44a6ca2093500b092f99dc588fb8bf9c184d Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 16 Mar 2016 12:34:38 +0800 Subject: Exclude unmanaged floating X windows when refreshing * exwm-layout.el (exwm-layout--refresh): Do not show unmanaged floating X windows. --- exwm-layout.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index 309bfb6fb6..6825cb56d3 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -258,8 +258,10 @@ (when (eq major-mode 'exwm-mode) (let ((window (frame-first-window frame))) (with-current-buffer (window-buffer window) - (exwm--log "Refresh floating window #x%x" exwm--id) - (exwm-layout--show exwm--id window)))) + ;; It may be a buffer waiting to be killed. + (when (exwm--id->buffer exwm--id) + (exwm--log "Refresh floating window #x%x" exwm--id) + (exwm-layout--show exwm--id window))))) ;; Other frames (e.g. terminal/graphical frame of emacsclient) ;; We shall bury all `exwm-mode' buffers in this case (setq windows (window-list frame 0)) ;exclude minibuffer -- cgit 1.4.1 From f6cd9503e62164f9e2513755f95f7c61eeddf51e Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Sat, 19 Mar 2016 00:00:00 +0000 Subject: Minor cleanup * exwm-layout.el (exwm-layout--refresh): Reuse car. --- exwm-layout.el | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index 6825cb56d3..fd8c164d9a 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -282,11 +282,11 @@ (if (not windows) (when (eq frame exwm--frame) ;for exwm-layout-show-all-buffers (exwm-layout--hide exwm--id)) - (if (eq frame exwm--frame) - (exwm-layout--show exwm--id (car windows)) - (exwm-workspace-move-window - (cl-position frame exwm-workspace--list) exwm--id)) (let ((window (car windows))) + (if (eq frame exwm--frame) + (exwm-layout--show exwm--id window) + (exwm-workspace-move-window + (cl-position frame exwm-workspace--list) exwm--id)) ;; Make sure this buffer is not displayed elsewhere (dolist (i (get-buffer-window-list (current-buffer) 0 t)) (unless (eq i window) -- cgit 1.4.1 From 8a1c3761e4eab018a001dcc522112269f83ba279 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Sat, 19 Mar 2016 00:00:00 +0000 Subject: Set the correct buffer before checking the `major-mode' * exwm-layout.el (exwm-layout--refresh): Make sure we test the `major-mode' of the first buffer of the floating frame. --- exwm-layout.el | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index fd8c164d9a..d084c96e44 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -255,13 +255,13 @@ (if (not (memq frame exwm-workspace--list)) (if (frame-parameter frame 'exwm-outer-id) ;; Refresh a floating frame - (when (eq major-mode 'exwm-mode) - (let ((window (frame-first-window frame))) - (with-current-buffer (window-buffer window) - ;; It may be a buffer waiting to be killed. - (when (exwm--id->buffer exwm--id) - (exwm--log "Refresh floating window #x%x" exwm--id) - (exwm-layout--show exwm--id window))))) + (let ((window (frame-first-window frame))) + (with-current-buffer (window-buffer window) + (when (and (eq major-mode 'exwm-mode) + ;; It may be a buffer waiting to be killed. + (exwm--id->buffer exwm--id)) + (exwm--log "Refresh floating window #x%x" exwm--id) + (exwm-layout--show exwm--id window)))) ;; Other frames (e.g. terminal/graphical frame of emacsclient) ;; We shall bury all `exwm-mode' buffers in this case (setq windows (window-list frame 0)) ;exclude minibuffer -- cgit 1.4.1 From 3763195fe3205090e1f405970421662523b7df7e Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Sat, 19 Mar 2016 00:00:00 +0000 Subject: Use `buffer-predicate' frame parameter to prevent switching to visible EXWM buffers * exwm-layout.el (exwm-layout--other-buffer-predicate): New function to be set as `buffer-predicate' frame parameter. * exwm-workspace.el (exwm-workspace--init): Use above function on workspace frames. --- exwm-layout.el | 11 +++++++++++ exwm-workspace.el | 4 ++++ 2 files changed, 15 insertions(+) diff --git a/exwm-layout.el b/exwm-layout.el index d084c96e44..b0b9467750 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -240,6 +240,17 @@ (xcb:flush exwm--connection))) (cl-incf exwm-layout--fullscreen-frame-count)) +(defun exwm-layout--other-buffer-predicate (buffer) + "Return non-nil when the BUFFER may be displayed in selected frame. + +Prevents EXWM-mode buffers already being displayed on some other window from +being selected. + +Should be set as `buffer-predicate' frame parameter for all +frames. Used by `other-buffer'." + (not (and (eq 'exwm-mode (buffer-local-value 'major-mode buffer)) + (get-buffer-window buffer t)))) + (defvar exwm-layout-show-all-buffers nil "Non-nil to allow switching to buffers on other workspaces.") diff --git a/exwm-workspace.el b/exwm-workspace.el index b2fb648304..c9a57f5a3d 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -235,6 +235,7 @@ The optional FORCE option is for internal use only." (declare-function exwm-layout--show "exwm-layout.el" (id &optional window)) (declare-function exwm-layout--hide "exwm-layout.el" (id)) (declare-function exwm-layout--refresh "exwm-layout.el") +(declare-function exwm-layout--other-buffer-predicate "exwm-layout.el" (buffer)) ;;;###autoload (defun exwm-workspace-move-window (index &optional id) @@ -684,6 +685,9 @@ The optional FORCE option is for internal use only." ;; The default behavior of `display-buffer' (indirectly called by ;; `minibuffer-completion-help') is not correct here. (cl-pushnew '(exwm-workspace--display-buffer) display-buffer-alist)) + ;; Prevent `other-buffer' from selecting already displayed EXWM buffers. + (modify-all-frames-parameters + '((buffer-predicate . exwm-layout--other-buffer-predicate))) ;; Configure workspaces (dolist (i exwm-workspace--list) (let ((outer-id (string-to-number (frame-parameter i 'outer-window-id))) -- cgit 1.4.1 From e4704479a85e3c1d02a2eb8116bc42dad63608b9 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Sat, 19 Mar 2016 00:00:00 +0000 Subject: Use `other-buffer' instead of "*scratch*" * exwm-workspace.el (exwm-workspace-move-window): Display `other-buffer' instead of "*scratch*" in the window whose buffer has been made floating. --- exwm-workspace.el | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index c9a57f5a3d..d8e84f3f87 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -347,11 +347,7 @@ The optional FORCE option is for internal use only." ;; Move the X window container. (if (= index exwm-workspace-current-index) (set-window-buffer (get-buffer-window (current-buffer) t) - (or (get-buffer "*scratch*") - (progn - (set-buffer-major-mode - (get-buffer-create "*scratch*")) - (get-buffer "*scratch*")))) + (other-buffer)) (bury-buffer) ;; Clear the 'exwm-selected-window' frame parameter. (set-frame-parameter frame 'exwm-selected-window nil)) -- cgit 1.4.1 From 4d6b19aece7de841226b65d1945a7ac4c146c807 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Sat, 19 Mar 2016 00:00:00 +0000 Subject: Restore a sensible buffer when replacing EXWM buffers * exwm-layout.el (exwm-layout--other-buffer-exclude-exwm-mode-buffers) (exwm-layout--other-buffer-exclude-buffers): New variables. (exwm-layout--other-buffer-predicate): Allow excluding EXWM buffers or buffers from a given set. (exwm-layout--refresh): Replace EXWM buffers with sensible buffers depending on the situation. When in non-workspace/non-floating frames, with some non-EXWM buffer; when the EXWM buffer is displayed elsewhere, some buffer previously displayed in that window (making sure it has been recently covered). --- exwm-layout.el | 71 +++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 21 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index b0b9467750..095e4611b1 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -240,6 +240,12 @@ (xcb:flush exwm--connection))) (cl-incf exwm-layout--fullscreen-frame-count)) +(defvar exwm-layout--other-buffer-exclude-exwm-mode-buffers nil + "When non-nil, prevent EXWM buffers from being selected by `other-buffer'.") + +(defvar exwm-layout--other-buffer-exclude-buffers nil + "List of buffers that should not be selected by `other-buffer'.") + (defun exwm-layout--other-buffer-predicate (buffer) "Return non-nil when the BUFFER may be displayed in selected frame. @@ -247,9 +253,19 @@ Prevents EXWM-mode buffers already being displayed on some other window from being selected. Should be set as `buffer-predicate' frame parameter for all -frames. Used by `other-buffer'." - (not (and (eq 'exwm-mode (buffer-local-value 'major-mode buffer)) - (get-buffer-window buffer t)))) +frames. Used by `other-buffer'. + +When variable `exwm-layout--other-buffer-exclude-exwm-mode-buffers' +is t EXWM buffers are never selected by `other-buffer'. + +When variable `exwm-layout--other-buffer-exclude-buffers' is a +list of buffers, EXWM buffers belonging to that list are never +selected by `other-buffer'." + (or (not (eq 'exwm-mode (buffer-local-value 'major-mode buffer))) + (and (not exwm-layout--other-buffer-exclude-exwm-mode-buffers) + (not (memq buffer exwm-layout--other-buffer-exclude-buffers)) + ;; Do not select if already shown in some window. + (not (get-buffer-window buffer t))))) (defvar exwm-layout-show-all-buffers nil "Non-nil to allow switching to buffers on other workspaces.") @@ -257,11 +273,8 @@ frames. Used by `other-buffer'." (defun exwm-layout--refresh () "Refresh layout." (let ((frame (selected-frame)) - (placeholder (or (get-buffer "*scratch*") - (progn - (set-buffer-major-mode - (get-buffer-create "*scratch*")) - (get-buffer "*scratch*")))) + covered-buffers ;EXWM-buffers covered by a new X window. + vacated-windows ;Windows previously displaying EXWM-buffers. windows) (if (not (memq frame exwm-workspace--list)) (if (frame-parameter frame 'exwm-outer-id) @@ -276,10 +289,11 @@ frames. Used by `other-buffer'." ;; Other frames (e.g. terminal/graphical frame of emacsclient) ;; We shall bury all `exwm-mode' buffers in this case (setq windows (window-list frame 0)) ;exclude minibuffer - (dolist (window windows) - (with-current-buffer (window-buffer window) - (when (eq major-mode 'exwm-mode) - (set-window-buffer window placeholder))))) + (let ((exwm-layout--other-buffer-exclude-exwm-mode-buffers t)) + (dolist (window windows) + (with-current-buffer (window-buffer window) + (when (eq major-mode 'exwm-mode) + (switch-to-prev-buffer window)))))) ;; Refresh the whole workspace ;; Workspaces other than the active one can also be refreshed (RandR) (exwm--log "Refresh workspace %s" frame) @@ -298,16 +312,31 @@ frames. Used by `other-buffer'." (exwm-layout--show exwm--id window) (exwm-workspace-move-window (cl-position frame exwm-workspace--list) exwm--id)) - ;; Make sure this buffer is not displayed elsewhere - (dolist (i (get-buffer-window-list (current-buffer) 0 t)) - (unless (eq i window) - (set-window-buffer i placeholder)))))))) + ;; Make sure this buffer is not displayed elsewhere. Note down + ;; windows displaying an EXWM-buffer now displayed elsewhere; we + ;; need to display with some other buffer there. + (setq vacated-windows + (append vacated-windows (cdr (get-buffer-window-list + (current-buffer) 0 t)))) + ;; Note down when an EXWM-buffer is being covered by this + ;; buffer; we don't want it to reappear in some vacated window. + (let ((prev-buffer (car-safe + (car-safe (window-prev-buffers window))))) + (and + prev-buffer + (eq 'exwm-mode (buffer-local-value 'major-mode prev-buffer)) + (push prev-buffer covered-buffers)))))))) + ;; Set some sensible buffer to vacated windows. + (let ((exwm-layout--other-buffer-exclude-buffers covered-buffers)) + (dolist (window vacated-windows) + (switch-to-prev-buffer window))) ;; Make sure windows floating / on other workspaces are excluded - (dolist (window (window-list frame 0)) - (with-current-buffer (window-buffer window) - (when (and (eq major-mode 'exwm-mode) - (or exwm--floating-frame (not (eq frame exwm--frame)))) - (set-window-buffer window placeholder)))) + (let ((exwm-layout--other-buffer-exclude-exwm-mode-buffers t)) + (dolist (window (window-list frame 0)) + (with-current-buffer (window-buffer window) + (when (and (eq major-mode 'exwm-mode) + (or exwm--floating-frame (not (eq frame exwm--frame)))) + (switch-to-prev-buffer window))))) ;; Update _NET_CLIENT_LIST_STACKING (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_CLIENT_LIST_STACKING -- cgit 1.4.1 From fa204e136718e106a87414dd145d16748d5b7262 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 25 Mar 2016 13:57:42 +0800 Subject: Fix emacsclient issues * exwm-layout.el (exwm-layout--on-minibuffer-setup) (exwm-layout--on-echo-area-change): * exwm-workspace.el (exwm-workspace--on-minibuffer-setup) (exwm-workspace--on-minibuffer-exit, exwm-workspace--on-echo-area-dirty) (exwm-workspace--on-echo-area-clear): Exclude non-graphical frames. * exwm.el (exwm--server-eval-at): Avoid using `x-dispaly-name'. --- exwm-layout.el | 15 +++++++++------ exwm-workspace.el | 21 ++++++++++++++------- exwm.el | 2 +- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index 095e4611b1..c392ff9724 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -353,16 +353,19 @@ selected by `other-buffer'." (defun exwm-layout--on-minibuffer-setup () "Refresh layout when minibuffer grows." - (run-with-idle-timer 0.01 nil ;FIXME - (lambda () - (when (< 1 (window-height (minibuffer-window))) - (exwm-layout--refresh)))) - ;; Set input focus on the Emacs frame - (x-focus-frame (window-frame (minibuffer-selected-window)))) + (when (frame-parameter nil 'exwm-outer-id) + (run-with-idle-timer 0.01 nil ;FIXME + (lambda () + (when (< 1 (window-height (minibuffer-window))) + (exwm-layout--refresh)))) + ;; Set input focus on the Emacs frame + (x-focus-frame (window-frame (minibuffer-selected-window))))) (defun exwm-layout--on-echo-area-change (&optional dirty) "Run when message arrives or in `echo-area-clear-hook' to refresh layout." (when (and (current-message) + ;; Exclude non-graphical frames. + (frame-parameter nil 'exwm-outer-id) (or (cl-position ?\n (current-message)) (> (length (current-message)) (frame-width exwm-workspace--current)))) diff --git a/exwm-workspace.el b/exwm-workspace.el index d8e84f3f87..8dd6284d1d 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -531,7 +531,9 @@ The optional FORCE option is for internal use only." (defun exwm-workspace--on-minibuffer-setup () "Run in minibuffer-setup-hook to show the minibuffer and its container." - (unless (> -1 (minibuffer-depth)) + (when (and (= 1 (minibuffer-depth)) + ;; Exclude non-graphical frames. + (frame-parameter nil 'exwm-outer-id)) (add-hook 'post-command-hook #'exwm-workspace--update-minibuffer) (exwm-workspace--show-minibuffer) ;; Set input focus on the Emacs frame @@ -539,7 +541,9 @@ The optional FORCE option is for internal use only." (defun exwm-workspace--on-minibuffer-exit () "Run in minibuffer-exit-hook to hide the minibuffer container." - (unless (> -1 (minibuffer-depth)) + (when (and (= 1 (minibuffer-depth)) + ;; Exclude non-graphical frames. + (frame-parameter nil 'exwm-outer-id)) (remove-hook 'post-command-hook #'exwm-workspace--update-minibuffer) (exwm-workspace--hide-minibuffer))) @@ -548,6 +552,8 @@ The optional FORCE option is for internal use only." (defun exwm-workspace--on-echo-area-dirty () "Run when new message arrives to show the echo area and its container." (when (and (not (active-minibuffer-window)) + ;; Exclude non-graphical frames. + (frame-parameter nil 'exwm-outer-id) (or (current-message) cursor-in-echo-area)) (exwm-workspace--update-minibuffer t) @@ -561,11 +567,12 @@ The optional FORCE option is for internal use only." (defun exwm-workspace--on-echo-area-clear () "Run in echo-area-clear-hook to hide echo area container." - (unless (active-minibuffer-window) - (exwm-workspace--hide-minibuffer)) - (when exwm-workspace--display-echo-area-timer - (cancel-timer exwm-workspace--display-echo-area-timer) - (setq exwm-workspace--display-echo-area-timer nil))) + (when (frame-parameter nil 'exwm-outer-id) ;Exclude non-graphical frames. + (unless (active-minibuffer-window) + (exwm-workspace--hide-minibuffer)) + (when exwm-workspace--display-echo-area-timer + (cancel-timer exwm-workspace--display-echo-area-timer) + (setq exwm-workspace--display-echo-area-timer nil)))) (declare-function exwm-manage--unmanage-window "exwm-manage.el") diff --git a/exwm.el b/exwm.el index e60b341bc3..ac10247f52 100644 --- a/exwm.el +++ b/exwm.el @@ -557,7 +557,7 @@ (start-process exwm--server-name nil (car command-line-args) ;The executable file - "-d" x-display-name + "-d" (frame-parameter nil 'display) "-Q" (concat "--daemon=" exwm--server-name) "--eval" -- cgit 1.4.1 From c7c233bc356fc6a846a09aa5fb13710e6706fce1 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 3 Apr 2016 12:24:50 +0800 Subject: Some commands should be called interactively * exwm-layout.el (exwm-layout-set-fullscreen, exwm-layout-unset-fullscreen) (exwm-layout-show-mode-line): * exwm.el (exwm-reset): Call `exwm-input-grab-keyboard' and `exwm-input-release-keyboard' interactively. --- exwm-layout.el | 6 +++--- exwm.el | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index c392ff9724..9cbdd2b83d 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -177,7 +177,7 @@ :data (vector xcb:Atom:_NET_WM_STATE_FULLSCREEN))) (xcb:flush exwm--connection) (setq exwm--fullscreen t) - (exwm-input-release-keyboard))) + (call-interactively #'exwm-input-release-keyboard))) ;;;###autoload (defun exwm-layout-unset-fullscreen (&optional id) @@ -210,7 +210,7 @@ (make-instance 'xcb:ewmh:set-_NET_WM_STATE :window exwm--id :data [])) (xcb:flush exwm--connection) (setq exwm--fullscreen nil) - (exwm-input-grab-keyboard))) + (call-interactively #'exwm-input-grab-keyboard))) (defvar exwm-layout--fullscreen-frame-count 0 "Count the fullscreen workspace frames.") @@ -516,7 +516,7 @@ See also `exwm-layout-enlarge-window'." (window-mode-line-height (frame-root-window exwm--floating-frame))) nil t) - (exwm-input-grab-keyboard)) + (call-interactively #'exwm-input-grab-keyboard)) (force-mode-line-update))) ;;;###autoload diff --git a/exwm.el b/exwm.el index ac10247f52..f8a3fc7272 100644 --- a/exwm.el +++ b/exwm.el @@ -80,7 +80,7 @@ (when exwm--fullscreen (exwm-layout-unset-fullscreen)) ;; Force refresh (exwm-layout--refresh) - (exwm-input-grab-keyboard)))) + (call-interactively #'exwm-input-grab-keyboard)))) (defun exwm--update-window-type (id &optional force) "Update _NET_WM_WINDOW_TYPE." -- cgit 1.4.1 From ddbbeda285b3b671ace99f1688e6bd3c3f84c742 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 7 Apr 2016 21:03:42 +0800 Subject: Fix 2 multi-monitor issues * exwm-workspace.el (exwm-workspace--on-focus-in, exwm-workspace--init): Handle unexpected frame switch in `focus-in-hook'. * exwm-floating.el (exwm-floating--set-floating): If the absolute position is (0, 0) then the relative position is also the same. --- exwm-floating.el | 4 +++- exwm-workspace.el | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/exwm-floating.el b/exwm-floating.el index a67bc1977e..770976d2d3 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -91,7 +91,9 @@ (frame-geometry (frame-parameter original-frame 'exwm-geometry))) (exwm--log "Floating geometry (original, absolute): %dx%d%+d%+d" width height x y) - (when frame-geometry + (when (and frame-geometry + (/= x 0) + (/= y 0)) (setq x (- x (slot-value frame-geometry 'x)) y (- y (slot-value frame-geometry 'y)))) (exwm--log "Floating geometry (original, relative): %dx%d%+d%+d" diff --git a/exwm-workspace.el b/exwm-workspace.el index 8dd6284d1d..60cf9dd777 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -229,6 +229,16 @@ The optional FORCE option is for internal use only." (xcb:flush exwm--connection)) (run-hooks 'exwm-workspace-switch-hook)))) +(defun exwm-workspace--on-focus-in () + "Handle unexpected frame switch." + ;; `focus-in-hook' is run by `handle-switch-frame'. + (unless (eq this-command #'handle-switch-frame) + (let ((index (cl-position (selected-frame) exwm-workspace--list))) + (exwm--log "Focus on workspace %s" index) + (when (and index (/= index exwm-workspace-current-index)) + (exwm--log "Workspace was switched unexpectedly") + (exwm-workspace-switch index))))) + (defvar exwm-floating-border-width) (defvar exwm-floating-border-color) @@ -679,6 +689,8 @@ The optional FORCE option is for internal use only." (add-hook 'minibuffer-exit-hook #'exwm-workspace--on-minibuffer-exit) (run-with-idle-timer 0 t #'exwm-workspace--on-echo-area-dirty) (add-hook 'echo-area-clear-hook #'exwm-workspace--on-echo-area-clear) + ;; Handle unexpected frame switch. + (add-hook 'focus-in-hook #'exwm-workspace--on-focus-in) ;; Create workspace frames. (dotimes (_ exwm-workspace-number) (push (make-frame `((window-system . x) -- cgit 1.4.1 From 367bdf2e1918dd61780e2284a4c89d5ef8cd1254 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 21 Apr 2016 19:30:55 +0800 Subject: Properly place undecorated X windows * exwm-manage.el (exwm-manage--manage-window): Convert absolute position of undecorated X windows. --- exwm-manage.el | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index b0657b920c..187703395c 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -126,12 +126,20 @@ corresponding buffer.") exwm-window-type) (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DOCK exwm-window-type)) - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window id - :parent (frame-parameter exwm-workspace--current - 'exwm-workspace) - :x x :y y))) + (let ((frame-geometry (frame-parameter exwm-workspace--current + 'exwm-geometry)) + (workspace (frame-parameter exwm-workspace--current + 'exwm-workspace))) + (when (and frame-geometry + (/= x 0) + (/= y 0)) + (setq x (- x (slot-value frame-geometry 'x)) + y (- y (slot-value frame-geometry 'y)))) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window id + :parent workspace + :x x :y y)))) ;; Center window of type _NET_WM_WINDOW_TYPE_SPLASH (when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_SPLASH exwm-window-type) (xcb:+request exwm--connection -- cgit 1.4.1 From 009854e766728ed17741bf9f7e6d86f9ccc71871 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 21 Apr 2016 21:37:02 +0800 Subject: Announce manager selection with client message * exwm-systemtray.el (exwm-systemtray--init): Send a client message to announce the manager selection. (xcb:systemtray:-ClientMessage): The client message. --- exwm-systemtray.el | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/exwm-systemtray.el b/exwm-systemtray.el index 861304ab7c..c85d7436bb 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -30,6 +30,7 @@ ;;; Code: +(require 'xcb-icccm) (require 'xcb-xembed) (require 'xcb-systemtray) (require 'exwm-core) @@ -40,6 +41,15 @@ (visible :initarg :visible)) :documentation "Attributes of a system tray icon.") +(defclass xcb:systemtray:-ClientMessage + (xcb:icccm:--ClientMessage xcb:ClientMessage) + ((format :initform 32) + (type :initform xcb:Atom:MANAGER) + (time :initarg :time :type xcb:TIMESTAMP) ;new slot + (selection :initarg :selection :type xcb:ATOM) ;new slot + (owner :initarg :owner :type xcb:WINDOW)) ;new slot + :documentation "A systemtray client message.") + ;; GTK icons require at least 16 pixels to show normally. (defconst exwm-systemtray--icon-min-size 16 "Minimum icon size.") @@ -325,6 +335,20 @@ You shall use the default value if using auto-hide minibuffer.") :owner id :selection xcb:Atom:_NET_SYSTEM_TRAY_S0 :time xcb:Time:CurrentTime)) + ;; Send a client message to announce the selection. + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:SendEvent + :propagate 0 + :destination exwm--root + :event-mask xcb:EventMask:StructureNotify + :event (xcb:marshal + (make-instance 'xcb:systemtray:-ClientMessage + :window exwm--root + :time xcb:Time:CurrentTime + :selection + xcb:Atom:_NET_SYSTEM_TRAY_S0 + :owner id) + exwm-systemtray--connection))) ;; Set _NET_WM_NAME. (xcb:+request exwm-systemtray--connection (make-instance 'xcb:ewmh:set-_NET_WM_NAME -- cgit 1.4.1 From 2dcb26ce9d4411200c635f19c372fc6c34f3bafe Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 13 May 2016 00:11:12 +0800 Subject: Use `xcb:connect' instead of `xcb:connect-to-socket' * exwm.el (exwm-init): * exwm-systemtray.el (exwm-systemtray--init): Use `xcb:connect' instead of `xcb:connect-to-socket'. --- exwm-systemtray.el | 2 +- exwm.el | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm-systemtray.el b/exwm-systemtray.el index c85d7436bb..cb08ba9560 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -306,7 +306,7 @@ You shall use the default value if using auto-hide minibuffer.") (setq exwm-systemtray-height (max exwm-systemtray--icon-min-size (line-pixel-height)))) ;; Create a new connection. - (setq exwm-systemtray--connection (xcb:connect-to-socket)) + (setq exwm-systemtray--connection (xcb:connect)) (set-process-query-on-exit-flag (slot-value exwm-systemtray--connection 'process) nil) diff --git a/exwm.el b/exwm.el index f8a3fc7272..c9dc6cd0eb 100644 --- a/exwm.el +++ b/exwm.el @@ -481,7 +481,7 @@ (exwm--log "Not running under X environment") (unless exwm--connection (exwm-enable 'undo) ;never initialize again - (setq exwm--connection (xcb:connect-to-socket)) + (setq exwm--connection (xcb:connect)) (set-process-query-on-exit-flag (slot-value exwm--connection 'process) nil) ;prevent query message on exit (setq exwm--root -- cgit 1.4.1 From dc0c0f5131296f31b02019d1d928a0a17f085818 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 21 May 2016 12:50:10 +0800 Subject: Always add `exwm-workspace--on-focus-in' * exwm-workspace.el (exwm-workspace--init): `exwm-workspace--on-focus-in' should always be added to `focus-in-hook'. --- exwm-workspace.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 60cf9dd777..124c6810bd 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -689,8 +689,6 @@ The optional FORCE option is for internal use only." (add-hook 'minibuffer-exit-hook #'exwm-workspace--on-minibuffer-exit) (run-with-idle-timer 0 t #'exwm-workspace--on-echo-area-dirty) (add-hook 'echo-area-clear-hook #'exwm-workspace--on-echo-area-clear) - ;; Handle unexpected frame switch. - (add-hook 'focus-in-hook #'exwm-workspace--on-focus-in) ;; Create workspace frames. (dotimes (_ exwm-workspace-number) (push (make-frame `((window-system . x) @@ -700,6 +698,8 @@ The optional FORCE option is for internal use only." ;; The default behavior of `display-buffer' (indirectly called by ;; `minibuffer-completion-help') is not correct here. (cl-pushnew '(exwm-workspace--display-buffer) display-buffer-alist)) + ;; Handle unexpected frame switch. + (add-hook 'focus-in-hook #'exwm-workspace--on-focus-in) ;; Prevent `other-buffer' from selecting already displayed EXWM buffers. (modify-all-frames-parameters '((buffer-predicate . exwm-layout--other-buffer-predicate))) -- cgit 1.4.1 From 1b2ae3749e98b83f94cc19cef8830ce823c63367 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 23 May 2016 19:13:42 +0800 Subject: Add cleanup codes for Emacs daemon * exwm-floating.el (exwm-floating--exit): * exwm-input.el (exwm-input--exit): * exwm-layout.el (exwm-layout--exit): * exwm-manage.el (exwm-manage--exit): * exwm-randr.el (exwm-randr--exit): * exwm-systemtray.el (exwm-systemtray--exit): * exwm-workspace.el (exwm-workspace--exit): New functions for cleanup each module. * exwm-input.el (exwm-input--on-pre-command, exwm-input--on-post-command) (exwm-input--init): Name lambda functions. * exwm-layout.el (exwm-layout--timer, exwm-layout--init): Save timer. * exwm-randr.el (exwm-randr-enable): Register the cleanup function. * exwm-systemtray.el (exwm-systemtray--init): Force refresh atoms in XEMBED and system tray protocols. (exwm-systemtray-enable): Register the cleanup function. * exwm-workspace.el (exwm-workspace--client): Save the server process. (exwm-workspace--confirm-kill-emacs): Add emacsclient-specific cleanup codes. (exwm-workspace--timer): Save the timer. (exwm-workspace--init): Save the server process and timer; fix problems with emacsclient frames. * exwm.el (exwm-init): Always select the newly created frame; force refresh ICCCM & EWMH atoms. (exwm-exit-hook): New hook for holding cleanup codes. (exwm--exit): Run `exwm-exit-hook', execute cleanup codes for each module and reset the environment. --- exwm-floating.el | 3 +++ exwm-input.el | 20 +++++++++++++--- exwm-layout.el | 14 ++++++++++- exwm-manage.el | 4 ++++ exwm-randr.el | 6 ++++- exwm-systemtray.el | 24 +++++++++++++++---- exwm-workspace.el | 70 ++++++++++++++++++++++++++++++++++++++++++++++-------- exwm.el | 30 ++++++++++++++++++++--- 8 files changed, 149 insertions(+), 22 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 770976d2d3..b3a5b18482 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -622,6 +622,9 @@ Both DELTA-X and DELTA-Y default to 1. This command should be bound locally." exwm-floating--cursor-left (xcb:cursor:load-cursor exwm--connection "left_side"))) +(defun exwm-floating--exit () + "Exit the floating module.") + (provide 'exwm-floating) diff --git a/exwm-input.el b/exwm-input.el index dc7f2b54ef..4bf2cd1ae8 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -476,6 +476,14 @@ SIMULATION-KEYS is an alist of the form (original-key . simulated-key)." (dolist (j pair) (exwm-input--fake-key j)))))) +(defun exwm-input--on-pre-command () + "Run in `pre-command-hook'." + (setq exwm-input--during-command t)) + +(defun exwm-input--on-post-command () + "Run in `post-command-hook'." + (setq exwm-input--during-command nil)) + (declare-function exwm-floating--stop-moveresize "exwm-floating.el" (&rest _args)) (declare-function exwm-floating--do-moveresize "exwm-floating.el" @@ -510,14 +518,20 @@ SIMULATION-KEYS is an alist of the form (original-key . simulated-key)." ;; `pre-command-hook' marks the end of a key sequence (existing or not) (add-hook 'pre-command-hook #'exwm-input--finish-key-sequence) ;; Control `exwm-input--during-command' - (add-hook 'pre-command-hook (lambda () (setq exwm-input--during-command t))) - (add-hook 'post-command-hook - (lambda () (setq exwm-input--during-command nil))) + (add-hook 'pre-command-hook #'exwm-input--on-pre-command) + (add-hook 'post-command-hook #'exwm-input--on-post-command) ;; Update focus when buffer list updates (add-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update) ;; Update prefix keys for global keys (exwm-input--update-global-prefix-keys)) +(defun exwm-input--exit () + "Exit the input module." + (remove-hook 'pre-command-hook #'exwm-input--finish-key-sequence) + (remove-hook 'pre-command-hook #'exwm-input--on-pre-command) + (remove-hook 'post-command-hook #'exwm-input--on-post-command) + (remove-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update)) + (provide 'exwm-input) diff --git a/exwm-layout.el b/exwm-layout.el index 9cbdd2b83d..0069767d1a 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -528,6 +528,8 @@ See also `exwm-layout-enlarge-window'." (exwm-layout-hide-mode-line) (exwm-layout-show-mode-line)))) +(defvar exwm-layout--timer nil "Timer used to track echo area changes.") + (defun exwm-layout--init () "Initialize layout module." ;; Auto refresh layout @@ -535,9 +537,19 @@ See also `exwm-layout-enlarge-window'." (unless (exwm-workspace--minibuffer-own-frame-p) ;; Refresh when minibuffer grows (add-hook 'minibuffer-setup-hook #'exwm-layout--on-minibuffer-setup t) - (run-with-idle-timer 0 t #'exwm-layout--on-echo-area-change t) + (setq exwm-layout--timer + (run-with-idle-timer 0 t #'exwm-layout--on-echo-area-change t)) (add-hook 'echo-area-clear-hook #'exwm-layout--on-echo-area-change))) +(defun exwm-layout--exit () + "Exit the layout module." + (remove-hook 'window-configuration-change-hook #'exwm-layout--refresh) + (remove-hook 'minibuffer-setup-hook #'exwm-layout--on-minibuffer-setup) + (when exwm-layout--timer + (cancel-timer exwm-layout--timer) + (setq exwm-layout--timer nil)) + (remove-hook 'echo-area-clear-hook #'exwm-layout--on-echo-area-change)) + (provide 'exwm-layout) diff --git a/exwm-manage.el b/exwm-manage.el index 187703395c..1c63134ba2 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -555,6 +555,10 @@ border-width: %d; sibling: #x%x; stack-mode: %d" (xcb:+event exwm--connection 'xcb:DestroyNotify #'exwm-manage--on-DestroyNotify)) +(defun exwm-manage--exit () + "Exit the manage module." + (setq exwm-manage--_MOTIF_WM_HINTS nil)) + (provide 'exwm-manage) diff --git a/exwm-randr.el b/exwm-randr.el index e761d8f2e3..0e4469de66 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -164,9 +164,13 @@ )) (xcb:flush exwm--connection))))) +(defun exwm-randr--exit () + "Exit the RandR module.") + (defun exwm-randr-enable () "Enable RandR support for EXWM." - (add-hook 'exwm-init-hook #'exwm-randr--init)) + (add-hook 'exwm-init-hook #'exwm-randr--init) + (add-hook 'exwm-exit-hook #'exwm-randr--exit)) diff --git a/exwm-systemtray.el b/exwm-systemtray.el index cb08ba9560..d1783debdb 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -311,8 +311,8 @@ You shall use the default value if using auto-hide minibuffer.") 'process) nil) ;; Initialize XELB modules. - (xcb:xembed:init exwm-systemtray--connection) - (xcb:systemtray:init exwm-systemtray--connection) + (xcb:xembed:init exwm-systemtray--connection t) + (xcb:systemtray:init exwm-systemtray--connection t) ;; Acquire the manager selection _NET_SYSTEM_TRAY_S0. (with-slots (owner) (xcb:+request-unchecked+reply exwm-systemtray--connection @@ -399,11 +399,27 @@ You shall use the default value if using auto-hide minibuffer.") #'exwm-systemtray--on-ClientMessage) ;; Add hook to move/reparent the embedder. (add-hook 'exwm-workspace-switch-hook #'exwm-systemtray--on-workspace-switch) - (add-hook 'exwm-randr-refresh-hook #'exwm-systemtray--on-randr-refresh)) + (when (boundp 'exwm-randr-refresh-hook) + (add-hook 'exwm-randr-refresh-hook #'exwm-systemtray--on-randr-refresh))) + +(defun exwm-systemtray--exit () + "Exit the systemtray module." + (when exwm-systemtray--connection + (xcb:disconnect exwm-systemtray--connection) + (setq exwm-systemtray--connection nil + exwm-systemtray--list nil + exwm-systemtray--selection-owner-window nil + exwm-systemtray--embedder nil) + (remove-hook 'exwm-workspace-switch-hook + #'exwm-systemtray--on-workspace-switch) + (when (boundp 'exwm-randr-refresh-hook) + (remove-hook 'exwm-randr-refresh-hook + #'exwm-systemtray--on-randr-refresh)))) (defun exwm-systemtray-enable () "Enable system tray support for EXWM." - (add-hook 'exwm-init-hook #'exwm-systemtray--init)) + (add-hook 'exwm-init-hook #'exwm-systemtray--init) + (add-hook 'exwm-exit-hook #'exwm-systemtray--exit)) diff --git a/exwm-workspace.el b/exwm-workspace.el index 124c6810bd..b61b81c91b 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -584,7 +584,11 @@ The optional FORCE option is for internal use only." (cancel-timer exwm-workspace--display-echo-area-timer) (setq exwm-workspace--display-echo-area-timer nil)))) +(defvar exwm-workspace--client nil + "The 'client' frame parameter of emacsclient frames.") + (declare-function exwm-manage--unmanage-window "exwm-manage.el") +(declare-function exwm--exit "exwm.el") (defun exwm-workspace--confirm-kill-emacs (prompt) "Confirm before exiting Emacs." @@ -615,9 +619,26 @@ The optional FORCE option is for internal use only." :x 0 :y 0))) (xcb:flush exwm--connection) - ;; Destroy all resources created by this connection. - (xcb:disconnect exwm--connection) - t)) + (if (not exwm-workspace--client) + (progn + ;; Destroy all resources created by this connection. + (xcb:disconnect exwm--connection) + t) + ;; Extra cleanups for emacsclient. + (dolist (f exwm-workspace--list) + (set-frame-parameter f 'client exwm-workspace--client)) + (when (exwm-workspace--minibuffer-own-frame-p) + (set-frame-parameter exwm-workspace--minibuffer 'client + exwm-workspace--client)) + (let ((connection exwm--connection)) + (exwm--exit) + ;; Destroy all resources created by this connection. + (xcb:disconnect connection)) + ;; Kill the client. + (server-save-buffers-kill-terminal nil) + nil))) + +(defvar exwm-workspace--timer nil "Timer used to track echo area changes.") (defun exwm-workspace--init () "Initialize workspace module." @@ -629,11 +650,13 @@ The optional FORCE option is for internal use only." (progn (setq exwm-workspace--list (frame-list)) (when (< 1 (length exwm-workspace--list)) - ;; Emacs client creates an extra (but unusable) frame. + ;; Exclude the initial frame. (dolist (i exwm-workspace--list) (unless (frame-parameter i 'window-id) (setq exwm-workspace--list (delq i exwm-workspace--list)))) (cl-assert (= 1 (length exwm-workspace--list))) + (setq exwm-workspace--client + (frame-parameter (car exwm-workspace--list) 'client)) ;; Prevent user from deleting this frame by accident. (set-frame-parameter (car exwm-workspace--list) 'client nil)) ;; Create remaining frames. @@ -645,12 +668,17 @@ The optional FORCE option is for internal use only." (setq exwm-workspace--minibuffer (make-frame '((window-system . x) (minibuffer . only) (left . 10000) (right . 10000) - (width . 0) (height . 0)))) + (width . 0) (height . 0) + (client . nil)))) ;; Remove/hide existing frames. (dolist (f old-frames) (if (frame-parameter f 'client) - (make-frame-invisible f) - (delete-frame f)))) + (progn + (unless exwm-workspace--client + (setq exwm-workspace--client (frame-parameter f 'client))) + (make-frame-invisible f)) + (when (eq 'x (framep f)) ;do not delete the initial frame. + (delete-frame f))))) ;; This is the only usable minibuffer frame. (setq default-minibuffer-frame exwm-workspace--minibuffer) (let ((outer-id (string-to-number @@ -687,17 +715,20 @@ The optional FORCE option is for internal use only." ;; Show/hide minibuffer / echo area when they're active/inactive. (add-hook 'minibuffer-setup-hook #'exwm-workspace--on-minibuffer-setup) (add-hook 'minibuffer-exit-hook #'exwm-workspace--on-minibuffer-exit) - (run-with-idle-timer 0 t #'exwm-workspace--on-echo-area-dirty) + (setq exwm-workspace--timer + (run-with-idle-timer 0 t #'exwm-workspace--on-echo-area-dirty)) (add-hook 'echo-area-clear-hook #'exwm-workspace--on-echo-area-clear) ;; Create workspace frames. (dotimes (_ exwm-workspace-number) (push (make-frame `((window-system . x) (minibuffer . ,(minibuffer-window - exwm-workspace--minibuffer)))) + exwm-workspace--minibuffer)) + (client . nil))) exwm-workspace--list)) ;; The default behavior of `display-buffer' (indirectly called by ;; `minibuffer-completion-help') is not correct here. - (cl-pushnew '(exwm-workspace--display-buffer) display-buffer-alist)) + (cl-pushnew '(exwm-workspace--display-buffer) display-buffer-alist + :test #'equal)) ;; Handle unexpected frame switch. (add-hook 'focus-in-hook #'exwm-workspace--on-focus-in) ;; Prevent `other-buffer' from selecting already displayed EXWM buffers. @@ -768,6 +799,25 @@ The optional FORCE option is for internal use only." ;; Switch to the first workspace (exwm-workspace-switch 0 t)) +(defun exwm-workspace--exit () + "Exit the workspace module." + (setq confirm-kill-emacs nil + exwm-workspace--list nil + exwm-workspace--client nil + exwm-workspace--minibuffer nil + default-minibuffer-frame nil) + (remove-hook 'minibuffer-setup-hook #'exwm-workspace--on-minibuffer-setup) + (remove-hook 'minibuffer-exit-hook #'exwm-workspace--on-minibuffer-exit) + (when exwm-workspace--timer + (cancel-timer exwm-workspace--timer) + (setq exwm-workspace--timer nil)) + (remove-hook 'echo-area-clear-hook #'exwm-workspace--on-echo-area-clear) + (setq display-buffer-alist + (cl-delete '(exwm-workspace--display-buffer) display-buffer-alist + :test #'equal)) + (remove-hook 'focus-in-hook #'exwm-workspace--on-focus-in) + (advice-remove 'x-create-frame #'exwm-workspace--x-create-frame)) + (defvar exwm-layout--fullscreen-frame-count) (defun exwm-workspace--post-init () diff --git a/exwm.el b/exwm.el index c9dc6cd0eb..0bc27d71d8 100644 --- a/exwm.el +++ b/exwm.el @@ -477,7 +477,11 @@ (defun exwm-init (&optional frame) "Initialize EXWM." - (if (not (eq 'x (framep (or frame (selected-frame))))) + (if frame + ;; The frame might not be selected if it's created by emacslicnet. + (select-frame-set-input-focus frame) + (setq frame (selected-frame))) + (if (not (eq 'x (framep frame))) (exwm--log "Not running under X environment") (unless exwm--connection (exwm-enable 'undo) ;never initialize again @@ -499,8 +503,8 @@ ;; Disable some features not working well with EXWM (setq use-dialog-box nil) ;; Initialize ICCCM/EWMH support - ;; (xcb:icccm:init exwm--connection) - (xcb:ewmh:init exwm--connection) + (xcb:icccm:init exwm--connection t) + (xcb:ewmh:init exwm--connection t) (exwm--lock) (exwm--init-icccm-ewmh) (exwm-layout--init) @@ -514,6 +518,26 @@ (exwm-manage--scan) (run-hooks 'exwm-init-hook))))) +(defvar exwm-exit-hook nil + "Normal hook run just before EXWM is about to exit. + +This hook is only run when EXWM is started with emacsclient.") + +(defun exwm--exit () + "Exit EXWM." + (run-hooks 'exwm-exit-hook) + ;; Exit modules. + (exwm-input--exit) + (exwm-workspace--exit) + (exwm-manage--exit) + (exwm-floating--exit) + (exwm-layout--exit) + ;; Reset several import variables. + (setq exwm--connection nil + exwm--root nil + exwm--id-buffer-alist nil) + (exwm-enable)) + (defvar exwm-blocking-subrs '(x-file-dialog x-popup-dialog x-select-font) "Subrs (primitives) that would normally block EXWM.") -- cgit 1.4.1 From 3dba5f156f2670f6226db80281a5879762259154 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 24 May 2016 12:30:53 +0800 Subject: Manage a certain type of undecorated X windows * exwm-core.el (exwm--mwm-hints): Removed. (exwm--mwm-hints-decorations): New buffer-local variable for indicating whether the X window should have decorations. * exwm-floating.el (exwm-floating--set-floating): Hide the mode-line of undecorated floating X windows by default. * exwm-manage.el (exwm-manage--update-mwm-hints): Set exwm--mwm-hints-decorations; (exwm-manage--manage-window): Manage an undecorated X window if its input model is not 'No Input' or 'Globally Active'. --- exwm-core.el | 2 +- exwm-floating.el | 6 ++++++ exwm-manage.el | 23 +++++++++++++++-------- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index e202d4ee47..9eb6b62f66 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -116,7 +116,7 @@ (defvar-local exwm--hints-input nil) (defvar-local exwm--hints-urgency nil) ;; _MOTIF_WM_HINTS -(defvar-local exwm--mwm-hints nil) +(defvar-local exwm--mwm-hints-decorations t) (defvar exwm-mode-map (let ((map (make-sparse-keymap))) diff --git a/exwm-floating.el b/exwm-floating.el index b3a5b18482..d4f57b73d2 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -159,6 +159,12 @@ (- (elt edges 2) (elt edges 0))))) (frame-height (+ height (- (frame-pixel-height frame) (- (elt edges 3) (elt edges 1)))))) + ;; Check `exwm--mwm-hints-decorations'. + (unless exwm--mwm-hints-decorations + (setq frame-height (- frame-height (window-mode-line-height + (frame-root-window frame))) + exwm--mode-line-format mode-line-format + mode-line-format nil)) (set-frame-size frame frame-width frame-height t) ;; Create the frame container as the parent of the frame and ;; a child of the X window container. diff --git a/exwm-manage.el b/exwm-manage.el index 1c63134ba2..3dcdf795b6 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -48,7 +48,7 @@ corresponding buffer.") (defun exwm-manage--update-mwm-hints (id &optional force) "Update _MOTIF_WM_HINTS." (with-current-buffer (exwm--id->buffer id) - (unless (and exwm--mwm-hints (not force)) + (unless (and (not exwm--mwm-hints-decorations) (not force)) (let ((reply (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:icccm:-GetProperty :window id @@ -56,7 +56,17 @@ corresponding buffer.") :type exwm-manage--_MOTIF_WM_HINTS :long-length 5)))) (when reply - (setq exwm--mwm-hints (append (slot-value reply 'value) nil))))))) + ;; Check MotifWmHints.decorations. + (with-slots (value) reply + (setq value (append value nil)) + (when (and value + ;; See for fields definitions. + (/= 0 (logand + (elt value 0) ;MotifWmHints.flags + 2)) ;MWM_HINTS_DECORATIONS + (= 0 + (elt value 2))) ;MotifWmHints.decorations + (setq exwm--mwm-hints-decorations nil)))))))) (defvar exwm-workspace--current) (defvar exwm-workspace--switch-history-outdated) @@ -90,6 +100,7 @@ corresponding buffer.") (exwm--update-class id) (exwm--update-transient-for id) (exwm--update-normal-hints id) + (exwm--update-hints id) (exwm-manage--update-geometry id) (exwm-manage--update-mwm-hints id) ;; No need to manage (please check OverrideRedirect outside) @@ -100,11 +111,8 @@ corresponding buffer.") (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG exwm-window-type) (memq xcb:Atom:_NET_WM_WINDOW_TYPE_NORMAL exwm-window-type))) ;; Check the _MOTIF_WM_HINTS property. - (and exwm--mwm-hints - ;; See for fields definitions. - (/= 0 (logand (elt exwm--mwm-hints 0) ;MotifWmHints.flags - 2)) ;MWM_HINTS_DECORATIONS - (= 0 (elt exwm--mwm-hints 2)) ;MotifWmHints.decorations + (and (not exwm--mwm-hints-decorations) + (not exwm--hints-input) ;; Floating windows only (or exwm-transient-for exwm--fixed-size (memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY @@ -202,7 +210,6 @@ corresponding buffer.") :data (vconcat (mapcar #'car exwm--id-buffer-alist)))) (xcb:flush exwm--connection) (exwm--update-title id) - (exwm--update-hints id) (exwm--update-protocols id) (exwm--update-state id) (if (or exwm-transient-for exwm--fixed-size -- cgit 1.4.1 From 2ec88bd32d0409f182e2532e0763ddb9bc8b21fc Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 24 May 2016 21:49:31 +0800 Subject: * exwm-input.el (exwm-input--update-focus): Do not update input focus when there's an active minibjffer --- exwm-input.el | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/exwm-input.el b/exwm-input.el index 4bf2cd1ae8..4307a187a6 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -101,7 +101,9 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (defun exwm-input--update-focus () "Update input focus." - (when (window-live-p exwm-input--focus-window) + (when (and (window-live-p exwm-input--focus-window) + ;; Do not update input focus when there's an active minibuffer. + (not (active-minibuffer-window))) (with-current-buffer (window-buffer exwm-input--focus-window) (if (eq major-mode 'exwm-mode) (if (not (eq exwm--frame exwm-workspace--current)) @@ -308,6 +310,10 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (setq event (xcb:keysyms:keysym->event exwm--connection keysym state))) (when (eq major-mode 'exwm-mode) + ;; FIXME: This functionality seems not working, e.g. when this + ;; command would activate the minibuffer, the temporary + ;; line-mode would actually quit before the minibuffer + ;; becomes active. (setq exwm-input--temp-line-mode t exwm-input--during-key-sequence t) (exwm-input--grab-keyboard)) ;grab keyboard temporarily -- cgit 1.4.1 From 9e754e7e138c8ac969bc69b8ffbe988fbcbe4a3e Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 25 May 2016 12:21:08 +0800 Subject: Bump version to 0.5 --- exwm.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm.el b/exwm.el index 0bc27d71d8..3748da2049 100644 --- a/exwm.el +++ b/exwm.el @@ -4,8 +4,8 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0.4 -;; Package-Requires: ((xelb "0.6")) +;; Version: 0.5 +;; Package-Requires: ((xelb "0.8")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From 38332925d0d5655e98416afaa7869bbaddb360b8 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 17 Jun 2016 13:30:13 +0800 Subject: * exwm-layout.el (exwm-layout--show): Resize the X window if the minibuffer is active but with line height 1. --- exwm-layout.el | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index 0069767d1a..0ce1fdcace 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -60,11 +60,12 @@ (with-current-buffer (exwm--id->buffer id) (if (not exwm--floating-frame) (let ((relative-edges (window-inside-pixel-edges window))) - (exwm-layout--resize-container id exwm--container - (elt relative-edges 0) - (elt relative-edges 1) - width height - (active-minibuffer-window))) + (exwm-layout--resize-container + id exwm--container + (elt relative-edges 0) (elt relative-edges 1) width height + ;; Do not resize the X window if the minibuffer resizes itself. + (and (active-minibuffer-window) + (< 1 (window-height (active-minibuffer-window)))))) ;; A floating X window is of the same size as the Emacs window, ;; whereas its container is of the same size as the Emacs frame. (setq frame-width (frame-pixel-width exwm--floating-frame) -- cgit 1.4.1 From 4a1b177bceb7e47ea75ddf779bf609d563713ffe Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 17 Jun 2016 18:45:34 +0800 Subject: Allow buffer-local simulation keys * exwm-input.el (exwm-input-set-local-simulation-keys): New function for setting buffer-local simulation keys. (exwm-input--local-simulation-keys): New internal variable. (exwm-input--update-simulation-prefix-keys): Modify either `exwm-mode-map' or the local keymap accordingly. --- exwm-input.el | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/exwm-input.el b/exwm-input.el index 4307a187a6..e786f0defd 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -453,11 +453,16 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") ;; (unless (listp last-input-event) ;not a key event ;; (exwm-input--fake-key last-input-event))) +(defvar exwm-input--local-simulation-keys nil + "Whether simulation keys are local.") + (defun exwm-input--update-simulation-prefix-keys () "Update the list of prefix keys of simulation keys." (setq exwm-input--simulation-prefix-keys nil) (dolist (i exwm-input--simulation-keys) - (define-key exwm-mode-map (car i) #'exwm-input-send-simulation-key) + (if exwm-input--local-simulation-keys + (local-set-key (car i) #'exwm-input-send-simulation-key) + (define-key exwm-mode-map (car i) #'exwm-input-send-simulation-key)) (cl-pushnew (elt (car i) 0) exwm-input--simulation-prefix-keys))) (defun exwm-input-set-simulation-keys (simulation-keys) @@ -469,6 +474,16 @@ SIMULATION-KEYS is an alist of the form (original-key . simulated-key)." (cl-pushnew `(,(vconcat (car i)) . ,(cdr i)) exwm-input--simulation-keys)) (exwm-input--update-simulation-prefix-keys)) +(defun exwm-input-set-local-simulation-keys (simulation-keys) + "Set buffer-local simulation keys. + +Its usage is the same with `exwm-input-set-simulation-keys'." + (make-local-variable 'exwm-input--simulation-keys) + (make-local-variable 'exwm-input--simulation-prefix-keys) + (use-local-map (copy-keymap exwm-mode-map)) + (let ((exwm-input--local-simulation-keys t)) + (exwm-input-set-simulation-keys simulation-keys))) + ;;;###autoload (defun exwm-input-send-simulation-key (times) "Fake a key event according to last input key sequence." -- cgit 1.4.1 From 9c8e95b376124ca192e65a3dca33dd40ca6ecee3 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 12 Jul 2016 11:55:02 +0800 Subject: Fix a bug of simulation keys * exwm-input.el (exwm-input--on-KeyPress-line-mode) (exwm-input--on-KeyPress-char-mode): Force events to be added to `this-command-keys'. (exwm-input-send-next-key): The read event can now be (t . EVENT). --- exwm-input.el | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index e786f0defd..2622253bae 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -294,7 +294,9 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (memq event exwm-input--simulation-prefix-keys))) (setq mode xcb:Allow:AsyncKeyboard) (unless minibuffer-window (setq exwm-input--during-key-sequence t)) - (push event unread-command-events)) + ;; Feed this event to command loop. Also force it to be added to + ;; `this-command-keys'. + (push (cons t event) unread-command-events)) (xcb:+request exwm--connection (make-instance 'xcb:AllowEvents :mode (or mode xcb:Allow:ReplayKeyboard) @@ -317,7 +319,9 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (setq exwm-input--temp-line-mode t exwm-input--during-key-sequence t) (exwm-input--grab-keyboard)) ;grab keyboard temporarily - (push event unread-command-events)))) + ;; Feed this event to command loop. Also force it to be added to + ;; `this-command-keys'. + (push (cons t event) unread-command-events)))) (xcb:+request exwm--connection (make-instance 'xcb:AllowEvents :mode xcb:Allow:AsyncKeyboard @@ -443,6 +447,8 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (setq key (read-key (format "Send key: %s (%d/%d)" (key-description keys) (1+ i) times))) + (when (and (listp key) (eq (car key) t)) + (setq key (cdr key))) (unless (listp key) (throw 'break nil)))) (setq exwm-input--during-key-sequence nil) (setq keys (vconcat keys (vector key))) -- cgit 1.4.1 From 0863f4149062089abdadd0dd795ad17d0e73da03 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 12 Jul 2016 12:13:50 +0800 Subject: * exwm-workspace.el (exwm-workspace--init): Remove possible internal borders of workspace frames. --- exwm-workspace.el | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index b61b81c91b..f5a320b574 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -657,18 +657,23 @@ The optional FORCE option is for internal use only." (cl-assert (= 1 (length exwm-workspace--list))) (setq exwm-workspace--client (frame-parameter (car exwm-workspace--list) 'client)) - ;; Prevent user from deleting this frame by accident. - (set-frame-parameter (car exwm-workspace--list) 'client nil)) + (let ((f (car exwm-workspace--list))) + ;; Remove the possible internal border. + (set-frame-parameter f 'internal-border-width 0) + ;; Prevent user from deleting this frame by accident. + (set-frame-parameter f 'client nil)) ;; Create remaining frames. (dotimes (_ (1- exwm-workspace-number)) (nconc exwm-workspace--list - (list (make-frame '((window-system . x))))))) + (list (make-frame '((window-system . x) + (internal-border-width . 0)))))))) ;; Initialize workspaces without minibuffers. (let ((old-frames (frame-list))) (setq exwm-workspace--minibuffer (make-frame '((window-system . x) (minibuffer . only) (left . 10000) (right . 10000) (width . 0) (height . 0) + (internal-border-width . 0) (client . nil)))) ;; Remove/hide existing frames. (dolist (f old-frames) @@ -723,6 +728,7 @@ The optional FORCE option is for internal use only." (push (make-frame `((window-system . x) (minibuffer . ,(minibuffer-window exwm-workspace--minibuffer)) + (internal-border-width . 0) (client . nil))) exwm-workspace--list)) ;; The default behavior of `display-buffer' (indirectly called by -- cgit 1.4.1 From 650ed0013c3fac9e88328086d1f4a029f4fb7221 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 12 Jul 2016 18:35:51 +0800 Subject: Add initial support for dock (panel) applications * exwm-layout.el (exwm-layout--fullscreen-frame-count) (exwm-layout--set-frame-fullscreen): Moved to workspace module. * exwm-workspace.el (exwm-workspace--fullscreen-frame-count) (exwm-workspace--set-fullscreen): Moved from layout module. * exwm-manage.el (exwm-manage--manage-window): Update struts for dock applications. * exwm-workspace.el (exwm-workspace--strut) (exwm-workspace--strut-is-partial): New variables for storing struts. (exwm-workspace--resize-minibuffer-frame) (exwm-workspace--on-ConfigureNotify): Take struts into consideration. * exwm.el (exwm--update-strut-legacy, exwm--update-strut-partial) (exwm--update-strut): New functions for updating _NET_WM_STRUT or _NET_WM_STRUT_PARTIAL. (exwm--on-PropertyNotify): Update struts on corresponding event. (exwm--init-icccm-ewmh): Declare _NET_WM_STRUT and _NET_WM_STRUT_PARTIAL as supported. * exwm-workspace.el (exwm-workspace--update-workareas): Dedicated function for updating _NET_WORKAREA. * exwm-randr.el (exwm-randr--refresh): * exwm-workspace.el (exwm-workspace--init): Use `exwm-workspace--update-workareas'. * exwm.el (exwm--init-icccm-ewmh): Do not set _NET_WORKAREA here. --- exwm-layout.el | 28 --------------- exwm-manage.el | 11 +++++- exwm-randr.el | 22 +++++------- exwm-workspace.el | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++--- exwm.el | 63 ++++++++++++++++++++++++++-------- 5 files changed, 163 insertions(+), 61 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index 0ce1fdcace..2d85580085 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -213,34 +213,6 @@ (setq exwm--fullscreen nil) (call-interactively #'exwm-input-grab-keyboard))) -(defvar exwm-layout--fullscreen-frame-count 0 - "Count the fullscreen workspace frames.") - -;; This function is superficially similar to `exwm-layout-set-fullscreen', but -;; they do very different things: `exwm-layout--set-frame-fullscreen' resizes a -;; frame to the actual monitor size, `exwm-layout-set-fullscreen' resizes an X -;; window to the frame size. -(defun exwm-layout--set-frame-fullscreen (frame) - "Make frame FRAME fullscreen, with regard to its RandR output if applicable." - (let ((geometry (or (frame-parameter frame 'exwm-geometry) - (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:GetGeometry - :drawable exwm--root)) - (make-instance 'xcb:RECTANGLE :x 0 :y 0 - :width (x-display-pixel-width) - :height (x-display-pixel-height)))) - (id (frame-parameter frame 'exwm-outer-id)) - (container (frame-parameter frame 'exwm-container)) - (workspace (frame-parameter frame 'exwm-workspace))) - (with-slots (x y width height) geometry - (when (and (eq frame exwm-workspace--current) - (exwm-workspace--minibuffer-own-frame-p)) - (exwm-workspace--resize-minibuffer-frame width height)) - (exwm-layout--resize-container id container 0 0 width height) - (exwm-layout--resize-container nil workspace x y width height t) - (xcb:flush exwm--connection))) - (cl-incf exwm-layout--fullscreen-frame-count)) - (defvar exwm-layout--other-buffer-exclude-exwm-mode-buffers nil "When non-nil, prevent EXWM buffers from being selected by `other-buffer'.") diff --git a/exwm-manage.el b/exwm-manage.el index 3dcdf795b6..924103e535 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -79,6 +79,7 @@ corresponding buffer.") (declare-function exwm--update-hints "exwm.el" (id &optional force)) (declare-function exwm--update-protocols "exwm.el" (id &optional force)) (declare-function exwm--update-state "exwm.el" (id &optional force)) +(declare-function exwm--update-strut "exwm.el" (id)) (declare-function exwm-floating--set-floating "exwm-floating.el" (id)) (declare-function exwm-floating--unset-floating "exwm-floating.el" (id)) @@ -120,11 +121,19 @@ corresponding buffer.") (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG exwm-window-type)))) (exwm--log "No need to manage #x%x" id) + ;; Update struts. + (when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DOCK exwm-window-type) + (exwm--update-strut id)) ;; Remove all events (xcb:+request exwm--connection (make-instance 'xcb:ChangeWindowAttributes :window id :value-mask xcb:CW:EventMask - :event-mask xcb:EventMask:NoEvent)) + :event-mask + (if (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DOCK + exwm-window-type) + ;; Listen for change of struts property of dock. + xcb:EventMask:PropertyChange + xcb:EventMask:NoEvent))) ;; The window needs to be mapped (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window id)) diff --git a/exwm-randr.el b/exwm-randr.el index 0e4469de66..d30f687789 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -54,16 +54,18 @@ (defvar exwm-randr-refresh-hook nil "Normal hook run when the RandR module just refreshed.") -(defvar exwm-layout--fullscreen-frame-count) +(defvar exwm-workspace--fullscreen-frame-count) (defvar exwm-workspace-number) (defvar exwm-workspace--list) -(declare-function exwm-layout--set-frame-fullscreen "exwm-layout.el" (frame)) +(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame)) +(declare-function exwm-workspace--update-workareas "exwm-workspace.el" + (&optional workareas)) (defun exwm-randr--refresh () "Refresh workspaces according to the updated RandR info." (let (output-name geometry output-plist default-geometry workareas - workarea-offset viewports) + viewports) ;; Query all outputs (with-slots (config-timestamp outputs) (xcb:+request-unchecked+reply exwm--connection @@ -93,10 +95,7 @@ (setq default-geometry geometry))))))) (exwm--log "(randr) outputs: %s" output-plist) (when output-plist - (setq workarea-offset (if (exwm-workspace--minibuffer-own-frame-p) - 0 - (window-pixel-height (minibuffer-window)))) - (setq exwm-layout--fullscreen-frame-count 0) + (setq exwm-workspace--fullscreen-frame-count 0) (dotimes (i exwm-workspace-number) (let* ((output (plist-get exwm-randr-workspace-output-plist i)) (geometry (lax-plist-get output-plist output)) @@ -106,16 +105,13 @@ output nil)) (set-frame-parameter frame 'exwm-randr-output output) (set-frame-parameter frame 'exwm-geometry geometry) - (exwm-layout--set-frame-fullscreen frame) + (exwm-workspace--set-fullscreen frame) (with-slots (x y width height) geometry (setq workareas - (nconc workareas (list x y width (- height - workarea-offset))) + (nconc workareas (list x y width height)) viewports (nconc viewports (list x y)))))) ;; Update _NET_WORKAREA - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_WORKAREA - :window exwm--root :data (vconcat workareas))) + (exwm-workspace--update-workareas (vconcat workareas)) ;; Update _NET_DESKTOP_VIEWPORT (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT diff --git a/exwm-workspace.el b/exwm-workspace.el index f5a320b574..64c636eb2c 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -128,6 +128,50 @@ Value nil means to use the default position which is fixed at bottom, while "Reports whether the minibuffer is displayed in its own frame." (memq exwm-workspace-minibuffer-position '(top bottom))) +;; FIXME: RandR and multiple docks. +(defvar exwm-workspace--strut nil "Areas occupied by struts.") +(defvar exwm-workspace--strut-is-partial nil + "Whether the struts are from _NET_WM_STRUT_PARTIAL.") + +(defvar exwm-workspace--fullscreen-frame-count 0 + "Count the fullscreen workspace frames.") + +(declare-function exwm-layout--resize-container "exwm-layout.el" + (id container x y width height &optional container-only)) + +(defun exwm-workspace--set-fullscreen (frame) + "Make frame FRAME fullscreen, with regard to its RandR output if applicable." + (let ((geometry (or (frame-parameter frame 'exwm-geometry) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetGeometry + :drawable exwm--root)) + (make-instance 'xcb:RECTANGLE :x 0 :y 0 + :width (x-display-pixel-width) + :height (x-display-pixel-height)))) + (id (frame-parameter frame 'exwm-outer-id)) + (container (frame-parameter frame 'exwm-container)) + (workspace (frame-parameter frame 'exwm-workspace)) + x* y* width* height*) + (with-slots (x y width height) geometry + (if exwm-workspace--strut + (setq x* (+ x (aref exwm-workspace--strut 0)) + y* (+ y (aref exwm-workspace--strut 2)) + width* (- width (aref exwm-workspace--strut 0) + (aref exwm-workspace--strut 1)) + height* (- height (aref exwm-workspace--strut 2) + (aref exwm-workspace--strut 3))) + (setq x* x + y* y + width* width + height* height)) + (when (and (eq frame exwm-workspace--current) + (exwm-workspace--minibuffer-own-frame-p)) + (exwm-workspace--resize-minibuffer-frame width* height*)) + (exwm-layout--resize-container id container 0 0 width* height*) + (exwm-layout--resize-container nil workspace x* y* width* height* t) + (xcb:flush exwm--connection))) + (cl-incf exwm-workspace--fullscreen-frame-count)) + ;;;###autoload (defun exwm-workspace--resize-minibuffer-frame (&optional width height) "Resize minibuffer (and its container) to fit the size of workspace. @@ -138,10 +182,19 @@ workspace frame." (let ((y (if (eq exwm-workspace-minibuffer-position 'top) 0 (- (or height (exwm-workspace--current-height)) + (if exwm-workspace--strut + (+ (aref exwm-workspace--strut 2) + (aref exwm-workspace--strut 3)) + 0) (frame-pixel-height exwm-workspace--minibuffer)))) - (width (or width (exwm-workspace--current-width))) (container (frame-parameter exwm-workspace--minibuffer 'exwm-container))) + (unless width + (setq width (exwm-workspace--current-width))) + (when exwm-workspace--strut + (setq width (- width + (aref exwm-workspace--strut 0) + (aref exwm-workspace--strut 1)))) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window container @@ -491,7 +544,12 @@ The optional FORCE option is for internal use only." (setq value-mask xcb:ConfigWindow:Height y 0) (setq value-mask (logior xcb:ConfigWindow:Y xcb:ConfigWindow:Height) - y (- (exwm-workspace--current-height) height))) + y (- (exwm-workspace--current-height) + (if exwm-workspace--strut + (+ (aref exwm-workspace--strut 2) + (aref exwm-workspace--strut 3)) + 0) + height))) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window (frame-parameter exwm-workspace--minibuffer @@ -638,6 +696,38 @@ The optional FORCE option is for internal use only." (server-save-buffers-kill-terminal nil) nil))) +(defun exwm-workspace--update-workareas (&optional workareas) + "Update _NET_WORKAREA." + ;; Calculate workareas if not present. + (unless workareas + (if (frame-parameter (car exwm-workspace--list) 'exwm-geometry) + ;; Use the 'exwm-geometry' frame parameter if possible. + (dolist (f exwm-workspace--list) + (with-slots (x y width height) (frame-parameter f 'exwm-geometry) + (setq workareas (vconcat workareas (vector x y width height))))) + (let ((workarea (vector 0 0 (x-display-pixel-width) + (x-display-pixel-height)))) + (dotimes (_ exwm-workspace-number) + (setq workareas (vconcat workareas workarea)))))) + ;; Exclude areas occupied by struts. + ;; FIXME: RandR. + (when exwm-workspace--strut + (let ((dx (aref exwm-workspace--strut 0)) + (dy (aref exwm-workspace--strut 2)) + (dw (- (+ (aref exwm-workspace--strut 0) + (aref exwm-workspace--strut 1)))) + (dh (- (+ (aref exwm-workspace--strut 2) + (aref exwm-workspace--strut 3))))) + (dotimes (i exwm-workspace-number) + (cl-incf (aref workareas (* i 4)) dx) + (cl-incf (aref workareas (+ (* i 4))) dy) + (cl-incf (aref workareas (+ (* i 4) 2)) dw) + (cl-incf (aref workareas (+ (* i 4) 3)) dh)))) + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WORKAREA + :window exwm--root :data workareas)) + (xcb:flush exwm--connection)) + (defvar exwm-workspace--timer nil "Timer used to track echo area changes.") (defun exwm-workspace--init () @@ -794,6 +884,8 @@ The optional FORCE option is for internal use only." (xcb:flush exwm--connection) ;; We have to advice `x-create-frame' or every call to it would hang EXWM (advice-add 'x-create-frame :around #'exwm-workspace--x-create-frame) + ;; Set _NET_WORKAREA. + (exwm-workspace--update-workareas) ;; Set _NET_VIRTUAL_ROOTS (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_VIRTUAL_ROOTS @@ -824,8 +916,6 @@ The optional FORCE option is for internal use only." (remove-hook 'focus-in-hook #'exwm-workspace--on-focus-in) (advice-remove 'x-create-frame #'exwm-workspace--x-create-frame)) -(defvar exwm-layout--fullscreen-frame-count) - (defun exwm-workspace--post-init () "The second stage in the initialization of the workspace module." ;; Make the workspaces fullscreen. @@ -833,7 +923,7 @@ The optional FORCE option is for internal use only." (set-frame-parameter i 'fullscreen 'fullboth)) ;; Wait until all workspace frames are resized. (with-timeout (1) - (while (< exwm-layout--fullscreen-frame-count exwm-workspace-number) + (while (< exwm-workspace--fullscreen-frame-count exwm-workspace-number) (accept-process-output nil 0.1)))) diff --git a/exwm.el b/exwm.el index 3748da2049..3b068786a1 100644 --- a/exwm.el +++ b/exwm.el @@ -236,6 +236,46 @@ ;; Default to normal state xcb:icccm:WM_STATE:NormalState))))))) +(defun exwm--update-strut-legacy (id) + "Update _NET_WM_STRUT." + (unless exwm-workspace--strut-is-partial + (let ((reply (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:ewmh:get-_NET_WM_STRUT + :window id)))) + (setq exwm-workspace--strut (when reply (slot-value reply 'value))) + ;; Update workspaces. + (dolist (f exwm-workspace--list) + (exwm-workspace--set-fullscreen f)) + ;; Resize the minibuffer frame. + (when (exwm-workspace--minibuffer-own-frame-p) + (exwm-workspace--resize-minibuffer-frame)) + ;; Update _NET_WORKAREA. + (exwm-workspace--update-workareas)))) + +(defun exwm--update-strut-partial (id) + "Update _NET_WM_STRUT_PARTIAL." + (let ((reply (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:ewmh:get-_NET_WM_STRUT_PARTIAL + :window id)))) + (setq exwm-workspace--strut (when reply (slot-value reply 'value))) + (if (not exwm-workspace--strut) + (setq exwm-workspace--strut-is-partial nil) + (setq exwm-workspace--strut (substring exwm-workspace--strut 0 4)) + (setq exwm-workspace--strut-is-partial t)) + ;; Update workspaces. + (dolist (f exwm-workspace--list) + (exwm-workspace--set-fullscreen f)) + ;; Resize the minibuffer frame. + (when (exwm-workspace--minibuffer-own-frame-p) + (exwm-workspace--resize-minibuffer-frame)) + ;; Update _NET_WORKAREA. + (exwm-workspace--update-workareas))) + +(defun exwm--update-strut (id) + "Update _NET_WM_STRUT_PARTIAL or _NET_WM_STRUT." + (exwm--update-strut-partial id) + (exwm--update-strut-legacy id)) + (defun exwm--on-PropertyNotify (data _synthetic) "Handle PropertyNotify event." (let ((obj (make-instance 'xcb:PropertyNotify)) @@ -245,7 +285,12 @@ atom (slot-value obj 'atom) exwm-input--timestamp (slot-value obj 'time)) (setq buffer (exwm--id->buffer id)) - (when (buffer-live-p buffer) + (if (not (buffer-live-p buffer)) + ;; Properties of unmanaged X windows. + (cond ((= atom xcb:Atom:_NET_WM_STRUT) + (exwm--update-strut-legacy id)) + ((= atom xcb:Atom:_NET_WM_STRUT_PARTIAL) + (exwm--update-strut-partial id))) (with-current-buffer buffer (cond ((= atom xcb:Atom:_NET_WM_WINDOW_TYPE) (exwm--update-window-type id t)) @@ -326,7 +371,7 @@ (= action xcb:ewmh:_NET_WM_STATE_ADD)) (dolist (f exwm-workspace--list) (when (equal (frame-parameter f 'exwm-outer-id) id) - (exwm-layout--set-frame-fullscreen f) + (exwm-workspace--set-fullscreen f) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_STATE @@ -410,6 +455,8 @@ xcb:Atom:_NET_REQUEST_FRAME_EXTENTS xcb:Atom:_NET_FRAME_EXTENTS xcb:Atom:_NET_WM_NAME + xcb:Atom:_NET_WM_STRUT + xcb:Atom:_NET_WM_STRUT_PARTIAL ;; xcb:Atom:_NET_WM_WINDOW_TYPE xcb:Atom:_NET_WM_WINDOW_TYPE_TOOLBAR @@ -458,18 +505,6 @@ (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT :window exwm--root :data (make-vector (* 2 exwm-workspace-number) 0))) - ;; Set _NET_WORKAREA (with minibuffer excluded) - (let* ((workareas - (vector 0 0 (x-display-pixel-width) - (- (x-display-pixel-height) - (if (exwm-workspace--minibuffer-own-frame-p) - 0 - (window-pixel-height (minibuffer-window)))))) - (workareas (mapconcat (lambda (_) workareas) - (make-list exwm-workspace-number 0) []))) - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_WORKAREA - :window exwm--root :data workareas))) (xcb:flush exwm--connection)) (defvar exwm-init-hook nil -- cgit 1.4.1 From eee5c6fa4dd776c1604e5ec7db34bc5401d8dea6 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 12 Jul 2016 20:08:20 +0800 Subject: * exwm-input.el (exwm-input--on-KeyPress-char-mode): No need to force events to be added to `this-command-keys' (and it causes problems). --- exwm-input.el | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 2622253bae..1c711b36c7 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -319,9 +319,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (setq exwm-input--temp-line-mode t exwm-input--during-key-sequence t) (exwm-input--grab-keyboard)) ;grab keyboard temporarily - ;; Feed this event to command loop. Also force it to be added to - ;; `this-command-keys'. - (push (cons t event) unread-command-events)))) + (push event unread-command-events)))) (xcb:+request exwm--connection (make-instance 'xcb:AllowEvents :mode xcb:Allow:AsyncKeyboard -- cgit 1.4.1 From 6c8255bf3978a4df3d76ffd6f7d6bbdbba8bba19 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 13 Jul 2016 18:51:32 +0800 Subject: Add/improve some ICCCM/EWMH features * exwm-floating.el (exwm-floating--set-allowed-actions) (exwm-floating--set-floating, exwm-floating--unset-floating): Add _NET_WM_ALLOWED_ACTIONS support. * exwm-floating.el (exwm-floating--set-floating) (exwm-floating--unset-floating): Support initial state hint. * exwm.el (exwm--update-hints): Fetch initial state. (exwm--update-state, exwm--on-PropertyNotify): WM_STATE is not intended to be read. * exwm-core.el (exwm-state): * exwm-floating.el (exwm-floating-hide): * exwm-input.el (exwm-input--update-focus): * exwm-layout.el (exwm-layout--set-state) (exwm-layout--iconic-state-p, exwm-layout--show, exwm-layout--hide): * exwm-manage.el (exwm-manage--on-MapRequest): Improve WM_STATE support. * exwm-input.el (exwm-input--set-focus): * exwm-input.el (exwm-input--update-focus) (exwm-input--set-active-window): * exwm.el (exwm--on-ClientMessage): Add _NET_ACTIVE_WINDOW support. * exwm-layout.el (exwm-layout--set-client-list-stacking): Improve _NET_CLIENT_LIST_STACKING support. * exwm-manage.el (exwm-manage--set-client-list) (exwm-manage--manage-window, exwm-manage--unmanage-window): Improve _NET_CLIENT_LIST support. * exwm-manage.el (exwm-manage--manage-window): * exwm-workspace.el (exwm-workspace--set-desktop) (exwm-workspace-move-window): * exwm.el (exwm--on-ClientMessage): Add _NET_WM_DESKTOP support. * exwm-randr.el (exwm-randr--refresh): * exwm-workspace.el (exwm-workspace--set-desktop-geometry) (exwm-workspace--init): Add _NET_DESKTOP_GEOMETRY support. * exwm-workspace.el (exwm-workspace--set-desktop-geometry): Renamed from `exwm-workspace--update-desktop-geometry'. * exwm-randr.el (exwm-randr--refresh): Improve _NET_WORKAREA support. * exwm-workspace.el (exwm-workspace--set-fullscreen): Correct variables names. * exwm-workspace.el (exwm-workspace--init): * exwm.el (exwm--init-icccm-ewmh): Set _NET_NUMBER_OF_DESKTOPS in workspace module. * exwm-workspace.el (exwm-workspace--init): * exwm.el (exwm--init-icccm-ewmh): Set _NET_DESKTOP_VIEWPORT in workspace module. * exwm.el (exwm--on-ClientMessage): Improve _NET_CURRENT_DESKTOP support. * exwm.el (exwm--on-ClientMessage): Add _NET_CLOSE_WINDOW support. * exwm.el (exwm--on-ClientMessage): Add WM_CHANGE_STATE support. * exwm.el (exwm--init-icccm-ewmh): Update supported atoms. --- exwm-core.el | 2 +- exwm-floating.el | 46 ++++++++++--- exwm-input.el | 19 +++++- exwm-layout.el | 68 ++++++++++++------- exwm-manage.el | 46 +++++++++---- exwm-randr.el | 21 +++--- exwm-workspace.el | 45 +++++++++++-- exwm.el | 194 ++++++++++++++++++++++++++++++++++++------------------ 8 files changed, 310 insertions(+), 131 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 9eb6b62f66..c07c069c49 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -101,7 +101,7 @@ (defvar-local exwm--title-is-utf8 nil) (defvar-local exwm-transient-for nil "WM_TRANSIENT_FOR.") (defvar-local exwm--protocols nil) -(defvar-local exwm-state nil "WM_STATE.") +(defvar-local exwm-state xcb:icccm:WM_STATE:NormalState "WM_STATE.") ;; _NET_WM_NORMAL_HINTS (defvar-local exwm--normal-hints-x nil) (defvar-local exwm--normal-hints-y nil) diff --git a/exwm-floating.el b/exwm-floating.el index d4f57b73d2..9d4d948d30 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -49,13 +49,31 @@ (defvar exwm-floating--cursor-bottom-left nil) (defvar exwm-floating--cursor-left nil) +(defun exwm-floating--set-allowed-actions (id tilling) + "Set _NET_WM_ALLOWED_ACTIONS." + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_ALLOWED_ACTIONS + :window id + :data (if tilling + (vector xcb:Atom:_NET_WM_ACTION_MINIMIZE + xcb:Atom:_NET_WM_ACTION_FULLSCREEN + xcb:Atom:_NET_WM_ACTION_CHANGE_DESKTOP + xcb:Atom:_NET_WM_ACTION_CLOSE) + (vector xcb:Atom:_NET_WM_ACTION_MOVE + xcb:Atom:_NET_WM_ACTION_RESIZE + xcb:Atom:_NET_WM_ACTION_MINIMIZE + xcb:Atom:_NET_WM_ACTION_FULLSCREEN + xcb:Atom:_NET_WM_ACTION_CHANGE_DESKTOP + xcb:Atom:_NET_WM_ACTION_CLOSE))))) + (defvar exwm-workspace--current) (defvar exwm-workspace--list) (defvar exwm-workspace-current-index) (defvar exwm-workspace--switch-history-outdated) -(declare-function exwm-layout--refresh "exwm-layout.el") -(declare-function exwm-layout--show "exwm-layout.el") +(declare-function exwm-layout--refresh "exwm-layout.el" ()) +(declare-function exwm-layout--show "exwm-layout.el" (id &optional window)) +(declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) (defun exwm-floating--set-floating (id) "Make window ID floating." @@ -205,6 +223,7 @@ xcb:ConfigWindow:Y)) :x (- x exwm-floating-border-width) :y (- y exwm-floating-border-width))) + (exwm-floating--set-allowed-actions id nil) (xcb:flush exwm--connection) ;; Set window/buffer (with-current-buffer (exwm--id->buffer id) @@ -217,9 +236,13 @@ (add-hook 'window-configuration-change-hook #'exwm-layout--refresh) (set-window-dedicated-p window t) (exwm-layout--show id window)) - (with-selected-frame exwm-workspace--current - (exwm-layout--refresh)) - (select-frame-set-input-focus frame)) + (if (exwm-layout--iconic-state-p id) + ;; Hide iconic floating X windows. + (with-current-buffer (exwm--id->buffer id) + (exwm-floating-hide)) + (with-selected-frame exwm-workspace--current + (exwm-layout--refresh)) + (select-frame-set-input-focus frame))) (run-hooks 'exwm-floating-setup-hook) ;; Redraw the frame. (redisplay)) @@ -269,6 +292,7 @@ :sibling (frame-parameter exwm-workspace--current 'exwm-container) :stack-mode xcb:StackMode:Above))) + (exwm-floating--set-allowed-actions id t) (xcb:flush exwm--connection) (with-current-buffer buffer (when exwm--floating-frame ;from floating to non-floating @@ -278,9 +302,11 @@ (setq window-size-fixed nil exwm--floating-frame nil exwm--frame exwm-workspace--current)) - (let ((window (frame-selected-window exwm-workspace--current))) - (set-window-buffer window buffer) - (select-window window))) + (unless (exwm-layout--iconic-state-p) + ;; Only show X windows in normal state. + (let ((window (frame-selected-window exwm-workspace--current))) + (set-window-buffer window buffer) + (select-window window)))) (run-hooks 'exwm-floating-exit-hook)) ;;;###autoload @@ -292,6 +318,8 @@ (exwm-floating--unset-floating exwm--id) (exwm-floating--set-floating exwm--id)))) +(declare-function exwm-layout--set-state "exwm-layout.el" (id state)) + ;;;###autoload (defun exwm-floating-hide () "Hide the current floating X window (which would show again when selected)." @@ -304,7 +332,7 @@ :window exwm--container :value-mask xcb:ConfigWindow:StackMode :stack-mode xcb:StackMode:Below)) - ;; FIXME: Should it be put into iconic state? + (exwm-layout--set-state exwm--id xcb:icccm:WM_STATE:IconicState) (xcb:flush exwm--connection) (select-frame-set-input-focus exwm-workspace--current))) diff --git a/exwm-input.el b/exwm-input.el index 1c711b36c7..3cb189bb50 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -76,6 +76,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") :revert-to xcb:InputFocus:PointerRoot :focus id :time xcb:Time:CurrentTime))) + (exwm-input--set-active-window id) (xcb:flush exwm--connection)))) (defvar exwm-input--focus-window nil "The (Emacs) window to be focused.") @@ -99,6 +100,9 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (defvar exwm-workspace-current-index) (defvar exwm-workspace--minibuffer) +(declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) +(declare-function exwm-layout--set-state "exwm-layout.el" (id state)) + (defun exwm-input--update-focus () "Update input focus." (when (and (window-live-p exwm-input--focus-window) @@ -136,13 +140,26 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") :window exwm--container :value-mask xcb:ConfigWindow:StackMode :stack-mode xcb:StackMode:Above))) + ;; This floating X window might be hide by `exwm-floating-hide'. + (when (exwm-layout--iconic-state-p) + (exwm-layout--set-state exwm--id + xcb:icccm:WM_STATE:NormalState)) (xcb:flush exwm--connection))) (when (eq (selected-window) exwm-input--focus-window) (exwm--log "Focus on %s" exwm-input--focus-window) (select-frame-set-input-focus (window-frame exwm-input--focus-window) - t))) + t) + (exwm-input--set-active-window) + (xcb:flush exwm--connection))) (setq exwm-input--focus-window nil)))) +(defun exwm-input--set-active-window (&optional id) + "Set _NET_ACTIVE_WINDOW." + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_ACTIVE_WINDOW + :window exwm--root + :data (or id xcb:Window:None)))) + (defvar exwm-input--during-key-sequence nil "Non-nil indicates Emacs is waiting for more keys to form a key sequence.") (defvar exwm-input--temp-line-mode nil diff --git a/exwm-layout.el b/exwm-layout.el index 2d85580085..259788f66c 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -50,6 +50,20 @@ xcb:ConfigWindow:Height)) :width width :height height)))) +(defun exwm-layout--set-state (id state) + "Set WM_STATE." + (xcb:+request exwm--connection + (make-instance 'xcb:icccm:set-WM_STATE + :window id :state state :icon xcb:Window:None)) + (with-current-buffer (exwm--id->buffer id) + (setq exwm-state state))) + +(defun exwm-layout--iconic-state-p (&optional id) + (= xcb:icccm:WM_STATE:IconicState + (if id + (buffer-local-value 'exwm-state (exwm--id->buffer id)) + exwm-state))) + (defun exwm-layout--show (id &optional window) "Show window ID exactly fit in the Emacs window WINDOW." (exwm--log "Show #x%x in %s" id window) @@ -101,11 +115,7 @@ (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window id)) (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window exwm--container)) - (xcb:+request exwm--connection - (make-instance 'xcb:icccm:set-WM_STATE - :window id :state xcb:icccm:WM_STATE:NormalState - :icon xcb:Window:None)) - (setq exwm-state xcb:icccm:WM_STATE:NormalState)) + (exwm-layout--set-state id xcb:icccm:WM_STATE:NormalState)) (xcb:+request exwm--connection (make-instance 'xcb:SendEvent :propagate 0 :destination id @@ -125,25 +135,21 @@ (defun exwm-layout--hide (id) "Hide window ID." (with-current-buffer (exwm--id->buffer id) - (unless (eq xcb:icccm:WM_STATE:IconicState exwm-state) ;already hidden + (unless (exwm-layout--iconic-state-p) ;already hidden (exwm--log "Hide #x%x" id) (xcb:+request exwm--connection (make-instance 'xcb:ChangeWindowAttributes :window id :value-mask xcb:CW:EventMask :event-mask xcb:EventMask:NoEvent)) - (xcb:+request exwm--connection (make-instance 'xcb:UnmapWindow :window id)) + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow :window id)) (xcb:+request exwm--connection (make-instance 'xcb:ChangeWindowAttributes :window id :value-mask xcb:CW:EventMask :event-mask exwm--client-event-mask)) (xcb:+request exwm--connection (make-instance 'xcb:UnmapWindow :window exwm--container)) - (xcb:+request exwm--connection - (make-instance 'xcb:icccm:set-WM_STATE - :window id - :state xcb:icccm:WM_STATE:IconicState - :icon xcb:Window:None)) - (setq exwm-state xcb:icccm:WM_STATE:IconicState) + (exwm-layout--set-state id xcb:icccm:WM_STATE:IconicState) (xcb:flush exwm--connection)))) (defvar exwm-workspace--current) @@ -243,6 +249,29 @@ selected by `other-buffer'." (defvar exwm-layout-show-all-buffers nil "Non-nil to allow switching to buffers on other workspaces.") +(defun exwm-layout--set-client-list-stacking () + "Set _NET_CLIENT_LIST_STACKING." + (let (id clients-floating clients clients-iconic clients-other) + (dolist (pair exwm--id-buffer-alist) + (setq id (car pair)) + (with-current-buffer (cdr pair) + (if (eq exwm--frame exwm-workspace--current) + (if exwm--floating-frame + ;; A floating X window on the current workspace. + (setq clients-floating (cons id clients-floating)) + (if (get-buffer-window (cdr pair) exwm-workspace--current) + ;; A normal tilling X window on the current workspace. + (setq clients (cons id clients)) + ;; An iconic tilling X window on the current workspace. + (setq clients-iconic (cons id clients-iconic)))) + ;; X window on other workspaces. + (setq clients-other (cons id clients-other))))) + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_CLIENT_LIST_STACKING + :window exwm--root + :data (vconcat (append clients-other clients-iconic + clients clients-floating)))))) + (defun exwm-layout--refresh () "Refresh layout." (let ((frame (selected-frame)) @@ -310,18 +339,7 @@ selected by `other-buffer'." (when (and (eq major-mode 'exwm-mode) (or exwm--floating-frame (not (eq frame exwm--frame)))) (switch-to-prev-buffer window))))) - ;; Update _NET_CLIENT_LIST_STACKING - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_CLIENT_LIST_STACKING - :window exwm--root - :data (vconcat - (delq nil - (mapcar - (lambda (buffer) - (with-current-buffer buffer - (when (eq major-mode 'exwm-mode) - exwm--id))) - (buffer-list)))))) + (exwm-layout--set-client-list-stacking) (xcb:flush exwm--connection)))) (defun exwm-layout--on-minibuffer-setup () diff --git a/exwm-manage.el b/exwm-manage.el index 924103e535..d4b3de48f9 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -68,8 +68,16 @@ corresponding buffer.") (elt value 2))) ;MotifWmHints.decorations (setq exwm--mwm-hints-decorations nil)))))))) +(defun exwm-manage--set-client-list () + "Set _NET_CLIENT_LIST." + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_CLIENT_LIST + :window exwm--root + :data (vconcat (mapcar #'car exwm--id-buffer-alist))))) + (defvar exwm-workspace--current) (defvar exwm-workspace--switch-history-outdated) +(defvar exwm-workspace-current-index) (declare-function exwm--update-window-type "exwm.el" (id &optional force)) (declare-function exwm--update-class "exwm.el" (id &optional force)) @@ -78,10 +86,10 @@ corresponding buffer.") (declare-function exwm--update-title "exwm.el" (id)) (declare-function exwm--update-hints "exwm.el" (id &optional force)) (declare-function exwm--update-protocols "exwm.el" (id &optional force)) -(declare-function exwm--update-state "exwm.el" (id &optional force)) (declare-function exwm--update-strut "exwm.el" (id)) (declare-function exwm-floating--set-floating "exwm-floating.el" (id)) (declare-function exwm-floating--unset-floating "exwm-floating.el" (id)) +(declare-function exwm-workspace--set-desktop "exwm-workspace.el" (id)) (defun exwm-manage--manage-window (id) "Manage window ID." @@ -94,7 +102,9 @@ corresponding buffer.") :event-mask exwm--client-event-mask)) (throw 'return 'dead)) (with-current-buffer (generate-new-buffer "*EXWM*") - (push `(,id . ,(current-buffer)) exwm--id-buffer-alist) + ;; Keep the oldest X window first. + (setq exwm--id-buffer-alist + (nconc exwm--id-buffer-alist `((,id . ,(current-buffer))))) (exwm-mode) (setq exwm--id id) (exwm--update-window-type id) @@ -213,14 +223,10 @@ corresponding buffer.") :keyboard-mode xcb:GrabMode:Async :confine-to xcb:Window:None :cursor xcb:Cursor:None :button button :modifiers xcb:ModMask:Any))) - (xcb:+request exwm--connection ;update _NET_CLIENT_LIST - (make-instance 'xcb:ewmh:set-_NET_CLIENT_LIST - :window exwm--root - :data (vconcat (mapcar #'car exwm--id-buffer-alist)))) + (exwm-manage--set-client-list) (xcb:flush exwm--connection) (exwm--update-title id) (exwm--update-protocols id) - (exwm--update-state id) (if (or exwm-transient-for exwm--fixed-size (memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY exwm-window-type) (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG exwm-window-type)) @@ -228,6 +234,16 @@ corresponding buffer.") (exwm-floating--unset-floating id)) (exwm-input-grab-keyboard id) (setq exwm-workspace--switch-history-outdated t) + ;; Set _NET_WM_DESKTOP or move window. + (let ((reply (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:ewmh:get-_NET_WM_DESKTOP + :window id))) + desktop) + (when reply + (setq desktop (slot-value reply 'value))) + (if (and desktop (/= desktop exwm-workspace-current-index)) + (exwm-workspace-move-window desktop id) + (exwm-workspace--set-desktop id))) (with-current-buffer (exwm--id->buffer id) (run-hooks 'exwm-manage-finish-hook))))) @@ -297,10 +313,7 @@ corresponding buffer.") (when floating (select-window (frame-selected-window exwm-workspace--current))))) - (xcb:+request exwm--connection ;update _NET_CLIENT_LIST - (make-instance 'xcb:ewmh:set-_NET_CLIENT_LIST - :window exwm--root - :data (vconcat (mapcar #'car exwm--id-buffer-alist)))) + (exwm-manage--set-client-list) (xcb:flush exwm--connection)))) (defun exwm-manage--scan () @@ -523,13 +536,22 @@ border-width: %d; sibling: #x%x; stack-mode: %d" :stack-mode stack-mode)))))) (xcb:flush exwm--connection)) +(declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) + (defun exwm-manage--on-MapRequest (data _synthetic) "Handle MapRequest event." (let ((obj (make-instance 'xcb:MapRequest))) (xcb:unmarshal obj data) (with-slots (parent window) obj (if (assoc window exwm--id-buffer-alist) - (exwm--log "#x%x is already managed" window) + (with-current-buffer (exwm--id->buffer window) + (if (exwm-layout--iconic-state-p) + ;; State change: iconic => normal. + (when (eq exwm--frame exwm-workspace--current) + (set-window-buffer (frame-selected-window exwm--frame) + (current-buffer)) + (select-window (frame-selected-window exwm--frame))) + (exwm--log "#x%x is already managed" window))) (if (/= exwm--root parent) (progn (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window window)) diff --git a/exwm-randr.el b/exwm-randr.el index d30f687789..f71120a4bb 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -59,13 +59,13 @@ (defvar exwm-workspace--list) (declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame)) -(declare-function exwm-workspace--update-workareas "exwm-workspace.el" +(declare-function exwm-workspace--set-workareas "exwm-workspace.el" (&optional workareas)) +(declare-function exwm-workspace--set-desktop-geometry "exwm-workspace.el" ()) (defun exwm-randr--refresh () "Refresh workspaces according to the updated RandR info." - (let (output-name geometry output-plist default-geometry workareas - viewports) + (let (output-name geometry output-plist default-geometry workareas) ;; Query all outputs (with-slots (config-timestamp outputs) (xcb:+request-unchecked+reply exwm--connection @@ -107,16 +107,11 @@ (set-frame-parameter frame 'exwm-geometry geometry) (exwm-workspace--set-fullscreen frame) (with-slots (x y width height) geometry - (setq workareas - (nconc workareas (list x y width height)) - viewports (nconc viewports (list x y)))))) - ;; Update _NET_WORKAREA - (exwm-workspace--update-workareas (vconcat workareas)) - ;; Update _NET_DESKTOP_VIEWPORT - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT - :window exwm--root - :data (vconcat viewports))) + (setq workareas (nconc workareas (list x y width height)))))) + ;; Set _NET_DESKTOP_GEOMETRY. + (exwm-workspace--set-desktop-geometry) + ;; Set _NET_WORKAREA. + (exwm-workspace--set-workareas (vconcat workareas)) (xcb:flush exwm--connection) (run-hooks 'exwm-randr-refresh-hook)))) diff --git a/exwm-workspace.el b/exwm-workspace.el index 64c636eb2c..bde423d727 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -166,7 +166,7 @@ Value nil means to use the default position which is fixed at bottom, while height* height)) (when (and (eq frame exwm-workspace--current) (exwm-workspace--minibuffer-own-frame-p)) - (exwm-workspace--resize-minibuffer-frame width* height*)) + (exwm-workspace--resize-minibuffer-frame width height)) (exwm-layout--resize-container id container 0 0 width* height*) (exwm-layout--resize-container nil workspace x* y* width* height* t) (xcb:flush exwm--connection))) @@ -275,7 +275,7 @@ The optional FORCE option is for internal use only." (set-frame-parameter frame 'exwm--urgency nil) ;; Update switch workspace history (setq exwm-workspace--switch-history-outdated t) - ;; Update _NET_CURRENT_DESKTOP + ;; Set _NET_CURRENT_DESKTOP. (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_CURRENT_DESKTOP :window exwm--root :data index)) @@ -292,6 +292,14 @@ The optional FORCE option is for internal use only." (exwm--log "Workspace was switched unexpectedly") (exwm-workspace-switch index))))) +(defun exwm-workspace--set-desktop (id) + "Set _NET_WM_DESKTOP for X window ID." + (with-current-buffer (exwm--id->buffer id) + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_DESKTOP + :window id + :data (cl-position exwm--frame exwm-workspace--list))))) + (defvar exwm-floating-border-width) (defvar exwm-floating-border-color) @@ -433,7 +441,10 @@ The optional FORCE option is for internal use only." :stack-mode xcb:StackMode:Above))) (xcb:flush exwm--connection) (set-window-buffer (frame-selected-window frame) - (exwm--id->buffer id))))) + (exwm--id->buffer id))) + ;; Set _NET_WM_DESKTOP. + (exwm-workspace--set-desktop id) + (xcb:flush exwm--connection))) (setq exwm-workspace--switch-history-outdated t))) ;;;###autoload @@ -696,8 +707,17 @@ The optional FORCE option is for internal use only." (server-save-buffers-kill-terminal nil) nil))) -(defun exwm-workspace--update-workareas (&optional workareas) - "Update _NET_WORKAREA." +(defun exwm-workspace--set-desktop-geometry () + "Set _NET_DESKTOP_GEOMETRY." + ;; We don't support large desktop so it's the same with screen size. + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_DESKTOP_GEOMETRY + :window exwm--root + :width (x-display-pixel-width) + :height (x-display-pixel-height)))) + +(defun exwm-workspace--set-workareas (&optional workareas) + "Set _NET_WORKAREA." ;; Calculate workareas if not present. (unless workareas (if (frame-parameter (car exwm-workspace--list) 'exwm-geometry) @@ -884,9 +904,20 @@ The optional FORCE option is for internal use only." (xcb:flush exwm--connection) ;; We have to advice `x-create-frame' or every call to it would hang EXWM (advice-add 'x-create-frame :around #'exwm-workspace--x-create-frame) + ;; Set _NET_NUMBER_OF_DESKTOPS (it's currently fixed). + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_NUMBER_OF_DESKTOPS + :window exwm--root :data exwm-workspace-number)) + ;; Set _NET_DESKTOP_GEOMETRY. + (exwm-workspace--set-desktop-geometry) + ;; Set _NET_DESKTOP_VIEWPORT (we don't support large desktop). + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT + :window exwm--root + :data (make-vector (* 2 exwm-workspace-number) 0))) ;; Set _NET_WORKAREA. - (exwm-workspace--update-workareas) - ;; Set _NET_VIRTUAL_ROOTS + (exwm-workspace--set-workareas) + ;; Set _NET_VIRTUAL_ROOTS (it's currently fixed.) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_VIRTUAL_ROOTS :window exwm--root diff --git a/exwm.el b/exwm.el index 3b068786a1..232c863fad 100644 --- a/exwm.el +++ b/exwm.el @@ -203,10 +203,12 @@ (let ((reply (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:icccm:get-WM_HINTS :window id)))) (when (and reply (slot-value reply 'flags)) ;nil when destroyed - (with-slots (flags input) reply + (with-slots (flags input initial-state) reply (when flags (unless (= 0 (logand flags xcb:icccm:WM_HINTS:InputHint)) (setq exwm--hints-input (when input (= 1 input)))) + (unless (= 0 (logand flags xcb:icccm:WM_HINTS:StateHint)) + (setq exwm-state initial-state)) (unless (= 0 (logand flags xcb:icccm:WM_HINTS:UrgencyHint)) (setq exwm--hints-urgency t)))) (when (and exwm--hints-urgency @@ -225,17 +227,6 @@ (when reply ;nil when destroyed (setq exwm--protocols (append (slot-value reply 'value) nil))))))) -(defun exwm--update-state (id &optional force) - "Update WM_STATE." - (with-current-buffer (exwm--id->buffer id) - (unless (and exwm-state (not force)) - (let ((reply (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:icccm:get-WM_STATE :window id)))) - (when reply ;nil when destroyed - (setq exwm-state (or (slot-value reply 'state) - ;; Default to normal state - xcb:icccm:WM_STATE:NormalState))))))) - (defun exwm--update-strut-legacy (id) "Update _NET_WM_STRUT." (unless exwm-workspace--strut-is-partial @@ -250,7 +241,7 @@ (when (exwm-workspace--minibuffer-own-frame-p) (exwm-workspace--resize-minibuffer-frame)) ;; Update _NET_WORKAREA. - (exwm-workspace--update-workareas)))) + (exwm-workspace--set-workareas)))) (defun exwm--update-strut-partial (id) "Update _NET_WM_STRUT_PARTIAL." @@ -269,7 +260,7 @@ (when (exwm-workspace--minibuffer-own-frame-p) (exwm-workspace--resize-minibuffer-frame)) ;; Update _NET_WORKAREA. - (exwm-workspace--update-workareas))) + (exwm-workspace--set-workareas))) (defun exwm--update-strut (id) "Update _NET_WM_STRUT_PARTIAL or _NET_WM_STRUT." @@ -308,8 +299,6 @@ (exwm--update-hints id t)) ((= atom xcb:Atom:WM_PROTOCOLS) (exwm--update-protocols id t)) - ((= atom xcb:Atom:WM_STATE) - (exwm--update-state id t)) ((= atom xcb:Atom:_NET_WM_USER_TIME)) ;ignored (t (exwm--log "Unhandled PropertyNotify: %s(%d)" (x-get-atom-name atom exwm-workspace--current) @@ -324,6 +313,26 @@ id (slot-value obj 'window) data (slot-value (slot-value obj 'data) 'data32)) (cond + ;; _NET_CURRENT_DESKTOP. + ((= type xcb:Atom:_NET_CURRENT_DESKTOP) + (exwm-workspace-switch (elt data 0))) + ;; _NET_ACTIVE_WINDOW. + ((= type xcb:Atom:_NET_ACTIVE_WINDOW) + (let ((buffer (exwm--id->buffer id))) + (when (buffer-live-p buffer) + (with-current-buffer buffer + (when (eq exwm--frame exwm-workspace--current) + (when (exwm-layout--iconic-state-p) + ;; State change: iconic => normal. + (set-window-buffer (frame-selected-window exwm--frame) + (current-buffer))) + ;; Focus transfer. + (select-window (get-buffer-window))))))) + ;; _NET_CLOSE_WINDOW. + ((= type xcb:Atom:_NET_CLOSE_WINDOW) + (let ((buffer (exwm--id->buffer id))) + (when (buffer-live-p buffer) + (kill-buffer buffer)))) ;; _NET_WM_MOVERESIZE ((= type xcb:Atom:_NET_WM_MOVERESIZE) (let ((direction (elt data 2)) @@ -359,6 +368,11 @@ :window id :left left :right right :top top :bottom btm))) (xcb:flush exwm--connection)) + ;; _NET_WM_DESKTOP. + ((= type xcb:Atom:_NET_WM_DESKTOP) + (let ((buffer (exwm--id->buffer id))) + (when (buffer-live-p buffer) + (exwm-workspace-move-window (elt data 0) id)))) ;; _NET_WM_STATE ((= type xcb:Atom:_NET_WM_STATE) (let ((action (elt data 0)) @@ -428,6 +442,12 @@ (cond ((= type xcb:Atom:_NET_WM_PING) (setq exwm-manage--ping-lock nil)) (t (exwm--log "Unhandled WM_PROTOCOLS of type: %d" type))))) + ((= type xcb:Atom:WM_CHANGE_STATE) + (let ((buffer (exwm--id->buffer id))) + (when (and (buffer-live-p buffer) + (= (elt data 0) xcb:icccm:WM_STATE:IconicState)) + (with-current-buffer buffer + (bury-buffer))))) (t (exwm--log "Unhandled client message: %s" obj))))) (defun exwm--init-icccm-ewmh () @@ -435,49 +455,106 @@ ;; Handle PropertyNotify event (xcb:+event exwm--connection 'xcb:PropertyNotify #'exwm--on-PropertyNotify) ;; Handle relevant client messages - ;; FIXME: WM_STATE client messages (normal => iconic) - ;; WM_COLORMAP_NOTIFY (xcb:+event exwm--connection 'xcb:ClientMessage #'exwm--on-ClientMessage) ;; Set _NET_SUPPORTED (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_SUPPORTED :window exwm--root - :data (vector xcb:Atom:_NET_SUPPORTED - xcb:Atom:_NET_CLIENT_LIST - xcb:Atom:_NET_CLIENT_LIST_STACKING - xcb:Atom:_NET_NUMBER_OF_DESKTOPS - xcb:Atom:_NET_DESKTOP_VIEWPORT - xcb:Atom:_NET_CURRENT_DESKTOP - xcb:Atom:_NET_WORKAREA - xcb:Atom:_NET_SUPPORTING_WM_CHECK - xcb:Atom:_NET_VIRTUAL_ROOTS - xcb:Atom:_NET_WM_MOVERESIZE - xcb:Atom:_NET_REQUEST_FRAME_EXTENTS - xcb:Atom:_NET_FRAME_EXTENTS - xcb:Atom:_NET_WM_NAME - xcb:Atom:_NET_WM_STRUT - xcb:Atom:_NET_WM_STRUT_PARTIAL - ;; - xcb:Atom:_NET_WM_WINDOW_TYPE - xcb:Atom:_NET_WM_WINDOW_TYPE_TOOLBAR - xcb:Atom:_NET_WM_WINDOW_TYPE_MENU - xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY - xcb:Atom:_NET_WM_WINDOW_TYPE_SPLASH - xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG - xcb:Atom:_NET_WM_WINDOW_TYPE_DROPDOWN_MENU - xcb:Atom:_NET_WM_WINDOW_TYPE_POPUP_MENU - xcb:Atom:_NET_WM_WINDOW_TYPE_TOOLTIP - xcb:Atom:_NET_WM_WINDOW_TYPE_NOTIFICATION - xcb:Atom:_NET_WM_WINDOW_TYPE_COMBO - xcb:Atom:_NET_WM_WINDOW_TYPE_DND - xcb:Atom:_NET_WM_WINDOW_TYPE_NORMAL - ;; - xcb:Atom:_NET_WM_STATE - xcb:Atom:_NET_WM_STATE_MODAL - xcb:Atom:_NET_WM_STATE_FULLSCREEN - xcb:Atom:_NET_WM_STATE_DEMANDS_ATTENTION - ;; FIXME: more? - ))) + :data (vector + ;; Root windows properties. + xcb:Atom:_NET_SUPPORTED + xcb:Atom:_NET_CLIENT_LIST + xcb:Atom:_NET_CLIENT_LIST_STACKING + xcb:Atom:_NET_NUMBER_OF_DESKTOPS + xcb:Atom:_NET_DESKTOP_GEOMETRY + xcb:Atom:_NET_DESKTOP_VIEWPORT + xcb:Atom:_NET_CURRENT_DESKTOP + ;; xcb:Atom:_NET_DESKTOP_NAMES + xcb:Atom:_NET_ACTIVE_WINDOW + xcb:Atom:_NET_WORKAREA + xcb:Atom:_NET_SUPPORTING_WM_CHECK + xcb:Atom:_NET_VIRTUAL_ROOTS + ;; xcb:Atom:_NET_DESKTOP_LAYOUT + ;; xcb:Atom:_NET_SHOWING_DESKTOP + + ;; Other root window messages. + xcb:Atom:_NET_CLOSE_WINDOW + ;; xcb:Atom:_NET_MOVERESIZE_WINDOW + xcb:Atom:_NET_WM_MOVERESIZE + ;; xcb:Atom:_NET_RESTACK_WINDOW + xcb:Atom:_NET_REQUEST_FRAME_EXTENTS + + ;; Application window properties. + xcb:Atom:_NET_WM_NAME + ;; xcb:Atom:_NET_WM_VISIBLE_NAME + ;; xcb:Atom:_NET_WM_ICON_NAME + ;; xcb:Atom:_NET_WM_VISIBLE_ICON_NAME + xcb:Atom:_NET_WM_DESKTOP + ;; + xcb:Atom:_NET_WM_WINDOW_TYPE + ;; xcb:Atom:_NET_WM_WINDOW_TYPE_DESKTOP + xcb:Atom:_NET_WM_WINDOW_TYPE_DOCK + xcb:Atom:_NET_WM_WINDOW_TYPE_TOOLBAR + xcb:Atom:_NET_WM_WINDOW_TYPE_MENU + xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY + xcb:Atom:_NET_WM_WINDOW_TYPE_SPLASH + xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG + xcb:Atom:_NET_WM_WINDOW_TYPE_DROPDOWN_MENU + xcb:Atom:_NET_WM_WINDOW_TYPE_POPUP_MENU + xcb:Atom:_NET_WM_WINDOW_TYPE_TOOLTIP + xcb:Atom:_NET_WM_WINDOW_TYPE_NOTIFICATION + xcb:Atom:_NET_WM_WINDOW_TYPE_COMBO + xcb:Atom:_NET_WM_WINDOW_TYPE_DND + xcb:Atom:_NET_WM_WINDOW_TYPE_NORMAL + ;; + xcb:Atom:_NET_WM_STATE + xcb:Atom:_NET_WM_STATE_MODAL + ;; xcb:Atom:_NET_WM_STATE_STICKY + ;; xcb:Atom:_NET_WM_STATE_MAXIMIZED_VERT + ;; xcb:Atom:_NET_WM_STATE_MAXIMIZED_HORZ + ;; xcb:Atom:_NET_WM_STATE_SHADED + ;; xcb:Atom:_NET_WM_STATE_SKIP_TASKBAR + ;; xcb:Atom:_NET_WM_STATE_SKIP_PAGER + ;; xcb:Atom:_NET_WM_STATE_HIDDEN + xcb:Atom:_NET_WM_STATE_FULLSCREEN + ;; xcb:Atom:_NET_WM_STATE_ABOVE + ;; xcb:Atom:_NET_WM_STATE_BELOW + xcb:Atom:_NET_WM_STATE_DEMANDS_ATTENTION + ;; xcb:Atom:_NET_WM_STATE_FOCUSED + ;; + xcb:Atom:_NET_WM_ALLOWED_ACTIONS + xcb:Atom:_NET_WM_ACTION_MOVE + xcb:Atom:_NET_WM_ACTION_RESIZE + xcb:Atom:_NET_WM_ACTION_MINIMIZE + ;; xcb:Atom:_NET_WM_ACTION_SHADE + ;; xcb:Atom:_NET_WM_ACTION_STICK + ;; xcb:Atom:_NET_WM_ACTION_MAXIMIZE_HORZ + ;; xcb:Atom:_NET_WM_ACTION_MAXIMIZE_VERT + xcb:Atom:_NET_WM_ACTION_FULLSCREEN + xcb:Atom:_NET_WM_ACTION_CHANGE_DESKTOP + xcb:Atom:_NET_WM_ACTION_CLOSE + ;; xcb:Atom:_NET_WM_ACTION_ABOVE + ;; xcb:Atom:_NET_WM_ACTION_BELOW + ;; + xcb:Atom:_NET_WM_STRUT + xcb:Atom:_NET_WM_STRUT_PARTIAL + ;; xcb:Atom:_NET_WM_ICON_GEOMETRY + ;; xcb:Atom:_NET_WM_ICON + xcb:Atom:_NET_WM_PID + ;; xcb:Atom:_NET_WM_HANDLED_ICONS + ;; xcb:Atom:_NET_WM_USER_TIME + ;; xcb:Atom:_NET_WM_USER_TIME_WINDOW + xcb:Atom:_NET_FRAME_EXTENTS + ;; xcb:Atom:_NET_WM_OPAQUE_REGION + ;; xcb:Atom:_NET_WM_BYPASS_COMPOSITOR + + ;; Window manager protocols. + xcb:Atom:_NET_WM_PING + ;; xcb:Atom:_NET_WM_SYNC_REQUEST + ;; xcb:Atom:_NET_WM_FULLSCREEN_MONITORS + + ;; Other properties. + xcb:Atom:_NET_WM_FULL_PLACEMENT))) ;; Create a child window for setting _NET_SUPPORTING_WM_CHECK (let ((new-id (xcb:generate-id exwm--connection))) (xcb:+request exwm--connection @@ -496,15 +573,6 @@ (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_NAME :window i :data "EXWM")))) - ;; Set _NET_NUMBER_OF_DESKTOPS - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_NUMBER_OF_DESKTOPS - :window exwm--root :data exwm-workspace-number)) - ;; Set _NET_DESKTOP_VIEWPORT - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT - :window exwm--root - :data (make-vector (* 2 exwm-workspace-number) 0))) (xcb:flush exwm--connection)) (defvar exwm-init-hook nil -- cgit 1.4.1 From 39dc328157a970742aa40d3d9169376d2208fce3 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 14 Jul 2016 22:08:27 +0800 Subject: Fix various stability issues * exwm-input.el (exwm-input--on-KeyPress-line-mode) (exwm-input--on-KeyPress-char-mode): Append events at the tail. * exwm-manage.el (exwm-manage--unmanage-window): Remove the _NET_WM_DESKTOP property when an X window is withdrawn. * exwm-systemtray.el (exwm-systemtray--init): * exwm-workspace.el (exwm-workspace--confirm-kill-emacs): Issue warning rather than error when there's an existing tray running. * exwm.el (exwm--on-ClientMessage): The buffer window can be on a floating frame. --- exwm-input.el | 6 ++++-- exwm-manage.el | 14 ++++++++++++-- exwm-systemtray.el | 6 ++++-- exwm-workspace.el | 2 +- exwm.el | 2 +- 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 3cb189bb50..8813f762c6 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -313,7 +313,8 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (unless minibuffer-window (setq exwm-input--during-key-sequence t)) ;; Feed this event to command loop. Also force it to be added to ;; `this-command-keys'. - (push (cons t event) unread-command-events)) + (setq unread-command-events + (append unread-command-events `((t . ,event))))) (xcb:+request exwm--connection (make-instance 'xcb:AllowEvents :mode (or mode xcb:Allow:ReplayKeyboard) @@ -336,7 +337,8 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (setq exwm-input--temp-line-mode t exwm-input--during-key-sequence t) (exwm-input--grab-keyboard)) ;grab keyboard temporarily - (push event unread-command-events)))) + (setq unread-command-events + (append unread-command-events (list event)))))) (xcb:+request exwm--connection (make-instance 'xcb:AllowEvents :mode xcb:Allow:AsyncKeyboard diff --git a/exwm-manage.el b/exwm-manage.el index d4b3de48f9..13948902d2 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -248,7 +248,11 @@ corresponding buffer.") (run-hooks 'exwm-manage-finish-hook))))) (defun exwm-manage--unmanage-window (id &optional withdraw-only) - "Unmanage window ID." + "Unmanage window ID. + +If WITHDRAW-ONLY is non-nil, the X window will be properly placed back to the +root window. Set WITHDRAW-ONLY to 'quit if this functions is used when window +manager is shutting down." (let ((buffer (exwm--id->buffer id))) (exwm--log "Unmanage #x%x (buffer: %s, widthdraw: %s)" id buffer withdraw-only) @@ -295,7 +299,13 @@ corresponding buffer.") ;; Delete WM_STATE property (xcb:+request exwm--connection (make-instance 'xcb:DeleteProperty - :window id :property xcb:Atom:WM_STATE))) + :window id :property xcb:Atom:WM_STATE)) + (unless (eq withdraw-only 'quit) + ;; Remove _NET_WM_DESKTOP. + (xcb:+request exwm--connection + (make-instance 'xcb:DeleteProperty + :window id + :property xcb:Atom:_NET_WM_DESKTOP)))) (when exwm--floating-frame ;; Unmap the floating frame before destroying the containers. (let ((window (frame-parameter exwm--floating-frame 'exwm-outer-id))) diff --git a/exwm-systemtray.el b/exwm-systemtray.el index d1783debdb..db0e0455d2 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -296,7 +296,7 @@ You shall use the default value if using auto-hide minibuffer.") (defvar xcb:Atom:_NET_SYSTEM_TRAY_S0) (defvar exwm-workspace--minibuffer) -(defun exwm-systemtray--init () +(cl-defun exwm-systemtray--init () "Initialize system tray module." (cl-assert (not exwm-systemtray--connection)) (cl-assert (not exwm-systemtray--list)) @@ -319,7 +319,9 @@ You shall use the default value if using auto-hide minibuffer.") (make-instance 'xcb:GetSelectionOwner :selection xcb:Atom:_NET_SYSTEM_TRAY_S0)) (when (/= owner xcb:Window:None) - (error "[EXWM] Other system tray detected"))) + (xcb:disconnect exwm-systemtray--connection) + (warn "[EXWM] Other system tray detected") + (cl-return-from exwm-systemtray--init))) (let ((id (xcb:generate-id exwm-systemtray--connection))) (setq exwm-systemtray--selection-owner-window id) (xcb:+request exwm-systemtray--connection diff --git a/exwm-workspace.el b/exwm-workspace.el index bde423d727..6902151f42 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -667,7 +667,7 @@ The optional FORCE option is for internal use only." x (if (= x 1) "" "s") prompt)))) ;; Unmanage all X windows. (dolist (i exwm--id-buffer-alist) - (exwm-manage--unmanage-window (car i) t) + (exwm-manage--unmanage-window (car i) 'quit) (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window (car i)))) ;; Reparent out the minibuffer frame. diff --git a/exwm.el b/exwm.el index 232c863fad..3d7edcfeac 100644 --- a/exwm.el +++ b/exwm.el @@ -327,7 +327,7 @@ (set-window-buffer (frame-selected-window exwm--frame) (current-buffer))) ;; Focus transfer. - (select-window (get-buffer-window))))))) + (select-window (get-buffer-window nil t))))))) ;; _NET_CLOSE_WINDOW. ((= type xcb:Atom:_NET_CLOSE_WINDOW) (let ((buffer (exwm--id->buffer id))) -- cgit 1.4.1 From 0b8a373e4fd092b527b34070b6556be35983062c Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 15 Jul 2016 08:11:33 +0800 Subject: Fix a `unread-command-events' issue for Emacs 24 * exwm-input.el (exwm-input--unread-event) (exwm-input--on-KeyPress-line-mode): Use `unread-command-events' differently on Emacs 24 and 25. --- exwm-input.el | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 8813f762c6..57c0cc801a 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -287,6 +287,19 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (global-set-key key command) (cl-pushnew key exwm-input--global-keys)) +;; FIXME: Putting (t . EVENT) into `unread-command-events' does not really work +;; as documented in Emacs 24. Since inserting a conventional EVENT does +;; add it into (this-command-keys) there, we use `unread-command-events' +;; differently on Emacs 24 and 25. +(eval-and-compile + (if (< emacs-major-version 25) + (defsubst exwm-input--unread-event (event) + (setq unread-command-events + (append unread-command-events (list event)))) + (defsubst exwm-input--unread-event (event) + (setq unread-command-events + (append unread-command-events `((t . ,event))))))) + (defvar exwm-input-command-whitelist nil "A list of commands that when active all keys should be forwarded to Emacs.") (make-obsolete-variable 'exwm-input-command-whitelist @@ -313,8 +326,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (unless minibuffer-window (setq exwm-input--during-key-sequence t)) ;; Feed this event to command loop. Also force it to be added to ;; `this-command-keys'. - (setq unread-command-events - (append unread-command-events `((t . ,event))))) + (exwm-input--unread-event event)) (xcb:+request exwm--connection (make-instance 'xcb:AllowEvents :mode (or mode xcb:Allow:ReplayKeyboard) -- cgit 1.4.1 From 7f12d9fc7a88369a479ed2f0489ff3b10b347d13 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 15 Jul 2016 20:04:56 +0800 Subject: Add multi-dock support and fix fullscreen issues with dock * exwm.el (exwm--update-strut-legacy, exwm--update-strut-partial) (exwm--update-strut): Rename (strut => struts). * exwm-manage.el (exwm-manage--manage-window): Listen for UnmapNotify/DestroyNotify events of docks to stop tracking them. (exwm-manage--unmanage-window): Remove dock from tracking list when it's unmapped/destroyed. * exwm-workspace.el (exwm-workspace--id-struts-alist): New variable for tracking docks. (exwm-workspace--struts): Now it stores merged struts. (exwm-workspace--update-struts): New function for doing the 'merge'. * exwm.el (exwm--update-struts-legacy, exwm--update-struts-partial): Now update struts for multiple docks. * exwm-layout.el (exwm-layout-set-fullscreen) (exwm-layout-unset-fullscreen): * exwm-manage.el (exwm-manage--unmanage-window): Fix fullscreen mode with dock. * exwm-workspace.el (exwm-workspace--set-fullscreen): Add optional arguments for ignoring struts / resizing container only. (exwm-workspace-switch): Restack workspace/docks appropriately. --- exwm-layout.el | 25 +++++++++++-- exwm-manage.el | 29 ++++++++++++--- exwm-randr.el | 3 +- exwm-workspace.el | 105 ++++++++++++++++++++++++++++++++++++------------------ exwm.el | 52 ++++++++++++++------------- 5 files changed, 149 insertions(+), 65 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index 259788f66c..b79e42e641 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -155,6 +155,9 @@ (defvar exwm-workspace--current) (defvar exwm-workspace--list) +(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" + (frame &optional no-struts container-only)) + ;;;###autoload (defun exwm-layout-set-fullscreen (&optional id) "Make window ID fullscreen." @@ -169,9 +172,25 @@ :drawable exwm--container)))) (setq exwm--floating-frame-position (vector (slot-value geometry 'x) (slot-value geometry 'y))))) - (exwm-layout--resize-container exwm--id exwm--container 0 0 + ;; Expand the workspace frame & its container to fill the whole screen. + (exwm-workspace--set-fullscreen exwm--frame t t) + ;; Raise the workspace container (in case there are docks). + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter exwm--frame 'exwm-workspace) + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Above)) + ;; Expand the X window and its container to fill the whole screen. + ;; Rationale: Floating X windows may not be positioned at (0, 0) + ;; due to the extra border. + (exwm-layout--resize-container nil exwm--container 0 0 + (exwm-workspace--current-width) + (exwm-workspace--current-height) + t) + (exwm-layout--resize-container nil exwm--id 0 0 (exwm-workspace--current-width) - (exwm-workspace--current-height)) + (exwm-workspace--current-height) + t) ;; Raise the X window. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow @@ -193,6 +212,8 @@ (with-current-buffer (if id (exwm--id->buffer id) (window-buffer)) (unless exwm--fullscreen (user-error "Not in full-screen mode.")) + ;; Restore the size of this workspace. + (exwm-workspace--set-fullscreen exwm--frame) (if exwm--floating-frame ;; Restore the floating frame. (xcb:+request exwm--connection diff --git a/exwm-manage.el b/exwm-manage.el index 13948902d2..359782229d 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -86,7 +86,7 @@ corresponding buffer.") (declare-function exwm--update-title "exwm.el" (id)) (declare-function exwm--update-hints "exwm.el" (id &optional force)) (declare-function exwm--update-protocols "exwm.el" (id &optional force)) -(declare-function exwm--update-strut "exwm.el" (id)) +(declare-function exwm--update-struts "exwm.el" (id)) (declare-function exwm-floating--set-floating "exwm-floating.el" (id)) (declare-function exwm-floating--unset-floating "exwm-floating.el" (id)) (declare-function exwm-workspace--set-desktop "exwm-workspace.el" (id)) @@ -133,7 +133,7 @@ corresponding buffer.") (exwm--log "No need to manage #x%x" id) ;; Update struts. (when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DOCK exwm-window-type) - (exwm--update-strut id)) + (exwm--update-struts id)) ;; Remove all events (xcb:+request exwm--connection (make-instance 'xcb:ChangeWindowAttributes @@ -141,8 +141,9 @@ corresponding buffer.") :event-mask (if (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DOCK exwm-window-type) - ;; Listen for change of struts property of dock. - xcb:EventMask:PropertyChange + ;; Listen for PropertyChange (struts) and + ;; UnmapNotify/DestroyNotify event of the dock. + exwm--client-event-mask xcb:EventMask:NoEvent))) ;; The window needs to be mapped (xcb:+request exwm--connection @@ -247,6 +248,15 @@ corresponding buffer.") (with-current-buffer (exwm--id->buffer id) (run-hooks 'exwm-manage-finish-hook))))) +(defvar exwm-workspace--id-struts-alist) +(defvar exwm-workspace--list) + +(declare-function exwm-workspace--update-struts "exwm-workspace.el" ()) +(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" + (frame &optional no-struts container-only)) +(declare-function exwm-workspace--set-workareas "exwm-workspace.el" + (&optional workareas)) + (defun exwm-manage--unmanage-window (id &optional withdraw-only) "Unmanage window ID. @@ -257,6 +267,14 @@ manager is shutting down." (exwm--log "Unmanage #x%x (buffer: %s, widthdraw: %s)" id buffer withdraw-only) (setq exwm--id-buffer-alist (assq-delete-all id exwm--id-buffer-alist)) + ;; Update workspaces when a dock is destroyed. + (when (assq id exwm-workspace--id-struts-alist) + (setq exwm-workspace--id-struts-alist + (assq-delete-all id exwm-workspace--id-struts-alist)) + (exwm-workspace--update-struts) + (dolist (f exwm-workspace--list) + (exwm-workspace--set-fullscreen f)) + (exwm-workspace--set-workareas)) (when (buffer-live-p buffer) (with-current-buffer buffer ;; Flickering seems unavoidable here if the DestroyWindow request is @@ -314,6 +332,9 @@ manager is shutting down." (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow :window window :parent exwm--root :x 0 :y 0)))) + ;; Restore the workspace if this X window is currently fullscreen. + (when exwm--fullscreen + (exwm-workspace--set-fullscreen exwm--frame)) ;; Destroy the X window container (and the frame container if any). (xcb:+request exwm--connection (make-instance 'xcb:DestroyWindow :window exwm--container)) diff --git a/exwm-randr.el b/exwm-randr.el index f71120a4bb..9f6be782f9 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -58,7 +58,8 @@ (defvar exwm-workspace-number) (defvar exwm-workspace--list) -(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame)) +(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" + (frame &optional no-struts container-only)) (declare-function exwm-workspace--set-workareas "exwm-workspace.el" (&optional workareas)) (declare-function exwm-workspace--set-desktop-geometry "exwm-workspace.el" ()) diff --git a/exwm-workspace.el b/exwm-workspace.el index 6902151f42..cfbe02afd2 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -128,10 +128,30 @@ Value nil means to use the default position which is fixed at bottom, while "Reports whether the minibuffer is displayed in its own frame." (memq exwm-workspace-minibuffer-position '(top bottom))) -;; FIXME: RandR and multiple docks. -(defvar exwm-workspace--strut nil "Areas occupied by struts.") -(defvar exwm-workspace--strut-is-partial nil - "Whether the struts are from _NET_WM_STRUT_PARTIAL.") +(defvar exwm-workspace--id-struts-alist nil "Alist of X window and struts.") +(defvar exwm-workspace--struts nil "Areas occupied by struts.") + +(defun exwm-workspace--update-struts () + "Update `exwm-workspace--struts'." + (let ((left 0) + (right 0) + (top 0) + (bottom 0) + struts) + (dolist (pair exwm-workspace--id-struts-alist) + (setq struts (cdr pair)) + (when struts + (when (< left (aref struts 0)) + (setq left (aref struts 0))) + (when (< right (aref struts 1)) + (setq right (aref struts 1))) + (when (< top (aref struts 2)) + (setq top (aref struts 2))) + (when (< bottom (aref struts 3)) + (setq bottom (aref struts 3))))) + (setq exwm-workspace--struts (vector left right top bottom)) + (when (equal exwm-workspace--struts [0 0 0 0]) + (setq exwm-workspace--struts nil)))) (defvar exwm-workspace--fullscreen-frame-count 0 "Count the fullscreen workspace frames.") @@ -139,8 +159,12 @@ Value nil means to use the default position which is fixed at bottom, while (declare-function exwm-layout--resize-container "exwm-layout.el" (id container x y width height &optional container-only)) -(defun exwm-workspace--set-fullscreen (frame) - "Make frame FRAME fullscreen, with regard to its RandR output if applicable." +(defun exwm-workspace--set-fullscreen (frame &optional no-struts + container-only) + "Make frame FRAME fullscreen, with regard to its RandR output if applicable. + +If NO-STRUTS is non-nil, struts are ignored. If CONTAINER-ONLY is non-nil, the +workspace frame and its container is not resized." (let ((geometry (or (frame-parameter frame 'exwm-geometry) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GetGeometry @@ -153,24 +177,27 @@ Value nil means to use the default position which is fixed at bottom, while (workspace (frame-parameter frame 'exwm-workspace)) x* y* width* height*) (with-slots (x y width height) geometry - (if exwm-workspace--strut - (setq x* (+ x (aref exwm-workspace--strut 0)) - y* (+ y (aref exwm-workspace--strut 2)) - width* (- width (aref exwm-workspace--strut 0) - (aref exwm-workspace--strut 1)) - height* (- height (aref exwm-workspace--strut 2) - (aref exwm-workspace--strut 3))) + (if (and exwm-workspace--struts (not no-struts)) + (setq x* (+ x (aref exwm-workspace--struts 0)) + y* (+ y (aref exwm-workspace--struts 2)) + width* (- width (aref exwm-workspace--struts 0) + (aref exwm-workspace--struts 1)) + height* (- height (aref exwm-workspace--struts 2) + (aref exwm-workspace--struts 3))) (setq x* x y* y width* width height* height)) (when (and (eq frame exwm-workspace--current) - (exwm-workspace--minibuffer-own-frame-p)) + (exwm-workspace--minibuffer-own-frame-p) + (not container-only)) (exwm-workspace--resize-minibuffer-frame width height)) - (exwm-layout--resize-container id container 0 0 width* height*) + (unless container-only + (exwm-layout--resize-container id container 0 0 width* height*)) (exwm-layout--resize-container nil workspace x* y* width* height* t) (xcb:flush exwm--connection))) - (cl-incf exwm-workspace--fullscreen-frame-count)) + (unless container-only + (cl-incf exwm-workspace--fullscreen-frame-count))) ;;;###autoload (defun exwm-workspace--resize-minibuffer-frame (&optional width height) @@ -182,19 +209,19 @@ workspace frame." (let ((y (if (eq exwm-workspace-minibuffer-position 'top) 0 (- (or height (exwm-workspace--current-height)) - (if exwm-workspace--strut - (+ (aref exwm-workspace--strut 2) - (aref exwm-workspace--strut 3)) + (if exwm-workspace--struts + (+ (aref exwm-workspace--struts 2) + (aref exwm-workspace--struts 3)) 0) (frame-pixel-height exwm-workspace--minibuffer)))) (container (frame-parameter exwm-workspace--minibuffer 'exwm-container))) (unless width (setq width (exwm-workspace--current-width))) - (when exwm-workspace--strut + (when exwm-workspace--struts (setq width (- width - (aref exwm-workspace--strut 0) - (aref exwm-workspace--strut 1)))) + (aref exwm-workspace--struts 0) + (aref exwm-workspace--struts 1)))) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window container @@ -233,11 +260,22 @@ The optional FORCE option is for internal use only." (let* ((frame (elt exwm-workspace--list index)) (workspace (frame-parameter frame 'exwm-workspace)) (window (frame-parameter frame 'exwm-selected-window))) + (unless (window-live-p window) + (setq window (frame-selected-window frame))) + ;; Raise the workspace container. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window workspace :value-mask xcb:ConfigWindow:StackMode :stack-mode xcb:StackMode:Above)) + ;; Raise X windows with struts set if there's no fullscreen X window. + (unless (buffer-local-value 'exwm--fullscreen (window-buffer window)) + (dolist (pair exwm-workspace--id-struts-alist) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (car pair) + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Above)))) (setq exwm-workspace--current frame exwm-workspace-current-index index) (unless (memq (selected-frame) exwm-workspace--list) @@ -245,8 +283,7 @@ The optional FORCE option is for internal use only." (set-frame-parameter (with-current-buffer (window-buffer) exwm--frame) 'exwm-selected-window (selected-window))) - (select-window (or (when (window-live-p window) window) - (frame-selected-window frame))) + (select-window window) (set-frame-parameter frame 'exwm-selected-window nil) ;; Close the (possible) active minibuffer (when (active-minibuffer-window) @@ -556,9 +593,9 @@ The optional FORCE option is for internal use only." y 0) (setq value-mask (logior xcb:ConfigWindow:Y xcb:ConfigWindow:Height) y (- (exwm-workspace--current-height) - (if exwm-workspace--strut - (+ (aref exwm-workspace--strut 2) - (aref exwm-workspace--strut 3)) + (if exwm-workspace--struts + (+ (aref exwm-workspace--struts 2) + (aref exwm-workspace--struts 3)) 0) height))) (xcb:+request exwm--connection @@ -731,13 +768,13 @@ The optional FORCE option is for internal use only." (setq workareas (vconcat workareas workarea)))))) ;; Exclude areas occupied by struts. ;; FIXME: RandR. - (when exwm-workspace--strut - (let ((dx (aref exwm-workspace--strut 0)) - (dy (aref exwm-workspace--strut 2)) - (dw (- (+ (aref exwm-workspace--strut 0) - (aref exwm-workspace--strut 1)))) - (dh (- (+ (aref exwm-workspace--strut 2) - (aref exwm-workspace--strut 3))))) + (when exwm-workspace--struts + (let ((dx (aref exwm-workspace--struts 0)) + (dy (aref exwm-workspace--struts 2)) + (dw (- (+ (aref exwm-workspace--struts 0) + (aref exwm-workspace--struts 1)))) + (dh (- (+ (aref exwm-workspace--struts 2) + (aref exwm-workspace--struts 3))))) (dotimes (i exwm-workspace-number) (cl-incf (aref workareas (* i 4)) dx) (cl-incf (aref workareas (+ (* i 4))) dy) diff --git a/exwm.el b/exwm.el index 3d7edcfeac..59f43136f7 100644 --- a/exwm.el +++ b/exwm.el @@ -227,45 +227,49 @@ (when reply ;nil when destroyed (setq exwm--protocols (append (slot-value reply 'value) nil))))))) -(defun exwm--update-strut-legacy (id) +(defun exwm--update-struts-legacy (id) "Update _NET_WM_STRUT." - (unless exwm-workspace--strut-is-partial - (let ((reply (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:ewmh:get-_NET_WM_STRUT - :window id)))) - (setq exwm-workspace--strut (when reply (slot-value reply 'value))) + (let ((pair (assq id exwm-workspace--id-struts-alist)) + reply struts) + (unless (and pair (< 4 (length (cdr pair)))) + (setq reply (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:ewmh:get-_NET_WM_STRUT + :window id))) + (when reply + (setq struts (slot-value reply 'value)) + (if pair + (setcdr pair struts) + (push (cons id struts) exwm-workspace--id-struts-alist)) + (exwm-workspace--update-struts)) ;; Update workspaces. (dolist (f exwm-workspace--list) (exwm-workspace--set-fullscreen f)) - ;; Resize the minibuffer frame. - (when (exwm-workspace--minibuffer-own-frame-p) - (exwm-workspace--resize-minibuffer-frame)) ;; Update _NET_WORKAREA. (exwm-workspace--set-workareas)))) -(defun exwm--update-strut-partial (id) +(defun exwm--update-struts-partial (id) "Update _NET_WM_STRUT_PARTIAL." (let ((reply (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:ewmh:get-_NET_WM_STRUT_PARTIAL - :window id)))) - (setq exwm-workspace--strut (when reply (slot-value reply 'value))) - (if (not exwm-workspace--strut) - (setq exwm-workspace--strut-is-partial nil) - (setq exwm-workspace--strut (substring exwm-workspace--strut 0 4)) - (setq exwm-workspace--strut-is-partial t)) + :window id))) + struts pair) + (when reply + (setq struts (slot-value reply 'value) + pair (assq id exwm-workspace--id-struts-alist)) + (if pair + (setcdr pair struts) + (push (cons id struts) exwm-workspace--id-struts-alist)) + (exwm-workspace--update-struts)) ;; Update workspaces. (dolist (f exwm-workspace--list) (exwm-workspace--set-fullscreen f)) - ;; Resize the minibuffer frame. - (when (exwm-workspace--minibuffer-own-frame-p) - (exwm-workspace--resize-minibuffer-frame)) ;; Update _NET_WORKAREA. (exwm-workspace--set-workareas))) -(defun exwm--update-strut (id) +(defun exwm--update-struts (id) "Update _NET_WM_STRUT_PARTIAL or _NET_WM_STRUT." - (exwm--update-strut-partial id) - (exwm--update-strut-legacy id)) + (exwm--update-struts-partial id) + (exwm--update-struts-legacy id)) (defun exwm--on-PropertyNotify (data _synthetic) "Handle PropertyNotify event." @@ -279,9 +283,9 @@ (if (not (buffer-live-p buffer)) ;; Properties of unmanaged X windows. (cond ((= atom xcb:Atom:_NET_WM_STRUT) - (exwm--update-strut-legacy id)) + (exwm--update-struts-legacy id)) ((= atom xcb:Atom:_NET_WM_STRUT_PARTIAL) - (exwm--update-strut-partial id))) + (exwm--update-struts-partial id))) (with-current-buffer buffer (cond ((= atom xcb:Atom:_NET_WM_WINDOW_TYPE) (exwm--update-window-type id t)) -- cgit 1.4.1 From 4ac71a7ddc78d1485a7fca7a8dbf4c4f6b4016f2 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 16 Jul 2016 14:34:57 +0800 Subject: Add RandR support for docks and reuse workareas * exwm-workspace (exwm-workspace--update-struts): Add RandR support for docks. * exwm-workspace (exwm-workspace--workareas): New variable for storing workareas. (exwm-workspace--update-workareas): Update workareas and set _NET_WORKAREA (replaces `exwm-workspace--set-workareas'). (exwm-workspace--set-fullscreen): Reuse workareas for resizing and drop optional arguments. (exwm-workspace--resize-minibuffer-frame) (exwm-workspace--on-ConfigureNotify): Reuse workareas for resizing/reposition the (optional) dedicated minibuffer frame. * exwm-layout.el (exwm-layout-set-fullscreen): Do not use `exwm-workspace--set-fullscreen' here. * exwm-manage.el (exwm-manage--unmanage-window): * exwm-randr.el (exwm-randr--refresh): * exwm.el (exwm--update-struts-legacy, exwm--update-struts-partial): Update workareas before resizing workspaces. * exwm.el (exwm--update-struts-legacy, exwm--update-struts-partial): Remove the corresponding record on receiving invalid struts. * exwm-workspace.el (exwm-workspace--get-geometry): New utility function for retrieving workspace geometry. --- exwm-layout.el | 14 ++-- exwm-manage.el | 10 +-- exwm-randr.el | 24 +++--- exwm-workspace.el | 238 ++++++++++++++++++++++++++++-------------------------- exwm.el | 32 +++++--- 5 files changed, 170 insertions(+), 148 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index b79e42e641..85e186f609 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -155,8 +155,7 @@ (defvar exwm-workspace--current) (defvar exwm-workspace--list) -(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" - (frame &optional no-struts container-only)) +(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame)) ;;;###autoload (defun exwm-layout-set-fullscreen (&optional id) @@ -165,15 +164,20 @@ (with-current-buffer (if id (exwm--id->buffer id) (window-buffer)) (when exwm--fullscreen (user-error "Already in full-screen mode.")) - ;; Set the floating frame fullscreen first when the client is floating + ;; Save the position of floating frame. (when exwm--floating-frame (let* ((geometry (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GetGeometry :drawable exwm--container)))) (setq exwm--floating-frame-position (vector (slot-value geometry 'x) (slot-value geometry 'y))))) - ;; Expand the workspace frame & its container to fill the whole screen. - (exwm-workspace--set-fullscreen exwm--frame t t) + ;; Expand the workspace to fill the whole screen. + (with-slots (x y width height) (exwm-workspace--get-geometry exwm--frame) + (exwm-layout--resize-container nil + (frame-parameter exwm--frame + 'exwm-workspace) + x y width height + t)) ;; Raise the workspace container (in case there are docks). (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow diff --git a/exwm-manage.el b/exwm-manage.el index 359782229d..18a6795f8a 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -252,10 +252,8 @@ corresponding buffer.") (defvar exwm-workspace--list) (declare-function exwm-workspace--update-struts "exwm-workspace.el" ()) -(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" - (frame &optional no-struts container-only)) -(declare-function exwm-workspace--set-workareas "exwm-workspace.el" - (&optional workareas)) +(declare-function exwm-workspace--update-workareas "exwm-workspace.el" ()) +(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame)) (defun exwm-manage--unmanage-window (id &optional withdraw-only) "Unmanage window ID. @@ -272,9 +270,9 @@ manager is shutting down." (setq exwm-workspace--id-struts-alist (assq-delete-all id exwm-workspace--id-struts-alist)) (exwm-workspace--update-struts) + (exwm-workspace--update-workareas) (dolist (f exwm-workspace--list) - (exwm-workspace--set-fullscreen f)) - (exwm-workspace--set-workareas)) + (exwm-workspace--set-fullscreen f))) (when (buffer-live-p buffer) (with-current-buffer buffer ;; Flickering seems unavoidable here if the DestroyWindow request is diff --git a/exwm-randr.el b/exwm-randr.el index 9f6be782f9..85daff245d 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -58,15 +58,13 @@ (defvar exwm-workspace-number) (defvar exwm-workspace--list) -(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" - (frame &optional no-struts container-only)) -(declare-function exwm-workspace--set-workareas "exwm-workspace.el" - (&optional workareas)) +(declare-function exwm-workspace--update-workareas "exwm-workspace.el" ()) +(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame)) (declare-function exwm-workspace--set-desktop-geometry "exwm-workspace.el" ()) (defun exwm-randr--refresh () "Refresh workspaces according to the updated RandR info." - (let (output-name geometry output-plist default-geometry workareas) + (let (output-name geometry output-plist default-geometry) ;; Query all outputs (with-slots (config-timestamp outputs) (xcb:+request-unchecked+reply exwm--connection @@ -96,7 +94,9 @@ (setq default-geometry geometry))))))) (exwm--log "(randr) outputs: %s" output-plist) (when output-plist - (setq exwm-workspace--fullscreen-frame-count 0) + (when exwm-workspace--fullscreen-frame-count + ;; Not all workspaces are fullscreen; reset this counter. + (setq exwm-workspace--fullscreen-frame-count 0)) (dotimes (i exwm-workspace-number) (let* ((output (plist-get exwm-randr-workspace-output-plist i)) (geometry (lax-plist-get output-plist output)) @@ -105,14 +105,14 @@ (setq geometry default-geometry output nil)) (set-frame-parameter frame 'exwm-randr-output output) - (set-frame-parameter frame 'exwm-geometry geometry) - (exwm-workspace--set-fullscreen frame) - (with-slots (x y width height) geometry - (setq workareas (nconc workareas (list x y width height)))))) + (set-frame-parameter frame 'exwm-geometry geometry))) + ;; Update workareas and set _NET_WORKAREA. + (exwm-workspace--update-workareas) + ;; Resize workspace. + (dolist (f exwm-workspace--list) + (exwm-workspace--set-fullscreen f)) ;; Set _NET_DESKTOP_GEOMETRY. (exwm-workspace--set-desktop-geometry) - ;; Set _NET_WORKAREA. - (exwm-workspace--set-workareas (vconcat workareas)) (xcb:flush exwm--connection) (run-hooks 'exwm-randr-refresh-hook)))) diff --git a/exwm-workspace.el b/exwm-workspace.el index cfbe02afd2..6228d99bbd 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -107,6 +107,16 @@ Value nil means to use the default position which is fixed at bottom, while (defvar exwm-workspace--display-echo-area-timer nil "Timer for auto-hiding echo area.") +;;;###autoload +(defun exwm-workspace--get-geometry (frame) + "Return the geometry of frame FRAME." + (or (frame-parameter frame 'exwm-geometry) + (make-instance 'xcb:RECTANGLE + :x 0 + :y 0 + :width (x-display-pixel-width) + :height (x-display-pixel-height)))) + ;;;###autoload (defun exwm-workspace--current-width () "Return the width of current workspace." @@ -133,25 +143,89 @@ Value nil means to use the default position which is fixed at bottom, while (defun exwm-workspace--update-struts () "Update `exwm-workspace--struts'." - (let ((left 0) - (right 0) - (top 0) - (bottom 0) - struts) + (setq exwm-workspace--struts nil) + (let (struts struts*) (dolist (pair exwm-workspace--id-struts-alist) (setq struts (cdr pair)) - (when struts - (when (< left (aref struts 0)) - (setq left (aref struts 0))) - (when (< right (aref struts 1)) - (setq right (aref struts 1))) - (when (< top (aref struts 2)) - (setq top (aref struts 2))) - (when (< bottom (aref struts 3)) - (setq bottom (aref struts 3))))) - (setq exwm-workspace--struts (vector left right top bottom)) - (when (equal exwm-workspace--struts [0 0 0 0]) - (setq exwm-workspace--struts nil)))) + (dotimes (i 4) + (when (/= 0 (aref struts i)) + (setq struts* + (vector (aref [left right top bottom] i) + (aref struts i) + (when (= 12 (length struts)) + (substring struts (+ 4 (* i 2)) (+ 6 (* i 2)))))) + (if (= 0 (mod i 2)) + ;; Make left/top processed first. + (push struts* exwm-workspace--struts) + (setq exwm-workspace--struts + (append exwm-workspace--struts (list struts*))))))))) + +(defvar exwm-workspace--workareas nil "Workareas (struts excluded).") + +(defun exwm-workspace--update-workareas () + "Update `exwm-workspace--workareas' and set _NET_WORKAREA." + (let ((root-width (x-display-pixel-width)) + (root-height (x-display-pixel-height)) + workareas + edge width position + delta) + ;; Calculate workareas with no struts. + (if (frame-parameter (car exwm-workspace--list) 'exwm-geometry) + ;; Use the 'exwm-geometry' frame parameter if possible. + (dolist (f exwm-workspace--list) + (with-slots (x y width height) (frame-parameter f 'exwm-geometry) + (setq workareas (append workareas + (list (vector x y width height)))))) + ;; Fall back to use the screen size. + (let ((workarea (vector 0 0 root-width root-height))) + (dotimes (_ exwm-workspace-number) + (push workarea workareas)))) + ;; Exclude areas occupied by struts. + (dolist (struts exwm-workspace--struts) + (setq edge (aref struts 0) + width (aref struts 1) + position (aref struts 2)) + (dolist (w workareas) + (pcase edge + ;; Left and top are always processed first. + (`left + (setq delta (- (aref w 0) width)) + (when (and (< delta 0) + (< (max (aref position 0) (aref w 1)) + (min (aref position 1) + (+ (aref w 1) (aref w 3))))) + (cl-incf (aref w 2) delta) + (setf (aref w 0) width))) + (`right + (setq delta (- root-width (aref w 0) (aref w 2) width)) + (when (and (< delta 0) + (< (max (aref position 0) (aref w 1)) + (min (aref position 1) + (+ (aref w 1) (aref w 3))))) + (cl-incf (aref w 2) delta))) + (`top + (setq delta (- (aref w 1) width)) + (when (and (< delta 0) + (< (max (aref position 0) (aref w 0)) + (min (aref position 1) + (+ (aref w 0) (aref w 2))))) + (cl-incf (aref w 3) delta) + (setf (aref w 1) width))) + (`bottom + (setq delta (- root-height (aref w 1) (aref w 3) width)) + (when (and (< delta 0) + (< (max (aref position 0) (aref w 0)) + (min (aref position 1) + (+ (aref w 0) (aref w 2))))) + (cl-incf (aref w 3) delta)))))) + ;; Save the result. + (setq exwm-workspace--workareas workareas) + ;; Update _NET_WORKAREA. + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WORKAREA + :window exwm--root + :data (mapconcat #'vconcat workareas []))) + (xcb:flush exwm--connection))) (defvar exwm-workspace--fullscreen-frame-count 0 "Count the fullscreen workspace frames.") @@ -159,69 +233,40 @@ Value nil means to use the default position which is fixed at bottom, while (declare-function exwm-layout--resize-container "exwm-layout.el" (id container x y width height &optional container-only)) -(defun exwm-workspace--set-fullscreen (frame &optional no-struts - container-only) - "Make frame FRAME fullscreen, with regard to its RandR output if applicable. - -If NO-STRUTS is non-nil, struts are ignored. If CONTAINER-ONLY is non-nil, the -workspace frame and its container is not resized." - (let ((geometry (or (frame-parameter frame 'exwm-geometry) - (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:GetGeometry - :drawable exwm--root)) - (make-instance 'xcb:RECTANGLE :x 0 :y 0 - :width (x-display-pixel-width) - :height (x-display-pixel-height)))) +(defun exwm-workspace--set-fullscreen (frame) + "Make frame FRAME fullscreen according to `exwm-workspace--workareas'." + (let ((workarea (elt exwm-workspace--workareas + (cl-position frame exwm-workspace--list))) (id (frame-parameter frame 'exwm-outer-id)) (container (frame-parameter frame 'exwm-container)) (workspace (frame-parameter frame 'exwm-workspace)) - x* y* width* height*) - (with-slots (x y width height) geometry - (if (and exwm-workspace--struts (not no-struts)) - (setq x* (+ x (aref exwm-workspace--struts 0)) - y* (+ y (aref exwm-workspace--struts 2)) - width* (- width (aref exwm-workspace--struts 0) - (aref exwm-workspace--struts 1)) - height* (- height (aref exwm-workspace--struts 2) - (aref exwm-workspace--struts 3))) - (setq x* x - y* y - width* width - height* height)) - (when (and (eq frame exwm-workspace--current) - (exwm-workspace--minibuffer-own-frame-p) - (not container-only)) - (exwm-workspace--resize-minibuffer-frame width height)) - (unless container-only - (exwm-layout--resize-container id container 0 0 width* height*)) - (exwm-layout--resize-container nil workspace x* y* width* height* t) - (xcb:flush exwm--connection))) - (unless container-only + x y width height) + (setq x (aref workarea 0) + y (aref workarea 1) + width (aref workarea 2) + height (aref workarea 3)) + (when (and (eq frame exwm-workspace--current) + (exwm-workspace--minibuffer-own-frame-p)) + (exwm-workspace--resize-minibuffer-frame)) + (exwm-layout--resize-container id container 0 0 width height) + (exwm-layout--resize-container nil workspace x y width height t) + (xcb:flush exwm--connection)) + ;; This is only used for workspace initialization. + (when exwm-workspace--fullscreen-frame-count (cl-incf exwm-workspace--fullscreen-frame-count))) -;;;###autoload -(defun exwm-workspace--resize-minibuffer-frame (&optional width height) - "Resize minibuffer (and its container) to fit the size of workspace. - -If WIDTH and HEIGHT of the workspace is not specified, they're get from the -workspace frame." +(defun exwm-workspace--resize-minibuffer-frame () + "Resize minibuffer (and its container) to fit the size of workspace." (cl-assert (exwm-workspace--minibuffer-own-frame-p)) - (let ((y (if (eq exwm-workspace-minibuffer-position 'top) - 0 - (- (or height (exwm-workspace--current-height)) - (if exwm-workspace--struts - (+ (aref exwm-workspace--struts 2) - (aref exwm-workspace--struts 3)) - 0) - (frame-pixel-height exwm-workspace--minibuffer)))) + (let ((workarea (elt exwm-workspace--workareas exwm-workspace-current-index)) (container (frame-parameter exwm-workspace--minibuffer - 'exwm-container))) - (unless width - (setq width (exwm-workspace--current-width))) - (when exwm-workspace--struts - (setq width (- width - (aref exwm-workspace--struts 0) - (aref exwm-workspace--struts 1)))) + 'exwm-container)) + y width) + (setq y (if (eq exwm-workspace-minibuffer-position 'top) + 0 + (- (aref workarea 3) + (frame-pixel-height exwm-workspace--minibuffer))) + width (aref workarea 2)) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window container @@ -592,11 +637,9 @@ The optional FORCE option is for internal use only." (setq value-mask xcb:ConfigWindow:Height y 0) (setq value-mask (logior xcb:ConfigWindow:Y xcb:ConfigWindow:Height) - y (- (exwm-workspace--current-height) - (if exwm-workspace--struts - (+ (aref exwm-workspace--struts 2) - (aref exwm-workspace--struts 3)) - 0) + y (- (aref (elt exwm-workspace--workareas + exwm-workspace-current-index) + 3) height))) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow @@ -753,38 +796,6 @@ The optional FORCE option is for internal use only." :width (x-display-pixel-width) :height (x-display-pixel-height)))) -(defun exwm-workspace--set-workareas (&optional workareas) - "Set _NET_WORKAREA." - ;; Calculate workareas if not present. - (unless workareas - (if (frame-parameter (car exwm-workspace--list) 'exwm-geometry) - ;; Use the 'exwm-geometry' frame parameter if possible. - (dolist (f exwm-workspace--list) - (with-slots (x y width height) (frame-parameter f 'exwm-geometry) - (setq workareas (vconcat workareas (vector x y width height))))) - (let ((workarea (vector 0 0 (x-display-pixel-width) - (x-display-pixel-height)))) - (dotimes (_ exwm-workspace-number) - (setq workareas (vconcat workareas workarea)))))) - ;; Exclude areas occupied by struts. - ;; FIXME: RandR. - (when exwm-workspace--struts - (let ((dx (aref exwm-workspace--struts 0)) - (dy (aref exwm-workspace--struts 2)) - (dw (- (+ (aref exwm-workspace--struts 0) - (aref exwm-workspace--struts 1)))) - (dh (- (+ (aref exwm-workspace--struts 2) - (aref exwm-workspace--struts 3))))) - (dotimes (i exwm-workspace-number) - (cl-incf (aref workareas (* i 4)) dx) - (cl-incf (aref workareas (+ (* i 4))) dy) - (cl-incf (aref workareas (+ (* i 4) 2)) dw) - (cl-incf (aref workareas (+ (* i 4) 3)) dh)))) - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_WORKAREA - :window exwm--root :data workareas)) - (xcb:flush exwm--connection)) - (defvar exwm-workspace--timer nil "Timer used to track echo area changes.") (defun exwm-workspace--init () @@ -952,8 +963,8 @@ The optional FORCE option is for internal use only." (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT :window exwm--root :data (make-vector (* 2 exwm-workspace-number) 0))) - ;; Set _NET_WORKAREA. - (exwm-workspace--set-workareas) + ;; Update and set _NET_WORKAREA. + (exwm-workspace--update-workareas) ;; Set _NET_VIRTUAL_ROOTS (it's currently fixed.) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_VIRTUAL_ROOTS @@ -992,7 +1003,8 @@ The optional FORCE option is for internal use only." ;; Wait until all workspace frames are resized. (with-timeout (1) (while (< exwm-workspace--fullscreen-frame-count exwm-workspace-number) - (accept-process-output nil 0.1)))) + (accept-process-output nil 0.1))) + (setq exwm-workspace--fullscreen-frame-count nil)) diff --git a/exwm.el b/exwm.el index 59f43136f7..814104a86d 100644 --- a/exwm.el +++ b/exwm.el @@ -237,15 +237,19 @@ :window id))) (when reply (setq struts (slot-value reply 'value)) - (if pair - (setcdr pair struts) - (push (cons id struts) exwm-workspace--id-struts-alist)) + (if struts + (if pair + (setcdr pair struts) + (push (cons id struts) exwm-workspace--id-struts-alist)) + (when pair + (setq exwm-workspace--id-struts-alist + (assq-delete-all id exwm-workspace--id-struts-alist)))) (exwm-workspace--update-struts)) + ;; Update workareas and set _NET_WORKAREA. + (exwm-workspace--update-workareas) ;; Update workspaces. (dolist (f exwm-workspace--list) - (exwm-workspace--set-fullscreen f)) - ;; Update _NET_WORKAREA. - (exwm-workspace--set-workareas)))) + (exwm-workspace--set-fullscreen f))))) (defun exwm--update-struts-partial (id) "Update _NET_WM_STRUT_PARTIAL." @@ -256,15 +260,19 @@ (when reply (setq struts (slot-value reply 'value) pair (assq id exwm-workspace--id-struts-alist)) - (if pair - (setcdr pair struts) - (push (cons id struts) exwm-workspace--id-struts-alist)) + (if struts + (if pair + (setcdr pair struts) + (push (cons id struts) exwm-workspace--id-struts-alist)) + (when pair + (setq exwm-workspace--id-struts-alist + (assq-delete-all id exwm-workspace--id-struts-alist)))) (exwm-workspace--update-struts)) + ;; Update workareas and set _NET_WORKAREA. + (exwm-workspace--update-workareas) ;; Update workspaces. (dolist (f exwm-workspace--list) - (exwm-workspace--set-fullscreen f)) - ;; Update _NET_WORKAREA. - (exwm-workspace--set-workareas))) + (exwm-workspace--set-fullscreen f)))) (defun exwm--update-struts (id) "Update _NET_WM_STRUT_PARTIAL or _NET_WM_STRUT." -- cgit 1.4.1 From 2195316821c7d54cb6858e00d810e7089491a24d Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 17 Jul 2016 12:46:19 +0800 Subject: Add major mode menu * exwm-core.el (exwm--keyboard-grabbed): New buffer-local variable for recoding grabbing state. (exwm-mode-menu, exwm-mode-map): Add major mode menu. * exwm-input.el (exwm-input-set-key): Update prefix keys when a global binding is interactively set. (exwm-input-grab-keyboard, exwm-input-release-keyboard): Update grabbing state. --- exwm-core.el | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ exwm-input.el | 6 +++++- 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/exwm-core.el b/exwm-core.el index c07c069c49..39964d2b47 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -90,6 +90,7 @@ (defvar-local exwm--fullscreen nil) ;used in fullscreen (defvar-local exwm--floating-frame-position nil) ;used in fullscreen (defvar-local exwm--fixed-size nil) ;fixed size +(defvar-local exwm--keyboard-grabbed nil) ;Keyboard grabbed. (defvar-local exwm--on-KeyPress ;KeyPress event handler #'exwm-input--on-KeyPress-line-mode) ;; Properties @@ -130,6 +131,73 @@ map) "Keymap for `exwm-mode'.") +;; This menu mainly acts as an reminder for users. Thus it should be as +;; detailed as possible, even some entries do not make much sense here. +;; Also, inactive entries should be disabled rather than hidden. +(easy-menu-define exwm-mode-menu exwm-mode-map + "Menu for `exwm-mode'." + '("EXWM" + "---" + "*General*" + "---" + ["Toggle floating" exwm-floating-toggle-floating] + ["Enter fullscreen" exwm-layout-set-fullscreen (not exwm--fullscreen)] + ["Leave fullscreen" exwm-reset exwm--fullscreen] + ["Hide window" exwm-floating-hide exwm--floating-frame] + + "---" + "*Resizing*" + "---" + ["Toggle mode-line" exwm-layout-toggle-mode-line :keys "C-c C-t C-m"] + ["Enlarge window vertically" exwm-layout-enlarge-window] + ["Enlarge window horizontally" exwm-layout-enlarge-window-horizontally] + ["Shrink window vertically" exwm-layout-shrink-window] + ["Shrink window horizontally" exwm-layout-shrink-window-horizontally] + + "---" + "*Keyboard*" + "---" + ["Capture keyboard" exwm-input-release-keyboard exwm--keyboard-grabbed] + ;; It's recommended to use `exwm-reset' rather than + ;; `exwm-input-grab-keyboard' to release keyboard (enter line-mode). + ["Release keyboard" exwm-reset (not exwm--keyboard-grabbed)] + ["Send key" exwm-input-send-next-key exwm--keyboard-grabbed] + ;; This is merely a reference. + ("Send simulation key" :filter + (lambda (&rest _args) + (mapcar (lambda (i) + (let ((keys (cdr i))) + (if (vectorp keys) + (setq keys (append keys)) + (unless (sequencep keys) + (setq keys (list keys)))) + (vector (key-description keys) + `(lambda () + (interactive) + (dolist (key ',keys) + (exwm-input--fake-key key))) + :keys (key-description (car i))))) + exwm-input--simulation-keys))) + + ["Define global binding" exwm-input-set-key] + + "---" + "*Workspace*" + "---" + ["Move window to" exwm-workspace-move-window :keys "C-c C-m"] + ["Switch to buffer" exwm-workspace-switch-to-buffer] + ["Switch workspace" exwm-workspace-switch] + ;; Place this entry at bottom to avoid selecting others by accident. + ("Switch to" :filter + (lambda (&rest _args) + (mapcar (lambda (i) + `[,(format "workspace %d" i) + (lambda () + (interactive) + (exwm-workspace-switch ,i)) + (/= ,i exwm-workspace-current-index)]) + (number-sequence 0 (1- exwm-workspace-number))))))) + (declare-function exwm-manage--kill-buffer-query-function "exwm-manage.el") (define-derived-mode exwm-mode nil "EXWM" diff --git a/exwm-input.el b/exwm-input.el index 57c0cc801a..6f18fbf132 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -285,7 +285,9 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") "Set a global key binding." (interactive "KSet key globally: \nCSet key %s to command: ") (global-set-key key command) - (cl-pushnew key exwm-input--global-keys)) + (cl-pushnew key exwm-input--global-keys) + (when (called-interactively-p 'any) + (exwm-input--update-global-prefix-keys))) ;; FIXME: Putting (t . EVENT) into `unread-command-events' does not really work ;; as documented in Emacs 24. Since inserting a conventional EVENT does @@ -421,6 +423,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (when id (with-current-buffer (exwm--id->buffer id) (exwm-input--grab-keyboard id) + (setq exwm--keyboard-grabbed t) (exwm-input--update-mode-line id) (force-mode-line-update)))) @@ -431,6 +434,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (when id (with-current-buffer (exwm--id->buffer id) (exwm-input--release-keyboard id) + (setq exwm--keyboard-grabbed nil) (exwm-input--update-mode-line id) (force-mode-line-update)))) -- cgit 1.4.1 From 18fc95def2bc6a7c920967ada698d965c07cfd3d Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Sun, 17 Jul 2016 12:00:00 +0000 Subject: Minor cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * exwm-workspace.el (exwm-workspace--update-workareas): Use `make-list' instead of looping. 2016-07-16 Adrián Medraño Calvo --- exwm-workspace.el | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 6228d99bbd..82c054fb92 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -178,8 +178,7 @@ Value nil means to use the default position which is fixed at bottom, while (list (vector x y width height)))))) ;; Fall back to use the screen size. (let ((workarea (vector 0 0 root-width root-height))) - (dotimes (_ exwm-workspace-number) - (push workarea workareas)))) + (setq workareas (make-list exwm-workspace-number workarea)))) ;; Exclude areas occupied by struts. (dolist (struts exwm-workspace--struts) (setq edge (aref struts 0) -- cgit 1.4.1 From 983fd468dc8acb910e25178379750dec3300d35a Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Sun, 17 Jul 2016 12:00:00 +0000 Subject: Add missing declarations * exwm-systemtray.el : * exwm-manage.el : * exwm-layout.el : * exwm-input.el : * exwm-floating.el : * exwm-core.el : Add missing function declarations. --- exwm-core.el | 9 +++++++++ exwm-floating.el | 4 +--- exwm-input.el | 3 +++ exwm-layout.el | 8 ++++++++ exwm-manage.el | 5 +++++ exwm-systemtray.el | 7 +++++-- 6 files changed, 31 insertions(+), 5 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 39964d2b47..9f869fefd5 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -119,6 +119,15 @@ ;; _MOTIF_WM_HINTS (defvar-local exwm--mwm-hints-decorations t) +(declare-function exwm-floating-hide "exwm-floating.el") +(declare-function exwm-floating-toggle-floating "exwm-floating.el") +(declare-function exwm-input-release-keyboard "exwm-input.el") +(declare-function exwm-input-send-next-key "exwm-input.el" (times)) +(declare-function exwm-layout-set-fullscreen "exwm-layout.el" (&optional id)) +(declare-function exwm-layout-toggle-mode-line "exwm-layout.el") +(declare-function exwm-workspace-move-window "exwm-workspace.el" + (index &optional id)) + (defvar exwm-mode-map (let ((map (make-sparse-keymap))) (define-key map "\C-c\C-f" #'exwm-layout-set-fullscreen) diff --git a/exwm-floating.el b/exwm-floating.el index 9d4d948d30..b58e71a2de 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -67,13 +67,11 @@ xcb:Atom:_NET_WM_ACTION_CLOSE))))) (defvar exwm-workspace--current) -(defvar exwm-workspace--list) -(defvar exwm-workspace-current-index) -(defvar exwm-workspace--switch-history-outdated) (declare-function exwm-layout--refresh "exwm-layout.el" ()) (declare-function exwm-layout--show "exwm-layout.el" (id &optional window)) (declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) +(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") (defun exwm-floating--set-floating (id) "Make window ID floating." diff --git a/exwm-input.el b/exwm-input.el index 6f18fbf132..67e9a7439a 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -102,6 +102,9 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) (declare-function exwm-layout--set-state "exwm-layout.el" (id state)) +(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") +(declare-function exwm-workspace-switch "exwm-workspace.el" + (index &optional force)) (defun exwm-input--update-focus () "Update input focus." diff --git a/exwm-layout.el b/exwm-layout.el index 85e186f609..92ca677e56 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -155,7 +155,15 @@ (defvar exwm-workspace--current) (defvar exwm-workspace--list) +(declare-function exwm-input-grab-keyboard "exwm-input.el") +(declare-function exwm-input-release-keyboard "exwm-input.el") +(declare-function exwm-workspace--current-height "exwm-workspace.el") +(declare-function exwm-workspace--current-width "exwm-workspace.el") +(declare-function exwm-workspace--get-geometry "exwm-workspace.el" (frame)) +(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") (declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame)) +(declare-function exwm-workspace-move-window "exwm-workspace.el" + (index &optional id)) ;;;###autoload (defun exwm-layout-set-fullscreen (&optional id) diff --git a/exwm-manage.el b/exwm-manage.el index 18a6795f8a..01d00b1acb 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -89,7 +89,12 @@ corresponding buffer.") (declare-function exwm--update-struts "exwm.el" (id)) (declare-function exwm-floating--set-floating "exwm-floating.el" (id)) (declare-function exwm-floating--unset-floating "exwm-floating.el" (id)) +(declare-function exwm-input-grab-keyboard "exwm-input.el") +(declare-function exwm-workspace--current-height "exwm-workspace.el") +(declare-function exwm-workspace--current-width "exwm-workspace.el") (declare-function exwm-workspace--set-desktop "exwm-workspace.el" (id)) +(declare-function exwm-workspace-move-window "exwm-workspace.el" + (index &optional id)) (defun exwm-manage--manage-window (id) "Manage window ID." diff --git a/exwm-systemtray.el b/exwm-systemtray.el index db0e0455d2..56e5fb8eb7 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -65,6 +65,11 @@ You shall use the default value if using auto-hide minibuffer.") "The selection owner window.") (defvar exwm-systemtray--embedder nil "The embedder window.") +(defvar exwm-workspace--current) +(declare-function exwm-workspace--current-height "exwm-workspace.el") +(declare-function exwm-workspace--current-width "exwm-workspace.el") +(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") + (defun exwm-systemtray--embed (icon) "Embed an icon." (exwm--log "(System Tray) Try to embed #x%x" icon) @@ -266,8 +271,6 @@ You shall use the default value if using auto-hide minibuffer.") (t (exwm--log "(System Tray) Unknown opcode message: %s" obj))))))) -(defvar exwm-workspace--current) - (defun exwm-systemtray--on-workspace-switch () "Reparent/Refresh the system tray in `exwm-workspace-switch-hook'." (unless (exwm-workspace--minibuffer-own-frame-p) -- cgit 1.4.1 From cd1372eaec2521ea163c505b38d36b1f787d1944 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Sun, 17 Jul 2016 12:00:00 +0000 Subject: Apply minibuffer in own frame configuration to created frames * exwm-workspace.el (exwm-workspace--modify-all-x-frames-parameters): Helper function to non-destructively modify `window-system-default-frame-alist'. (exwm-workspace--init): Use above function to default all frames to the shared minibuffer when `exwm-workspace--minibuffer-own-frame-p' is true. --- exwm-workspace.el | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 82c054fb92..b2138aa138 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -797,6 +797,20 @@ The optional FORCE option is for internal use only." (defvar exwm-workspace--timer nil "Timer used to track echo area changes.") +(defun exwm-workspace--modify-all-x-frames-parameters (new-x-parameters) + "Modifies `window-system-default-frame-alist' for the X Window System. +NEW-X-PARAMETERS is an alist of frame parameters, merged into current +`window-system-default-frame-alist' for the X Window System. The parameters are +applied to all subsequently created X frames." + ;; The parameters are modified in place; take current + ;; ones or insert a new X-specific list. + (let ((x-parameters (or (assq 'x window-system-default-frame-alist) + (let ((new-x-parameters '(x))) + (push new-x-parameters window-system-default-frame-alist) + new-x-parameters)))) + (setf (cdr x-parameters) + (append new-x-parameters (cdr x-parameters))))) + (defun exwm-workspace--init () "Initialize workspace module." (cl-assert (and (< 0 exwm-workspace-number) (>= 10 exwm-workspace-number))) @@ -843,6 +857,8 @@ The optional FORCE option is for internal use only." (delete-frame f))))) ;; This is the only usable minibuffer frame. (setq default-minibuffer-frame exwm-workspace--minibuffer) + (exwm-workspace--modify-all-x-frames-parameters + '((minibuffer . nil))) (let ((outer-id (string-to-number (frame-parameter exwm-workspace--minibuffer 'outer-window-id))) @@ -883,8 +899,6 @@ The optional FORCE option is for internal use only." ;; Create workspace frames. (dotimes (_ exwm-workspace-number) (push (make-frame `((window-system . x) - (minibuffer . ,(minibuffer-window - exwm-workspace--minibuffer)) (internal-border-width . 0) (client . nil))) exwm-workspace--list)) -- cgit 1.4.1 From ed6a18a697bc1eb8bf09a12c04ffa3200a5248cd Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Sun, 17 Jul 2016 12:00:00 +0000 Subject: Add `exwm-workspace--position' helper * exwm-workspace.el (exwm-workspace--position): New function. * exwm-layout.el (exwm-layout--refresh): * exwm-input.el (exwm-input--on-ButtonPress): * exwm-workspace.el (exwm-workspace--update-switch-history): * exwm.el (exwm--on-ClientMessage, exwm-workspace--init): Use it. --- exwm-input.el | 5 +++-- exwm-layout.el | 3 ++- exwm-workspace.el | 13 ++++++++++--- exwm.el | 2 +- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 67e9a7439a..d581e7e857 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -179,6 +179,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (declare-function exwm-floating--start-moveresize "exwm-floating.el" (id &optional type)) +(declare-function exwm-workspace--position "exwm-workspace.el" (frame)) (defvar exwm-workspace--list) @@ -208,14 +209,14 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (if (memq frame exwm-workspace--list) ;; The X window is on another workspace (exwm-workspace-switch - (cl-position frame exwm-workspace--list)) + (exwm-workspace--position frame)) (with-current-buffer (window-buffer window) (when (and (eq major-mode 'exwm-mode) (not (eq exwm--frame exwm-workspace--current))) ;; The floating X window is on another workspace (exwm-workspace-switch - (cl-position exwm--frame exwm-workspace--list)))))) + (exwm-workspace--position exwm--frame)))))) ;; It has been reported that the `window' may have be deleted (if (window-live-p window) (select-window window) diff --git a/exwm-layout.el b/exwm-layout.el index 92ca677e56..db1aa0d5fd 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -161,6 +161,7 @@ (declare-function exwm-workspace--current-width "exwm-workspace.el") (declare-function exwm-workspace--get-geometry "exwm-workspace.el" (frame)) (declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") +(declare-function exwm-workspace--position "exwm-workspace.el" (frame)) (declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame)) (declare-function exwm-workspace-move-window "exwm-workspace.el" (index &optional id)) @@ -346,7 +347,7 @@ selected by `other-buffer'." (if (eq frame exwm--frame) (exwm-layout--show exwm--id window) (exwm-workspace-move-window - (cl-position frame exwm-workspace--list) exwm--id)) + (exwm-workspace--position frame) exwm--id)) ;; Make sure this buffer is not displayed elsewhere. Note down ;; windows displaying an EXWM-buffer now displayed elsewhere; we ;; need to display with some other buffer there. diff --git a/exwm-workspace.el b/exwm-workspace.el index b2138aa138..641f3f2f22 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -29,6 +29,13 @@ (defvar exwm-workspace-number 4 "Number of workspaces (1 ~ 10).") (defvar exwm-workspace--list nil "List of all workspaces (Emacs frames).") + +(defsubst exwm-workspace--position (frame) + "Retrieve index of given FRAME in workspace list. + +NIL if FRAME is not a workspace" + (cl-position frame exwm-workspace--list)) + (defvar exwm-workspace--switch-map (let ((map (make-sparse-keymap))) (define-key map [t] (lambda () (interactive))) @@ -72,7 +79,7 @@ (with-current-buffer (cdr i) (when exwm--frame (setf (aref not-empty - (cl-position exwm--frame exwm-workspace--list)) + (exwm-workspace--position exwm--frame)) t)))) (setq exwm-workspace--switch-history (mapcar @@ -948,13 +955,13 @@ applied to all subsequently created X frames." :window workspace :data (format "EXWM workspace %d" - (cl-position i exwm-workspace--list)))) + (exwm-workspace--position i)))) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_NAME :window container :data (format "EXWM workspace %d frame container" - (cl-position i exwm-workspace--list))))) + (exwm-workspace--position i))))) (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow :window outer-id :parent container :x 0 :y 0)) diff --git a/exwm.el b/exwm.el index 814104a86d..4a6e9fd5d2 100644 --- a/exwm.el +++ b/exwm.el @@ -438,7 +438,7 @@ ;; FIXME: check (may require other properties set) (when (memq xcb:Atom:_NET_WM_STATE_DEMANDS_ATTENTION props) (when (= action xcb:ewmh:_NET_WM_STATE_ADD) - (let ((idx (cl-position exwm--frame exwm-workspace--list))) + (let ((idx (exwm-workspace--position exwm--frame))) (unless (= idx exwm-workspace-current-index) (set-frame-parameter exwm--frame 'exwm--urgency t) (setq exwm-workspace--switch-history-outdated t)))) -- cgit 1.4.1 From 8e2da00b6e7e530a53b584184dc94b9366ae7c69 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Sun, 17 Jul 2016 12:00:00 +0000 Subject: Add helper for counting number of workspaces * exwm-workspace.el (exwm-workspace--count): New function. * exwm-randr.el (exwm-randr--refresh): * exwm-workspace.el (exwm-workspace--switch-map) (exwm-workspace--update-switch-history, exwm-workspace-switch) (exwm-workspace-move-window, exwm-workspace--init) (exwm-workspace--post-init): Use it. --- exwm-randr.el | 6 +++--- exwm-workspace.el | 29 +++++++++++++++++------------ 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/exwm-randr.el b/exwm-randr.el index 85daff245d..ac3341db3c 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -55,11 +55,11 @@ "Normal hook run when the RandR module just refreshed.") (defvar exwm-workspace--fullscreen-frame-count) -(defvar exwm-workspace-number) (defvar exwm-workspace--list) -(declare-function exwm-workspace--update-workareas "exwm-workspace.el" ()) +(declare-function exwm-workspace--count "exwm-workspace.el") (declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame)) +(declare-function exwm-workspace--update-workareas "exwm-workspace.el" ()) (declare-function exwm-workspace--set-desktop-geometry "exwm-workspace.el" ()) (defun exwm-randr--refresh () @@ -97,7 +97,7 @@ (when exwm-workspace--fullscreen-frame-count ;; Not all workspaces are fullscreen; reset this counter. (setq exwm-workspace--fullscreen-frame-count 0)) - (dotimes (i exwm-workspace-number) + (dotimes (i (exwm-workspace--count)) (let* ((output (plist-get exwm-randr-workspace-output-plist i)) (geometry (lax-plist-get output-plist output)) (frame (elt exwm-workspace--list i))) diff --git a/exwm-workspace.el b/exwm-workspace.el index 641f3f2f22..a3f4c2e75b 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -36,6 +36,10 @@ NIL if FRAME is not a workspace" (cl-position frame exwm-workspace--list)) +(defsubst exwm-workspace--count () + "Retrieve total number of workspaces." + (length exwm-workspace--list)) + (defvar exwm-workspace--switch-map (let ((map (make-sparse-keymap))) (define-key map [t] (lambda () (interactive))) @@ -43,13 +47,13 @@ NIL if FRAME is not a workspace" (define-key map (int-to-string i) `(lambda () (interactive) - (when (< ,i exwm-workspace-number) + (when (< ,i (exwm-workspace--count)) (goto-history-element ,(1+ i)) (exit-minibuffer))))) (define-key map "\C-a" (lambda () (interactive) (goto-history-element 1))) (define-key map "\C-e" (lambda () (interactive) - (goto-history-element exwm-workspace-number))) + (goto-history-element (exwm-workspace--count)))) (define-key map "\C-g" #'abort-recursive-edit) (define-key map "\C-]" #'abort-recursive-edit) (define-key map "\C-j" #'exit-minibuffer) @@ -73,8 +77,9 @@ NIL if FRAME is not a workspace" "Update the history for switching workspace to reflect the latest status." (when exwm-workspace--switch-history-outdated (setq exwm-workspace--switch-history-outdated nil) - (let ((sequence (number-sequence 0 (1- exwm-workspace-number))) - (not-empty (make-vector exwm-workspace-number nil))) + (let* ((num (exwm-workspace--count)) + (sequence (number-sequence 0 (1- num))) + (not-empty (make-vector num nil))) (dolist (i exwm--id-buffer-alist) (with-current-buffer (cdr i) (when exwm--frame @@ -185,7 +190,7 @@ Value nil means to use the default position which is fixed at bottom, while (list (vector x y width height)))))) ;; Fall back to use the screen size. (let ((workarea (vector 0 0 root-width root-height))) - (setq workareas (make-list exwm-workspace-number workarea)))) + (setq workareas (make-list (exwm-workspace--count) workarea)))) ;; Exclude areas occupied by struts. (dolist (struts exwm-workspace--struts) (setq edge (aref struts 0) @@ -305,7 +310,7 @@ The optional FORCE option is for internal use only." . ,(1+ exwm-workspace-current-index))))) (cl-position idx exwm-workspace--switch-history :test #'equal))))) (when index - (unless (and (<= 0 index) (< index exwm-workspace-number)) + (unless (and (<= 0 index) (< index (exwm-workspace--count))) (user-error "[EXWM] Workspace index out of range: %d" index)) (when (or force (/= exwm-workspace-current-index index)) (let* ((frame (elt exwm-workspace--list index)) @@ -412,7 +417,7 @@ The optional FORCE option is for internal use only." . ,(1+ exwm-workspace-current-index))))) (cl-position idx exwm-workspace--switch-history :test #'equal))))) (unless id (setq id (exwm--buffer->id (window-buffer)))) - (unless (and (<= 0 index) (< index exwm-workspace-number)) + (unless (and (<= 0 index) (< index (exwm-workspace--count))) (user-error "[EXWM] Workspace index out of range: %d" index)) (with-current-buffer (exwm--id->buffer id) (let ((frame (elt exwm-workspace--list index))) @@ -827,12 +832,12 @@ applied to all subsequently created X frames." ;; Initialize workspaces with minibuffers. (progn (setq exwm-workspace--list (frame-list)) - (when (< 1 (length exwm-workspace--list)) + (when (< 1 (exwm-workspace--count)) ;; Exclude the initial frame. (dolist (i exwm-workspace--list) (unless (frame-parameter i 'window-id) (setq exwm-workspace--list (delq i exwm-workspace--list)))) - (cl-assert (= 1 (length exwm-workspace--list))) + (cl-assert (= 1 (exwm-workspace--count))) (setq exwm-workspace--client (frame-parameter (car exwm-workspace--list) 'client)) (let ((f (car exwm-workspace--list))) @@ -975,14 +980,14 @@ applied to all subsequently created X frames." ;; Set _NET_NUMBER_OF_DESKTOPS (it's currently fixed). (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_NUMBER_OF_DESKTOPS - :window exwm--root :data exwm-workspace-number)) + :window exwm--root :data (exwm-workspace--count))) ;; Set _NET_DESKTOP_GEOMETRY. (exwm-workspace--set-desktop-geometry) ;; Set _NET_DESKTOP_VIEWPORT (we don't support large desktop). (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT :window exwm--root - :data (make-vector (* 2 exwm-workspace-number) 0))) + :data (make-vector (* 2 (exwm-workspace--count)) 0))) ;; Update and set _NET_WORKAREA. (exwm-workspace--update-workareas) ;; Set _NET_VIRTUAL_ROOTS (it's currently fixed.) @@ -1022,7 +1027,7 @@ applied to all subsequently created X frames." (set-frame-parameter i 'fullscreen 'fullboth)) ;; Wait until all workspace frames are resized. (with-timeout (1) - (while (< exwm-workspace--fullscreen-frame-count exwm-workspace-number) + (while (< exwm-workspace--fullscreen-frame-count (exwm-workspace--count)) (accept-process-output nil 0.1))) (setq exwm-workspace--fullscreen-frame-count nil)) -- cgit 1.4.1 From 90185457261eb77ac80609d5d219e0837e81af82 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Sun, 17 Jul 2016 12:00:00 +0000 Subject: New function exwm-workspace--workspace-p * exwm-workspace.el (exwm-workspace--workspace-p): New function. * exwm-input.el (exwm-input--on-ButtonPress): * exwm-workspace.el (exwm-workspace-switch): * exwm-layout.el (exwm-layout--refresh): Use it. --- exwm-input.el | 3 ++- exwm-layout.el | 5 +++-- exwm-workspace.el | 6 +++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index d581e7e857..22c7a3606b 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -180,6 +180,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (declare-function exwm-floating--start-moveresize "exwm-floating.el" (id &optional type)) (declare-function exwm-workspace--position "exwm-workspace.el" (frame)) +(declare-function exwm-workspace--workspace-p "exwm-workspace.el" (workspace)) (defvar exwm-workspace--list) @@ -206,7 +207,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (unless (eq window (selected-window)) (setq frame (window-frame window)) (unless (eq frame exwm-workspace--current) - (if (memq frame exwm-workspace--list) + (if (exwm-workspace--workspace-p frame) ;; The X window is on another workspace (exwm-workspace-switch (exwm-workspace--position frame)) diff --git a/exwm-layout.el b/exwm-layout.el index db1aa0d5fd..4d85390047 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -153,7 +153,6 @@ (xcb:flush exwm--connection)))) (defvar exwm-workspace--current) -(defvar exwm-workspace--list) (declare-function exwm-input-grab-keyboard "exwm-input.el") (declare-function exwm-input-release-keyboard "exwm-input.el") @@ -282,6 +281,8 @@ selected by `other-buffer'." (defvar exwm-layout-show-all-buffers nil "Non-nil to allow switching to buffers on other workspaces.") +(declare-function exwm-workspace--workspace-p "exwm-workspace.el" + (workspace)) (defun exwm-layout--set-client-list-stacking () "Set _NET_CLIENT_LIST_STACKING." @@ -312,7 +313,7 @@ selected by `other-buffer'." covered-buffers ;EXWM-buffers covered by a new X window. vacated-windows ;Windows previously displaying EXWM-buffers. windows) - (if (not (memq frame exwm-workspace--list)) + (if (not (exwm-workspace--workspace-p frame)) (if (frame-parameter frame 'exwm-outer-id) ;; Refresh a floating frame (let ((window (frame-first-window frame))) diff --git a/exwm-workspace.el b/exwm-workspace.el index a3f4c2e75b..e2df37ede9 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -40,6 +40,10 @@ NIL if FRAME is not a workspace" "Retrieve total number of workspaces." (length exwm-workspace--list)) +(defsubst exwm-workspace--workspace-p (frame) + "Return t if FRAME is a workspace." + (memq frame exwm-workspace--list)) + (defvar exwm-workspace--switch-map (let ((map (make-sparse-keymap))) (define-key map [t] (lambda () (interactive))) @@ -334,7 +338,7 @@ The optional FORCE option is for internal use only." :stack-mode xcb:StackMode:Above)))) (setq exwm-workspace--current frame exwm-workspace-current-index index) - (unless (memq (selected-frame) exwm-workspace--list) + (unless (exwm-workspace--workspace-p (selected-frame)) ;; Save the floating frame window selected on the previous workspace. (set-frame-parameter (with-current-buffer (window-buffer) exwm--frame) -- cgit 1.4.1 From 35e1655dc58d5653d4c3192d12cb5bfe61aa6960 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Sun, 17 Jul 2016 12:00:00 +0000 Subject: Move defvars to the top * exwm-workspace.el (exwm-workspace--current) (exwm-workspace-current-index): Do it. --- exwm-workspace.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index e2df37ede9..4d8380d081 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -29,6 +29,8 @@ (defvar exwm-workspace-number 4 "Number of workspaces (1 ~ 10).") (defvar exwm-workspace--list nil "List of all workspaces (Emacs frames).") +(defvar exwm-workspace--current nil "Current active workspace.") +(defvar exwm-workspace-current-index 0 "Index of current active workspace.") (defsubst exwm-workspace--position (frame) "Retrieve index of given FRAME in workspace list. @@ -107,8 +109,6 @@ NIL if FRAME is not a workspace" sequence "")) sequence))))) -(defvar exwm-workspace--current nil "Current active workspace.") -(defvar exwm-workspace-current-index 0 "Index of current active workspace.") (defvar exwm-workspace-show-all-buffers nil "Non-nil to show buffers on other workspaces.") (defvar exwm-workspace--minibuffer nil -- cgit 1.4.1 From 07120a0562e4a09e189899ef8523aba927cbea0f Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Sun, 17 Jul 2016 12:00:00 +0000 Subject: Work with workspace frames instead of indices * exwm.el (exwm--on-ClientMessage): * exwm-workspace.el (exwm-workspace-switch) (exwm-workspace-move-window, exwm-workspace-switch-to-buffer): * exwm-layout.el (exwm-layout--refresh): * exwm-input.el (exwm-input--update-focus) (exwm-input--on-ButtonPress): Accept frame as well as workspace index as argument. * exwm-workspace.el (exwm-workspace--workspace-from-frame-or-index): New function. --- exwm-core.el | 2 +- exwm-input.el | 10 ++- exwm-layout.el | 6 +- exwm-manage.el | 2 +- exwm-workspace.el | 197 ++++++++++++++++++++++++++++-------------------------- exwm.el | 7 +- 6 files changed, 114 insertions(+), 110 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 9f869fefd5..70a759cde3 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -126,7 +126,7 @@ (declare-function exwm-layout-set-fullscreen "exwm-layout.el" (&optional id)) (declare-function exwm-layout-toggle-mode-line "exwm-layout.el") (declare-function exwm-workspace-move-window "exwm-workspace.el" - (index &optional id)) + (frame-or-index &optional id)) (defvar exwm-mode-map (let ((map (make-sparse-keymap))) diff --git a/exwm-input.el b/exwm-input.el index 22c7a3606b..b9e47058f0 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -104,7 +104,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (declare-function exwm-layout--set-state "exwm-layout.el" (id state)) (declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") (declare-function exwm-workspace-switch "exwm-workspace.el" - (index &optional force)) + (frame-or-index &optional force)) (defun exwm-input--update-focus () "Update input focus." @@ -120,7 +120,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (setq exwm-workspace--switch-history-outdated t) (force-mode-line-update) ;; The application may have changed its input focus - (exwm-workspace-switch exwm-workspace-current-index t)) + (exwm-workspace-switch exwm-workspace--current t)) (exwm--log "Set focus on #x%x" exwm--id) (exwm-input--set-focus exwm--id) (when exwm--floating-frame @@ -209,15 +209,13 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (unless (eq frame exwm-workspace--current) (if (exwm-workspace--workspace-p frame) ;; The X window is on another workspace - (exwm-workspace-switch - (exwm-workspace--position frame)) + (exwm-workspace-switch frame) (with-current-buffer (window-buffer window) (when (and (eq major-mode 'exwm-mode) (not (eq exwm--frame exwm-workspace--current))) ;; The floating X window is on another workspace - (exwm-workspace-switch - (exwm-workspace--position exwm--frame)))))) + (exwm-workspace-switch exwm--frame))))) ;; It has been reported that the `window' may have be deleted (if (window-live-p window) (select-window window) diff --git a/exwm-layout.el b/exwm-layout.el index 4d85390047..e3d1d74a5d 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -160,10 +160,9 @@ (declare-function exwm-workspace--current-width "exwm-workspace.el") (declare-function exwm-workspace--get-geometry "exwm-workspace.el" (frame)) (declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") -(declare-function exwm-workspace--position "exwm-workspace.el" (frame)) (declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame)) (declare-function exwm-workspace-move-window "exwm-workspace.el" - (index &optional id)) + (frame-or-index &optional id)) ;;;###autoload (defun exwm-layout-set-fullscreen (&optional id) @@ -347,8 +346,7 @@ selected by `other-buffer'." (let ((window (car windows))) (if (eq frame exwm--frame) (exwm-layout--show exwm--id window) - (exwm-workspace-move-window - (exwm-workspace--position frame) exwm--id)) + (exwm-workspace-move-window frame exwm--id)) ;; Make sure this buffer is not displayed elsewhere. Note down ;; windows displaying an EXWM-buffer now displayed elsewhere; we ;; need to display with some other buffer there. diff --git a/exwm-manage.el b/exwm-manage.el index 01d00b1acb..d67929a9d2 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -94,7 +94,7 @@ corresponding buffer.") (declare-function exwm-workspace--current-width "exwm-workspace.el") (declare-function exwm-workspace--set-desktop "exwm-workspace.el" (id)) (declare-function exwm-workspace-move-window "exwm-workspace.el" - (index &optional id)) + (frame-or-index &optional id)) (defun exwm-manage--manage-window (id) "Manage window ID." diff --git a/exwm-workspace.el b/exwm-workspace.el index 4d8380d081..346e89809e 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -46,6 +46,19 @@ NIL if FRAME is not a workspace" "Return t if FRAME is a workspace." (memq frame exwm-workspace--list)) +(defun exwm-workspace--workspace-from-frame-or-index (frame-or-index) + "Retrieve the workspace frame from FRAME-OR-INDEX." + (cond + ((framep frame-or-index) + (unless (exwm-workspace--position frame-or-index) + (user-error "[EXWM] Frame is not a workspace %S" frame-or-index)) + frame-or-index) + ((integerp frame-or-index) + (unless (and (<= 0 frame-or-index) (< frame-or-index (exwm-workspace--count))) + (user-error "[EXWM] Workspace index out of range: %d" frame-or-index)) + (elt exwm-workspace--list frame-or-index)) + (t (user-error "[EXWM] Invalid workspace: %s" frame-or-index)))) + (defvar exwm-workspace--switch-map (let ((map (make-sparse-keymap))) (define-key map [t] (lambda () (interactive))) @@ -297,87 +310,85 @@ Value nil means to use the default position which is fixed at bottom, while "Normal hook run after switching workspace.") ;;;###autoload -(defun exwm-workspace-switch (index &optional force) - "Switch to workspace INDEX. Query for INDEX if it's not specified. +(defun exwm-workspace-switch (frame-or-index &optional force) + "Switch to workspace INDEX. Query for FRAME-OR-INDEX if it's not specified. The optional FORCE option is for internal use only." (interactive (list (unless (and (eq major-mode 'exwm-mode) exwm--fullscreen) ;it's invisible (exwm-workspace--update-switch-history) - (let* ((history-add-new-input nil) ;prevent modifying history - (idx (read-from-minibuffer - "Workspace: " (elt exwm-workspace--switch-history - exwm-workspace-current-index) - exwm-workspace--switch-map nil - `(exwm-workspace--switch-history - . ,(1+ exwm-workspace-current-index))))) - (cl-position idx exwm-workspace--switch-history :test #'equal))))) - (when index - (unless (and (<= 0 index) (< index (exwm-workspace--count))) - (user-error "[EXWM] Workspace index out of range: %d" index)) - (when (or force (/= exwm-workspace-current-index index)) - (let* ((frame (elt exwm-workspace--list index)) - (workspace (frame-parameter frame 'exwm-workspace)) - (window (frame-parameter frame 'exwm-selected-window))) - (unless (window-live-p window) - (setq window (frame-selected-window frame))) - ;; Raise the workspace container. - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window workspace - :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:Above)) - ;; Raise X windows with struts set if there's no fullscreen X window. - (unless (buffer-local-value 'exwm--fullscreen (window-buffer window)) - (dolist (pair exwm-workspace--id-struts-alist) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window (car pair) - :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:Above)))) - (setq exwm-workspace--current frame - exwm-workspace-current-index index) - (unless (exwm-workspace--workspace-p (selected-frame)) - ;; Save the floating frame window selected on the previous workspace. - (set-frame-parameter (with-current-buffer (window-buffer) - exwm--frame) - 'exwm-selected-window (selected-window))) - (select-window window) - (set-frame-parameter frame 'exwm-selected-window nil) - ;; Close the (possible) active minibuffer - (when (active-minibuffer-window) - (run-with-idle-timer 0 nil (lambda () (abort-recursive-edit)))) - (if (not (exwm-workspace--minibuffer-own-frame-p)) - (setq default-minibuffer-frame frame) - ;; Resize/reposition the minibuffer frame + (let* ((current-idx (exwm-workspace--position exwm-workspace--current)) + (history-add-new-input nil) ;prevent modifying history + (history-idx (read-from-minibuffer + "Workspace: " (elt exwm-workspace--switch-history current-idx) + exwm-workspace--switch-map nil + `(exwm-workspace--switch-history . ,(1+ current-idx)))) + (workspace-idx (cl-position history-idx exwm-workspace--switch-history :test #'equal))) + (elt exwm-workspace--list workspace-idx))))) + (let* ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index)) + (index (exwm-workspace--position frame)) + (workspace (frame-parameter frame 'exwm-workspace)) + (window (frame-parameter frame 'exwm-selected-window))) + (when (or force (not (eq frame exwm-workspace--current))) + (unless (window-live-p window) + (setq window (frame-selected-window frame))) + ;; Raise the workspace container. + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window workspace + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Above)) + ;; Raise X windows with struts set if there's no fullscreen X window. + (unless (buffer-local-value 'exwm--fullscreen (window-buffer window)) + (dolist (pair exwm-workspace--id-struts-alist) (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window - (frame-parameter exwm-workspace--minibuffer - 'exwm-container) - :parent (frame-parameter frame 'exwm-workspace) - :x 0 :y 0)) - (exwm-workspace--resize-minibuffer-frame)) - ;; Hide windows in other workspaces by preprending a space - (unless exwm-workspace-show-all-buffers - (dolist (i exwm--id-buffer-alist) - (with-current-buffer (cdr i) - (let ((name (replace-regexp-in-string "^\\s-*" "" - (buffer-name)))) - (exwm-workspace-rename-buffer (if (eq frame exwm--frame) - name - (concat " " name))))))) - ;; Update demands attention flag - (set-frame-parameter frame 'exwm--urgency nil) - ;; Update switch workspace history - (setq exwm-workspace--switch-history-outdated t) - ;; Set _NET_CURRENT_DESKTOP. + (make-instance 'xcb:ConfigureWindow + :window (car pair) + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Above)))) + (setq exwm-workspace--current frame + exwm-workspace-current-index index) + (unless (exwm-workspace--workspace-p (selected-frame)) + ;; Save the floating frame window selected on the previous workspace. + (set-frame-parameter (with-current-buffer (window-buffer) + exwm--frame) + 'exwm-selected-window (selected-window))) + (select-window window) + (set-frame-parameter frame 'exwm-selected-window nil) + ;; Close the (possible) active minibuffer + (when (active-minibuffer-window) + (run-with-idle-timer 0 nil (lambda () (abort-recursive-edit)))) + (if (not (exwm-workspace--minibuffer-own-frame-p)) + (setq default-minibuffer-frame frame) + ;; Resize/reposition the minibuffer frame (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_CURRENT_DESKTOP - :window exwm--root :data index)) - (xcb:flush exwm--connection)) - (run-hooks 'exwm-workspace-switch-hook)))) + (make-instance 'xcb:ReparentWindow + :window + (frame-parameter exwm-workspace--minibuffer + 'exwm-container) + :parent (frame-parameter frame 'exwm-workspace) + :x 0 :y 0)) + (exwm-workspace--resize-minibuffer-frame)) + ;; Hide windows in other workspaces by preprending a space + (unless exwm-workspace-show-all-buffers + (dolist (i exwm--id-buffer-alist) + (with-current-buffer (cdr i) + (let ((name (replace-regexp-in-string "^\\s-*" "" + (buffer-name)))) + (exwm-workspace-rename-buffer (if (eq frame exwm--frame) + name + (concat " " name))))))) + ;; Update demands attention flag + (set-frame-parameter frame 'exwm--urgency nil) + ;; Update switch workspace history + (setq exwm-workspace--switch-history-outdated t) + ;; Set _NET_CURRENT_DESKTOP + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_CURRENT_DESKTOP + :window exwm--root :data index)) + (xcb:flush exwm--connection)) + (run-hooks 'exwm-workspace-switch-hook))) (defun exwm-workspace--on-focus-in () "Handle unexpected frame switch." @@ -406,30 +417,28 @@ The optional FORCE option is for internal use only." (declare-function exwm-layout--other-buffer-predicate "exwm-layout.el" (buffer)) ;;;###autoload -(defun exwm-workspace-move-window (index &optional id) - "Move window ID to workspace INDEX." +(defun exwm-workspace-move-window (frame-or-index &optional id) + "Move window ID to workspace FRAME-OR-INDEX." (interactive (list (progn (exwm-workspace--update-switch-history) - (let* ((history-add-new-input nil) ;prevent modifying history - (idx (read-from-minibuffer - "Workspace: " (elt exwm-workspace--switch-history - exwm-workspace-current-index) - exwm-workspace--switch-map nil - `(exwm-workspace--switch-history - . ,(1+ exwm-workspace-current-index))))) - (cl-position idx exwm-workspace--switch-history :test #'equal))))) - (unless id (setq id (exwm--buffer->id (window-buffer)))) - (unless (and (<= 0 index) (< index (exwm-workspace--count))) - (user-error "[EXWM] Workspace index out of range: %d" index)) - (with-current-buffer (exwm--id->buffer id) - (let ((frame (elt exwm-workspace--list index))) + (let* ((current-idx (exwm-workspace--position exwm-workspace--current)) + (history-add-new-input nil) ;prevent modifying history + (history-idx (read-from-minibuffer + "Workspace: " (elt exwm-workspace--switch-history current-idx) + exwm-workspace--switch-map nil + `(exwm-workspace--switch-history . ,(1+ current-idx)))) + (workspace-idx (cl-position history-idx exwm-workspace--switch-history :test #'equal))) + (elt exwm-workspace--list workspace-idx))))) + (let ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index))) + (unless id (setq id (exwm--buffer->id (window-buffer)))) + (with-current-buffer (exwm--id->buffer id) (unless (eq exwm--frame frame) (unless exwm-workspace-show-all-buffers (let ((name (replace-regexp-in-string "^\\s-*" "" (buffer-name)))) (exwm-workspace-rename-buffer - (if (= index exwm-workspace-current-index) + (if (eq frame exwm-workspace--current) name (concat " " name))))) (setq exwm--frame frame) @@ -446,7 +455,7 @@ The optional FORCE option is for internal use only." :x x :y y)) (xcb:flush exwm--connection) (if (exwm-workspace--minibuffer-own-frame-p) - (when (= index exwm-workspace-current-index) + (when (eq frame exwm-workspace--current) (select-frame-set-input-focus exwm--floating-frame) (exwm-layout--refresh)) ;; The frame needs to be recreated since it won't use the @@ -502,18 +511,18 @@ The optional FORCE option is for internal use only." (delete-frame old-frame) (set-window-dedicated-p window t) (exwm-layout--show id window)) - (if (/= index exwm-workspace-current-index) + (if (not (eq frame exwm-workspace--current)) (make-frame-visible new-frame) (select-frame-set-input-focus new-frame) (redisplay)))) ;; Update the 'exwm-selected-window' frame parameter. - (when (/= index exwm-workspace-current-index) + (when (not (eq frame exwm-workspace--current)) (with-current-buffer (exwm--id->buffer id) (set-frame-parameter frame 'exwm-selected-window (frame-root-window exwm--floating-frame))))) ;; Move the X window container. - (if (= index exwm-workspace-current-index) + (if (eq frame exwm-workspace--current) (set-window-buffer (get-buffer-window (current-buffer) t) (other-buffer)) (bury-buffer) @@ -577,7 +586,7 @@ The optional FORCE option is for internal use only." (select-frame-set-input-focus exwm--floating-frame) (select-window (frame-root-window exwm--floating-frame))) ;; On another workspace. - (exwm-workspace-move-window exwm-workspace-current-index + (exwm-workspace-move-window exwm-workspace--current exwm--id)) ;; Ordinary buffer. (switch-to-buffer buffer-or-name))))) diff --git a/exwm.el b/exwm.el index 4a6e9fd5d2..9151fdc5c3 100644 --- a/exwm.el +++ b/exwm.el @@ -438,10 +438,9 @@ ;; FIXME: check (may require other properties set) (when (memq xcb:Atom:_NET_WM_STATE_DEMANDS_ATTENTION props) (when (= action xcb:ewmh:_NET_WM_STATE_ADD) - (let ((idx (exwm-workspace--position exwm--frame))) - (unless (= idx exwm-workspace-current-index) - (set-frame-parameter exwm--frame 'exwm--urgency t) - (setq exwm-workspace--switch-history-outdated t)))) + (unless (eq exwm--frame exwm-workspace--current) + (set-frame-parameter exwm--frame 'exwm--urgency t) + (setq exwm-workspace--switch-history-outdated t))) ;; xcb:ewmh:_NET_WM_STATE_REMOVE? ;; xcb:ewmh:_NET_WM_STATE_TOGGLE? ) -- cgit 1.4.1 From 2de2d42586a0a8866e7f5447d490a0c345c7ce10 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Sun, 17 Jul 2016 12:00:00 +0000 Subject: Factor out prompt for workspace * exwm-workspace.el (exwm-workspace--prompt-for-workspace): New function to interactively ask the user for a workspace. (exwm-workspace-switch, exwm-workspace-move-window): Use it. --- exwm-workspace.el | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 346e89809e..21a415eb81 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -92,6 +92,18 @@ NIL if FRAME is not a workspace" (defvar exwm-workspace--switch-history-outdated nil "Non-nil to indicate `exwm-workspace--switch-history' is outdated.") +(defun exwm-workspace--prompt-for-workspace () + "Prompt for a workspace, returning the workspace frame." + (exwm-workspace--update-switch-history) + (let* ((current-idx (exwm-workspace--position exwm-workspace--current)) + (history-add-new-input nil) ;prevent modifying history + (history-idx (read-from-minibuffer + "Workspace: " (elt exwm-workspace--switch-history current-idx) + exwm-workspace--switch-map nil + `(exwm-workspace--switch-history . ,(1+ current-idx)))) + (workspace-idx (cl-position history-idx exwm-workspace--switch-history :test #'equal))) + (elt exwm-workspace--list workspace-idx))) + (defun exwm-workspace--update-switch-history () "Update the history for switching workspace to reflect the latest status." (when exwm-workspace--switch-history-outdated @@ -317,15 +329,7 @@ The optional FORCE option is for internal use only." (interactive (list (unless (and (eq major-mode 'exwm-mode) exwm--fullscreen) ;it's invisible - (exwm-workspace--update-switch-history) - (let* ((current-idx (exwm-workspace--position exwm-workspace--current)) - (history-add-new-input nil) ;prevent modifying history - (history-idx (read-from-minibuffer - "Workspace: " (elt exwm-workspace--switch-history current-idx) - exwm-workspace--switch-map nil - `(exwm-workspace--switch-history . ,(1+ current-idx)))) - (workspace-idx (cl-position history-idx exwm-workspace--switch-history :test #'equal))) - (elt exwm-workspace--list workspace-idx))))) + (exwm-workspace--prompt-for-workspace)))) (let* ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index)) (index (exwm-workspace--position frame)) (workspace (frame-parameter frame 'exwm-workspace)) @@ -419,18 +423,7 @@ The optional FORCE option is for internal use only." ;;;###autoload (defun exwm-workspace-move-window (frame-or-index &optional id) "Move window ID to workspace FRAME-OR-INDEX." - (interactive - (list - (progn - (exwm-workspace--update-switch-history) - (let* ((current-idx (exwm-workspace--position exwm-workspace--current)) - (history-add-new-input nil) ;prevent modifying history - (history-idx (read-from-minibuffer - "Workspace: " (elt exwm-workspace--switch-history current-idx) - exwm-workspace--switch-map nil - `(exwm-workspace--switch-history . ,(1+ current-idx)))) - (workspace-idx (cl-position history-idx exwm-workspace--switch-history :test #'equal))) - (elt exwm-workspace--list workspace-idx))))) + (interactive (list (exwm-workspace--prompt-for-workspace))) (let ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index))) (unless id (setq id (exwm--buffer->id (window-buffer)))) (with-current-buffer (exwm--id->buffer id) -- cgit 1.4.1 From e4911181d317942e9946da4cbceeeb4c6ca98884 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Sun, 17 Jul 2016 12:00:00 +0000 Subject: Support swapping workspaces * exwm-workspace.el (exwm-workspace-swap-workspaces): New function to interchange the position of two workspaces. --- exwm-workspace.el | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/exwm-workspace.el b/exwm-workspace.el index 21a415eb81..8627432e56 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -394,6 +394,28 @@ The optional FORCE option is for internal use only." (xcb:flush exwm--connection)) (run-hooks 'exwm-workspace-switch-hook))) +;;;###autoload +(defun exwm-workspace-swap-workspaces (workspace1 workspace2) + "Swap position of WORKSPACE1 with that of WORKSPACE2." + (interactive + (unless (and (eq major-mode 'exwm-mode) exwm--fullscreen) ;it's invisible + (list + (exwm-workspace--prompt-for-workspace) + (exwm-workspace--prompt-for-workspace)))) + (let ((pos1 (exwm-workspace--position workspace1)) + (pos2 (exwm-workspace--position workspace2))) + (if (and pos1 pos2) + (progn + (setf (elt exwm-workspace--list pos1) workspace2) + (setf (elt exwm-workspace--list pos2) workspace1) + (cond + ((eq exwm-workspace--current workspace1) + (setq exwm-workspace-current-index pos2)) + ((eq exwm-workspace--current workspace2) + (setq exwm-workspace-current-index pos1)))) + (user-error "[EXWM] Frames are not workspaces")))) + + (defun exwm-workspace--on-focus-in () "Handle unexpected frame switch." ;; `focus-in-hook' is run by `handle-switch-frame'. -- cgit 1.4.1 From f4b8cc47c7a00456566a8a794cd9ee0bf06efaaa Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Sun, 17 Jul 2016 12:00:00 +0000 Subject: Support moving workspaces * exwm-workspace.el (exwm-workspace-move-workspace): New function to move a workspace to a certain position. --- exwm-workspace.el | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 8627432e56..9bbbf2a51b 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -396,7 +396,7 @@ The optional FORCE option is for internal use only." ;;;###autoload (defun exwm-workspace-swap-workspaces (workspace1 workspace2) - "Swap position of WORKSPACE1 with that of WORKSPACE2." + "Interchange position of WORKSPACE1 with that of WORKSPACE2." (interactive (unless (and (eq major-mode 'exwm-mode) exwm--fullscreen) ;it's invisible (list @@ -415,6 +415,20 @@ The optional FORCE option is for internal use only." (setq exwm-workspace-current-index pos1)))) (user-error "[EXWM] Frames are not workspaces")))) +;;;###autoload +(defun exwm-workspace-move-workspace (workspace nth) + "Move WORKSPACE to the NTH position. +When called interactively, prompt for a workspace and move current one just +before it." + (interactive + (unless (and (eq major-mode 'exwm-mode) exwm--fullscreen) ;it's invisible + (list exwm-workspace--current + (exwm-workspace--position (exwm-workspace--prompt-for-workspace))))) + (let ((pos (exwm-workspace--position workspace))) + (if (= nth pos) + (user-error "[EXWM] Cannot move to same position") + (pop (nthcdr pos exwm-workspace--list)) + (push workspace (nthcdr nth exwm-workspace--list))))) (defun exwm-workspace--on-focus-in () "Handle unexpected frame switch." -- cgit 1.4.1 From 0fbc725de1b13572cfc7f4da58c89c576039f249 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Sun, 17 Jul 2016 12:00:00 +0000 Subject: Support adding and removing workspaces Frames created via `make-frame' are added to the workspace list; `delete-frame' removes them from the list. Floating frames, non-graphical frames, as well as those associated to different displays are ignored. When deleting a workspace, care is taken to reparent that all X clients another workspace. * exwm-workspace.el (exwm-workspace--add-frame-as-workspace) (exwm-workspace--remove-frame-as-workspace): New functions that intercept created and deleted frames and configure them as EXWM workspaces. (exwm-workspace--update-ewmh-props): New function to update desktop-related EWMH properties after workspace changes. (exwm-workspace--init): Use `exwm-workspace--add-frame-as-workspace' to create the initial workspaces. --- exwm-workspace.el | 368 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 223 insertions(+), 145 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 9bbbf2a51b..2a11756fbb 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -851,129 +851,41 @@ before it." (defvar exwm-workspace--timer nil "Timer used to track echo area changes.") -(defun exwm-workspace--modify-all-x-frames-parameters (new-x-parameters) - "Modifies `window-system-default-frame-alist' for the X Window System. -NEW-X-PARAMETERS is an alist of frame parameters, merged into current -`window-system-default-frame-alist' for the X Window System. The parameters are -applied to all subsequently created X frames." - ;; The parameters are modified in place; take current - ;; ones or insert a new X-specific list. - (let ((x-parameters (or (assq 'x window-system-default-frame-alist) - (let ((new-x-parameters '(x))) - (push new-x-parameters window-system-default-frame-alist) - new-x-parameters)))) - (setf (cdr x-parameters) - (append new-x-parameters (cdr x-parameters))))) - -(defun exwm-workspace--init () - "Initialize workspace module." - (cl-assert (and (< 0 exwm-workspace-number) (>= 10 exwm-workspace-number))) - ;; Prevent unexpected exit - (setq confirm-kill-emacs #'exwm-workspace--confirm-kill-emacs) - (if (not (exwm-workspace--minibuffer-own-frame-p)) - ;; Initialize workspaces with minibuffers. - (progn - (setq exwm-workspace--list (frame-list)) - (when (< 1 (exwm-workspace--count)) - ;; Exclude the initial frame. - (dolist (i exwm-workspace--list) - (unless (frame-parameter i 'window-id) - (setq exwm-workspace--list (delq i exwm-workspace--list)))) - (cl-assert (= 1 (exwm-workspace--count))) - (setq exwm-workspace--client - (frame-parameter (car exwm-workspace--list) 'client)) - (let ((f (car exwm-workspace--list))) - ;; Remove the possible internal border. - (set-frame-parameter f 'internal-border-width 0) - ;; Prevent user from deleting this frame by accident. - (set-frame-parameter f 'client nil)) - ;; Create remaining frames. - (dotimes (_ (1- exwm-workspace-number)) - (nconc exwm-workspace--list - (list (make-frame '((window-system . x) - (internal-border-width . 0)))))))) - ;; Initialize workspaces without minibuffers. - (let ((old-frames (frame-list))) - (setq exwm-workspace--minibuffer - (make-frame '((window-system . x) (minibuffer . only) - (left . 10000) (right . 10000) - (width . 0) (height . 0) - (internal-border-width . 0) - (client . nil)))) - ;; Remove/hide existing frames. - (dolist (f old-frames) - (if (frame-parameter f 'client) - (progn - (unless exwm-workspace--client - (setq exwm-workspace--client (frame-parameter f 'client))) - (make-frame-invisible f)) - (when (eq 'x (framep f)) ;do not delete the initial frame. - (delete-frame f))))) - ;; This is the only usable minibuffer frame. - (setq default-minibuffer-frame exwm-workspace--minibuffer) - (exwm-workspace--modify-all-x-frames-parameters - '((minibuffer . nil))) - (let ((outer-id (string-to-number - (frame-parameter exwm-workspace--minibuffer - 'outer-window-id))) - (container (xcb:generate-id exwm--connection))) - (set-frame-parameter exwm-workspace--minibuffer 'exwm-outer-id outer-id) - (set-frame-parameter exwm-workspace--minibuffer 'exwm-container - container) - (xcb:+request exwm--connection - (make-instance 'xcb:CreateWindow - :depth 0 :wid container :parent exwm--root - :x -1 :y -1 :width 1 :height 1 - :border-width 0 :class xcb:WindowClass:CopyFromParent - :visual 0 ;CopyFromParent - :value-mask xcb:CW:OverrideRedirect - :override-redirect 1)) - (exwm--debug - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_WM_NAME - :window container - :data "Minibuffer container"))) - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window outer-id :parent container :x 0 :y 0)) - ;; Attach event listener for monitoring the frame - (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window outer-id - :value-mask xcb:CW:EventMask - :event-mask xcb:EventMask:StructureNotify)) - (xcb:+event exwm--connection 'xcb:ConfigureNotify - #'exwm-workspace--on-ConfigureNotify)) - ;; Show/hide minibuffer / echo area when they're active/inactive. - (add-hook 'minibuffer-setup-hook #'exwm-workspace--on-minibuffer-setup) - (add-hook 'minibuffer-exit-hook #'exwm-workspace--on-minibuffer-exit) - (setq exwm-workspace--timer - (run-with-idle-timer 0 t #'exwm-workspace--on-echo-area-dirty)) - (add-hook 'echo-area-clear-hook #'exwm-workspace--on-echo-area-clear) - ;; Create workspace frames. - (dotimes (_ exwm-workspace-number) - (push (make-frame `((window-system . x) - (internal-border-width . 0) - (client . nil))) - exwm-workspace--list)) - ;; The default behavior of `display-buffer' (indirectly called by - ;; `minibuffer-completion-help') is not correct here. - (cl-pushnew '(exwm-workspace--display-buffer) display-buffer-alist - :test #'equal)) - ;; Handle unexpected frame switch. - (add-hook 'focus-in-hook #'exwm-workspace--on-focus-in) - ;; Prevent `other-buffer' from selecting already displayed EXWM buffers. - (modify-all-frames-parameters - '((buffer-predicate . exwm-layout--other-buffer-predicate))) - ;; Configure workspaces - (dolist (i exwm-workspace--list) - (let ((outer-id (string-to-number (frame-parameter i 'outer-window-id))) +(defun exwm-workspace--add-frame-as-workspace (frame) + "Configure frame FRAME to be treated as a workspace." + (cond + ((>= (exwm-workspace--count) exwm-workspace-number) + (delete-frame frame) + (user-error "[EXWM] Too many workspaces: maximum is %d" exwm-workspace-number)) + ((exwm-workspace--workspace-p frame) + (exwm--log "Frame `%s' is already a workspace" frame)) + ((not (display-graphic-p frame)) + (exwm--log "Frame `%s' is not graphical" frame)) + ((not (string-equal (slot-value exwm--connection 'display) + (frame-parameter frame 'display))) + (exwm--log "Frame `%s' is on a different DISPLAY (%S instead of %S)" + frame + (frame-parameter frame 'display) + (slot-value exwm--connection 'display))) + ((frame-parameter frame 'exwm-floating) + (exwm--log "Frame `%s' is floating" frame)) + (t + (exwm--log "Adding frame `%s' as workspace" frame) + (setq exwm-workspace--list (nconc exwm-workspace--list (list frame)) + exwm-workspace--current frame) + (let ((outer-id (string-to-number (frame-parameter frame 'outer-window-id))) (container (xcb:generate-id exwm--connection)) (workspace (xcb:generate-id exwm--connection))) ;; Save window IDs - (set-frame-parameter i 'exwm-outer-id outer-id) - (set-frame-parameter i 'exwm-container container) - (set-frame-parameter i 'exwm-workspace workspace) + (set-frame-parameter frame 'exwm-outer-id outer-id) + (set-frame-parameter frame 'exwm-container container) + (set-frame-parameter frame 'exwm-workspace workspace) + ;; Use same RandR output and geometry as previous workspace. + (let ((prev-workspace (selected-frame))) + (dolist (param '(exwm-randr-output + exwm-geometry)) + (set-frame-parameter frame param + (frame-parameter prev-workspace param)))) (xcb:+request exwm--connection (make-instance 'xcb:CreateWindow :depth 0 :wid workspace :parent exwm--root @@ -1002,44 +914,208 @@ applied to all subsequently created X frames." :window workspace :data (format "EXWM workspace %d" - (exwm-workspace--position i)))) + (exwm-workspace--position frame)))) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_NAME :window container :data (format "EXWM workspace %d frame container" - (exwm-workspace--position i))))) + (exwm-workspace--position frame))))) (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow :window outer-id :parent container :x 0 :y 0)) (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window container)) (xcb:+request exwm--connection - (make-instance 'xcb:MapWindow :window workspace)))) + (make-instance 'xcb:MapWindow :window workspace))) + (xcb:flush exwm--connection) + ;; Delay making the workspace fullscreen until Emacs becomes idle + (run-with-idle-timer 0 nil + `(lambda () + (set-frame-parameter ,frame 'fullscreen 'fullboth))) + ;; Update EWMH properties. + (exwm-workspace--update-ewmh-props) + (exwm-workspace-switch frame t)))) + +(defun exwm-workspace--remove-frame-as-workspace (frame) + "Stop treating frame FRAME as a workspace." + (cond + ((= 1 (exwm-workspace--count)) + (exwm--log "Cannot remove last workspace")) + ((not (exwm-workspace--workspace-p frame)) + (exwm--log "Frame `%s' is not a workspace" frame)) + (t + (exwm--log "Removing frame `%s' as workspace" frame) + (let* ((index (exwm-workspace--position frame)) + (lastp (= index (1- (exwm-workspace--count)))) + ;; As we are removing this workspace, the one on its left is its + ;; natural substitutes... except when this is already the last one + ;; and there is none on its left. + (nextw (elt exwm-workspace--list (+ index (if lastp -1 +1))))) + ;; Clients need to be moved to some other workspace before this is being + ;; removed. + (dolist (pair exwm--id-buffer-alist) + (with-current-buffer (cdr pair) + (when (eq exwm--frame frame) + (exwm-workspace-move-window nextw exwm--id)))) + ;; Need to remove the workspace from the list in order for + ;; `exwm-workspace-switch' to calculate the right index. + (setq exwm-workspace--list (delete frame exwm-workspace--list)) + (when (eq frame exwm-workspace--current) + (exwm-workspace-switch nextw))) + ;; Update EWMH properties. + (exwm-workspace--update-ewmh-props) + ;; Update switch history. + (setq exwm-workspace--switch-history-outdated t)))) + +(defun exwm-workspace--update-ewmh-props () + "Update EWMH properties to match the workspace list." + (let ((num-workspaces (exwm-workspace--count))) + ;; Set _NET_NUMBER_OF_DESKTOPS (it's currently fixed). + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_NUMBER_OF_DESKTOPS + :window exwm--root :data num-workspaces)) + ;; Set _NET_DESKTOP_GEOMETRY. + (exwm-workspace--set-desktop-geometry) + ;; Set _NET_DESKTOP_VIEWPORT (we don't support large desktop). + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT + :window exwm--root + :data (make-vector (* 2 num-workspaces) 0))) + ;; Update and set _NET_WORKAREA. + (exwm-workspace--update-workareas) + ;; Set _NET_VIRTUAL_ROOTS (it's currently fixed.) + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_VIRTUAL_ROOTS + :window exwm--root + :data (vconcat (mapcar + (lambda (i) + (frame-parameter i 'exwm-workspace)) + exwm-workspace--list))))) + (xcb:flush exwm--connection)) + +(defun exwm-workspace--modify-all-x-frames-parameters (new-x-parameters) + "Modifies `window-system-default-frame-alist' for the X Window System. +NEW-X-PARAMETERS is an alist of frame parameters, merged into current +`window-system-default-frame-alist' for the X Window System. The parameters are +applied to all subsequently created X frames." + ;; The parameters are modified in place; take current + ;; ones or insert a new X-specific list. + (let ((x-parameters (or (assq 'x window-system-default-frame-alist) + (let ((new-x-parameters '(x))) + (push new-x-parameters window-system-default-frame-alist) + new-x-parameters)))) + (setf (cdr x-parameters) + (append new-x-parameters (cdr x-parameters))))) + +(defun exwm-workspace--init () + "Initialize workspace module." + (cl-assert (and (< 0 exwm-workspace-number) (>= 10 exwm-workspace-number))) + ;; Prevent unexpected exit + (setq confirm-kill-emacs #'exwm-workspace--confirm-kill-emacs) + (let ((initial-workspaces (frame-list))) + (if (not (exwm-workspace--minibuffer-own-frame-p)) + ;; Initialize workspaces with minibuffers. + (progn + (when (< 1 (exwm-workspace--count)) + ;; Exclude the initial frame. + (dolist (i initial-workspaces) + (unless (frame-parameter i 'window-id) + (setq initial-workspaces (delq i initial-workspaces)))) + (cl-assert (= 1 (length initial-workspaces))) + (setq exwm-workspace--client + (frame-parameter (car exwm-workspace--list) 'client)) + (let ((f (car initial-workspaces))) + ;; Remove the possible internal border. + (set-frame-parameter f 'internal-border-width 0) + ;; Prevent user from deleting this frame by accident. + (set-frame-parameter f 'client nil))) + ;; Create remaining frames. + (dotimes (_ (1- exwm-workspace-number)) + (nconc initial-workspaces + (list (make-frame '((window-system . x) + (internal-border-width . 0))))))) + ;; Initialize workspaces without minibuffers. + (setq exwm-workspace--minibuffer + (make-frame '((window-system . x) (minibuffer . only) + (left . 10000) (right . 10000) + (width . 0) (height . 0) + (internal-border-width . 0) + (client . nil)))) + ;; Remove/hide existing frames. + (dolist (f initial-workspaces) + (if (frame-parameter f 'client) + (progn + (unless exwm-workspace--client + (setq exwm-workspace--client (frame-parameter f 'client))) + (make-frame-invisible f)) + (when (eq 'x (framep f)) ;do not delete the initial frame. + (delete-frame f)))) + ;; This is the only usable minibuffer frame. + (setq default-minibuffer-frame exwm-workspace--minibuffer) + (exwm-workspace--modify-all-x-frames-parameters + '((minibuffer . nil))) + (let ((outer-id (string-to-number + (frame-parameter exwm-workspace--minibuffer + 'outer-window-id))) + (container (xcb:generate-id exwm--connection))) + (set-frame-parameter exwm-workspace--minibuffer 'exwm-outer-id outer-id) + (set-frame-parameter exwm-workspace--minibuffer 'exwm-container + container) + (xcb:+request exwm--connection + (make-instance 'xcb:CreateWindow + :depth 0 :wid container :parent exwm--root + :x -1 :y -1 :width 1 :height 1 + :border-width 0 :class xcb:WindowClass:CopyFromParent + :visual 0 ;CopyFromParent + :value-mask xcb:CW:OverrideRedirect + :override-redirect 1)) + (exwm--debug + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_NAME + :window container + :data "Minibuffer container"))) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window outer-id :parent container :x 0 :y 0)) + ;; Attach event listener for monitoring the frame + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window outer-id + :value-mask xcb:CW:EventMask + :event-mask xcb:EventMask:StructureNotify)) + (xcb:+event exwm--connection 'xcb:ConfigureNotify + #'exwm-workspace--on-ConfigureNotify)) + ;; Show/hide minibuffer / echo area when they're active/inactive. + (add-hook 'minibuffer-setup-hook #'exwm-workspace--on-minibuffer-setup) + (add-hook 'minibuffer-exit-hook #'exwm-workspace--on-minibuffer-exit) + (setq exwm-workspace--timer + (run-with-idle-timer 0 t #'exwm-workspace--on-echo-area-dirty)) + (add-hook 'echo-area-clear-hook #'exwm-workspace--on-echo-area-clear) + ;; Create workspace frames. + (dotimes (_ exwm-workspace-number) + (push (make-frame `((window-system . x) + (internal-border-width . 0) + (client . nil))) + exwm-workspace--list)) + ;; The default behavior of `display-buffer' (indirectly called by + ;; `minibuffer-completion-help') is not correct here. + (cl-pushnew '(exwm-workspace--display-buffer) display-buffer-alist + :test #'equal)) + ;; Handle unexpected frame switch. + (add-hook 'focus-in-hook #'exwm-workspace--on-focus-in) + ;; Prevent `other-buffer' from selecting already displayed EXWM buffers. + (modify-all-frames-parameters + '((buffer-predicate . exwm-layout--other-buffer-predicate))) + ;; Configure workspaces + (dolist (i initial-workspaces) + (exwm-workspace--add-frame-as-workspace i))) (xcb:flush exwm--connection) ;; We have to advice `x-create-frame' or every call to it would hang EXWM (advice-add 'x-create-frame :around #'exwm-workspace--x-create-frame) - ;; Set _NET_NUMBER_OF_DESKTOPS (it's currently fixed). - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_NUMBER_OF_DESKTOPS - :window exwm--root :data (exwm-workspace--count))) - ;; Set _NET_DESKTOP_GEOMETRY. - (exwm-workspace--set-desktop-geometry) - ;; Set _NET_DESKTOP_VIEWPORT (we don't support large desktop). - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT - :window exwm--root - :data (make-vector (* 2 (exwm-workspace--count)) 0))) - ;; Update and set _NET_WORKAREA. - (exwm-workspace--update-workareas) - ;; Set _NET_VIRTUAL_ROOTS (it's currently fixed.) - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_VIRTUAL_ROOTS - :window exwm--root - :data (vconcat (mapcar - (lambda (i) - (frame-parameter i 'exwm-workspace)) - exwm-workspace--list)))) + ;; Make new frames create new workspaces. + (add-hook 'after-make-frame-functions #'exwm-workspace--add-frame-as-workspace) + (add-hook 'delete-frame-functions #'exwm-workspace--remove-frame-as-workspace) ;; Switch to the first workspace (exwm-workspace-switch 0 t)) @@ -1060,7 +1136,9 @@ applied to all subsequently created X frames." (cl-delete '(exwm-workspace--display-buffer) display-buffer-alist :test #'equal)) (remove-hook 'focus-in-hook #'exwm-workspace--on-focus-in) - (advice-remove 'x-create-frame #'exwm-workspace--x-create-frame)) + (advice-remove 'x-create-frame #'exwm-workspace--x-create-frame) + (remove-hook 'after-make-frame-functions #'exwm-workspace--add-frame-as-workspace) + (remove-hook 'delete-frame-functions #'exwm-workspace--remove-frame-as-workspace)) (defun exwm-workspace--post-init () "The second stage in the initialization of the workspace module." -- cgit 1.4.1 From d0797d03ded13aff09211ea77c6b37cccaf6ff12 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Sun, 17 Jul 2016 12:00:00 +0000 Subject: Remove `exwm-worspace-number' No longer fill initial workspace list, nor limit the number of workspaces to `exwm-worspace-number'. Users are free to create as many as they like by hitting 'C-x 5 2' or running `make-frame'. The initial workspace list can be set up by creating frames in a configuration file. For example, to start up with 4 workspaces: (dolist (i 3) (make-frame)) The interactive workspace switcher is improved to support selecting workspaces with a many-digits position. * exwm-workspace.el (exwm-workspace-number): Remove variable, as we no longer have a fixed number of workspaces. (exwm-workspace--switch-map) (exwm-workspace--switch-map-nth-prefix) (exwm-workspace--switch-map-select-nth): Improve support for selecting workspaces with multiple-digit positions (e.g. workspace number 12). (exwm-workspace--add-frame-as-workspace, exwm-workspace--init): Remove limit on number of workspaces. (exwm-workspace--init): Stop creating workspaces at startup. * exwm-config.el (exwm-config-default): Bind keys to namespaces 0-9 in the default configuration. --- exwm-config.el | 2 +- exwm-workspace.el | 99 +++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 64 insertions(+), 37 deletions(-) diff --git a/exwm-config.el b/exwm-config.el index d6e10f23c9..e1e5010d3d 100644 --- a/exwm-config.el +++ b/exwm-config.el @@ -39,7 +39,7 @@ ;; 's-w': Switch workspace (exwm-input-set-key (kbd "s-w") #'exwm-workspace-switch) ;; 's-N': Switch to certain workspace - (dotimes (i exwm-workspace-number) + (dotimes (i 10) (exwm-input-set-key (kbd (format "s-%d" i)) `(lambda () (interactive) (exwm-workspace-switch ,i)))) ;; 's-&': Launch application diff --git a/exwm-workspace.el b/exwm-workspace.el index 2a11756fbb..75fcc96e04 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -27,7 +27,6 @@ (require 'exwm-core) -(defvar exwm-workspace-number 4 "Number of workspaces (1 ~ 10).") (defvar exwm-workspace--list nil "List of all workspaces (Emacs frames).") (defvar exwm-workspace--current nil "Current active workspace.") (defvar exwm-workspace-current-index 0 "Index of current active workspace.") @@ -63,12 +62,7 @@ NIL if FRAME is not a workspace" (let ((map (make-sparse-keymap))) (define-key map [t] (lambda () (interactive))) (dotimes (i 10) - (define-key map (int-to-string i) - `(lambda () - (interactive) - (when (< ,i (exwm-workspace--count)) - (goto-history-element ,(1+ i)) - (exit-minibuffer))))) + (define-key map (int-to-string i) #'exwm-workspace--switch-map-nth-prefix)) (define-key map "\C-a" (lambda () (interactive) (goto-history-element 1))) (define-key map "\C-e" (lambda () (interactive) @@ -318,6 +312,48 @@ Value nil means to use the default position which is fixed at bottom, while :stack-mode xcb:StackMode:Above)) (set-frame-width exwm-workspace--minibuffer width nil t))) +(defun exwm-workspace--switch-map-nth-prefix (&optional prefix-digits) + "Allow selecting a workspace by number. + +PREFIX-DIGITS is a list of the digits introduced so far." + (interactive) + (let* ((ev (this-command-keys-vector)) + (off (1- (length ev))) + (k (elt ev off)) + ;; 0 is ASCII 48. + (d (- k 48)) + ;; Convert prefix-digits to number. For example, '(2 1) to 120. + (o 1) + (pn (apply #'+ (mapcar (lambda (x) + (setq o (* 10 o)) + (* o x)) + prefix-digits))) + (n (+ pn d)) + (num-workspaces (exwm-workspace--count))) + (if (= (length prefix-digits) ; Go ahead if there are enough + (floor (log num-workspaces 10))) ; digits to select any workspace. + (exwm-workspace--switch-map-select-nth n) + (set-transient-map + (let ((map (make-sparse-keymap)) + (cmd `(lambda () + (interactive) + (exwm-workspace--switch-map-nth-prefix ',(cons d prefix-digits)) + ))) + (dotimes (i 10) + (define-key map (int-to-string i) cmd)) + ;; Accept + (define-key map [return] + `(lambda () + (interactive) + (exwm-workspace--switch-map-select-nth ,n))) + map))))) + +(defun exwm-workspace--switch-map-select-nth (n) + "Select Nth workspace." + (interactive) + (goto-history-element (1+ n)) + (exit-minibuffer)) + (defvar exwm-workspace-switch-hook nil "Normal hook run after switching workspace.") @@ -854,9 +890,6 @@ before it." (defun exwm-workspace--add-frame-as-workspace (frame) "Configure frame FRAME to be treated as a workspace." (cond - ((>= (exwm-workspace--count) exwm-workspace-number) - (delete-frame frame) - (user-error "[EXWM] Too many workspaces: maximum is %d" exwm-workspace-number)) ((exwm-workspace--workspace-p frame) (exwm--log "Frame `%s' is already a workspace" frame)) ((not (display-graphic-p frame)) @@ -1010,31 +1043,23 @@ applied to all subsequently created X frames." (defun exwm-workspace--init () "Initialize workspace module." - (cl-assert (and (< 0 exwm-workspace-number) (>= 10 exwm-workspace-number))) ;; Prevent unexpected exit (setq confirm-kill-emacs #'exwm-workspace--confirm-kill-emacs) (let ((initial-workspaces (frame-list))) (if (not (exwm-workspace--minibuffer-own-frame-p)) ;; Initialize workspaces with minibuffers. - (progn - (when (< 1 (exwm-workspace--count)) - ;; Exclude the initial frame. - (dolist (i initial-workspaces) - (unless (frame-parameter i 'window-id) - (setq initial-workspaces (delq i initial-workspaces)))) - (cl-assert (= 1 (length initial-workspaces))) - (setq exwm-workspace--client - (frame-parameter (car exwm-workspace--list) 'client)) - (let ((f (car initial-workspaces))) - ;; Remove the possible internal border. - (set-frame-parameter f 'internal-border-width 0) - ;; Prevent user from deleting this frame by accident. - (set-frame-parameter f 'client nil))) - ;; Create remaining frames. - (dotimes (_ (1- exwm-workspace-number)) - (nconc initial-workspaces - (list (make-frame '((window-system . x) - (internal-border-width . 0))))))) + (when (< 1 (length initial-workspaces)) + ;; Exclude the initial frame. + (dolist (i initial-workspaces) + (unless (frame-parameter i 'window-id) + (setq initial-workspaces (delq i initial-workspaces)))) + (setq exwm-workspace--client + (frame-parameter (car exwm-workspace--list) 'client)) + (let ((f (car initial-workspaces))) + ;; Remove the possible internal border. + (set-frame-parameter f 'internal-border-width 0) + ;; Prevent user from deleting the first frame by accident. + (set-frame-parameter f 'client nil))) ;; Initialize workspaces without minibuffers. (setq exwm-workspace--minibuffer (make-frame '((window-system . x) (minibuffer . only) @@ -1092,12 +1117,14 @@ applied to all subsequently created X frames." (setq exwm-workspace--timer (run-with-idle-timer 0 t #'exwm-workspace--on-echo-area-dirty)) (add-hook 'echo-area-clear-hook #'exwm-workspace--on-echo-area-clear) - ;; Create workspace frames. - (dotimes (_ exwm-workspace-number) - (push (make-frame `((window-system . x) - (internal-border-width . 0) - (client . nil))) - exwm-workspace--list)) + ;; Recreate frames with the external minibuffer set. + (setq initial-workspaces + (mapcar + (lambda (_) + (make-frame `((window-system . x) + (internal-border-width . 0) + (client . nil)))) + initial-workspaces)) ;; The default behavior of `display-buffer' (indirectly called by ;; `minibuffer-completion-help') is not correct here. (cl-pushnew '(exwm-workspace--display-buffer) display-buffer-alist -- cgit 1.4.1 From 1a716d39397edfc2a083c479817ea7d40b23fd54 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 18 Jul 2016 01:46:46 +0800 Subject: * exwm-workspace.el (exwm-workspace--init): Rebalance parentheses. --- exwm-workspace.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 6228d99bbd..a7e668110a 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -819,12 +819,12 @@ The optional FORCE option is for internal use only." ;; Remove the possible internal border. (set-frame-parameter f 'internal-border-width 0) ;; Prevent user from deleting this frame by accident. - (set-frame-parameter f 'client nil)) + (set-frame-parameter f 'client nil))) ;; Create remaining frames. (dotimes (_ (1- exwm-workspace-number)) (nconc exwm-workspace--list (list (make-frame '((window-system . x) - (internal-border-width . 0)))))))) + (internal-border-width . 0))))))) ;; Initialize workspaces without minibuffers. (let ((old-frames (frame-list))) (setq exwm-workspace--minibuffer -- cgit 1.4.1 From c22f35620f0db3e86f4b0678a29dc3eee48df4d9 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 18 Jul 2016 12:55:27 +0800 Subject: Fix 2 dock-related issues * exwm-floating.el (exwm-floating--set-floating): Add a workaround to prevent accidental move of Emacs frame when struts are set. * exwm-workspace.el (exwm-workspace--update-workareas): Make legacy docks working. --- exwm-floating.el | 14 +++++++++++++- exwm-workspace.el | 28 ++++++++++++++++------------ 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 9d4d948d30..20b3fc454e 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -70,6 +70,7 @@ (defvar exwm-workspace--list) (defvar exwm-workspace-current-index) (defvar exwm-workspace--switch-history-outdated) +(defvar exwm-workspace--struts) (declare-function exwm-layout--refresh "exwm-layout.el" ()) (declare-function exwm-layout--show "exwm-layout.el" (id &optional window)) @@ -242,7 +243,18 @@ (exwm-floating-hide)) (with-selected-frame exwm-workspace--current (exwm-layout--refresh)) - (select-frame-set-input-focus frame))) + (select-frame-set-input-focus frame)) + ;; FIXME: Strangely, the Emacs frame can move itself at this point + ;; when there are left/top struts set. Force resetting its + ;; position seems working, but it'd better to figure out why. + (when exwm-workspace--struts + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window outer-id + :value-mask (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y) + :x 0 :y 0)) + (xcb:flush exwm--connection))) (run-hooks 'exwm-floating-setup-hook) ;; Redraw the frame. (redisplay)) diff --git a/exwm-workspace.el b/exwm-workspace.el index a7e668110a..5c8279ede9 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -191,32 +191,36 @@ Value nil means to use the default position which is fixed at bottom, while (`left (setq delta (- (aref w 0) width)) (when (and (< delta 0) - (< (max (aref position 0) (aref w 1)) - (min (aref position 1) - (+ (aref w 1) (aref w 3))))) + (or (not position) + (< (max (aref position 0) (aref w 1)) + (min (aref position 1) + (+ (aref w 1) (aref w 3)))))) (cl-incf (aref w 2) delta) (setf (aref w 0) width))) (`right (setq delta (- root-width (aref w 0) (aref w 2) width)) (when (and (< delta 0) - (< (max (aref position 0) (aref w 1)) - (min (aref position 1) - (+ (aref w 1) (aref w 3))))) + (or (not position) + (< (max (aref position 0) (aref w 1)) + (min (aref position 1) + (+ (aref w 1) (aref w 3)))))) (cl-incf (aref w 2) delta))) (`top (setq delta (- (aref w 1) width)) (when (and (< delta 0) - (< (max (aref position 0) (aref w 0)) - (min (aref position 1) - (+ (aref w 0) (aref w 2))))) + (or (not position) + (< (max (aref position 0) (aref w 0)) + (min (aref position 1) + (+ (aref w 0) (aref w 2)))))) (cl-incf (aref w 3) delta) (setf (aref w 1) width))) (`bottom (setq delta (- root-height (aref w 1) (aref w 3) width)) (when (and (< delta 0) - (< (max (aref position 0) (aref w 0)) - (min (aref position 1) - (+ (aref w 0) (aref w 2))))) + (or (not position) + (< (max (aref position 0) (aref w 0)) + (min (aref position 1) + (+ (aref w 0) (aref w 2)))))) (cl-incf (aref w 3) delta)))))) ;; Save the result. (setq exwm-workspace--workareas workareas) -- cgit 1.4.1 From 73d890aad4e06cc722a00ed40532a7df07a83550 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 19 Jul 2016 10:24:23 +0800 Subject: Fix various minor issues ; Coding style fixes * exwm-core.el (exwm-mode-menu exwm-mode-map): Use `exwm-workspace--count'. * exwm-workspace.el (exwm-workspace--set-fullscreen) (exwm-workspace--on-focus-in, exwm-workspace--set-desktop): Use `exwm-workspace--position'. * exwm-workspace.el (exwm-workspace-swap, exwm-workspace-move): Renamed from `exwm-workspace-swap-workspace' and `exwm-workspace-move-workspace'. * exwm-workspace.el (exwm-workspace--update-ewmh-props): Update comments. * exwm-workspace.el (exwm-workspace--switch-map-nth-prefix): Fix a calculation. * exwm-workspace.el (exwm-workspace-switch): Fix a potential timer problem. * exwm-workspace.el (exwm-workspace-swap, exwm-workspace-move) (exwm-workspace--remove-frame-as-workspace): Update workspace and clients involved. * exwm-workspace.el (exwm-workspace--remove-frame-as-workspace): Remove workspace first. --- exwm-core.el | 2 +- exwm-layout.el | 2 +- exwm-manage.el | 2 +- exwm-workspace.el | 150 +++++++++++++++++++++++++++++++++++------------------- 4 files changed, 102 insertions(+), 54 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 70a759cde3..fd26d2cdd3 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -205,7 +205,7 @@ (interactive) (exwm-workspace-switch ,i)) (/= ,i exwm-workspace-current-index)]) - (number-sequence 0 (1- exwm-workspace-number))))))) + (number-sequence 0 (1- (exwm-workspace--count)))))))) (declare-function exwm-manage--kill-buffer-query-function "exwm-manage.el") diff --git a/exwm-layout.el b/exwm-layout.el index e3d1d74a5d..667e2faeca 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -304,7 +304,7 @@ selected by `other-buffer'." (make-instance 'xcb:ewmh:set-_NET_CLIENT_LIST_STACKING :window exwm--root :data (vconcat (append clients-other clients-iconic - clients clients-floating)))))) + clients clients-floating)))))) (defun exwm-layout--refresh () "Refresh layout." diff --git a/exwm-manage.el b/exwm-manage.el index d67929a9d2..7a3a18b88e 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -515,7 +515,7 @@ border-width: %d; sibling: #x%x; stack-mode: %d" (< (abs width-delta) exwm-manage--width-delta-min)) (if (= 0 (logand value-mask - xcb:ConfigWindow:Height)) + xcb:ConfigWindow:Height)) t (< (abs height-delta) exwm-manage--height-delta-min)))))))) diff --git a/exwm-workspace.el b/exwm-workspace.el index 75fcc96e04..629f940214 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -53,7 +53,8 @@ NIL if FRAME is not a workspace" (user-error "[EXWM] Frame is not a workspace %S" frame-or-index)) frame-or-index) ((integerp frame-or-index) - (unless (and (<= 0 frame-or-index) (< frame-or-index (exwm-workspace--count))) + (unless (and (<= 0 frame-or-index) + (< frame-or-index (exwm-workspace--count))) (user-error "[EXWM] Workspace index out of range: %d" frame-or-index)) (elt exwm-workspace--list frame-or-index)) (t (user-error "[EXWM] Invalid workspace: %s" frame-or-index)))) @@ -62,7 +63,8 @@ NIL if FRAME is not a workspace" (let ((map (make-sparse-keymap))) (define-key map [t] (lambda () (interactive))) (dotimes (i 10) - (define-key map (int-to-string i) #'exwm-workspace--switch-map-nth-prefix)) + (define-key map (int-to-string i) + #'exwm-workspace--switch-map-nth-prefix)) (define-key map "\C-a" (lambda () (interactive) (goto-history-element 1))) (define-key map "\C-e" (lambda () (interactive) @@ -95,7 +97,8 @@ NIL if FRAME is not a workspace" "Workspace: " (elt exwm-workspace--switch-history current-idx) exwm-workspace--switch-map nil `(exwm-workspace--switch-history . ,(1+ current-idx)))) - (workspace-idx (cl-position history-idx exwm-workspace--switch-history :test #'equal))) + (workspace-idx (cl-position history-idx exwm-workspace--switch-history + :test #'equal))) (elt exwm-workspace--list workspace-idx))) (defun exwm-workspace--update-switch-history () @@ -270,7 +273,7 @@ Value nil means to use the default position which is fixed at bottom, while (defun exwm-workspace--set-fullscreen (frame) "Make frame FRAME fullscreen according to `exwm-workspace--workareas'." (let ((workarea (elt exwm-workspace--workareas - (cl-position frame exwm-workspace--list))) + (exwm-workspace--position frame))) (id (frame-parameter frame 'exwm-outer-id)) (container (frame-parameter frame 'exwm-container)) (workspace (frame-parameter frame 'exwm-workspace)) @@ -317,11 +320,8 @@ Value nil means to use the default position which is fixed at bottom, while PREFIX-DIGITS is a list of the digits introduced so far." (interactive) - (let* ((ev (this-command-keys-vector)) - (off (1- (length ev))) - (k (elt ev off)) - ;; 0 is ASCII 48. - (d (- k 48)) + (let* ((k (aref (substring (this-command-keys-vector) -1) 0)) + (d (- k ?0)) ;; Convert prefix-digits to number. For example, '(2 1) to 120. (o 1) (pn (apply #'+ (mapcar (lambda (x) @@ -330,15 +330,16 @@ PREFIX-DIGITS is a list of the digits introduced so far." prefix-digits))) (n (+ pn d)) (num-workspaces (exwm-workspace--count))) - (if (= (length prefix-digits) ; Go ahead if there are enough - (floor (log num-workspaces 10))) ; digits to select any workspace. + (if (= (length prefix-digits) + (floor (log (1- num-workspaces) 10))) (exwm-workspace--switch-map-select-nth n) + ;; go ahead if there are enough digits to select any workspace. (set-transient-map (let ((map (make-sparse-keymap)) (cmd `(lambda () (interactive) - (exwm-workspace--switch-map-nth-prefix ',(cons d prefix-digits)) - ))) + (exwm-workspace--switch-map-nth-prefix + ',(cons d prefix-digits))))) (dotimes (i 10) (define-key map (int-to-string i) cmd)) ;; Accept @@ -383,10 +384,10 @@ The optional FORCE option is for internal use only." (unless (buffer-local-value 'exwm--fullscreen (window-buffer window)) (dolist (pair exwm-workspace--id-struts-alist) (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window (car pair) - :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:Above)))) + (make-instance 'xcb:ConfigureWindow + :window (car pair) + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Above)))) (setq exwm-workspace--current frame exwm-workspace-current-index index) (unless (exwm-workspace--workspace-p (selected-frame)) @@ -398,7 +399,10 @@ The optional FORCE option is for internal use only." (set-frame-parameter frame 'exwm-selected-window nil) ;; Close the (possible) active minibuffer (when (active-minibuffer-window) - (run-with-idle-timer 0 nil (lambda () (abort-recursive-edit)))) + (run-with-idle-timer 0 nil (lambda () + ;; Might be aborted by then. + (when (active-minibuffer-window) + (abort-recursive-edit))))) (if (not (exwm-workspace--minibuffer-own-frame-p)) (setq default-minibuffer-frame frame) ;; Resize/reposition the minibuffer frame @@ -428,10 +432,11 @@ The optional FORCE option is for internal use only." (make-instance 'xcb:ewmh:set-_NET_CURRENT_DESKTOP :window exwm--root :data index)) (xcb:flush exwm--connection)) - (run-hooks 'exwm-workspace-switch-hook))) + (run-hooks 'exwm-workspace-switch-hook))) + ;;;###autoload -(defun exwm-workspace-swap-workspaces (workspace1 workspace2) +(defun exwm-workspace-swap (workspace1 workspace2) "Interchange position of WORKSPACE1 with that of WORKSPACE2." (interactive (unless (and (eq major-mode 'exwm-mode) exwm--fullscreen) ;it's invisible @@ -440,19 +445,24 @@ The optional FORCE option is for internal use only." (exwm-workspace--prompt-for-workspace)))) (let ((pos1 (exwm-workspace--position workspace1)) (pos2 (exwm-workspace--position workspace2))) - (if (and pos1 pos2) - (progn - (setf (elt exwm-workspace--list pos1) workspace2) - (setf (elt exwm-workspace--list pos2) workspace1) - (cond - ((eq exwm-workspace--current workspace1) - (setq exwm-workspace-current-index pos2)) - ((eq exwm-workspace--current workspace2) - (setq exwm-workspace-current-index pos1)))) - (user-error "[EXWM] Frames are not workspaces")))) + (if (or (not pos1) (not pos2) (= pos1 pos2)) + (user-error "[EXWM] Cannot swap %s and %s" workspace1 workspace2) + (setf (elt exwm-workspace--list pos1) workspace2) + (setf (elt exwm-workspace--list pos2) workspace1) + ;; Update the _NET_WM_DESKTOP property of each X window affected. + (dolist (pair exwm--id-buffer-alist) + (when (memq (buffer-local-value 'exwm--frame (cdr pair)) + (list workspace1 workspace2)) + (exwm-workspace--set-desktop (car pair)))) + (xcb:flush exwm--connection) + (when (memq exwm-workspace--current (list workspace1 workspace2)) + ;; With the current workspace involved, lots of stuffs need refresh. + (set-frame-parameter exwm-workspace--current 'exwm-selected-window + (selected-window)) + (exwm-workspace-switch exwm-workspace--current t))))) ;;;###autoload -(defun exwm-workspace-move-workspace (workspace nth) +(defun exwm-workspace-move (workspace nth) "Move WORKSPACE to the NTH position. When called interactively, prompt for a workspace and move current one just before it." @@ -461,16 +471,36 @@ before it." (list exwm-workspace--current (exwm-workspace--position (exwm-workspace--prompt-for-workspace))))) (let ((pos (exwm-workspace--position workspace))) + (let ((pos (exwm-workspace--position workspace)) + flag start end index) (if (= nth pos) (user-error "[EXWM] Cannot move to same position") + ;; Set if the current workspace is involved. + (setq flag (or (eq workspace exwm-workspace--current) + (eq (elt exwm-workspace--list nth) + exwm-workspace--current))) + ;; Do the move. (pop (nthcdr pos exwm-workspace--list)) - (push workspace (nthcdr nth exwm-workspace--list))))) + (push workspace (nthcdr nth exwm-workspace--list)) + ;; Update the _NET_WM_DESKTOP property of each X window affected. + (setq start (min pos nth) + end (max pos nth)) + (dolist (pair exwm--id-buffer-alist) + (setq index (exwm-workspace--position + (buffer-local-value 'exwm--frame (cdr pair)))) + (unless (or (< index start) (> index end)) + (exwm-workspace--set-desktop (car pair)))) + (when flag + ;; With the current workspace involved, lots of stuffs need refresh. + (set-frame-parameter exwm-workspace--current 'exwm-selected-window + (selected-window)) + (exwm-workspace-switch exwm-workspace--current t))))) (defun exwm-workspace--on-focus-in () "Handle unexpected frame switch." ;; `focus-in-hook' is run by `handle-switch-frame'. (unless (eq this-command #'handle-switch-frame) - (let ((index (cl-position (selected-frame) exwm-workspace--list))) + (let ((index (exwm-workspace--position (selected-frame)))) (exwm--log "Focus on workspace %s" index) (when (and index (/= index exwm-workspace-current-index)) (exwm--log "Workspace was switched unexpectedly") @@ -482,7 +512,7 @@ before it." (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_DESKTOP :window id - :data (cl-position exwm--frame exwm-workspace--list))))) + :data (exwm-workspace--position exwm--frame))))) (defvar exwm-floating-border-width) (defvar exwm-floating-border-color) @@ -490,7 +520,8 @@ before it." (declare-function exwm-layout--show "exwm-layout.el" (id &optional window)) (declare-function exwm-layout--hide "exwm-layout.el" (id)) (declare-function exwm-layout--refresh "exwm-layout.el") -(declare-function exwm-layout--other-buffer-predicate "exwm-layout.el" (buffer)) +(declare-function exwm-layout--other-buffer-predicate "exwm-layout.el" + (buffer)) ;;;###autoload (defun exwm-workspace-move-window (frame-or-index &optional id) @@ -906,7 +937,8 @@ before it." (exwm--log "Adding frame `%s' as workspace" frame) (setq exwm-workspace--list (nconc exwm-workspace--list (list frame)) exwm-workspace--current frame) - (let ((outer-id (string-to-number (frame-parameter frame 'outer-window-id))) + (let ((outer-id (string-to-number (frame-parameter frame + 'outer-window-id))) (container (xcb:generate-id exwm--connection)) (workspace (xcb:generate-id exwm--connection))) ;; Save window IDs @@ -965,7 +997,8 @@ before it." ;; Delay making the workspace fullscreen until Emacs becomes idle (run-with-idle-timer 0 nil `(lambda () - (set-frame-parameter ,frame 'fullscreen 'fullboth))) + (set-frame-parameter ,frame + 'fullscreen 'fullboth))) ;; Update EWMH properties. (exwm-workspace--update-ewmh-props) (exwm-workspace-switch frame t)))) @@ -984,16 +1017,24 @@ before it." ;; As we are removing this workspace, the one on its left is its ;; natural substitutes... except when this is already the last one ;; and there is none on its left. + ;; FIXME (ch11ng): Which direction is "left"? (nextw (elt exwm-workspace--list (+ index (if lastp -1 +1))))) + ;; Need to remove the workspace from the list in order for + ;; the correct calculation of indexes. + (setq exwm-workspace--list (delete frame exwm-workspace--list)) ;; Clients need to be moved to some other workspace before this is being ;; removed. (dolist (pair exwm--id-buffer-alist) (with-current-buffer (cdr pair) (when (eq exwm--frame frame) (exwm-workspace-move-window nextw exwm--id)))) - ;; Need to remove the workspace from the list in order for - ;; `exwm-workspace-switch' to calculate the right index. - (setq exwm-workspace--list (delete frame exwm-workspace--list)) + ;; Update the _NET_WM_DESKTOP property of each X window affected. + (dolist (pair exwm--id-buffer-alist) + (when (<= (1- index) + (exwm-workspace--position (buffer-local-value 'exwm--frame + (cdr pair)))) + (exwm-workspace--set-desktop (car pair)))) + ;; If the current workspace is deleted, switch to next one. (when (eq frame exwm-workspace--current) (exwm-workspace-switch nextw))) ;; Update EWMH properties. @@ -1004,7 +1045,7 @@ before it." (defun exwm-workspace--update-ewmh-props () "Update EWMH properties to match the workspace list." (let ((num-workspaces (exwm-workspace--count))) - ;; Set _NET_NUMBER_OF_DESKTOPS (it's currently fixed). + ;; Set _NET_NUMBER_OF_DESKTOPS. (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_NUMBER_OF_DESKTOPS :window exwm--root :data num-workspaces)) @@ -1017,7 +1058,7 @@ before it." :data (make-vector (* 2 num-workspaces) 0))) ;; Update and set _NET_WORKAREA. (exwm-workspace--update-workareas) - ;; Set _NET_VIRTUAL_ROOTS (it's currently fixed.) + ;; Set _NET_VIRTUAL_ROOTS. (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_VIRTUAL_ROOTS :window exwm--root @@ -1036,7 +1077,8 @@ applied to all subsequently created X frames." ;; ones or insert a new X-specific list. (let ((x-parameters (or (assq 'x window-system-default-frame-alist) (let ((new-x-parameters '(x))) - (push new-x-parameters window-system-default-frame-alist) + (push new-x-parameters + window-system-default-frame-alist) new-x-parameters)))) (setf (cdr x-parameters) (append new-x-parameters (cdr x-parameters))))) @@ -1084,14 +1126,16 @@ applied to all subsequently created X frames." (frame-parameter exwm-workspace--minibuffer 'outer-window-id))) (container (xcb:generate-id exwm--connection))) - (set-frame-parameter exwm-workspace--minibuffer 'exwm-outer-id outer-id) + (set-frame-parameter exwm-workspace--minibuffer + 'exwm-outer-id outer-id) (set-frame-parameter exwm-workspace--minibuffer 'exwm-container container) (xcb:+request exwm--connection (make-instance 'xcb:CreateWindow :depth 0 :wid container :parent exwm--root :x -1 :y -1 :width 1 :height 1 - :border-width 0 :class xcb:WindowClass:CopyFromParent + :border-width 0 + :class xcb:WindowClass:CopyFromParent :visual 0 ;CopyFromParent :value-mask xcb:CW:OverrideRedirect :override-redirect 1)) @@ -1115,13 +1159,13 @@ applied to all subsequently created X frames." (add-hook 'minibuffer-setup-hook #'exwm-workspace--on-minibuffer-setup) (add-hook 'minibuffer-exit-hook #'exwm-workspace--on-minibuffer-exit) (setq exwm-workspace--timer - (run-with-idle-timer 0 t #'exwm-workspace--on-echo-area-dirty)) + (run-with-idle-timer 0 t #'exwm-workspace--on-echo-area-dirty)) (add-hook 'echo-area-clear-hook #'exwm-workspace--on-echo-area-clear) ;; Recreate frames with the external minibuffer set. (setq initial-workspaces (mapcar (lambda (_) - (make-frame `((window-system . x) + (make-frame '((window-system . x) (internal-border-width . 0) (client . nil)))) initial-workspaces)) @@ -1141,8 +1185,10 @@ applied to all subsequently created X frames." ;; We have to advice `x-create-frame' or every call to it would hang EXWM (advice-add 'x-create-frame :around #'exwm-workspace--x-create-frame) ;; Make new frames create new workspaces. - (add-hook 'after-make-frame-functions #'exwm-workspace--add-frame-as-workspace) - (add-hook 'delete-frame-functions #'exwm-workspace--remove-frame-as-workspace) + (add-hook 'after-make-frame-functions + #'exwm-workspace--add-frame-as-workspace) + (add-hook 'delete-frame-functions + #'exwm-workspace--remove-frame-as-workspace) ;; Switch to the first workspace (exwm-workspace-switch 0 t)) @@ -1164,8 +1210,10 @@ applied to all subsequently created X frames." :test #'equal)) (remove-hook 'focus-in-hook #'exwm-workspace--on-focus-in) (advice-remove 'x-create-frame #'exwm-workspace--x-create-frame) - (remove-hook 'after-make-frame-functions #'exwm-workspace--add-frame-as-workspace) - (remove-hook 'delete-frame-functions #'exwm-workspace--remove-frame-as-workspace)) + (remove-hook 'after-make-frame-functions + #'exwm-workspace--add-frame-as-workspace) + (remove-hook 'delete-frame-functions + #'exwm-workspace--remove-frame-as-workspace)) (defun exwm-workspace--post-init () "The second stage in the initialization of the workspace module." -- cgit 1.4.1 From 622618ac6e401857b335b7e5dd1969cb9e1f948b Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 19 Jul 2016 10:30:21 +0800 Subject: Improve the handling of workspaces * exwm-workspace.el (exwm-workspace--prompt-for-workspace): Add an optional argument to modify prompt. (exwm-workspace-switch, exwm-workspace-swap) (exwm-workspace-move, exwm-workspace-move-window): Use it. * exwm-workspace.el (exwm-workspace-number): Re-introduce the variable (now it stands for the initial workspace number). (exwm-workspace--init): Create remaining initial workspaces. * exwm-workspace.el (exwm-workspace-add, exwm-workspace-delete): New commands for adding/deleting workspaces. (exwm-workspace--switch-map): Add "+"/"-" to increase/descrease workspace number. * exwm-workspace.el (exwm-workspace-switch): Automatically add missing workspaces. * exwm.el (exwm--on-ClientMessage): Support _NET_NUMBER_OF_DESKTOPS client message for adjusting workspace number. --- exwm-workspace.el | 75 +++++++++++++++++++++++++++++++++++++++++++++++-------- exwm.el | 11 ++++++++ 2 files changed, 76 insertions(+), 10 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 629f940214..0843c27e92 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -27,6 +27,7 @@ (require 'exwm-core) +(defvar exwm-workspace-number 1 "Initial number of workspaces.") (defvar exwm-workspace--list nil "List of all workspaces (Emacs frames).") (defvar exwm-workspace--current nil "Current active workspace.") (defvar exwm-workspace-current-index 0 "Index of current active workspace.") @@ -62,6 +63,8 @@ NIL if FRAME is not a workspace" (defvar exwm-workspace--switch-map (let ((map (make-sparse-keymap))) (define-key map [t] (lambda () (interactive))) + (define-key map "+" #'exwm-workspace-add) + (define-key map "-" #'exwm-workspace-delete) (dotimes (i 10) (define-key map (int-to-string i) #'exwm-workspace--switch-map-nth-prefix)) @@ -88,13 +91,14 @@ NIL if FRAME is not a workspace" (defvar exwm-workspace--switch-history-outdated nil "Non-nil to indicate `exwm-workspace--switch-history' is outdated.") -(defun exwm-workspace--prompt-for-workspace () +(defun exwm-workspace--prompt-for-workspace (&optional prompt) "Prompt for a workspace, returning the workspace frame." (exwm-workspace--update-switch-history) (let* ((current-idx (exwm-workspace--position exwm-workspace--current)) (history-add-new-input nil) ;prevent modifying history (history-idx (read-from-minibuffer - "Workspace: " (elt exwm-workspace--switch-history current-idx) + (or prompt "Workspace: ") + (elt exwm-workspace--switch-history current-idx) exwm-workspace--switch-map nil `(exwm-workspace--switch-history . ,(1+ current-idx)))) (workspace-idx (cl-position history-idx exwm-workspace--switch-history @@ -359,14 +363,25 @@ PREFIX-DIGITS is a list of the digits introduced so far." "Normal hook run after switching workspace.") ;;;###autoload -(defun exwm-workspace-switch (frame-or-index &optional force) +(cl-defun exwm-workspace-switch (frame-or-index &optional force) "Switch to workspace INDEX. Query for FRAME-OR-INDEX if it's not specified. The optional FORCE option is for internal use only." (interactive (list (unless (and (eq major-mode 'exwm-mode) exwm--fullscreen) ;it's invisible - (exwm-workspace--prompt-for-workspace)))) + (exwm-workspace--prompt-for-workspace "Workspace [+/-]: ")))) + ;; Try to create workspace(s) when INDEX is out-of-range. + (when (and (integerp frame-or-index) + (>= frame-or-index (exwm-workspace--count))) + (run-with-idle-timer 0 nil + (lambda (times) + (dotimes (_ times) + (make-frame))) + ;; Create no more than 9 workspaces. + (min 9 + (1+ (- frame-or-index (exwm-workspace--count))))) + (cl-return-from exwm-workspace-switch)) (let* ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index)) (index (exwm-workspace--position frame)) (workspace (frame-parameter frame 'exwm-workspace)) @@ -440,9 +455,11 @@ The optional FORCE option is for internal use only." "Interchange position of WORKSPACE1 with that of WORKSPACE2." (interactive (unless (and (eq major-mode 'exwm-mode) exwm--fullscreen) ;it's invisible - (list - (exwm-workspace--prompt-for-workspace) - (exwm-workspace--prompt-for-workspace)))) + (let* ((w1 (exwm-workspace--prompt-for-workspace "Pick a workspace: ")) + (w2 (exwm-workspace--prompt-for-workspace + (format "Swap workspace %d with: " + (exwm-workspace--position w1))))) + (list w1 w2)))) (let ((pos1 (exwm-workspace--position workspace1)) (pos2 (exwm-workspace--position workspace2))) (if (or (not pos1) (not pos2) (= pos1 pos2)) @@ -469,8 +486,8 @@ before it." (interactive (unless (and (eq major-mode 'exwm-mode) exwm--fullscreen) ;it's invisible (list exwm-workspace--current - (exwm-workspace--position (exwm-workspace--prompt-for-workspace))))) - (let ((pos (exwm-workspace--position workspace))) + (exwm-workspace--position + (exwm-workspace--prompt-for-workspace "Move workspace to: "))))) (let ((pos (exwm-workspace--position workspace)) flag start end index) (if (= nth pos) @@ -496,6 +513,38 @@ before it." (selected-window)) (exwm-workspace-switch exwm-workspace--current t))))) +;;;###autoload +(defun exwm-workspace-add (&optional index) + "Add a workspace as the INDEX-th workspace, or the last one if INDEX is nil. + +INDEX must not exceed the current number of workspaces." + (interactive) + (run-with-idle-timer + 0 nil + (lambda (index) + (if (and index + ;; No need to move if it's the last one. + (< index (exwm-workspace--count))) + (exwm-workspace-move (make-frame) index) + (make-frame))) + index) + (when (active-minibuffer-window) + (abort-recursive-edit))) + +;;;###autoload +(defun exwm-workspace-delete (&optional frame-or-index) + "Delete the workspace FRAME-OR-INDEX." + (interactive) + (run-with-idle-timer 0 nil #'delete-frame + ;; We should not simply delete the selected frame + ;; since it can be e.g. a floating frame. + (if frame-or-index + (exwm-workspace--workspace-from-frame-or-index + frame-or-index) + exwm-workspace--current)) + (when (active-minibuffer-window) + (abort-recursive-edit))) + (defun exwm-workspace--on-focus-in () "Handle unexpected frame switch." ;; `focus-in-hook' is run by `handle-switch-frame'. @@ -526,7 +575,8 @@ before it." ;;;###autoload (defun exwm-workspace-move-window (frame-or-index &optional id) "Move window ID to workspace FRAME-OR-INDEX." - (interactive (list (exwm-workspace--prompt-for-workspace))) + (interactive (list + (exwm-workspace--prompt-for-workspace "Move to: "))) (let ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index))) (unless id (setq id (exwm--buffer->id (window-buffer)))) (with-current-buffer (exwm--id->buffer id) @@ -1178,6 +1228,11 @@ applied to all subsequently created X frames." ;; Prevent `other-buffer' from selecting already displayed EXWM buffers. (modify-all-frames-parameters '((buffer-predicate . exwm-layout--other-buffer-predicate))) + ;; Create remaining workspaces. + (dotimes (_ (- exwm-workspace-number (length initial-workspaces))) + (nconc initial-workspaces (list (make-frame '((window-system . x) + (internal-border-width . 0) + (client . nil)))))) ;; Configure workspaces (dolist (i initial-workspaces) (exwm-workspace--add-frame-as-workspace i))) diff --git a/exwm.el b/exwm.el index 9151fdc5c3..f7d027ebcf 100644 --- a/exwm.el +++ b/exwm.el @@ -325,6 +325,17 @@ id (slot-value obj 'window) data (slot-value (slot-value obj 'data) 'data32)) (cond + ;; _NET_NUMBER_OF_DESKTOPS. + ((= type xcb:Atom:_NET_NUMBER_OF_DESKTOPS) + (let ((current (exwm-workspace--count)) + (requested (elt data 0))) + ;; Only allow increasing/decreasing the workspace number by 1. + (cond + ((< current requested) + (make-frame)) + ((and (> current requested) + (> current 1)) + (delete-frame (car (last exwm-workspace--list))))))) ;; _NET_CURRENT_DESKTOP. ((= type xcb:Atom:_NET_CURRENT_DESKTOP) (exwm-workspace-switch (elt data 0))) -- cgit 1.4.1 From 4c9afc25b322d30b21eeea2cf3af7def0fc1dbb4 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 19 Jul 2016 10:33:11 +0800 Subject: Adapt dynamic workspace for floating module * exwm-workspace.el (exwm-workspace--add-frame-as-workspace): Fix the checking criteria of floating frames; Copy RandR frame parameters from the first workspace frame (rather than the selected one which can be a floating frame). * exwm-workspace.el (exwm-workspace--remove-frame-as-workspace): Check frame type first. --- exwm-workspace.el | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 0843c27e92..36502a2503 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -981,7 +981,10 @@ INDEX must not exceed the current number of workspaces." frame (frame-parameter frame 'display) (slot-value exwm--connection 'display))) - ((frame-parameter frame 'exwm-floating) + ((frame-parameter frame 'unsplittable) + ;; We create floating frames with the "unsplittable" parameter set. + ;; Though it may not be a floating frame, we won't treat an + ;; unsplittable frame as a workspace anyway. (exwm--log "Frame `%s' is floating" frame)) (t (exwm--log "Adding frame `%s' as workspace" frame) @@ -995,12 +998,13 @@ INDEX must not exceed the current number of workspaces." (set-frame-parameter frame 'exwm-outer-id outer-id) (set-frame-parameter frame 'exwm-container container) (set-frame-parameter frame 'exwm-workspace workspace) - ;; Use same RandR output and geometry as previous workspace. - (let ((prev-workspace (selected-frame))) + ;; Copy RandR frame parameters from the first workspace to + ;; prevent potential problems. The values do not matter here as + ;; they'll be updated by the RandR module later. + (let ((w (car exwm-workspace--list))) (dolist (param '(exwm-randr-output exwm-geometry)) - (set-frame-parameter frame param - (frame-parameter prev-workspace param)))) + (set-frame-parameter frame param (frame-parameter w param)))) (xcb:+request exwm--connection (make-instance 'xcb:CreateWindow :depth 0 :wid workspace :parent exwm--root @@ -1056,10 +1060,12 @@ INDEX must not exceed the current number of workspaces." (defun exwm-workspace--remove-frame-as-workspace (frame) "Stop treating frame FRAME as a workspace." (cond - ((= 1 (exwm-workspace--count)) - (exwm--log "Cannot remove last workspace")) ((not (exwm-workspace--workspace-p frame)) (exwm--log "Frame `%s' is not a workspace" frame)) + ((= 1 (exwm-workspace--count)) + ;; FIXME: When using dedicated minibuffer frame, deleting the last + ;; frame hangs Emacs. + (user-error "[EXWM] Cannot remove last workspace")) (t (exwm--log "Removing frame `%s' as workspace" frame) (let* ((index (exwm-workspace--position frame)) -- cgit 1.4.1 From 2ebeec12570636e673d08c8b4d47e12ce0da048b Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 19 Jul 2016 10:34:38 +0800 Subject: Adapt dynamic workspace for RandR module * exwm-workspace.el (exwm-workspace-list-change-hook): New hook run when the workspace list is modified. * exwm-randr.el (exwm-randr--init, exwm-randr--exit): * exwm-workspace.el (exwm-workspace-swap, exwm-workspace-move) (exwm-workspace--add-frame-as-workspace) (exwm-workspace--remove-frame-as-workspace): Use it. --- exwm-randr.el | 6 ++++-- exwm-workspace.el | 15 +++++++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/exwm-randr.el b/exwm-randr.el index ac3341db3c..709469a44d 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -154,10 +154,12 @@ ;; xcb:randr:NotifyMask:OutputProperty ;; xcb:randr:NotifyMask:CrtcChange)) )) - (xcb:flush exwm--connection))))) + (xcb:flush exwm--connection) + (add-hook 'exwm-workspace-list-change-hook #'exwm-randr--refresh))))) (defun exwm-randr--exit () - "Exit the RandR module.") + "Exit the RandR module." + (remove-hook 'exwm-workspace-list-change-hook #'exwm-randr--refresh)) (defun exwm-randr-enable () "Enable RandR support for EXWM." diff --git a/exwm-workspace.el b/exwm-workspace.el index 36502a2503..d57505870a 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -449,6 +449,9 @@ The optional FORCE option is for internal use only." (xcb:flush exwm--connection)) (run-hooks 'exwm-workspace-switch-hook))) +(defvar exwm-workspace-list-change-hook nil + "Normal hook run when the workspace list is changed (workspace added, +deleted, moved, etc).") ;;;###autoload (defun exwm-workspace-swap (workspace1 workspace2) @@ -476,7 +479,8 @@ The optional FORCE option is for internal use only." ;; With the current workspace involved, lots of stuffs need refresh. (set-frame-parameter exwm-workspace--current 'exwm-selected-window (selected-window)) - (exwm-workspace-switch exwm-workspace--current t))))) + (exwm-workspace-switch exwm-workspace--current t)) + (run-hooks 'exwm-workspace-list-change-hook)))) ;;;###autoload (defun exwm-workspace-move (workspace nth) @@ -511,7 +515,8 @@ before it." ;; With the current workspace involved, lots of stuffs need refresh. (set-frame-parameter exwm-workspace--current 'exwm-selected-window (selected-window)) - (exwm-workspace-switch exwm-workspace--current t))))) + (exwm-workspace-switch exwm-workspace--current t)) + (run-hooks 'exwm-workspace-list-change-hook)))) ;;;###autoload (defun exwm-workspace-add (&optional index) @@ -1055,7 +1060,8 @@ INDEX must not exceed the current number of workspaces." 'fullscreen 'fullboth))) ;; Update EWMH properties. (exwm-workspace--update-ewmh-props) - (exwm-workspace-switch frame t)))) + (exwm-workspace-switch frame t) + (run-hooks 'exwm-workspace-list-change-hook)))) (defun exwm-workspace--remove-frame-as-workspace (frame) "Stop treating frame FRAME as a workspace." @@ -1096,7 +1102,8 @@ INDEX must not exceed the current number of workspaces." ;; Update EWMH properties. (exwm-workspace--update-ewmh-props) ;; Update switch history. - (setq exwm-workspace--switch-history-outdated t)))) + (setq exwm-workspace--switch-history-outdated t) + (run-hooks 'exwm-workspace-list-change-hook)))) (defun exwm-workspace--update-ewmh-props () "Update EWMH properties to match the workspace list." -- cgit 1.4.1 From b51f3e65f15385da70b659ef093230913429572b Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 19 Jul 2016 10:36:14 +0800 Subject: Mention dynamic workspace in various places * README.md: * exwm.el: Update README and comments. * exwm-core.el (exwm-mode-menu exwm-mode-map): Add menu entries for dynamic workspace feature. --- README.md | 19 +++++++++++-------- exwm-core.el | 8 ++++++-- exwm.el | 11 ++++++----- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 7f918bd297..103948c633 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,20 @@ # Emacs X Window Manager -EXWM (Emacs X Window Manager) is a full-featured tiling X window manager for -Emacs built on top of [XELB](https://github.com/ch11ng/xelb). +EXWM (Emacs X Window Manager) is a full-featured tiling X window manager +for Emacs built on top of [XELB](https://github.com/ch11ng/xelb). It features: + Fully keyboard-driven operations + Hybrid layout modes (tiling & stacking) -+ Workspace support ++ Dynamic workspace support + ICCCM/EWMH compliance + (Optional) RandR (multi-monitor) support -+ (Optional) system tray ++ (Optional) Built-in system tray -Please check the [User Guide](https://github.com/ch11ng/exwm/wiki) -for more details. +Please check out the +[screenshots](https://github.com/ch11ng/exwm/wiki/Screenshots) +to get an overview of what EXWM is capable of, +and the [user guide](https://github.com/ch11ng/exwm/wiki) +for a detailed explanation of its usage. -**Note**: If you install EXWM from source, you need to manually install XELB -(either from source or GNU ELPA). +**Note**: If you install EXWM from source, it's recommended to install +XELB also from source (otherwise install both from GNU ELPA). diff --git a/exwm-core.el b/exwm-core.el index fd26d2cdd3..fe46c8bcb9 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -193,8 +193,12 @@ "---" "*Workspace*" "---" - ["Move window to" exwm-workspace-move-window :keys "C-c C-m"] - ["Switch to buffer" exwm-workspace-switch-to-buffer] + ["Add workspace" exwm-workspace-add] + ["Delete current workspace" exwm-workspace-delete] + ["Move workspace to" exwm-workspace-move] + ["Swap workspaces" exwm-workspace-swap] + ["Move X window to" exwm-workspace-move-window :keys "C-c C-m"] + ["Move X window from" exwm-workspace-switch-to-buffer] ["Switch workspace" exwm-workspace-switch] ;; Place this entry at bottom to avoid selecting others by accident. ("Switch to" :filter diff --git a/exwm.el b/exwm.el index f7d027ebcf..b6bc164e54 100644 --- a/exwm.el +++ b/exwm.el @@ -28,14 +28,15 @@ ;; Overview ;; -------- -;; EXWM (Emacs X Window Manager) is a full-featured tiling X window manager for -;; Emacs built on top of XELB. It features: +;; EXWM (Emacs X Window Manager) is a full-featured tiling X window manager +;; for Emacs built on top of [XELB](https://github.com/ch11ng/xelb). +;; It features: ;; + Fully keyboard-driven operations ;; + Hybrid layout modes (tiling & stacking) -;; + Workspace support +;; + Dynamic workspace support ;; + ICCCM/EWMH compliance -;; ++ (Optional) RandR (multi-monitor) support -;; ++ (Optional) system tray +;; + (Optional) RandR (multi-monitor) support +;; + (Optional) Builtin system tray ;; Installation & configuration ;; ---------------------------- -- cgit 1.4.1 From 37e51e65ade14ded8d4a45cc725b4b66bab6b879 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 19 Jul 2016 19:16:19 +0800 Subject: Fix a workspace switch issue * exwm-workspace.el (exwm-workspace--switch-map-nth-prefix): Add more checking conditions. --- exwm-workspace.el | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 551ccbfba9..5d4d78f402 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -337,11 +337,17 @@ PREFIX-DIGITS is a list of the digits introduced so far." (* o x)) prefix-digits))) (n (+ pn d)) - (num-workspaces (exwm-workspace--count))) - (if (= (length prefix-digits) - (floor (log (1- num-workspaces) 10))) + prefix-length index-max index-length) + (if (or (= n 0) + (> n + (setq index-max (1- (exwm-workspace--count)))) + (>= (setq prefix-length (length prefix-digits)) + (setq index-length (floor (log index-max 10)))) + ;; Check if it's still possible to do a match. + (> (* n (expt 10 (- index-length prefix-length))) + index-max)) (exwm-workspace--switch-map-select-nth n) - ;; go ahead if there are enough digits to select any workspace. + ;; Go ahead if there are enough digits to select any workspace. (set-transient-map (let ((map (make-sparse-keymap)) (cmd `(lambda () -- cgit 1.4.1 From 624c72945a529c8221d12882063c7115af15a4c5 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 19 Jul 2016 19:18:00 +0800 Subject: Fix an bug for emacsclient * exwm-workspace.el (exwm-workspace--init): Always create one initial workspace by default. --- exwm-workspace.el | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 5d4d78f402..438ccdc94f 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1234,14 +1234,10 @@ applied to all subsequently created X frames." (setq exwm-workspace--timer (run-with-idle-timer 0 t #'exwm-workspace--on-echo-area-dirty)) (add-hook 'echo-area-clear-hook #'exwm-workspace--on-echo-area-clear) - ;; Recreate frames with the external minibuffer set. - (setq initial-workspaces - (mapcar - (lambda (_) - (make-frame '((window-system . x) - (internal-border-width . 0) - (client . nil)))) - initial-workspaces)) + ;; Recreate one frame with the external minibuffer set. + (setq initial-workspaces (list (make-frame '((window-system . x) + (internal-border-width . 0) + (client . nil))))) ;; The default behavior of `display-buffer' (indirectly called by ;; `minibuffer-completion-help') is not correct here. (cl-pushnew '(exwm-workspace--display-buffer) display-buffer-alist -- cgit 1.4.1 From 3909f65bae17af118ea7b1acf714289b35ea1b3d Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 19 Jul 2016 19:23:37 +0800 Subject: Add support for attachable/detachable minibuffer * exwm-workspace.el (exwm-workspace--update-minibuffer-height): Renamed from `exwm-workspace--update-minibuffer' to better reflect its usage. * exwm-input.el (exwm-input--update-focus): Directly put the floating X window at top since the minibuffer is no longer a sibling. * exwm-workspace.el (exwm-workspace--resize-minibuffer-frame): Calculate position in absolute coordinate; put minibuffer at bottom by default; resize the minibuffer frame with ConfigureWindow rather than `set-frame-width'. (exwm-workspace-switch): No need to reparent minibuffer any more. (exwm-workspace--on-ConfigureNotify): Calculate position in absolute coordinate. (exwm-workspace--show-minibuffer, exwm-workspace--hide-minibuffer): Show/Hide the minibuffer by adjusting the stacking order rather than mapping/unmapping. (exwm-workspace--init): Map the minibuffer frame container on creation. * exwm-workspace.el (exwm-workspace--minibuffer-attached-p): New function telling whether the minibuffer is attached. (exwm-workspace--attached-minibuffer-height): New variable storing the height of the attached minibuffer. (exwm-workspace-attach-minibuffer, exwm-workspace-detach-minibuffer): New functions for attaching/detaching the minibuffer. * exwm-workspace.el (exwm-workspace--show-minibuffer): Remove the workaround for minibuffer cursor (seems fixed). --- exwm-input.el | 23 ++------- exwm-workspace.el | 149 +++++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 113 insertions(+), 59 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index b9e47058f0..b0c9b2c129 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -125,24 +125,11 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (exwm-input--set-focus exwm--id) (when exwm--floating-frame ;; Adjust stacking orders of the floating container. - (if (exwm-workspace--minibuffer-own-frame-p) - ;; Put this floating X window just below the minibuffer. - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window exwm--container - :value-mask - (logior xcb:ConfigWindow:Sibling - xcb:ConfigWindow:StackMode) - :sibling (frame-parameter - exwm-workspace--minibuffer - 'exwm-container) - :stack-mode xcb:StackMode:Below)) - ;; Put this floating X window at top. - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window exwm--container - :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:Above))) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window exwm--container + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Above)) ;; This floating X window might be hide by `exwm-floating-hide'. (when (exwm-layout--iconic-state-p) (exwm-layout--set-state exwm--id diff --git a/exwm-workspace.el b/exwm-workspace.el index 438ccdc94f..060be3b47b 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -308,20 +308,32 @@ Value nil means to use the default position which is fixed at bottom, while 'exwm-container)) y width) (setq y (if (eq exwm-workspace-minibuffer-position 'top) - 0 - (- (aref workarea 3) - (frame-pixel-height exwm-workspace--minibuffer))) + (- (aref workarea 1) + exwm-workspace--attached-minibuffer-height) + ;; Reset the frame size. + (set-frame-height exwm-workspace--minibuffer 1) + (redisplay) ;FIXME. + (+ (aref workarea 1) (aref workarea 3) + (- (frame-pixel-height exwm-workspace--minibuffer)) + exwm-workspace--attached-minibuffer-height)) width (aref workarea 2)) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window container - :value-mask (logior xcb:ConfigWindow:Y + :value-mask (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y xcb:ConfigWindow:Width xcb:ConfigWindow:StackMode) + :x (aref workarea 0) :y y :width width - :stack-mode xcb:StackMode:Above)) - (set-frame-width exwm-workspace--minibuffer width nil t))) + :stack-mode xcb:StackMode:Below)) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter exwm-workspace--minibuffer + 'exwm-outer-id) + :value-mask xcb:ConfigWindow:Width + :width width)))) (defun exwm-workspace--switch-map-nth-prefix (&optional prefix-digits) "Allow selecting a workspace by number. @@ -428,17 +440,11 @@ The optional FORCE option is for internal use only." ;; Might be aborted by then. (when (active-minibuffer-window) (abort-recursive-edit))))) - (if (not (exwm-workspace--minibuffer-own-frame-p)) - (setq default-minibuffer-frame frame) - ;; Resize/reposition the minibuffer frame - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window - (frame-parameter exwm-workspace--minibuffer - 'exwm-container) - :parent (frame-parameter frame 'exwm-workspace) - :x 0 :y 0)) - (exwm-workspace--resize-minibuffer-frame)) + (if (exwm-workspace--minibuffer-own-frame-p) + ;; Resize the minibuffer frame. + (exwm-workspace--resize-minibuffer-frame) + ;; Set a default minibuffer frame. + (setq default-minibuffer-frame frame)) ;; Hide windows in other workspaces by preprending a space (unless exwm-workspace-show-all-buffers (dolist (i exwm--id-buffer-alist) @@ -778,8 +784,59 @@ INDEX must not exceed the current number of workspaces." (xcb:flush exwm--connection) frame)) -(defun exwm-workspace--update-minibuffer (&optional echo-area) - "Update the minibuffer frame." +(defsubst exwm-workspace--minibuffer-attached-p () + "Return non-nil if the minibuffer is attached. + +Please check `exwm-workspace--minibuffer-own-frame-p' first." + (assq (frame-parameter exwm-workspace--minibuffer 'exwm-container) + exwm-workspace--id-struts-alist)) + +(defvar exwm-workspace--attached-minibuffer-height 0 + "Height (in pixel) of the attached minibuffer. + +If the minibuffer is detached, this value is 0.") + +(defun exwm-workspace-attach-minibuffer () + "Attach the minibuffer so that it always shows." + (interactive) + (when (and (exwm-workspace--minibuffer-own-frame-p) + (not (exwm-workspace--minibuffer-attached-p))) + ;; Reset the frame size. + (set-frame-height exwm-workspace--minibuffer 1) + (redisplay) ;FIXME. + (setq exwm-workspace--attached-minibuffer-height + (frame-pixel-height exwm-workspace--minibuffer)) + (let ((container (frame-parameter exwm-workspace--minibuffer + 'exwm-container))) + (push (cons container + (if (eq exwm-workspace-minibuffer-position 'top) + (vector 0 0 exwm-workspace--attached-minibuffer-height 0) + (vector 0 0 0 exwm-workspace--attached-minibuffer-height))) + exwm-workspace--id-struts-alist) + (exwm-workspace--update-struts) + (exwm-workspace--update-workareas) + (dolist (f exwm-workspace--list) + (exwm-workspace--set-fullscreen f)) + (exwm-workspace--show-minibuffer)))) + +(defun exwm-workspace-detach-minibuffer () + "Detach the minibuffer so that it automatically hides." + (interactive) + (when (and (exwm-workspace--minibuffer-own-frame-p) + (exwm-workspace--minibuffer-attached-p)) + (setq exwm-workspace--attached-minibuffer-height 0) + (let ((container (frame-parameter exwm-workspace--minibuffer + 'exwm-container))) + (setq exwm-workspace--id-struts-alist + (assq-delete-all container exwm-workspace--id-struts-alist)) + (exwm-workspace--update-struts) + (exwm-workspace--update-workareas) + (dolist (f exwm-workspace--list) + (exwm-workspace--set-fullscreen f)) + (exwm-workspace--hide-minibuffer)))) + +(defun exwm-workspace--update-minibuffer-height (&optional echo-area) + "Update the minibuffer frame height." (let ((height (with-current-buffer (window-buffer (minibuffer-window exwm-workspace--minibuffer)) @@ -802,7 +859,7 @@ INDEX must not exceed the current number of workspaces." (defun exwm-workspace--on-ConfigureNotify (data _synthetic) "Adjust the container to fit the minibuffer frame." (let ((obj (make-instance 'xcb:ConfigureNotify)) - value-mask y) + workarea y) (xcb:unmarshal obj data) (with-slots (window height) obj (when (eq (frame-parameter exwm-workspace--minibuffer 'exwm-outer-id) @@ -818,19 +875,19 @@ INDEX must not exceed the current number of workspaces." :window window :value-mask xcb:ConfigWindow:Height :height height))) - (if (eq exwm-workspace-minibuffer-position 'top) - (setq value-mask xcb:ConfigWindow:Height - y 0) - (setq value-mask (logior xcb:ConfigWindow:Y xcb:ConfigWindow:Height) - y (- (aref (elt exwm-workspace--workareas - exwm-workspace-current-index) - 3) - height))) + (setq workarea (elt exwm-workspace--workareas + exwm-workspace-current-index) + y (if (eq exwm-workspace-minibuffer-position 'top) + (- (aref workarea 1) + exwm-workspace--attached-minibuffer-height) + (+ (aref workarea 1) (aref workarea 3) (- height) + exwm-workspace--attached-minibuffer-height))) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window (frame-parameter exwm-workspace--minibuffer 'exwm-container) - :value-mask value-mask + :value-mask (logior xcb:ConfigWindow:Y + xcb:ConfigWindow:Height) :y y :height height)) (xcb:flush exwm--connection))))) @@ -851,26 +908,31 @@ INDEX must not exceed the current number of workspaces." (setq exwm-workspace--display-echo-area-timer nil)) ;; Show the minibuffer frame. (xcb:+request exwm--connection - (make-instance 'xcb:MapWindow + (make-instance 'xcb:ConfigureWindow :window (frame-parameter exwm-workspace--minibuffer - 'exwm-container))) + 'exwm-container) + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Above)) (xcb:flush exwm--connection) ;; Unfortunately we need the following lines to workaround a cursor ;; flickering issue for line-mode floating X windows. They just make the ;; minibuffer appear to be focused. - (with-current-buffer (window-buffer (minibuffer-window - exwm-workspace--minibuffer)) - (setq cursor-in-non-selected-windows - (frame-parameter exwm-workspace--minibuffer 'cursor-type)))) - + ;; (FIXED?) + ;; (with-current-buffer (window-buffer (minibuffer-window + ;; exwm-workspace--minibuffer)) + ;; (setq cursor-in-non-selected-windows + ;; (frame-parameter exwm-workspace--minibuffer 'cursor-type))) + ) (defun exwm-workspace--hide-minibuffer () "Hide the minibuffer frame." ;; Hide the minibuffer frame. (xcb:+request exwm--connection - (make-instance 'xcb:UnmapWindow + (make-instance 'xcb:ConfigureWindow :window (frame-parameter exwm-workspace--minibuffer - 'exwm-container))) + 'exwm-container) + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Below)) (xcb:flush exwm--connection)) (defun exwm-workspace--on-minibuffer-setup () @@ -878,7 +940,7 @@ INDEX must not exceed the current number of workspaces." (when (and (= 1 (minibuffer-depth)) ;; Exclude non-graphical frames. (frame-parameter nil 'exwm-outer-id)) - (add-hook 'post-command-hook #'exwm-workspace--update-minibuffer) + (add-hook 'post-command-hook #'exwm-workspace--update-minibuffer-height) (exwm-workspace--show-minibuffer) ;; Set input focus on the Emacs frame (x-focus-frame (window-frame (minibuffer-selected-window))))) @@ -888,7 +950,7 @@ INDEX must not exceed the current number of workspaces." (when (and (= 1 (minibuffer-depth)) ;; Exclude non-graphical frames. (frame-parameter nil 'exwm-outer-id)) - (remove-hook 'post-command-hook #'exwm-workspace--update-minibuffer) + (remove-hook 'post-command-hook #'exwm-workspace--update-minibuffer-height) (exwm-workspace--hide-minibuffer))) (defvar exwm-input--during-command) @@ -900,7 +962,7 @@ INDEX must not exceed the current number of workspaces." (frame-parameter nil 'exwm-outer-id) (or (current-message) cursor-in-echo-area)) - (exwm-workspace--update-minibuffer t) + (exwm-workspace--update-minibuffer-height t) (exwm-workspace--show-minibuffer) (unless (or (not exwm-workspace-display-echo-area-timeout) exwm-input--during-command ;e.g. read-event @@ -1217,9 +1279,14 @@ applied to all subsequently created X frames." (make-instance 'xcb:ewmh:set-_NET_WM_NAME :window container :data "Minibuffer container"))) + ;; Reparent the minibuffer frame to the container. (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow :window outer-id :parent container :x 0 :y 0)) + ;; Map the container. + (xcb:+request exwm--connection + (make-instance 'xcb:MapWindow + :window container)) ;; Attach event listener for monitoring the frame (xcb:+request exwm--connection (make-instance 'xcb:ChangeWindowAttributes -- cgit 1.4.1 From 6571bb5761241be16c49f4b9af4314851b36eb18 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 21 Jul 2016 12:41:51 +0800 Subject: Minor cleanups ; Eliminate compile warnings. ; Rename frame parameter 'exwm--urgency' to 'exwm-urgency'. ; Simplify expressions. --- exwm-core.el | 3 +-- exwm-floating.el | 10 +++++----- exwm-input.el | 2 +- exwm-manage.el | 4 ++-- exwm-workspace.el | 27 ++++++++++----------------- exwm.el | 13 ++++++------- 6 files changed, 25 insertions(+), 34 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index fe46c8bcb9..f0508c6029 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -221,8 +221,7 @@ (setq mode-name '(:eval (propertize "EXWM" 'face (when (cl-some (lambda (i) - (frame-parameter i - 'exwm--urgency)) + (frame-parameter i 'exwm-urgency)) exwm-workspace--list) 'font-lock-warning-face)))) ;; Change major-mode is not allowed diff --git a/exwm-floating.el b/exwm-floating.el index 27a50169a1..7931c661c7 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -67,6 +67,7 @@ xcb:Atom:_NET_WM_ACTION_CLOSE))))) (defvar exwm-workspace--current) +(defvar exwm-workspace--struts) (declare-function exwm-layout--refresh "exwm-layout.el" ()) (declare-function exwm-layout--show "exwm-layout.el" (id &optional window)) @@ -96,8 +97,7 @@ (height . ,window-min-height) (unsplittable . t))))) ;and fix the size later (outer-id (string-to-number (frame-parameter frame 'outer-window-id))) - (container (with-current-buffer (exwm--id->buffer id) - exwm--container)) + (container (buffer-local-value 'exwm--container (exwm--id->buffer id))) (frame-container (xcb:generate-id exwm--connection)) (window (frame-first-window frame)) ;and it's the only window (x (slot-value exwm--geometry 'x)) @@ -524,8 +524,8 @@ ;; Unmanaged. (eq major-mode 'exwm-mode)) (let ((edges (window-inside-absolute-pixel-edges (frame-selected-window))) - (id (with-current-buffer (window-buffer (frame-selected-window)) - exwm--id))) + (id (buffer-local-value 'exwm--id + (window-buffer (frame-selected-window))))) (xcb:+request exwm--connection (make-instance 'xcb:SendEvent :propagate 0 :destination id @@ -570,7 +570,7 @@ (setq container-or-id (if (bufferp buffer-or-id) ;; Managed. - (with-current-buffer buffer-or-id exwm--container) + (buffer-local-value 'exwm--container buffer-or-id) ;; Unmanaged. buffer-or-id)) (xcb:+request exwm--connection diff --git a/exwm-input.el b/exwm-input.el index b0c9b2c129..c1028ac8e3 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -116,7 +116,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (if (not (eq exwm--frame exwm-workspace--current)) ;; Do not focus X windows on other workspace (progn - (set-frame-parameter exwm--frame 'exwm--urgency t) + (set-frame-parameter exwm--frame 'exwm-urgency t) (setq exwm-workspace--switch-history-outdated t) (force-mode-line-update) ;; The application may have changed its input focus diff --git a/exwm-manage.el b/exwm-manage.el index 7a3a18b88e..260571b6c0 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -420,8 +420,8 @@ manager is shutting down." (xcb:+request exwm--connection (make-instance 'xcb:UnmapWindow :window exwm--container)) (xcb:flush exwm--connection) - (run-with-timer exwm-manage-ping-timeout nil - `(lambda () (exwm-manage--kill-client ,id))) + (run-with-timer exwm-manage-ping-timeout nil #'exwm-manage--kill-client + id) ;; Wait for DestroyNotify event. (throw 'return nil)) ;; Try to determine if the X window is dead with _NET_WM_PING. diff --git a/exwm-workspace.el b/exwm-workspace.el index 060be3b47b..80a7cbd215 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -128,7 +128,7 @@ NIL if FRAME is not a workspace" (int-to-string j) 'face (cond ((frame-parameter (elt exwm-workspace--list j) - 'exwm--urgency) + 'exwm-urgency) '(:foreground "orange")) ((aref not-empty j) '(:foreground "green")) (t nil))))) @@ -300,6 +300,11 @@ Value nil means to use the default position which is fixed at bottom, while (when exwm-workspace--fullscreen-frame-count (cl-incf exwm-workspace--fullscreen-frame-count))) +(defvar exwm-workspace--attached-minibuffer-height 0 + "Height (in pixel) of the attached minibuffer. + +If the minibuffer is detached, this value is 0.") + (defun exwm-workspace--resize-minibuffer-frame () "Resize minibuffer (and its container) to fit the size of workspace." (cl-assert (exwm-workspace--minibuffer-own-frame-p)) @@ -429,8 +434,7 @@ The optional FORCE option is for internal use only." exwm-workspace-current-index index) (unless (exwm-workspace--workspace-p (selected-frame)) ;; Save the floating frame window selected on the previous workspace. - (set-frame-parameter (with-current-buffer (window-buffer) - exwm--frame) + (set-frame-parameter (buffer-local-value 'exwm--frame (window-buffer)) 'exwm-selected-window (selected-window))) (select-window window) (set-frame-parameter frame 'exwm-selected-window nil) @@ -455,7 +459,7 @@ The optional FORCE option is for internal use only." name (concat " " name))))))) ;; Update demands attention flag - (set-frame-parameter frame 'exwm--urgency nil) + (set-frame-parameter frame 'exwm-urgency nil) ;; Update switch workspace history (setq exwm-workspace--switch-history-outdated t) ;; Set _NET_CURRENT_DESKTOP @@ -791,11 +795,6 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (assq (frame-parameter exwm-workspace--minibuffer 'exwm-container) exwm-workspace--id-struts-alist)) -(defvar exwm-workspace--attached-minibuffer-height 0 - "Height (in pixel) of the attached minibuffer. - -If the minibuffer is detached, this value is 0.") - (defun exwm-workspace-attach-minibuffer () "Attach the minibuffer so that it always shows." (interactive) @@ -1126,10 +1125,8 @@ If the minibuffer is detached, this value is 0.") (make-instance 'xcb:MapWindow :window workspace))) (xcb:flush exwm--connection) ;; Delay making the workspace fullscreen until Emacs becomes idle - (run-with-idle-timer 0 nil - `(lambda () - (set-frame-parameter ,frame - 'fullscreen 'fullboth))) + (run-with-idle-timer 0 nil #'set-frame-parameter + frame 'fullscreen 'fullboth) ;; Update EWMH properties. (exwm-workspace--update-ewmh-props) (exwm-workspace-switch frame t) @@ -1148,10 +1145,6 @@ If the minibuffer is detached, this value is 0.") (exwm--log "Removing frame `%s' as workspace" frame) (let* ((index (exwm-workspace--position frame)) (lastp (= index (1- (exwm-workspace--count)))) - ;; As we are removing this workspace, the one on its left is its - ;; natural substitutes... except when this is already the last one - ;; and there is none on its left. - ;; FIXME (ch11ng): Which direction is "left"? (nextw (elt exwm-workspace--list (+ index (if lastp -1 +1))))) ;; Need to remove the workspace from the list in order for ;; the correct calculation of indexes. diff --git a/exwm.el b/exwm.el index b6bc164e54..f7df651cde 100644 --- a/exwm.el +++ b/exwm.el @@ -214,8 +214,8 @@ (setq exwm--hints-urgency t)))) (when (and exwm--hints-urgency (not (eq exwm--frame exwm-workspace--current))) - (unless (frame-parameter exwm--frame 'exwm--urgency) - (set-frame-parameter exwm--frame 'exwm--urgency t) + (unless (frame-parameter exwm--frame 'exwm-urgency) + (set-frame-parameter exwm--frame 'exwm-urgency t) (setq exwm-workspace--switch-history-outdated t)))))))) (defun exwm--update-protocols (id &optional force) @@ -361,8 +361,8 @@ ((= type xcb:Atom:_NET_WM_MOVERESIZE) (let ((direction (elt data 2)) (buffer (exwm--id->buffer id))) - (unless (and buffer (with-current-buffer buffer - (not exwm--floating-frame))) + (unless (and buffer + (not (buffer-local-value 'exwm--floating-frame buffer))) (cond ((= direction xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_KEYBOARD) ;; FIXME @@ -379,8 +379,7 @@ (let ((buffer (exwm--id->buffer id)) left right top btm) (if (or (not buffer) - (with-current-buffer buffer - (not exwm--floating-frame))) + (not (buffer-local-value 'exwm--floating-frame buffer))) (setq left 0 right 0 top 0 btm 0) (setq left exwm-floating-border-width right exwm-floating-border-width @@ -451,7 +450,7 @@ (when (memq xcb:Atom:_NET_WM_STATE_DEMANDS_ATTENTION props) (when (= action xcb:ewmh:_NET_WM_STATE_ADD) (unless (eq exwm--frame exwm-workspace--current) - (set-frame-parameter exwm--frame 'exwm--urgency t) + (set-frame-parameter exwm--frame 'exwm-urgency t) (setq exwm-workspace--switch-history-outdated t))) ;; xcb:ewmh:_NET_WM_STATE_REMOVE? ;; xcb:ewmh:_NET_WM_STATE_TOGGLE? -- cgit 1.4.1 From f48b8eafb0b8f8afab0d42459a29f605cf452daa Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 21 Jul 2016 12:44:05 +0800 Subject: Minor fixes * exwm-core.el (exwm-mode-menu, exwm-mode-map): Add workspace attach/detach commands. * exwm-workspace.el (exwm-workspace--add-frame-as-workspace): Cleanup containers. * exwm-workspace.el (exwm-workspace--update-ewmh-props): Create the frame in size 1x1 (Lucid build does no support zero sized frames). * exwm-workspace.el (exwm-workspace--post-init): Reset the 'fullscreen' frame parameter for emacsclient. --- exwm-core.el | 6 ++++++ exwm-workspace.el | 22 ++++++++++++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index f0508c6029..09e16582be 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -199,6 +199,12 @@ ["Swap workspaces" exwm-workspace-swap] ["Move X window to" exwm-workspace-move-window :keys "C-c C-m"] ["Move X window from" exwm-workspace-switch-to-buffer] + ["Attach minibuffer" exwm-workspace-attach-minibuffer + (and (exwm-workspace--minibuffer-own-frame-p) + (not (exwm-workspace--minibuffer-attached-p)))] + ["Detach minibuffer" exwm-workspace-detach-minibuffer + (and (exwm-workspace--minibuffer-own-frame-p) + (exwm-workspace--minibuffer-attached-p))] ["Switch workspace" exwm-workspace-switch] ;; Place this entry at bottom to avoid selecting others by accident. ("Switch to" :filter diff --git a/exwm-workspace.el b/exwm-workspace.el index 80a7cbd215..e33144ec6a 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1164,6 +1164,17 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." ;; If the current workspace is deleted, switch to next one. (when (eq frame exwm-workspace--current) (exwm-workspace-switch nextw))) + ;; Reparent out the frame. + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window (frame-parameter frame 'exwm-outer-id) + :parent exwm--root + :x 0 + :y 0)) + ;; Destroy the containers. + (xcb:+request exwm--connection + (make-instance 'xcb:DestroyWindow + :window (frame-parameter frame 'exwm-workspace))) ;; Update EWMH properties. (exwm-workspace--update-ewmh-props) ;; Update switch history. @@ -1234,7 +1245,7 @@ applied to all subsequently created X frames." (setq exwm-workspace--minibuffer (make-frame '((window-system . x) (minibuffer . only) (left . 10000) (right . 10000) - (width . 0) (height . 0) + (width . 1) (height . 1) (internal-border-width . 0) (client . nil)))) ;; Remove/hide existing frames. @@ -1351,9 +1362,12 @@ applied to all subsequently created X frames." (defun exwm-workspace--post-init () "The second stage in the initialization of the workspace module." - ;; Make the workspaces fullscreen. - (dolist (i exwm-workspace--list) - (set-frame-parameter i 'fullscreen 'fullboth)) + (when exwm-workspace--client + ;; Reset the 'fullscreen' frame parameter to make emacsclinet frames + ;; fullscreen (even without the RandR module enabled). + (dolist (i exwm-workspace--list) + (set-frame-parameter i 'fullscreen nil) + (set-frame-parameter i 'fullscreen 'fullboth))) ;; Wait until all workspace frames are resized. (with-timeout (1) (while (< exwm-workspace--fullscreen-frame-count (exwm-workspace--count)) -- cgit 1.4.1 From 0c114d97b78f806ebe2904c8f55f573fd7c879e7 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 21 Jul 2016 12:48:12 +0800 Subject: Fix workspace creation and deletion * exwm-workspace.el (exwm-workspace-switch) (exwm-workspace-switch-create): Move support for creating missing workspaces from the former to the latter.. (exwm-workspace-switch-create-limit): New variable limiting the number of new workspaces allowed to create each time. * exwm-workspace.el (exwm-workspace--prompt-add) (exwm-workspace--prompt-delete): New commands for adding and deleting workspaces from the `read-from-minibuffer' prompt. (exwm-workspace--prompt-add-allowed) (exwm-workspace--prompt-delete-allowed): New variables telling whether the above two commands are allowed to run. (exwm-workspace--switch-map): Change "+" / "-" to use the new commands. * exwm-workspace.el (exwm-workspace-switch, exwm-workspace-swap) (exwm-workspace-move-window): Use this new feature. * exwm-workspace.el (exwm-workspace-add, exwm-workspace-delete): Since they are not used by the keymap any more, drop the use of idle timer. * exwm-workspace.el (exwm-workspace--create-silently): New variable indicating whether new workspaces should be created in the background. (exwm-workspace--add-frame-as-workspace): Support creating new workspaces in the background. * exwm-workspace.el (exwm-workspace--on-ConfigureNotify): Update workareas if it's not up to date. * exwm-randr.el (exwm-randr--refresh): Raise the standalone minibuffer when refreshed. * exwm-config.el (exwm-config-default): Add `exwm-workspace-number' and `exwm-workspace-switch-create'. --- exwm-config.el | 6 ++- exwm-randr.el | 5 +++ exwm-workspace.el | 128 ++++++++++++++++++++++++++++++++++++------------------ 3 files changed, 96 insertions(+), 43 deletions(-) diff --git a/exwm-config.el b/exwm-config.el index e1e5010d3d..8c54607006 100644 --- a/exwm-config.el +++ b/exwm-config.el @@ -30,6 +30,8 @@ (defun exwm-config-default () "Default configuration of EXWM." + ;; Set the initial workspace number. + (setq exwm-workspace-number 4) ;; Make class name the buffer name (add-hook 'exwm-update-class-hook (lambda () @@ -41,7 +43,9 @@ ;; 's-N': Switch to certain workspace (dotimes (i 10) (exwm-input-set-key (kbd (format "s-%d" i)) - `(lambda () (interactive) (exwm-workspace-switch ,i)))) + `(lambda () + (interactive) + (exwm-workspace-switch-create ,i)))) ;; 's-&': Launch application (exwm-input-set-key (kbd "s-&") (lambda (command) diff --git a/exwm-randr.el b/exwm-randr.el index 709469a44d..4ce1752d9e 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -60,6 +60,7 @@ (declare-function exwm-workspace--count "exwm-workspace.el") (declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame)) (declare-function exwm-workspace--update-workareas "exwm-workspace.el" ()) +(declare-function exwm-workspace--show-minibuffer "exwm-workspace.el" ()) (declare-function exwm-workspace--set-desktop-geometry "exwm-workspace.el" ()) (defun exwm-randr--refresh () @@ -111,6 +112,10 @@ ;; Resize workspace. (dolist (f exwm-workspace--list) (exwm-workspace--set-fullscreen f)) + ;; Raise the minibuffer if it's active. + (when (and (active-minibuffer-window) + (exwm-workspace--minibuffer-own-frame-p)) + (exwm-workspace--show-minibuffer)) ;; Set _NET_DESKTOP_GEOMETRY. (exwm-workspace--set-desktop-geometry) (xcb:flush exwm--connection) diff --git a/exwm-workspace.el b/exwm-workspace.el index e33144ec6a..0d636398c4 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -63,8 +63,8 @@ NIL if FRAME is not a workspace" (defvar exwm-workspace--switch-map (let ((map (make-sparse-keymap))) (define-key map [t] (lambda () (interactive))) - (define-key map "+" #'exwm-workspace-add) - (define-key map "-" #'exwm-workspace-delete) + (define-key map "+" #'exwm-workspace--prompt-add) + (define-key map "-" #'exwm-workspace--prompt-delete) (dotimes (i 10) (define-key map (int-to-string i) #'exwm-workspace--switch-map-nth-prefix)) @@ -105,6 +105,38 @@ NIL if FRAME is not a workspace" :test #'equal))) (elt exwm-workspace--list workspace-idx))) +(defvar exwm-workspace--prompt-add-allowed nil + "Non-nil to allow adding workspace from the prompt.") +(defvar exwm-workspace--prompt-delete-allowed nil + "Non-nil to allow deleting workspace from the prompt") +(defvar exwm-workspace--create-silently nil + "When non-nil workspaces are created in the background (not switched to).") + +(defun exwm-workspace--prompt-add () + "Add workspace from the prompt." + (interactive) + (when exwm-workspace--prompt-add-allowed + (let ((exwm-workspace--create-silently t)) + (make-frame)) + (exwm-workspace--update-switch-history) + (goto-history-element minibuffer-history-position))) + +(defun exwm-workspace--prompt-delete () + "Delete workspace from the prompt." + (interactive) + (when (and exwm-workspace--prompt-delete-allowed + (< 1 (exwm-workspace--count))) + (let ((frame (elt exwm-workspace--list (1- minibuffer-history-position)))) + (if (eq frame exwm-workspace--current) + ;; Abort the recursive minibuffer if deleting the current workspace. + (progn + (run-with-idle-timer 0 nil #'delete-frame frame) + (abort-recursive-edit)) + (delete-frame frame) + (exwm-workspace--update-switch-history) + (goto-history-element (min minibuffer-history-position + (exwm-workspace--count))))))) + (defun exwm-workspace--update-switch-history () "Update the history for switching workspace to reflect the latest status." (when exwm-workspace--switch-history-outdated @@ -390,25 +422,16 @@ PREFIX-DIGITS is a list of the digits introduced so far." "Normal hook run after switching workspace.") ;;;###autoload -(cl-defun exwm-workspace-switch (frame-or-index &optional force) +(defun exwm-workspace-switch (frame-or-index &optional force) "Switch to workspace INDEX. Query for FRAME-OR-INDEX if it's not specified. The optional FORCE option is for internal use only." (interactive (list (unless (and (eq major-mode 'exwm-mode) exwm--fullscreen) ;it's invisible - (exwm-workspace--prompt-for-workspace "Workspace [+/-]: ")))) - ;; Try to create workspace(s) when INDEX is out-of-range. - (when (and (integerp frame-or-index) - (>= frame-or-index (exwm-workspace--count))) - (run-with-idle-timer 0 nil - (lambda (times) - (dotimes (_ times) - (make-frame))) - ;; Create no more than 9 workspaces. - (min 9 - (1+ (- frame-or-index (exwm-workspace--count))))) - (cl-return-from exwm-workspace-switch)) + (let ((exwm-workspace--prompt-add-allowed t) + (exwm-workspace--prompt-delete-allowed t)) + (exwm-workspace--prompt-for-workspace "Switch to [+/-]: "))))) (let* ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index)) (index (exwm-workspace--position frame)) (workspace (frame-parameter frame 'exwm-workspace)) @@ -469,6 +492,23 @@ The optional FORCE option is for internal use only." (xcb:flush exwm--connection)) (run-hooks 'exwm-workspace-switch-hook))) +(defvar exwm-workspace-switch-create-limit 10 + "Number of workspaces `exwm-workspace-switch-create' allowed to create +each time.") + +(defun exwm-workspace-switch-create (frame-or-index) + "Switch to workspace FRAME-OR-INDEX, creating it if it does not exist yet." + (interactive) + (if (or (framep frame-or-index) + (< frame-or-index (exwm-workspace--count))) + (exwm-workspace-switch frame-or-index) + (let ((exwm-workspace--create-silently t)) + (dotimes (_ (min exwm-workspace-switch-create-limit + (1+ (- frame-or-index + (exwm-workspace--count))))) + (make-frame))) + (exwm-workspace-switch (car (last exwm-workspace--list))))) + (defvar exwm-workspace-list-change-hook nil "Normal hook run when the workspace list is changed (workspace added, deleted, moved, etc).") @@ -478,10 +518,14 @@ deleted, moved, etc).") "Interchange position of WORKSPACE1 with that of WORKSPACE2." (interactive (unless (and (eq major-mode 'exwm-mode) exwm--fullscreen) ;it's invisible - (let* ((w1 (exwm-workspace--prompt-for-workspace "Pick a workspace: ")) - (w2 (exwm-workspace--prompt-for-workspace + (let (w1 w2) + (let ((exwm-workspace--prompt-add-allowed t) + (exwm-workspace--prompt-delete-allowed t)) + (setq w1 (exwm-workspace--prompt-for-workspace + "Pick a workspace [+/-]: "))) + (setq w2 (exwm-workspace--prompt-for-workspace (format "Swap workspace %d with: " - (exwm-workspace--position w1))))) + (exwm-workspace--position w1)))) (list w1 w2)))) (let ((pos1 (exwm-workspace--position workspace1)) (pos2 (exwm-workspace--position workspace2))) @@ -544,31 +588,20 @@ before it." INDEX must not exceed the current number of workspaces." (interactive) - (run-with-idle-timer - 0 nil - (lambda (index) - (if (and index - ;; No need to move if it's the last one. - (< index (exwm-workspace--count))) - (exwm-workspace-move (make-frame) index) - (make-frame))) - index) - (when (active-minibuffer-window) - (abort-recursive-edit))) + (if (and index + ;; No need to move if it's the last one. + (< index (exwm-workspace--count))) + (exwm-workspace-move (make-frame) index) + (make-frame))) ;;;###autoload (defun exwm-workspace-delete (&optional frame-or-index) "Delete the workspace FRAME-OR-INDEX." (interactive) - (run-with-idle-timer 0 nil #'delete-frame - ;; We should not simply delete the selected frame - ;; since it can be e.g. a floating frame. - (if frame-or-index - (exwm-workspace--workspace-from-frame-or-index - frame-or-index) - exwm-workspace--current)) - (when (active-minibuffer-window) - (abort-recursive-edit))) + (delete-frame + (if frame-or-index + (exwm-workspace--workspace-from-frame-or-index frame-or-index) + exwm-workspace--current))) (defun exwm-workspace--on-focus-in () "Handle unexpected frame switch." @@ -601,7 +634,9 @@ INDEX must not exceed the current number of workspaces." (defun exwm-workspace-move-window (frame-or-index &optional id) "Move window ID to workspace FRAME-OR-INDEX." (interactive (list - (exwm-workspace--prompt-for-workspace "Move to: "))) + (let ((exwm-workspace--prompt-add-allowed t) + (exwm-workspace--prompt-delete-allowed t)) + (exwm-workspace--prompt-for-workspace "Move to [+/-]: ")))) (let ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index))) (unless id (setq id (exwm--buffer->id (window-buffer)))) (with-current-buffer (exwm--id->buffer id) @@ -874,6 +909,9 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." :window window :value-mask xcb:ConfigWindow:Height :height height))) + (when (/= (exwm-workspace--count) (length exwm-workspace--workareas)) + ;; There is a chance the workareas are not updated timely. + (exwm-workspace--update-workareas)) (setq workarea (elt exwm-workspace--workareas exwm-workspace-current-index) y (if (eq exwm-workspace-minibuffer-position 'top) @@ -1064,8 +1102,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (exwm--log "Frame `%s' is floating" frame)) (t (exwm--log "Adding frame `%s' as workspace" frame) - (setq exwm-workspace--list (nconc exwm-workspace--list (list frame)) - exwm-workspace--current frame) + (setq exwm-workspace--list (nconc exwm-workspace--list (list frame))) (let ((outer-id (string-to-number (frame-parameter frame 'outer-window-id))) (container (xcb:generate-id exwm--connection)) @@ -1093,6 +1130,11 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." xcb:CW:EventMask) :override-redirect 1 :event-mask xcb:EventMask:SubstructureRedirect)) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window workspace + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Below)) (xcb:+request exwm--connection (make-instance 'xcb:CreateWindow :depth 0 :wid container :parent workspace @@ -1129,7 +1171,9 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." frame 'fullscreen 'fullboth) ;; Update EWMH properties. (exwm-workspace--update-ewmh-props) - (exwm-workspace-switch frame t) + (if exwm-workspace--create-silently + (setq exwm-workspace--switch-history-outdated t) + (exwm-workspace-switch frame t)) (run-hooks 'exwm-workspace-list-change-hook)))) (defun exwm-workspace--remove-frame-as-workspace (frame) -- cgit 1.4.1 From 76ced38ae43a9192ed97ca35dd1cc83c62b2a073 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 21 Jul 2016 12:51:37 +0800 Subject: Fix input focus issues revealed by recent commits * exwm-input.el (exwm-input--update-focus-window) (exwm-input--on-buffer-list-update, exwm-input--update-focus-interval) (exwm-input--update-focus-lock, exwm-input--update-focus-defer-timer) (exwm-input--update-focus-timer, exwm-input--update-focus-defer) (defun exwm-input--update-focus): Rework the input focus update mechanism, mainly to overcome the input focus update contention. * exwm-input.el (defun exwm-input--update-focus): Use `select-window' instead of `exwm-workspace-switch'; calling the latter is too expensive. * exwm-layout.el (exwm-layout--on-minibuffer-setup): Drop a unnecessary line. * exwm-workspace.el (exwm-workspace-switch): Set input focus to the new workspace frame. --- exwm-input.el | 79 ++++++++++++++++++++++++++++++++++++++----------------- exwm-layout.el | 4 +-- exwm-workspace.el | 1 + 3 files changed, 57 insertions(+), 27 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index c1028ac8e3..b79a55127d 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -79,21 +79,51 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (exwm-input--set-active-window id) (xcb:flush exwm--connection)))) -(defvar exwm-input--focus-window nil "The (Emacs) window to be focused.") -(defvar exwm-input--timer nil "Currently running timer.") +(defvar exwm-input--update-focus-window nil "The (Emacs) window to be focused. + +This value should always be overwritten.") (defun exwm-input--on-buffer-list-update () - "Run in buffer-list-update-hook to track input focus." - (let ((frame (selected-frame)) - (window (selected-window)) - (buffer (current-buffer))) - (when (and (not (minibufferp buffer)) - (frame-parameter frame 'exwm-outer-id) ;e.g. emacsclient frame - (eq buffer (window-buffer))) ;e.g. `with-temp-buffer' - (when exwm-input--timer (cancel-timer exwm-input--timer)) - (setq exwm-input--focus-window window - exwm-input--timer - (run-with-idle-timer 0.01 nil #'exwm-input--update-focus))))) + "Run in `buffer-list-update-hook' to track input focus." + (when (and (not (minibufferp)) ;Do not set input focus on minibuffer window. + (eq (current-buffer) (window-buffer)) ;e.g. `with-temp-buffer'. + (frame-parameter nil 'exwm-outer-id)) ;e.g. emacsclient frame. + (setq exwm-input--update-focus-window (selected-window)) + (exwm-input--update-focus-defer))) + +;; Input focus update requests should be accumulated for a short time +;; interval so that only the last one need to be processed. This not +;; improves the overall performance, but avoids the problem of input +;; focus loop, which is a result of the interaction with Emacs frames. +;; +;; FIXME: The time interval is hard to decide and perhaps machine-dependent. +;; A value too small can cause redundant updates of input focus, +;; and even worse, dead loops. OTOH a large value would bring +;; laggy experience. +(defconst exwm-input--update-focus-interval 0.01 + "Time interval (in seconds) for accumulating input focus update requests.") + +(defvar exwm-input--update-focus-lock nil + "Lock for solving input focus update contention.") +(defvar exwm-input--update-focus-defer-timer nil "Timer for polling the lock.") +(defvar exwm-input--update-focus-timer nil + "Timer for deferring the update of input focus.") + +(defun exwm-input--update-focus-defer () + "Defer updating input focus." + (when exwm-input--update-focus-defer-timer + (cancel-timer exwm-input--update-focus-defer-timer)) + (if exwm-input--update-focus-lock + (setq exwm-input--update-focus-defer-timer + (run-with-idle-timer 0 nil + #'exwm-input--update-focus-defer)) + (setq exwm-input--update-focus-defer-timer nil) + (when exwm-input--update-focus-timer + (cancel-timer exwm-input--update-focus-timer)) + (setq exwm-input--update-focus-timer + (run-with-idle-timer exwm-input--update-focus-interval nil + #'exwm-input--update-focus + exwm-input--update-focus-window)))) (defvar exwm-workspace--current) (defvar exwm-workspace--switch-history-outdated) @@ -106,21 +136,23 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (declare-function exwm-workspace-switch "exwm-workspace.el" (frame-or-index &optional force)) -(defun exwm-input--update-focus () +(defun exwm-input--update-focus (window) "Update input focus." - (when (and (window-live-p exwm-input--focus-window) + (setq exwm-input--update-focus-lock t) + (when (and (window-live-p window) ;; Do not update input focus when there's an active minibuffer. (not (active-minibuffer-window))) - (with-current-buffer (window-buffer exwm-input--focus-window) + (with-current-buffer (window-buffer window) (if (eq major-mode 'exwm-mode) (if (not (eq exwm--frame exwm-workspace--current)) - ;; Do not focus X windows on other workspace + ;; Do not focus X windows on other workspace. (progn (set-frame-parameter exwm--frame 'exwm-urgency t) (setq exwm-workspace--switch-history-outdated t) (force-mode-line-update) ;; The application may have changed its input focus - (exwm-workspace-switch exwm-workspace--current t)) + (select-window + (frame-selected-window exwm-workspace--current))) (exwm--log "Set focus on #x%x" exwm--id) (exwm-input--set-focus exwm--id) (when exwm--floating-frame @@ -135,13 +167,12 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (exwm-layout--set-state exwm--id xcb:icccm:WM_STATE:NormalState)) (xcb:flush exwm--connection))) - (when (eq (selected-window) exwm-input--focus-window) - (exwm--log "Focus on %s" exwm-input--focus-window) - (select-frame-set-input-focus (window-frame exwm-input--focus-window) - t) + (when (eq (selected-window) window) + (exwm--log "Focus on %s" window) + (select-frame-set-input-focus (window-frame window) t) (exwm-input--set-active-window) - (xcb:flush exwm--connection))) - (setq exwm-input--focus-window nil)))) + (xcb:flush exwm--connection))))) + (setq exwm-input--update-focus-lock nil)) (defun exwm-input--set-active-window (&optional id) "Set _NET_ACTIVE_WINDOW." diff --git a/exwm-layout.el b/exwm-layout.el index 667e2faeca..905a1e3c99 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -381,9 +381,7 @@ selected by `other-buffer'." (run-with-idle-timer 0.01 nil ;FIXME (lambda () (when (< 1 (window-height (minibuffer-window))) - (exwm-layout--refresh)))) - ;; Set input focus on the Emacs frame - (x-focus-frame (window-frame (minibuffer-selected-window))))) + (exwm-layout--refresh)))))) (defun exwm-layout--on-echo-area-change (&optional dirty) "Run when message arrives or in `echo-area-clear-hook' to refresh layout." diff --git a/exwm-workspace.el b/exwm-workspace.el index 0d636398c4..2ff5e0ce04 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -460,6 +460,7 @@ The optional FORCE option is for internal use only." (set-frame-parameter (buffer-local-value 'exwm--frame (window-buffer)) 'exwm-selected-window (selected-window))) (select-window window) + (x-focus-frame frame) ;essential for transferring input focus (set-frame-parameter frame 'exwm-selected-window nil) ;; Close the (possible) active minibuffer (when (active-minibuffer-window) -- cgit 1.4.1 From 86777c54f341e3a0921c0970a4ed3fb99753e66d Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 21 Jul 2016 13:02:07 +0800 Subject: Improve the performance of workspace creation * exwm-workspace.el (exwm-workspace--add-frame-as-workspace): Do not run `exwm-workspace-list-change-hook' when create workspace in the background. (exwm-workspace-switch-create): Run the hook once. (exwm-workspace--prompt-add): Run the hook. --- exwm-workspace.el | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 2ff5e0ce04..b3c2390936 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -110,14 +110,17 @@ NIL if FRAME is not a workspace" (defvar exwm-workspace--prompt-delete-allowed nil "Non-nil to allow deleting workspace from the prompt") (defvar exwm-workspace--create-silently nil - "When non-nil workspaces are created in the background (not switched to).") + "When non-nil workspaces are created in the background (not switched to). + +Please manually run the hook `exwm-workspace-list-change-hook' afterwards.") (defun exwm-workspace--prompt-add () "Add workspace from the prompt." (interactive) (when exwm-workspace--prompt-add-allowed (let ((exwm-workspace--create-silently t)) - (make-frame)) + (make-frame) + (run-hooks 'exwm-workspace-list-change-hook)) (exwm-workspace--update-switch-history) (goto-history-element minibuffer-history-position))) @@ -507,7 +510,8 @@ each time.") (dotimes (_ (min exwm-workspace-switch-create-limit (1+ (- frame-or-index (exwm-workspace--count))))) - (make-frame))) + (make-frame)) + (run-hooks 'exwm-workspace-list-change-hook)) (exwm-workspace-switch (car (last exwm-workspace--list))))) (defvar exwm-workspace-list-change-hook nil @@ -1174,8 +1178,8 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (exwm-workspace--update-ewmh-props) (if exwm-workspace--create-silently (setq exwm-workspace--switch-history-outdated t) - (exwm-workspace-switch frame t)) - (run-hooks 'exwm-workspace-list-change-hook)))) + (exwm-workspace-switch frame t) + (run-hooks 'exwm-workspace-list-change-hook))))) (defun exwm-workspace--remove-frame-as-workspace (frame) "Stop treating frame FRAME as a workspace." -- cgit 1.4.1 From d86db3edd675d4c9fc00b4c3677507a9fd453c54 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 22 Jul 2016 12:26:29 +0800 Subject: Add minibuffer toggle command * exwm-workspace.el (exwm-workspace-toggle-minibuffer): Add minibuffer toggle command. * exwm-core.el (exwm-mode-menu, exwm-mode-map): Substitute minibuffer attach/detach commands with minibuffer toggle command. * exwm-workspace.el (exwm-workspace-toggle-minibuffer): Insert various auto load cookies. --- exwm-core.el | 7 +------ exwm-workspace.el | 12 ++++++++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 09e16582be..7606a9e941 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -199,12 +199,7 @@ ["Swap workspaces" exwm-workspace-swap] ["Move X window to" exwm-workspace-move-window :keys "C-c C-m"] ["Move X window from" exwm-workspace-switch-to-buffer] - ["Attach minibuffer" exwm-workspace-attach-minibuffer - (and (exwm-workspace--minibuffer-own-frame-p) - (not (exwm-workspace--minibuffer-attached-p)))] - ["Detach minibuffer" exwm-workspace-detach-minibuffer - (and (exwm-workspace--minibuffer-own-frame-p) - (exwm-workspace--minibuffer-attached-p))] + ["Toggle minibuffer" exwm-workspace-toggle-minibuffer] ["Switch workspace" exwm-workspace-switch] ;; Place this entry at bottom to avoid selecting others by accident. ("Switch to" :filter diff --git a/exwm-workspace.el b/exwm-workspace.el index b3c2390936..e58c554114 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -500,6 +500,7 @@ The optional FORCE option is for internal use only." "Number of workspaces `exwm-workspace-switch-create' allowed to create each time.") +;;;###autoload (defun exwm-workspace-switch-create (frame-or-index) "Switch to workspace FRAME-OR-INDEX, creating it if it does not exist yet." (interactive) @@ -835,6 +836,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (assq (frame-parameter exwm-workspace--minibuffer 'exwm-container) exwm-workspace--id-struts-alist)) +;;;###autoload (defun exwm-workspace-attach-minibuffer () "Attach the minibuffer so that it always shows." (interactive) @@ -858,6 +860,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (exwm-workspace--set-fullscreen f)) (exwm-workspace--show-minibuffer)))) +;;;###autoload (defun exwm-workspace-detach-minibuffer () "Detach the minibuffer so that it automatically hides." (interactive) @@ -874,6 +877,15 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (exwm-workspace--set-fullscreen f)) (exwm-workspace--hide-minibuffer)))) +;;;###autoload +(defun exwm-workspace-toggle-minibuffer () + "Attach the minibuffer if it's detached, or detach it if it's attached." + (interactive) + (when (exwm-workspace--minibuffer-own-frame-p) + (if (exwm-workspace--minibuffer-attached-p) + (exwm-workspace-detach-minibuffer) + (exwm-workspace-attach-minibuffer)))) + (defun exwm-workspace--update-minibuffer-height (&optional echo-area) "Update the minibuffer frame height." (let ((height -- cgit 1.4.1 From 6ecd8b921fade26f30bdf4c0b047fcc9d9b57e9d Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 22 Jul 2016 12:29:01 +0800 Subject: Fix issues with deleting the last workspace * exwm-workspace.el (exwm-workspace-delete): Prevent deleting the last workspace. * exwm-workspace.el (exwm-workspace--remove-frame-as-workspace): Create a new workspace if the last one is deleted. --- exwm-workspace.el | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index e58c554114..01f5b505fb 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -604,10 +604,11 @@ INDEX must not exceed the current number of workspaces." (defun exwm-workspace-delete (&optional frame-or-index) "Delete the workspace FRAME-OR-INDEX." (interactive) - (delete-frame - (if frame-or-index - (exwm-workspace--workspace-from-frame-or-index frame-or-index) - exwm-workspace--current))) + (when (< 1 (exwm-workspace--count)) + (delete-frame + (if frame-or-index + (exwm-workspace--workspace-from-frame-or-index frame-or-index) + exwm-workspace--current)))) (defun exwm-workspace--on-focus-in () "Handle unexpected frame switch." @@ -1198,12 +1199,13 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (cond ((not (exwm-workspace--workspace-p frame)) (exwm--log "Frame `%s' is not a workspace" frame)) - ((= 1 (exwm-workspace--count)) - ;; FIXME: When using dedicated minibuffer frame, deleting the last - ;; frame hangs Emacs. - (user-error "[EXWM] Cannot remove last workspace")) (t (exwm--log "Removing frame `%s' as workspace" frame) + (when (= 1 (exwm-workspace--count)) + ;; The user managed to delete the last workspace, so create a new one. + (exwm--log "Last worksapce deleted; create a new one") + (let ((exwm-workspace--create-silently t)) + (make-frame))) (let* ((index (exwm-workspace--position frame)) (lastp (= index (1- (exwm-workspace--count)))) (nextw (elt exwm-workspace--list (+ index (if lastp -1 +1))))) -- cgit 1.4.1 From 5529790b039ec6bac3e5eb7da17931b1dec50c64 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 22 Jul 2016 12:31:22 +0800 Subject: ; * exwm-input.el: Prevent moving/resizing a tiling X window. --- exwm-input.el | 54 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index b79a55127d..fa7883f32d 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -205,41 +205,49 @@ This value should always be overwritten.") (defun exwm-input--on-ButtonPress (data _synthetic) "Handle ButtonPress event." (let ((obj (make-instance 'xcb:ButtonPress)) - (mode xcb:Allow:SyncPointer)) + (mode xcb:Allow:SyncPointer) + window buffer frame) (xcb:unmarshal obj data) (with-slots (detail time event state) obj (setq exwm-input--timestamp time) + (setq window (get-buffer-window (exwm--id->buffer event) t) + buffer (window-buffer window)) (cond ((and (= state exwm-input--move-mask) - (= detail exwm-input--move-keysym)) + (= detail exwm-input--move-keysym) + ;; Either an undecorated or a floating X window. + (with-current-buffer buffer + (or (not (eq major-mode 'exwm-mode)) + exwm--floating-frame))) ;; Move (exwm-floating--start-moveresize event xcb:ewmh:_NET_WM_MOVERESIZE_MOVE)) ((and (= state exwm-input--resize-mask) - (= detail exwm-input--resize-keysym)) + (= detail exwm-input--resize-keysym) + (with-current-buffer buffer + (or (not (eq major-mode 'exwm-mode)) + exwm--floating-frame))) ;; Resize (exwm-floating--start-moveresize event)) (t ;; Click to focus - (let ((window (get-buffer-window (exwm--id->buffer event) t)) - frame) - (unless (eq window (selected-window)) - (setq frame (window-frame window)) - (unless (eq frame exwm-workspace--current) - (if (exwm-workspace--workspace-p frame) - ;; The X window is on another workspace - (exwm-workspace-switch frame) - (with-current-buffer (window-buffer window) - (when (and (eq major-mode 'exwm-mode) - (not (eq exwm--frame - exwm-workspace--current))) - ;; The floating X window is on another workspace - (exwm-workspace-switch exwm--frame))))) - ;; It has been reported that the `window' may have be deleted - (if (window-live-p window) - (select-window window) - (setq window - (get-buffer-window (exwm--id->buffer event) t)) - (when window (select-window window))))) + (unless (eq window (selected-window)) + (setq frame (window-frame window)) + (unless (eq frame exwm-workspace--current) + (if (exwm-workspace--workspace-p frame) + ;; The X window is on another workspace + (exwm-workspace-switch frame) + (with-current-buffer buffer + (when (and (eq major-mode 'exwm-mode) + (not (eq exwm--frame + exwm-workspace--current))) + ;; The floating X window is on another workspace + (exwm-workspace-switch exwm--frame))))) + ;; It has been reported that the `window' may have be deleted + (if (window-live-p window) + (select-window window) + (setq window + (get-buffer-window (exwm--id->buffer event) t)) + (when window (select-window window)))) ;; The event should be replayed (setq mode xcb:Allow:ReplayPointer)))) (xcb:+request exwm--connection -- cgit 1.4.1 From f4d89d7e2f571d0b78a7242fc13c21ca0f46bebe Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 25 Jul 2016 12:14:26 +0800 Subject: Minor fixes for past few commits * exwm-input.el (exwm-input--unread-event): Temporarily disable it due to bug#23980. * exwm-workspace.el (exwm-workspace--init): Set the default frame internal border to 0. * exwm-workspace.el (exwm-workspace--remove-frame-as-workspace): Fix a typo. --- exwm-input.el | 2 +- exwm-workspace.el | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index fa7883f32d..62779c9e2b 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -323,7 +323,7 @@ This value should always be overwritten.") ;; add it into (this-command-keys) there, we use `unread-command-events' ;; differently on Emacs 24 and 25. (eval-and-compile - (if (< emacs-major-version 25) + (if (< emacs-major-version 26) (defsubst exwm-input--unread-event (event) (setq unread-command-events (append unread-command-events (list event)))) diff --git a/exwm-workspace.el b/exwm-workspace.el index 01f5b505fb..08c90751db 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1203,7 +1203,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (exwm--log "Removing frame `%s' as workspace" frame) (when (= 1 (exwm-workspace--count)) ;; The user managed to delete the last workspace, so create a new one. - (exwm--log "Last worksapce deleted; create a new one") + (exwm--log "Last workspace deleted; create a new one") (let ((exwm-workspace--create-silently t)) (make-frame))) (let* ((index (exwm-workspace--position frame)) @@ -1289,6 +1289,8 @@ applied to all subsequently created X frames." "Initialize workspace module." ;; Prevent unexpected exit (setq confirm-kill-emacs #'exwm-workspace--confirm-kill-emacs) + (exwm-workspace--modify-all-x-frames-parameters + '((internal-border-width . 0))) (let ((initial-workspaces (frame-list))) (if (not (exwm-workspace--minibuffer-own-frame-p)) ;; Initialize workspaces with minibuffers. @@ -1309,7 +1311,6 @@ applied to all subsequently created X frames." (make-frame '((window-system . x) (minibuffer . only) (left . 10000) (right . 10000) (width . 1) (height . 1) - (internal-border-width . 0) (client . nil)))) ;; Remove/hide existing frames. (dolist (f initial-workspaces) @@ -1370,7 +1371,6 @@ applied to all subsequently created X frames." (add-hook 'echo-area-clear-hook #'exwm-workspace--on-echo-area-clear) ;; Recreate one frame with the external minibuffer set. (setq initial-workspaces (list (make-frame '((window-system . x) - (internal-border-width . 0) (client . nil))))) ;; The default behavior of `display-buffer' (indirectly called by ;; `minibuffer-completion-help') is not correct here. @@ -1384,7 +1384,6 @@ applied to all subsequently created X frames." ;; Create remaining workspaces. (dotimes (_ (- exwm-workspace-number (length initial-workspaces))) (nconc initial-workspaces (list (make-frame '((window-system . x) - (internal-border-width . 0) (client . nil)))))) ;; Configure workspaces (dolist (i initial-workspaces) -- cgit 1.4.1 From 915ecc8979e8bb531d05c4d239a95b6f2a457ceb Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 25 Jul 2016 12:18:36 +0800 Subject: Bump version to 0.6 --- exwm.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm.el b/exwm.el index f7df651cde..dd803f789b 100644 --- a/exwm.el +++ b/exwm.el @@ -4,8 +4,8 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0.5 -;; Package-Requires: ((xelb "0.8")) +;; Version: 0.6 +;; Package-Requires: ((xelb "0.9")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From fdeb39dc0831f589e4924e17dad43b31a96b8dfe Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 26 Jul 2016 21:39:39 +0800 Subject: Add an option to force using tiling layout * exwm-manage.el (exwm-manage-force-tiling): New variable indicating whether X windows should always be managed in tiling layout. (exwm-manage--manage-window): Add a condition. --- exwm-manage.el | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index 260571b6c0..9e05ea2482 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -28,6 +28,11 @@ (require 'exwm-core) +(defvar exwm-manage-force-tiling nil + "Non-nil to force managing all X windows in tiling layout. + +You can still make the X windows floating afterwards.") + (defvar exwm-manage-finish-hook nil "Normal hook run after a window is just managed, in the context of the corresponding buffer.") @@ -233,9 +238,11 @@ corresponding buffer.") (xcb:flush exwm--connection) (exwm--update-title id) (exwm--update-protocols id) - (if (or exwm-transient-for exwm--fixed-size - (memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY exwm-window-type) - (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG exwm-window-type)) + (if (and (not exwm-manage-force-tiling) + (or exwm-transient-for exwm--fixed-size + (memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY exwm-window-type) + (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG + exwm-window-type))) (exwm-floating--set-floating id) (exwm-floating--unset-floating id)) (exwm-input-grab-keyboard id) -- cgit 1.4.1 From e6bf1b45ad77328af2b0a9be680c46e268bb2571 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 26 Jul 2016 21:42:22 +0800 Subject: Always update input focus on entering minibuffer * exwm-input.el (exwm-input--on-minibuffer-setup): New function for setting input focus on the workspace frame. (exwm-input--init): Add the function to `minibuffer-setup-hook'. * exwm-workspace.el (exwm-workspace--on-minibuffer-setup): Avoid setting input focus here. --- exwm-input.el | 7 +++++++ exwm-workspace.el | 4 +--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 62779c9e2b..4a6d56e2fb 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -174,6 +174,11 @@ This value should always be overwritten.") (xcb:flush exwm--connection))))) (setq exwm-input--update-focus-lock nil)) +(defun exwm-input--on-minibuffer-setup () + "Run in `minibuffer-setup-hook' to set input focus." + ;; Set input focus on the Emacs frame + (x-focus-frame (window-frame (minibuffer-selected-window)))) + (defun exwm-input--set-active-window (&optional id) "Set _NET_ACTIVE_WINDOW." (xcb:+request exwm--connection @@ -604,6 +609,8 @@ Its usage is the same with `exwm-input-set-simulation-keys'." #'exwm-floating--stop-moveresize) (xcb:+event exwm--connection 'xcb:MotionNotify #'exwm-floating--do-moveresize) + ;; The input focus should be set on the frame when minibuffer is active. + (add-hook 'minibuffer-setup-hook #'exwm-input--on-minibuffer-setup) ;; `pre-command-hook' marks the end of a key sequence (existing or not) (add-hook 'pre-command-hook #'exwm-input--finish-key-sequence) ;; Control `exwm-input--during-command' diff --git a/exwm-workspace.el b/exwm-workspace.el index 08c90751db..3a4a5a8ef6 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -996,9 +996,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." ;; Exclude non-graphical frames. (frame-parameter nil 'exwm-outer-id)) (add-hook 'post-command-hook #'exwm-workspace--update-minibuffer-height) - (exwm-workspace--show-minibuffer) - ;; Set input focus on the Emacs frame - (x-focus-frame (window-frame (minibuffer-selected-window))))) + (exwm-workspace--show-minibuffer))) (defun exwm-workspace--on-minibuffer-exit () "Run in minibuffer-exit-hook to hide the minibuffer container." -- cgit 1.4.1 From 2220c8cea21093afb17da24e8c8ecb32c357a09d Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 27 Jul 2016 12:00:39 +0800 Subject: Bump version to 0.7 --- exwm.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm.el b/exwm.el index dd803f789b..7de22972f0 100644 --- a/exwm.el +++ b/exwm.el @@ -4,7 +4,7 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0.6 +;; Version: 0.7 ;; Package-Requires: ((xelb "0.9")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From 719b825bc4e5471ca2a51f1f9f3b1149a1532c24 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 29 Jul 2016 17:05:09 +0800 Subject: Sync with XELB * exwm-input.el (exwm-input--update-global-prefix-keys) (exwm-input--on-KeyPress-line-mode, exwm-input--on-KeyPress-char-mode) (exwm-input--fake-key, exwm-input--init): Sync with XELB. * exwm-input.el (exwm-input--on-KeyPress) (exwm-input--on-KeyPress-line-mode): Resend XKB events with SendEvent since AllowEvents in ReplayKeyboard mode doesn't seem to work. --- exwm-core.el | 2 +- exwm-input.el | 54 +++++++++++++++++++++++++++++++++++------------------- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 7606a9e941..226e7fc312 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -79,7 +79,7 @@ "Event mask set on all managed windows.") (declare-function exwm-input--on-KeyPress-line-mode "exwm-input.el" - (key-press)) + (key-press raw-data)) ;; Internal variables (defvar-local exwm--id nil) ;window ID diff --git a/exwm-input.el b/exwm-input.el index 4a6d56e2fb..0b5ce98e6a 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -265,7 +265,7 @@ This value should always be overwritten.") (xcb:unmarshal obj data) (setq exwm-input--timestamp (slot-value obj 'time)) (if (eq major-mode 'exwm-mode) - (funcall exwm--on-KeyPress obj) + (funcall exwm--on-KeyPress obj data) (exwm-input--on-KeyPress-char-mode obj)))) (defvar exwm-input--global-keys nil "Global key bindings.") @@ -307,9 +307,9 @@ This value should always be overwritten.") keycode (xcb:keysyms:keysym->keycode exwm--connection (car keysym))) (setf (slot-value grab-key 'grab-window) workspace - (slot-value grab-key 'modifiers) (cadr keysym) + (slot-value grab-key 'modifiers) (cdr keysym) (slot-value grab-key 'key) keycode) - (when (or (not keycode) + (when (or (= 0 keycode) (xcb:+request-checked+request-check exwm--connection grab-key)) (user-error "[EXWM] Failed to grab key: %s" @@ -344,14 +344,15 @@ This value should always be overwritten.") (defvar exwm-input--during-command nil "Indicate whether between `pre-command-hook' and `post-command-hook'.") -(defun exwm-input--on-KeyPress-line-mode (key-press) +(defun exwm-input--on-KeyPress-line-mode (key-press raw-data) "Parse X KeyPress event to Emacs key event and then feed the command loop." (with-slots (detail state) key-press (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) event minibuffer-window mode) - (when (and keysym - (setq event (xcb:keysyms:keysym->event exwm--connection - keysym state)) + (when (and (/= 0 (car keysym)) + (setq event (xcb:keysyms:keysym->event + exwm--connection (car keysym) + (logand state (lognot (cdr keysym))))) (or exwm-input--during-key-sequence exwm-input--during-command (setq minibuffer-window (active-minibuffer-window)) @@ -363,20 +364,35 @@ This value should always be overwritten.") ;; Feed this event to command loop. Also force it to be added to ;; `this-command-keys'. (exwm-input--unread-event event)) + (unless mode + (if (= 0 (logand #x6000 state)) ;Check the 13~14 bits. + ;; Not an XKB state; just replay it. + (setq mode xcb:Allow:ReplayKeyboard) + ;; An XKB state; sent it with SendEvent. + ;; FIXME: Can this also be replayed? + ;; FIXME: KeyRelease events are lost. + (setq mode xcb:Allow:AsyncKeyboard) + (xcb:+request exwm--connection + (make-instance 'xcb:SendEvent + :propagate 0 + :destination (slot-value key-press 'event) + :event-mask xcb:EventMask:NoEvent + :event raw-data)))) (xcb:+request exwm--connection (make-instance 'xcb:AllowEvents - :mode (or mode xcb:Allow:ReplayKeyboard) + :mode mode :time xcb:Time:CurrentTime)) (xcb:flush exwm--connection)))) -(defun exwm-input--on-KeyPress-char-mode (key-press) +(defun exwm-input--on-KeyPress-char-mode (key-press &optional _raw-data) "Handle KeyPress event in char-mode." (with-slots (detail state) key-press (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) event) - (when (and keysym - (setq event (xcb:keysyms:keysym->event exwm--connection - keysym state))) + (when (and (/= 0 (car keysym)) + (setq event (xcb:keysyms:keysym->event + exwm--connection (car keysym) + (logand state (lognot (cdr keysym)))))) (when (eq major-mode 'exwm-mode) ;; FIXME: This functionality seems not working, e.g. when this ;; command would activate the minibuffer, the temporary @@ -476,11 +492,11 @@ This value should always be overwritten.") "Fake a key event equivalent to Emacs event EVENT." (let* ((keysym (xcb:keysyms:event->keysym exwm--connection event)) keycode id) - (unless keysym + (when (= 0 (car keysym)) (user-error "[EXWM] Invalid key: %s" (single-key-description event))) (setq keycode (xcb:keysyms:keysym->keycode exwm--connection (car keysym))) - (when keycode + (when (/= 0 keycode) (setq id (exwm--buffer->id (window-buffer (selected-window)))) (dolist (class '(xcb:KeyPress xcb:KeyRelease)) (xcb:+request exwm--connection @@ -495,7 +511,7 @@ This value should always be overwritten.") :child 0 :root-x 0 :root-y 0 :event-x 0 :event-y 0 - :state (cadr keysym) + :state (cdr keysym) :same-screen 1) exwm--connection))))) (xcb:flush exwm--connection))) @@ -592,16 +608,16 @@ Its usage is the same with `exwm-input-set-simulation-keys'." exwm-input-move-event)) (resize-key (xcb:keysyms:event->keysym exwm--connection exwm-input-resize-event))) - (unless move-key + (when (= 0 (car move-key)) (user-error "[EXWM] Invalid key: %s" (single-key-description exwm-input-move-event))) - (unless resize-key + (when (= 0 (car resize-key)) (user-error "[EXWM] Invalid key: %s" (single-key-description exwm-input-resize-event))) (setq exwm-input--move-keysym (car move-key) - exwm-input--move-mask (cadr move-key) + exwm-input--move-mask (cdr move-key) exwm-input--resize-keysym (car resize-key) - exwm-input--resize-mask (cadr resize-key))) + exwm-input--resize-mask (cdr resize-key))) ;; Attach event listeners (xcb:+event exwm--connection 'xcb:KeyPress #'exwm-input--on-KeyPress) (xcb:+event exwm--connection 'xcb:ButtonPress #'exwm-input--on-ButtonPress) -- cgit 1.4.1 From f52848595d44064bb092d3028a7f9afe1107faf1 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 29 Jul 2016 17:11:28 +0800 Subject: ; * exwm-floating.el (exwm-floating--unset-floating): Reposition an X ; window when it changes from floating to tiling layout. --- exwm-floating.el | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/exwm-floating.el b/exwm-floating.el index 7931c661c7..6702e38d61 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -277,6 +277,14 @@ (make-instance 'xcb:ChangeWindowAttributes :window id :value-mask xcb:CW:EventMask :event-mask exwm--client-event-mask)) + ;; The X window might have been moved due to the floating border. + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window id + :value-mask (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y) + :x 0 + :y 0)) ;; Reparent the floating frame back to the root window. (let ((frame-id (frame-parameter exwm--floating-frame 'exwm-outer-id)) (frame-container (frame-parameter exwm--floating-frame -- cgit 1.4.1 From 6bfedf8bf71ef11a26f2e8c3cddbac55ce873e5c Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 30 Jul 2016 10:00:11 +0800 Subject: Fix input focus transfer between workspaces * exwm-input.el (exwm-input--set-focus): Make input focus revert to parent by default (to prevent input focus from following pointer). * exwm-input.el (exwm-input--update-focus): Switch to another workspace if input focus is transfered to it. * exwm-workspace.el (exwm-workspace--on-focus-in, exwm-workspace--init) (exwm-workspace--exit): Remove `exwm-workspace--on-focus-in' and related uses. --- exwm-input.el | 14 ++++++++++---- exwm-workspace.el | 13 ------------- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 0b5ce98e6a..0da0558e84 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -73,7 +73,7 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (exwm--log "Focus on #x%x with SetInputFocus" id) (xcb:+request exwm--connection (make-instance 'xcb:SetInputFocus - :revert-to xcb:InputFocus:PointerRoot + :revert-to xcb:InputFocus:Parent :focus id :time xcb:Time:CurrentTime))) (exwm-input--set-active-window id) @@ -169,9 +169,15 @@ This value should always be overwritten.") (xcb:flush exwm--connection))) (when (eq (selected-window) window) (exwm--log "Focus on %s" window) - (select-frame-set-input-focus (window-frame window) t) - (exwm-input--set-active-window) - (xcb:flush exwm--connection))))) + (if (and (exwm-workspace--workspace-p (selected-frame)) + (not (eq (selected-frame) exwm-workspace--current))) + ;; The focus is on another workspace (e.g. it got clicked) + ;; so switch to it. + (exwm-workspace-switch (selected-frame)) + ;; The focus is still on the current workspace. + (select-frame-set-input-focus (window-frame window) t) + (exwm-input--set-active-window) + (xcb:flush exwm--connection)))))) (setq exwm-input--update-focus-lock nil)) (defun exwm-input--on-minibuffer-setup () diff --git a/exwm-workspace.el b/exwm-workspace.el index 3a4a5a8ef6..b2a0cab153 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -610,16 +610,6 @@ INDEX must not exceed the current number of workspaces." (exwm-workspace--workspace-from-frame-or-index frame-or-index) exwm-workspace--current)))) -(defun exwm-workspace--on-focus-in () - "Handle unexpected frame switch." - ;; `focus-in-hook' is run by `handle-switch-frame'. - (unless (eq this-command #'handle-switch-frame) - (let ((index (exwm-workspace--position (selected-frame)))) - (exwm--log "Focus on workspace %s" index) - (when (and index (/= index exwm-workspace-current-index)) - (exwm--log "Workspace was switched unexpectedly") - (exwm-workspace-switch index))))) - (defun exwm-workspace--set-desktop (id) "Set _NET_WM_DESKTOP for X window ID." (with-current-buffer (exwm--id->buffer id) @@ -1374,8 +1364,6 @@ applied to all subsequently created X frames." ;; `minibuffer-completion-help') is not correct here. (cl-pushnew '(exwm-workspace--display-buffer) display-buffer-alist :test #'equal)) - ;; Handle unexpected frame switch. - (add-hook 'focus-in-hook #'exwm-workspace--on-focus-in) ;; Prevent `other-buffer' from selecting already displayed EXWM buffers. (modify-all-frames-parameters '((buffer-predicate . exwm-layout--other-buffer-predicate))) @@ -1413,7 +1401,6 @@ applied to all subsequently created X frames." (setq display-buffer-alist (cl-delete '(exwm-workspace--display-buffer) display-buffer-alist :test #'equal)) - (remove-hook 'focus-in-hook #'exwm-workspace--on-focus-in) (advice-remove 'x-create-frame #'exwm-workspace--x-create-frame) (remove-hook 'after-make-frame-functions #'exwm-workspace--add-frame-as-workspace) -- cgit 1.4.1 From 9f68fbd906ca14856a70a3c3b535188a670d01f4 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 30 Jul 2016 10:06:00 +0800 Subject: Grab global keys on newly created workspaces * exwm-input.el (exwm-input--update-global-prefix-keys): Label grabbed workspaces and re-grab keys only when keys change or there are newly created workspaces. (exwm-input--init, exwm-input--exit): Use the function in `exwm-workspace-list-change-hook'. --- exwm-input.el | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 0da0558e84..f7a67040d6 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -292,7 +292,11 @@ This value should always be overwritten.") (setq exwm-input--global-prefix-keys nil) (dolist (i exwm-input--global-keys) (cl-pushnew (elt i 0) exwm-input--global-prefix-keys)) - (unless (equal original exwm-input--global-prefix-keys) + ;; Stop here if the global prefix keys are update-to-date and + ;; there's no new workspace. + (unless (and (equal original exwm-input--global-prefix-keys) + (cl-every (lambda (w) (frame-parameter w 'exwm-grabbed)) + exwm-workspace--list)) (setq ungrab-key (make-instance 'xcb:UngrabKey :key xcb:Grab:Any :grab-window nil :modifiers xcb:ModMask:Any) @@ -308,6 +312,8 @@ This value should always be overwritten.") (setf (slot-value ungrab-key 'grab-window) workspace) (if (xcb:+request-checked+request-check exwm--connection ungrab-key) (exwm--log "Failed to ungrab keys") + ;; Label this frame. + (set-frame-parameter w 'exwm-grabbed t) (dolist (k exwm-input--global-prefix-keys) (setq keysym (xcb:keysyms:event->keysym exwm--connection k) keycode (xcb:keysyms:keysym->keycode exwm--connection @@ -640,6 +646,9 @@ Its usage is the same with `exwm-input-set-simulation-keys'." (add-hook 'post-command-hook #'exwm-input--on-post-command) ;; Update focus when buffer list updates (add-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update) + ;; Re-grab global keys. + (add-hook 'exwm-workspace-list-change-hook + #'exwm-input--update-global-prefix-keys) ;; Update prefix keys for global keys (exwm-input--update-global-prefix-keys)) @@ -648,7 +657,9 @@ Its usage is the same with `exwm-input-set-simulation-keys'." (remove-hook 'pre-command-hook #'exwm-input--finish-key-sequence) (remove-hook 'pre-command-hook #'exwm-input--on-pre-command) (remove-hook 'post-command-hook #'exwm-input--on-post-command) - (remove-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update)) + (remove-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update) + (remove-hook 'exwm-workspace-list-change-hook + #'exwm-input--update-global-prefix-keys)) -- cgit 1.4.1 From ba0f41db1b377196af8aa6607ef215e48a3daa26 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 30 Jul 2016 10:09:43 +0800 Subject: ; * exwm-manage.el (exwm-manage--manage-window): Check the value range ; of _NET_WM_DESKTOP. --- exwm-manage.el | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/exwm-manage.el b/exwm-manage.el index 9e05ea2482..c083229c2d 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -98,6 +98,7 @@ corresponding buffer.") (declare-function exwm-workspace--current-height "exwm-workspace.el") (declare-function exwm-workspace--current-width "exwm-workspace.el") (declare-function exwm-workspace--set-desktop "exwm-workspace.el" (id)) +(declare-function exwm-workspace--count "exwm-workspace.el" ()) (declare-function exwm-workspace-move-window "exwm-workspace.el" (frame-or-index &optional id)) @@ -254,7 +255,10 @@ corresponding buffer.") desktop) (when reply (setq desktop (slot-value reply 'value))) - (if (and desktop (/= desktop exwm-workspace-current-index)) + (if (and desktop + (/= desktop exwm-workspace-current-index) + ;; Check the range. + (< desktop (exwm-workspace--count))) (exwm-workspace-move-window desktop id) (exwm-workspace--set-desktop id))) (with-current-buffer (exwm--id->buffer id) -- cgit 1.4.1 From aa7de9dc6f13b0d937fe265b64368b7be6e8798e Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 30 Jul 2016 10:12:31 +0800 Subject: Exit the active minibuffer on unmanaging X windows * exwm-manage.el (exwm-manage--unmanage-window): Exit the minibuffer as a precaution to prevent the "selecting deleted buffer" error. --- exwm-manage.el | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index c083229c2d..16abe9845b 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -352,14 +352,26 @@ manager is shutting down." ;; Destroy the X window container (and the frame container if any). (xcb:+request exwm--connection (make-instance 'xcb:DestroyWindow :window exwm--container)) - (let ((kill-buffer-query-functions nil) - (floating exwm--floating-frame)) - (kill-buffer) - (when floating - (select-window - (frame-selected-window exwm-workspace--current))))) - (exwm-manage--set-client-list) - (xcb:flush exwm--connection)))) + (exwm-manage--set-client-list) + (xcb:flush exwm--connection)) + (let ((kill-buffer-func + (lambda (buffer) + (with-current-buffer buffer + (let ((kill-buffer-query-functions nil) + (floating exwm--floating-frame)) + (kill-buffer) + (when floating + (select-window + (frame-selected-window exwm-workspace--current)))))))) + (if (not (active-minibuffer-window)) + ;; Kill the buffer as usual. + (funcall kill-buffer-func buffer) + ;; This can happen when this buffer was requested to be killed + ;; from the minibuffer (e.g. with `ido-kill-buffer-at-head'). + ;; We have to exit the minibuffer first or there'll be a + ;; "selecting deleted buffer" error. + (run-with-idle-timer 0 nil kill-buffer-func buffer) + (exit-minibuffer)))))) (defun exwm-manage--scan () "Search for existing windows and try to manage them." -- cgit 1.4.1 From d225f191a908346535cf4525a4c6c56d93a84b31 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 30 Jul 2016 10:17:57 +0800 Subject: Fit the *Completions* buffer * exwm-workspace.el (exwm-workspace--on-minibuffer-setup): Work around the *Completions* buffer not getting fitted problem. * exwm.el (exwm-enable): Enable `window-resize-pixelwise'. --- exwm-workspace.el | 12 +++++++++++- exwm.el | 3 ++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index b2a0cab153..b425f9249b 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -986,7 +986,17 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." ;; Exclude non-graphical frames. (frame-parameter nil 'exwm-outer-id)) (add-hook 'post-command-hook #'exwm-workspace--update-minibuffer-height) - (exwm-workspace--show-minibuffer))) + (exwm-workspace--show-minibuffer)) + ;; FIXME: This is a temporary fix for the *Completions* buffer not + ;; being correctly fitted by its displaying window. As with + ;; `exwm-workspace--display-buffer', the problem is caused by + ;; the fact that the minibuffer (rather than the workspace) + ;; frame is the 'selected frame'. `get-buffer-window' will + ;; fail to retrieve the correct window. It's likely there are + ;; other related issues. + (let ((window (get-buffer-window "*Completions*" exwm-workspace--current))) + (when window + (fit-window-to-buffer window nil nil nil nil t)))) (defun exwm-workspace--on-minibuffer-exit () "Run in minibuffer-exit-hook to hide the minibuffer container." diff --git a/exwm.el b/exwm.el index 7de22972f0..4c156dcd23 100644 --- a/exwm.el +++ b/exwm.el @@ -679,7 +679,8 @@ This hook is only run when EXWM is started with emacsclient.") (dolist (i exwm-blocking-subrs) (advice-remove i #'exwm--server-eval-at))) (_ ;enable EXWM - (setq frame-resize-pixelwise t) ;mandatory; before init + (setq frame-resize-pixelwise t ;mandatory; before init + window-resize-pixelwise t) (add-hook 'window-setup-hook #'exwm-init t) ;for Emacs (add-hook 'after-make-frame-functions #'exwm-init t) ;for Emacs Client (add-hook 'kill-emacs-hook #'exwm--server-stop) -- cgit 1.4.1 From 0b0982b5ac4c002b099aaef804328e9851d33e1c Mon Sep 17 00:00:00 2001 From: Taichi Uemura Date: Sat, 30 Jul 2016 18:48:09 +0900 Subject: set fullscreen-frame-count to 0 on exiting --- exwm-workspace.el | 1 + 1 file changed, 1 insertion(+) diff --git a/exwm-workspace.el b/exwm-workspace.el index b425f9249b..98c114813b 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1401,6 +1401,7 @@ applied to all subsequently created X frames." exwm-workspace--list nil exwm-workspace--client nil exwm-workspace--minibuffer nil + exwm-workspace--fullscreen-frame-count 0 default-minibuffer-frame nil) (remove-hook 'minibuffer-setup-hook #'exwm-workspace--on-minibuffer-setup) (remove-hook 'minibuffer-exit-hook #'exwm-workspace--on-minibuffer-exit) -- cgit 1.4.1 From 8061e93878440a386a5fe308b0829e707e5f8c9d Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 30 Jul 2016 18:57:27 +0800 Subject: ; * exwm-workspace.el (exwm-workspace-switch): Input focus should be set ; on the frame where the target window resides. --- exwm-workspace.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 98c114813b..ce2ed6f645 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -463,7 +463,7 @@ The optional FORCE option is for internal use only." (set-frame-parameter (buffer-local-value 'exwm--frame (window-buffer)) 'exwm-selected-window (selected-window))) (select-window window) - (x-focus-frame frame) ;essential for transferring input focus + (x-focus-frame (window-frame window)) ;The real input focus. (set-frame-parameter frame 'exwm-selected-window nil) ;; Close the (possible) active minibuffer (when (active-minibuffer-window) -- cgit 1.4.1 From b87f4fbd7170ec156d6fa575584f55c25dd1da1b Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 30 Jul 2016 18:59:19 +0800 Subject: ; * exwm.el (exwm-enable): Ignore unrecognized command line arguments. --- exwm.el | 3 +++ 1 file changed, 3 insertions(+) diff --git a/exwm.el b/exwm.el index 4c156dcd23..692c863559 100644 --- a/exwm.el +++ b/exwm.el @@ -681,6 +681,9 @@ This hook is only run when EXWM is started with emacsclient.") (_ ;enable EXWM (setq frame-resize-pixelwise t ;mandatory; before init window-resize-pixelwise t) + ;; Ignore unrecognized command line arguments. This can be helpful + ;; when EXWM is launched by some session manager. + (push #'vector command-line-functions) (add-hook 'window-setup-hook #'exwm-init t) ;for Emacs (add-hook 'after-make-frame-functions #'exwm-init t) ;for Emacs Client (add-hook 'kill-emacs-hook #'exwm--server-stop) -- cgit 1.4.1 From 1e78045f958edbb2f3ef7c21953f8b55b3bbae42 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 30 Jul 2016 19:01:33 +0800 Subject: Add restart support * exwm-input.el (exwm-input--exit): Cancel timers. * exwm-manage.el (exwm-manage--manage-window): Add reparented X windows to save-set. * exwm-systemtray.el (exwm-systemtray--embed): Add embeded icons to save-set. * exwm-workspace.el (exwm-workspace--confirm-kill-emacs): No need to unmanage; also hide Emacs frames; always call `exwm--exit'. * exwm.el (exwm-restart): New command for restarting EXWM. (exwm--exit-icccm-ewmh): New function for cleaning up ICCCM/EWMH properties. (exwm-exit-hook): Update doc string. (exwm--exit): Call `exwm--exit-icccm-ewmh' and do not reset variables. --- exwm-input.el | 6 ++++- exwm-manage.el | 5 +++++ exwm-systemtray.el | 5 +++++ exwm-workspace.el | 65 +++++++++++++++++++++++++++--------------------------- exwm.el | 49 ++++++++++++++++++++++++++++++++-------- 5 files changed, 87 insertions(+), 43 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index f7a67040d6..555cc87f9f 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -659,7 +659,11 @@ Its usage is the same with `exwm-input-set-simulation-keys'." (remove-hook 'post-command-hook #'exwm-input--on-post-command) (remove-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update) (remove-hook 'exwm-workspace-list-change-hook - #'exwm-input--update-global-prefix-keys)) + #'exwm-input--update-global-prefix-keys) + (when exwm-input--update-focus-defer-timer + (cancel-timer exwm-input--update-focus-defer-timer)) + (when exwm-input--update-focus-timer + (cancel-timer exwm-input--update-focus-timer))) diff --git a/exwm-manage.el b/exwm-manage.el index 16abe9845b..0b7b475590 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -112,6 +112,11 @@ corresponding buffer.") :window id :value-mask xcb:CW:EventMask :event-mask exwm--client-event-mask)) (throw 'return 'dead)) + ;; Add this X window to save-set. + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeSaveSet + :mode xcb:SetMode:Insert + :window id)) (with-current-buffer (generate-new-buffer "*EXWM*") ;; Keep the oldest X window first. (setq exwm--id-buffer-alist diff --git a/exwm-systemtray.el b/exwm-systemtray.el index 56e5fb8eb7..25f5fa584c 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -89,6 +89,11 @@ You shall use the default value if using auto-hide minibuffer.") height* (round (* height (/ (float width*) width))))) (exwm--log "(System Tray) Resize from %dx%d to %dx%d" width height width* height*)) + ;; Add this icon to save-set. + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:ChangeSaveSet + :mode xcb:SetMode:Insert + :window icon)) ;; Reparent to the embedder. (xcb:+request exwm-systemtray--connection (make-instance 'xcb:ReparentWindow diff --git a/exwm-workspace.el b/exwm-workspace.el index ce2ed6f645..b73059ab4d 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1045,47 +1045,46 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (0 (y-or-n-p prompt)) (x (yes-or-no-p (format "[EXWM] %d window%s currently alive. %s" x (if (= x 1) "" "s") prompt)))) - ;; Unmanage all X windows. - (dolist (i exwm--id-buffer-alist) - (exwm-manage--unmanage-window (car i) 'quit) - (xcb:+request exwm--connection - (make-instance 'xcb:MapWindow :window (car i)))) - ;; Reparent out the minibuffer frame. + ;; Hide & reparent out all frames (save-set can't be used here since + ;; X windows will be re-mapped). (when (exwm-workspace--minibuffer-own-frame-p) - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window (frame-parameter exwm-workspace--minibuffer - 'exwm-outer-id) - :parent exwm--root - :x 0 - :y 0))) - ;; Reparent out all workspace frames. + (let ((id (frame-parameter exwm-workspace--minibuffer 'exwm-outer-id))) + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow + :window id)) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window id + :parent exwm--root + :x 0 + :y 0)))) (dolist (f exwm-workspace--list) - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window (frame-parameter f 'exwm-outer-id) - :parent exwm--root - :x 0 - :y 0))) - (xcb:flush exwm--connection) - (if (not exwm-workspace--client) - (progn - ;; Destroy all resources created by this connection. - (xcb:disconnect exwm--connection) - t) - ;; Extra cleanups for emacsclient. + (let ((id (frame-parameter f 'exwm-outer-id))) + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow + :window id)) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window id + :parent exwm--root + :x 0 + :y 0)))) + ;; Exit each module. + (exwm--exit) + ;; Destroy all resources created by this connection. + (xcb:disconnect exwm--connection) + (setq exwm--connection nil) + ;; Extra cleanups for emacsclient. + (when exwm-workspace--client (dolist (f exwm-workspace--list) (set-frame-parameter f 'client exwm-workspace--client)) (when (exwm-workspace--minibuffer-own-frame-p) (set-frame-parameter exwm-workspace--minibuffer 'client exwm-workspace--client)) - (let ((connection exwm--connection)) - (exwm--exit) - ;; Destroy all resources created by this connection. - (xcb:disconnect connection)) ;; Kill the client. - (server-save-buffers-kill-terminal nil) - nil))) + (server-save-buffers-kill-terminal nil)) + ;; Set the return value. + (not exwm-workspace--client))) (defun exwm-workspace--set-desktop-geometry () "Set _NET_DESKTOP_GEOMETRY." diff --git a/exwm.el b/exwm.el index 692c863559..b04990b329 100644 --- a/exwm.el +++ b/exwm.el @@ -83,6 +83,20 @@ (exwm-layout--refresh) (call-interactively #'exwm-input-grab-keyboard)))) +;;;###autoload +(defun exwm-restart () + "Restart EXWM." + (interactive) + (when (exwm-workspace--confirm-kill-emacs "[EXWM] Restart? ") + (server-force-delete) + (run-hooks 'kill-emacs-hook) + ;; FIXME: more? + (apply #'call-process (car command-line-args) nil nil nil + (cdr command-line-args)) + ;; Kill this instance at last. + (let ((kill-emacs-hook nil)) + (kill-emacs)))) + (defun exwm--update-window-type (id &optional force) "Update _NET_WM_WINDOW_TYPE." (with-current-buffer (exwm--id->buffer id) @@ -597,6 +611,30 @@ :window i :data "EXWM")))) (xcb:flush exwm--connection)) +(defun exwm--exit-icccm-ewmh () + "Remove ICCCM/EWMH properties." + (dolist (p (list + xcb:Atom:_NET_WM_NAME + xcb:Atom:_NET_SUPPORTED + xcb:Atom:_NET_CLIENT_LIST + xcb:Atom:_NET_CLIENT_LIST_STACKING + xcb:Atom:_NET_NUMBER_OF_DESKTOPS + xcb:Atom:_NET_DESKTOP_GEOMETRY + xcb:Atom:_NET_DESKTOP_VIEWPORT + xcb:Atom:_NET_CURRENT_DESKTOP + xcb:Atom:_NET_ACTIVE_WINDOW + xcb:Atom:_NET_WORKAREA + xcb:Atom:_NET_SUPPORTING_WM_CHECK + xcb:Atom:_NET_VIRTUAL_ROOTS + ;; TODO: Keep this list synchronized with that in + ;; `exwm--init-icccm-ewmh'. + )) + (xcb:+request exwm--connection + (make-instance 'xcb:DeleteProperty + :window exwm--root + :property p)) + (xcb:flush exwm--connection))) + (defvar exwm-init-hook nil "Normal hook run when EXWM has just finished initialization.") @@ -643,10 +681,7 @@ (exwm-manage--scan) (run-hooks 'exwm-init-hook))))) -(defvar exwm-exit-hook nil - "Normal hook run just before EXWM is about to exit. - -This hook is only run when EXWM is started with emacsclient.") +(defvar exwm-exit-hook nil "Normal hook run just before EXWM exits.") (defun exwm--exit () "Exit EXWM." @@ -657,11 +692,7 @@ This hook is only run when EXWM is started with emacsclient.") (exwm-manage--exit) (exwm-floating--exit) (exwm-layout--exit) - ;; Reset several import variables. - (setq exwm--connection nil - exwm--root nil - exwm--id-buffer-alist nil) - (exwm-enable)) + (exwm--exit-icccm-ewmh)) (defvar exwm-blocking-subrs '(x-file-dialog x-popup-dialog x-select-font) "Subrs (primitives) that would normally block EXWM.") -- cgit 1.4.1 From a3dba8edd9001e89e24c204b9186fdf8b444e78c Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 31 Jul 2016 13:14:43 +0800 Subject: Fix restarting issues * exwm-workspace.el (exwm-workspace--confirm-kill-emacs): * exwm.el (exwm-restart): Run `kill-emacs-hook' early; do not check for X windows before restarting. --- exwm-workspace.el | 15 ++++++++++----- exwm.el | 8 ++------ 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index b73059ab4d..3e2fe1abb5 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1039,12 +1039,17 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (declare-function exwm-manage--unmanage-window "exwm-manage.el") (declare-function exwm--exit "exwm.el") -(defun exwm-workspace--confirm-kill-emacs (prompt) +(defun exwm-workspace--confirm-kill-emacs (prompt &optional force) "Confirm before exiting Emacs." - (when (pcase (length exwm--id-buffer-alist) - (0 (y-or-n-p prompt)) - (x (yes-or-no-p (format "[EXWM] %d window%s currently alive. %s" - x (if (= x 1) "" "s") prompt)))) + (when (or (and force (not (eq force 'no-check))) + (and (or (eq force 'no-check) (not exwm--id-buffer-alist)) + (y-or-n-p prompt)) + (yes-or-no-p (format "[EXWM] %d window(s) will be destroyed. %s" + (length exwm--id-buffer-alist) prompt))) + ;; Run `kill-emacs-hook' before Emacs frames are unmapped so that + ;; errors can be visible. + (run-hooks 'kill-emacs-hook) + (setq kill-emacs-hook nil) ;; Hide & reparent out all frames (save-set can't be used here since ;; X windows will be re-mapped). (when (exwm-workspace--minibuffer-own-frame-p) diff --git a/exwm.el b/exwm.el index b04990b329..9a593d5eca 100644 --- a/exwm.el +++ b/exwm.el @@ -87,15 +87,11 @@ (defun exwm-restart () "Restart EXWM." (interactive) - (when (exwm-workspace--confirm-kill-emacs "[EXWM] Restart? ") - (server-force-delete) - (run-hooks 'kill-emacs-hook) - ;; FIXME: more? + (when (exwm-workspace--confirm-kill-emacs "[EXWM] Restart? " 'no-check) (apply #'call-process (car command-line-args) nil nil nil (cdr command-line-args)) ;; Kill this instance at last. - (let ((kill-emacs-hook nil)) - (kill-emacs)))) + (kill-emacs))) (defun exwm--update-window-type (id &optional force) "Update _NET_WM_WINDOW_TYPE." -- cgit 1.4.1 From fc542bbcc9e703f7e187045f3620f08b46320be4 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 31 Jul 2016 13:16:51 +0800 Subject: ; Eliminate compile warnings --- exwm-input.el | 2 +- exwm-workspace.el | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 555cc87f9f..f218ea26e1 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -135,6 +135,7 @@ This value should always be overwritten.") (declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") (declare-function exwm-workspace-switch "exwm-workspace.el" (frame-or-index &optional force)) +(declare-function exwm-workspace--workspace-p "exwm-workspace.el" (workspace)) (defun exwm-input--update-focus (window) "Update input focus." @@ -209,7 +210,6 @@ This value should always be overwritten.") (declare-function exwm-floating--start-moveresize "exwm-floating.el" (id &optional type)) (declare-function exwm-workspace--position "exwm-workspace.el" (frame)) -(declare-function exwm-workspace--workspace-p "exwm-workspace.el" (workspace)) (defvar exwm-workspace--list) diff --git a/exwm-workspace.el b/exwm-workspace.el index 3e2fe1abb5..7c2601a3bc 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -571,7 +571,8 @@ before it." (eq (elt exwm-workspace--list nth) exwm-workspace--current))) ;; Do the move. - (pop (nthcdr pos exwm-workspace--list)) + (with-no-warnings ;For Emacs 24. + (pop (nthcdr pos exwm-workspace--list))) (push workspace (nthcdr nth exwm-workspace--list)) ;; Update the _NET_WM_DESKTOP property of each X window affected. (setq start (min pos nth) @@ -994,9 +995,13 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." ;; frame is the 'selected frame'. `get-buffer-window' will ;; fail to retrieve the correct window. It's likely there are ;; other related issues. - (let ((window (get-buffer-window "*Completions*" exwm-workspace--current))) - (when window - (fit-window-to-buffer window nil nil nil nil t)))) + ;; This is not required by Emacs 24. + (when (fboundp 'window-preserve-size) + (let ((window (get-buffer-window "*Completions*" + exwm-workspace--current))) + (when window + (fit-window-to-buffer window) + (window-preserve-size window))))) (defun exwm-workspace--on-minibuffer-exit () "Run in minibuffer-exit-hook to hide the minibuffer container." -- cgit 1.4.1 From 173bbde88599cf9bf8dbda87d3282c2f243716be Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 31 Jul 2016 13:17:12 +0800 Subject: Bump version to 0.8 --- exwm.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm.el b/exwm.el index 9a593d5eca..861d51a970 100644 --- a/exwm.el +++ b/exwm.el @@ -4,8 +4,8 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0.7 -;; Package-Requires: ((xelb "0.9")) +;; Version: 0.8 +;; Package-Requires: ((xelb "0.10")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From e7ff9a9f90f0356a9ab8a9ca3857df03deeb1696 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 1 Aug 2016 19:49:43 +0800 Subject: Fix restarting issues * exwm-workspace.el (exwm-workspace--confirm-kill-emacs): Prompt for unsaved files before restarting; avoid running `server-force-stop' early; restore the 'client' frame parameter before calling `exwm--exit'; correctly handle emacsclient. * exwm.el (exwm-restart): Always kill subordinate Emacs instances. --- exwm-workspace.el | 55 ++++++++++++++++++++++++++++++++++++------------------- exwm.el | 27 +++++++++++++++++++++++---- 2 files changed, 59 insertions(+), 23 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 7c2601a3bc..097a950a6e 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -25,6 +25,8 @@ ;;; Code: +(require 'server) + (require 'exwm-core) (defvar exwm-workspace-number 1 "Initial number of workspaces.") @@ -1046,15 +1048,32 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (defun exwm-workspace--confirm-kill-emacs (prompt &optional force) "Confirm before exiting Emacs." - (when (or (and force (not (eq force 'no-check))) - (and (or (eq force 'no-check) (not exwm--id-buffer-alist)) - (y-or-n-p prompt)) - (yes-or-no-p (format "[EXWM] %d window(s) will be destroyed. %s" - (length exwm--id-buffer-alist) prompt))) - ;; Run `kill-emacs-hook' before Emacs frames are unmapped so that - ;; errors can be visible. - (run-hooks 'kill-emacs-hook) - (setq kill-emacs-hook nil) + (when (cond + ((and force (not (eq force 'no-check))) + ;; Force killing Emacs. + t) + ((or (eq force 'no-check) (not exwm--id-buffer-alist)) + ;; Check if there's any unsaved file. + (pcase (catch 'break + (let ((kill-emacs-query-functions + (append kill-emacs-query-functions + (list (lambda () + (throw 'break 'break)))))) + (save-buffers-kill-emacs))) + (`break (y-or-n-p prompt)) + (x x))) + (t + (yes-or-no-p (format "[EXWM] %d window(s) will be destroyed. %s" + (length exwm--id-buffer-alist) prompt)))) + ;; Run `kill-emacs-hook' (`server-force-stop' excluded) before Emacs + ;; frames are unmapped so that errors (if any) can be visible. + (if (memq #'server-force-stop kill-emacs-hook) + (progn + (setq kill-emacs-hook (delq #'server-force-stop kill-emacs-hook)) + (run-hooks 'kill-emacs-hook) + (setq kill-emacs-hook (list #'server-force-stop))) + (run-hooks 'kill-emacs-hook) + (setq kill-emacs-hook nil)) ;; Hide & reparent out all frames (save-set can't be used here since ;; X windows will be re-mapped). (when (exwm-workspace--minibuffer-own-frame-p) @@ -1079,22 +1098,20 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." :parent exwm--root :x 0 :y 0)))) - ;; Exit each module. - (exwm--exit) - ;; Destroy all resources created by this connection. - (xcb:disconnect exwm--connection) - (setq exwm--connection nil) - ;; Extra cleanups for emacsclient. + ;; Restore the 'client' frame parameter (before `exwm--exit'). (when exwm-workspace--client (dolist (f exwm-workspace--list) (set-frame-parameter f 'client exwm-workspace--client)) (when (exwm-workspace--minibuffer-own-frame-p) (set-frame-parameter exwm-workspace--minibuffer 'client - exwm-workspace--client)) - ;; Kill the client. - (server-save-buffers-kill-terminal nil)) + exwm-workspace--client))) + ;; Exit each module. + (exwm--exit) + ;; Destroy all resources created by this connection. + (xcb:disconnect exwm--connection) + (setq exwm--connection nil) ;; Set the return value. - (not exwm-workspace--client))) + t)) (defun exwm-workspace--set-desktop-geometry () "Set _NET_DESKTOP_GEOMETRY." diff --git a/exwm.el b/exwm.el index 861d51a970..dd279bbcd0 100644 --- a/exwm.el +++ b/exwm.el @@ -88,10 +88,29 @@ "Restart EXWM." (interactive) (when (exwm-workspace--confirm-kill-emacs "[EXWM] Restart? " 'no-check) - (apply #'call-process (car command-line-args) nil nil nil - (cdr command-line-args)) - ;; Kill this instance at last. - (kill-emacs))) + (let* ((attr (process-attributes (emacs-pid))) + (args (cdr (assq 'args attr))) + (ppid (cdr (assq 'ppid attr))) + (pargs (cdr (assq 'args (process-attributes ppid))))) + (cond + ((= ppid 1) + ;; The parent is the init process. This probably means this + ;; instance is an emacsclient. Anyway, start a control instance + ;; to manage the subsequent ones. + (call-process (car command-line-args)) + (kill-emacs)) + ((string= args pargs) + ;; This is a subordinate instance. Return a magic number to + ;; inform the parent (control instance) to start another one. + (kill-emacs ?R)) + (t + ;; This is the control instance. Keep starting subordinate + ;; instances until told to exit. + ;; Run `server-force-stop' if it exists. + (run-hooks 'kill-emacs-hook) + (with-temp-buffer + (while (= ?R (shell-command-on-region (point) (point) args)))) + (kill-emacs)))))) (defun exwm--update-window-type (id &optional force) "Update _NET_WM_WINDOW_TYPE." -- cgit 1.4.1 From f04b041cae5b119355737a9272c683ea8a791d7b Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 1 Aug 2016 19:53:04 +0800 Subject: Fix emacsclient issues * exwm-workspace.el (exwm-workspace--client-p): New function for testing emacsclient frames. (exwm-workspace--update-minibuffer-height) (exwm-workspace--on-minibuffer-setup) (exwm-workspace--on-minibuffer-exit, exwm-workspace--on-echo-area-dirty) (exwm-workspace--on-echo-area-clear): * exwm-input.el (exwm-input--on-buffer-list-update) (exwm-input--on-minibuffer-setup): * exwm-layout.el (exwm-layout--on-minibuffer-setup) (exwm-layout--on-echo-area-change): Use it. * exwm-workspace.el (exwm-workspace--add-frame-as-workspace): Always clear the 'client' frame parameter. (exwm-workspace--init): Fix a typo. --- exwm-input.el | 10 +++++++--- exwm-layout.el | 8 +++++--- exwm-workspace.el | 56 +++++++++++++++++++++++++++++-------------------------- 3 files changed, 42 insertions(+), 32 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index f218ea26e1..cbfd1dc7d3 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -79,6 +79,9 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (exwm-input--set-active-window id) (xcb:flush exwm--connection)))) +(declare-function exwm-workspace--client-p "exwm-workspace.el" + (&optional frame)) + (defvar exwm-input--update-focus-window nil "The (Emacs) window to be focused. This value should always be overwritten.") @@ -87,7 +90,7 @@ This value should always be overwritten.") "Run in `buffer-list-update-hook' to track input focus." (when (and (not (minibufferp)) ;Do not set input focus on minibuffer window. (eq (current-buffer) (window-buffer)) ;e.g. `with-temp-buffer'. - (frame-parameter nil 'exwm-outer-id)) ;e.g. emacsclient frame. + (not (exwm-workspace--client-p))) (setq exwm-input--update-focus-window (selected-window)) (exwm-input--update-focus-defer))) @@ -183,8 +186,9 @@ This value should always be overwritten.") (defun exwm-input--on-minibuffer-setup () "Run in `minibuffer-setup-hook' to set input focus." - ;; Set input focus on the Emacs frame - (x-focus-frame (window-frame (minibuffer-selected-window)))) + (unless (exwm-workspace--client-p) + ;; Set input focus on the Emacs frame + (x-focus-frame (window-frame (minibuffer-selected-window))))) (defun exwm-input--set-active-window (&optional id) "Set _NET_ACTIVE_WINDOW." diff --git a/exwm-layout.el b/exwm-layout.el index 905a1e3c99..ee97849e03 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -375,9 +375,12 @@ selected by `other-buffer'." (exwm-layout--set-client-list-stacking) (xcb:flush exwm--connection)))) +(declare-function exwm-workspace--client-p "exwm-workspace.el" + (&optional frame)) + (defun exwm-layout--on-minibuffer-setup () "Refresh layout when minibuffer grows." - (when (frame-parameter nil 'exwm-outer-id) + (unless (exwm-workspace--client-p) (run-with-idle-timer 0.01 nil ;FIXME (lambda () (when (< 1 (window-height (minibuffer-window))) @@ -386,8 +389,7 @@ selected by `other-buffer'." (defun exwm-layout--on-echo-area-change (&optional dirty) "Run when message arrives or in `echo-area-clear-hook' to refresh layout." (when (and (current-message) - ;; Exclude non-graphical frames. - (frame-parameter nil 'exwm-outer-id) + (not (exwm-workspace--client-p)) (or (cl-position ?\n (current-message)) (> (length (current-message)) (frame-width exwm-workspace--current)))) diff --git a/exwm-workspace.el b/exwm-workspace.el index 097a950a6e..4789aeb18e 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -48,6 +48,10 @@ NIL if FRAME is not a workspace" "Return t if FRAME is a workspace." (memq frame exwm-workspace--list)) +(defsubst exwm-workspace--client-p (&optional frame) + "Return non-nil if FRAME is an emacsclient frame." + (frame-parameter frame 'client)) + (defun exwm-workspace--workspace-from-frame-or-index (frame-or-index) "Retrieve the workspace frame from FRAME-OR-INDEX." (cond @@ -882,24 +886,25 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (defun exwm-workspace--update-minibuffer-height (&optional echo-area) "Update the minibuffer frame height." - (let ((height - (with-current-buffer - (window-buffer (minibuffer-window exwm-workspace--minibuffer)) - (max 1 - (if echo-area - (let ((width (frame-width exwm-workspace--minibuffer)) - (result 0)) - (mapc (lambda (i) - (setq result - (+ result - (ceiling (1+ (length i)) width)))) - (split-string (or (current-message) "") "\n")) - result) - (count-screen-lines)))))) - (when (and (integerp max-mini-window-height) - (> height max-mini-window-height)) - (setq height max-mini-window-height)) - (set-frame-height exwm-workspace--minibuffer height))) + (unless (exwm-workspace--client-p) + (let ((height + (with-current-buffer + (window-buffer (minibuffer-window exwm-workspace--minibuffer)) + (max 1 + (if echo-area + (let ((width (frame-width exwm-workspace--minibuffer)) + (result 0)) + (mapc (lambda (i) + (setq result + (+ result + (ceiling (1+ (length i)) width)))) + (split-string (or (current-message) "") "\n")) + result) + (count-screen-lines)))))) + (when (and (integerp max-mini-window-height) + (> height max-mini-window-height)) + (setq height max-mini-window-height)) + (set-frame-height exwm-workspace--minibuffer height)))) (defun exwm-workspace--on-ConfigureNotify (data _synthetic) "Adjust the container to fit the minibuffer frame." @@ -986,8 +991,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (defun exwm-workspace--on-minibuffer-setup () "Run in minibuffer-setup-hook to show the minibuffer and its container." (when (and (= 1 (minibuffer-depth)) - ;; Exclude non-graphical frames. - (frame-parameter nil 'exwm-outer-id)) + (not (exwm-workspace--client-p))) (add-hook 'post-command-hook #'exwm-workspace--update-minibuffer-height) (exwm-workspace--show-minibuffer)) ;; FIXME: This is a temporary fix for the *Completions* buffer not @@ -1008,8 +1012,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (defun exwm-workspace--on-minibuffer-exit () "Run in minibuffer-exit-hook to hide the minibuffer container." (when (and (= 1 (minibuffer-depth)) - ;; Exclude non-graphical frames. - (frame-parameter nil 'exwm-outer-id)) + (not (exwm-workspace--client-p))) (remove-hook 'post-command-hook #'exwm-workspace--update-minibuffer-height) (exwm-workspace--hide-minibuffer))) @@ -1018,8 +1021,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (defun exwm-workspace--on-echo-area-dirty () "Run when new message arrives to show the echo area and its container." (when (and (not (active-minibuffer-window)) - ;; Exclude non-graphical frames. - (frame-parameter nil 'exwm-outer-id) + (not (exwm-workspace--client-p)) (or (current-message) cursor-in-echo-area)) (exwm-workspace--update-minibuffer-height t) @@ -1033,7 +1035,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (defun exwm-workspace--on-echo-area-clear () "Run in echo-area-clear-hook to hide echo area container." - (when (frame-parameter nil 'exwm-outer-id) ;Exclude non-graphical frames. + (unless (exwm-workspace--client-p) (unless (active-minibuffer-window) (exwm-workspace--hide-minibuffer)) (when exwm-workspace--display-echo-area-timer @@ -1153,6 +1155,8 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (set-frame-parameter frame 'exwm-outer-id outer-id) (set-frame-parameter frame 'exwm-container container) (set-frame-parameter frame 'exwm-workspace workspace) + ;; In case it's created by emacsclient. + (set-frame-parameter frame 'client nil) ;; Copy RandR frame parameters from the first workspace to ;; prevent potential problems. The values do not matter here as ;; they'll be updated by the RandR module later. @@ -1324,7 +1328,7 @@ applied to all subsequently created X frames." (unless (frame-parameter i 'window-id) (setq initial-workspaces (delq i initial-workspaces)))) (setq exwm-workspace--client - (frame-parameter (car exwm-workspace--list) 'client)) + (frame-parameter (car initial-workspaces) 'client)) (let ((f (car initial-workspaces))) ;; Remove the possible internal border. (set-frame-parameter f 'internal-border-width 0) -- cgit 1.4.1 From 0d00a92fad1f70131c35c4e90e57ef9d3e2f0ab4 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 6 Aug 2016 21:38:43 +0800 Subject: Avoid setting input focus on workspace containers * exwm-input.el (exwm-input--on-FocusIn): Set input focus to the workspace frame when input focus is received by a workspace container. (exwm-input--on-workspace-list-change): Select FocusChange event mask on workspace containers. (exwm-input--init): Attach the event listener; adapt function calls. --- exwm-input.el | 45 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index cbfd1dc7d3..7506267ee7 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -53,6 +53,12 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") +(defvar exwm-workspace--current) +(defvar exwm-workspace--switch-history-outdated) +(defvar exwm-workspace-current-index) +(defvar exwm-workspace--minibuffer) +(defvar exwm-workspace--list) + (defun exwm-input--set-focus (id) "Set input focus to window ID in a proper way." (when (exwm--id->buffer id) @@ -79,6 +85,30 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (exwm-input--set-active-window id) (xcb:flush exwm--connection)))) +(defun exwm-input--on-FocusIn (data _synthetic) + "Handle FocusIn events." + (let ((obj (make-instance 'xcb:FocusIn))) + (xcb:unmarshal obj data) + (when (= (slot-value obj 'detail) xcb:NotifyDetail:Inferior) + ;; Transfer input focus back to the workspace when the workspace + ;; container unexpectedly receives it. + (x-focus-frame exwm-workspace--current)))) + +(defun exwm-input--on-workspace-list-change () + "Run in `exwm-input--update-global-prefix-keys'." + (dolist (f exwm-workspace--list) + ;; Reuse the 'exwm-grabbed' frame parameter set in + ;; `exwm-input--update-global-prefix-keys'. + (unless (frame-parameter f 'exwm-grabbed) + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window (frame-parameter f 'exwm-workspace) + :value-mask xcb:CW:EventMask + ;; There should no other event selected there. + :event-mask xcb:EventMask:FocusChange)))) + (exwm-input--update-global-prefix-keys) + (xcb:flush exwm--connection)) + (declare-function exwm-workspace--client-p "exwm-workspace.el" (&optional frame)) @@ -128,11 +158,6 @@ This value should always be overwritten.") #'exwm-input--update-focus exwm-input--update-focus-window)))) -(defvar exwm-workspace--current) -(defvar exwm-workspace--switch-history-outdated) -(defvar exwm-workspace-current-index) -(defvar exwm-workspace--minibuffer) - (declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) (declare-function exwm-layout--set-state "exwm-layout.el" (id state)) (declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") @@ -215,8 +240,6 @@ This value should always be overwritten.") (id &optional type)) (declare-function exwm-workspace--position "exwm-workspace.el" (frame)) -(defvar exwm-workspace--list) - (defun exwm-input--on-ButtonPress (data _synthetic) "Handle ButtonPress event." (let ((obj (make-instance 'xcb:ButtonPress)) @@ -641,6 +664,7 @@ Its usage is the same with `exwm-input-set-simulation-keys'." #'exwm-floating--stop-moveresize) (xcb:+event exwm--connection 'xcb:MotionNotify #'exwm-floating--do-moveresize) + (xcb:+event exwm--connection 'xcb:FocusIn #'exwm-input--on-FocusIn) ;; The input focus should be set on the frame when minibuffer is active. (add-hook 'minibuffer-setup-hook #'exwm-input--on-minibuffer-setup) ;; `pre-command-hook' marks the end of a key sequence (existing or not) @@ -652,9 +676,8 @@ Its usage is the same with `exwm-input-set-simulation-keys'." (add-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update) ;; Re-grab global keys. (add-hook 'exwm-workspace-list-change-hook - #'exwm-input--update-global-prefix-keys) - ;; Update prefix keys for global keys - (exwm-input--update-global-prefix-keys)) + #'exwm-input--on-workspace-list-change) + (exwm-input--on-workspace-list-change)) (defun exwm-input--exit () "Exit the input module." @@ -663,7 +686,7 @@ Its usage is the same with `exwm-input-set-simulation-keys'." (remove-hook 'post-command-hook #'exwm-input--on-post-command) (remove-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update) (remove-hook 'exwm-workspace-list-change-hook - #'exwm-input--update-global-prefix-keys) + #'exwm-input--on-workspace-list-change) (when exwm-input--update-focus-defer-timer (cancel-timer exwm-input--update-focus-defer-timer)) (when exwm-input--update-focus-timer -- cgit 1.4.1 From dcec998bb446320c6029522a089800cbc6b73b55 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 6 Aug 2016 21:42:43 +0800 Subject: * exwm-layout.el (exwm-layout--show): Fix the position of floating X windows. --- exwm-layout.el | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index ee97849e03..2886e43b4c 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -68,18 +68,24 @@ "Show window ID exactly fit in the Emacs window WINDOW." (exwm--log "Show #x%x in %s" id window) (let* ((edges (window-inside-absolute-pixel-edges window)) - (width (- (elt edges 2) (elt edges 0))) - (height (- (elt edges 3) (elt edges 1))) + (x (pop edges)) + (y (pop edges)) + (width (- (pop edges) x)) + (height (- (pop edges) y)) + (edges (window-inside-pixel-edges window)) + (relative-x (pop edges)) + (relative-y (pop edges)) frame-width frame-height) (with-current-buffer (exwm--id->buffer id) (if (not exwm--floating-frame) - (let ((relative-edges (window-inside-pixel-edges window))) - (exwm-layout--resize-container - id exwm--container - (elt relative-edges 0) (elt relative-edges 1) width height - ;; Do not resize the X window if the minibuffer resizes itself. - (and (active-minibuffer-window) - (< 1 (window-height (active-minibuffer-window)))))) + (exwm-layout--resize-container id exwm--container + relative-x relative-y width height + ;; Keep the size of the X window if + ;; it's the minibuffer that resized. + (and + (active-minibuffer-window) + (< 1 (window-height + (active-minibuffer-window))))) ;; A floating X window is of the same size as the Emacs window, ;; whereas its container is of the same size as the Emacs frame. (setq frame-width (frame-pixel-width exwm--floating-frame) @@ -106,8 +112,8 @@ xcb:ConfigWindow:Y xcb:ConfigWindow:Width xcb:ConfigWindow:Height) - :x exwm-floating-border-width - :y exwm-floating-border-width + :x relative-x + :y relative-y :width width :height height))) ;; Make the resizing take effect. @@ -122,11 +128,13 @@ :event-mask xcb:EventMask:StructureNotify :event (xcb:marshal (make-instance 'xcb:ConfigureNotify - :event id :window id + :event id + :window id :above-sibling xcb:Window:None - :x (elt edges 0) - :y (elt edges 1) - :width width :height height + :x x + :y y + :width width + :height height :border-width 0 :override-redirect 0) exwm--connection)))) -- cgit 1.4.1 From 06c1b0485a1815d13ded7bd717adf5d9735be3df Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 7 Aug 2016 13:34:50 +0800 Subject: Set background pixmap for system tray embedder * exwm-systemtray.el (exwm-systemtray--init): Make embedder use the same depth and pixmap of the parent; the parent is always a Emacs frame. --- exwm-systemtray.el | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/exwm-systemtray.el b/exwm-systemtray.el index 25f5fa584c..e978f64e15 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -370,27 +370,38 @@ You shall use the default value if using auto-hide minibuffer.") :data xcb:systemtray:ORIENTATION:HORZ))) ;; Create the embedder. (let ((id (xcb:generate-id exwm-systemtray--connection)) - parent y) + frame parent depth y) (setq exwm-systemtray--embedder id) - (xcb:+request exwm-systemtray--connection - (make-instance 'xcb:CreateWindow - :depth 0 :wid id :parent exwm--root - :x 0 :y 0 :width 1 :height exwm-systemtray-height - :border-width 0 :class xcb:WindowClass:CopyFromParent - :visual 0 :value-mask xcb:CW:EventMask - :event-mask xcb:EventMask:SubstructureNotify)) (if (exwm-workspace--minibuffer-own-frame-p) - (setq parent (frame-parameter exwm-workspace--minibuffer - 'exwm-container) - ;; Vertically centered. - y (/ (- (line-pixel-height) exwm-systemtray-height) 2)) - (setq parent (string-to-number (frame-parameter exwm-workspace--current - 'window-id)) + (setq frame exwm-workspace--minibuffer + y (if (>= (line-pixel-height) exwm-systemtray-height) + ;; Bottom aligned. + (- (line-pixel-height) exwm-systemtray-height) + ;; Vertically centered. + (/ (- (line-pixel-height) exwm-systemtray-height) 2))) + (setq frame exwm-workspace--current ;; Bottom aligned. y (- (exwm-workspace--current-height) exwm-systemtray-height))) + (setq parent (string-to-number (frame-parameter frame 'window-id)) + depth (slot-value (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetGeometry + :drawable parent)) + 'depth)) (xcb:+request exwm-systemtray--connection - (make-instance 'xcb:ReparentWindow - :window id :parent parent :x 0 :y y)) + (make-instance 'xcb:CreateWindow + :depth depth + :wid id + :parent parent + :x 0 + :y y + :width 1 + :height exwm-systemtray-height + :border-width 0 + :class xcb:WindowClass:CopyFromParent + :visual 0 + :value-mask (logior xcb:CW:BackPixmap xcb:CW:EventMask) + :background-pixmap xcb:BackPixmap:ParentRelative + :event-mask xcb:EventMask:SubstructureNotify)) ;; Set _NET_WM_NAME. (xcb:+request exwm-systemtray--connection (make-instance 'xcb:ewmh:set-_NET_WM_NAME -- cgit 1.4.1 From 6e0b944c2de14fe67d292f3351bf4353f7b846d8 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 9 Aug 2016 13:20:36 +0800 Subject: Ensure floating hooks are run in the right context * exwm-floating.el (exwm-floating-setup-hook, exwm-floating-exit-hook): Fix doc string. (exwm-floating--set-floating, exwm-floating--unset-floating): Set the context. --- exwm-floating.el | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 6702e38d61..464dd20e10 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -34,9 +34,11 @@ "Border color of the floating window.") (defvar exwm-floating-setup-hook nil - "Normal hook run when a window has been made floating.") + "Normal hook run when an X window has been made floating, in the +context of the corresponding buffer.") (defvar exwm-floating-exit-hook nil - "Normal hook run when a window has exited floating state.") + "Normal hook run when an X window has exited floating state, in the +context of the corresponding buffer.") ;; Cursors for moving/resizing a window (defvar exwm-floating--cursor-move nil) @@ -252,7 +254,8 @@ xcb:ConfigWindow:Y) :x 0 :y 0)) (xcb:flush exwm--connection))) - (run-hooks 'exwm-floating-setup-hook) + (with-current-buffer (exwm--id->buffer id) + (run-hooks 'exwm-floating-setup-hook)) ;; Redraw the frame. (redisplay)) @@ -324,7 +327,8 @@ (let ((window (frame-selected-window exwm-workspace--current))) (set-window-buffer window buffer) (select-window window)))) - (run-hooks 'exwm-floating-exit-hook)) + (with-current-buffer (exwm--id->buffer id) + (run-hooks 'exwm-floating-exit-hook))) ;;;###autoload (defun exwm-floating-toggle-floating () -- cgit 1.4.1 From 767abdf9e6fa20699faccf7a3388ad2fa6a52b9f Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 9 Aug 2016 13:26:15 +0800 Subject: Fix coordinates calculations concerning workspaces * exwm-floating.el (exwm-floating--set-floating) (exwm-floating--do-moveresize): * exwm-manage.el (exwm-manage--manage-window): Use the computed workareas rather than RandR output geometries. --- exwm-floating.el | 26 ++++++++++++-------------- exwm-manage.el | 16 ++++++++-------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 464dd20e10..56d2932d2c 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -105,15 +105,15 @@ context of the corresponding buffer.") (x (slot-value exwm--geometry 'x)) (y (slot-value exwm--geometry 'y)) (width (slot-value exwm--geometry 'width)) - (height (slot-value exwm--geometry 'height)) - (frame-geometry (frame-parameter original-frame 'exwm-geometry))) + (height (slot-value exwm--geometry 'height))) (exwm--log "Floating geometry (original, absolute): %dx%d%+d%+d" width height x y) - (when (and frame-geometry - (/= x 0) + (when (and (/= x 0) (/= y 0)) - (setq x (- x (slot-value frame-geometry 'x)) - y (- y (slot-value frame-geometry 'y)))) + (let ((workarea (elt exwm-workspace--workareas + (exwm-workspace--position original-frame)))) + (setq x (- x (aref workarea 0)) + y (- y (aref workarea 1))))) (exwm--log "Floating geometry (original, relative): %dx%d%+d%+d" width height x y) ;; Save frame parameters. @@ -561,14 +561,12 @@ context of the corresponding buffer.") (defun exwm-floating--do-moveresize (data _synthetic) "Perform move/resize." (when exwm-floating--moveresize-calculate - (let ((obj (make-instance 'xcb:MotionNotify)) - (geometry (frame-parameter exwm-workspace--current 'exwm-geometry)) - (frame-x 0) - (frame-y 0) - result value-mask width height buffer-or-id container-or-id) - (when geometry - (setq frame-x (slot-value geometry 'x) - frame-y (slot-value geometry 'y))) + (let* ((obj (make-instance 'xcb:MotionNotify)) + (workarea (elt exwm-workspace--workareas + exwm-workspace-current-index)) + (frame-x (aref workarea 0)) + (frame-y (aref workarea 1)) + result value-mask width height buffer-or-id container-or-id) (xcb:unmarshal obj data) (setq result (funcall exwm-floating--moveresize-calculate (slot-value obj 'root-x) (slot-value obj 'root-y)) diff --git a/exwm-manage.el b/exwm-manage.el index 0b7b475590..b88312455d 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -170,15 +170,15 @@ corresponding buffer.") exwm-window-type) (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DOCK exwm-window-type)) - (let ((frame-geometry (frame-parameter exwm-workspace--current - 'exwm-geometry)) - (workspace (frame-parameter exwm-workspace--current - 'exwm-workspace))) - (when (and frame-geometry - (/= x 0) + (let ((workspace (frame-parameter exwm-workspace--current + 'exwm-workspace)) + workarea) + (when (and (/= x 0) (/= y 0)) - (setq x (- x (slot-value frame-geometry 'x)) - y (- y (slot-value frame-geometry 'y)))) + (setq workarea (elt exwm-workspace--workareas + exwm-workspace-current-index) + x (- x (aref workarea 0)) + y (- y (aref workarea 1)))) (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow :window id -- cgit 1.4.1 From 810b4716a10169e1de29c52cf2e3aeb2e79f2018 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 9 Aug 2016 13:34:29 +0800 Subject: Update timestamp for WM_TAKE_FOCUS ClientMessage * exwm-input.el (exwm-input--timestamp-window) (exwm-input--timestamp-atom, exwm-input--timestamp-callback): New variables for updating timestamp. (exwm-input--set-focus): Send WM_TAKE_FOCUS ClientMessage with updated timestamp. (exwm-input--update-timestamp): New utility function for fetching timestamp. (exwm-input--on-PropertyNotify): New function for handling PropertyNotify event to extract the timestamp. (exwm-input--init): Create resources for updating timestamp; attach the event listener. (exwm-input--on-ButtonPress, exwm-input--on-KeyPress): * exwm.el (exwm--on-PropertyNotify): No longer update timestamp. * exwm-input.el (exwm-input--set-focus): Avoid setting input focus on already focused X windows, or when the input focus in not on a Emacs frame if globally active model is in use. * exwm-floating.el (exwm-floating--set-floating): * exwm-workspace.el (exwm-workspace-move-window) (exwm-workspace--add-frame-as-workspace, exwm-workspace--init): Set 'exwm-id' frame parameter as the numerical (inner) frame X ID. --- exwm-floating.el | 5 ++- exwm-input.el | 129 +++++++++++++++++++++++++++++++++++++++++------------- exwm-workspace.el | 9 ++++ exwm.el | 3 +- 4 files changed, 112 insertions(+), 34 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 56d2932d2c..7c5d811bf5 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -99,7 +99,9 @@ context of the corresponding buffer.") (height . ,window-min-height) (unsplittable . t))))) ;and fix the size later (outer-id (string-to-number (frame-parameter frame 'outer-window-id))) - (container (buffer-local-value 'exwm--container (exwm--id->buffer id))) + (window-id (string-to-number (frame-parameter frame 'window-id))) + (container (buffer-local-value 'exwm--container + (exwm--id->buffer id))) (frame-container (xcb:generate-id exwm--connection)) (window (frame-first-window frame)) ;and it's the only window (x (slot-value exwm--geometry 'x)) @@ -118,6 +120,7 @@ context of the corresponding buffer.") width height x y) ;; Save frame parameters. (set-frame-parameter frame 'exwm-outer-id outer-id) + (set-frame-parameter frame 'exwm-id window-id) (set-frame-parameter frame 'exwm-container frame-container) ;; Fix illegal parameters ;; FIXME: check normal hints restrictions diff --git a/exwm-input.el b/exwm-input.el index 7506267ee7..668e49585e 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -48,10 +48,9 @@ (defvar exwm-input--resize-keysym nil) (defvar exwm-input--resize-mask nil) -(defvar exwm-input--timestamp xcb:Time:CurrentTime - "A recent timestamp received from X server. - -It's updated in several occasions, and only used by `exwm-input--set-focus'.") +(defvar exwm-input--timestamp-window nil) +(defvar exwm-input--timestamp-atom nil) +(defvar exwm-input--timestamp-callback nil) (defvar exwm-workspace--current) (defvar exwm-workspace--switch-history-outdated) @@ -62,37 +61,81 @@ It's updated in several occasions, and only used by `exwm-input--set-focus'.") (defun exwm-input--set-focus (id) "Set input focus to window ID in a proper way." (when (exwm--id->buffer id) - (with-current-buffer (exwm--id->buffer id) - (if (and (not exwm--hints-input) - (memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols)) - (progn - (exwm--log "Focus on #x%x with WM_TAKE_FOCUS" id) + (let ((focus (slot-value (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetInputFocus)) + 'focus))) + (unless (= focus id) + (with-current-buffer (exwm--id->buffer id) + (cond + ((and (not exwm--hints-input) + (memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols)) + (when (= focus (frame-parameter nil 'exwm-id)) + (exwm--log "Focus on #x%x with WM_TAKE_FOCUS" id) + (exwm-input--update-timestamp + (lambda (timestamp id) + (let ((event (make-instance 'xcb:icccm:WM_TAKE_FOCUS + :window id + :time timestamp))) + (setq event (xcb:marshal event exwm--connection)) + (xcb:+request exwm--connection + (make-instance 'xcb:icccm:SendEvent + :destination id + :event event)) + (exwm-input--set-active-window id) + (xcb:flush exwm--connection))) + id))) + (t + (exwm--log "Focus on #x%x with SetInputFocus" id) (xcb:+request exwm--connection - (make-instance 'xcb:icccm:SendEvent - :destination id - :event (xcb:marshal - (make-instance 'xcb:icccm:WM_TAKE_FOCUS - :window id - :time - exwm-input--timestamp) - exwm--connection)))) - (exwm--log "Focus on #x%x with SetInputFocus" id) - (xcb:+request exwm--connection - (make-instance 'xcb:SetInputFocus - :revert-to xcb:InputFocus:Parent - :focus id - :time xcb:Time:CurrentTime))) - (exwm-input--set-active-window id) - (xcb:flush exwm--connection)))) + (make-instance 'xcb:SetInputFocus + :revert-to xcb:InputFocus:Parent + :focus id + :time xcb:Time:CurrentTime)) + (exwm-input--set-active-window id) + (xcb:flush exwm--connection)))))))) + +(defun exwm-input--update-timestamp (callback &rest args) + "Fetch the latest timestamp from the server and feed it to CALLBACK. + +ARGS are additional arguments to CALLBACK." + (setq exwm-input--timestamp-callback (cons callback args)) + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeProperty + :mode xcb:PropMode:Replace + :window exwm-input--timestamp-window + :property exwm-input--timestamp-atom + :type xcb:Atom:CARDINAL + :format 32 + :data-len 0 + :data nil)) + (xcb:flush exwm--connection)) + +(defun exwm-input--on-PropertyNotify (data _synthetic) + "Handle PropertyNotify events." + (when exwm-input--timestamp-callback + (let ((obj (make-instance 'xcb:PropertyNotify))) + (xcb:unmarshal obj data) + (when (= exwm-input--timestamp-window + (slot-value obj 'window)) + (apply (car exwm-input--timestamp-callback) + (slot-value obj 'time) + (cdr exwm-input--timestamp-callback)) + (setq exwm-input--timestamp-callback nil))))) (defun exwm-input--on-FocusIn (data _synthetic) "Handle FocusIn events." (let ((obj (make-instance 'xcb:FocusIn))) (xcb:unmarshal obj data) - (when (= (slot-value obj 'detail) xcb:NotifyDetail:Inferior) - ;; Transfer input focus back to the workspace when the workspace - ;; container unexpectedly receives it. - (x-focus-frame exwm-workspace--current)))) + ;; Not sure if this is the right thing to do but the point is the + ;; input focus should not stay at the root window or any container, + ;; or the result would be unpredictable. `x-focus-frame' would + ;; first set the input focus to the (previously) selected frame, and + ;; then `select-window' would further update the input focus if the + ;; selected window is displaying an `exwm-mode' buffer. Perhaps we + ;; should carefully filter out FocusIn events with certain 'detail' + ;; and 'mode' combinations, but this just works. + (x-focus-frame (selected-frame)) + (select-window (selected-window)))) (defun exwm-input--on-workspace-list-change () "Run in `exwm-input--update-global-prefix-keys'." @@ -247,7 +290,6 @@ This value should always be overwritten.") window buffer frame) (xcb:unmarshal obj data) (with-slots (detail time event state) obj - (setq exwm-input--timestamp time) (setq window (get-buffer-window (exwm--id->buffer event) t) buffer (window-buffer window)) (cond ((and (= state exwm-input--move-mask) @@ -296,7 +338,6 @@ This value should always be overwritten.") "Handle KeyPress event." (let ((obj (make-instance 'xcb:KeyPress))) (xcb:unmarshal obj data) - (setq exwm-input--timestamp (slot-value obj 'time)) (if (eq major-mode 'exwm-mode) (funcall exwm--on-KeyPress obj data) (exwm-input--on-KeyPress-char-mode obj)))) @@ -657,7 +698,33 @@ Its usage is the same with `exwm-input-set-simulation-keys'." exwm-input--move-mask (cdr move-key) exwm-input--resize-keysym (car resize-key) exwm-input--resize-mask (cdr resize-key))) + ;; Create the X window and intern the atom used to fetch timestamp. + (setq exwm-input--timestamp-window (xcb:generate-id exwm--connection)) + (xcb:+request exwm--connection + (make-instance 'xcb:CreateWindow + :depth 0 + :wid exwm-input--timestamp-window + :parent exwm--root + :x -1 + :y -1 + :width 1 + :height 1 + :border-width 0 + :class xcb:WindowClass:CopyFromParent + :visual 0 + :value-mask xcb:CW:EventMask + :event-mask xcb:EventMask:PropertyChange)) + (let ((atom "_TIME")) + (setq exwm-input--timestamp-atom + (slot-value (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:InternAtom + :only-if-exists 0 + :name-len (length atom) + :name atom)) + 'atom))) ;; Attach event listeners + (xcb:+event exwm--connection 'xcb:PropertyNotify + #'exwm-input--on-PropertyNotify) (xcb:+event exwm--connection 'xcb:KeyPress #'exwm-input--on-KeyPress) (xcb:+event exwm--connection 'xcb:ButtonPress #'exwm-input--on-ButtonPress) (xcb:+event exwm--connection 'xcb:ButtonRelease diff --git a/exwm-workspace.el b/exwm-workspace.el index 4789aeb18e..977cfe64bb 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -691,10 +691,13 @@ INDEX must not exceed the current number of workspaces." (outer-id (string-to-number (frame-parameter new-frame 'outer-window-id))) + (window-id (string-to-number + (frame-parameter new-frame 'window-id))) (frame-container (frame-parameter old-frame 'exwm-container)) (window (frame-root-window new-frame))) (set-frame-parameter new-frame 'exwm-outer-id outer-id) + (set-frame-parameter new-frame 'exwm-id window-id) (set-frame-parameter new-frame 'exwm-container frame-container) (make-frame-invisible new-frame) @@ -1149,10 +1152,12 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (setq exwm-workspace--list (nconc exwm-workspace--list (list frame))) (let ((outer-id (string-to-number (frame-parameter frame 'outer-window-id))) + (window-id (string-to-number (frame-parameter frame 'window-id))) (container (xcb:generate-id exwm--connection)) (workspace (xcb:generate-id exwm--connection))) ;; Save window IDs (set-frame-parameter frame 'exwm-outer-id outer-id) + (set-frame-parameter frame 'exwm-id window-id) (set-frame-parameter frame 'exwm-container container) (set-frame-parameter frame 'exwm-workspace workspace) ;; In case it's created by emacsclient. @@ -1356,9 +1361,13 @@ applied to all subsequently created X frames." (let ((outer-id (string-to-number (frame-parameter exwm-workspace--minibuffer 'outer-window-id))) + (window-id (string-to-number + (frame-parameter exwm-workspace--minibuffer + 'window-id))) (container (xcb:generate-id exwm--connection))) (set-frame-parameter exwm-workspace--minibuffer 'exwm-outer-id outer-id) + (set-frame-parameter exwm-workspace--minibuffer 'exwm-id window-id) (set-frame-parameter exwm-workspace--minibuffer 'exwm-container container) (xcb:+request exwm--connection diff --git a/exwm.el b/exwm.el index dd279bbcd0..afec1526ce 100644 --- a/exwm.el +++ b/exwm.el @@ -315,8 +315,7 @@ atom id buffer) (xcb:unmarshal obj data) (setq id (slot-value obj 'window) - atom (slot-value obj 'atom) - exwm-input--timestamp (slot-value obj 'time)) + atom (slot-value obj 'atom)) (setq buffer (exwm--id->buffer id)) (if (not (buffer-live-p buffer)) ;; Properties of unmanaged X windows. -- cgit 1.4.1 From 8e3fc3602f649fca47a61a2be2072521a352a62a Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 10 Aug 2016 19:45:52 +0800 Subject: Adapt for the changes in `window-configuration-change-hook' * exwm-layout.el (exwm-layout--refresh): Accept frame as an optional argument. (exwm-layout--init): Add `exwm-layout--refresh' to `window-size-change-functions' when appropriate. --- exwm-layout.el | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index 2886e43b4c..f5e0e149e9 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -314,11 +314,14 @@ selected by `other-buffer'." :data (vconcat (append clients-other clients-iconic clients clients-floating)))))) -(defun exwm-layout--refresh () +(defun exwm-layout--refresh (&optional frame) "Refresh layout." - (let ((frame (selected-frame)) - covered-buffers ;EXWM-buffers covered by a new X window. - vacated-windows ;Windows previously displaying EXWM-buffers. + ;; `window-size-change-functions' sets this argument while + ;; `window-configuration-change-hook' makes the frame selected. + (unless frame + (setq frame (selected-frame))) + (let (covered-buffers ;EXWM-buffers covered by a new X window. + vacated-windows ;Windows previously displaying EXWM-buffers. windows) (if (not (exwm-workspace--workspace-p frame)) (if (frame-parameter frame 'exwm-outer-id) @@ -566,6 +569,9 @@ See also `exwm-layout-enlarge-window'." "Initialize layout module." ;; Auto refresh layout (add-hook 'window-configuration-change-hook #'exwm-layout--refresh) + ;; The behavior of `window-configuration-change-hook' will be changed. + (when (fboundp 'window-pixel-width-before-size-change) + (add-hook 'window-size-change-functions #'exwm-layout--refresh)) (unless (exwm-workspace--minibuffer-own-frame-p) ;; Refresh when minibuffer grows (add-hook 'minibuffer-setup-hook #'exwm-layout--on-minibuffer-setup t) -- cgit 1.4.1 From db5128c1b9f77ecefd62a7150ccbdef33dd870af Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 12 Aug 2016 19:18:32 +0800 Subject: Fix CreateWindow attributes ; Also fix various compile warnings. * exwm-floating.el (exwm-floating--set-floating): * exwm-manage.el (exwm-manage--manage-window): * exwm-systemtray.el (exwm-systemtray--init): * exwm-workspace.el (exwm-workspace--add-frame-as-workspace) (exwm-workspace--init): * exwm.el (exwm--init-icccm-ewmh): Explicitly specify the class (InputOutput or InputOnly) and for an InputOutput X window the background pixmap when creating an X window. --- exwm-floating.el | 20 +++++++++++++++----- exwm-manage.el | 18 +++++++++++++----- exwm-systemtray.el | 17 ++++++++++++----- exwm-workspace.el | 49 ++++++++++++++++++++++++++++++++++--------------- exwm.el | 15 +++++++++++---- 5 files changed, 85 insertions(+), 34 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 7c5d811bf5..6f6cfecbfe 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -70,11 +70,14 @@ context of the corresponding buffer.") (defvar exwm-workspace--current) (defvar exwm-workspace--struts) +(defvar exwm-workspace--workareas) +(defvar exwm-workspace-current-index) (declare-function exwm-layout--refresh "exwm-layout.el" ()) (declare-function exwm-layout--show "exwm-layout.el" (id &optional window)) (declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) (declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") +(declare-function exwm-workspace--position "exwm-workspace.el" (frame)) (defun exwm-floating--set-floating (id) "Make window ID floating." @@ -191,12 +194,19 @@ context of the corresponding buffer.") ;; a child of the X window container. (xcb:+request exwm--connection (make-instance 'xcb:CreateWindow - :depth 0 :wid frame-container + :depth 0 + :wid frame-container :parent container - :x 0 :y 0 :width width :height height :border-width 0 - :class xcb:WindowClass:CopyFromParent - :visual 0 ;CopyFromParent - :value-mask xcb:CW:OverrideRedirect + :x 0 + :y 0 + :width width + :height height + :border-width 0 + :class xcb:WindowClass:InputOutput + :visual 0 + :value-mask (logior xcb:CW:BackPixmap + xcb:CW:OverrideRedirect) + :background-pixmap xcb:BackPixmap:ParentRelative :override-redirect 1)) ;; Put it at bottom. (xcb:+request exwm--connection diff --git a/exwm-manage.el b/exwm-manage.el index b88312455d..a8713ff714 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -83,6 +83,7 @@ corresponding buffer.") (defvar exwm-workspace--current) (defvar exwm-workspace--switch-history-outdated) (defvar exwm-workspace-current-index) +(defvar exwm-workspace--workareas) (declare-function exwm--update-window-type "exwm.el" (id &optional force)) (declare-function exwm--update-class "exwm.el" (id &optional force)) @@ -208,14 +209,21 @@ corresponding buffer.") (setq exwm--container (xcb:generate-id exwm--connection)) (xcb:+request exwm--connection (make-instance 'xcb:CreateWindow - :depth 0 :wid exwm--container + :depth 0 + :wid exwm--container :parent (frame-parameter exwm-workspace--current 'exwm-workspace) - :x 0 :y 0 :width 1 :height 1 :border-width 0 - :class xcb:WindowClass:CopyFromParent - :visual 0 ;CopyFromParent - :value-mask (logior xcb:CW:OverrideRedirect + :x 0 + :y 0 + :width 1 + :height 1 + :border-width 0 + :class xcb:WindowClass:InputOutput + :visual 0 + :value-mask (logior xcb:CW:BackPixmap + xcb:CW:OverrideRedirect xcb:CW:EventMask) + :background-pixmap xcb:BackPixmap:ParentRelative :override-redirect 1 :event-mask xcb:EventMask:SubstructureRedirect)) (exwm--debug diff --git a/exwm-systemtray.el b/exwm-systemtray.el index e978f64e15..36f7f3b601 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -334,10 +334,17 @@ You shall use the default value if using auto-hide minibuffer.") (setq exwm-systemtray--selection-owner-window id) (xcb:+request exwm-systemtray--connection (make-instance 'xcb:CreateWindow - :depth 0 :wid id :parent exwm--root - :x 0 :y 0 :width 1 :height 1 - :border-width 0 :class xcb:WindowClass:InputOnly - :visual 0 :value-mask xcb:CW:OverrideRedirect + :depth 0 + :wid id + :parent exwm--root + :x 0 + :y 0 + :width 1 + :height 1 + :border-width 0 + :class xcb:WindowClass:InputOnly + :visual 0 + :value-mask xcb:CW:OverrideRedirect :override-redirect 1)) ;; Get the selection ownership. (xcb:+request exwm-systemtray--connection @@ -397,7 +404,7 @@ You shall use the default value if using auto-hide minibuffer.") :width 1 :height exwm-systemtray-height :border-width 0 - :class xcb:WindowClass:CopyFromParent + :class xcb:WindowClass:InputOutput :visual 0 :value-mask (logior xcb:CW:BackPixmap xcb:CW:EventMask) :background-pixmap xcb:BackPixmap:ParentRelative diff --git a/exwm-workspace.el b/exwm-workspace.el index 977cfe64bb..3d3a542eaf 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1171,14 +1171,20 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (set-frame-parameter frame param (frame-parameter w param)))) (xcb:+request exwm--connection (make-instance 'xcb:CreateWindow - :depth 0 :wid workspace :parent exwm--root - :x 0 :y 0 + :depth 0 + :wid workspace + :parent exwm--root + :x 0 + :y 0 :width (x-display-pixel-width) :height (x-display-pixel-height) - :border-width 0 :class xcb:WindowClass:CopyFromParent - :visual 0 ;CopyFromParent - :value-mask (logior xcb:CW:OverrideRedirect + :border-width 0 + :class xcb:WindowClass:InputOutput + :visual 0 + :value-mask (logior xcb:CW:BackPixmap + xcb:CW:OverrideRedirect xcb:CW:EventMask) + :background-pixmap xcb:BackPixmap:ParentRelative :override-redirect 1 :event-mask xcb:EventMask:SubstructureRedirect)) (xcb:+request exwm--connection @@ -1188,13 +1194,19 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." :stack-mode xcb:StackMode:Below)) (xcb:+request exwm--connection (make-instance 'xcb:CreateWindow - :depth 0 :wid container :parent workspace - :x 0 :y 0 + :depth 0 + :wid container + :parent workspace + :x 0 + :y 0 :width (x-display-pixel-width) :height (x-display-pixel-height) - :border-width 0 :class xcb:WindowClass:CopyFromParent - :visual 0 ;CopyFromParent - :value-mask xcb:CW:OverrideRedirect + :border-width 0 + :class xcb:WindowClass:InputOutput + :visual 0 + :value-mask (logior xcb:CW:BackPixmap + xcb:CW:OverrideRedirect) + :background-pixmap xcb:BackPixmap:ParentRelative :override-redirect 1)) (exwm--debug (xcb:+request exwm--connection @@ -1372,12 +1384,19 @@ applied to all subsequently created X frames." container) (xcb:+request exwm--connection (make-instance 'xcb:CreateWindow - :depth 0 :wid container :parent exwm--root - :x -1 :y -1 :width 1 :height 1 + :depth 0 + :wid container + :parent exwm--root + :x 0 + :y 0 + :width 1 + :height 1 :border-width 0 - :class xcb:WindowClass:CopyFromParent - :visual 0 ;CopyFromParent - :value-mask xcb:CW:OverrideRedirect + :class xcb:WindowClass:InputOutput + :visual 0 + :value-mask (logior xcb:CW:BackPixmap + xcb:CW:OverrideRedirect) + :background-pixmap xcb:BackPixmap:ParentRelative :override-redirect 1)) (exwm--debug (xcb:+request exwm--connection diff --git a/exwm.el b/exwm.el index afec1526ce..0bc9701c03 100644 --- a/exwm.el +++ b/exwm.el @@ -609,10 +609,17 @@ (let ((new-id (xcb:generate-id exwm--connection))) (xcb:+request exwm--connection (make-instance 'xcb:CreateWindow - :depth 0 :wid new-id :parent exwm--root - :x -1 :y -1 :width 1 :height 1 - :border-width 0 :class xcb:WindowClass:CopyFromParent - :visual 0 :value-mask xcb:CW:OverrideRedirect + :depth 0 + :wid new-id + :parent exwm--root + :x 0 + :y 0 + :width 1 + :height 1 + :border-width 0 + :class xcb:WindowClass:InputOnly + :visual 0 + :value-mask xcb:CW:OverrideRedirect :override-redirect 1)) (dolist (i (list exwm--root new-id)) ;; Set _NET_SUPPORTING_WM_CHECK -- cgit 1.4.1 From 6bd85db30053b493e079893af0ef9668d134057d Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 12 Aug 2016 19:19:58 +0800 Subject: * exwm-manage.el (exwm-manage--scan): Check for possibly destroyed child. --- exwm-manage.el | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index a8713ff714..1dfd3c96cb 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -389,16 +389,23 @@ manager is shutting down." (defun exwm-manage--scan () "Search for existing windows and try to manage them." (let* ((tree (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:QueryTree :window exwm--root)))) + (make-instance 'xcb:QueryTree + :window exwm--root))) + reply) (dolist (i (slot-value tree 'children)) - (with-slots (override-redirect map-state) - (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:GetWindowAttributes :window i)) - (when (and (= 0 override-redirect) (= xcb:MapState:Viewable map-state)) - (xcb:+request exwm--connection - (make-instance 'xcb:UnmapWindow :window i)) - (xcb:flush exwm--connection) - (exwm-manage--manage-window i)))))) + (setq reply (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetWindowAttributes + :window i))) + ;; It's possible the X window has been destroyed. + (when reply + (with-slots (override-redirect map-state) reply + (when (and (= 0 override-redirect) + (= xcb:MapState:Viewable map-state)) + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow + :window i)) + (xcb:flush exwm--connection) + (exwm-manage--manage-window i))))))) (defvar exwm-manage--ping-lock nil "Non-nil indicates EXWM is pinging a window.") -- cgit 1.4.1 From 8a438c2c172b57fda19c36802bd183d44241af95 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 12 Aug 2016 19:22:18 +0800 Subject: Always put the desktop at bottom * exwm-manage.el (exwm-manage--desktop): New variable for recording the desktop X window. (exwm-manage--manage-window): Check for desktop. * exwm-workspace.el (exwm-workspace--resize-minibuffer-frame) (exwm-workspace--hide-minibuffer): Put the minibuffer container above desktop if any. --- exwm-manage.el | 12 ++++++++++++ exwm-workspace.el | 20 +++++++++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index 1dfd3c96cb..2ec21b3ed3 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -37,6 +37,8 @@ You can still make the X windows floating afterwards.") "Normal hook run after a window is just managed, in the context of the corresponding buffer.") +(defvar exwm-manage--desktop nil "The desktop X window.") + (defun exwm-manage--update-geometry (id &optional force) "Update window geometry." (with-current-buffer (exwm--id->buffer id) @@ -198,6 +200,16 @@ corresponding buffer.") :y (/ (- (exwm-workspace--current-height) height) 2))))) + ;; Check for desktop. + (when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DESKTOP exwm-window-type) + ;; There should be only one desktop X window. + (setq exwm-manage--desktop id) + ;; Put it at bottom. + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window id + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Below))) (xcb:flush exwm--connection) (setq exwm--id-buffer-alist (assq-delete-all id exwm--id-buffer-alist)) (let ((kill-buffer-query-functions nil)) diff --git a/exwm-workspace.el b/exwm-workspace.el index 3d3a542eaf..fec83d9147 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -29,6 +29,8 @@ (require 'exwm-core) +(defvar exwm-manage--desktop) + (defvar exwm-workspace-number 1 "Initial number of workspaces.") (defvar exwm-workspace--list nil "List of all workspaces (Emacs frames).") (defvar exwm-workspace--current nil "Current active workspace.") @@ -369,11 +371,17 @@ If the minibuffer is detached, this value is 0.") :value-mask (logior xcb:ConfigWindow:X xcb:ConfigWindow:Y xcb:ConfigWindow:Width + (if exwm-manage--desktop + xcb:ConfigWindow:Sibling + 0) xcb:ConfigWindow:StackMode) :x (aref workarea 0) :y y :width width - :stack-mode xcb:StackMode:Below)) + :sibling exwm-manage--desktop + :stack-mode (if exwm-manage--desktop + xcb:StackMode:Above + xcb:StackMode:Below))) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window (frame-parameter exwm-workspace--minibuffer @@ -987,8 +995,14 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (make-instance 'xcb:ConfigureWindow :window (frame-parameter exwm-workspace--minibuffer 'exwm-container) - :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:Below)) + :value-mask (logior (if exwm-manage--desktop + xcb:ConfigWindow:Sibling + 0) + xcb:ConfigWindow:StackMode) + :sibling exwm-manage--desktop + :stack-mode (if exwm-manage--desktop + xcb:StackMode:Above + xcb:StackMode:Below))) (xcb:flush exwm--connection)) (defun exwm-workspace--on-minibuffer-setup () -- cgit 1.4.1 From e4ecd792103c33e03ef9ff590d9c8e6b86431efd Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 12 Aug 2016 19:25:14 +0800 Subject: * exwm-manage.el (exwm-manage--unmanage-window): Do not clear struts when unmapping. --- exwm-manage.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exwm-manage.el b/exwm-manage.el index 2ec21b3ed3..60ff2e24b8 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -307,7 +307,8 @@ manager is shutting down." id buffer withdraw-only) (setq exwm--id-buffer-alist (assq-delete-all id exwm--id-buffer-alist)) ;; Update workspaces when a dock is destroyed. - (when (assq id exwm-workspace--id-struts-alist) + (when (and (null withdraw-only) + (assq id exwm-workspace--id-struts-alist)) (setq exwm-workspace--id-struts-alist (assq-delete-all id exwm-workspace--id-struts-alist)) (exwm-workspace--update-struts) -- cgit 1.4.1 From ebcc9591f3c1210efda007bec6852369b75fa8ad Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 12 Aug 2016 19:27:26 +0800 Subject: Check for _NET_WM_STATE_FULLSCREEN on managing * exwm-core.el (exwm--fullscreen): Removed. (exwm--ewmh-state): New variable for recording the _NET_WM_STATE hint. * exwm-core.el (exwm-mode-menu, exwm-mode-map): * exwm-layout.el (exwm-layout-set-fullscreen) (exwm-layout-unset-fullscreen): * exwm-manage.el (exwm-manage--unmanage-window) (exwm-manage--on-ConfigureRequest): * exwm-workspace.el (exwm-workspace-switch, exwm-workspace-swap) (exwm-workspace-move): * exwm.el (exwm-reset, exwm--on-ClientMessage): Use the new variable. * exwm-manage.el (exwm-manage--update-ewmh-state): New function for updating _NET_WM_STATE. (exwm-manage--manage-window): Update _NET_WM_STATE and check for _NET_WM_STATE_FULLSCREEN. --- exwm-core.el | 8 +++++--- exwm-layout.el | 9 +++++---- exwm-manage.el | 21 ++++++++++++++++++--- exwm-workspace.el | 15 +++++++++++---- exwm.el | 14 ++++++++++---- 5 files changed, 49 insertions(+), 18 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 226e7fc312..0c92d90339 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -87,7 +87,6 @@ (defvar-local exwm--frame nil) ;workspace frame (defvar-local exwm--floating-frame nil) ;floating frame (defvar-local exwm--mode-line-format nil) ;save mode-line-format -(defvar-local exwm--fullscreen nil) ;used in fullscreen (defvar-local exwm--floating-frame-position nil) ;used in fullscreen (defvar-local exwm--fixed-size nil) ;fixed size (defvar-local exwm--keyboard-grabbed nil) ;Keyboard grabbed. @@ -103,6 +102,7 @@ (defvar-local exwm-transient-for nil "WM_TRANSIENT_FOR.") (defvar-local exwm--protocols nil) (defvar-local exwm-state xcb:icccm:WM_STATE:NormalState "WM_STATE.") +(defvar-local exwm--ewmh-state nil "_NET_WM_STATE.") ;; _NET_WM_NORMAL_HINTS (defvar-local exwm--normal-hints-x nil) (defvar-local exwm--normal-hints-y nil) @@ -150,8 +150,10 @@ "*General*" "---" ["Toggle floating" exwm-floating-toggle-floating] - ["Enter fullscreen" exwm-layout-set-fullscreen (not exwm--fullscreen)] - ["Leave fullscreen" exwm-reset exwm--fullscreen] + ["Enter fullscreen" exwm-layout-set-fullscreen + (null (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state))] + ["Leave fullscreen" exwm-reset + (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)] ["Hide window" exwm-floating-hide exwm--floating-frame] "---" diff --git a/exwm-layout.el b/exwm-layout.el index f5e0e149e9..e8fd8e50c7 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -177,7 +177,7 @@ "Make window ID fullscreen." (interactive) (with-current-buffer (if id (exwm--id->buffer id) (window-buffer)) - (when exwm--fullscreen + (when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) (user-error "Already in full-screen mode.")) ;; Save the position of floating frame. (when exwm--floating-frame @@ -221,7 +221,7 @@ :window exwm--id :data (vector xcb:Atom:_NET_WM_STATE_FULLSCREEN))) (xcb:flush exwm--connection) - (setq exwm--fullscreen t) + (cl-pushnew xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) (call-interactively #'exwm-input-release-keyboard))) ;;;###autoload @@ -229,7 +229,7 @@ "Restore window from fullscreen state." (interactive) (with-current-buffer (if id (exwm--id->buffer id) (window-buffer)) - (unless exwm--fullscreen + (unless (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) (user-error "Not in full-screen mode.")) ;; Restore the size of this workspace. (exwm-workspace--set-fullscreen exwm--frame) @@ -256,7 +256,8 @@ (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_STATE :window exwm--id :data [])) (xcb:flush exwm--connection) - (setq exwm--fullscreen nil) + (setq exwm--ewmh-state + (delq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) (call-interactively #'exwm-input-grab-keyboard))) (defvar exwm-layout--other-buffer-exclude-exwm-mode-buffers nil diff --git a/exwm-manage.el b/exwm-manage.el index 60ff2e24b8..0f1a80b729 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -48,6 +48,16 @@ corresponding buffer.") (when reply ;nil when destroyed (setq exwm--geometry reply)))))) +(defun exwm-manage--update-ewmh-state (id) + "Update _NET_WM_STATE." + (with-current-buffer (exwm--id->buffer id) + (unless exwm--ewmh-state + (let ((reply (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:ewmh:get-_NET_WM_STATE + :window id)))) + (when reply + (setq exwm--ewmh-state (append (slot-value reply 'value) nil))))))) + ;; The _MOTIF_WM_HINTS atom (see for more details) ;; It's currently only used in 'exwm-manage' module (defvar exwm-manage--_MOTIF_WM_HINTS nil "_MOTIF_WM_HINTS atom.") @@ -286,7 +296,12 @@ corresponding buffer.") (< desktop (exwm-workspace--count))) (exwm-workspace-move-window desktop id) (exwm-workspace--set-desktop id))) + (exwm-manage--update-ewmh-state id) (with-current-buffer (exwm--id->buffer id) + (when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) + (setq exwm--ewmh-state + (delq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) + (exwm-layout-set-fullscreen id)) (run-hooks 'exwm-manage-finish-hook))))) (defvar exwm-workspace--id-struts-alist) @@ -373,7 +388,7 @@ manager is shutting down." (make-instance 'xcb:ReparentWindow :window window :parent exwm--root :x 0 :y 0)))) ;; Restore the workspace if this X window is currently fullscreen. - (when exwm--fullscreen + (when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) (exwm-workspace--set-fullscreen exwm--frame)) ;; Destroy the X window container (and the frame container if any). (xcb:+request exwm--connection @@ -552,7 +567,7 @@ border-width: %d; sibling: #x%x; stack-mode: %d" border-width sibling stack-mode) (if (and (setq buffer (exwm--id->buffer window)) (with-current-buffer buffer - (or exwm--fullscreen + (or (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) ;; Make sure it's a floating X window wanting to resize ;; itself. (or (not exwm--floating-frame) @@ -578,7 +593,7 @@ border-width: %d; sibling: #x%x; stack-mode: %d" ;; Send client message for managed windows (with-current-buffer buffer (setq edges - (if exwm--fullscreen + (if (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) (list 0 0 (exwm-workspace--current-width) (exwm-workspace--current-height)) diff --git a/exwm-workspace.el b/exwm-workspace.el index fec83d9147..befa8e00bc 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -445,7 +445,9 @@ PREFIX-DIGITS is a list of the digits introduced so far." The optional FORCE option is for internal use only." (interactive (list - (unless (and (eq major-mode 'exwm-mode) exwm--fullscreen) ;it's invisible + (unless (and (eq major-mode 'exwm-mode) + ;; The prompt is invisible in fullscreen mode. + (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) (let ((exwm-workspace--prompt-add-allowed t) (exwm-workspace--prompt-delete-allowed t)) (exwm-workspace--prompt-for-workspace "Switch to [+/-]: "))))) @@ -463,7 +465,8 @@ The optional FORCE option is for internal use only." :value-mask xcb:ConfigWindow:StackMode :stack-mode xcb:StackMode:Above)) ;; Raise X windows with struts set if there's no fullscreen X window. - (unless (buffer-local-value 'exwm--fullscreen (window-buffer window)) + (unless (with-current-buffer (window-buffer window) + (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) (dolist (pair exwm-workspace--id-struts-alist) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow @@ -537,7 +540,9 @@ deleted, moved, etc).") (defun exwm-workspace-swap (workspace1 workspace2) "Interchange position of WORKSPACE1 with that of WORKSPACE2." (interactive - (unless (and (eq major-mode 'exwm-mode) exwm--fullscreen) ;it's invisible + (unless (and (eq major-mode 'exwm-mode) + ;; The prompt is invisible in fullscreen mode. + (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) (let (w1 w2) (let ((exwm-workspace--prompt-add-allowed t) (exwm-workspace--prompt-delete-allowed t)) @@ -572,7 +577,9 @@ deleted, moved, etc).") When called interactively, prompt for a workspace and move current one just before it." (interactive - (unless (and (eq major-mode 'exwm-mode) exwm--fullscreen) ;it's invisible + (unless (and (eq major-mode 'exwm-mode) + ;; The prompt is invisible in fullscreen mode. + (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) (list exwm-workspace--current (exwm-workspace--position (exwm-workspace--prompt-for-workspace "Move workspace to: "))))) diff --git a/exwm.el b/exwm.el index 0bc9701c03..0544308bfe 100644 --- a/exwm.el +++ b/exwm.el @@ -78,7 +78,8 @@ (interactive) (with-current-buffer (window-buffer) (when (eq major-mode 'exwm-mode) - (when exwm--fullscreen (exwm-layout-unset-fullscreen)) + (when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) + (exwm-layout-unset-fullscreen)) ;; Force refresh (exwm-layout--refresh) (call-interactively #'exwm-input-grab-keyboard)))) @@ -464,12 +465,17 @@ (when (or (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN props) (memq xcb:Atom:_NET_WM_STATE_ABOVE props)) (cond ((= action xcb:ewmh:_NET_WM_STATE_ADD) - (unless exwm--fullscreen (exwm-layout-set-fullscreen id)) + (unless (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN + exwm--ewmh-state) + (exwm-layout-set-fullscreen id)) (push xcb:Atom:_NET_WM_STATE_FULLSCREEN props-new)) ((= action xcb:ewmh:_NET_WM_STATE_REMOVE) - (when exwm--fullscreen (exwm-layout-unset-fullscreen id))) + (when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN + exwm--ewmh-state) + (exwm-layout-unset-fullscreen id))) ((= action xcb:ewmh:_NET_WM_STATE_TOGGLE) - (if exwm--fullscreen + (if (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN + exwm--ewmh-state) (exwm-layout-unset-fullscreen id) (exwm-layout-set-fullscreen id) (push xcb:Atom:_NET_WM_STATE_FULLSCREEN props-new))))) -- cgit 1.4.1 From badf1c30a04cdc008f189c1cdef93c3db7ee7b6d Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 12 Aug 2016 20:28:05 +0800 Subject: Raise all docks/panels when switching workspace * exwm.el (exwm--update-struts-legacy, exwm--update-struts-partial): Always set the struts value, even it's nil. * exwm-workspace.el (exwm-workspace--update-struts): Check for nil struts values. --- exwm-workspace.el | 25 +++++++++++++------------ exwm.el | 20 ++++++-------------- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index befa8e00bc..0e078b7cc9 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -232,18 +232,19 @@ Value nil means to use the default position which is fixed at bottom, while (let (struts struts*) (dolist (pair exwm-workspace--id-struts-alist) (setq struts (cdr pair)) - (dotimes (i 4) - (when (/= 0 (aref struts i)) - (setq struts* - (vector (aref [left right top bottom] i) - (aref struts i) - (when (= 12 (length struts)) - (substring struts (+ 4 (* i 2)) (+ 6 (* i 2)))))) - (if (= 0 (mod i 2)) - ;; Make left/top processed first. - (push struts* exwm-workspace--struts) - (setq exwm-workspace--struts - (append exwm-workspace--struts (list struts*))))))))) + (when struts + (dotimes (i 4) + (when (/= 0 (aref struts i)) + (setq struts* + (vector (aref [left right top bottom] i) + (aref struts i) + (when (= 12 (length struts)) + (substring struts (+ 4 (* i 2)) (+ 6 (* i 2)))))) + (if (= 0 (mod i 2)) + ;; Make left/top processed first. + (push struts* exwm-workspace--struts) + (setq exwm-workspace--struts + (append exwm-workspace--struts (list struts*)))))))))) (defvar exwm-workspace--workareas nil "Workareas (struts excluded).") diff --git a/exwm.el b/exwm.el index 0544308bfe..67374430c1 100644 --- a/exwm.el +++ b/exwm.el @@ -268,13 +268,9 @@ :window id))) (when reply (setq struts (slot-value reply 'value)) - (if struts - (if pair - (setcdr pair struts) - (push (cons id struts) exwm-workspace--id-struts-alist)) - (when pair - (setq exwm-workspace--id-struts-alist - (assq-delete-all id exwm-workspace--id-struts-alist)))) + (if pair + (setcdr pair struts) + (push (cons id struts) exwm-workspace--id-struts-alist)) (exwm-workspace--update-struts)) ;; Update workareas and set _NET_WORKAREA. (exwm-workspace--update-workareas) @@ -291,13 +287,9 @@ (when reply (setq struts (slot-value reply 'value) pair (assq id exwm-workspace--id-struts-alist)) - (if struts - (if pair - (setcdr pair struts) - (push (cons id struts) exwm-workspace--id-struts-alist)) - (when pair - (setq exwm-workspace--id-struts-alist - (assq-delete-all id exwm-workspace--id-struts-alist)))) + (if pair + (setcdr pair struts) + (push (cons id struts) exwm-workspace--id-struts-alist)) (exwm-workspace--update-struts)) ;; Update workareas and set _NET_WORKAREA. (exwm-workspace--update-workareas) -- cgit 1.4.1 From 108b3949c26f55cfdb96866dedac3f68c893692f Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 12 Aug 2016 20:30:07 +0800 Subject: Fix _NET_DESKTOP_VIEWPORT * exwm-workspace.el (exwm-workspace--update-ewmh-props): * exwm.el (exwm--init-icccm-ewmh): Set _NET_DESKTOP_VIEWPORT on startup since it's a constant. --- exwm-workspace.el | 5 ----- exwm.el | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 0e078b7cc9..77c9417744 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1320,11 +1320,6 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." :window exwm--root :data num-workspaces)) ;; Set _NET_DESKTOP_GEOMETRY. (exwm-workspace--set-desktop-geometry) - ;; Set _NET_DESKTOP_VIEWPORT (we don't support large desktop). - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT - :window exwm--root - :data (make-vector (* 2 num-workspaces) 0))) ;; Update and set _NET_WORKAREA. (exwm-workspace--update-workareas) ;; Set _NET_VIRTUAL_ROOTS. diff --git a/exwm.el b/exwm.el index 67374430c1..a97236786b 100644 --- a/exwm.el +++ b/exwm.el @@ -628,6 +628,11 @@ (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_NAME :window i :data "EXWM")))) + ;; Set _NET_DESKTOP_VIEWPORT (we don't support large desktop). + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT + :window exwm--root + :data [0 0])) (xcb:flush exwm--connection)) (defun exwm--exit-icccm-ewmh () -- cgit 1.4.1 From fe653ba244758362ac83603a28389660834fcb5a Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 14 Aug 2016 12:24:40 +0800 Subject: Only check input focus for globally active model * exwm-input.el (exwm-input--set-focus): Partly revert 810b471. --- exwm-input.el | 61 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 668e49585e..e5828126f9 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -61,38 +61,37 @@ (defun exwm-input--set-focus (id) "Set input focus to window ID in a proper way." (when (exwm--id->buffer id) - (let ((focus (slot-value (xcb:+request-unchecked+reply exwm--connection + (with-current-buffer (exwm--id->buffer id) + (cond + ((and (not exwm--hints-input) + (memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols)) + (when (= (frame-parameter nil 'exwm-id) + (slot-value (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GetInputFocus)) - 'focus))) - (unless (= focus id) - (with-current-buffer (exwm--id->buffer id) - (cond - ((and (not exwm--hints-input) - (memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols)) - (when (= focus (frame-parameter nil 'exwm-id)) - (exwm--log "Focus on #x%x with WM_TAKE_FOCUS" id) - (exwm-input--update-timestamp - (lambda (timestamp id) - (let ((event (make-instance 'xcb:icccm:WM_TAKE_FOCUS - :window id - :time timestamp))) - (setq event (xcb:marshal event exwm--connection)) - (xcb:+request exwm--connection - (make-instance 'xcb:icccm:SendEvent - :destination id - :event event)) - (exwm-input--set-active-window id) - (xcb:flush exwm--connection))) - id))) - (t - (exwm--log "Focus on #x%x with SetInputFocus" id) - (xcb:+request exwm--connection - (make-instance 'xcb:SetInputFocus - :revert-to xcb:InputFocus:Parent - :focus id - :time xcb:Time:CurrentTime)) - (exwm-input--set-active-window id) - (xcb:flush exwm--connection)))))))) + 'focus)) + (exwm--log "Focus on #x%x with WM_TAKE_FOCUS" id) + (exwm-input--update-timestamp + (lambda (timestamp id) + (let ((event (make-instance 'xcb:icccm:WM_TAKE_FOCUS + :window id + :time timestamp))) + (setq event (xcb:marshal event exwm--connection)) + (xcb:+request exwm--connection + (make-instance 'xcb:icccm:SendEvent + :destination id + :event event)) + (exwm-input--set-active-window id) + (xcb:flush exwm--connection))) + id))) + (t + (exwm--log "Focus on #x%x with SetInputFocus" id) + (xcb:+request exwm--connection + (make-instance 'xcb:SetInputFocus + :revert-to xcb:InputFocus:Parent + :focus id + :time xcb:Time:CurrentTime)) + (exwm-input--set-active-window id) + (xcb:flush exwm--connection)))))) (defun exwm-input--update-timestamp (callback &rest args) "Fetch the latest timestamp from the server and feed it to CALLBACK. -- cgit 1.4.1 From ea0fbaf8cfa0d0969c32dce7779e569d34ded72a Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 14 Aug 2016 12:27:14 +0800 Subject: Bump version to 0.9 --- exwm.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm.el b/exwm.el index a97236786b..3898ba4095 100644 --- a/exwm.el +++ b/exwm.el @@ -4,8 +4,8 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0.8 -;; Package-Requires: ((xelb "0.10")) +;; Version: 0.9 +;; Package-Requires: ((xelb "0.11")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From c97ceadce5c699999b103463b212207f15280ba6 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 14 Aug 2016 18:16:55 +0800 Subject: Implement compositing manager module * exwm-cm.el: New compositing manager module. --- exwm-cm.el | 1778 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1778 insertions(+) create mode 100644 exwm-cm.el diff --git a/exwm-cm.el b/exwm-cm.el new file mode 100644 index 0000000000..8e019bbc75 --- /dev/null +++ b/exwm-cm.el @@ -0,0 +1,1778 @@ +;;; exwm-cm.el --- Compositing Manager for EXWM -*- lexical-binding: t -*- + +;; Copyright (C) 2016 Free Software Foundation, Inc. + +;; Author: Chris Feng + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Commentary: + +;; This module provides a compositing manager (CM) for EXWM, mainly to +;; enable transparency support. + +;; Usage: +;; Add following lines to .emacs and modify accordingly: +;; +;; (require 'exwm-cm) +;; ;; Make all Emacs frames opaque. +;; (setq window-system-default-frame-alist '((x . ((alpha . 100))))) +;; ;; Assign everything else a 80% opacity. +;; (setq exwm-cm-opacity 80) +;; (exwm-cm-enable) +;; +;; With the last line this CM would be started with EXWM. You can also +;; start and stop this CM with `exwm-cm-start' and `exwm-cm-stop' at any +;; time. + +;; Theory: +;; Due to its unique way of managing X windows, EXWM can not work with +;; any existing CMs. And this CM, designed specifically for EXWM, +;; probably won't work well with other WMs, too. The theories behind +;; all CMs are basically the same, some peculiarities of this CM are +;; summarized as the following sections. + +;; + Data structures: +;; This CM organizes all X windows concerned with compositing in a +;; tree hierarchy. Below is a stripped-down version of such tree with +;; each node representing an X window (except the root placeholder), +;; +;; (nil +;; (root-xwin +;; (unmanaged-xwin) +;; (workspace-container +;; (unmanaged-xwin) +;; (xwin-container +;; (xwin) +;; (floating-frame-container +;; (floating-frame))) +;; (xwin-container +;; (xwin)) +;; (workspace-frame-container +;; (workspace-frame))) +;; (minibuffer-frame-container +;; (minibuffer-frame)))) +;; +;; where +;; - nodes with non-nil CDRs are containers, +;; - siblings are arranged in stacking order (top to bottom), +;; - and "managed" and "unmanaged" are in WM's sense. +;; +;; During a painting process, the tree is traversed starting from the +;; root node, with each leaf visited and painted. The attributes of +;; each X window (position, size, etc) are recorded as an instance of +;; class `exwm-cm--attr'. Such instance is associated with the +;; corresponding X window ID through a hash table. The instance also +;; contains a slot pointing to a subtree of the aforementioned tree, +;; with the root node being the parent of the X window. This makes it +;; convenient to carry out operations such as insertion, deletion, +;; restacking and reparenting. + +;; + Compositing strategies: +;; - Only leaves are painted, since branches (containers) are always +;; invisible. +;; - The root X window is painted separately. +;; - Siblings below a workspace frame container are not painted; they +;; are considered hidden. +;; - Only the top workspace in one (RandR) output is painted. +;; - Workspace frames and floating frames are always clipped by its +;; Emacs windows displaying `exwm-mode' buffers, therefore they +;; don't block X windows. + +;; Reference: +;; + xcompmgr (http://cgit.freedesktop.org/xorg/app/xcompmgr/) + +;;; Code: + +(require 'xcb-composite) +(require 'xcb-damage) +(require 'xcb-ewmh) +(require 'xcb-icccm) +(require 'xcb-renderutil) +(require 'xcb-shape) + +(require 'exwm-core) +(require 'exwm-workspace) +(require 'exwm-manage) + +(defconst exwm-cm--OPAQUE (float #xFFFFFFFF) + "The opacity value of the _NET_WM_WINDOW_OPACITY property.") +(defvar exwm-cm--_NET_WM_WINDOW_OPACITY nil "The _NET_WM_WINDOW_OPACITY atom.") +(defvar exwm-cm-opacity nil + "The default value of opacity when it's not explicitly specified. + +The value should be a floating number between 0 (transparent) and 100 +(opaque). A value of nil also means opaque.") + +(defvar exwm-cm--hash nil + "The hash table associating X window IDs to their attributes.") + +(defvar exwm-cm--conn nil "The X connection used by the CM.") +(defvar exwm-cm--buffer nil "The rendering buffer.") +(defvar exwm-cm--depth nil "Default depth.") +(defvar exwm-cm--clip-changed t "Whether clip has changed.") +(defvar exwm-cm--damages nil "All damaged regions.") +(defvar exwm-cm--expose-rectangles nil + "Used by Expose event handler to collect exposed regions.") + +(defvar exwm-cm--background nil "The background (render) picture.") +(defvar exwm-cm--background-atom-names '("_XROOTPMAP_ID" "_XSETROOT_ID") + "Property names for background pixmap.") +(defvar exwm-cm--background-atoms nil "Interned atoms of the property names.") + +(defun exwm-cm--get-opacity (xwin) + "Get the opacity of X window XWIN. + +The value is between 0 (fully transparent) to #xFFFFFFFF (opaque)." + (let ((reply (xcb:+request-unchecked+reply exwm-cm--conn + (make-instance 'xcb:icccm:-GetProperty-single + :window xwin + :property exwm-cm--_NET_WM_WINDOW_OPACITY + :type xcb:Atom:CARDINAL)))) + ;; The X window might have already been destroyed. + (when reply + (slot-value reply 'value)))) + +(defun exwm-cm-set-opacity (xwin opacity) + "Set the opacity of X window XWIN to OPACITY. + +The value is between 0 (fully transparent) to 100 (opaque). + +If called interactively, XWIN would be the selected X window." + (interactive + (list (exwm--buffer->id (window-buffer)) + (read-number "Opacity (0 ~ 100): " 100))) + (when (and xwin + (<= 0 opacity 100)) + (setq opacity (round (* exwm-cm--OPAQUE (/ opacity 100.0)))) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:icccm:-ChangeProperty-single + :window xwin + :property exwm-cm--_NET_WM_WINDOW_OPACITY + :type xcb:Atom:CARDINAL + :data opacity)) + (xcb:flush exwm-cm--conn))) + +(defclass exwm-cm--attr () + ( + ;; The entity associated with this X window; can be a frame, a buffer + ;; or nil. + (entity :initform nil) + ;; The subtree of which the root node is the parent of this X window. + (tree :initarg :tree) + ;; Geometry. + (x :initarg :x) + (y :initarg :y) + (width :initarg :width) + (height :initarg :height) + ;; X window attributes. + (visual :initarg :visual) + (class :initarg :class) + ;; The opacity of this X window; can be 0 ~ #xFFFE or nil. + (opacity :initform nil) + ;; Determine whether this X window should be treated as opaque or + ;; transparent; can be nil (opaque), 'argb or 'transparent (both + ;; should be treated as transparent). + (mode :initform nil) + ;; The (render) picture of this X window. + (picture :initform nil) + ;; The 1x1 (render) picture with only alpha channel. + (alpha-picture :initform nil) + ;; Whether this X window is ever damaged. + (damaged :initform nil) + ;; The damage object monitoring this X window. + (damage :initarg :damage) + ;; The bounding region of this X window (can be irregular). + (border-size :initform nil) + ;; The rectangular bounding region of this X window. + (extents :initform nil) + ;; The region require repainting (used for transparent X windows). + (border-clip :initform nil) + ;; Shape-related parameters. + (shaped :initform nil) + (shape-x :initarg :shape-x) + (shape-y :initarg :shape-y) + (shape-width :initarg :shape-width) + (shape-height :initarg :shape-height)) + :documentation "Attributes of an X window.") + +(defsubst exwm-cm--xwin->attr (xwin) + "Get the attributes of X window XWIN." + (gethash xwin exwm-cm--hash)) + +(defsubst exwm-cm--get-tree (xwin) + "Get the subtree of the parent of X window XWIN." + (slot-value (exwm-cm--xwin->attr xwin) 'tree)) + +(defsubst exwm-cm--set-tree (xwin tree) + "Reparent X window XWIN to another tree TREE." + (setf (slot-value (exwm-cm--xwin->attr xwin) 'tree) tree)) + +(defsubst exwm-cm--get-parent (xwin) + "Get the parent of X window XWIN." + (car (exwm-cm--get-tree xwin))) + +(defsubst exwm-cm--get-siblings (xwin) + "Get a list of subtrees of the siblings of X window XWIN" + (cdr (exwm-cm--get-tree xwin))) + +(defsubst exwm-cm--get-subtree (xwin) + "Get the subtree of which the X window XWIN is the root node." + (assq xwin (exwm-cm--get-siblings xwin))) + +(defun exwm-cm--create-attr (xwin tree x y width height) + "Create attributes for X window XWIN. + +TREE is the subtree and the parent of this X window is the tree's root. +X and Y specify the position with regard to the root X window. WIDTH +and HEIGHT specify the size of the X window." + (let (visual class map-state damage attr) + (cond + ((= xwin exwm--root) + ;; Redirect all subwindows to off-screen storage. + (xcb:+request exwm-cm--conn + (make-instance 'xcb:composite:RedirectSubwindows + :window exwm--root + :update xcb:composite:Redirect:Manual)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:ChangeWindowAttributes + :window xwin + :value-mask xcb:CW:EventMask + :event-mask (logior xcb:EventMask:StructureNotify + xcb:EventMask:PropertyChange + xcb:EventMask:SubstructureNotify + xcb:EventMask:Exposure))) + (setq visual (slot-value (car (slot-value (xcb:get-setup exwm-cm--conn) + 'roots)) + 'root-visual) + class xcb:WindowClass:InputOutput)) + ((eq xwin exwm-manage--desktop) + ;; Ignore any desktop; paint the background ourselves. + (setq visual 0 + class xcb:WindowClass:InputOnly + map-state xcb:MapState:Unmapped)) + (t + ;; Redirect this window to off-screen storage, or the content + ;; would be mirrored to its parent. + (xcb:+request exwm-cm--conn + (make-instance 'xcb:composite:RedirectWindow + :window xwin + :update xcb:composite:Redirect:Manual)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:ChangeWindowAttributes + :window xwin + :value-mask xcb:CW:EventMask + :event-mask (logior xcb:EventMask:StructureNotify + xcb:EventMask:PropertyChange))) + (let ((reply (xcb:+request-unchecked+reply exwm-cm--conn + (make-instance 'xcb:GetWindowAttributes + :window xwin)))) + (if reply + (with-slots ((visual* visual) + (class* class) + (map-state* map-state)) + reply + (setq visual visual* + class class* + map-state map-state*)) + ;; The X window has been destroyed actually. It'll get + ;; removed by a DestroyNotify event. + (setq visual 0 + class xcb:WindowClass:InputOnly + map-state xcb:MapState:Unmapped))) + (when (/= class xcb:WindowClass:InputOnly) + (setq damage (xcb:generate-id exwm-cm--conn)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:damage:Create + :damage damage + :drawable xwin + :level xcb:damage:ReportLevel:NonEmpty)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:shape:SelectInput + :destination-window xwin + :enable 1))))) + (setq attr (make-instance 'exwm-cm--attr + :tree tree + :x x + :y y + :width width + :height height + :visual visual + :class class + :damage damage + :shape-x x + :shape-y y + :shape-width width + :shape-height height)) + (puthash xwin attr exwm-cm--hash) + (unless (or (= xwin exwm--root) + (= class xcb:WindowClass:InputOnly)) + (exwm-cm--update-opacity xwin) + (when (= map-state xcb:MapState:Viewable) + (exwm-cm--map-xwin xwin t))))) + +(defun exwm-cm--update-geometry (xwin x y width height &optional above-sibling) + "Update the geometry of X window XWIN. + +X, Y, WIDTH and HEIGHT have the same meaning with the arguments used in +`exwm-cm--create-attr'. If ABOVE-SIBLING is non-nil, restack XWIN with +`exwm-cm--restack.'" + (with-slots ((x* x) + (y* y) + (width* width) + (height* height) + extents shaped shape-x shape-y shape-width shape-height) + (exwm-cm--xwin->attr xwin) + (let ((stack-changed (and above-sibling + (exwm-cm--restack xwin above-sibling))) + (position-changed (or (and x (/= x x*)) + (and y (/= y y*)))) + (size-changed (or (and width (/= width width*)) + (and height (/= height height*)))) + subtree dx dy damage new-extents) + (when position-changed + (setq subtree (exwm-cm--get-subtree xwin) + dx (- x x*) + dy (- y y*)) + (dolist (node (cdr subtree)) + (with-slots (x y) (exwm-cm--xwin->attr (car node)) + (exwm--log "(CM) #x%X(*): @%+d%+d => @%+d%+d" + (car node) x y (+ x dx) (+ y dy)) + (exwm-cm--update-geometry (car node) (+ x dx) (+ y dy) nil nil))) + (exwm--log "(CM) #x%X: @%+d%+d => @%+d%+d" xwin x* y* x y) + (setf x* x + y* y) + (cl-incf shape-x dx) + (cl-incf shape-y dy)) + (when size-changed + (setf width* width + height* height) + (unless shaped + (setf shape-width width + shape-height height))) + (when (or stack-changed position-changed size-changed) + (setq damage (xcb:generate-id exwm-cm--conn) + new-extents (xcb:generate-id exwm-cm--conn)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:CreateRegion + :region damage + :rectangles nil)) + (when extents + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:CopyRegion + :source extents + :destination damage))) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:CreateRegion + :region new-extents + :rectangles (list (make-instance 'xcb:RECTANGLE + :x x* + :y y* + :width width* + :height height*)))) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:UnionRegion + :source1 damage + :source2 new-extents + :destination damage)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:DestroyRegion + :region new-extents)) + (exwm-cm--add-damage damage))))) + +(defun exwm-cm--update-opacity (xwin) + "Update the opacity of X window XWIN." + (with-slots (visual opacity mode alpha-picture extents) + (exwm-cm--xwin->attr xwin) + (let (format forminfo) + ;; Get the opacity. + (setf opacity (exwm-cm--get-opacity xwin)) + (if opacity + (setf opacity (round (* #xFFFF (/ opacity exwm-cm--OPAQUE)))) + (when (numberp exwm-cm-opacity) + (setf opacity (round (* #xFFFF (/ exwm-cm-opacity 100.0)))))) + (when (and opacity + (>= opacity #xFFFF)) + (setf opacity nil)) + ;; Determine the mode of the X window. + (setq format (xcb:renderutil:find-visual-format + (xcb:renderutil:query-formats exwm-cm--conn) visual)) + (when format + (catch 'break + (dolist (f (slot-value (xcb:renderutil:query-formats exwm-cm--conn) + 'formats)) + (when (eq format (slot-value f 'id)) + (setq forminfo f) + (throw 'break nil))))) + (if (and forminfo + (eq xcb:render:PictType:Direct (slot-value forminfo 'type)) + (/= 0 (slot-value (slot-value forminfo 'direct) 'alpha-mask))) + (setf mode 'argb) + (if opacity + (setf mode 'transparent) + (setf mode nil))) + ;; Clear resources. + (when alpha-picture + (xcb:+request exwm-cm--conn + (make-instance 'xcb:render:FreePicture + :picture alpha-picture)) + (setf alpha-picture nil)) + (when extents + (let ((damage (xcb:generate-id exwm-cm--conn))) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:CreateRegion + :region damage + :rectangles nil)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:CopyRegion + :source extents + :destination damage)) + (exwm-cm--add-damage damage)))))) + +(defsubst exwm-cm--push (newelt place) + "Similar to `push' but preserve the reference." + (let ((oldelt (car place))) + (setf (car place) newelt + (cdr place) (cons oldelt (cdr place))))) + +(defsubst exwm-cm--delq (elt list) + "Similar to `delq' but preserve the reference." + (if (eq elt (car list)) + (setf (car list) (cadr list) + (cdr list) (cddr list)) + (delq elt list))) + +(defsubst exwm-cm--assq-delete-all (key alist) + "Similar to `assq-delete-all' but preserve the reference." + (when (eq key (caar alist)) + (setf (car alist) (cadr alist) + (cdr alist) (cddr alist))) + (assq-delete-all key alist)) + +(defun exwm-cm--create-tree (&optional xwin) + "Create a tree with XWIN being the root node." + (let (tree0 x0 y0 children containers) + ;; Get the position of this branch. + (if xwin + (with-slots (tree x y) (exwm-cm--xwin->attr xwin) + (setq tree0 (assq xwin (cdr tree)) + x0 x + y0 y)) + (setq tree0 (list nil) + x0 0 + y0 0)) + ;; Get children nodes. + (if (null xwin) + (setq children (list exwm--root)) + (setq children + (reverse (slot-value (xcb:+request-unchecked+reply exwm-cm--conn + (make-instance 'xcb:QueryTree + :window xwin)) + 'children)))) + ;; Get container nodes. + ;; Floating frame containers are determined dynamically. + (cond + ((null xwin) + (setq containers `((,exwm--root)))) + ((= xwin exwm--root) + ;; Workspace containers and the minibuffer frame container. + (setq containers (mapcar (lambda (f) + (cons (frame-parameter f 'exwm-workspace) f)) + exwm-workspace--list)) + (when (exwm-workspace--minibuffer-own-frame-p) + (push (cons + (frame-parameter exwm-workspace--minibuffer 'exwm-container) + exwm-workspace--minibuffer) + containers))) + ;; No containers in the minibuffer container. + ((and (exwm-workspace--minibuffer-own-frame-p) + (= xwin + (frame-parameter exwm-workspace--minibuffer 'exwm-container)))) + ((= exwm--root + (slot-value (xcb:+request-unchecked+reply exwm-cm--conn + (make-instance 'xcb:QueryTree + :window xwin)) + 'parent)) + ;; Managed X window containers and the workspace frame container. + (let (frame) + (catch 'break + (dolist (f exwm-workspace--list) + (when (= xwin (frame-parameter f 'exwm-workspace)) + (setq frame f) + (throw 'break nil)))) + (cl-assert frame) + (dolist (pair exwm--id-buffer-alist) + (with-current-buffer (cdr pair) + (when (eq frame exwm--frame) + (push (cons exwm--container (cdr pair)) containers)))) + (push (cons (frame-parameter frame 'exwm-container) frame) + containers)))) + ;; Create subnodes. + (dolist (xwin children) + ;; Create attributes. + (let ((reply (xcb:+request-unchecked+reply exwm-cm--conn + (make-instance 'xcb:GetGeometry + :drawable xwin)))) + ;; It's possible the X window has been destroyed. + (if (null reply) + (setq xwin nil) + (when reply + (with-slots (x y width height) reply + (exwm-cm--create-attr xwin tree0 + (+ x x0) (+ y y0) width height)) + ;; Insert the node. + (setcdr (or (last (cdr tree0)) tree0) `((,xwin)))))) + (cond + ((null xwin)) + ((assq xwin containers) + ;; A branch. Repeat the process. + (exwm-cm--create-tree xwin) + (let ((entity (cdr (assq xwin containers))) + entity-xwin) + (when entity + (setq entity-xwin (if (framep entity) + (frame-parameter entity 'exwm-outer-id) + (buffer-local-value 'exwm--id entity))) + (setf (slot-value (exwm-cm--xwin->attr entity-xwin) 'entity) entity + (slot-value (exwm-cm--xwin->attr xwin) 'entity) entity) + (let ((tmp (exwm-cm--get-parent entity-xwin))) + (when (/= xwin tmp) + ;; Workspace frame container. + (setf (slot-value (exwm-cm--xwin->attr tmp) 'entity) + entity)))))) + ((and (null containers) + (exwm--id->buffer xwin)) + ;; A leaf but a floating frame container might follow. + (with-current-buffer (exwm--id->buffer xwin) + (when exwm--floating-frame + (push (cons (frame-parameter exwm--floating-frame 'exwm-container) + exwm--floating-frame) + containers)))))))) + +(defun exwm-cm--restack (xwin above-sibling) + "Restack X window XWIN so as to it's exactly on top of ABOVE-SIBLING." + (let ((siblings (exwm-cm--get-siblings xwin)) + node tmp) + (unless (= 1 (length siblings)) + (setq node (assq xwin siblings)) + (if (= above-sibling xcb:Window:None) + ;; Put at bottom. + (unless (eq node (cdr (last siblings))) + (exwm-cm--delq node siblings) + (setcdr (last siblings) (list node)) + ;; Set the return value. + t) + ;; Insert before the sibling. + (setq tmp siblings) + (while (and tmp + (/= above-sibling (caar tmp))) + (setq tmp (cdr tmp))) + (cl-assert tmp) + ;; Check if it's already at the requested position. + (unless (eq tmp (cdr siblings)) + (exwm-cm--delq node siblings) + (exwm-cm--push node tmp) + ;; Set the return value. + t))))) + +(declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) + +(defun exwm-cm--paint-tree (tree region &optional force-opaque frame-clip) + "Paint the tree TREE, with REGION specifying the clipping region. + +If FORCE-OPAQUE is non-nil, all X windows painted in this tree is +assumed opaque. FRAME-CLIP specifies the region should be clipped when +painting a frame." + (unless tree + (setq tree (exwm-cm--get-tree exwm--root))) + (let ((root (car tree)) + xwin attr entity current output outputs queue rectangles) + ;; Paint subtrees. + (catch 'break + (dolist (subtree (cdr tree)) + (setq xwin (car subtree) + attr (exwm-cm--xwin->attr xwin)) + (cond + ;; Skip destroyed X windows. + ((null attr)) + ;; Skip InputOnly X windows. + ((= xcb:WindowClass:InputOnly + (slot-value attr 'class))) + ((and (eq root exwm--root) + (frame-live-p (setq entity (slot-value attr 'entity))) + (if (eq entity exwm-workspace--minibuffer) + ;; Skip the minibuffer if the current workspace is + ;; already painted. + (unless (exwm-workspace--minibuffer-attached-p) + current) + ;; Skip lower workspaces on visited RandR output. + ;; If RandR is not enabled, it'll just paint the first. + (memq (setq output (frame-parameter entity + 'exwm-randr-output)) + outputs)))) + ((cdr subtree) + ;; Paint the subtree. + (setq entity (slot-value attr 'entity)) + (let (fullscreen clip) + (cond + ((buffer-live-p entity) + (with-current-buffer entity + ;; Collect frame clip but exclude fullscreen and + ;; floating X windows. + (setq fullscreen (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN + exwm--ewmh-state)) + (when (and (null fullscreen) + ;; In case it's hidden. + (null (exwm-layout--iconic-state-p)) + ;; The buffer of a floating X windows is not + ;; displayed on a workspace frame. + (null exwm--floating-frame) + ;; Opaque regions are always clipped. + (slot-value (exwm-cm--xwin->attr xwin) 'mode)) + ;; Prepare rectangles to clip the workspace frame. + (with-slots (x y width height) (exwm-cm--xwin->attr xwin) + (push (make-instance 'xcb:RECTANGLE + :x x + :y y + :width width + :height height) + rectangles))))) + ((and rectangles + (frame-live-p entity)) + ;; Prepare region to clip the frame. + (setq clip (xcb:generate-id exwm-cm--conn)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:CreateRegion + :region clip + :rectangles rectangles)))) + (setq queue + (nconc (exwm-cm--paint-tree subtree region fullscreen clip) + queue)) + (when fullscreen + ;; Fullscreen X windows are always opaque thus occludes + ;; anything in this workspace. + (throw 'break 'fullscreen)) + (when clip + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:DestroyRegion + :region clip)))) + (if (not (eq root exwm--root)) + ;; Avoid painting any siblings below the workspace frame + ;; container. + (when (exwm-workspace--workspace-p (slot-value attr 'entity)) + (throw 'break nil)) + ;; Save some status. + (when (and (frame-live-p entity) + (not (eq entity exwm-workspace--minibuffer))) + (push output outputs) + (when (eq entity exwm-workspace--current) + (setq current t))))) + ((and force-opaque + (slot-value attr 'damaged)) + (exwm-cm--paint-opaque xwin region t)) + ((slot-value attr 'damaged) + ;; Paint damaged leaf. + (setq entity (slot-value attr 'entity)) + (when (slot-value attr 'mode) + (push xwin queue)) + (cond + ((buffer-live-p entity) + (with-current-buffer entity + (cl-assert (= xwin exwm--id)) + (when (and exwm--floating-frame + ;; Opaque regions are always clipped. + (slot-value (exwm-cm--xwin->attr xwin) 'mode)) + ;; Prepare rectangles to clip the floating frame. + (with-slots (x y width height) (exwm-cm--xwin->attr xwin) + (push (make-instance 'xcb:RECTANGLE + :x x + :y y + :width width + :height height) + rectangles))))) + ((and frame-clip + (frame-live-p entity)) + ;; Apply frame clip. + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:IntersectRegion + :source1 region + :source2 frame-clip + :destination frame-clip)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:SubtractRegion + :source1 region + :source2 frame-clip + :destination region)))) + (exwm-cm--paint-opaque xwin region) + (when (and frame-clip + (frame-live-p entity)) + ;; Restore frame clip. + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:UnionRegion + :source1 region + :source2 frame-clip + :destination region))))))) + ;; Return the queue. + queue)) + +(defun exwm-cm--paint-opaque (xwin region &optional force-opaque) + "Paint an X window XWIN clipped by region REGION if XWIN is opaque. + +Also update the attributes of XWIN and clip the region." + (with-slots (x y width height visual mode picture + border-size extents border-clip) + (exwm-cm--xwin->attr xwin) + ;; Prepare the X window picture. + (unless picture + (setf picture (xcb:generate-id exwm-cm--conn)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:render:CreatePicture + :pid picture + :drawable xwin + :format (xcb:renderutil:find-visual-format + (xcb:renderutil:query-formats exwm-cm--conn) + visual) + :value-mask 0))) + ;; Clear cached resources if clip changed. + (when exwm-cm--clip-changed + (when border-size + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:DestroyRegion + :region border-size)) + (setf border-size nil)) + (when extents + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:DestroyRegion + :region extents)) + (setf extents nil)) + (when border-clip + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:DestroyRegion + :region border-clip)) + (setf border-clip nil))) + ;; Retrieve the border. + (unless border-size + (setf border-size (xcb:generate-id exwm-cm--conn)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:CreateRegionFromWindow + :region border-size + :window xwin + :kind xcb:shape:SK:Bounding)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:TranslateRegion + :region border-size + :dx x + :dy y))) + ;; Retrieve the extents. + (unless extents + (setf extents (xcb:generate-id exwm-cm--conn)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:CreateRegion + :region extents + :rectangles (list (make-instance 'xcb:RECTANGLE + :x x + :y y + :width width + :height height))))) + (cond + ((and mode + (null force-opaque)) + ;; Calculate clipped border for the transparent X window. + (unless border-clip + (setf border-clip (xcb:generate-id exwm-cm--conn)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:CreateRegion + :region border-clip + :rectangles nil)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:CopyRegion + :source region + :destination border-clip)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:IntersectRegion + :source1 border-clip + :source2 border-size + :destination border-clip)))) + (t + ;; Clip & render for the opaque X window. + ;; Set the clip region for the rendering buffer. + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:SetPictureClipRegion + :picture exwm-cm--buffer + :region region + :x-origin 0 + :y-origin 0)) + ;; Clip the region with border. + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:SubtractRegion + :source1 region + :source2 border-size + :destination region)) + ;; Render the picture to the buffer. + (xcb:+request exwm-cm--conn + (make-instance 'xcb:render:Composite + :op xcb:render:PictOp:Src + :src picture + :mask xcb:render:Picture:None + :dst exwm-cm--buffer + :src-x 0 + :src-y 0 + :mask-x 0 + :mask-y 0 + :dst-x x + :dst-y y + :width width + :height height)))))) + +(defun exwm-cm--paint-transparent (xwin) + "Paint a transparent X window XWIN." + (with-slots (x y width height opacity picture alpha-picture border-clip) + (exwm-cm--xwin->attr xwin) + ;; Prepare the alpha picture for transparent X windows. + (when (and opacity (null alpha-picture)) + (setf alpha-picture (xcb:generate-id exwm-cm--conn)) + (let ((pixmap (xcb:generate-id exwm-cm--conn))) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:CreatePixmap + :depth 8 + :pid pixmap + :drawable exwm--root + :width 1 + :height 1)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:render:CreatePicture + :pid alpha-picture + :drawable pixmap + :format (xcb:renderutil:find-standard + (xcb:renderutil:query-formats + exwm-cm--conn) + xcb:renderutil:PICT_STANDARD:A_8) + :value-mask xcb:render:CP:Repeat + :repeat 1)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:render:FillRectangles + :op xcb:render:PictOp:Src + :dst alpha-picture + :color (make-instance 'xcb:render:COLOR + :red 0 + :green 0 + :blue 0 + :alpha opacity) + :rects (list (make-instance 'xcb:RECTANGLE + :x 0 + :y 0 + :width 1 + :height 1)))))) + ;; Set the clip region for the rendering buffer. + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:SetPictureClipRegion + :picture exwm-cm--buffer + :region border-clip + :x-origin 0 + :y-origin 0)) + ;; Render the picture to the buffer. + (xcb:+request exwm-cm--conn + (make-instance 'xcb:render:Composite + :op xcb:render:PictOp:Over + :src picture + :mask (or alpha-picture xcb:render:Picture:None) + :dst exwm-cm--buffer + :src-x 0 + :src-y 0 + :mask-x 0 + :mask-y 0 + :dst-x x + :dst-y y + :width width + :height height)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:DestroyRegion + :region border-clip)) + (setf border-clip nil))) + +(defun exwm-cm--paint (&optional region) + "Paint the whole tree within clipping region REGION. + +If REGION is omitted, `exwm-cm--damages' is assumed. If it's t, paint +the whole screen." + ;; Prepare the clipping region. + (cond + ((null region) + (when exwm-cm--damages + (setq region exwm-cm--damages))) + ((eq region t) + (with-slots (width height) (exwm-cm--xwin->attr exwm--root) + (let ((rect (make-instance 'xcb:RECTANGLE + :x 0 + :y 0 + :width width + :height height))) + (setq region (xcb:generate-id exwm-cm--conn)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:CreateRegion + :region region + :rectangles (list rect))))))) + (when region + ;; Prepare the rendering buffer. + (unless exwm-cm--buffer + (let ((pixmap (xcb:generate-id exwm-cm--conn)) + (picture (xcb:generate-id exwm-cm--conn))) + (setq exwm-cm--buffer picture) + (with-slots (width height visual) (exwm-cm--xwin->attr exwm--root) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:CreatePixmap + :depth exwm-cm--depth + :pid pixmap + :drawable exwm--root + :width width + :height height)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:render:CreatePicture + :pid picture + :drawable pixmap + :format (xcb:renderutil:find-visual-format + (xcb:renderutil:query-formats + exwm-cm--conn) + visual) + :value-mask 0))) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:FreePixmap + :pixmap pixmap)))) + (let (queue) + ;; Paint opaque X windows and update clipping region. + (setq queue (exwm-cm--paint-tree nil region)) + ;; Paint the background. + (exwm-cm--paint-background region) + ;; Paint transparent X windows. + (while queue + (exwm-cm--paint-transparent (pop queue)))) + ;; Submit changes. + (with-slots (width height picture) (exwm-cm--xwin->attr exwm--root) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:SetPictureClipRegion + :picture exwm-cm--buffer + :region xcb:xfixes:Region:None + :x-origin 0 + :y-origin 0)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:render:Composite + :op xcb:render:PictOp:Src + :src exwm-cm--buffer + :mask xcb:render:Picture:None + :dst picture + :src-x 0 + :src-y 0 + :mask-x 0 + :mask-y 0 + :dst-x 0 + :dst-y 0 + :width width + :height height))) + ;; Cleanup. + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:DestroyRegion + :region region)) + (when (eq region exwm-cm--damages) + (setq exwm-cm--damages nil)) + (setq exwm-cm--clip-changed nil) + (xcb:flush exwm-cm--conn))) + +(defun exwm-cm--paint-background (region) + "Paint the background." + (unless exwm-cm--background + (setq exwm-cm--background (xcb:generate-id exwm-cm--conn)) + (let (pixmap exist) + (catch 'break + (dolist (atom exwm-cm--background-atoms) + (with-slots (~lsb format value-len value) + (xcb:+request-unchecked+reply exwm-cm--conn + (make-instance 'xcb:GetProperty + :delete 0 + :window exwm--root + :property atom + :type xcb:Atom:PIXMAP + :long-offset 0 + :long-length 4)) + (when (and (= format 32) + (= 1 value-len)) + (setq pixmap (if ~lsb + (xcb:-unpack-u4-lsb value 0) + (xcb:-unpack-u4 value 0))) + (setq exist t) + (throw 'break nil))))) + (unless pixmap + (setq pixmap (xcb:generate-id exwm-cm--conn)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:CreatePixmap + :depth exwm-cm--depth + :pid pixmap + :drawable exwm--root + :width 1 + :height 1))) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:render:CreatePicture + :pid exwm-cm--background + :drawable pixmap + :format (xcb:renderutil:find-visual-format + (xcb:renderutil:query-formats exwm-cm--conn) + (slot-value (exwm-cm--xwin->attr exwm--root) + 'visual)) + :value-mask xcb:render:CP:Repeat + :repeat 1)) + (unless exist + (xcb:+request exwm-cm--conn + (make-instance 'xcb:render:FillRectangles + :op xcb:render:PictOp:Src + :dst exwm-cm--background + :color (make-instance 'xcb:render:COLOR + :red #x8080 + :green #x8080 + :blue #x8080 + :alpha #xFFFF) + :rects (list (make-instance 'xcb:RECTANGLE + :x 0 + :y 0 + :width 1 + :height 1))))))) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:SetPictureClipRegion + :picture exwm-cm--buffer + :region region + :x-origin 0 + :y-origin 0)) + (with-slots (width height) (exwm-cm--xwin->attr exwm--root) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:render:Composite + :op xcb:render:PictOp:Src + :src exwm-cm--background + :mask xcb:render:Picture:None + :dst exwm-cm--buffer + :src-x 0 + :src-y 0 + :mask-x 0 + :mask-y 0 + :dst-x 0 + :dst-y 0 + :width width + :height height)))) + +(defun exwm-cm--map-xwin (xwin &optional silent) + "Prepare to map X window XWIN." + (let ((attr (exwm-cm--xwin->attr xwin))) + (setf (slot-value attr 'damaged) nil) + ;; Add to damage. + (when (slot-value attr 'extents) + (let ((damage (xcb:generate-id exwm-cm--conn))) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:CreateRegion + :region damage + :rectangles nil)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:CopyRegion + :source (slot-value attr 'extents) + :destination damage)) + (exwm-cm--add-damage damage)) + (unless silent + (exwm-cm--paint))))) + +(defun exwm-cm--on-MapNotify (data _synthetic) + "Handle MapNotify events." + (let ((obj (make-instance 'xcb:MapNotify)) + attr) + (xcb:unmarshal obj data) + (with-slots (event window) obj + (exwm--log "(CM) MapNotify: Try to map #x%X" window) + (setq attr (exwm-cm--xwin->attr window)) + (when (and attr + (/= (slot-value attr 'class) xcb:WindowClass:InputOnly) + (or (= event exwm--root) + ;; Filter out duplicated events. + (/= exwm--root (exwm-cm--get-parent window)))) + (exwm--log "(CM) MapNotify: Map") + (exwm-cm--map-xwin window))))) + +(defun exwm-cm--on-UnmapNotify (data _synthetic) + "Handle UnmapNotify events." + (let ((obj (make-instance 'xcb:UnmapNotify)) + attr) + (xcb:unmarshal obj data) + (with-slots (event window) obj + (exwm--log "(CM) UnmapNotify: Try to unmap #x%X" window) + (setq attr (exwm-cm--xwin->attr window)) + (when (and attr + (or (= event exwm--root) + ;; Filter out duplicated events. + (/= exwm--root (exwm-cm--get-parent window)))) + (exwm--log "(CM) UnmapNotify: Unmap") + (with-slots (picture damaged border-size extents border-clip) attr + (setf damaged nil) + (when picture + (xcb:+request exwm-cm--conn + (make-instance 'xcb:render:FreePicture + :picture picture)) + (setf picture nil)) + (when border-size + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:DestroyRegion + :region border-size)) + (setf border-size nil)) + (when extents + (exwm-cm--add-damage extents) + (setf extents nil)) + (when border-clip + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:DestroyRegion + :region border-clip)) + (setf border-clip nil))) + (setq exwm-cm--clip-changed t) + (exwm-cm--paint))))) + +(defun exwm-cm--on-CreateNotify (data _synthetic) + "Handle CreateNotify events." + (let ((obj (make-instance 'xcb:CreateNotify)) + tree0) + (xcb:unmarshal obj data) + (with-slots (window parent x y width height) obj + (exwm--log "(CM) CreateNotify: Create #x%X on #x%X @%sx%s%+d%+d" + window parent width height x y) + (cl-assert (= parent exwm--root)) + (cl-assert (null (exwm-cm--xwin->attr window))) + (setq tree0 (exwm-cm--get-subtree parent)) + (exwm-cm--create-attr window tree0 x y width height) + (if (cdr tree0) + (exwm-cm--push (list window) (cdr tree0)) + (setcdr tree0 `((,window))))))) + +(defun exwm-cm--on-ConfigureNotify (data synthetic) + "Handle ConfigureNotify events." + ;; Ignore synthetic ConfigureNotify events sent by the WM. + (unless synthetic + (let ((obj (make-instance 'xcb:ConfigureNotify))) + (xcb:unmarshal obj data) + (with-slots (event window above-sibling x y width height) obj + (exwm--log + "(CM) ConfigureNotify: Try to configure #x%X @%sx%s%+d%+d, above #x%X" + window width height x y above-sibling) + (cond + ((= window exwm--root) + (exwm--log "(CM) ConfigureNotify: Configure the root X window") + (when exwm-cm--buffer + (xcb:+request exwm-cm--conn + (make-instance 'xcb:render:FreePicture + :picture exwm-cm--buffer)) + (setq exwm-cm--buffer nil)) + (with-slots ((x* x) + (y* y) + (width* width) + (height* height)) + (exwm-cm--xwin->attr exwm--root) + (setf x* x + y* y + width* width + height* height)) + (exwm-cm--paint)) + ((null (exwm-cm--xwin->attr window))) + ((or (= event exwm--root) + ;; Filter out duplicated events. + (/= exwm--root (exwm-cm--get-parent window))) + (exwm--log "(CM) ConfigureNotify: Configure") + (with-slots ((x0 x) + (y0 y)) + (exwm-cm--xwin->attr (exwm-cm--get-parent window)) + (exwm-cm--update-geometry window (+ x x0) (+ y y0) width height + above-sibling)) + (setq exwm-cm--clip-changed t) + (exwm-cm--paint)) + (t + (exwm--log "(CM) ConfigureNotify: Skip event from #x%X" event))))))) + +(defun exwm-cm--destroy (xwin) + "Prepare to destroy X window XWIN." + (with-slots (tree picture alpha-picture damage + border-size extents border-clip) + (exwm-cm--xwin->attr xwin) + (cl-assert (assq xwin (cdr tree))) + (if (= 1 (length (cdr tree))) + (setcdr tree nil) + (exwm-cm--assq-delete-all xwin (cdr tree))) + (remhash xwin exwm-cm--hash) + ;; Release resources. + (when picture + (xcb:+request exwm-cm--conn + (make-instance 'xcb:render:FreePicture + :picture picture)) + (setf picture nil)) + (when alpha-picture + (xcb:+request exwm-cm--conn + (make-instance 'xcb:render:FreePicture + :picture alpha-picture)) + (setf alpha-picture nil)) + (when damage + (xcb:+request exwm-cm--conn + (make-instance 'xcb:damage:Destroy + :damage damage)) + (setf damage nil)) + (when border-size + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:DestroyRegion + :region border-size)) + (setf border-size nil)) + (when extents + (exwm-cm--add-damage extents) + (setf extents nil)) + (when border-clip + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:DestroyRegion + :region border-clip)) + (setf border-clip nil)))) + +(defun exwm-cm--on-DestroyNotify (data _synthetic) + "Handle DestroyNotify events." + (let ((obj (make-instance 'xcb:DestroyNotify)) + xwin) + (xcb:unmarshal obj data) + (setq xwin (slot-value obj 'window)) + (exwm--log "(CM) DestroyNotify: Try to destroy #x%X" xwin) + (when (exwm-cm--xwin->attr xwin) + (exwm--log "(CM) DestroyNotify: Destroy") + (exwm-cm--destroy xwin)))) + +(defun exwm-cm--on-CirculateNotify (data _synthetic) + "Handle CirculateNotify events." + (let ((obj (make-instance 'xcb:CirculateNotify)) + attr) + (xcb:unmarshal obj data) + (with-slots (event window place) obj + (setq attr (exwm-cm--xwin->attr window)) + (exwm--log "(CM) CirculateNotify: Try to circulate #x%X to %s" + window place) + (when (and attr + (or (= event exwm--root) + ;; Filter out duplicated events. + (/= exwm--root (exwm-cm--get-parent window)))) + (exwm--log "(CM) CirculateNotify: Circulate") + (exwm-cm--update-geometry window nil nil nil nil + (if (= place xcb:Circulate:LowerHighest) + xcb:Window:None + (caar (exwm-cm--get-siblings window)))) + (setq exwm-cm--clip-changed t) + (exwm-cm--paint))))) + +(defun exwm-cm--on-Expose (data _synthetic) + "Handle Expose events." + (let ((obj (make-instance 'xcb:Expose))) + (xcb:unmarshal obj data) + (with-slots (window x y width height count) obj + (when (eq window exwm--root) + (push (make-instance 'xcb:RECTANGLE + :x x + :y y + :width width + :height height) + exwm-cm--expose-rectangles)) + (when (= count 0) + (let ((region (xcb:generate-id exwm-cm--conn))) + (xcb:+request exwm-cm--conn + (xcb:xfixes:CreateRegion + :region region + :rectangles exwm-cm--expose-rectangles)) + (exwm-cm--add-damage region)) + (setq exwm-cm--expose-rectangles nil) + (exwm-cm--paint))))) + +(defun exwm-cm--on-PropertyNotify (data _synthetic) + "Handle PropertyNotify events." + (let ((obj (make-instance 'xcb:PropertyNotify))) + (xcb:unmarshal obj data) + (with-slots (window atom) obj + (cond + ((and (= window exwm--root) + (memq atom exwm-cm--background-atoms)) + (exwm--log "(CM) PropertyNotify: Update background") + (when exwm-cm--background + (xcb:+request exwm-cm--conn + (make-instance 'xcb:render:FreePicture + :picture exwm-cm--background)) + (setq exwm-cm--background nil) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:ClearArea + :exposures 1 + :window exwm--root + :x 0 + :y 0 + :width 0 + :height 0)) + (xcb:flush exwm-cm--conn))) + ((and (= atom exwm-cm--_NET_WM_WINDOW_OPACITY) + ;; Some applications also set this property on their parents. + (null (cdr (exwm-cm--get-subtree window)))) + (when (exwm-cm--xwin->attr window) + (exwm--log "(CM) PropertyNotify: Update opacity for #x%X" window) + (exwm-cm--update-opacity window) + (exwm-cm--paint))))))) + +(defun exwm-cm--prepare-container (xwin) + "Make X window XWIN a container by deselecting unnecessary events." + (with-slots (damage) (exwm-cm--xwin->attr xwin) + (when damage + (xcb:+request exwm-cm--conn + (make-instance 'xcb:damage:Destroy + :damage damage))) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:shape:SelectInput + :destination-window xwin + :enable 0)))) + +(defun exwm-cm--on-ReparentNotify (data _synthetic) + "Handle ReparentNotify events." + (let ((obj (make-instance 'xcb:ReparentNotify)) + tree tree0 grandparent great-grandparent entity) + (xcb:unmarshal obj data) + (with-slots (window parent x y) obj + (exwm--log "(CM) ReparentNotify: Try to reparent #x%X to #x%X @%+d%+d" + window parent x y) + (cond + ((null (exwm-cm--xwin->attr window)) + (when (eq parent exwm--root) + (exwm--log "(CM) ReparentNotify: Create on the root X window") + (let ((reply (xcb:+request-unchecked+reply exwm-cm--conn + (make-instance 'xcb:GetGeometry + :drawable window)))) + (when reply + (with-slots (width height) reply + (setq tree0 (exwm-cm--get-subtree exwm--root)) + (exwm-cm--create-attr window tree0 x y width height) + (if (cdr tree0) + (exwm-cm--push (list window) (cdr tree0)) + (setcdr tree0 `((,window))))) + (exwm-cm--paint))))) + ((= parent (exwm-cm--get-parent window))) + (t + (unless (exwm-cm--xwin->attr parent) + ;; Only allow workspace frame here. + (setq grandparent + (slot-value (xcb:+request-unchecked+reply exwm-cm--conn + (make-instance 'xcb:QueryTree + :window parent)) + 'parent)) + (cond + ((null (exwm-cm--xwin->attr grandparent)) + (exwm--log "(CM) ReparentNotify: Destroy (too deep)")) + ((and (= exwm--root + (setq great-grandparent (exwm-cm--get-parent grandparent))) + (setq tree0 (exwm-cm--get-subtree grandparent)) + (or (setq entity (exwm--id->buffer window)) + (null (cdr tree0)))) + ;; Reparent a workspace frame or an X window into its + ;; container. + (exwm--debug + (if entity + (exwm--log "(CM) ReparentNotify: \ +Create implicit X window container") + (exwm--log "(CM) ReparentNotify: \ +Create implicit workspace frame container"))) + (unless entity + (setq entity 'workspace-frame)) + (with-slots ((x0 x) + (y0 y)) + (exwm-cm--xwin->attr grandparent) + (with-slots (x y width height) + (xcb:+request-unchecked+reply exwm-cm--conn + (make-instance 'xcb:GetGeometry + :drawable parent)) + (exwm-cm--create-attr parent tree0 + (+ x x0) (+ y y0) width height))) + (if (null (cdr tree0)) + (setcdr tree0 `((,parent))) + ;; The stacking order of the parent is unknown. + (let* ((siblings + (slot-value (xcb:+request-unchecked+reply exwm-cm--conn + (make-instance 'xcb:QueryTree + :window grandparent)) + 'children))) + (cl-assert (memq parent siblings)) + (if (= parent (car siblings)) + ;; At the bottom. + (setcdr (last (cdr tree0)) `((,parent))) + ;; Insert it. + (exwm-cm--push (list parent) + ;; The stacking order is reversed. + (nthcdr (- (length siblings) 1 + (cl-position parent siblings)) + (cdr tree0))))))) + ((and (= exwm--root + (exwm-cm--get-parent great-grandparent)) + (setq tree0 (exwm-cm--get-subtree grandparent)) + (= 1 (length (cdr tree0))) + (exwm--id->buffer (caar (cdr tree0)))) + ;; Reparent a floating frame into its container. + (exwm--log "(CM) ReparentNotify: Create floating frame container") + (setq entity 'floating-frame) + (with-slots ((x0 x) + (y0 y)) + (exwm-cm--xwin->attr grandparent) + (with-slots (x y width height) + (xcb:+request-unchecked+reply exwm-cm--conn + (make-instance 'xcb:GetGeometry + :drawable parent)) + (exwm-cm--create-attr parent tree0 + (+ x x0) (+ y y0) width height))) + (nconc (cdr tree0) `((,parent)))) + (t + (exwm--log "(CM) ReparentNotify: Destroy") + (exwm-cm--destroy window)))) + ;; Ensure there's a valid parent. + (when (exwm-cm--xwin->attr parent) + (exwm--log "(CM) ReparentNotify: Reparent") + (when (null (cdr (exwm-cm--get-subtree parent))) + ;; The parent is a new container. + (exwm-cm--prepare-container parent)) + (setq tree (exwm-cm--get-subtree window)) + (let ((tree (exwm-cm--get-tree window))) + (if (= 1 (length (cdr tree))) + (setcdr tree nil) + (exwm-cm--assq-delete-all window (cdr tree)))) + (setq tree0 (exwm-cm--get-subtree parent)) + (exwm-cm--set-tree window tree0) + ;; The size might have already changed (e.g. when reparenting + ;; a workspace frame). + (let ((reply (xcb:+request-unchecked+reply exwm-cm--conn + (make-instance 'xcb:GetGeometry + :drawable window)))) + ;; The X window might have already been destroyed. + (when reply + (with-slots (width height) reply + (with-slots ((x0 x) + (y0 y)) + (exwm-cm--xwin->attr parent) + (exwm-cm--update-geometry window (+ x x0) (+ y y0) + width height))))) + (when entity + ;; Decide frame entity. + (when (symbolp entity) + (catch 'break + (dolist (f (if (eq entity 'workspace-frame) + exwm-workspace--list + (frame-list))) + (when (eq window (frame-parameter f 'exwm-outer-id)) + (setq entity f) + (throw 'break nil)))) + (when (exwm-workspace--workspace-p entity) + ;; The grandparent is a new workspace container. + (exwm-cm--prepare-container grandparent) + (setf (slot-value (exwm-cm--xwin->attr grandparent) 'entity) + entity))) + (setf (slot-value (exwm-cm--xwin->attr parent) 'entity) entity) + (setf (slot-value (exwm-cm--xwin->attr window) 'entity) entity)) + (if (cdr tree0) + (exwm-cm--push tree (cdr tree0)) + (setcdr tree0 `(,tree))) + (exwm-cm--paint))))))) + +(defun exwm-cm--add-damage (damage) + "Add region DAMAGE to `exwm-cm--damages'." + (if (not exwm-cm--damages) + (setq exwm-cm--damages damage) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:UnionRegion + :source1 exwm-cm--damages + :source2 damage + :destination exwm-cm--damages)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:DestroyRegion + :region damage)))) + +(defun exwm-cm--on-DamageNotify (data _synthetic) + "Handle DamageNotify events." + (let ((obj (make-instance 'xcb:damage:Notify)) + parts) + (xcb:unmarshal obj data) + (cl-assert (exwm-cm--xwin->attr (slot-value obj 'drawable))) + (with-slots (x y width height damaged damage) + (exwm-cm--xwin->attr (slot-value obj 'drawable)) + (setq parts (xcb:generate-id exwm-cm--conn)) + (cond + (damaged + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:CreateRegion + :region parts + :rectangles nil)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:damage:Subtract + :damage damage + :repair xcb:xfixes:Region:None + :parts parts)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:TranslateRegion + :region parts + :dx x + :dy y))) + (t + (setf damaged t) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:CreateRegion + :region parts + :rectangles (list (make-instance 'xcb:RECTANGLE + :width width + :height height + :x x + :y y)))) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:damage:Subtract + :damage damage + :repair xcb:xfixes:Region:None + :parts xcb:xfixes:Region:None)))) + (exwm-cm--add-damage parts)) + ;; Check if there are more damages immediately followed. + (unless (/= 0 (logand #x80 (slot-value obj 'level))) + (exwm-cm--paint)))) + +(defun exwm-cm--on-ShapeNotify (data _synthetic) + "Handle ShapeNotify events." + (let ((obj (make-instance 'xcb:shape:Notify)) + attr region1 region2) + (xcb:unmarshal obj data) + (with-slots (shape-kind affected-window shaped + extents-x extents-y extents-width extents-height) + obj + (exwm--log "(CM) ShapeNotify: #x%X" affected-window) + (when (and (or (eq shape-kind xcb:shape:SK:Clip) + (eq shape-kind xcb:shape:SK:Bounding)) + (setq attr (exwm-cm--xwin->attr affected-window))) + (with-slots ((shaped* shaped) + x y width height + shape-x shape-y shape-width shape-height) + attr + (setq region1 (xcb:generate-id exwm-cm--conn) + region2 (xcb:generate-id exwm-cm--conn)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:CreateRegion + :region region1 + :rectangles `(,(make-instance 'xcb:RECTANGLE + :width shape-width + :height shape-height + :x shape-x + :y shape-y)))) + (if shaped + (setf shaped* t + shape-x (+ x extents-x) + shape-y (+ y extents-y) + shape-width (+ width extents-width) + shape-height (+ height extents-height)) + (setf shaped* nil + shape-x x + shape-y y + shape-width width + shape-height height)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:CreateRegion + :region region2 + :rectangles `(,(make-instance 'xcb:RECTANGLE + :width shape-width + :height shape-height + :x shape-x + :y shape-y)))) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:UnionRegion + :source1 region1 + :source2 region2 + :destination region1)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:xfixes:DestroyRegion + :region region2)) + (setq exwm-cm--clip-changed t) + (exwm-cm--paint region1)))))) + +(defun exwm-cm--init () + "Initialize EXWM compositing manager." + ;; Create a new connection. + (setq exwm-cm--conn (xcb:connect)) + (set-process-query-on-exit-flag (slot-value exwm-cm--conn 'process) nil) + ;; Initialize ICCCM/EWMH support. + (xcb:icccm:init exwm-cm--conn) + (xcb:ewmh:init exwm-cm--conn) + ;; Check for Render extension. + (let ((version (xcb:renderutil:query-version exwm-cm--conn))) + (unless (and version + (= 0 (slot-value version 'major-version)) + (<= 2 (slot-value version 'minor-version))) + (error "[EXWM] The server does not support Render extension"))) + ;; Check for Composite extension. + (when (or (= 0 + (slot-value (xcb:get-extension-data exwm-cm--conn + 'xcb:composite) + 'present)) + (with-slots (major-version minor-version) + (xcb:+request-unchecked+reply exwm-cm--conn + (make-instance 'xcb:composite:QueryVersion + :client-major-version 0 + :client-minor-version 1)) + (or (/= major-version 0) (< minor-version 1)))) + (error "[EXWM] The server does not support Composite extension")) + ;; Check for Damage extension. + (when (or (= 0 (slot-value (xcb:get-extension-data exwm-cm--conn 'xcb:damage) + 'present)) + (with-slots (major-version minor-version) + (xcb:+request-unchecked+reply exwm-cm--conn + (make-instance 'xcb:damage:QueryVersion + :client-major-version 1 + :client-minor-version 1)) + (or (/= major-version 1) (< minor-version 1)))) + (error "[EXWM] The server does not support Damage extension")) + ;; Check for XFixes extension. + (when (or (= 0 (slot-value (xcb:get-extension-data exwm-cm--conn 'xcb:xfixes) + 'present)) + (with-slots (major-version minor-version) + (xcb:+request-unchecked+reply exwm-cm--conn + (make-instance 'xcb:xfixes:QueryVersion + :client-major-version 2 + :client-minor-version 0)) + (or (/= major-version 2) (/= minor-version 0)))) + (error "[EXWM] The server does not support XFixes extension")) + ;; Check for Shape extension. + (when (or (= 0 (slot-value (xcb:get-extension-data exwm-cm--conn 'xcb:shape) + 'present)) + (with-slots (major-version minor-version) + (xcb:+request-unchecked+reply exwm-cm--conn + (make-instance 'xcb:shape:QueryVersion)) + (or (/= major-version 1) (< minor-version 1)))) + (error "[EXWM] The server does not support Shape extension")) + ;; Intern atoms. + (let ((atom-name "_NET_WM_WINDOW_OPACITY")) + (setq exwm-cm--_NET_WM_WINDOW_OPACITY + (slot-value (xcb:+request-unchecked+reply exwm-cm--conn + (make-instance 'xcb:InternAtom + :only-if-exists 0 + :name-len (length atom-name) + :name atom-name)) + 'atom))) + (setq exwm-cm--background-atoms + (mapcar (lambda (atom-name) + (slot-value (xcb:+request-unchecked+reply exwm-cm--conn + (make-instance 'xcb:InternAtom + :only-if-exists 0 + :name-len (length atom-name) + :name atom-name)) + 'atom)) + exwm-cm--background-atom-names)) + ;; Register CM. + (with-slots (owner) + (xcb:+request-unchecked+reply exwm-cm--conn + (make-instance 'xcb:GetSelectionOwner + :selection xcb:Atom:_NET_WM_CM_S0)) + (when (/= owner xcb:Window:None) + (error "[EXWM] Other compositing manager detected"))) + (let ((id (xcb:generate-id exwm-cm--conn))) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:CreateWindow + :depth 0 + :wid id + :parent exwm--root + :x 0 + :y 0 + :width 1 + :height 1 + :border-width 0 + :class xcb:WindowClass:InputOnly + :visual 0 + :value-mask xcb:CW:OverrideRedirect + :override-redirect 1)) + ;; Set _NET_WM_NAME. + (xcb:+request exwm-cm--conn + (make-instance 'xcb:ewmh:set-_NET_WM_NAME + :window id + :data "EXWM-CM")) + ;; Get the selection ownership. + (xcb:+request exwm-cm--conn + (make-instance 'xcb:SetSelectionOwner + :owner id + :selection xcb:Atom:_NET_WM_CM_S0 + :time xcb:Time:CurrentTime))) + ;; Attach event listeners. + (xcb:+event exwm-cm--conn 'xcb:MapNotify #'exwm-cm--on-MapNotify) + (xcb:+event exwm-cm--conn 'xcb:UnmapNotify #'exwm-cm--on-UnmapNotify) + (xcb:+event exwm-cm--conn 'xcb:CreateNotify #'exwm-cm--on-CreateNotify) + (xcb:+event exwm-cm--conn 'xcb:ConfigureNotify #'exwm-cm--on-ConfigureNotify) + (xcb:+event exwm-cm--conn 'xcb:DestroyNotify #'exwm-cm--on-DestroyNotify) + (xcb:+event exwm-cm--conn 'xcb:ReparentNotify #'exwm-cm--on-ReparentNotify) + (xcb:+event exwm-cm--conn 'xcb:CirculateNotify #'exwm-cm--on-CirculateNotify) + (xcb:+event exwm-cm--conn 'xcb:Expose #'exwm-cm--on-Expose) + (xcb:+event exwm-cm--conn 'xcb:PropertyNotify #'exwm-cm--on-PropertyNotify) + (xcb:+event exwm-cm--conn 'xcb:damage:Notify #'exwm-cm--on-DamageNotify) + (xcb:+event exwm-cm--conn 'xcb:shape:Notify #'exwm-cm--on-ShapeNotify) + ;; Scan the window tree. + (setq exwm-cm--hash (make-hash-table)) + (exwm-cm--create-tree) + ;; Set up the root X window. + (setq exwm-cm--depth + (slot-value (car (slot-value (xcb:get-setup exwm-cm--conn) 'roots)) + 'root-depth)) + (with-slots (visual picture) (exwm-cm--xwin->attr exwm--root) + (setf picture (xcb:generate-id exwm-cm--conn)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:render:CreatePicture + :pid picture + :drawable exwm--root + :format (xcb:renderutil:find-visual-format + (xcb:renderutil:query-formats exwm-cm--conn) + visual) + :value-mask xcb:render:CP:SubwindowMode + :subwindowmode xcb:SubwindowMode:IncludeInferiors))) + (xcb:flush exwm-cm--conn) + ;; Paint once. + (exwm-cm--paint t)) + +(defun exwm-cm--exit () + "Exit EXWM compositing manager." + (when exwm-cm--conn + (xcb:disconnect exwm-cm--conn) + (clrhash exwm-cm--hash) + (setq exwm-cm--hash nil + exwm-cm--conn nil + exwm-cm--buffer nil + exwm-cm--clip-changed t + exwm-cm--damages nil + exwm-cm--expose-rectangles nil + exwm-cm--background nil))) + +(defun exwm-cm-enable () + "Enable compositing support for EXWM." + (add-hook 'exwm-init-hook #'exwm-cm--init t) + (add-hook 'exwm-exit-hook #'exwm-cm--exit t)) + +(defun exwm-cm-start () + "Start EXWM composting manager." + (interactive) + (unless exwm-cm--conn + (exwm-cm--init))) + +(defun exwm-cm-stop () + "Stop EXWM compositing manager." + (interactive) + (exwm-cm--exit)) + +(defun exwm-cm-toggle () + "Toggle the running state of EXWM compositing manager." + (interactive) + (if exwm-cm--conn + (exwm-cm-stop) + (exwm-cm-start))) + + + +(provide 'exwm-cm) + +;;; exwm-cm.el ends here -- cgit 1.4.1 From a5ea75e9a2657f103f7a556c20349309eb4535e5 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 14 Aug 2016 18:19:16 +0800 Subject: Document the compositing manager module * README.md: * exwm.el: Mention this new feature. --- README.md | 1 + exwm.el | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index 103948c633..409087c81b 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ It features: + Dynamic workspace support + ICCCM/EWMH compliance + (Optional) RandR (multi-monitor) support ++ (Optional) Built-in composting manager + (Optional) Built-in system tray Please check out the diff --git a/exwm.el b/exwm.el index a97236786b..bb6b6a7173 100644 --- a/exwm.el +++ b/exwm.el @@ -36,6 +36,7 @@ ;; + Dynamic workspace support ;; + ICCCM/EWMH compliance ;; + (Optional) RandR (multi-monitor) support +;; + (Optional) Built-in composting manager ;; + (Optional) Builtin system tray ;; Installation & configuration -- cgit 1.4.1 From 9ff99d63281ced50a34fa803e8cc48633c1f8449 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 15 Aug 2016 18:42:35 +0800 Subject: Do not set _NET_WORKAREA * exwm-workspace.el (exwm-workspace--update-workareas): Do not set _NET_WORKAREA. * exwm.el (exwm--init-icccm-ewmh, exwm--exit-icccm-ewmh): Remove _NET_WORKAREA from the supported list. --- exwm-randr.el | 2 +- exwm-workspace.el | 9 ++------- exwm.el | 7 +++---- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/exwm-randr.el b/exwm-randr.el index 4ce1752d9e..5caf875b2e 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -107,7 +107,7 @@ output nil)) (set-frame-parameter frame 'exwm-randr-output output) (set-frame-parameter frame 'exwm-geometry geometry))) - ;; Update workareas and set _NET_WORKAREA. + ;; Update workareas. (exwm-workspace--update-workareas) ;; Resize workspace. (dolist (f exwm-workspace--list) diff --git a/exwm-workspace.el b/exwm-workspace.el index 77c9417744..958511270b 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -249,7 +249,7 @@ Value nil means to use the default position which is fixed at bottom, while (defvar exwm-workspace--workareas nil "Workareas (struts excluded).") (defun exwm-workspace--update-workareas () - "Update `exwm-workspace--workareas' and set _NET_WORKAREA." + "Update `exwm-workspace--workareas'." (let ((root-width (x-display-pixel-width)) (root-height (x-display-pixel-height)) workareas @@ -309,11 +309,6 @@ Value nil means to use the default position which is fixed at bottom, while (cl-incf (aref w 3) delta)))))) ;; Save the result. (setq exwm-workspace--workareas workareas) - ;; Update _NET_WORKAREA. - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_WORKAREA - :window exwm--root - :data (mapconcat #'vconcat workareas []))) (xcb:flush exwm--connection))) (defvar exwm-workspace--fullscreen-frame-count 0 @@ -1320,7 +1315,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." :window exwm--root :data num-workspaces)) ;; Set _NET_DESKTOP_GEOMETRY. (exwm-workspace--set-desktop-geometry) - ;; Update and set _NET_WORKAREA. + ;; Update workareas. (exwm-workspace--update-workareas) ;; Set _NET_VIRTUAL_ROOTS. (xcb:+request exwm--connection diff --git a/exwm.el b/exwm.el index de45f4f1f3..293072c9f9 100644 --- a/exwm.el +++ b/exwm.el @@ -273,7 +273,7 @@ (setcdr pair struts) (push (cons id struts) exwm-workspace--id-struts-alist)) (exwm-workspace--update-struts)) - ;; Update workareas and set _NET_WORKAREA. + ;; Update workareas. (exwm-workspace--update-workareas) ;; Update workspaces. (dolist (f exwm-workspace--list) @@ -292,7 +292,7 @@ (setcdr pair struts) (push (cons id struts) exwm-workspace--id-struts-alist)) (exwm-workspace--update-struts)) - ;; Update workareas and set _NET_WORKAREA. + ;; Update workareas. (exwm-workspace--update-workareas) ;; Update workspaces. (dolist (f exwm-workspace--list) @@ -520,7 +520,7 @@ xcb:Atom:_NET_CURRENT_DESKTOP ;; xcb:Atom:_NET_DESKTOP_NAMES xcb:Atom:_NET_ACTIVE_WINDOW - xcb:Atom:_NET_WORKAREA + ;; xcb:Atom:_NET_WORKAREA xcb:Atom:_NET_SUPPORTING_WM_CHECK xcb:Atom:_NET_VIRTUAL_ROOTS ;; xcb:Atom:_NET_DESKTOP_LAYOUT @@ -648,7 +648,6 @@ xcb:Atom:_NET_DESKTOP_VIEWPORT xcb:Atom:_NET_CURRENT_DESKTOP xcb:Atom:_NET_ACTIVE_WINDOW - xcb:Atom:_NET_WORKAREA xcb:Atom:_NET_SUPPORTING_WM_CHECK xcb:Atom:_NET_VIRTUAL_ROOTS ;; TODO: Keep this list synchronized with that in -- cgit 1.4.1 From cb75d4814daac2f6d52d11550199be65d185f996 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 15 Aug 2016 22:59:48 +0800 Subject: ; Fix typos --- README.md | 2 +- exwm-cm.el | 2 +- exwm.el | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 409087c81b..1b65486c69 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ It features: + Dynamic workspace support + ICCCM/EWMH compliance + (Optional) RandR (multi-monitor) support -+ (Optional) Built-in composting manager ++ (Optional) Built-in compositing manager + (Optional) Built-in system tray Please check out the diff --git a/exwm-cm.el b/exwm-cm.el index 8e019bbc75..f7a37598e5 100644 --- a/exwm-cm.el +++ b/exwm-cm.el @@ -1754,7 +1754,7 @@ Create implicit workspace frame container"))) (add-hook 'exwm-exit-hook #'exwm-cm--exit t)) (defun exwm-cm-start () - "Start EXWM composting manager." + "Start EXWM compositing manager." (interactive) (unless exwm-cm--conn (exwm-cm--init))) diff --git a/exwm.el b/exwm.el index 293072c9f9..6811a5425b 100644 --- a/exwm.el +++ b/exwm.el @@ -36,8 +36,8 @@ ;; + Dynamic workspace support ;; + ICCCM/EWMH compliance ;; + (Optional) RandR (multi-monitor) support -;; + (Optional) Built-in composting manager -;; + (Optional) Builtin system tray +;; + (Optional) Built-in compositing manager +;; + (Optional) Built-in system tray ;; Installation & configuration ;; ---------------------------- -- cgit 1.4.1 From a9136213020efb69672996e2306ae8c26b14e629 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 24 Aug 2016 19:21:17 +0800 Subject: Add toggle commands * exwm-input.el (exwm-input-toggle-keyboard): New command for toggling keyboard mode. * exwm-layout.el (exwm-layout-toggle-fullscreen): New command for toggling fullscreen mode. * exwm-core.el (exwm-mode-menu, exwm-mode-map): Use them. --- exwm-core.el | 10 ++-------- exwm-input.el | 10 ++++++++++ exwm-layout.el | 10 ++++++++++ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 0c92d90339..e65ad51690 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -150,10 +150,7 @@ "*General*" "---" ["Toggle floating" exwm-floating-toggle-floating] - ["Enter fullscreen" exwm-layout-set-fullscreen - (null (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state))] - ["Leave fullscreen" exwm-reset - (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)] + ["Toggle fullscreen mode" exwm-layout-toggle-fullscreen] ["Hide window" exwm-floating-hide exwm--floating-frame] "---" @@ -168,10 +165,7 @@ "---" "*Keyboard*" "---" - ["Capture keyboard" exwm-input-release-keyboard exwm--keyboard-grabbed] - ;; It's recommended to use `exwm-reset' rather than - ;; `exwm-input-grab-keyboard' to release keyboard (enter line-mode). - ["Release keyboard" exwm-reset (not exwm--keyboard-grabbed)] + ["Toggle keyboard mode" exwm-input-toggle-keyboard] ["Send key" exwm-input-send-next-key exwm--keyboard-grabbed] ;; This is merely a reference. ("Send simulation key" :filter diff --git a/exwm-input.el b/exwm-input.el index e5828126f9..61858e93d2 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -567,6 +567,16 @@ This value should always be overwritten.") (exwm-input--update-mode-line id) (force-mode-line-update)))) +;;;###autoload +(defun exwm-input-toggle-keyboard (&optional id) + "Toggle between 'line-mode' and 'char-mode'." + (interactive (list (exwm--buffer->id (window-buffer)))) + (when id + (with-current-buffer (exwm--id->buffer id) + (if exwm--keyboard-grabbed + (exwm-input-release-keyboard id) + (exwm-reset))))) + (defun exwm-input--fake-key (event) "Fake a key event equivalent to Emacs event EVENT." (let* ((keysym (xcb:keysyms:event->keysym exwm--connection event)) diff --git a/exwm-layout.el b/exwm-layout.el index e8fd8e50c7..355b834650 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -260,6 +260,16 @@ (delq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) (call-interactively #'exwm-input-grab-keyboard))) +;;;###autoload +(defun exwm-layout-toggle-fullscreen (&optional id) + "Toggle fullscreen mode." + (interactive (list (exwm--buffer->id (window-buffer)))) + (when id + (with-current-buffer (exwm--id->buffer id) + (if (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) + (exwm-reset) + (exwm-layout-set-fullscreen id))))) + (defvar exwm-layout--other-buffer-exclude-exwm-mode-buffers nil "When non-nil, prevent EXWM buffers from being selected by `other-buffer'.") -- cgit 1.4.1 From cfcaed691bff4270a8bccf9f873a4ba8f0524b4b Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 28 Aug 2016 12:59:22 +0800 Subject: Bump version to 0.10 --- exwm.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm.el b/exwm.el index 6811a5425b..76f8541b07 100644 --- a/exwm.el +++ b/exwm.el @@ -4,7 +4,7 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0.9 +;; Version: 0.10 ;; Package-Requires: ((xelb "0.11")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From 9105f2312161a402ab7e7b4d6e8bcf127cbf51f5 Mon Sep 17 00:00:00 2001 From: Ved Vyas Date: Tue, 30 Aug 2016 12:10:45 -0400 Subject: Improve exwm-workspace-move-window behavior in specific case This is a small change that improves the behavior of `exwm-workspace-move-window` in the following situation: 0. `exwm-workspace-show-all-buffers` and `exwm-layout-show-all-buffers` are `nil`*. 1. On active workspace `i`, there is X window `a` in the selected Emacs window. 2. On workspace `j`, there is X window `b` in the selected Emacs window on that workspace frame. 3. While workspace `i` is active, use `exwm-workspace-move-window` to move `a` to workspace `j`. 4. Switch to workspace `j` and use `exwm-workspace-move-window` to move `a` back to workspace `i`. Expected behavior: X window `a` is once again shown in the selected Emacs window on workspace `i` and X window `b` is once again shown in the selected Emacs window on workspace `j`. What is observed: `a` is OK but the selected Emacs window on workspace `j` does not show `b`. However, `b` is the first candidate when doing a `switch-to-buffer` in that Emacs window on workspace `j`. I'm not sure if this is the correct and complete change required, but it is working well so far. *The expected behavior is observed with EXWM 0.10 if exwm-{workspace,layout}-show-all-buffers are non-nil. --- exwm-workspace.el | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 958511270b..a99186ceb0 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -746,10 +746,9 @@ INDEX must not exceed the current number of workspaces." (frame-root-window exwm--floating-frame))))) ;; Move the X window container. - (if (eq frame exwm-workspace--current) - (set-window-buffer (get-buffer-window (current-buffer) t) - (other-buffer)) - (bury-buffer) + (set-window-buffer (get-buffer-window (current-buffer) t) + (other-buffer)) + (unless (eq frame exwm-workspace--current) ;; Clear the 'exwm-selected-window' frame parameter. (set-frame-parameter frame 'exwm-selected-window nil)) (exwm-layout--hide id) -- cgit 1.4.1 From b4517fbfa058e38b9c229d3e76674221d89a2e2b Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 31 Aug 2016 19:18:42 +0800 Subject: Force using visible buffers in `other-buffer' * exwm-floating.el (exwm-floating--set-floating): * exwm-workspace.el (exwm-workspace-move-window): Buffers visible on other frames should be treated as invisible. One side effect is visible buffers on the current frame is also taken into account. --- exwm-floating.el | 5 +++-- exwm-workspace.el | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 6f6cfecbfe..f2cc09be46 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -82,8 +82,9 @@ context of the corresponding buffer.") (defun exwm-floating--set-floating (id) "Make window ID floating." (let ((window (get-buffer-window (exwm--id->buffer id)))) - (when window ;window in non-floating state - (set-window-buffer window (other-buffer)))) ;hide it first + (when window + ;; Hide the non-floating X window first. + (set-window-buffer window (other-buffer nil t)))) (let* ((original-frame exwm-workspace--current) ;; Create new frame (frame (with-current-buffer diff --git a/exwm-workspace.el b/exwm-workspace.el index a99186ceb0..24a59f9fab 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -747,7 +747,7 @@ INDEX must not exceed the current number of workspaces." exwm--floating-frame))))) ;; Move the X window container. (set-window-buffer (get-buffer-window (current-buffer) t) - (other-buffer)) + (other-buffer nil t)) (unless (eq frame exwm-workspace--current) ;; Clear the 'exwm-selected-window' frame parameter. (set-frame-parameter frame 'exwm-selected-window nil)) -- cgit 1.4.1 From 2497c45a1d965cae92af49e4fad0cc84415109f6 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 22 Sep 2016 19:02:54 +0800 Subject: ; Remove dead code --- exwm-input.el | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 61858e93d2..dd65b6e8f1 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -121,20 +121,18 @@ ARGS are additional arguments to CALLBACK." (cdr exwm-input--timestamp-callback)) (setq exwm-input--timestamp-callback nil))))) -(defun exwm-input--on-FocusIn (data _synthetic) +(defun exwm-input--on-FocusIn (&rest _args) "Handle FocusIn events." - (let ((obj (make-instance 'xcb:FocusIn))) - (xcb:unmarshal obj data) - ;; Not sure if this is the right thing to do but the point is the - ;; input focus should not stay at the root window or any container, - ;; or the result would be unpredictable. `x-focus-frame' would - ;; first set the input focus to the (previously) selected frame, and - ;; then `select-window' would further update the input focus if the - ;; selected window is displaying an `exwm-mode' buffer. Perhaps we - ;; should carefully filter out FocusIn events with certain 'detail' - ;; and 'mode' combinations, but this just works. - (x-focus-frame (selected-frame)) - (select-window (selected-window)))) + ;; Not sure if this is the right thing to do but the point is the + ;; input focus should not stay at the root window or any container, + ;; or the result would be unpredictable. `x-focus-frame' would + ;; first set the input focus to the (previously) selected frame, and + ;; then `select-window' would further update the input focus if the + ;; selected window is displaying an `exwm-mode' buffer. Perhaps we + ;; should carefully filter out FocusIn events with certain 'detail' + ;; and 'mode' combinations, but this just works. + (x-focus-frame (selected-frame)) + (select-window (selected-window))) (defun exwm-input--on-workspace-list-change () "Run in `exwm-input--update-global-prefix-keys'." -- cgit 1.4.1 From 43af6bb6a295fc843bd8b1f890cca1c246293e90 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 22 Sep 2016 19:08:13 +0800 Subject: Drop support for _NET_WM_STATE_MODAL ; Modal X windows are not necessarily floating. * exwm.el (exwm--on-ClientMessage, exwm--init-icccm-ewmh): Drop support for _NET_WM_STATE_MODAL. --- exwm.el | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/exwm.el b/exwm.el index 76f8541b07..cb7fd80aa1 100644 --- a/exwm.el +++ b/exwm.el @@ -440,20 +440,6 @@ (xcb:flush exwm--connection)))) (when buffer ;ensure it's managed (with-current-buffer buffer - ;; _NET_WM_STATE_MODAL - (when (memq xcb:Atom:_NET_WM_STATE_MODAL props) - (cond ((= action xcb:ewmh:_NET_WM_STATE_ADD) - (unless exwm--floating-frame - (exwm-floating--set-floating id)) - (push xcb:Atom:_NET_WM_STATE_MODAL props-new)) - ((= action xcb:ewmh:_NET_WM_STATE_REMOVE) - (when exwm--floating-frame - (exwm-floating--unset-floating id))) - ((= action xcb:ewmh:_NET_WM_STATE_TOGGLE) - (if exwm--floating-frame - (exwm-floating--unset-floating id) - (exwm-floating--set-floating id) - (push xcb:Atom:_NET_WM_STATE_MODAL props-new))))) ;; _NET_WM_STATE_FULLSCREEN (when (or (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN props) (memq xcb:Atom:_NET_WM_STATE_ABOVE props)) @@ -557,7 +543,7 @@ xcb:Atom:_NET_WM_WINDOW_TYPE_NORMAL ;; xcb:Atom:_NET_WM_STATE - xcb:Atom:_NET_WM_STATE_MODAL + ;; xcb:Atom:_NET_WM_STATE_MODAL ;; xcb:Atom:_NET_WM_STATE_STICKY ;; xcb:Atom:_NET_WM_STATE_MAXIMIZED_VERT ;; xcb:Atom:_NET_WM_STATE_MAXIMIZED_HORZ -- cgit 1.4.1 From f96f565d548521a87e0f3a23bfab7f5b836e4640 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 22 Sep 2016 19:10:38 +0800 Subject: Add support for keyboard macro * exwm-core.el (exwm--kmacro-map): New keymap used when executing keyboard macros. (exwm-mode): Use `exwm--kmacro-map' to override the default keymap. * exwm-input.el (exwm-input--on-KeyPress-line-mode): Send extra key events when defining keyboard macros. --- exwm-core.el | 31 +++++++++++++++++++++++++++++++ exwm-input.el | 6 +++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/exwm-core.el b/exwm-core.el index e65ad51690..8f5299f16b 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -26,6 +26,8 @@ ;;; Code: +(require 'kmacro) + (require 'xcb) (require 'xcb-icccm) (require 'xcb-ewmh) @@ -78,6 +80,12 @@ (logior xcb:EventMask:StructureNotify xcb:EventMask:PropertyChange)) "Event mask set on all managed windows.") +(defvar exwm-input--during-key-sequence) +(defvar exwm-input--global-prefix-keys) +(defvar exwm-input-prefix-keys) +(defvar exwm-input--simulation-prefix-keys) + +(declare-function exwm-input--fake-key "exwm-input.el" (event)) (declare-function exwm-input--on-KeyPress-line-mode "exwm-input.el" (key-press raw-data)) @@ -140,6 +148,26 @@ map) "Keymap for `exwm-mode'.") +(defvar exwm--kmacro-map + (let ((map (make-sparse-keymap))) + (define-key map [t] + (lambda () + (interactive) + (cond + ((or exwm-input--during-key-sequence + ;; Do not test `exwm-input--during-command'. + (active-minibuffer-window) + (memq last-input-event exwm-input--global-prefix-keys) + (memq last-input-event exwm-input-prefix-keys) + (memq last-input-event exwm-input--simulation-prefix-keys)) + (set-transient-map (make-composed-keymap (list exwm-mode-map + global-map))) + (push last-input-event unread-command-events)) + (t + (exwm-input--fake-key last-input-event))))) + map) + "Keymap used when executing keyboard macros.") + ;; This menu mainly acts as an reminder for users. Thus it should be as ;; detailed as possible, even some entries do not make much sense here. ;; Also, inactive entries should be disabled rather than hidden. @@ -226,6 +254,9 @@ ;; Kill buffer -> close window (add-hook 'kill-buffer-query-functions #'exwm-manage--kill-buffer-query-function nil t) + ;; Redirect events when executing keyboard macros. + (push `(executing-kbd-macro . ,exwm--kmacro-map) + minor-mode-overriding-map-alist) (setq buffer-read-only t left-margin-width nil right-margin-width nil diff --git a/exwm-input.el b/exwm-input.el index dd65b6e8f1..8101cbd528 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -454,7 +454,11 @@ This value should always be overwritten.") :propagate 0 :destination (slot-value key-press 'event) :event-mask xcb:EventMask:NoEvent - :event raw-data)))) + :event raw-data))) + ;; Make Emacs aware of this event when defining keyboard macros. + (when (and defining-kbd-macro event) + (set-transient-map '(keymap (t . (lambda () (interactive))))) + (exwm-input--unread-event event))) (xcb:+request exwm--connection (make-instance 'xcb:AllowEvents :mode mode -- cgit 1.4.1 From 84dad20d6648638443bc7c731d7d60bbf0872bdb Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 23 Sep 2016 18:24:37 +0800 Subject: Allow selecting an X window from another workspace * exwm-input.el (exwm-input--on-buffer-list-update): Filter out switch-frame events. (exwm-input--update-focus): Switch workspace to set input focus on an X window from another workspace. --- exwm-input.el | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 8101cbd528..b956f5c663 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -160,6 +160,7 @@ This value should always be overwritten.") "Run in `buffer-list-update-hook' to track input focus." (when (and (not (minibufferp)) ;Do not set input focus on minibuffer window. (eq (current-buffer) (window-buffer)) ;e.g. `with-temp-buffer'. + (not (eq this-command #'handle-switch-frame)) (not (exwm-workspace--client-p))) (setq exwm-input--update-focus-window (selected-window)) (exwm-input--update-focus-defer))) @@ -214,14 +215,7 @@ This value should always be overwritten.") (with-current-buffer (window-buffer window) (if (eq major-mode 'exwm-mode) (if (not (eq exwm--frame exwm-workspace--current)) - ;; Do not focus X windows on other workspace. - (progn - (set-frame-parameter exwm--frame 'exwm-urgency t) - (setq exwm-workspace--switch-history-outdated t) - (force-mode-line-update) - ;; The application may have changed its input focus - (select-window - (frame-selected-window exwm-workspace--current))) + (exwm-workspace-switch exwm--frame) (exwm--log "Set focus on #x%x" exwm--id) (exwm-input--set-focus exwm--id) (when exwm--floating-frame -- cgit 1.4.1 From 0833e8dc4a3fdb6f0ea9651ece2e7d2d4fae97ec Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 23 Sep 2016 18:29:11 +0800 Subject: Redirect KeyPress events sent to system tray icons * exwm-systemtray.el (exwm-systemtray--embed): Select KeyPress events on system tray icons. (exwm-systemtray--on-KeyPress): New function for redirecting KeyPress events. (exwm-systemtray--init): Attach the event listener. * exwm-systemtray.el (exwm-systemtray--init): Fix a typo. --- exwm-systemtray.el | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/exwm-systemtray.el b/exwm-systemtray.el index 36f7f3b601..49c93cf004 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -118,7 +118,18 @@ You shall use the default value if using auto-hide minibuffer.") :window icon :value-mask xcb:CW:EventMask :event-mask (logior xcb:EventMask:ResizeRedirect + xcb:EventMask:KeyPress xcb:EventMask:PropertyChange))) + ;; Grab all keys and forward them to Emacs frame. + (unless (exwm-workspace--minibuffer-own-frame-p) + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:GrabKey + :owner-events 0 + :grab-window icon + :modifiers xcb:ModMask:Any + :key xcb:Grab:Any + :pointer-mode xcb:GrabMode:Async + :keyboard-mode xcb:GrabMode:Async))) (setq visible (slot-value info 'flags)) (if visible (setq visible @@ -276,6 +287,23 @@ You shall use the default value if using auto-hide minibuffer.") (t (exwm--log "(System Tray) Unknown opcode message: %s" obj))))))) +(defun exwm-systemtray--on-KeyPress (data _synthetic) + "Forward all KeyPress events to Emacs frame." + ;; This function is only executed when there's no autohide minibuffer, + ;; a workspace frame has the input focus and the pointer is over a + ;; tray icon. + (let ((dest (frame-parameter (selected-frame) 'exwm-outer-id)) + (obj (make-instance 'xcb:KeyPress))) + (xcb:unmarshal obj data) + (setf (slot-value obj 'event) dest) + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:SendEvent + :propagate 0 + :destination dest + :event-mask xcb:EventMask:NoEvent + :event (xcb:marshal obj exwm-systemtray--connection)))) + (xcb:flush exwm-systemtray--connection)) + (defun exwm-systemtray--on-workspace-switch () "Reparent/Refresh the system tray in `exwm-workspace-switch-hook'." (unless (exwm-workspace--minibuffer-own-frame-p) @@ -390,7 +418,8 @@ You shall use the default value if using auto-hide minibuffer.") ;; Bottom aligned. y (- (exwm-workspace--current-height) exwm-systemtray-height))) (setq parent (string-to-number (frame-parameter frame 'window-id)) - depth (slot-value (xcb:+request-unchecked+reply exwm--connection + depth (slot-value (xcb:+request-unchecked+reply + exwm-systemtray--connection (make-instance 'xcb:GetGeometry :drawable parent)) 'depth)) @@ -425,6 +454,9 @@ You shall use the default value if using auto-hide minibuffer.") #'exwm-systemtray--on-PropertyNotify) (xcb:+event exwm-systemtray--connection 'xcb:ClientMessage #'exwm-systemtray--on-ClientMessage) + (unless (exwm-workspace--minibuffer-own-frame-p) + (xcb:+event exwm-systemtray--connection 'xcb:KeyPress + #'exwm-systemtray--on-KeyPress)) ;; Add hook to move/reparent the embedder. (add-hook 'exwm-workspace-switch-hook #'exwm-systemtray--on-workspace-switch) (when (boundp 'exwm-randr-refresh-hook) -- cgit 1.4.1 From 2597f74c7fb9ad290a45c89d0025bebcf739a976 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 23 Sep 2016 18:36:09 +0800 Subject: Remember the geometries of floating X windows * exwm-floating.el (exwm-floating--stop-moveresize): * exwm-layout.el (exwm-layout-enlarge-window): Update the geometry after resizing. --- exwm-floating.el | 31 ++++++++++++++++++++++--------- exwm-layout.el | 2 ++ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index f2cc09be46..5b8cc89abc 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -550,22 +550,35 @@ context of the corresponding buffer.") ;; Unmanaged. (eq major-mode 'exwm-mode)) (let ((edges (window-inside-absolute-pixel-edges (frame-selected-window))) - (id (buffer-local-value 'exwm--id - (window-buffer (frame-selected-window))))) + x y width height id) + (setq x (pop edges) + y (pop edges) + width (- (pop edges) x) + height (- (pop edges) y)) + (with-current-buffer (window-buffer (frame-selected-window)) + (setq id exwm--id) + (with-slots ((x* x) + (y* y) + (width* width) + (height* height)) + exwm--geometry + (setf x* x + y* y + width* width + height* height))) (xcb:+request exwm--connection (make-instance 'xcb:SendEvent - :propagate 0 :destination id + :propagate 0 + :destination id :event-mask xcb:EventMask:StructureNotify :event (xcb:marshal (make-instance 'xcb:ConfigureNotify :event id :window id :above-sibling xcb:Window:None - :x (elt edges 0) - :y (elt edges 1) - :width (- (elt edges 2) - (elt edges 0)) - :height (- (elt edges 3) - (elt edges 1)) + :x x + :y y + :width width + :height height :border-width 0 :override-redirect 0) exwm--connection))))) diff --git a/exwm-layout.el b/exwm-layout.el index 355b834650..5a31c947c1 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -460,6 +460,7 @@ windows." (setq width (max (+ exwm--normal-hints-min-width margin) (+ width delta)))))) (when width + (setf (slot-value exwm--geometry 'width) width) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window (frame-parameter exwm--floating-frame @@ -492,6 +493,7 @@ windows." (setq height (max (+ exwm--normal-hints-min-height margin) (+ height delta)))))) (when height + (setf (slot-value exwm--geometry 'height) height) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window (frame-parameter exwm--floating-frame -- cgit 1.4.1 From 6be75083c2adeb54882061e82597fab4cdf2a4f4 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 23 Sep 2016 18:41:43 +0800 Subject: Use X window borders ; This commit replaces the internal borders of Emacs frames with X ; window borders. This should make the flickering issue of floating X ; windows less serious. * exwm-floating.el (exwm-floating--border-pixel) (exwm-floating--border-colormap): New variables for storing border pixel and its colormap. (exwm-floating--set-floating): Do not set the internal border (and background color) of floating frames; do not take `exwm-floating-border-width' into account when calculating geometries; set the border of floating X window containers. (exwm-floating--unset-floating): No need to restore the position of X windows any more; hide the border of floating X window containers. (exwm-floating--init): Initialize the border pixel. * exwm-layout.el (exwm-layout-set-fullscreen) (exwm-layout-unset-fullscreen): Show/Hide container border respectively. * exwm-manage.el (exwm-manage--manage-window): Set the border pixel and colormap of X window containers. * exwm-workspace.el (exwm-workspace-move-window): Do not set the internal border and background color of floating frames. * exwm.el (exwm--on-ClientMessage): Simplify the code for calculating _NET_REQUEST_FRAME_EXTENTS. --- exwm-floating.el | 56 +++++++++++++++++++++++++++++++++++++++---------------- exwm-layout.el | 10 +++++++--- exwm-manage.el | 11 +++++++++-- exwm-workspace.el | 6 ------ exwm.el | 19 ++++++++++--------- 5 files changed, 66 insertions(+), 36 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 5b8cc89abc..1780e5fc32 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -32,6 +32,12 @@ (defvar exwm-floating-border-width 1 "Border width of the floating window.") (defvar exwm-floating-border-color "navy" "Border color of the floating window.") +(defvar exwm-floating--border-pixel nil + "Border pixel drawn around floating X windows.") +(defvar exwm-floating--border-colormap nil + "Colormap used by the border pixel. + +This is also used by X window containers.") (defvar exwm-floating-setup-hook nil "Normal hook run when an X window has been made floating, in the @@ -95,8 +101,6 @@ context of the corresponding buffer.") (get-buffer "*scratch*"))) (make-frame `((minibuffer . nil) ;use the default minibuffer. - (background-color . ,exwm-floating-border-color) - (internal-border-width . ,exwm-floating-border-width) (left . 10000) (top . 10000) (width . ,window-min-width) @@ -135,8 +139,7 @@ context of the corresponding buffer.") (window-pixel-height (minibuffer-window original-frame))) (* 2 (window-mode-line-height)) - (window-header-line-height window) - (* 2 exwm-floating-border-width))) + (window-header-line-height window))) (display-height (* 2 (/ display-height 2)))) ;round to even (if (> width display-width) ;; Too wide @@ -229,14 +232,17 @@ context of the corresponding buffer.") (make-instance 'xcb:ReparentWindow :window outer-id :parent frame-container :x 0 :y 0)) ;; Place the X window container. + ;; Also show the floating border. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window container :value-mask (eval-when-compile (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y)) - :x (- x exwm-floating-border-width) - :y (- y exwm-floating-border-width))) + xcb:ConfigWindow:Y + xcb:ConfigWindow:BorderWidth)) + :x x + :y y + :border-width exwm-floating-border-width)) (exwm-floating--set-allowed-actions id nil) (xcb:flush exwm--connection) ;; Set window/buffer @@ -294,14 +300,6 @@ context of the corresponding buffer.") (make-instance 'xcb:ChangeWindowAttributes :window id :value-mask xcb:CW:EventMask :event-mask exwm--client-event-mask)) - ;; The X window might have been moved due to the floating border. - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window id - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y) - :x 0 - :y 0)) ;; Reparent the floating frame back to the root window. (let ((frame-id (frame-parameter exwm--floating-frame 'exwm-outer-id)) (frame-container (frame-parameter exwm--floating-frame @@ -318,11 +316,14 @@ context of the corresponding buffer.") (make-instance 'xcb:DestroyWindow :window frame-container)))) ;; Put the X window container just above the Emacs frame container ;; (the stacking order won't change from now on). + ;; Also hide the possible floating border. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window exwm--container - :value-mask (logior xcb:ConfigWindow:Sibling + :value-mask (logior xcb:ConfigWindow:BorderWidth + xcb:ConfigWindow:Sibling xcb:ConfigWindow:StackMode) + :border-width 0 :sibling (frame-parameter exwm-workspace--current 'exwm-container) :stack-mode xcb:StackMode:Above))) @@ -681,6 +682,29 @@ Both DELTA-X and DELTA-Y default to 1. This command should be bound locally." (defun exwm-floating--init () "Initialize floating module." + ;; Check border width. + (unless (and (integerp exwm-floating-border-width) + (> exwm-floating-border-width 0)) + (setq exwm-floating-border-width 0)) + ;; Initialize border pixel. + (when (> exwm-floating-border-width 0) + (setq exwm-floating--border-colormap + (slot-value (car (slot-value + (xcb:get-setup exwm--connection) 'roots)) + 'default-colormap)) + (unless (stringp exwm-floating-border-color) + (setq exwm-floating-border-color "")) + (let* ((color (x-color-values exwm-floating-border-color)) + reply) + (when color + (setq reply (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:AllocColor + :cmap exwm-floating--border-colormap + :red (pop color) + :green (pop color) + :blue (pop color)))) + (when reply + (setq exwm-floating--border-pixel (slot-value reply 'pixel)))))) ;; Initialize cursors for moving/resizing a window (xcb:cursor:init exwm--connection) (setq exwm-floating--cursor-move diff --git a/exwm-layout.el b/exwm-layout.el index 5a31c947c1..79728bb7c4 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -214,7 +214,9 @@ (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window exwm--container - :value-mask xcb:ConfigWindow:StackMode + :value-mask (logior xcb:ConfigWindow:BorderWidth + xcb:ConfigWindow:StackMode) + :border-width 0 :stack-mode xcb:StackMode:Above)) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_STATE @@ -240,9 +242,11 @@ :window exwm--container :value-mask (eval-when-compile (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y)) + xcb:ConfigWindow:Y + xcb:ConfigWindow:BorderWidth)) :x (elt exwm--floating-frame-position 0) - :y (elt exwm--floating-frame-position 1))) + :y (elt exwm--floating-frame-position 1) + :border-width exwm-floating-border-width)) ;; Put the X window just above the Emacs frame. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow diff --git a/exwm-manage.el b/exwm-manage.el index 0f1a80b729..f1deda0759 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -92,6 +92,8 @@ corresponding buffer.") :window exwm--root :data (vconcat (mapcar #'car exwm--id-buffer-alist))))) +(defvar exwm-floating--border-colormap) +(defvar exwm-floating--border-pixel) (defvar exwm-workspace--current) (defvar exwm-workspace--switch-history-outdated) (defvar exwm-workspace-current-index) @@ -243,11 +245,16 @@ corresponding buffer.") :class xcb:WindowClass:InputOutput :visual 0 :value-mask (logior xcb:CW:BackPixmap + (if exwm-floating--border-pixel + xcb:CW:BorderPixel 0) xcb:CW:OverrideRedirect - xcb:CW:EventMask) + xcb:CW:EventMask + xcb:CW:Colormap) :background-pixmap xcb:BackPixmap:ParentRelative + :border-pixel exwm-floating--border-pixel :override-redirect 1 - :event-mask xcb:EventMask:SubstructureRedirect)) + :event-mask xcb:EventMask:SubstructureRedirect + :colormap exwm-floating--border-colormap)) (exwm--debug (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_NAME diff --git a/exwm-workspace.el b/exwm-workspace.el index 24a59f9fab..9034c11337 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -636,9 +636,6 @@ INDEX must not exceed the current number of workspaces." :window id :data (exwm-workspace--position exwm--frame))))) -(defvar exwm-floating-border-width) -(defvar exwm-floating-border-color) - (declare-function exwm-layout--show "exwm-layout.el" (id &optional window)) (declare-function exwm-layout--hide "exwm-layout.el" (id)) (declare-function exwm-layout--refresh "exwm-layout.el") @@ -691,9 +688,6 @@ INDEX must not exceed the current number of workspaces." (get-buffer "*scratch*"))) (make-frame `((minibuffer . ,(minibuffer-window frame)) - (background-color . ,exwm-floating-border-color) - (internal-border-width - . ,exwm-floating-border-width) (left . 10000) (top . 10000) (width . ,window-min-width) diff --git a/exwm.el b/exwm.el index cb7fd80aa1..850becfd9e 100644 --- a/exwm.el +++ b/exwm.el @@ -399,19 +399,20 @@ ;; _NET_REQUEST_FRAME_EXTENTS ((= type xcb:Atom:_NET_REQUEST_FRAME_EXTENTS) (let ((buffer (exwm--id->buffer id)) - left right top btm) + top btm) (if (or (not buffer) (not (buffer-local-value 'exwm--floating-frame buffer))) - (setq left 0 right 0 top 0 btm 0) - (setq left exwm-floating-border-width - right exwm-floating-border-width - top (+ exwm-floating-border-width (window-header-line-height)) - btm (+ exwm-floating-border-width - (window-mode-line-height)))) + (setq top 0 + btm 0) + (setq top (window-header-line-height) + btm (window-mode-line-height))) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_FRAME_EXTENTS - :window id :left left :right right - :top top :bottom btm))) + :window id + :left 0 + :right 0 + :top top + :bottom btm))) (xcb:flush exwm--connection)) ;; _NET_WM_DESKTOP. ((= type xcb:Atom:_NET_WM_DESKTOP) -- cgit 1.4.1 From 7fbd5220f2e92059ad38776a3f8c674002396870 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 25 Sep 2016 19:54:13 +0800 Subject: Fix `exwm-manage--kill-buffer-query-function' * exwm-manage.el (exwm-manage--kill-buffer-query-function): Check buffer-local variables for destroyed X windows; Avoid force killing clients that support WM_DELETE_WINDOW but not _NET_WM_PING; Use `y-or-n-p' instead for querying. --- exwm-manage.el | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index f1deda0759..42a4353dcd 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -449,14 +449,19 @@ manager is shutting down." (defun exwm-manage--kill-buffer-query-function () "Run in `kill-buffer-query-functions'." (catch 'return - (when (xcb:+request-checked+request-check exwm--connection - (make-instance 'xcb:MapWindow :window exwm--id)) + (when (or (not exwm--id) + (not exwm--container) + (xcb:+request-checked+request-check exwm--connection + (make-instance 'xcb:MapWindow + :window exwm--id))) ;; The X window is no longer alive so just close the buffer. ;; Destroy the container. ;; Hide the container to prevent flickering. - (xcb:+request exwm--connection - (make-instance 'xcb:UnmapWindow :window exwm--container)) - (xcb:flush exwm--connection) + (when exwm--container + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow + :window exwm--container)) + (xcb:flush exwm--connection)) (when exwm--floating-frame (let ((window (frame-parameter exwm--floating-frame 'exwm-outer-id))) (xcb:+request exwm--connection @@ -466,8 +471,10 @@ manager is shutting down." :window window :parent exwm--root :x 0 :y 0)))) - (xcb:+request exwm--connection - (make-instance 'xcb:DestroyWindow :window exwm--container)) + (when exwm--container + (xcb:+request exwm--connection + (make-instance 'xcb:DestroyWindow + :window exwm--container))) (xcb:flush exwm--connection) (throw 'return t)) (unless (memq xcb:Atom:WM_DELETE_WINDOW exwm--protocols) @@ -492,15 +499,8 @@ manager is shutting down." (xcb:flush exwm--connection) ;; (unless (memq xcb:Atom:_NET_WM_PING exwm--protocols) - ;; The window does not support _NET_WM_PING. To make sure it'll die, - ;; kill it after the time runs out. - ;; Hide the container to prevent flickering. - (xcb:+request exwm--connection - (make-instance 'xcb:UnmapWindow :window exwm--container)) - (xcb:flush exwm--connection) - (run-with-timer exwm-manage-ping-timeout nil #'exwm-manage--kill-client - id) - ;; Wait for DestroyNotify event. + ;; For X windows without _NET_WM_PING support, we'd better just + ;; wait for DestroyNotify events. (throw 'return nil)) ;; Try to determine if the X window is dead with _NET_WM_PING. (setq exwm-manage--ping-lock t) @@ -517,7 +517,7 @@ manager is shutting down." exwm--connection))) (xcb:flush exwm--connection) (with-timeout (exwm-manage-ping-timeout - (if (yes-or-no-p (format "'%s' is not responding. \ + (if (y-or-n-p (format "'%s' is not responding. \ Would you like to kill it? " (buffer-name))) (progn (exwm-manage--kill-client id) -- cgit 1.4.1 From 1c8101afbf8eda6f990cde6b551a6bd59e1f7dfd Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 25 Sep 2016 19:58:16 +0800 Subject: Fix a frame resizing problem for Lucid build * exwm-manage.el (exwm-manage--frame-outer-id-list): New variable for storing frame window-outer-id's. (exwm-manage--add-frame, exwm-manage--remove-frame): New functions for adding/removing ids to/from the variable. (exwm-manage--init): Add the functions to the corresponding hooks. (exwm-manage--on-ConfigureRequest): Check for frames and avoid handling them. --- exwm-manage.el | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index 42a4353dcd..52c324db36 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -560,6 +560,20 @@ Would you like to kill it? " (defconst exwm-manage--width-delta-min 5) (defconst exwm-manage--height-delta-min 5) +(defvar exwm-manage--frame-outer-id-list nil + "List of window-outer-id's of all frames.") + +(defun exwm-manage--add-frame (frame) + "Run in `after-make-frame-functions'." + (push (string-to-number (frame-parameter frame 'outer-window-id)) + exwm-manage--frame-outer-id-list)) + +(defun exwm-manage--remove-frame (frame) + "Run in `delete-frame-functions'." + (setq exwm-manage--frame-outer-id-list + (delq (string-to-number (frame-parameter frame 'outer-window-id)) + exwm-manage--frame-outer-id-list))) + (defun exwm-manage--on-ConfigureRequest (data _synthetic) "Handle ConfigureRequest event." (let ((obj (make-instance 'xcb:ConfigureRequest)) @@ -638,14 +652,19 @@ border-width: %d; sibling: #x%x; stack-mode: %d" nil t))) (exwm--log "ConfigureWindow (preserve geometry)") ;; Configure the unmanaged window. - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window window - :value-mask value-mask - :x x :y y :width width :height height - :border-width border-width - :sibling sibling - :stack-mode stack-mode)))))) + ;; But Emacs frames should be excluded. Generally we don't + ;; receive ConfigureRequest events from Emacs frames since we + ;; have set OverrideRedirect on them, but this is not true for + ;; Lucid build (as of 25.1). + (unless (memq window exwm-manage--frame-outer-id-list) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window window + :value-mask value-mask + :x x :y y :width width :height height + :border-width border-width + :sibling sibling + :stack-mode stack-mode))))))) (xcb:flush exwm--connection)) (declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) @@ -698,6 +717,8 @@ border-width: %d; sibling: #x%x; stack-mode: %d" :name-len (length atom-name) :name atom-name)) 'atom))) + (add-hook 'after-make-frame-functions #'exwm-manage--add-frame) + (add-hook 'delete-frame-functions #'exwm-manage--remove-frame) (xcb:+event exwm--connection 'xcb:ConfigureRequest #'exwm-manage--on-ConfigureRequest) (xcb:+event exwm--connection 'xcb:MapRequest #'exwm-manage--on-MapRequest) -- cgit 1.4.1 From 7d732d78f5b3eec89cbbde65596c19a691052625 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 30 Sep 2016 13:30:31 +0200 Subject: Use read-buffer-to-switch in exwm-workspace-switch-to-buffer read-buffer-to-switch elides the current buffer from the list of completions and selects a sane default. --- exwm-workspace.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 9034c11337..12f0be7748 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -783,7 +783,7 @@ INDEX must not exceed the current number of workspaces." (rename-buffer (substring (buffer-name) 1)))))) (prog1 (with-local-quit - (list (get-buffer (read-buffer "Switch to buffer: " nil t)))) + (list (get-buffer (read-buffer-to-switch "Switch to buffer: ")))) ;; Hide buffers on other workspaces (unless exwm-workspace-show-all-buffers (dolist (pair exwm--id-buffer-alist) -- cgit 1.4.1 From 575162b6b66a794ea3fc027ddcd72096e30276ab Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 1 Oct 2016 18:54:45 +0800 Subject: Bump version to 0.11 --- exwm.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm.el b/exwm.el index 850becfd9e..eff9bac867 100644 --- a/exwm.el +++ b/exwm.el @@ -4,7 +4,7 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0.10 +;; Version: 0.11 ;; Package-Requires: ((xelb "0.11")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From 089afdc8ccbd37647f3b9d3b6181db5bcf3e43e9 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 6 Oct 2016 12:47:56 +0800 Subject: Fix problems with active minibuffer * exwm-floating.el (exwm-floating--unset-floating): Never use the minibuffer window to display an `exwm-mode' buffer. * exwm-input.el (exwm-input--on-buffer-list-update) (exwm-input--update-focus): Allow updating input focus when the minibuffer is active. (exwm-input--update-focus): Handle the case when an auto-hiding minibuffer is active. (exwm-input--during-key-sequence): Renamed to `exwm-input--line-mode-passthrough'. (exwm-input--line-mode-passthrough): New variable for forcing all events to be passed to Emacs in line-mode. (exwm-input--on-KeyPress-line-mode, exwm-input-send-next-key): Use it. (exwm-input--finish-key-sequence, exwm-input--init, exwm-input--exit): Drop `exwm-input--finish-key-sequence'. (exwm-input--line-mode-cache): New variable for caching incomplete key sequences. (exwm-input--cache-event): New function for handling new key events. (exwm-input--on-KeyPress-line-mode, exwm-input--on-KeyPress-char-mode): Use it. --- exwm-floating.el | 6 ++-- exwm-input.el | 83 +++++++++++++++++++++++++++++--------------------------- 2 files changed, 47 insertions(+), 42 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 1780e5fc32..dffdc3cd11 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -337,9 +337,11 @@ context of the corresponding buffer.") (setq window-size-fixed nil exwm--floating-frame nil exwm--frame exwm-workspace--current)) + ;; Only show X windows in normal state. (unless (exwm-layout--iconic-state-p) - ;; Only show X windows in normal state. - (let ((window (frame-selected-window exwm-workspace--current))) + ;; Show it in the selected Emacs window but skip the mini-window. + (let ((window (or (minibuffer-selected-window) + (frame-selected-window exwm-workspace--current)))) (set-window-buffer window buffer) (select-window window)))) (with-current-buffer (exwm--id->buffer id) diff --git a/exwm-input.el b/exwm-input.el index b956f5c663..4a8fdbb016 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -158,8 +158,7 @@ This value should always be overwritten.") (defun exwm-input--on-buffer-list-update () "Run in `buffer-list-update-hook' to track input focus." - (when (and (not (minibufferp)) ;Do not set input focus on minibuffer window. - (eq (current-buffer) (window-buffer)) ;e.g. `with-temp-buffer'. + (when (and (eq (current-buffer) (window-buffer)) ;e.g. `with-temp-buffer'. (not (eq this-command #'handle-switch-frame)) (not (exwm-workspace--client-p))) (setq exwm-input--update-focus-window (selected-window)) @@ -209,9 +208,7 @@ This value should always be overwritten.") (defun exwm-input--update-focus (window) "Update input focus." (setq exwm-input--update-focus-lock t) - (when (and (window-live-p window) - ;; Do not update input focus when there's an active minibuffer. - (not (active-minibuffer-window))) + (when (window-live-p window) (with-current-buffer (window-buffer window) (if (eq major-mode 'exwm-mode) (if (not (eq exwm--frame exwm-workspace--current)) @@ -238,7 +235,15 @@ This value should always be overwritten.") ;; so switch to it. (exwm-workspace-switch (selected-frame)) ;; The focus is still on the current workspace. - (select-frame-set-input-focus (window-frame window) t) + (if (not (and (exwm-workspace--minibuffer-own-frame-p) + (minibufferp))) + (select-frame-set-input-focus (window-frame window) t) + ;; X input focus should be set on the previously selected + ;; frame. + (select-frame-set-input-focus (window-frame + (minibuffer-selected-window)) + t) + (select-frame (window-frame window) t)) (exwm-input--set-active-window) (xcb:flush exwm--connection)))))) (setq exwm-input--update-focus-lock nil)) @@ -256,20 +261,6 @@ This value should always be overwritten.") :window exwm--root :data (or id xcb:Window:None)))) -(defvar exwm-input--during-key-sequence nil - "Non-nil indicates Emacs is waiting for more keys to form a key sequence.") -(defvar exwm-input--temp-line-mode nil - "Non-nil indicates it's in temporary line-mode for char-mode.") - -(defun exwm-input--finish-key-sequence () - "Mark the end of a key sequence (with the aid of `pre-command-hook')." - (when (and exwm-input--during-key-sequence - (not (equal [?\C-u] (this-single-command-keys)))) - (setq exwm-input--during-key-sequence nil) - (when exwm-input--temp-line-mode - (setq exwm-input--temp-line-mode nil) - (exwm-input--release-keyboard)))) - (declare-function exwm-floating--start-moveresize "exwm-floating.el" (id &optional type)) (declare-function exwm-workspace--position "exwm-workspace.el" (frame)) @@ -415,26 +406,47 @@ This value should always be overwritten.") (defvar exwm-input--during-command nil "Indicate whether between `pre-command-hook' and `post-command-hook'.") +(defvar exwm-input--line-mode-passthrough nil + "Non-nil makes 'line-mode' forwards all events to Emacs.") + +(defvar exwm-input--line-mode-cache nil "Cache for incomplete key sequence.") + +(defvar exwm-input--temp-line-mode nil + "Non-nil indicates it's in temporary line-mode for char-mode.") + +(defun exwm-input--cache-event (event) + "Cache EVENT." + (setq exwm-input--line-mode-cache + (vconcat exwm-input--line-mode-cache (vector event))) + ;; When the key sequence is complete. + (unless (keymapp (key-binding exwm-input--line-mode-cache)) + (setq exwm-input--line-mode-cache nil) + (when exwm-input--temp-line-mode + (setq exwm-input--temp-line-mode nil) + (exwm-input--release-keyboard))) + (exwm-input--unread-event event)) + (defun exwm-input--on-KeyPress-line-mode (key-press raw-data) "Parse X KeyPress event to Emacs key event and then feed the command loop." (with-slots (detail state) key-press (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) - event minibuffer-window mode) + event mode) (when (and (/= 0 (car keysym)) (setq event (xcb:keysyms:keysym->event exwm--connection (car keysym) (logand state (lognot (cdr keysym))))) - (or exwm-input--during-key-sequence + (or exwm-input--line-mode-passthrough exwm-input--during-command - (setq minibuffer-window (active-minibuffer-window)) + ;; Forward the event when there is an incomplete key + ;; sequence or when the minibuffer is active. + exwm-input--line-mode-cache + (eq (active-minibuffer-window) (selected-window)) + ;; (memq event exwm-input--global-prefix-keys) (memq event exwm-input-prefix-keys) (memq event exwm-input--simulation-prefix-keys))) (setq mode xcb:Allow:AsyncKeyboard) - (unless minibuffer-window (setq exwm-input--during-key-sequence t)) - ;; Feed this event to command loop. Also force it to be added to - ;; `this-command-keys'. - (exwm-input--unread-event event)) + (exwm-input--cache-event event)) (unless mode (if (= 0 (logand #x6000 state)) ;Check the 13~14 bits. ;; Not an XKB state; just replay it. @@ -469,15 +481,9 @@ This value should always be overwritten.") exwm--connection (car keysym) (logand state (lognot (cdr keysym)))))) (when (eq major-mode 'exwm-mode) - ;; FIXME: This functionality seems not working, e.g. when this - ;; command would activate the minibuffer, the temporary - ;; line-mode would actually quit before the minibuffer - ;; becomes active. - (setq exwm-input--temp-line-mode t - exwm-input--during-key-sequence t) + (setq exwm-input--temp-line-mode t) (exwm-input--grab-keyboard)) ;grab keyboard temporarily - (setq unread-command-events - (append unread-command-events (list event)))))) + (exwm-input--cache-event event)))) (xcb:+request exwm--connection (make-instance 'xcb:AllowEvents :mode xcb:Allow:AsyncKeyboard @@ -609,7 +615,7 @@ This value should always be overwritten.") (let (key keys) (dotimes (i times) ;; Skip events not from keyboard - (setq exwm-input--during-key-sequence t) + (setq exwm-input--line-mode-passthrough t) (catch 'break (while t (setq key (read-key (format "Send key: %s (%d/%d)" @@ -618,7 +624,7 @@ This value should always be overwritten.") (when (and (listp key) (eq (car key) t)) (setq key (cdr key))) (unless (listp key) (throw 'break nil)))) - (setq exwm-input--during-key-sequence nil) + (setq exwm-input--line-mode-passthrough nil) (setq keys (vconcat keys (vector key))) (exwm-input--fake-key key)))) @@ -739,8 +745,6 @@ Its usage is the same with `exwm-input-set-simulation-keys'." (xcb:+event exwm--connection 'xcb:FocusIn #'exwm-input--on-FocusIn) ;; The input focus should be set on the frame when minibuffer is active. (add-hook 'minibuffer-setup-hook #'exwm-input--on-minibuffer-setup) - ;; `pre-command-hook' marks the end of a key sequence (existing or not) - (add-hook 'pre-command-hook #'exwm-input--finish-key-sequence) ;; Control `exwm-input--during-command' (add-hook 'pre-command-hook #'exwm-input--on-pre-command) (add-hook 'post-command-hook #'exwm-input--on-post-command) @@ -753,7 +757,6 @@ Its usage is the same with `exwm-input-set-simulation-keys'." (defun exwm-input--exit () "Exit the input module." - (remove-hook 'pre-command-hook #'exwm-input--finish-key-sequence) (remove-hook 'pre-command-hook #'exwm-input--on-pre-command) (remove-hook 'post-command-hook #'exwm-input--on-post-command) (remove-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update) -- cgit 1.4.1 From 09b4f0915828222942d8ac3ae7f970e8d3d8468a Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 6 Oct 2016 13:04:30 +0800 Subject: Avoid updating input focus when renaming buffers * exwm-workspace.el (exwm-workspace-switch-to-buffer) (exwm-workspace-rename-buffer): Do not update input focus. --- exwm-workspace.el | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 12f0be7748..617ad38208 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -636,6 +636,7 @@ INDEX must not exceed the current number of workspaces." :window id :data (exwm-workspace--position exwm--frame))))) +(declare-function exwm-input--on-buffer-list-update "exwm-input.el" ()) (declare-function exwm-layout--show "exwm-layout.el" (id &optional window)) (declare-function exwm-layout--hide "exwm-layout.el" (id)) (declare-function exwm-layout--refresh "exwm-layout.el") @@ -780,7 +781,10 @@ INDEX must not exceed the current number of workspaces." (dolist (pair exwm--id-buffer-alist) (with-current-buffer (cdr pair) (when (= ?\s (aref (buffer-name) 0)) - (rename-buffer (substring (buffer-name) 1)))))) + (let ((buffer-list-update-hook + (remq #'exwm-input--on-buffer-list-update + buffer-list-update-hook))) + (rename-buffer (substring (buffer-name) 1))))))) (prog1 (with-local-quit (list (get-buffer (read-buffer-to-switch "Switch to buffer: ")))) @@ -790,7 +794,10 @@ INDEX must not exceed the current number of workspaces." (with-current-buffer (cdr pair) (unless (or (eq exwm--frame exwm-workspace--current) (= ?\s (aref (buffer-name) 0))) - (rename-buffer (concat " " (buffer-name)))))))))) + (let ((buffer-list-update-hook + (remq #'exwm-input--on-buffer-list-update + buffer-list-update-hook))) + (rename-buffer (concat " " (buffer-name))))))))))) (when buffer-or-name (with-current-buffer buffer-or-name (if (eq major-mode 'exwm-mode) @@ -820,7 +827,10 @@ INDEX must not exceed the current number of workspaces." (get-buffer (concat " " newname)))) (not (eq tmp (current-buffer)))) (setq newname (format "%s<%d>" basename (cl-incf counter)))) - (rename-buffer (concat (and hidden " ") newname)))) + (let ((buffer-list-update-hook + (remq #'exwm-input--on-buffer-list-update + buffer-list-update-hook))) + (rename-buffer (concat (and hidden " ") newname))))) (defun exwm-workspace--x-create-frame (orig-fun params) "Set override-redirect on the frame created by `x-create-frame'." -- cgit 1.4.1 From d0c2ca75f8ac78d87aba3e80902fb0f113b10279 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 6 Oct 2016 19:26:53 +0800 Subject: Fix a possible deadlock * exwm-input.el (exwm-input--update-focus): Unlock before switching workspace. Also make sure the correct Emacs window is chosen for the target workspace. --- exwm-input.el | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 4a8fdbb016..a2f478068e 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -212,7 +212,10 @@ This value should always be overwritten.") (with-current-buffer (window-buffer window) (if (eq major-mode 'exwm-mode) (if (not (eq exwm--frame exwm-workspace--current)) - (exwm-workspace-switch exwm--frame) + (progn + (set-frame-parameter exwm--frame 'exwm-selected-window window) + (run-with-idle-timer 0 nil #'exwm-workspace-switch + exwm--frame)) (exwm--log "Set focus on #x%x" exwm--id) (exwm-input--set-focus exwm--id) (when exwm--floating-frame @@ -233,7 +236,11 @@ This value should always be overwritten.") (not (eq (selected-frame) exwm-workspace--current))) ;; The focus is on another workspace (e.g. it got clicked) ;; so switch to it. - (exwm-workspace-switch (selected-frame)) + (progn + (set-frame-parameter (selected-frame) 'exwm-selected-window + window) + (run-with-idle-timer 0 nil #'exwm-workspace-switch + (selected-frame))) ;; The focus is still on the current workspace. (if (not (and (exwm-workspace--minibuffer-own-frame-p) (minibufferp))) -- cgit 1.4.1 From bb0c5f4c6b05e030e6afc29b08594d4141780223 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 13 Nov 2016 19:23:10 +0800 Subject: Grab global prefix keys with num-lock mask set. * exwm-input.el (exwm-input--update-global-prefix-keys): Grab global prefix keys with num-lock mask set, or those keys won't be activated when num-lock is enabled. --- exwm-input.el | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/exwm-input.el b/exwm-input.el index a2f478068e..9e98c62e12 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -380,7 +380,14 @@ This value should always be overwritten.") (slot-value grab-key 'key) keycode) (when (or (= 0 keycode) (xcb:+request-checked+request-check exwm--connection - grab-key)) + grab-key) + ;; Also grab this key with num-lock mask set. + (when (/= 0 xcb:keysyms:num-lock-mask) + (setf (slot-value grab-key 'modifiers) + (logior (cdr keysym) + xcb:keysyms:num-lock-mask)) + (xcb:+request-checked+request-check exwm--connection + grab-key))) (user-error "[EXWM] Failed to grab key: %s" (single-key-description k)))))))))) -- cgit 1.4.1 From 197745e6a4ca8e6fd21ee88243c6cff3563a1d02 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 19 Nov 2016 23:05:47 +0800 Subject: Bump version to 0.12 --- exwm.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm.el b/exwm.el index eff9bac867..2316f2ed77 100644 --- a/exwm.el +++ b/exwm.el @@ -4,8 +4,8 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0.11 -;; Package-Requires: ((xelb "0.11")) +;; Version: 0.12 +;; Package-Requires: ((xelb "0.12")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From b11ac2e6dc0821a5b1c6079cf24771fde3aeb4d4 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 26 Dec 2016 22:22:46 +0800 Subject: Fix a bug with zero floating border width * exwm-manage.el (exwm-manage--manage-window): Avoid setting ColorMap mask when creating an X window if floating border is zero. --- exwm-manage.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exwm-manage.el b/exwm-manage.el index 52c324db36..aab6199a88 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -249,7 +249,8 @@ corresponding buffer.") xcb:CW:BorderPixel 0) xcb:CW:OverrideRedirect xcb:CW:EventMask - xcb:CW:Colormap) + (if exwm-floating--border-colormap + xcb:CW:Colormap 0)) :background-pixmap xcb:BackPixmap:ParentRelative :border-pixel exwm-floating--border-pixel :override-redirect 1 -- cgit 1.4.1 From 4c043471c5e2b3c45e94bd98a3a5b6f83283fc39 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 3 Jan 2017 00:14:33 +0800 Subject: Prevent certain frame parameters from being saved/restored * exwm-input.el (exwm-input--init): * exwm-randr.el (exwm-randr--init): * exwm-workspace.el (exwm-workspace--init): Add certain frame parameters into `frameset-filter-alist' to prevent them from being saved/restored which makes little sense and is problematic. --- exwm-input.el | 6 +++++- exwm-randr.el | 6 +++++- exwm-workspace.el | 7 ++++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 9e98c62e12..7f174c2ad7 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -767,7 +767,11 @@ Its usage is the same with `exwm-input-set-simulation-keys'." ;; Re-grab global keys. (add-hook 'exwm-workspace-list-change-hook #'exwm-input--on-workspace-list-change) - (exwm-input--on-workspace-list-change)) + (exwm-input--on-workspace-list-change) + ;; Prevent frame parameters introduced by this module from being + ;; saved/restored. + (dolist (i '(exwm-grabbed)) + (push (cons i :never) frameset-filter-alist))) (defun exwm-input--exit () "Exit the input module." diff --git a/exwm-randr.el b/exwm-randr.el index 5caf875b2e..1d77fb5563 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -160,7 +160,11 @@ ;; xcb:randr:NotifyMask:CrtcChange)) )) (xcb:flush exwm--connection) - (add-hook 'exwm-workspace-list-change-hook #'exwm-randr--refresh))))) + (add-hook 'exwm-workspace-list-change-hook #'exwm-randr--refresh)))) + ;; Prevent frame parameters introduced by this module from being + ;; saved/restored. + (dolist (i '(exwm-randr-output exwm-geometry)) + (push (cons i :never) frameset-filter-alist))) (defun exwm-randr--exit () "Exit the RandR module." diff --git a/exwm-workspace.el b/exwm-workspace.el index 617ad38208..9d5401e28b 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1466,7 +1466,12 @@ applied to all subsequently created X frames." (add-hook 'delete-frame-functions #'exwm-workspace--remove-frame-as-workspace) ;; Switch to the first workspace - (exwm-workspace-switch 0 t)) + (exwm-workspace-switch 0 t) + ;; Prevent frame parameters introduced by this module from being + ;; saved/restored. + (dolist (i '(exwm-outer-id exwm-id exwm-container exwm-workspace + fullscreen exwm-selected-window exwm-urgency)) + (push (cons i :never) frameset-filter-alist))) (defun exwm-workspace--exit () "Exit the workspace module." -- cgit 1.4.1 From 9926d87b65f1a5e627032b8da329799cd612f289 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 5 Feb 2017 17:49:42 +0800 Subject: Update copyright year to 2017 --- exwm-cm.el | 2 +- exwm-config.el | 2 +- exwm-core.el | 2 +- exwm-floating.el | 2 +- exwm-input.el | 2 +- exwm-layout.el | 2 +- exwm-manage.el | 2 +- exwm-randr.el | 2 +- exwm-systemtray.el | 2 +- exwm-workspace.el | 2 +- exwm.el | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/exwm-cm.el b/exwm-cm.el index f7a37598e5..532a8b583a 100644 --- a/exwm-cm.el +++ b/exwm-cm.el @@ -1,6 +1,6 @@ ;;; exwm-cm.el --- Compositing Manager for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2016 Free Software Foundation, Inc. +;; Copyright (C) 2016-2017 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-config.el b/exwm-config.el index 8c54607006..2f1fa797c2 100644 --- a/exwm-config.el +++ b/exwm-config.el @@ -1,6 +1,6 @@ ;;; exwm-config.el --- Predefined configurations -*- lexical-binding: t -*- -;; Copyright (C) 2015-2016 Free Software Foundation, Inc. +;; Copyright (C) 2015-2017 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-core.el b/exwm-core.el index 8f5299f16b..ef54773b77 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -1,6 +1,6 @@ ;;; exwm-core.el --- Core definitions -*- lexical-binding: t -*- -;; Copyright (C) 2015-2016 Free Software Foundation, Inc. +;; Copyright (C) 2015-2017 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-floating.el b/exwm-floating.el index dffdc3cd11..2d1bf17783 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -1,6 +1,6 @@ ;;; exwm-floating.el --- Floating Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015-2016 Free Software Foundation, Inc. +;; Copyright (C) 2015-2017 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-input.el b/exwm-input.el index 7f174c2ad7..5b43370abd 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -1,6 +1,6 @@ ;;; exwm-input.el --- Input Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015-2016 Free Software Foundation, Inc. +;; Copyright (C) 2015-2017 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-layout.el b/exwm-layout.el index 79728bb7c4..c10eecbd6d 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -1,6 +1,6 @@ ;;; exwm-layout.el --- Layout Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015-2016 Free Software Foundation, Inc. +;; Copyright (C) 2015-2017 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-manage.el b/exwm-manage.el index aab6199a88..c47d5c1c7f 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -1,7 +1,7 @@ ;;; exwm-manage.el --- Window Management Module for -*- lexical-binding: t -*- ;;; EXWM -;; Copyright (C) 2015-2016 Free Software Foundation, Inc. +;; Copyright (C) 2015-2017 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-randr.el b/exwm-randr.el index 1d77fb5563..19b9bb93b8 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -1,6 +1,6 @@ ;;; exwm-randr.el --- RandR Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015-2016 Free Software Foundation, Inc. +;; Copyright (C) 2015-2017 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-systemtray.el b/exwm-systemtray.el index 49c93cf004..a7bb664d41 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -1,7 +1,7 @@ ;;; exwm-systemtray.el --- System Tray Module for -*- lexical-binding: t -*- ;;; EXWM -;; Copyright (C) 2016 Free Software Foundation, Inc. +;; Copyright (C) 2016-2017 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-workspace.el b/exwm-workspace.el index 9d5401e28b..fa1d47f079 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1,6 +1,6 @@ ;;; exwm-workspace.el --- Workspace Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015-2016 Free Software Foundation, Inc. +;; Copyright (C) 2015-2017 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm.el b/exwm.el index 2316f2ed77..032c5db99a 100644 --- a/exwm.el +++ b/exwm.el @@ -1,6 +1,6 @@ ;;; exwm.el --- Emacs X Window Manager -*- lexical-binding: t -*- -;; Copyright (C) 2015-2016 Free Software Foundation, Inc. +;; Copyright (C) 2015-2017 Free Software Foundation, Inc. ;; Author: Chris Feng ;; Maintainer: Chris Feng -- cgit 1.4.1 From f221f837cb1e38e00fdbe54d25301c09d77c7ae9 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 5 Feb 2017 17:50:52 +0800 Subject: Fix checkdoc warnings --- exwm-cm.el | 4 ++-- exwm-config.el | 2 +- exwm-input.el | 4 ++-- exwm-layout.el | 4 ++-- exwm-randr.el | 7 ++++--- exwm-systemtray.el | 2 +- exwm-workspace.el | 2 +- 7 files changed, 13 insertions(+), 12 deletions(-) diff --git a/exwm-cm.el b/exwm-cm.el index 532a8b583a..233d5e0773 100644 --- a/exwm-cm.el +++ b/exwm-cm.el @@ -115,7 +115,7 @@ "The default value of opacity when it's not explicitly specified. The value should be a floating number between 0 (transparent) and 100 -(opaque). A value of nil also means opaque.") +\(opaque). A value of nil also means opaque.") (defvar exwm-cm--hash nil "The hash table associating X window IDs to their attributes.") @@ -226,7 +226,7 @@ If called interactively, XWIN would be the selected X window." (car (exwm-cm--get-tree xwin))) (defsubst exwm-cm--get-siblings (xwin) - "Get a list of subtrees of the siblings of X window XWIN" + "Get a list of subtrees of the siblings of X window XWIN." (cdr (exwm-cm--get-tree xwin))) (defsubst exwm-cm--get-subtree (xwin) diff --git a/exwm-config.el b/exwm-config.el index 2f1fa797c2..73004f6480 100644 --- a/exwm-config.el +++ b/exwm-config.el @@ -119,4 +119,4 @@ You can find the original one at `exwm-config-ido-buffer-window-other-frame'." (provide 'exwm-config) -;; exwm-config.el ends here +;;; exwm-config.el ends here diff --git a/exwm-input.el b/exwm-input.el index 5b43370abd..d410bec791 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -30,8 +30,8 @@ ;; + Pointer simulation mode (e.g. 'C-c 1'/'C-c 2' for single/double click, ;; move with arrow keys). ;; + Simulation keys to mimic Emacs key bindings for text edit (redo, select, -;; cancel, clear, etc). Some of them are not present on common keyboard -;; (keycode = 0). May need to use XKB extension. +;; cancel, clear, etc). Some of them are not present on common keyboard +;; (keycode = 0). May need to use XKB extension. ;;; Code: diff --git a/exwm-layout.el b/exwm-layout.el index c10eecbd6d..ba7f65cf3b 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -178,7 +178,7 @@ (interactive) (with-current-buffer (if id (exwm--id->buffer id) (window-buffer)) (when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) - (user-error "Already in full-screen mode.")) + (user-error "Already in full-screen mode")) ;; Save the position of floating frame. (when exwm--floating-frame (let* ((geometry (xcb:+request-unchecked+reply exwm--connection @@ -232,7 +232,7 @@ (interactive) (with-current-buffer (if id (exwm--id->buffer id) (window-buffer)) (unless (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) - (user-error "Not in full-screen mode.")) + (user-error "Not in full-screen mode")) ;; Restore the size of this workspace. (exwm-workspace--set-fullscreen exwm--frame) (if exwm--floating-frame diff --git a/exwm-randr.el b/exwm-randr.el index 19b9bb93b8..d9c083c8af 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -21,9 +21,10 @@ ;;; Commentary: -;; This module adds RandR support for EXWM. Currently it requires external -;; tools such as xrandr(1) to properly configure RandR first. This dependency -;; may be removed in the future, but more work is needed before that. +;; This module adds RandR support for EXWM. Currently it requires external +;; tools such as xrandr(1) to properly configure RandR first. This +;; dependency may be removed in the future, but more work is needed before +;; that. ;; To use this module, load, enable it and configure ;; `exwm-randr-workspace-output-plist' and `exwm-randr-screen-change-hook' diff --git a/exwm-systemtray.el b/exwm-systemtray.el index a7bb664d41..60a2e3b3de 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -485,4 +485,4 @@ You shall use the default value if using auto-hide minibuffer.") (provide 'exwm-systemtray) -;; exwm-systemtray.el ends here +;;; exwm-systemtray.el ends here diff --git a/exwm-workspace.el b/exwm-workspace.el index fa1d47f079..17aa2c08cb 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -116,7 +116,7 @@ NIL if FRAME is not a workspace" (defvar exwm-workspace--prompt-add-allowed nil "Non-nil to allow adding workspace from the prompt.") (defvar exwm-workspace--prompt-delete-allowed nil - "Non-nil to allow deleting workspace from the prompt") + "Non-nil to allow deleting workspace from the prompt.") (defvar exwm-workspace--create-silently nil "When non-nil workspaces are created in the background (not switched to). -- cgit 1.4.1 From d69e7075ad1198034b0ea3264e0159373375a0a9 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 5 Feb 2017 17:51:43 +0800 Subject: Add a menu entry to `exwm-mode-map' * exwm-core.el (exwm-mode-menu): Add a menu entry for closing X windows. --- exwm-core.el | 1 + 1 file changed, 1 insertion(+) diff --git a/exwm-core.el b/exwm-core.el index ef54773b77..8fc1a73dc7 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -180,6 +180,7 @@ ["Toggle floating" exwm-floating-toggle-floating] ["Toggle fullscreen mode" exwm-layout-toggle-fullscreen] ["Hide window" exwm-floating-hide exwm--floating-frame] + ["Close window" (kill-buffer (current-buffer))] "---" "*Resizing*" -- cgit 1.4.1 From 6723436052e80da11e11c50ea1dfb21853f99d11 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 10 Feb 2017 22:14:56 +0800 Subject: Correct a renamed variable * exwm-core.el (exwm--kmacro-map): Correct `exwm-input--during-key-sequence' that was left out in 089afdc8. --- exwm-core.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 8fc1a73dc7..c5f28cb79c 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -80,7 +80,7 @@ (logior xcb:EventMask:StructureNotify xcb:EventMask:PropertyChange)) "Event mask set on all managed windows.") -(defvar exwm-input--during-key-sequence) +(defvar exwm-input--line-mode-passthrough) (defvar exwm-input--global-prefix-keys) (defvar exwm-input-prefix-keys) (defvar exwm-input--simulation-prefix-keys) @@ -154,7 +154,7 @@ (lambda () (interactive) (cond - ((or exwm-input--during-key-sequence + ((or exwm-input--line-mode-passthrough ;; Do not test `exwm-input--during-command'. (active-minibuffer-window) (memq last-input-event exwm-input--global-prefix-keys) -- cgit 1.4.1 From 0ae1e7327ea900ad1b35ce145db63cb0837251de Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 12 Feb 2017 23:11:27 +0800 Subject: Bump version to 0.13 --- exwm.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm.el b/exwm.el index 032c5db99a..7a25479036 100644 --- a/exwm.el +++ b/exwm.el @@ -4,7 +4,7 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0.12 +;; Version: 0.13 ;; Package-Requires: ((xelb "0.12")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From 46e1d7a754bdf66cf89e6d3fad3d1c4bf003db3a Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 19 Feb 2017 14:53:43 -0800 Subject: Use let instead of setq when setting exwm-input--line-mode-passthrough That way, exwm won't clobber user modifications to this variable. --- exwm-input.el | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index d410bec791..22e21d69a4 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -629,16 +629,15 @@ This value should always be overwritten.") (let (key keys) (dotimes (i times) ;; Skip events not from keyboard - (setq exwm-input--line-mode-passthrough t) - (catch 'break - (while t - (setq key (read-key (format "Send key: %s (%d/%d)" - (key-description keys) - (1+ i) times))) - (when (and (listp key) (eq (car key) t)) - (setq key (cdr key))) - (unless (listp key) (throw 'break nil)))) - (setq exwm-input--line-mode-passthrough nil) + (let ((exwm-input--line-mode-passthrough t)) + (catch 'break + (while t + (setq key (read-key (format "Send key: %s (%d/%d)" + (key-description keys) + (1+ i) times))) + (when (and (listp key) (eq (car key) t)) + (setq key (cdr key))) + (unless (listp key) (throw 'break nil))))) (setq keys (vconcat keys (vector key))) (exwm-input--fake-key key)))) -- cgit 1.4.1 From f299ca5ed7056842aa59e9978061bca650316a71 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 24 Feb 2017 23:05:43 +0800 Subject: Fix systemtray position * exwm-workspace.el (exwm-workspace--update-workareas-hook): New hook run when workareas get updated. (exwm-workspace--update-workareas): Run the hook. * exwm-systemtray.el (exwm-systemtray--on-workspace-switch) (exwm-systemtray--on-randr-refresh): Take struts into account when calculating the position for systemtray. (exwm-systemtray--on-struts-update): Alias of `exwm-systemtray--on-randr-refresh'. (exwm-systemtray--init, exwm-systemtray--exit): Manipulate `exwm-workspace--update-workareas-hook'. --- exwm-systemtray.el | 18 ++++++++++++++++-- exwm-workspace.el | 5 ++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/exwm-systemtray.el b/exwm-systemtray.el index 60a2e3b3de..82a2ccdbf2 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -304,6 +304,9 @@ You shall use the default value if using auto-hide minibuffer.") :event (xcb:marshal obj exwm-systemtray--connection)))) (xcb:flush exwm-systemtray--connection)) +(defvar exwm-workspace--workareas) +(defvar exwm-workspace-current-index) + (defun exwm-systemtray--on-workspace-switch () "Reparent/Refresh the system tray in `exwm-workspace-switch-hook'." (unless (exwm-workspace--minibuffer-own-frame-p) @@ -314,7 +317,9 @@ You shall use the default value if using auto-hide minibuffer.") (frame-parameter exwm-workspace--current 'window-id)) :x 0 - :y (- (exwm-workspace--current-height) + :y (- (elt (elt exwm-workspace--workareas + exwm-workspace-current-index) + 3) exwm-systemtray-height)))) (exwm-systemtray--refresh)) @@ -325,10 +330,15 @@ You shall use the default value if using auto-hide minibuffer.") (make-instance 'xcb:ConfigureWindow :window exwm-systemtray--embedder :value-mask xcb:ConfigWindow:Y - :y (- (exwm-workspace--current-height) + :y (- (elt (elt exwm-workspace--workareas + exwm-workspace-current-index) + 3) exwm-systemtray-height)))) (exwm-systemtray--refresh)) +(defalias 'exwm-systemtray--on-struts-update + #'exwm-systemtray--on-randr-refresh) + (defvar xcb:Atom:_NET_SYSTEM_TRAY_S0) (defvar exwm-workspace--minibuffer) @@ -459,6 +469,8 @@ You shall use the default value if using auto-hide minibuffer.") #'exwm-systemtray--on-KeyPress)) ;; Add hook to move/reparent the embedder. (add-hook 'exwm-workspace-switch-hook #'exwm-systemtray--on-workspace-switch) + (add-hook 'exwm-workspace--update-workareas-hook + #'exwm-systemtray--on-struts-update) (when (boundp 'exwm-randr-refresh-hook) (add-hook 'exwm-randr-refresh-hook #'exwm-systemtray--on-randr-refresh))) @@ -472,6 +484,8 @@ You shall use the default value if using auto-hide minibuffer.") exwm-systemtray--embedder nil) (remove-hook 'exwm-workspace-switch-hook #'exwm-systemtray--on-workspace-switch) + (remove-hook 'exwm-workspace--update-workareas-hook + #'exwm-systemtray--on-struts-update) (when (boundp 'exwm-randr-refresh-hook) (remove-hook 'exwm-randr-refresh-hook #'exwm-systemtray--on-randr-refresh)))) diff --git a/exwm-workspace.el b/exwm-workspace.el index 17aa2c08cb..66865989dd 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -247,6 +247,8 @@ Value nil means to use the default position which is fixed at bottom, while (append exwm-workspace--struts (list struts*)))))))))) (defvar exwm-workspace--workareas nil "Workareas (struts excluded).") +(defvar exwm-workspace--update-workareas-hook nil + "Normal hook run when workareas get updated.") (defun exwm-workspace--update-workareas () "Update `exwm-workspace--workareas'." @@ -309,7 +311,8 @@ Value nil means to use the default position which is fixed at bottom, while (cl-incf (aref w 3) delta)))))) ;; Save the result. (setq exwm-workspace--workareas workareas) - (xcb:flush exwm--connection))) + (xcb:flush exwm--connection)) + (run-hooks 'exwm-workspace--update-workareas-hook)) (defvar exwm-workspace--fullscreen-frame-count 0 "Count the fullscreen workspace frames.") -- cgit 1.4.1 From d0e98957485afa6a91d3c8be1520d65e46987718 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 24 Feb 2017 23:25:02 +0800 Subject: ; Expose `exwm-input-line-mode-passthrough' as a public interface. --- exwm-core.el | 4 ++-- exwm-input.el | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index c5f28cb79c..750f134d1e 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -80,7 +80,7 @@ (logior xcb:EventMask:StructureNotify xcb:EventMask:PropertyChange)) "Event mask set on all managed windows.") -(defvar exwm-input--line-mode-passthrough) +(defvar exwm-input-line-mode-passthrough) (defvar exwm-input--global-prefix-keys) (defvar exwm-input-prefix-keys) (defvar exwm-input--simulation-prefix-keys) @@ -154,7 +154,7 @@ (lambda () (interactive) (cond - ((or exwm-input--line-mode-passthrough + ((or exwm-input-line-mode-passthrough ;; Do not test `exwm-input--during-command'. (active-minibuffer-window) (memq last-input-event exwm-input--global-prefix-keys) diff --git a/exwm-input.el b/exwm-input.el index 22e21d69a4..809f41ecec 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -420,7 +420,7 @@ This value should always be overwritten.") (defvar exwm-input--during-command nil "Indicate whether between `pre-command-hook' and `post-command-hook'.") -(defvar exwm-input--line-mode-passthrough nil +(defvar exwm-input-line-mode-passthrough nil "Non-nil makes 'line-mode' forwards all events to Emacs.") (defvar exwm-input--line-mode-cache nil "Cache for incomplete key sequence.") @@ -449,7 +449,7 @@ This value should always be overwritten.") (setq event (xcb:keysyms:keysym->event exwm--connection (car keysym) (logand state (lognot (cdr keysym))))) - (or exwm-input--line-mode-passthrough + (or exwm-input-line-mode-passthrough exwm-input--during-command ;; Forward the event when there is an incomplete key ;; sequence or when the minibuffer is active. @@ -629,7 +629,7 @@ This value should always be overwritten.") (let (key keys) (dotimes (i times) ;; Skip events not from keyboard - (let ((exwm-input--line-mode-passthrough t)) + (let ((exwm-input-line-mode-passthrough t)) (catch 'break (while t (setq key (read-key (format "Send key: %s (%d/%d)" -- cgit 1.4.1 From eebf764edd1eebcd40c71dc61ab08f8ddb9e517f Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 5 Mar 2017 01:09:26 +0800 Subject: ; Update the workaround for bug#23980 --- exwm-input.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm-input.el b/exwm-input.el index 809f41ecec..54b5f30ff1 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -404,7 +404,7 @@ This value should always be overwritten.") ;; add it into (this-command-keys) there, we use `unread-command-events' ;; differently on Emacs 24 and 25. (eval-and-compile - (if (< emacs-major-version 26) + (if (< emacs-major-version 27) (defsubst exwm-input--unread-event (event) (setq unread-command-events (append unread-command-events (list event)))) -- cgit 1.4.1 From 52dc2616240d43b8c15e10edfdd34b1e0039a10f Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 5 Mar 2017 01:10:43 +0800 Subject: Manually run `focus-in-hook' and `focus-out-hook' ; Since X windows are managed by EXWM, the current Emacs frame should always be considered focused logically. * exwm-workspace.el (exwm-workspace-switch): Manually run `focus-in-hook' and `focus-out-hook'. (exwm-workspace--handle-focus-in, exwm-workspace--handle-focus-out): New functions for overriding `handle-focus-in' and `handle-focus-out'. (exwm-workspace--init, exwm-workspace--exit): Override `handle-focus-in' and `handle-focus-out' --- exwm-workspace.el | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/exwm-workspace.el b/exwm-workspace.el index 66865989dd..1b904d4f11 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -451,6 +451,7 @@ The optional FORCE option is for internal use only." (exwm-workspace--prompt-delete-allowed t)) (exwm-workspace--prompt-for-workspace "Switch to [+/-]: "))))) (let* ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index)) + (old-frame exwm-workspace--current) (index (exwm-workspace--position frame)) (workspace (frame-parameter frame 'exwm-workspace)) (window (frame-parameter frame 'exwm-selected-window))) @@ -510,6 +511,10 @@ The optional FORCE option is for internal use only." (make-instance 'xcb:ewmh:set-_NET_CURRENT_DESKTOP :window exwm--root :data index)) (xcb:flush exwm--connection)) + (when (frame-live-p old-frame) + (with-selected-frame old-frame + (run-hooks 'focus-out-hook))) + (run-hooks 'focus-in-hook) (run-hooks 'exwm-workspace-switch-hook))) (defvar exwm-workspace-switch-create-limit 10 @@ -1348,6 +1353,14 @@ applied to all subsequently created X frames." (setf (cdr x-parameters) (append new-x-parameters (cdr x-parameters))))) +(defun exwm-workspace--handle-focus-in (_orig-func _event) + "Replacement for `handle-focus-in'." + (interactive "e")) + +(defun exwm-workspace--handle-focus-out (_orig-func _event) + "Replacement for `handle-focus-out'." + (interactive "e")) + (defun exwm-workspace--init () "Initialize workspace module." ;; Prevent unexpected exit @@ -1463,6 +1476,10 @@ applied to all subsequently created X frames." (xcb:flush exwm--connection) ;; We have to advice `x-create-frame' or every call to it would hang EXWM (advice-add 'x-create-frame :around #'exwm-workspace--x-create-frame) + ;; We have to manually handle focus-in and focus-out events for Emacs + ;; frames. + (advice-add 'handle-focus-in :around #'exwm-workspace--handle-focus-in) + (advice-add 'handle-focus-out :around #'exwm-workspace--handle-focus-out) ;; Make new frames create new workspaces. (add-hook 'after-make-frame-functions #'exwm-workspace--add-frame-as-workspace) @@ -1494,6 +1511,8 @@ applied to all subsequently created X frames." (cl-delete '(exwm-workspace--display-buffer) display-buffer-alist :test #'equal)) (advice-remove 'x-create-frame #'exwm-workspace--x-create-frame) + (advice-remove 'handle-focus-in #'exwm-workspace--handle-focus-in) + (advice-remove 'handle-focus-out #'exwm-workspace--handle-focus-out) (remove-hook 'after-make-frame-functions #'exwm-workspace--add-frame-as-workspace) (remove-hook 'delete-frame-functions -- cgit 1.4.1 From 7a5bb1156d07848e465f55fd31f8ec755b52aa01 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 10 Apr 2017 00:30:43 +0800 Subject: Only add graphic frames to `exwm-manage--frame-outer-id-list' * exwm-manage.el (exwm-manage--add-frame, exwm-manage--remove-frame): Add checks for graphic frames. --- exwm-manage.el | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index c47d5c1c7f..84dcf0a3a3 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -566,14 +566,16 @@ Would you like to kill it? " (defun exwm-manage--add-frame (frame) "Run in `after-make-frame-functions'." - (push (string-to-number (frame-parameter frame 'outer-window-id)) - exwm-manage--frame-outer-id-list)) + (when (display-graphic-p frame) + (push (string-to-number (frame-parameter frame 'outer-window-id)) + exwm-manage--frame-outer-id-list))) (defun exwm-manage--remove-frame (frame) "Run in `delete-frame-functions'." - (setq exwm-manage--frame-outer-id-list - (delq (string-to-number (frame-parameter frame 'outer-window-id)) - exwm-manage--frame-outer-id-list))) + (when (display-graphic-p frame) + (setq exwm-manage--frame-outer-id-list + (delq (string-to-number (frame-parameter frame 'outer-window-id)) + exwm-manage--frame-outer-id-list)))) (defun exwm-manage--on-ConfigureRequest (data _synthetic) "Handle ConfigureRequest event." -- cgit 1.4.1 From d4cfa564be680cec3fe1372e3bc9b29834e0d98a Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 16 Apr 2017 21:46:07 +0800 Subject: Avoid comparing X display names * exwm-workspace.el (exwm-workspace--add-frame-as-workspace): An X display name set by XELB can be different from (but equivalent with) the one set by Emacs. --- exwm-workspace.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 1b904d4f11..3efd084525 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1164,8 +1164,8 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (exwm--log "Frame `%s' is already a workspace" frame)) ((not (display-graphic-p frame)) (exwm--log "Frame `%s' is not graphical" frame)) - ((not (string-equal (slot-value exwm--connection 'display) - (frame-parameter frame 'display))) + ((not (memq frame (frames-on-display-list (slot-value exwm--connection + 'display)))) (exwm--log "Frame `%s' is on a different DISPLAY (%S instead of %S)" frame (frame-parameter frame 'display) -- cgit 1.4.1 From 2b7449ea425df236471496cd96f061d70f3ba15a Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 23 Apr 2017 23:39:06 +0800 Subject: ; Do not cache events for non-`exwm-mode' buffers --- exwm-input.el | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 54b5f30ff1..b602a92561 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -494,10 +494,12 @@ This value should always be overwritten.") (setq event (xcb:keysyms:keysym->event exwm--connection (car keysym) (logand state (lognot (cdr keysym)))))) - (when (eq major-mode 'exwm-mode) + (if (not (eq major-mode 'exwm-mode)) + (exwm-input--unread-event event) + ;; Grab keyboard temporarily. (setq exwm-input--temp-line-mode t) - (exwm-input--grab-keyboard)) ;grab keyboard temporarily - (exwm-input--cache-event event)))) + (exwm-input--grab-keyboard) + (exwm-input--cache-event event))))) (xcb:+request exwm--connection (make-instance 'xcb:AllowEvents :mode xcb:Allow:AsyncKeyboard -- cgit 1.4.1 From 267ebd7f559cab0b864e0a31515d04c09a9f38e7 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 7 May 2017 18:40:08 +0800 Subject: Force repositioning floating Emacs frames * exwm-floating.el (exwm-floating--set-floating): Ditto. --- exwm-floating.el | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 2d1bf17783..ea68cb500b 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -266,14 +266,15 @@ context of the corresponding buffer.") ;; FIXME: Strangely, the Emacs frame can move itself at this point ;; when there are left/top struts set. Force resetting its ;; position seems working, but it'd better to figure out why. - (when exwm-workspace--struts - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window outer-id - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y) - :x 0 :y 0)) - (xcb:flush exwm--connection))) + ;; FIXME: This also happens in another case (#220) where the cause is + ;; still unclear. + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window outer-id + :value-mask (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y) + :x 0 :y 0)) + (xcb:flush exwm--connection)) (with-current-buffer (exwm--id->buffer id) (run-hooks 'exwm-floating-setup-hook)) ;; Redraw the frame. -- cgit 1.4.1 From 2babc8d070fdd1bc95391f66d49db33d6102cffb Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 7 May 2017 18:43:42 +0800 Subject: Bump version to 0.14 --- exwm.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm.el b/exwm.el index 7a25479036..e33f2452a1 100644 --- a/exwm.el +++ b/exwm.el @@ -4,7 +4,7 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0.13 +;; Version: 0.14 ;; Package-Requires: ((xelb "0.12")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From 851ed8c280cb9fe47f388773f3ee0ab90815fa99 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 30 May 2017 01:25:58 +0800 Subject: Fix X display name comparison * exwm-workspace--add-frame-as-workspace (exwm-workspace--add-frame-as-workspace): Manually compare X display names as there's no built-in function handles this correctly. --- exwm-workspace.el | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 3efd084525..302e82e6df 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1164,8 +1164,11 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (exwm--log "Frame `%s' is already a workspace" frame)) ((not (display-graphic-p frame)) (exwm--log "Frame `%s' is not graphical" frame)) - ((not (memq frame (frames-on-display-list (slot-value exwm--connection - 'display)))) + ((not (string-equal + (replace-regexp-in-string "\\.0$" "" + (slot-value exwm--connection 'display)) + (replace-regexp-in-string "\\.0$" "" + (frame-parameter frame 'display)))) (exwm--log "Frame `%s' is on a different DISPLAY (%S instead of %S)" frame (frame-parameter frame 'display) -- cgit 1.4.1 From 19515dc0749e820be5ec528c7a9a7bdceaac7c65 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 31 May 2017 00:12:22 +0800 Subject: Allow switching to a workspace by selecting one X window on it * exwm-workspace.el (exwm-workspace-switch-to-buffer): Only allows it when `exwm-layout-show-all-buffers' is nil. --- exwm-workspace.el | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 302e82e6df..8c22b7c1d8 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -779,6 +779,8 @@ INDEX must not exceed the current number of workspaces." (xcb:flush exwm--connection))) (setq exwm-workspace--switch-history-outdated t))) +(defvar exwm-layout-show-all-buffers) + ;;;###autoload (defun exwm-workspace-switch-to-buffer (buffer-or-name) "Make the current Emacs window display another buffer." @@ -818,8 +820,16 @@ INDEX must not exceed the current number of workspaces." (select-frame-set-input-focus exwm--floating-frame) (select-window (frame-root-window exwm--floating-frame))) ;; On another workspace. - (exwm-workspace-move-window exwm-workspace--current - exwm--id)) + (if exwm-layout-show-all-buffers + (exwm-workspace-move-window exwm-workspace--current + exwm--id) + (let ((window (get-buffer-window buffer-or-name exwm--frame))) + (if window + (set-frame-parameter exwm--frame + 'exwm-selected-window window) + (set-window-buffer (frame-selected-window exwm--frame) + buffer-or-name))) + (exwm-workspace-switch exwm--frame))) ;; Ordinary buffer. (switch-to-buffer buffer-or-name))))) -- cgit 1.4.1 From 7d967e73619e3628848b711f482b5db816636bda Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 16 Jul 2017 17:08:24 +0800 Subject: Allow customizing workspace indexes * exwm-workspace.el (exwm-workspace-index-map): New variable for customizing workspace indexes. (exwm-workspace--update-switch-history): Use it. --- exwm-workspace.el | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 8c22b7c1d8..bebd954940 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -35,6 +35,10 @@ (defvar exwm-workspace--list nil "List of all workspaces (Emacs frames).") (defvar exwm-workspace--current nil "Current active workspace.") (defvar exwm-workspace-current-index 0 "Index of current active workspace.") +(defvar exwm-workspace-index-map #'number-to-string + "Function for mapping a workspace index to a string for display. + +By default `number-to-string' is applied which yields 0 1 2 ... .") (defsubst exwm-workspace--position (frame) "Retrieve index of given FRAME in workspace list. @@ -168,7 +172,7 @@ Please manually run the hook `exwm-workspace-list-change-hook' afterwards.") (lambda (j) (format (if (= i j) "[%s]" " %s ") (propertize - (int-to-string j) + (apply exwm-workspace-index-map (list j)) 'face (cond ((frame-parameter (elt exwm-workspace--list j) 'exwm-urgency) -- cgit 1.4.1 From 4eda6dde41e04765dcc519d123de20b020db6e4a Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 2 Aug 2017 23:05:57 +0800 Subject: Bump version to 0,15 --- exwm.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm.el b/exwm.el index e33f2452a1..8ef1fc4fe7 100644 --- a/exwm.el +++ b/exwm.el @@ -4,7 +4,7 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0.14 +;; Version: 0.15 ;; Package-Requires: ((xelb "0.12")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From 46dfaeb0310e4a331ffe5d19c0774c64e786ff27 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 31 Aug 2017 00:58:39 +0800 Subject: Avoid reusing dedicated window * exwm-floating.el (exwm-floating--unset-floating): * exwm-manage.el (exwm-manage--on-MapRequest): Do not select a dedicated window for displaying a buffer. --- exwm-floating.el | 6 +----- exwm-manage.el | 4 +--- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index ea68cb500b..af11b877b9 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -340,11 +340,7 @@ context of the corresponding buffer.") exwm--frame exwm-workspace--current)) ;; Only show X windows in normal state. (unless (exwm-layout--iconic-state-p) - ;; Show it in the selected Emacs window but skip the mini-window. - (let ((window (or (minibuffer-selected-window) - (frame-selected-window exwm-workspace--current)))) - (set-window-buffer window buffer) - (select-window window)))) + (pop-to-buffer-same-window buffer))) (with-current-buffer (exwm--id->buffer id) (run-hooks 'exwm-floating-exit-hook))) diff --git a/exwm-manage.el b/exwm-manage.el index 84dcf0a3a3..36a989665e 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -682,9 +682,7 @@ border-width: %d; sibling: #x%x; stack-mode: %d" (if (exwm-layout--iconic-state-p) ;; State change: iconic => normal. (when (eq exwm--frame exwm-workspace--current) - (set-window-buffer (frame-selected-window exwm--frame) - (current-buffer)) - (select-window (frame-selected-window exwm--frame))) + (pop-to-buffer-same-window (current-buffer))) (exwm--log "#x%x is already managed" window))) (if (/= exwm--root parent) (progn (xcb:+request exwm--connection -- cgit 1.4.1 From 7bc4ea5a6ee1d6d2948e8ba7e065be8ac33b8a47 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 31 Aug 2017 01:00:51 +0800 Subject: Update systemtray when initializing * exwm-systemtray.el (exwm-systemtray--init): Systemtray is not placed correctly when there's a panel launched before EXWM. --- exwm-systemtray.el | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/exwm-systemtray.el b/exwm-systemtray.el index 82a2ccdbf2..48bd8eacb1 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -472,7 +472,10 @@ You shall use the default value if using auto-hide minibuffer.") (add-hook 'exwm-workspace--update-workareas-hook #'exwm-systemtray--on-struts-update) (when (boundp 'exwm-randr-refresh-hook) - (add-hook 'exwm-randr-refresh-hook #'exwm-systemtray--on-randr-refresh))) + (add-hook 'exwm-randr-refresh-hook #'exwm-systemtray--on-randr-refresh)) + ;; The struts can be updated already. + (when exwm-workspace--workareas + (exwm-systemtray--on-struts-update))) (defun exwm-systemtray--exit () "Exit the systemtray module." -- cgit 1.4.1 From 75eb43e1e0a0e2efb7107ebed565abd40fdfd0c4 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 10 Sep 2017 18:00:01 +0800 Subject: Avoid comparing raw button events * exwm-input.el (exwm-input--move-keysym, exwm-input--move-mask) (exwm-input--resize-keysym, exwm-input--resize-mask): Removed. (exwm-input--on-ButtonPress): Compare using Emacs events rather than raw X11 events or it would fail with num-lock no. (exwm-input--init): No longer cache raw keysym/state for button events. --- exwm-input.el | 32 ++++++-------------------------- 1 file changed, 6 insertions(+), 26 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index b602a92561..d14b0b52d5 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -43,11 +43,6 @@ (defvar exwm-input-resize-event 's-down-mouse-3 "Emacs event to start resizing a window.") -(defvar exwm-input--move-keysym nil) -(defvar exwm-input--move-mask nil) -(defvar exwm-input--resize-keysym nil) -(defvar exwm-input--resize-mask nil) - (defvar exwm-input--timestamp-window nil) (defvar exwm-input--timestamp-atom nil) (defvar exwm-input--timestamp-callback nil) @@ -276,13 +271,14 @@ This value should always be overwritten.") "Handle ButtonPress event." (let ((obj (make-instance 'xcb:ButtonPress)) (mode xcb:Allow:SyncPointer) - window buffer frame) + button-event window buffer frame) (xcb:unmarshal obj data) (with-slots (detail time event state) obj - (setq window (get-buffer-window (exwm--id->buffer event) t) + (setq button-event (xcb:keysyms:keysym->event exwm--connection + detail state) + window (get-buffer-window (exwm--id->buffer event) t) buffer (window-buffer window)) - (cond ((and (= state exwm-input--move-mask) - (= detail exwm-input--move-keysym) + (cond ((and (eq button-event exwm-input-move-event) ;; Either an undecorated or a floating X window. (with-current-buffer buffer (or (not (eq major-mode 'exwm-mode)) @@ -290,8 +286,7 @@ This value should always be overwritten.") ;; Move (exwm-floating--start-moveresize event xcb:ewmh:_NET_WM_MOVERESIZE_MOVE)) - ((and (= state exwm-input--resize-mask) - (= detail exwm-input--resize-keysym) + ((and (eq button-event exwm-input-resize-event) (with-current-buffer buffer (or (not (eq major-mode 'exwm-mode)) exwm--floating-frame))) @@ -709,21 +704,6 @@ Its usage is the same with `exwm-input-set-simulation-keys'." "Initialize the keyboard module." ;; Refresh keyboard mapping (xcb:keysyms:init exwm--connection) - ;; Convert move/resize buttons - (let ((move-key (xcb:keysyms:event->keysym exwm--connection - exwm-input-move-event)) - (resize-key (xcb:keysyms:event->keysym exwm--connection - exwm-input-resize-event))) - (when (= 0 (car move-key)) - (user-error "[EXWM] Invalid key: %s" - (single-key-description exwm-input-move-event))) - (when (= 0 (car resize-key)) - (user-error "[EXWM] Invalid key: %s" - (single-key-description exwm-input-resize-event))) - (setq exwm-input--move-keysym (car move-key) - exwm-input--move-mask (cdr move-key) - exwm-input--resize-keysym (car resize-key) - exwm-input--resize-mask (cdr resize-key))) ;; Create the X window and intern the atom used to fetch timestamp. (setq exwm-input--timestamp-window (xcb:generate-id exwm--connection)) (xcb:+request exwm--connection -- cgit 1.4.1 From 3edf2e8880aff11803f4d4889914e05c3543e6e7 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 9 Oct 2017 00:29:05 +0800 Subject: Fix systemtray applications crash when restarting * exwm-systemtray.el (exwm-systemtray--exit): Reparent out the tray embedder to protect tray icons. --- exwm-systemtray.el | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/exwm-systemtray.el b/exwm-systemtray.el index 48bd8eacb1..4fc7ded331 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -480,6 +480,18 @@ You shall use the default value if using auto-hide minibuffer.") (defun exwm-systemtray--exit () "Exit the systemtray module." (when exwm-systemtray--connection + ;; Hide & reparent out the embedder before disconnection to prevent + ;; embedded icons from being reparented to an Emacs frame (which is the + ;; parent of the embedder). + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:UnmapWindow + :window exwm-systemtray--embedder)) + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:ReparentWindow + :window exwm-systemtray--embedder + :parent exwm--root + :x 0 + :y 0)) (xcb:disconnect exwm-systemtray--connection) (setq exwm-systemtray--connection nil exwm-systemtray--list nil -- cgit 1.4.1 From dea874e240896762183e90d9aecb38ff95bbc62f Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 5 Nov 2017 22:31:27 +0800 Subject: Filter out events relating to temp buffers * exwm-input.el (exwm-input--on-buffer-list-update): Filter out events relating to temp buffers by detecting if temp-buffer is present. --- exwm-input.el | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index d14b0b52d5..bee2ac6450 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -153,9 +153,12 @@ This value should always be overwritten.") (defun exwm-input--on-buffer-list-update () "Run in `buffer-list-update-hook' to track input focus." - (when (and (eq (current-buffer) (window-buffer)) ;e.g. `with-temp-buffer'. - (not (eq this-command #'handle-switch-frame)) - (not (exwm-workspace--client-p))) + (when (and (not (eq this-command #'handle-switch-frame)) + (not (exwm-workspace--client-p)) + ;; The following conditions filter out events relating to temp + ;; buffers. + (eq (current-buffer) (window-buffer)) + (not (get-buffer " *temp*"))) (setq exwm-input--update-focus-window (selected-window)) (exwm-input--update-focus-defer))) -- cgit 1.4.1 From 93d96757b611cc3d9e990d28e82f349c61dbdaa3 Mon Sep 17 00:00:00 2001 From: Ram Krishnan Date: Wed, 25 Oct 2017 20:50:25 -0700 Subject: Fix for "Selecting deleted buffer" exception --- exwm-manage.el | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index 36a989665e..fffc677c7f 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -405,21 +405,13 @@ manager is shutting down." (xcb:flush exwm--connection)) (let ((kill-buffer-func (lambda (buffer) - (with-current-buffer buffer - (let ((kill-buffer-query-functions nil) - (floating exwm--floating-frame)) - (kill-buffer) - (when floating - (select-window - (frame-selected-window exwm-workspace--current)))))))) - (if (not (active-minibuffer-window)) - ;; Kill the buffer as usual. - (funcall kill-buffer-func buffer) - ;; This can happen when this buffer was requested to be killed - ;; from the minibuffer (e.g. with `ido-kill-buffer-at-head'). - ;; We have to exit the minibuffer first or there'll be a - ;; "selecting deleted buffer" error. - (run-with-idle-timer 0 nil kill-buffer-func buffer) + (let ((kill-buffer-query-functions nil)) + (when exwm--floating-frame + (select-window + (frame-selected-window exwm-workspace--current))) + (kill-buffer buffer))))) + (run-with-idle-timer 0 nil kill-buffer-func buffer) + (when (active-minibuffer-window) (exit-minibuffer)))))) (defun exwm-manage--scan () -- cgit 1.4.1 From 088818fedbc7293a5d35e0a7962f4ebebf859b92 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 9 Nov 2017 01:17:58 +0800 Subject: Update the workarounds for `unread-command-events' * exwm-input.el (exwm-input--unread-event): Update the note. (exwm-input-send-next-key): Remove the workaround. --- exwm-input.el | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index bee2ac6450..3a817be4cd 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -397,10 +397,8 @@ This value should always be overwritten.") (when (called-interactively-p 'any) (exwm-input--update-global-prefix-keys))) -;; FIXME: Putting (t . EVENT) into `unread-command-events' does not really work -;; as documented in Emacs 24. Since inserting a conventional EVENT does -;; add it into (this-command-keys) there, we use `unread-command-events' -;; differently on Emacs 24 and 25. +;; Putting (t . EVENT) into `unread-command-events' does not really work +;; as documented for Emacs < 27. (eval-and-compile (if (< emacs-major-version 27) (defsubst exwm-input--unread-event (event) @@ -635,8 +633,6 @@ This value should always be overwritten.") (setq key (read-key (format "Send key: %s (%d/%d)" (key-description keys) (1+ i) times))) - (when (and (listp key) (eq (car key) t)) - (setq key (cdr key))) (unless (listp key) (throw 'break nil))))) (setq keys (vconcat keys (vector key))) (exwm-input--fake-key key)))) -- cgit 1.4.1 From 21351f6be32d6867edbf887cfb12c5c9d73873c6 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 9 Nov 2017 01:28:50 +0800 Subject: Be more precise when choosing the init hook * exwm.el (exwm-enable): Run `exwm-init' in `after-make-frame-functions' only for emacsclient and `window-setup-hook' only for ordinary sessions. --- exwm.el | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/exwm.el b/exwm.el index 8ef1fc4fe7..fe1024676b 100644 --- a/exwm.el +++ b/exwm.el @@ -726,8 +726,11 @@ ;; Ignore unrecognized command line arguments. This can be helpful ;; when EXWM is launched by some session manager. (push #'vector command-line-functions) - (add-hook 'window-setup-hook #'exwm-init t) ;for Emacs - (add-hook 'after-make-frame-functions #'exwm-init t) ;for Emacs Client + (if (display-graphic-p) + ;; emacs. + (add-hook 'window-setup-hook #'exwm-init t) + ;; emacsclient. + (add-hook 'after-make-frame-functions #'exwm-init t)) (add-hook 'kill-emacs-hook #'exwm--server-stop) (dolist (i exwm-blocking-subrs) (advice-add i :around #'exwm--server-eval-at))))) -- cgit 1.4.1 From 0106dd69c767c8f7fdecb5f6f804ecaa0094848b Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 19 Nov 2017 14:44:28 +0800 Subject: Correct the detection of emacsclient * exwm.el (exwm-enable): Use `daemonp' instead of `display-graphic-p'. --- exwm.el | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/exwm.el b/exwm.el index fe1024676b..b64bdeecec 100644 --- a/exwm.el +++ b/exwm.el @@ -726,11 +726,10 @@ ;; Ignore unrecognized command line arguments. This can be helpful ;; when EXWM is launched by some session manager. (push #'vector command-line-functions) - (if (display-graphic-p) - ;; emacs. - (add-hook 'window-setup-hook #'exwm-init t) - ;; emacsclient. - (add-hook 'after-make-frame-functions #'exwm-init t)) + (add-hook (if (daemonp) + 'after-make-frame-functions ;emacsclient + 'window-setup-hook) ;emacs + #'exwm-init t) (add-hook 'kill-emacs-hook #'exwm--server-stop) (dolist (i exwm-blocking-subrs) (advice-add i :around #'exwm--server-eval-at))))) -- cgit 1.4.1 From 55626530f4a523cccd1d24c8819f7e80543e0276 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 19 Nov 2017 14:46:35 +0800 Subject: Free unused X resources * exwm-cm.el (exwm-cm--paint-tree): Make sure the clip can be destroyed even the X window is in full screen mode. (exwm-cm--paint-transparent, exwm-cm--paint-background): Free pixmaps after creating pictures. --- exwm-cm.el | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/exwm-cm.el b/exwm-cm.el index 233d5e0773..095b59f7a1 100644 --- a/exwm-cm.el +++ b/exwm-cm.el @@ -661,14 +661,14 @@ painting a frame." (setq queue (nconc (exwm-cm--paint-tree subtree region fullscreen clip) queue)) - (when fullscreen - ;; Fullscreen X windows are always opaque thus occludes - ;; anything in this workspace. - (throw 'break 'fullscreen)) (when clip (xcb:+request exwm-cm--conn (make-instance 'xcb:xfixes:DestroyRegion - :region clip)))) + :region clip))) + (when fullscreen + ;; Fullscreen X windows are always opaque thus occludes + ;; anything in this workspace. + (throw 'break 'fullscreen))) (if (not (eq root exwm--root)) ;; Avoid painting any siblings below the workspace frame ;; container. @@ -862,6 +862,9 @@ Also update the attributes of XWIN and clip the region." xcb:renderutil:PICT_STANDARD:A_8) :value-mask xcb:render:CP:Repeat :repeat 1)) + (xcb:+request exwm-cm--conn + (make-instance 'xcb:FreePixmap + :pixmap pixmap)) (xcb:+request exwm-cm--conn (make-instance 'xcb:render:FillRectangles :op xcb:render:PictOp:Src @@ -1033,6 +1036,9 @@ the whole screen." :value-mask xcb:render:CP:Repeat :repeat 1)) (unless exist + (xcb:+request exwm-cm--conn + (make-instance 'xcb:FreePixmap + :pixmap pixmap)) (xcb:+request exwm-cm--conn (make-instance 'xcb:render:FillRectangles :op xcb:render:PictOp:Src -- cgit 1.4.1 From 71a39840b239a51eed7455877517ebe612892eff Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 19 Nov 2017 14:51:45 +0800 Subject: Fix various input focus issues * exwm-input.el (exwm-input--on-buffer-list-update): Cancel the frame redirection introduced by 421c0512f7. * exwm-input.el (exwm-input--on-buffer-list-update): Only ignore temp buffers just switched from. * exwm-input.el (exwm-input--update-focus-commit): New function for ensuring the input focus lock can always be released. (exwm-input--update-focus-defer, exwm-input--update-focus): Use it. * exwm-input.el (exwm-input--update-focus): No need to select frames; only transfer X input focus. * exwm-core.el (exwm--defer): New macro for correcting the use of `run-with-idle-timer' by taking `current-idle-time' into account. * exwm-input.el (exwm-input--update-focus-defer) (exwm-input--update-focus): * exwm-layout.el (exwm-layout--on-minibuffer-setup) (exwm-layout--on-echo-area-change): * exwm-manage.el (exwm-manage--unmanage-window) (exwm-workspace--prompt-delete): * exwm-workspace.el (exwm-workspace-switch) (exwm-workspace--add-frame-as-workspace): Use it. --- exwm-core.el | 9 +++++++++ exwm-input.el | 37 +++++++++++++++++++------------------ exwm-layout.el | 9 ++++----- exwm-manage.el | 2 +- exwm-workspace.el | 13 ++++++------- 5 files changed, 39 insertions(+), 31 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 750f134d1e..146594da0a 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -75,6 +75,15 @@ xcb:EventMask:StructureNotify)))) (xcb:flush exwm--connection)) +(defmacro exwm--defer (secs function &rest args) + "Defer the action until SECS seconds later. + +The action is to call FUNCTION with arguments ARGS." + `(run-with-idle-timer (time-add (or (current-idle-time) 0) ,secs) + nil + ,function + ,@args)) + (defconst exwm--client-event-mask (eval-when-compile (logior xcb:EventMask:StructureNotify xcb:EventMask:PropertyChange)) diff --git a/exwm-input.el b/exwm-input.el index 3a817be4cd..6a60ac35d0 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -158,7 +158,9 @@ This value should always be overwritten.") ;; The following conditions filter out events relating to temp ;; buffers. (eq (current-buffer) (window-buffer)) - (not (get-buffer " *temp*"))) + (not (string-prefix-p " *temp*" + (buffer-name (car (last (buffer-list))))))) + (redirect-frame-focus (selected-frame) nil) (setq exwm-input--update-focus-window (selected-window)) (exwm-input--update-focus-defer))) @@ -186,15 +188,14 @@ This value should always be overwritten.") (cancel-timer exwm-input--update-focus-defer-timer)) (if exwm-input--update-focus-lock (setq exwm-input--update-focus-defer-timer - (run-with-idle-timer 0 nil - #'exwm-input--update-focus-defer)) + (exwm--defer 0 #'exwm-input--update-focus-defer)) (setq exwm-input--update-focus-defer-timer nil) (when exwm-input--update-focus-timer (cancel-timer exwm-input--update-focus-timer)) (setq exwm-input--update-focus-timer - (run-with-idle-timer exwm-input--update-focus-interval nil - #'exwm-input--update-focus - exwm-input--update-focus-window)))) + (exwm--defer exwm-input--update-focus-interval + #'exwm-input--update-focus-commit + exwm-input--update-focus-window)))) (declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) (declare-function exwm-layout--set-state "exwm-layout.el" (id state)) @@ -203,17 +204,22 @@ This value should always be overwritten.") (frame-or-index &optional force)) (declare-function exwm-workspace--workspace-p "exwm-workspace.el" (workspace)) +(defun exwm-input--update-focus-commit (window) + "Commit updating input focus." + (setq exwm-input--update-focus-lock t) + (unwind-protect + (exwm-input--update-focus window) + (setq exwm-input--update-focus-lock nil))) + (defun exwm-input--update-focus (window) "Update input focus." - (setq exwm-input--update-focus-lock t) (when (window-live-p window) (with-current-buffer (window-buffer window) (if (eq major-mode 'exwm-mode) (if (not (eq exwm--frame exwm-workspace--current)) (progn (set-frame-parameter exwm--frame 'exwm-selected-window window) - (run-with-idle-timer 0 nil #'exwm-workspace-switch - exwm--frame)) + (exwm--defer 0 #'exwm-workspace-switch exwm--frame)) (exwm--log "Set focus on #x%x" exwm--id) (exwm-input--set-focus exwm--id) (when exwm--floating-frame @@ -237,21 +243,16 @@ This value should always be overwritten.") (progn (set-frame-parameter (selected-frame) 'exwm-selected-window window) - (run-with-idle-timer 0 nil #'exwm-workspace-switch - (selected-frame))) + (exwm--defer 0 #'exwm-workspace-switch (selected-frame))) ;; The focus is still on the current workspace. (if (not (and (exwm-workspace--minibuffer-own-frame-p) (minibufferp))) - (select-frame-set-input-focus (window-frame window) t) + (x-focus-frame (window-frame window)) ;; X input focus should be set on the previously selected ;; frame. - (select-frame-set-input-focus (window-frame - (minibuffer-selected-window)) - t) - (select-frame (window-frame window) t)) + (x-focus-frame (window-frame (minibuffer-selected-window)))) (exwm-input--set-active-window) - (xcb:flush exwm--connection)))))) - (setq exwm-input--update-focus-lock nil)) + (xcb:flush exwm--connection))))))) (defun exwm-input--on-minibuffer-setup () "Run in `minibuffer-setup-hook' to set input focus." diff --git a/exwm-layout.el b/exwm-layout.el index ba7f65cf3b..29273a933e 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -407,10 +407,9 @@ selected by `other-buffer'." (defun exwm-layout--on-minibuffer-setup () "Refresh layout when minibuffer grows." (unless (exwm-workspace--client-p) - (run-with-idle-timer 0.01 nil ;FIXME - (lambda () - (when (< 1 (window-height (minibuffer-window))) - (exwm-layout--refresh)))))) + (exwm--defer 0 (lambda () + (when (< 1 (window-height (minibuffer-window))) + (exwm-layout--refresh)))))) (defun exwm-layout--on-echo-area-change (&optional dirty) "Run when message arrives or in `echo-area-clear-hook' to refresh layout." @@ -421,7 +420,7 @@ selected by `other-buffer'." (frame-width exwm-workspace--current)))) (if dirty (exwm-layout--refresh) - (run-with-idle-timer 0.01 nil #'exwm-layout--refresh)))) ;FIXME + (exwm--defer 0 #'exwm-layout--refresh)))) ;;;###autoload (defun exwm-layout-enlarge-window (delta &optional horizontal) diff --git a/exwm-manage.el b/exwm-manage.el index fffc677c7f..2b96475635 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -410,7 +410,7 @@ manager is shutting down." (select-window (frame-selected-window exwm-workspace--current))) (kill-buffer buffer))))) - (run-with-idle-timer 0 nil kill-buffer-func buffer) + (exwm--defer 0 kill-buffer-func buffer) (when (active-minibuffer-window) (exit-minibuffer)))))) diff --git a/exwm-workspace.el b/exwm-workspace.el index bebd954940..d513172b66 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -145,7 +145,7 @@ Please manually run the hook `exwm-workspace-list-change-hook' afterwards.") (if (eq frame exwm-workspace--current) ;; Abort the recursive minibuffer if deleting the current workspace. (progn - (run-with-idle-timer 0 nil #'delete-frame frame) + (exwm--defer 0 #'delete-frame frame) (abort-recursive-edit)) (delete-frame frame) (exwm-workspace--update-switch-history) @@ -488,10 +488,10 @@ The optional FORCE option is for internal use only." (set-frame-parameter frame 'exwm-selected-window nil) ;; Close the (possible) active minibuffer (when (active-minibuffer-window) - (run-with-idle-timer 0 nil (lambda () - ;; Might be aborted by then. - (when (active-minibuffer-window) - (abort-recursive-edit))))) + (exwm--defer 0 (lambda () + ;; Might be aborted by then. + (when (active-minibuffer-window) + (abort-recursive-edit))))) (if (exwm-workspace--minibuffer-own-frame-p) ;; Resize the minibuffer frame. (exwm-workspace--resize-minibuffer-frame) @@ -1275,8 +1275,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (make-instance 'xcb:MapWindow :window workspace))) (xcb:flush exwm--connection) ;; Delay making the workspace fullscreen until Emacs becomes idle - (run-with-idle-timer 0 nil #'set-frame-parameter - frame 'fullscreen 'fullboth) + (exwm--defer 0 #'set-frame-parameter frame 'fullscreen 'fullboth) ;; Update EWMH properties. (exwm-workspace--update-ewmh-props) (if exwm-workspace--create-silently -- cgit 1.4.1 From 589b84040980504f0de7d80771b4ccfd63af4eef Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 24 Nov 2017 22:43:16 +0800 Subject: Fix possible wrong context when killing buffers * exwm-manage.el (exwm-manage--unmanage-window): Make sure to use the correct context to read/set buffer-local variables when killing buffers. --- exwm-manage.el | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index 2b96475635..d61a55a58c 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -405,11 +405,12 @@ manager is shutting down." (xcb:flush exwm--connection)) (let ((kill-buffer-func (lambda (buffer) - (let ((kill-buffer-query-functions nil)) - (when exwm--floating-frame - (select-window - (frame-selected-window exwm-workspace--current))) - (kill-buffer buffer))))) + (when (buffer-local-value 'exwm--floating-frame buffer) + (select-window + (frame-selected-window exwm-workspace--current))) + (with-current-buffer buffer + (let ((kill-buffer-query-functions nil)) + (kill-buffer buffer)))))) (exwm--defer 0 kill-buffer-func buffer) (when (active-minibuffer-window) (exit-minibuffer)))))) -- cgit 1.4.1 From dd6596b1f41a02021d5b60e823a3ae7e5664c92a Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 24 Nov 2017 22:47:42 +0800 Subject: Another fix for input focus issues * exwm-core.el (exwm--defer): * exwm-input.el (exwm-input--update-focus-defer): Avoid unnecessarily long delay. * exwm-input.el (exwm-input--on-FocusIn): Filter out FocusIn events generated as a result of grab/ungrab or when the keyboard is grabbed. --- exwm-core.el | 8 +++++--- exwm-input.el | 29 ++++++++++++++--------------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 146594da0a..2c810dfc64 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -76,10 +76,12 @@ (xcb:flush exwm--connection)) (defmacro exwm--defer (secs function &rest args) - "Defer the action until SECS seconds later. + "Defer the execution of FUNCTION. -The action is to call FUNCTION with arguments ARGS." - `(run-with-idle-timer (time-add (or (current-idle-time) 0) ,secs) +The action is to call FUNCTION with arguments ARGS. If Emacs is not idle, +defer the action until Emacs is idle. Otherwise, defer the action until at +least SECS seconds later." + `(run-with-idle-timer (time-add (or (current-idle-time) (- ,secs)) ,secs) nil ,function ,@args)) diff --git a/exwm-input.el b/exwm-input.el index 6a60ac35d0..7bcbc9e4dc 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -116,18 +116,16 @@ ARGS are additional arguments to CALLBACK." (cdr exwm-input--timestamp-callback)) (setq exwm-input--timestamp-callback nil))))) -(defun exwm-input--on-FocusIn (&rest _args) +(defun exwm-input--on-FocusIn (data _synthetic) "Handle FocusIn events." - ;; Not sure if this is the right thing to do but the point is the - ;; input focus should not stay at the root window or any container, - ;; or the result would be unpredictable. `x-focus-frame' would - ;; first set the input focus to the (previously) selected frame, and - ;; then `select-window' would further update the input focus if the - ;; selected window is displaying an `exwm-mode' buffer. Perhaps we - ;; should carefully filter out FocusIn events with certain 'detail' - ;; and 'mode' combinations, but this just works. - (x-focus-frame (selected-frame)) - (select-window (selected-window))) + (let ((obj (make-instance 'xcb:FocusIn))) + (xcb:unmarshal obj data) + (with-slots (mode) obj + ;; Revert input focus back to Emacs frame / X window when it's set on + ;; the root window or some workspace container. + (when (eq mode xcb:NotifyMode:Normal) + (x-focus-frame (selected-frame)) + (select-window (selected-window)))))) (defun exwm-input--on-workspace-list-change () "Run in `exwm-input--update-global-prefix-keys'." @@ -139,7 +137,6 @@ ARGS are additional arguments to CALLBACK." (make-instance 'xcb:ChangeWindowAttributes :window (frame-parameter f 'exwm-workspace) :value-mask xcb:CW:EventMask - ;; There should no other event selected there. :event-mask xcb:EventMask:FocusChange)))) (exwm-input--update-global-prefix-keys) (xcb:flush exwm--connection)) @@ -193,9 +190,11 @@ This value should always be overwritten.") (when exwm-input--update-focus-timer (cancel-timer exwm-input--update-focus-timer)) (setq exwm-input--update-focus-timer - (exwm--defer exwm-input--update-focus-interval - #'exwm-input--update-focus-commit - exwm-input--update-focus-window)))) + ;; Attempt to accumulate successive events close enough. + (run-with-timer exwm-input--update-focus-interval + nil + #'exwm-input--update-focus-commit + exwm-input--update-focus-window)))) (declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) (declare-function exwm-layout--set-state "exwm-layout.el" (id state)) -- cgit 1.4.1 From 76b567dddb6a6fdaacf56312b4b3d698cd940eaa Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 26 Nov 2017 11:18:31 +0800 Subject: Bump version to 0.16 --- exwm.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm.el b/exwm.el index b64bdeecec..c5777ec502 100644 --- a/exwm.el +++ b/exwm.el @@ -4,7 +4,7 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0.15 +;; Version: 0.16 ;; Package-Requires: ((xelb "0.12")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From 2b183f221239a24da8d65cb9043f1010aa14c640 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 26 Nov 2017 20:26:57 +0800 Subject: Re-grab global keys on keyboard update * exwm-input.el (exwm-input--on-keysyms-update): New function for re-grabbing global keys. (exwm-input--init): Register it to XELB. --- exwm-input.el | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/exwm-input.el b/exwm-input.el index 7bcbc9e4dc..097b392937 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -127,6 +127,10 @@ ARGS are additional arguments to CALLBACK." (x-focus-frame (selected-frame)) (select-window (selected-window)))))) +(defun exwm-input--on-keysyms-update () + (let ((exwm-input--global-prefix-keys nil)) + (exwm-input--update-global-prefix-keys))) + (defun exwm-input--on-workspace-list-change () "Run in `exwm-input--update-global-prefix-keys'." (dolist (f exwm-workspace--list) @@ -702,7 +706,7 @@ Its usage is the same with `exwm-input-set-simulation-keys'." (defun exwm-input--init () "Initialize the keyboard module." ;; Refresh keyboard mapping - (xcb:keysyms:init exwm--connection) + (xcb:keysyms:init exwm--connection #'exwm-input--on-keysyms-update) ;; Create the X window and intern the atom used to fetch timestamp. (setq exwm-input--timestamp-window (xcb:generate-id exwm--connection)) (xcb:+request exwm--connection -- cgit 1.4.1 From a145445da675ca36f533d742ad7ff05f7f5ec127 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 10 Dec 2017 17:58:13 +0800 Subject: ; Eliminate a compilation warning. --- exwm-input.el | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 097b392937..e34ed58734 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -53,6 +53,16 @@ (defvar exwm-workspace--minibuffer) (defvar exwm-workspace--list) +(defvar exwm-input--global-keys nil "Global key bindings.") +(defvar exwm-input--global-prefix-keys nil + "List of prefix keys of global key bindings.") +(defvar exwm-input-prefix-keys + '(?\C-c ?\C-x ?\C-u ?\C-h ?\M-x ?\M-` ?\M-& ?\M-:) + "List of prefix keys EXWM should forward to Emacs when in line-mode.") +(defvar exwm-input--simulation-keys nil "Simulation keys in line-mode.") +(defvar exwm-input--simulation-prefix-keys nil + "List of prefix keys of simulation keys in line-mode.") + (defun exwm-input--set-focus (id) "Set input focus to window ID in a proper way." (when (exwm--id->buffer id) @@ -333,16 +343,6 @@ This value should always be overwritten.") (funcall exwm--on-KeyPress obj data) (exwm-input--on-KeyPress-char-mode obj)))) -(defvar exwm-input--global-keys nil "Global key bindings.") -(defvar exwm-input--global-prefix-keys nil - "List of prefix keys of global key bindings.") -(defvar exwm-input-prefix-keys - '(?\C-c ?\C-x ?\C-u ?\C-h ?\M-x ?\M-` ?\M-& ?\M-:) - "List of prefix keys EXWM should forward to Emacs when in line-mode.") -(defvar exwm-input--simulation-keys nil "Simulation keys in line-mode.") -(defvar exwm-input--simulation-prefix-keys nil - "List of prefix keys of simulation keys in line-mode.") - (defun exwm-input--update-global-prefix-keys () "Update `exwm-input--global-prefix-keys'." (when exwm--connection -- cgit 1.4.1 From 76d6f608bc4a40ff13d102b7482c71ef152a3fa6 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 31 Dec 2017 20:49:37 +0800 Subject: Update copyright year to 2018 --- exwm-cm.el | 2 +- exwm-config.el | 2 +- exwm-core.el | 2 +- exwm-floating.el | 2 +- exwm-input.el | 2 +- exwm-layout.el | 2 +- exwm-manage.el | 2 +- exwm-randr.el | 2 +- exwm-systemtray.el | 2 +- exwm-workspace.el | 2 +- exwm.el | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/exwm-cm.el b/exwm-cm.el index 095b59f7a1..223ef5da30 100644 --- a/exwm-cm.el +++ b/exwm-cm.el @@ -1,6 +1,6 @@ ;;; exwm-cm.el --- Compositing Manager for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2016-2017 Free Software Foundation, Inc. +;; Copyright (C) 2016-2018 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-config.el b/exwm-config.el index 73004f6480..a434fe8d90 100644 --- a/exwm-config.el +++ b/exwm-config.el @@ -1,6 +1,6 @@ ;;; exwm-config.el --- Predefined configurations -*- lexical-binding: t -*- -;; Copyright (C) 2015-2017 Free Software Foundation, Inc. +;; Copyright (C) 2015-2018 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-core.el b/exwm-core.el index 2c810dfc64..b0f3ab603c 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -1,6 +1,6 @@ ;;; exwm-core.el --- Core definitions -*- lexical-binding: t -*- -;; Copyright (C) 2015-2017 Free Software Foundation, Inc. +;; Copyright (C) 2015-2018 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-floating.el b/exwm-floating.el index af11b877b9..9d4e76c6e2 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -1,6 +1,6 @@ ;;; exwm-floating.el --- Floating Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015-2017 Free Software Foundation, Inc. +;; Copyright (C) 2015-2018 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-input.el b/exwm-input.el index e34ed58734..213c9a1a10 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -1,6 +1,6 @@ ;;; exwm-input.el --- Input Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015-2017 Free Software Foundation, Inc. +;; Copyright (C) 2015-2018 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-layout.el b/exwm-layout.el index 29273a933e..b5835cbe27 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -1,6 +1,6 @@ ;;; exwm-layout.el --- Layout Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015-2017 Free Software Foundation, Inc. +;; Copyright (C) 2015-2018 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-manage.el b/exwm-manage.el index d61a55a58c..97c9d8e3e2 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -1,7 +1,7 @@ ;;; exwm-manage.el --- Window Management Module for -*- lexical-binding: t -*- ;;; EXWM -;; Copyright (C) 2015-2017 Free Software Foundation, Inc. +;; Copyright (C) 2015-2018 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-randr.el b/exwm-randr.el index d9c083c8af..07a000c3b3 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -1,6 +1,6 @@ ;;; exwm-randr.el --- RandR Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015-2017 Free Software Foundation, Inc. +;; Copyright (C) 2015-2018 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-systemtray.el b/exwm-systemtray.el index 4fc7ded331..33e97623c0 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -1,7 +1,7 @@ ;;; exwm-systemtray.el --- System Tray Module for -*- lexical-binding: t -*- ;;; EXWM -;; Copyright (C) 2016-2017 Free Software Foundation, Inc. +;; Copyright (C) 2016-2018 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-workspace.el b/exwm-workspace.el index d513172b66..0aabbefcdd 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1,6 +1,6 @@ ;;; exwm-workspace.el --- Workspace Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015-2017 Free Software Foundation, Inc. +;; Copyright (C) 2015-2018 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm.el b/exwm.el index c5777ec502..3c0124672b 100644 --- a/exwm.el +++ b/exwm.el @@ -1,6 +1,6 @@ ;;; exwm.el --- Emacs X Window Manager -*- lexical-binding: t -*- -;; Copyright (C) 2015-2017 Free Software Foundation, Inc. +;; Copyright (C) 2015-2018 Free Software Foundation, Inc. ;; Author: Chris Feng ;; Maintainer: Chris Feng -- cgit 1.4.1 From dd0a62c405b52244f42fd8a0e72282c742772ad3 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 31 Dec 2017 20:53:58 +0800 Subject: Add support for `mouse-autoselect-window' ; This feature requires both `mouse-autoselect-window` and ; `focus-follows-mouse' being set. Delaying autoselection is not ; supported yet. * exwm-core.el (exwm--client-event-mask): Select the EnterNotify event on each X window when `mouse-autoselect-window' is set. * exwm-input.el (exwm-input--on-EnterNotify): New function for making `mouse-autoselect-window' work on X windows. (exwm-input--init): Listen to EnterNotify event when `mouse-autoselect-window' is set. --- exwm-core.el | 7 ++++--- exwm-input.el | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index b0f3ab603c..ec3efc6e57 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -86,9 +86,10 @@ least SECS seconds later." ,function ,@args)) -(defconst exwm--client-event-mask - (eval-when-compile - (logior xcb:EventMask:StructureNotify xcb:EventMask:PropertyChange)) +(defconst exwm--client-event-mask (logior xcb:EventMask:StructureNotify + xcb:EventMask:PropertyChange + (if mouse-autoselect-window + xcb:EventMask:EnterWindow 0)) "Event mask set on all managed windows.") (defvar exwm-input-line-mode-passthrough) diff --git a/exwm-input.el b/exwm-input.el index 213c9a1a10..302b3d9f2a 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -137,6 +137,48 @@ ARGS are additional arguments to CALLBACK." (x-focus-frame (selected-frame)) (select-window (selected-window)))))) +(defun exwm-input--on-EnterNotify (data _synthetic) + "Handle EnterNotify events." + (let ((evt (make-instance 'xcb:EnterNotify)) + buffer window frame frame-xid edges fake-evt) + (xcb:unmarshal evt data) + (with-slots (time root event root-x root-y event-x event-y state) evt + (setq buffer (exwm--id->buffer event) + window (get-buffer-window buffer t)) + (when (and buffer window (not (eq window (selected-window)))) + (setq frame (window-frame window) + frame-xid (frame-parameter frame 'exwm-id)) + (unless (eq frame exwm-workspace--current) + (if (exwm-workspace--workspace-p frame) + ;; The X window is on another workspace. + (exwm-workspace-switch frame) + (with-current-buffer buffer + (when (and (eq major-mode 'exwm-mode) + (not (eq exwm--frame exwm-workspace--current))) + ;; The floating X window is on another workspace. + (exwm-workspace-switch exwm--frame))))) + ;; Send a fake MotionNotify event to Emacs. + (setq edges (window-inside-pixel-edges window) + fake-evt (make-instance 'xcb:MotionNotify + :detail 0 + :time time + :root root + :event frame-xid + :child xcb:Window:None + :root-x root-x + :root-y root-y + :event-x (+ event-x (elt edges 0)) + :event-y (+ event-y (elt edges 1)) + :state state + :same-screen 1)) + (xcb:+request exwm--connection + (make-instance 'xcb:SendEvent + :propagate 0 + :destination frame-xid + :event-mask xcb:EventMask:NoEvent + :event (xcb:marshal fake-evt exwm--connection))) + (xcb:flush exwm--connection))))) + (defun exwm-input--on-keysyms-update () (let ((exwm-input--global-prefix-keys nil)) (exwm-input--update-global-prefix-keys))) @@ -741,6 +783,9 @@ Its usage is the same with `exwm-input-set-simulation-keys'." (xcb:+event exwm--connection 'xcb:MotionNotify #'exwm-floating--do-moveresize) (xcb:+event exwm--connection 'xcb:FocusIn #'exwm-input--on-FocusIn) + (when mouse-autoselect-window + (xcb:+event exwm--connection 'xcb:EnterNotify + #'exwm-input--on-EnterNotify)) ;; The input focus should be set on the frame when minibuffer is active. (add-hook 'minibuffer-setup-hook #'exwm-input--on-minibuffer-setup) ;; Control `exwm-input--during-command' -- cgit 1.4.1 From 46c40363063299f354d87cde3439d108eb2010cc Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 31 Dec 2017 21:01:23 +0800 Subject: ; Add missing autoload cookies. --- exwm-cm.el | 4 ++++ exwm-input.el | 1 + 2 files changed, 5 insertions(+) diff --git a/exwm-cm.el b/exwm-cm.el index 223ef5da30..060dce780b 100644 --- a/exwm-cm.el +++ b/exwm-cm.el @@ -146,6 +146,7 @@ The value is between 0 (fully transparent) to #xFFFFFFFF (opaque)." (when reply (slot-value reply 'value)))) +;;;###autoload (defun exwm-cm-set-opacity (xwin opacity) "Set the opacity of X window XWIN to OPACITY. @@ -1759,17 +1760,20 @@ Create implicit workspace frame container"))) (add-hook 'exwm-init-hook #'exwm-cm--init t) (add-hook 'exwm-exit-hook #'exwm-cm--exit t)) +;;;###autoload (defun exwm-cm-start () "Start EXWM compositing manager." (interactive) (unless exwm-cm--conn (exwm-cm--init))) +;;;###autoload (defun exwm-cm-stop () "Stop EXWM compositing manager." (interactive) (exwm-cm--exit)) +;;;###autoload (defun exwm-cm-toggle () "Toggle the running state of EXWM compositing manager." (interactive) diff --git a/exwm-input.el b/exwm-input.el index 302b3d9f2a..38fd751aff 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -435,6 +435,7 @@ This value should always be overwritten.") (user-error "[EXWM] Failed to grab key: %s" (single-key-description k)))))))))) +;;;###autoload (defun exwm-input-set-key (key command) "Set a global key binding." (interactive "KSet key globally: \nCSet key %s to command: ") -- cgit 1.4.1 From 895633fe61b4934f1328fa3e314a523023d3d1e4 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 14 Jan 2018 23:46:04 +0800 Subject: Raise docks after quitting full screen mode * exwm-layout.el (exwm-layout-unset-fullscreen): Raise docks lowered when entering full screen mode. --- exwm-layout.el | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/exwm-layout.el b/exwm-layout.el index b5835cbe27..5177ef5bcd 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -28,6 +28,7 @@ (require 'exwm-core) (defvar exwm-floating-border-width) +(defvar exwm-workspace--id-struts-alist) (defun exwm-layout--resize-container (id container x y width height &optional container-only) @@ -259,6 +260,13 @@ (exwm-layout--show exwm--id) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_STATE :window exwm--id :data [])) + ;; Raise X windows with struts set again. + (dolist (pair exwm-workspace--id-struts-alist) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (car pair) + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Above))) (xcb:flush exwm--connection) (setq exwm--ewmh-state (delq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) -- cgit 1.4.1 From 6b4bfad87bdf8b4cf13cdfb211ae6c8c24c415ff Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 14 Jan 2018 23:48:29 +0800 Subject: Support key translation in line-mode * exwm-input.el (exwm-input--translate): New function for translating keys according to `input-decode-map', `local-function-key-map' and `key-translation-map'. (exwm-input--cache-event): Use it. --- exwm-input.el | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/exwm-input.el b/exwm-input.el index 38fd751aff..54d0540e46 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -471,10 +471,25 @@ This value should always be overwritten.") (defvar exwm-input--temp-line-mode nil "Non-nil indicates it's in temporary line-mode for char-mode.") +(cl-defun exwm-input--translate (key) + (let (translation) + (dolist (map (list input-decode-map + local-function-key-map + key-translation-map)) + (setq translation (lookup-key map key)) + (if (functionp translation) + (cl-return-from exwm-input--translate (funcall translation nil)) + (when (vectorp translation) + (cl-return-from exwm-input--translate translation))))) + key) + (defun exwm-input--cache-event (event) "Cache EVENT." (setq exwm-input--line-mode-cache (vconcat exwm-input--line-mode-cache (vector event))) + ;; Attempt to translate this key sequence. + (setq exwm-input--line-mode-cache + (exwm-input--translate exwm-input--line-mode-cache)) ;; When the key sequence is complete. (unless (keymapp (key-binding exwm-input--line-mode-cache)) (setq exwm-input--line-mode-cache nil) -- cgit 1.4.1 From 83c0a2db3448091e44cded075104a0b7df636431 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 4 Feb 2018 22:38:02 +0800 Subject: Avoid crashing Emacs by resizing its frame into 0x0 * exwm-floating.el (exwm-floating--do-moveresize): * exwm-layout.el (exwm-layout-enlarge-window): Resizing a frame into 0x0 crashes Emacs so additional checks are required. --- exwm-floating.el | 4 ++-- exwm-layout.el | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 9d4e76c6e2..a695346cb2 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -601,8 +601,8 @@ context of the corresponding buffer.") (eval-when-compile (logior xcb:ConfigWindow:Width xcb:ConfigWindow:Height))) - width (aref result 4) - height (aref result 5)) + width (max 1 (aref result 4)) + height (max 1 (aref result 5))) (setq buffer-or-id (aref result 0)) (setq container-or-id (if (bufferp buffer-or-id) diff --git a/exwm-layout.el b/exwm-layout.el index 5177ef5bcd..bcf9c3a67f 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -470,7 +470,7 @@ windows." (setq width nil) (setq width (max (+ exwm--normal-hints-min-width margin) (+ width delta)))))) - (when width + (when (and width (> width 0)) (setf (slot-value exwm--geometry 'width) width) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow @@ -503,7 +503,7 @@ windows." (setq height nil) (setq height (max (+ exwm--normal-hints-min-height margin) (+ height delta)))))) - (when height + (when (and height (> height 0)) (setf (slot-value exwm--geometry 'height) height) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow -- cgit 1.4.1 From 7823eb988c22f5dc804ef862d91a0fcf474ca718 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 18 Feb 2018 01:04:04 +0800 Subject: Make X windows container-less ; This is an attempt to make (managed) X windows container-less, i.e. direct children of the root window. This is mainly to make EXWM compatible with third-party compositors. Other issues like wrong absolute position should also get resolved by the way. The workspace containers ("virtual roots") are also removed. However Emacs frames are still wrapped in containers to avoid unexpected stack reordering. * exwm-cm.el: Make this module obsolete as EXWM supports third-party compositors now. * exwm-core.el (exwm--container): * exwm-floating.el (exwm-floating--set-floating) (exwm-floating--unset-floating, exwm-floating-hide) (exwm-floating--start-moveresize, exwm-floating--stop-moveresize) (exwm-floating--do-moveresize, exwm-floating-move): * exwm-input.el (exwm-input--update-focus): * exwm-layout.el (exwm-layout--show, exwm-layout--hide) (exwm-layout-set-fullscreen, exwm-layout-unset-fullscreen): * exwm-manage.el (exwm-manage--manage-window, exwm-manage--unmanage-window) (exwm-manage--kill-buffer-query-function, exwm-manage--kill-client): * exwm-workspace.el (exwm-workspace--set-fullscreen, exwm-workspace-switch) (exwm-workspace-move-window, exwm-workspace--add-frame-as-workspace) (exwm-workspace--remove-frame-as-workspace): Make adaptions for container-less X windows. * exwm-workspace.el (exwm-workspace--update-ewmh-props): * exwm.el (exwm--init-icccm-ewmh, exwm--exit-icccm-ewmh): No longer use virtual roots. * exwm-input.el (exwm-input--on-workspace-list-change) (exwm-input--update-global-prefix-keys, exwm-input--init, exwm-input--exit): From now on global key bindings are grabbed on the root window so it's no long required to re-grab them each time the workspace list changes. As a result `exwm-input--on-workspace-list-change' and its corresponding references are discarded. It remains to be seen if this change will raise input focus issues. * exwm-manage.el (exwm-manage--manage-window): Explicitly set the workspace for newly managed X windows. * exwm-floating.el (exwm-floating--set-floating): Avoid implicit reference to the current workspace. * exwm-core.el (exwm--set-geometry): New function for setting the geometry of an X window. * exwm-layout.el (exwm-layout--resize-container): Replaced by `exwm-layout--resize-container'. * exwm-core.el (exwm--guide-window): New global variable recording the guide X window. * exwm.el (exwm--init-icccm-ewmh): Set it. * exwm-input.el (exwm-input--post-init): New function containing staffs for initialization but should better get called after the event loop starts. * exwm.el (exwm-init): Use it. --- README.md | 1 - exwm-cm.el | 1756 +---------------------------------------------------- exwm-core.el | 20 +- exwm-floating.el | 258 +++----- exwm-input.el | 112 ++-- exwm-layout.el | 178 ++---- exwm-manage.el | 145 +---- exwm-randr.el | 2 +- exwm-workspace.el | 320 +++++----- exwm.el | 10 +- 10 files changed, 360 insertions(+), 2442 deletions(-) diff --git a/README.md b/README.md index 1b65486c69..103948c633 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ It features: + Dynamic workspace support + ICCCM/EWMH compliance + (Optional) RandR (multi-monitor) support -+ (Optional) Built-in compositing manager + (Optional) Built-in system tray Please check out the diff --git a/exwm-cm.el b/exwm-cm.el index 060dce780b..77dd2774a1 100644 --- a/exwm-cm.el +++ b/exwm-cm.el @@ -21,1765 +21,27 @@ ;;; Commentary: -;; This module provides a compositing manager (CM) for EXWM, mainly to -;; enable transparency support. - -;; Usage: -;; Add following lines to .emacs and modify accordingly: -;; -;; (require 'exwm-cm) -;; ;; Make all Emacs frames opaque. -;; (setq window-system-default-frame-alist '((x . ((alpha . 100))))) -;; ;; Assign everything else a 80% opacity. -;; (setq exwm-cm-opacity 80) -;; (exwm-cm-enable) -;; -;; With the last line this CM would be started with EXWM. You can also -;; start and stop this CM with `exwm-cm-start' and `exwm-cm-stop' at any -;; time. - -;; Theory: -;; Due to its unique way of managing X windows, EXWM can not work with -;; any existing CMs. And this CM, designed specifically for EXWM, -;; probably won't work well with other WMs, too. The theories behind -;; all CMs are basically the same, some peculiarities of this CM are -;; summarized as the following sections. - -;; + Data structures: -;; This CM organizes all X windows concerned with compositing in a -;; tree hierarchy. Below is a stripped-down version of such tree with -;; each node representing an X window (except the root placeholder), -;; -;; (nil -;; (root-xwin -;; (unmanaged-xwin) -;; (workspace-container -;; (unmanaged-xwin) -;; (xwin-container -;; (xwin) -;; (floating-frame-container -;; (floating-frame))) -;; (xwin-container -;; (xwin)) -;; (workspace-frame-container -;; (workspace-frame))) -;; (minibuffer-frame-container -;; (minibuffer-frame)))) -;; -;; where -;; - nodes with non-nil CDRs are containers, -;; - siblings are arranged in stacking order (top to bottom), -;; - and "managed" and "unmanaged" are in WM's sense. -;; -;; During a painting process, the tree is traversed starting from the -;; root node, with each leaf visited and painted. The attributes of -;; each X window (position, size, etc) are recorded as an instance of -;; class `exwm-cm--attr'. Such instance is associated with the -;; corresponding X window ID through a hash table. The instance also -;; contains a slot pointing to a subtree of the aforementioned tree, -;; with the root node being the parent of the X window. This makes it -;; convenient to carry out operations such as insertion, deletion, -;; restacking and reparenting. - -;; + Compositing strategies: -;; - Only leaves are painted, since branches (containers) are always -;; invisible. -;; - The root X window is painted separately. -;; - Siblings below a workspace frame container are not painted; they -;; are considered hidden. -;; - Only the top workspace in one (RandR) output is painted. -;; - Workspace frames and floating frames are always clipped by its -;; Emacs windows displaying `exwm-mode' buffers, therefore they -;; don't block X windows. - -;; Reference: -;; + xcompmgr (http://cgit.freedesktop.org/xorg/app/xcompmgr/) +;; This module is obsolete since EXWM now supports third-porty compositors. ;;; Code: -(require 'xcb-composite) -(require 'xcb-damage) -(require 'xcb-ewmh) -(require 'xcb-icccm) -(require 'xcb-renderutil) -(require 'xcb-shape) - -(require 'exwm-core) -(require 'exwm-workspace) -(require 'exwm-manage) - -(defconst exwm-cm--OPAQUE (float #xFFFFFFFF) - "The opacity value of the _NET_WM_WINDOW_OPACITY property.") -(defvar exwm-cm--_NET_WM_WINDOW_OPACITY nil "The _NET_WM_WINDOW_OPACITY atom.") -(defvar exwm-cm-opacity nil - "The default value of opacity when it's not explicitly specified. - -The value should be a floating number between 0 (transparent) and 100 -\(opaque). A value of nil also means opaque.") - -(defvar exwm-cm--hash nil - "The hash table associating X window IDs to their attributes.") - -(defvar exwm-cm--conn nil "The X connection used by the CM.") -(defvar exwm-cm--buffer nil "The rendering buffer.") -(defvar exwm-cm--depth nil "Default depth.") -(defvar exwm-cm--clip-changed t "Whether clip has changed.") -(defvar exwm-cm--damages nil "All damaged regions.") -(defvar exwm-cm--expose-rectangles nil - "Used by Expose event handler to collect exposed regions.") - -(defvar exwm-cm--background nil "The background (render) picture.") -(defvar exwm-cm--background-atom-names '("_XROOTPMAP_ID" "_XSETROOT_ID") - "Property names for background pixmap.") -(defvar exwm-cm--background-atoms nil "Interned atoms of the property names.") - -(defun exwm-cm--get-opacity (xwin) - "Get the opacity of X window XWIN. - -The value is between 0 (fully transparent) to #xFFFFFFFF (opaque)." - (let ((reply (xcb:+request-unchecked+reply exwm-cm--conn - (make-instance 'xcb:icccm:-GetProperty-single - :window xwin - :property exwm-cm--_NET_WM_WINDOW_OPACITY - :type xcb:Atom:CARDINAL)))) - ;; The X window might have already been destroyed. - (when reply - (slot-value reply 'value)))) - -;;;###autoload -(defun exwm-cm-set-opacity (xwin opacity) - "Set the opacity of X window XWIN to OPACITY. - -The value is between 0 (fully transparent) to 100 (opaque). - -If called interactively, XWIN would be the selected X window." - (interactive - (list (exwm--buffer->id (window-buffer)) - (read-number "Opacity (0 ~ 100): " 100))) - (when (and xwin - (<= 0 opacity 100)) - (setq opacity (round (* exwm-cm--OPAQUE (/ opacity 100.0)))) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:icccm:-ChangeProperty-single - :window xwin - :property exwm-cm--_NET_WM_WINDOW_OPACITY - :type xcb:Atom:CARDINAL - :data opacity)) - (xcb:flush exwm-cm--conn))) - -(defclass exwm-cm--attr () - ( - ;; The entity associated with this X window; can be a frame, a buffer - ;; or nil. - (entity :initform nil) - ;; The subtree of which the root node is the parent of this X window. - (tree :initarg :tree) - ;; Geometry. - (x :initarg :x) - (y :initarg :y) - (width :initarg :width) - (height :initarg :height) - ;; X window attributes. - (visual :initarg :visual) - (class :initarg :class) - ;; The opacity of this X window; can be 0 ~ #xFFFE or nil. - (opacity :initform nil) - ;; Determine whether this X window should be treated as opaque or - ;; transparent; can be nil (opaque), 'argb or 'transparent (both - ;; should be treated as transparent). - (mode :initform nil) - ;; The (render) picture of this X window. - (picture :initform nil) - ;; The 1x1 (render) picture with only alpha channel. - (alpha-picture :initform nil) - ;; Whether this X window is ever damaged. - (damaged :initform nil) - ;; The damage object monitoring this X window. - (damage :initarg :damage) - ;; The bounding region of this X window (can be irregular). - (border-size :initform nil) - ;; The rectangular bounding region of this X window. - (extents :initform nil) - ;; The region require repainting (used for transparent X windows). - (border-clip :initform nil) - ;; Shape-related parameters. - (shaped :initform nil) - (shape-x :initarg :shape-x) - (shape-y :initarg :shape-y) - (shape-width :initarg :shape-width) - (shape-height :initarg :shape-height)) - :documentation "Attributes of an X window.") - -(defsubst exwm-cm--xwin->attr (xwin) - "Get the attributes of X window XWIN." - (gethash xwin exwm-cm--hash)) - -(defsubst exwm-cm--get-tree (xwin) - "Get the subtree of the parent of X window XWIN." - (slot-value (exwm-cm--xwin->attr xwin) 'tree)) - -(defsubst exwm-cm--set-tree (xwin tree) - "Reparent X window XWIN to another tree TREE." - (setf (slot-value (exwm-cm--xwin->attr xwin) 'tree) tree)) - -(defsubst exwm-cm--get-parent (xwin) - "Get the parent of X window XWIN." - (car (exwm-cm--get-tree xwin))) - -(defsubst exwm-cm--get-siblings (xwin) - "Get a list of subtrees of the siblings of X window XWIN." - (cdr (exwm-cm--get-tree xwin))) - -(defsubst exwm-cm--get-subtree (xwin) - "Get the subtree of which the X window XWIN is the root node." - (assq xwin (exwm-cm--get-siblings xwin))) - -(defun exwm-cm--create-attr (xwin tree x y width height) - "Create attributes for X window XWIN. - -TREE is the subtree and the parent of this X window is the tree's root. -X and Y specify the position with regard to the root X window. WIDTH -and HEIGHT specify the size of the X window." - (let (visual class map-state damage attr) - (cond - ((= xwin exwm--root) - ;; Redirect all subwindows to off-screen storage. - (xcb:+request exwm-cm--conn - (make-instance 'xcb:composite:RedirectSubwindows - :window exwm--root - :update xcb:composite:Redirect:Manual)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:ChangeWindowAttributes - :window xwin - :value-mask xcb:CW:EventMask - :event-mask (logior xcb:EventMask:StructureNotify - xcb:EventMask:PropertyChange - xcb:EventMask:SubstructureNotify - xcb:EventMask:Exposure))) - (setq visual (slot-value (car (slot-value (xcb:get-setup exwm-cm--conn) - 'roots)) - 'root-visual) - class xcb:WindowClass:InputOutput)) - ((eq xwin exwm-manage--desktop) - ;; Ignore any desktop; paint the background ourselves. - (setq visual 0 - class xcb:WindowClass:InputOnly - map-state xcb:MapState:Unmapped)) - (t - ;; Redirect this window to off-screen storage, or the content - ;; would be mirrored to its parent. - (xcb:+request exwm-cm--conn - (make-instance 'xcb:composite:RedirectWindow - :window xwin - :update xcb:composite:Redirect:Manual)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:ChangeWindowAttributes - :window xwin - :value-mask xcb:CW:EventMask - :event-mask (logior xcb:EventMask:StructureNotify - xcb:EventMask:PropertyChange))) - (let ((reply (xcb:+request-unchecked+reply exwm-cm--conn - (make-instance 'xcb:GetWindowAttributes - :window xwin)))) - (if reply - (with-slots ((visual* visual) - (class* class) - (map-state* map-state)) - reply - (setq visual visual* - class class* - map-state map-state*)) - ;; The X window has been destroyed actually. It'll get - ;; removed by a DestroyNotify event. - (setq visual 0 - class xcb:WindowClass:InputOnly - map-state xcb:MapState:Unmapped))) - (when (/= class xcb:WindowClass:InputOnly) - (setq damage (xcb:generate-id exwm-cm--conn)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:damage:Create - :damage damage - :drawable xwin - :level xcb:damage:ReportLevel:NonEmpty)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:shape:SelectInput - :destination-window xwin - :enable 1))))) - (setq attr (make-instance 'exwm-cm--attr - :tree tree - :x x - :y y - :width width - :height height - :visual visual - :class class - :damage damage - :shape-x x - :shape-y y - :shape-width width - :shape-height height)) - (puthash xwin attr exwm-cm--hash) - (unless (or (= xwin exwm--root) - (= class xcb:WindowClass:InputOnly)) - (exwm-cm--update-opacity xwin) - (when (= map-state xcb:MapState:Viewable) - (exwm-cm--map-xwin xwin t))))) - -(defun exwm-cm--update-geometry (xwin x y width height &optional above-sibling) - "Update the geometry of X window XWIN. - -X, Y, WIDTH and HEIGHT have the same meaning with the arguments used in -`exwm-cm--create-attr'. If ABOVE-SIBLING is non-nil, restack XWIN with -`exwm-cm--restack.'" - (with-slots ((x* x) - (y* y) - (width* width) - (height* height) - extents shaped shape-x shape-y shape-width shape-height) - (exwm-cm--xwin->attr xwin) - (let ((stack-changed (and above-sibling - (exwm-cm--restack xwin above-sibling))) - (position-changed (or (and x (/= x x*)) - (and y (/= y y*)))) - (size-changed (or (and width (/= width width*)) - (and height (/= height height*)))) - subtree dx dy damage new-extents) - (when position-changed - (setq subtree (exwm-cm--get-subtree xwin) - dx (- x x*) - dy (- y y*)) - (dolist (node (cdr subtree)) - (with-slots (x y) (exwm-cm--xwin->attr (car node)) - (exwm--log "(CM) #x%X(*): @%+d%+d => @%+d%+d" - (car node) x y (+ x dx) (+ y dy)) - (exwm-cm--update-geometry (car node) (+ x dx) (+ y dy) nil nil))) - (exwm--log "(CM) #x%X: @%+d%+d => @%+d%+d" xwin x* y* x y) - (setf x* x - y* y) - (cl-incf shape-x dx) - (cl-incf shape-y dy)) - (when size-changed - (setf width* width - height* height) - (unless shaped - (setf shape-width width - shape-height height))) - (when (or stack-changed position-changed size-changed) - (setq damage (xcb:generate-id exwm-cm--conn) - new-extents (xcb:generate-id exwm-cm--conn)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:CreateRegion - :region damage - :rectangles nil)) - (when extents - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:CopyRegion - :source extents - :destination damage))) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:CreateRegion - :region new-extents - :rectangles (list (make-instance 'xcb:RECTANGLE - :x x* - :y y* - :width width* - :height height*)))) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:UnionRegion - :source1 damage - :source2 new-extents - :destination damage)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:DestroyRegion - :region new-extents)) - (exwm-cm--add-damage damage))))) - -(defun exwm-cm--update-opacity (xwin) - "Update the opacity of X window XWIN." - (with-slots (visual opacity mode alpha-picture extents) - (exwm-cm--xwin->attr xwin) - (let (format forminfo) - ;; Get the opacity. - (setf opacity (exwm-cm--get-opacity xwin)) - (if opacity - (setf opacity (round (* #xFFFF (/ opacity exwm-cm--OPAQUE)))) - (when (numberp exwm-cm-opacity) - (setf opacity (round (* #xFFFF (/ exwm-cm-opacity 100.0)))))) - (when (and opacity - (>= opacity #xFFFF)) - (setf opacity nil)) - ;; Determine the mode of the X window. - (setq format (xcb:renderutil:find-visual-format - (xcb:renderutil:query-formats exwm-cm--conn) visual)) - (when format - (catch 'break - (dolist (f (slot-value (xcb:renderutil:query-formats exwm-cm--conn) - 'formats)) - (when (eq format (slot-value f 'id)) - (setq forminfo f) - (throw 'break nil))))) - (if (and forminfo - (eq xcb:render:PictType:Direct (slot-value forminfo 'type)) - (/= 0 (slot-value (slot-value forminfo 'direct) 'alpha-mask))) - (setf mode 'argb) - (if opacity - (setf mode 'transparent) - (setf mode nil))) - ;; Clear resources. - (when alpha-picture - (xcb:+request exwm-cm--conn - (make-instance 'xcb:render:FreePicture - :picture alpha-picture)) - (setf alpha-picture nil)) - (when extents - (let ((damage (xcb:generate-id exwm-cm--conn))) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:CreateRegion - :region damage - :rectangles nil)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:CopyRegion - :source extents - :destination damage)) - (exwm-cm--add-damage damage)))))) - -(defsubst exwm-cm--push (newelt place) - "Similar to `push' but preserve the reference." - (let ((oldelt (car place))) - (setf (car place) newelt - (cdr place) (cons oldelt (cdr place))))) - -(defsubst exwm-cm--delq (elt list) - "Similar to `delq' but preserve the reference." - (if (eq elt (car list)) - (setf (car list) (cadr list) - (cdr list) (cddr list)) - (delq elt list))) - -(defsubst exwm-cm--assq-delete-all (key alist) - "Similar to `assq-delete-all' but preserve the reference." - (when (eq key (caar alist)) - (setf (car alist) (cadr alist) - (cdr alist) (cddr alist))) - (assq-delete-all key alist)) - -(defun exwm-cm--create-tree (&optional xwin) - "Create a tree with XWIN being the root node." - (let (tree0 x0 y0 children containers) - ;; Get the position of this branch. - (if xwin - (with-slots (tree x y) (exwm-cm--xwin->attr xwin) - (setq tree0 (assq xwin (cdr tree)) - x0 x - y0 y)) - (setq tree0 (list nil) - x0 0 - y0 0)) - ;; Get children nodes. - (if (null xwin) - (setq children (list exwm--root)) - (setq children - (reverse (slot-value (xcb:+request-unchecked+reply exwm-cm--conn - (make-instance 'xcb:QueryTree - :window xwin)) - 'children)))) - ;; Get container nodes. - ;; Floating frame containers are determined dynamically. - (cond - ((null xwin) - (setq containers `((,exwm--root)))) - ((= xwin exwm--root) - ;; Workspace containers and the minibuffer frame container. - (setq containers (mapcar (lambda (f) - (cons (frame-parameter f 'exwm-workspace) f)) - exwm-workspace--list)) - (when (exwm-workspace--minibuffer-own-frame-p) - (push (cons - (frame-parameter exwm-workspace--minibuffer 'exwm-container) - exwm-workspace--minibuffer) - containers))) - ;; No containers in the minibuffer container. - ((and (exwm-workspace--minibuffer-own-frame-p) - (= xwin - (frame-parameter exwm-workspace--minibuffer 'exwm-container)))) - ((= exwm--root - (slot-value (xcb:+request-unchecked+reply exwm-cm--conn - (make-instance 'xcb:QueryTree - :window xwin)) - 'parent)) - ;; Managed X window containers and the workspace frame container. - (let (frame) - (catch 'break - (dolist (f exwm-workspace--list) - (when (= xwin (frame-parameter f 'exwm-workspace)) - (setq frame f) - (throw 'break nil)))) - (cl-assert frame) - (dolist (pair exwm--id-buffer-alist) - (with-current-buffer (cdr pair) - (when (eq frame exwm--frame) - (push (cons exwm--container (cdr pair)) containers)))) - (push (cons (frame-parameter frame 'exwm-container) frame) - containers)))) - ;; Create subnodes. - (dolist (xwin children) - ;; Create attributes. - (let ((reply (xcb:+request-unchecked+reply exwm-cm--conn - (make-instance 'xcb:GetGeometry - :drawable xwin)))) - ;; It's possible the X window has been destroyed. - (if (null reply) - (setq xwin nil) - (when reply - (with-slots (x y width height) reply - (exwm-cm--create-attr xwin tree0 - (+ x x0) (+ y y0) width height)) - ;; Insert the node. - (setcdr (or (last (cdr tree0)) tree0) `((,xwin)))))) - (cond - ((null xwin)) - ((assq xwin containers) - ;; A branch. Repeat the process. - (exwm-cm--create-tree xwin) - (let ((entity (cdr (assq xwin containers))) - entity-xwin) - (when entity - (setq entity-xwin (if (framep entity) - (frame-parameter entity 'exwm-outer-id) - (buffer-local-value 'exwm--id entity))) - (setf (slot-value (exwm-cm--xwin->attr entity-xwin) 'entity) entity - (slot-value (exwm-cm--xwin->attr xwin) 'entity) entity) - (let ((tmp (exwm-cm--get-parent entity-xwin))) - (when (/= xwin tmp) - ;; Workspace frame container. - (setf (slot-value (exwm-cm--xwin->attr tmp) 'entity) - entity)))))) - ((and (null containers) - (exwm--id->buffer xwin)) - ;; A leaf but a floating frame container might follow. - (with-current-buffer (exwm--id->buffer xwin) - (when exwm--floating-frame - (push (cons (frame-parameter exwm--floating-frame 'exwm-container) - exwm--floating-frame) - containers)))))))) - -(defun exwm-cm--restack (xwin above-sibling) - "Restack X window XWIN so as to it's exactly on top of ABOVE-SIBLING." - (let ((siblings (exwm-cm--get-siblings xwin)) - node tmp) - (unless (= 1 (length siblings)) - (setq node (assq xwin siblings)) - (if (= above-sibling xcb:Window:None) - ;; Put at bottom. - (unless (eq node (cdr (last siblings))) - (exwm-cm--delq node siblings) - (setcdr (last siblings) (list node)) - ;; Set the return value. - t) - ;; Insert before the sibling. - (setq tmp siblings) - (while (and tmp - (/= above-sibling (caar tmp))) - (setq tmp (cdr tmp))) - (cl-assert tmp) - ;; Check if it's already at the requested position. - (unless (eq tmp (cdr siblings)) - (exwm-cm--delq node siblings) - (exwm-cm--push node tmp) - ;; Set the return value. - t))))) - -(declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) - -(defun exwm-cm--paint-tree (tree region &optional force-opaque frame-clip) - "Paint the tree TREE, with REGION specifying the clipping region. - -If FORCE-OPAQUE is non-nil, all X windows painted in this tree is -assumed opaque. FRAME-CLIP specifies the region should be clipped when -painting a frame." - (unless tree - (setq tree (exwm-cm--get-tree exwm--root))) - (let ((root (car tree)) - xwin attr entity current output outputs queue rectangles) - ;; Paint subtrees. - (catch 'break - (dolist (subtree (cdr tree)) - (setq xwin (car subtree) - attr (exwm-cm--xwin->attr xwin)) - (cond - ;; Skip destroyed X windows. - ((null attr)) - ;; Skip InputOnly X windows. - ((= xcb:WindowClass:InputOnly - (slot-value attr 'class))) - ((and (eq root exwm--root) - (frame-live-p (setq entity (slot-value attr 'entity))) - (if (eq entity exwm-workspace--minibuffer) - ;; Skip the minibuffer if the current workspace is - ;; already painted. - (unless (exwm-workspace--minibuffer-attached-p) - current) - ;; Skip lower workspaces on visited RandR output. - ;; If RandR is not enabled, it'll just paint the first. - (memq (setq output (frame-parameter entity - 'exwm-randr-output)) - outputs)))) - ((cdr subtree) - ;; Paint the subtree. - (setq entity (slot-value attr 'entity)) - (let (fullscreen clip) - (cond - ((buffer-live-p entity) - (with-current-buffer entity - ;; Collect frame clip but exclude fullscreen and - ;; floating X windows. - (setq fullscreen (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN - exwm--ewmh-state)) - (when (and (null fullscreen) - ;; In case it's hidden. - (null (exwm-layout--iconic-state-p)) - ;; The buffer of a floating X windows is not - ;; displayed on a workspace frame. - (null exwm--floating-frame) - ;; Opaque regions are always clipped. - (slot-value (exwm-cm--xwin->attr xwin) 'mode)) - ;; Prepare rectangles to clip the workspace frame. - (with-slots (x y width height) (exwm-cm--xwin->attr xwin) - (push (make-instance 'xcb:RECTANGLE - :x x - :y y - :width width - :height height) - rectangles))))) - ((and rectangles - (frame-live-p entity)) - ;; Prepare region to clip the frame. - (setq clip (xcb:generate-id exwm-cm--conn)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:CreateRegion - :region clip - :rectangles rectangles)))) - (setq queue - (nconc (exwm-cm--paint-tree subtree region fullscreen clip) - queue)) - (when clip - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:DestroyRegion - :region clip))) - (when fullscreen - ;; Fullscreen X windows are always opaque thus occludes - ;; anything in this workspace. - (throw 'break 'fullscreen))) - (if (not (eq root exwm--root)) - ;; Avoid painting any siblings below the workspace frame - ;; container. - (when (exwm-workspace--workspace-p (slot-value attr 'entity)) - (throw 'break nil)) - ;; Save some status. - (when (and (frame-live-p entity) - (not (eq entity exwm-workspace--minibuffer))) - (push output outputs) - (when (eq entity exwm-workspace--current) - (setq current t))))) - ((and force-opaque - (slot-value attr 'damaged)) - (exwm-cm--paint-opaque xwin region t)) - ((slot-value attr 'damaged) - ;; Paint damaged leaf. - (setq entity (slot-value attr 'entity)) - (when (slot-value attr 'mode) - (push xwin queue)) - (cond - ((buffer-live-p entity) - (with-current-buffer entity - (cl-assert (= xwin exwm--id)) - (when (and exwm--floating-frame - ;; Opaque regions are always clipped. - (slot-value (exwm-cm--xwin->attr xwin) 'mode)) - ;; Prepare rectangles to clip the floating frame. - (with-slots (x y width height) (exwm-cm--xwin->attr xwin) - (push (make-instance 'xcb:RECTANGLE - :x x - :y y - :width width - :height height) - rectangles))))) - ((and frame-clip - (frame-live-p entity)) - ;; Apply frame clip. - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:IntersectRegion - :source1 region - :source2 frame-clip - :destination frame-clip)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:SubtractRegion - :source1 region - :source2 frame-clip - :destination region)))) - (exwm-cm--paint-opaque xwin region) - (when (and frame-clip - (frame-live-p entity)) - ;; Restore frame clip. - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:UnionRegion - :source1 region - :source2 frame-clip - :destination region))))))) - ;; Return the queue. - queue)) - -(defun exwm-cm--paint-opaque (xwin region &optional force-opaque) - "Paint an X window XWIN clipped by region REGION if XWIN is opaque. - -Also update the attributes of XWIN and clip the region." - (with-slots (x y width height visual mode picture - border-size extents border-clip) - (exwm-cm--xwin->attr xwin) - ;; Prepare the X window picture. - (unless picture - (setf picture (xcb:generate-id exwm-cm--conn)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:render:CreatePicture - :pid picture - :drawable xwin - :format (xcb:renderutil:find-visual-format - (xcb:renderutil:query-formats exwm-cm--conn) - visual) - :value-mask 0))) - ;; Clear cached resources if clip changed. - (when exwm-cm--clip-changed - (when border-size - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:DestroyRegion - :region border-size)) - (setf border-size nil)) - (when extents - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:DestroyRegion - :region extents)) - (setf extents nil)) - (when border-clip - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:DestroyRegion - :region border-clip)) - (setf border-clip nil))) - ;; Retrieve the border. - (unless border-size - (setf border-size (xcb:generate-id exwm-cm--conn)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:CreateRegionFromWindow - :region border-size - :window xwin - :kind xcb:shape:SK:Bounding)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:TranslateRegion - :region border-size - :dx x - :dy y))) - ;; Retrieve the extents. - (unless extents - (setf extents (xcb:generate-id exwm-cm--conn)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:CreateRegion - :region extents - :rectangles (list (make-instance 'xcb:RECTANGLE - :x x - :y y - :width width - :height height))))) - (cond - ((and mode - (null force-opaque)) - ;; Calculate clipped border for the transparent X window. - (unless border-clip - (setf border-clip (xcb:generate-id exwm-cm--conn)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:CreateRegion - :region border-clip - :rectangles nil)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:CopyRegion - :source region - :destination border-clip)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:IntersectRegion - :source1 border-clip - :source2 border-size - :destination border-clip)))) - (t - ;; Clip & render for the opaque X window. - ;; Set the clip region for the rendering buffer. - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:SetPictureClipRegion - :picture exwm-cm--buffer - :region region - :x-origin 0 - :y-origin 0)) - ;; Clip the region with border. - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:SubtractRegion - :source1 region - :source2 border-size - :destination region)) - ;; Render the picture to the buffer. - (xcb:+request exwm-cm--conn - (make-instance 'xcb:render:Composite - :op xcb:render:PictOp:Src - :src picture - :mask xcb:render:Picture:None - :dst exwm-cm--buffer - :src-x 0 - :src-y 0 - :mask-x 0 - :mask-y 0 - :dst-x x - :dst-y y - :width width - :height height)))))) - -(defun exwm-cm--paint-transparent (xwin) - "Paint a transparent X window XWIN." - (with-slots (x y width height opacity picture alpha-picture border-clip) - (exwm-cm--xwin->attr xwin) - ;; Prepare the alpha picture for transparent X windows. - (when (and opacity (null alpha-picture)) - (setf alpha-picture (xcb:generate-id exwm-cm--conn)) - (let ((pixmap (xcb:generate-id exwm-cm--conn))) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:CreatePixmap - :depth 8 - :pid pixmap - :drawable exwm--root - :width 1 - :height 1)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:render:CreatePicture - :pid alpha-picture - :drawable pixmap - :format (xcb:renderutil:find-standard - (xcb:renderutil:query-formats - exwm-cm--conn) - xcb:renderutil:PICT_STANDARD:A_8) - :value-mask xcb:render:CP:Repeat - :repeat 1)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:FreePixmap - :pixmap pixmap)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:render:FillRectangles - :op xcb:render:PictOp:Src - :dst alpha-picture - :color (make-instance 'xcb:render:COLOR - :red 0 - :green 0 - :blue 0 - :alpha opacity) - :rects (list (make-instance 'xcb:RECTANGLE - :x 0 - :y 0 - :width 1 - :height 1)))))) - ;; Set the clip region for the rendering buffer. - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:SetPictureClipRegion - :picture exwm-cm--buffer - :region border-clip - :x-origin 0 - :y-origin 0)) - ;; Render the picture to the buffer. - (xcb:+request exwm-cm--conn - (make-instance 'xcb:render:Composite - :op xcb:render:PictOp:Over - :src picture - :mask (or alpha-picture xcb:render:Picture:None) - :dst exwm-cm--buffer - :src-x 0 - :src-y 0 - :mask-x 0 - :mask-y 0 - :dst-x x - :dst-y y - :width width - :height height)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:DestroyRegion - :region border-clip)) - (setf border-clip nil))) - -(defun exwm-cm--paint (&optional region) - "Paint the whole tree within clipping region REGION. - -If REGION is omitted, `exwm-cm--damages' is assumed. If it's t, paint -the whole screen." - ;; Prepare the clipping region. - (cond - ((null region) - (when exwm-cm--damages - (setq region exwm-cm--damages))) - ((eq region t) - (with-slots (width height) (exwm-cm--xwin->attr exwm--root) - (let ((rect (make-instance 'xcb:RECTANGLE - :x 0 - :y 0 - :width width - :height height))) - (setq region (xcb:generate-id exwm-cm--conn)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:CreateRegion - :region region - :rectangles (list rect))))))) - (when region - ;; Prepare the rendering buffer. - (unless exwm-cm--buffer - (let ((pixmap (xcb:generate-id exwm-cm--conn)) - (picture (xcb:generate-id exwm-cm--conn))) - (setq exwm-cm--buffer picture) - (with-slots (width height visual) (exwm-cm--xwin->attr exwm--root) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:CreatePixmap - :depth exwm-cm--depth - :pid pixmap - :drawable exwm--root - :width width - :height height)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:render:CreatePicture - :pid picture - :drawable pixmap - :format (xcb:renderutil:find-visual-format - (xcb:renderutil:query-formats - exwm-cm--conn) - visual) - :value-mask 0))) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:FreePixmap - :pixmap pixmap)))) - (let (queue) - ;; Paint opaque X windows and update clipping region. - (setq queue (exwm-cm--paint-tree nil region)) - ;; Paint the background. - (exwm-cm--paint-background region) - ;; Paint transparent X windows. - (while queue - (exwm-cm--paint-transparent (pop queue)))) - ;; Submit changes. - (with-slots (width height picture) (exwm-cm--xwin->attr exwm--root) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:SetPictureClipRegion - :picture exwm-cm--buffer - :region xcb:xfixes:Region:None - :x-origin 0 - :y-origin 0)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:render:Composite - :op xcb:render:PictOp:Src - :src exwm-cm--buffer - :mask xcb:render:Picture:None - :dst picture - :src-x 0 - :src-y 0 - :mask-x 0 - :mask-y 0 - :dst-x 0 - :dst-y 0 - :width width - :height height))) - ;; Cleanup. - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:DestroyRegion - :region region)) - (when (eq region exwm-cm--damages) - (setq exwm-cm--damages nil)) - (setq exwm-cm--clip-changed nil) - (xcb:flush exwm-cm--conn))) - -(defun exwm-cm--paint-background (region) - "Paint the background." - (unless exwm-cm--background - (setq exwm-cm--background (xcb:generate-id exwm-cm--conn)) - (let (pixmap exist) - (catch 'break - (dolist (atom exwm-cm--background-atoms) - (with-slots (~lsb format value-len value) - (xcb:+request-unchecked+reply exwm-cm--conn - (make-instance 'xcb:GetProperty - :delete 0 - :window exwm--root - :property atom - :type xcb:Atom:PIXMAP - :long-offset 0 - :long-length 4)) - (when (and (= format 32) - (= 1 value-len)) - (setq pixmap (if ~lsb - (xcb:-unpack-u4-lsb value 0) - (xcb:-unpack-u4 value 0))) - (setq exist t) - (throw 'break nil))))) - (unless pixmap - (setq pixmap (xcb:generate-id exwm-cm--conn)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:CreatePixmap - :depth exwm-cm--depth - :pid pixmap - :drawable exwm--root - :width 1 - :height 1))) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:render:CreatePicture - :pid exwm-cm--background - :drawable pixmap - :format (xcb:renderutil:find-visual-format - (xcb:renderutil:query-formats exwm-cm--conn) - (slot-value (exwm-cm--xwin->attr exwm--root) - 'visual)) - :value-mask xcb:render:CP:Repeat - :repeat 1)) - (unless exist - (xcb:+request exwm-cm--conn - (make-instance 'xcb:FreePixmap - :pixmap pixmap)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:render:FillRectangles - :op xcb:render:PictOp:Src - :dst exwm-cm--background - :color (make-instance 'xcb:render:COLOR - :red #x8080 - :green #x8080 - :blue #x8080 - :alpha #xFFFF) - :rects (list (make-instance 'xcb:RECTANGLE - :x 0 - :y 0 - :width 1 - :height 1))))))) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:SetPictureClipRegion - :picture exwm-cm--buffer - :region region - :x-origin 0 - :y-origin 0)) - (with-slots (width height) (exwm-cm--xwin->attr exwm--root) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:render:Composite - :op xcb:render:PictOp:Src - :src exwm-cm--background - :mask xcb:render:Picture:None - :dst exwm-cm--buffer - :src-x 0 - :src-y 0 - :mask-x 0 - :mask-y 0 - :dst-x 0 - :dst-y 0 - :width width - :height height)))) - -(defun exwm-cm--map-xwin (xwin &optional silent) - "Prepare to map X window XWIN." - (let ((attr (exwm-cm--xwin->attr xwin))) - (setf (slot-value attr 'damaged) nil) - ;; Add to damage. - (when (slot-value attr 'extents) - (let ((damage (xcb:generate-id exwm-cm--conn))) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:CreateRegion - :region damage - :rectangles nil)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:CopyRegion - :source (slot-value attr 'extents) - :destination damage)) - (exwm-cm--add-damage damage)) - (unless silent - (exwm-cm--paint))))) - -(defun exwm-cm--on-MapNotify (data _synthetic) - "Handle MapNotify events." - (let ((obj (make-instance 'xcb:MapNotify)) - attr) - (xcb:unmarshal obj data) - (with-slots (event window) obj - (exwm--log "(CM) MapNotify: Try to map #x%X" window) - (setq attr (exwm-cm--xwin->attr window)) - (when (and attr - (/= (slot-value attr 'class) xcb:WindowClass:InputOnly) - (or (= event exwm--root) - ;; Filter out duplicated events. - (/= exwm--root (exwm-cm--get-parent window)))) - (exwm--log "(CM) MapNotify: Map") - (exwm-cm--map-xwin window))))) - -(defun exwm-cm--on-UnmapNotify (data _synthetic) - "Handle UnmapNotify events." - (let ((obj (make-instance 'xcb:UnmapNotify)) - attr) - (xcb:unmarshal obj data) - (with-slots (event window) obj - (exwm--log "(CM) UnmapNotify: Try to unmap #x%X" window) - (setq attr (exwm-cm--xwin->attr window)) - (when (and attr - (or (= event exwm--root) - ;; Filter out duplicated events. - (/= exwm--root (exwm-cm--get-parent window)))) - (exwm--log "(CM) UnmapNotify: Unmap") - (with-slots (picture damaged border-size extents border-clip) attr - (setf damaged nil) - (when picture - (xcb:+request exwm-cm--conn - (make-instance 'xcb:render:FreePicture - :picture picture)) - (setf picture nil)) - (when border-size - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:DestroyRegion - :region border-size)) - (setf border-size nil)) - (when extents - (exwm-cm--add-damage extents) - (setf extents nil)) - (when border-clip - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:DestroyRegion - :region border-clip)) - (setf border-clip nil))) - (setq exwm-cm--clip-changed t) - (exwm-cm--paint))))) - -(defun exwm-cm--on-CreateNotify (data _synthetic) - "Handle CreateNotify events." - (let ((obj (make-instance 'xcb:CreateNotify)) - tree0) - (xcb:unmarshal obj data) - (with-slots (window parent x y width height) obj - (exwm--log "(CM) CreateNotify: Create #x%X on #x%X @%sx%s%+d%+d" - window parent width height x y) - (cl-assert (= parent exwm--root)) - (cl-assert (null (exwm-cm--xwin->attr window))) - (setq tree0 (exwm-cm--get-subtree parent)) - (exwm-cm--create-attr window tree0 x y width height) - (if (cdr tree0) - (exwm-cm--push (list window) (cdr tree0)) - (setcdr tree0 `((,window))))))) - -(defun exwm-cm--on-ConfigureNotify (data synthetic) - "Handle ConfigureNotify events." - ;; Ignore synthetic ConfigureNotify events sent by the WM. - (unless synthetic - (let ((obj (make-instance 'xcb:ConfigureNotify))) - (xcb:unmarshal obj data) - (with-slots (event window above-sibling x y width height) obj - (exwm--log - "(CM) ConfigureNotify: Try to configure #x%X @%sx%s%+d%+d, above #x%X" - window width height x y above-sibling) - (cond - ((= window exwm--root) - (exwm--log "(CM) ConfigureNotify: Configure the root X window") - (when exwm-cm--buffer - (xcb:+request exwm-cm--conn - (make-instance 'xcb:render:FreePicture - :picture exwm-cm--buffer)) - (setq exwm-cm--buffer nil)) - (with-slots ((x* x) - (y* y) - (width* width) - (height* height)) - (exwm-cm--xwin->attr exwm--root) - (setf x* x - y* y - width* width - height* height)) - (exwm-cm--paint)) - ((null (exwm-cm--xwin->attr window))) - ((or (= event exwm--root) - ;; Filter out duplicated events. - (/= exwm--root (exwm-cm--get-parent window))) - (exwm--log "(CM) ConfigureNotify: Configure") - (with-slots ((x0 x) - (y0 y)) - (exwm-cm--xwin->attr (exwm-cm--get-parent window)) - (exwm-cm--update-geometry window (+ x x0) (+ y y0) width height - above-sibling)) - (setq exwm-cm--clip-changed t) - (exwm-cm--paint)) - (t - (exwm--log "(CM) ConfigureNotify: Skip event from #x%X" event))))))) - -(defun exwm-cm--destroy (xwin) - "Prepare to destroy X window XWIN." - (with-slots (tree picture alpha-picture damage - border-size extents border-clip) - (exwm-cm--xwin->attr xwin) - (cl-assert (assq xwin (cdr tree))) - (if (= 1 (length (cdr tree))) - (setcdr tree nil) - (exwm-cm--assq-delete-all xwin (cdr tree))) - (remhash xwin exwm-cm--hash) - ;; Release resources. - (when picture - (xcb:+request exwm-cm--conn - (make-instance 'xcb:render:FreePicture - :picture picture)) - (setf picture nil)) - (when alpha-picture - (xcb:+request exwm-cm--conn - (make-instance 'xcb:render:FreePicture - :picture alpha-picture)) - (setf alpha-picture nil)) - (when damage - (xcb:+request exwm-cm--conn - (make-instance 'xcb:damage:Destroy - :damage damage)) - (setf damage nil)) - (when border-size - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:DestroyRegion - :region border-size)) - (setf border-size nil)) - (when extents - (exwm-cm--add-damage extents) - (setf extents nil)) - (when border-clip - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:DestroyRegion - :region border-clip)) - (setf border-clip nil)))) - -(defun exwm-cm--on-DestroyNotify (data _synthetic) - "Handle DestroyNotify events." - (let ((obj (make-instance 'xcb:DestroyNotify)) - xwin) - (xcb:unmarshal obj data) - (setq xwin (slot-value obj 'window)) - (exwm--log "(CM) DestroyNotify: Try to destroy #x%X" xwin) - (when (exwm-cm--xwin->attr xwin) - (exwm--log "(CM) DestroyNotify: Destroy") - (exwm-cm--destroy xwin)))) - -(defun exwm-cm--on-CirculateNotify (data _synthetic) - "Handle CirculateNotify events." - (let ((obj (make-instance 'xcb:CirculateNotify)) - attr) - (xcb:unmarshal obj data) - (with-slots (event window place) obj - (setq attr (exwm-cm--xwin->attr window)) - (exwm--log "(CM) CirculateNotify: Try to circulate #x%X to %s" - window place) - (when (and attr - (or (= event exwm--root) - ;; Filter out duplicated events. - (/= exwm--root (exwm-cm--get-parent window)))) - (exwm--log "(CM) CirculateNotify: Circulate") - (exwm-cm--update-geometry window nil nil nil nil - (if (= place xcb:Circulate:LowerHighest) - xcb:Window:None - (caar (exwm-cm--get-siblings window)))) - (setq exwm-cm--clip-changed t) - (exwm-cm--paint))))) - -(defun exwm-cm--on-Expose (data _synthetic) - "Handle Expose events." - (let ((obj (make-instance 'xcb:Expose))) - (xcb:unmarshal obj data) - (with-slots (window x y width height count) obj - (when (eq window exwm--root) - (push (make-instance 'xcb:RECTANGLE - :x x - :y y - :width width - :height height) - exwm-cm--expose-rectangles)) - (when (= count 0) - (let ((region (xcb:generate-id exwm-cm--conn))) - (xcb:+request exwm-cm--conn - (xcb:xfixes:CreateRegion - :region region - :rectangles exwm-cm--expose-rectangles)) - (exwm-cm--add-damage region)) - (setq exwm-cm--expose-rectangles nil) - (exwm-cm--paint))))) - -(defun exwm-cm--on-PropertyNotify (data _synthetic) - "Handle PropertyNotify events." - (let ((obj (make-instance 'xcb:PropertyNotify))) - (xcb:unmarshal obj data) - (with-slots (window atom) obj - (cond - ((and (= window exwm--root) - (memq atom exwm-cm--background-atoms)) - (exwm--log "(CM) PropertyNotify: Update background") - (when exwm-cm--background - (xcb:+request exwm-cm--conn - (make-instance 'xcb:render:FreePicture - :picture exwm-cm--background)) - (setq exwm-cm--background nil) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:ClearArea - :exposures 1 - :window exwm--root - :x 0 - :y 0 - :width 0 - :height 0)) - (xcb:flush exwm-cm--conn))) - ((and (= atom exwm-cm--_NET_WM_WINDOW_OPACITY) - ;; Some applications also set this property on their parents. - (null (cdr (exwm-cm--get-subtree window)))) - (when (exwm-cm--xwin->attr window) - (exwm--log "(CM) PropertyNotify: Update opacity for #x%X" window) - (exwm-cm--update-opacity window) - (exwm-cm--paint))))))) - -(defun exwm-cm--prepare-container (xwin) - "Make X window XWIN a container by deselecting unnecessary events." - (with-slots (damage) (exwm-cm--xwin->attr xwin) - (when damage - (xcb:+request exwm-cm--conn - (make-instance 'xcb:damage:Destroy - :damage damage))) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:shape:SelectInput - :destination-window xwin - :enable 0)))) - -(defun exwm-cm--on-ReparentNotify (data _synthetic) - "Handle ReparentNotify events." - (let ((obj (make-instance 'xcb:ReparentNotify)) - tree tree0 grandparent great-grandparent entity) - (xcb:unmarshal obj data) - (with-slots (window parent x y) obj - (exwm--log "(CM) ReparentNotify: Try to reparent #x%X to #x%X @%+d%+d" - window parent x y) - (cond - ((null (exwm-cm--xwin->attr window)) - (when (eq parent exwm--root) - (exwm--log "(CM) ReparentNotify: Create on the root X window") - (let ((reply (xcb:+request-unchecked+reply exwm-cm--conn - (make-instance 'xcb:GetGeometry - :drawable window)))) - (when reply - (with-slots (width height) reply - (setq tree0 (exwm-cm--get-subtree exwm--root)) - (exwm-cm--create-attr window tree0 x y width height) - (if (cdr tree0) - (exwm-cm--push (list window) (cdr tree0)) - (setcdr tree0 `((,window))))) - (exwm-cm--paint))))) - ((= parent (exwm-cm--get-parent window))) - (t - (unless (exwm-cm--xwin->attr parent) - ;; Only allow workspace frame here. - (setq grandparent - (slot-value (xcb:+request-unchecked+reply exwm-cm--conn - (make-instance 'xcb:QueryTree - :window parent)) - 'parent)) - (cond - ((null (exwm-cm--xwin->attr grandparent)) - (exwm--log "(CM) ReparentNotify: Destroy (too deep)")) - ((and (= exwm--root - (setq great-grandparent (exwm-cm--get-parent grandparent))) - (setq tree0 (exwm-cm--get-subtree grandparent)) - (or (setq entity (exwm--id->buffer window)) - (null (cdr tree0)))) - ;; Reparent a workspace frame or an X window into its - ;; container. - (exwm--debug - (if entity - (exwm--log "(CM) ReparentNotify: \ -Create implicit X window container") - (exwm--log "(CM) ReparentNotify: \ -Create implicit workspace frame container"))) - (unless entity - (setq entity 'workspace-frame)) - (with-slots ((x0 x) - (y0 y)) - (exwm-cm--xwin->attr grandparent) - (with-slots (x y width height) - (xcb:+request-unchecked+reply exwm-cm--conn - (make-instance 'xcb:GetGeometry - :drawable parent)) - (exwm-cm--create-attr parent tree0 - (+ x x0) (+ y y0) width height))) - (if (null (cdr tree0)) - (setcdr tree0 `((,parent))) - ;; The stacking order of the parent is unknown. - (let* ((siblings - (slot-value (xcb:+request-unchecked+reply exwm-cm--conn - (make-instance 'xcb:QueryTree - :window grandparent)) - 'children))) - (cl-assert (memq parent siblings)) - (if (= parent (car siblings)) - ;; At the bottom. - (setcdr (last (cdr tree0)) `((,parent))) - ;; Insert it. - (exwm-cm--push (list parent) - ;; The stacking order is reversed. - (nthcdr (- (length siblings) 1 - (cl-position parent siblings)) - (cdr tree0))))))) - ((and (= exwm--root - (exwm-cm--get-parent great-grandparent)) - (setq tree0 (exwm-cm--get-subtree grandparent)) - (= 1 (length (cdr tree0))) - (exwm--id->buffer (caar (cdr tree0)))) - ;; Reparent a floating frame into its container. - (exwm--log "(CM) ReparentNotify: Create floating frame container") - (setq entity 'floating-frame) - (with-slots ((x0 x) - (y0 y)) - (exwm-cm--xwin->attr grandparent) - (with-slots (x y width height) - (xcb:+request-unchecked+reply exwm-cm--conn - (make-instance 'xcb:GetGeometry - :drawable parent)) - (exwm-cm--create-attr parent tree0 - (+ x x0) (+ y y0) width height))) - (nconc (cdr tree0) `((,parent)))) - (t - (exwm--log "(CM) ReparentNotify: Destroy") - (exwm-cm--destroy window)))) - ;; Ensure there's a valid parent. - (when (exwm-cm--xwin->attr parent) - (exwm--log "(CM) ReparentNotify: Reparent") - (when (null (cdr (exwm-cm--get-subtree parent))) - ;; The parent is a new container. - (exwm-cm--prepare-container parent)) - (setq tree (exwm-cm--get-subtree window)) - (let ((tree (exwm-cm--get-tree window))) - (if (= 1 (length (cdr tree))) - (setcdr tree nil) - (exwm-cm--assq-delete-all window (cdr tree)))) - (setq tree0 (exwm-cm--get-subtree parent)) - (exwm-cm--set-tree window tree0) - ;; The size might have already changed (e.g. when reparenting - ;; a workspace frame). - (let ((reply (xcb:+request-unchecked+reply exwm-cm--conn - (make-instance 'xcb:GetGeometry - :drawable window)))) - ;; The X window might have already been destroyed. - (when reply - (with-slots (width height) reply - (with-slots ((x0 x) - (y0 y)) - (exwm-cm--xwin->attr parent) - (exwm-cm--update-geometry window (+ x x0) (+ y y0) - width height))))) - (when entity - ;; Decide frame entity. - (when (symbolp entity) - (catch 'break - (dolist (f (if (eq entity 'workspace-frame) - exwm-workspace--list - (frame-list))) - (when (eq window (frame-parameter f 'exwm-outer-id)) - (setq entity f) - (throw 'break nil)))) - (when (exwm-workspace--workspace-p entity) - ;; The grandparent is a new workspace container. - (exwm-cm--prepare-container grandparent) - (setf (slot-value (exwm-cm--xwin->attr grandparent) 'entity) - entity))) - (setf (slot-value (exwm-cm--xwin->attr parent) 'entity) entity) - (setf (slot-value (exwm-cm--xwin->attr window) 'entity) entity)) - (if (cdr tree0) - (exwm-cm--push tree (cdr tree0)) - (setcdr tree0 `(,tree))) - (exwm-cm--paint))))))) - -(defun exwm-cm--add-damage (damage) - "Add region DAMAGE to `exwm-cm--damages'." - (if (not exwm-cm--damages) - (setq exwm-cm--damages damage) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:UnionRegion - :source1 exwm-cm--damages - :source2 damage - :destination exwm-cm--damages)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:DestroyRegion - :region damage)))) - -(defun exwm-cm--on-DamageNotify (data _synthetic) - "Handle DamageNotify events." - (let ((obj (make-instance 'xcb:damage:Notify)) - parts) - (xcb:unmarshal obj data) - (cl-assert (exwm-cm--xwin->attr (slot-value obj 'drawable))) - (with-slots (x y width height damaged damage) - (exwm-cm--xwin->attr (slot-value obj 'drawable)) - (setq parts (xcb:generate-id exwm-cm--conn)) - (cond - (damaged - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:CreateRegion - :region parts - :rectangles nil)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:damage:Subtract - :damage damage - :repair xcb:xfixes:Region:None - :parts parts)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:TranslateRegion - :region parts - :dx x - :dy y))) - (t - (setf damaged t) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:CreateRegion - :region parts - :rectangles (list (make-instance 'xcb:RECTANGLE - :width width - :height height - :x x - :y y)))) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:damage:Subtract - :damage damage - :repair xcb:xfixes:Region:None - :parts xcb:xfixes:Region:None)))) - (exwm-cm--add-damage parts)) - ;; Check if there are more damages immediately followed. - (unless (/= 0 (logand #x80 (slot-value obj 'level))) - (exwm-cm--paint)))) - -(defun exwm-cm--on-ShapeNotify (data _synthetic) - "Handle ShapeNotify events." - (let ((obj (make-instance 'xcb:shape:Notify)) - attr region1 region2) - (xcb:unmarshal obj data) - (with-slots (shape-kind affected-window shaped - extents-x extents-y extents-width extents-height) - obj - (exwm--log "(CM) ShapeNotify: #x%X" affected-window) - (when (and (or (eq shape-kind xcb:shape:SK:Clip) - (eq shape-kind xcb:shape:SK:Bounding)) - (setq attr (exwm-cm--xwin->attr affected-window))) - (with-slots ((shaped* shaped) - x y width height - shape-x shape-y shape-width shape-height) - attr - (setq region1 (xcb:generate-id exwm-cm--conn) - region2 (xcb:generate-id exwm-cm--conn)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:CreateRegion - :region region1 - :rectangles `(,(make-instance 'xcb:RECTANGLE - :width shape-width - :height shape-height - :x shape-x - :y shape-y)))) - (if shaped - (setf shaped* t - shape-x (+ x extents-x) - shape-y (+ y extents-y) - shape-width (+ width extents-width) - shape-height (+ height extents-height)) - (setf shaped* nil - shape-x x - shape-y y - shape-width width - shape-height height)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:CreateRegion - :region region2 - :rectangles `(,(make-instance 'xcb:RECTANGLE - :width shape-width - :height shape-height - :x shape-x - :y shape-y)))) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:UnionRegion - :source1 region1 - :source2 region2 - :destination region1)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:xfixes:DestroyRegion - :region region2)) - (setq exwm-cm--clip-changed t) - (exwm-cm--paint region1)))))) - -(defun exwm-cm--init () - "Initialize EXWM compositing manager." - ;; Create a new connection. - (setq exwm-cm--conn (xcb:connect)) - (set-process-query-on-exit-flag (slot-value exwm-cm--conn 'process) nil) - ;; Initialize ICCCM/EWMH support. - (xcb:icccm:init exwm-cm--conn) - (xcb:ewmh:init exwm-cm--conn) - ;; Check for Render extension. - (let ((version (xcb:renderutil:query-version exwm-cm--conn))) - (unless (and version - (= 0 (slot-value version 'major-version)) - (<= 2 (slot-value version 'minor-version))) - (error "[EXWM] The server does not support Render extension"))) - ;; Check for Composite extension. - (when (or (= 0 - (slot-value (xcb:get-extension-data exwm-cm--conn - 'xcb:composite) - 'present)) - (with-slots (major-version minor-version) - (xcb:+request-unchecked+reply exwm-cm--conn - (make-instance 'xcb:composite:QueryVersion - :client-major-version 0 - :client-minor-version 1)) - (or (/= major-version 0) (< minor-version 1)))) - (error "[EXWM] The server does not support Composite extension")) - ;; Check for Damage extension. - (when (or (= 0 (slot-value (xcb:get-extension-data exwm-cm--conn 'xcb:damage) - 'present)) - (with-slots (major-version minor-version) - (xcb:+request-unchecked+reply exwm-cm--conn - (make-instance 'xcb:damage:QueryVersion - :client-major-version 1 - :client-minor-version 1)) - (or (/= major-version 1) (< minor-version 1)))) - (error "[EXWM] The server does not support Damage extension")) - ;; Check for XFixes extension. - (when (or (= 0 (slot-value (xcb:get-extension-data exwm-cm--conn 'xcb:xfixes) - 'present)) - (with-slots (major-version minor-version) - (xcb:+request-unchecked+reply exwm-cm--conn - (make-instance 'xcb:xfixes:QueryVersion - :client-major-version 2 - :client-minor-version 0)) - (or (/= major-version 2) (/= minor-version 0)))) - (error "[EXWM] The server does not support XFixes extension")) - ;; Check for Shape extension. - (when (or (= 0 (slot-value (xcb:get-extension-data exwm-cm--conn 'xcb:shape) - 'present)) - (with-slots (major-version minor-version) - (xcb:+request-unchecked+reply exwm-cm--conn - (make-instance 'xcb:shape:QueryVersion)) - (or (/= major-version 1) (< minor-version 1)))) - (error "[EXWM] The server does not support Shape extension")) - ;; Intern atoms. - (let ((atom-name "_NET_WM_WINDOW_OPACITY")) - (setq exwm-cm--_NET_WM_WINDOW_OPACITY - (slot-value (xcb:+request-unchecked+reply exwm-cm--conn - (make-instance 'xcb:InternAtom - :only-if-exists 0 - :name-len (length atom-name) - :name atom-name)) - 'atom))) - (setq exwm-cm--background-atoms - (mapcar (lambda (atom-name) - (slot-value (xcb:+request-unchecked+reply exwm-cm--conn - (make-instance 'xcb:InternAtom - :only-if-exists 0 - :name-len (length atom-name) - :name atom-name)) - 'atom)) - exwm-cm--background-atom-names)) - ;; Register CM. - (with-slots (owner) - (xcb:+request-unchecked+reply exwm-cm--conn - (make-instance 'xcb:GetSelectionOwner - :selection xcb:Atom:_NET_WM_CM_S0)) - (when (/= owner xcb:Window:None) - (error "[EXWM] Other compositing manager detected"))) - (let ((id (xcb:generate-id exwm-cm--conn))) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:CreateWindow - :depth 0 - :wid id - :parent exwm--root - :x 0 - :y 0 - :width 1 - :height 1 - :border-width 0 - :class xcb:WindowClass:InputOnly - :visual 0 - :value-mask xcb:CW:OverrideRedirect - :override-redirect 1)) - ;; Set _NET_WM_NAME. - (xcb:+request exwm-cm--conn - (make-instance 'xcb:ewmh:set-_NET_WM_NAME - :window id - :data "EXWM-CM")) - ;; Get the selection ownership. - (xcb:+request exwm-cm--conn - (make-instance 'xcb:SetSelectionOwner - :owner id - :selection xcb:Atom:_NET_WM_CM_S0 - :time xcb:Time:CurrentTime))) - ;; Attach event listeners. - (xcb:+event exwm-cm--conn 'xcb:MapNotify #'exwm-cm--on-MapNotify) - (xcb:+event exwm-cm--conn 'xcb:UnmapNotify #'exwm-cm--on-UnmapNotify) - (xcb:+event exwm-cm--conn 'xcb:CreateNotify #'exwm-cm--on-CreateNotify) - (xcb:+event exwm-cm--conn 'xcb:ConfigureNotify #'exwm-cm--on-ConfigureNotify) - (xcb:+event exwm-cm--conn 'xcb:DestroyNotify #'exwm-cm--on-DestroyNotify) - (xcb:+event exwm-cm--conn 'xcb:ReparentNotify #'exwm-cm--on-ReparentNotify) - (xcb:+event exwm-cm--conn 'xcb:CirculateNotify #'exwm-cm--on-CirculateNotify) - (xcb:+event exwm-cm--conn 'xcb:Expose #'exwm-cm--on-Expose) - (xcb:+event exwm-cm--conn 'xcb:PropertyNotify #'exwm-cm--on-PropertyNotify) - (xcb:+event exwm-cm--conn 'xcb:damage:Notify #'exwm-cm--on-DamageNotify) - (xcb:+event exwm-cm--conn 'xcb:shape:Notify #'exwm-cm--on-ShapeNotify) - ;; Scan the window tree. - (setq exwm-cm--hash (make-hash-table)) - (exwm-cm--create-tree) - ;; Set up the root X window. - (setq exwm-cm--depth - (slot-value (car (slot-value (xcb:get-setup exwm-cm--conn) 'roots)) - 'root-depth)) - (with-slots (visual picture) (exwm-cm--xwin->attr exwm--root) - (setf picture (xcb:generate-id exwm-cm--conn)) - (xcb:+request exwm-cm--conn - (make-instance 'xcb:render:CreatePicture - :pid picture - :drawable exwm--root - :format (xcb:renderutil:find-visual-format - (xcb:renderutil:query-formats exwm-cm--conn) - visual) - :value-mask xcb:render:CP:SubwindowMode - :subwindowmode xcb:SubwindowMode:IncludeInferiors))) - (xcb:flush exwm-cm--conn) - ;; Paint once. - (exwm-cm--paint t)) +(make-obsolete-variable 'exwm-cm-opacity + "This variable should no longer be used." "26") -(defun exwm-cm--exit () - "Exit EXWM compositing manager." - (when exwm-cm--conn - (xcb:disconnect exwm-cm--conn) - (clrhash exwm-cm--hash) - (setq exwm-cm--hash nil - exwm-cm--conn nil - exwm-cm--buffer nil - exwm-cm--clip-changed t - exwm-cm--damages nil - exwm-cm--expose-rectangles nil - exwm-cm--background nil))) +(defun exwm-cm-set-opacity (&rest _args) + (declare (obsolete nil "26"))) (defun exwm-cm-enable () - "Enable compositing support for EXWM." - (add-hook 'exwm-init-hook #'exwm-cm--init t) - (add-hook 'exwm-exit-hook #'exwm-cm--exit t)) + (declare (obsolete nil "26"))) -;;;###autoload (defun exwm-cm-start () - "Start EXWM compositing manager." - (interactive) - (unless exwm-cm--conn - (exwm-cm--init))) + (declare (obsolete nil "26"))) -;;;###autoload (defun exwm-cm-stop () - "Stop EXWM compositing manager." - (interactive) - (exwm-cm--exit)) + (declare (obsolete nil "26"))) -;;;###autoload (defun exwm-cm-toggle () - "Toggle the running state of EXWM compositing manager." - (interactive) - (if exwm-cm--conn - (exwm-cm-stop) - (exwm-cm-start))) + (declare (obsolete nil "26"))) diff --git a/exwm-core.el b/exwm-core.el index ec3efc6e57..4e9a3899e4 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -46,6 +46,8 @@ (defvar exwm--connection nil "X connection.") (defvar exwm--root nil "Root window.") (defvar exwm--id-buffer-alist nil "Alist of ( . ).") +(defvar exwm--guide-window nil + "An X window separating workspaces and X windows.") (defsubst exwm--id->buffer (id) "X window ID => Emacs buffer." @@ -75,6 +77,20 @@ xcb:EventMask:StructureNotify)))) (xcb:flush exwm--connection)) +(defun exwm--set-geometry (xwin x y width height) + "Set the geometry of X window XWIN to WIDTHxHEIGHT+X+Y. + +Nil can be passed as placeholder." + (exwm--log "Setting #x%x to %sx%s+%s+%s" xwin width height x y) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window xwin + :value-mask (logior (if x xcb:ConfigWindow:X 0) + (if y xcb:ConfigWindow:Y 0) + (if width xcb:ConfigWindow:Width 0) + (if height xcb:ConfigWindow:Height 0)) + :x x :y y :width width :height height))) + (defmacro exwm--defer (secs function &rest args) "Defer the execution of FUNCTION. @@ -103,11 +119,10 @@ least SECS seconds later." ;; Internal variables (defvar-local exwm--id nil) ;window ID -(defvar-local exwm--container nil) ;container (defvar-local exwm--frame nil) ;workspace frame (defvar-local exwm--floating-frame nil) ;floating frame (defvar-local exwm--mode-line-format nil) ;save mode-line-format -(defvar-local exwm--floating-frame-position nil) ;used in fullscreen +(defvar-local exwm--floating-frame-position nil) ;set when hidden. (defvar-local exwm--fixed-size nil) ;fixed size (defvar-local exwm--keyboard-grabbed nil) ;Keyboard grabbed. (defvar-local exwm--on-KeyPress ;KeyPress event handler @@ -271,6 +286,7 @@ least SECS seconds later." (push `(executing-kbd-macro . ,exwm--kmacro-map) minor-mode-overriding-map-alist) (setq buffer-read-only t + cursor-type nil left-margin-width nil right-margin-width nil left-fringe-width 0 diff --git a/exwm-floating.el b/exwm-floating.el index a695346cb2..b0afc1dad3 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -75,12 +75,11 @@ context of the corresponding buffer.") xcb:Atom:_NET_WM_ACTION_CLOSE))))) (defvar exwm-workspace--current) -(defvar exwm-workspace--struts) (defvar exwm-workspace--workareas) -(defvar exwm-workspace-current-index) (declare-function exwm-layout--refresh "exwm-layout.el" ()) (declare-function exwm-layout--show "exwm-layout.el" (id &optional window)) +(declare-function exwm-layout--hide "exwm-layout.el" (id)) (declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) (declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") (declare-function exwm-workspace--position "exwm-workspace.el" (frame)) @@ -91,7 +90,8 @@ context of the corresponding buffer.") (when window ;; Hide the non-floating X window first. (set-window-buffer window (other-buffer nil t)))) - (let* ((original-frame exwm-workspace--current) + (let* ((original-frame (buffer-local-value 'exwm--frame + (exwm--id->buffer id))) ;; Create new frame (frame (with-current-buffer (or (get-buffer "*scratch*") @@ -100,16 +100,14 @@ context of the corresponding buffer.") (get-buffer-create "*scratch*")) (get-buffer "*scratch*"))) (make-frame - `((minibuffer . nil) ;use the default minibuffer. - (left . 10000) - (top . 10000) + `((minibuffer . ,(minibuffer-window exwm--frame)) + (left . ,(* window-min-width -100)) + (top . ,(* window-min-height -100)) (width . ,window-min-width) (height . ,window-min-height) (unsplittable . t))))) ;and fix the size later (outer-id (string-to-number (frame-parameter frame 'outer-window-id))) (window-id (string-to-number (frame-parameter frame 'window-id))) - (container (buffer-local-value 'exwm--container - (exwm--id->buffer id))) (frame-container (xcb:generate-id exwm--connection)) (window (frame-first-window frame)) ;and it's the only window (x (slot-value exwm--geometry 'x)) @@ -176,6 +174,8 @@ context of the corresponding buffer.") ;; Put at the center of screen (setq x (/ (- display-width width) 2) y (/ (- display-height height) 2)))))) + (exwm--set-geometry id x y nil nil) + (xcb:flush exwm--connection) (exwm--log "Floating geometry (corrected): %dx%d%+d%+d" width height x y) ;; Fit frame to client ;; It seems we have to make the frame invisible in order to resize it @@ -194,61 +194,55 @@ context of the corresponding buffer.") exwm--mode-line-format mode-line-format mode-line-format nil)) (set-frame-size frame frame-width frame-height t) - ;; Create the frame container as the parent of the frame and - ;; a child of the X window container. + ;; Create the frame container as the parent of the frame. (xcb:+request exwm--connection (make-instance 'xcb:CreateWindow :depth 0 :wid frame-container - :parent container - :x 0 - :y 0 + :parent exwm--root + :x (- x (elt edges 0)) + :y (- y (elt edges 1)) :width width :height height - :border-width 0 + :border-width exwm-floating-border-width :class xcb:WindowClass:InputOutput :visual 0 :value-mask (logior xcb:CW:BackPixmap - xcb:CW:OverrideRedirect) + (if exwm-floating--border-pixel + xcb:CW:BorderPixel 0) + xcb:CW:OverrideRedirect + (if exwm-floating--border-colormap + xcb:CW:Colormap 0)) :background-pixmap xcb:BackPixmap:ParentRelative - :override-redirect 1)) - ;; Put it at bottom. - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window frame-container - :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:Below)) - ;; Map it. - (xcb:+request exwm--connection - (make-instance 'xcb:MapWindow :window frame-container)) + :border-pixel exwm-floating--border-pixel + :override-redirect 1 + :colormap exwm-floating--border-colormap)) (exwm--debug (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_NAME :window frame-container :data - (format "floating frame container for 0x%x" id))))) + (format "floating frame container for 0x%x" id)))) + ;; Map it. + (xcb:+request exwm--connection + (make-instance 'xcb:MapWindow :window frame-container)) + ;; Put the X window right above this frame container. + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window id + :value-mask (logior xcb:ConfigWindow:Sibling + xcb:ConfigWindow:StackMode) + :sibling frame-container + :stack-mode xcb:StackMode:Above))) ;; Reparent this frame to its container. (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow :window outer-id :parent frame-container :x 0 :y 0)) - ;; Place the X window container. - ;; Also show the floating border. - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window container - :value-mask (eval-when-compile - (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:BorderWidth)) - :x x - :y y - :border-width exwm-floating-border-width)) (exwm-floating--set-allowed-actions id nil) (xcb:flush exwm--connection) ;; Set window/buffer (with-current-buffer (exwm--id->buffer id) (setq window-size-fixed exwm--fixed-size - exwm--frame original-frame exwm--floating-frame frame) ;; Do the refresh manually. (remove-hook 'window-configuration-change-hook #'exwm-layout--refresh) @@ -256,24 +250,19 @@ context of the corresponding buffer.") (add-hook 'window-configuration-change-hook #'exwm-layout--refresh) (set-window-dedicated-p window t) (exwm-layout--show id window)) - (if (exwm-layout--iconic-state-p id) - ;; Hide iconic floating X windows. - (with-current-buffer (exwm--id->buffer id) - (exwm-floating-hide)) - (with-selected-frame exwm-workspace--current - (exwm-layout--refresh)) + (with-current-buffer (exwm--id->buffer id) + (if (exwm-layout--iconic-state-p id) + ;; Hide iconic floating X windows. + (exwm-floating-hide) + (with-selected-frame exwm--frame + (exwm-layout--refresh))) (select-frame-set-input-focus frame)) ;; FIXME: Strangely, the Emacs frame can move itself at this point ;; when there are left/top struts set. Force resetting its ;; position seems working, but it'd better to figure out why. ;; FIXME: This also happens in another case (#220) where the cause is ;; still unclear. - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window outer-id - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y) - :x 0 :y 0)) + (exwm--set-geometry outer-id 0 0 nil nil) (xcb:flush exwm--connection)) (with-current-buffer (exwm--id->buffer id) (run-hooks 'exwm-floating-setup-hook)) @@ -286,10 +275,6 @@ context of the corresponding buffer.") (with-current-buffer buffer (when exwm--floating-frame ;; The X window is already mapped. - ;; Unmap the container to prevent flickering. - (xcb:+request exwm--connection - (make-instance 'xcb:UnmapWindow :window exwm--container)) - (xcb:flush exwm--connection) ;; Unmap the X window. (xcb:+request exwm--connection (make-instance 'xcb:ChangeWindowAttributes @@ -315,29 +300,30 @@ context of the corresponding buffer.") ;; Also destroy its container. (xcb:+request exwm--connection (make-instance 'xcb:DestroyWindow :window frame-container)))) - ;; Put the X window container just above the Emacs frame container + ;; Place the X window just above the reference X window. ;; (the stacking order won't change from now on). ;; Also hide the possible floating border. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow - :window exwm--container + :window id :value-mask (logior xcb:ConfigWindow:BorderWidth xcb:ConfigWindow:Sibling xcb:ConfigWindow:StackMode) :border-width 0 - :sibling (frame-parameter exwm-workspace--current - 'exwm-container) + :sibling exwm--guide-window :stack-mode xcb:StackMode:Above))) (exwm-floating--set-allowed-actions id t) (xcb:flush exwm--connection) (with-current-buffer buffer (when exwm--floating-frame ;from floating to non-floating (set-window-dedicated-p (frame-first-window exwm--floating-frame) nil) - (delete-frame exwm--floating-frame))) ;remove the floating frame + ;; Select a tiling window and delete the old frame. + (select-window (frame-selected-window exwm-workspace--current)) + (with-current-buffer buffer + (delete-frame exwm--floating-frame)))) (with-current-buffer buffer (setq window-size-fixed nil - exwm--floating-frame nil - exwm--frame exwm-workspace--current)) + exwm--floating-frame nil)) ;; Only show X windows in normal state. (unless (exwm-layout--iconic-state-p) (pop-to-buffer-same-window buffer))) @@ -361,14 +347,7 @@ context of the corresponding buffer.") (interactive) (when (and (eq major-mode 'exwm-mode) exwm--floating-frame) - ;; Put this floating X window at bottom. - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window exwm--container - :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:Below)) - (exwm-layout--set-state exwm--id xcb:icccm:WM_STATE:IconicState) - (xcb:flush exwm--connection) + (exwm-layout--hide exwm--id) (select-frame-set-input-focus exwm-workspace--current))) (define-obsolete-function-alias 'exwm-floating-hide-mode-line @@ -387,7 +366,8 @@ context of the corresponding buffer.") ;; Managed. (with-current-buffer buffer-or-id (setq frame exwm--floating-frame - container-or-id exwm--container)) + container-or-id (frame-parameter exwm--floating-frame + 'exwm-container))) ;; Unmanaged. (setq container-or-id id)) (when (and container-or-id @@ -545,96 +525,58 @@ context of the corresponding buffer.") "Stop move/resize." (xcb:+request exwm--connection (make-instance 'xcb:UngrabPointer :time xcb:Time:CurrentTime)) - ;; Inform the X window that its absolute position is changed - (when (and exwm-floating--moveresize-calculate - ;; Unmanaged. - (eq major-mode 'exwm-mode)) - (let ((edges (window-inside-absolute-pixel-edges (frame-selected-window))) - x y width height id) - (setq x (pop edges) - y (pop edges) - width (- (pop edges) x) - height (- (pop edges) y)) - (with-current-buffer (window-buffer (frame-selected-window)) - (setq id exwm--id) - (with-slots ((x* x) - (y* y) - (width* width) - (height* height)) - exwm--geometry - (setf x* x - y* y - width* width - height* height))) - (xcb:+request exwm--connection - (make-instance 'xcb:SendEvent - :propagate 0 - :destination id - :event-mask xcb:EventMask:StructureNotify - :event (xcb:marshal - (make-instance 'xcb:ConfigureNotify - :event id :window id - :above-sibling xcb:Window:None - :x x - :y y - :width width - :height height - :border-width 0 - :override-redirect 0) - exwm--connection))))) - (xcb:flush exwm--connection) - (setq exwm-floating--moveresize-calculate nil)) + (when exwm-floating--moveresize-calculate + (let (result buffer-or-id) + (setq result (funcall exwm-floating--moveresize-calculate 0 0) + buffer-or-id (aref result 0)) + (when (bufferp buffer-or-id) + (with-current-buffer buffer-or-id + (exwm-layout--show exwm--id + (frame-root-window exwm--floating-frame))))) + (setq exwm-floating--moveresize-calculate nil))) (defun exwm-floating--do-moveresize (data _synthetic) "Perform move/resize." (when exwm-floating--moveresize-calculate (let* ((obj (make-instance 'xcb:MotionNotify)) - (workarea (elt exwm-workspace--workareas - exwm-workspace-current-index)) - (frame-x (aref workarea 0)) - (frame-y (aref workarea 1)) - result value-mask width height buffer-or-id container-or-id) + result value-mask x y width height buffer-or-id container-or-id) (xcb:unmarshal obj data) (setq result (funcall exwm-floating--moveresize-calculate (slot-value obj 'root-x) (slot-value obj 'root-y)) - value-mask (logand (aref result 1) - (eval-when-compile - (logior xcb:ConfigWindow:Width - xcb:ConfigWindow:Height))) + buffer-or-id (aref result 0) + value-mask (aref result 1) + x (aref result 2) + y (aref result 3) width (max 1 (aref result 4)) height (max 1 (aref result 5))) - (setq buffer-or-id (aref result 0)) (setq container-or-id (if (bufferp buffer-or-id) ;; Managed. - (buffer-local-value 'exwm--container buffer-or-id) + (with-current-buffer buffer-or-id + (frame-parameter exwm--floating-frame 'exwm-container)) ;; Unmanaged. buffer-or-id)) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window container-or-id :value-mask (aref result 1) - :x (- (aref result 2) frame-x) - :y (- (aref result 3) frame-y) + :x x + :y y :width width :height height)) (when (bufferp buffer-or-id) ;; Managed. - (with-current-buffer buffer-or-id - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window (frame-parameter exwm--floating-frame - 'exwm-container) - :value-mask value-mask - :width width - :height height)) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window (frame-parameter exwm--floating-frame - 'exwm-outer-id) - :value-mask value-mask - :width width - :height height)))) + (setq value-mask (logand value-mask (logior xcb:ConfigWindow:Width + xcb:ConfigWindow:Height))) + (when (/= 0 value-mask) + (with-current-buffer buffer-or-id + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter exwm--floating-frame + 'exwm-outer-id) + :value-mask value-mask + :width width + :height height))))) (xcb:flush exwm--connection)))) (defun exwm-floating-move (&optional delta-x delta-y) @@ -646,37 +588,19 @@ Both DELTA-X and DELTA-Y default to 1. This command should be bound locally." (unless delta-x (setq delta-x 1)) (unless delta-y (setq delta-y 1)) (unless (and (= 0 delta-x) (= 0 delta-y)) - (let* ((geometry (xcb:+request-unchecked+reply exwm--connection + (let* ((floating-container (frame-parameter exwm--floating-frame + 'exwm-container)) + (geometry (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GetGeometry - :drawable exwm--container))) + :drawable floating-container))) (edges (window-inside-absolute-pixel-edges))) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window exwm--container - :value-mask (eval-when-compile - (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y)) - :x (+ (slot-value geometry 'x) delta-x) - :y (+ (slot-value geometry 'y) delta-y))) - ;; Inform the X window that its absolute position is changed - (xcb:+request exwm--connection - (make-instance 'xcb:SendEvent - :propagate 0 :destination exwm--id - :event-mask xcb:EventMask:StructureNotify - :event (xcb:marshal - (make-instance 'xcb:ConfigureNotify - :event exwm--id - :window exwm--id - :above-sibling xcb:Window:None - :x (+ (elt edges 0) delta-x) - :y (+ (elt edges 1) delta-y) - :width (- (elt edges 2) - (elt edges 0)) - :height (- (elt edges 3) - (elt edges 1)) - :border-width 0 - :override-redirect 0) - exwm--connection)))) + (with-slots (x y) geometry + (exwm--set-geometry floating-container + (+ x delta-x) (+ y delta-y) nil nil)) + (exwm--set-geometry exwm--id + (+ (pop edges) delta-x) + (+ (pop edges) delta-y) + nil nil)) (xcb:flush exwm--connection))) (defun exwm-floating--init () diff --git a/exwm-input.el b/exwm-input.el index 54d0540e46..eaddf6b252 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -63,6 +63,8 @@ (defvar exwm-input--simulation-prefix-keys nil "List of prefix keys of simulation keys in line-mode.") +(declare-function exwm-layout--show "exwm-layout.el" (id &optional window)) + (defun exwm-input--set-focus (id) "Set input focus to window ID in a proper way." (when (exwm--id->buffer id) @@ -183,20 +185,6 @@ ARGS are additional arguments to CALLBACK." (let ((exwm-input--global-prefix-keys nil)) (exwm-input--update-global-prefix-keys))) -(defun exwm-input--on-workspace-list-change () - "Run in `exwm-input--update-global-prefix-keys'." - (dolist (f exwm-workspace--list) - ;; Reuse the 'exwm-grabbed' frame parameter set in - ;; `exwm-input--update-global-prefix-keys'. - (unless (frame-parameter f 'exwm-grabbed) - (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window (frame-parameter f 'exwm-workspace) - :value-mask xcb:CW:EventMask - :event-mask xcb:EventMask:FocusChange)))) - (exwm-input--update-global-prefix-keys) - (xcb:flush exwm--connection)) - (declare-function exwm-workspace--client-p "exwm-workspace.el" (&optional frame)) @@ -253,7 +241,6 @@ This value should always be overwritten.") exwm-input--update-focus-window)))) (declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) -(declare-function exwm-layout--set-state "exwm-layout.el" (id state)) (declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") (declare-function exwm-workspace-switch "exwm-workspace.el" (frame-or-index &optional force)) @@ -276,19 +263,27 @@ This value should always be overwritten.") (set-frame-parameter exwm--frame 'exwm-selected-window window) (exwm--defer 0 #'exwm-workspace-switch exwm--frame)) (exwm--log "Set focus on #x%x" exwm--id) - (exwm-input--set-focus exwm--id) (when exwm--floating-frame - ;; Adjust stacking orders of the floating container. + ;; Adjust stacking orders of the floating X window. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow - :window exwm--container + :window exwm--id :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:Above)) + :stack-mode xcb:StackMode:TopIf)) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter exwm--floating-frame + 'exwm-container) + :value-mask (logior + xcb:ConfigWindow:Sibling + xcb:ConfigWindow:StackMode) + :sibling exwm--id + :stack-mode xcb:StackMode:Below)) ;; This floating X window might be hide by `exwm-floating-hide'. (when (exwm-layout--iconic-state-p) - (exwm-layout--set-state exwm--id - xcb:icccm:WM_STATE:NormalState)) - (xcb:flush exwm--connection))) + (exwm-layout--show exwm--id window)) + (xcb:flush exwm--connection)) + (exwm-input--set-focus exwm--id)) (when (eq (selected-window) window) (exwm--log "Focus on %s" window) (if (and (exwm-workspace--workspace-p (selected-frame)) @@ -389,51 +384,38 @@ This value should always be overwritten.") "Update `exwm-input--global-prefix-keys'." (when exwm--connection (let ((original exwm-input--global-prefix-keys) - keysym keycode ungrab-key grab-key workspace) + keysym keycode grab-key) (setq exwm-input--global-prefix-keys nil) (dolist (i exwm-input--global-keys) (cl-pushnew (elt i 0) exwm-input--global-prefix-keys)) ;; Stop here if the global prefix keys are update-to-date and ;; there's no new workspace. - (unless (and (equal original exwm-input--global-prefix-keys) - (cl-every (lambda (w) (frame-parameter w 'exwm-grabbed)) - exwm-workspace--list)) - (setq ungrab-key (make-instance 'xcb:UngrabKey - :key xcb:Grab:Any :grab-window nil - :modifiers xcb:ModMask:Any) - grab-key (make-instance 'xcb:GrabKey + (unless (equal original exwm-input--global-prefix-keys) + (setq grab-key (make-instance 'xcb:GrabKey :owner-events 0 - :grab-window nil + :grab-window exwm--root :modifiers nil :key nil :pointer-mode xcb:GrabMode:Async :keyboard-mode xcb:GrabMode:Async)) - (dolist (w exwm-workspace--list) - (setq workspace (frame-parameter w 'exwm-workspace)) - (setf (slot-value ungrab-key 'grab-window) workspace) - (if (xcb:+request-checked+request-check exwm--connection ungrab-key) - (exwm--log "Failed to ungrab keys") - ;; Label this frame. - (set-frame-parameter w 'exwm-grabbed t) - (dolist (k exwm-input--global-prefix-keys) - (setq keysym (xcb:keysyms:event->keysym exwm--connection k) - keycode (xcb:keysyms:keysym->keycode exwm--connection - (car keysym))) - (setf (slot-value grab-key 'grab-window) workspace - (slot-value grab-key 'modifiers) (cdr keysym) - (slot-value grab-key 'key) keycode) - (when (or (= 0 keycode) - (xcb:+request-checked+request-check exwm--connection - grab-key) - ;; Also grab this key with num-lock mask set. - (when (/= 0 xcb:keysyms:num-lock-mask) - (setf (slot-value grab-key 'modifiers) - (logior (cdr keysym) - xcb:keysyms:num-lock-mask)) - (xcb:+request-checked+request-check exwm--connection - grab-key))) - (user-error "[EXWM] Failed to grab key: %s" - (single-key-description k)))))))))) + (dolist (k exwm-input--global-prefix-keys) + (setq keysym (xcb:keysyms:event->keysym exwm--connection k) + keycode (xcb:keysyms:keysym->keycode exwm--connection + (car keysym))) + (setf (slot-value grab-key 'modifiers) (cdr keysym) + (slot-value grab-key 'key) keycode) + (when (or (= 0 keycode) + (xcb:+request-checked+request-check exwm--connection + grab-key) + ;; Also grab this key with num-lock mask set. + (when (/= 0 xcb:keysyms:num-lock-mask) + (setf (slot-value grab-key 'modifiers) + (logior (cdr keysym) + xcb:keysyms:num-lock-mask)) + (xcb:+request-checked+request-check exwm--connection + grab-key))) + (user-error "[EXWM] Failed to grab key: %s" + (single-key-description k)))))))) ;;;###autoload (defun exwm-input-set-key (key command) @@ -808,23 +790,17 @@ Its usage is the same with `exwm-input-set-simulation-keys'." (add-hook 'pre-command-hook #'exwm-input--on-pre-command) (add-hook 'post-command-hook #'exwm-input--on-post-command) ;; Update focus when buffer list updates - (add-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update) - ;; Re-grab global keys. - (add-hook 'exwm-workspace-list-change-hook - #'exwm-input--on-workspace-list-change) - (exwm-input--on-workspace-list-change) - ;; Prevent frame parameters introduced by this module from being - ;; saved/restored. - (dolist (i '(exwm-grabbed)) - (push (cons i :never) frameset-filter-alist))) + (add-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update)) + +(defun exwm-input--post-init () + "The second stage in the initialization of the input module." + (exwm-input--update-global-prefix-keys)) (defun exwm-input--exit () "Exit the input module." (remove-hook 'pre-command-hook #'exwm-input--on-pre-command) (remove-hook 'post-command-hook #'exwm-input--on-post-command) (remove-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update) - (remove-hook 'exwm-workspace-list-change-hook - #'exwm-input--on-workspace-list-change) (when exwm-input--update-focus-defer-timer (cancel-timer exwm-input--update-focus-defer-timer)) (when exwm-input--update-focus-timer diff --git a/exwm-layout.el b/exwm-layout.el index bcf9c3a67f..cda942e47d 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -30,27 +30,6 @@ (defvar exwm-floating-border-width) (defvar exwm-workspace--id-struts-alist) -(defun exwm-layout--resize-container (id container x y width height - &optional container-only) - "Resize a container (and its content unless CONTAINER-ONLY is non-nil)." - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window container - :value-mask (eval-when-compile - (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height)) - :x x :y y :width width :height height)) - (unless container-only - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window id - :value-mask (eval-when-compile - (logior xcb:ConfigWindow:Width - xcb:ConfigWindow:Height)) - :width width :height height)))) - (defun exwm-layout--set-state (id state) "Set WM_STATE." (xcb:+request exwm--connection @@ -73,72 +52,24 @@ (y (pop edges)) (width (- (pop edges) x)) (height (- (pop edges) y)) - (edges (window-inside-pixel-edges window)) - (relative-x (pop edges)) - (relative-y (pop edges)) - frame-width frame-height) + frame-x frame-y frame-width frame-height) (with-current-buffer (exwm--id->buffer id) - (if (not exwm--floating-frame) - (exwm-layout--resize-container id exwm--container - relative-x relative-y width height - ;; Keep the size of the X window if - ;; it's the minibuffer that resized. - (and - (active-minibuffer-window) - (< 1 (window-height - (active-minibuffer-window))))) - ;; A floating X window is of the same size as the Emacs window, - ;; whereas its container is of the same size as the Emacs frame. + (when exwm--floating-frame (setq frame-width (frame-pixel-width exwm--floating-frame) frame-height (frame-pixel-height exwm--floating-frame)) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window exwm--container - :value-mask (logior xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) - :width frame-width - :height frame-height)) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window (frame-parameter exwm--floating-frame - 'exwm-container) - :value-mask (logior xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) - :width frame-width - :height frame-height)) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window exwm--id - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height) - :x relative-x - :y relative-y - :width width - :height height))) - ;; Make the resizing take effect. - (xcb:flush exwm--connection) + (when exwm--floating-frame-position + (setq frame-x (elt exwm--floating-frame-position 0) + frame-y (elt exwm--floating-frame-position 1) + ;; The frame was placed at (-1, -1). + x (+ x frame-x 1) + y (+ y frame-y 1)) + (setq exwm--floating-frame-position nil)) + (exwm--set-geometry (frame-parameter exwm--floating-frame + 'exwm-container) + frame-x frame-y frame-width frame-height)) + (exwm--set-geometry id x y width height) (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window id)) - (xcb:+request exwm--connection - (make-instance 'xcb:MapWindow :window exwm--container)) - (exwm-layout--set-state id xcb:icccm:WM_STATE:NormalState)) - (xcb:+request exwm--connection - (make-instance 'xcb:SendEvent - :propagate 0 :destination id - :event-mask xcb:EventMask:StructureNotify - :event (xcb:marshal - (make-instance 'xcb:ConfigureNotify - :event id - :window id - :above-sibling xcb:Window:None - :x x - :y y - :width width - :height height - :border-width 0 - :override-redirect 0) - exwm--connection)))) + (exwm-layout--set-state id xcb:icccm:WM_STATE:NormalState))) (xcb:flush exwm--connection)) (defun exwm-layout--hide (id) @@ -146,6 +77,15 @@ (with-current-buffer (exwm--id->buffer id) (unless (exwm-layout--iconic-state-p) ;already hidden (exwm--log "Hide #x%x" id) + (when exwm--floating-frame + (let* ((container (frame-parameter exwm--floating-frame + 'exwm-container)) + (geometry (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetGeometry + :drawable container)))) + (setq exwm--floating-frame-position + (vector (slot-value geometry 'x) (slot-value geometry 'y))) + (exwm--set-geometry container -1 -1 1 1))) (xcb:+request exwm--connection (make-instance 'xcb:ChangeWindowAttributes :window id :value-mask xcb:CW:EventMask @@ -156,8 +96,6 @@ (make-instance 'xcb:ChangeWindowAttributes :window id :value-mask xcb:CW:EventMask :event-mask exwm--client-event-mask)) - (xcb:+request exwm--connection - (make-instance 'xcb:UnmapWindow :window exwm--container)) (exwm-layout--set-state id xcb:icccm:WM_STATE:IconicState) (xcb:flush exwm--connection)))) @@ -167,9 +105,7 @@ (declare-function exwm-input-release-keyboard "exwm-input.el") (declare-function exwm-workspace--current-height "exwm-workspace.el") (declare-function exwm-workspace--current-width "exwm-workspace.el") -(declare-function exwm-workspace--get-geometry "exwm-workspace.el" (frame)) (declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") -(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame)) (declare-function exwm-workspace-move-window "exwm-workspace.el" (frame-or-index &optional id)) @@ -180,41 +116,16 @@ (with-current-buffer (if id (exwm--id->buffer id) (window-buffer)) (when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) (user-error "Already in full-screen mode")) - ;; Save the position of floating frame. - (when exwm--floating-frame - (let* ((geometry (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:GetGeometry - :drawable exwm--container)))) - (setq exwm--floating-frame-position - (vector (slot-value geometry 'x) (slot-value geometry 'y))))) - ;; Expand the workspace to fill the whole screen. - (with-slots (x y width height) (exwm-workspace--get-geometry exwm--frame) - (exwm-layout--resize-container nil - (frame-parameter exwm--frame - 'exwm-workspace) - x y width height - t)) - ;; Raise the workspace container (in case there are docks). - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window (frame-parameter exwm--frame 'exwm-workspace) - :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:Above)) - ;; Expand the X window and its container to fill the whole screen. + ;; Expand the X window to fill the whole screen. ;; Rationale: Floating X windows may not be positioned at (0, 0) ;; due to the extra border. - (exwm-layout--resize-container nil exwm--container 0 0 - (exwm-workspace--current-width) - (exwm-workspace--current-height) - t) - (exwm-layout--resize-container nil exwm--id 0 0 - (exwm-workspace--current-width) - (exwm-workspace--current-height) - t) + (exwm--set-geometry exwm--id 0 0 + (exwm-workspace--current-width) + (exwm-workspace--current-height)) ;; Raise the X window. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow - :window exwm--container + :window exwm--id :value-mask (logior xcb:ConfigWindow:BorderWidth xcb:ConfigWindow:StackMode) :border-width 0 @@ -234,39 +145,20 @@ (with-current-buffer (if id (exwm--id->buffer id) (window-buffer)) (unless (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) (user-error "Not in full-screen mode")) - ;; Restore the size of this workspace. - (exwm-workspace--set-fullscreen exwm--frame) (if exwm--floating-frame - ;; Restore the floating frame. - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window exwm--container - :value-mask (eval-when-compile - (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:BorderWidth)) - :x (elt exwm--floating-frame-position 0) - :y (elt exwm--floating-frame-position 1) - :border-width exwm-floating-border-width)) - ;; Put the X window just above the Emacs frame. + (exwm-layout--show exwm--id (frame-root-window exwm--floating-frame)) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow - :window exwm--container + :window exwm--id :value-mask (logior xcb:ConfigWindow:Sibling xcb:ConfigWindow:StackMode) - :sibling (frame-parameter exwm-workspace--current - 'exwm-container) - :stack-mode xcb:StackMode:Above))) - (exwm-layout--show exwm--id) + :sibling exwm--guide-window + :stack-mode xcb:StackMode:Above)) + (let ((window (get-buffer-window nil t))) + (when window + (exwm-layout--show exwm--id window)))) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_STATE :window exwm--id :data [])) - ;; Raise X windows with struts set again. - (dolist (pair exwm-workspace--id-struts-alist) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window (car pair) - :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:Above))) (xcb:flush exwm--connection) (setq exwm--ewmh-state (delq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) diff --git a/exwm-manage.el b/exwm-manage.el index 97c9d8e3e2..0a9d92fc02 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -92,8 +92,6 @@ corresponding buffer.") :window exwm--root :data (vconcat (mapcar #'car exwm--id-buffer-alist))))) -(defvar exwm-floating--border-colormap) -(defvar exwm-floating--border-pixel) (defvar exwm-workspace--current) (defvar exwm-workspace--switch-history-outdated) (defvar exwm-workspace-current-index) @@ -137,7 +135,8 @@ corresponding buffer.") (setq exwm--id-buffer-alist (nconc exwm--id-buffer-alist `((,id . ,(current-buffer))))) (exwm-mode) - (setq exwm--id id) + (setq exwm--id id + exwm--frame exwm-workspace--current) (exwm--update-window-type id) (exwm--update-class id) (exwm--update-transient-for id) @@ -180,38 +179,13 @@ corresponding buffer.") (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window id)) (with-slots (x y width height) exwm--geometry - ;; Reparent to virtual root - (unless (or (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DESKTOP - exwm-window-type) - (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DOCK - exwm-window-type)) - (let ((workspace (frame-parameter exwm-workspace--current - 'exwm-workspace)) - workarea) - (when (and (/= x 0) - (/= y 0)) - (setq workarea (elt exwm-workspace--workareas - exwm-workspace-current-index) - x (- x (aref workarea 0)) - y (- y (aref workarea 1)))) - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window id - :parent workspace - :x x :y y)))) ;; Center window of type _NET_WM_WINDOW_TYPE_SPLASH (when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_SPLASH exwm-window-type) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window id - :value-mask (eval-when-compile - (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y)) - :x (/ (- (exwm-workspace--current-width) width) - 2) - :y (/ (- (exwm-workspace--current-height) - height) - 2))))) + (exwm--set-geometry id + (/ (- (exwm-workspace--current-width) width) 2) + (/ (- (exwm-workspace--current-height) height) + 2) + nil nil))) ;; Check for desktop. (when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DESKTOP exwm-window-type) ;; There should be only one desktop X window. @@ -229,41 +203,6 @@ corresponding buffer.") (throw 'return 'ignored)) ;; Manage the window (exwm--log "Manage #x%x" id) - ;; Create a new container as the parent of this X window - (setq exwm--container (xcb:generate-id exwm--connection)) - (xcb:+request exwm--connection - (make-instance 'xcb:CreateWindow - :depth 0 - :wid exwm--container - :parent (frame-parameter exwm-workspace--current - 'exwm-workspace) - :x 0 - :y 0 - :width 1 - :height 1 - :border-width 0 - :class xcb:WindowClass:InputOutput - :visual 0 - :value-mask (logior xcb:CW:BackPixmap - (if exwm-floating--border-pixel - xcb:CW:BorderPixel 0) - xcb:CW:OverrideRedirect - xcb:CW:EventMask - (if exwm-floating--border-colormap - xcb:CW:Colormap 0)) - :background-pixmap xcb:BackPixmap:ParentRelative - :border-pixel exwm-floating--border-pixel - :override-redirect 1 - :event-mask xcb:EventMask:SubstructureRedirect - :colormap exwm-floating--border-colormap)) - (exwm--debug - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_WM_NAME - :window exwm--container - :data (format "EXWM container for 0x%x" id)))) - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window id :parent exwm--container :x 0 :y 0)) (xcb:+request exwm--connection ;remove border (make-instance 'xcb:ConfigureWindow :window id :value-mask xcb:ConfigWindow:BorderWidth @@ -340,12 +279,6 @@ manager is shutting down." (exwm-workspace--set-fullscreen f))) (when (buffer-live-p buffer) (with-current-buffer buffer - ;; Flickering seems unavoidable here if the DestroyWindow request is - ;; not initiated by us. - ;; What we can do is to hide the its container ASAP. - (xcb:+request exwm--connection - (make-instance 'xcb:UnmapWindow :window exwm--container)) - (xcb:flush exwm--connection) ;; Unmap the X window. (xcb:+request exwm--connection (make-instance 'xcb:UnmapWindow :window id)) @@ -353,30 +286,10 @@ manager is shutting down." (setq exwm-workspace--switch-history-outdated t) ;; (when withdraw-only - ;; Reparent back to root (xcb:+request exwm--connection (make-instance 'xcb:ChangeWindowAttributes :window id :value-mask xcb:CW:EventMask :event-mask xcb:EventMask:NoEvent)) - (let (x y geometry geometry-parent) - (if (not exwm--floating-frame) - (setq x 0 y 0) ;the position does not matter - (setq geometry-parent - (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:GetGeometry - :drawable exwm--container)) - geometry (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:GetGeometry - :drawable id))) - (if (not (and geometry-parent geometry)) - (setq x 0 y 0) ;e.g. have been destroyed - (setq x (+ (slot-value geometry-parent 'x) - (slot-value geometry 'x)) - y (+ (slot-value geometry-parent 'y) - (slot-value geometry 'y))))) - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window id :parent exwm--root :x x :y y))) ;; Delete WM_STATE property (xcb:+request exwm--connection (make-instance 'xcb:DeleteProperty @@ -388,19 +301,20 @@ manager is shutting down." :window id :property xcb:Atom:_NET_WM_DESKTOP)))) (when exwm--floating-frame - ;; Unmap the floating frame before destroying the containers. - (let ((window (frame-parameter exwm--floating-frame 'exwm-outer-id))) + ;; Unmap the floating frame before destroying its container. + (let ((window (frame-parameter exwm--floating-frame 'exwm-outer-id)) + (container (frame-parameter exwm--floating-frame + 'exwm-container))) (xcb:+request exwm--connection (make-instance 'xcb:UnmapWindow :window window)) (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow - :window window :parent exwm--root :x 0 :y 0)))) + :window window :parent exwm--root :x 0 :y 0)) + (xcb:+request exwm--connection + (make-instance 'xcb:DestroyWindow :window container)))) ;; Restore the workspace if this X window is currently fullscreen. (when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) (exwm-workspace--set-fullscreen exwm--frame)) - ;; Destroy the X window container (and the frame container if any). - (xcb:+request exwm--connection - (make-instance 'xcb:DestroyWindow :window exwm--container)) (exwm-manage--set-client-list) (xcb:flush exwm--connection)) (let ((kill-buffer-func @@ -444,38 +358,28 @@ manager is shutting down." "Run in `kill-buffer-query-functions'." (catch 'return (when (or (not exwm--id) - (not exwm--container) (xcb:+request-checked+request-check exwm--connection (make-instance 'xcb:MapWindow :window exwm--id))) ;; The X window is no longer alive so just close the buffer. - ;; Destroy the container. - ;; Hide the container to prevent flickering. - (when exwm--container - (xcb:+request exwm--connection - (make-instance 'xcb:UnmapWindow - :window exwm--container)) - (xcb:flush exwm--connection)) (when exwm--floating-frame - (let ((window (frame-parameter exwm--floating-frame 'exwm-outer-id))) + (let ((window (frame-parameter exwm--floating-frame 'exwm-outer-id)) + (container (frame-parameter exwm--floating-frame + 'exwm-container))) (xcb:+request exwm--connection (make-instance 'xcb:UnmapWindow :window window)) (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow :window window :parent exwm--root - :x 0 :y 0)))) - (when exwm--container - (xcb:+request exwm--connection - (make-instance 'xcb:DestroyWindow - :window exwm--container))) + :x 0 :y 0)) + (xcb:+request exwm--connection + (make-instance 'xcb:DestroyWindow + :window container)))) (xcb:flush exwm--connection) (throw 'return t)) (unless (memq xcb:Atom:WM_DELETE_WINDOW exwm--protocols) ;; The X window does not support WM_DELETE_WINDOW; destroy it. - ;; Hide the container to prevent flickering. - (xcb:+request exwm--connection - (make-instance 'xcb:UnmapWindow :window exwm--container)) (xcb:+request exwm--connection (make-instance 'xcb:DestroyWindow :window exwm--id)) (xcb:flush exwm--connection) @@ -529,13 +433,6 @@ Would you like to kill it? " (defun exwm-manage--kill-client (&optional id) "Kill an X client." (unless id (setq id (exwm--buffer->id (current-buffer)))) - ;; Hide the container to prevent flickering. - (let ((buffer (exwm--id->buffer id))) - (when buffer - (with-current-buffer buffer - (xcb:+request exwm--connection - (make-instance 'xcb:UnmapWindow :window exwm--container)) - (xcb:flush exwm--connection)))) (let* ((response (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:ewmh:get-_NET_WM_PID :window id))) (pid (and response (slot-value response 'value))) diff --git a/exwm-randr.el b/exwm-randr.el index 07a000c3b3..74938d6b71 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -164,7 +164,7 @@ (add-hook 'exwm-workspace-list-change-hook #'exwm-randr--refresh)))) ;; Prevent frame parameters introduced by this module from being ;; saved/restored. - (dolist (i '(exwm-randr-output exwm-geometry)) + (dolist (i '(exwm-randr-output)) (push (cons i :never) frameset-filter-alist))) (defun exwm-randr--exit () diff --git a/exwm-workspace.el b/exwm-workspace.el index 0aabbefcdd..2917c6910b 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -321,16 +321,12 @@ Value nil means to use the default position which is fixed at bottom, while (defvar exwm-workspace--fullscreen-frame-count 0 "Count the fullscreen workspace frames.") -(declare-function exwm-layout--resize-container "exwm-layout.el" - (id container x y width height &optional container-only)) - (defun exwm-workspace--set-fullscreen (frame) "Make frame FRAME fullscreen according to `exwm-workspace--workareas'." (let ((workarea (elt exwm-workspace--workareas (exwm-workspace--position frame))) (id (frame-parameter frame 'exwm-outer-id)) (container (frame-parameter frame 'exwm-container)) - (workspace (frame-parameter frame 'exwm-workspace)) x y width height) (setq x (aref workarea 0) y (aref workarea 1) @@ -339,8 +335,8 @@ Value nil means to use the default position which is fixed at bottom, while (when (and (eq frame exwm-workspace--current) (exwm-workspace--minibuffer-own-frame-p)) (exwm-workspace--resize-minibuffer-frame)) - (exwm-layout--resize-container id container 0 0 width height) - (exwm-layout--resize-container nil workspace x y width height t) + (exwm--set-geometry container x y width height) + (exwm--set-geometry id nil nil width height) (xcb:flush exwm--connection)) ;; This is only used for workspace initialization. (when exwm-workspace--fullscreen-frame-count @@ -457,26 +453,18 @@ The optional FORCE option is for internal use only." (let* ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index)) (old-frame exwm-workspace--current) (index (exwm-workspace--position frame)) - (workspace (frame-parameter frame 'exwm-workspace)) (window (frame-parameter frame 'exwm-selected-window))) (when (or force (not (eq frame exwm-workspace--current))) (unless (window-live-p window) (setq window (frame-selected-window frame))) - ;; Raise the workspace container. + ;; Raise this frame. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow - :window workspace - :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:Above)) - ;; Raise X windows with struts set if there's no fullscreen X window. - (unless (with-current-buffer (window-buffer window) - (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) - (dolist (pair exwm-workspace--id-struts-alist) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window (car pair) - :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:Above)))) + :window (frame-parameter frame 'exwm-container) + :value-mask (logior xcb:ConfigWindow:Sibling + xcb:ConfigWindow:StackMode) + :sibling exwm--guide-window + :stack-mode xcb:StackMode:Below)) (setq exwm-workspace--current frame exwm-workspace-current-index index) (unless (exwm-workspace--workspace-p (selected-frame)) @@ -497,6 +485,15 @@ The optional FORCE option is for internal use only." (exwm-workspace--resize-minibuffer-frame) ;; Set a default minibuffer frame. (setq default-minibuffer-frame frame)) + ;; Show/Hide X windows. + (dolist (i exwm--id-buffer-alist) + (with-current-buffer (cdr i) + (if (eq old-frame exwm--frame) + (exwm-layout--hide exwm--id) + (when (eq frame exwm--frame) + (let ((window (get-buffer-window nil t))) + (when window + (exwm-layout--show exwm--id window))))))) ;; Hide windows in other workspaces by preprending a space (unless exwm-workspace-show-all-buffers (dolist (i exwm--id-buffer-alist) @@ -538,7 +535,7 @@ each time.") (exwm-workspace--count))))) (make-frame)) (run-hooks 'exwm-workspace-list-change-hook)) - (exwm-workspace-switch (car (last exwm-workspace--list))))) + (exwm-workspace-switch frame-or-index))) (defvar exwm-workspace-list-change-hook nil "Normal hook run when the workspace list is changed (workspace added, @@ -662,7 +659,8 @@ INDEX must not exceed the current number of workspaces." (let ((exwm-workspace--prompt-add-allowed t) (exwm-workspace--prompt-delete-allowed t)) (exwm-workspace--prompt-for-workspace "Move to [+/-]: ")))) - (let ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index))) + (let ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index)) + old-frame container) (unless id (setq id (exwm--buffer->id (window-buffer)))) (with-current-buffer (exwm--id->buffer id) (unless (eq exwm--frame frame) @@ -672,112 +670,111 @@ INDEX must not exceed the current number of workspaces." (if (eq frame exwm-workspace--current) name (concat " " name))))) - (setq exwm--frame frame) - (if exwm--floating-frame - ;; Move the floating container. - (with-slots (x y) - (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:GetGeometry :drawable exwm--container)) + (setq old-frame exwm--frame + exwm--frame frame) + (if (not exwm--floating-frame) + ;; Tiling. + (progn + (set-window-buffer (get-buffer-window nil t) + (other-buffer nil t)) + (unless (eq frame exwm-workspace--current) + ;; Clear the 'exwm-selected-window' frame parameter. + (set-frame-parameter frame 'exwm-selected-window nil)) + (set-window-buffer (frame-selected-window frame) + (exwm--id->buffer id)) + (if (eq frame exwm-workspace--current) + (select-window (frame-selected-window frame)) + (exwm-layout--hide id))) + ;; Floating. + (setq container (frame-parameter exwm--floating-frame + 'exwm-container)) + (with-slots ((x1 x) + (y1 y)) + (exwm-workspace--get-geometry old-frame) + (with-slots ((x2 x) + (y2 y)) + (exwm-workspace--get-geometry frame) + (unless (and (= x1 x2) + (= y1 y2)) + (with-slots (x y) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetGeometry + :drawable container)) + (setq x (+ x (- x2 x1)) + y (+ y (- y2 y1))) + (exwm--set-geometry id x y nil nil) + (exwm--set-geometry container x y nil nil))))) + (if (exwm-workspace--minibuffer-own-frame-p) + (if (eq frame exwm-workspace--current) + (select-window (frame-root-window exwm--floating-frame)) + (select-window (frame-selected-window exwm-workspace--current)) + (exwm-layout--hide id)) + ;; The frame needs to be recreated since it won't use the + ;; minibuffer on the new workspace. + ;; The code is mostly copied from `exwm-floating--set-floating'. + (let* ((old-frame exwm--floating-frame) + (new-frame + (with-current-buffer + (or (get-buffer "*scratch*") + (progn + (set-buffer-major-mode + (get-buffer-create "*scratch*")) + (get-buffer "*scratch*"))) + (make-frame + `((minibuffer . ,(minibuffer-window frame)) + (left . ,(* window-min-width -100)) + (top . ,(* window-min-height -100)) + (width . ,window-min-width) + (height . ,window-min-height) + (unsplittable . t))))) + (outer-id (string-to-number + (frame-parameter new-frame + 'outer-window-id))) + (window-id (string-to-number + (frame-parameter new-frame 'window-id))) + (window (frame-root-window new-frame))) + (set-frame-parameter new-frame 'exwm-outer-id outer-id) + (set-frame-parameter new-frame 'exwm-id window-id) + (set-frame-parameter new-frame 'exwm-container container) + (make-frame-invisible new-frame) + (set-frame-size new-frame + (frame-pixel-width old-frame) + (frame-pixel-height old-frame) + t) (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow - :window exwm--container - :parent - (frame-parameter frame 'exwm-workspace) - :x x :y y)) + :window outer-id + :parent container + :x 0 :y 0)) + (xcb:flush exwm--connection) + (with-current-buffer (exwm--id->buffer id) + (setq window-size-fixed nil + exwm--floating-frame new-frame) + (set-window-dedicated-p (frame-root-window old-frame) nil) + (remove-hook 'window-configuration-change-hook + #'exwm-layout--refresh) + (set-window-buffer window (current-buffer)) + (add-hook 'window-configuration-change-hook + #'exwm-layout--refresh) + (set-window-dedicated-p window t)) + ;; Select a tiling window and delete the old frame. + (select-window (frame-selected-window exwm-workspace--current)) + (delete-frame old-frame) + ;; The rest is the same. + (make-frame-visible new-frame) + (exwm--set-geometry outer-id 0 0 nil nil) (xcb:flush exwm--connection) - (if (exwm-workspace--minibuffer-own-frame-p) - (when (eq frame exwm-workspace--current) - (select-frame-set-input-focus exwm--floating-frame) - (exwm-layout--refresh)) - ;; The frame needs to be recreated since it won't use the - ;; minibuffer on the new workspace. - (let* ((old-frame exwm--floating-frame) - (new-frame - (with-current-buffer - (or (get-buffer "*scratch*") - (progn - (set-buffer-major-mode - (get-buffer-create "*scratch*")) - (get-buffer "*scratch*"))) - (make-frame - `((minibuffer . ,(minibuffer-window frame)) - (left . 10000) - (top . 10000) - (width . ,window-min-width) - (height . ,window-min-height) - (unsplittable . t))))) - (outer-id (string-to-number - (frame-parameter new-frame - 'outer-window-id))) - (window-id (string-to-number - (frame-parameter new-frame 'window-id))) - (frame-container (frame-parameter old-frame - 'exwm-container)) - (window (frame-root-window new-frame))) - (set-frame-parameter new-frame 'exwm-outer-id outer-id) - (set-frame-parameter new-frame 'exwm-id window-id) - (set-frame-parameter new-frame 'exwm-container - frame-container) - (make-frame-invisible new-frame) - (set-frame-size new-frame - (frame-pixel-width old-frame) - (frame-pixel-height old-frame) - t) - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window outer-id - :parent frame-container - :x 0 :y 0)) - (xcb:flush exwm--connection) + (redisplay) + (if (eq frame exwm-workspace--current) (with-current-buffer (exwm--id->buffer id) - (setq window-size-fixed nil - exwm--frame frame - exwm--floating-frame new-frame) - (set-window-dedicated-p (frame-root-window old-frame) nil) - (remove-hook 'window-configuration-change-hook - #'exwm-layout--refresh) - (set-window-buffer window (current-buffer)) - (add-hook 'window-configuration-change-hook - #'exwm-layout--refresh) - (delete-frame old-frame) - (set-window-dedicated-p window t) - (exwm-layout--show id window)) - (if (not (eq frame exwm-workspace--current)) - (make-frame-visible new-frame) - (select-frame-set-input-focus new-frame) - (redisplay)))) - ;; Update the 'exwm-selected-window' frame parameter. - (when (not (eq frame exwm-workspace--current)) - (with-current-buffer (exwm--id->buffer id) - (set-frame-parameter frame 'exwm-selected-window - (frame-root-window - exwm--floating-frame))))) - ;; Move the X window container. - (set-window-buffer (get-buffer-window (current-buffer) t) - (other-buffer nil t)) - (unless (eq frame exwm-workspace--current) - ;; Clear the 'exwm-selected-window' frame parameter. - (set-frame-parameter frame 'exwm-selected-window nil)) - (exwm-layout--hide id) - ;; (current-buffer) is changed. - (with-current-buffer (exwm--id->buffer id) - ;; Reparent to the destination workspace. - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window exwm--container - :parent (frame-parameter frame 'exwm-workspace) - :x 0 :y 0)) - ;; Place it just above the destination frame container. - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window exwm--container - :value-mask (logior xcb:ConfigWindow:Sibling - xcb:ConfigWindow:StackMode) - :sibling (frame-parameter frame 'exwm-container) - :stack-mode xcb:StackMode:Above))) - (xcb:flush exwm--connection) - (set-window-buffer (frame-selected-window frame) - (exwm--id->buffer id))) + (select-window (frame-root-window exwm--floating-frame))) + (exwm-layout--hide id)))) + ;; Update the 'exwm-selected-window' frame parameter. + (when (not (eq frame exwm-workspace--current)) + (with-current-buffer (exwm--id->buffer id) + (set-frame-parameter frame 'exwm-selected-window + (frame-root-window + exwm--floating-frame))))) ;; Set _NET_WM_DESKTOP. (exwm-workspace--set-desktop id) (xcb:flush exwm--connection))) @@ -1005,16 +1002,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." 'exwm-container) :value-mask xcb:ConfigWindow:StackMode :stack-mode xcb:StackMode:Above)) - (xcb:flush exwm--connection) - ;; Unfortunately we need the following lines to workaround a cursor - ;; flickering issue for line-mode floating X windows. They just make the - ;; minibuffer appear to be focused. - ;; (FIXED?) - ;; (with-current-buffer (window-buffer (minibuffer-window - ;; exwm-workspace--minibuffer)) - ;; (setq cursor-in-non-selected-windows - ;; (frame-parameter exwm-workspace--minibuffer 'cursor-type))) - ) + (xcb:flush exwm--connection)) (defun exwm-workspace--hide-minibuffer () "Hide the minibuffer frame." @@ -1198,13 +1186,11 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (let ((outer-id (string-to-number (frame-parameter frame 'outer-window-id))) (window-id (string-to-number (frame-parameter frame 'window-id))) - (container (xcb:generate-id exwm--connection)) - (workspace (xcb:generate-id exwm--connection))) + (container (xcb:generate-id exwm--connection))) ;; Save window IDs (set-frame-parameter frame 'exwm-outer-id outer-id) (set-frame-parameter frame 'exwm-id window-id) (set-frame-parameter frame 'exwm-container container) - (set-frame-parameter frame 'exwm-workspace workspace) ;; In case it's created by emacsclient. (set-frame-parameter frame 'client nil) ;; Copy RandR frame parameters from the first workspace to @@ -1217,49 +1203,25 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (xcb:+request exwm--connection (make-instance 'xcb:CreateWindow :depth 0 - :wid workspace + :wid container :parent exwm--root - :x 0 - :y 0 - :width (x-display-pixel-width) - :height (x-display-pixel-height) + :x -1 + :y -1 + :width 1 + :height 1 :border-width 0 :class xcb:WindowClass:InputOutput :visual 0 :value-mask (logior xcb:CW:BackPixmap - xcb:CW:OverrideRedirect - xcb:CW:EventMask) + xcb:CW:OverrideRedirect) :background-pixmap xcb:BackPixmap:ParentRelative - :override-redirect 1 - :event-mask xcb:EventMask:SubstructureRedirect)) + :override-redirect 1)) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow - :window workspace + :window container :value-mask xcb:ConfigWindow:StackMode :stack-mode xcb:StackMode:Below)) - (xcb:+request exwm--connection - (make-instance 'xcb:CreateWindow - :depth 0 - :wid container - :parent workspace - :x 0 - :y 0 - :width (x-display-pixel-width) - :height (x-display-pixel-height) - :border-width 0 - :class xcb:WindowClass:InputOutput - :visual 0 - :value-mask (logior xcb:CW:BackPixmap - xcb:CW:OverrideRedirect) - :background-pixmap xcb:BackPixmap:ParentRelative - :override-redirect 1)) (exwm--debug - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_WM_NAME - :window workspace - :data - (format "EXWM workspace %d" - (exwm-workspace--position frame)))) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_NAME :window container @@ -1270,9 +1232,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (make-instance 'xcb:ReparentWindow :window outer-id :parent container :x 0 :y 0)) (xcb:+request exwm--connection - (make-instance 'xcb:MapWindow :window container)) - (xcb:+request exwm--connection - (make-instance 'xcb:MapWindow :window workspace))) + (make-instance 'xcb:MapWindow :window container))) (xcb:flush exwm--connection) ;; Delay making the workspace fullscreen until Emacs becomes idle (exwm--defer 0 #'set-frame-parameter frame 'fullscreen 'fullboth) @@ -1323,10 +1283,10 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." :parent exwm--root :x 0 :y 0)) - ;; Destroy the containers. + ;; Destroy the container. (xcb:+request exwm--connection (make-instance 'xcb:DestroyWindow - :window (frame-parameter frame 'exwm-workspace))) + :window (frame-parameter frame 'exwm-container))) ;; Update EWMH properties. (exwm-workspace--update-ewmh-props) ;; Update switch history. @@ -1343,15 +1303,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." ;; Set _NET_DESKTOP_GEOMETRY. (exwm-workspace--set-desktop-geometry) ;; Update workareas. - (exwm-workspace--update-workareas) - ;; Set _NET_VIRTUAL_ROOTS. - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_VIRTUAL_ROOTS - :window exwm--root - :data (vconcat (mapcar - (lambda (i) - (frame-parameter i 'exwm-workspace)) - exwm-workspace--list))))) + (exwm-workspace--update-workareas)) (xcb:flush exwm--connection)) (defun exwm-workspace--modify-all-x-frames-parameters (new-x-parameters) @@ -1505,7 +1457,7 @@ applied to all subsequently created X frames." (exwm-workspace-switch 0 t) ;; Prevent frame parameters introduced by this module from being ;; saved/restored. - (dolist (i '(exwm-outer-id exwm-id exwm-container exwm-workspace + (dolist (i '(exwm-outer-id exwm-id exwm-container exwm-geometry fullscreen exwm-selected-window exwm-urgency)) (push (cons i :never) frameset-filter-alist))) diff --git a/exwm.el b/exwm.el index 3c0124672b..02e9152ed0 100644 --- a/exwm.el +++ b/exwm.el @@ -36,7 +36,6 @@ ;; + Dynamic workspace support ;; + ICCCM/EWMH compliance ;; + (Optional) RandR (multi-monitor) support -;; + (Optional) Built-in compositing manager ;; + (Optional) Built-in system tray ;; Installation & configuration @@ -509,7 +508,7 @@ xcb:Atom:_NET_ACTIVE_WINDOW ;; xcb:Atom:_NET_WORKAREA xcb:Atom:_NET_SUPPORTING_WM_CHECK - xcb:Atom:_NET_VIRTUAL_ROOTS + ;; xcb:Atom:_NET_VIRTUAL_ROOTS ;; xcb:Atom:_NET_DESKTOP_LAYOUT ;; xcb:Atom:_NET_SHOWING_DESKTOP @@ -593,13 +592,14 @@ xcb:Atom:_NET_WM_FULL_PLACEMENT))) ;; Create a child window for setting _NET_SUPPORTING_WM_CHECK (let ((new-id (xcb:generate-id exwm--connection))) + (setq exwm--guide-window new-id) (xcb:+request exwm--connection (make-instance 'xcb:CreateWindow :depth 0 :wid new-id :parent exwm--root - :x 0 - :y 0 + :x -1 + :y -1 :width 1 :height 1 :border-width 0 @@ -636,7 +636,6 @@ xcb:Atom:_NET_CURRENT_DESKTOP xcb:Atom:_NET_ACTIVE_WINDOW xcb:Atom:_NET_SUPPORTING_WM_CHECK - xcb:Atom:_NET_VIRTUAL_ROOTS ;; TODO: Keep this list synchronized with that in ;; `exwm--init-icccm-ewmh'. )) @@ -688,6 +687,7 @@ (exwm-input--init) (exwm--unlock) (exwm-workspace--post-init) + (exwm-input--post-init) ;; Manage existing windows (exwm-manage--scan) (run-hooks 'exwm-init-hook))))) -- cgit 1.4.1 From d22e6740d761bd2c67e928579502a6c2816516a9 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 19 Feb 2018 00:04:27 +0800 Subject: Add customization settings ; Also fix documentations. --- exwm-cm.el | 2 +- exwm-core.el | 52 ++++++------- exwm-floating.el | 73 ++++++++++-------- exwm-input.el | 207 ++++++++++++++++++++++++++++++-------------------- exwm-layout.el | 75 +++++++++--------- exwm-manage.el | 106 +++++++++++++------------- exwm-randr.el | 38 +++++++--- exwm-systemtray.el | 34 ++++++--- exwm-workspace.el | 218 ++++++++++++++++++++++++++++++----------------------- exwm.el | 59 ++++++++------- xinitrc | 3 + 11 files changed, 495 insertions(+), 372 deletions(-) diff --git a/exwm-cm.el b/exwm-cm.el index 77dd2774a1..ff556fb219 100644 --- a/exwm-cm.el +++ b/exwm-cm.el @@ -21,7 +21,7 @@ ;;; Commentary: -;; This module is obsolete since EXWM now supports third-porty compositors. +;; This module is obsolete since EXWM now supports third-party compositors. ;;; Code: diff --git a/exwm-core.el b/exwm-core.el index 4e9a3899e4..68ec53b03e 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -35,6 +35,32 @@ (eval-and-compile (defvar exwm-debug-on nil "Non-nil to turn on debug for EXWM.")) +(defvar exwm--connection nil "X connection.") + +(defvar exwm--guide-window nil + "An X window separating workspaces and X windows.") + +(defvar exwm--id-buffer-alist nil "Alist of ( . ).") + +(defvar exwm--root nil "Root window.") + +(defvar exwm-input--global-prefix-keys) +(defvar exwm-input--simulation-prefix-keys) +(defvar exwm-input-line-mode-passthrough) +(defvar exwm-input-prefix-keys) +(declare-function exwm-input--fake-key "exwm-input.el" (event)) +(declare-function exwm-input--on-KeyPress-line-mode "exwm-input.el" + (key-press raw-data)) +(declare-function exwm-floating-hide "exwm-floating.el") +(declare-function exwm-floating-toggle-floating "exwm-floating.el") +(declare-function exwm-input-release-keyboard "exwm-input.el") +(declare-function exwm-input-send-next-key "exwm-input.el" (times)) +(declare-function exwm-layout-set-fullscreen "exwm-layout.el" (&optional id)) +(declare-function exwm-layout-toggle-mode-line "exwm-layout.el") +(declare-function exwm-manage--kill-buffer-query-function "exwm-manage.el") +(declare-function exwm-workspace-move-window "exwm-workspace.el" + (frame-or-index &optional id)) + (defmacro exwm--log (format-string &rest args) "Print debug message." (when exwm-debug-on @@ -43,12 +69,6 @@ (defmacro exwm--debug (&rest forms) (when exwm-debug-on `(progn ,@forms))) -(defvar exwm--connection nil "X connection.") -(defvar exwm--root nil "Root window.") -(defvar exwm--id-buffer-alist nil "Alist of ( . ).") -(defvar exwm--guide-window nil - "An X window separating workspaces and X windows.") - (defsubst exwm--id->buffer (id) "X window ID => Emacs buffer." (cdr (assoc id exwm--id-buffer-alist))) @@ -108,15 +128,6 @@ least SECS seconds later." xcb:EventMask:EnterWindow 0)) "Event mask set on all managed windows.") -(defvar exwm-input-line-mode-passthrough) -(defvar exwm-input--global-prefix-keys) -(defvar exwm-input-prefix-keys) -(defvar exwm-input--simulation-prefix-keys) - -(declare-function exwm-input--fake-key "exwm-input.el" (event)) -(declare-function exwm-input--on-KeyPress-line-mode "exwm-input.el" - (key-press raw-data)) - ;; Internal variables (defvar-local exwm--id nil) ;window ID (defvar-local exwm--frame nil) ;workspace frame @@ -154,15 +165,6 @@ least SECS seconds later." ;; _MOTIF_WM_HINTS (defvar-local exwm--mwm-hints-decorations t) -(declare-function exwm-floating-hide "exwm-floating.el") -(declare-function exwm-floating-toggle-floating "exwm-floating.el") -(declare-function exwm-input-release-keyboard "exwm-input.el") -(declare-function exwm-input-send-next-key "exwm-input.el" (times)) -(declare-function exwm-layout-set-fullscreen "exwm-layout.el" (&optional id)) -(declare-function exwm-layout-toggle-mode-line "exwm-layout.el") -(declare-function exwm-workspace-move-window "exwm-workspace.el" - (frame-or-index &optional id)) - (defvar exwm-mode-map (let ((map (make-sparse-keymap))) (define-key map "\C-c\C-f" #'exwm-layout-set-fullscreen) @@ -264,8 +266,6 @@ least SECS seconds later." (/= ,i exwm-workspace-current-index)]) (number-sequence 0 (1- (exwm-workspace--count)))))))) -(declare-function exwm-manage--kill-buffer-query-function "exwm-manage.el") - (define-derived-mode exwm-mode nil "EXWM" "Major mode for managing X windows. diff --git a/exwm-floating.el b/exwm-floating.el index b0afc1dad3..8cd0491bea 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -29,22 +29,35 @@ (require 'xcb-cursor) (require 'exwm-core) -(defvar exwm-floating-border-width 1 "Border width of the floating window.") -(defvar exwm-floating-border-color "navy" - "Border color of the floating window.") -(defvar exwm-floating--border-pixel nil - "Border pixel drawn around floating X windows.") +(defgroup exwm-floating nil + "Floating." + :version "25.3" + :group 'exwm) + +(defcustom exwm-floating-setup-hook nil + "Normal hook run when an X window has been made floating, in the +context of the corresponding buffer." + :type 'hook) + +(defcustom exwm-floating-exit-hook nil + "Normal hook run when an X window has exited floating state, in the +context of the corresponding buffer." + :type 'hook) + +(defcustom exwm-floating-border-color "navy" + "Border color of floating windows." + :type 'color) + +(defcustom exwm-floating-border-width 1 + "Border width of floating windows." + :type 'integer) + (defvar exwm-floating--border-colormap nil "Colormap used by the border pixel. This is also used by X window containers.") - -(defvar exwm-floating-setup-hook nil - "Normal hook run when an X window has been made floating, in the -context of the corresponding buffer.") -(defvar exwm-floating-exit-hook nil - "Normal hook run when an X window has exited floating state, in the -context of the corresponding buffer.") +(defvar exwm-floating--border-pixel nil + "Border pixel drawn around floating X windows.") ;; Cursors for moving/resizing a window (defvar exwm-floating--cursor-move nil) @@ -57,6 +70,18 @@ context of the corresponding buffer.") (defvar exwm-floating--cursor-bottom-left nil) (defvar exwm-floating--cursor-left nil) +(defvar exwm-floating--moveresize-calculate nil + "Calculate move/resize parameters [buffer event-mask x y width height].") + +(defvar exwm-workspace--current) +(defvar exwm-workspace--workareas) +(declare-function exwm-layout--hide "exwm-layout.el" (id)) +(declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) +(declare-function exwm-layout--refresh "exwm-layout.el" ()) +(declare-function exwm-layout--show "exwm-layout.el" (id &optional window)) +(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") +(declare-function exwm-workspace--position "exwm-workspace.el" (frame)) + (defun exwm-floating--set-allowed-actions (id tilling) "Set _NET_WM_ALLOWED_ACTIONS." (xcb:+request exwm--connection @@ -74,16 +99,6 @@ context of the corresponding buffer.") xcb:Atom:_NET_WM_ACTION_CHANGE_DESKTOP xcb:Atom:_NET_WM_ACTION_CLOSE))))) -(defvar exwm-workspace--current) -(defvar exwm-workspace--workareas) - -(declare-function exwm-layout--refresh "exwm-layout.el" ()) -(declare-function exwm-layout--show "exwm-layout.el" (id &optional window)) -(declare-function exwm-layout--hide "exwm-layout.el" (id)) -(declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) -(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") -(declare-function exwm-workspace--position "exwm-workspace.el" (frame)) - (defun exwm-floating--set-floating (id) "Make window ID floating." (let ((window (get-buffer-window (exwm--id->buffer id)))) @@ -331,16 +346,16 @@ context of the corresponding buffer.") (run-hooks 'exwm-floating-exit-hook))) ;;;###autoload -(defun exwm-floating-toggle-floating () +(cl-defun exwm-floating-toggle-floating () "Toggle the current window between floating and non-floating states." (interactive) + (unless (derived-mode-p 'exwm-mode) + (cl-return-from 'exwm-floating-toggle-floating)) (with-current-buffer (window-buffer) (if exwm--floating-frame (exwm-floating--unset-floating exwm--id) (exwm-floating--set-floating exwm--id)))) -(declare-function exwm-layout--set-state "exwm-layout.el" (id state)) - ;;;###autoload (defun exwm-floating-hide () "Hide the current floating X window (which would show again when selected)." @@ -350,14 +365,6 @@ context of the corresponding buffer.") (exwm-layout--hide exwm--id) (select-frame-set-input-focus exwm-workspace--current))) -(define-obsolete-function-alias 'exwm-floating-hide-mode-line - 'exwm-layout-hide-mode-line "25.1" "Hide mode-line of a floating frame.") -(define-obsolete-function-alias 'exwm-floating-show-mode-line - 'exwm-layout-show-mode-line "25.1" "Show mode-line of a floating frame.") - -(defvar exwm-floating--moveresize-calculate nil - "Calculate move/resize parameters [buffer event-mask x y width height].") - (defun exwm-floating--start-moveresize (id &optional type) "Start move/resize." (let ((buffer-or-id (or (exwm--id->buffer id) id)) diff --git a/exwm-input.el b/exwm-input.el index eaddf6b252..e536a14144 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -38,32 +38,119 @@ (require 'xcb-keysyms) (require 'exwm-core) -(defvar exwm-input-move-event 's-down-mouse-1 - "Emacs event to start moving a window.") -(defvar exwm-input-resize-event 's-down-mouse-3 - "Emacs event to start resizing a window.") +(defgroup exwm-input nil + "Input." + :version "25.3" + :group 'exwm) -(defvar exwm-input--timestamp-window nil) -(defvar exwm-input--timestamp-atom nil) -(defvar exwm-input--timestamp-callback nil) +(defcustom exwm-input-prefix-keys + '(?\C-c ?\C-x ?\C-u ?\C-h ?\M-x ?\M-` ?\M-& ?\M-:) + "List of prefix keys EXWM should forward to Emacs when in line-mode." + :type '(repeat key-sequence) + :get (lambda (symbol) + (mapcar #'vector (default-value symbol))) + :set (lambda (symbol value) + (set symbol (mapcar (lambda (i) + (if (sequencep i) + (aref i 0) + i)) + value)))) + +(defcustom exwm-input-move-event 's-down-mouse-1 + "Emacs event to start moving a window." + :type 'key-sequence + :get (lambda (symbol) + (let ((value (default-value symbol))) + (if (mouse-event-p value) + value + (vector value)))) + :set (lambda (symbol value) + (set symbol (if (sequencep value) + (aref value 0) + value)))) + +(defcustom exwm-input-resize-event 's-down-mouse-3 + "Emacs event to start resizing a window." + :type 'key-sequence + :get (lambda (symbol) + (let ((value (default-value symbol))) + (if (mouse-event-p value) + value + (vector value)))) + :set (lambda (symbol value) + (set symbol (if (sequencep value) + (aref value 0) + value)))) + +(defcustom exwm-input-line-mode-passthrough nil + "Non-nil makes 'line-mode' forwards all events to Emacs." + :type 'boolean) -(defvar exwm-workspace--current) -(defvar exwm-workspace--switch-history-outdated) -(defvar exwm-workspace-current-index) -(defvar exwm-workspace--minibuffer) -(defvar exwm-workspace--list) +;; Input focus update requests should be accumulated for a short time +;; interval so that only the last one need to be processed. This not +;; improves the overall performance, but avoids the problem of input +;; focus loop, which is a result of the interaction with Emacs frames. +;; +;; FIXME: The time interval is hard to decide and perhaps machine-dependent. +;; A value too small can cause redundant updates of input focus, +;; and even worse, dead loops. OTOH a large value would bring +;; laggy experience. +(defconst exwm-input--update-focus-interval 0.01 + "Time interval (in seconds) for accumulating input focus update requests.") + +(defvar exwm-input--during-command nil + "Indicate whether between `pre-command-hook' and `post-command-hook'.") (defvar exwm-input--global-keys nil "Global key bindings.") + (defvar exwm-input--global-prefix-keys nil "List of prefix keys of global key bindings.") -(defvar exwm-input-prefix-keys - '(?\C-c ?\C-x ?\C-u ?\C-h ?\M-x ?\M-` ?\M-& ?\M-:) - "List of prefix keys EXWM should forward to Emacs when in line-mode.") + +(defvar exwm-input--line-mode-cache nil "Cache for incomplete key sequence.") + +(defvar exwm-input--local-simulation-keys nil + "Whether simulation keys are local.") + (defvar exwm-input--simulation-keys nil "Simulation keys in line-mode.") + (defvar exwm-input--simulation-prefix-keys nil "List of prefix keys of simulation keys in line-mode.") +(defvar exwm-input--temp-line-mode nil + "Non-nil indicates it's in temporary line-mode for char-mode.") + +(defvar exwm-input--timestamp-atom nil) + +(defvar exwm-input--timestamp-callback nil) + +(defvar exwm-input--timestamp-window nil) + +(defvar exwm-input--update-focus-defer-timer nil "Timer for polling the lock.") + +(defvar exwm-input--update-focus-lock nil + "Lock for solving input focus update contention.") + +(defvar exwm-input--update-focus-timer nil + "Timer for deferring the update of input focus.") + +(defvar exwm-input--update-focus-window nil "The (Emacs) window to be focused. +This value should always be overwritten.") + +(defvar exwm-workspace--current) +(declare-function exwm-floating--do-moveresize "exwm-floating.el" + (data _synthetic)) +(declare-function exwm-floating--start-moveresize "exwm-floating.el" + (id &optional type)) +(declare-function exwm-floating--stop-moveresize "exwm-floating.el" + (&rest _args)) +(declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) (declare-function exwm-layout--show "exwm-layout.el" (id &optional window)) +(declare-function exwm-workspace--client-p "exwm-workspace.el" + (&optional frame)) +(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") +(declare-function exwm-workspace--workspace-p "exwm-workspace.el" (workspace)) +(declare-function exwm-workspace-switch "exwm-workspace.el" + (frame-or-index &optional force)) (defun exwm-input--set-focus (id) "Set input focus to window ID in a proper way." @@ -185,13 +272,6 @@ ARGS are additional arguments to CALLBACK." (let ((exwm-input--global-prefix-keys nil)) (exwm-input--update-global-prefix-keys))) -(declare-function exwm-workspace--client-p "exwm-workspace.el" - (&optional frame)) - -(defvar exwm-input--update-focus-window nil "The (Emacs) window to be focused. - -This value should always be overwritten.") - (defun exwm-input--on-buffer-list-update () "Run in `buffer-list-update-hook' to track input focus." (when (and (not (eq this-command #'handle-switch-frame)) @@ -205,24 +285,6 @@ This value should always be overwritten.") (setq exwm-input--update-focus-window (selected-window)) (exwm-input--update-focus-defer))) -;; Input focus update requests should be accumulated for a short time -;; interval so that only the last one need to be processed. This not -;; improves the overall performance, but avoids the problem of input -;; focus loop, which is a result of the interaction with Emacs frames. -;; -;; FIXME: The time interval is hard to decide and perhaps machine-dependent. -;; A value too small can cause redundant updates of input focus, -;; and even worse, dead loops. OTOH a large value would bring -;; laggy experience. -(defconst exwm-input--update-focus-interval 0.01 - "Time interval (in seconds) for accumulating input focus update requests.") - -(defvar exwm-input--update-focus-lock nil - "Lock for solving input focus update contention.") -(defvar exwm-input--update-focus-defer-timer nil "Timer for polling the lock.") -(defvar exwm-input--update-focus-timer nil - "Timer for deferring the update of input focus.") - (defun exwm-input--update-focus-defer () "Defer updating input focus." (when exwm-input--update-focus-defer-timer @@ -240,12 +302,6 @@ This value should always be overwritten.") #'exwm-input--update-focus-commit exwm-input--update-focus-window)))) -(declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) -(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") -(declare-function exwm-workspace-switch "exwm-workspace.el" - (frame-or-index &optional force)) -(declare-function exwm-workspace--workspace-p "exwm-workspace.el" (workspace)) - (defun exwm-input--update-focus-commit (window) "Commit updating input focus." (setq exwm-input--update-focus-lock t) @@ -317,10 +373,6 @@ This value should always be overwritten.") :window exwm--root :data (or id xcb:Window:None)))) -(declare-function exwm-floating--start-moveresize "exwm-floating.el" - (id &optional type)) -(declare-function exwm-workspace--position "exwm-workspace.el" (frame)) - (defun exwm-input--on-ButtonPress (data _synthetic) "Handle ButtonPress event." (let ((obj (make-instance 'xcb:ButtonPress)) @@ -419,7 +471,10 @@ This value should always be overwritten.") ;;;###autoload (defun exwm-input-set-key (key command) - "Set a global key binding." + "Set a global key binding. + +The new key binding only takes effect in real time when this command is +called interactively. Only invoke it non-interactively in configuration." (interactive "KSet key globally: \nCSet key %s to command: ") (global-set-key key command) (cl-pushnew key exwm-input--global-keys) @@ -437,22 +492,6 @@ This value should always be overwritten.") (setq unread-command-events (append unread-command-events `((t . ,event))))))) -(defvar exwm-input-command-whitelist nil - "A list of commands that when active all keys should be forwarded to Emacs.") -(make-obsolete-variable 'exwm-input-command-whitelist - "This variable can be safely removed." "25.1") - -(defvar exwm-input--during-command nil - "Indicate whether between `pre-command-hook' and `post-command-hook'.") - -(defvar exwm-input-line-mode-passthrough nil - "Non-nil makes 'line-mode' forwards all events to Emacs.") - -(defvar exwm-input--line-mode-cache nil "Cache for incomplete key sequence.") - -(defvar exwm-input--temp-line-mode nil - "Non-nil indicates it's in temporary line-mode for char-mode.") - (cl-defun exwm-input--translate (key) (let (translation) (dolist (map (list input-decode-map @@ -606,7 +645,8 @@ This value should always be overwritten.") ;;;###autoload (defun exwm-input-grab-keyboard (&optional id) "Switch to line-mode." - (interactive (list (exwm--buffer->id (window-buffer)))) + (interactive (list (when (derived-mode-p 'exwm-mode) + (exwm--buffer->id (window-buffer))))) (when id (with-current-buffer (exwm--id->buffer id) (exwm-input--grab-keyboard id) @@ -617,7 +657,8 @@ This value should always be overwritten.") ;;;###autoload (defun exwm-input-release-keyboard (&optional id) "Switch to char-mode." - (interactive (list (exwm--buffer->id (window-buffer)))) + (interactive (list (when (derived-mode-p 'exwm-mode) + (exwm--buffer->id (window-buffer))))) (when id (with-current-buffer (exwm--id->buffer id) (exwm-input--release-keyboard id) @@ -628,7 +669,8 @@ This value should always be overwritten.") ;;;###autoload (defun exwm-input-toggle-keyboard (&optional id) "Toggle between 'line-mode' and 'char-mode'." - (interactive (list (exwm--buffer->id (window-buffer)))) + (interactive (list (when (derived-mode-p 'exwm-mode) + (exwm--buffer->id (window-buffer))))) (when id (with-current-buffer (exwm--id->buffer id) (if exwm--keyboard-grabbed @@ -664,9 +706,14 @@ This value should always be overwritten.") (xcb:flush exwm--connection))) ;;;###autoload -(defun exwm-input-send-next-key (times) - "Send next key to client window." +(cl-defun exwm-input-send-next-key (times) + "Send next key to client window. + +EXWM will prompt for the key to send. This command can be prefixed to send +multiple keys." (interactive "p") + (unless (derived-mode-p 'exwm-mode) + (cl-return-from 'exwm-input-send-next-key)) (when (> times 12) (setq times 12)) (let (key keys) (dotimes (i times) @@ -686,9 +733,6 @@ This value should always be overwritten.") ;; (unless (listp last-input-event) ;not a key event ;; (exwm-input--fake-key last-input-event))) -(defvar exwm-input--local-simulation-keys nil - "Whether simulation keys are local.") - (defun exwm-input--update-simulation-prefix-keys () "Update the list of prefix keys of simulation keys." (setq exwm-input--simulation-prefix-keys nil) @@ -718,9 +762,13 @@ Its usage is the same with `exwm-input-set-simulation-keys'." (exwm-input-set-simulation-keys simulation-keys))) ;;;###autoload -(defun exwm-input-send-simulation-key (times) - "Fake a key event according to last input key sequence." +(cl-defun exwm-input-send-simulation-key (times) + "Fake a key event according to the last input key sequence. + +Sending multiple fake keys at once is only supported by Emacs 27 and later." (interactive "p") + (unless (derived-mode-p 'exwm-mode) + (cl-return-from 'exwm-input-send-simulation-key)) (let ((pair (assoc (this-single-command-keys) exwm-input--simulation-keys))) (when pair (setq pair (cdr pair)) @@ -738,11 +786,6 @@ Its usage is the same with `exwm-input-set-simulation-keys'." "Run in `post-command-hook'." (setq exwm-input--during-command nil)) -(declare-function exwm-floating--stop-moveresize "exwm-floating.el" - (&rest _args)) -(declare-function exwm-floating--do-moveresize "exwm-floating.el" - (data _synthetic)) - (defun exwm-input--init () "Initialize the keyboard module." ;; Refresh keyboard mapping diff --git a/exwm-layout.el b/exwm-layout.el index cda942e47d..a98e261117 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -27,8 +27,35 @@ (require 'exwm-core) -(defvar exwm-floating-border-width) -(defvar exwm-workspace--id-struts-alist) +(defgroup exwm-layout nil + "Layout." + :version "25.3" + :group 'exwm) + +(defcustom exwm-layout-show-all-buffers nil + "Non-nil to allow switching to buffers on other workspaces." + :type 'boolean) + +(defvar exwm-layout--other-buffer-exclude-buffers nil + "List of buffers that should not be selected by `other-buffer'.") + +(defvar exwm-layout--other-buffer-exclude-exwm-mode-buffers nil + "When non-nil, prevent EXWM buffers from being selected by `other-buffer'.") + +(defvar exwm-layout--timer nil "Timer used to track echo area changes.") + +(defvar exwm-workspace--current) +(declare-function exwm-input-grab-keyboard "exwm-input.el") +(declare-function exwm-input-release-keyboard "exwm-input.el") +(declare-function exwm-workspace--client-p "exwm-workspace.el" + (&optional frame)) +(declare-function exwm-workspace--current-height "exwm-workspace.el") +(declare-function exwm-workspace--current-width "exwm-workspace.el") +(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") +(declare-function exwm-workspace--workspace-p "exwm-workspace.el" + (workspace)) +(declare-function exwm-workspace-move-window "exwm-workspace.el" + (frame-or-index &optional id)) (defun exwm-layout--set-state (id state) "Set WM_STATE." @@ -99,23 +126,14 @@ (exwm-layout--set-state id xcb:icccm:WM_STATE:IconicState) (xcb:flush exwm--connection)))) -(defvar exwm-workspace--current) - -(declare-function exwm-input-grab-keyboard "exwm-input.el") -(declare-function exwm-input-release-keyboard "exwm-input.el") -(declare-function exwm-workspace--current-height "exwm-workspace.el") -(declare-function exwm-workspace--current-width "exwm-workspace.el") -(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") -(declare-function exwm-workspace-move-window "exwm-workspace.el" - (frame-or-index &optional id)) - ;;;###autoload -(defun exwm-layout-set-fullscreen (&optional id) +(cl-defun exwm-layout-set-fullscreen (&optional id) "Make window ID fullscreen." (interactive) + (unless (and (or id (derived-mode-p 'exwm-mode)) + (not (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state))) + (cl-return-from 'exwm-layout-set-fullscreen)) (with-current-buffer (if id (exwm--id->buffer id) (window-buffer)) - (when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) - (user-error "Already in full-screen mode")) ;; Expand the X window to fill the whole screen. ;; Rationale: Floating X windows may not be positioned at (0, 0) ;; due to the extra border. @@ -139,12 +157,13 @@ (call-interactively #'exwm-input-release-keyboard))) ;;;###autoload -(defun exwm-layout-unset-fullscreen (&optional id) +(cl-defun exwm-layout-unset-fullscreen (&optional id) "Restore window from fullscreen state." (interactive) + (unless (and (or id (derived-mode-p 'exwm-mode)) + (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) + (cl-return-from 'exwm-layout-unset-fullscreen)) (with-current-buffer (if id (exwm--id->buffer id) (window-buffer)) - (unless (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) - (user-error "Not in full-screen mode")) (if exwm--floating-frame (exwm-layout--show exwm--id (frame-root-window exwm--floating-frame)) (xcb:+request exwm--connection @@ -165,21 +184,17 @@ (call-interactively #'exwm-input-grab-keyboard))) ;;;###autoload -(defun exwm-layout-toggle-fullscreen (&optional id) +(cl-defun exwm-layout-toggle-fullscreen (&optional id) "Toggle fullscreen mode." (interactive (list (exwm--buffer->id (window-buffer)))) + (unless (or id (derived-mode-p 'exwm-mode)) + (cl-return-from 'exwm-layout-toggle-fullscreen)) (when id (with-current-buffer (exwm--id->buffer id) (if (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) (exwm-reset) (exwm-layout-set-fullscreen id))))) -(defvar exwm-layout--other-buffer-exclude-exwm-mode-buffers nil - "When non-nil, prevent EXWM buffers from being selected by `other-buffer'.") - -(defvar exwm-layout--other-buffer-exclude-buffers nil - "List of buffers that should not be selected by `other-buffer'.") - (defun exwm-layout--other-buffer-predicate (buffer) "Return non-nil when the BUFFER may be displayed in selected frame. @@ -201,11 +216,6 @@ selected by `other-buffer'." ;; Do not select if already shown in some window. (not (get-buffer-window buffer t))))) -(defvar exwm-layout-show-all-buffers nil - "Non-nil to allow switching to buffers on other workspaces.") -(declare-function exwm-workspace--workspace-p "exwm-workspace.el" - (workspace)) - (defun exwm-layout--set-client-list-stacking () "Set _NET_CLIENT_LIST_STACKING." (let (id clients-floating clients clients-iconic clients-other) @@ -301,9 +311,6 @@ selected by `other-buffer'." (exwm-layout--set-client-list-stacking) (xcb:flush exwm--connection)))) -(declare-function exwm-workspace--client-p "exwm-workspace.el" - (&optional frame)) - (defun exwm-layout--on-minibuffer-setup () "Refresh layout when minibuffer grows." (unless (exwm-workspace--client-p) @@ -479,8 +486,6 @@ See also `exwm-layout-enlarge-window'." (exwm-layout-hide-mode-line) (exwm-layout-show-mode-line)))) -(defvar exwm-layout--timer nil "Timer used to track echo area changes.") - (defun exwm-layout--init () "Initialize layout module." ;; Auto refresh layout diff --git a/exwm-manage.el b/exwm-manage.el index 0a9d92fc02..b983ebf80d 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -28,17 +28,66 @@ (require 'exwm-core) -(defvar exwm-manage-force-tiling nil +(defgroup exwm-manage nil + "Manage." + :version "25.3" + :group 'exwm) + +(defcustom exwm-manage-finish-hook nil + "Normal hook run after a window is just managed, in the context of the +corresponding buffer." + :type 'hook) + +(defcustom exwm-manage-force-tiling nil "Non-nil to force managing all X windows in tiling layout. +You can still make the X windows floating afterwards." + :type 'boolean) -You can still make the X windows floating afterwards.") +;; FIXME: Make the following values as small as possible. +(defconst exwm-manage--height-delta-min 5) +(defconst exwm-manage--width-delta-min 5) -(defvar exwm-manage-finish-hook nil - "Normal hook run after a window is just managed, in the context of the -corresponding buffer.") +;; The _MOTIF_WM_HINTS atom (see for more details) +;; It's currently only used in 'exwm-manage' module +(defvar exwm-manage--_MOTIF_WM_HINTS nil "_MOTIF_WM_HINTS atom.") (defvar exwm-manage--desktop nil "The desktop X window.") +(defvar exwm-manage--frame-outer-id-list nil + "List of window-outer-id's of all frames.") + +(defvar exwm-manage--ping-lock nil + "Non-nil indicates EXWM is pinging a window.") + +(defvar exwm-manage-ping-timeout 3 "Seconds to wait before killing a client.") + +(defvar exwm-workspace--current) +(defvar exwm-workspace--id-struts-alist) +(defvar exwm-workspace--list) +(defvar exwm-workspace--switch-history-outdated) +(defvar exwm-workspace-current-index) +(declare-function exwm--update-class "exwm.el" (id &optional force)) +(declare-function exwm--update-hints "exwm.el" (id &optional force)) +(declare-function exwm--update-normal-hints "exwm.el" (id &optional force)) +(declare-function exwm--update-protocols "exwm.el" (id &optional force)) +(declare-function exwm--update-struts "exwm.el" (id)) +(declare-function exwm--update-title "exwm.el" (id)) +(declare-function exwm--update-transient-for "exwm.el" (id &optional force)) +(declare-function exwm--update-window-type "exwm.el" (id &optional force)) +(declare-function exwm-floating--set-floating "exwm-floating.el" (id)) +(declare-function exwm-floating--unset-floating "exwm-floating.el" (id)) +(declare-function exwm-input-grab-keyboard "exwm-input.el") +(declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) +(declare-function exwm-workspace--count "exwm-workspace.el" ()) +(declare-function exwm-workspace--current-height "exwm-workspace.el") +(declare-function exwm-workspace--current-width "exwm-workspace.el") +(declare-function exwm-workspace--set-desktop "exwm-workspace.el" (id)) +(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame)) +(declare-function exwm-workspace--update-struts "exwm-workspace.el" ()) +(declare-function exwm-workspace--update-workareas "exwm-workspace.el" ()) +(declare-function exwm-workspace-move-window "exwm-workspace.el" + (frame-or-index &optional id)) + (defun exwm-manage--update-geometry (id &optional force) "Update window geometry." (with-current-buffer (exwm--id->buffer id) @@ -58,10 +107,6 @@ corresponding buffer.") (when reply (setq exwm--ewmh-state (append (slot-value reply 'value) nil))))))) -;; The _MOTIF_WM_HINTS atom (see for more details) -;; It's currently only used in 'exwm-manage' module -(defvar exwm-manage--_MOTIF_WM_HINTS nil "_MOTIF_WM_HINTS atom.") - (defun exwm-manage--update-mwm-hints (id &optional force) "Update _MOTIF_WM_HINTS." (with-current-buffer (exwm--id->buffer id) @@ -92,29 +137,6 @@ corresponding buffer.") :window exwm--root :data (vconcat (mapcar #'car exwm--id-buffer-alist))))) -(defvar exwm-workspace--current) -(defvar exwm-workspace--switch-history-outdated) -(defvar exwm-workspace-current-index) -(defvar exwm-workspace--workareas) - -(declare-function exwm--update-window-type "exwm.el" (id &optional force)) -(declare-function exwm--update-class "exwm.el" (id &optional force)) -(declare-function exwm--update-transient-for "exwm.el" (id &optional force)) -(declare-function exwm--update-normal-hints "exwm.el" (id &optional force)) -(declare-function exwm--update-title "exwm.el" (id)) -(declare-function exwm--update-hints "exwm.el" (id &optional force)) -(declare-function exwm--update-protocols "exwm.el" (id &optional force)) -(declare-function exwm--update-struts "exwm.el" (id)) -(declare-function exwm-floating--set-floating "exwm-floating.el" (id)) -(declare-function exwm-floating--unset-floating "exwm-floating.el" (id)) -(declare-function exwm-input-grab-keyboard "exwm-input.el") -(declare-function exwm-workspace--current-height "exwm-workspace.el") -(declare-function exwm-workspace--current-width "exwm-workspace.el") -(declare-function exwm-workspace--set-desktop "exwm-workspace.el" (id)) -(declare-function exwm-workspace--count "exwm-workspace.el" ()) -(declare-function exwm-workspace-move-window "exwm-workspace.el" - (frame-or-index &optional id)) - (defun exwm-manage--manage-window (id) "Manage window ID." (exwm--log "Try to manage #x%x" id) @@ -251,13 +273,6 @@ corresponding buffer.") (exwm-layout-set-fullscreen id)) (run-hooks 'exwm-manage-finish-hook))))) -(defvar exwm-workspace--id-struts-alist) -(defvar exwm-workspace--list) - -(declare-function exwm-workspace--update-struts "exwm-workspace.el" ()) -(declare-function exwm-workspace--update-workareas "exwm-workspace.el" ()) -(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame)) - (defun exwm-manage--unmanage-window (id &optional withdraw-only) "Unmanage window ID. @@ -350,10 +365,6 @@ manager is shutting down." (xcb:flush exwm--connection) (exwm-manage--manage-window i))))))) -(defvar exwm-manage--ping-lock nil - "Non-nil indicates EXWM is pinging a window.") -(defvar exwm-manage-ping-timeout 3 "Seconds to wait before killing a client.") - (defun exwm-manage--kill-buffer-query-function () "Run in `kill-buffer-query-functions'." (catch 'return @@ -447,13 +458,6 @@ Would you like to kill it? " (xcb:+request exwm--connection ,request)))) (xcb:flush exwm--connection))) -;; FIXME: Make the following values as small as possible. -(defconst exwm-manage--width-delta-min 5) -(defconst exwm-manage--height-delta-min 5) - -(defvar exwm-manage--frame-outer-id-list nil - "List of window-outer-id's of all frames.") - (defun exwm-manage--add-frame (frame) "Run in `after-make-frame-functions'." (when (display-graphic-p frame) @@ -560,8 +564,6 @@ border-width: %d; sibling: #x%x; stack-mode: %d" :stack-mode stack-mode))))))) (xcb:flush exwm--connection)) -(declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) - (defun exwm-manage--on-MapRequest (data _synthetic) "Handle MapRequest event." (let ((obj (make-instance 'xcb:MapRequest))) diff --git a/exwm-randr.el b/exwm-randr.el index 74938d6b71..f49073c455 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -50,19 +50,42 @@ (require 'xcb-randr) (require 'exwm-core) -(defvar exwm-randr-workspace-output-plist nil) +(defgroup exwm-randr nil + "RandR." + :version "25.3" + :group 'exwm) -(defvar exwm-randr-refresh-hook nil - "Normal hook run when the RandR module just refreshed.") +(defcustom exwm-randr-refresh-hook nil + "Normal hook run when the RandR module just refreshed." + :type 'hook) + +(defcustom exwm-randr-screen-change-hook nil + "Normal hook run when screen changes." + :type 'hook) + +(defcustom exwm-randr-workspace-output-plist nil + "Plist mapping workspace to output. + +If an output is not available, the workspaces mapped to it are displayed on +the primary output until it becomes available. Unspecified workspaces are +all mapped to the primary output. For example, with the following value +workspace other than 1 and 3 would always be displayed on the primary output +where workspace 1 and 3 would be displayed on their corresponding output +whenever the outputs are available. + + '(1 \"HDMI-1\" 3 \"DP-1\") + +The outputs available can be identified by running the 'xrandr' utility with +the first one in result being the primary output." + :type '(plist :key-type integer :value-type string)) (defvar exwm-workspace--fullscreen-frame-count) (defvar exwm-workspace--list) - (declare-function exwm-workspace--count "exwm-workspace.el") +(declare-function exwm-workspace--set-desktop-geometry "exwm-workspace.el" ()) (declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame)) -(declare-function exwm-workspace--update-workareas "exwm-workspace.el" ()) (declare-function exwm-workspace--show-minibuffer "exwm-workspace.el" ()) -(declare-function exwm-workspace--set-desktop-geometry "exwm-workspace.el" ()) +(declare-function exwm-workspace--update-workareas "exwm-workspace.el" ()) (defun exwm-randr--refresh () "Refresh workspaces according to the updated RandR info." @@ -122,9 +145,6 @@ (xcb:flush exwm--connection) (run-hooks 'exwm-randr-refresh-hook)))) -(defvar exwm-randr-screen-change-hook nil - "Normal hook run when screen changes.") - (defun exwm-randr--init () "Initialize RandR extension and EXWM RandR module." (if (= 0 (slot-value (xcb:get-extension-data exwm--connection 'xcb:randr) diff --git a/exwm-systemtray.el b/exwm-systemtray.el index 33e97623c0..5377ef8ed6 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -50,22 +50,38 @@ (owner :initarg :owner :type xcb:WINDOW)) ;new slot :documentation "A systemtray client message.") -;; GTK icons require at least 16 pixels to show normally. -(defconst exwm-systemtray--icon-min-size 16 "Minimum icon size.") +(defgroup exwm-systemtray nil + "System tray." + :version "25.3" + :group 'exwm) + +(defcustom exwm-systemtray-height nil + "System tray height. -(defvar exwm-systemtray-height nil "System tray height. +You shall use the default value if using auto-hide minibuffer." + :type 'integer) -You shall use the default value if using auto-hide minibuffer.") +(defcustom exwm-systemtray-icon-gap 2 + "Gap between icons." + :type 'integer) -(defvar exwm-systemtray-icon-gap 2 "Gap between icons.") +;; GTK icons require at least 16 pixels to show normally. +(defconst exwm-systemtray--icon-min-size 16 "Minimum icon size.") (defvar exwm-systemtray--connection nil "The X connection.") + +(defvar exwm-systemtray--embedder nil "The embedder window.") + (defvar exwm-systemtray--list nil "The icon list.") + (defvar exwm-systemtray--selection-owner-window nil "The selection owner window.") -(defvar exwm-systemtray--embedder nil "The embedder window.") (defvar exwm-workspace--current) +(defvar exwm-workspace--minibuffer) +(defvar exwm-workspace--workareas) +(defvar exwm-workspace-current-index) +(defvar xcb:Atom:_NET_SYSTEM_TRAY_S0) (declare-function exwm-workspace--current-height "exwm-workspace.el") (declare-function exwm-workspace--current-width "exwm-workspace.el") (declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") @@ -304,9 +320,6 @@ You shall use the default value if using auto-hide minibuffer.") :event (xcb:marshal obj exwm-systemtray--connection)))) (xcb:flush exwm-systemtray--connection)) -(defvar exwm-workspace--workareas) -(defvar exwm-workspace-current-index) - (defun exwm-systemtray--on-workspace-switch () "Reparent/Refresh the system tray in `exwm-workspace-switch-hook'." (unless (exwm-workspace--minibuffer-own-frame-p) @@ -339,9 +352,6 @@ You shall use the default value if using auto-hide minibuffer.") (defalias 'exwm-systemtray--on-struts-update #'exwm-systemtray--on-randr-refresh) -(defvar xcb:Atom:_NET_SYSTEM_TRAY_S0) -(defvar exwm-workspace--minibuffer) - (cl-defun exwm-systemtray--init () "Initialize system tray module." (cl-assert (not exwm-systemtray--connection)) diff --git a/exwm-workspace.el b/exwm-workspace.el index 2917c6910b..b9cda25160 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -29,16 +29,110 @@ (require 'exwm-core) -(defvar exwm-manage--desktop) +(defgroup exwm-workspace nil + "Workspace." + :version "25.3" + :group 'exwm) -(defvar exwm-workspace-number 1 "Initial number of workspaces.") -(defvar exwm-workspace--list nil "List of all workspaces (Emacs frames).") -(defvar exwm-workspace--current nil "Current active workspace.") -(defvar exwm-workspace-current-index 0 "Index of current active workspace.") -(defvar exwm-workspace-index-map #'number-to-string +(defcustom exwm-workspace-switch-hook nil + "Normal hook run after switching workspace." + :type 'hook) + +(defcustom exwm-workspace-list-change-hook nil + "Normal hook run when the workspace list is changed (workspace added, +deleted, moved, etc)." + :type 'hook) + +(defcustom exwm-workspace-show-all-buffers nil + "Non-nil to show buffers on other workspaces." + :type 'boolean) + +(defcustom exwm-workspace-number 1 + "Initial number of workspaces." + :type 'integer) + +(defcustom exwm-workspace-index-map #'number-to-string "Function for mapping a workspace index to a string for display. -By default `number-to-string' is applied which yields 0 1 2 ... .") +By default `number-to-string' is applied which yields 0 1 2 ... ." + :type 'function) + +(defcustom exwm-workspace-minibuffer-position nil + "Position of the minibuffer frame." + :type '(choice (const :tag "Bottom (fixed)" nil) + (const :tag "Bottom (auto-hide)" bottom) + (const :tag "Top (auto-hide)" top))) + +(defcustom exwm-workspace-display-echo-area-timeout 1 + "Timeout for displaying echo area." + :type 'integer) + +(defcustom exwm-workspace-switch-create-limit 10 + "Number of workspaces `exwm-workspace-switch-create' allowed to create +each time." + :type 'integer) + +(defvar exwm-workspace-current-index 0 "Index of current active workspace.") + +(defvar exwm-workspace--attached-minibuffer-height 0 + "Height (in pixel) of the attached minibuffer. + +If the minibuffer is detached, this value is 0.") + +(defvar exwm-workspace--client nil + "The 'client' frame parameter of emacsclient frames.") + +(defvar exwm-workspace--create-silently nil + "When non-nil workspaces are created in the background (not switched to). + +Please manually run the hook `exwm-workspace-list-change-hook' afterwards.") + +(defvar exwm-workspace--current nil "Current active workspace.") + +(defvar exwm-workspace--display-echo-area-timer nil + "Timer for auto-hiding echo area.") + +(defvar exwm-workspace--id-struts-alist nil "Alist of X window and struts.") + +(defvar exwm-workspace--fullscreen-frame-count 0 + "Count the fullscreen workspace frames.") + +(defvar exwm-workspace--list nil "List of all workspaces (Emacs frames).") + +(defvar exwm-workspace--minibuffer nil + "The minibuffer frame shared among all frames.") + +(defvar exwm-workspace--prompt-add-allowed nil + "Non-nil to allow adding workspace from the prompt.") + +(defvar exwm-workspace--prompt-delete-allowed nil + "Non-nil to allow deleting workspace from the prompt.") + +(defvar exwm-workspace--struts nil "Areas occupied by struts.") + +(defvar exwm-workspace--switch-history nil + "History for `read-from-minibuffer' to interactively switch workspace.") + +(defvar exwm-workspace--switch-history-outdated nil + "Non-nil to indicate `exwm-workspace--switch-history' is outdated.") + +(defvar exwm-workspace--timer nil "Timer used to track echo area changes.") + +(defvar exwm-workspace--update-workareas-hook nil + "Normal hook run when workareas get updated.") + +(defvar exwm-workspace--workareas nil "Workareas (struts excluded).") + +(defvar exwm-input--during-command) +(defvar exwm-layout-show-all-buffers) +(defvar exwm-manage--desktop) +(declare-function exwm--exit "exwm.el") +(declare-function exwm-input--on-buffer-list-update "exwm-input.el" ()) +(declare-function exwm-layout--hide "exwm-layout.el" (id)) +(declare-function exwm-layout--other-buffer-predicate "exwm-layout.el" + (buffer)) +(declare-function exwm-layout--refresh "exwm-layout.el") +(declare-function exwm-layout--show "exwm-layout.el" (id &optional window)) (defsubst exwm-workspace--position (frame) "Retrieve index of given FRAME in workspace list. @@ -58,20 +152,6 @@ NIL if FRAME is not a workspace" "Return non-nil if FRAME is an emacsclient frame." (frame-parameter frame 'client)) -(defun exwm-workspace--workspace-from-frame-or-index (frame-or-index) - "Retrieve the workspace frame from FRAME-OR-INDEX." - (cond - ((framep frame-or-index) - (unless (exwm-workspace--position frame-or-index) - (user-error "[EXWM] Frame is not a workspace %S" frame-or-index)) - frame-or-index) - ((integerp frame-or-index) - (unless (and (<= 0 frame-or-index) - (< frame-or-index (exwm-workspace--count))) - (user-error "[EXWM] Workspace index out of range: %d" frame-or-index)) - (elt exwm-workspace--list frame-or-index)) - (t (user-error "[EXWM] Invalid workspace: %s" frame-or-index)))) - (defvar exwm-workspace--switch-map (let ((map (make-sparse-keymap))) (define-key map [t] (lambda () (interactive))) @@ -98,10 +178,19 @@ NIL if FRAME is not a workspace" map) "Keymap used for interactively switch workspace.") -(defvar exwm-workspace--switch-history nil - "History for `read-from-minibuffer' to interactively switch workspace.") -(defvar exwm-workspace--switch-history-outdated nil - "Non-nil to indicate `exwm-workspace--switch-history' is outdated.") +(defun exwm-workspace--workspace-from-frame-or-index (frame-or-index) + "Retrieve the workspace frame from FRAME-OR-INDEX." + (cond + ((framep frame-or-index) + (unless (exwm-workspace--position frame-or-index) + (user-error "[EXWM] Frame is not a workspace %S" frame-or-index)) + frame-or-index) + ((integerp frame-or-index) + (unless (and (<= 0 frame-or-index) + (< frame-or-index (exwm-workspace--count))) + (user-error "[EXWM] Workspace index out of range: %d" frame-or-index)) + (elt exwm-workspace--list frame-or-index)) + (t (user-error "[EXWM] Invalid workspace: %s" frame-or-index)))) (defun exwm-workspace--prompt-for-workspace (&optional prompt) "Prompt for a workspace, returning the workspace frame." @@ -117,15 +206,6 @@ NIL if FRAME is not a workspace" :test #'equal))) (elt exwm-workspace--list workspace-idx))) -(defvar exwm-workspace--prompt-add-allowed nil - "Non-nil to allow adding workspace from the prompt.") -(defvar exwm-workspace--prompt-delete-allowed nil - "Non-nil to allow deleting workspace from the prompt.") -(defvar exwm-workspace--create-silently nil - "When non-nil workspaces are created in the background (not switched to). - -Please manually run the hook `exwm-workspace-list-change-hook' afterwards.") - (defun exwm-workspace--prompt-add () "Add workspace from the prompt." (interactive) @@ -182,20 +262,6 @@ Please manually run the hook `exwm-workspace-list-change-hook' afterwards.") sequence "")) sequence))))) -(defvar exwm-workspace-show-all-buffers nil - "Non-nil to show buffers on other workspaces.") -(defvar exwm-workspace--minibuffer nil - "The minibuffer frame shared among all frames.") -(defvar exwm-workspace-minibuffer-position nil - "Position of the minibuffer frame. - -Value nil means to use the default position which is fixed at bottom, while -'top and 'bottom mean to use an auto-hiding minibuffer.") -(defvar exwm-workspace-display-echo-area-timeout 1 - "Timeout for displaying echo area.") -(defvar exwm-workspace--display-echo-area-timer nil - "Timer for auto-hiding echo area.") - ;;;###autoload (defun exwm-workspace--get-geometry (frame) "Return the geometry of frame FRAME." @@ -227,9 +293,6 @@ Value nil means to use the default position which is fixed at bottom, while "Reports whether the minibuffer is displayed in its own frame." (memq exwm-workspace-minibuffer-position '(top bottom))) -(defvar exwm-workspace--id-struts-alist nil "Alist of X window and struts.") -(defvar exwm-workspace--struts nil "Areas occupied by struts.") - (defun exwm-workspace--update-struts () "Update `exwm-workspace--struts'." (setq exwm-workspace--struts nil) @@ -250,10 +313,6 @@ Value nil means to use the default position which is fixed at bottom, while (setq exwm-workspace--struts (append exwm-workspace--struts (list struts*)))))))))) -(defvar exwm-workspace--workareas nil "Workareas (struts excluded).") -(defvar exwm-workspace--update-workareas-hook nil - "Normal hook run when workareas get updated.") - (defun exwm-workspace--update-workareas () "Update `exwm-workspace--workareas'." (let ((root-width (x-display-pixel-width)) @@ -318,9 +377,6 @@ Value nil means to use the default position which is fixed at bottom, while (xcb:flush exwm--connection)) (run-hooks 'exwm-workspace--update-workareas-hook)) -(defvar exwm-workspace--fullscreen-frame-count 0 - "Count the fullscreen workspace frames.") - (defun exwm-workspace--set-fullscreen (frame) "Make frame FRAME fullscreen according to `exwm-workspace--workareas'." (let ((workarea (elt exwm-workspace--workareas @@ -342,11 +398,6 @@ Value nil means to use the default position which is fixed at bottom, while (when exwm-workspace--fullscreen-frame-count (cl-incf exwm-workspace--fullscreen-frame-count))) -(defvar exwm-workspace--attached-minibuffer-height 0 - "Height (in pixel) of the attached minibuffer. - -If the minibuffer is detached, this value is 0.") - (defun exwm-workspace--resize-minibuffer-frame () "Resize minibuffer (and its container) to fit the size of workspace." (cl-assert (exwm-workspace--minibuffer-own-frame-p)) @@ -434,14 +485,13 @@ PREFIX-DIGITS is a list of the digits introduced so far." (goto-history-element (1+ n)) (exit-minibuffer)) -(defvar exwm-workspace-switch-hook nil - "Normal hook run after switching workspace.") - ;;;###autoload (defun exwm-workspace-switch (frame-or-index &optional force) - "Switch to workspace INDEX. Query for FRAME-OR-INDEX if it's not specified. + "Switch to workspace INDEX (0-based). -The optional FORCE option is for internal use only." +Query for the index if not specified when called interactively. Passing a +workspace frame as the first option or making use of the rest options are +for internal use only." (interactive (list (unless (and (eq major-mode 'exwm-mode) @@ -518,13 +568,11 @@ The optional FORCE option is for internal use only." (run-hooks 'focus-in-hook) (run-hooks 'exwm-workspace-switch-hook))) -(defvar exwm-workspace-switch-create-limit 10 - "Number of workspaces `exwm-workspace-switch-create' allowed to create -each time.") - ;;;###autoload (defun exwm-workspace-switch-create (frame-or-index) - "Switch to workspace FRAME-OR-INDEX, creating it if it does not exist yet." + "Switch to workspace INDEX or creating it first if it does not exist yet. + +Passing a workspace frame as the first option is for internal use only." (interactive) (if (or (framep frame-or-index) (< frame-or-index (exwm-workspace--count))) @@ -537,10 +585,6 @@ each time.") (run-hooks 'exwm-workspace-list-change-hook)) (exwm-workspace-switch frame-or-index))) -(defvar exwm-workspace-list-change-hook nil - "Normal hook run when the workspace list is changed (workspace added, -deleted, moved, etc).") - ;;;###autoload (defun exwm-workspace-swap (workspace1 workspace2) "Interchange position of WORKSPACE1 with that of WORKSPACE2." @@ -579,6 +623,7 @@ deleted, moved, etc).") ;;;###autoload (defun exwm-workspace-move (workspace nth) "Move WORKSPACE to the NTH position. + When called interactively, prompt for a workspace and move current one just before it." (interactive @@ -645,13 +690,6 @@ INDEX must not exceed the current number of workspaces." :window id :data (exwm-workspace--position exwm--frame))))) -(declare-function exwm-input--on-buffer-list-update "exwm-input.el" ()) -(declare-function exwm-layout--show "exwm-layout.el" (id &optional window)) -(declare-function exwm-layout--hide "exwm-layout.el" (id)) -(declare-function exwm-layout--refresh "exwm-layout.el") -(declare-function exwm-layout--other-buffer-predicate "exwm-layout.el" - (buffer)) - ;;;###autoload (defun exwm-workspace-move-window (frame-or-index &optional id) "Move window ID to workspace FRAME-OR-INDEX." @@ -780,8 +818,6 @@ INDEX must not exceed the current number of workspaces." (xcb:flush exwm--connection))) (setq exwm-workspace--switch-history-outdated t))) -(defvar exwm-layout-show-all-buffers) - ;;;###autoload (defun exwm-workspace-switch-to-buffer (buffer-or-name) "Make the current Emacs window display another buffer." @@ -1049,8 +1085,6 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (remove-hook 'post-command-hook #'exwm-workspace--update-minibuffer-height) (exwm-workspace--hide-minibuffer))) -(defvar exwm-input--during-command) - (defun exwm-workspace--on-echo-area-dirty () "Run when new message arrives to show the echo area and its container." (when (and (not (active-minibuffer-window)) @@ -1075,12 +1109,6 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (cancel-timer exwm-workspace--display-echo-area-timer) (setq exwm-workspace--display-echo-area-timer nil)))) -(defvar exwm-workspace--client nil - "The 'client' frame parameter of emacsclient frames.") - -(declare-function exwm-manage--unmanage-window "exwm-manage.el") -(declare-function exwm--exit "exwm.el") - (defun exwm-workspace--confirm-kill-emacs (prompt &optional force) "Confirm before exiting Emacs." (when (cond @@ -1157,8 +1185,6 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." :width (x-display-pixel-width) :height (x-display-pixel-height)))) -(defvar exwm-workspace--timer nil "Timer used to track echo area changes.") - (defun exwm-workspace--add-frame-as-workspace (frame) "Configure frame FRAME to be treated as a workspace." (cond diff --git a/exwm.el b/exwm.el index 02e9152ed0..3e445b21ee 100644 --- a/exwm.el +++ b/exwm.el @@ -72,9 +72,41 @@ (require 'exwm-manage) (require 'exwm-input) +(defgroup exwm nil + "Emacs X Window Manager." + :tag "EXWM" + :version "25.3" + :group 'applications + :prefix "exwm-") + +(defcustom exwm-init-hook nil + "Normal hook run when EXWM has just finished initialization." + :type 'hook) + +(defcustom exwm-exit-hook nil + "Normal hook run just before EXWM exits." + :type 'hook) + +(defcustom exwm-update-class-hook nil + "Normal hook run when window class is updated." + :type 'hook) + +(defcustom exwm-update-title-hook nil + "Normal hook run when window title is updated." + :type 'hook) + +(defcustom exwm-blocking-subrs '(x-file-dialog x-popup-dialog x-select-font) + "Subrs (primitives) that would normally block EXWM." + :type '(repeat function)) + +(defconst exwm--server-name "server-exwm" + "Name of the subordinate Emacs server.") + +(defvar exwm--server-process nil "Process of the subordinate Emacs server.") + ;;;###autoload (defun exwm-reset () - "Reset window to standard state: non-fullscreen, line-mode." + "Reset the state of the selected window (non-fullscreen, line-mode, etc)." (interactive) (with-current-buffer (window-buffer) (when (eq major-mode 'exwm-mode) @@ -123,9 +155,6 @@ (when reply ;nil when destroyed (setq exwm-window-type (append (slot-value reply 'value) nil))))))) -(defvar exwm-update-class-hook nil - "Normal hook run when window class is updated.") - (defun exwm--update-class (id &optional force) "Update WM_CLASS." (with-current-buffer (exwm--id->buffer id) @@ -138,9 +167,6 @@ (when (and exwm-instance-name exwm-class-name) (run-hooks 'exwm-update-class-hook))))))) -(defvar exwm-update-title-hook nil - "Normal hook run when window title is updated.") - (defun exwm--update-utf8-title (id &optional force) "Update _NET_WM_NAME." (with-current-buffer (exwm--id->buffer id) @@ -645,9 +671,6 @@ :property p)) (xcb:flush exwm--connection))) -(defvar exwm-init-hook nil - "Normal hook run when EXWM has just finished initialization.") - (defun exwm-init (&optional frame) "Initialize EXWM." (if frame @@ -692,8 +715,6 @@ (exwm-manage--scan) (run-hooks 'exwm-init-hook))))) -(defvar exwm-exit-hook nil "Normal hook run just before EXWM exits.") - (defun exwm--exit () "Exit EXWM." (run-hooks 'exwm-exit-hook) @@ -705,9 +726,6 @@ (exwm-layout--exit) (exwm--exit-icccm-ewmh)) -(defvar exwm-blocking-subrs '(x-file-dialog x-popup-dialog x-select-font) - "Subrs (primitives) that would normally block EXWM.") - (defun exwm-enable (&optional undo) "Enable/Disable EXWM." (pcase undo @@ -734,10 +752,6 @@ (dolist (i exwm-blocking-subrs) (advice-add i :around #'exwm--server-eval-at))))) -(defconst exwm--server-name "server-exwm" - "Name of the subordinate Emacs server.") -(defvar exwm--server-process nil "Process of the subordinate Emacs server.") - (defun exwm--server-stop () "Stop the subordinate Emacs server." (server-force-delete exwm--server-name) @@ -784,13 +798,6 @@ ;; For other types, return the value as-is. (t result)))))) -(define-obsolete-function-alias 'exwm-enable-ido-workaround 'exwm-config-ido - "25.1" "Enable workarounds for Ido.") - -(defun exwm-disable-ido-workaround () - "This function does nothing actually." - (declare (obsolete nil "25.1"))) - (provide 'exwm) diff --git a/xinitrc b/xinitrc index 873265fb64..0adc068450 100644 --- a/xinitrc +++ b/xinitrc @@ -1,6 +1,9 @@ # Disable access control xhost +SI:localuser:$USER +# Make Java applications aware this is a non-reparenting window manager. +export _JAVA_AWT_WM_NONREPARENTING=1 + # Themes, etc gnome-settings-daemon & -- cgit 1.4.1 From b8ce20b4f3eac1228aef58b83a72a87ccdb819d0 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 19 Feb 2018 22:34:03 +0800 Subject: Support displaying floating X windows on all workspaces ; Setting _NET_WM_DESKTOP to 0xffffffff makes an X windows appearing ; on all desktops (EWMH). It's tricky to do it for tiling X windows ; so it's not implemented. * exwm-core.el (exwm--desktop): New buffer-local variable recording the value of _NET_WM_DESKTOP. * exwm-layout.el (exwm-layout--hide): Do not hide X windows with this property set to 0xffffffff. * exwm.el (exwm--update-desktop): New function for fetching the value of _NET_WM_DESKTOP and setting `exwm--desktop'. * exwm-manage.el (exwm-manage--manage-window): Use it. * exwm-workspace.el (exwm-workspace--set-desktop): Also update `exwm--desktop'. --- exwm-core.el | 1 + exwm-layout.el | 4 +++- exwm-manage.el | 15 ++------------- exwm-workspace.el | 10 ++++++---- exwm.el | 26 ++++++++++++++++++++++++++ 5 files changed, 38 insertions(+), 18 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 68ec53b03e..71498c9dc8 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -139,6 +139,7 @@ least SECS seconds later." (defvar-local exwm--on-KeyPress ;KeyPress event handler #'exwm-input--on-KeyPress-line-mode) ;; Properties +(defvar-local exwm--desktop nil "_NET_WM_DESKTOP.") (defvar-local exwm-window-type nil "_NET_WM_WINDOW_TYPE.") (defvar-local exwm--geometry nil) (defvar-local exwm-class-name nil "Class name in WM_CLASS.") diff --git a/exwm-layout.el b/exwm-layout.el index a98e261117..3f624ad7fe 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -102,7 +102,9 @@ (defun exwm-layout--hide (id) "Hide window ID." (with-current-buffer (exwm--id->buffer id) - (unless (exwm-layout--iconic-state-p) ;already hidden + (unless (or (exwm-layout--iconic-state-p) + (and exwm--floating-frame + (eq #xffffffff exwm--desktop))) (exwm--log "Hide #x%x" id) (when exwm--floating-frame (let* ((container (frame-parameter exwm--floating-frame diff --git a/exwm-manage.el b/exwm-manage.el index b983ebf80d..790b6f1479 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -73,6 +73,7 @@ You can still make the X windows floating afterwards." (declare-function exwm--update-struts "exwm.el" (id)) (declare-function exwm--update-title "exwm.el" (id)) (declare-function exwm--update-transient-for "exwm.el" (id &optional force)) +(declare-function exwm--update-desktop "exwm.el" (id &optional force)) (declare-function exwm--update-window-type "exwm.el" (id &optional force)) (declare-function exwm-floating--set-floating "exwm-floating.el" (id)) (declare-function exwm-floating--unset-floating "exwm-floating.el" (id)) @@ -252,19 +253,7 @@ You can still make the X windows floating afterwards." (exwm-floating--unset-floating id)) (exwm-input-grab-keyboard id) (setq exwm-workspace--switch-history-outdated t) - ;; Set _NET_WM_DESKTOP or move window. - (let ((reply (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:ewmh:get-_NET_WM_DESKTOP - :window id))) - desktop) - (when reply - (setq desktop (slot-value reply 'value))) - (if (and desktop - (/= desktop exwm-workspace-current-index) - ;; Check the range. - (< desktop (exwm-workspace--count))) - (exwm-workspace-move-window desktop id) - (exwm-workspace--set-desktop id))) + (exwm--update-desktop id) (exwm-manage--update-ewmh-state id) (with-current-buffer (exwm--id->buffer id) (when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) diff --git a/exwm-workspace.el b/exwm-workspace.el index b9cda25160..e2dfa232f8 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -685,10 +685,12 @@ INDEX must not exceed the current number of workspaces." (defun exwm-workspace--set-desktop (id) "Set _NET_WM_DESKTOP for X window ID." (with-current-buffer (exwm--id->buffer id) - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_WM_DESKTOP - :window id - :data (exwm-workspace--position exwm--frame))))) + (let ((desktop (exwm-workspace--position exwm--frame))) + (setq exwm--desktop desktop) + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_DESKTOP + :window id + :data desktop))))) ;;;###autoload (defun exwm-workspace-move-window (frame-or-index &optional id) diff --git a/exwm.el b/exwm.el index 3e445b21ee..5cba3785f2 100644 --- a/exwm.el +++ b/exwm.el @@ -145,6 +145,32 @@ (while (= ?R (shell-command-on-region (point) (point) args)))) (kill-emacs)))))) +(defun exwm--update-desktop (xwin) + "Update _NET_WM_DESKTOP." + (with-current-buffer (exwm--id->buffer xwin) + (let ((reply (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:ewmh:get-_NET_WM_DESKTOP + :window xwin))) + desktop) + (when reply + (setq desktop (slot-value reply 'value)) + (cond + ((eq desktop #xffffffff) + (unless (or (not exwm--floating-frame) + (eq exwm--frame exwm-workspace--current) + (and exwm--desktop + (= desktop exwm--desktop))) + (exwm-layout--show xwin (frame-root-window exwm--floating-frame))) + (setq exwm--desktop desktop)) + ((and desktop + (< desktop (exwm-workspace--count)) + (if exwm--desktop + (/= desktop exwm--desktop) + (/= desktop (exwm-workspace--position exwm--frame)))) + (exwm-workspace-move-window desktop xwin)) + (t + (exwm-workspace--set-desktop xwin))))))) + (defun exwm--update-window-type (id &optional force) "Update _NET_WM_WINDOW_TYPE." (with-current-buffer (exwm--id->buffer id) -- cgit 1.4.1 From 5c5729c0d4832a870a928d6d7cc2f990a7e8f649 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 19 Feb 2018 22:40:27 +0800 Subject: Fix various issues with multi-monitor support * exwm-workspace.el (exwm-workspace-switch): Do not hide X windows when switching to a workspace on another output; update the timestamp (last switched to) of a workspace frame. (exwm-workspace-move-window): Do not hide an X window when moving it to an active workspace on another output. * exwm-floating.el (exwm-floating--set-floating): * exwm-layout.el (exwm-layout-set-fullscreen): * exwm-manage.el (exwm-manage--manage-window) (exwm-manage--on-ConfigureRequest): * exwm-systemtray.el (exwm-systemtray--refresh) (exwm-systemtray--init): Correct coordinate calculations. * exwm-workspace.el (exwm-workspace--current-width): Removed since no longer used. --- exwm-floating.el | 74 ++++++++++++++++--------------------- exwm-layout.el | 9 +---- exwm-manage.el | 26 ++++++++----- exwm-systemtray.el | 26 +++++++------ exwm-workspace.el | 105 ++++++++++++++++++++++++++++++++++------------------- 5 files changed, 131 insertions(+), 109 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 8cd0491bea..5a2c61e878 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -129,52 +129,22 @@ This is also used by X window containers.") (y (slot-value exwm--geometry 'y)) (width (slot-value exwm--geometry 'width)) (height (slot-value exwm--geometry 'height))) - (exwm--log "Floating geometry (original, absolute): %dx%d%+d%+d" - width height x y) - (when (and (/= x 0) - (/= y 0)) - (let ((workarea (elt exwm-workspace--workareas - (exwm-workspace--position original-frame)))) - (setq x (- x (aref workarea 0)) - y (- y (aref workarea 1))))) - (exwm--log "Floating geometry (original, relative): %dx%d%+d%+d" - width height x y) + (exwm--log "Floating geometry (original): %dx%d%+d%+d" width height x y) ;; Save frame parameters. (set-frame-parameter frame 'exwm-outer-id outer-id) (set-frame-parameter frame 'exwm-id window-id) (set-frame-parameter frame 'exwm-container frame-container) ;; Fix illegal parameters ;; FIXME: check normal hints restrictions - (let* ((display-width (frame-pixel-width original-frame)) - (display-height (- (frame-pixel-height original-frame) - (if (exwm-workspace--minibuffer-own-frame-p) - 0 - (window-pixel-height (minibuffer-window - original-frame))) - (* 2 (window-mode-line-height)) - (window-header-line-height window))) - (display-height (* 2 (/ display-height 2)))) ;round to even - (if (> width display-width) - ;; Too wide - (progn (setq x 0 - width display-width)) - ;; Invalid width - (when (= 0 width) (setq width (/ display-width 2))) - ;; Make sure at least half of the window is visible - (when (or (> (+ x (/ width 2)) display-width) (> 0 (+ x (/ width 2)))) - (setq x (/ (- display-width width) 2)))) - (if (> height display-height) - ;; Too tall - (setq y 0 - height display-height) - ;; Invalid height - (when (= 0 height) (setq height (/ display-height 2))) - ;; Make sure at least half of the window is visible - (when (or (> (+ y (/ height 2)) display-height) - (> 0 (+ y (/ height 2)))) - (setq y (/ (- display-height height) 2)))) + (let* ((workarea (elt exwm-workspace--workareas + (exwm-workspace--position original-frame))) + (x* (aref workarea 0)) + (y* (aref workarea 1)) + (width* (aref workarea 2)) + (height* (aref workarea 3))) ;; Center floating windows - (when (and (= x 0) (= y 0)) + (when (and (or (= x 0) (= x x*)) + (or (= y 0) (= y y*))) (let ((buffer (exwm--id->buffer exwm-transient-for)) window edges) (when (and buffer (setq window (get-buffer-window buffer))) @@ -184,11 +154,29 @@ This is also used by X window containers.") (setq edges nil))) (if edges ;; Put at the center of leading window - (setq x (/ (- (elt edges 2) (elt edges 0) width) 2) - y (/ (- (elt edges 3) (elt edges 1) height) 2)) + (setq x (+ x* (/ (- (elt edges 2) (elt edges 0) width) 2)) + y (+ y* (/ (- (elt edges 3) (elt edges 1) height) 2))) ;; Put at the center of screen - (setq x (/ (- display-width width) 2) - y (/ (- display-height height) 2)))))) + (setq x (/ (- width* width) 2) + y (/ (- height* height) 2))))) + (if (> width width*) + ;; Too wide + (progn (setq x x* + width width*)) + ;; Invalid width + (when (= 0 width) (setq width (/ width* 2))) + ;; Make sure at least half of the window is visible + (unless (< x* (+ x (/ width 2)) (+ x* width*)) + (setq x (+ x* (/ (- width* width) 2))))) + (if (> height height*) + ;; Too tall + (setq y y* + height height*) + ;; Invalid height + (when (= 0 height) (setq height (/ height* 2))) + ;; Make sure at least half of the window is visible + (unless (< y* (+ y (/ height 2)) (+ y* height*)) + (setq y (+ y* (/ (- height* height) 2)))))) (exwm--set-geometry id x y nil nil) (xcb:flush exwm--connection) (exwm--log "Floating geometry (corrected): %dx%d%+d%+d" width height x y) diff --git a/exwm-layout.el b/exwm-layout.el index 3f624ad7fe..5a01f90032 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -49,8 +49,6 @@ (declare-function exwm-input-release-keyboard "exwm-input.el") (declare-function exwm-workspace--client-p "exwm-workspace.el" (&optional frame)) -(declare-function exwm-workspace--current-height "exwm-workspace.el") -(declare-function exwm-workspace--current-width "exwm-workspace.el") (declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") (declare-function exwm-workspace--workspace-p "exwm-workspace.el" (workspace)) @@ -137,11 +135,8 @@ (cl-return-from 'exwm-layout-set-fullscreen)) (with-current-buffer (if id (exwm--id->buffer id) (window-buffer)) ;; Expand the X window to fill the whole screen. - ;; Rationale: Floating X windows may not be positioned at (0, 0) - ;; due to the extra border. - (exwm--set-geometry exwm--id 0 0 - (exwm-workspace--current-width) - (exwm-workspace--current-height)) + (with-slots (x y width height) (exwm-workspace--get-geometry exwm--frame) + (exwm--set-geometry exwm--id x y width height)) ;; Raise the X window. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow diff --git a/exwm-manage.el b/exwm-manage.el index 790b6f1479..dd7c2e3953 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -65,6 +65,7 @@ You can still make the X windows floating afterwards." (defvar exwm-workspace--id-struts-alist) (defvar exwm-workspace--list) (defvar exwm-workspace--switch-history-outdated) +(defvar exwm-workspace--workareas) (defvar exwm-workspace-current-index) (declare-function exwm--update-class "exwm.el" (id &optional force)) (declare-function exwm--update-hints "exwm.el" (id &optional force)) @@ -80,8 +81,7 @@ You can still make the X windows floating afterwards." (declare-function exwm-input-grab-keyboard "exwm-input.el") (declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) (declare-function exwm-workspace--count "exwm-workspace.el" ()) -(declare-function exwm-workspace--current-height "exwm-workspace.el") -(declare-function exwm-workspace--current-width "exwm-workspace.el") +(declare-function exwm-workspace--position "exwm-workspace.el" (frame)) (declare-function exwm-workspace--set-desktop "exwm-workspace.el" (id)) (declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame)) (declare-function exwm-workspace--update-struts "exwm-workspace.el" ()) @@ -204,11 +204,17 @@ You can still make the X windows floating afterwards." (with-slots (x y width height) exwm--geometry ;; Center window of type _NET_WM_WINDOW_TYPE_SPLASH (when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_SPLASH exwm-window-type) - (exwm--set-geometry id - (/ (- (exwm-workspace--current-width) width) 2) - (/ (- (exwm-workspace--current-height) height) - 2) - nil nil))) + (let* ((workarea (elt exwm-workspace--workareas + (exwm-workspace--position exwm--frame))) + (x* (aref workarea 0)) + (y* (aref workarea 1)) + (width* (aref workarea 2)) + (height* (aref workarea 3))) + (exwm--set-geometry id + (+ x* (/ (- width* width) 2)) + (+ y* (/ (- height* height) 2)) + nil + nil)))) ;; Check for desktop. (when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DESKTOP exwm-window-type) ;; There should be only one desktop X window. @@ -501,9 +507,9 @@ border-width: %d; sibling: #x%x; stack-mode: %d" (with-current-buffer buffer (setq edges (if (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) - (list 0 0 - (exwm-workspace--current-width) - (exwm-workspace--current-height)) + (with-slots (x y width height) + (exwm-workspace--get-geometry exwm--frame) + (list x y width height)) (window-inside-absolute-pixel-edges (get-buffer-window buffer t)))) (exwm--log "Reply with ConfigureNotify (edges): %s" edges) diff --git a/exwm-systemtray.el b/exwm-systemtray.el index 5377ef8ed6..d932032e59 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -82,8 +82,6 @@ You shall use the default value if using auto-hide minibuffer." (defvar exwm-workspace--workareas) (defvar exwm-workspace-current-index) (defvar xcb:Atom:_NET_SYSTEM_TRAY_S0) -(declare-function exwm-workspace--current-height "exwm-workspace.el") -(declare-function exwm-workspace--current-width "exwm-workspace.el") (declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") (defun exwm-systemtray--embed (icon) @@ -205,13 +203,15 @@ You shall use the default value if using auto-hide minibuffer." (setq x (+ x (slot-value (cdr pair) 'width) exwm-systemtray-icon-gap)) (setq map t))) - (xcb:+request exwm-systemtray--connection - (make-instance 'xcb:ConfigureWindow - :window exwm-systemtray--embedder - :value-mask (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Width) - :x (- (exwm-workspace--current-width) x) - :width x)) + (let ((workarea (elt exwm-workspace--workareas + exwm-workspace-current-index))) + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:ConfigureWindow + :window exwm-systemtray--embedder + :value-mask (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Width) + :x (- (aref workarea 2) x) + :width x))) (when map (xcb:+request exwm-systemtray--connection (make-instance 'xcb:MapWindow :window exwm-systemtray--embedder)))) @@ -434,9 +434,11 @@ You shall use the default value if using auto-hide minibuffer." (- (line-pixel-height) exwm-systemtray-height) ;; Vertically centered. (/ (- (line-pixel-height) exwm-systemtray-height) 2))) - (setq frame exwm-workspace--current - ;; Bottom aligned. - y (- (exwm-workspace--current-height) exwm-systemtray-height))) + (let ((workarea (elt exwm-workspace--workareas + exwm-workspace-current-index))) + (setq frame exwm-workspace--current + ;; Bottom aligned. + y (- (aref workarea 3) exwm-systemtray-height)))) (setq parent (string-to-number (frame-parameter frame 'window-id)) depth (slot-value (xcb:+request-unchecked+reply exwm-systemtray--connection diff --git a/exwm-workspace.el b/exwm-workspace.el index e2dfa232f8..9b0bfeb338 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -272,14 +272,6 @@ NIL if FRAME is not a workspace" :width (x-display-pixel-width) :height (x-display-pixel-height)))) -;;;###autoload -(defun exwm-workspace--current-width () - "Return the width of current workspace." - (let ((geometry (frame-parameter exwm-workspace--current 'exwm-geometry))) - (if geometry - (slot-value geometry 'width) - (x-display-pixel-width)))) - ;;;###autoload (defun exwm-workspace--current-height () "Return the height of current workspace." @@ -536,14 +528,23 @@ for internal use only." ;; Set a default minibuffer frame. (setq default-minibuffer-frame frame)) ;; Show/Hide X windows. - (dolist (i exwm--id-buffer-alist) - (with-current-buffer (cdr i) - (if (eq old-frame exwm--frame) - (exwm-layout--hide exwm--id) - (when (eq frame exwm--frame) - (let ((window (get-buffer-window nil t))) - (when window - (exwm-layout--show exwm--id window))))))) + (let ((hide-x-windows-on-old-frame + (with-slots ((x1 x) + (y1 y)) + (exwm-workspace--get-geometry frame) + (with-slots ((x2 x) + (y2 y)) + (exwm-workspace--get-geometry old-frame) + (and (= x1 x2) (= y1 y2)))))) + (dolist (i exwm--id-buffer-alist) + (with-current-buffer (cdr i) + (if (eq old-frame exwm--frame) + (when hide-x-windows-on-old-frame + (exwm-layout--hide exwm--id)) + (when (eq frame exwm--frame) + (let ((window (get-buffer-window nil t))) + (when window + (exwm-layout--show exwm--id window)))))))) ;; Hide windows in other workspaces by preprending a space (unless exwm-workspace-show-all-buffers (dolist (i exwm--id-buffer-alist) @@ -553,6 +554,8 @@ for internal use only." (exwm-workspace-rename-buffer (if (eq frame exwm--frame) name (concat " " name))))))) + ;; Update frame's timestamp. + (set-frame-parameter frame 'exwm-timestamp (float-time)) ;; Update demands attention flag (set-frame-parameter frame 'exwm-urgency nil) ;; Update switch workspace history @@ -700,7 +703,7 @@ INDEX must not exceed the current number of workspaces." (exwm-workspace--prompt-delete-allowed t)) (exwm-workspace--prompt-for-workspace "Move to [+/-]: ")))) (let ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index)) - old-frame container) + x-old y-old x-new y-new should-hide old-frame container) (unless id (setq id (exwm--buffer->id (window-buffer)))) (with-current-buffer (exwm--id->buffer id) (unless (eq exwm--frame frame) @@ -712,6 +715,36 @@ INDEX must not exceed the current number of workspaces." (concat " " name))))) (setq old-frame exwm--frame exwm--frame frame) + ;; Save the positions of new & old frames. + (with-slots ((x1 x) + (y1 y)) + (exwm-workspace--get-geometry old-frame) + (with-slots ((x2 x) + (y2 y)) + (exwm-workspace--get-geometry frame) + (setq x-old x1 + y-old y1 + x-new x2 + y-new y2))) + (if (and (= x-old x-new) + (= y-old y-new)) + ;; Switch to a workspace on the same output. + (setq should-hide t) + ;; Check if this frame has the largest timestamp of that output. + (let ((timestamp (frame-parameter frame 'exwm-timestamp)) + (timestamp-active + (apply #'max + (mapcar (lambda (w) + (with-slots (x y) + (exwm-workspace--get-geometry w) + (if (and (= x x-new) + (= y y-new)) + (frame-parameter w 'exwm-timestamp) + -1))) + exwm-workspace--list)))) + (when (< timestamp timestamp-active) + ;; Switch to a workspace not active on another output. + (setq should-hide t)))) (if (not exwm--floating-frame) ;; Tiling. (progn @@ -724,31 +757,27 @@ INDEX must not exceed the current number of workspaces." (exwm--id->buffer id)) (if (eq frame exwm-workspace--current) (select-window (frame-selected-window frame)) - (exwm-layout--hide id))) + (when should-hide + (exwm-layout--hide id)))) ;; Floating. (setq container (frame-parameter exwm--floating-frame 'exwm-container)) - (with-slots ((x1 x) - (y1 y)) - (exwm-workspace--get-geometry old-frame) - (with-slots ((x2 x) - (y2 y)) - (exwm-workspace--get-geometry frame) - (unless (and (= x1 x2) - (= y1 y2)) - (with-slots (x y) - (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:GetGeometry - :drawable container)) - (setq x (+ x (- x2 x1)) - y (+ y (- y2 y1))) - (exwm--set-geometry id x y nil nil) - (exwm--set-geometry container x y nil nil))))) + (unless (and (= x-old x-new) + (= y-old y-new)) + (with-slots (x y) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetGeometry + :drawable container)) + (setq x (+ x (- x-new x-old)) + y (+ y (- y-new y-old))) + (exwm--set-geometry id x y nil nil) + (exwm--set-geometry container x y nil nil))) (if (exwm-workspace--minibuffer-own-frame-p) (if (eq frame exwm-workspace--current) (select-window (frame-root-window exwm--floating-frame)) (select-window (frame-selected-window exwm-workspace--current)) - (exwm-layout--hide id)) + (when should-hide + (exwm-layout--hide id))) ;; The frame needs to be recreated since it won't use the ;; minibuffer on the new workspace. ;; The code is mostly copied from `exwm-floating--set-floating'. @@ -808,7 +837,8 @@ INDEX must not exceed the current number of workspaces." (if (eq frame exwm-workspace--current) (with-current-buffer (exwm--id->buffer id) (select-window (frame-root-window exwm--floating-frame))) - (exwm-layout--hide id)))) + (when should-hide + (exwm-layout--hide id))))) ;; Update the 'exwm-selected-window' frame parameter. (when (not (eq frame exwm-workspace--current)) (with-current-buffer (exwm--id->buffer id) @@ -1486,7 +1516,8 @@ applied to all subsequently created X frames." ;; Prevent frame parameters introduced by this module from being ;; saved/restored. (dolist (i '(exwm-outer-id exwm-id exwm-container exwm-geometry - fullscreen exwm-selected-window exwm-urgency)) + exwm-selected-window exwm-timestamp exwm-urgency + fullscreen)) (push (cons i :never) frameset-filter-alist))) (defun exwm-workspace--exit () -- cgit 1.4.1 From 7d4c57a6abc6bc724beaffb9d4f91971f9a62426 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 20 Feb 2018 21:57:49 +0800 Subject: Add Customize interface for simulation keys * exwm-input.el (exwm-input-simulation-keys): New user option for setting simulation keys. (exwm-input--init): Initialize simulation keys from this variable. * exwm-input.el (exwm-input--simulation-prefix-keys) (exwm-input--on-KeyPress-line-mode) (exwm-input--update-simulation-prefix-keys) (exwm-input-set-simulation-keys, exwm-input-set-local-simulation-keys) (exwm-input-send-simulation-key): `exwm-input--simulation-keys' is now a hash table storing both simulation keys and prefix keys. (exwm-input--unset-simulation-keys): Clear the hash table. (exwm-input--exit): Use `exwm-input--unset-simulation-keys'. * exwm-core.el (exwm--kmacro-map, exwm-mode-menu): Adapt to the change of `exwm-input--simulation-keys'. * exwm-config.el (exwm-config-default): Update the example. --- exwm-config.el | 22 +++++------ exwm-core.el | 31 +++++++-------- exwm-input.el | 120 +++++++++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 118 insertions(+), 55 deletions(-) diff --git a/exwm-config.el b/exwm-config.el index a434fe8d90..5742cac187 100644 --- a/exwm-config.el +++ b/exwm-config.el @@ -52,17 +52,17 @@ (interactive (list (read-shell-command "$ "))) (start-process-shell-command command nil command))) ;; Line-editing shortcuts - (exwm-input-set-simulation-keys - '(([?\C-b] . left) - ([?\C-f] . right) - ([?\C-p] . up) - ([?\C-n] . down) - ([?\C-a] . home) - ([?\C-e] . end) - ([?\M-v] . prior) - ([?\C-v] . next) - ([?\C-d] . delete) - ([?\C-k] . (S-end delete)))) + (setq exwm-input-simulation-keys + '(([?\C-b] . [left]) + ([?\C-f] . [right]) + ([?\C-p] . [up]) + ([?\C-n] . [down]) + ([?\C-a] . [home]) + ([?\C-e] . [end]) + ([?\M-v] . [prior]) + ([?\C-v] . [next]) + ([?\C-d] . [delete]) + ([?\C-k] . [S-end delete]))) ;; Enable EXWM (exwm-enable) ;; Configure Ido diff --git a/exwm-core.el b/exwm-core.el index 71498c9dc8..41c3b57724 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -45,7 +45,7 @@ (defvar exwm--root nil "Root window.") (defvar exwm-input--global-prefix-keys) -(defvar exwm-input--simulation-prefix-keys) +(defvar exwm-input--simulation-keys) (defvar exwm-input-line-mode-passthrough) (defvar exwm-input-prefix-keys) (declare-function exwm-input--fake-key "exwm-input.el" (event)) @@ -189,7 +189,7 @@ least SECS seconds later." (active-minibuffer-window) (memq last-input-event exwm-input--global-prefix-keys) (memq last-input-event exwm-input-prefix-keys) - (memq last-input-event exwm-input--simulation-prefix-keys)) + (gethash last-input-event exwm-input--simulation-keys)) (set-transient-map (make-composed-keymap (list exwm-mode-map global-map))) (push last-input-event unread-command-events)) @@ -229,19 +229,20 @@ least SECS seconds later." ;; This is merely a reference. ("Send simulation key" :filter (lambda (&rest _args) - (mapcar (lambda (i) - (let ((keys (cdr i))) - (if (vectorp keys) - (setq keys (append keys)) - (unless (sequencep keys) - (setq keys (list keys)))) - (vector (key-description keys) - `(lambda () - (interactive) - (dolist (key ',keys) - (exwm-input--fake-key key))) - :keys (key-description (car i))))) - exwm-input--simulation-keys))) + (let (result) + (maphash + (lambda (key value) + (when (sequencep key) + (setq result (append result + `([ + ,(key-description value) + (lambda () + (interactive) + (dolist (i ',value) + (exwm-input--fake-key i))) + :keys ,(key-description key)]))))) + exwm-input--simulation-keys) + result))) ["Define global binding" exwm-input-set-key] diff --git a/exwm-input.el b/exwm-input.el index e536a14144..73a0dba6f2 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -113,9 +113,6 @@ (defvar exwm-input--simulation-keys nil "Simulation keys in line-mode.") -(defvar exwm-input--simulation-prefix-keys nil - "List of prefix keys of simulation keys in line-mode.") - (defvar exwm-input--temp-line-mode nil "Non-nil indicates it's in temporary line-mode for char-mode.") @@ -537,7 +534,7 @@ called interactively. Only invoke it non-interactively in configuration." ;; (memq event exwm-input--global-prefix-keys) (memq event exwm-input-prefix-keys) - (memq event exwm-input--simulation-prefix-keys))) + (gethash event exwm-input--simulation-keys))) (setq mode xcb:Allow:AsyncKeyboard) (exwm-input--cache-event event)) (unless mode @@ -733,50 +730,111 @@ multiple keys." ;; (unless (listp last-input-event) ;not a key event ;; (exwm-input--fake-key last-input-event))) -(defun exwm-input--update-simulation-prefix-keys () - "Update the list of prefix keys of simulation keys." - (setq exwm-input--simulation-prefix-keys nil) - (dolist (i exwm-input--simulation-keys) - (if exwm-input--local-simulation-keys - (local-set-key (car i) #'exwm-input-send-simulation-key) - (define-key exwm-mode-map (car i) #'exwm-input-send-simulation-key)) - (cl-pushnew (elt (car i) 0) exwm-input--simulation-prefix-keys))) - (defun exwm-input-set-simulation-keys (simulation-keys) "Set simulation keys. -SIMULATION-KEYS is an alist of the form (original-key . simulated-key)." - (setq exwm-input--simulation-keys nil) +SIMULATION-KEYS is an alist of the form (original-key . simulated-key), +where both original-key and simulated-key are key sequences. + +Simulation keys set this way take effect in real time. For configuration +it's recommended to customize or set `exwm-input-simulation-keys' instead." + ;; Clear keymaps and the hash table. + (when (hash-table-p exwm-input--simulation-keys) + (maphash (lambda (key _value) + (when (sequencep key) + (if exwm-input--local-simulation-keys + (local-unset-key key) + (define-key exwm-mode-map key nil)))) + exwm-input--simulation-keys) + (clrhash exwm-input--simulation-keys)) + ;; Update the hash table. + (setq exwm-input--simulation-keys (make-hash-table :test #'equal)) (dolist (i simulation-keys) - (cl-pushnew `(,(vconcat (car i)) . ,(cdr i)) exwm-input--simulation-keys)) - (exwm-input--update-simulation-prefix-keys)) + (let ((original (vconcat (car i))) + (simulated (cdr i))) + (setq simulated (if (sequencep simulated) + (append simulated nil) + (list simulated))) + ;; The key stored is a key sequence (vector). + ;; The value stored is a list of key events. + (puthash original simulated exwm-input--simulation-keys) + ;; Also mark the prefix key as used. + (puthash (aref original 0) t exwm-input--simulation-keys))) + ;; Update keymaps. + (maphash (lambda (key _value) + (when (sequencep key) + (if exwm-input--local-simulation-keys + (local-set-key key #'exwm-input-send-simulation-key) + (define-key exwm-mode-map key + #'exwm-input-send-simulation-key)))) + exwm-input--simulation-keys)) + +(defcustom exwm-input-simulation-keys nil + "Simulation keys. + +It is an alist of the form (original-key . simulated-key), where both +original-key and simulated-key are key sequences. Original-key is what you +type to an X window in line-mode which then gets translated to simulated-key +by EXWM and forwarded to the X window. + +Notes: +* Setting the value directly (rather than customizing it) after EXWM + finishes initialization has no effect. +* Original-keys consist of multiple key events are only supported in Emacs + 27 and later. +* A minority of applications do not accept simulated keys by default. It's + required to customize them to accept events sent by SendEvent. +* The predefined examples in the Customize interface are not guaranteed to + work for all applications. This can be tweaked on a per application basis + with `exwm-input-set-local-simulation-keys'." + :type '(alist :key-type (choice (key-sequence :tag "Original")) + :value-type (choice (key-sequence :tag "Move left" [left]) + (key-sequence :tag "Move right" [right]) + (key-sequence :tag "Move up" [up]) + (key-sequence :tag "Move down" [down]) + (key-sequence :tag "Move to BOL" [home]) + (key-sequence :tag "Move to EOL" [end]) + (key-sequence :tag "Page up" [prior]) + (key-sequence :tag "Page down" [next]) + (key-sequence :tag "Copy" [C-c]) + (key-sequence :tag "Paste" [C-v]) + (key-sequence :tag "Delete" [delete]) + (key-sequence :tag "Delete to EOL" + [S-end delete]) + (key-sequence :tag "User-defined"))) + :set (lambda (symbol value) + (set symbol value) + (exwm-input-set-simulation-keys value))) + +(defun exwm-input--unset-simulation-keys () + "Clear simulation keys and key bindings defined." + (when (hash-table-p exwm-input--simulation-keys) + (maphash (lambda (key _value) + (when (sequencep key) + (define-key exwm-mode-map key nil))) + exwm-input--simulation-keys) + (clrhash exwm-input--simulation-keys))) (defun exwm-input-set-local-simulation-keys (simulation-keys) "Set buffer-local simulation keys. Its usage is the same with `exwm-input-set-simulation-keys'." (make-local-variable 'exwm-input--simulation-keys) - (make-local-variable 'exwm-input--simulation-prefix-keys) (use-local-map (copy-keymap exwm-mode-map)) (let ((exwm-input--local-simulation-keys t)) (exwm-input-set-simulation-keys simulation-keys))) ;;;###autoload (cl-defun exwm-input-send-simulation-key (times) - "Fake a key event according to the last input key sequence. - -Sending multiple fake keys at once is only supported by Emacs 27 and later." + "Fake a key event according to the last input key sequence." (interactive "p") (unless (derived-mode-p 'exwm-mode) (cl-return-from 'exwm-input-send-simulation-key)) - (let ((pair (assoc (this-single-command-keys) exwm-input--simulation-keys))) - (when pair - (setq pair (cdr pair)) - (unless (listp pair) - (setq pair (list pair))) - (dotimes (_ times) - (dolist (j pair) - (exwm-input--fake-key j)))))) + (let ((keys (gethash (this-single-command-keys) + exwm-input--simulation-keys))) + (dotimes (_ times) + (dolist (key keys) + (exwm-input--fake-key key))))) (defun exwm-input--on-pre-command () "Run in `pre-command-hook'." @@ -814,6 +872,9 @@ Sending multiple fake keys at once is only supported by Emacs 27 and later." :name-len (length atom) :name atom)) 'atom))) + ;; Initialize simulation keys. + (when exwm-input-simulation-keys + (exwm-input-set-simulation-keys exwm-input-simulation-keys)) ;; Attach event listeners (xcb:+event exwm--connection 'xcb:PropertyNotify #'exwm-input--on-PropertyNotify) @@ -841,6 +902,7 @@ Sending multiple fake keys at once is only supported by Emacs 27 and later." (defun exwm-input--exit () "Exit the input module." + (exwm-input--unset-simulation-keys) (remove-hook 'pre-command-hook #'exwm-input--on-pre-command) (remove-hook 'post-command-hook #'exwm-input--on-post-command) (remove-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update) -- cgit 1.4.1 From bfa35c0e388e5b4daf27aa162ecb6323f7716c85 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 20 Feb 2018 22:09:27 +0800 Subject: Remove redundant code for fullscreen mode * exwm-manage.el (exwm-manage--unmanage-window) (exwm--on-ClientMessage): No need to modify workspace when an X window enters/leaves fullscreen mode. --- exwm-manage.el | 3 --- exwm.el | 17 +++++++---------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index dd7c2e3953..e067eda1f2 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -322,9 +322,6 @@ manager is shutting down." :window window :parent exwm--root :x 0 :y 0)) (xcb:+request exwm--connection (make-instance 'xcb:DestroyWindow :window container)))) - ;; Restore the workspace if this X window is currently fullscreen. - (when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) - (exwm-workspace--set-fullscreen exwm--frame)) (exwm-manage--set-client-list) (xcb:flush exwm--connection)) (let ((kill-buffer-func diff --git a/exwm.el b/exwm.el index 5cba3785f2..0b78b6b0b9 100644 --- a/exwm.el +++ b/exwm.el @@ -480,16 +480,13 @@ (when (and (not buffer) (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN props) (= action xcb:ewmh:_NET_WM_STATE_ADD)) - (dolist (f exwm-workspace--list) - (when (equal (frame-parameter f 'exwm-outer-id) id) - (exwm-workspace--set-fullscreen f) - (xcb:+request - exwm--connection - (make-instance 'xcb:ewmh:set-_NET_WM_STATE - :window id - :data (vector - xcb:Atom:_NET_WM_STATE_FULLSCREEN))) - (xcb:flush exwm--connection)))) + (xcb:+request + exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_STATE + :window id + :data (vector + xcb:Atom:_NET_WM_STATE_FULLSCREEN))) + (xcb:flush exwm--connection)) (when buffer ;ensure it's managed (with-current-buffer buffer ;; _NET_WM_STATE_FULLSCREEN -- cgit 1.4.1 From c821f76dfef9d7592b263f8fea789b6da7fbddf4 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 20 Feb 2018 22:17:22 +0800 Subject: Use the 'exwm-randr-output' frame parameter to determine the output * exwm-randr.el (exwm-randr--refresh): Always set the name of primary output. * exwm-workspace.el (exwm-workspace-switch) (exwm-workspace-move-window): Use 'exwm-randr-output' frame parameter to check if two frames are on the same output. --- exwm-randr.el | 9 ++++---- exwm-workspace.el | 68 ++++++++++++++++++++++++------------------------------- 2 files changed, 35 insertions(+), 42 deletions(-) diff --git a/exwm-randr.el b/exwm-randr.el index f49073c455..39edb7b1b9 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -89,7 +89,7 @@ the first one in result being the primary output." (defun exwm-randr--refresh () "Refresh workspaces according to the updated RandR info." - (let (output-name geometry output-plist default-geometry) + (let (output-name geometry output-plist primary-output default-geometry) ;; Query all outputs (with-slots (config-timestamp outputs) (xcb:+request-unchecked+reply exwm--connection @@ -115,8 +115,9 @@ the first one in result being the primary output." :x x :y y :width width :height height) output-plist (plist-put output-plist output-name geometry)) - (unless default-geometry ;assume the first output as primary - (setq default-geometry geometry))))))) + (unless primary-output + (setq primary-output output-name + default-geometry geometry))))))) (exwm--log "(randr) outputs: %s" output-plist) (when output-plist (when exwm-workspace--fullscreen-frame-count @@ -128,7 +129,7 @@ the first one in result being the primary output." (frame (elt exwm-workspace--list i))) (unless geometry (setq geometry default-geometry - output nil)) + output primary-output)) (set-frame-parameter frame 'exwm-randr-output output) (set-frame-parameter frame 'exwm-geometry geometry))) ;; Update workareas. diff --git a/exwm-workspace.el b/exwm-workspace.el index 9b0bfeb338..5bd746a897 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -528,18 +528,14 @@ for internal use only." ;; Set a default minibuffer frame. (setq default-minibuffer-frame frame)) ;; Show/Hide X windows. - (let ((hide-x-windows-on-old-frame - (with-slots ((x1 x) - (y1 y)) - (exwm-workspace--get-geometry frame) - (with-slots ((x2 x) - (y2 y)) - (exwm-workspace--get-geometry old-frame) - (and (= x1 x2) (= y1 y2)))))) + (let ((hide-old-workspace + (and old-frame + (eq (frame-parameter old-frame 'exwm-randr-output) + (frame-parameter frame 'exwm-randr-output))))) (dolist (i exwm--id-buffer-alist) (with-current-buffer (cdr i) (if (eq old-frame exwm--frame) - (when hide-x-windows-on-old-frame + (when hide-old-workspace (exwm-layout--hide exwm--id)) (when (eq frame exwm--frame) (let ((window (get-buffer-window nil t))) @@ -703,7 +699,7 @@ INDEX must not exceed the current number of workspaces." (exwm-workspace--prompt-delete-allowed t)) (exwm-workspace--prompt-for-workspace "Move to [+/-]: ")))) (let ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index)) - x-old y-old x-new y-new should-hide old-frame container) + should-hide old-frame container) (unless id (setq id (exwm--buffer->id (window-buffer)))) (with-current-buffer (exwm--id->buffer id) (unless (eq exwm--frame frame) @@ -715,33 +711,23 @@ INDEX must not exceed the current number of workspaces." (concat " " name))))) (setq old-frame exwm--frame exwm--frame frame) - ;; Save the positions of new & old frames. - (with-slots ((x1 x) - (y1 y)) - (exwm-workspace--get-geometry old-frame) - (with-slots ((x2 x) - (y2 y)) - (exwm-workspace--get-geometry frame) - (setq x-old x1 - y-old y1 - x-new x2 - y-new y2))) - (if (and (= x-old x-new) - (= y-old y-new)) + (if (eq (frame-parameter old-frame 'exwm-randr-output) + (frame-parameter frame 'exwm-randr-output)) ;; Switch to a workspace on the same output. (setq should-hide t) ;; Check if this frame has the largest timestamp of that output. - (let ((timestamp (frame-parameter frame 'exwm-timestamp)) - (timestamp-active - (apply #'max - (mapcar (lambda (w) - (with-slots (x y) - (exwm-workspace--get-geometry w) - (if (and (= x x-new) - (= y y-new)) - (frame-parameter w 'exwm-timestamp) - -1))) - exwm-workspace--list)))) + (let* ((timestamp (frame-parameter frame 'exwm-timestamp)) + (output (frame-parameter frame 'exwm-randr-output)) + (timestamp-active + (apply #'max + (mapcar + (lambda (w) + (or (when (eq output + (frame-parameter w + 'exwm-randr-output)) + (frame-parameter w 'exwm-timestamp)) + -1)) + exwm-workspace--list)))) (when (< timestamp timestamp-active) ;; Switch to a workspace not active on another output. (setq should-hide t)))) @@ -762,14 +748,20 @@ INDEX must not exceed the current number of workspaces." ;; Floating. (setq container (frame-parameter exwm--floating-frame 'exwm-container)) - (unless (and (= x-old x-new) - (= y-old y-new)) + (unless (eq (frame-parameter old-frame 'exwm-randr-output) + (frame-parameter frame 'exwm-randr-output)) (with-slots (x y) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GetGeometry :drawable container)) - (setq x (+ x (- x-new x-old)) - y (+ y (- y-new y-old))) + (with-slots ((x1 x) + (y1 y)) + (exwm-workspace--get-geometry old-frame) + (with-slots ((x2 x) + (y2 y)) + (exwm-workspace--get-geometry frame) + (setq x (+ x (- x2 x1)) + y (+ y (- y2 y1))))) (exwm--set-geometry id x y nil nil) (exwm--set-geometry container x y nil nil))) (if (exwm-workspace--minibuffer-own-frame-p) -- cgit 1.4.1 From 86f2215be3db25a6b6aacd6f8b0cb132b049e034 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 20 Feb 2018 22:21:02 +0800 Subject: Fix unexpected focus change after a global key binding is activated * exwm-core.el (exwm--unlock): Select FocusChange events on the root window. * exwm-input.el (exwm-input--on-FocusIn): Input focus should stay on the current workspace when the root window receives a FocusIn event. --- exwm-core.el | 3 ++- exwm-input.el | 7 +++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 41c3b57724..f64a7f25fa 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -93,7 +93,8 @@ :window exwm--root :value-mask xcb:CW:EventMask :event-mask (eval-when-compile - (logior xcb:EventMask:SubstructureRedirect + (logior xcb:EventMask:FocusChange + xcb:EventMask:SubstructureRedirect xcb:EventMask:StructureNotify)))) (xcb:flush exwm--connection)) diff --git a/exwm-input.el b/exwm-input.el index 73a0dba6f2..8102eb2ca7 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -218,10 +218,9 @@ ARGS are additional arguments to CALLBACK." (xcb:unmarshal obj data) (with-slots (mode) obj ;; Revert input focus back to Emacs frame / X window when it's set on - ;; the root window or some workspace container. - (when (eq mode xcb:NotifyMode:Normal) - (x-focus-frame (selected-frame)) - (select-window (selected-window)))))) + ;; the root window. + (x-focus-frame exwm-workspace--current) + (select-window (frame-selected-window exwm-workspace--current))))) (defun exwm-input--on-EnterNotify (data _synthetic) "Handle EnterNotify events." -- cgit 1.4.1 From 6200417697544317ee91badd702654def9a1d645 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 22 Feb 2018 00:26:04 +0800 Subject: Grab global keys on top-level X windows * exwm-input.el (exwm-input--on-CreateNotify): New function for grabbing global keys on newly created X windows. (exwm-input--update-global-prefix-keys): Grab global keys on top-level X windows instead of the root window. (exwm-input--grab-global-prefix-keys): New function for grabbing global keys on X windows. (exwm-input--release-keyboard): Grab global keys in char-mode. (exwm-input--init): Select CreateNotify events. * exwm-core.el (exwm--unlock): * exwm-input.el (exwm-input--on-FocusIn, exwm-input--init): Do not handle FocusIn events on the root window. --- exwm-core.el | 3 +-- exwm-input.el | 80 +++++++++++++++++++++++++++++------------------------------ 2 files changed, 41 insertions(+), 42 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index f64a7f25fa..41c3b57724 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -93,8 +93,7 @@ :window exwm--root :value-mask xcb:CW:EventMask :event-mask (eval-when-compile - (logior xcb:EventMask:FocusChange - xcb:EventMask:SubstructureRedirect + (logior xcb:EventMask:SubstructureRedirect xcb:EventMask:StructureNotify)))) (xcb:flush exwm--connection)) diff --git a/exwm-input.el b/exwm-input.el index 8102eb2ca7..ea3d659340 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -212,16 +212,6 @@ ARGS are additional arguments to CALLBACK." (cdr exwm-input--timestamp-callback)) (setq exwm-input--timestamp-callback nil))))) -(defun exwm-input--on-FocusIn (data _synthetic) - "Handle FocusIn events." - (let ((obj (make-instance 'xcb:FocusIn))) - (xcb:unmarshal obj data) - (with-slots (mode) obj - ;; Revert input focus back to Emacs frame / X window when it's set on - ;; the root window. - (x-focus-frame exwm-workspace--current) - (select-window (frame-selected-window exwm-workspace--current))))) - (defun exwm-input--on-EnterNotify (data _synthetic) "Handle EnterNotify events." (let ((evt (make-instance 'xcb:EnterNotify)) @@ -428,42 +418,51 @@ ARGS are additional arguments to CALLBACK." (funcall exwm--on-KeyPress obj data) (exwm-input--on-KeyPress-char-mode obj)))) +(defun exwm-input--on-CreateNotify (data _synthetic) + "Handle CreateNotify events." + (let ((evt (make-instance 'xcb:CreateNotify))) + (xcb:unmarshal evt data) + (with-slots (window) evt + (exwm-input--grab-global-prefix-keys window)))) + (defun exwm-input--update-global-prefix-keys () "Update `exwm-input--global-prefix-keys'." (when exwm--connection - (let ((original exwm-input--global-prefix-keys) - keysym keycode grab-key) + (let ((original exwm-input--global-prefix-keys)) (setq exwm-input--global-prefix-keys nil) (dolist (i exwm-input--global-keys) (cl-pushnew (elt i 0) exwm-input--global-prefix-keys)) - ;; Stop here if the global prefix keys are update-to-date and - ;; there's no new workspace. (unless (equal original exwm-input--global-prefix-keys) - (setq grab-key (make-instance 'xcb:GrabKey - :owner-events 0 - :grab-window exwm--root - :modifiers nil - :key nil - :pointer-mode xcb:GrabMode:Async - :keyboard-mode xcb:GrabMode:Async)) - (dolist (k exwm-input--global-prefix-keys) - (setq keysym (xcb:keysyms:event->keysym exwm--connection k) - keycode (xcb:keysyms:keysym->keycode exwm--connection - (car keysym))) - (setf (slot-value grab-key 'modifiers) (cdr keysym) - (slot-value grab-key 'key) keycode) - (when (or (= 0 keycode) - (xcb:+request-checked+request-check exwm--connection - grab-key) - ;; Also grab this key with num-lock mask set. - (when (/= 0 xcb:keysyms:num-lock-mask) - (setf (slot-value grab-key 'modifiers) - (logior (cdr keysym) - xcb:keysyms:num-lock-mask)) - (xcb:+request-checked+request-check exwm--connection - grab-key))) - (user-error "[EXWM] Failed to grab key: %s" - (single-key-description k)))))))) + (apply #'exwm-input--grab-global-prefix-keys + (slot-value (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:QueryTree + :window exwm--root)) + 'children)))))) + +(defun exwm-input--grab-global-prefix-keys (&rest xwins) + (let ((req (make-instance 'xcb:GrabKey + :owner-events 0 + :grab-window nil + :modifiers nil + :key nil + :pointer-mode xcb:GrabMode:Async + :keyboard-mode xcb:GrabMode:Async)) + keysym keycode) + (dolist (k exwm-input--global-prefix-keys) + (setq keysym (xcb:keysyms:event->keysym exwm--connection k) + keycode (xcb:keysyms:keysym->keycode exwm--connection + (car keysym))) + (setf (slot-value req 'modifiers) (cdr keysym) + (slot-value req 'key) keycode) + (dolist (xwin xwins) + (setf (slot-value req 'grab-window) xwin) + (xcb:+request exwm--connection req) + ;; Also grab this key with num-lock mask set. + (when (/= 0 xcb:keysyms:num-lock-mask) + (setf (slot-value req 'modifiers) + (logior (cdr keysym) xcb:keysyms:num-lock-mask)) + (xcb:+request exwm--connection req)))) + (xcb:flush exwm--connection))) ;;;###autoload (defun exwm-input-set-key (key command) @@ -635,6 +634,7 @@ called interactively. Only invoke it non-interactively in configuration." :grab-window id :modifiers xcb:ModMask:Any)) (exwm--log "Failed to release keyboard for #x%x" id)) + (exwm-input--grab-global-prefix-keys id) (with-current-buffer (exwm--id->buffer id) (setq exwm--on-KeyPress #'exwm-input--on-KeyPress-char-mode)))) @@ -877,13 +877,13 @@ Its usage is the same with `exwm-input-set-simulation-keys'." ;; Attach event listeners (xcb:+event exwm--connection 'xcb:PropertyNotify #'exwm-input--on-PropertyNotify) + (xcb:+event exwm--connection 'xcb:CreateNotify #'exwm-input--on-CreateNotify) (xcb:+event exwm--connection 'xcb:KeyPress #'exwm-input--on-KeyPress) (xcb:+event exwm--connection 'xcb:ButtonPress #'exwm-input--on-ButtonPress) (xcb:+event exwm--connection 'xcb:ButtonRelease #'exwm-floating--stop-moveresize) (xcb:+event exwm--connection 'xcb:MotionNotify #'exwm-floating--do-moveresize) - (xcb:+event exwm--connection 'xcb:FocusIn #'exwm-input--on-FocusIn) (when mouse-autoselect-window (xcb:+event exwm--connection 'xcb:EnterNotify #'exwm-input--on-EnterNotify)) -- cgit 1.4.1 From 32b88f4bd0274e8ee1d4215ed52ee6d3c460c013 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 22 Feb 2018 00:31:57 +0800 Subject: Mark active workspaces on each output * exwm-randr.el (exwm-randr--refresh): Mark active workspaces. * exwm-workspace.el (exwm-workspace-switch): Use the marks to show/hide X windows when switching workspace; do not update timestamp. (exwm-workspace-move-window): Use the marks to hide X windows after moving them. (exwm-workspace--init): Update `frameset-filter-alist'. --- exwm-randr.el | 23 ++++++++++++++-- exwm-workspace.el | 79 +++++++++++++++++++++++++------------------------------ 2 files changed, 57 insertions(+), 45 deletions(-) diff --git a/exwm-randr.el b/exwm-randr.el index 39edb7b1b9..c78a4ec812 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -89,7 +89,8 @@ the first one in result being the primary output." (defun exwm-randr--refresh () "Refresh workspaces according to the updated RandR info." - (let (output-name geometry output-plist primary-output default-geometry) + (let (output-name geometry output-plist primary-output default-geometry + container-output-alist container-frame-alist) ;; Query all outputs (with-slots (config-timestamp outputs) (xcb:+request-unchecked+reply exwm--connection @@ -126,12 +127,30 @@ the first one in result being the primary output." (dotimes (i (exwm-workspace--count)) (let* ((output (plist-get exwm-randr-workspace-output-plist i)) (geometry (lax-plist-get output-plist output)) - (frame (elt exwm-workspace--list i))) + (frame (elt exwm-workspace--list i)) + (container (frame-parameter frame 'exwm-container))) (unless geometry (setq geometry default-geometry output primary-output)) + (setq container-output-alist (nconc `((,container . ,output)) + container-output-alist) + container-frame-alist (nconc `((,container . ,frame)) + container-frame-alist)) (set-frame-parameter frame 'exwm-randr-output output) (set-frame-parameter frame 'exwm-geometry geometry))) + ;; Update the 'exwm-active' frame parameter. + (dolist (xwin + (reverse + (slot-value (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:QueryTree + :window exwm--root)) + 'children))) + (let ((output (cdr (assq xwin container-output-alist)))) + (when output + (setq container-output-alist + (rassq-delete-all output container-output-alist)) + (set-frame-parameter (cdr (assq xwin container-frame-alist)) + 'exwm-active t)))) ;; Update workareas. (exwm-workspace--update-workareas) ;; Resize workspace. diff --git a/exwm-workspace.el b/exwm-workspace.el index 5bd746a897..6025ace41a 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -513,6 +513,36 @@ for internal use only." ;; Save the floating frame window selected on the previous workspace. (set-frame-parameter (buffer-local-value 'exwm--frame (window-buffer)) 'exwm-selected-window (selected-window))) + ;; Show/Hide X windows. + (let ((output-old (frame-parameter old-frame 'exwm-randr-output)) + (output-new (frame-parameter frame 'exwm-randr-output)) + (active-old (frame-parameter old-frame 'exwm-active)) + (active-new (frame-parameter frame 'exwm-active)) + workspaces-to-hide) + (cond + ((not active-old) + (set-frame-parameter frame 'exwm-active t)) + ((eq output-old output-new) + (set-frame-parameter old-frame 'exwm-active nil) + (set-frame-parameter frame 'exwm-active t) + (setq workspaces-to-hide (list old-frame))) + (active-new) + (t + (dolist (w exwm-workspace--list) + (when (and (frame-parameter w 'exwm-active) + (eq output-new + (frame-parameter w 'exwm-randr-output))) + (set-frame-parameter w 'exwm-active nil) + (setq workspaces-to-hide (append workspaces-to-hide (list w))))) + (set-frame-parameter frame 'exwm-active t))) + (dolist (i exwm--id-buffer-alist) + (with-current-buffer (cdr i) + (if (memq exwm--frame workspaces-to-hide) + (exwm-layout--hide exwm--id) + (when (eq frame exwm--frame) + (let ((window (get-buffer-window nil t))) + (when window + (exwm-layout--show exwm--id window)))))))) (select-window window) (x-focus-frame (window-frame window)) ;The real input focus. (set-frame-parameter frame 'exwm-selected-window nil) @@ -527,20 +557,6 @@ for internal use only." (exwm-workspace--resize-minibuffer-frame) ;; Set a default minibuffer frame. (setq default-minibuffer-frame frame)) - ;; Show/Hide X windows. - (let ((hide-old-workspace - (and old-frame - (eq (frame-parameter old-frame 'exwm-randr-output) - (frame-parameter frame 'exwm-randr-output))))) - (dolist (i exwm--id-buffer-alist) - (with-current-buffer (cdr i) - (if (eq old-frame exwm--frame) - (when hide-old-workspace - (exwm-layout--hide exwm--id)) - (when (eq frame exwm--frame) - (let ((window (get-buffer-window nil t))) - (when window - (exwm-layout--show exwm--id window)))))))) ;; Hide windows in other workspaces by preprending a space (unless exwm-workspace-show-all-buffers (dolist (i exwm--id-buffer-alist) @@ -550,8 +566,6 @@ for internal use only." (exwm-workspace-rename-buffer (if (eq frame exwm--frame) name (concat " " name))))))) - ;; Update frame's timestamp. - (set-frame-parameter frame 'exwm-timestamp (float-time)) ;; Update demands attention flag (set-frame-parameter frame 'exwm-urgency nil) ;; Update switch workspace history @@ -699,7 +713,7 @@ INDEX must not exceed the current number of workspaces." (exwm-workspace--prompt-delete-allowed t)) (exwm-workspace--prompt-for-workspace "Move to [+/-]: ")))) (let ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index)) - should-hide old-frame container) + old-frame container) (unless id (setq id (exwm--buffer->id (window-buffer)))) (with-current-buffer (exwm--id->buffer id) (unless (eq exwm--frame frame) @@ -711,26 +725,6 @@ INDEX must not exceed the current number of workspaces." (concat " " name))))) (setq old-frame exwm--frame exwm--frame frame) - (if (eq (frame-parameter old-frame 'exwm-randr-output) - (frame-parameter frame 'exwm-randr-output)) - ;; Switch to a workspace on the same output. - (setq should-hide t) - ;; Check if this frame has the largest timestamp of that output. - (let* ((timestamp (frame-parameter frame 'exwm-timestamp)) - (output (frame-parameter frame 'exwm-randr-output)) - (timestamp-active - (apply #'max - (mapcar - (lambda (w) - (or (when (eq output - (frame-parameter w - 'exwm-randr-output)) - (frame-parameter w 'exwm-timestamp)) - -1)) - exwm-workspace--list)))) - (when (< timestamp timestamp-active) - ;; Switch to a workspace not active on another output. - (setq should-hide t)))) (if (not exwm--floating-frame) ;; Tiling. (progn @@ -743,7 +737,7 @@ INDEX must not exceed the current number of workspaces." (exwm--id->buffer id)) (if (eq frame exwm-workspace--current) (select-window (frame-selected-window frame)) - (when should-hide + (unless (frame-parameter frame 'exwm-active) (exwm-layout--hide id)))) ;; Floating. (setq container (frame-parameter exwm--floating-frame @@ -768,7 +762,7 @@ INDEX must not exceed the current number of workspaces." (if (eq frame exwm-workspace--current) (select-window (frame-root-window exwm--floating-frame)) (select-window (frame-selected-window exwm-workspace--current)) - (when should-hide + (unless (frame-parameter frame 'exwm-active) (exwm-layout--hide id))) ;; The frame needs to be recreated since it won't use the ;; minibuffer on the new workspace. @@ -829,7 +823,7 @@ INDEX must not exceed the current number of workspaces." (if (eq frame exwm-workspace--current) (with-current-buffer (exwm--id->buffer id) (select-window (frame-root-window exwm--floating-frame))) - (when should-hide + (unless (frame-parameter frame 'exwm-active) (exwm-layout--hide id))))) ;; Update the 'exwm-selected-window' frame parameter. (when (not (eq frame exwm-workspace--current)) @@ -1507,9 +1501,8 @@ applied to all subsequently created X frames." (exwm-workspace-switch 0 t) ;; Prevent frame parameters introduced by this module from being ;; saved/restored. - (dolist (i '(exwm-outer-id exwm-id exwm-container exwm-geometry - exwm-selected-window exwm-timestamp exwm-urgency - fullscreen)) + (dolist (i '(exwm-active exwm-outer-id exwm-id exwm-container exwm-geometry + exwm-selected-window exwm-urgency fullscreen)) (push (cons i :never) frameset-filter-alist))) (defun exwm-workspace--exit () -- cgit 1.4.1 From 1b6272e4585c5176705944f397932d9a9de212e6 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Thu, 22 Feb 2018 22:21:54 +0800 Subject: Hide blocked frames (they are visible with a compositor) * exwm-workspace.el (exwm-workspace--set-active): New function for setting the 'exwm-active' frame parameter and show/hide frames BTW. (exwm-workspace--active-p): New function checking whether a frame is active. (exwm-workspace--set-fullscreen, exwm-workspace-switch) (exwm-workspace-move-window): * exwm-randr.el (exwm-randr--refresh): Use them. * exwm-workspace.el (exwm-workspace-attach-minibuffer) (exwm-workspace--show-minibuffer, exwm-workspace--hide-minibuffer): Show/Hide the minibuffer frame. --- exwm-randr.el | 11 +++++--- exwm-workspace.el | 76 ++++++++++++++++++++++++++++++++++++------------------- 2 files changed, 58 insertions(+), 29 deletions(-) diff --git a/exwm-randr.el b/exwm-randr.el index c78a4ec812..a3593ec05b 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -81,7 +81,10 @@ the first one in result being the primary output." (defvar exwm-workspace--fullscreen-frame-count) (defvar exwm-workspace--list) +(declare-function exwm-workspace--active-p "exwm-workspace.el" (frame)) (declare-function exwm-workspace--count "exwm-workspace.el") +(declare-function exwm-workspace--set-active "exwm-workspace.el" + (frame active)) (declare-function exwm-workspace--set-desktop-geometry "exwm-workspace.el" ()) (declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame)) (declare-function exwm-workspace--show-minibuffer "exwm-workspace.el" ()) @@ -138,7 +141,9 @@ the first one in result being the primary output." container-frame-alist)) (set-frame-parameter frame 'exwm-randr-output output) (set-frame-parameter frame 'exwm-geometry geometry))) - ;; Update the 'exwm-active' frame parameter. + ;; Update active/inactive workspaces. + (dolist (w exwm-workspace--list) + (exwm-workspace--set-active w nil)) (dolist (xwin (reverse (slot-value (xcb:+request-unchecked+reply exwm--connection @@ -149,8 +154,8 @@ the first one in result being the primary output." (when output (setq container-output-alist (rassq-delete-all output container-output-alist)) - (set-frame-parameter (cdr (assq xwin container-frame-alist)) - 'exwm-active t)))) + (exwm-workspace--set-active (cdr (assq xwin container-frame-alist)) + t)))) ;; Update workareas. (exwm-workspace--update-workareas) ;; Resize workspace. diff --git a/exwm-workspace.el b/exwm-workspace.el index 6025ace41a..a0a8d0db5b 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -369,6 +369,18 @@ NIL if FRAME is not a workspace" (xcb:flush exwm--connection)) (run-hooks 'exwm-workspace--update-workareas-hook)) +(defun exwm-workspace--set-active (frame active) + "Make frame FRAME active on its output." + (set-frame-parameter frame 'exwm-active active) + (if active + (exwm-workspace--set-fullscreen frame) + (exwm--set-geometry (frame-parameter frame 'exwm-container) nil nil 1 1) + (xcb:flush exwm--connection))) + +(defun exwm-workspace--active-p (frame) + "Return non-nil if FRAME is active" + (frame-parameter frame 'exwm-active)) + (defun exwm-workspace--set-fullscreen (frame) "Make frame FRAME fullscreen according to `exwm-workspace--workareas'." (let ((workarea (elt exwm-workspace--workareas @@ -383,7 +395,9 @@ NIL if FRAME is not a workspace" (when (and (eq frame exwm-workspace--current) (exwm-workspace--minibuffer-own-frame-p)) (exwm-workspace--resize-minibuffer-frame)) - (exwm--set-geometry container x y width height) + (if (exwm-workspace--active-p frame) + (exwm--set-geometry container x y width height) + (exwm--set-geometry container x y 1 1)) (exwm--set-geometry id nil nil width height) (xcb:flush exwm--connection)) ;; This is only used for workspace initialization. @@ -516,25 +530,25 @@ for internal use only." ;; Show/Hide X windows. (let ((output-old (frame-parameter old-frame 'exwm-randr-output)) (output-new (frame-parameter frame 'exwm-randr-output)) - (active-old (frame-parameter old-frame 'exwm-active)) - (active-new (frame-parameter frame 'exwm-active)) + (active-old (exwm-workspace--active-p old-frame)) + (active-new (exwm-workspace--active-p frame)) workspaces-to-hide) (cond ((not active-old) - (set-frame-parameter frame 'exwm-active t)) + (exwm-workspace--set-active frame t)) ((eq output-old output-new) - (set-frame-parameter old-frame 'exwm-active nil) - (set-frame-parameter frame 'exwm-active t) + (exwm-workspace--set-active old-frame nil) + (exwm-workspace--set-active frame t) (setq workspaces-to-hide (list old-frame))) (active-new) (t (dolist (w exwm-workspace--list) - (when (and (frame-parameter w 'exwm-active) + (when (and (exwm-workspace--active-p w) (eq output-new (frame-parameter w 'exwm-randr-output))) - (set-frame-parameter w 'exwm-active nil) + (exwm-workspace--set-active w nil) (setq workspaces-to-hide (append workspaces-to-hide (list w))))) - (set-frame-parameter frame 'exwm-active t))) + (exwm-workspace--set-active frame t))) (dolist (i exwm--id-buffer-alist) (with-current-buffer (cdr i) (if (memq exwm--frame workspaces-to-hide) @@ -737,7 +751,7 @@ INDEX must not exceed the current number of workspaces." (exwm--id->buffer id)) (if (eq frame exwm-workspace--current) (select-window (frame-selected-window frame)) - (unless (frame-parameter frame 'exwm-active) + (unless (exwm-workspace--active-p frame) (exwm-layout--hide id)))) ;; Floating. (setq container (frame-parameter exwm--floating-frame @@ -762,7 +776,7 @@ INDEX must not exceed the current number of workspaces." (if (eq frame exwm-workspace--current) (select-window (frame-root-window exwm--floating-frame)) (select-window (frame-selected-window exwm-workspace--current)) - (unless (frame-parameter frame 'exwm-active) + (unless (exwm-workspace--active-p frame) (exwm-layout--hide id))) ;; The frame needs to be recreated since it won't use the ;; minibuffer on the new workspace. @@ -823,7 +837,7 @@ INDEX must not exceed the current number of workspaces." (if (eq frame exwm-workspace--current) (with-current-buffer (exwm--id->buffer id) (select-window (frame-root-window exwm--floating-frame))) - (unless (frame-parameter frame 'exwm-active) + (unless (exwm-workspace--active-p frame) (exwm-layout--hide id))))) ;; Update the 'exwm-selected-window' frame parameter. (when (not (eq frame exwm-workspace--current)) @@ -935,6 +949,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (redisplay) ;FIXME. (setq exwm-workspace--attached-minibuffer-height (frame-pixel-height exwm-workspace--minibuffer)) + (exwm-workspace--show-minibuffer) (let ((container (frame-parameter exwm-workspace--minibuffer 'exwm-container))) (push (cons container @@ -945,8 +960,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (exwm-workspace--update-struts) (exwm-workspace--update-workareas) (dolist (f exwm-workspace--list) - (exwm-workspace--set-fullscreen f)) - (exwm-workspace--show-minibuffer)))) + (exwm-workspace--set-fullscreen f))))) ;;;###autoload (defun exwm-workspace-detach-minibuffer () @@ -1050,6 +1064,12 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (cancel-timer exwm-workspace--display-echo-area-timer) (setq exwm-workspace--display-echo-area-timer nil)) ;; Show the minibuffer frame. + (unless (exwm-workspace--minibuffer-attached-p) + (exwm--set-geometry (frame-parameter exwm-workspace--minibuffer + 'exwm-container) + nil nil + (frame-pixel-width exwm-workspace--minibuffer) + (frame-pixel-height exwm-workspace--minibuffer))) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window (frame-parameter exwm-workspace--minibuffer @@ -1061,18 +1081,22 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (defun exwm-workspace--hide-minibuffer () "Hide the minibuffer frame." ;; Hide the minibuffer frame. - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window (frame-parameter exwm-workspace--minibuffer - 'exwm-container) - :value-mask (logior (if exwm-manage--desktop - xcb:ConfigWindow:Sibling - 0) - xcb:ConfigWindow:StackMode) - :sibling exwm-manage--desktop - :stack-mode (if exwm-manage--desktop - xcb:StackMode:Above - xcb:StackMode:Below))) + (if (exwm-workspace--minibuffer-attached-p) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window (frame-parameter exwm-workspace--minibuffer + 'exwm-container) + :value-mask (logior (if exwm-manage--desktop + xcb:ConfigWindow:Sibling + 0) + xcb:ConfigWindow:StackMode) + :sibling exwm-manage--desktop + :stack-mode (if exwm-manage--desktop + xcb:StackMode:Above + xcb:StackMode:Below))) + (exwm--set-geometry (frame-parameter exwm-workspace--minibuffer + 'exwm-container) + nil nil 1 1)) (xcb:flush exwm--connection)) (defun exwm-workspace--on-minibuffer-setup () -- cgit 1.4.1 From c719c3f54b242b06df1a7a9db05db648fa26bc2b Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 24 Feb 2018 22:49:39 +0800 Subject: Bump version to 0.17 --- exwm.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm.el b/exwm.el index 0b78b6b0b9..ed251f30ad 100644 --- a/exwm.el +++ b/exwm.el @@ -4,8 +4,8 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0.16 -;; Package-Requires: ((xelb "0.12")) +;; Version: 0.17 +;; Package-Requires: ((xelb "0.13")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From a50058be786bfa58d24c839dd1c9b9267f10003f Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 26 Feb 2018 00:15:44 +0800 Subject: Add Customize interface for global keys * exwm-input.el (exwm-input--set-key): New function for actually setting the binding. (exwm-input-global-keys): New user option for customizing global keys. (exwm-input-set-key): Update `exwm-input-global-keys' and call `exwm-input--set-key' (exwm-input--init): Initialize global keys. --- exwm-input.el | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index ea3d659340..edb7eb390c 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -464,15 +464,45 @@ ARGS are additional arguments to CALLBACK." (xcb:+request exwm--connection req)))) (xcb:flush exwm--connection))) +(defun exwm-input--set-key (key command) + (global-set-key key command) + (cl-pushnew key exwm-input--global-keys)) + +(defcustom exwm-input-global-keys nil + "Global keys. + +It is an alist of the form (key . command), meaning giving KEY (a key +sequence) a global binding as COMMAND. + +Notes: +* Setting the value directly (rather than customizing it) after EXWM + finishes initialization has no effect." + :type '(alist :key-type key-sequence :value-type function) + :set (lambda (symbol value) + (when (boundp symbol) + (dolist (i (symbol-value symbol)) + (global-unset-key (car i)))) + (set symbol value) + (setq exwm-input--global-keys nil) + (dolist (i value) + (exwm-input--set-key (car i) (cdr i))) + (when exwm--connection + (exwm-input--update-global-prefix-keys)))) + ;;;###autoload (defun exwm-input-set-key (key command) "Set a global key binding. The new key binding only takes effect in real time when this command is -called interactively. Only invoke it non-interactively in configuration." +called interactively, and is lost when this session ends unless it's +specifically saved in the Customize interface for `exwm-input-global-keys'. + +In configuration you should customize or set `exwm-input-global-keys' +instead." (interactive "KSet key globally: \nCSet key %s to command: ") - (global-set-key key command) - (cl-pushnew key exwm-input--global-keys) + (setq exwm-input-global-keys (append exwm-input-global-keys + (list (cons key command)))) + (exwm-input--set-key key command) (when (called-interactively-p 'any) (exwm-input--update-global-prefix-keys))) @@ -871,6 +901,9 @@ Its usage is the same with `exwm-input-set-simulation-keys'." :name-len (length atom) :name atom)) 'atom))) + ;; Initialize global keys. + (dolist (i exwm-input-global-keys) + (exwm-input--set-key (car i) (cdr i))) ;; Initialize simulation keys. (when exwm-input-simulation-keys (exwm-input-set-simulation-keys exwm-input-simulation-keys)) -- cgit 1.4.1 From 7cef4320cca8aeba5c4c6df37842bd73f16996d9 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 26 Feb 2018 00:19:28 +0800 Subject: Improve the Customize interface for simulation keys * exwm-input.el (exwm-input--set-simulation-keys): New function for actullay setting simulation keys. (exwm-input-simulation-keys, exwm-input-set-local-simulation-keys) (exwm-input--init): Use this instead of `exwm-input-set-simulation-keys'. (exwm-input-set-simulation-keys): Make obsolete. (exwm-input--read-keys): New function for reading an arbitrary key sequence. (exwm-input-set-simulation-key): New command for setting a new simulation key (which can be saved in the Customize interface). --- exwm-input.el | 84 +++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 28 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index edb7eb390c..fa9c1eb1d5 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -754,30 +754,20 @@ multiple keys." (setq keys (vconcat keys (vector key))) (exwm-input--fake-key key)))) -;; (defun exwm-input-send-last-key () -;; (interactive) -;; (unless (listp last-input-event) ;not a key event -;; (exwm-input--fake-key last-input-event))) - -(defun exwm-input-set-simulation-keys (simulation-keys) - "Set simulation keys. - -SIMULATION-KEYS is an alist of the form (original-key . simulated-key), -where both original-key and simulated-key are key sequences. - -Simulation keys set this way take effect in real time. For configuration -it's recommended to customize or set `exwm-input-simulation-keys' instead." - ;; Clear keymaps and the hash table. - (when (hash-table-p exwm-input--simulation-keys) - (maphash (lambda (key _value) - (when (sequencep key) - (if exwm-input--local-simulation-keys - (local-unset-key key) - (define-key exwm-mode-map key nil)))) - exwm-input--simulation-keys) - (clrhash exwm-input--simulation-keys)) - ;; Update the hash table. - (setq exwm-input--simulation-keys (make-hash-table :test #'equal)) +(defun exwm-input--set-simulation-keys (simulation-keys &optional no-refresh) + "Set simulation keys." + (unless no-refresh + ;; Clear keymaps and the hash table. + (when (hash-table-p exwm-input--simulation-keys) + (maphash (lambda (key _value) + (when (sequencep key) + (if exwm-input--local-simulation-keys + (local-unset-key key) + (define-key exwm-mode-map key nil)))) + exwm-input--simulation-keys) + (clrhash exwm-input--simulation-keys)) + ;; Update the hash table. + (setq exwm-input--simulation-keys (make-hash-table :test #'equal))) (dolist (i simulation-keys) (let ((original (vconcat (car i))) (simulated (cdr i))) @@ -798,6 +788,11 @@ it's recommended to customize or set `exwm-input-simulation-keys' instead." #'exwm-input-send-simulation-key)))) exwm-input--simulation-keys)) +(defun exwm-input-set-simulation-keys (simulation-keys) + "Please customize or set `exwm-input-simulation-keys' instead." + (declare (obsolete nil "26")) + (exwm-input--set-simulation-keys simulation-keys)) + (defcustom exwm-input-simulation-keys nil "Simulation keys. @@ -833,7 +828,39 @@ Notes: (key-sequence :tag "User-defined"))) :set (lambda (symbol value) (set symbol value) - (exwm-input-set-simulation-keys value))) + (exwm-input--set-simulation-keys value))) + +(cl-defun exwm-input--read-keys (prompt stop-key) + (let ((cursor-in-echo-area t) + keys key) + (while (not (eq key stop-key)) + (setq key (read-key (format "%s (terminate with %s): %s" + prompt + (key-description (vector stop-key)) + (key-description keys))) + keys (vconcat keys (vector key)))) + (substring keys 0 -1))) + +;;;###autoload +(defun exwm-input-set-simulation-key (original-key simulated-key) + "Set a simulation key. + +The simulation key takes effect in real time, but is lost when this session +ends unless it's specifically saved in the Customize interface for +`exwm-input-simulation-keys'." + (interactive + (let (original simulated) + (setq original (exwm-input--read-keys "Original keys" ?\C-g)) + (when original + (setq simulated (exwm-input--read-keys + (format "Simulate %s as" (key-description original)) + ?\C-g))) + (list original simulated))) + (when (and original-key simulated-key) + (let ((entry `((,original-key . ,simulated-key)))) + (setq exwm-input-simulation-keys (append exwm-input-simulation-keys + entry)) + (exwm-input--set-simulation-keys entry t)))) (defun exwm-input--unset-simulation-keys () "Clear simulation keys and key bindings defined." @@ -847,11 +874,12 @@ Notes: (defun exwm-input-set-local-simulation-keys (simulation-keys) "Set buffer-local simulation keys. -Its usage is the same with `exwm-input-set-simulation-keys'." +SIMULATION-KEYS is an alist of the form (original-key . simulated-key), +where both ORIGINAL-KEY and SIMULATED-KEY are key sequences." (make-local-variable 'exwm-input--simulation-keys) (use-local-map (copy-keymap exwm-mode-map)) (let ((exwm-input--local-simulation-keys t)) - (exwm-input-set-simulation-keys simulation-keys))) + (exwm-input--set-simulation-keys simulation-keys))) ;;;###autoload (cl-defun exwm-input-send-simulation-key (times) @@ -906,7 +934,7 @@ Its usage is the same with `exwm-input-set-simulation-keys'." (exwm-input--set-key (car i) (cdr i))) ;; Initialize simulation keys. (when exwm-input-simulation-keys - (exwm-input-set-simulation-keys exwm-input-simulation-keys)) + (exwm-input--set-simulation-keys exwm-input-simulation-keys)) ;; Attach event listeners (xcb:+event exwm--connection 'xcb:PropertyNotify #'exwm-input--on-PropertyNotify) -- cgit 1.4.1 From a6cfe3f22d4dff076e3e5da6676c411a52d6590e Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 26 Feb 2018 00:23:40 +0800 Subject: Forward keys defined in `exwm-mode-map' to Emacs by default * exwm-input.el (exwm-input-prefix-keys): Remove ?\C-c. (exwm-input--on-KeyPress-line-mode): * exwm-core.el (): Detect keys in `exwm-mode-map' in line-mode. --- exwm-core.el | 1 + exwm-input.el | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 41c3b57724..649ef0420b 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -189,6 +189,7 @@ least SECS seconds later." (active-minibuffer-window) (memq last-input-event exwm-input--global-prefix-keys) (memq last-input-event exwm-input-prefix-keys) + (assq last-input-event (cdr exwm-mode-map)) (gethash last-input-event exwm-input--simulation-keys)) (set-transient-map (make-composed-keymap (list exwm-mode-map global-map))) diff --git a/exwm-input.el b/exwm-input.el index fa9c1eb1d5..e051603bad 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -44,8 +44,12 @@ :group 'exwm) (defcustom exwm-input-prefix-keys - '(?\C-c ?\C-x ?\C-u ?\C-h ?\M-x ?\M-` ?\M-& ?\M-:) - "List of prefix keys EXWM should forward to Emacs when in line-mode." + '(?\C-x ?\C-u ?\C-h ?\M-x ?\M-` ?\M-& ?\M-:) + "List of prefix keys EXWM should forward to Emacs when in line-mode. + +The point is to make keys like 'C-x C-f' forwarded to Emacs in line-mode. +There is no need to add prefix keys for global/simulation keys or those +defined in `exwm-mode-map' here." :type '(repeat key-sequence) :get (lambda (symbol) (mapcar #'vector (default-value symbol))) @@ -562,6 +566,7 @@ instead." ;; (memq event exwm-input--global-prefix-keys) (memq event exwm-input-prefix-keys) + (assq event (cdr exwm-mode-map)) (gethash event exwm-input--simulation-keys))) (setq mode xcb:Allow:AsyncKeyboard) (exwm-input--cache-event event)) -- cgit 1.4.1 From ad90af19a6b3db0e9aa987b7b3125b9e1533cc71 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 26 Feb 2018 00:25:11 +0800 Subject: Make `exwm-manage-ping-timeout' a user option * exwm-manage.el (exwm-manage-ping-timeout): Now a user option. --- exwm-manage.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index e067eda1f2..64f8028579 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -43,6 +43,10 @@ corresponding buffer." You can still make the X windows floating afterwards." :type 'boolean) +(defcustom exwm-manage-ping-timeout 3 + "Seconds to wait before killing a client." + :type 'integer) + ;; FIXME: Make the following values as small as possible. (defconst exwm-manage--height-delta-min 5) (defconst exwm-manage--width-delta-min 5) @@ -59,8 +63,6 @@ You can still make the X windows floating afterwards." (defvar exwm-manage--ping-lock nil "Non-nil indicates EXWM is pinging a window.") -(defvar exwm-manage-ping-timeout 3 "Seconds to wait before killing a client.") - (defvar exwm-workspace--current) (defvar exwm-workspace--id-struts-alist) (defvar exwm-workspace--list) -- cgit 1.4.1 From 98c8fd4bc5b819501e90cfe9b7e7e25de5cc35ea Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 2 Mar 2018 00:49:27 +0800 Subject: Improve multi-monitor performance * exwm-randr.el (exwm-randr--refresh): Use GetScreenResourcesCurrent instead of GetScreenResources to avoid polling hardware changes. (exwm-randr--init): GetScreenResourcesCurrent requires RandR 1.3 . --- exwm-randr.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/exwm-randr.el b/exwm-randr.el index a3593ec05b..c1d2bf5255 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -97,7 +97,7 @@ the first one in result being the primary output." ;; Query all outputs (with-slots (config-timestamp outputs) (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:randr:GetScreenResources + (make-instance 'xcb:randr:GetScreenResourcesCurrent :window exwm--root)) (dolist (output outputs) (with-slots (crtc connection name) @@ -178,8 +178,8 @@ the first one in result being the primary output." (with-slots (major-version minor-version) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:randr:QueryVersion - :major-version 1 :minor-version 2)) - (if (or (/= major-version 1) (< minor-version 2)) + :major-version 1 :minor-version 3)) + (if (or (/= major-version 1) (< minor-version 3)) (error "[EXWM] The server only support RandR version up to %d.%d" major-version minor-version) ;; External monitor(s) may already be connected. -- cgit 1.4.1 From dbc06b50ff7f6cac5279b38be8f58706a0e00bb0 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 2 Mar 2018 00:52:03 +0800 Subject: Add initial support for per-application configurations * exwm-core.el (exwm--configurations): New buffer-local variable recording the configurations of an X window. * exwm-manage.el (exwm-manage-configurations): New user option as the per-application configurations. (exwm-manage--get-configurations): New function for fetching the configurations of this X window. (exwm-manage--manage-window): Fetch the configurations and check if the X window should be floating/tiling. --- exwm-core.el | 1 + exwm-manage.el | 50 +++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 649ef0420b..0f48de2a10 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -130,6 +130,7 @@ least SECS seconds later." ;; Internal variables (defvar-local exwm--id nil) ;window ID +(defvar-local exwm--configurations nil) ;initial configurations. (defvar-local exwm--frame nil) ;workspace frame (defvar-local exwm--floating-frame nil) ;floating frame (defvar-local exwm--mode-line-format nil) ;save mode-line-format diff --git a/exwm-manage.el b/exwm-manage.el index 64f8028579..3954d3ea24 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -47,6 +47,26 @@ You can still make the X windows floating afterwards." "Seconds to wait before killing a client." :type 'integer) +(defcustom exwm-manage-configurations nil + "Per-application configurations." + :type '(alist :key-type (sexp :tag "Matching criterion" nil) + :value-type + (plist :tag "Configurations" + :key-type + (choice + (const :tag "Floating" floating) + (const :tag "X" x) + (const :tag "Y" y) + (const :tag "Width" width) + (const :tag "Height" height) + (const :tag "Border width" border-width) + (const :tag "Char-mode" char-mode) + (const :tag "Prefix keys" prefix-keys) + (const :tag "Simulation keys" simulation-keys) + ;; For forward compatibility. + (other)) + :value-type (sexp :tag "Value" nil)))) + ;; FIXME: Make the following values as small as possible. (defconst exwm-manage--height-delta-min 5) (defconst exwm-manage--width-delta-min 5) @@ -140,6 +160,14 @@ You can still make the X windows floating afterwards." :window exwm--root :data (vconcat (mapcar #'car exwm--id-buffer-alist))))) +(cl-defun exwm-manage--get-configurations () + "Retrieve configurations for this buffer." + (when (derived-mode-p 'exwm-mode) + (dolist (i exwm-manage-configurations) + (save-current-buffer + (when (eval (car i)) + (cl-return-from exwm-manage--get-configurations (cdr i))))))) + (defun exwm-manage--manage-window (id) "Manage window ID." (exwm--log "Try to manage #x%x" id) @@ -169,6 +197,7 @@ You can still make the X windows floating afterwards." (exwm--update-hints id) (exwm-manage--update-geometry id) (exwm-manage--update-mwm-hints id) + (setq exwm--configurations (exwm-manage--get-configurations)) ;; No need to manage (please check OverrideRedirect outside) (when (or (not @@ -252,13 +281,20 @@ You can still make the X windows floating afterwards." (xcb:flush exwm--connection) (exwm--update-title id) (exwm--update-protocols id) - (if (and (not exwm-manage-force-tiling) - (or exwm-transient-for exwm--fixed-size - (memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY exwm-window-type) - (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG - exwm-window-type))) - (exwm-floating--set-floating id) - (exwm-floating--unset-floating id)) + (if (plist-member exwm--configurations 'floating) + ;; User has specified whether it should be floating. + (if (plist-get exwm--configurations 'floating) + (exwm-floating--set-floating id) + (exwm-floating--unset-floating id)) + ;; Try to determine if it should be floating. + (if (and (not exwm-manage-force-tiling) + (or exwm-transient-for exwm--fixed-size + (memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY + exwm-window-type) + (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG + exwm-window-type))) + (exwm-floating--set-floating id) + (exwm-floating--unset-floating id))) (exwm-input-grab-keyboard id) (setq exwm-workspace--switch-history-outdated t) (exwm--update-desktop id) -- cgit 1.4.1 From 0d1a32312409a6196724d9a69035ea9f7137ed24 Mon Sep 17 00:00:00 2001 From: Johan Johansson <96.bryal@gmail.com> Date: Fri, 2 Mar 2018 17:55:36 +0100 Subject: Fix EXWM buffers not being hidden on workspace switch * exwm-workspace.el (exwm-workspace-switch, exwm-workspace-move-window): Use `equal' to compare RandR output names. --- exwm-workspace.el | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index a0a8d0db5b..b423b26fe1 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -536,7 +536,7 @@ for internal use only." (cond ((not active-old) (exwm-workspace--set-active frame t)) - ((eq output-old output-new) + ((equal output-old output-new) (exwm-workspace--set-active old-frame nil) (exwm-workspace--set-active frame t) (setq workspaces-to-hide (list old-frame))) @@ -544,8 +544,8 @@ for internal use only." (t (dolist (w exwm-workspace--list) (when (and (exwm-workspace--active-p w) - (eq output-new - (frame-parameter w 'exwm-randr-output))) + (equal output-new + (frame-parameter w 'exwm-randr-output))) (exwm-workspace--set-active w nil) (setq workspaces-to-hide (append workspaces-to-hide (list w))))) (exwm-workspace--set-active frame t))) @@ -756,8 +756,8 @@ INDEX must not exceed the current number of workspaces." ;; Floating. (setq container (frame-parameter exwm--floating-frame 'exwm-container)) - (unless (eq (frame-parameter old-frame 'exwm-randr-output) - (frame-parameter frame 'exwm-randr-output)) + (unless (equal (frame-parameter old-frame 'exwm-randr-output) + (frame-parameter frame 'exwm-randr-output)) (with-slots (x y) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GetGeometry -- cgit 1.4.1 From e141ee684763d93cba02022d76274aeaaba407e5 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 3 Mar 2018 01:00:28 +0800 Subject: Add various per-application configurations * exwm-manage.el (exwm-manage-configurations): Add options for fullscreen mode and floating & tiling mode-line formats. (exwm-manage--manage-window): Add support for configuring line-mode/char-mode, prefix keys, simulation keys and fullscreen mode. * exwm-floating.el (exwm-floating--set-floating): Add support for configuring the geometry, mode-line format and border width of a floating X window. (exwm-floating--unset-floating): Add support for configuring the mode-line format of a tiling X window. --- exwm-floating.el | 69 ++++++++++++++++++++++++++++++++++++++++++++++++-------- exwm-manage.el | 23 +++++++++++++++---- 2 files changed, 78 insertions(+), 14 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 5a2c61e878..f9b1402fe7 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -176,7 +176,32 @@ This is also used by X window containers.") (when (= 0 height) (setq height (/ height* 2))) ;; Make sure at least half of the window is visible (unless (< y* (+ y (/ height 2)) (+ y* height*)) - (setq y (+ y* (/ (- height* height) 2)))))) + (setq y (+ y* (/ (- height* height) 2))))) + ;; The geometry can be overridden by user options. + (let ((x** (plist-get exwm--configurations 'x)) + (y** (plist-get exwm--configurations 'y)) + (width** (plist-get exwm--configurations 'width)) + (height** (plist-get exwm--configurations 'height))) + (if (integerp x**) + (setq x (+ x* x**)) + (when (and (floatp x**) + (>= 1 x** 0)) + (setq x (+ x* (round (* x** width*)))))) + (if (integerp y**) + (setq y (+ y* y**)) + (when (and (floatp y**) + (>= 1 y** 0)) + (setq y (+ y* (round (* y** height*)))))) + (if (integerp width**) + (setq width width**) + (when (and (floatp width**) + (> 1 width** 0)) + (setq width (max 1 (round (* width** width*)))))) + (if (integerp height**) + (setq height height**) + (when (and (floatp height**) + (> 1 height** 0)) + (setq height (max 1 (round (* height** height*)))))))) (exwm--set-geometry id x y nil nil) (xcb:flush exwm--connection) (exwm--log "Floating geometry (corrected): %dx%d%+d%+d" width height x y) @@ -189,13 +214,23 @@ This is also used by X window containers.") (frame-width (+ width (- (frame-pixel-width frame) (- (elt edges 2) (elt edges 0))))) (frame-height (+ height (- (frame-pixel-height frame) - (- (elt edges 3) (elt edges 1)))))) - ;; Check `exwm--mwm-hints-decorations'. - (unless exwm--mwm-hints-decorations - (setq frame-height (- frame-height (window-mode-line-height - (frame-root-window frame))) - exwm--mode-line-format mode-line-format - mode-line-format nil)) + (- (elt edges 3) (elt edges 1))))) + (floating-mode-line (plist-get exwm--configurations + 'floating-mode-line))) + (if floating-mode-line + (setq exwm--mode-line-format (or exwm--mode-line-format + mode-line-format) + mode-line-format floating-mode-line) + (if (and (not (plist-member exwm--configurations 'floating-mode-line)) + exwm--mwm-hints-decorations) + (when exwm--mode-line-format + (setq mode-line-format exwm--mode-line-format)) + ;; The mode-line need to be hidden in floating mode. + (setq frame-height (- frame-height (window-mode-line-height + (frame-root-window frame))) + exwm--mode-line-format (or exwm--mode-line-format + mode-line-format) + mode-line-format nil))) (set-frame-size frame frame-width frame-height t) ;; Create the frame container as the parent of the frame. (xcb:+request exwm--connection @@ -207,7 +242,14 @@ This is also used by X window containers.") :y (- y (elt edges 1)) :width width :height height - :border-width exwm-floating-border-width + :border-width + (with-current-buffer (exwm--id->buffer id) + (let ((border-witdh (plist-get exwm--configurations + 'border-width))) + (if (and (integerp border-witdh) + (>= border-witdh 0)) + border-witdh + exwm-floating-border-width))) :class xcb:WindowClass:InputOutput :visual 0 :value-mask (logior xcb:CW:BackPixmap @@ -326,7 +368,14 @@ This is also used by X window containers.") (delete-frame exwm--floating-frame)))) (with-current-buffer buffer (setq window-size-fixed nil - exwm--floating-frame nil)) + exwm--floating-frame nil) + (if (not (plist-member exwm--configurations 'tiling-mode-line)) + (when exwm--mode-line-format + (setq mode-line-format exwm--mode-line-format)) + (setq exwm--mode-line-format (or exwm--mode-line-format + mode-line-format) + mode-line-format (plist-get exwm--configurations + 'tiling-mode-line)))) ;; Only show X windows in normal state. (unless (exwm-layout--iconic-state-p) (pop-to-buffer-same-window buffer))) diff --git a/exwm-manage.el b/exwm-manage.el index 3954d3ea24..00914475f1 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -60,6 +60,9 @@ You can still make the X windows floating afterwards." (const :tag "Width" width) (const :tag "Height" height) (const :tag "Border width" border-width) + (const :tag "Fullscreen" fullscreen) + (const :tag "Floating mode-line" floating-mode-line) + (const :tag "Tiling mode-line" tiling-mode-line) (const :tag "Char-mode" char-mode) (const :tag "Prefix keys" prefix-keys) (const :tag "Simulation keys" simulation-keys) @@ -83,6 +86,7 @@ You can still make the X windows floating afterwards." (defvar exwm-manage--ping-lock nil "Non-nil indicates EXWM is pinging a window.") +(defvar exwm-input-prefix-keys) (defvar exwm-workspace--current) (defvar exwm-workspace--id-struts-alist) (defvar exwm-workspace--list) @@ -101,6 +105,7 @@ You can still make the X windows floating afterwards." (declare-function exwm-floating--set-floating "exwm-floating.el" (id)) (declare-function exwm-floating--unset-floating "exwm-floating.el" (id)) (declare-function exwm-input-grab-keyboard "exwm-input.el") +(declare-function exwm-input-set-local-simulation-keys "exwm-input.el") (declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) (declare-function exwm-workspace--count "exwm-workspace.el" ()) (declare-function exwm-workspace--position "exwm-workspace.el" (frame)) @@ -295,14 +300,24 @@ You can still make the X windows floating afterwards." exwm-window-type))) (exwm-floating--set-floating id) (exwm-floating--unset-floating id))) - (exwm-input-grab-keyboard id) + (if (plist-get exwm--configurations 'char-mode) + (exwm-input-release-keyboard id) + (exwm-input-grab-keyboard id)) + (let ((simulation-keys (plist-get exwm--configurations 'simulation-keys)) + (prefix-keys (plist-get exwm--configurations 'prefix-keys))) + (with-current-buffer (exwm--id->buffer id) + (when simulation-keys + (exwm-input-set-local-simulation-keys simulation-keys)) + (when prefix-keys + (setq-local exwm-input-prefix-keys prefix-keys)))) (setq exwm-workspace--switch-history-outdated t) (exwm--update-desktop id) (exwm-manage--update-ewmh-state id) (with-current-buffer (exwm--id->buffer id) - (when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) - (setq exwm--ewmh-state - (delq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) + (when (or (plist-get exwm--configurations 'fullscreen) + (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) + (setq exwm--ewmh-state (delq xcb:Atom:_NET_WM_STATE_FULLSCREEN + exwm--ewmh-state)) (exwm-layout-set-fullscreen id)) (run-hooks 'exwm-manage-finish-hook))))) -- cgit 1.4.1 From cc974555db6056ac799b3ce67234aec4e019cf84 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 4 Mar 2018 01:36:36 +0800 Subject: Add prefix arguments support for various commands * exwm-workspace.el (exwm-workspace-switch) (exwm-workspace-switch-create, exwm-workspace-move) (exwm-workspace-move-window): Add prefix arguments support. --- exwm-workspace.el | 57 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index b423b26fe1..0651b575d8 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -500,12 +500,18 @@ workspace frame as the first option or making use of the rest options are for internal use only." (interactive (list - (unless (and (eq major-mode 'exwm-mode) - ;; The prompt is invisible in fullscreen mode. - (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) - (let ((exwm-workspace--prompt-add-allowed t) - (exwm-workspace--prompt-delete-allowed t)) - (exwm-workspace--prompt-for-workspace "Switch to [+/-]: "))))) + (cond + ((null current-prefix-arg) + (unless (and (eq major-mode 'exwm-mode) + ;; The prompt is invisible in fullscreen mode. + (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) + (let ((exwm-workspace--prompt-add-allowed t) + (exwm-workspace--prompt-delete-allowed t)) + (exwm-workspace--prompt-for-workspace "Switch to [+/-]: ")))) + ((and (integerp current-prefix-arg) + (<= 0 current-prefix-arg (exwm-workspace--count))) + current-prefix-arg) + (t 0)))) (let* ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index)) (old-frame exwm-workspace--current) (index (exwm-workspace--position frame)) @@ -600,7 +606,14 @@ for internal use only." "Switch to workspace INDEX or creating it first if it does not exist yet. Passing a workspace frame as the first option is for internal use only." - (interactive) + (interactive + (list + (cond + ((integerp current-prefix-arg) + current-prefix-arg) + (t 0)))) + (unless frame-or-index + (setq frame-or-index 0)) (if (or (framep frame-or-index) (< frame-or-index (exwm-workspace--count))) (exwm-workspace-switch frame-or-index) @@ -654,12 +667,18 @@ Passing a workspace frame as the first option is for internal use only." When called interactively, prompt for a workspace and move current one just before it." (interactive - (unless (and (eq major-mode 'exwm-mode) - ;; The prompt is invisible in fullscreen mode. - (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) - (list exwm-workspace--current - (exwm-workspace--position - (exwm-workspace--prompt-for-workspace "Move workspace to: "))))) + (cond + ((null current-prefix-arg) + (unless (and (eq major-mode 'exwm-mode) + ;; The prompt is invisible in fullscreen mode. + (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) + (list exwm-workspace--current + (exwm-workspace--position + (exwm-workspace--prompt-for-workspace "Move workspace to: "))))) + ((and (integerp current-prefix-arg) + (<= 0 current-prefix-arg (exwm-workspace--count))) + (list exwm-workspace--current current-prefix-arg)) + (t (list exwm-workspace--current 0)))) (let ((pos (exwm-workspace--position workspace)) flag start end index) (if (= nth pos) @@ -723,9 +742,15 @@ INDEX must not exceed the current number of workspaces." (defun exwm-workspace-move-window (frame-or-index &optional id) "Move window ID to workspace FRAME-OR-INDEX." (interactive (list - (let ((exwm-workspace--prompt-add-allowed t) - (exwm-workspace--prompt-delete-allowed t)) - (exwm-workspace--prompt-for-workspace "Move to [+/-]: ")))) + (cond + ((null current-prefix-arg) + (let ((exwm-workspace--prompt-add-allowed t) + (exwm-workspace--prompt-delete-allowed t)) + (exwm-workspace--prompt-for-workspace "Move to [+/-]: "))) + ((and (integerp current-prefix-arg) + (<= 0 current-prefix-arg (exwm-workspace--count))) + current-prefix-arg) + (t 0)))) (let ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index)) old-frame container) (unless id (setq id (exwm--buffer->id (window-buffer)))) -- cgit 1.4.1 From 7013b0122a79df24b93e1db44e4bebff8fe9acd4 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 4 Mar 2018 01:39:12 +0800 Subject: Add header-line format support in per-application configurations * exwm-manage.el (exwm-manage-configurations): * exwm-floating.el (exwm-floating--set-floating) (exwm-floating--unset-floating): Allow customizing header-line format for floating/tiling X windows. --- exwm-floating.el | 20 ++++++++++++++++++-- exwm-manage.el | 3 +++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index f9b1402fe7..0c8fc6fb23 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -216,7 +216,9 @@ This is also used by X window containers.") (frame-height (+ height (- (frame-pixel-height frame) (- (elt edges 3) (elt edges 1))))) (floating-mode-line (plist-get exwm--configurations - 'floating-mode-line))) + 'floating-mode-line)) + (floating-header-line (plist-get exwm--configurations + 'floating-header-line))) (if floating-mode-line (setq exwm--mode-line-format (or exwm--mode-line-format mode-line-format) @@ -231,6 +233,16 @@ This is also used by X window containers.") exwm--mode-line-format (or exwm--mode-line-format mode-line-format) mode-line-format nil))) + (if floating-header-line + (setq header-line-format floating-header-line) + (if (and (not (plist-member exwm--configurations + 'floating-header-line)) + exwm--mwm-hints-decorations) + (setq header-line-format nil) + ;; The header-line need to be hidden in floating header. + (setq frame-height (- frame-height (window-header-line-height + (frame-root-window frame))) + header-line-format nil))) (set-frame-size frame frame-width frame-height t) ;; Create the frame container as the parent of the frame. (xcb:+request exwm--connection @@ -375,7 +387,11 @@ This is also used by X window containers.") (setq exwm--mode-line-format (or exwm--mode-line-format mode-line-format) mode-line-format (plist-get exwm--configurations - 'tiling-mode-line)))) + 'tiling-mode-line))) + (if (not (plist-member exwm--configurations 'tiling-header-line)) + (setq header-line-format nil) + (setq header-line-format (plist-get exwm--configurations + 'tiling-header-line)))) ;; Only show X windows in normal state. (unless (exwm-layout--iconic-state-p) (pop-to-buffer-same-window buffer))) diff --git a/exwm-manage.el b/exwm-manage.el index 00914475f1..94aefd8c55 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -63,6 +63,9 @@ You can still make the X windows floating afterwards." (const :tag "Fullscreen" fullscreen) (const :tag "Floating mode-line" floating-mode-line) (const :tag "Tiling mode-line" tiling-mode-line) + (const :tag "Floating header-line" + floating-header-line) + (const :tag "Tiling header-line" tiling-header-line) (const :tag "Char-mode" char-mode) (const :tag "Prefix keys" prefix-keys) (const :tag "Simulation keys" simulation-keys) -- cgit 1.4.1 From 277377c718429555f75a88a85f336d80f3716dce Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 4 Mar 2018 12:21:53 +0800 Subject: Fix local simulation keys * exwm-input.el (exwm-input--set-simulation-keys): Try the local binding first do not clear the hash table (rely on GC). --- exwm-input.el | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index e051603bad..379442600f 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -762,16 +762,17 @@ multiple keys." (defun exwm-input--set-simulation-keys (simulation-keys &optional no-refresh) "Set simulation keys." (unless no-refresh - ;; Clear keymaps and the hash table. - (when (hash-table-p exwm-input--simulation-keys) - (maphash (lambda (key _value) - (when (sequencep key) - (if exwm-input--local-simulation-keys - (local-unset-key key) - (define-key exwm-mode-map key nil)))) - exwm-input--simulation-keys) - (clrhash exwm-input--simulation-keys)) - ;; Update the hash table. + ;; Unbind simulation keys. + (let ((hash (buffer-local-value 'exwm-input--simulation-keys + (current-buffer)))) + (when (hash-table-p hash) + (maphash (lambda (key _value) + (when (sequencep key) + (if exwm-input--local-simulation-keys + (local-unset-key key) + (define-key exwm-mode-map key nil)))) + hash))) + ;; Abandon the old hash table. (setq exwm-input--simulation-keys (make-hash-table :test #'equal))) (dolist (i simulation-keys) (let ((original (vconcat (car i))) -- cgit 1.4.1 From 46fe764634ff4fa0eff7ea87c3b96209a05c89be Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 4 Mar 2018 12:23:37 +0800 Subject: Fix fullscreen mode after switching workspace and back * exwm-layout.el (exwm-layout--show): Always set an X window in fullscreen mode the size in fullscreen. (exwm-layout-unset-fullscreen): Leave the fullscreen mode first. * exwm-layout.el (exwm-layout--fullscreen-p): New function telling whether the current buffer is in fullscreen mode. (exwm-layout-set-fullscreen, exwm-layout-unset-fullscreen) (exwm-layout-toggle-fullscreen): * exwm-manage.el (exwm-manage--manage-window) (exwm-manage--on-ConfigureRequest): * exwm-workspace.el (exwm-workspace-switch, exwm-workspace-swap) (exwm-workspace-move): * exwm.el (exwm-reset, exwm--on-ClientMessage): Use it. --- exwm-layout.el | 24 +++++++++++++++++++----- exwm-manage.el | 7 ++++--- exwm-workspace.el | 7 ++++--- exwm.el | 14 +++++--------- 4 files changed, 32 insertions(+), 20 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index 5a01f90032..998556bc89 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -69,6 +69,10 @@ (buffer-local-value 'exwm-state (exwm--id->buffer id)) exwm-state))) +(defun exwm-layout--fullscreen-p () + (when (derived-mode-p 'exwm-mode) + (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state))) + (defun exwm-layout--show (id &optional window) "Show window ID exactly fit in the Emacs window WINDOW." (exwm--log "Show #x%x in %s" id window) @@ -92,6 +96,16 @@ (exwm--set-geometry (frame-parameter exwm--floating-frame 'exwm-container) frame-x frame-y frame-width frame-height)) + (when (exwm-layout--fullscreen-p) + (with-slots ((x* x) + (y* y) + (width* width) + (height* height)) + (exwm-workspace--get-geometry exwm--frame) + (setq x x* + y y* + width width* + height height*))) (exwm--set-geometry id x y width height) (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window id)) (exwm-layout--set-state id xcb:icccm:WM_STATE:NormalState))) @@ -131,7 +145,7 @@ "Make window ID fullscreen." (interactive) (unless (and (or id (derived-mode-p 'exwm-mode)) - (not (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state))) + (not (exwm-layout--fullscreen-p))) (cl-return-from 'exwm-layout-set-fullscreen)) (with-current-buffer (if id (exwm--id->buffer id) (window-buffer)) ;; Expand the X window to fill the whole screen. @@ -158,9 +172,11 @@ "Restore window from fullscreen state." (interactive) (unless (and (or id (derived-mode-p 'exwm-mode)) - (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) + (exwm-layout--fullscreen-p)) (cl-return-from 'exwm-layout-unset-fullscreen)) (with-current-buffer (if id (exwm--id->buffer id) (window-buffer)) + (setq exwm--ewmh-state + (delq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) (if exwm--floating-frame (exwm-layout--show exwm--id (frame-root-window exwm--floating-frame)) (xcb:+request exwm--connection @@ -176,8 +192,6 @@ (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_STATE :window exwm--id :data [])) (xcb:flush exwm--connection) - (setq exwm--ewmh-state - (delq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) (call-interactively #'exwm-input-grab-keyboard))) ;;;###autoload @@ -188,7 +202,7 @@ (cl-return-from 'exwm-layout-toggle-fullscreen)) (when id (with-current-buffer (exwm--id->buffer id) - (if (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) + (if (exwm-layout--fullscreen-p) (exwm-reset) (exwm-layout-set-fullscreen id))))) diff --git a/exwm-manage.el b/exwm-manage.el index 94aefd8c55..b5c3f58dfd 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -109,6 +109,7 @@ You can still make the X windows floating afterwards." (declare-function exwm-floating--unset-floating "exwm-floating.el" (id)) (declare-function exwm-input-grab-keyboard "exwm-input.el") (declare-function exwm-input-set-local-simulation-keys "exwm-input.el") +(declare-function exwm-layout--fullscreen-p "exwm-layout.el" ()) (declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) (declare-function exwm-workspace--count "exwm-workspace.el" ()) (declare-function exwm-workspace--position "exwm-workspace.el" (frame)) @@ -318,7 +319,7 @@ You can still make the X windows floating afterwards." (exwm-manage--update-ewmh-state id) (with-current-buffer (exwm--id->buffer id) (when (or (plist-get exwm--configurations 'fullscreen) - (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) + (exwm-layout--fullscreen-p)) (setq exwm--ewmh-state (delq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) (exwm-layout-set-fullscreen id)) @@ -533,7 +534,7 @@ border-width: %d; sibling: #x%x; stack-mode: %d" border-width sibling stack-mode) (if (and (setq buffer (exwm--id->buffer window)) (with-current-buffer buffer - (or (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) + (or (exwm-layout--fullscreen-p) ;; Make sure it's a floating X window wanting to resize ;; itself. (or (not exwm--floating-frame) @@ -559,7 +560,7 @@ border-width: %d; sibling: #x%x; stack-mode: %d" ;; Send client message for managed windows (with-current-buffer buffer (setq edges - (if (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) + (if (exwm-layout--fullscreen-p) (with-slots (x y width height) (exwm-workspace--get-geometry exwm--frame) (list x y width height)) diff --git a/exwm-workspace.el b/exwm-workspace.el index 0651b575d8..deb6a65357 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -128,6 +128,7 @@ Please manually run the hook `exwm-workspace-list-change-hook' afterwards.") (defvar exwm-manage--desktop) (declare-function exwm--exit "exwm.el") (declare-function exwm-input--on-buffer-list-update "exwm-input.el" ()) +(declare-function exwm-layout--fullscreen-p "exwm-layout.el" ()) (declare-function exwm-layout--hide "exwm-layout.el" (id)) (declare-function exwm-layout--other-buffer-predicate "exwm-layout.el" (buffer)) @@ -504,7 +505,7 @@ for internal use only." ((null current-prefix-arg) (unless (and (eq major-mode 'exwm-mode) ;; The prompt is invisible in fullscreen mode. - (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) + (exwm-layout--fullscreen-p)) (let ((exwm-workspace--prompt-add-allowed t) (exwm-workspace--prompt-delete-allowed t)) (exwm-workspace--prompt-for-workspace "Switch to [+/-]: ")))) @@ -631,7 +632,7 @@ Passing a workspace frame as the first option is for internal use only." (interactive (unless (and (eq major-mode 'exwm-mode) ;; The prompt is invisible in fullscreen mode. - (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) + (exwm-layout--fullscreen-p)) (let (w1 w2) (let ((exwm-workspace--prompt-add-allowed t) (exwm-workspace--prompt-delete-allowed t)) @@ -671,7 +672,7 @@ before it." ((null current-prefix-arg) (unless (and (eq major-mode 'exwm-mode) ;; The prompt is invisible in fullscreen mode. - (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) + (exwm-layout--fullscreen-p)) (list exwm-workspace--current (exwm-workspace--position (exwm-workspace--prompt-for-workspace "Move workspace to: "))))) diff --git a/exwm.el b/exwm.el index ed251f30ad..0387426ffc 100644 --- a/exwm.el +++ b/exwm.el @@ -110,7 +110,7 @@ (interactive) (with-current-buffer (window-buffer) (when (eq major-mode 'exwm-mode) - (when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) + (when (exwm-layout--fullscreen-p) (exwm-layout-unset-fullscreen)) ;; Force refresh (exwm-layout--refresh) @@ -484,8 +484,7 @@ exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_STATE :window id - :data (vector - xcb:Atom:_NET_WM_STATE_FULLSCREEN))) + :data (vector xcb:Atom:_NET_WM_STATE_FULLSCREEN))) (xcb:flush exwm--connection)) (when buffer ;ensure it's managed (with-current-buffer buffer @@ -493,17 +492,14 @@ (when (or (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN props) (memq xcb:Atom:_NET_WM_STATE_ABOVE props)) (cond ((= action xcb:ewmh:_NET_WM_STATE_ADD) - (unless (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN - exwm--ewmh-state) + (unless (exwm-layout--fullscreen-p) (exwm-layout-set-fullscreen id)) (push xcb:Atom:_NET_WM_STATE_FULLSCREEN props-new)) ((= action xcb:ewmh:_NET_WM_STATE_REMOVE) - (when (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN - exwm--ewmh-state) + (when (exwm-layout--fullscreen-p) (exwm-layout-unset-fullscreen id))) ((= action xcb:ewmh:_NET_WM_STATE_TOGGLE) - (if (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN - exwm--ewmh-state) + (if (exwm-layout--fullscreen-p) (exwm-layout-unset-fullscreen id) (exwm-layout-set-fullscreen id) (push xcb:Atom:_NET_WM_STATE_FULLSCREEN props-new))))) -- cgit 1.4.1 From 57328b439fdc367519e3ce9f7c6ef2d3a4934cbc Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 4 Mar 2018 21:59:19 +0800 Subject: Avoid switching buffer when moving an X window already in place * exwm-workspace.el (exwm-workspace-move-window): This is required when calling `exwm-workspace-move-window' from `exwm-layout--refresh'. --- exwm-workspace.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index deb6a65357..a99971904c 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -767,7 +767,9 @@ INDEX must not exceed the current number of workspaces." exwm--frame frame) (if (not exwm--floating-frame) ;; Tiling. - (progn + (if (get-buffer-window nil frame) + (when (eq frame exwm-workspace--current) + (run-window-configuration-change-hook frame)) (set-window-buffer (get-buffer-window nil t) (other-buffer nil t)) (unless (eq frame exwm-workspace--current) -- cgit 1.4.1 From dd0f10775a1893abe3b5c753e9db12d1397c2f6b Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Mon, 5 Mar 2018 00:00:00 +0000 Subject: Support disabling default `exwm-mode-map' bindings * exwm-input.el (exwm-input--on-KeyPress-line-mode): Use `lookup-key' to check whether a the event is defined in the `exwm-mode-map' keymap. --- exwm-core.el | 2 +- exwm-input.el | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 0f48de2a10..7fd3a61211 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -190,7 +190,7 @@ least SECS seconds later." (active-minibuffer-window) (memq last-input-event exwm-input--global-prefix-keys) (memq last-input-event exwm-input-prefix-keys) - (assq last-input-event (cdr exwm-mode-map)) + (lookup-key exwm-mode-map (vector last-input-event)) (gethash last-input-event exwm-input--simulation-keys)) (set-transient-map (make-composed-keymap (list exwm-mode-map global-map))) diff --git a/exwm-input.el b/exwm-input.el index 379442600f..ee44624508 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -566,7 +566,7 @@ instead." ;; (memq event exwm-input--global-prefix-keys) (memq event exwm-input-prefix-keys) - (assq event (cdr exwm-mode-map)) + (lookup-key exwm-mode-map (vector event)) (gethash event exwm-input--simulation-keys))) (setq mode xcb:Allow:AsyncKeyboard) (exwm-input--cache-event event)) -- cgit 1.4.1 From 4660e040a0a21de58e47b1b18900a4d277af888d Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Tue, 6 Mar 2018 00:00:00 +0000 Subject: Prevent duplicate keys in frameset-filter-alist * exwm-workspace.el (exwm-workspace--init): * exwm-randr.el (exwm-randr--init): Prevent duplicate keys in frameset-filter-alist --- exwm-randr.el | 3 ++- exwm-workspace.el | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/exwm-randr.el b/exwm-randr.el index c1d2bf5255..37f40f12c1 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -210,7 +210,8 @@ the first one in result being the primary output." ;; Prevent frame parameters introduced by this module from being ;; saved/restored. (dolist (i '(exwm-randr-output)) - (push (cons i :never) frameset-filter-alist))) + (unless (assq i frameset-filter-alist) + (push (cons i :never) frameset-filter-alist)))) (defun exwm-randr--exit () "Exit the RandR module." diff --git a/exwm-workspace.el b/exwm-workspace.el index a99971904c..f1ca8d594e 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1555,7 +1555,8 @@ applied to all subsequently created X frames." ;; saved/restored. (dolist (i '(exwm-active exwm-outer-id exwm-id exwm-container exwm-geometry exwm-selected-window exwm-urgency fullscreen)) - (push (cons i :never) frameset-filter-alist))) + (unless (assq i frameset-filter-alist) + (push (cons i :never) frameset-filter-alist)))) (defun exwm-workspace--exit () "Exit the workspace module." -- cgit 1.4.1 From 81529c2e89e1a9ea757428d47de2a65211095d45 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Tue, 6 Mar 2018 00:00:00 +0000 Subject: Cleanup exwm-input on exit * exwm-input.el (exwm-input--exit): Remove missing hook reset window variable. --- exwm-input.el | 1 + 1 file changed, 1 insertion(+) diff --git a/exwm-input.el b/exwm-input.el index ee44624508..d194b53821 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -969,6 +969,7 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences." (defun exwm-input--exit () "Exit the input module." (exwm-input--unset-simulation-keys) + (remove-hook 'minibuffer-setup-hook #'exwm-input--on-minibuffer-setup) (remove-hook 'pre-command-hook #'exwm-input--on-pre-command) (remove-hook 'post-command-hook #'exwm-input--on-post-command) (remove-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update) -- cgit 1.4.1 From c00331a7e6e4282ca4e0cf1ee733fe642a6c6721 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Tue, 6 Mar 2018 00:00:00 +0000 Subject: Cleanup exwm-layout on exit * exwm-layout.el (exwm-layout--exit): Remove `window-pixel-width-before-size-change' hook. --- exwm-layout.el | 2 ++ 1 file changed, 2 insertions(+) diff --git a/exwm-layout.el b/exwm-layout.el index 998556bc89..babd37410a 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -514,6 +514,8 @@ See also `exwm-layout-enlarge-window'." (defun exwm-layout--exit () "Exit the layout module." (remove-hook 'window-configuration-change-hook #'exwm-layout--refresh) + (when (fboundp 'window-pixel-width-before-size-change) + (remove-hook 'window-size-change-functions #'exwm-layout--refresh)) (remove-hook 'minibuffer-setup-hook #'exwm-layout--on-minibuffer-setup) (when exwm-layout--timer (cancel-timer exwm-layout--timer) -- cgit 1.4.1 From 4f7abf4bfd41932fe23ce3e9544b8c425f1b2cd3 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Tue, 6 Mar 2018 00:00:00 +0000 Subject: Cleanup exwm-manage on exit * exwm-manage.el (exwm-manage--unmanage-window): Map windows when quitting. (exwm-manage--exit): Remap all windows. * exwm.el (exwm--exit): Reorder deinitialization sequence so that windows are reparented before the workspaces are removed. --- exwm-manage.el | 13 +++++++++++-- exwm.el | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index b5c3f58dfd..cb2bf28b17 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -361,12 +361,17 @@ manager is shutting down." (xcb:+request exwm--connection (make-instance 'xcb:DeleteProperty :window id :property xcb:Atom:WM_STATE)) - (unless (eq withdraw-only 'quit) + (cond + ((eq withdraw-only 'quit) + ;; Remap the window when exiting. + (xcb:+request exwm--connection + (make-instance 'xcb:MapWindow :window id))) + (t ;; Remove _NET_WM_DESKTOP. (xcb:+request exwm--connection (make-instance 'xcb:DeleteProperty :window id - :property xcb:Atom:_NET_WM_DESKTOP)))) + :property xcb:Atom:_NET_WM_DESKTOP))))) (when exwm--floating-frame ;; Unmap the floating frame before destroying its container. (let ((window (frame-parameter exwm--floating-frame 'exwm-outer-id)) @@ -670,6 +675,10 @@ border-width: %d; sibling: #x%x; stack-mode: %d" (defun exwm-manage--exit () "Exit the manage module." + (dolist (pair exwm--id-buffer-alist) + (exwm-manage--unmanage-window (car pair) 'quit)) + (remove-hook 'after-make-frame-functions #'exwm-manage--add-frame) + (remove-hook 'delete-frame-functions #'exwm-manage--remove-frame) (setq exwm-manage--_MOTIF_WM_HINTS nil)) diff --git a/exwm.el b/exwm.el index 0387426ffc..f80af05114 100644 --- a/exwm.el +++ b/exwm.el @@ -739,8 +739,8 @@ (run-hooks 'exwm-exit-hook) ;; Exit modules. (exwm-input--exit) - (exwm-workspace--exit) (exwm-manage--exit) + (exwm-workspace--exit) (exwm-floating--exit) (exwm-layout--exit) (exwm--exit-icccm-ewmh)) -- cgit 1.4.1 From d3be64e743b0e794b91743fa1fa31b721d1a3aca Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Tue, 6 Mar 2018 00:00:00 +0000 Subject: Factor out `exwm-randr--on-ScreenChangeNotify' * exwm-randr.el (exwm-randr--on-ScreenChangeNotify) (exwm-randr--init): Factor ScreenChangeNotify event callback into a function. --- exwm-randr.el | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/exwm-randr.el b/exwm-randr.el index 37f40f12c1..485d9c8826 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -170,6 +170,11 @@ the first one in result being the primary output." (xcb:flush exwm--connection) (run-hooks 'exwm-randr-refresh-hook)))) +(defun exwm-randr--on-ScreenChangeNotify (_data _synthetic) + (exwm--log "(RandR) ScreenChangeNotify") + (run-hooks 'exwm-randr-screen-change-hook) + (exwm-randr--refresh)) + (defun exwm-randr--init () "Initialize RandR extension and EXWM RandR module." (if (= 0 (slot-value (xcb:get-extension-data exwm--connection 'xcb:randr) @@ -186,10 +191,7 @@ the first one in result being the primary output." (run-hooks 'exwm-randr-screen-change-hook) (exwm-randr--refresh) (xcb:+event exwm--connection 'xcb:randr:ScreenChangeNotify - (lambda (_data _synthetic) - (exwm--log "(RandR) ScreenChangeNotify") - (run-hooks 'exwm-randr-screen-change-hook) - (exwm-randr--refresh))) + #'exwm-randr--on-ScreenChangeNotify) ;; (xcb:+event exwm--connection 'xcb:randr:Notify ;; (lambda (_data _synthetic) ;; (exwm--log "(RandR) Notify") -- cgit 1.4.1 From 33a1a284762c80dd3751ed152c872e3612b21a47 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Tue, 6 Mar 2018 00:00:00 +0000 Subject: Dissociate frame creation and deletion handlers from the actual configuration of frames as workspaces * exwm-workspace.el (exwm-workspace--add-frame-as-workspace) (exwm-workspace--remove-frame-as-workspace): Limit functionality to the configuration of frames as workspaces. (exwm-workspace--on-after-make-frame) (exwm-workspace--on-delete-frame): Callbacks run on frame creation and deletion that may use or stop them from being used as workspaces. --- exwm-workspace.el | 252 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 139 insertions(+), 113 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index f1ca8d594e..323f5fc34b 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1257,6 +1257,141 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (defun exwm-workspace--add-frame-as-workspace (frame) "Configure frame FRAME to be treated as a workspace." + (setq exwm-workspace--list (nconc exwm-workspace--list (list frame))) + (let ((outer-id (string-to-number (frame-parameter frame + 'outer-window-id))) + (window-id (string-to-number (frame-parameter frame 'window-id))) + (container (xcb:generate-id exwm--connection))) + ;; Save window IDs + (set-frame-parameter frame 'exwm-outer-id outer-id) + (set-frame-parameter frame 'exwm-id window-id) + (set-frame-parameter frame 'exwm-container container) + ;; In case it's created by emacsclient. + (set-frame-parameter frame 'client nil) + ;; Copy RandR frame parameters from the first workspace to + ;; prevent potential problems. The values do not matter here as + ;; they'll be updated by the RandR module later. + (let ((w (car exwm-workspace--list))) + (dolist (param '(exwm-randr-output + exwm-geometry)) + (set-frame-parameter frame param (frame-parameter w param)))) + (xcb:+request exwm--connection + (make-instance 'xcb:CreateWindow + :depth 0 + :wid container + :parent exwm--root + :x -1 + :y -1 + :width 1 + :height 1 + :border-width 0 + :class xcb:WindowClass:InputOutput + :visual 0 + :value-mask (logior xcb:CW:BackPixmap + xcb:CW:OverrideRedirect) + :background-pixmap xcb:BackPixmap:ParentRelative + :override-redirect 1)) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window container + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Below)) + (exwm--debug + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_NAME + :window container + :data + (format "EXWM workspace %d frame container" + (exwm-workspace--position frame))))) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window outer-id :parent container :x 0 :y 0)) + (xcb:+request exwm--connection + (make-instance 'xcb:MapWindow :window container))) + (xcb:flush exwm--connection) + ;; Delay making the workspace fullscreen until Emacs becomes idle + (exwm--defer 0 #'set-frame-parameter frame 'fullscreen 'fullboth) + ;; Update EWMH properties. + (exwm-workspace--update-ewmh-props) + (if exwm-workspace--create-silently + (setq exwm-workspace--switch-history-outdated t) + (exwm-workspace-switch frame t) + (run-hooks 'exwm-workspace-list-change-hook))) + +(defun exwm-workspace--remove-frame-as-workspace (frame) + "Stop treating frame FRAME as a workspace." + ;; TODO: restore all frame parameters (e.g. exwm-workspace, buffer-predicate, + ;; etc) + (exwm--log "Removing frame `%s' as workspace" frame) + (let* ((index (exwm-workspace--position frame)) + (lastp (= index (1- (exwm-workspace--count)))) + (nextw (elt exwm-workspace--list (+ index (if lastp -1 +1))))) + ;; Need to remove the workspace from the list in order for + ;; the correct calculation of indexes. + (setq exwm-workspace--list (delete frame exwm-workspace--list)) + ;; Clients need to be moved to some other workspace before this is being + ;; removed. + (dolist (pair exwm--id-buffer-alist) + (with-current-buffer (cdr pair) + (when (eq exwm--frame frame) + (exwm-workspace-move-window nextw exwm--id)))) + ;; Update the _NET_WM_DESKTOP property of each X window affected. + (dolist (pair exwm--id-buffer-alist) + (when (<= (1- index) + (exwm-workspace--position (buffer-local-value 'exwm--frame + (cdr pair)))) + (exwm-workspace--set-desktop (car pair)))) + ;; If the current workspace is deleted, switch to next one. + (when (eq frame exwm-workspace--current) + (exwm-workspace-switch nextw))) + ;; Reparent out the frame. + (let ((outer-id (frame-parameter frame 'exwm-outer-id))) + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow + :window outer-id)) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window outer-id + :parent exwm--root + :x 0 + :y 0)) + ;; Reset the override-redirect. + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window outer-id + :value-mask xcb:CW:OverrideRedirect + :override-redirect 0)) + (xcb:+request exwm--connection + (make-instance 'xcb:MapWindow + :window outer-id))) + ;; Destroy the container. + (xcb:+request exwm--connection + (make-instance 'xcb:DestroyWindow + :window (frame-parameter frame 'exwm-container))) + (xcb:flush exwm--connection) + ;; Update EWMH properties. + (exwm-workspace--update-ewmh-props) + ;; Update switch history. + (setq exwm-workspace--switch-history-outdated t) + (run-hooks 'exwm-workspace-list-change-hook)) + +(defun exwm-workspace--on-delete-frame (frame) + "Hook run upon `delete-frame' that tears down FRAME's configuration as a workspace." + (cond + ((not (exwm-workspace--workspace-p frame)) + (exwm--log "Frame `%s' is not a workspace" frame)) + (t + (when (= 1 (exwm-workspace--count)) + ;; The user managed to delete the last workspace, so create a new one. + (exwm--log "Last workspace deleted; create a new one") + ;; TODO: this makes sense in the hook. But we need a function that takes + ;; care of converting a workspace into a regular unmanaged frame. + (let ((exwm-workspace--create-silently t)) + (make-frame))) + (exwm-workspace--remove-frame-as-workspace frame)))) + +(defun exwm-workspace--on-after-make-frame (frame) + "Hook run upon `delete-frame' that configures FRAME as a workspace." (cond ((exwm-workspace--workspace-p frame) (exwm--log "Frame `%s' is already a workspace" frame)) @@ -1278,116 +1413,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (exwm--log "Frame `%s' is floating" frame)) (t (exwm--log "Adding frame `%s' as workspace" frame) - (setq exwm-workspace--list (nconc exwm-workspace--list (list frame))) - (let ((outer-id (string-to-number (frame-parameter frame - 'outer-window-id))) - (window-id (string-to-number (frame-parameter frame 'window-id))) - (container (xcb:generate-id exwm--connection))) - ;; Save window IDs - (set-frame-parameter frame 'exwm-outer-id outer-id) - (set-frame-parameter frame 'exwm-id window-id) - (set-frame-parameter frame 'exwm-container container) - ;; In case it's created by emacsclient. - (set-frame-parameter frame 'client nil) - ;; Copy RandR frame parameters from the first workspace to - ;; prevent potential problems. The values do not matter here as - ;; they'll be updated by the RandR module later. - (let ((w (car exwm-workspace--list))) - (dolist (param '(exwm-randr-output - exwm-geometry)) - (set-frame-parameter frame param (frame-parameter w param)))) - (xcb:+request exwm--connection - (make-instance 'xcb:CreateWindow - :depth 0 - :wid container - :parent exwm--root - :x -1 - :y -1 - :width 1 - :height 1 - :border-width 0 - :class xcb:WindowClass:InputOutput - :visual 0 - :value-mask (logior xcb:CW:BackPixmap - xcb:CW:OverrideRedirect) - :background-pixmap xcb:BackPixmap:ParentRelative - :override-redirect 1)) - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window container - :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:Below)) - (exwm--debug - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_WM_NAME - :window container - :data - (format "EXWM workspace %d frame container" - (exwm-workspace--position frame))))) - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window outer-id :parent container :x 0 :y 0)) - (xcb:+request exwm--connection - (make-instance 'xcb:MapWindow :window container))) - (xcb:flush exwm--connection) - ;; Delay making the workspace fullscreen until Emacs becomes idle - (exwm--defer 0 #'set-frame-parameter frame 'fullscreen 'fullboth) - ;; Update EWMH properties. - (exwm-workspace--update-ewmh-props) - (if exwm-workspace--create-silently - (setq exwm-workspace--switch-history-outdated t) - (exwm-workspace-switch frame t) - (run-hooks 'exwm-workspace-list-change-hook))))) - -(defun exwm-workspace--remove-frame-as-workspace (frame) - "Stop treating frame FRAME as a workspace." - (cond - ((not (exwm-workspace--workspace-p frame)) - (exwm--log "Frame `%s' is not a workspace" frame)) - (t - (exwm--log "Removing frame `%s' as workspace" frame) - (when (= 1 (exwm-workspace--count)) - ;; The user managed to delete the last workspace, so create a new one. - (exwm--log "Last workspace deleted; create a new one") - (let ((exwm-workspace--create-silently t)) - (make-frame))) - (let* ((index (exwm-workspace--position frame)) - (lastp (= index (1- (exwm-workspace--count)))) - (nextw (elt exwm-workspace--list (+ index (if lastp -1 +1))))) - ;; Need to remove the workspace from the list in order for - ;; the correct calculation of indexes. - (setq exwm-workspace--list (delete frame exwm-workspace--list)) - ;; Clients need to be moved to some other workspace before this is being - ;; removed. - (dolist (pair exwm--id-buffer-alist) - (with-current-buffer (cdr pair) - (when (eq exwm--frame frame) - (exwm-workspace-move-window nextw exwm--id)))) - ;; Update the _NET_WM_DESKTOP property of each X window affected. - (dolist (pair exwm--id-buffer-alist) - (when (<= (1- index) - (exwm-workspace--position (buffer-local-value 'exwm--frame - (cdr pair)))) - (exwm-workspace--set-desktop (car pair)))) - ;; If the current workspace is deleted, switch to next one. - (when (eq frame exwm-workspace--current) - (exwm-workspace-switch nextw))) - ;; Reparent out the frame. - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window (frame-parameter frame 'exwm-outer-id) - :parent exwm--root - :x 0 - :y 0)) - ;; Destroy the container. - (xcb:+request exwm--connection - (make-instance 'xcb:DestroyWindow - :window (frame-parameter frame 'exwm-container))) - ;; Update EWMH properties. - (exwm-workspace--update-ewmh-props) - ;; Update switch history. - (setq exwm-workspace--switch-history-outdated t) - (run-hooks 'exwm-workspace-list-change-hook)))) + (exwm-workspace--add-frame-as-workspace frame)))) (defun exwm-workspace--update-ewmh-props () "Update EWMH properties to match the workspace list." @@ -1546,7 +1572,7 @@ applied to all subsequently created X frames." (advice-add 'handle-focus-out :around #'exwm-workspace--handle-focus-out) ;; Make new frames create new workspaces. (add-hook 'after-make-frame-functions - #'exwm-workspace--add-frame-as-workspace) + #'exwm-workspace--on-after-make-frame) (add-hook 'delete-frame-functions #'exwm-workspace--remove-frame-as-workspace) ;; Switch to the first workspace @@ -1579,9 +1605,9 @@ applied to all subsequently created X frames." (advice-remove 'handle-focus-in #'exwm-workspace--handle-focus-in) (advice-remove 'handle-focus-out #'exwm-workspace--handle-focus-out) (remove-hook 'after-make-frame-functions - #'exwm-workspace--add-frame-as-workspace) + #'exwm-workspace--on-after-make-frame) (remove-hook 'delete-frame-functions - #'exwm-workspace--remove-frame-as-workspace)) + #'exwm-workspace--on-delete-frame)) (defun exwm-workspace--post-init () "The second stage in the initialization of the workspace module." -- cgit 1.4.1 From a51be88c1e9affde23ce06069e338bf3f62e26db Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Tue, 6 Mar 2018 00:00:00 +0000 Subject: Cleanup exwm-workspace on exit * exwm-workspace.el (exwm-workspace--confirm-kill-emacs): Move deinitialization to `exwm-workspace--exit'. (exwm-workspace--exit): Reparent minibuffer frame to the root window, turn workspace frames into regular frames, restore frame parameters. (exwm-workspace--init): Reset the fullscreen counter. (exwm-workspace--remove-frame-as-workspace): Reset fullscreen state. (exwm-workspace--init-minibuffer-frame) (exwm-workspace--exit-minibuffer-frame): New functions configuring the minibuffer frame. (exwm-workspace--init, exwm-workspace--exit): Use the above functions to configure the minibuffer frame. --- exwm-workspace.el | 249 +++++++++++++++++++++++++++++------------------------- 1 file changed, 132 insertions(+), 117 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 323f5fc34b..08d7a2863b 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1207,37 +1207,6 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (setq kill-emacs-hook (list #'server-force-stop))) (run-hooks 'kill-emacs-hook) (setq kill-emacs-hook nil)) - ;; Hide & reparent out all frames (save-set can't be used here since - ;; X windows will be re-mapped). - (when (exwm-workspace--minibuffer-own-frame-p) - (let ((id (frame-parameter exwm-workspace--minibuffer 'exwm-outer-id))) - (xcb:+request exwm--connection - (make-instance 'xcb:UnmapWindow - :window id)) - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window id - :parent exwm--root - :x 0 - :y 0)))) - (dolist (f exwm-workspace--list) - (let ((id (frame-parameter f 'exwm-outer-id))) - (xcb:+request exwm--connection - (make-instance 'xcb:UnmapWindow - :window id)) - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window id - :parent exwm--root - :x 0 - :y 0)))) - ;; Restore the 'client' frame parameter (before `exwm--exit'). - (when exwm-workspace--client - (dolist (f exwm-workspace--list) - (set-frame-parameter f 'client exwm-workspace--client)) - (when (exwm-workspace--minibuffer-own-frame-p) - (set-frame-parameter exwm-workspace--minibuffer 'client - exwm-workspace--client))) ;; Exit each module. (exwm--exit) ;; Destroy all resources created by this connection. @@ -1361,6 +1330,11 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." :window outer-id :value-mask xcb:CW:OverrideRedirect :override-redirect 0)) + ;; Remove fullscreen state. + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_STATE + :window outer-id + :data nil)) (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window outer-id))) @@ -1451,10 +1425,109 @@ applied to all subsequently created X frames." "Replacement for `handle-focus-out'." (interactive "e")) +(defun exwm-workspace--init-minibuffer-frame () + ;; Initialize workspaces without minibuffers. + (setq exwm-workspace--minibuffer + (make-frame '((window-system . x) (minibuffer . only) + (left . 10000) (right . 10000) + (width . 1) (height . 1) + (client . nil)))) + ;; This is the only usable minibuffer frame. + (setq default-minibuffer-frame exwm-workspace--minibuffer) + (exwm-workspace--modify-all-x-frames-parameters + '((minibuffer . nil))) + (let ((outer-id (string-to-number + (frame-parameter exwm-workspace--minibuffer + 'outer-window-id))) + (window-id (string-to-number + (frame-parameter exwm-workspace--minibuffer + 'window-id))) + (container (xcb:generate-id exwm--connection))) + (set-frame-parameter exwm-workspace--minibuffer + 'exwm-outer-id outer-id) + (set-frame-parameter exwm-workspace--minibuffer 'exwm-id window-id) + (set-frame-parameter exwm-workspace--minibuffer 'exwm-container + container) + (xcb:+request exwm--connection + (make-instance 'xcb:CreateWindow + :depth 0 + :wid container + :parent exwm--root + :x 0 + :y 0 + :width 1 + :height 1 + :border-width 0 + :class xcb:WindowClass:InputOutput + :visual 0 + :value-mask (logior xcb:CW:BackPixmap + xcb:CW:OverrideRedirect) + :background-pixmap xcb:BackPixmap:ParentRelative + :override-redirect 1)) + (exwm--debug + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_NAME + :window container + :data "Minibuffer container"))) + ;; Reparent the minibuffer frame to the container. + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window outer-id :parent container :x 0 :y 0)) + ;; Map the container. + (xcb:+request exwm--connection + (make-instance 'xcb:MapWindow + :window container)) + ;; Attach event listener for monitoring the frame + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window outer-id + :value-mask xcb:CW:EventMask + :event-mask xcb:EventMask:StructureNotify)) + (xcb:+event exwm--connection 'xcb:ConfigureNotify + #'exwm-workspace--on-ConfigureNotify)) + ;; Show/hide minibuffer / echo area when they're active/inactive. + (add-hook 'minibuffer-setup-hook #'exwm-workspace--on-minibuffer-setup) + (add-hook 'minibuffer-exit-hook #'exwm-workspace--on-minibuffer-exit) + (setq exwm-workspace--timer + (run-with-idle-timer 0 t #'exwm-workspace--on-echo-area-dirty)) + (add-hook 'echo-area-clear-hook #'exwm-workspace--on-echo-area-clear) + ;; The default behavior of `display-buffer' (indirectly called by + ;; `minibuffer-completion-help') is not correct here. + (cl-pushnew '(exwm-workspace--display-buffer) display-buffer-alist + :test #'equal)) + +(defun exwm-workspace--exit-minibuffer-frame () + ;; Only on minibuffer-frame. + (remove-hook 'minibuffer-setup-hook #'exwm-workspace--on-minibuffer-setup) + (remove-hook 'minibuffer-exit-hook #'exwm-workspace--on-minibuffer-exit) + (remove-hook 'echo-area-clear-hook #'exwm-workspace--on-echo-area-clear) + (when exwm-workspace--timer + (cancel-timer exwm-workspace--timer) + (setq exwm-workspace--timer nil)) + (setq display-buffer-alist + (cl-delete '(exwm-workspace--display-buffer) display-buffer-alist + :test #'equal)) + (setq default-minibuffer-frame nil) + (let ((id (frame-parameter exwm-workspace--minibuffer 'exwm-outer-id))) + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow + :window id)) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window id + :parent exwm--root + :x 0 + :y 0)) + (xcb:+request exwm--connection + (make-instance 'xcb:MapWindow + :window id))) + (setq exwm-workspace--minibuffer nil)) + (defun exwm-workspace--init () "Initialize workspace module." ;; Prevent unexpected exit (setq confirm-kill-emacs #'exwm-workspace--confirm-kill-emacs) + (setq exwm-workspace--fullscreen-frame-count 0) (exwm-workspace--modify-all-x-frames-parameters '((internal-border-width . 0))) (let ((initial-workspaces (frame-list))) @@ -1472,12 +1545,7 @@ applied to all subsequently created X frames." (set-frame-parameter f 'internal-border-width 0) ;; Prevent user from deleting the first frame by accident. (set-frame-parameter f 'client nil))) - ;; Initialize workspaces without minibuffers. - (setq exwm-workspace--minibuffer - (make-frame '((window-system . x) (minibuffer . only) - (left . 10000) (right . 10000) - (width . 1) (height . 1) - (client . nil)))) + (exwm-workspace--init-minibuffer-frame) ;; Remove/hide existing frames. (dolist (f initial-workspaces) (if (frame-parameter f 'client) @@ -1487,72 +1555,9 @@ applied to all subsequently created X frames." (make-frame-invisible f)) (when (eq 'x (framep f)) ;do not delete the initial frame. (delete-frame f)))) - ;; This is the only usable minibuffer frame. - (setq default-minibuffer-frame exwm-workspace--minibuffer) - (exwm-workspace--modify-all-x-frames-parameters - '((minibuffer . nil))) - (let ((outer-id (string-to-number - (frame-parameter exwm-workspace--minibuffer - 'outer-window-id))) - (window-id (string-to-number - (frame-parameter exwm-workspace--minibuffer - 'window-id))) - (container (xcb:generate-id exwm--connection))) - (set-frame-parameter exwm-workspace--minibuffer - 'exwm-outer-id outer-id) - (set-frame-parameter exwm-workspace--minibuffer 'exwm-id window-id) - (set-frame-parameter exwm-workspace--minibuffer 'exwm-container - container) - (xcb:+request exwm--connection - (make-instance 'xcb:CreateWindow - :depth 0 - :wid container - :parent exwm--root - :x 0 - :y 0 - :width 1 - :height 1 - :border-width 0 - :class xcb:WindowClass:InputOutput - :visual 0 - :value-mask (logior xcb:CW:BackPixmap - xcb:CW:OverrideRedirect) - :background-pixmap xcb:BackPixmap:ParentRelative - :override-redirect 1)) - (exwm--debug - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_WM_NAME - :window container - :data "Minibuffer container"))) - ;; Reparent the minibuffer frame to the container. - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window outer-id :parent container :x 0 :y 0)) - ;; Map the container. - (xcb:+request exwm--connection - (make-instance 'xcb:MapWindow - :window container)) - ;; Attach event listener for monitoring the frame - (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window outer-id - :value-mask xcb:CW:EventMask - :event-mask xcb:EventMask:StructureNotify)) - (xcb:+event exwm--connection 'xcb:ConfigureNotify - #'exwm-workspace--on-ConfigureNotify)) - ;; Show/hide minibuffer / echo area when they're active/inactive. - (add-hook 'minibuffer-setup-hook #'exwm-workspace--on-minibuffer-setup) - (add-hook 'minibuffer-exit-hook #'exwm-workspace--on-minibuffer-exit) - (setq exwm-workspace--timer - (run-with-idle-timer 0 t #'exwm-workspace--on-echo-area-dirty)) - (add-hook 'echo-area-clear-hook #'exwm-workspace--on-echo-area-clear) ;; Recreate one frame with the external minibuffer set. (setq initial-workspaces (list (make-frame '((window-system . x) - (client . nil))))) - ;; The default behavior of `display-buffer' (indirectly called by - ;; `minibuffer-completion-help') is not correct here. - (cl-pushnew '(exwm-workspace--display-buffer) display-buffer-alist - :test #'equal)) + (client . nil)))))) ;; Prevent `other-buffer' from selecting already displayed EXWM buffers. (modify-all-frames-parameters '((buffer-predicate . exwm-layout--other-buffer-predicate))) @@ -1586,28 +1591,38 @@ applied to all subsequently created X frames." (defun exwm-workspace--exit () "Exit the workspace module." - (setq confirm-kill-emacs nil - exwm-workspace--list nil - exwm-workspace--client nil - exwm-workspace--minibuffer nil - exwm-workspace--fullscreen-frame-count 0 - default-minibuffer-frame nil) - (remove-hook 'minibuffer-setup-hook #'exwm-workspace--on-minibuffer-setup) - (remove-hook 'minibuffer-exit-hook #'exwm-workspace--on-minibuffer-exit) - (when exwm-workspace--timer - (cancel-timer exwm-workspace--timer) - (setq exwm-workspace--timer nil)) - (remove-hook 'echo-area-clear-hook #'exwm-workspace--on-echo-area-clear) - (setq display-buffer-alist - (cl-delete '(exwm-workspace--display-buffer) display-buffer-alist - :test #'equal)) + (setq confirm-kill-emacs nil) + (when (exwm-workspace--minibuffer-own-frame-p) + (exwm-workspace--exit-minibuffer-frame)) (advice-remove 'x-create-frame #'exwm-workspace--x-create-frame) (advice-remove 'handle-focus-in #'exwm-workspace--handle-focus-in) (advice-remove 'handle-focus-out #'exwm-workspace--handle-focus-out) (remove-hook 'after-make-frame-functions #'exwm-workspace--on-after-make-frame) (remove-hook 'delete-frame-functions - #'exwm-workspace--on-delete-frame)) + #'exwm-workspace--on-delete-frame) + ;; Hide & reparent out all frames (save-set can't be used here since + ;; X windows will be re-mapped). + (setq exwm-workspace--current nil) + (dolist (i exwm-workspace--list) + (exwm-workspace--remove-frame-as-workspace i) + (modify-frame-parameters i '((exwm-selected-window . nil) + (exwm-urgency . nil) + (exwm-outer-id . nil) + (exwm-id . nil) + (exwm-container . nil) + ;; (internal-border-width . nil) ; integerp + ;; (client . nil) + (fullscreen . nil) + (buffer-predicate . nil)))) + ;; Restore the 'client' frame parameter (before `exwm--exit'). + (when exwm-workspace--client + (dolist (f exwm-workspace--list) + (set-frame-parameter f 'client exwm-workspace--client)) + (when (exwm-workspace--own-frame-p) + (set-frame-parameter exwm-workspace--minibuffer 'client + exwm-workspace--client)) + (setq exwm-workspace--client nil))) (defun exwm-workspace--post-init () "The second stage in the initialization of the workspace module." -- cgit 1.4.1 From a2b6cfb8787fc6388e7129de3620c2e5d910fb4e Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Tue, 6 Mar 2018 00:00:00 +0000 Subject: * exwm.el (exwm--exit): Drop SubstructureRedirect on root window. --- exwm.el | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/exwm.el b/exwm.el index f80af05114..eeafd0ba37 100644 --- a/exwm.el +++ b/exwm.el @@ -743,7 +743,11 @@ (exwm-workspace--exit) (exwm-floating--exit) (exwm-layout--exit) - (exwm--exit-icccm-ewmh)) + (exwm--exit-icccm-ewmh) + (xcb:+request-checked+request-check exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window exwm--root :value-mask xcb:CW:EventMask + :event-mask xcb:EventMask:NoEvent))) (defun exwm-enable (&optional undo) "Enable/Disable EXWM." -- cgit 1.4.1 From ce8af83ffb1ff56f31956b9343a035e2137bc08b Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Tue, 6 Mar 2018 00:00:00 +0000 Subject: Disconnect on `exwm--exit' * exwm.el (exwm-exit): Disconnect `exwm-connection'. * exwm.el (exwm-init, exwm-exit, exwm--confirm-kill-emacs) * exwm-workspace.el (exwm-workspace--set-desktop-geometry) (exwm-workspace--init, exwm-workspace--exit): Move `confirm-kill-emacs' to this exwm.el`. Delegate disconnection of `exwm--connection' to `exwm-exit'. --- exwm-workspace.el | 39 --------------------------------------- exwm.el | 44 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 42 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 08d7a2863b..a852eb8b7d 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -126,7 +126,6 @@ Please manually run the hook `exwm-workspace-list-change-hook' afterwards.") (defvar exwm-input--during-command) (defvar exwm-layout-show-all-buffers) (defvar exwm-manage--desktop) -(declare-function exwm--exit "exwm.el") (declare-function exwm-input--on-buffer-list-update "exwm-input.el" ()) (declare-function exwm-layout--fullscreen-p "exwm-layout.el" ()) (declare-function exwm-layout--hide "exwm-layout.el" (id)) @@ -1179,42 +1178,6 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (cancel-timer exwm-workspace--display-echo-area-timer) (setq exwm-workspace--display-echo-area-timer nil)))) -(defun exwm-workspace--confirm-kill-emacs (prompt &optional force) - "Confirm before exiting Emacs." - (when (cond - ((and force (not (eq force 'no-check))) - ;; Force killing Emacs. - t) - ((or (eq force 'no-check) (not exwm--id-buffer-alist)) - ;; Check if there's any unsaved file. - (pcase (catch 'break - (let ((kill-emacs-query-functions - (append kill-emacs-query-functions - (list (lambda () - (throw 'break 'break)))))) - (save-buffers-kill-emacs))) - (`break (y-or-n-p prompt)) - (x x))) - (t - (yes-or-no-p (format "[EXWM] %d window(s) will be destroyed. %s" - (length exwm--id-buffer-alist) prompt)))) - ;; Run `kill-emacs-hook' (`server-force-stop' excluded) before Emacs - ;; frames are unmapped so that errors (if any) can be visible. - (if (memq #'server-force-stop kill-emacs-hook) - (progn - (setq kill-emacs-hook (delq #'server-force-stop kill-emacs-hook)) - (run-hooks 'kill-emacs-hook) - (setq kill-emacs-hook (list #'server-force-stop))) - (run-hooks 'kill-emacs-hook) - (setq kill-emacs-hook nil)) - ;; Exit each module. - (exwm--exit) - ;; Destroy all resources created by this connection. - (xcb:disconnect exwm--connection) - (setq exwm--connection nil) - ;; Set the return value. - t)) - (defun exwm-workspace--set-desktop-geometry () "Set _NET_DESKTOP_GEOMETRY." ;; We don't support large desktop so it's the same with screen size. @@ -1526,7 +1489,6 @@ applied to all subsequently created X frames." (defun exwm-workspace--init () "Initialize workspace module." ;; Prevent unexpected exit - (setq confirm-kill-emacs #'exwm-workspace--confirm-kill-emacs) (setq exwm-workspace--fullscreen-frame-count 0) (exwm-workspace--modify-all-x-frames-parameters '((internal-border-width . 0))) @@ -1591,7 +1553,6 @@ applied to all subsequently created X frames." (defun exwm-workspace--exit () "Exit the workspace module." - (setq confirm-kill-emacs nil) (when (exwm-workspace--minibuffer-own-frame-p) (exwm-workspace--exit-minibuffer-frame)) (advice-remove 'x-create-frame #'exwm-workspace--x-create-frame) diff --git a/exwm.el b/exwm.el index eeafd0ba37..8c57a07df2 100644 --- a/exwm.el +++ b/exwm.el @@ -120,7 +120,7 @@ (defun exwm-restart () "Restart EXWM." (interactive) - (when (exwm-workspace--confirm-kill-emacs "[EXWM] Restart? " 'no-check) + (when (exwm--confirm-kill-emacs "[EXWM] Restart? " 'no-check) (let* ((attr (process-attributes (emacs-pid))) (args (cdr (assq 'args attr))) (ppid (cdr (assq 'ppid attr))) @@ -716,7 +716,8 @@ (setq exwm--connection nil) (exwm--log "Other window manager detected")) ;; Disable some features not working well with EXWM - (setq use-dialog-box nil) + (setq use-dialog-box nil + confirm-kill-emacs #'exwm--confirm-kill-emacs) ;; Initialize ICCCM/EWMH support (xcb:icccm:init exwm--connection t) (xcb:ewmh:init exwm--connection t) @@ -737,6 +738,7 @@ (defun exwm--exit () "Exit EXWM." (run-hooks 'exwm-exit-hook) + (setq confirm-kill-emacs nil) ;; Exit modules. (exwm-input--exit) (exwm-manage--exit) @@ -747,7 +749,10 @@ (xcb:+request-checked+request-check exwm--connection (make-instance 'xcb:ChangeWindowAttributes :window exwm--root :value-mask xcb:CW:EventMask - :event-mask xcb:EventMask:NoEvent))) + :event-mask xcb:EventMask:NoEvent)) + (xcb:flush exwm--connection) + (xcb:disconnect exwm--connection) + (setq exwm--connection nil)) (defun exwm-enable (&optional undo) "Enable/Disable EXWM." @@ -821,6 +826,39 @@ ;; For other types, return the value as-is. (t result)))))) +(defun exwm--confirm-kill-emacs (prompt &optional force) + "Confirm before exiting Emacs." + (when (cond + ((and force (not (eq force 'no-check))) + ;; Force killing Emacs. + t) + ((or (eq force 'no-check) (not exwm--id-buffer-alist)) + ;; Check if there's any unsaved file. + (pcase (catch 'break + (let ((kill-emacs-query-functions + (append kill-emacs-query-functions + (list (lambda () + (throw 'break 'break)))))) + (save-buffers-kill-emacs))) + (`break (y-or-n-p prompt)) + (x x))) + (t + (yes-or-no-p (format "[EXWM] %d window(s) will be destroyed. %s" + (length exwm--id-buffer-alist) prompt)))) + ;; Run `kill-emacs-hook' (`server-force-stop' excluded) before Emacs + ;; frames are unmapped so that errors (if any) can be visible. + (if (memq #'server-force-stop kill-emacs-hook) + (progn + (setq kill-emacs-hook (delq #'server-force-stop kill-emacs-hook)) + (run-hooks 'kill-emacs-hook) + (setq kill-emacs-hook (list #'server-force-stop))) + (run-hooks 'kill-emacs-hook) + (setq kill-emacs-hook nil)) + ;; Exit each module, destroying all resources created by this connection. + (exwm--exit) + ;; Set the return value. + t)) + (provide 'exwm) -- cgit 1.4.1 From 587a8cad1db06f2735054ad2dff0d6346093a7be Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Tue, 6 Mar 2018 00:00:00 +0000 Subject: Add interactive commands for starting and stopping EXWM * exwm.el (exwm-reset): Remove autoload cookie. (exwm-init, exwm-exit): Add autoload cookie and interactive declaration. --- exwm-workspace.el | 2 +- exwm.el | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index a852eb8b7d..0a1984bdf8 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1576,7 +1576,7 @@ applied to all subsequently created X frames." ;; (client . nil) (fullscreen . nil) (buffer-predicate . nil)))) - ;; Restore the 'client' frame parameter (before `exwm--exit'). + ;; Restore the 'client' frame parameter (before `exwm-exit'). (when exwm-workspace--client (dolist (f exwm-workspace--list) (set-frame-parameter f 'client exwm-workspace--client)) diff --git a/exwm.el b/exwm.el index 8c57a07df2..06b66c2fdb 100644 --- a/exwm.el +++ b/exwm.el @@ -104,7 +104,6 @@ (defvar exwm--server-process nil "Process of the subordinate Emacs server.") -;;;###autoload (defun exwm-reset () "Reset the state of the selected window (non-fullscreen, line-mode, etc)." (interactive) @@ -690,8 +689,10 @@ :property p)) (xcb:flush exwm--connection))) +;;;###autoload (defun exwm-init (&optional frame) "Initialize EXWM." + (interactive) (if frame ;; The frame might not be selected if it's created by emacslicnet. (select-frame-set-input-focus frame) @@ -735,8 +736,10 @@ (exwm-manage--scan) (run-hooks 'exwm-init-hook))))) -(defun exwm--exit () +;;;###autoload +(defun exwm-exit () "Exit EXWM." + (interactive) (run-hooks 'exwm-exit-hook) (setq confirm-kill-emacs nil) ;; Exit modules. @@ -855,7 +858,7 @@ (run-hooks 'kill-emacs-hook) (setq kill-emacs-hook nil)) ;; Exit each module, destroying all resources created by this connection. - (exwm--exit) + (exwm-exit) ;; Set the return value. t)) -- cgit 1.4.1 From cf98e3d921ea55bcd58f0b7b3071d84a9a8fbcd0 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Tue, 6 Mar 2018 00:00:00 +0000 Subject: Name all helper windows created by EXWM * exwm-workspace.el (exwm-workspace--add-frame-as-workspace) (exwm-workspace--init): * exwm-input.el (exwm-input--init): * exwm-floating.el (exwm-floating--set-floating): Name created helper windows with prefix "EXWM". --- exwm-floating.el | 11 +++++------ exwm-input.el | 4 ++++ exwm-workspace.el | 22 ++++++++++------------ 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 0c8fc6fb23..6f8f9d8d3c 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -274,12 +274,11 @@ This is also used by X window containers.") :border-pixel exwm-floating--border-pixel :override-redirect 1 :colormap exwm-floating--border-colormap)) - (exwm--debug - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_WM_NAME - :window frame-container - :data - (format "floating frame container for 0x%x" id)))) + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_NAME + :window frame-container + :data + (format "EXWM floating frame container for 0x%x" id))) ;; Map it. (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window frame-container)) diff --git a/exwm-input.el b/exwm-input.el index d194b53821..0c8853c5f3 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -927,6 +927,10 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences." :visual 0 :value-mask xcb:CW:EventMask :event-mask xcb:EventMask:PropertyChange)) + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_NAME + :window exwm-input--timestamp-window + :data "EXWM: exwm-input--timestamp-window")) (let ((atom "_TIME")) (setq exwm-input--timestamp-atom (slot-value (xcb:+request-unchecked+reply exwm--connection diff --git a/exwm-workspace.el b/exwm-workspace.el index 0a1984bdf8..5178e50d19 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1228,13 +1228,12 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." :window container :value-mask xcb:ConfigWindow:StackMode :stack-mode xcb:StackMode:Below)) - (exwm--debug - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_WM_NAME - :window container - :data - (format "EXWM workspace %d frame container" - (exwm-workspace--position frame))))) + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_NAME + :window container + :data + (format "EXWM workspace %d frame container" + (exwm-workspace--position frame)))) (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow :window outer-id :parent container :x 0 :y 0)) @@ -1427,11 +1426,10 @@ applied to all subsequently created X frames." xcb:CW:OverrideRedirect) :background-pixmap xcb:BackPixmap:ParentRelative :override-redirect 1)) - (exwm--debug - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_WM_NAME - :window container - :data "Minibuffer container"))) + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_NAME + :window container + :data "EXWM minibuffer container")) ;; Reparent the minibuffer frame to the container. (xcb:+request exwm--connection (make-instance 'xcb:ReparentWindow -- cgit 1.4.1 From 350950abfce892a1f6f6fc5023ae576801a253ca Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Tue, 6 Mar 2018 00:00:00 +0000 Subject: Clean up when failing to start * exwm.el (exwm-init, exwm-exit): Clean up when failing to start. (exwm--exit-icccm-ewmh): Stop deleting root window properties on exit: delete function. --- exwm.el | 73 +++++++++++++++++++++++++---------------------------------------- 1 file changed, 28 insertions(+), 45 deletions(-) diff --git a/exwm.el b/exwm.el index 06b66c2fdb..f2a023b91e 100644 --- a/exwm.el +++ b/exwm.el @@ -667,28 +667,6 @@ :data [0 0])) (xcb:flush exwm--connection)) -(defun exwm--exit-icccm-ewmh () - "Remove ICCCM/EWMH properties." - (dolist (p (list - xcb:Atom:_NET_WM_NAME - xcb:Atom:_NET_SUPPORTED - xcb:Atom:_NET_CLIENT_LIST - xcb:Atom:_NET_CLIENT_LIST_STACKING - xcb:Atom:_NET_NUMBER_OF_DESKTOPS - xcb:Atom:_NET_DESKTOP_GEOMETRY - xcb:Atom:_NET_DESKTOP_VIEWPORT - xcb:Atom:_NET_CURRENT_DESKTOP - xcb:Atom:_NET_ACTIVE_WINDOW - xcb:Atom:_NET_SUPPORTING_WM_CHECK - ;; TODO: Keep this list synchronized with that in - ;; `exwm--init-icccm-ewmh'. - )) - (xcb:+request exwm--connection - (make-instance 'xcb:DeleteProperty - :window exwm--root - :property p)) - (xcb:flush exwm--connection))) - ;;;###autoload (defun exwm-init (&optional frame) "Initialize EXWM." @@ -697,25 +675,25 @@ ;; The frame might not be selected if it's created by emacslicnet. (select-frame-set-input-focus frame) (setq frame (selected-frame))) - (if (not (eq 'x (framep frame))) - (exwm--log "Not running under X environment") - (unless exwm--connection - (exwm-enable 'undo) ;never initialize again - (setq exwm--connection (xcb:connect)) - (set-process-query-on-exit-flag (slot-value exwm--connection 'process) - nil) ;prevent query message on exit - (setq exwm--root - (slot-value (car (slot-value - (xcb:get-setup exwm--connection) 'roots)) - 'root)) - (if (xcb:+request-checked+request-check exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window exwm--root :value-mask xcb:CW:EventMask - :event-mask xcb:EventMask:SubstructureRedirect)) - ;; Other window manager is running - (progn (xcb:disconnect exwm--connection) - (setq exwm--connection nil) - (exwm--log "Other window manager detected")) + (when (not (eq 'x (framep frame))) + (user-error "Not running under X environment")) + (when exwm--connection + (user-error "EXWM already running")) + (condition-case err + (progn + (exwm-enable 'undo) ;never initialize again + (setq exwm--connection (xcb:connect)) + (set-process-query-on-exit-flag (slot-value exwm--connection 'process) + nil) ;prevent query message on exit + (setq exwm--root + (slot-value (car (slot-value + (xcb:get-setup exwm--connection) 'roots)) + 'root)) + (when (xcb:+request-checked+request-check exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window exwm--root :value-mask xcb:CW:EventMask + :event-mask xcb:EventMask:SubstructureRedirect)) + (error "Other window manager is running")) ;; Disable some features not working well with EXWM (setq use-dialog-box nil confirm-kill-emacs #'exwm--confirm-kill-emacs) @@ -734,7 +712,12 @@ (exwm-input--post-init) ;; Manage existing windows (exwm-manage--scan) - (run-hooks 'exwm-init-hook))))) + (run-hooks 'exwm-init-hook)) + ((quit error) + (exwm-exit) + ;; Rethrow error + (signal (car err) (cdr err))))) + ;;;###autoload (defun exwm-exit () @@ -748,13 +731,13 @@ (exwm-workspace--exit) (exwm-floating--exit) (exwm-layout--exit) - (exwm--exit-icccm-ewmh) (xcb:+request-checked+request-check exwm--connection (make-instance 'xcb:ChangeWindowAttributes :window exwm--root :value-mask xcb:CW:EventMask :event-mask xcb:EventMask:NoEvent)) - (xcb:flush exwm--connection) - (xcb:disconnect exwm--connection) + (when exwm--connection + (xcb:flush exwm--connection) + (xcb:disconnect exwm--connection)) (setq exwm--connection nil)) (defun exwm-enable (&optional undo) -- cgit 1.4.1 From 7aae6efdcd5d64c528315d08e49cdef2e956b540 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Tue, 6 Mar 2018 00:00:00 +0000 Subject: Support replacing and being replaced by other window managers * exwm.el (exwm--on-SelectionClear, exwm--init-icccm-ewmh) (exwm--exit-icccm-ewmh, exwm--wmsn-acquire, exwm--wmsn-release): Get the window manager selection; die when it is cleared. --- exwm-core.el | 9 ++++++ exwm.el | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 103 insertions(+), 3 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 7fd3a61211..355b8b96c0 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -37,6 +37,15 @@ (defvar exwm--connection nil "X connection.") +(defvar exwm--wmsn-window nil + "An X window owning the WM_S0 selection.") + +(defvar exwm--wmsn-acquire-timeout 3 + "Number of seconds to wait for other window managers to release the selection.") + +(defvar exwm--wmsn-replace 'ask + "Replace existing window manager.") + (defvar exwm--guide-window nil "An X window separating workspaces and X windows.") diff --git a/exwm.el b/exwm.el index f2a023b91e..90b1fe4e2a 100644 --- a/exwm.el +++ b/exwm.el @@ -529,12 +529,26 @@ (bury-buffer))))) (t (exwm--log "Unhandled client message: %s" obj))))) +(defun exwm--on-SelectionClear (data _synthetic) + "Handle SelectionClear events." + (exwm--log "SelectionClear") + (let ((obj (make-instance 'xcb:SelectionClear)) + owner selection) + (xcb:unmarshal obj data) + (setq owner (slot-value obj 'owner) + selection (slot-value obj 'selection)) + (when (and (eq owner exwm--wmsn-window) + (eq selection xcb:Atom:WM_S0)) + (exwm-exit)))) + (defun exwm--init-icccm-ewmh () "Initialize ICCCM/EWMH support." ;; Handle PropertyNotify event (xcb:+event exwm--connection 'xcb:PropertyNotify #'exwm--on-PropertyNotify) ;; Handle relevant client messages (xcb:+event exwm--connection 'xcb:ClientMessage #'exwm--on-ClientMessage) + ;; Handle SelectionClear + (xcb:+event exwm--connection 'xcb:SelectionClear #'exwm--on-SelectionClear) ;; Set _NET_SUPPORTED (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_SUPPORTED @@ -667,6 +681,81 @@ :data [0 0])) (xcb:flush exwm--connection)) +(defun exwm--wmsn-acquire (replace) + "Acquire the WM_Sn selection. + +REPLACE specifies what to do in case there already is a window +manager. If t, replace it, if nil, abort and if `ask'." + (with-slots (owner) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetSelectionOwner + :selection xcb:Atom:WM_S0)) + (when (/= owner xcb:Window:None) + (when (eq replace 'ask) + (setq replace (yes-or-no-p "Replace existing window manager?"))) + (when (not replace) + (error "Other window manager detected"))) + (let ((new-owner (xcb:generate-id exwm--connection))) + (xcb:+request exwm--connection + (make-instance 'xcb:CreateWindow + :depth 0 + :wid new-owner + :parent exwm--root + :x -1 + :y -1 + :width 1 + :height 1 + :border-width 0 + :class xcb:WindowClass:CopyFromParent + :visual 0 + :value-mask 0 + :override-redirect 0)) + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_NAME + :window new-owner :data "EXWM selection owner")) + (xcb:+request-checked+request-check exwm--connection + (make-instance 'xcb:SetSelectionOwner + :selection xcb:Atom:WM_S0 + :owner new-owner + :time xcb:Time:CurrentTime)) + (with-slots (owner) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetSelectionOwner + :selection xcb:Atom:WM_S0)) + (unless (eq owner new-owner) + (error "Could not acquire ownership of WM selection"))) + ;; Wait for the other window manager to terminate. + (when (/= owner xcb:Window:None) + (let (reply) + (cl-dotimes (i 10) ;exwm--wmsn-acquire-timeout) + (setq reply (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetGeometry :drawable owner))) + (when (not reply) + (cl-return)) + (message "Waiting for other window manager to quit... %ds" i) + (sleep-for 1)) + (when reply + (error "Other window manager did not release selection in time")))) + ;; announce + (let* ((cmd (make-instance 'xcb:ClientMessageData + :data32 (vector xcb:Time:CurrentTime + xcb:Atom:WM_S0 + new-owner + 0 + 0))) + (cm (make-instance 'xcb:ClientMessage + :window exwm--root + :format 32 + :type xcb:Atom:MANAGER + :data cmd)) + (se (make-instance 'xcb:SendEvent + :propagate 0 + :destination exwm--root + :event-mask xcb:EventMask:NoEvent + :event (xcb:marshal cm exwm--connection)))) + (xcb:+request exwm--connection se)) + (setq exwm--wmsn-window new-owner)))) + ;;;###autoload (defun exwm-init (&optional frame) "Initialize EXWM." @@ -689,6 +778,11 @@ (slot-value (car (slot-value (xcb:get-setup exwm--connection) 'roots)) 'root)) + ;; Initialize ICCCM/EWMH support + (xcb:icccm:init exwm--connection t) + (xcb:ewmh:init exwm--connection t) + ;; Try to register window manager selection. + (exwm--wmsn-acquire 'ask) (when (xcb:+request-checked+request-check exwm--connection (make-instance 'xcb:ChangeWindowAttributes :window exwm--root :value-mask xcb:CW:EventMask @@ -697,9 +791,6 @@ ;; Disable some features not working well with EXWM (setq use-dialog-box nil confirm-kill-emacs #'exwm--confirm-kill-emacs) - ;; Initialize ICCCM/EWMH support - (xcb:icccm:init exwm--connection t) - (xcb:ewmh:init exwm--connection t) (exwm--lock) (exwm--init-icccm-ewmh) (exwm-layout--init) -- cgit 1.4.1 From 2f430db735f33abb4fc009fc6ec8c12f74f57dba Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 9 Mar 2018 01:06:39 +0800 Subject: Minor fixes --- exwm-floating.el | 1 - exwm-input.el | 1 + exwm-layout.el | 1 + exwm-manage.el | 4 ---- exwm-randr.el | 1 - exwm-systemtray.el | 1 + exwm-workspace.el | 31 ++++++++++++++----------------- exwm.el | 16 +++++++--------- 8 files changed, 24 insertions(+), 32 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 6f8f9d8d3c..8b1612e42a 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -79,7 +79,6 @@ This is also used by X window containers.") (declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) (declare-function exwm-layout--refresh "exwm-layout.el" ()) (declare-function exwm-layout--show "exwm-layout.el" (id &optional window)) -(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") (declare-function exwm-workspace--position "exwm-workspace.el" (frame)) (defun exwm-floating--set-allowed-actions (id tilling) diff --git a/exwm-input.el b/exwm-input.el index 0c8853c5f3..6f96d71738 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -146,6 +146,7 @@ This value should always be overwritten.") (&rest _args)) (declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) (declare-function exwm-layout--show "exwm-layout.el" (id &optional window)) +(declare-function exwm-reset "exwm.el" ()) (declare-function exwm-workspace--client-p "exwm-workspace.el" (&optional frame)) (declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") diff --git a/exwm-layout.el b/exwm-layout.el index babd37410a..19d14d1140 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -47,6 +47,7 @@ (defvar exwm-workspace--current) (declare-function exwm-input-grab-keyboard "exwm-input.el") (declare-function exwm-input-release-keyboard "exwm-input.el") +(declare-function exwm-reset "exwm.el" ()) (declare-function exwm-workspace--client-p "exwm-workspace.el" (&optional frame)) (declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") diff --git a/exwm-manage.el b/exwm-manage.el index cb2bf28b17..3e47f749c9 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -111,14 +111,10 @@ You can still make the X windows floating afterwards." (declare-function exwm-input-set-local-simulation-keys "exwm-input.el") (declare-function exwm-layout--fullscreen-p "exwm-layout.el" ()) (declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) -(declare-function exwm-workspace--count "exwm-workspace.el" ()) (declare-function exwm-workspace--position "exwm-workspace.el" (frame)) -(declare-function exwm-workspace--set-desktop "exwm-workspace.el" (id)) (declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame)) (declare-function exwm-workspace--update-struts "exwm-workspace.el" ()) (declare-function exwm-workspace--update-workareas "exwm-workspace.el" ()) -(declare-function exwm-workspace-move-window "exwm-workspace.el" - (frame-or-index &optional id)) (defun exwm-manage--update-geometry (id &optional force) "Update window geometry." diff --git a/exwm-randr.el b/exwm-randr.el index 485d9c8826..0658bee779 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -81,7 +81,6 @@ the first one in result being the primary output." (defvar exwm-workspace--fullscreen-frame-count) (defvar exwm-workspace--list) -(declare-function exwm-workspace--active-p "exwm-workspace.el" (frame)) (declare-function exwm-workspace--count "exwm-workspace.el") (declare-function exwm-workspace--set-active "exwm-workspace.el" (frame active)) diff --git a/exwm-systemtray.el b/exwm-systemtray.el index d932032e59..dea5dbbc2e 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -376,6 +376,7 @@ You shall use the default value if using auto-hide minibuffer." :selection xcb:Atom:_NET_SYSTEM_TRAY_S0)) (when (/= owner xcb:Window:None) (xcb:disconnect exwm-systemtray--connection) + (setq exwm-systemtray--connection nil) (warn "[EXWM] Other system tray detected") (cl-return-from exwm-systemtray--init))) (let ((id (xcb:generate-id exwm-systemtray--connection))) diff --git a/exwm-workspace.el b/exwm-workspace.el index 5178e50d19..9031721547 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1327,7 +1327,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (exwm-workspace--remove-frame-as-workspace frame)))) (defun exwm-workspace--on-after-make-frame (frame) - "Hook run upon `delete-frame' that configures FRAME as a workspace." + "Hook run upon `make-frame' that configures FRAME as a workspace." (cond ((exwm-workspace--workspace-p frame) (exwm--log "Frame `%s' is already a workspace" frame)) @@ -1354,6 +1354,9 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (defun exwm-workspace--update-ewmh-props () "Update EWMH properties to match the workspace list." (let ((num-workspaces (exwm-workspace--count))) + ;; Avoid setting 0 desktops. + (when (= 0 num-workspaces) + (setq num-workspaces 1)) ;; Set _NET_NUMBER_OF_DESKTOPS. (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_NUMBER_OF_DESKTOPS @@ -1470,19 +1473,14 @@ applied to all subsequently created X frames." :test #'equal)) (setq default-minibuffer-frame nil) (let ((id (frame-parameter exwm-workspace--minibuffer 'exwm-outer-id))) - (xcb:+request exwm--connection - (make-instance 'xcb:UnmapWindow - :window id)) - (xcb:+request exwm--connection - (make-instance 'xcb:ReparentWindow - :window id - :parent exwm--root - :x 0 - :y 0)) - (xcb:+request exwm--connection - (make-instance 'xcb:MapWindow - :window id))) - (setq exwm-workspace--minibuffer nil)) + (when (and exwm-workspace--minibuffer id) + (xcb:+request exwm--connection + (make-instance 'xcb:ReparentWindow + :window id + :parent exwm--root + :x 0 + :y 0))) + (setq exwm-workspace--minibuffer nil))) (defun exwm-workspace--init () "Initialize workspace module." @@ -1538,8 +1536,7 @@ applied to all subsequently created X frames." ;; Make new frames create new workspaces. (add-hook 'after-make-frame-functions #'exwm-workspace--on-after-make-frame) - (add-hook 'delete-frame-functions - #'exwm-workspace--remove-frame-as-workspace) + (add-hook 'delete-frame-functions #'exwm-workspace--on-delete-frame) ;; Switch to the first workspace (exwm-workspace-switch 0 t) ;; Prevent frame parameters introduced by this module from being @@ -1578,7 +1575,7 @@ applied to all subsequently created X frames." (when exwm-workspace--client (dolist (f exwm-workspace--list) (set-frame-parameter f 'client exwm-workspace--client)) - (when (exwm-workspace--own-frame-p) + (when (exwm-workspace--minibuffer-own-frame-p) (set-frame-parameter exwm-workspace--minibuffer 'client exwm-workspace--client)) (setq exwm-workspace--client nil))) diff --git a/exwm.el b/exwm.el index 90b1fe4e2a..379500b412 100644 --- a/exwm.el +++ b/exwm.el @@ -685,14 +685,14 @@ "Acquire the WM_Sn selection. REPLACE specifies what to do in case there already is a window -manager. If t, replace it, if nil, abort and if `ask'." +manager. If t, replace it, if nil, abort and ask the user if `ask'." (with-slots (owner) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GetSelectionOwner :selection xcb:Atom:WM_S0)) (when (/= owner xcb:Window:None) (when (eq replace 'ask) - (setq replace (yes-or-no-p "Replace existing window manager?"))) + (setq replace (yes-or-no-p "Replace existing window manager? "))) (when (not replace) (error "Other window manager detected"))) (let ((new-owner (xcb:generate-id exwm--connection))) @@ -727,7 +727,7 @@ manager. If t, replace it, if nil, abort and if `ask'." ;; Wait for the other window manager to terminate. (when (/= owner xcb:Window:None) (let (reply) - (cl-dotimes (i 10) ;exwm--wmsn-acquire-timeout) + (cl-dotimes (i exwm--wmsn-acquire-timeout) (setq reply (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GetGeometry :drawable owner))) (when (not reply) @@ -785,8 +785,10 @@ manager. If t, replace it, if nil, abort and if `ask'." (exwm--wmsn-acquire 'ask) (when (xcb:+request-checked+request-check exwm--connection (make-instance 'xcb:ChangeWindowAttributes - :window exwm--root :value-mask xcb:CW:EventMask - :event-mask xcb:EventMask:SubstructureRedirect)) + :window exwm--root + :value-mask xcb:CW:EventMask + :event-mask + xcb:EventMask:SubstructureRedirect)) (error "Other window manager is running")) ;; Disable some features not working well with EXWM (setq use-dialog-box nil @@ -822,10 +824,6 @@ manager. If t, replace it, if nil, abort and if `ask'." (exwm-workspace--exit) (exwm-floating--exit) (exwm-layout--exit) - (xcb:+request-checked+request-check exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window exwm--root :value-mask xcb:CW:EventMask - :event-mask xcb:EventMask:NoEvent)) (when exwm--connection (xcb:flush exwm--connection) (xcb:disconnect exwm--connection)) -- cgit 1.4.1 From cb8706f91c1c343a9bf392c9deaadc0c42d4dba6 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 10 Mar 2018 00:12:47 +0800 Subject: Delay setting workspaces active/inactive * exwm-randr.el (exwm-randr--refresh): Set workspaces active/inactive after their geometries have been updated. --- exwm-randr.el | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/exwm-randr.el b/exwm-randr.el index 0658bee779..b3c6ce8707 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -140,6 +140,18 @@ the first one in result being the primary output." container-frame-alist)) (set-frame-parameter frame 'exwm-randr-output output) (set-frame-parameter frame 'exwm-geometry geometry))) + ;; Update workareas. + (exwm-workspace--update-workareas) + ;; Resize workspace. + (dolist (f exwm-workspace--list) + (exwm-workspace--set-fullscreen f)) + (xcb:flush exwm--connection) + ;; Raise the minibuffer if it's active. + (when (and (active-minibuffer-window) + (exwm-workspace--minibuffer-own-frame-p)) + (exwm-workspace--show-minibuffer)) + ;; Set _NET_DESKTOP_GEOMETRY. + (exwm-workspace--set-desktop-geometry) ;; Update active/inactive workspaces. (dolist (w exwm-workspace--list) (exwm-workspace--set-active w nil)) @@ -155,17 +167,6 @@ the first one in result being the primary output." (rassq-delete-all output container-output-alist)) (exwm-workspace--set-active (cdr (assq xwin container-frame-alist)) t)))) - ;; Update workareas. - (exwm-workspace--update-workareas) - ;; Resize workspace. - (dolist (f exwm-workspace--list) - (exwm-workspace--set-fullscreen f)) - ;; Raise the minibuffer if it's active. - (when (and (active-minibuffer-window) - (exwm-workspace--minibuffer-own-frame-p)) - (exwm-workspace--show-minibuffer)) - ;; Set _NET_DESKTOP_GEOMETRY. - (exwm-workspace--set-desktop-geometry) (xcb:flush exwm--connection) (run-hooks 'exwm-randr-refresh-hook)))) -- cgit 1.4.1 From 3f6c609a2b1580d77a9863e205718cbba3872bcb Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 10 Mar 2018 17:28:43 +0800 Subject: Fix regressions (exwm-init): Do not signal an error on startup. * exwm-floating.el (exwm-floating-toggle-floating): * exwm-input (exwm-input-send-next-key) (exwm-input-send-simulation-key): * exwm-layout (exwm-layout-set-fullscreen) (exwm-layout-unset-fullscreen, exwm-layout-toggle-fullscreen): Fix incorrect use of `cl-return-from'. --- exwm-floating.el | 2 +- exwm-input.el | 4 ++-- exwm-layout.el | 6 +++--- exwm.el | 10 ++++++---- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 8b1612e42a..0210492b18 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -401,7 +401,7 @@ This is also used by X window containers.") "Toggle the current window between floating and non-floating states." (interactive) (unless (derived-mode-p 'exwm-mode) - (cl-return-from 'exwm-floating-toggle-floating)) + (cl-return-from exwm-floating-toggle-floating)) (with-current-buffer (window-buffer) (if exwm--floating-frame (exwm-floating--unset-floating exwm--id) diff --git a/exwm-input.el b/exwm-input.el index 6f96d71738..4c1ce00b12 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -745,7 +745,7 @@ EXWM will prompt for the key to send. This command can be prefixed to send multiple keys." (interactive "p") (unless (derived-mode-p 'exwm-mode) - (cl-return-from 'exwm-input-send-next-key)) + (cl-return-from exwm-input-send-next-key)) (when (> times 12) (setq times 12)) (let (key keys) (dotimes (i times) @@ -893,7 +893,7 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences." "Fake a key event according to the last input key sequence." (interactive "p") (unless (derived-mode-p 'exwm-mode) - (cl-return-from 'exwm-input-send-simulation-key)) + (cl-return-from exwm-input-send-simulation-key)) (let ((keys (gethash (this-single-command-keys) exwm-input--simulation-keys))) (dotimes (_ times) diff --git a/exwm-layout.el b/exwm-layout.el index 19d14d1140..847dd36389 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -147,7 +147,7 @@ (interactive) (unless (and (or id (derived-mode-p 'exwm-mode)) (not (exwm-layout--fullscreen-p))) - (cl-return-from 'exwm-layout-set-fullscreen)) + (cl-return-from exwm-layout-set-fullscreen)) (with-current-buffer (if id (exwm--id->buffer id) (window-buffer)) ;; Expand the X window to fill the whole screen. (with-slots (x y width height) (exwm-workspace--get-geometry exwm--frame) @@ -174,7 +174,7 @@ (interactive) (unless (and (or id (derived-mode-p 'exwm-mode)) (exwm-layout--fullscreen-p)) - (cl-return-from 'exwm-layout-unset-fullscreen)) + (cl-return-from exwm-layout-unset-fullscreen)) (with-current-buffer (if id (exwm--id->buffer id) (window-buffer)) (setq exwm--ewmh-state (delq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) @@ -200,7 +200,7 @@ "Toggle fullscreen mode." (interactive (list (exwm--buffer->id (window-buffer)))) (unless (or id (derived-mode-p 'exwm-mode)) - (cl-return-from 'exwm-layout-toggle-fullscreen)) + (cl-return-from exwm-layout-toggle-fullscreen)) (when id (with-current-buffer (exwm--id->buffer id) (if (exwm-layout--fullscreen-p) diff --git a/exwm.el b/exwm.el index 379500b412..359489ff34 100644 --- a/exwm.el +++ b/exwm.el @@ -757,7 +757,7 @@ manager. If t, replace it, if nil, abort and ask the user if `ask'." (setq exwm--wmsn-window new-owner)))) ;;;###autoload -(defun exwm-init (&optional frame) +(cl-defun exwm-init (&optional frame) "Initialize EXWM." (interactive) (if frame @@ -765,9 +765,11 @@ manager. If t, replace it, if nil, abort and ask the user if `ask'." (select-frame-set-input-focus frame) (setq frame (selected-frame))) (when (not (eq 'x (framep frame))) - (user-error "Not running under X environment")) + (message "[EXWM] Not running under X environment") + (cl-return-from exwm-init)) (when exwm--connection - (user-error "EXWM already running")) + (exwm--log "EXWM already running") + (cl-return-from exwm-init)) (condition-case err (progn (exwm-enable 'undo) ;never initialize again @@ -809,7 +811,7 @@ manager. If t, replace it, if nil, abort and ask the user if `ask'." ((quit error) (exwm-exit) ;; Rethrow error - (signal (car err) (cdr err))))) + (warn "[EXWM] EXWM fails to start (%s: %s)" (car err) (cdr err))))) ;;;###autoload -- cgit 1.4.1 From 8db0a7838233946b3b9ea1ef4b33ab0cb113c5e1 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 14 Mar 2018 01:14:36 +0800 Subject: Fix wrong exwm-active frame parameters * exwm-randr.el (exwm-randr--refresh): Intern output names. --- exwm-randr.el | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/exwm-randr.el b/exwm-randr.el index b3c6ce8707..af900ab36f 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -134,8 +134,9 @@ the first one in result being the primary output." (unless geometry (setq geometry default-geometry output primary-output)) - (setq container-output-alist (nconc `((,container . ,output)) - container-output-alist) + (setq container-output-alist (nconc + `((,container . ,(intern output))) + container-output-alist) container-frame-alist (nconc `((,container . ,frame)) container-frame-alist)) (set-frame-parameter frame 'exwm-randr-output output) -- cgit 1.4.1 From 5448fb75fac0bfa56e15c8f3630fafc24c07f109 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 14 Mar 2018 01:20:00 +0800 Subject: Bump version to 0.18 --- exwm.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm.el b/exwm.el index 359489ff34..195ca7b80e 100644 --- a/exwm.el +++ b/exwm.el @@ -4,8 +4,8 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0.17 -;; Package-Requires: ((xelb "0.13")) +;; Version: 0.18 +;; Package-Requires: ((xelb "0.14")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From b6d660042e8f949890e6b4d5c397235d1653fdfe Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 23 Mar 2018 23:49:39 +0800 Subject: Add workspace support in per-application configurations * exwm-manage.el (exwm-manage-configurations) (exwm-manage--manage-window): Allow to specify which workspace an applications should be created on. --- exwm-manage.el | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index 3e47f749c9..f44071020d 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -69,6 +69,7 @@ You can still make the X windows floating afterwards." (const :tag "Char-mode" char-mode) (const :tag "Prefix keys" prefix-keys) (const :tag "Simulation keys" simulation-keys) + (const :tag "Workspace" workspace) ;; For forward compatibility. (other)) :value-type (sexp :tag "Value" nil)))) @@ -202,7 +203,6 @@ You can still make the X windows floating afterwards." (exwm--update-hints id) (exwm-manage--update-geometry id) (exwm-manage--update-mwm-hints id) - (setq exwm--configurations (exwm-manage--get-configurations)) ;; No need to manage (please check OverrideRedirect outside) (when (or (not @@ -266,6 +266,10 @@ You can still make the X windows floating afterwards." (let ((kill-buffer-query-functions nil)) (kill-buffer (current-buffer))) (throw 'return 'ignored)) + (setq exwm--configurations (exwm-manage--get-configurations)) + (let ((index (plist-get exwm--configurations 'workspace))) + (when (and index (< index (length exwm-workspace--list))) + (setq exwm--frame (elt exwm-workspace--list index)))) ;; Manage the window (exwm--log "Manage #x%x" id) (xcb:+request exwm--connection ;remove border @@ -290,7 +294,8 @@ You can still make the X windows floating afterwards." ;; User has specified whether it should be floating. (if (plist-get exwm--configurations 'floating) (exwm-floating--set-floating id) - (exwm-floating--unset-floating id)) + (with-selected-window (frame-selected-window exwm--frame) + (exwm-floating--unset-floating id))) ;; Try to determine if it should be floating. (if (and (not exwm-manage-force-tiling) (or exwm-transient-for exwm--fixed-size @@ -299,7 +304,8 @@ You can still make the X windows floating afterwards." (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG exwm-window-type))) (exwm-floating--set-floating id) - (exwm-floating--unset-floating id))) + (with-selected-window (frame-selected-window exwm--frame) + (exwm-floating--unset-floating id)))) (if (plist-get exwm--configurations 'char-mode) (exwm-input-release-keyboard id) (exwm-input-grab-keyboard id)) -- cgit 1.4.1 From 02b1be7160051610a542764ec392e3a50f23fb5a Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 23 Mar 2018 23:53:05 +0800 Subject: Avoid entering line-mode after exit minibuffer * exwm-input.el (exwm-input--on-pre-command): Keys should be forwarded to the X window (if its Emacs window is currently selected) after exiting the minibuffer. --- exwm-input.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exwm-input.el b/exwm-input.el index 4c1ce00b12..504307768c 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -902,7 +902,8 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences." (defun exwm-input--on-pre-command () "Run in `pre-command-hook'." - (setq exwm-input--during-command t)) + (unless (eq this-command #'exit-minibuffer) + (setq exwm-input--during-command t))) (defun exwm-input--on-post-command () "Run in `post-command-hook'." -- cgit 1.4.1 From 10eb27eddcfdea1ec27382ea74625af867ceca61 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 23 Mar 2018 23:55:28 +0800 Subject: Fix local simulation keys * exwm-input.el (exwm-input--on-KeyPress-line-mode): Do not test `exwm-mode-map' to see if a key should be forwarded to Emacs as it's overridden when local simulation keys are present. --- exwm-input.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm-input.el b/exwm-input.el index 504307768c..dae0153e54 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -567,7 +567,7 @@ instead." ;; (memq event exwm-input--global-prefix-keys) (memq event exwm-input-prefix-keys) - (lookup-key exwm-mode-map (vector event)) + (lookup-key (current-local-map) (vector event)) (gethash event exwm-input--simulation-keys))) (setq mode xcb:Allow:AsyncKeyboard) (exwm-input--cache-event event)) -- cgit 1.4.1 From f4aa8389fc005240dcc2591fead706fd84ccaac8 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 23 Mar 2018 23:57:18 +0800 Subject: Fix a compatibility issue with Emacs 24 * exwm-core.el (exwm--defer): `time-add' in Emacs 24 only accept lists. --- exwm-core.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/exwm-core.el b/exwm-core.el index 355b8b96c0..ab5159c6a7 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -126,7 +126,9 @@ Nil can be passed as placeholder." The action is to call FUNCTION with arguments ARGS. If Emacs is not idle, defer the action until Emacs is idle. Otherwise, defer the action until at least SECS seconds later." - `(run-with-idle-timer (time-add (or (current-idle-time) (- ,secs)) ,secs) + `(run-with-idle-timer (+ (float-time (or (current-idle-time) + (seconds-to-time (- ,secs)))) + ,secs) nil ,function ,@args)) -- cgit 1.4.1 From fbdcd42b0a2aadb4f3e27cd2a75c863d2f67eb39 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 1 Apr 2018 23:30:19 +0800 Subject: Add support for various key processing variables * exwm-input.el (exwm-input--mimic-read-event): New function for handling `extra-keyboard-modifiers' and `keyboard-translate-table'. (exwm-input--cache-event): Do not unread events here. (exwm-input--on-KeyPress-line-mode) (exwm-input--on-KeyPress-char-mode): Compare with preprocessed events and unread raw ones. * exwm-input.el (exwm-input--on-KeyPress-line-mode): Add support for `overriding-terminal-local-map'. --- exwm-input.el | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index dae0153e54..153410828f 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -522,6 +522,19 @@ instead." (setq unread-command-events (append unread-command-events `((t . ,event))))))) +(defun exwm-input--mimic-read-event (event) + "Process EVENT as if it were returned by `read-event'." + (unless (eq 0 extra-keyboard-modifiers) + (setq event (event-convert-list (append (event-modifiers + extra-keyboard-modifiers) + event)))) + (when (characterp event) + (let ((event* (when keyboard-translate-table + (aref keyboard-translate-table event)))) + (when event* + (setq event event*)))) + event) + (cl-defun exwm-input--translate (key) (let (translation) (dolist (map (list input-decode-map @@ -546,18 +559,18 @@ instead." (setq exwm-input--line-mode-cache nil) (when exwm-input--temp-line-mode (setq exwm-input--temp-line-mode nil) - (exwm-input--release-keyboard))) - (exwm-input--unread-event event)) + (exwm-input--release-keyboard)))) (defun exwm-input--on-KeyPress-line-mode (key-press raw-data) "Parse X KeyPress event to Emacs key event and then feed the command loop." (with-slots (detail state) key-press (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) - event mode) + event raw-event mode) (when (and (/= 0 (car keysym)) - (setq event (xcb:keysyms:keysym->event - exwm--connection (car keysym) - (logand state (lognot (cdr keysym))))) + (setq raw-event (xcb:keysyms:keysym->event + exwm--connection (car keysym) + (logand state (lognot (cdr keysym))))) + (setq event (exwm-input--mimic-read-event raw-event)) (or exwm-input-line-mode-passthrough exwm-input--during-command ;; Forward the event when there is an incomplete key @@ -567,10 +580,14 @@ instead." ;; (memq event exwm-input--global-prefix-keys) (memq event exwm-input-prefix-keys) + (when overriding-terminal-local-map + (lookup-key overriding-terminal-local-map + (vector event))) (lookup-key (current-local-map) (vector event)) (gethash event exwm-input--simulation-keys))) (setq mode xcb:Allow:AsyncKeyboard) - (exwm-input--cache-event event)) + (exwm-input--cache-event event) + (exwm-input--unread-event raw-event)) (unless mode (if (= 0 (logand #x6000 state)) ;Check the 13~14 bits. ;; Not an XKB state; just replay it. @@ -599,17 +616,19 @@ instead." "Handle KeyPress event in char-mode." (with-slots (detail state) key-press (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) - event) + event raw-event) (when (and (/= 0 (car keysym)) - (setq event (xcb:keysyms:keysym->event - exwm--connection (car keysym) - (logand state (lognot (cdr keysym)))))) + (setq raw-event (xcb:keysyms:keysym->event + exwm--connection (car keysym) + (logand state (lognot (cdr keysym))))) + (setq event (exwm-input--mimic-read-event raw-event))) (if (not (eq major-mode 'exwm-mode)) - (exwm-input--unread-event event) + (exwm-input--unread-event raw-event) ;; Grab keyboard temporarily. (setq exwm-input--temp-line-mode t) (exwm-input--grab-keyboard) - (exwm-input--cache-event event))))) + (exwm-input--cache-event event) + (exwm-input--unread-event raw-event))))) (xcb:+request exwm--connection (make-instance 'xcb:AllowEvents :mode xcb:Allow:AsyncKeyboard -- cgit 1.4.1 From 87db8b42a33876818ad8ee6b950c1f2aaa73d3d5 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 1 Apr 2018 23:38:48 +0800 Subject: Preserve keyboard grab state after quitting fullscreen mode * exwm-layout.el (exwm-layout-set-fullscreen) (exwm-layout-unset-fullscreen, exwm-layout-toggle-fullscreen): Ditto. --- exwm-layout.el | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index 847dd36389..b74f512a4e 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -166,7 +166,7 @@ :data (vector xcb:Atom:_NET_WM_STATE_FULLSCREEN))) (xcb:flush exwm--connection) (cl-pushnew xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) - (call-interactively #'exwm-input-release-keyboard))) + (exwm-input--release-keyboard exwm--id))) ;;;###autoload (cl-defun exwm-layout-unset-fullscreen (&optional id) @@ -193,7 +193,8 @@ (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_STATE :window exwm--id :data [])) (xcb:flush exwm--connection) - (call-interactively #'exwm-input-grab-keyboard))) + (when exwm--keyboard-grabbed + (exwm-input--grab-keyboard exwm--id)))) ;;;###autoload (cl-defun exwm-layout-toggle-fullscreen (&optional id) @@ -204,7 +205,7 @@ (when id (with-current-buffer (exwm--id->buffer id) (if (exwm-layout--fullscreen-p) - (exwm-reset) + (exwm-layout-unset-fullscreen id) (exwm-layout-set-fullscreen id))))) (defun exwm-layout--other-buffer-predicate (buffer) -- cgit 1.4.1 From ff4ae82fd7ca9101da92f21c7f46f991da99a30e Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sun, 15 Apr 2018 14:52:39 -0700 Subject: Port to 32-bit Emacs on master branch --- exwm-layout.el | 2 +- exwm.el | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index b74f512a4e..764259870d 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -117,7 +117,7 @@ (with-current-buffer (exwm--id->buffer id) (unless (or (exwm-layout--iconic-state-p) (and exwm--floating-frame - (eq #xffffffff exwm--desktop))) + (eq 4294967295. exwm--desktop))) (exwm--log "Hide #x%x" id) (when exwm--floating-frame (let* ((container (frame-parameter exwm--floating-frame diff --git a/exwm.el b/exwm.el index 195ca7b80e..17f73d8ca0 100644 --- a/exwm.el +++ b/exwm.el @@ -154,7 +154,7 @@ (when reply (setq desktop (slot-value reply 'value)) (cond - ((eq desktop #xffffffff) + ((eq desktop 4294967295.) (unless (or (not exwm--floating-frame) (eq exwm--frame exwm-workspace--current) (and exwm--desktop -- cgit 1.4.1 From 0037cba87b1ceefc3a65fa3c458aa685689e7e27 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 13 May 2018 23:57:36 +0800 Subject: Avoid hiding full screen X windows unexpectedly * exwm-layout.el (exwm-layout-set-fullscreen) (exwm-layout-unset-fullscreen): Set the Emacs window of a full screen X window dedicated to its buffer such that newly created X windows won't replace it. --- exwm-layout.el | 2 ++ 1 file changed, 2 insertions(+) diff --git a/exwm-layout.el b/exwm-layout.el index 764259870d..5b7fe999a1 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -165,6 +165,7 @@ :window exwm--id :data (vector xcb:Atom:_NET_WM_STATE_FULLSCREEN))) (xcb:flush exwm--connection) + (set-window-dedicated-p (get-buffer-window) t) (cl-pushnew xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) (exwm-input--release-keyboard exwm--id))) @@ -193,6 +194,7 @@ (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_STATE :window exwm--id :data [])) (xcb:flush exwm--connection) + (set-window-dedicated-p (get-buffer-window) nil) (when exwm--keyboard-grabbed (exwm-input--grab-keyboard exwm--id)))) -- cgit 1.4.1 From df8de921132520cccf4236906bcd37aec83fa0ce Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 20 May 2018 16:14:01 +0800 Subject: Provide a fallback value for X window geometry * exwm-manage.el (exwm-manage--update-geometry): Ditto. --- exwm-layout.el | 4 ++-- exwm-manage.el | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index 5b7fe999a1..b5685f417e 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -45,9 +45,9 @@ (defvar exwm-layout--timer nil "Timer used to track echo area changes.") (defvar exwm-workspace--current) +(declare-function exwm-input--release-keyboard "exwm-input.el") +(declare-function exwm-input--grab-keyboard "exwm-input.el") (declare-function exwm-input-grab-keyboard "exwm-input.el") -(declare-function exwm-input-release-keyboard "exwm-input.el") -(declare-function exwm-reset "exwm.el" ()) (declare-function exwm-workspace--client-p "exwm-workspace.el" (&optional frame)) (declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") diff --git a/exwm-manage.el b/exwm-manage.el index f44071020d..ba5bc83b00 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -123,8 +123,14 @@ You can still make the X windows floating afterwards." (unless (and exwm--geometry (not force)) (let ((reply (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GetGeometry :drawable id)))) - (when reply ;nil when destroyed - (setq exwm--geometry reply)))))) + (setq exwm--geometry + (or reply + ;; Provide a reasonable fallback value. + (make-instance 'xcb:RECTANGLE + :x 0 + :y 0 + :width (/ (x-display-pixel-width) 2) + :height (/ (x-display-pixel-height) 2)))))))) (defun exwm-manage--update-ewmh-state (id) "Update _NET_WM_STATE." -- cgit 1.4.1 From b75c89cae2a1c4c70044f885c44a95fd2f9950dd Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 27 May 2018 23:29:36 +0800 Subject: Fix systemtray position when menu-bar / tool-bar is enabled * exwm-systemtray.el (exwm-systemtray--on-workspace-switch) (exwm-systemtray--on-randr-refresh): Take menu-bar / tool-bar into account when placing systemtray. --- exwm-systemtray.el | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/exwm-systemtray.el b/exwm-systemtray.el index dea5dbbc2e..ba1e388934 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -330,9 +330,7 @@ You shall use the default value if using auto-hide minibuffer." (frame-parameter exwm-workspace--current 'window-id)) :x 0 - :y (- (elt (elt exwm-workspace--workareas - exwm-workspace-current-index) - 3) + :y (- (frame-pixel-height exwm-workspace--current) exwm-systemtray-height)))) (exwm-systemtray--refresh)) @@ -343,9 +341,7 @@ You shall use the default value if using auto-hide minibuffer." (make-instance 'xcb:ConfigureWindow :window exwm-systemtray--embedder :value-mask xcb:ConfigWindow:Y - :y (- (elt (elt exwm-workspace--workareas - exwm-workspace-current-index) - 3) + :y (- (frame-pixel-height exwm-workspace--current) exwm-systemtray-height)))) (exwm-systemtray--refresh)) -- cgit 1.4.1 From 0680be104f9394e39dd55b5c4e33b9b7e4e77926 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 18 Jun 2018 22:20:23 +0800 Subject: Grab & Replay key events with XI2 and XTEST ; A change has been made in Xorg server 1.20 that replaying a key ; event with keyboard grabbed would generate extra focus change and ; enter/leave events. This basically breaks line-mode for apps like ; Firefox. This commit reimplements the grab & replay functionality ; with XI2 and XTEST. * exwm-input.el (exwm-input--devices): New variable for caching slave keyboards. (exwm-input--update-devices): Update it and re-grab keys if necessary. (exwm-input--on-Hierarchy): Event listener for the Hierarchy event that would in turn call `exwm-input--update-devices' to update the cache. * exwm-input.el (exwm-input--on-KeyPress): Use XI2 KeyPress events. (exwm-input--on-KeyRelease): Event listener for the KeyRelease events. (exwm-input--grab-global-prefix-keys): Use XI2 and also select KeyRelease events. (exwm-input--on-KeyPress-line-mode): Use XI2 KeyPress events and replay key events with XTEST. (exwm-input--on-KeyPress-char-mode, exwm-input--grab-keyboard) (exwm-input--release-keyboard): Use XI2 KeyPress events. * exwm-input.el (exwm-input--init): Initialize the XI2 and XTEST extensions; add listeners for XI2 KeyPress, KeyRelease and Hierarchy events. --- exwm-input.el | 307 ++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 222 insertions(+), 85 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 153410828f..9bb6444808 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -36,6 +36,9 @@ ;;; Code: (require 'xcb-keysyms) +(require 'xcb-xinput) +(require 'xcb-xtest) + (require 'exwm-core) (defgroup exwm-input nil @@ -102,6 +105,8 @@ defined in `exwm-mode-map' here." (defconst exwm-input--update-focus-interval 0.01 "Time interval (in seconds) for accumulating input focus update requests.") +(defvar exwm-input--devices nil "List of slave keyboard devices.") + (defvar exwm-input--during-command nil "Indicate whether between `pre-command-hook' and `post-command-hook'.") @@ -364,6 +369,44 @@ ARGS are additional arguments to CALLBACK." :window exwm--root :data (or id xcb:Window:None)))) +(defun exwm-input--update-devices (update) + "Update the cache of slave keyboards." + (with-slots (infos) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:xinput:XIQueryDevice + :deviceid xcb:xinput:Device:All)) + (setq exwm-input--devices + (delq nil + (mapcar (lambda (info) + (with-slots (deviceid type enabled name) info + (setq name (downcase name)) + (when (and (= xcb:xinput:DeviceType:SlaveKeyboard + type) + (string-match-p "keyboard" name) + ;; Exclude XTEST keyboard. + (not (string-match-p "xtest" name))) + deviceid))) + infos))) + (unless exwm-input--devices + (error "Failed to retrieve keyboards")) + (when update + ;; Try to re-grab all keys. + (exwm-input--update-global-prefix-keys) + (dolist (pair exwm--id-buffer-alist) + (with-current-buffer (cdr pair) + (when exwm--keyboard-grabbed + (exwm-input--grab-keyboard (car pair)))))))) + +(defun exwm-input--on-Hierarchy (data _synthetic) + "Handle Hierarchy events." + (let ((evt (make-instance 'xcb:xinput:Hierarchy))) + (xcb:unmarshal evt data) + (with-slots (flags infos) evt + (when (/= 0 (logand flags + (logior xcb:xinput:HierarchyMask:SlaveAdded + xcb:xinput:HierarchyMask:SlaveRemoved))) + (exwm-input--update-devices t))))) + (defun exwm-input--on-ButtonPress (data _synthetic) "Handle ButtonPress event." (let ((obj (make-instance 'xcb:ButtonPress)) @@ -417,12 +460,30 @@ ARGS are additional arguments to CALLBACK." (defun exwm-input--on-KeyPress (data _synthetic) "Handle KeyPress event." - (let ((obj (make-instance 'xcb:KeyPress))) + (let ((obj (make-instance 'xcb:xinput:KeyPress))) (xcb:unmarshal obj data) (if (eq major-mode 'exwm-mode) (funcall exwm--on-KeyPress obj data) (exwm-input--on-KeyPress-char-mode obj)))) +(defun exwm-input--on-KeyRelease (data _synthetic) + "Handle KeyRelease event." + ;; TODO: For simplicity every KeyRelease event is replayed which is likely + ;; to cause overheads and perhaps problems. + (let ((evt (make-instance 'xcb:xinput:KeyRelease))) + (xcb:unmarshal evt data) ;FIXME: optimize. + (with-slots (deviceid detail root-x root-y) evt + (xcb:+request exwm--connection + (make-instance 'xcb:xtest:FakeInput + :type 3 ;KeyRelease + :detail detail + :time xcb:Time:CurrentTime + :root exwm--root + :rootX root-x + :rootY root-y + :deviceid deviceid)) + (xcb:flush exwm--connection)))) + (defun exwm-input--on-CreateNotify (data _synthetic) "Handle CreateNotify events." (let ((evt (make-instance 'xcb:CreateNotify))) @@ -445,29 +506,44 @@ ARGS are additional arguments to CALLBACK." 'children)))))) (defun exwm-input--grab-global-prefix-keys (&rest xwins) - (let ((req (make-instance 'xcb:GrabKey - :owner-events 0 + (let ((req (make-instance 'xcb:xinput:XIPassiveGrabDevice + :time xcb:Time:CurrentTime :grab-window nil - :modifiers nil - :key nil - :pointer-mode xcb:GrabMode:Async - :keyboard-mode xcb:GrabMode:Async)) - keysym keycode) + :cursor 0 + :detail nil + :deviceid nil + :num-modifiers 1 + :mask-len 1 + :grab-type xcb:xinput:GrabType:Keycode + :grab-mode xcb:xinput:GrabMode22:Sync + :paired-device-mode 0 + :owner-events xcb:xinput:GrabOwner:NoOwner + :mask (list + (logior xcb:xinput:XIEventMask:KeyPress + xcb:xinput:XIEventMask:KeyRelease)) + :modifiers nil)) + keysym keycode sequences) (dolist (k exwm-input--global-prefix-keys) (setq keysym (xcb:keysyms:event->keysym exwm--connection k) keycode (xcb:keysyms:keysym->keycode exwm--connection (car keysym))) - (setf (slot-value req 'modifiers) (cdr keysym) - (slot-value req 'key) keycode) - (dolist (xwin xwins) - (setf (slot-value req 'grab-window) xwin) - (xcb:+request exwm--connection req) - ;; Also grab this key with num-lock mask set. - (when (/= 0 xcb:keysyms:num-lock-mask) - (setf (slot-value req 'modifiers) - (logior (cdr keysym) xcb:keysyms:num-lock-mask)) - (xcb:+request exwm--connection req)))) - (xcb:flush exwm--connection))) + (setf (slot-value req 'modifiers) (list (cdr keysym)) + (slot-value req 'detail) keycode) + (dolist (device exwm-input--devices) + (setf (slot-value req 'deviceid) device) + (dolist (xwin xwins) + (setf (slot-value req 'grab-window) xwin) + (setq sequences (append sequences + (list (xcb:+request exwm--connection req)))) + ;; Also grab this key with num-lock mask set. + (when (/= 0 xcb:keysyms:num-lock-mask) + (setf (slot-value req 'modifiers) + (list (logior (cdr keysym) xcb:keysyms:num-lock-mask))) + (setq sequences (append sequences + (list (xcb:+request exwm--connection + req)))))))) + (dolist (sequence sequences) + (xcb:+reply exwm--connection sequence)))) (defun exwm-input--set-key (key command) (global-set-key key command) @@ -563,60 +639,71 @@ instead." (defun exwm-input--on-KeyPress-line-mode (key-press raw-data) "Parse X KeyPress event to Emacs key event and then feed the command loop." - (with-slots (detail state) key-press - (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) - event raw-event mode) - (when (and (/= 0 (car keysym)) - (setq raw-event (xcb:keysyms:keysym->event - exwm--connection (car keysym) - (logand state (lognot (cdr keysym))))) - (setq event (exwm-input--mimic-read-event raw-event)) - (or exwm-input-line-mode-passthrough - exwm-input--during-command - ;; Forward the event when there is an incomplete key - ;; sequence or when the minibuffer is active. - exwm-input--line-mode-cache - (eq (active-minibuffer-window) (selected-window)) - ;; - (memq event exwm-input--global-prefix-keys) - (memq event exwm-input-prefix-keys) - (when overriding-terminal-local-map - (lookup-key overriding-terminal-local-map - (vector event))) - (lookup-key (current-local-map) (vector event)) - (gethash event exwm-input--simulation-keys))) - (setq mode xcb:Allow:AsyncKeyboard) - (exwm-input--cache-event event) - (exwm-input--unread-event raw-event)) - (unless mode - (if (= 0 (logand #x6000 state)) ;Check the 13~14 bits. - ;; Not an XKB state; just replay it. - (setq mode xcb:Allow:ReplayKeyboard) - ;; An XKB state; sent it with SendEvent. - ;; FIXME: Can this also be replayed? - ;; FIXME: KeyRelease events are lost. - (setq mode xcb:Allow:AsyncKeyboard) + (with-slots (deviceid detail root-x root-y mods) key-press + (let* ((state (slot-value mods 'effective)) + (keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) + event raw-event) + (if (and (/= 0 (car keysym)) + (setq raw-event (xcb:keysyms:keysym->event + exwm--connection (car keysym) + (logand state (lognot (cdr keysym))))) + (setq event (exwm-input--mimic-read-event raw-event)) + (or exwm-input-line-mode-passthrough + exwm-input--during-command + ;; Forward the event when there is an incomplete key + ;; sequence or when the minibuffer is active. + exwm-input--line-mode-cache + (eq (active-minibuffer-window) (selected-window)) + ;; + (memq event exwm-input--global-prefix-keys) + (memq event exwm-input-prefix-keys) + (when overriding-terminal-local-map + (lookup-key overriding-terminal-local-map + (vector event))) + (lookup-key (current-local-map) (vector event)) + (gethash event exwm-input--simulation-keys))) + (progn + (exwm-input--cache-event event) + (exwm-input--unread-event raw-event)) + (if (/= 0 (logand #x6000 state)) ;Check the 13~14 bits. + ;; An XKB state; sent it with SendEvent. + ;; FIXME: Can this also be replayed? + ;; FIXME: KeyRelease events are lost. + (xcb:+request exwm--connection + (make-instance 'xcb:SendEvent + :propagate 0 + :destination (slot-value key-press 'event) + :event-mask xcb:EventMask:NoEvent + :event raw-data)) + ;; Replay the key. (xcb:+request exwm--connection - (make-instance 'xcb:SendEvent - :propagate 0 - :destination (slot-value key-press 'event) - :event-mask xcb:EventMask:NoEvent - :event raw-data))) + (make-instance 'xcb:xtest:FakeInput + :type 2 ;KeyPress + :detail detail + :time xcb:Time:CurrentTime + :root exwm--root + :rootX root-x + :rootY root-y + :deviceid deviceid))) ;; Make Emacs aware of this event when defining keyboard macros. (when (and defining-kbd-macro event) (set-transient-map '(keymap (t . (lambda () (interactive))))) (exwm-input--unread-event event))) (xcb:+request exwm--connection - (make-instance 'xcb:AllowEvents - :mode mode - :time xcb:Time:CurrentTime)) + (make-instance 'xcb:xinput:XIAllowEvents + :time xcb:Time:CurrentTime + :deviceid deviceid + :event-mode xcb:xinput:EventMode:AsyncDevice + :touchid 0 + :grab-window 0)) (xcb:flush exwm--connection)))) (defun exwm-input--on-KeyPress-char-mode (key-press &optional _raw-data) "Handle KeyPress event in char-mode." - (with-slots (detail state) key-press - (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) - event raw-event) + (with-slots (deviceid detail mods) key-press + (let* ((state (slot-value mods 'effective)) + (keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) + event raw-event) (when (and (/= 0 (car keysym)) (setq raw-event (xcb:keysyms:keysym->event exwm--connection (car keysym) @@ -628,12 +715,15 @@ instead." (setq exwm-input--temp-line-mode t) (exwm-input--grab-keyboard) (exwm-input--cache-event event) - (exwm-input--unread-event raw-event))))) - (xcb:+request exwm--connection - (make-instance 'xcb:AllowEvents - :mode xcb:Allow:AsyncKeyboard - :time xcb:Time:CurrentTime)) - (xcb:flush exwm--connection)) + (exwm-input--unread-event raw-event)))) + (xcb:+request exwm--connection + (make-instance 'xcb:xinput:XIAllowEvents + :time xcb:Time:CurrentTime + :deviceid deviceid + :event-mode xcb:xinput:EventMode:AsyncDevice + :touchid 0 + :grab-window 0)) + (xcb:flush exwm--connection))) (defun exwm-input--update-mode-line (id) "Update the propertized `mode-line-process' for window ID." @@ -667,15 +757,30 @@ instead." "Grab all key events on window ID." (unless id (setq id (exwm--buffer->id (window-buffer)))) (when id - (when (xcb:+request-checked+request-check exwm--connection - (make-instance 'xcb:GrabKey - :owner-events 0 - :grab-window id - :modifiers xcb:ModMask:Any - :key xcb:Grab:Any - :pointer-mode xcb:GrabMode:Async - :keyboard-mode xcb:GrabMode:Sync)) - (exwm--log "Failed to grab keyboard for #x%x" id)) + (let ((sequences + (mapcar + (lambda (device) + (xcb:+request exwm--connection + (make-instance 'xcb:xinput:XIPassiveGrabDevice + :time xcb:Time:CurrentTime + :grab-window id + :cursor 0 + :detail xcb:Grab:Any + :deviceid device + :num-modifiers 1 + :mask-len 1 + :grab-type xcb:xinput:GrabType:Keycode + :grab-mode xcb:xinput:GrabMode22:Sync + :paired-device-mode 0 + :owner-events xcb:xinput:GrabOwner:NoOwner + :mask + (list + (logior xcb:xinput:XIEventMask:KeyPress + xcb:xinput:XIEventMask:KeyRelease)) + :modifiers (list 2147483648.)))) + exwm-input--devices))) + (dolist (sequence sequences) + (xcb:+reply exwm--connection sequence))) (with-current-buffer (exwm--id->buffer id) (setq exwm--on-KeyPress #'exwm-input--on-KeyPress-line-mode)))) @@ -683,12 +788,16 @@ instead." "Ungrab all key events on window ID." (unless id (setq id (exwm--buffer->id (window-buffer)))) (when id - (when (xcb:+request-checked+request-check exwm--connection - (make-instance 'xcb:UngrabKey - :key xcb:Grab:Any - :grab-window id - :modifiers xcb:ModMask:Any)) - (exwm--log "Failed to release keyboard for #x%x" id)) + (dolist (device exwm-input--devices) + (xcb:+request exwm--connection + (make-instance 'xcb:xinput:XIPassiveUngrabDevice + :grab-window id + :detail xcb:Grab:Any + :deviceid device + :num-modifiers 1 + :grab-type xcb:xinput:GrabType:Keycode + :modifiers (list 2147483648.)))) ;1 << 31 + (xcb:flush exwm--connection) (exwm-input--grab-global-prefix-keys id) (with-current-buffer (exwm--id->buffer id) (setq exwm--on-KeyPress #'exwm-input--on-KeyPress-char-mode)))) @@ -930,6 +1039,30 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences." (defun exwm-input--init () "Initialize the keyboard module." + ;; Initialize the XI2 extension. + (if (= 0 (slot-value (xcb:get-extension-data exwm--connection 'xcb:xinput) + 'present)) + (error "[EXWM] XI2 extension is not supported by the server") + (with-slots (major-version minor-version) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:xinput:XIQueryVersion + :major-version 2 + :minor-version 0)) + (when (or (/= major-version 2) (/= minor-version 0)) + (error "[EXWM] XI2 extension 2.0 is not supported by the server")))) + (exwm-input--update-devices nil) + ;; Initialize the XTEST extension. + (if (= 0 (slot-value (xcb:get-extension-data exwm--connection 'xcb:xtest) + 'present)) + (error "[EXWM] XTEST extension is not supported by the server") + (with-slots (major-version minor-version) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:xtest:GetVersion + :major-version 2 + :minor-version 2)) + (when (or (/= major-version 2) (/= minor-version 2)) + (error "[EXWM] XTEST extension 2.2 is not supported by the server")))) + ;; Refresh keyboard mapping (xcb:keysyms:init exwm--connection #'exwm-input--on-keysyms-update) ;; Create the X window and intern the atom used to fetch timestamp. @@ -970,7 +1103,11 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences." (xcb:+event exwm--connection 'xcb:PropertyNotify #'exwm-input--on-PropertyNotify) (xcb:+event exwm--connection 'xcb:CreateNotify #'exwm-input--on-CreateNotify) - (xcb:+event exwm--connection 'xcb:KeyPress #'exwm-input--on-KeyPress) + (xcb:+event exwm--connection 'xcb:xinput:KeyPress #'exwm-input--on-KeyPress) + (xcb:+event exwm--connection 'xcb:xinput:KeyRelease + #'exwm-input--on-KeyRelease) + (xcb:+event exwm--connection 'xcb:xinput:Hierarchy + #'exwm-input--on-Hierarchy) (xcb:+event exwm--connection 'xcb:ButtonPress #'exwm-input--on-ButtonPress) (xcb:+event exwm--connection 'xcb:ButtonRelease #'exwm-floating--stop-moveresize) -- cgit 1.4.1 From 4bb2d87a8dbaeaabdd3a79550421f60222d0de3c Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 15 Jul 2018 00:00:00 +0800 Subject: Revert "Grab & Replay key events with XI2 and XTEST" This reverts commit 0680be104f9394e39dd55b5c4e33b9b7e4e77926. --- exwm-input.el | 307 ++++++++++++++++------------------------------------------ 1 file changed, 85 insertions(+), 222 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 9bb6444808..153410828f 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -36,9 +36,6 @@ ;;; Code: (require 'xcb-keysyms) -(require 'xcb-xinput) -(require 'xcb-xtest) - (require 'exwm-core) (defgroup exwm-input nil @@ -105,8 +102,6 @@ defined in `exwm-mode-map' here." (defconst exwm-input--update-focus-interval 0.01 "Time interval (in seconds) for accumulating input focus update requests.") -(defvar exwm-input--devices nil "List of slave keyboard devices.") - (defvar exwm-input--during-command nil "Indicate whether between `pre-command-hook' and `post-command-hook'.") @@ -369,44 +364,6 @@ ARGS are additional arguments to CALLBACK." :window exwm--root :data (or id xcb:Window:None)))) -(defun exwm-input--update-devices (update) - "Update the cache of slave keyboards." - (with-slots (infos) - (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:xinput:XIQueryDevice - :deviceid xcb:xinput:Device:All)) - (setq exwm-input--devices - (delq nil - (mapcar (lambda (info) - (with-slots (deviceid type enabled name) info - (setq name (downcase name)) - (when (and (= xcb:xinput:DeviceType:SlaveKeyboard - type) - (string-match-p "keyboard" name) - ;; Exclude XTEST keyboard. - (not (string-match-p "xtest" name))) - deviceid))) - infos))) - (unless exwm-input--devices - (error "Failed to retrieve keyboards")) - (when update - ;; Try to re-grab all keys. - (exwm-input--update-global-prefix-keys) - (dolist (pair exwm--id-buffer-alist) - (with-current-buffer (cdr pair) - (when exwm--keyboard-grabbed - (exwm-input--grab-keyboard (car pair)))))))) - -(defun exwm-input--on-Hierarchy (data _synthetic) - "Handle Hierarchy events." - (let ((evt (make-instance 'xcb:xinput:Hierarchy))) - (xcb:unmarshal evt data) - (with-slots (flags infos) evt - (when (/= 0 (logand flags - (logior xcb:xinput:HierarchyMask:SlaveAdded - xcb:xinput:HierarchyMask:SlaveRemoved))) - (exwm-input--update-devices t))))) - (defun exwm-input--on-ButtonPress (data _synthetic) "Handle ButtonPress event." (let ((obj (make-instance 'xcb:ButtonPress)) @@ -460,30 +417,12 @@ ARGS are additional arguments to CALLBACK." (defun exwm-input--on-KeyPress (data _synthetic) "Handle KeyPress event." - (let ((obj (make-instance 'xcb:xinput:KeyPress))) + (let ((obj (make-instance 'xcb:KeyPress))) (xcb:unmarshal obj data) (if (eq major-mode 'exwm-mode) (funcall exwm--on-KeyPress obj data) (exwm-input--on-KeyPress-char-mode obj)))) -(defun exwm-input--on-KeyRelease (data _synthetic) - "Handle KeyRelease event." - ;; TODO: For simplicity every KeyRelease event is replayed which is likely - ;; to cause overheads and perhaps problems. - (let ((evt (make-instance 'xcb:xinput:KeyRelease))) - (xcb:unmarshal evt data) ;FIXME: optimize. - (with-slots (deviceid detail root-x root-y) evt - (xcb:+request exwm--connection - (make-instance 'xcb:xtest:FakeInput - :type 3 ;KeyRelease - :detail detail - :time xcb:Time:CurrentTime - :root exwm--root - :rootX root-x - :rootY root-y - :deviceid deviceid)) - (xcb:flush exwm--connection)))) - (defun exwm-input--on-CreateNotify (data _synthetic) "Handle CreateNotify events." (let ((evt (make-instance 'xcb:CreateNotify))) @@ -506,44 +445,29 @@ ARGS are additional arguments to CALLBACK." 'children)))))) (defun exwm-input--grab-global-prefix-keys (&rest xwins) - (let ((req (make-instance 'xcb:xinput:XIPassiveGrabDevice - :time xcb:Time:CurrentTime + (let ((req (make-instance 'xcb:GrabKey + :owner-events 0 :grab-window nil - :cursor 0 - :detail nil - :deviceid nil - :num-modifiers 1 - :mask-len 1 - :grab-type xcb:xinput:GrabType:Keycode - :grab-mode xcb:xinput:GrabMode22:Sync - :paired-device-mode 0 - :owner-events xcb:xinput:GrabOwner:NoOwner - :mask (list - (logior xcb:xinput:XIEventMask:KeyPress - xcb:xinput:XIEventMask:KeyRelease)) - :modifiers nil)) - keysym keycode sequences) + :modifiers nil + :key nil + :pointer-mode xcb:GrabMode:Async + :keyboard-mode xcb:GrabMode:Async)) + keysym keycode) (dolist (k exwm-input--global-prefix-keys) (setq keysym (xcb:keysyms:event->keysym exwm--connection k) keycode (xcb:keysyms:keysym->keycode exwm--connection (car keysym))) - (setf (slot-value req 'modifiers) (list (cdr keysym)) - (slot-value req 'detail) keycode) - (dolist (device exwm-input--devices) - (setf (slot-value req 'deviceid) device) - (dolist (xwin xwins) - (setf (slot-value req 'grab-window) xwin) - (setq sequences (append sequences - (list (xcb:+request exwm--connection req)))) - ;; Also grab this key with num-lock mask set. - (when (/= 0 xcb:keysyms:num-lock-mask) - (setf (slot-value req 'modifiers) - (list (logior (cdr keysym) xcb:keysyms:num-lock-mask))) - (setq sequences (append sequences - (list (xcb:+request exwm--connection - req)))))))) - (dolist (sequence sequences) - (xcb:+reply exwm--connection sequence)))) + (setf (slot-value req 'modifiers) (cdr keysym) + (slot-value req 'key) keycode) + (dolist (xwin xwins) + (setf (slot-value req 'grab-window) xwin) + (xcb:+request exwm--connection req) + ;; Also grab this key with num-lock mask set. + (when (/= 0 xcb:keysyms:num-lock-mask) + (setf (slot-value req 'modifiers) + (logior (cdr keysym) xcb:keysyms:num-lock-mask)) + (xcb:+request exwm--connection req)))) + (xcb:flush exwm--connection))) (defun exwm-input--set-key (key command) (global-set-key key command) @@ -639,71 +563,60 @@ instead." (defun exwm-input--on-KeyPress-line-mode (key-press raw-data) "Parse X KeyPress event to Emacs key event and then feed the command loop." - (with-slots (deviceid detail root-x root-y mods) key-press - (let* ((state (slot-value mods 'effective)) - (keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) - event raw-event) - (if (and (/= 0 (car keysym)) - (setq raw-event (xcb:keysyms:keysym->event - exwm--connection (car keysym) - (logand state (lognot (cdr keysym))))) - (setq event (exwm-input--mimic-read-event raw-event)) - (or exwm-input-line-mode-passthrough - exwm-input--during-command - ;; Forward the event when there is an incomplete key - ;; sequence or when the minibuffer is active. - exwm-input--line-mode-cache - (eq (active-minibuffer-window) (selected-window)) - ;; - (memq event exwm-input--global-prefix-keys) - (memq event exwm-input-prefix-keys) - (when overriding-terminal-local-map - (lookup-key overriding-terminal-local-map - (vector event))) - (lookup-key (current-local-map) (vector event)) - (gethash event exwm-input--simulation-keys))) - (progn - (exwm-input--cache-event event) - (exwm-input--unread-event raw-event)) - (if (/= 0 (logand #x6000 state)) ;Check the 13~14 bits. - ;; An XKB state; sent it with SendEvent. - ;; FIXME: Can this also be replayed? - ;; FIXME: KeyRelease events are lost. - (xcb:+request exwm--connection - (make-instance 'xcb:SendEvent - :propagate 0 - :destination (slot-value key-press 'event) - :event-mask xcb:EventMask:NoEvent - :event raw-data)) - ;; Replay the key. + (with-slots (detail state) key-press + (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) + event raw-event mode) + (when (and (/= 0 (car keysym)) + (setq raw-event (xcb:keysyms:keysym->event + exwm--connection (car keysym) + (logand state (lognot (cdr keysym))))) + (setq event (exwm-input--mimic-read-event raw-event)) + (or exwm-input-line-mode-passthrough + exwm-input--during-command + ;; Forward the event when there is an incomplete key + ;; sequence or when the minibuffer is active. + exwm-input--line-mode-cache + (eq (active-minibuffer-window) (selected-window)) + ;; + (memq event exwm-input--global-prefix-keys) + (memq event exwm-input-prefix-keys) + (when overriding-terminal-local-map + (lookup-key overriding-terminal-local-map + (vector event))) + (lookup-key (current-local-map) (vector event)) + (gethash event exwm-input--simulation-keys))) + (setq mode xcb:Allow:AsyncKeyboard) + (exwm-input--cache-event event) + (exwm-input--unread-event raw-event)) + (unless mode + (if (= 0 (logand #x6000 state)) ;Check the 13~14 bits. + ;; Not an XKB state; just replay it. + (setq mode xcb:Allow:ReplayKeyboard) + ;; An XKB state; sent it with SendEvent. + ;; FIXME: Can this also be replayed? + ;; FIXME: KeyRelease events are lost. + (setq mode xcb:Allow:AsyncKeyboard) (xcb:+request exwm--connection - (make-instance 'xcb:xtest:FakeInput - :type 2 ;KeyPress - :detail detail - :time xcb:Time:CurrentTime - :root exwm--root - :rootX root-x - :rootY root-y - :deviceid deviceid))) + (make-instance 'xcb:SendEvent + :propagate 0 + :destination (slot-value key-press 'event) + :event-mask xcb:EventMask:NoEvent + :event raw-data))) ;; Make Emacs aware of this event when defining keyboard macros. (when (and defining-kbd-macro event) (set-transient-map '(keymap (t . (lambda () (interactive))))) (exwm-input--unread-event event))) (xcb:+request exwm--connection - (make-instance 'xcb:xinput:XIAllowEvents - :time xcb:Time:CurrentTime - :deviceid deviceid - :event-mode xcb:xinput:EventMode:AsyncDevice - :touchid 0 - :grab-window 0)) + (make-instance 'xcb:AllowEvents + :mode mode + :time xcb:Time:CurrentTime)) (xcb:flush exwm--connection)))) (defun exwm-input--on-KeyPress-char-mode (key-press &optional _raw-data) "Handle KeyPress event in char-mode." - (with-slots (deviceid detail mods) key-press - (let* ((state (slot-value mods 'effective)) - (keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) - event raw-event) + (with-slots (detail state) key-press + (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) + event raw-event) (when (and (/= 0 (car keysym)) (setq raw-event (xcb:keysyms:keysym->event exwm--connection (car keysym) @@ -715,15 +628,12 @@ instead." (setq exwm-input--temp-line-mode t) (exwm-input--grab-keyboard) (exwm-input--cache-event event) - (exwm-input--unread-event raw-event)))) - (xcb:+request exwm--connection - (make-instance 'xcb:xinput:XIAllowEvents - :time xcb:Time:CurrentTime - :deviceid deviceid - :event-mode xcb:xinput:EventMode:AsyncDevice - :touchid 0 - :grab-window 0)) - (xcb:flush exwm--connection))) + (exwm-input--unread-event raw-event))))) + (xcb:+request exwm--connection + (make-instance 'xcb:AllowEvents + :mode xcb:Allow:AsyncKeyboard + :time xcb:Time:CurrentTime)) + (xcb:flush exwm--connection)) (defun exwm-input--update-mode-line (id) "Update the propertized `mode-line-process' for window ID." @@ -757,30 +667,15 @@ instead." "Grab all key events on window ID." (unless id (setq id (exwm--buffer->id (window-buffer)))) (when id - (let ((sequences - (mapcar - (lambda (device) - (xcb:+request exwm--connection - (make-instance 'xcb:xinput:XIPassiveGrabDevice - :time xcb:Time:CurrentTime - :grab-window id - :cursor 0 - :detail xcb:Grab:Any - :deviceid device - :num-modifiers 1 - :mask-len 1 - :grab-type xcb:xinput:GrabType:Keycode - :grab-mode xcb:xinput:GrabMode22:Sync - :paired-device-mode 0 - :owner-events xcb:xinput:GrabOwner:NoOwner - :mask - (list - (logior xcb:xinput:XIEventMask:KeyPress - xcb:xinput:XIEventMask:KeyRelease)) - :modifiers (list 2147483648.)))) - exwm-input--devices))) - (dolist (sequence sequences) - (xcb:+reply exwm--connection sequence))) + (when (xcb:+request-checked+request-check exwm--connection + (make-instance 'xcb:GrabKey + :owner-events 0 + :grab-window id + :modifiers xcb:ModMask:Any + :key xcb:Grab:Any + :pointer-mode xcb:GrabMode:Async + :keyboard-mode xcb:GrabMode:Sync)) + (exwm--log "Failed to grab keyboard for #x%x" id)) (with-current-buffer (exwm--id->buffer id) (setq exwm--on-KeyPress #'exwm-input--on-KeyPress-line-mode)))) @@ -788,16 +683,12 @@ instead." "Ungrab all key events on window ID." (unless id (setq id (exwm--buffer->id (window-buffer)))) (when id - (dolist (device exwm-input--devices) - (xcb:+request exwm--connection - (make-instance 'xcb:xinput:XIPassiveUngrabDevice - :grab-window id - :detail xcb:Grab:Any - :deviceid device - :num-modifiers 1 - :grab-type xcb:xinput:GrabType:Keycode - :modifiers (list 2147483648.)))) ;1 << 31 - (xcb:flush exwm--connection) + (when (xcb:+request-checked+request-check exwm--connection + (make-instance 'xcb:UngrabKey + :key xcb:Grab:Any + :grab-window id + :modifiers xcb:ModMask:Any)) + (exwm--log "Failed to release keyboard for #x%x" id)) (exwm-input--grab-global-prefix-keys id) (with-current-buffer (exwm--id->buffer id) (setq exwm--on-KeyPress #'exwm-input--on-KeyPress-char-mode)))) @@ -1039,30 +930,6 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences." (defun exwm-input--init () "Initialize the keyboard module." - ;; Initialize the XI2 extension. - (if (= 0 (slot-value (xcb:get-extension-data exwm--connection 'xcb:xinput) - 'present)) - (error "[EXWM] XI2 extension is not supported by the server") - (with-slots (major-version minor-version) - (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:xinput:XIQueryVersion - :major-version 2 - :minor-version 0)) - (when (or (/= major-version 2) (/= minor-version 0)) - (error "[EXWM] XI2 extension 2.0 is not supported by the server")))) - (exwm-input--update-devices nil) - ;; Initialize the XTEST extension. - (if (= 0 (slot-value (xcb:get-extension-data exwm--connection 'xcb:xtest) - 'present)) - (error "[EXWM] XTEST extension is not supported by the server") - (with-slots (major-version minor-version) - (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:xtest:GetVersion - :major-version 2 - :minor-version 2)) - (when (or (/= major-version 2) (/= minor-version 2)) - (error "[EXWM] XTEST extension 2.2 is not supported by the server")))) - ;; Refresh keyboard mapping (xcb:keysyms:init exwm--connection #'exwm-input--on-keysyms-update) ;; Create the X window and intern the atom used to fetch timestamp. @@ -1103,11 +970,7 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences." (xcb:+event exwm--connection 'xcb:PropertyNotify #'exwm-input--on-PropertyNotify) (xcb:+event exwm--connection 'xcb:CreateNotify #'exwm-input--on-CreateNotify) - (xcb:+event exwm--connection 'xcb:xinput:KeyPress #'exwm-input--on-KeyPress) - (xcb:+event exwm--connection 'xcb:xinput:KeyRelease - #'exwm-input--on-KeyRelease) - (xcb:+event exwm--connection 'xcb:xinput:Hierarchy - #'exwm-input--on-Hierarchy) + (xcb:+event exwm--connection 'xcb:KeyPress #'exwm-input--on-KeyPress) (xcb:+event exwm--connection 'xcb:ButtonPress #'exwm-input--on-ButtonPress) (xcb:+event exwm--connection 'xcb:ButtonRelease #'exwm-floating--stop-moveresize) -- cgit 1.4.1 From 6be729847e78576df599cbf70512e3f9e436114b Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 15 Jul 2018 00:00:00 +0800 Subject: Also send a WM_TAKE_FOCUS when setting focus with SetInputFocus * exwm-input.el (exwm-input--set-focus): Send an extra WM_TAKE_FOCUS event to workaround the key replay issue with Xorg 1.20 when keyboard is grabbed. --- exwm-input.el | 51 +++++++++++++++++++++------------------------------ 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 153410828f..55949aa1b2 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -158,36 +158,27 @@ This value should always be overwritten.") "Set input focus to window ID in a proper way." (when (exwm--id->buffer id) (with-current-buffer (exwm--id->buffer id) - (cond - ((and (not exwm--hints-input) - (memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols)) - (when (= (frame-parameter nil 'exwm-id) - (slot-value (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:GetInputFocus)) - 'focus)) - (exwm--log "Focus on #x%x with WM_TAKE_FOCUS" id) - (exwm-input--update-timestamp - (lambda (timestamp id) - (let ((event (make-instance 'xcb:icccm:WM_TAKE_FOCUS - :window id - :time timestamp))) - (setq event (xcb:marshal event exwm--connection)) - (xcb:+request exwm--connection - (make-instance 'xcb:icccm:SendEvent - :destination id - :event event)) - (exwm-input--set-active-window id) - (xcb:flush exwm--connection))) - id))) - (t - (exwm--log "Focus on #x%x with SetInputFocus" id) - (xcb:+request exwm--connection - (make-instance 'xcb:SetInputFocus - :revert-to xcb:InputFocus:Parent - :focus id - :time xcb:Time:CurrentTime)) - (exwm-input--set-active-window id) - (xcb:flush exwm--connection)))))) + (exwm-input--update-timestamp + (lambda (timestamp id send-input-focus) + (when send-input-focus + (xcb:+request exwm--connection + (make-instance 'xcb:SetInputFocus + :revert-to xcb:InputFocus:Parent + :focus id + :time timestamp))) + (let ((event (make-instance 'xcb:icccm:WM_TAKE_FOCUS + :window id + :time timestamp))) + (setq event (xcb:marshal event exwm--connection)) + (xcb:+request exwm--connection + (make-instance 'xcb:icccm:SendEvent + :destination id + :event event))) + (exwm-input--set-active-window id) + (xcb:flush exwm--connection)) + id + (or exwm--hints-input + (not (memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols))))))) (defun exwm-input--update-timestamp (callback &rest args) "Fetch the latest timestamp from the server and feed it to CALLBACK. -- cgit 1.4.1 From c1b6a296a8fd57f6364a674486f54cafdd5d6255 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 15 Jul 2018 00:00:00 +0800 Subject: Shrink Emacs frames with X request * exwm-manage.el (exwm-manage--on-ConfigureRequest): There's a problem in shrinking Emacs frames with `set-frame-width' and `set-frame-height'. Use `exwm--set-geometry' instead. --- exwm-manage.el | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index ba5bc83b00..a0a9e05948 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -597,18 +597,14 @@ border-width: %d; sibling: #x%x; stack-mode: %d" (if buffer (with-current-buffer buffer (exwm--log "ConfigureWindow (resize floating X window)") - (when (and (/= 0 (logand value-mask xcb:ConfigWindow:Width)) - (>= (abs width-delta) exwm-manage--width-delta-min)) - (set-frame-width exwm--floating-frame - (+ (frame-pixel-width exwm--floating-frame) - width-delta) - nil t)) - (when (and (/= 0 (logand value-mask xcb:ConfigWindow:Height)) - (>= (abs height-delta) exwm-manage--height-delta-min)) - (set-frame-height exwm--floating-frame + (exwm--set-geometry (frame-parameter exwm--floating-frame + 'exwm-outer-id) + nil + nil + (+ (frame-pixel-width exwm--floating-frame) + width-delta) (+ (frame-pixel-height exwm--floating-frame) - height-delta) - nil t))) + height-delta))) (exwm--log "ConfigureWindow (preserve geometry)") ;; Configure the unmanaged window. ;; But Emacs frames should be excluded. Generally we don't -- cgit 1.4.1 From 1364f80f09668a16358aa8f509266bae41d55685 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 15 Jul 2018 00:00:00 +0800 Subject: Add support for automatic cursor warping * exwm-workspace.el (exwm-workspace-warp-cursor): New user option. (exwm-workspace-switch): Automatically warp cursor after workspace switch. --- exwm-workspace.el | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/exwm-workspace.el b/exwm-workspace.el index 9031721547..935d9d14a2 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -47,6 +47,10 @@ deleted, moved, etc)." "Non-nil to show buffers on other workspaces." :type 'boolean) +(defcustom exwm-workspace-warp-cursor nil + "Non-nil to warp cursor automatically after workspace switch." + :type 'boolean) + (defcustom exwm-workspace-number 1 "Initial number of workspaces." :type 'integer) @@ -595,6 +599,17 @@ for internal use only." (make-instance 'xcb:ewmh:set-_NET_CURRENT_DESKTOP :window exwm--root :data index)) (xcb:flush exwm--connection)) + (when exwm-workspace-warp-cursor + (with-slots (win-x win-y) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:QueryPointer + :window (frame-parameter frame + 'exwm-outer-id))) + (when (or (> win-x (frame-pixel-width frame)) + (> win-y (frame-pixel-height))) + (set-mouse-position frame + (/ (frame-width frame) 2) + (/ (frame-height frame) 2))))) (when (frame-live-p old-frame) (with-selected-frame old-frame (run-hooks 'focus-out-hook))) -- cgit 1.4.1 From bc5f0b3ffac4d262d4b8537baae0baf72402d079 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 15 Jul 2018 00:00:00 +0800 Subject: ; Use `derived-mode-p'. --- exwm-config.el | 6 +++--- exwm-floating.el | 4 ++-- exwm-input.el | 14 +++++++------- exwm-layout.el | 19 ++++++++++--------- exwm-workspace.el | 8 ++++---- exwm.el | 2 +- 6 files changed, 27 insertions(+), 26 deletions(-) diff --git a/exwm-config.el b/exwm-config.el index 5742cac187..89320bc1e6 100644 --- a/exwm-config.el +++ b/exwm-config.el @@ -79,11 +79,11 @@ You can find the original one at `exwm-config-ido-buffer-window-other-frame'." (with-current-buffer (window-buffer (selected-window)) - (if (and (eq major-mode 'exwm-mode) + (if (and (derived-mode-p 'exwm-mode) exwm--floating-frame) ;; Switch from a floating frame. (with-current-buffer buffer - (if (and (eq major-mode 'exwm-mode) + (if (and (derived-mode-p 'exwm-mode) exwm--floating-frame (eq exwm--frame exwm-workspace--current)) ;; Switch to another floating frame. @@ -92,7 +92,7 @@ You can find the original one at `exwm-config-ido-buffer-window-other-frame'." (or (get-buffer-window buffer exwm-workspace--current) (selected-window)))) (with-current-buffer buffer - (when (eq major-mode 'exwm-mode) + (when (derived-mode-p 'exwm-mode) (if (eq exwm--frame exwm-workspace--current) (when exwm--floating-frame ;; Switch to a floating frame on the current workspace. diff --git a/exwm-floating.el b/exwm-floating.el index 0210492b18..aa2f98822a 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -411,7 +411,7 @@ This is also used by X window containers.") (defun exwm-floating-hide () "Hide the current floating X window (which would show again when selected)." (interactive) - (when (and (eq major-mode 'exwm-mode) + (when (and (derived-mode-p 'exwm-mode) exwm--floating-frame) (exwm-layout--hide exwm--id) (select-frame-set-input-focus exwm-workspace--current))) @@ -641,7 +641,7 @@ This is also used by X window containers.") "Move a floating window right by DELTA-X pixels and down by DELTA-Y pixels. Both DELTA-X and DELTA-Y default to 1. This command should be bound locally." - (unless (and (eq major-mode 'exwm-mode) exwm--floating-frame) + (unless (and (derived-mode-p 'exwm-mode) exwm--floating-frame) (user-error "[EXWM] `exwm-floating-move' is only for floating X windows")) (unless delta-x (setq delta-x 1)) (unless delta-y (setq delta-y 1)) diff --git a/exwm-input.el b/exwm-input.el index 55949aa1b2..80ab352109 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -224,7 +224,7 @@ ARGS are additional arguments to CALLBACK." ;; The X window is on another workspace. (exwm-workspace-switch frame) (with-current-buffer buffer - (when (and (eq major-mode 'exwm-mode) + (when (and (derived-mode-p 'exwm-mode) (not (eq exwm--frame exwm-workspace--current))) ;; The floating X window is on another workspace. (exwm-workspace-switch exwm--frame))))) @@ -295,7 +295,7 @@ ARGS are additional arguments to CALLBACK." "Update input focus." (when (window-live-p window) (with-current-buffer (window-buffer window) - (if (eq major-mode 'exwm-mode) + (if (derived-mode-p 'exwm-mode) (if (not (eq exwm--frame exwm-workspace--current)) (progn (set-frame-parameter exwm--frame 'exwm-selected-window window) @@ -369,14 +369,14 @@ ARGS are additional arguments to CALLBACK." (cond ((and (eq button-event exwm-input-move-event) ;; Either an undecorated or a floating X window. (with-current-buffer buffer - (or (not (eq major-mode 'exwm-mode)) + (or (not (derived-mode-p 'exwm-mode)) exwm--floating-frame))) ;; Move (exwm-floating--start-moveresize event xcb:ewmh:_NET_WM_MOVERESIZE_MOVE)) ((and (eq button-event exwm-input-resize-event) (with-current-buffer buffer - (or (not (eq major-mode 'exwm-mode)) + (or (not (derived-mode-p 'exwm-mode)) exwm--floating-frame))) ;; Resize (exwm-floating--start-moveresize event)) @@ -389,7 +389,7 @@ ARGS are additional arguments to CALLBACK." ;; The X window is on another workspace (exwm-workspace-switch frame) (with-current-buffer buffer - (when (and (eq major-mode 'exwm-mode) + (when (and (derived-mode-p 'exwm-mode) (not (eq exwm--frame exwm-workspace--current))) ;; The floating X window is on another workspace @@ -410,7 +410,7 @@ ARGS are additional arguments to CALLBACK." "Handle KeyPress event." (let ((obj (make-instance 'xcb:KeyPress))) (xcb:unmarshal obj data) - (if (eq major-mode 'exwm-mode) + (if (derived-mode-p 'exwm-mode) (funcall exwm--on-KeyPress obj data) (exwm-input--on-KeyPress-char-mode obj)))) @@ -613,7 +613,7 @@ instead." exwm--connection (car keysym) (logand state (lognot (cdr keysym))))) (setq event (exwm-input--mimic-read-event raw-event))) - (if (not (eq major-mode 'exwm-mode)) + (if (not (derived-mode-p 'exwm-mode)) (exwm-input--unread-event raw-event) ;; Grab keyboard temporarily. (setq exwm-input--temp-line-mode t) diff --git a/exwm-layout.el b/exwm-layout.el index b5685f417e..1d3de291a7 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -225,7 +225,7 @@ is t EXWM buffers are never selected by `other-buffer'. When variable `exwm-layout--other-buffer-exclude-buffers' is a list of buffers, EXWM buffers belonging to that list are never selected by `other-buffer'." - (or (not (eq 'exwm-mode (buffer-local-value 'major-mode buffer))) + (or (not (with-current-buffer buffer (derived-mode-p 'exwm-mode))) (and (not exwm-layout--other-buffer-exclude-exwm-mode-buffers) (not (memq buffer exwm-layout--other-buffer-exclude-buffers)) ;; Do not select if already shown in some window. @@ -268,7 +268,7 @@ selected by `other-buffer'." ;; Refresh a floating frame (let ((window (frame-first-window frame))) (with-current-buffer (window-buffer window) - (when (and (eq major-mode 'exwm-mode) + (when (and (derived-mode-p 'exwm-mode) ;; It may be a buffer waiting to be killed. (exwm--id->buffer exwm--id)) (exwm--log "Refresh floating window #x%x" exwm--id) @@ -279,7 +279,7 @@ selected by `other-buffer'." (let ((exwm-layout--other-buffer-exclude-exwm-mode-buffers t)) (dolist (window windows) (with-current-buffer (window-buffer window) - (when (eq major-mode 'exwm-mode) + (when (derived-mode-p 'exwm-mode) (switch-to-prev-buffer window)))))) ;; Refresh the whole workspace ;; Workspaces other than the active one can also be refreshed (RandR) @@ -310,7 +310,8 @@ selected by `other-buffer'." (car-safe (window-prev-buffers window))))) (and prev-buffer - (eq 'exwm-mode (buffer-local-value 'major-mode prev-buffer)) + (with-current-buffer prev-buffer + (derived-mode-p 'exwm-mode)) (push prev-buffer covered-buffers)))))))) ;; Set some sensible buffer to vacated windows. (let ((exwm-layout--other-buffer-exclude-buffers covered-buffers)) @@ -320,7 +321,7 @@ selected by `other-buffer'." (let ((exwm-layout--other-buffer-exclude-exwm-mode-buffers t)) (dolist (window (window-list frame 0)) (with-current-buffer (window-buffer window) - (when (and (eq major-mode 'exwm-mode) + (when (and (derived-mode-p 'exwm-mode) (or exwm--floating-frame (not (eq frame exwm--frame)))) (switch-to-prev-buffer window))))) (exwm-layout--set-client-list-stacking) @@ -359,7 +360,7 @@ windows." (cond ((zerop delta)) ;no operation ((window-minibuffer-p)) ;avoid resize minibuffer-window - ((not (and (eq major-mode 'exwm-mode) exwm--floating-frame)) + ((not (and (derived-mode-p 'exwm-mode) exwm--floating-frame)) ;; Resize on tiling layout (unless (= 0 (window-resizable nil delta horizontal nil t)) ;not resizable (let ((window-resize-pixelwise t)) @@ -461,7 +462,7 @@ See also `exwm-layout-enlarge-window'." (defun exwm-layout-hide-mode-line () "Hide mode-line." (interactive) - (when (and (eq major-mode 'exwm-mode) mode-line-format) + (when (and (derived-mode-p 'exwm-mode) mode-line-format) (let (mode-line-height) (when exwm--floating-frame (setq mode-line-height (window-mode-line-height @@ -479,7 +480,7 @@ See also `exwm-layout-enlarge-window'." (defun exwm-layout-show-mode-line () "Show mode-line." (interactive) - (when (and (eq major-mode 'exwm-mode) (not mode-line-format)) + (when (and (derived-mode-p 'exwm-mode) (not mode-line-format)) (setq mode-line-format exwm--mode-line-format exwm--mode-line-format nil) (if (not exwm--floating-frame) @@ -496,7 +497,7 @@ See also `exwm-layout-enlarge-window'." (defun exwm-layout-toggle-mode-line () "Toggle the display of mode-line." (interactive) - (when (eq major-mode 'exwm-mode) + (when (derived-mode-p 'exwm-mode) (if mode-line-format (exwm-layout-hide-mode-line) (exwm-layout-show-mode-line)))) diff --git a/exwm-workspace.el b/exwm-workspace.el index 935d9d14a2..6535e11f27 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -506,7 +506,7 @@ for internal use only." (list (cond ((null current-prefix-arg) - (unless (and (eq major-mode 'exwm-mode) + (unless (and (derived-mode-p 'exwm-mode) ;; The prompt is invisible in fullscreen mode. (exwm-layout--fullscreen-p)) (let ((exwm-workspace--prompt-add-allowed t) @@ -644,7 +644,7 @@ Passing a workspace frame as the first option is for internal use only." (defun exwm-workspace-swap (workspace1 workspace2) "Interchange position of WORKSPACE1 with that of WORKSPACE2." (interactive - (unless (and (eq major-mode 'exwm-mode) + (unless (and (derived-mode-p 'exwm-mode) ;; The prompt is invisible in fullscreen mode. (exwm-layout--fullscreen-p)) (let (w1 w2) @@ -684,7 +684,7 @@ before it." (interactive (cond ((null current-prefix-arg) - (unless (and (eq major-mode 'exwm-mode) + (unless (and (derived-mode-p 'exwm-mode) ;; The prompt is invisible in fullscreen mode. (exwm-layout--fullscreen-p)) (list exwm-workspace--current @@ -921,7 +921,7 @@ INDEX must not exceed the current number of workspaces." (rename-buffer (concat " " (buffer-name))))))))))) (when buffer-or-name (with-current-buffer buffer-or-name - (if (eq major-mode 'exwm-mode) + (if (derived-mode-p 'exwm-mode) ;; EXWM buffer. (if (eq exwm--frame exwm-workspace--current) ;; On the current workspace. diff --git a/exwm.el b/exwm.el index 17f73d8ca0..33cced44a9 100644 --- a/exwm.el +++ b/exwm.el @@ -108,7 +108,7 @@ "Reset the state of the selected window (non-fullscreen, line-mode, etc)." (interactive) (with-current-buffer (window-buffer) - (when (eq major-mode 'exwm-mode) + (when (derived-mode-p 'exwm-mode) (when (exwm-layout--fullscreen-p) (exwm-layout-unset-fullscreen)) ;; Force refresh -- cgit 1.4.1 From f45e9b92731432cd2b01e065d9910321e3a9c9f7 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 15 Jul 2018 00:00:00 +0800 Subject: Fix XTerm crash on startup * exwm-input.el (exwm-input--set-focus): Applications like XTerm crashes on receiving WM_TAKE_FOCUS so only send it to X windows accepting it. --- exwm-input.el | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 80ab352109..57a2873503 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -159,26 +159,28 @@ This value should always be overwritten.") (when (exwm--id->buffer id) (with-current-buffer (exwm--id->buffer id) (exwm-input--update-timestamp - (lambda (timestamp id send-input-focus) + (lambda (timestamp id send-input-focus wm-take-focus) (when send-input-focus (xcb:+request exwm--connection (make-instance 'xcb:SetInputFocus :revert-to xcb:InputFocus:Parent :focus id :time timestamp))) - (let ((event (make-instance 'xcb:icccm:WM_TAKE_FOCUS - :window id - :time timestamp))) - (setq event (xcb:marshal event exwm--connection)) - (xcb:+request exwm--connection - (make-instance 'xcb:icccm:SendEvent - :destination id - :event event))) + (when wm-take-focus + (let ((event (make-instance 'xcb:icccm:WM_TAKE_FOCUS + :window id + :time timestamp))) + (setq event (xcb:marshal event exwm--connection)) + (xcb:+request exwm--connection + (make-instance 'xcb:icccm:SendEvent + :destination id + :event event)))) (exwm-input--set-active-window id) (xcb:flush exwm--connection)) id (or exwm--hints-input - (not (memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols))))))) + (not (memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols))) + (memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols))))) (defun exwm-input--update-timestamp (callback &rest args) "Fetch the latest timestamp from the server and feed it to CALLBACK. -- cgit 1.4.1 From 7e5750392c08e662420631395352d1e76a792a51 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 18 Jul 2018 00:00:00 +0800 Subject: Bump version to 0.19 --- exwm.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm.el b/exwm.el index 33cced44a9..9f9b6f4681 100644 --- a/exwm.el +++ b/exwm.el @@ -4,8 +4,8 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0.18 -;; Package-Requires: ((xelb "0.14")) +;; Version: 0.19 +;; Package-Requires: ((xelb "0.15")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From 13a14579cc1bb772735f895dd5b4b90c6812f3ee Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 29 Jul 2018 00:00:00 +0000 Subject: Fix issues with destroying full screen X windows * exwm-manage.el (exwm-manage--unmanage-window): Set the Emacs window of an full screen X window as non-dedicated before killing its buffer so as not to cause other side effects. --- exwm-manage.el | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/exwm-manage.el b/exwm-manage.el index a0a9e05948..349157f1ff 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -392,6 +392,10 @@ manager is shutting down." :window window :parent exwm--root :x 0 :y 0)) (xcb:+request exwm--connection (make-instance 'xcb:DestroyWindow :window container)))) + (when (exwm-layout--fullscreen-p) + (let ((window (get-buffer-window))) + (when window + (set-window-dedicated-p window nil)))) (exwm-manage--set-client-list) (xcb:flush exwm--connection)) (let ((kill-buffer-func -- cgit 1.4.1 From aebcb0344f18b1aa284a432811175fde2d2feae5 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 5 Aug 2018 00:00:00 +0000 Subject: When mapping an X window check if it's on an active workspace * exwm-layout.el (exwm-layout--refresh): Avoid mapping X windows on inactive workspaces. --- exwm-layout.el | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index 1d3de291a7..8c86bbd37b 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -48,6 +48,7 @@ (declare-function exwm-input--release-keyboard "exwm-input.el") (declare-function exwm-input--grab-keyboard "exwm-input.el") (declare-function exwm-input-grab-keyboard "exwm-input.el") +(declare-function exwm-workspace--active-p "exwm-workspace.el" (frame)) (declare-function exwm-workspace--client-p "exwm-workspace.el" (&optional frame)) (declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") @@ -272,7 +273,9 @@ selected by `other-buffer'." ;; It may be a buffer waiting to be killed. (exwm--id->buffer exwm--id)) (exwm--log "Refresh floating window #x%x" exwm--id) - (exwm-layout--show exwm--id window)))) + (if (exwm-workspace--active-p exwm--frame) + (exwm-layout--show exwm--id window) + (exwm-layout--hide exwm--id))))) ;; Other frames (e.g. terminal/graphical frame of emacsclient) ;; We shall bury all `exwm-mode' buffers in this case (setq windows (window-list frame 0)) ;exclude minibuffer @@ -292,11 +295,11 @@ selected by `other-buffer'." (eq frame exwm--frame))) (setq windows (get-buffer-window-list (current-buffer) 0)) (if (not windows) - (when (eq frame exwm--frame) ;for exwm-layout-show-all-buffers - (exwm-layout--hide exwm--id)) + (exwm-layout--hide exwm--id) (let ((window (car windows))) (if (eq frame exwm--frame) - (exwm-layout--show exwm--id window) + (when (exwm-workspace--active-p frame) + (exwm-layout--show exwm--id window)) (exwm-workspace-move-window frame exwm--id)) ;; Make sure this buffer is not displayed elsewhere. Note down ;; windows displaying an EXWM-buffer now displayed elsewhere; we -- cgit 1.4.1 From e6527227b387d08146963613ada37022ddf33d80 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 12 Aug 2018 00:00:00 +0000 Subject: Exclude irrelevant X windows when refreshing * exwm-layout.el (exwm-layout--refresh): Only check X windows on the workspace being examined. --- exwm-layout.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exwm-layout.el b/exwm-layout.el index 8c86bbd37b..9427607712 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -295,7 +295,8 @@ selected by `other-buffer'." (eq frame exwm--frame))) (setq windows (get-buffer-window-list (current-buffer) 0)) (if (not windows) - (exwm-layout--hide exwm--id) + (when (eq frame exwm--frame) + (exwm-layout--hide exwm--id)) (let ((window (car windows))) (if (eq frame exwm--frame) (when (exwm-workspace--active-p frame) -- cgit 1.4.1 From 4d43e3119a2d0cb002d87340cd11c1d372ad126e Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 12 Aug 2018 00:00:00 +0000 Subject: Avoid using `set-mouse-position' to warp pointer * exwm-workspace.el (exwm-workspace-switch): Warp pointer with the WarpPointer request. --- exwm-workspace.el | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 6535e11f27..1034966374 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -607,9 +607,18 @@ for internal use only." 'exwm-outer-id))) (when (or (> win-x (frame-pixel-width frame)) (> win-y (frame-pixel-height))) - (set-mouse-position frame - (/ (frame-width frame) 2) - (/ (frame-height frame) 2))))) + (xcb:+request exwm--connection + (make-instance 'xcb:WarpPointer + :src-window xcb:Window:None + :dst-window (frame-parameter frame + 'exwm-outer-id) + :src-x 0 + :src-y 0 + :src-width 0 + :src-height 0 + :dst-x (/ (frame-pixel-width frame) 2) + :dst-y (/ (frame-pixel-height frame) 2))) + (xcb:flush exwm--connection)))) (when (frame-live-p old-frame) (with-selected-frame old-frame (run-hooks 'focus-out-hook))) -- cgit 1.4.1 From 8d15a39c4d4928d9bc38bed63e2bb85e4536af45 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Mon, 13 Aug 2018 12:00:00 +0000 Subject: Print log output to an EXWM-specific messages buffer Using `message' to log debugging information is cumbersome, as the output appears constantly in the minibuffer, obscuring prompts and other information. In the case of long messages, it might resize the minibuffer, which causes EXWM to perform additional actions due to the log output. This change reimplements EXWM debug logging using a separate buffer (*EXWM-DEBUG*). Basic functionality, like scrolling when point is at the end of the buffer is maintained. * exwm-core.el (exwm--log): Use `exwm-debug--message' instead of `message'. Prefix all messages with the name of the function. Make FORMAT-STRING argument optional. * exwm-debug.el: New file. (exwm-debug-buffer): New variable holding the buffer where debug messages are output to. (exwm-debug--message): New function printing a message to `exwm-debug-buffer'. (exwm-debug--backtrace): New function printing a backtrace. --- exwm-core.el | 15 +++++++-- exwm-debug.el | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 exwm-debug.el diff --git a/exwm-core.el b/exwm-core.el index ab5159c6a7..66b79171f0 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -31,6 +31,7 @@ (require 'xcb) (require 'xcb-icccm) (require 'xcb-ewmh) +(require 'exwm-debug) (eval-and-compile (defvar exwm-debug-on nil "Non-nil to turn on debug for EXWM.")) @@ -70,10 +71,18 @@ (declare-function exwm-workspace-move-window "exwm-workspace.el" (frame-or-index &optional id)) -(defmacro exwm--log (format-string &rest args) - "Print debug message." +(defmacro exwm--log (&optional format-string &rest objects) + "Emit a message prepending the name of the function being executed. + +FORMAT-STRING is a string specifying the message to output, as in +`format'. The OBJECTS arguments specify the substitutions." (when exwm-debug-on - `(message (concat "[EXWM] " ,format-string) ,@args))) + (unless format-string (setq format-string "")) + `(progn + (exwm-debug--message (concat "%s:\t" ,format-string "\n") + (exwm-debug--compile-time-function-name) + ,@objects) + nil))) (defmacro exwm--debug (&rest forms) (when exwm-debug-on `(progn ,@forms))) diff --git a/exwm-debug.el b/exwm-debug.el new file mode 100644 index 0000000000..89421da651 --- /dev/null +++ b/exwm-debug.el @@ -0,0 +1,104 @@ +;;; exwm-debug.el --- Debugging helpers for EXWM -*- lexical-binding: t -*- + +;; Copyright (C) 2015-2016 Free Software Foundation, Inc. + +;; Author: Chris Feng +;; Adrián Medraño Calvo + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Commentary: + +;; This module collects functions that help in debugging EXWM. + +;;; Code: + +(eval-and-compile + (defvar exwm-debug-on nil "Non-nil to turn on debug for EXWM.")) + +(defvar exwm-debug-buffer + (when exwm-debug-on + (let ((buffer (get-buffer-create "*EXWM-DEBUG*"))) + (buffer-disable-undo buffer) + buffer)) + "Buffer to write debug messages to.") + +(defun exwm-debug--call-stack () + "Return the current call stack frames." + (let (frames frame + ;; No need to acount for our setq, while, let, ... + (index 5)) + (while (setq frame (backtrace-frame index)) + (push frame frames) + (cl-incf index)) + (cl-remove-if-not 'car frames))) + +(defmacro exwm-debug--compile-time-function-name () + "Get the name of outermost definition at expansion time." + (let* ((frame (cl-find-if + (lambda (frame) + (ignore-errors + (let ((clause (car (cl-third frame)))) + (or (equal clause 'defalias) + (equal clause 'cl-defmethod))))) + (reverse (exwm-debug--call-stack)))) + (defn (cl-third frame)) + (deftype (car defn))) + (cl-case deftype + ((defalias) (symbol-name (cl-cadadr defn))) + ((cl-defmethod) (symbol-name (cadr defn))) + (t "")))) + +(defmacro exwm-debug--with-debug-buffer (&rest forms) + "Evaluate FORMS making sure `exwm-debug-buffer' is correctly updated." + `(with-current-buffer exwm-debug-buffer + (let (windows-eob) + ;; Note windows whose point is at EOB. + (dolist (w (get-buffer-window-list exwm-debug-buffer t t)) + (when (= (window-point w) (point-max)) + (push w windows-eob))) + (save-excursion + (goto-char (point-max)) + ,@forms) + ;; Restore point. + (dolist (w windows-eob) + (set-window-point w (point-max)))))) + +(defun exwm-debug--message (format-string &rest objects) + "Print a message to `exwm-debug-buffer'. + +The FORMAT-STRING argument follows the speficies how to print each of +the passed OBJECTS. See `format' for details." + (exwm-debug--with-debug-buffer + (insert (apply #'format format-string objects)))) + +(defmacro exwm-debug--backtrace () + "Print a backtrace to the `exwm-debug-buffer'." + '(exwm-debug--with-debug-buffer + (let ((standard-output exwm-debug-buffer)) + (backtrace)))) + +(defmacro exwm-debug--backtrace-on-error (&rest forms) + "Evaluate FORMS. Printing a backtrace if an error is signaled." + `(let ((debug-on-error t) + (debugger (lambda (&rest _) (exwm-debug--backtrace)))) + ,@forms)) + + + +(provide 'exwm-debug) + +;;; exwm-debug.el ends here -- cgit 1.4.1 From ac600f091630480188932ad8d2ee315c8ee84c8e Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Mon, 13 Aug 2018 12:00:00 +0000 Subject: Commands for interacting with the log buffer remotely * exwm-debug.el (exwm-debug--clear, exwm-debug--mark): New functions. --- exwm-core.el | 5 +++++ exwm-debug.el | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/exwm-core.el b/exwm-core.el index 66b79171f0..5c501e4e3f 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -290,6 +290,11 @@ least SECS seconds later." (/= ,i exwm-workspace-current-index)]) (number-sequence 0 (1- (exwm-workspace--count)))))))) +(exwm--debug + (let ((map exwm-mode-map)) + (define-key map "\C-c\C-l" #'exwm-debug--clear) + (define-key map "\C-c\C-m" #'exwm-debug--mark))) + (define-derived-mode exwm-mode nil "EXWM" "Major mode for managing X windows. diff --git a/exwm-debug.el b/exwm-debug.el index 89421da651..cd2ec393d7 100644 --- a/exwm-debug.el +++ b/exwm-debug.el @@ -97,6 +97,18 @@ the passed OBJECTS. See `format' for details." (debugger (lambda (&rest _) (exwm-debug--backtrace)))) ,@forms)) +(defun exwm-debug--clear () + "Clear the debug buffer." + (interactive) + (exwm-debug--with-debug-buffer + (erase-buffer))) + +(defun exwm-debug--mark () + "Insert a mark in the debug buffer." + (interactive) + (exwm-debug--with-debug-buffer + (insert " \n"))) + (provide 'exwm-debug) -- cgit 1.4.1 From 33dec8d1a382f77b3bd8a64f8d56e8e06c2043b1 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Mon, 13 Aug 2018 12:00:00 +0000 Subject: Trace more functions --- exwm-core.el | 2 ++ exwm-input.el | 32 ++++++++++++++++++++++++++++++++ exwm-layout.el | 14 ++++++++++++++ exwm-manage.el | 16 +++++++++++++++- exwm-workspace.el | 7 +++++++ exwm.el | 2 ++ 6 files changed, 72 insertions(+), 1 deletion(-) diff --git a/exwm-core.el b/exwm-core.el index 5c501e4e3f..3159519f12 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -97,6 +97,7 @@ FORMAT-STRING is a string specifying the message to output, as in (defun exwm--lock (&rest _args) "Lock (disable all events)." + (exwm--log) (xcb:+request exwm--connection (make-instance 'xcb:ChangeWindowAttributes :window exwm--root @@ -106,6 +107,7 @@ FORMAT-STRING is a string specifying the message to output, as in (defun exwm--unlock (&rest _args) "Unlock (enable all events)." + (exwm--log) (xcb:+request exwm--connection (make-instance 'xcb:ChangeWindowAttributes :window exwm--root diff --git a/exwm-input.el b/exwm-input.el index 57a2873503..7885d189aa 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -157,6 +157,7 @@ This value should always be overwritten.") (defun exwm-input--set-focus (id) "Set input focus to window ID in a proper way." (when (exwm--id->buffer id) + (exwm--log "id=#x%x" id) (with-current-buffer (exwm--id->buffer id) (exwm-input--update-timestamp (lambda (timestamp id send-input-focus wm-take-focus) @@ -187,6 +188,7 @@ This value should always be overwritten.") ARGS are additional arguments to CALLBACK." (setq exwm-input--timestamp-callback (cons callback args)) + (exwm--log) (xcb:+request exwm--connection (make-instance 'xcb:ChangeProperty :mode xcb:PropMode:Replace @@ -200,6 +202,7 @@ ARGS are additional arguments to CALLBACK." (defun exwm-input--on-PropertyNotify (data _synthetic) "Handle PropertyNotify events." + (exwm--log) (when exwm-input--timestamp-callback (let ((obj (make-instance 'xcb:PropertyNotify))) (xcb:unmarshal obj data) @@ -218,6 +221,7 @@ ARGS are additional arguments to CALLBACK." (with-slots (time root event root-x root-y event-x event-y state) evt (setq buffer (exwm--id->buffer event) window (get-buffer-window buffer t)) + (exwm--log "EnterNotify: buffer=%s; window=%s" buffer window) (when (and buffer window (not (eq window (selected-window)))) (setq frame (window-frame window) frame-xid (frame-parameter frame 'exwm-id)) @@ -265,6 +269,8 @@ ARGS are additional arguments to CALLBACK." (eq (current-buffer) (window-buffer)) (not (string-prefix-p " *temp*" (buffer-name (car (last (buffer-list))))))) + (exwm--log "current-buffer=%S selected-window=%S" + (current-buffer) (selected-window)) (redirect-frame-focus (selected-frame) nil) (setq exwm-input--update-focus-window (selected-window)) (exwm-input--update-focus-defer))) @@ -296,6 +302,7 @@ ARGS are additional arguments to CALLBACK." (defun exwm-input--update-focus (window) "Update input focus." (when (window-live-p window) + (exwm--log "focus-window=%s focus-buffer=%s" window (window-buffer window)) (with-current-buffer (window-buffer window) (if (derived-mode-p 'exwm-mode) (if (not (eq exwm--frame exwm-workspace--current)) @@ -331,6 +338,10 @@ ARGS are additional arguments to CALLBACK." ;; The focus is on another workspace (e.g. it got clicked) ;; so switch to it. (progn + (exwm--log "Switching to %s's workspace %s (%s)" + window + (window-frame window) + (selected-frame)) (set-frame-parameter (selected-frame) 'exwm-selected-window window) (exwm--defer 0 #'exwm-workspace-switch (selected-frame))) @@ -346,12 +357,14 @@ ARGS are additional arguments to CALLBACK." (defun exwm-input--on-minibuffer-setup () "Run in `minibuffer-setup-hook' to set input focus." + (exwm--log) (unless (exwm-workspace--client-p) ;; Set input focus on the Emacs frame (x-focus-frame (window-frame (minibuffer-selected-window))))) (defun exwm-input--set-active-window (&optional id) "Set _NET_ACTIVE_WINDOW." + (exwm--log) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_ACTIVE_WINDOW :window exwm--root @@ -363,6 +376,8 @@ ARGS are additional arguments to CALLBACK." (mode xcb:Allow:SyncPointer) button-event window buffer frame) (xcb:unmarshal obj data) + (exwm--log "major-mode=%s buffer=%s" + major-mode (buffer-name (current-buffer))) (with-slots (detail time event state) obj (setq button-event (xcb:keysyms:keysym->event exwm--connection detail state) @@ -412,12 +427,15 @@ ARGS are additional arguments to CALLBACK." "Handle KeyPress event." (let ((obj (make-instance 'xcb:KeyPress))) (xcb:unmarshal obj data) + (exwm--log "major-mode=%s buffer=%s" + major-mode (buffer-name (current-buffer))) (if (derived-mode-p 'exwm-mode) (funcall exwm--on-KeyPress obj data) (exwm-input--on-KeyPress-char-mode obj)))) (defun exwm-input--on-CreateNotify (data _synthetic) "Handle CreateNotify events." + (exwm--log) (let ((evt (make-instance 'xcb:CreateNotify))) (xcb:unmarshal evt data) (with-slots (window) evt @@ -425,6 +443,7 @@ ARGS are additional arguments to CALLBACK." (defun exwm-input--update-global-prefix-keys () "Update `exwm-input--global-prefix-keys'." + (exwm--log) (when exwm--connection (let ((original exwm-input--global-prefix-keys)) (setq exwm-input--global-prefix-keys nil) @@ -438,6 +457,7 @@ ARGS are additional arguments to CALLBACK." 'children)))))) (defun exwm-input--grab-global-prefix-keys (&rest xwins) + (exwm--log) (let ((req (make-instance 'xcb:GrabKey :owner-events 0 :grab-window nil @@ -450,6 +470,8 @@ ARGS are additional arguments to CALLBACK." (setq keysym (xcb:keysyms:event->keysym exwm--connection k) keycode (xcb:keysyms:keysym->keycode exwm--connection (car keysym))) + (exwm--log "Grabbing key=%s (keysym=%s keycode=%s)" + (single-key-description k) keysym keycode) (setf (slot-value req 'modifiers) (cdr keysym) (slot-value req 'key) keycode) (dolist (xwin xwins) @@ -498,6 +520,7 @@ specifically saved in the Customize interface for `exwm-input-global-keys'. In configuration you should customize or set `exwm-input-global-keys' instead." (interactive "KSet key globally: \nCSet key %s to command: ") + (exwm--log) (setq exwm-input-global-keys (append exwm-input-global-keys (list (cons key command)))) (exwm-input--set-key key command) @@ -517,6 +540,7 @@ instead." (defun exwm-input--mimic-read-event (event) "Process EVENT as if it were returned by `read-event'." + (exwm--log) (unless (eq 0 extra-keyboard-modifiers) (setq event (event-convert-list (append (event-modifiers extra-keyboard-modifiers) @@ -559,6 +583,7 @@ instead." (with-slots (detail state) key-press (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) event raw-event mode) + (exwm--log "%s" keysym) (when (and (/= 0 (car keysym)) (setq raw-event (xcb:keysyms:keysym->event exwm--connection (car keysym) @@ -610,6 +635,7 @@ instead." (with-slots (detail state) key-press (let ((keysym (xcb:keysyms:keycode->keysym exwm--connection detail state)) event raw-event) + (exwm--log "%s" keysym) (when (and (/= 0 (car keysym)) (setq raw-event (xcb:keysyms:keysym->event exwm--connection (car keysym) @@ -660,6 +686,7 @@ instead." "Grab all key events on window ID." (unless id (setq id (exwm--buffer->id (window-buffer)))) (when id + (exwm--log "id=#x%x" id) (when (xcb:+request-checked+request-check exwm--connection (make-instance 'xcb:GrabKey :owner-events 0 @@ -676,6 +703,7 @@ instead." "Ungrab all key events on window ID." (unless id (setq id (exwm--buffer->id (window-buffer)))) (when id + (exwm--log "id=#x%x" id) (when (xcb:+request-checked+request-check exwm--connection (make-instance 'xcb:UngrabKey :key xcb:Grab:Any @@ -692,6 +720,7 @@ instead." (interactive (list (when (derived-mode-p 'exwm-mode) (exwm--buffer->id (window-buffer))))) (when id + (exwm--log "id=#x%x" id) (with-current-buffer (exwm--id->buffer id) (exwm-input--grab-keyboard id) (setq exwm--keyboard-grabbed t) @@ -704,6 +733,7 @@ instead." (interactive (list (when (derived-mode-p 'exwm-mode) (exwm--buffer->id (window-buffer))))) (when id + (exwm--log "id=#x%x" id) (with-current-buffer (exwm--id->buffer id) (exwm-input--release-keyboard id) (setq exwm--keyboard-grabbed nil) @@ -716,6 +746,7 @@ instead." (interactive (list (when (derived-mode-p 'exwm-mode) (exwm--buffer->id (window-buffer))))) (when id + (exwm--log "id=#x%x" id) (with-current-buffer (exwm--id->buffer id) (if exwm--keyboard-grabbed (exwm-input-release-keyboard id) @@ -731,6 +762,7 @@ instead." (car keysym))) (when (/= 0 keycode) (setq id (exwm--buffer->id (window-buffer (selected-window)))) + (exwm--log "id=#x%x event=%s keycode" id event keycode) (dolist (class '(xcb:KeyPress xcb:KeyRelease)) (xcb:+request exwm--connection (make-instance 'xcb:SendEvent diff --git a/exwm-layout.el b/exwm-layout.el index 9427607712..4aa4804a73 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -59,6 +59,7 @@ (defun exwm-layout--set-state (id state) "Set WM_STATE." + (exwm--log "id=#x%x" id) (xcb:+request exwm--connection (make-instance 'xcb:icccm:set-WM_STATE :window id :state state :icon xcb:Window:None)) @@ -146,6 +147,7 @@ (cl-defun exwm-layout-set-fullscreen (&optional id) "Make window ID fullscreen." (interactive) + (exwm--log "id=#x%x" (or id 0)) (unless (and (or id (derived-mode-p 'exwm-mode)) (not (exwm-layout--fullscreen-p))) (cl-return-from exwm-layout-set-fullscreen)) @@ -174,6 +176,7 @@ (cl-defun exwm-layout-unset-fullscreen (&optional id) "Restore window from fullscreen state." (interactive) + (exwm--log "id=#x%x" (or id 0)) (unless (and (or id (derived-mode-p 'exwm-mode)) (exwm-layout--fullscreen-p)) (cl-return-from exwm-layout-unset-fullscreen)) @@ -203,6 +206,7 @@ (cl-defun exwm-layout-toggle-fullscreen (&optional id) "Toggle fullscreen mode." (interactive (list (exwm--buffer->id (window-buffer)))) + (exwm--log "id=#x%x" (or id 0)) (unless (or id (derived-mode-p 'exwm-mode)) (cl-return-from exwm-layout-toggle-fullscreen)) (when id @@ -234,6 +238,7 @@ selected by `other-buffer'." (defun exwm-layout--set-client-list-stacking () "Set _NET_CLIENT_LIST_STACKING." + (exwm--log) (let (id clients-floating clients clients-iconic clients-other) (dolist (pair exwm--id-buffer-alist) (setq id (car pair)) @@ -261,6 +266,7 @@ selected by `other-buffer'." ;; `window-configuration-change-hook' makes the frame selected. (unless frame (setq frame (selected-frame))) + (exwm--log "frame=%s" frame) (let (covered-buffers ;EXWM-buffers covered by a new X window. vacated-windows ;Windows previously displaying EXWM-buffers. windows) @@ -333,6 +339,7 @@ selected by `other-buffer'." (defun exwm-layout--on-minibuffer-setup () "Refresh layout when minibuffer grows." + (exwm--log) (unless (exwm-workspace--client-p) (exwm--defer 0 (lambda () (when (< 1 (window-height (minibuffer-window))) @@ -345,6 +352,7 @@ selected by `other-buffer'." (or (cl-position ?\n (current-message)) (> (length (current-message)) (frame-width exwm-workspace--current)))) + (exwm--log) (if dirty (exwm-layout--refresh) (exwm--defer 0 #'exwm-layout--refresh)))) @@ -361,6 +369,7 @@ Normal hints are checked and regarded if the selected window is displaying an `exwm-mode' buffer. However, this may violate the normal hints set on other X windows." (interactive "p") + (exwm--log) (cond ((zerop delta)) ;no operation ((window-minibuffer-p)) ;avoid resize minibuffer-window @@ -466,6 +475,7 @@ See also `exwm-layout-enlarge-window'." (defun exwm-layout-hide-mode-line () "Hide mode-line." (interactive) + (exwm--log) (when (and (derived-mode-p 'exwm-mode) mode-line-format) (let (mode-line-height) (when exwm--floating-frame @@ -484,6 +494,7 @@ See also `exwm-layout-enlarge-window'." (defun exwm-layout-show-mode-line () "Show mode-line." (interactive) + (exwm--log) (when (and (derived-mode-p 'exwm-mode) (not mode-line-format)) (setq mode-line-format exwm--mode-line-format exwm--mode-line-format nil) @@ -501,6 +512,7 @@ See also `exwm-layout-enlarge-window'." (defun exwm-layout-toggle-mode-line () "Toggle the display of mode-line." (interactive) + (exwm--log) (when (derived-mode-p 'exwm-mode) (if mode-line-format (exwm-layout-hide-mode-line) @@ -509,6 +521,7 @@ See also `exwm-layout-enlarge-window'." (defun exwm-layout--init () "Initialize layout module." ;; Auto refresh layout + (exwm--log) (add-hook 'window-configuration-change-hook #'exwm-layout--refresh) ;; The behavior of `window-configuration-change-hook' will be changed. (when (fboundp 'window-pixel-width-before-size-change) @@ -522,6 +535,7 @@ See also `exwm-layout-enlarge-window'." (defun exwm-layout--exit () "Exit the layout module." + (exwm--log) (remove-hook 'window-configuration-change-hook #'exwm-layout--refresh) (when (fboundp 'window-pixel-width-before-size-change) (remove-hook 'window-size-change-functions #'exwm-layout--refresh)) diff --git a/exwm-manage.el b/exwm-manage.el index 349157f1ff..81a486c097 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -119,6 +119,7 @@ You can still make the X windows floating afterwards." (defun exwm-manage--update-geometry (id &optional force) "Update window geometry." + (exwm--log "id=#x%x" id) (with-current-buffer (exwm--id->buffer id) (unless (and exwm--geometry (not force)) (let ((reply (xcb:+request-unchecked+reply exwm--connection @@ -134,6 +135,7 @@ You can still make the X windows floating afterwards." (defun exwm-manage--update-ewmh-state (id) "Update _NET_WM_STATE." + (exwm--log "id=#x%x" id) (with-current-buffer (exwm--id->buffer id) (unless exwm--ewmh-state (let ((reply (xcb:+request-unchecked+reply exwm--connection @@ -144,6 +146,7 @@ You can still make the X windows floating afterwards." (defun exwm-manage--update-mwm-hints (id &optional force) "Update _MOTIF_WM_HINTS." + (exwm--log "id=#x%x" id) (with-current-buffer (exwm--id->buffer id) (unless (and (not exwm--mwm-hints-decorations) (not force)) (let ((reply (xcb:+request-unchecked+reply exwm--connection @@ -167,6 +170,7 @@ You can still make the X windows floating afterwards." (defun exwm-manage--set-client-list () "Set _NET_CLIENT_LIST." + (exwm--log) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_CLIENT_LIST :window exwm--root @@ -174,6 +178,7 @@ You can still make the X windows floating afterwards." (cl-defun exwm-manage--get-configurations () "Retrieve configurations for this buffer." + (exwm--log) (when (derived-mode-p 'exwm-mode) (dolist (i exwm-manage-configurations) (save-current-buffer @@ -412,6 +417,7 @@ manager is shutting down." (defun exwm-manage--scan () "Search for existing windows and try to manage them." + (exwm--log) (let* ((tree (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:QueryTree :window exwm--root))) @@ -433,6 +439,7 @@ manager is shutting down." (defun exwm-manage--kill-buffer-query-function () "Run in `kill-buffer-query-functions'." + (exwm--log "id=#x%x; buffer=%s" exwm--id (current-buffer)) (catch 'return (when (or (not exwm--id) (xcb:+request-checked+request-check exwm--connection @@ -510,6 +517,7 @@ Would you like to kill it? " (defun exwm-manage--kill-client (&optional id) "Kill an X client." (unless id (setq id (exwm--buffer->id (current-buffer)))) + (exwm--log "id=#x%x" id) (let* ((response (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:ewmh:get-_NET_WM_PID :window id))) (pid (and response (slot-value response 'value))) @@ -526,12 +534,14 @@ Would you like to kill it? " (defun exwm-manage--add-frame (frame) "Run in `after-make-frame-functions'." + (exwm--log "frame=%s" frame) (when (display-graphic-p frame) (push (string-to-number (frame-parameter frame 'outer-window-id)) exwm-manage--frame-outer-id-list))) (defun exwm-manage--remove-frame (frame) "Run in `delete-frame-functions'." + (exwm--log "frame=%s" frame) (when (display-graphic-p frame) (setq exwm-manage--frame-outer-id-list (delq (string-to-number (frame-parameter frame 'outer-window-id)) @@ -539,6 +549,7 @@ Would you like to kill it? " (defun exwm-manage--on-ConfigureRequest (data _synthetic) "Handle ConfigureRequest event." + (exwm--log) (let ((obj (make-instance 'xcb:ConfigureRequest)) buffer edges width-delta height-delta) (xcb:unmarshal obj data) @@ -631,6 +642,7 @@ border-width: %d; sibling: #x%x; stack-mode: %d" (let ((obj (make-instance 'xcb:MapRequest))) (xcb:unmarshal obj data) (with-slots (parent window) obj + (exwm--log "id=#x%x parent=#x%x" window parent) (if (assoc window exwm--id-buffer-alist) (with-current-buffer (exwm--id->buffer window) (if (exwm-layout--iconic-state-p) @@ -650,12 +662,13 @@ border-width: %d; sibling: #x%x; stack-mode: %d" (let ((obj (make-instance 'xcb:UnmapNotify))) (xcb:unmarshal obj data) (with-slots (window) obj - (exwm--log "UnmapNotify from #x%x" window) + (exwm--log "id=#x%x" window) (exwm-manage--unmanage-window window t)))) (defun exwm-manage--on-DestroyNotify (data synthetic) "Handle DestroyNotify event." (unless synthetic + (exwm--log) (let ((obj (make-instance 'xcb:DestroyNotify))) (xcb:unmarshal obj data) (exwm--log "DestroyNotify from #x%x" (slot-value obj 'window)) @@ -664,6 +677,7 @@ border-width: %d; sibling: #x%x; stack-mode: %d" (defun exwm-manage--init () "Initialize manage module." ;; Intern _MOTIF_WM_HINTS + (exwm--log) (let ((atom-name "_MOTIF_WM_HINTS")) (setq exwm-manage--_MOTIF_WM_HINTS (slot-value (xcb:+request-unchecked+reply exwm--connection diff --git a/exwm-workspace.el b/exwm-workspace.el index 1034966374..bcfffff1d3 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -375,6 +375,7 @@ NIL if FRAME is not a workspace" (defun exwm-workspace--set-active (frame active) "Make frame FRAME active on its output." + (exwm--log "active=%s; frame=%s" frame active) (set-frame-parameter frame 'exwm-active active) (if active (exwm-workspace--set-fullscreen frame) @@ -387,6 +388,7 @@ NIL if FRAME is not a workspace" (defun exwm-workspace--set-fullscreen (frame) "Make frame FRAME fullscreen according to `exwm-workspace--workareas'." + (exwm--log "frame=%s" frame) (let ((workarea (elt exwm-workspace--workareas (exwm-workspace--position frame))) (id (frame-parameter frame 'exwm-outer-id)) @@ -396,6 +398,7 @@ NIL if FRAME is not a workspace" y (aref workarea 1) width (aref workarea 2) height (aref workarea 3)) + (exwm--log "x=%s; y=%s; w=%s; h=%s" x y width height) (when (and (eq frame exwm-workspace--current) (exwm-workspace--minibuffer-own-frame-p)) (exwm-workspace--resize-minibuffer-frame)) @@ -516,6 +519,7 @@ for internal use only." (<= 0 current-prefix-arg (exwm-workspace--count))) current-prefix-arg) (t 0)))) + (exwm--log) (let* ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index)) (old-frame exwm-workspace--current) (index (exwm-workspace--position frame)) @@ -665,6 +669,7 @@ Passing a workspace frame as the first option is for internal use only." (format "Swap workspace %d with: " (exwm-workspace--position w1)))) (list w1 w2)))) + (exwm--log) (let ((pos1 (exwm-workspace--position workspace1)) (pos2 (exwm-workspace--position workspace2))) (if (or (not pos1) (not pos2) (= pos1 pos2)) @@ -703,6 +708,7 @@ before it." (<= 0 current-prefix-arg (exwm-workspace--count))) (list exwm-workspace--current current-prefix-arg)) (t (list exwm-workspace--current 0)))) + (exwm--log) (let ((pos (exwm-workspace--position workspace)) flag start end index) (if (= nth pos) @@ -928,6 +934,7 @@ INDEX must not exceed the current number of workspaces." (remq #'exwm-input--on-buffer-list-update buffer-list-update-hook))) (rename-buffer (concat " " (buffer-name))))))))))) + (exwm--log) (when buffer-or-name (with-current-buffer buffer-or-name (if (derived-mode-p 'exwm-mode) diff --git a/exwm.el b/exwm.el index 9f9b6f4681..6b48a2bdc3 100644 --- a/exwm.el +++ b/exwm.el @@ -360,6 +360,7 @@ (xcb:unmarshal obj data) (setq id (slot-value obj 'window) atom (slot-value obj 'atom)) + (exwm--log "atom=%s(%s)" (x-get-atom-name atom exwm-workspace--current) atom) (setq buffer (exwm--id->buffer id)) (if (not (buffer-live-p buffer)) ;; Properties of unmanaged X windows. @@ -397,6 +398,7 @@ (setq type (slot-value obj 'type) id (slot-value obj 'window) data (slot-value (slot-value obj 'data) 'data32)) + (exwm--log "atom=%s(%s)" (x-get-atom-name type exwm-workspace--current) type) (cond ;; _NET_NUMBER_OF_DESKTOPS. ((= type xcb:Atom:_NET_NUMBER_OF_DESKTOPS) -- cgit 1.4.1 From 5c1aa4dc310444c1a73c273b581f9e5868cb9a6d Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Thu, 16 Aug 2018 12:00:00 +0000 Subject: Consider windows of the frame being refreshed, not the selected one at the time exwm-layout--refresh runs * exwm-layout.el (exwm-layout--refresh): Consider windows of the frame being refreshed instead of the selected frame. --- exwm-layout.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm-layout.el b/exwm-layout.el index 9427607712..a03b6fb685 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -293,7 +293,7 @@ selected by `other-buffer'." (or exwm-layout-show-all-buffers ;; Exclude X windows on other workspaces (eq frame exwm--frame))) - (setq windows (get-buffer-window-list (current-buffer) 0)) + (setq windows (get-buffer-window-list (current-buffer) 0 frame)) (if (not windows) (when (eq frame exwm--frame) (exwm-layout--hide exwm--id)) -- cgit 1.4.1 From 11fecb5186ceac31aa4a78261da3969ddb6856ff Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Thu, 16 Aug 2018 12:00:00 +0000 Subject: Use more explicit argument for excluding minibuffers * exwm-layout.el (exwm-layout--refresh): Use a more intuitive value for specifying exclusion of minibuffers. --- exwm-layout.el | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index a03b6fb685..18c2c27e22 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -278,7 +278,7 @@ selected by `other-buffer'." (exwm-layout--hide exwm--id))))) ;; Other frames (e.g. terminal/graphical frame of emacsclient) ;; We shall bury all `exwm-mode' buffers in this case - (setq windows (window-list frame 0)) ;exclude minibuffer + (setq windows (window-list frame 'nomini)) (let ((exwm-layout--other-buffer-exclude-exwm-mode-buffers t)) (dolist (window windows) (with-current-buffer (window-buffer window) @@ -293,7 +293,7 @@ selected by `other-buffer'." (or exwm-layout-show-all-buffers ;; Exclude X windows on other workspaces (eq frame exwm--frame))) - (setq windows (get-buffer-window-list (current-buffer) 0 frame)) + (setq windows (get-buffer-window-list (current-buffer) 'nomini frame)) (if (not windows) (when (eq frame exwm--frame) (exwm-layout--hide exwm--id)) @@ -307,7 +307,7 @@ selected by `other-buffer'." ;; need to display with some other buffer there. (setq vacated-windows (append vacated-windows (cdr (get-buffer-window-list - (current-buffer) 0 t)))) + (current-buffer) 'nomini t)))) ;; Note down when an EXWM-buffer is being covered by this ;; buffer; we don't want it to reappear in some vacated window. (let ((prev-buffer (car-safe @@ -323,7 +323,7 @@ selected by `other-buffer'." (switch-to-prev-buffer window))) ;; Make sure windows floating / on other workspaces are excluded (let ((exwm-layout--other-buffer-exclude-exwm-mode-buffers t)) - (dolist (window (window-list frame 0)) + (dolist (window (window-list frame 'nomini)) (with-current-buffer (window-buffer window) (when (and (derived-mode-p 'exwm-mode) (or exwm--floating-frame (not (eq frame exwm--frame)))) -- cgit 1.4.1 From 633065ad55f84431db6aa380dc2467c38b5fbdcb Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Thu, 16 Aug 2018 12:00:00 +0000 Subject: Don't assume order of `get-buffer-window-list' results It only guarantees that the first result *if* the buffer appears on the selected window. --- exwm-layout.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index 18c2c27e22..8f3c47f693 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -306,8 +306,10 @@ selected by `other-buffer'." ;; windows displaying an EXWM-buffer now displayed elsewhere; we ;; need to display with some other buffer there. (setq vacated-windows - (append vacated-windows (cdr (get-buffer-window-list - (current-buffer) 'nomini t)))) + (append vacated-windows (remove + window + (get-buffer-window-list + (current-buffer) 'nomini t)))) ;; Note down when an EXWM-buffer is being covered by this ;; buffer; we don't want it to reappear in some vacated window. (let ((prev-buffer (car-safe -- cgit 1.4.1 From f820217d00623489a655c79506816397319eb762 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Thu, 16 Aug 2018 12:00:00 +0000 Subject: Split exwm-layout--refresh into three functions * exwm-layout.el (exwm-layout--refresh): Split in three functions for clarity. (exwm-layout--refresh-workspace, exwm-layout--refresh-other) (exwm-layout--refresh-floating): New functions. --- exwm-layout.el | 103 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 57 insertions(+), 46 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index 8f3c47f693..b8c5bb333a 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -261,39 +261,50 @@ selected by `other-buffer'." ;; `window-configuration-change-hook' makes the frame selected. (unless frame (setq frame (selected-frame))) + (if (not (exwm-workspace--workspace-p frame)) + (if (frame-parameter frame 'exwm-outer-id) + (exwm-layout--refresh-floating frame) + (exwm-layout--refresh-other frame)) + (exwm-layout--refresh-workspace frame))) + +(defun exwm-layout--refresh-floating (frame) + "Refresh floating frame FRAME." + (exwm--log "Refresh floating %s" frame) + (let ((window (frame-first-window frame))) + (with-current-buffer (window-buffer window) + (when (and (derived-mode-p 'exwm-mode) + ;; It may be a buffer waiting to be killed. + (exwm--id->buffer exwm--id)) + (exwm--log "Refresh floating window #x%x" exwm--id) + (if (exwm-workspace--active-p exwm--frame) + (exwm-layout--show exwm--id window) + (exwm-layout--hide exwm--id)))))) + +(defun exwm-layout--refresh-other (frame) + "Refresh client or nox frame FRAME." + ;; Other frames (e.g. terminal/graphical frame of emacsclient) + ;; We shall bury all `exwm-mode' buffers in this case + (exwm--log "Refresh other %s" frame) + (let ((windows (window-list frame 'nomini)) ;exclude minibuffer + (exwm-layout--other-buffer-exclude-exwm-mode-buffers t)) + (dolist (window windows) + (with-current-buffer (window-buffer window) + (when (derived-mode-p 'exwm-mode) + (switch-to-prev-buffer window)))))) + +(defun exwm-layout--refresh-workspace (frame) + "Refresh workspace frame FRAME." + (exwm--log "Refresh workspace %s" frame) + ;; Workspaces other than the active one can also be refreshed (RandR) (let (covered-buffers ;EXWM-buffers covered by a new X window. - vacated-windows ;Windows previously displaying EXWM-buffers. - windows) - (if (not (exwm-workspace--workspace-p frame)) - (if (frame-parameter frame 'exwm-outer-id) - ;; Refresh a floating frame - (let ((window (frame-first-window frame))) - (with-current-buffer (window-buffer window) - (when (and (derived-mode-p 'exwm-mode) - ;; It may be a buffer waiting to be killed. - (exwm--id->buffer exwm--id)) - (exwm--log "Refresh floating window #x%x" exwm--id) - (if (exwm-workspace--active-p exwm--frame) - (exwm-layout--show exwm--id window) - (exwm-layout--hide exwm--id))))) - ;; Other frames (e.g. terminal/graphical frame of emacsclient) - ;; We shall bury all `exwm-mode' buffers in this case - (setq windows (window-list frame 'nomini)) - (let ((exwm-layout--other-buffer-exclude-exwm-mode-buffers t)) - (dolist (window windows) - (with-current-buffer (window-buffer window) - (when (derived-mode-p 'exwm-mode) - (switch-to-prev-buffer window)))))) - ;; Refresh the whole workspace - ;; Workspaces other than the active one can also be refreshed (RandR) - (exwm--log "Refresh workspace %s" frame) - (dolist (pair exwm--id-buffer-alist) - (with-current-buffer (cdr pair) - (when (and (not exwm--floating-frame) ;exclude floating X windows - (or exwm-layout-show-all-buffers - ;; Exclude X windows on other workspaces - (eq frame exwm--frame))) - (setq windows (get-buffer-window-list (current-buffer) 'nomini frame)) + vacated-windows) ;Windows previously displaying EXWM-buffers. + (dolist (pair exwm--id-buffer-alist) + (with-current-buffer (cdr pair) + (when (and (not exwm--floating-frame) ;exclude floating X windows + (or exwm-layout-show-all-buffers + ;; Exclude X windows on other workspaces + (eq frame exwm--frame))) + (let ((windows (get-buffer-window-list (current-buffer) 'nomini frame))) (if (not windows) (when (eq frame exwm--frame) (exwm-layout--hide exwm--id)) @@ -318,20 +329,20 @@ selected by `other-buffer'." prev-buffer (with-current-buffer prev-buffer (derived-mode-p 'exwm-mode)) - (push prev-buffer covered-buffers)))))))) - ;; Set some sensible buffer to vacated windows. - (let ((exwm-layout--other-buffer-exclude-buffers covered-buffers)) - (dolist (window vacated-windows) - (switch-to-prev-buffer window))) - ;; Make sure windows floating / on other workspaces are excluded - (let ((exwm-layout--other-buffer-exclude-exwm-mode-buffers t)) - (dolist (window (window-list frame 'nomini)) - (with-current-buffer (window-buffer window) - (when (and (derived-mode-p 'exwm-mode) - (or exwm--floating-frame (not (eq frame exwm--frame)))) - (switch-to-prev-buffer window))))) - (exwm-layout--set-client-list-stacking) - (xcb:flush exwm--connection)))) + (push prev-buffer covered-buffers))))))))) + ;; Set some sensible buffer to vacated windows. + (let ((exwm-layout--other-buffer-exclude-buffers covered-buffers)) + (dolist (window vacated-windows) + (switch-to-prev-buffer window))) + ;; Make sure windows floating / on other workspaces are excluded + (let ((exwm-layout--other-buffer-exclude-exwm-mode-buffers t)) + (dolist (window (window-list frame 'nomini)) + (with-current-buffer (window-buffer window) + (when (and (derived-mode-p 'exwm-mode) + (or exwm--floating-frame (not (eq frame exwm--frame)))) + (switch-to-prev-buffer window))))) + (exwm-layout--set-client-list-stacking) + (xcb:flush exwm--connection))) (defun exwm-layout--on-minibuffer-setup () "Refresh layout when minibuffer grows." -- cgit 1.4.1 From b6a3b7b3ad67fe69d82d335e5520ba7368bab768 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 19 Aug 2018 00:00:00 +0000 Subject: ; Unimportant tweaks --- exwm-core.el | 7 ++----- exwm-debug.el | 23 ++++++++++------------- exwm-input.el | 4 ++-- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 3159519f12..8d5e6dd691 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -33,9 +33,6 @@ (require 'xcb-ewmh) (require 'exwm-debug) -(eval-and-compile - (defvar exwm-debug-on nil "Non-nil to turn on debug for EXWM.")) - (defvar exwm--connection nil "X connection.") (defvar exwm--wmsn-window nil @@ -294,8 +291,8 @@ least SECS seconds later." (exwm--debug (let ((map exwm-mode-map)) - (define-key map "\C-c\C-l" #'exwm-debug--clear) - (define-key map "\C-c\C-m" #'exwm-debug--mark))) + (define-key map "\C-c\C-l" #'exwm-debug-clear) + (define-key map "\C-c\C-m" #'exwm-debug-mark))) (define-derived-mode exwm-mode nil "EXWM" "Major mode for managing X windows. diff --git a/exwm-debug.el b/exwm-debug.el index cd2ec393d7..4d1ca7b403 100644 --- a/exwm-debug.el +++ b/exwm-debug.el @@ -1,9 +1,8 @@ ;;; exwm-debug.el --- Debugging helpers for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015-2016 Free Software Foundation, Inc. +;; Copyright (C) 2018 Free Software Foundation, Inc. -;; Author: Chris Feng -;; Adrián Medraño Calvo +;; Author: Adrián Medraño Calvo ;; This file is part of GNU Emacs. @@ -29,18 +28,16 @@ (eval-and-compile (defvar exwm-debug-on nil "Non-nil to turn on debug for EXWM.")) -(defvar exwm-debug-buffer - (when exwm-debug-on - (let ((buffer (get-buffer-create "*EXWM-DEBUG*"))) - (buffer-disable-undo buffer) - buffer)) - "Buffer to write debug messages to.") +(defvar exwm-debug-buffer "*EXWM-DEBUG*" "Buffer to write debug messages to.") + +(defvar exwm-debug-backtrace-start-frame 5 + "From which frame to start collecting backtraces.") (defun exwm-debug--call-stack () "Return the current call stack frames." (let (frames frame ;; No need to acount for our setq, while, let, ... - (index 5)) + (index exwm-debug-backtrace-start-frame)) (while (setq frame (backtrace-frame index)) (push frame frames) (cl-incf index)) @@ -64,7 +61,7 @@ (defmacro exwm-debug--with-debug-buffer (&rest forms) "Evaluate FORMS making sure `exwm-debug-buffer' is correctly updated." - `(with-current-buffer exwm-debug-buffer + `(with-current-buffer (get-buffer-create exwm-debug-buffer) (let (windows-eob) ;; Note windows whose point is at EOB. (dolist (w (get-buffer-window-list exwm-debug-buffer t t)) @@ -97,13 +94,13 @@ the passed OBJECTS. See `format' for details." (debugger (lambda (&rest _) (exwm-debug--backtrace)))) ,@forms)) -(defun exwm-debug--clear () +(defun exwm-debug-clear () "Clear the debug buffer." (interactive) (exwm-debug--with-debug-buffer (erase-buffer))) -(defun exwm-debug--mark () +(defun exwm-debug-mark () "Insert a mark in the debug buffer." (interactive) (exwm-debug--with-debug-buffer diff --git a/exwm-input.el b/exwm-input.el index 7885d189aa..57fed2d4b8 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -377,7 +377,7 @@ ARGS are additional arguments to CALLBACK." button-event window buffer frame) (xcb:unmarshal obj data) (exwm--log "major-mode=%s buffer=%s" - major-mode (buffer-name (current-buffer))) + major-mode (buffer-name (current-buffer))) (with-slots (detail time event state) obj (setq button-event (xcb:keysyms:keysym->event exwm--connection detail state) @@ -428,7 +428,7 @@ ARGS are additional arguments to CALLBACK." (let ((obj (make-instance 'xcb:KeyPress))) (xcb:unmarshal obj data) (exwm--log "major-mode=%s buffer=%s" - major-mode (buffer-name (current-buffer))) + major-mode (buffer-name (current-buffer))) (if (derived-mode-p 'exwm-mode) (funcall exwm--on-KeyPress obj data) (exwm-input--on-KeyPress-char-mode obj)))) -- cgit 1.4.1 From 29f2289a750c9cf8f0d5cd968d306ee953a7bc0d Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Mon, 13 Aug 2018 12:00:00 +0000 Subject: Consistently name helper windows * exwm.el (exwm--init-icccm-ewmh): Avoid naming the root window. (exwm--wmsn-acquire): Use the symbol name in the window name. * exwm-systemtray.el (exwm-systemtray--embedder-window): Rename `exwm-systemtray--embedder' consistency. (exwm-systemtray--init): Use symbol names in the window name. --- exwm-systemtray.el | 37 +++++++++++++++++++++---------------- exwm.el | 12 ++++++------ 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/exwm-systemtray.el b/exwm-systemtray.el index ba1e388934..d3244ab8d0 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -70,7 +70,7 @@ You shall use the default value if using auto-hide minibuffer." (defvar exwm-systemtray--connection nil "The X connection.") -(defvar exwm-systemtray--embedder nil "The embedder window.") +(defvar exwm-systemtray--embedder-window nil "The embedder window.") (defvar exwm-systemtray--list nil "The icon list.") @@ -112,7 +112,7 @@ You shall use the default value if using auto-hide minibuffer." (xcb:+request exwm-systemtray--connection (make-instance 'xcb:ReparentWindow :window icon - :parent exwm-systemtray--embedder + :parent exwm-systemtray--embedder-window :x 0 ;; Vertically centered. :y (/ (- exwm-systemtray-height height*) 2))) @@ -162,7 +162,8 @@ You shall use the default value if using auto-hide minibuffer." (make-instance 'xcb:xembed:EMBEDDED-NOTIFY :window icon :time xcb:Time:CurrentTime - :embedder exwm-systemtray--embedder + :embedder + exwm-systemtray--embedder-window :version 0) exwm-systemtray--connection))) (push `(,icon . ,(make-instance 'exwm-systemtray--icon @@ -190,7 +191,8 @@ You shall use the default value if using auto-hide minibuffer." "Refresh the system tray." ;; Make sure to redraw the embedder. (xcb:+request exwm-systemtray--connection - (make-instance 'xcb:UnmapWindow :window exwm-systemtray--embedder)) + (make-instance 'xcb:UnmapWindow + :window exwm-systemtray--embedder-window)) (let ((x exwm-systemtray-icon-gap) map) (dolist (pair exwm-systemtray--list) @@ -207,14 +209,15 @@ You shall use the default value if using auto-hide minibuffer." exwm-workspace-current-index))) (xcb:+request exwm-systemtray--connection (make-instance 'xcb:ConfigureWindow - :window exwm-systemtray--embedder + :window exwm-systemtray--embedder-window :value-mask (logior xcb:ConfigWindow:X xcb:ConfigWindow:Width) :x (- (aref workarea 2) x) :width x))) (when map (xcb:+request exwm-systemtray--connection - (make-instance 'xcb:MapWindow :window exwm-systemtray--embedder)))) + (make-instance 'xcb:MapWindow + :window exwm-systemtray--embedder-window)))) (xcb:flush exwm-systemtray--connection)) (defun exwm-systemtray--on-DestroyNotify (data _synthetic) @@ -230,7 +233,7 @@ You shall use the default value if using auto-hide minibuffer." (let ((obj (make-instance 'xcb:ReparentNotify))) (xcb:unmarshal obj data) (with-slots (window parent) obj - (when (and (/= parent exwm-systemtray--embedder) + (when (and (/= parent exwm-systemtray--embedder-window) (assoc window exwm-systemtray--list)) (exwm-systemtray--unembed window))))) @@ -325,7 +328,7 @@ You shall use the default value if using auto-hide minibuffer." (unless (exwm-workspace--minibuffer-own-frame-p) (xcb:+request exwm-systemtray--connection (make-instance 'xcb:ReparentWindow - :window exwm-systemtray--embedder + :window exwm-systemtray--embedder-window :parent (string-to-number (frame-parameter exwm-workspace--current 'window-id)) @@ -339,7 +342,7 @@ You shall use the default value if using auto-hide minibuffer." (unless (exwm-workspace--minibuffer-own-frame-p) (xcb:+request exwm-systemtray--connection (make-instance 'xcb:ConfigureWindow - :window exwm-systemtray--embedder + :window exwm-systemtray--embedder-window :value-mask xcb:ConfigWindow:Y :y (- (frame-pixel-height exwm-workspace--current) exwm-systemtray-height)))) @@ -353,7 +356,7 @@ You shall use the default value if using auto-hide minibuffer." (cl-assert (not exwm-systemtray--connection)) (cl-assert (not exwm-systemtray--list)) (cl-assert (not exwm-systemtray--selection-owner-window)) - (cl-assert (not exwm-systemtray--embedder)) + (cl-assert (not exwm-systemtray--embedder-window)) (unless exwm-systemtray-height (setq exwm-systemtray-height (max exwm-systemtray--icon-min-size (line-pixel-height)))) @@ -414,7 +417,8 @@ You shall use the default value if using auto-hide minibuffer." ;; Set _NET_WM_NAME. (xcb:+request exwm-systemtray--connection (make-instance 'xcb:ewmh:set-_NET_WM_NAME - :window id :data "EXWM system tray selection owner")) + :window id + :data "EXWM: exwm-systemtray--selection-owner-window")) ;; Set the _NET_SYSTEM_TRAY_ORIENTATION property. (xcb:+request exwm-systemtray--connection (make-instance 'xcb:xembed:set-_NET_SYSTEM_TRAY_ORIENTATION @@ -423,7 +427,7 @@ You shall use the default value if using auto-hide minibuffer." ;; Create the embedder. (let ((id (xcb:generate-id exwm-systemtray--connection)) frame parent depth y) - (setq exwm-systemtray--embedder id) + (setq exwm-systemtray--embedder-window id) (if (exwm-workspace--minibuffer-own-frame-p) (setq frame exwm-workspace--minibuffer y (if (>= (line-pixel-height) exwm-systemtray-height) @@ -460,7 +464,8 @@ You shall use the default value if using auto-hide minibuffer." ;; Set _NET_WM_NAME. (xcb:+request exwm-systemtray--connection (make-instance 'xcb:ewmh:set-_NET_WM_NAME - :window id :data "EXWM system tray embedder"))) + :window id + :data "EXWM: exwm-systemtray--embedder-window"))) (xcb:flush exwm-systemtray--connection) ;; Attach event listeners. (xcb:+event exwm-systemtray--connection 'xcb:DestroyNotify @@ -494,10 +499,10 @@ You shall use the default value if using auto-hide minibuffer." ;; parent of the embedder). (xcb:+request exwm-systemtray--connection (make-instance 'xcb:UnmapWindow - :window exwm-systemtray--embedder)) + :window exwm-systemtray--embedder-window)) (xcb:+request exwm-systemtray--connection (make-instance 'xcb:ReparentWindow - :window exwm-systemtray--embedder + :window exwm-systemtray--embedder-window :parent exwm--root :x 0 :y 0)) @@ -505,7 +510,7 @@ You shall use the default value if using auto-hide minibuffer." (setq exwm-systemtray--connection nil exwm-systemtray--list nil exwm-systemtray--selection-owner-window nil - exwm-systemtray--embedder nil) + exwm-systemtray--embedder-window nil) (remove-hook 'exwm-workspace-switch-hook #'exwm-systemtray--on-workspace-switch) (remove-hook 'exwm-workspace--update-workareas-hook diff --git a/exwm.el b/exwm.el index 9f9b6f4681..fd824f099f 100644 --- a/exwm.el +++ b/exwm.el @@ -665,15 +665,15 @@ :visual 0 :value-mask xcb:CW:OverrideRedirect :override-redirect 1)) + ;; Set _NET_WM_NAME + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_NAME + :window new-id :data "EXWM: exwm--guide-window")) (dolist (i (list exwm--root new-id)) ;; Set _NET_SUPPORTING_WM_CHECK (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_SUPPORTING_WM_CHECK - :window i :data new-id)) - ;; Set _NET_WM_NAME - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_WM_NAME - :window i :data "EXWM")))) + :window i :data new-id)))) ;; Set _NET_DESKTOP_VIEWPORT (we don't support large desktop). (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_DESKTOP_VIEWPORT @@ -712,7 +712,7 @@ manager. If t, replace it, if nil, abort and ask the user if `ask'." :override-redirect 0)) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_NAME - :window new-owner :data "EXWM selection owner")) + :window new-owner :data "EXWM: exwm--wmsn-window")) (xcb:+request-checked+request-check exwm--connection (make-instance 'xcb:SetSelectionOwner :selection xcb:Atom:WM_S0 -- cgit 1.4.1 From d4a772f536eab469b17315e83d843ce3cba3092c Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Thu, 16 Aug 2018 12:00:00 +0000 Subject: ; Comment layout algorithm. --- exwm-layout.el | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index b8c5bb333a..98a27d0ca0 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -304,25 +304,31 @@ selected by `other-buffer'." (or exwm-layout-show-all-buffers ;; Exclude X windows on other workspaces (eq frame exwm--frame))) - (let ((windows (get-buffer-window-list (current-buffer) 'nomini frame))) + (let (;; List of windows in current frame displaying the `exwm-mode' + ;; buffers. + (windows (get-buffer-window-list (current-buffer) 'nomini + frame))) (if (not windows) (when (eq frame exwm--frame) + ;; Hide it if it was being shown in this workspace. (exwm-layout--hide exwm--id)) (let ((window (car windows))) (if (eq frame exwm--frame) (when (exwm-workspace--active-p frame) + ;; Show it if `frame' is active. (exwm-layout--show exwm--id window)) + ;; It was last shown in other workspace; move it here. (exwm-workspace-move-window frame exwm--id)) - ;; Make sure this buffer is not displayed elsewhere. Note down - ;; windows displaying an EXWM-buffer now displayed elsewhere; we - ;; need to display with some other buffer there. + ;; Vacate any other windows (in any workspace) showing this + ;; `exwm-mode' buffer. (setq vacated-windows (append vacated-windows (remove window (get-buffer-window-list (current-buffer) 'nomini t)))) - ;; Note down when an EXWM-buffer is being covered by this - ;; buffer; we don't want it to reappear in some vacated window. + ;; Note any `exwm-mode' buffer is being covered by another + ;; `exwm-mode' buffer. We want to avoid that `exwm-mode' + ;; buffer to be reappear in any of the vacated windows. (let ((prev-buffer (car-safe (car-safe (window-prev-buffers window))))) (and -- cgit 1.4.1 From 5f6b866cfed7214caabd5f5dd0f952cce93a307c Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Thu, 30 Aug 2018 12:00:00 +0000 Subject: Refresh layout after activating/deactivating workspaces * exwm-workspace.el (exwm-workspace--set-active): Refresh layout after activating or deactivating workspaces. * exwm-layout.el (exwm-layout--refresh-workspace): Hide X windows on inactive workspaces. --- exwm-layout.el | 7 ++++--- exwm-workspace.el | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index 09a34985a7..56faadd992 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -320,9 +320,10 @@ selected by `other-buffer'." (exwm-layout--hide exwm--id)) (let ((window (car windows))) (if (eq frame exwm--frame) - (when (exwm-workspace--active-p frame) - ;; Show it if `frame' is active. - (exwm-layout--show exwm--id window)) + ;; Show it if `frame' is active, hide otherwise. + (if (exwm-workspace--active-p frame) + (exwm-layout--show exwm--id window) + (exwm-layout--hide exwm--id)) ;; It was last shown in other workspace; move it here. (exwm-workspace-move-window frame exwm--id)) ;; Vacate any other windows (in any workspace) showing this diff --git a/exwm-workspace.el b/exwm-workspace.el index bcfffff1d3..4d82ae5963 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -379,8 +379,9 @@ NIL if FRAME is not a workspace" (set-frame-parameter frame 'exwm-active active) (if active (exwm-workspace--set-fullscreen frame) - (exwm--set-geometry (frame-parameter frame 'exwm-container) nil nil 1 1) - (xcb:flush exwm--connection))) + (exwm--set-geometry (frame-parameter frame 'exwm-container) nil nil 1 1)) + (exwm-layout--refresh frame) + (xcb:flush exwm--connection)) (defun exwm-workspace--active-p (frame) "Return non-nil if FRAME is active" -- cgit 1.4.1 From 2399a0bb227e39ac3896a5cad15f3e2194438af5 Mon Sep 17 00:00:00 2001 From: James Ferguson Date: Tue, 28 Aug 2018 08:46:14 -0400 Subject: Fix cursor warping conditional for cursor left of frame --- exwm-workspace.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 4d82ae5963..a4454d10f0 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -610,7 +610,9 @@ for internal use only." (make-instance 'xcb:QueryPointer :window (frame-parameter frame 'exwm-outer-id))) - (when (or (> win-x (frame-pixel-width frame)) + (when (or (< win-x 0) + (< win-y 0) + (> win-x (frame-pixel-width frame)) (> win-y (frame-pixel-height))) (xcb:+request exwm--connection (make-instance 'xcb:WarpPointer -- cgit 1.4.1 From dd57c5eebb213c29c3b250634e316abf4917a19b Mon Sep 17 00:00:00 2001 From: James Ferguson Date: Tue, 28 Aug 2018 08:47:01 -0400 Subject: Explicitly specify frame for cursor warping conditional --- exwm-workspace.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index a4454d10f0..31108892fe 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -613,7 +613,7 @@ for internal use only." (when (or (< win-x 0) (< win-y 0) (> win-x (frame-pixel-width frame)) - (> win-y (frame-pixel-height))) + (> win-y (frame-pixel-height frame))) (xcb:+request exwm--connection (make-instance 'xcb:WarpPointer :src-window xcb:Window:None -- cgit 1.4.1 From 74ef19ff44104b0f056e30af90e0970eb705eb6a Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Tue, 4 Sep 2018 12:00:00 +0000 Subject: Use XELB's debugging facilities * exwm-debug.el: Move to XELB as `xcb-debug'. * exwm-core.el (exwm--log): Use it. (exwm--log): Support switching debugging output at runtime. --- exwm-core.el | 27 +++++++------- exwm-debug.el | 113 ---------------------------------------------------------- 2 files changed, 14 insertions(+), 126 deletions(-) delete mode 100644 exwm-debug.el diff --git a/exwm-core.el b/exwm-core.el index 8d5e6dd691..de2c9de9f9 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -31,7 +31,7 @@ (require 'xcb) (require 'xcb-icccm) (require 'xcb-ewmh) -(require 'exwm-debug) +(require 'xcb-debug) (defvar exwm--connection nil "X connection.") @@ -68,21 +68,22 @@ (declare-function exwm-workspace-move-window "exwm-workspace.el" (frame-or-index &optional id)) +(defvar exwm-debug-on nil "Non-nil to turn on debug for EXWM.") + +(defmacro exwm--debug (&rest forms) + (when exwm-debug-on `(progn ,@forms))) + (defmacro exwm--log (&optional format-string &rest objects) "Emit a message prepending the name of the function being executed. FORMAT-STRING is a string specifying the message to output, as in `format'. The OBJECTS arguments specify the substitutions." - (when exwm-debug-on - (unless format-string (setq format-string "")) - `(progn - (exwm-debug--message (concat "%s:\t" ,format-string "\n") - (exwm-debug--compile-time-function-name) - ,@objects) - nil))) - -(defmacro exwm--debug (&rest forms) - (when exwm-debug-on `(progn ,@forms))) + (unless format-string (setq format-string "")) + `(when exwm-debug-on + (xcb-debug-message ,(concat "%s:\t" format-string "\n") + (xcb-debug-compile-time-function-name) + ,@objects) + nil)) (defsubst exwm--id->buffer (id) "X window ID => Emacs buffer." @@ -291,8 +292,8 @@ least SECS seconds later." (exwm--debug (let ((map exwm-mode-map)) - (define-key map "\C-c\C-l" #'exwm-debug-clear) - (define-key map "\C-c\C-m" #'exwm-debug-mark))) + (define-key map "\C-c\C-l" #'xcb-debug-clear) + (define-key map "\C-c\C-m" #'xcb-debug-mark))) (define-derived-mode exwm-mode nil "EXWM" "Major mode for managing X windows. diff --git a/exwm-debug.el b/exwm-debug.el deleted file mode 100644 index 4d1ca7b403..0000000000 --- a/exwm-debug.el +++ /dev/null @@ -1,113 +0,0 @@ -;;; exwm-debug.el --- Debugging helpers for EXWM -*- lexical-binding: t -*- - -;; Copyright (C) 2018 Free Software Foundation, Inc. - -;; Author: Adrián Medraño Calvo - -;; This file is part of GNU Emacs. - -;; GNU Emacs is free software: you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; GNU Emacs is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see . - -;;; Commentary: - -;; This module collects functions that help in debugging EXWM. - -;;; Code: - -(eval-and-compile - (defvar exwm-debug-on nil "Non-nil to turn on debug for EXWM.")) - -(defvar exwm-debug-buffer "*EXWM-DEBUG*" "Buffer to write debug messages to.") - -(defvar exwm-debug-backtrace-start-frame 5 - "From which frame to start collecting backtraces.") - -(defun exwm-debug--call-stack () - "Return the current call stack frames." - (let (frames frame - ;; No need to acount for our setq, while, let, ... - (index exwm-debug-backtrace-start-frame)) - (while (setq frame (backtrace-frame index)) - (push frame frames) - (cl-incf index)) - (cl-remove-if-not 'car frames))) - -(defmacro exwm-debug--compile-time-function-name () - "Get the name of outermost definition at expansion time." - (let* ((frame (cl-find-if - (lambda (frame) - (ignore-errors - (let ((clause (car (cl-third frame)))) - (or (equal clause 'defalias) - (equal clause 'cl-defmethod))))) - (reverse (exwm-debug--call-stack)))) - (defn (cl-third frame)) - (deftype (car defn))) - (cl-case deftype - ((defalias) (symbol-name (cl-cadadr defn))) - ((cl-defmethod) (symbol-name (cadr defn))) - (t "")))) - -(defmacro exwm-debug--with-debug-buffer (&rest forms) - "Evaluate FORMS making sure `exwm-debug-buffer' is correctly updated." - `(with-current-buffer (get-buffer-create exwm-debug-buffer) - (let (windows-eob) - ;; Note windows whose point is at EOB. - (dolist (w (get-buffer-window-list exwm-debug-buffer t t)) - (when (= (window-point w) (point-max)) - (push w windows-eob))) - (save-excursion - (goto-char (point-max)) - ,@forms) - ;; Restore point. - (dolist (w windows-eob) - (set-window-point w (point-max)))))) - -(defun exwm-debug--message (format-string &rest objects) - "Print a message to `exwm-debug-buffer'. - -The FORMAT-STRING argument follows the speficies how to print each of -the passed OBJECTS. See `format' for details." - (exwm-debug--with-debug-buffer - (insert (apply #'format format-string objects)))) - -(defmacro exwm-debug--backtrace () - "Print a backtrace to the `exwm-debug-buffer'." - '(exwm-debug--with-debug-buffer - (let ((standard-output exwm-debug-buffer)) - (backtrace)))) - -(defmacro exwm-debug--backtrace-on-error (&rest forms) - "Evaluate FORMS. Printing a backtrace if an error is signaled." - `(let ((debug-on-error t) - (debugger (lambda (&rest _) (exwm-debug--backtrace)))) - ,@forms)) - -(defun exwm-debug-clear () - "Clear the debug buffer." - (interactive) - (exwm-debug--with-debug-buffer - (erase-buffer))) - -(defun exwm-debug-mark () - "Insert a mark in the debug buffer." - (interactive) - (exwm-debug--with-debug-buffer - (insert " \n"))) - - - -(provide 'exwm-debug) - -;;; exwm-debug.el ends here -- cgit 1.4.1 From 3419337f52b7aaa820670e8b393f4f1db9079372 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Tue, 4 Sep 2018 12:00:00 +0000 Subject: Substitute overlapping keybindings * exwm-core.el (exwm-mode-map): Change keybindings to avoid overlap. --- exwm-core.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index de2c9de9f9..2d74abadae 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -292,8 +292,8 @@ least SECS seconds later." (exwm--debug (let ((map exwm-mode-map)) - (define-key map "\C-c\C-l" #'xcb-debug-clear) - (define-key map "\C-c\C-m" #'xcb-debug-mark))) + (define-key map "\C-c\C-d\C-l" #'xcb-debug-clear) + (define-key map "\C-c\C-d\C-m" #'xcb-debug-mark))) (define-derived-mode exwm-mode nil "EXWM" "Major mode for managing X windows. -- cgit 1.4.1 From ac1372eb1154a536e32096befa4c975c814c813c Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Tue, 4 Sep 2018 12:00:00 +0000 Subject: Command for toggling debugging output * exwm-core.el (exwm-debug-toggle): New function for toggling debugging output. (exwm-mode-map): Use it. --- exwm-core.el | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/exwm-core.el b/exwm-core.el index 2d74abadae..49914d3e2c 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -85,6 +85,16 @@ FORMAT-STRING is a string specifying the message to output, as in ,@objects) nil)) +(defun exwm-debug-toggle (&optional arg) + "Toggle EXWM debugging output. +When ARG is positive, turn debugging on; when negative off. When +ARG is nil, toggle debugging output." + (interactive + (list (or current-prefix-arg 'toggle))) + (setq exwm-debug-on (if (eq arg 'toggle) + (not exwm-debug-on) + (> 0 arg)))) + (defsubst exwm--id->buffer (id) "X window ID => Emacs buffer." (cdr (assoc id exwm--id-buffer-alist))) @@ -293,7 +303,8 @@ least SECS seconds later." (exwm--debug (let ((map exwm-mode-map)) (define-key map "\C-c\C-d\C-l" #'xcb-debug-clear) - (define-key map "\C-c\C-d\C-m" #'xcb-debug-mark))) + (define-key map "\C-c\C-d\C-m" #'xcb-debug-mark) + (define-key map "\C-c\C-d\C-t" #'exwm-debug-toggle))) (define-derived-mode exwm-mode nil "EXWM" "Major mode for managing X windows. -- cgit 1.4.1 From 6978c1f45c4d72edab2440faf93fb917afe356ec Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Tue, 4 Sep 2018 12:00:00 +0000 Subject: Restack X-Windows after being mapped to ensure EnterNotify events fire * exwm-manage.el (exwm-manage--on-MapNotify, exwm-manage--init): Restack X windows after being mapped in order to ensure they receive an EnterNotify event (does not happen under XQuartz). --- exwm-manage.el | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/exwm-manage.el b/exwm-manage.el index 81a486c097..7b28d68768 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -665,6 +665,22 @@ border-width: %d; sibling: #x%x; stack-mode: %d" (exwm--log "id=#x%x" window) (exwm-manage--unmanage-window window t)))) +(defun exwm-manage--on-MapNotify (data _synthetic) + "Handle MapNotify event." + (let ((obj (make-instance 'xcb:MapNotify))) + (xcb:unmarshal obj data) + (with-slots (window) obj + (when (assoc window exwm--id-buffer-alist) + (exwm--log "id=#x%x" window) + ;; With this we ensure that a "window hierarchy change" happens after + ;; mapping the window, as some servers (XQuartz) do not generate it. + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window window + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Above)) + (xcb:flush exwm--connection))))) + (defun exwm-manage--on-DestroyNotify (data synthetic) "Handle DestroyNotify event." (unless synthetic @@ -692,6 +708,7 @@ border-width: %d; sibling: #x%x; stack-mode: %d" #'exwm-manage--on-ConfigureRequest) (xcb:+event exwm--connection 'xcb:MapRequest #'exwm-manage--on-MapRequest) (xcb:+event exwm--connection 'xcb:UnmapNotify #'exwm-manage--on-UnmapNotify) + (xcb:+event exwm--connection 'xcb:MapNotify #'exwm-manage--on-MapNotify) (xcb:+event exwm--connection 'xcb:DestroyNotify #'exwm-manage--on-DestroyNotify)) -- cgit 1.4.1 From 72bba1176cbb13829b271bb028d99abccc506f4f Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 9 Sep 2018 00:00:00 +0000 Subject: ; Minor fixes for Calvo's patch set. --- exwm-core.el | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 49914d3e2c..365e24b426 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -80,8 +80,8 @@ FORMAT-STRING is a string specifying the message to output, as in `format'. The OBJECTS arguments specify the substitutions." (unless format-string (setq format-string "")) `(when exwm-debug-on - (xcb-debug-message ,(concat "%s:\t" format-string "\n") - (xcb-debug-compile-time-function-name) + (xcb-debug:message ,(concat "%s:\t" format-string "\n") + (xcb-debug:compile-time-function-name) ,@objects) nil)) @@ -199,6 +199,9 @@ least SECS seconds later." (defvar exwm-mode-map (let ((map (make-sparse-keymap))) + (define-key map "\C-c\C-d\C-l" #'xcb-debug:clear) + (define-key map "\C-c\C-d\C-m" #'xcb-debug:mark) + (define-key map "\C-c\C-d\C-t" #'exwm-debug-toggle) (define-key map "\C-c\C-f" #'exwm-layout-set-fullscreen) (define-key map "\C-c\C-h" #'exwm-floating-hide) (define-key map "\C-c\C-k" #'exwm-input-release-keyboard) @@ -300,12 +303,6 @@ least SECS seconds later." (/= ,i exwm-workspace-current-index)]) (number-sequence 0 (1- (exwm-workspace--count)))))))) -(exwm--debug - (let ((map exwm-mode-map)) - (define-key map "\C-c\C-d\C-l" #'xcb-debug-clear) - (define-key map "\C-c\C-d\C-m" #'xcb-debug-mark) - (define-key map "\C-c\C-d\C-t" #'exwm-debug-toggle))) - (define-derived-mode exwm-mode nil "EXWM" "Major mode for managing X windows. -- cgit 1.4.1 From 472f7cb82b67b98843f10c12e6bda9b8ae7262bc Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 16 Sep 2018 00:00:00 +0000 Subject: Simplify debugging and fix dynamic-scoping `eval' * exwm-core.el (exwm-debug): New global minor mode to replace `exwm-debug-on' and `exwm-debug-toggle'. * exwm-manage.el (exwm-manage--get-configurations): Use lexical-scoping `eval'. --- exwm-core.el | 20 ++++++-------------- exwm-manage.el | 2 +- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 365e24b426..66eb98cf90 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -68,10 +68,12 @@ (declare-function exwm-workspace-move-window "exwm-workspace.el" (frame-or-index &optional id)) -(defvar exwm-debug-on nil "Non-nil to turn on debug for EXWM.") +(define-minor-mode exwm-debug + "Debug-logging enabled if non-nil" + :global t) (defmacro exwm--debug (&rest forms) - (when exwm-debug-on `(progn ,@forms))) + (when exwm-debug `(progn ,@forms))) (defmacro exwm--log (&optional format-string &rest objects) "Emit a message prepending the name of the function being executed. @@ -79,22 +81,12 @@ FORMAT-STRING is a string specifying the message to output, as in `format'. The OBJECTS arguments specify the substitutions." (unless format-string (setq format-string "")) - `(when exwm-debug-on + `(when exwm-debug (xcb-debug:message ,(concat "%s:\t" format-string "\n") (xcb-debug:compile-time-function-name) ,@objects) nil)) -(defun exwm-debug-toggle (&optional arg) - "Toggle EXWM debugging output. -When ARG is positive, turn debugging on; when negative off. When -ARG is nil, toggle debugging output." - (interactive - (list (or current-prefix-arg 'toggle))) - (setq exwm-debug-on (if (eq arg 'toggle) - (not exwm-debug-on) - (> 0 arg)))) - (defsubst exwm--id->buffer (id) "X window ID => Emacs buffer." (cdr (assoc id exwm--id-buffer-alist))) @@ -201,7 +193,7 @@ least SECS seconds later." (let ((map (make-sparse-keymap))) (define-key map "\C-c\C-d\C-l" #'xcb-debug:clear) (define-key map "\C-c\C-d\C-m" #'xcb-debug:mark) - (define-key map "\C-c\C-d\C-t" #'exwm-debug-toggle) + (define-key map "\C-c\C-d\C-t" #'exwm-debug) (define-key map "\C-c\C-f" #'exwm-layout-set-fullscreen) (define-key map "\C-c\C-h" #'exwm-floating-hide) (define-key map "\C-c\C-k" #'exwm-input-release-keyboard) diff --git a/exwm-manage.el b/exwm-manage.el index 7b28d68768..79c5405fff 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -182,7 +182,7 @@ You can still make the X windows floating afterwards." (when (derived-mode-p 'exwm-mode) (dolist (i exwm-manage-configurations) (save-current-buffer - (when (eval (car i)) + (when (eval (car i) t) (cl-return-from exwm-manage--get-configurations (cdr i))))))) (defun exwm-manage--manage-window (id) -- cgit 1.4.1 From dff1ef6a3c36fd7d8b3a3bafbd66a91b8e576b26 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Mon, 8 Oct 2018 12:00:00 +0000 Subject: Simplify input handling No functional change. * exwm-input.el (exwm-input--current-input-mode): New function indicating keyboard input mode. (exwm-input--on-KeyPress, exwm-input--update-mode-line): Use `exwm-input--current-input-mode'. (exwm-input-grab-keyboard, exwm-input-release-keyboard) (exwm-input--update-mode-line): Simplify. * exwh-core.el (exwm--on-KeyPress): Remove variable, use `exwm--keyboard-grabbed' and `exwm-input--current-input-mode' instead. --- exwm-core.el | 6 ++---- exwm-input.el | 50 +++++++++++++++++++++++++++++--------------------- exwm-layout.el | 3 ++- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 66eb98cf90..612a26f59f 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -158,9 +158,7 @@ least SECS seconds later." (defvar-local exwm--mode-line-format nil) ;save mode-line-format (defvar-local exwm--floating-frame-position nil) ;set when hidden. (defvar-local exwm--fixed-size nil) ;fixed size -(defvar-local exwm--keyboard-grabbed nil) ;Keyboard grabbed. -(defvar-local exwm--on-KeyPress ;KeyPress event handler - #'exwm-input--on-KeyPress-line-mode) +(defvar-local exwm--input-mode 'line-mode) ;Keyboard grabbed. ;; Properties (defvar-local exwm--desktop nil "_NET_WM_DESKTOP.") (defvar-local exwm-window-type nil "_NET_WM_WINDOW_TYPE.") @@ -252,7 +250,7 @@ least SECS seconds later." "*Keyboard*" "---" ["Toggle keyboard mode" exwm-input-toggle-keyboard] - ["Send key" exwm-input-send-next-key exwm--keyboard-grabbed] + ["Send key" exwm-input-send-next-key (eq exwm--input-mode 'line-mode)] ;; This is merely a reference. ("Send simulation key" :filter (lambda (&rest _args) diff --git a/exwm-input.el b/exwm-input.el index 57fed2d4b8..d9ad3d3c66 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -428,9 +428,13 @@ ARGS are additional arguments to CALLBACK." (let ((obj (make-instance 'xcb:KeyPress))) (xcb:unmarshal obj data) (exwm--log "major-mode=%s buffer=%s" - major-mode (buffer-name (current-buffer))) + major-mode (buffer-name (current-buffer))) (if (derived-mode-p 'exwm-mode) - (funcall exwm--on-KeyPress obj data) + (cl-case (exwm-input--current-input-mode) + (line-mode + (exwm-input--on-KeyPress-line-mode obj data)) + (char-mode + (exwm-input--on-KeyPress-char-mode obj data))) (exwm-input--on-KeyPress-char-mode obj)))) (defun exwm-input--on-CreateNotify (data _synthetic) @@ -654,17 +658,24 @@ instead." :time xcb:Time:CurrentTime)) (xcb:flush exwm--connection)) +(defun exwm-input--current-input-mode () + "Return current input mode. +The return value is one of the symbols \\='line-mode or \\=`char-mode. + +Current buffer must be an `exwm-mode' buffer." + exwm--input-mode) + (defun exwm-input--update-mode-line (id) "Update the propertized `mode-line-process' for window ID." (let (help-echo cmd mode) - (cl-case exwm--on-KeyPress - ((exwm-input--on-KeyPress-line-mode) + (cl-case (exwm-input--current-input-mode) + (line-mode (setq mode "line" help-echo "mouse-1: Switch to char-mode" cmd `(lambda () (interactive) (exwm-input-release-keyboard ,id)))) - ((exwm-input--on-KeyPress-char-mode) + (char-mode (setq mode "char" help-echo "mouse-1: Switch to line-mode" cmd `(lambda () @@ -680,7 +691,8 @@ instead." (keymap (mode-line keymap - (down-mouse-1 . ,cmd))))))))) + (down-mouse-1 . ,cmd)))))) + (force-mode-line-update)))) (defun exwm-input--grab-keyboard (&optional id) "Grab all key events on window ID." @@ -697,7 +709,7 @@ instead." :keyboard-mode xcb:GrabMode:Sync)) (exwm--log "Failed to grab keyboard for #x%x" id)) (with-current-buffer (exwm--id->buffer id) - (setq exwm--on-KeyPress #'exwm-input--on-KeyPress-line-mode)))) + (setq exwm--input-mode 'line-mode)))) (defun exwm-input--release-keyboard (&optional id) "Ungrab all key events on window ID." @@ -712,7 +724,7 @@ instead." (exwm--log "Failed to release keyboard for #x%x" id)) (exwm-input--grab-global-prefix-keys id) (with-current-buffer (exwm--id->buffer id) - (setq exwm--on-KeyPress #'exwm-input--on-KeyPress-char-mode)))) + (setq exwm--input-mode 'char-mode)))) ;;;###autoload (defun exwm-input-grab-keyboard (&optional id) @@ -721,11 +733,8 @@ instead." (exwm--buffer->id (window-buffer))))) (when id (exwm--log "id=#x%x" id) - (with-current-buffer (exwm--id->buffer id) - (exwm-input--grab-keyboard id) - (setq exwm--keyboard-grabbed t) - (exwm-input--update-mode-line id) - (force-mode-line-update)))) + (exwm-input--grab-keyboard id) + (exwm-input--update-mode-line id))) ;;;###autoload (defun exwm-input-release-keyboard (&optional id) @@ -734,11 +743,8 @@ instead." (exwm--buffer->id (window-buffer))))) (when id (exwm--log "id=#x%x" id) - (with-current-buffer (exwm--id->buffer id) - (exwm-input--release-keyboard id) - (setq exwm--keyboard-grabbed nil) - (exwm-input--update-mode-line id) - (force-mode-line-update)))) + (exwm-input--release-keyboard id) + (exwm-input--update-mode-line id))) ;;;###autoload (defun exwm-input-toggle-keyboard (&optional id) @@ -748,9 +754,11 @@ instead." (when id (exwm--log "id=#x%x" id) (with-current-buffer (exwm--id->buffer id) - (if exwm--keyboard-grabbed - (exwm-input-release-keyboard id) - (exwm-reset))))) + (cl-case (exwm-input--current-input-mode) + (line-mode + (exwm-input-release-keyboard id)) + (char-mode + (exwm-reset)))))) (defun exwm-input--fake-key (event) "Fake a key event equivalent to Emacs event EVENT." diff --git a/exwm-layout.el b/exwm-layout.el index 56faadd992..90988faae0 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -45,6 +45,7 @@ (defvar exwm-layout--timer nil "Timer used to track echo area changes.") (defvar exwm-workspace--current) +(declare-function exwm-input--current-input-mode "exwm-input.el") (declare-function exwm-input--release-keyboard "exwm-input.el") (declare-function exwm-input--grab-keyboard "exwm-input.el") (declare-function exwm-input-grab-keyboard "exwm-input.el") @@ -199,7 +200,7 @@ (make-instance 'xcb:ewmh:set-_NET_WM_STATE :window exwm--id :data [])) (xcb:flush exwm--connection) (set-window-dedicated-p (get-buffer-window) nil) - (when exwm--keyboard-grabbed + (when (eq 'line-mode (exwm-input--current-input-mode)) (exwm-input--grab-keyboard exwm--id)))) ;;;###autoload -- cgit 1.4.1 From c9984ca2163a18c19f46a90c003569b628de17f0 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Mon, 8 Oct 2018 12:00:00 +0000 Subject: Fix focus jumps with minibuffer-only frames * exwm-input.el (exwm-input--update-focus): Focus the minibuffer's frame, which is different than that of the `minibuffer-selected-window' on minibuffer-only frames. (exwm-input--on-minibuffer-setup, exwm-input--init) (exwm-input-exit): Remove unneeded function. --- exwm-input.el | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 57fed2d4b8..876b6a1125 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -351,17 +351,10 @@ ARGS are additional arguments to CALLBACK." (x-focus-frame (window-frame window)) ;; X input focus should be set on the previously selected ;; frame. - (x-focus-frame (window-frame (minibuffer-selected-window)))) + (x-focus-frame (window-frame (minibuffer-window)))) (exwm-input--set-active-window) (xcb:flush exwm--connection))))))) -(defun exwm-input--on-minibuffer-setup () - "Run in `minibuffer-setup-hook' to set input focus." - (exwm--log) - (unless (exwm-workspace--client-p) - ;; Set input focus on the Emacs frame - (x-focus-frame (window-frame (minibuffer-selected-window))))) - (defun exwm-input--set-active-window (&optional id) "Set _NET_ACTIVE_WINDOW." (exwm--log) @@ -1004,8 +997,6 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences." (when mouse-autoselect-window (xcb:+event exwm--connection 'xcb:EnterNotify #'exwm-input--on-EnterNotify)) - ;; The input focus should be set on the frame when minibuffer is active. - (add-hook 'minibuffer-setup-hook #'exwm-input--on-minibuffer-setup) ;; Control `exwm-input--during-command' (add-hook 'pre-command-hook #'exwm-input--on-pre-command) (add-hook 'post-command-hook #'exwm-input--on-post-command) @@ -1019,7 +1010,6 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences." (defun exwm-input--exit () "Exit the input module." (exwm-input--unset-simulation-keys) - (remove-hook 'minibuffer-setup-hook #'exwm-input--on-minibuffer-setup) (remove-hook 'pre-command-hook #'exwm-input--on-pre-command) (remove-hook 'post-command-hook #'exwm-input--on-post-command) (remove-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update) -- cgit 1.4.1 From 57d0e9e1d3d254d6e82ea2ce877b0d5fe56284f2 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Mon, 8 Oct 2018 12:00:00 +0000 Subject: Support binding mouse events in line-mode * exwm-input.el (exwm-input--event-passthrough-p): Predicate checking whether an event should be forwarded to Emacs. (exwm-input--on-KeyPress-line-mode): Use it. * exwm-input.el (exwm-input--cache-event): Protect against translations swallow the event. * exwm-input.el (exwm-input--on-ButtonPress-line-mode) (exwm-input--on-ButtonPress-char-mode): New functions. (exwm-input--on-ButtonPress): Forward bound mouse events to Emacs when in line-mode. --- exwm-input.el | 85 ++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 24 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index d9ad3d3c66..e4c4f9ee3a 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -87,7 +87,7 @@ defined in `exwm-mode-map' here." value)))) (defcustom exwm-input-line-mode-passthrough nil - "Non-nil makes 'line-mode' forwards all events to Emacs." + "Non-nil makes 'line-mode' forward all events to Emacs." :type 'boolean) ;; Input focus update requests should be accumulated for a short time @@ -378,11 +378,11 @@ ARGS are additional arguments to CALLBACK." (xcb:unmarshal obj data) (exwm--log "major-mode=%s buffer=%s" major-mode (buffer-name (current-buffer))) - (with-slots (detail time event state) obj + (with-slots (detail event state) obj (setq button-event (xcb:keysyms:keysym->event exwm--connection detail state) - window (get-buffer-window (exwm--id->buffer event) t) - buffer (window-buffer window)) + buffer (exwm--id->buffer event) + window (get-buffer-window buffer t)) (cond ((and (eq button-event exwm-input-move-event) ;; Either an undecorated or a floating X window. (with-current-buffer buffer @@ -414,11 +414,15 @@ ARGS are additional arguments to CALLBACK." ;; It has been reported that the `window' may have be deleted (if (window-live-p window) (select-window window) - (setq window - (get-buffer-window (exwm--id->buffer event) t)) + (setq window (get-buffer-window buffer t)) (when window (select-window window)))) - ;; The event should be replayed - (setq mode xcb:Allow:ReplayPointer)))) + (with-current-buffer buffer + (when (derived-mode-p 'exwm-mode) + (cl-case (exwm-input--current-input-mode) + (line-mode + (setq mode (exwm-input--on-ButtonPress-line-mode buffer button-event))) + (char-mode + (setq mode (exwm-input--on-ButtonPress-char-mode))))))))) (xcb:+request exwm--connection (make-instance 'xcb:AllowEvents :mode mode :time xcb:Time:CurrentTime)) (xcb:flush exwm--connection))) @@ -575,13 +579,34 @@ instead." ;; Attempt to translate this key sequence. (setq exwm-input--line-mode-cache (exwm-input--translate exwm-input--line-mode-cache)) - ;; When the key sequence is complete. - (unless (keymapp (key-binding exwm-input--line-mode-cache)) + ;; When the key sequence is complete (not a keymap). + ;; Note that `exwm-input--line-mode-cache' might get translated to nil, for + ;; example 'mouse--down-1-maybe-follows-link' does this. + (unless (and exwm-input--line-mode-cache + (keymapp (key-binding exwm-input--line-mode-cache))) (setq exwm-input--line-mode-cache nil) (when exwm-input--temp-line-mode (setq exwm-input--temp-line-mode nil) (exwm-input--release-keyboard)))) +(defun exwm-input--event-passthrough-p (event) + "Whether EVENT should be passed to Emacs. +Current buffer must be an `exwm-mode' buffer." + (or exwm-input-line-mode-passthrough + exwm-input--during-command + ;; Forward the event when there is an incomplete key + ;; sequence or when the minibuffer is active. + exwm-input--line-mode-cache + (eq (active-minibuffer-window) (selected-window)) + ;; + (memq event exwm-input--global-prefix-keys) + (memq event exwm-input-prefix-keys) + (when overriding-terminal-local-map + (lookup-key overriding-terminal-local-map + (vector event))) + (lookup-key (current-local-map) (vector event)) + (gethash event exwm-input--simulation-keys))) + (defun exwm-input--on-KeyPress-line-mode (key-press raw-data) "Parse X KeyPress event to Emacs key event and then feed the command loop." (with-slots (detail state) key-press @@ -593,20 +618,7 @@ instead." exwm--connection (car keysym) (logand state (lognot (cdr keysym))))) (setq event (exwm-input--mimic-read-event raw-event)) - (or exwm-input-line-mode-passthrough - exwm-input--during-command - ;; Forward the event when there is an incomplete key - ;; sequence or when the minibuffer is active. - exwm-input--line-mode-cache - (eq (active-minibuffer-window) (selected-window)) - ;; - (memq event exwm-input--global-prefix-keys) - (memq event exwm-input-prefix-keys) - (when overriding-terminal-local-map - (lookup-key overriding-terminal-local-map - (vector event))) - (lookup-key (current-local-map) (vector event)) - (gethash event exwm-input--simulation-keys))) + (exwm-input--event-passthrough-p event)) (setq mode xcb:Allow:AsyncKeyboard) (exwm-input--cache-event event) (exwm-input--unread-event raw-event)) @@ -658,6 +670,31 @@ instead." :time xcb:Time:CurrentTime)) (xcb:flush exwm--connection)) +(defun exwm-input--on-ButtonPress-line-mode (buffer button-event) + "Handle button events in line mode. +BUFFER is the `exwm-mode' buffer the event was generated +on. BUTTON-EVENT is the X event converted into an Emacs event. + +The return value is used as event_mode to release the original +button event." + (with-current-buffer buffer + (let ((read-event (exwm-input--mimic-read-event button-event))) + (if (and read-event + (exwm-input--event-passthrough-p read-event)) + ;; The event should be forwarded to emacs + (progn + (exwm-input--cache-event read-event) + (exwm-input--unread-event button-event) + xcb:Allow:SyncPointer) + ;; The event should be replayed + xcb:Allow:ReplayPointer)))) + +(defun exwm-input--on-ButtonPress-char-mode () + "Handle button events in char-mode. +The return value is used as event_mode to release the original +button event." + xcb:Allow:ReplayPointer) + (defun exwm-input--current-input-mode () "Return current input mode. The return value is one of the symbols \\='line-mode or \\=`char-mode. -- cgit 1.4.1 From 24287f2691e31c4342f75ec12235a9f08cc2c8e5 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Mon, 8 Oct 2018 12:00:00 +0000 Subject: Reduce flicker when switching workspaces * exwm-workspace.el (exwm-workspace-switch): Hide the old workspace after having shown the new one when switching to avoid flicker. --- exwm-workspace.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 31108892fe..8a59c36b54 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -552,8 +552,8 @@ for internal use only." ((not active-old) (exwm-workspace--set-active frame t)) ((equal output-old output-new) - (exwm-workspace--set-active old-frame nil) (exwm-workspace--set-active frame t) + (exwm-workspace--set-active old-frame nil) (setq workspaces-to-hide (list old-frame))) (active-new) (t -- cgit 1.4.1 From 449cd9d37910f55ed25e293c4b67223bb7ee7eef Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 14 Oct 2018 00:00:00 +0000 Subject: Bump version to 0.20 --- exwm.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm.el b/exwm.el index b9c1d0ba34..6021f85b01 100644 --- a/exwm.el +++ b/exwm.el @@ -4,8 +4,8 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0.19 -;; Package-Requires: ((xelb "0.15")) +;; Version: 0.20 +;; Package-Requires: ((xelb "0.16")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From 01b0b54c04d043c47184019c75cc668da2427aa8 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 21 Oct 2018 00:00:00 +0000 Subject: Fix single workspace invisible problem with Xephyr * exwm-workspace.el (exwm-workspace-switch): On startup EXWM switches to workspace 0 by force so the rest code can not assume the frames before and after a switch different. --- exwm-workspace.el | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 8a59c36b54..383bf530b8 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -553,8 +553,9 @@ for internal use only." (exwm-workspace--set-active frame t)) ((equal output-old output-new) (exwm-workspace--set-active frame t) - (exwm-workspace--set-active old-frame nil) - (setq workspaces-to-hide (list old-frame))) + (unless (eq frame old-frame) + (exwm-workspace--set-active old-frame nil) + (setq workspaces-to-hide (list old-frame)))) (active-new) (t (dolist (w exwm-workspace--list) @@ -626,7 +627,8 @@ for internal use only." :dst-x (/ (frame-pixel-width frame) 2) :dst-y (/ (frame-pixel-height frame) 2))) (xcb:flush exwm--connection)))) - (when (frame-live-p old-frame) + (when (and (not (eq frame old-frame)) + (frame-live-p old-frame)) (with-selected-frame old-frame (run-hooks 'focus-out-hook))) (run-hooks 'focus-in-hook) -- cgit 1.4.1 From dba1ed94c2e06b46da77a2aa80aa35e17b08fdd6 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Sun, 21 Oct 2018 12:00:00 +0000 Subject: Do away with `exwm-input--input-mode' * exwm-input.el (exwm-input--update-mode-line): Remove. (exwm-input--on-ButtonPress, exwm-input--on-KeyPress) (exwm-input--update-mode-line, exwm-input-toggle-keyboard): Use `exwm--input-mode' variable directly. --- exwm-input.el | 15 ++++----------- exwm-layout.el | 3 +-- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 6e8f6d814a..78e74cdcf8 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -411,7 +411,7 @@ ARGS are additional arguments to CALLBACK." (when window (select-window window)))) (with-current-buffer buffer (when (derived-mode-p 'exwm-mode) - (cl-case (exwm-input--current-input-mode) + (cl-case exwm--input-mode (line-mode (setq mode (exwm-input--on-ButtonPress-line-mode buffer button-event))) (char-mode @@ -427,7 +427,7 @@ ARGS are additional arguments to CALLBACK." (exwm--log "major-mode=%s buffer=%s" major-mode (buffer-name (current-buffer))) (if (derived-mode-p 'exwm-mode) - (cl-case (exwm-input--current-input-mode) + (cl-case exwm--input-mode (line-mode (exwm-input--on-KeyPress-line-mode obj data)) (char-mode @@ -688,17 +688,10 @@ The return value is used as event_mode to release the original button event." xcb:Allow:ReplayPointer) -(defun exwm-input--current-input-mode () - "Return current input mode. -The return value is one of the symbols \\='line-mode or \\=`char-mode. - -Current buffer must be an `exwm-mode' buffer." - exwm--input-mode) - (defun exwm-input--update-mode-line (id) "Update the propertized `mode-line-process' for window ID." (let (help-echo cmd mode) - (cl-case (exwm-input--current-input-mode) + (cl-case exwm--input-mode (line-mode (setq mode "line" help-echo "mouse-1: Switch to char-mode" @@ -784,7 +777,7 @@ Current buffer must be an `exwm-mode' buffer." (when id (exwm--log "id=#x%x" id) (with-current-buffer (exwm--id->buffer id) - (cl-case (exwm-input--current-input-mode) + (cl-case exwm--input-mode (line-mode (exwm-input-release-keyboard id)) (char-mode diff --git a/exwm-layout.el b/exwm-layout.el index 90988faae0..fe394e226e 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -45,7 +45,6 @@ (defvar exwm-layout--timer nil "Timer used to track echo area changes.") (defvar exwm-workspace--current) -(declare-function exwm-input--current-input-mode "exwm-input.el") (declare-function exwm-input--release-keyboard "exwm-input.el") (declare-function exwm-input--grab-keyboard "exwm-input.el") (declare-function exwm-input-grab-keyboard "exwm-input.el") @@ -200,7 +199,7 @@ (make-instance 'xcb:ewmh:set-_NET_WM_STATE :window exwm--id :data [])) (xcb:flush exwm--connection) (set-window-dedicated-p (get-buffer-window) nil) - (when (eq 'line-mode (exwm-input--current-input-mode)) + (when (eq 'line-mode exwm--input-mode) (exwm-input--grab-keyboard exwm--id)))) ;;;###autoload -- cgit 1.4.1 From eb91ee2c77787bbeb1df74f0237f5087d031217f Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Sun, 21 Oct 2018 12:00:00 +0000 Subject: Restore keyboard grabbing after exiting fullscreen Recover the difference between the input mode selected by the user and actual one, which might be transient. This was inadvertently removed in recent changes. * exwm-core.el (exwm--selected-input-mode): New variable. * exwm-input.el (exwm-input-grab-keyboard) (exwm-input-release-keyboard): Use it. * exwm-layout.el (exwm-layout-unset-fullscreen): Return to the selected input mode. --- exwm-core.el | 6 +++++- exwm-input.el | 2 ++ exwm-layout.el | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 612a26f59f..1f939491c5 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -158,7 +158,11 @@ least SECS seconds later." (defvar-local exwm--mode-line-format nil) ;save mode-line-format (defvar-local exwm--floating-frame-position nil) ;set when hidden. (defvar-local exwm--fixed-size nil) ;fixed size -(defvar-local exwm--input-mode 'line-mode) ;Keyboard grabbed. +(defvar-local exwm--selected-input-mode 'line-mode + "Input mode as selected by the user. +One of `line-mode' or `char-mode'.") +(defvar-local exwm--input-mode 'line-mode + "Actual input mode, i.e. whether mouse and keyboard are grabbed.") ;; Properties (defvar-local exwm--desktop nil "_NET_WM_DESKTOP.") (defvar-local exwm-window-type nil "_NET_WM_WINDOW_TYPE.") diff --git a/exwm-input.el b/exwm-input.el index 78e74cdcf8..c757b09d3f 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -756,6 +756,7 @@ button event." (exwm--buffer->id (window-buffer))))) (when id (exwm--log "id=#x%x" id) + (setq exwm--selected-input-mode 'line-mode) (exwm-input--grab-keyboard id) (exwm-input--update-mode-line id))) @@ -766,6 +767,7 @@ button event." (exwm--buffer->id (window-buffer))))) (when id (exwm--log "id=#x%x" id) + (setq exwm--selected-input-mode 'char-mode) (exwm-input--release-keyboard id) (exwm-input--update-mode-line id))) diff --git a/exwm-layout.el b/exwm-layout.el index fe394e226e..bee6901f82 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -199,7 +199,7 @@ (make-instance 'xcb:ewmh:set-_NET_WM_STATE :window exwm--id :data [])) (xcb:flush exwm--connection) (set-window-dedicated-p (get-buffer-window) nil) - (when (eq 'line-mode exwm--input-mode) + (when (eq 'line-mode exwm--selected-input-mode) (exwm-input--grab-keyboard exwm--id)))) ;;;###autoload -- cgit 1.4.1 From 0dd909a11baf3d7d766c1672d726eb59b1474ef2 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Sat, 3 Nov 2018 12:00:00 +0000 Subject: Stop hiding the minibuffer when a message is being displayed * exwm-workspace.el (exwm-workspace--echo-area-maybe-clear): New function that postpones hiding the minibuffer when it's displaying a message. (exwm-workspace--on-echo-area-dirty): Use it. --- exwm-workspace.el | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 383bf530b8..0f0d10c367 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1203,7 +1203,18 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." input-method-use-echo-area) (setq exwm-workspace--display-echo-area-timer (run-with-timer exwm-workspace-display-echo-area-timeout nil - #'exwm-workspace--on-echo-area-clear))))) + #'exwm-workspace--echo-area-maybe-clear))))) + +(defun exwm-workspace--echo-area-maybe-clear () + "Eventually clear the echo area container." + (exwm--log) + (if (not (current-message)) + (exwm-workspace--on-echo-area-clear) + ;; Reschedule. + (cancel-timer exwm-workspace--display-echo-area-timer) + (setq exwm-workspace--display-echo-area-timer + (run-with-timer exwm-workspace-display-echo-area-timeout nil + #'exwm-workspace--echo-area-maybe-clear)))) (defun exwm-workspace--on-echo-area-clear () "Run in echo-area-clear-hook to hide echo area container." -- cgit 1.4.1 From cd7b32d1c203b87d428ea75e5a5e1452d8236742 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Sat, 3 Nov 2018 12:00:00 +0000 Subject: Hide minibuffer upon receiving any event * exwm-input.el (exwm-input--event-hook): New variable. (exwm-input--on-ButtonPress, exwm-input--on-KeyPress): Run `exwm-input--event-hook'. * exwm-workspace.el (exwm-workspace--init, exwm-workspace--exit): Hide minibuffer upon noticing an event. --- exwm-input.el | 9 +++++++-- exwm-workspace.el | 7 +++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index c757b09d3f..a7fb16a175 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -137,6 +137,9 @@ defined in `exwm-mode-map' here." (defvar exwm-input--update-focus-window nil "The (Emacs) window to be focused. This value should always be overwritten.") +(defvar exwm-input--event-hook nil + "Hook to run when EXWM receives an event.") + (defvar exwm-workspace--current) (declare-function exwm-floating--do-moveresize "exwm-floating.el" (data _synthetic)) @@ -418,7 +421,8 @@ ARGS are additional arguments to CALLBACK." (setq mode (exwm-input--on-ButtonPress-char-mode))))))))) (xcb:+request exwm--connection (make-instance 'xcb:AllowEvents :mode mode :time xcb:Time:CurrentTime)) - (xcb:flush exwm--connection))) + (xcb:flush exwm--connection)) + (run-hooks 'exwm-input--event-hook)) (defun exwm-input--on-KeyPress (data _synthetic) "Handle KeyPress event." @@ -432,7 +436,8 @@ ARGS are additional arguments to CALLBACK." (exwm-input--on-KeyPress-line-mode obj data)) (char-mode (exwm-input--on-KeyPress-char-mode obj data))) - (exwm-input--on-KeyPress-char-mode obj)))) + (exwm-input--on-KeyPress-char-mode obj))) + (run-hooks 'exwm-input--event-hook)) (defun exwm-input--on-CreateNotify (data _synthetic) "Handle CreateNotify events." diff --git a/exwm-workspace.el b/exwm-workspace.el index 0f0d10c367..d58758fc1f 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -128,6 +128,7 @@ Please manually run the hook `exwm-workspace-list-change-hook' afterwards.") (defvar exwm-workspace--workareas nil "Workareas (struts excluded).") (defvar exwm-input--during-command) +(defvar exwm-input--event-hook) (defvar exwm-layout-show-all-buffers) (defvar exwm-manage--desktop) (declare-function exwm-input--on-buffer-list-update "exwm-input.el" ()) @@ -1584,6 +1585,9 @@ applied to all subsequently created X frames." (add-hook 'after-make-frame-functions #'exwm-workspace--on-after-make-frame) (add-hook 'delete-frame-functions #'exwm-workspace--on-delete-frame) + (when (exwm-workspace--minibuffer-own-frame-p) + (add-hook 'exwm-input--event-hook + #'exwm-workspace--on-echo-area-clear)) ;; Switch to the first workspace (exwm-workspace-switch 0 t) ;; Prevent frame parameters introduced by this module from being @@ -1604,6 +1608,9 @@ applied to all subsequently created X frames." #'exwm-workspace--on-after-make-frame) (remove-hook 'delete-frame-functions #'exwm-workspace--on-delete-frame) + (when (exwm-workspace--minibuffer-own-frame-p) + (remove-hook 'exwm-input--event-hook + #'exwm-workspace--on-echo-area-clear)) ;; Hide & reparent out all frames (save-set can't be used here since ;; X windows will be re-mapped). (setq exwm-workspace--current nil) -- cgit 1.4.1 From fec85bb72a2d93fb86949f3409327b0f88ae60bc Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 4 Nov 2018 00:00:00 +0000 Subject: Select more sensible buffers after creating new windows * exwm-layout.el (exwm-layout--refresh-other) (exwm-layout--refresh-workspace): When filling vacated windows select more sensible buffer by calling `switch-to-next-buffer' for newly created windows. --- exwm-layout.el | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index bee6901f82..b97c2f3ed2 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -296,7 +296,9 @@ selected by `other-buffer'." (dolist (window windows) (with-current-buffer (window-buffer window) (when (derived-mode-p 'exwm-mode) - (switch-to-prev-buffer window)))))) + (if (window-prev-buffers window) + (switch-to-prev-buffer window) + (switch-to-next-buffer window))))))) (defun exwm-layout--refresh-workspace (frame) "Refresh workspace frame FRAME." @@ -346,14 +348,18 @@ selected by `other-buffer'." ;; Set some sensible buffer to vacated windows. (let ((exwm-layout--other-buffer-exclude-buffers covered-buffers)) (dolist (window vacated-windows) - (switch-to-prev-buffer window))) + (if (window-prev-buffers window) + (switch-to-prev-buffer window) + (switch-to-next-buffer window)))) ;; Make sure windows floating / on other workspaces are excluded (let ((exwm-layout--other-buffer-exclude-exwm-mode-buffers t)) (dolist (window (window-list frame 'nomini)) (with-current-buffer (window-buffer window) (when (and (derived-mode-p 'exwm-mode) (or exwm--floating-frame (not (eq frame exwm--frame)))) - (switch-to-prev-buffer window))))) + (if (window-prev-buffers window) + (switch-to-prev-buffer window) + (switch-to-next-buffer window)))))) (exwm-layout--set-client-list-stacking) (xcb:flush exwm--connection))) -- cgit 1.4.1 From e597ab4f785982d44f56f61ba113b54c501256ac Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 4 Nov 2018 00:00:00 +0000 Subject: Add support for RandR 1.5 monitor * exwm-randr.el (exwm-randr-workspace-monitor-plist): New user option for specifying which monitor each workspace should be displayed on. (exwm-randr-workspace-monitor-plist): Made obsolete. (exwm-randr--get-monitors): New function for fetching active monitors. (exwm-randr--refresh): Adapted to use monitor. (exwm-randr--init): Now requires RandR 1.5. * exwm-randr.el: * exwm-workspace.el: Rename `output' to `monitor'. --- exwm-randr.el | 145 +++++++++++++++++++++++++++--------------------------- exwm-workspace.el | 18 +++---- 2 files changed, 81 insertions(+), 82 deletions(-) diff --git a/exwm-randr.el b/exwm-randr.el index af900ab36f..5422252652 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -27,11 +27,11 @@ ;; that. ;; To use this module, load, enable it and configure -;; `exwm-randr-workspace-output-plist' and `exwm-randr-screen-change-hook' +;; `exwm-randr-workspace-monitor-plist' and `exwm-randr-screen-change-hook' ;; as follows: ;; ;; (require 'exwm-randr) -;; (setq exwm-randr-workspace-output-plist '(0 "VGA1")) +;; (setq exwm-randr-workspace-monitor-plist '(0 "VGA1")) ;; (add-hook 'exwm-randr-screen-change-hook ;; (lambda () ;; (start-process-shell-command @@ -63,22 +63,34 @@ "Normal hook run when screen changes." :type 'hook) -(defcustom exwm-randr-workspace-output-plist nil - "Plist mapping workspace to output. +(defcustom exwm-randr-workspace-monitor-plist nil + "Plist mapping workspaces to monitors. -If an output is not available, the workspaces mapped to it are displayed on -the primary output until it becomes available. Unspecified workspaces are -all mapped to the primary output. For example, with the following value -workspace other than 1 and 3 would always be displayed on the primary output -where workspace 1 and 3 would be displayed on their corresponding output -whenever the outputs are available. +In RandR 1.5 a monitor is a rectangle region decoupled from the physical +size of screens, and can be identified with `xrandr --listmonitors' (name of +the primary monitor is prefixed with an `*'). When no monitor is created it +automatically fallback to RandR 1.2 output which represents the physical +screen size. RandR 1.5 monitors can be created with `xrandr --setmonitor'. +For example, to split an output (`LVDS-1') of size 1280x800 into two +side-by-side monitors one could invoke (the digits after `/' are size in mm) - '(1 \"HDMI-1\" 3 \"DP-1\") + xrandr --setmonitor *LVDS-1-L 640/135x800/163+0+0 LVDS-1 + xrandr --setmonitor LVDS-1-R 640/135x800/163+640+0 none -The outputs available can be identified by running the 'xrandr' utility with -the first one in result being the primary output." +If a monitor is not active, the workspaces mapped to it are displayed on the +primary monitor until it becomes active (if ever). Unspecified workspaces +are all mapped to the primary monitor. For example, with the following +setting workspace other than 1 and 3 would always be displayed on the +primary monitor where workspace 1 and 3 would be displayed on their +corresponding monitors whenever the monitors are active. + + \\='(1 \"HDMI-1\" 3 \"DP-1\")" :type '(plist :key-type integer :value-type string)) +(with-no-warnings + (define-obsolete-variable-alias 'exwm-randr-workspace-output-plist + 'exwm-randr-workspace-monitor-plist "27.1")) + (defvar exwm-workspace--fullscreen-frame-count) (defvar exwm-workspace--list) (declare-function exwm-workspace--count "exwm-workspace.el") @@ -89,57 +101,54 @@ the first one in result being the primary output." (declare-function exwm-workspace--show-minibuffer "exwm-workspace.el" ()) (declare-function exwm-workspace--update-workareas "exwm-workspace.el" ()) +(defun exwm-randr--get-monitors () + "Get RandR monitors." + (let (monitor-name geometry monitor-plist primary-monitor) + (with-slots (monitors) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:randr:GetMonitors + :window exwm--root + :get-active 1)) + (dolist (monitor monitors) + (with-slots (name primary x y width height) monitor + (setq monitor-name (x-get-atom-name name) + geometry (make-instance 'xcb:RECTANGLE + :x x + :y y + :width width + :height height) + monitor-plist (plist-put monitor-plist monitor-name geometry)) + ;; Save primary monitor when available. + (when (/= 0 primary) + (setq primary-monitor monitor-name))))) + (exwm--log "Primary monitor: %s" primary-monitor) + (exwm--log "Monitors: %s" monitor-plist) + (list primary-monitor monitor-plist))) + (defun exwm-randr--refresh () "Refresh workspaces according to the updated RandR info." - (let (output-name geometry output-plist primary-output default-geometry - container-output-alist container-frame-alist) - ;; Query all outputs - (with-slots (config-timestamp outputs) - (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:randr:GetScreenResourcesCurrent - :window exwm--root)) - (dolist (output outputs) - (with-slots (crtc connection name) - (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:randr:GetOutputInfo - :output output - :config-timestamp config-timestamp)) - (setf output-name ;UTF-8 encoded - (decode-coding-string (apply #'unibyte-string name) 'utf-8)) - (if (or (/= connection xcb:randr:Connection:Connected) - (= 0 crtc)) ;FIXME - (plist-put output-plist output-name nil) - (with-slots (x y width height) - (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:randr:GetCrtcInfo - :crtc crtc - :config-timestamp config-timestamp)) - (setq geometry (make-instance 'xcb:RECTANGLE - :x x :y y - :width width :height height) - output-plist (plist-put output-plist output-name geometry)) - (unless primary-output - (setq primary-output output-name - default-geometry geometry))))))) - (exwm--log "(randr) outputs: %s" output-plist) - (when output-plist + (let* ((result (exwm-randr--get-monitors)) + (primary-monitor (elt result 0)) + (monitor-plist (elt result 1)) + container-monitor-alist container-frame-alist) + (when (and primary-monitor monitor-plist) (when exwm-workspace--fullscreen-frame-count ;; Not all workspaces are fullscreen; reset this counter. (setq exwm-workspace--fullscreen-frame-count 0)) (dotimes (i (exwm-workspace--count)) - (let* ((output (plist-get exwm-randr-workspace-output-plist i)) - (geometry (lax-plist-get output-plist output)) + (let* ((monitor (plist-get exwm-randr-workspace-monitor-plist i)) + (geometry (lax-plist-get monitor-plist monitor)) (frame (elt exwm-workspace--list i)) (container (frame-parameter frame 'exwm-container))) (unless geometry - (setq geometry default-geometry - output primary-output)) - (setq container-output-alist (nconc - `((,container . ,(intern output))) - container-output-alist) + (setq monitor primary-monitor + geometry (lax-plist-get monitor-plist primary-monitor))) + (setq container-monitor-alist (nconc + `((,container . ,(intern monitor))) + container-monitor-alist) container-frame-alist (nconc `((,container . ,frame)) container-frame-alist)) - (set-frame-parameter frame 'exwm-randr-output output) + (set-frame-parameter frame 'exwm-randr-monitor monitor) (set-frame-parameter frame 'exwm-geometry geometry))) ;; Update workareas. (exwm-workspace--update-workareas) @@ -156,23 +165,24 @@ the first one in result being the primary output." ;; Update active/inactive workspaces. (dolist (w exwm-workspace--list) (exwm-workspace--set-active w nil)) + ;; Mark the workspace on the top of each monitor as active. (dolist (xwin (reverse (slot-value (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:QueryTree :window exwm--root)) 'children))) - (let ((output (cdr (assq xwin container-output-alist)))) - (when output - (setq container-output-alist - (rassq-delete-all output container-output-alist)) + (let ((monitor (cdr (assq xwin container-monitor-alist)))) + (when monitor + (setq container-monitor-alist + (rassq-delete-all monitor container-monitor-alist)) (exwm-workspace--set-active (cdr (assq xwin container-frame-alist)) t)))) (xcb:flush exwm--connection) (run-hooks 'exwm-randr-refresh-hook)))) (defun exwm-randr--on-ScreenChangeNotify (_data _synthetic) - (exwm--log "(RandR) ScreenChangeNotify") + (exwm--log) (run-hooks 'exwm-randr-screen-change-hook) (exwm-randr--refresh)) @@ -184,8 +194,8 @@ the first one in result being the primary output." (with-slots (major-version minor-version) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:randr:QueryVersion - :major-version 1 :minor-version 3)) - (if (or (/= major-version 1) (< minor-version 3)) + :major-version 1 :minor-version 5)) + (if (or (/= major-version 1) (< minor-version 5)) (error "[EXWM] The server only support RandR version up to %d.%d" major-version minor-version) ;; External monitor(s) may already be connected. @@ -193,26 +203,15 @@ the first one in result being the primary output." (exwm-randr--refresh) (xcb:+event exwm--connection 'xcb:randr:ScreenChangeNotify #'exwm-randr--on-ScreenChangeNotify) - ;; (xcb:+event exwm--connection 'xcb:randr:Notify - ;; (lambda (_data _synthetic) - ;; (exwm--log "(RandR) Notify") - ;; (exwm-randr--refresh))) (xcb:+request exwm--connection (make-instance 'xcb:randr:SelectInput :window exwm--root - :enable xcb:randr:NotifyMask:ScreenChange - ;; :enable (eval-when-compile - ;; (logior - ;; xcb:randr:NotifyMask:ScreenChange - ;; xcb:randr:NotifyMask:OutputChange - ;; xcb:randr:NotifyMask:OutputProperty - ;; xcb:randr:NotifyMask:CrtcChange)) - )) + :enable xcb:randr:NotifyMask:ScreenChange)) (xcb:flush exwm--connection) (add-hook 'exwm-workspace-list-change-hook #'exwm-randr--refresh)))) ;; Prevent frame parameters introduced by this module from being ;; saved/restored. - (dolist (i '(exwm-randr-output)) + (dolist (i '(exwm-randr-monitor)) (unless (assq i frameset-filter-alist) (push (cons i :never) frameset-filter-alist)))) diff --git a/exwm-workspace.el b/exwm-workspace.el index 383bf530b8..b8e48b2586 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -374,7 +374,7 @@ NIL if FRAME is not a workspace" (run-hooks 'exwm-workspace--update-workareas-hook)) (defun exwm-workspace--set-active (frame active) - "Make frame FRAME active on its output." + "Make frame FRAME active on its monitor." (exwm--log "active=%s; frame=%s" frame active) (set-frame-parameter frame 'exwm-active active) (if active @@ -543,15 +543,15 @@ for internal use only." (set-frame-parameter (buffer-local-value 'exwm--frame (window-buffer)) 'exwm-selected-window (selected-window))) ;; Show/Hide X windows. - (let ((output-old (frame-parameter old-frame 'exwm-randr-output)) - (output-new (frame-parameter frame 'exwm-randr-output)) + (let ((monitor-old (frame-parameter old-frame 'exwm-randr-monitor)) + (monitor-new (frame-parameter frame 'exwm-randr-monitor)) (active-old (exwm-workspace--active-p old-frame)) (active-new (exwm-workspace--active-p frame)) workspaces-to-hide) (cond ((not active-old) (exwm-workspace--set-active frame t)) - ((equal output-old output-new) + ((equal monitor-old monitor-new) (exwm-workspace--set-active frame t) (unless (eq frame old-frame) (exwm-workspace--set-active old-frame nil) @@ -560,8 +560,8 @@ for internal use only." (t (dolist (w exwm-workspace--list) (when (and (exwm-workspace--active-p w) - (equal output-new - (frame-parameter w 'exwm-randr-output))) + (equal monitor-new + (frame-parameter w 'exwm-randr-monitor))) (exwm-workspace--set-active w nil) (setq workspaces-to-hide (append workspaces-to-hide (list w))))) (exwm-workspace--set-active frame t))) @@ -818,8 +818,8 @@ INDEX must not exceed the current number of workspaces." ;; Floating. (setq container (frame-parameter exwm--floating-frame 'exwm-container)) - (unless (equal (frame-parameter old-frame 'exwm-randr-output) - (frame-parameter frame 'exwm-randr-output)) + (unless (equal (frame-parameter old-frame 'exwm-randr-monitor) + (frame-parameter frame 'exwm-randr-monitor)) (with-slots (x y) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GetGeometry @@ -1240,7 +1240,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." ;; prevent potential problems. The values do not matter here as ;; they'll be updated by the RandR module later. (let ((w (car exwm-workspace--list))) - (dolist (param '(exwm-randr-output + (dolist (param '(exwm-randr-monitor exwm-geometry)) (set-frame-parameter frame param (frame-parameter w param)))) (xcb:+request exwm--connection -- cgit 1.4.1 From 9dcfff568f8c1205bf57877e3d1d5ebe8466ce8d Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 11 Nov 2018 00:00:00 +0000 Subject: Expose `exwm-randr-refresh' public interface * exwm-randr.el (exwm-randr-refresh): New public interface allowing users to manually refresh when RandR settings are changed output EXWM. (exwm-randr--refresh): Made obsolete. --- exwm-randr.el | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/exwm-randr.el b/exwm-randr.el index 5422252652..2ec4789730 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -125,8 +125,10 @@ corresponding monitors whenever the monitors are active. (exwm--log "Monitors: %s" monitor-plist) (list primary-monitor monitor-plist))) -(defun exwm-randr--refresh () +;;;###autoload +(defun exwm-randr-refresh () "Refresh workspaces according to the updated RandR info." + (interactive) (let* ((result (exwm-randr--get-monitors)) (primary-monitor (elt result 0)) (monitor-plist (elt result 1)) @@ -181,6 +183,9 @@ corresponding monitors whenever the monitors are active. (xcb:flush exwm--connection) (run-hooks 'exwm-randr-refresh-hook)))) +(define-obsolete-function-alias 'exwm-randr--refresh #'exwm-randr-refresh + "27.1") + (defun exwm-randr--on-ScreenChangeNotify (_data _synthetic) (exwm--log) (run-hooks 'exwm-randr-screen-change-hook) -- cgit 1.4.1 From d650159648e88ec4a67c126834cd24fed6e3fc24 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 11 Nov 2018 00:00:00 +0000 Subject: Fix wrong stacking order of tiling X windows * exwm-manage.el (exwm-manage--on-MapNotify): Avoid restacking tiling X windows on the top when force triggering hierarchy change events. --- exwm-manage.el | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index 79c5405fff..d7b5c3662e 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -674,11 +674,20 @@ border-width: %d; sibling: #x%x; stack-mode: %d" (exwm--log "id=#x%x" window) ;; With this we ensure that a "window hierarchy change" happens after ;; mapping the window, as some servers (XQuartz) do not generate it. - (xcb:+request exwm--connection - (make-instance 'xcb:ConfigureWindow - :window window - :value-mask xcb:ConfigWindow:StackMode - :stack-mode xcb:StackMode:Above)) + (with-current-buffer (exwm--id->buffer window) + (if exwm--floating-frame + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window window + :value-mask xcb:ConfigWindow:StackMode + :stack-mode xcb:StackMode:Above)) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window window + :value-mask (logior xcb:ConfigWindow:Sibling + xcb:ConfigWindow:StackMode) + :sibling exwm--guide-window + :stack-mode xcb:StackMode:Above)))) (xcb:flush exwm--connection))))) (defun exwm-manage--on-DestroyNotify (data synthetic) -- cgit 1.4.1 From 5fde63cc453b080f3436751a1912440664a09663 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 18 Nov 2018 00:00:00 +0000 Subject: Avoid activating already active X windows * exwm.el (exwm--on-ClientMessage): On receiving `_NET_ACTIVE_WINDOW' events, check if the requested X windows are already active. --- exwm.el | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/exwm.el b/exwm.el index 6021f85b01..98b30f9495 100644 --- a/exwm.el +++ b/exwm.el @@ -416,16 +416,21 @@ (exwm-workspace-switch (elt data 0))) ;; _NET_ACTIVE_WINDOW. ((= type xcb:Atom:_NET_ACTIVE_WINDOW) - (let ((buffer (exwm--id->buffer id))) + (let ((buffer (exwm--id->buffer id)) + iconic window) (when (buffer-live-p buffer) (with-current-buffer buffer (when (eq exwm--frame exwm-workspace--current) - (when (exwm-layout--iconic-state-p) + (setq iconic (exwm-layout--iconic-state-p)) + (when iconic ;; State change: iconic => normal. (set-window-buffer (frame-selected-window exwm--frame) (current-buffer))) ;; Focus transfer. - (select-window (get-buffer-window nil t))))))) + (setq window (get-buffer-window nil t)) + (when (or iconic + (not (eq window (selected-window)))) + (select-window window))))))) ;; _NET_CLOSE_WINDOW. ((= type xcb:Atom:_NET_CLOSE_WINDOW) (let ((buffer (exwm--id->buffer id))) -- cgit 1.4.1 From 882a628daad04e27fe19c03ceeb287ac9e6a8323 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 18 Nov 2018 00:00:00 +0000 Subject: Automatically refresh RandR settings * exwm-randr.el (exwm-randr--last-timestamp): New variable recording last seen timestamp after requesting `GetMonitors'. (exwm-randr--get-monitors): Use it. (exwm-randr--on-ScreenChangeNotify): Do not refresh any more. (exwm-randr--on-Notify): New function for handling `CrtcChangeNotify' and `OutputChangeNotify' events. (exwm-randr--on-ConfigureNotify): New function for handling `ConfigureNotify' event. (exwm-randr--init): Add listeners for additional events. --- exwm-randr.el | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/exwm-randr.el b/exwm-randr.el index 2ec4789730..106ed6f83a 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -91,6 +91,8 @@ corresponding monitors whenever the monitors are active. (define-obsolete-variable-alias 'exwm-randr-workspace-output-plist 'exwm-randr-workspace-monitor-plist "27.1")) +(defvar exwm-randr--last-timestamp 0 "Used for debouncing events.") + (defvar exwm-workspace--fullscreen-frame-count) (defvar exwm-workspace--list) (declare-function exwm-workspace--count "exwm-workspace.el") @@ -103,12 +105,15 @@ corresponding monitors whenever the monitors are active. (defun exwm-randr--get-monitors () "Get RandR monitors." + (exwm--log) (let (monitor-name geometry monitor-plist primary-monitor) - (with-slots (monitors) + (with-slots (timestamp monitors) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:randr:GetMonitors :window exwm--root :get-active 1)) + (when (> timestamp exwm-randr--last-timestamp) + (setq exwm-randr--last-timestamp timestamp)) (dolist (monitor monitors) (with-slots (name primary x y width height) monitor (setq monitor-name (x-get-atom-name name) @@ -129,6 +134,7 @@ corresponding monitors whenever the monitors are active. (defun exwm-randr-refresh () "Refresh workspaces according to the updated RandR info." (interactive) + (exwm--log) (let* ((result (exwm-randr--get-monitors)) (primary-monitor (elt result 0)) (monitor-plist (elt result 1)) @@ -187,9 +193,42 @@ corresponding monitors whenever the monitors are active. "27.1") (defun exwm-randr--on-ScreenChangeNotify (_data _synthetic) + "Handle `ScreenChangeNotify' event. + +Run `exwm-randr-screen-change-hook' (usually user scripts to configure RandR)." + (exwm--log) + (run-hooks 'exwm-randr-screen-change-hook)) + +(defun exwm-randr--on-Notify (data _synthetic) + "Handle `CrtcChangeNotify' and `OutputChangeNotify' events. + +Refresh when any CRTC/output changes." + (exwm--log) + (let ((evt (make-instance 'xcb:randr:Notify)) + notify) + (xcb:unmarshal evt data) + (with-slots (subCode u) evt + (cl-case subCode + (xcb:randr:Notify:CrtcChange + (setq notify (slot-value u 'cc))) + (xcb:randr:Notify:OutputChange + (setq notify (slot-value u 'oc)))) + (when notify + (with-slots (timestamp) notify + (when (> timestamp exwm-randr--last-timestamp) + (exwm-randr-refresh) + (setq exwm-randr--last-timestamp timestamp))))))) + +(defun exwm-randr--on-ConfigureNotify (data _synthetic) + "Handle `ConfigureNotify' event. + +Refresh when any RandR 1.5 monitor changes." (exwm--log) - (run-hooks 'exwm-randr-screen-change-hook) - (exwm-randr--refresh)) + (let ((evt (make-instance 'xcb:ConfigureNotify))) + (xcb:unmarshal evt data) + (with-slots (window) evt + (when (eq window exwm--root) + (exwm-randr-refresh))))) (defun exwm-randr--init () "Initialize RandR extension and EXWM RandR module." @@ -205,15 +244,23 @@ corresponding monitors whenever the monitors are active. major-version minor-version) ;; External monitor(s) may already be connected. (run-hooks 'exwm-randr-screen-change-hook) - (exwm-randr--refresh) + (exwm-randr-refresh) + ;; Listen for `ScreenChangeNotify' to notify external tools to + ;; configure RandR and `CrtcChangeNotify/OutputChangeNotify' to + ;; refresh the workspace layout. (xcb:+event exwm--connection 'xcb:randr:ScreenChangeNotify #'exwm-randr--on-ScreenChangeNotify) + (xcb:+event exwm--connection 'xcb:randr:Notify #'exwm-randr--on-Notify) + (xcb:+event exwm--connection 'xcb:ConfigureNotify + #'exwm-randr--on-ConfigureNotify) (xcb:+request exwm--connection (make-instance 'xcb:randr:SelectInput :window exwm--root - :enable xcb:randr:NotifyMask:ScreenChange)) + :enable (logior xcb:randr:NotifyMask:ScreenChange + xcb:randr:NotifyMask:CrtcChange + xcb:randr:NotifyMask:OutputChange))) (xcb:flush exwm--connection) - (add-hook 'exwm-workspace-list-change-hook #'exwm-randr--refresh)))) + (add-hook 'exwm-workspace-list-change-hook #'exwm-randr-refresh)))) ;; Prevent frame parameters introduced by this module from being ;; saved/restored. (dolist (i '(exwm-randr-monitor)) @@ -222,7 +269,7 @@ corresponding monitors whenever the monitors are active. (defun exwm-randr--exit () "Exit the RandR module." - (remove-hook 'exwm-workspace-list-change-hook #'exwm-randr--refresh)) + (remove-hook 'exwm-workspace-list-change-hook #'exwm-randr-refresh)) (defun exwm-randr-enable () "Enable RandR support for EXWM." -- cgit 1.4.1 From dc3b86d1b88c725fbba9685c38f09970dfe73d73 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 18 Nov 2018 00:00:00 +0000 Subject: Add support for 'managed' per-application configuration * exwm-manage.el (exwm-manage-configurations): Add a new value type 'managed' to allow users to specify whether a certain X window should be managed or not. (exwm-manage--manage-window): Use it. --- exwm-manage.el | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index d7b5c3662e..365b94d55c 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -70,6 +70,7 @@ You can still make the X windows floating afterwards." (const :tag "Prefix keys" prefix-keys) (const :tag "Simulation keys" simulation-keys) (const :tag "Workspace" workspace) + (const :tag "Managed" managed) ;; For forward compatibility. (other)) :value-type (sexp :tag "Value" nil)))) @@ -214,22 +215,31 @@ You can still make the X windows floating afterwards." (exwm--update-hints id) (exwm-manage--update-geometry id) (exwm-manage--update-mwm-hints id) - ;; No need to manage (please check OverrideRedirect outside) - (when (or - (not - (or (not exwm-window-type) - (memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY exwm-window-type) - (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG exwm-window-type) - (memq xcb:Atom:_NET_WM_WINDOW_TYPE_NORMAL exwm-window-type))) - ;; Check the _MOTIF_WM_HINTS property. - (and (not exwm--mwm-hints-decorations) - (not exwm--hints-input) - ;; Floating windows only - (or exwm-transient-for exwm--fixed-size - (memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY - exwm-window-type) - (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG - exwm-window-type)))) + (setq exwm--configurations (exwm-manage--get-configurations)) + ;; OverrideRedirect is not checked here. + (when (and + ;; The user has specified to manage it. + (not (plist-get exwm--configurations 'managed)) + (or + ;; The user has specified not to manage it. + (plist-member exwm--configurations 'managed) + ;; This is not a type of X window we can manage. + (and exwm-window-type + (not (cl-intersection + exwm-window-type + (list xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY + xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG + xcb:Atom:_NET_WM_WINDOW_TYPE_NORMAL)))) + ;; Check the _MOTIF_WM_HINTS property to not manage floating X + ;; windows without decoration. + (and (not exwm--mwm-hints-decorations) + (not exwm--hints-input) + ;; Floating windows only + (or exwm-transient-for exwm--fixed-size + (memq xcb:Atom:_NET_WM_WINDOW_TYPE_UTILITY + exwm-window-type) + (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DIALOG + exwm-window-type))))) (exwm--log "No need to manage #x%x" id) ;; Update struts. (when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DOCK exwm-window-type) @@ -277,7 +287,6 @@ You can still make the X windows floating afterwards." (let ((kill-buffer-query-functions nil)) (kill-buffer (current-buffer))) (throw 'return 'ignored)) - (setq exwm--configurations (exwm-manage--get-configurations)) (let ((index (plist-get exwm--configurations 'workspace))) (when (and index (< index (length exwm-workspace--list))) (setq exwm--frame (elt exwm-workspace--list index)))) -- cgit 1.4.1 From 786c2b4f7d2e92f77746918b442419fa3b5794e5 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 18 Nov 2018 00:00:00 +0000 Subject: Fallback to the first monitor as the primary one * exwm-randr.el (exwm-randr--get-monitors): When no primary monitor is specified, pick the first one. --- exwm-randr.el | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/exwm-randr.el b/exwm-randr.el index 106ed6f83a..f488bd9ded 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -123,8 +123,9 @@ corresponding monitors whenever the monitors are active. :width width :height height) monitor-plist (plist-put monitor-plist monitor-name geometry)) - ;; Save primary monitor when available. - (when (/= 0 primary) + ;; Save primary monitor when available (fallback to the first one). + (when (or (/= 0 primary) + (not primary-monitor)) (setq primary-monitor monitor-name))))) (exwm--log "Primary monitor: %s" primary-monitor) (exwm--log "Monitors: %s" monitor-plist) -- cgit 1.4.1 From 38e343ff076e1a00826986b302e389e3f2873cbd Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 25 Nov 2018 00:00:00 +0000 Subject: Avoid unnecessary keyboard grab/ungrab in char-mode * exwm-input.el (exwm-input--cache-event): Optimize the handling of single event global key by not grabbing/ungrabbing the keyboard. --- exwm-input.el | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index c757b09d3f..e184b3948a 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -565,7 +565,7 @@ instead." (cl-return-from exwm-input--translate translation))))) key) -(defun exwm-input--cache-event (event) +(defun exwm-input--cache-event (event &optional temp-line-mode) "Cache EVENT." (setq exwm-input--line-mode-cache (vconcat exwm-input--line-mode-cache (vector event))) @@ -575,8 +575,12 @@ instead." ;; When the key sequence is complete (not a keymap). ;; Note that `exwm-input--line-mode-cache' might get translated to nil, for ;; example 'mouse--down-1-maybe-follows-link' does this. - (unless (and exwm-input--line-mode-cache - (keymapp (key-binding exwm-input--line-mode-cache))) + (if (and exwm-input--line-mode-cache + (keymapp (key-binding exwm-input--line-mode-cache))) + ;; Grab keyboard temporarily to intercept the complete key sequence. + (when temp-line-mode + (setq exwm-input--temp-line-mode t) + (exwm-input--grab-keyboard)) (setq exwm-input--line-mode-cache nil) (when exwm-input--temp-line-mode (setq exwm-input--temp-line-mode nil) @@ -652,10 +656,7 @@ Current buffer must be an `exwm-mode' buffer." (setq event (exwm-input--mimic-read-event raw-event))) (if (not (derived-mode-p 'exwm-mode)) (exwm-input--unread-event raw-event) - ;; Grab keyboard temporarily. - (setq exwm-input--temp-line-mode t) - (exwm-input--grab-keyboard) - (exwm-input--cache-event event) + (exwm-input--cache-event event t) (exwm-input--unread-event raw-event))))) (xcb:+request exwm--connection (make-instance 'xcb:AllowEvents -- cgit 1.4.1 From fe8274ca7ed6b5bbb397fbe93158ad41b13f5577 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 1 Dec 2018 00:00:00 +0000 Subject: Bump version to 0.21 --- exwm.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm.el b/exwm.el index 98b30f9495..e25506237f 100644 --- a/exwm.el +++ b/exwm.el @@ -4,7 +4,7 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0.20 +;; Version: 0.21 ;; Package-Requires: ((xelb "0.16")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From 58f7916619d11a8a4ad5d0bb926e7281054dd964 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 2 Dec 2018 00:00:00 +0000 Subject: ; Improve debug logs. --- exwm-floating.el | 11 ++++++++++- exwm-input.el | 17 ++++++++++++++++- exwm-layout.el | 3 +++ exwm-manage.el | 7 ++++--- exwm-randr.el | 5 ++++- exwm-systemtray.el | 26 +++++++++++++++++++------- exwm-workspace.el | 31 +++++++++++++++++++++++++++++-- exwm.el | 36 +++++++++++++++++++++++++++++++----- 8 files changed, 116 insertions(+), 20 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index aa2f98822a..d5cb53b9c7 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -83,6 +83,7 @@ This is also used by X window containers.") (defun exwm-floating--set-allowed-actions (id tilling) "Set _NET_WM_ALLOWED_ACTIONS." + (exwm--log "#x%x" id) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_ALLOWED_ACTIONS :window id @@ -326,6 +327,7 @@ This is also used by X window containers.") (defun exwm-floating--unset-floating (id) "Make window ID non-floating." + (exwm--log "#x%x" id) (let ((buffer (exwm--id->buffer id))) (with-current-buffer buffer (when exwm--floating-frame @@ -400,6 +402,7 @@ This is also used by X window containers.") (cl-defun exwm-floating-toggle-floating () "Toggle the current window between floating and non-floating states." (interactive) + (exwm--log) (unless (derived-mode-p 'exwm-mode) (cl-return-from exwm-floating-toggle-floating)) (with-current-buffer (window-buffer) @@ -411,6 +414,7 @@ This is also used by X window containers.") (defun exwm-floating-hide () "Hide the current floating X window (which would show again when selected)." (interactive) + (exwm--log) (when (and (derived-mode-p 'exwm-mode) exwm--floating-frame) (exwm-layout--hide exwm--id) @@ -418,6 +422,7 @@ This is also used by X window containers.") (defun exwm-floating--start-moveresize (id &optional type) "Start move/resize." + (exwm--log "#x%x" id) (let ((buffer-or-id (or (exwm--id->buffer id) id)) frame container-or-id x y width height cursor) (if (bufferp buffer-or-id) @@ -581,6 +586,7 @@ This is also used by X window containers.") (defun exwm-floating--stop-moveresize (&rest _args) "Stop move/resize." + (exwm--log) (xcb:+request exwm--connection (make-instance 'xcb:UngrabPointer :time xcb:Time:CurrentTime)) (when exwm-floating--moveresize-calculate @@ -641,6 +647,7 @@ This is also used by X window containers.") "Move a floating window right by DELTA-X pixels and down by DELTA-Y pixels. Both DELTA-X and DELTA-Y default to 1. This command should be bound locally." + (exwm--log "delta-x: %s, delta-y: %s" delta-x delta-y) (unless (and (derived-mode-p 'exwm-mode) exwm--floating-frame) (user-error "[EXWM] `exwm-floating-move' is only for floating X windows")) (unless delta-x (setq delta-x 1)) @@ -663,6 +670,7 @@ Both DELTA-X and DELTA-Y default to 1. This command should be bound locally." (defun exwm-floating--init () "Initialize floating module." + (exwm--log) ;; Check border width. (unless (and (integerp exwm-floating-border-width) (> exwm-floating-border-width 0)) @@ -708,7 +716,8 @@ Both DELTA-X and DELTA-Y default to 1. This command should be bound locally." (xcb:cursor:load-cursor exwm--connection "left_side"))) (defun exwm-floating--exit () - "Exit the floating module.") + "Exit the floating module." + (exwm--log)) diff --git a/exwm-input.el b/exwm-input.el index e184b3948a..b7b55d88f7 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -221,7 +221,7 @@ ARGS are additional arguments to CALLBACK." (with-slots (time root event root-x root-y event-x event-y state) evt (setq buffer (exwm--id->buffer event) window (get-buffer-window buffer t)) - (exwm--log "EnterNotify: buffer=%s; window=%s" buffer window) + (exwm--log "buffer=%s; window=%s" buffer window) (when (and buffer window (not (eq window (selected-window)))) (setq frame (window-frame window) frame-xid (frame-parameter frame 'exwm-id)) @@ -257,6 +257,7 @@ ARGS are additional arguments to CALLBACK." (xcb:flush exwm--connection))))) (defun exwm-input--on-keysyms-update () + (exwm--log) (let ((exwm-input--global-prefix-keys nil)) (exwm-input--update-global-prefix-keys))) @@ -486,6 +487,7 @@ ARGS are additional arguments to CALLBACK." (xcb:flush exwm--connection))) (defun exwm-input--set-key (key command) + (exwm--log "key: %s, command: %s" key command) (global-set-key key command) (cl-pushnew key exwm-input--global-keys)) @@ -567,6 +569,7 @@ instead." (defun exwm-input--cache-event (event &optional temp-line-mode) "Cache EVENT." + (exwm--log "%s" event) (setq exwm-input--line-mode-cache (vconcat exwm-input--line-mode-cache (vector event))) ;; Attempt to translate this key sequence. @@ -673,6 +676,7 @@ The return value is used as event_mode to release the original button event." (with-current-buffer buffer (let ((read-event (exwm-input--mimic-read-event button-event))) + (exwm--log "%s" read-event) (if (and read-event (exwm-input--event-passthrough-p read-event)) ;; The event should be forwarded to emacs @@ -687,10 +691,12 @@ button event." "Handle button events in char-mode. The return value is used as event_mode to release the original button event." + (exwm--log) xcb:Allow:ReplayPointer) (defun exwm-input--update-mode-line (id) "Update the propertized `mode-line-process' for window ID." + (exwm--log "#x%x" id) (let (help-echo cmd mode) (cl-case exwm--input-mode (line-mode @@ -822,6 +828,7 @@ button event." EXWM will prompt for the key to send. This command can be prefixed to send multiple keys." (interactive "p") + (exwm--log) (unless (derived-mode-p 'exwm-mode) (cl-return-from exwm-input-send-next-key)) (when (> times 12) (setq times 12)) @@ -840,6 +847,7 @@ multiple keys." (defun exwm-input--set-simulation-keys (simulation-keys &optional no-refresh) "Set simulation keys." + (exwm--log "%s" simulation-keys) (unless no-refresh ;; Unbind simulation keys. (let ((hash (buffer-local-value 'exwm-input--simulation-keys @@ -941,6 +949,7 @@ ends unless it's specifically saved in the Customize interface for (format "Simulate %s as" (key-description original)) ?\C-g))) (list original simulated))) + (exwm--log "original: %s, simulated: %s" original-key simulated-key) (when (and original-key simulated-key) (let ((entry `((,original-key . ,simulated-key)))) (setq exwm-input-simulation-keys (append exwm-input-simulation-keys @@ -949,6 +958,7 @@ ends unless it's specifically saved in the Customize interface for (defun exwm-input--unset-simulation-keys () "Clear simulation keys and key bindings defined." + (exwm--log) (when (hash-table-p exwm-input--simulation-keys) (maphash (lambda (key _value) (when (sequencep key) @@ -961,6 +971,7 @@ ends unless it's specifically saved in the Customize interface for SIMULATION-KEYS is an alist of the form (original-key . simulated-key), where both ORIGINAL-KEY and SIMULATED-KEY are key sequences." + (exwm--log) (make-local-variable 'exwm-input--simulation-keys) (use-local-map (copy-keymap exwm-mode-map)) (let ((exwm-input--local-simulation-keys t)) @@ -970,6 +981,7 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences." (cl-defun exwm-input-send-simulation-key (times) "Fake a key event according to the last input key sequence." (interactive "p") + (exwm--log) (unless (derived-mode-p 'exwm-mode) (cl-return-from exwm-input-send-simulation-key)) (let ((keys (gethash (this-single-command-keys) @@ -989,6 +1001,7 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences." (defun exwm-input--init () "Initialize the keyboard module." + (exwm--log) ;; Refresh keyboard mapping (xcb:keysyms:init exwm--connection #'exwm-input--on-keysyms-update) ;; Create the X window and intern the atom used to fetch timestamp. @@ -1046,10 +1059,12 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences." (defun exwm-input--post-init () "The second stage in the initialization of the input module." + (exwm--log) (exwm-input--update-global-prefix-keys)) (defun exwm-input--exit () "Exit the input module." + (exwm--log) (exwm-input--unset-simulation-keys) (remove-hook 'pre-command-hook #'exwm-input--on-pre-command) (remove-hook 'post-command-hook #'exwm-input--on-post-command) diff --git a/exwm-layout.el b/exwm-layout.el index b97c2f3ed2..8b2fc2ae0d 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -479,6 +479,7 @@ windows." See also `exwm-layout-enlarge-window'." (interactive "p") + (exwm--log "%s" delta) (exwm-layout-enlarge-window delta t)) ;;;###autoload @@ -487,6 +488,7 @@ See also `exwm-layout-enlarge-window'." See also `exwm-layout-enlarge-window'." (interactive "p") + (exwm--log "%s" delta) (exwm-layout-enlarge-window (- delta))) ;;;###autoload @@ -495,6 +497,7 @@ See also `exwm-layout-enlarge-window'." See also `exwm-layout-enlarge-window'." (interactive "p") + (exwm--log "%s" delta) (exwm-layout-enlarge-window (- delta) t)) ;;;###autoload diff --git a/exwm-manage.el b/exwm-manage.el index 365b94d55c..7000c53a6f 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -565,7 +565,7 @@ Would you like to kill it? " (with-slots (window x y width height border-width sibling stack-mode value-mask) obj - (exwm--log "ConfigureRequest from #x%x (#x%x) @%dx%d%+d%+d; \ + (exwm--log "#x%x (#x%x) @%dx%d%+d%+d; \ border-width: %d; sibling: #x%x; stack-mode: %d" window value-mask width height x y border-width sibling stack-mode) @@ -663,7 +663,7 @@ border-width: %d; sibling: #x%x; stack-mode: %d" (progn (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window window)) (xcb:flush exwm--connection)) - (exwm--log "MapRequest from #x%x" window) + (exwm--log "#x%x" window) (exwm-manage--manage-window window)))))) (defun exwm-manage--on-UnmapNotify (data _synthetic) @@ -705,7 +705,7 @@ border-width: %d; sibling: #x%x; stack-mode: %d" (exwm--log) (let ((obj (make-instance 'xcb:DestroyNotify))) (xcb:unmarshal obj data) - (exwm--log "DestroyNotify from #x%x" (slot-value obj 'window)) + (exwm--log "#x%x" (slot-value obj 'window)) (exwm-manage--unmanage-window (slot-value obj 'window))))) (defun exwm-manage--init () @@ -732,6 +732,7 @@ border-width: %d; sibling: #x%x; stack-mode: %d" (defun exwm-manage--exit () "Exit the manage module." + (exwm--log) (dolist (pair exwm--id-buffer-alist) (exwm-manage--unmanage-window (car pair) 'quit)) (remove-hook 'after-make-frame-functions #'exwm-manage--add-frame) diff --git a/exwm-randr.el b/exwm-randr.el index f488bd9ded..a0fe959a7c 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -123,12 +123,12 @@ corresponding monitors whenever the monitors are active. :width width :height height) monitor-plist (plist-put monitor-plist monitor-name geometry)) + (exwm--log "%s: %sx%s+%s+%s" monitor-name x y width height) ;; Save primary monitor when available (fallback to the first one). (when (or (/= 0 primary) (not primary-monitor)) (setq primary-monitor monitor-name))))) (exwm--log "Primary monitor: %s" primary-monitor) - (exwm--log "Monitors: %s" monitor-plist) (list primary-monitor monitor-plist))) ;;;###autoload @@ -233,6 +233,7 @@ Refresh when any RandR 1.5 monitor changes." (defun exwm-randr--init () "Initialize RandR extension and EXWM RandR module." + (exwm--log) (if (= 0 (slot-value (xcb:get-extension-data exwm--connection 'xcb:randr) 'present)) (error "[EXWM] RandR extension is not supported by the server") @@ -270,10 +271,12 @@ Refresh when any RandR 1.5 monitor changes." (defun exwm-randr--exit () "Exit the RandR module." + (exwm--log) (remove-hook 'exwm-workspace-list-change-hook #'exwm-randr-refresh)) (defun exwm-randr-enable () "Enable RandR support for EXWM." + (exwm--log) (add-hook 'exwm-init-hook #'exwm-randr--init) (add-hook 'exwm-exit-hook #'exwm-randr--exit)) diff --git a/exwm-systemtray.el b/exwm-systemtray.el index d3244ab8d0..ea16733160 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -86,13 +86,13 @@ You shall use the default value if using auto-hide minibuffer." (defun exwm-systemtray--embed (icon) "Embed an icon." - (exwm--log "(System Tray) Try to embed #x%x" icon) + (exwm--log "Try to embed #x%x" icon) (let ((info (xcb:+request-unchecked+reply exwm-systemtray--connection (make-instance 'xcb:xembed:get-_XEMBED_INFO :window icon))) width* height* visible) (when info - (exwm--log "(System Tray) Embed #x%x" icon) + (exwm--log "Embed #x%x" icon) (with-slots (width height) (xcb:+request-unchecked+reply exwm-systemtray--connection (make-instance 'xcb:GetGeometry :drawable icon)) @@ -101,7 +101,7 @@ You shall use the default value if using auto-hide minibuffer." (when (< width* exwm-systemtray--icon-min-size) (setq width* exwm-systemtray--icon-min-size height* (round (* height (/ (float width*) width))))) - (exwm--log "(System Tray) Resize from %dx%d to %dx%d" + (exwm--log "Resize from %dx%d to %dx%d" width height width* height*)) ;; Add this icon to save-set. (xcb:+request exwm-systemtray--connection @@ -151,7 +151,7 @@ You shall use the default value if using auto-hide minibuffer." ;; Default to visible. (setq visible t)) (when visible - (exwm--log "(System Tray) Map the window") + (exwm--log "Map the window") (xcb:+request exwm-systemtray--connection (make-instance 'xcb:MapWindow :window icon))) (xcb:+request exwm-systemtray--connection @@ -175,7 +175,7 @@ You shall use the default value if using auto-hide minibuffer." (defun exwm-systemtray--unembed (icon) "Unembed an icon." - (exwm--log "(System Tray) Unembed #x%x" icon) + (exwm--log "Unembed #x%x" icon) (xcb:+request exwm-systemtray--connection (make-instance 'xcb:UnmapWindow :window icon)) (xcb:+request exwm-systemtray--connection @@ -189,6 +189,7 @@ You shall use the default value if using auto-hide minibuffer." (defun exwm-systemtray--refresh () "Refresh the system tray." + (exwm--log) ;; Make sure to redraw the embedder. (xcb:+request exwm-systemtray--connection (make-instance 'xcb:UnmapWindow @@ -222,6 +223,7 @@ You shall use the default value if using auto-hide minibuffer." (defun exwm-systemtray--on-DestroyNotify (data _synthetic) "Unembed icons on DestroyNotify." + (exwm--log) (let ((obj (make-instance 'xcb:DestroyNotify))) (xcb:unmarshal obj data) (with-slots (window) obj @@ -230,6 +232,7 @@ You shall use the default value if using auto-hide minibuffer." (defun exwm-systemtray--on-ReparentNotify (data _synthetic) "Unembed icons on ReparentNotify." + (exwm--log) (let ((obj (make-instance 'xcb:ReparentNotify))) (xcb:unmarshal obj data) (with-slots (window parent) obj @@ -239,6 +242,7 @@ You shall use the default value if using auto-hide minibuffer." (defun exwm-systemtray--on-ResizeRequest (data _synthetic) "Resize the tray icon on ResizeRequest." + (exwm--log) (let ((obj (make-instance 'xcb:ResizeRequest)) attr) (xcb:unmarshal obj data) @@ -266,6 +270,7 @@ You shall use the default value if using auto-hide minibuffer." (defun exwm-systemtray--on-PropertyNotify (data _synthetic) "Map/Unmap the tray icon on PropertyNotify." + (exwm--log) (let ((obj (make-instance 'xcb:PropertyNotify)) attr info visible) (xcb:unmarshal obj data) @@ -279,7 +284,7 @@ You shall use the default value if using auto-hide minibuffer." (when info (setq visible (/= 0 (logand (slot-value info 'flags) xcb:xembed:MAPPED))) - (exwm--log "(System Tray) #x%x visible? %s" window visible) + (exwm--log "#x%x visible? %s" window visible) (if visible (xcb:+request exwm-systemtray--connection (make-instance 'xcb:MapWindow :window window)) @@ -297,6 +302,7 @@ You shall use the default value if using auto-hide minibuffer." (when (eq type xcb:Atom:_NET_SYSTEM_TRAY_OPCODE) (setq data32 (slot-value data 'data32) opcode (elt data32 1)) + (exwm--log "opcode: %s" opcode) (cond ((= opcode xcb:systemtray:opcode:REQUEST-DOCK) (unless (assoc (elt data32 2) exwm-systemtray--list) (exwm-systemtray--embed (elt data32 2)))) @@ -304,10 +310,11 @@ You shall use the default value if using auto-hide minibuffer." ((or (= opcode xcb:systemtray:opcode:BEGIN-MESSAGE) (= opcode xcb:systemtray:opcode:CANCEL-MESSAGE))) (t - (exwm--log "(System Tray) Unknown opcode message: %s" obj))))))) + (exwm--log "Unknown opcode message: %s" obj))))))) (defun exwm-systemtray--on-KeyPress (data _synthetic) "Forward all KeyPress events to Emacs frame." + (exwm--log) ;; This function is only executed when there's no autohide minibuffer, ;; a workspace frame has the input focus and the pointer is over a ;; tray icon. @@ -325,6 +332,7 @@ You shall use the default value if using auto-hide minibuffer." (defun exwm-systemtray--on-workspace-switch () "Reparent/Refresh the system tray in `exwm-workspace-switch-hook'." + (exwm--log) (unless (exwm-workspace--minibuffer-own-frame-p) (xcb:+request exwm-systemtray--connection (make-instance 'xcb:ReparentWindow @@ -339,6 +347,7 @@ You shall use the default value if using auto-hide minibuffer." (defun exwm-systemtray--on-randr-refresh () "Reposition/Refresh the system tray in `exwm-randr-refresh-hook'." + (exwm--log) (unless (exwm-workspace--minibuffer-own-frame-p) (xcb:+request exwm-systemtray--connection (make-instance 'xcb:ConfigureWindow @@ -353,6 +362,7 @@ You shall use the default value if using auto-hide minibuffer." (cl-defun exwm-systemtray--init () "Initialize system tray module." + (exwm--log) (cl-assert (not exwm-systemtray--connection)) (cl-assert (not exwm-systemtray--list)) (cl-assert (not exwm-systemtray--selection-owner-window)) @@ -493,6 +503,7 @@ You shall use the default value if using auto-hide minibuffer." (defun exwm-systemtray--exit () "Exit the systemtray module." + (exwm--log) (when exwm-systemtray--connection ;; Hide & reparent out the embedder before disconnection to prevent ;; embedded icons from being reparented to an Emacs frame (which is the @@ -521,6 +532,7 @@ You shall use the default value if using auto-hide minibuffer." (defun exwm-systemtray-enable () "Enable system tray support for EXWM." + (exwm--log) (add-hook 'exwm-init-hook #'exwm-systemtray--init) (add-hook 'exwm-exit-hook #'exwm-systemtray--exit)) diff --git a/exwm-workspace.el b/exwm-workspace.el index b8e48b2586..42fbefbf18 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -307,7 +307,8 @@ NIL if FRAME is not a workspace" ;; Make left/top processed first. (push struts* exwm-workspace--struts) (setq exwm-workspace--struts - (append exwm-workspace--struts (list struts*)))))))))) + (append exwm-workspace--struts (list struts*)))))))) + (exwm--log "%s" exwm-workspace--struts))) (defun exwm-workspace--update-workareas () "Update `exwm-workspace--workareas'." @@ -371,6 +372,7 @@ NIL if FRAME is not a workspace" ;; Save the result. (setq exwm-workspace--workareas workareas) (xcb:flush exwm--connection)) + (exwm--log "%s" exwm-workspace--workareas) (run-hooks 'exwm-workspace--update-workareas-hook)) (defun exwm-workspace--set-active (frame active) @@ -451,7 +453,8 @@ NIL if FRAME is not a workspace" :window (frame-parameter exwm-workspace--minibuffer 'exwm-outer-id) :value-mask xcb:ConfigWindow:Width - :width width)))) + :width width)) + (exwm--log "y: %s, width: %s" y width))) (defun exwm-workspace--switch-map-nth-prefix (&optional prefix-digits) "Allow selecting a workspace by number. @@ -647,6 +650,7 @@ Passing a workspace frame as the first option is for internal use only." (t 0)))) (unless frame-or-index (setq frame-or-index 0)) + (exwm--log "%s" frame-or-index) (if (or (framep frame-or-index) (< frame-or-index (exwm-workspace--count))) (exwm-workspace-switch frame-or-index) @@ -747,6 +751,7 @@ before it." INDEX must not exceed the current number of workspaces." (interactive) + (exwm--log "%s" index) (if (and index ;; No need to move if it's the last one. (< index (exwm-workspace--count))) @@ -757,6 +762,7 @@ INDEX must not exceed the current number of workspaces." (defun exwm-workspace-delete (&optional frame-or-index) "Delete the workspace FRAME-OR-INDEX." (interactive) + (exwm--log "%s" frame-or-index) (when (< 1 (exwm-workspace--count)) (delete-frame (if frame-or-index @@ -765,6 +771,7 @@ INDEX must not exceed the current number of workspaces." (defun exwm-workspace--set-desktop (id) "Set _NET_WM_DESKTOP for X window ID." + (exwm--log "#x%x" id) (with-current-buffer (exwm--id->buffer id) (let ((desktop (exwm-workspace--position exwm--frame))) (setq exwm--desktop desktop) @@ -789,6 +796,7 @@ INDEX must not exceed the current number of workspaces." (let ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index)) old-frame container) (unless id (setq id (exwm--buffer->id (window-buffer)))) + (exwm--log "Moving #x%x to %s" id frame-or-index) (with-current-buffer (exwm--id->buffer id) (unless (eq exwm--frame frame) (unless exwm-workspace-show-all-buffers @@ -984,6 +992,7 @@ INDEX must not exceed the current number of workspaces." (defun exwm-workspace--x-create-frame (orig-fun params) "Set override-redirect on the frame created by `x-create-frame'." + (exwm--log) (let ((frame (funcall orig-fun params))) (xcb:+request exwm--connection (make-instance 'xcb:ChangeWindowAttributes @@ -1005,6 +1014,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (defun exwm-workspace-attach-minibuffer () "Attach the minibuffer so that it always shows." (interactive) + (exwm--log) (when (and (exwm-workspace--minibuffer-own-frame-p) (not (exwm-workspace--minibuffer-attached-p))) ;; Reset the frame size. @@ -1029,6 +1039,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (defun exwm-workspace-detach-minibuffer () "Detach the minibuffer so that it automatically hides." (interactive) + (exwm--log) (when (and (exwm-workspace--minibuffer-own-frame-p) (exwm-workspace--minibuffer-attached-p)) (setq exwm-workspace--attached-minibuffer-height 0) @@ -1046,6 +1057,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (defun exwm-workspace-toggle-minibuffer () "Attach the minibuffer if it's detached, or detach it if it's attached." (interactive) + (exwm--log) (when (exwm-workspace--minibuffer-own-frame-p) (if (exwm-workspace--minibuffer-attached-p) (exwm-workspace-detach-minibuffer) @@ -1071,6 +1083,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (when (and (integerp max-mini-window-height) (> height max-mini-window-height)) (setq height max-mini-window-height)) + (exwm--log "%s" height) (set-frame-height exwm-workspace--minibuffer height)))) (defun exwm-workspace--on-ConfigureNotify (data _synthetic) @@ -1081,6 +1094,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (with-slots (window height) obj (when (eq (frame-parameter exwm-workspace--minibuffer 'exwm-outer-id) window) + (exwm--log) (when (and (floatp max-mini-window-height) (> height (* max-mini-window-height (exwm-workspace--current-height)))) @@ -1122,6 +1136,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (defun exwm-workspace--show-minibuffer () "Show the minibuffer frame." + (exwm--log) ;; Cancel pending timer. (when exwm-workspace--display-echo-area-timer (cancel-timer exwm-workspace--display-echo-area-timer) @@ -1143,6 +1158,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (defun exwm-workspace--hide-minibuffer () "Hide the minibuffer frame." + (exwm--log) ;; Hide the minibuffer frame. (if (exwm-workspace--minibuffer-attached-p) (xcb:+request exwm--connection @@ -1164,6 +1180,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (defun exwm-workspace--on-minibuffer-setup () "Run in minibuffer-setup-hook to show the minibuffer and its container." + (exwm--log) (when (and (= 1 (minibuffer-depth)) (not (exwm-workspace--client-p))) (add-hook 'post-command-hook #'exwm-workspace--update-minibuffer-height) @@ -1185,6 +1202,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (defun exwm-workspace--on-minibuffer-exit () "Run in minibuffer-exit-hook to hide the minibuffer container." + (exwm--log) (when (and (= 1 (minibuffer-depth)) (not (exwm-workspace--client-p))) (remove-hook 'post-command-hook #'exwm-workspace--update-minibuffer-height) @@ -1216,6 +1234,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (defun exwm-workspace--set-desktop-geometry () "Set _NET_DESKTOP_GEOMETRY." + (exwm--log) ;; We don't support large desktop so it's the same with screen size. (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_DESKTOP_GEOMETRY @@ -1225,6 +1244,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (defun exwm-workspace--add-frame-as-workspace (frame) "Configure frame FRAME to be treated as a workspace." + (exwm--log "%s" frame) (setq exwm-workspace--list (nconc exwm-workspace--list (list frame))) (let ((outer-id (string-to-number (frame-parameter frame 'outer-window-id))) @@ -1389,6 +1409,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (defun exwm-workspace--update-ewmh-props () "Update EWMH properties to match the workspace list." + (exwm--log) (let ((num-workspaces (exwm-workspace--count))) ;; Avoid setting 0 desktops. (when (= 0 num-workspaces) @@ -1408,6 +1429,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." NEW-X-PARAMETERS is an alist of frame parameters, merged into current `window-system-default-frame-alist' for the X Window System. The parameters are applied to all subsequently created X frames." + (exwm--log) ;; The parameters are modified in place; take current ;; ones or insert a new X-specific list. (let ((x-parameters (or (assq 'x window-system-default-frame-alist) @@ -1427,6 +1449,7 @@ applied to all subsequently created X frames." (interactive "e")) (defun exwm-workspace--init-minibuffer-frame () + (exwm--log) ;; Initialize workspaces without minibuffers. (setq exwm-workspace--minibuffer (make-frame '((window-system . x) (minibuffer . only) @@ -1497,6 +1520,7 @@ applied to all subsequently created X frames." :test #'equal)) (defun exwm-workspace--exit-minibuffer-frame () + (exwm--log) ;; Only on minibuffer-frame. (remove-hook 'minibuffer-setup-hook #'exwm-workspace--on-minibuffer-setup) (remove-hook 'minibuffer-exit-hook #'exwm-workspace--on-minibuffer-exit) @@ -1520,6 +1544,7 @@ applied to all subsequently created X frames." (defun exwm-workspace--init () "Initialize workspace module." + (exwm--log) ;; Prevent unexpected exit (setq exwm-workspace--fullscreen-frame-count 0) (exwm-workspace--modify-all-x-frames-parameters @@ -1584,6 +1609,7 @@ applied to all subsequently created X frames." (defun exwm-workspace--exit () "Exit the workspace module." + (exwm--log) (when (exwm-workspace--minibuffer-own-frame-p) (exwm-workspace--exit-minibuffer-frame)) (advice-remove 'x-create-frame #'exwm-workspace--x-create-frame) @@ -1618,6 +1644,7 @@ applied to all subsequently created X frames." (defun exwm-workspace--post-init () "The second stage in the initialization of the workspace module." + (exwm--log) (when exwm-workspace--client ;; Reset the 'fullscreen' frame parameter to make emacsclinet frames ;; fullscreen (even without the RandR module enabled). diff --git a/exwm.el b/exwm.el index e25506237f..4b59b8b6b5 100644 --- a/exwm.el +++ b/exwm.el @@ -107,6 +107,7 @@ (defun exwm-reset () "Reset the state of the selected window (non-fullscreen, line-mode, etc)." (interactive) + (exwm--log) (with-current-buffer (window-buffer) (when (derived-mode-p 'exwm-mode) (when (exwm-layout--fullscreen-p) @@ -119,6 +120,7 @@ (defun exwm-restart () "Restart EXWM." (interactive) + (exwm--log) (when (exwm--confirm-kill-emacs "[EXWM] Restart? " 'no-check) (let* ((attr (process-attributes (emacs-pid))) (args (cdr (assq 'args attr))) @@ -146,6 +148,7 @@ (defun exwm--update-desktop (xwin) "Update _NET_WM_DESKTOP." + (exwm--log "#x%x" xwin) (with-current-buffer (exwm--id->buffer xwin) (let ((reply (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:ewmh:get-_NET_WM_DESKTOP @@ -172,6 +175,7 @@ (defun exwm--update-window-type (id &optional force) "Update _NET_WM_WINDOW_TYPE." + (exwm--log "#x%x" id) (with-current-buffer (exwm--id->buffer id) (unless (and exwm-window-type (not force)) (let ((reply (xcb:+request-unchecked+reply exwm--connection @@ -182,6 +186,7 @@ (defun exwm--update-class (id &optional force) "Update WM_CLASS." + (exwm--log "#x%x" id) (with-current-buffer (exwm--id->buffer id) (unless (and exwm-instance-name exwm-class-name (not force)) (let ((reply (xcb:+request-unchecked+reply exwm--connection @@ -194,6 +199,7 @@ (defun exwm--update-utf8-title (id &optional force) "Update _NET_WM_NAME." + (exwm--log "#x%x" id) (with-current-buffer (exwm--id->buffer id) (when (or force (not exwm-title)) (let ((reply (xcb:+request-unchecked+reply exwm--connection @@ -206,6 +212,7 @@ (defun exwm--update-ctext-title (id &optional force) "Update WM_NAME." + (exwm--log "#x%x" id) (with-current-buffer (exwm--id->buffer id) (unless (or exwm--title-is-utf8 (and exwm-title (not force))) @@ -218,11 +225,13 @@ (defun exwm--update-title (id) "Update _NET_WM_NAME or WM_NAME." + (exwm--log "#x%x" id) (exwm--update-utf8-title id) (exwm--update-ctext-title id)) (defun exwm--update-transient-for (id &optional force) "Update WM_TRANSIENT_FOR." + (exwm--log "#x%x" id) (with-current-buffer (exwm--id->buffer id) (unless (and exwm-transient-for (not force)) (let ((reply (xcb:+request-unchecked+reply exwm--connection @@ -233,6 +242,7 @@ (defun exwm--update-normal-hints (id &optional force) "Update WM_NORMAL_HINTS." + (exwm--log "#x%x" id) (with-current-buffer (exwm--id->buffer id) (unless (and (not force) (or exwm--normal-hints-x exwm--normal-hints-y @@ -280,6 +290,7 @@ (defun exwm--update-hints (id &optional force) "Update WM_HINTS." + (exwm--log "#x%x" id) (with-current-buffer (exwm--id->buffer id) (unless (and (not force) exwm--hints-input exwm--hints-urgency) (let ((reply (xcb:+request-unchecked+reply exwm--connection @@ -301,6 +312,7 @@ (defun exwm--update-protocols (id &optional force) "Update WM_PROTOCOLS." + (exwm--log "#x%x" id) (with-current-buffer (exwm--id->buffer id) (unless (and exwm--protocols (not force)) (let ((reply (xcb:+request-unchecked+reply exwm--connection @@ -311,6 +323,7 @@ (defun exwm--update-struts-legacy (id) "Update _NET_WM_STRUT." + (exwm--log "#x%x" id) (let ((pair (assq id exwm-workspace--id-struts-alist)) reply struts) (unless (and pair (< 4 (length (cdr pair)))) @@ -331,6 +344,7 @@ (defun exwm--update-struts-partial (id) "Update _NET_WM_STRUT_PARTIAL." + (exwm--log "#x%x" id) (let ((reply (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:ewmh:get-_NET_WM_STRUT_PARTIAL :window id))) @@ -350,6 +364,7 @@ (defun exwm--update-struts (id) "Update _NET_WM_STRUT_PARTIAL or _NET_WM_STRUT." + (exwm--log "#x%x" id) (exwm--update-struts-partial id) (exwm--update-struts-legacy id)) @@ -386,9 +401,10 @@ ((= atom xcb:Atom:WM_PROTOCOLS) (exwm--update-protocols id t)) ((= atom xcb:Atom:_NET_WM_USER_TIME)) ;ignored - (t (exwm--log "Unhandled PropertyNotify: %s(%d)" - (x-get-atom-name atom exwm-workspace--current) - atom))))))) + (t + (exwm--log "Unhandled: %s(%d)" + (x-get-atom-name atom exwm-workspace--current) + atom))))))) (defun exwm--on-ClientMessage (raw-data _synthetic) "Handle ClientMessage event." @@ -534,11 +550,13 @@ (= (elt data 0) xcb:icccm:WM_STATE:IconicState)) (with-current-buffer buffer (bury-buffer))))) - (t (exwm--log "Unhandled client message: %s" obj))))) + (t + (exwm--log "Unhandled: %s(%d)" + (x-get-atom-name type exwm-workspace--current) type))))) (defun exwm--on-SelectionClear (data _synthetic) "Handle SelectionClear events." - (exwm--log "SelectionClear") + (exwm--log) (let ((obj (make-instance 'xcb:SelectionClear)) owner selection) (xcb:unmarshal obj data) @@ -550,6 +568,7 @@ (defun exwm--init-icccm-ewmh () "Initialize ICCCM/EWMH support." + (exwm--log) ;; Handle PropertyNotify event (xcb:+event exwm--connection 'xcb:PropertyNotify #'exwm--on-PropertyNotify) ;; Handle relevant client messages @@ -693,6 +712,7 @@ REPLACE specifies what to do in case there already is a window manager. If t, replace it, if nil, abort and ask the user if `ask'." + (exwm--log "%s" replace) (with-slots (owner) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GetSelectionOwner @@ -767,6 +787,7 @@ manager. If t, replace it, if nil, abort and ask the user if `ask'." (cl-defun exwm-init (&optional frame) "Initialize EXWM." (interactive) + (exwm--log "%s" frame) (if frame ;; The frame might not be selected if it's created by emacslicnet. (select-frame-set-input-focus frame) @@ -825,6 +846,7 @@ manager. If t, replace it, if nil, abort and ask the user if `ask'." (defun exwm-exit () "Exit EXWM." (interactive) + (exwm--log) (run-hooks 'exwm-exit-hook) (setq confirm-kill-emacs nil) ;; Exit modules. @@ -840,6 +862,7 @@ manager. If t, replace it, if nil, abort and ask the user if `ask'." (defun exwm-enable (&optional undo) "Enable/Disable EXWM." + (exwm--log "%s" undo) (pcase undo (`undo ;prevent reinitialization (remove-hook 'window-setup-hook #'exwm-init) @@ -866,6 +889,7 @@ manager. If t, replace it, if nil, abort and ask the user if `ask'." (defun exwm--server-stop () "Stop the subordinate Emacs server." + (exwm--log) (server-force-delete exwm--server-name) (when exwm--server-process (delete-process exwm--server-process) @@ -874,6 +898,7 @@ manager. If t, replace it, if nil, abort and ask the user if `ask'." (defun exwm--server-eval-at (&rest args) "Wrapper of `server-eval-at' used to advice subrs." ;; Start the subordinate Emacs server if it's not alive + (exwm--log "%s" args) (unless (server-running-p exwm--server-name) (when exwm--server-process (delete-process exwm--server-process)) (setq exwm--server-process @@ -912,6 +937,7 @@ manager. If t, replace it, if nil, abort and ask the user if `ask'." (defun exwm--confirm-kill-emacs (prompt &optional force) "Confirm before exiting Emacs." + (exwm--log) (when (cond ((and force (not (eq force 'no-check))) ;; Force killing Emacs. -- cgit 1.4.1 From e6892216a6851307c822a71841ad2dc986facb77 Mon Sep 17 00:00:00 2001 From: James Ferguson Date: Mon, 3 Dec 2018 11:25:11 -0500 Subject: Only call exwm-randr-screen-change-hook on new event sequence number Multiple event callbacks are triggered per physical monitor plug event. This de-duplicates the events triggering the running of the hook. --- exwm-randr.el | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/exwm-randr.el b/exwm-randr.el index a0fe959a7c..59b291261f 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -95,6 +95,8 @@ corresponding monitors whenever the monitors are active. (defvar exwm-workspace--fullscreen-frame-count) (defvar exwm-workspace--list) +(defvar exwm-randr--prev-screen-change-seqnum nil + "The most recent ScreenChangeNotify sequence number.") (declare-function exwm-workspace--count "exwm-workspace.el") (declare-function exwm-workspace--set-active "exwm-workspace.el" (frame active)) @@ -193,12 +195,17 @@ corresponding monitors whenever the monitors are active. (define-obsolete-function-alias 'exwm-randr--refresh #'exwm-randr-refresh "27.1") -(defun exwm-randr--on-ScreenChangeNotify (_data _synthetic) +(defun exwm-randr--on-ScreenChangeNotify (data _synthetic) "Handle `ScreenChangeNotify' event. Run `exwm-randr-screen-change-hook' (usually user scripts to configure RandR)." (exwm--log) - (run-hooks 'exwm-randr-screen-change-hook)) + (let ((evt (make-instance 'xcb:randr:ScreenChangeNotify))) + (xcb:unmarshal evt data) + (let ((seqnum (slot-value evt '~sequence))) + (unless (equal seqnum exwm-randr--prev-screen-change-seqnum) + (setq exwm-randr--prev-screen-change-seqnum seqnum) + (run-hooks 'exwm-randr-screen-change-hook))))) (defun exwm-randr--on-Notify (data _synthetic) "Handle `CrtcChangeNotify' and `OutputChangeNotify' events. -- cgit 1.4.1 From 2efd7495d9755e12a611dbf27dac6057287cd590 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 16 Dec 2018 00:00:00 +0000 Subject: Avoid unnecessary focus changes when creating/killing buffers * exwm-input.el (exwm-input--skip-buffer-list-update): New internal variable for skipping the next 'buffer-list-update'. (exwm-input--on-buffer-list-update): Skip when `exwm-input--skip-buffer-list-update` is set. * exwm-manage.el (exwm-manage--manage-window): Set `exwm-input--skip-buffer-list-update` when creating/killing buffers. --- exwm-input.el | 4 ++++ exwm-manage.el | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index b7b55d88f7..39fcbadec8 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -117,6 +117,9 @@ defined in `exwm-mode-map' here." (defvar exwm-input--simulation-keys nil "Simulation keys in line-mode.") +(defvar exwm-input--skip-buffer-list-update nil + "Skip the upcoming 'buffer-list-update'.") + (defvar exwm-input--temp-line-mode nil "Non-nil indicates it's in temporary line-mode for char-mode.") @@ -264,6 +267,7 @@ ARGS are additional arguments to CALLBACK." (defun exwm-input--on-buffer-list-update () "Run in `buffer-list-update-hook' to track input focus." (when (and (not (eq this-command #'handle-switch-frame)) + (not exwm-input--skip-buffer-list-update) (not (exwm-workspace--client-p)) ;; The following conditions filter out events relating to temp ;; buffers. diff --git a/exwm-manage.el b/exwm-manage.el index 7000c53a6f..6bb26f228d 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -91,6 +91,7 @@ You can still make the X windows floating afterwards." (defvar exwm-manage--ping-lock nil "Non-nil indicates EXWM is pinging a window.") +(defvar exwm-input--skip-buffer-list-update) (defvar exwm-input-prefix-keys) (defvar exwm-workspace--current) (defvar exwm-workspace--id-struts-alist) @@ -201,7 +202,8 @@ You can still make the X windows floating afterwards." (make-instance 'xcb:ChangeSaveSet :mode xcb:SetMode:Insert :window id)) - (with-current-buffer (generate-new-buffer "*EXWM*") + (with-current-buffer (let ((exwm-input--skip-buffer-list-update t)) + (generate-new-buffer "*EXWM*")) ;; Keep the oldest X window first. (setq exwm--id-buffer-alist (nconc exwm--id-buffer-alist `((,id . ,(current-buffer))))) @@ -284,7 +286,8 @@ You can still make the X windows floating afterwards." :stack-mode xcb:StackMode:Below))) (xcb:flush exwm--connection) (setq exwm--id-buffer-alist (assq-delete-all id exwm--id-buffer-alist)) - (let ((kill-buffer-query-functions nil)) + (let ((kill-buffer-query-functions nil) + (exwm-input--skip-buffer-list-update t)) (kill-buffer (current-buffer))) (throw 'return 'ignored)) (let ((index (plist-get exwm--configurations 'workspace))) -- cgit 1.4.1 From 404c94568d581fb66fca5e2524c908d631883884 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 30 Dec 2018 00:00:00 +0000 Subject: Automatically iconify floating X windows * exwm-layout.el (exwm-layout-auto-iconify): New user option to specify whether to automatically iconify X windows. (exwm-layout--auto-iconify): Automatically iconify floating X windows when its main X window (if any) is iconified. (exwm-layout--show, exwm-layout--hide): Use it. --- exwm-layout.el | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/exwm-layout.el b/exwm-layout.el index 8b2fc2ae0d..6c9f0ff7d8 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -32,6 +32,10 @@ :version "25.3" :group 'exwm) +(defcustom exwm-layout-auto-iconify t + "Non-nil to automatically iconify unused X windows when possible." + :type 'boolean) + (defcustom exwm-layout-show-all-buffers nil "Non-nil to allow switching to buffers on other workspaces." :type 'boolean) @@ -76,6 +80,20 @@ (when (derived-mode-p 'exwm-mode) (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state))) +(defun exwm-layout--auto-iconify () + (when (and exwm-layout-auto-iconify + (not exwm-transient-for)) + (let ((xwin exwm--id) + (state exwm-state)) + (dolist (pair exwm--id-buffer-alist) + (with-current-buffer (cdr pair) + (when (and exwm--floating-frame + (eq exwm-transient-for xwin) + (not (eq exwm-state state))) + (if (eq state xcb:icccm:WM_STATE:NormalState) + (exwm-layout--refresh-floating exwm--floating-frame) + (exwm-layout--hide exwm--id)))))))) + (defun exwm-layout--show (id &optional window) "Show window ID exactly fit in the Emacs window WINDOW." (exwm--log "Show #x%x in %s" id window) @@ -111,7 +129,8 @@ height height*))) (exwm--set-geometry id x y width height) (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window id)) - (exwm-layout--set-state id xcb:icccm:WM_STATE:NormalState))) + (exwm-layout--set-state id xcb:icccm:WM_STATE:NormalState) + (exwm-layout--auto-iconify))) (xcb:flush exwm--connection)) (defun exwm-layout--hide (id) @@ -141,6 +160,7 @@ :window id :value-mask xcb:CW:EventMask :event-mask exwm--client-event-mask)) (exwm-layout--set-state id xcb:icccm:WM_STATE:IconicState) + (exwm-layout--auto-iconify) (xcb:flush exwm--connection)))) ;;;###autoload -- cgit 1.4.1 From 4f854e9fffe540da8acca9bee6a340754ff86810 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 20 Jan 2019 00:00:00 +0000 Subject: Fix issue with managed tray icons * exwm-input.el (exwm-input--on-ButtonPress): Replay button events destined for managed tray icons. --- exwm-input.el | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 39fcbadec8..a950d42236 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -382,6 +382,7 @@ ARGS are additional arguments to CALLBACK." buffer (exwm--id->buffer event) window (get-buffer-window buffer t)) (cond ((and (eq button-event exwm-input-move-event) + buffer ;; Either an undecorated or a floating X window. (with-current-buffer buffer (or (not (derived-mode-p 'exwm-mode)) @@ -390,12 +391,13 @@ ARGS are additional arguments to CALLBACK." (exwm-floating--start-moveresize event xcb:ewmh:_NET_WM_MOVERESIZE_MOVE)) ((and (eq button-event exwm-input-resize-event) + buffer (with-current-buffer buffer (or (not (derived-mode-p 'exwm-mode)) exwm--floating-frame))) ;; Resize (exwm-floating--start-moveresize event)) - (t + (buffer ;; Click to focus (unless (eq window (selected-window)) (setq frame (window-frame window)) @@ -414,13 +416,18 @@ ARGS are additional arguments to CALLBACK." (select-window window) (setq window (get-buffer-window buffer t)) (when window (select-window window)))) + ;; Also process keybindings. (with-current-buffer buffer (when (derived-mode-p 'exwm-mode) (cl-case exwm--input-mode (line-mode - (setq mode (exwm-input--on-ButtonPress-line-mode buffer button-event))) + (setq mode (exwm-input--on-ButtonPress-line-mode + buffer button-event))) (char-mode - (setq mode (exwm-input--on-ButtonPress-char-mode))))))))) + (setq mode (exwm-input--on-ButtonPress-char-mode))))))) + (t + ;; Replay this event by default. + (setq mode xcb:Allow:ReplayPointer)))) (xcb:+request exwm--connection (make-instance 'xcb:AllowEvents :mode mode :time xcb:Time:CurrentTime)) (xcb:flush exwm--connection))) -- cgit 1.4.1 From 993ca8a13a82c93851b9bd4f023a85b06f530782 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 1 Feb 2019 00:00:00 +0000 Subject: Update copyright year to 2019 --- exwm-cm.el | 2 +- exwm-config.el | 2 +- exwm-core.el | 2 +- exwm-floating.el | 2 +- exwm-input.el | 2 +- exwm-layout.el | 2 +- exwm-manage.el | 2 +- exwm-randr.el | 2 +- exwm-systemtray.el | 2 +- exwm-workspace.el | 2 +- exwm.el | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/exwm-cm.el b/exwm-cm.el index ff556fb219..1c33f875a6 100644 --- a/exwm-cm.el +++ b/exwm-cm.el @@ -1,6 +1,6 @@ ;;; exwm-cm.el --- Compositing Manager for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2016-2018 Free Software Foundation, Inc. +;; Copyright (C) 2016-2019 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-config.el b/exwm-config.el index 89320bc1e6..50f13664c4 100644 --- a/exwm-config.el +++ b/exwm-config.el @@ -1,6 +1,6 @@ ;;; exwm-config.el --- Predefined configurations -*- lexical-binding: t -*- -;; Copyright (C) 2015-2018 Free Software Foundation, Inc. +;; Copyright (C) 2015-2019 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-core.el b/exwm-core.el index 1f939491c5..e13a319bf3 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -1,6 +1,6 @@ ;;; exwm-core.el --- Core definitions -*- lexical-binding: t -*- -;; Copyright (C) 2015-2018 Free Software Foundation, Inc. +;; Copyright (C) 2015-2019 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-floating.el b/exwm-floating.el index d5cb53b9c7..b7430d719e 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -1,6 +1,6 @@ ;;; exwm-floating.el --- Floating Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015-2018 Free Software Foundation, Inc. +;; Copyright (C) 2015-2019 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-input.el b/exwm-input.el index a950d42236..a44bd9334a 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -1,6 +1,6 @@ ;;; exwm-input.el --- Input Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015-2018 Free Software Foundation, Inc. +;; Copyright (C) 2015-2019 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-layout.el b/exwm-layout.el index 6c9f0ff7d8..52fd08e152 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -1,6 +1,6 @@ ;;; exwm-layout.el --- Layout Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015-2018 Free Software Foundation, Inc. +;; Copyright (C) 2015-2019 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-manage.el b/exwm-manage.el index 6bb26f228d..20546bd044 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -1,7 +1,7 @@ ;;; exwm-manage.el --- Window Management Module for -*- lexical-binding: t -*- ;;; EXWM -;; Copyright (C) 2015-2018 Free Software Foundation, Inc. +;; Copyright (C) 2015-2019 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-randr.el b/exwm-randr.el index 59b291261f..4338152d7b 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -1,6 +1,6 @@ ;;; exwm-randr.el --- RandR Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015-2018 Free Software Foundation, Inc. +;; Copyright (C) 2015-2019 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-systemtray.el b/exwm-systemtray.el index ea16733160..0ff4efcd8d 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -1,7 +1,7 @@ ;;; exwm-systemtray.el --- System Tray Module for -*- lexical-binding: t -*- ;;; EXWM -;; Copyright (C) 2016-2018 Free Software Foundation, Inc. +;; Copyright (C) 2016-2019 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-workspace.el b/exwm-workspace.el index 42fbefbf18..5a84ec9f00 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1,6 +1,6 @@ ;;; exwm-workspace.el --- Workspace Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015-2018 Free Software Foundation, Inc. +;; Copyright (C) 2015-2019 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm.el b/exwm.el index 4b59b8b6b5..6f6458da7c 100644 --- a/exwm.el +++ b/exwm.el @@ -1,6 +1,6 @@ ;;; exwm.el --- Emacs X Window Manager -*- lexical-binding: t -*- -;; Copyright (C) 2015-2018 Free Software Foundation, Inc. +;; Copyright (C) 2015-2019 Free Software Foundation, Inc. ;; Author: Chris Feng ;; Maintainer: Chris Feng -- cgit 1.4.1 From 65d371d55f22109a6ab886df4c7dfcdb4b893e6c Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 1 Feb 2019 00:00:00 +0000 Subject: Fetch necessary properties before checking per-app configurations * exwm-manage.el (exwm-manage--manage-window): Fetch X window title & protocols so they can be available when checking per-application configurations. --- exwm-manage.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index 20546bd044..759ec0b776 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -217,6 +217,8 @@ You can still make the X windows floating afterwards." (exwm--update-hints id) (exwm-manage--update-geometry id) (exwm-manage--update-mwm-hints id) + (exwm--update-title id) + (exwm--update-protocols id) (setq exwm--configurations (exwm-manage--get-configurations)) ;; OverrideRedirect is not checked here. (when (and @@ -311,8 +313,6 @@ You can still make the X windows floating afterwards." :button button :modifiers xcb:ModMask:Any))) (exwm-manage--set-client-list) (xcb:flush exwm--connection) - (exwm--update-title id) - (exwm--update-protocols id) (if (plist-member exwm--configurations 'floating) ;; User has specified whether it should be floating. (if (plist-get exwm--configurations 'floating) -- cgit 1.4.1 From 44629818bacf3e3a10a37e570fdee2e963dbcc7b Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Tue, 5 Feb 2019 00:00:00 +0000 Subject: Scan for existing X windows only after running `exwm-init-hook' * exwm.el (exwm-init): Delay the call to `exwm-manage--scan' since managing existing X windows too early may result in issues like losing input focus. --- exwm.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm.el b/exwm.el index 6f6458da7c..7affebfff2 100644 --- a/exwm.el +++ b/exwm.el @@ -833,9 +833,9 @@ manager. If t, replace it, if nil, abort and ask the user if `ask'." (exwm--unlock) (exwm-workspace--post-init) (exwm-input--post-init) + (run-hooks 'exwm-init-hook) ;; Manage existing windows - (exwm-manage--scan) - (run-hooks 'exwm-init-hook)) + (exwm-manage--scan)) ((quit error) (exwm-exit) ;; Rethrow error -- cgit 1.4.1 From ec108a61dc38f84a3b290f022929331e97784842 Mon Sep 17 00:00:00 2001 From: Ian Eure Date: Fri, 1 Feb 2019 16:55:24 -0800 Subject: Cleanup simulation key config issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * exwm-input.el (exwm-input-simulation-keys): Original key only has one option, so probably shouldn’t be a `choice` type; Move the "User-defined" key value to the top, since that’s the one someone is most likely to want). * exwm-config.el (exwm-config-default): Only set custom vars if there isn’t a saved value for them. --- exwm-config.el | 26 ++++++++++++++------------ exwm-input.el | 8 ++++---- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/exwm-config.el b/exwm-config.el index 50f13664c4..8223633c45 100644 --- a/exwm-config.el +++ b/exwm-config.el @@ -31,7 +31,8 @@ (defun exwm-config-default () "Default configuration of EXWM." ;; Set the initial workspace number. - (setq exwm-workspace-number 4) + (unless (get 'exwm-workspace-number 'saved-value) + (setq exwm-workspace-number 4)) ;; Make class name the buffer name (add-hook 'exwm-update-class-hook (lambda () @@ -52,17 +53,18 @@ (interactive (list (read-shell-command "$ "))) (start-process-shell-command command nil command))) ;; Line-editing shortcuts - (setq exwm-input-simulation-keys - '(([?\C-b] . [left]) - ([?\C-f] . [right]) - ([?\C-p] . [up]) - ([?\C-n] . [down]) - ([?\C-a] . [home]) - ([?\C-e] . [end]) - ([?\M-v] . [prior]) - ([?\C-v] . [next]) - ([?\C-d] . [delete]) - ([?\C-k] . [S-end delete]))) + (unless (get 'exwm-input-simulation-keys 'saved-value) + (setq exwm-input-simulation-keys + '(([?\C-b] . [left]) + ([?\C-f] . [right]) + ([?\C-p] . [up]) + ([?\C-n] . [down]) + ([?\C-a] . [home]) + ([?\C-e] . [end]) + ([?\M-v] . [prior]) + ([?\C-v] . [next]) + ([?\C-d] . [delete]) + ([?\C-k] . [S-end delete])))) ;; Enable EXWM (exwm-enable) ;; Configure Ido diff --git a/exwm-input.el b/exwm-input.el index 4f64d35dd6..04020acfaf 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -915,8 +915,9 @@ Notes: * The predefined examples in the Customize interface are not guaranteed to work for all applications. This can be tweaked on a per application basis with `exwm-input-set-local-simulation-keys'." - :type '(alist :key-type (choice (key-sequence :tag "Original")) - :value-type (choice (key-sequence :tag "Move left" [left]) + :type '(alist :key-type (key-sequence :tag "Original") + :value-type (choice (key-sequence :tag "User-defined") + (key-sequence :tag "Move left" [left]) (key-sequence :tag "Move right" [right]) (key-sequence :tag "Move up" [up]) (key-sequence :tag "Move down" [down]) @@ -928,8 +929,7 @@ Notes: (key-sequence :tag "Paste" [C-v]) (key-sequence :tag "Delete" [delete]) (key-sequence :tag "Delete to EOL" - [S-end delete]) - (key-sequence :tag "User-defined"))) + [S-end delete]))) :set (lambda (symbol value) (set symbol value) (exwm-input--set-simulation-keys value))) -- cgit 1.4.1 From 97b1fb7c13ad7505ffe144f15875b366f1619f29 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 6 Feb 2019 00:00:00 +0000 Subject: Do the same for `exwm-input-global-keys' --- exwm-config.el | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/exwm-config.el b/exwm-config.el index 8223633c45..6635e4345d 100644 --- a/exwm-config.el +++ b/exwm-config.el @@ -37,21 +37,25 @@ (add-hook 'exwm-update-class-hook (lambda () (exwm-workspace-rename-buffer exwm-class-name))) - ;; 's-r': Reset - (exwm-input-set-key (kbd "s-r") #'exwm-reset) - ;; 's-w': Switch workspace - (exwm-input-set-key (kbd "s-w") #'exwm-workspace-switch) - ;; 's-N': Switch to certain workspace - (dotimes (i 10) - (exwm-input-set-key (kbd (format "s-%d" i)) - `(lambda () - (interactive) - (exwm-workspace-switch-create ,i)))) - ;; 's-&': Launch application - (exwm-input-set-key (kbd "s-&") - (lambda (command) - (interactive (list (read-shell-command "$ "))) - (start-process-shell-command command nil command))) + ;; Global keybindings. + (unless (get 'exwm-input-global-keys 'saved-value) + (setq exwm-input-global-keys + `( + ;; 's-r': Reset (to line-mode). + ([?\s-r] . exwm-reset) + ;; 's-w': Switch workspace. + ([?\s-w] . exwm-workspace-switch) + ;; 's-&': Launch application. + ([?\s-&] . (lambda (command) + (interactive (list (read-shell-command "$ "))) + (start-process-shell-command command nil command))) + ;; 's-N': Switch to certain workspace. + ,@(mapcar (lambda (i) + `(,(kbd (format "s-%d" i)) . + (lambda () + (interactive) + (exwm-workspace-switch-create ,i)))) + (number-sequence 0 9))))) ;; Line-editing shortcuts (unless (get 'exwm-input-simulation-keys 'saved-value) (setq exwm-input-simulation-keys -- cgit 1.4.1 From 0f7269c4ec666eb8bcf2616abbb5af46087cbdea Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 6 Feb 2019 00:00:00 +0000 Subject: Add input method support ; The code is basically refactored from ; https://github.com/ch11ng/exim to get better maintenance. * exwm-xim.el: New module making Emacs's builtin input methods usable for interacting with X windows. * exwm-core.el (exwm--intern-atom): New function for intern X11 atoms. * exwm-input.el (exwm-input--init): * exwm-manage.el (exwm-manage--init): Use it. --- README.md | 3 +- exwm-core.el | 9 + exwm-input.el | 15 +- exwm-manage.el | 9 +- exwm-randr.el | 11 +- exwm-systemtray.el | 7 +- exwm-xim.el | 781 +++++++++++++++++++++++++++++++++++++++++++++++++++++ xinitrc | 19 +- 8 files changed, 812 insertions(+), 42 deletions(-) create mode 100644 exwm-xim.el diff --git a/README.md b/README.md index 103948c633..6d7e0dd1ff 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,8 @@ It features: + Dynamic workspace support + ICCCM/EWMH compliance + (Optional) RandR (multi-monitor) support -+ (Optional) Built-in system tray ++ (Optional) Builtin system tray ++ (Optional) Builtin input method Please check out the [screenshots](https://github.com/ch11ng/exwm/wiki/Screenshots) diff --git a/exwm-core.el b/exwm-core.el index e13a319bf3..9b6877b83f 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -131,6 +131,15 @@ Nil can be passed as placeholder." (if height xcb:ConfigWindow:Height 0)) :x x :y y :width width :height height))) +(defun exwm--intern-atom (atom) + "Intern X11 ATOM." + (slot-value (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:InternAtom + :only-if-exists 0 + :name-len (length atom) + :name atom)) + 'atom)) + (defmacro exwm--defer (secs function &rest args) "Defer the execution of FUNCTION. diff --git a/exwm-input.el b/exwm-input.el index a44bd9334a..4f64d35dd6 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -542,9 +542,9 @@ instead." (exwm-input--update-global-prefix-keys))) ;; Putting (t . EVENT) into `unread-command-events' does not really work -;; as documented for Emacs < 27. +;; as documented for Emacs < 26.2. (eval-and-compile - (if (< emacs-major-version 27) + (if (string-version-lessp emacs-version "26.2") (defsubst exwm-input--unread-event (event) (setq unread-command-events (append unread-command-events (list event)))) @@ -909,7 +909,7 @@ Notes: * Setting the value directly (rather than customizing it) after EXWM finishes initialization has no effect. * Original-keys consist of multiple key events are only supported in Emacs - 27 and later. + 26.2 and later. * A minority of applications do not accept simulated keys by default. It's required to customize them to accept events sent by SendEvent. * The predefined examples in the Customize interface are not guaranteed to @@ -1035,14 +1035,7 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences." (make-instance 'xcb:ewmh:set-_NET_WM_NAME :window exwm-input--timestamp-window :data "EXWM: exwm-input--timestamp-window")) - (let ((atom "_TIME")) - (setq exwm-input--timestamp-atom - (slot-value (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:InternAtom - :only-if-exists 0 - :name-len (length atom) - :name atom)) - 'atom))) + (setq exwm-input--timestamp-atom (exwm--intern-atom "_TIME")) ;; Initialize global keys. (dolist (i exwm-input-global-keys) (exwm-input--set-key (car i) (cdr i))) diff --git a/exwm-manage.el b/exwm-manage.el index 759ec0b776..b41512c485 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -715,14 +715,7 @@ border-width: %d; sibling: #x%x; stack-mode: %d" "Initialize manage module." ;; Intern _MOTIF_WM_HINTS (exwm--log) - (let ((atom-name "_MOTIF_WM_HINTS")) - (setq exwm-manage--_MOTIF_WM_HINTS - (slot-value (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:InternAtom - :only-if-exists 0 - :name-len (length atom-name) - :name atom-name)) - 'atom))) + (setq exwm-manage--_MOTIF_WM_HINTS (exwm--intern-atom "_MOTIF_WM_HINTS")) (add-hook 'after-make-frame-functions #'exwm-manage--add-frame) (add-hook 'delete-frame-functions #'exwm-manage--remove-frame) (xcb:+event exwm--connection 'xcb:ConfigureRequest diff --git a/exwm-randr.el b/exwm-randr.el index 4338152d7b..7d20022e9e 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -48,7 +48,9 @@ ;;; Code: (require 'xcb-randr) + (require 'exwm-core) +(require 'exwm-workspace) (defgroup exwm-randr nil "RandR." @@ -93,17 +95,8 @@ corresponding monitors whenever the monitors are active. (defvar exwm-randr--last-timestamp 0 "Used for debouncing events.") -(defvar exwm-workspace--fullscreen-frame-count) -(defvar exwm-workspace--list) (defvar exwm-randr--prev-screen-change-seqnum nil "The most recent ScreenChangeNotify sequence number.") -(declare-function exwm-workspace--count "exwm-workspace.el") -(declare-function exwm-workspace--set-active "exwm-workspace.el" - (frame active)) -(declare-function exwm-workspace--set-desktop-geometry "exwm-workspace.el" ()) -(declare-function exwm-workspace--set-fullscreen "exwm-workspace.el" (frame)) -(declare-function exwm-workspace--show-minibuffer "exwm-workspace.el" ()) -(declare-function exwm-workspace--update-workareas "exwm-workspace.el" ()) (defun exwm-randr--get-monitors () "Get RandR monitors." diff --git a/exwm-systemtray.el b/exwm-systemtray.el index 0ff4efcd8d..80505c22a5 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -33,7 +33,9 @@ (require 'xcb-icccm) (require 'xcb-xembed) (require 'xcb-systemtray) + (require 'exwm-core) +(require 'exwm-workspace) (defclass exwm-systemtray--icon () ((width :initarg :width) @@ -77,12 +79,7 @@ You shall use the default value if using auto-hide minibuffer." (defvar exwm-systemtray--selection-owner-window nil "The selection owner window.") -(defvar exwm-workspace--current) -(defvar exwm-workspace--minibuffer) -(defvar exwm-workspace--workareas) -(defvar exwm-workspace-current-index) (defvar xcb:Atom:_NET_SYSTEM_TRAY_S0) -(declare-function exwm-workspace--minibuffer-own-frame-p "exwm-workspace.el") (defun exwm-systemtray--embed (icon) "Embed an icon." diff --git a/exwm-xim.el b/exwm-xim.el new file mode 100644 index 0000000000..6a213acc0c --- /dev/null +++ b/exwm-xim.el @@ -0,0 +1,781 @@ +;;; exwm-xim.el --- XIM Module for EXWM -*- lexical-binding: t -*- + +;; Copyright (C) 2019 Free Software Foundation, Inc. + +;; Author: Chris Feng + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Commentary: + +;; This module adds XIM support for EXWM and allows sending characters +;; generated by any Emacs's builtin input method (info node `Input Methods') +;; to X windows. + +;; This module is essentially an X input method server utilizing Emacs as +;; its backend. It talks with X windows through the XIM protocol. The XIM +;; protocol is quite flexible by itself, stating that an implementation can +;; create network connections of various types as well as make use of an +;; existing X connection for communication, and that an IM server may +;; support multiple transport versions, various input styles and several +;; event flow modals, etc. Here we only make choices that are most popular +;; among other IM servers and more importantly, practical for Emacs to act +;; as an IM server: +;; +;; + Packets are transported on top of an X connection like most IMEs. +;; + Only transport version 0.0 (i.e. only-CM & Property-with-CM) is +;; supported (same as "IM Server Developers Kit", adopted by most IMEs). +;; + Only support static event flow, on-demand-synchronous method. +;; + Only "root-window" input style is supported. + +;; To use this module, first load and enable it as follows: +;; +;; (require 'exwm-xim) +;; (exwm-xim-enable) +;; +;; A keybinding for `toggle-input-method' is probably required to turn on & +;; off an input method (default to `default-input-method'). It's bound to +;; 'C-\' by default and can be made reachable when working with X windows: +;; +;; (push ?\C-\\ exwm-input-prefix-keys) +;; +;; It's also required (and error-prone) to setup environment variables to +;; make applications actually use this input method. Typically the +;; following lines should be inserted into '~/.xinitrc'. +;; +;; export XMODIFIERS=@im=exwm-xim +;; export GTK_IM_MODULE=xim +;; export QT_IM_MODULE=xim +;; export CLUTTER_IM_MODULE=xim + +;; References: +;; + XIM (http://www.x.org/releases/X11R7.6/doc/libX11/specs/XIM/xim.html) +;; + IMdkit (http://xorg.freedesktop.org/archive/unsupported/lib/IMdkit/) +;; + UIM (https://github.com/uim/uim) + +;;; Code: + +(eval-when-compile (require 'cl-lib)) + +(require 'xcb-keysyms) +(require 'xcb-xim) + +(require 'exwm-core) +(require 'exwm-input) + +(defconst exwm-xim--locales + "@locale=\ +aa,af,ak,am,an,anp,ar,as,ast,ayc,az,be,bem,ber,bg,bhb,bho,bn,bo,br,brx,bs,byn,\ +ca,ce,cmn,crh,cs,csb,cv,cy,da,de,doi,dv,dz,el,en,es,et,eu,fa,ff,fi,fil,fo,fr,\ +fur,fy,ga,gd,gez,gl,gu,gv,ha,hak,he,hi,hne,hr,hsb,ht,hu,hy,ia,id,ig,ik,is,it,\ +iu,iw,ja,ka,kk,kl,km,kn,ko,kok,ks,ku,kw,ky,lb,lg,li,li,lij,lo,lt,lv,lzh,mag,\ +mai,mg,mhr,mi,mk,ml,mn,mni,mr,ms,mt,my,nan,nb,nds,ne,nhn,niu,nl,nn,nr,nso,oc,\ +om,or,os,pa,pa,pap,pl,ps,pt,quz,raj,ro,ru,rw,sa,sat,sc,sd,se,shs,si,sid,sk,sl,\ +so,sq,sr,ss,st,sv,sw,szl,ta,tcy,te,tg,th,the,ti,tig,tk,tl,tn,tr,ts,tt,ug,uk,\ +unm,ur,uz,ve,vi,wa,wae,wal,wo,xh,yi,yo,yue,zh,zu,\ +C,no" + "All supported locales (stolen from glibc).") + +(defconst exwm-xim--default-error + (make-instance 'xim:error + :im-id 0 + :ic-id 0 + :flag xim:error-flag:invalid-both + :error-code xim:error-code:bad-something + :length 0 + :type 0 + :detail nil) + "Default error returned to clients.") + +(defconst exwm-xim--default-im-attrs + (list (make-instance 'xim:XIMATTR + :id 0 + :type xim:ATTRIBUTE-VALUE-TYPE:xim-styles + :length (length xlib:XNQueryInputStyle) + :attribute xlib:XNQueryInputStyle)) + "Default IM attrs returned to clients.") + +(defconst exwm-xim--default-ic-attrs + (list (make-instance 'xim:XICATTR + :id 0 + :type xim:ATTRIBUTE-VALUE-TYPE:long-data + :length (length xlib:XNInputStyle) + :attribute xlib:XNInputStyle) + (make-instance 'xim:XICATTR + :id 1 + :type xim:ATTRIBUTE-VALUE-TYPE:window + :length (length xlib:XNClientWindow) + :attribute xlib:XNClientWindow) + ;; Required by e.g. xterm. + (make-instance 'xim:XICATTR + :id 2 + :type xim:ATTRIBUTE-VALUE-TYPE:window + :length (length xlib:XNFocusWindow) + :attribute xlib:XNFocusWindow)) + "Default IC attrs returned to clients.") + +(defconst exwm-xim--default-styles + (make-instance 'xim:XIMStyles + :number nil + :styles (list (logior xlib:XIMPreeditNothing + xlib:XIMStatusNothing))) + "Default styles: root-window, i.e. no preediting or status display support.") + +(defconst exwm-xim--default-attributes + (list (make-instance 'xim:XIMATTRIBUTE + :id 0 + :length nil + :value exwm-xim--default-styles)) + "Default IM/IC attributes returned to clients.") + +(defvar exwm-xim--conn nil + "The X connection for initiating other XIM connections.") +(defvar exwm-xim--event-xwin nil + "X window for initiating new XIM connections.") +(defvar exwm-xim--server-client-plist '(nil nil) + "Plist mapping server window to [X connection, client window, byte-order].") +(defvar exwm-xim--client-server-plist '(nil nil) + "Plist mapping client window to server window.") +(defvar exwm-xim--property-index 0 "For generating a unique property name.") +(defvar exwm-xim--im-id 0 "Last IM ID.") +(defvar exwm-xim--ic-id 0 "Last IC ID.") +(defvar exwm-xim--event-pending nil + "Indicating whether Emacs requires more events.") + +;; X11 atoms. +(defvar exwm-xim--@server nil) +(defvar exwm-xim--LOCALES nil) +(defvar exwm-xim--TRANSPORT nil) +(defvar exwm-xim--XIM_SERVERS nil) +(defvar exwm-xim--_XIM_PROTOCOL nil) +(defvar exwm-xim--_XIM_XCONNECT nil) + +(defun exwm-xim--on-SelectionRequest (data _synthetic) + "Handle SelectionRequest events on IMS window. + +Such events would be received when clients query for LOCALES or TRANSPORT." + (exwm--log) + (let ((evt (make-instance 'xcb:SelectionRequest)) + value fake-event) + (xcb:unmarshal evt data) + (with-slots (time requestor selection target property) evt + (setq value (cond ((= target exwm-xim--LOCALES) + ;; Return supported locales. + exwm-xim--locales) + ((= target exwm-xim--TRANSPORT) + ;; Use XIM over an X connection. + "@transport=X/"))) + (when value + ;; Change the property. + (xcb:+request exwm-xim--conn + (make-instance 'xcb:ChangeProperty + :mode xcb:PropMode:Replace + :window requestor + :property property + :type target + :format 8 + :data-len (length value) + :data value)) + ;; Send a SelectionNotify event. + (setq fake-event (make-instance 'xcb:SelectionNotify + :time time + :requestor requestor + :selection selection + :target target + :property property)) + (xcb:+request exwm-xim--conn + (make-instance 'xcb:SendEvent + :propagate 0 + :destination requestor + :event-mask xcb:EventMask:NoEvent + :event (xcb:marshal fake-event exwm-xim--conn))) + (xcb:flush exwm-xim--conn))))) + +(cl-defun exwm-xim--on-ClientMessage-0 (data _synthetic) + "Handle ClientMessage event on IMS window (new connection). + +Such events would be received when clients request for _XIM_XCONNECT. +A new X connection and server window would be created to communicate with +this client." + (exwm--log) + (let ((evt (make-instance 'xcb:ClientMessage)) + conn client-xwin server-xwin) + (xcb:unmarshal evt data) + (with-slots (window type data) evt + (unless (= type exwm-xim--_XIM_XCONNECT) + ;; Only handle _XIM_XCONNECT. + (exwm--log "Ignore ClientMessage %s" type) + (cl-return-from exwm-xim--on-ClientMessage-0)) + (setq client-xwin (elt (slot-value data 'data32) 0) + ;; Create a new X connection and a new server window. + conn (xcb:connect) + server-xwin (xcb:generate-id conn)) + (set-process-query-on-exit-flag (slot-value conn 'process) nil) + ;; Store this client. + (plist-put exwm-xim--server-client-plist server-xwin + `[,conn ,client-xwin nil]) + (plist-put exwm-xim--client-server-plist client-xwin server-xwin) + ;; Select DestroyNotify events on this client window. + (xcb:+request exwm-xim--conn + (make-instance 'xcb:ChangeWindowAttributes + :window client-xwin + :value-mask xcb:CW:EventMask + :event-mask xcb:EventMask:StructureNotify)) + (xcb:flush exwm-xim--conn) + ;; Handle ClientMessage events from this new connection. + (xcb:+event conn 'xcb:ClientMessage #'exwm-xim--on-ClientMessage) + ;; Create a communication window. + (xcb:+request conn + (make-instance 'xcb:CreateWindow + :depth 0 + :wid server-xwin + :parent exwm--root + :x 0 + :y 0 + :width 1 + :height 1 + :border-width 0 + :class xcb:WindowClass:InputOutput + :visual 0 + :value-mask xcb:CW:OverrideRedirect + :override-redirect 1)) + (xcb:flush conn) + ;; Send connection establishment ClientMessage. + (setf window client-xwin + (slot-value data 'data32) `(,server-xwin 0 0 0 0)) + (slot-makeunbound data 'data8) + (slot-makeunbound data 'data16) + (xcb:+request exwm-xim--conn + (make-instance 'xcb:SendEvent + :propagate 0 + :destination client-xwin + :event-mask xcb:EventMask:NoEvent + :event (xcb:marshal evt exwm-xim--conn))) + (xcb:flush exwm-xim--conn)))) + +(cl-defun exwm-xim--on-ClientMessage (data _synthetic) + "Handle ClientMessage event on IMS communication window (request). + +Such events would be received when clients request for _XIM_PROTOCOL. +The actual XIM request is in client message data or a property." + (exwm--log) + (let ((evt (make-instance 'xcb:ClientMessage)) + conn client-xwin server-xwin) + (xcb:unmarshal evt data) + (with-slots (format window type data) evt + (unless (= type exwm-xim--_XIM_PROTOCOL) + (exwm--log "Ignore ClientMessage %s" type) + (cl-return-from exwm-xim--on-ClientMessage)) + (setq server-xwin window + conn (plist-get exwm-xim--server-client-plist server-xwin) + client-xwin (elt conn 1) + conn (elt conn 0)) + (cond ((= format 8) + ;; Data. + (exwm-xim--on-request (vconcat (slot-value data 'data8)) + conn client-xwin server-xwin)) + ((= format 32) + ;; Atom. + (with-slots (data32) data + (with-slots (value) + (xcb:+request-unchecked+reply conn + (make-instance 'xcb:GetProperty + :delete 1 + :window server-xwin + :property (elt data32 1) + :type xcb:GetPropertyType:Any + :long-offset 0 + :long-length (elt data32 0))) + (when (> (length value) 0) + (exwm-xim--on-request value conn client-xwin + server-xwin))))))))) + +(defun exwm-xim--on-request (data conn client-xwin server-xwin) + "Handle an XIM reuqest." + (exwm--log) + (let ((opcode (elt data 0)) + ;; Let-bind `xim:lsb' to make pack/unpack functions work correctly. + (xim:lsb (elt (plist-get exwm-xim--server-client-plist server-xwin) 2)) + req replies) + (cond ((= opcode xim:opcode:error) + (exwm--log "ERROR: %s" data)) + ((= opcode xim:opcode:connect) + (exwm--log "CONNECT") + (setq xim:lsb (= (elt data 4) xim:connect-byte-order:lsb-first)) + ;; Store byte-order. + (setf (elt (plist-get exwm-xim--server-client-plist server-xwin) 2) + xim:lsb) + (setq req (make-instance 'xim:connect)) + (xcb:unmarshal req data) + (if (and (= (slot-value req 'major-version) 1) + (= (slot-value req 'minor-version) 0) + ;; Do not support authentication. + (= (slot-value req 'number) 0)) + ;; Accept the connection. + (push (make-instance 'xim:connect-reply) replies) + ;; Deny it. + (push exwm-xim--default-error replies))) + ((memq opcode (list xim:opcode:auth-required + xim:opcode:auth-reply + xim:opcode:auth-next + xim:opcode:auth-ng)) + (exwm--log "AUTH: %d" opcode) + ;; Deny any attempt to make authentication. + (push exwm-xim--default-error replies)) + ((= opcode xim:opcode:disconnect) + (exwm--log "DISCONNECT") + ;; Gracefully disconnect from the client. + (exwm-xim--make-request (make-instance 'xim:disconnect-reply) + conn client-xwin) + ;; Destroy the communication window & connection. + (xcb:+request conn + (make-instance 'xcb:DestroyWindow + :window server-xwin)) + (xcb:disconnect conn) + ;; Clean up cache. + (cl-remf exwm-xim--server-client-plist server-xwin) + (cl-remf exwm-xim--client-server-plist client-xwin)) + ((= opcode xim:opcode:open) + (exwm--log "OPEN") + ;; Note: We make no check here. + (setq exwm-xim--im-id (if (< exwm-xim--im-id #xffff) + (1+ exwm-xim--im-id) + 1)) + (setq replies + (list + (make-instance 'xim:open-reply + :im-id exwm-xim--im-id + :im-attrs-length nil + :im-attrs exwm-xim--default-im-attrs + :ic-attrs-length nil + :ic-attrs exwm-xim--default-ic-attrs) + (make-instance 'xim:set-event-mask + :im-id exwm-xim--im-id + :ic-id 0 + ;; Static event flow. + :forward-event-mask xcb:EventMask:KeyPress + ;; on-demand-synchronous method. + :synchronous-event-mask + xcb:EventMask:NoEvent)))) + ((= opcode xim:opcode:close) + (exwm--log "CLOSE") + (setq req (make-instance 'xim:close)) + (xcb:unmarshal req data) + (push (make-instance 'xim:close-reply + :im-id (slot-value req 'im-id)) + replies)) + ((= opcode xim:opcode:trigger-notify) + (exwm--log "TRIGGER-NOTIFY") + ;; Only static event flow modal is supported. + (push exwm-xim--default-error replies)) + ((= opcode xim:opcode:encoding-negotiation) + (exwm--log "ENCODING-NEGOTIATION") + (setq req (make-instance 'xim:encoding-negotiation)) + (xcb:unmarshal req data) + (let ((index (cl-position "COMPOUND_TEXT" + (mapcar (lambda (i) (slot-value i 'name)) + (slot-value req 'names)) + :test #'equal))) + (unless index + ;; Fallback to portable character encoding (a subset of ASCII). + (setq index -1)) + (push (make-instance 'xim:encoding-negotiation-reply + :im-id (slot-value req 'im-id) + :category + xim:encoding-negotiation-reply-category:name + :index index) + replies))) + ((= opcode xim:opcode:query-extension) + (exwm--log "QUERY-EXTENSION") + (setq req (make-instance 'xim:query-extension)) + (xcb:unmarshal req data) + (push (make-instance 'xim:query-extension-reply + :im-id (slot-value req 'im-id) + ;; No extension support. + :length 0 + :extensions nil) + replies)) + ((= opcode xim:opcode:set-im-values) + (exwm--log "SET-IM-VALUES") + ;; There's only one possible input method attribute. + (setq req (make-instance 'xim:set-im-values)) + (xcb:unmarshal req data) + (push (make-instance 'xim:set-im-values-reply + :im-id (slot-value req 'im-id)) + replies)) + ((= opcode xim:opcode:get-im-values) + (exwm--log "GET-IM-VALUES") + (setq req (make-instance 'xim:get-im-values)) + (let (im-attributes-id) + (xcb:unmarshal req data) + (setq im-attributes-id (slot-value req 'im-attributes-id)) + (if (cl-notevery (lambda (i) (= i 0)) im-attributes-id) + ;; Only support one IM attributes. + (push (make-instance 'xim:error + :im-id (slot-value req 'im-id) + :ic-id 0 + :flag xim:error-flag:invalid-ic-id + :error-code xim:error-code:bad-something + :length 0 + :type 0 + :detail nil) + replies) + (push + (make-instance 'xim:get-im-values-reply + :im-id (slot-value req 'im-id) + :length nil + :im-attributes exwm-xim--default-attributes) + replies)))) + ((= opcode xim:opcode:create-ic) + (exwm--log "CREATE-IC") + (setq req (make-instance 'xim:create-ic)) + (xcb:unmarshal req data) + ;; Note: The ic-attributes slot is ignored. + (setq exwm-xim--ic-id (if (< exwm-xim--ic-id #xffff) + (1+ exwm-xim--ic-id) + 1)) + (push (make-instance 'xim:create-ic-reply + :im-id (slot-value req 'im-id) + :ic-id exwm-xim--ic-id) + replies)) + ((= opcode xim:opcode:destroy-ic) + (exwm--log "DESTROY-IC") + (setq req (make-instance 'xim:destroy-ic)) + (xcb:unmarshal req data) + (push (make-instance 'xim:destroy-ic-reply + :im-id (slot-value req 'im-id) + :ic-id (slot-value req 'ic-id)) + replies)) + ((= opcode xim:opcode:set-ic-values) + (exwm--log "SET-IC-VALUES") + (setq req (make-instance 'xim:set-ic-values)) + (xcb:unmarshal req data) + ;; We don't distinguish between input contexts. + (push (make-instance 'xim:set-ic-values-reply + :im-id (slot-value req 'im-id) + :ic-id (slot-value req 'ic-id)) + replies)) + ((= opcode xim:opcode:get-ic-values) + (exwm--log "GET-IC-VALUES") + (setq req (make-instance 'xim:get-ic-values)) + (xcb:unmarshal req data) + (push (make-instance 'xim:get-ic-values-reply + :im-id (slot-value req 'im-id) + :ic-id (slot-value req 'ic-id) + :length nil + :ic-attributes exwm-xim--default-attributes) + replies)) + ((= opcode xim:opcode:set-ic-focus) + (exwm--log "SET-IC-FOCUS") + ;; All input contexts are the same. + ) + ((= opcode xim:opcode:unset-ic-focus) + (exwm--log "UNSET-IC-FOCUS") + ;; All input contexts are the same. + ) + ((= opcode xim:opcode:forward-event) + (exwm--log "FORWARD-EVENT") + (setq req (make-instance 'xim:forward-event)) + (xcb:unmarshal req data) + (let ((im-func (with-current-buffer (window-buffer) + input-method-function)) + key-event keysym event result) + ;; Note: The flag slot is ignored. + ;; Do conversion in client's byte-order. + (let ((xcb:lsb xim:lsb)) + (setq key-event (make-instance 'xcb:KeyPress)) + (xcb:unmarshal key-event (slot-value req 'event))) + (with-slots (detail state) key-event + (setq keysym (xcb:keysyms:keycode->keysym exwm-xim--conn detail + state)) + (when (/= (car keysym) 0) + (setq event (xcb:keysyms:keysym->event + exwm-xim--conn + (car keysym) + (logand state (lognot (cdr keysym))))))) + (if exwm-xim--event-pending + ;; In case any event reaches here, it should be forwarded + ;; to Emacs. + (when event + (setq unread-command-events + (append unread-command-events (list event)))) + (setq exwm-xim--event-pending t) + (if (or (not im-func) + ;; `list' is the default method. + (eq im-func #'list) + (not event) + ;; Select only printable keys. + (not (integerp event)) (> #x20 event) (< #x7e event)) + ;; Either there is no active input method, or invalid key + ;; is detected. + (with-slots (im-id ic-id serial-number event) req + (push (make-instance 'xim:forward-event + :im-id im-id + :ic-id ic-id + :flag xim:commit-flag:synchronous + :serial-number serial-number + :event event) + replies)) + (when (eq exwm--selected-input-mode 'char-mode) + ;; Grab keyboard temporarily for char-mode. + (exwm-input--grab-keyboard)) + (unwind-protect + (with-temp-buffer + ;; Always show key strokes. + (let ((input-method-use-echo-area t)) + (setq result (funcall im-func event)))) + (when (eq exwm--selected-input-mode 'char-mode) + (exwm-input--release-keyboard))) + ;; This also works for portable character encoding. + (setq result + (encode-coding-string (concat result) + 'compound-text-with-extensions)) + (message "") + (push + (make-instance 'xim:commit-x-lookup-chars + :im-id (slot-value req 'im-id) + :ic-id (slot-value req 'ic-id) + :flag (logior xim:commit-flag:synchronous + xim:commit-flag:x-lookup-chars) + :length (length result) + :string result) + replies)) + (setq exwm-xim--event-pending nil)))) + ((= opcode xim:opcode:sync) + (exwm--log "SYNC") + (setq req (make-instance 'xim:sync)) + (xcb:unmarshal req data) + (push (make-instance 'xim:sync-reply + :im-id (slot-value req 'im-id) + :ic-id (slot-value req 'ic-id)) + replies)) + ((= opcode xim:opcode:sync-reply) + (exwm--log "SYNC-REPLY")) + ((= opcode xim:opcode:reset-ic) + (exwm--log "RESET-IC") + ;; No context-specific data saved. + (setq req (make-instance 'xim:reset-ic)) + (xcb:unmarshal req data) + (push (make-instance 'xim:reset-ic-reply + :im-id (slot-value req 'im-id) + :ic-id (slot-value req 'ic-id) + :length 0 + :string "") + replies)) + ((memq opcode (list xim:opcode:str-conversion-reply + xim:opcode:preedit-start-reply + xim:opcode:preedit-caret-reply)) + (exwm--log "PREEDIT: %d" opcode) + ;; No preedit support. + (push exwm-xim--default-error replies)) + (t + (exwm--log "Bad protocol") + (push exwm-xim--default-error replies))) + ;; Actually send the replies. + (when replies + (mapc (lambda (reply) + (exwm-xim--make-request reply conn client-xwin)) + replies) + (xcb:flush conn)))) + +(defun exwm-xim--make-request (req conn client-xwin) + "Make an XIM request REQ via connection CONN. + +CLIENT-XWIN would receive a ClientMessage event either telling the client +the request data or where to fetch the data." + (exwm--log) + (let ((data (xcb:marshal req)) + property format client-message-data client-message) + (if (<= (length data) 20) + ;; Send short requests directly with client messages. + (setq format 8 + ;; Pad to 20 bytes. + data (append data (make-list (- 20 (length data)) 0)) + client-message-data (make-instance 'xcb:ClientMessageData + :data8 data)) + ;; Send long requests with properties. + (setq property (exwm--intern-atom (format "_EXWM_XIM_%x" + exwm-xim--property-index))) + (cl-incf exwm-xim--property-index) + (xcb:+request conn + (make-instance 'xcb:ChangeProperty + :mode xcb:PropMode:Append + :window client-xwin + :property property + :type xcb:Atom:STRING + :format 8 + :data-len (length data) + :data data)) + ;; Also send a client message to notify the client about this property. + (setq format 32 + client-message-data (make-instance 'xcb:ClientMessageData + :data32 `(,(length data) + ,property + ;; Pad to 20 bytes. + 0 0 0)))) + ;; Send the client message. + (setq client-message (make-instance 'xcb:ClientMessage + :format format + :window client-xwin + :type exwm-xim--_XIM_PROTOCOL + :data client-message-data)) + (xcb:+request conn + (make-instance 'xcb:SendEvent + :propagate 0 + :destination client-xwin + :event-mask xcb:EventMask:NoEvent + :event (xcb:marshal client-message conn))))) + +(defun exwm-xim--on-DestroyNotify (data synthetic) + "Do cleanups on receiving DestroyNotify event. + +Such event would be received when the client window is destroyed." + (exwm--log) + (unless synthetic + (let ((evt (make-instance 'xcb:DestroyNotify)) + conn client-xwin server-xwin) + (xcb:unmarshal evt data) + (setq client-xwin (slot-value evt 'window) + server-xwin (plist-get exwm-xim--client-server-plist client-xwin)) + (when server-xwin + (setq conn (aref (plist-get exwm-xim--server-client-plist server-xwin) + 0)) + (cl-remf exwm-xim--server-client-plist server-xwin) + (cl-remf exwm-xim--client-server-plist client-xwin) + ;; Destroy the communication window & connection. + (xcb:+request conn + (make-instance 'xcb:DestroyWindow + :window server-xwin)) + (xcb:disconnect conn))))) + +(cl-defun exwm-xim--init () + "Initialize the XIM module." + (exwm--log) + (when exwm-xim--conn + (cl-return-from exwm-xim--init)) + ;; Initialize atoms. + (setq exwm-xim--@server (exwm--intern-atom "@server=exwm-xim") + exwm-xim--LOCALES (exwm--intern-atom "LOCALES") + exwm-xim--TRANSPORT (exwm--intern-atom "TRANSPORT") + exwm-xim--XIM_SERVERS (exwm--intern-atom "XIM_SERVERS") + exwm-xim--_XIM_PROTOCOL (exwm--intern-atom "_XIM_PROTOCOL") + exwm-xim--_XIM_XCONNECT (exwm--intern-atom "_XIM_XCONNECT")) + ;; Create a new connection and event window. + (setq exwm-xim--conn (xcb:connect) + exwm-xim--event-xwin (xcb:generate-id exwm-xim--conn)) + (set-process-query-on-exit-flag (slot-value exwm-xim--conn 'process) nil) + ;; Initialize xcb:keysyms module. + (xcb:keysyms:init exwm-xim--conn) + ;; Listen to SelectionRequest event for connection establishment. + (xcb:+event exwm-xim--conn 'xcb:SelectionRequest + #'exwm-xim--on-SelectionRequest) + ;; Listen to ClientMessage event on IMS window for new XIM connection. + (xcb:+event exwm-xim--conn 'xcb:ClientMessage #'exwm-xim--on-ClientMessage-0) + ;; Listen to DestroyNotify event to do cleanups. + (xcb:+event exwm-xim--conn 'xcb:DestroyNotify #'exwm-xim--on-DestroyNotify) + ;; Create the event window. + (xcb:+request exwm-xim--conn + (make-instance 'xcb:CreateWindow + :depth 0 + :wid exwm-xim--event-xwin + :parent exwm--root + :x 0 + :y 0 + :width 1 + :height 1 + :border-width 0 + :class xcb:WindowClass:InputOutput + :visual 0 + :value-mask xcb:CW:OverrideRedirect + :override-redirect 1)) + ;; Set the selection owner. + (xcb:+request exwm-xim--conn + (make-instance 'xcb:SetSelectionOwner + :owner exwm-xim--event-xwin + :selection exwm-xim--@server + :time xcb:Time:CurrentTime)) + ;; Set XIM_SERVERS property on the root window. + (xcb:+request exwm-xim--conn + (make-instance 'xcb:ChangeProperty + :mode xcb:PropMode:Prepend + :window exwm--root + :property exwm-xim--XIM_SERVERS + :type xcb:Atom:ATOM + :format 32 + :data-len 1 + :data (funcall (if xcb:lsb + #'xcb:-pack-u4-lsb + #'xcb:-pack-u4) + exwm-xim--@server))) + (xcb:flush exwm-xim--conn)) + +(cl-defun exwm-xim--exit () + "Exit the XIM module." + (exwm--log) + ;; Close IMS communication connections. + (mapc (lambda (i) + (when (vectorp i) + (xcb:disconnect (elt i 0)))) + exwm-xim--server-client-plist) + ;; Close the IMS connection. + (unless exwm-xim--conn + (cl-return-from exwm-xim--exit)) + ;; Remove exwm-xim from XIM_SERVERS. + (let ((reply (xcb:+request-unchecked+reply exwm-xim--conn + (make-instance 'xcb:GetProperty + :delete 1 + :window exwm--root + :property exwm-xim--XIM_SERVERS + :type xcb:Atom:ATOM + :long-offset 0 + :long-length 1000))) + unpacked-reply pack unpack) + (unless reply + (cl-return-from exwm-xim--exit)) + (setq reply (slot-value reply 'value)) + (unless (> (length reply) 4) + (cl-return-from exwm-xim--exit)) + (setq reply (vconcat reply) + pack (if xcb:lsb #'xcb:-pack-u4-lsb #'xcb:-pack-u4) + unpack (if xcb:lsb #'xcb:-unpack-u4-lsb #'xcb:-unpack-u4)) + (dotimes (i (/ (length reply) 4)) + (push (funcall unpack reply (* i 4)) unpacked-reply)) + (setq unpacked-reply (delq exwm-xim--@server unpacked-reply) + reply (mapcar pack unpacked-reply)) + (xcb:+request exwm-xim--conn + (make-instance 'xcb:ChangeProperty + :mode xcb:PropMode:Replace + :window exwm--root + :property exwm-xim--XIM_SERVERS + :type xcb:Atom:ATOM + :format 32 + :data-len (length reply) + :data reply)) + (xcb:flush exwm-xim--conn)) + (xcb:disconnect exwm-xim--conn) + (setq exwm-xim--conn nil)) + +(defun exwm-xim-enable () + "Enable XIM support for EXWM." + (exwm--log) + (add-hook 'exwm-init-hook #'exwm-xim--init) + (add-hook 'exwm-exit-hook #'exwm-xim--exit)) + + + +(provide 'exwm-xim) + +;;; exwm-xim.el ends here diff --git a/xinitrc b/xinitrc index 0adc068450..591e419914 100644 --- a/xinitrc +++ b/xinitrc @@ -1,17 +1,20 @@ -# Disable access control +# Disable access control for the current user. xhost +SI:localuser:$USER # Make Java applications aware this is a non-reparenting window manager. export _JAVA_AWT_WM_NONREPARENTING=1 -# Themes, etc -gnome-settings-daemon & - -# Fallback cursor +# Set default cursor. xsetroot -cursor_name left_ptr -# Keyboard repeat rate +# Set keyboard repeat rate. xset r rate 200 60 -# Start Emacs -exec dbus-launch --exit-with-session emacs +# Uncomment the following block to use the exwm-xim module. +#export XMODIFIERS=@im=exwm-xim +#export GTK_IM_MODULE=xim +#export QT_IM_MODULE=xim +#export CLUTTER_IM_MODULE=xim + +# Finally start Emacs +exec emacs -- cgit 1.4.1 From b1f74203bee715774e5f22a26edd941464f5e236 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 10 Feb 2019 00:00:00 +0000 Subject: Bump version to 0.22 --- exwm.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm.el b/exwm.el index 7affebfff2..c7ebe12845 100644 --- a/exwm.el +++ b/exwm.el @@ -4,7 +4,7 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0.21 +;; Version: 0.22 ;; Package-Requires: ((xelb "0.16")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From 2434bdb57dd49a32b61ca588fd5bb350d71a9236 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 11 Feb 2019 00:00:00 +0000 Subject: Fix compatibility issue with Emacs 25 * exwm-input.el (exwm-input--unread-event): `string-version-lessp' is not available on Emacs 25. --- exwm-input.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/exwm-input.el b/exwm-input.el index d0ae4ad812..9b9816e975 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -549,7 +549,9 @@ instead." ;; Putting (t . EVENT) into `unread-command-events' does not really work ;; as documented for Emacs < 26.2. (eval-and-compile - (if (string-version-lessp emacs-version "26.2") + (if (or (< emacs-major-version 26) + (and (= emacs-major-version 26) + (< emacs-minor-version 2))) (defsubst exwm-input--unread-event (event) (setq unread-command-events (append unread-command-events (list event)))) -- cgit 1.4.1 From 88c690217aa165db301c47981b52fb7c6df75154 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 16 Feb 2019 00:00:00 +0000 Subject: Bump version to 0.22.1 --- exwm.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm.el b/exwm.el index c7ebe12845..fca8b5d7e8 100644 --- a/exwm.el +++ b/exwm.el @@ -4,7 +4,7 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0.22 +;; Version: 0.22.1 ;; Package-Requires: ((xelb "0.16")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From b12c67de2ed10e2528b1c494aa08d51b43e563dd Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 10 Mar 2019 00:00:00 +0000 Subject: Make replacing existing WM optional * exwm.el (exwm-replace): New user option for specifying whether to replace existing WM. (exwm-init): Use it. (exwm--wmsn-acquire, exwm-init): Do not print warning message when user gives up replacing. * exwm-core.el (exwm--wmsn-replace): Remove dead code. --- exwm-core.el | 3 --- exwm.el | 11 +++++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 9b6877b83f..55fbecdddd 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -41,9 +41,6 @@ (defvar exwm--wmsn-acquire-timeout 3 "Number of seconds to wait for other window managers to release the selection.") -(defvar exwm--wmsn-replace 'ask - "Replace existing window manager.") - (defvar exwm--guide-window nil "An X window separating workspaces and X windows.") diff --git a/exwm.el b/exwm.el index fca8b5d7e8..7d301ab4bd 100644 --- a/exwm.el +++ b/exwm.el @@ -99,6 +99,12 @@ "Subrs (primitives) that would normally block EXWM." :type '(repeat function)) +(defcustom exwm-replace 'ask + "Whether to replace existing window manager." + :type '(radio (const :tag "Ask" ask) + (const :tag "Replace by default" t) + (const :tag "Do not replace" nil))) + (defconst exwm--server-name "server-exwm" "Name of the subordinate Emacs server.") @@ -721,7 +727,7 @@ manager. If t, replace it, if nil, abort and ask the user if `ask'." (when (eq replace 'ask) (setq replace (yes-or-no-p "Replace existing window manager? "))) (when (not replace) - (error "Other window manager detected"))) + (user-error "Other window manager detected"))) (let ((new-owner (xcb:generate-id exwm--connection))) (xcb:+request exwm--connection (make-instance 'xcb:CreateWindow @@ -812,7 +818,7 @@ manager. If t, replace it, if nil, abort and ask the user if `ask'." (xcb:icccm:init exwm--connection t) (xcb:ewmh:init exwm--connection t) ;; Try to register window manager selection. - (exwm--wmsn-acquire 'ask) + (exwm--wmsn-acquire exwm-replace) (when (xcb:+request-checked+request-check exwm--connection (make-instance 'xcb:ChangeWindowAttributes :window exwm--root @@ -836,6 +842,7 @@ manager. If t, replace it, if nil, abort and ask the user if `ask'." (run-hooks 'exwm-init-hook) ;; Manage existing windows (exwm-manage--scan)) + (user-error) ((quit error) (exwm-exit) ;; Rethrow error -- cgit 1.4.1 From dd96fffb52a4e7e5b506b28881b518b0359375c4 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 17 Mar 2019 00:00:00 +0000 Subject: Add extra keys for selecting workspace * exwm-workspace.el (exwm-workspace--switch-map) (exwm-workspace--init): Avoid initializing the keymap when loading. (exwm-workspace--init-switch-map): Initialize `exwm-workspace--switch-map' and also add extra keybindings when `exwm-workspace-index-map' has been customized. --- exwm-workspace.el | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 783287366a..f6b8fbac06 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -157,7 +157,10 @@ NIL if FRAME is not a workspace" "Return non-nil if FRAME is an emacsclient frame." (frame-parameter frame 'client)) -(defvar exwm-workspace--switch-map +(defvar exwm-workspace--switch-map nil + "Keymap used for interactively selecting workspace.") + +(defun exwm-workspace--init-switch-map () (let ((map (make-sparse-keymap))) (define-key map [t] (lambda () (interactive))) (define-key map "+" #'exwm-workspace--prompt-add) @@ -165,6 +168,17 @@ NIL if FRAME is not a workspace" (dotimes (i 10) (define-key map (int-to-string i) #'exwm-workspace--switch-map-nth-prefix)) + (unless (eq exwm-workspace-index-map #'number-to-string) + ;; Add extra (and possibly override) keys for selecting workspace. + (dotimes (i 10) + (let ((key (funcall exwm-workspace-index-map i))) + (when (and (stringp key) + (= (length key) 1) + (<= 0 (elt key 0) 127)) + (define-key map key + `(lambda () + (interactive) + (exwm-workspace--switch-map-select-nth ,i))))))) (define-key map "\C-a" (lambda () (interactive) (goto-history-element 1))) (define-key map "\C-e" (lambda () (interactive) @@ -180,8 +194,7 @@ NIL if FRAME is not a workspace" ;; Alternative keys (define-key map [right] #'previous-history-element) (define-key map [left] #'next-history-element) - map) - "Keymap used for interactively switch workspace.") + (setq exwm-workspace--switch-map map))) (defun exwm-workspace--workspace-from-frame-or-index (frame-or-index) "Retrieve the workspace frame from FRAME-OR-INDEX." @@ -1557,6 +1570,7 @@ applied to all subsequently created X frames." (defun exwm-workspace--init () "Initialize workspace module." (exwm--log) + (exwm-workspace--init-switch-map) ;; Prevent unexpected exit (setq exwm-workspace--fullscreen-frame-count 0) (exwm-workspace--modify-all-x-frames-parameters -- cgit 1.4.1 From 397ca5497e82168b0de1cac9a2204dc695b1de5a Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 17 Mar 2019 00:00:00 +0000 Subject: Remove loading order dependency on `mouse-autoselect-window' * exwm-core.el (exwm--get-client-event-mask): Renamed from `exwm--client-event-mask' and used as a function. * exwm-floating.el (exwm-floating--unset-floating): * exwm-layout.el (exwm-layout--hide): * exwm-manage.el (exwm-manage--manage-window): Use it. --- exwm-core.el | 11 ++++++----- exwm-floating.el | 2 +- exwm-layout.el | 2 +- exwm-manage.el | 4 ++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 55fbecdddd..39928e1565 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -150,11 +150,12 @@ least SECS seconds later." ,function ,@args)) -(defconst exwm--client-event-mask (logior xcb:EventMask:StructureNotify - xcb:EventMask:PropertyChange - (if mouse-autoselect-window - xcb:EventMask:EnterWindow 0)) - "Event mask set on all managed windows.") +(defun exwm--get-client-event-mask () + "Return event mask set on all managed windows." + (logior xcb:EventMask:StructureNotify + xcb:EventMask:PropertyChange + (if mouse-autoselect-window + xcb:EventMask:EnterWindow 0))) ;; Internal variables (defvar-local exwm--id nil) ;window ID diff --git a/exwm-floating.el b/exwm-floating.el index b7430d719e..7ac2c2714b 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -342,7 +342,7 @@ This is also used by X window containers.") (xcb:+request exwm--connection (make-instance 'xcb:ChangeWindowAttributes :window id :value-mask xcb:CW:EventMask - :event-mask exwm--client-event-mask)) + :event-mask (exwm--get-client-event-mask))) ;; Reparent the floating frame back to the root window. (let ((frame-id (frame-parameter exwm--floating-frame 'exwm-outer-id)) (frame-container (frame-parameter exwm--floating-frame diff --git a/exwm-layout.el b/exwm-layout.el index 52fd08e152..9ef516bd06 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -158,7 +158,7 @@ (xcb:+request exwm--connection (make-instance 'xcb:ChangeWindowAttributes :window id :value-mask xcb:CW:EventMask - :event-mask exwm--client-event-mask)) + :event-mask (exwm--get-client-event-mask))) (exwm-layout--set-state id xcb:icccm:WM_STATE:IconicState) (exwm-layout--auto-iconify) (xcb:flush exwm--connection)))) diff --git a/exwm-manage.el b/exwm-manage.el index b41512c485..5c32284e47 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -195,7 +195,7 @@ You can still make the X windows floating afterwards." (when (xcb:+request-checked+request-check exwm--connection (make-instance 'xcb:ChangeWindowAttributes :window id :value-mask xcb:CW:EventMask - :event-mask exwm--client-event-mask)) + :event-mask (exwm--get-client-event-mask))) (throw 'return 'dead)) ;; Add this X window to save-set. (xcb:+request exwm--connection @@ -257,7 +257,7 @@ You can still make the X windows floating afterwards." exwm-window-type) ;; Listen for PropertyChange (struts) and ;; UnmapNotify/DestroyNotify event of the dock. - exwm--client-event-mask + (exwm--get-client-event-mask) xcb:EventMask:NoEvent))) ;; The window needs to be mapped (xcb:+request exwm--connection -- cgit 1.4.1 From 81e52263a74d1fcd322249f1674729597d607a76 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Sun, 17 Mar 2019 10:47:28 -0400 Subject: * exwm-workspace.el: Use closures rather than `(lambda ...) --- exwm-workspace.el | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index f6b8fbac06..cca4ed8c43 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -176,9 +176,9 @@ NIL if FRAME is not a workspace" (= (length key) 1) (<= 0 (elt key 0) 127)) (define-key map key - `(lambda () - (interactive) - (exwm-workspace--switch-map-select-nth ,i))))))) + (lambda () + (interactive) + (exwm-workspace--switch-map-select-nth i))))))) (define-key map "\C-a" (lambda () (interactive) (goto-history-element 1))) (define-key map "\C-e" (lambda () (interactive) @@ -497,17 +497,17 @@ PREFIX-DIGITS is a list of the digits introduced so far." ;; Go ahead if there are enough digits to select any workspace. (set-transient-map (let ((map (make-sparse-keymap)) - (cmd `(lambda () + (cmd (let ((digits (cons d prefix-digits))) + (lambda () (interactive) - (exwm-workspace--switch-map-nth-prefix - ',(cons d prefix-digits))))) + (exwm-workspace--switch-map-nth-prefix digits))))) (dotimes (i 10) (define-key map (int-to-string i) cmd)) ;; Accept (define-key map [return] - `(lambda () - (interactive) - (exwm-workspace--switch-map-select-nth ,n))) + (lambda () + (interactive) + (exwm-workspace--switch-map-select-nth n))) map))))) (defun exwm-workspace--switch-map-select-nth (n) -- cgit 1.4.1 From 73b4d6f96620b9697606e674d613b0a0ca281a98 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 24 Mar 2019 00:00:00 +0000 Subject: ; Follows up --- exwm-floating.el | 122 +++++++++++++++++++++++++++---------------------------- exwm-input.el | 12 +++--- exwm-manage.el | 4 +- 3 files changed, 69 insertions(+), 69 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index 7ac2c2714b..c478e75c77 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -486,91 +486,91 @@ This is also used by X window containers.") (cond ((= type xcb:ewmh:_NET_WM_MOVERESIZE_MOVE) (setq cursor exwm-floating--cursor-move exwm-floating--moveresize-calculate - `(lambda (x y) - (vector ,buffer-or-id - ,(eval-when-compile - (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y)) - (- x ,win-x) (- y ,win-y) 0 0)))) + (lambda (x y) + (vector buffer-or-id + (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y)) + (- x win-x) (- y win-y) 0 0)))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPLEFT) (setq cursor exwm-floating--cursor-top-left exwm-floating--moveresize-calculate - `(lambda (x y) - (vector ,buffer-or-id - ,(eval-when-compile - (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height)) - (- x ,win-x) (- y ,win-y) - (- ,(+ root-x width) x) - (- ,(+ root-y height) y))))) + (lambda (x y) + (vector buffer-or-id + (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height)) + (- x win-x) (- y win-y) + (- (+ root-x width) x) + (- (+ root-y height) y))))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOP) (setq cursor exwm-floating--cursor-top exwm-floating--moveresize-calculate - `(lambda (x y) - (vector ,buffer-or-id - ,(eval-when-compile - (logior xcb:ConfigWindow:Y - xcb:ConfigWindow:Height)) - 0 (- y ,win-y) 0 (- ,(+ root-y height) y))))) + (lambda (_x y) + (vector buffer-or-id + (eval-when-compile + (logior xcb:ConfigWindow:Y + xcb:ConfigWindow:Height)) + 0 (- y win-y) 0 (- (+ root-y height) y))))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_TOPRIGHT) (setq cursor exwm-floating--cursor-top-right exwm-floating--moveresize-calculate - `(lambda (x y) - (vector ,buffer-or-id - ,(eval-when-compile - (logior xcb:ConfigWindow:Y - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height)) - 0 (- y ,win-y) (- x ,(- root-x width)) - (- ,(+ root-y height) y))))) + (lambda (x y) + (vector buffer-or-id + (eval-when-compile + (logior xcb:ConfigWindow:Y + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height)) + 0 (- y win-y) (- x (- root-x width)) + (- (+ root-y height) y))))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_RIGHT) (setq cursor exwm-floating--cursor-right exwm-floating--moveresize-calculate - `(lambda (x y) - (vector ,buffer-or-id - ,xcb:ConfigWindow:Width - 0 0 (- x ,(- root-x width)) 0)))) + (lambda (x _y) + (vector buffer-or-id + xcb:ConfigWindow:Width + 0 0 (- x (- root-x width)) 0)))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT) (setq cursor exwm-floating--cursor-bottom-right exwm-floating--moveresize-calculate - `(lambda (x y) - (vector ,buffer-or-id - ,(eval-when-compile - (logior xcb:ConfigWindow:Width - xcb:ConfigWindow:Height)) - 0 0 (- x ,(- root-x width)) - (- y ,(- root-y height)))))) + (lambda (x y) + (vector buffer-or-id + (eval-when-compile + (logior xcb:ConfigWindow:Width + xcb:ConfigWindow:Height)) + 0 0 (- x (- root-x width)) + (- y (- root-y height)))))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOM) (setq cursor exwm-floating--cursor-bottom exwm-floating--moveresize-calculate - `(lambda (x y) - (vector ,buffer-or-id - ,xcb:ConfigWindow:Height - 0 0 0 (- y ,(- root-y height)))))) + (lambda (_x y) + (vector buffer-or-id + xcb:ConfigWindow:Height + 0 0 0 (- y (- root-y height)))))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT) (setq cursor exwm-floating--cursor-bottom-left exwm-floating--moveresize-calculate - `(lambda (x y) - (vector ,buffer-or-id - ,(eval-when-compile - (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Width - xcb:ConfigWindow:Height)) - (- x ,win-x) - 0 - (- ,(+ root-x width) x) - (- y ,(- root-y height)))))) + (lambda (x y) + (vector buffer-or-id + (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Width + xcb:ConfigWindow:Height)) + (- x win-x) + 0 + (- (+ root-x width) x) + (- y (- root-y height)))))) ((= type xcb:ewmh:_NET_WM_MOVERESIZE_SIZE_LEFT) (setq cursor exwm-floating--cursor-left exwm-floating--moveresize-calculate - `(lambda (x y) - (vector ,buffer-or-id - ,(eval-when-compile - (logior xcb:ConfigWindow:X - xcb:ConfigWindow:Width)) - (- x ,win-x) 0 (- ,(+ root-x width) x) 0))))) + (lambda (x _y) + (vector buffer-or-id + (eval-when-compile + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Width)) + (- x win-x) 0 (- (+ root-x width) x) 0))))) ;; Select events and change cursor (should always succeed) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GrabPointer diff --git a/exwm-input.el b/exwm-input.el index 9b9816e975..eeea36e5a2 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -720,15 +720,15 @@ button event." (line-mode (setq mode "line" help-echo "mouse-1: Switch to char-mode" - cmd `(lambda () - (interactive) - (exwm-input-release-keyboard ,id)))) + cmd (lambda () + (interactive) + (exwm-input-release-keyboard id)))) (char-mode (setq mode "char" help-echo "mouse-1: Switch to line-mode" - cmd `(lambda () - (interactive) - (exwm-input-grab-keyboard ,id))))) + cmd (lambda () + (interactive) + (exwm-input-grab-keyboard id))))) (with-current-buffer (exwm--id->buffer id) (setq mode-line-process `(": " diff --git a/exwm-manage.el b/exwm-manage.el index 5c32284e47..8f8f6e44b8 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -540,8 +540,8 @@ Would you like to kill it? " (signal-process pid 'SIGKILL) ;; Ensure it's dead (run-with-timer exwm-manage-ping-timeout nil - `(lambda () - (xcb:+request exwm--connection ,request)))) + (lambda () + (xcb:+request exwm--connection request)))) (xcb:flush exwm--connection))) (defun exwm-manage--add-frame (frame) -- cgit 1.4.1 From bd99d8cf7fe3be24d370f8a99332f33c4106f510 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 24 Mar 2019 00:00:00 +0000 Subject: Support monitor mirroring * exwm-randr.el (exwm-randr--get-monitors): Also return an alist of monitor name aliases. (exwm-randr-refresh): Unify mirrored monitor names. --- exwm-randr.el | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/exwm-randr.el b/exwm-randr.el index 7d20022e9e..022a213153 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -101,7 +101,8 @@ corresponding monitors whenever the monitors are active. (defun exwm-randr--get-monitors () "Get RandR monitors." (exwm--log) - (let (monitor-name geometry monitor-plist primary-monitor) + (let (monitor-name geometry monitor-geometry-alist primary-monitor + monitor-position-alist monitor-alias-alist) (with-slots (timestamp monitors) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:randr:GetMonitors @@ -117,14 +118,36 @@ corresponding monitors whenever the monitors are active. :y y :width width :height height) - monitor-plist (plist-put monitor-plist monitor-name geometry)) + monitor-geometry-alist (cons (cons monitor-name geometry) + monitor-geometry-alist)) (exwm--log "%s: %sx%s+%s+%s" monitor-name x y width height) ;; Save primary monitor when available (fallback to the first one). (when (or (/= 0 primary) (not primary-monitor)) (setq primary-monitor monitor-name))))) (exwm--log "Primary monitor: %s" primary-monitor) - (list primary-monitor monitor-plist))) + ;; In a mirroring setup some monitors overlap and should be treated + ;; as one. + (setq monitor-position-alist (with-slots (x y) + (cdr (assoc primary-monitor + monitor-geometry-alist)) + (list (cons primary-monitor (vector x y))))) + (setq monitor-alias-alist (list (cons primary-monitor primary-monitor))) + (dolist (pair monitor-geometry-alist) + (setq monitor-name (car pair) + geometry (cdr pair)) + (unless (assoc monitor-name monitor-alias-alist) + (let* ((position (vector (slot-value geometry 'x) + (slot-value geometry 'y))) + (alias (car (rassoc position monitor-position-alist)))) + (if alias + (setq monitor-alias-alist (cons (cons monitor-name alias) + monitor-alias-alist)) + (setq monitor-position-alist (cons (cons monitor-name position) + monitor-position-alist) + monitor-alias-alist (cons (cons monitor-name monitor-name) + monitor-alias-alist)))))) + (list primary-monitor monitor-geometry-alist monitor-alias-alist))) ;;;###autoload (defun exwm-randr-refresh () @@ -133,20 +156,25 @@ corresponding monitors whenever the monitors are active. (exwm--log) (let* ((result (exwm-randr--get-monitors)) (primary-monitor (elt result 0)) - (monitor-plist (elt result 1)) + (monitor-geometry-alist (elt result 1)) + (monitor-alias-alist (elt result 2)) container-monitor-alist container-frame-alist) - (when (and primary-monitor monitor-plist) + (when (and primary-monitor monitor-geometry-alist) (when exwm-workspace--fullscreen-frame-count ;; Not all workspaces are fullscreen; reset this counter. (setq exwm-workspace--fullscreen-frame-count 0)) (dotimes (i (exwm-workspace--count)) (let* ((monitor (plist-get exwm-randr-workspace-monitor-plist i)) - (geometry (lax-plist-get monitor-plist monitor)) + (geometry (cdr (assoc monitor monitor-geometry-alist))) (frame (elt exwm-workspace--list i)) (container (frame-parameter frame 'exwm-container))) - (unless geometry + (if geometry + ;; Unify monitor names in case it's a mirroring setup. + (setq monitor (cdr (assoc monitor monitor-alias-alist))) + ;; Missing monitors fallback to the primary one. (setq monitor primary-monitor - geometry (lax-plist-get monitor-plist primary-monitor))) + geometry (cdr (assoc primary-monitor + monitor-geometry-alist)))) (setq container-monitor-alist (nconc `((,container . ,(intern monitor))) container-monitor-alist) -- cgit 1.4.1 From 4ac0d6c1fd00ffd9229ef2d5fd1c63ee6151ed2d Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 14 Apr 2019 00:00:00 +0000 Subject: Clarify the usage of `exwm-manage-configurations' --- exwm-manage.el | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/exwm-manage.el b/exwm-manage.el index 8f8f6e44b8..6f89fff4f0 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -48,7 +48,29 @@ You can still make the X windows floating afterwards." :type 'integer) (defcustom exwm-manage-configurations nil - "Per-application configurations." + "Per-application configurations. + +Configuration options allow to override various default behaviors of EXWM +and only take effect when they are present. Note for certain options +specifying nil is not exactly the same as leaving them out. Currently +possible choices: +* floating: Force floating (non-nil) or tiling (nil) on startup. +* x/y/width/height: Override the initial geometry (floating X window only). +* border-width: Override the border width (only visible when floating). +* fullscreen: Force full screen (non-nil) on startup. +* floating-mode-line: `mode-line-format' used when floating. +* tiling-mode-line: `mode-line-format' used when tiling. +* floating-header-line: `header-line-format' used when floating. +* tiling-header-line: `header-line-format' used when tiling. +* char-mode: Force char-mode (non-nil) on startup. +* prefix-keys: `exwm-input-prefix-keys' local to this X window. +* simulation-keys: `exwm-input-simulation-keys' local to this X window. +* workspace: The initial workspace. +* managed: Force to manage (non-nil) or not manage (nil) the X window. + +For each X window managed for the first time, matching criteria (sexps) are +evaluated sequentially and the first configuration with a non-nil matching +criterion would be applied." :type '(alist :key-type (sexp :tag "Matching criterion" nil) :value-type (plist :tag "Configurations" -- cgit 1.4.1 From f70bdb5868fc098b7f1f44e6eced3819eb13ea42 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 14 Apr 2019 00:00:00 +0000 Subject: Add compatibility mode for legacy servers * exwm-randr.el (exwm-randr--compatibility-mode): Indicating whether RandR 1.5 is supported by the server. (exwm-randr--init): Set it. (exwm-randr--get-monitor-alias): Split out from exwm-randr--get-monitors for reuse. (exwm-randr--get-outputs): New function for retrieving RandR 1.2 outputs when RandR 1.5 is not supported. (exwm-randr-refresh): Call `exwm-randr--get-outputs' in compatibility mode. --- exwm-randr.el | 132 ++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 96 insertions(+), 36 deletions(-) diff --git a/exwm-randr.el b/exwm-randr.el index 022a213153..c028c30296 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -98,11 +98,13 @@ corresponding monitors whenever the monitors are active. (defvar exwm-randr--prev-screen-change-seqnum nil "The most recent ScreenChangeNotify sequence number.") +(defvar exwm-randr--compatibility-mode nil + "Non-nil when the server does not support RandR 1.5 protocol.") + (defun exwm-randr--get-monitors () - "Get RandR monitors." + "Get RandR 1.5 monitors." (exwm--log) - (let (monitor-name geometry monitor-geometry-alist primary-monitor - monitor-position-alist monitor-alias-alist) + (let (monitor-name geometry monitor-geometry-alist primary-monitor) (with-slots (timestamp monitors) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:randr:GetMonitors @@ -126,8 +128,58 @@ corresponding monitors whenever the monitors are active. (not primary-monitor)) (setq primary-monitor monitor-name))))) (exwm--log "Primary monitor: %s" primary-monitor) - ;; In a mirroring setup some monitors overlap and should be treated - ;; as one. + (list primary-monitor monitor-geometry-alist + (exwm-randr--get-monitor-alias primary-monitor + monitor-geometry-alist)))) + +(defun exwm-randr--get-outputs () + "Get RandR 1.2 outputs. + +Only used when RandR 1.5 is not supported by the server." + (exwm--log) + (let (output-name geometry output-geometry-alist primary-output) + (with-slots (config-timestamp outputs) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:randr:GetScreenResourcesCurrent + :window exwm--root)) + (when (> config-timestamp exwm-randr--last-timestamp) + (setq exwm-randr--last-timestamp config-timestamp)) + (dolist (output outputs) + (with-slots (crtc connection name) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:randr:GetOutputInfo + :output output + :config-timestamp config-timestamp)) + (when (and (= connection xcb:randr:Connection:Connected) + (/= crtc 0)) + (with-slots (x y width height) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:randr:GetCrtcInfo + :crtc crtc + :config-timestamp config-timestamp)) + (setq output-name (decode-coding-string + (apply #'unibyte-string name) 'utf-8) + geometry (make-instance 'xcb:RECTANGLE + :x x + :y y + :width width + :height height) + output-geometry-alist (cons (cons output-name geometry) + output-geometry-alist)) + (exwm--log "%s: %sx%s+%s+%s" output-name x y width height) + ;; The primary output is the first one. + (unless primary-output + (setq primary-output output-name))))))) + (exwm--log "Primary output: %s" primary-output) + (list primary-output output-geometry-alist + (exwm-randr--get-monitor-alias primary-output + output-geometry-alist)))) + +(defun exwm-randr--get-monitor-alias (primary-monitor monitor-geometry-alist) + "Generate monitor aliases using PRIMARY-MONITOR MONITOR-GEOMETRY-ALIST. + +In a mirroring setup some monitors overlap and should be treated as one." + (let (monitor-position-alist monitor-alias-alist monitor-name geometry) (setq monitor-position-alist (with-slots (x y) (cdr (assoc primary-monitor monitor-geometry-alist)) @@ -147,14 +199,16 @@ corresponding monitors whenever the monitors are active. monitor-position-alist) monitor-alias-alist (cons (cons monitor-name monitor-name) monitor-alias-alist)))))) - (list primary-monitor monitor-geometry-alist monitor-alias-alist))) + monitor-alias-alist)) ;;;###autoload (defun exwm-randr-refresh () "Refresh workspaces according to the updated RandR info." (interactive) (exwm--log) - (let* ((result (exwm-randr--get-monitors)) + (let* ((result (if exwm-randr--compatibility-mode + (exwm-randr--get-outputs) + (exwm-randr--get-monitors))) (primary-monitor (elt result 0)) (monitor-geometry-alist (elt result 1)) (monitor-alias-alist (elt result 2)) @@ -262,35 +316,41 @@ Refresh when any RandR 1.5 monitor changes." (defun exwm-randr--init () "Initialize RandR extension and EXWM RandR module." (exwm--log) - (if (= 0 (slot-value (xcb:get-extension-data exwm--connection 'xcb:randr) - 'present)) - (error "[EXWM] RandR extension is not supported by the server") - (with-slots (major-version minor-version) - (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:randr:QueryVersion - :major-version 1 :minor-version 5)) - (if (or (/= major-version 1) (< minor-version 5)) - (error "[EXWM] The server only support RandR version up to %d.%d" - major-version minor-version) - ;; External monitor(s) may already be connected. - (run-hooks 'exwm-randr-screen-change-hook) - (exwm-randr-refresh) - ;; Listen for `ScreenChangeNotify' to notify external tools to - ;; configure RandR and `CrtcChangeNotify/OutputChangeNotify' to - ;; refresh the workspace layout. - (xcb:+event exwm--connection 'xcb:randr:ScreenChangeNotify - #'exwm-randr--on-ScreenChangeNotify) - (xcb:+event exwm--connection 'xcb:randr:Notify #'exwm-randr--on-Notify) - (xcb:+event exwm--connection 'xcb:ConfigureNotify - #'exwm-randr--on-ConfigureNotify) - (xcb:+request exwm--connection - (make-instance 'xcb:randr:SelectInput - :window exwm--root - :enable (logior xcb:randr:NotifyMask:ScreenChange - xcb:randr:NotifyMask:CrtcChange - xcb:randr:NotifyMask:OutputChange))) - (xcb:flush exwm--connection) - (add-hook 'exwm-workspace-list-change-hook #'exwm-randr-refresh)))) + (when (= 0 (slot-value (xcb:get-extension-data exwm--connection 'xcb:randr) + 'present)) + (error "[EXWM] RandR extension is not supported by the server")) + (with-slots (major-version minor-version) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:randr:QueryVersion + :major-version 1 :minor-version 5)) + (cond ((and (= major-version 1) (= minor-version 5)) + (setq exwm-randr--compatibility-mode nil)) + ((and (= major-version 1) (>= minor-version 2)) + (setq exwm-randr--compatibility-mode t)) + (t + (error "[EXWM] The server only support RandR version up to %d.%d" + major-version minor-version))) + ;; External monitor(s) may already be connected. + (run-hooks 'exwm-randr-screen-change-hook) + (exwm-randr-refresh) + ;; Listen for `ScreenChangeNotify' to notify external tools to + ;; configure RandR and `CrtcChangeNotify/OutputChangeNotify' to + ;; refresh the workspace layout. + (xcb:+event exwm--connection 'xcb:randr:ScreenChangeNotify + #'exwm-randr--on-ScreenChangeNotify) + (xcb:+event exwm--connection 'xcb:randr:Notify + #'exwm-randr--on-Notify) + (xcb:+event exwm--connection 'xcb:ConfigureNotify + #'exwm-randr--on-ConfigureNotify) + (xcb:+request exwm--connection + (make-instance 'xcb:randr:SelectInput + :window exwm--root + :enable (logior + xcb:randr:NotifyMask:ScreenChange + xcb:randr:NotifyMask:CrtcChange + xcb:randr:NotifyMask:OutputChange))) + (xcb:flush exwm--connection) + (add-hook 'exwm-workspace-list-change-hook #'exwm-randr-refresh)) ;; Prevent frame parameters introduced by this module from being ;; saved/restored. (dolist (i '(exwm-randr-monitor)) -- cgit 1.4.1 From fe2336a11e26a488f9fc428efce095a6c10981df Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 2 Jun 2019 00:00:00 +0000 Subject: Fix auto commit issue with input methods * exwm-xim.el (exwm-xim--handle-forward-event-request): Factored out from `exwm-xim--on-request' to make auto commit work. With input methods providing candidates the first candidate can be implicitly selected if no further matching is possible. The last event would be stored in `unread-command-events' (at least for `quail-input-method') and should be reused by the input method. (exwm-xim--on-request): Use it. --- exwm-xim.el | 138 ++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 74 insertions(+), 64 deletions(-) diff --git a/exwm-xim.el b/exwm-xim.el index 6a213acc0c..23512144c9 100644 --- a/exwm-xim.el +++ b/exwm-xim.el @@ -490,70 +490,8 @@ The actual XIM request is in client message data or a property." (exwm--log "FORWARD-EVENT") (setq req (make-instance 'xim:forward-event)) (xcb:unmarshal req data) - (let ((im-func (with-current-buffer (window-buffer) - input-method-function)) - key-event keysym event result) - ;; Note: The flag slot is ignored. - ;; Do conversion in client's byte-order. - (let ((xcb:lsb xim:lsb)) - (setq key-event (make-instance 'xcb:KeyPress)) - (xcb:unmarshal key-event (slot-value req 'event))) - (with-slots (detail state) key-event - (setq keysym (xcb:keysyms:keycode->keysym exwm-xim--conn detail - state)) - (when (/= (car keysym) 0) - (setq event (xcb:keysyms:keysym->event - exwm-xim--conn - (car keysym) - (logand state (lognot (cdr keysym))))))) - (if exwm-xim--event-pending - ;; In case any event reaches here, it should be forwarded - ;; to Emacs. - (when event - (setq unread-command-events - (append unread-command-events (list event)))) - (setq exwm-xim--event-pending t) - (if (or (not im-func) - ;; `list' is the default method. - (eq im-func #'list) - (not event) - ;; Select only printable keys. - (not (integerp event)) (> #x20 event) (< #x7e event)) - ;; Either there is no active input method, or invalid key - ;; is detected. - (with-slots (im-id ic-id serial-number event) req - (push (make-instance 'xim:forward-event - :im-id im-id - :ic-id ic-id - :flag xim:commit-flag:synchronous - :serial-number serial-number - :event event) - replies)) - (when (eq exwm--selected-input-mode 'char-mode) - ;; Grab keyboard temporarily for char-mode. - (exwm-input--grab-keyboard)) - (unwind-protect - (with-temp-buffer - ;; Always show key strokes. - (let ((input-method-use-echo-area t)) - (setq result (funcall im-func event)))) - (when (eq exwm--selected-input-mode 'char-mode) - (exwm-input--release-keyboard))) - ;; This also works for portable character encoding. - (setq result - (encode-coding-string (concat result) - 'compound-text-with-extensions)) - (message "") - (push - (make-instance 'xim:commit-x-lookup-chars - :im-id (slot-value req 'im-id) - :ic-id (slot-value req 'ic-id) - :flag (logior xim:commit-flag:synchronous - xim:commit-flag:x-lookup-chars) - :length (length result) - :string result) - replies)) - (setq exwm-xim--event-pending nil)))) + (exwm-xim--handle-forward-event-request req xim:lsb conn + client-xwin)) ((= opcode xim:opcode:sync) (exwm--log "SYNC") (setq req (make-instance 'xim:sync)) @@ -591,6 +529,78 @@ The actual XIM request is in client message data or a property." replies) (xcb:flush conn)))) +(defun exwm-xim--handle-forward-event-request (req lsb conn client-xwin) + (let ((im-func (with-current-buffer (window-buffer) + input-method-function)) + key-event keysym event result) + ;; Note: The flag slot is ignored. + ;; Do conversion in client's byte-order. + (let ((xcb:lsb lsb)) + (setq key-event (make-instance 'xcb:KeyPress)) + (xcb:unmarshal key-event (slot-value req 'event))) + (with-slots (detail state) key-event + (setq keysym (xcb:keysyms:keycode->keysym exwm-xim--conn detail + state)) + (when (/= (car keysym) 0) + (setq event (xcb:keysyms:keysym->event + exwm-xim--conn + (car keysym) + (logand state (lognot (cdr keysym))))))) + (if exwm-xim--event-pending + ;; In case any event reaches here, it should be forwarded + ;; to Emacs. + (when event + (setq unread-command-events + (append unread-command-events (list event)))) + (setq exwm-xim--event-pending t) + (if (or (not im-func) + ;; `list' is the default method. + (eq im-func #'list) + (not event) + ;; Select only printable keys. + (not (integerp event)) (> #x20 event) (< #x7e event)) + ;; Either there is no active input method, or invalid key + ;; is detected. + (with-slots (im-id ic-id serial-number event) req + (exwm-xim--make-request + (make-instance 'xim:forward-event + :im-id im-id + :ic-id ic-id + :flag xim:commit-flag:synchronous + :serial-number serial-number + :event event) + conn client-xwin) + (xcb:flush conn)) + (when (eq exwm--selected-input-mode 'char-mode) + ;; Grab keyboard temporarily for char-mode. + (exwm-input--grab-keyboard)) + (unwind-protect + (with-temp-buffer + ;; Always show key strokes. + (let ((input-method-use-echo-area t)) + (while (or event unread-command-events) + (unless event + (setq event (pop unread-command-events))) + (setq result (funcall im-func event) + event nil) + ;; This also works for portable character encoding. + (setq result + (encode-coding-string (concat result) + 'compound-text-with-extensions)) + (exwm-xim--make-request + (make-instance 'xim:commit-x-lookup-chars + :im-id (slot-value req 'im-id) + :ic-id (slot-value req 'ic-id) + :flag (logior xim:commit-flag:synchronous + xim:commit-flag:x-lookup-chars) + :length (length result) + :string result) + conn client-xwin) + (xcb:flush conn)))) + (when (eq exwm--selected-input-mode 'char-mode) + (exwm-input--release-keyboard)))) + (setq exwm-xim--event-pending nil)))) + (defun exwm-xim--make-request (req conn client-xwin) "Make an XIM request REQ via connection CONN. -- cgit 1.4.1 From 672a5e2a23e1d41609136170bfa851e25244e4c4 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 2 Jun 2019 00:00:00 +0000 Subject: Fix unreading 'return event * exwm-xim.el (exwm-xim--event-pending): Drop unused variable. (exwm-xim--handle-forward-event-request): Convert 'return back to ?\n to avoid error. --- exwm-xim.el | 81 +++++++++++++++++++++++++++++-------------------------------- 1 file changed, 38 insertions(+), 43 deletions(-) diff --git a/exwm-xim.el b/exwm-xim.el index 23512144c9..82e3351fd0 100644 --- a/exwm-xim.el +++ b/exwm-xim.el @@ -152,8 +152,6 @@ C,no" (defvar exwm-xim--property-index 0 "For generating a unique property name.") (defvar exwm-xim--im-id 0 "Last IM ID.") (defvar exwm-xim--ic-id 0 "Last IC ID.") -(defvar exwm-xim--event-pending nil - "Indicating whether Emacs requires more events.") ;; X11 atoms. (defvar exwm-xim--@server nil) @@ -546,43 +544,40 @@ The actual XIM request is in client message data or a property." exwm-xim--conn (car keysym) (logand state (lognot (cdr keysym))))))) - (if exwm-xim--event-pending - ;; In case any event reaches here, it should be forwarded - ;; to Emacs. - (when event - (setq unread-command-events - (append unread-command-events (list event)))) - (setq exwm-xim--event-pending t) - (if (or (not im-func) - ;; `list' is the default method. - (eq im-func #'list) - (not event) - ;; Select only printable keys. - (not (integerp event)) (> #x20 event) (< #x7e event)) - ;; Either there is no active input method, or invalid key - ;; is detected. - (with-slots (im-id ic-id serial-number event) req - (exwm-xim--make-request - (make-instance 'xim:forward-event - :im-id im-id - :ic-id ic-id - :flag xim:commit-flag:synchronous - :serial-number serial-number - :event event) - conn client-xwin) - (xcb:flush conn)) - (when (eq exwm--selected-input-mode 'char-mode) - ;; Grab keyboard temporarily for char-mode. - (exwm-input--grab-keyboard)) - (unwind-protect - (with-temp-buffer - ;; Always show key strokes. - (let ((input-method-use-echo-area t)) - (while (or event unread-command-events) - (unless event - (setq event (pop unread-command-events))) - (setq result (funcall im-func event) - event nil) + (if (or (not im-func) + ;; `list' is the default method. + (eq im-func #'list) + (not event) + ;; Select only printable keys. + (not (integerp event)) (> #x20 event) (< #x7e event)) + ;; Either there is no active input method, or invalid key + ;; is detected. + (with-slots (im-id ic-id serial-number event) req + (exwm-xim--make-request + (make-instance 'xim:forward-event + :im-id im-id + :ic-id ic-id + :flag xim:commit-flag:synchronous + :serial-number serial-number + :event event) + conn client-xwin) + (xcb:flush conn)) + (when (eq exwm--selected-input-mode 'char-mode) + ;; Grab keyboard temporarily for char-mode. + (exwm-input--grab-keyboard)) + (unwind-protect + (with-temp-buffer + ;; Always show key strokes. + (let ((input-method-use-echo-area t)) + (while (or event unread-command-events) + (unless event + (setq event (pop unread-command-events))) + ;; `quail-input-method' seems to unread 'return instead of + ;; ?\n hence this conversion. + (when (eq event 'return) + (setq event ?\n)) + (when (characterp event) + (setq result (funcall im-func event)) ;; This also works for portable character encoding. (setq result (encode-coding-string (concat result) @@ -596,10 +591,10 @@ The actual XIM request is in client message data or a property." :length (length result) :string result) conn client-xwin) - (xcb:flush conn)))) - (when (eq exwm--selected-input-mode 'char-mode) - (exwm-input--release-keyboard)))) - (setq exwm-xim--event-pending nil)))) + (xcb:flush conn)) + (setq event nil)))) + (when (eq exwm--selected-input-mode 'char-mode) + (exwm-input--release-keyboard)))))) (defun exwm-xim--make-request (req conn client-xwin) "Make an XIM request REQ via connection CONN. -- cgit 1.4.1 From 75833e0c53fa92d9b7c7f6ab78e206df6e07f9b9 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 9 Jun 2019 00:00:00 +0000 Subject: Fix character loss with exwm-xim * exwm-xim.el (exwm-xim--handle-forward-event-request): Events unread can either be reused by input methods or forwarded to X windows as is. --- exwm-xim.el | 107 ++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 57 insertions(+), 50 deletions(-) diff --git a/exwm-xim.el b/exwm-xim.el index 82e3351fd0..f6cd5299be 100644 --- a/exwm-xim.el +++ b/exwm-xim.el @@ -544,57 +544,64 @@ The actual XIM request is in client message data or a property." exwm-xim--conn (car keysym) (logand state (lognot (cdr keysym))))))) - (if (or (not im-func) - ;; `list' is the default method. - (eq im-func #'list) - (not event) - ;; Select only printable keys. - (not (integerp event)) (> #x20 event) (< #x7e event)) - ;; Either there is no active input method, or invalid key - ;; is detected. - (with-slots (im-id ic-id serial-number event) req - (exwm-xim--make-request - (make-instance 'xim:forward-event - :im-id im-id - :ic-id ic-id - :flag xim:commit-flag:synchronous - :serial-number serial-number - :event event) - conn client-xwin) - (xcb:flush conn)) - (when (eq exwm--selected-input-mode 'char-mode) - ;; Grab keyboard temporarily for char-mode. - (exwm-input--grab-keyboard)) - (unwind-protect - (with-temp-buffer - ;; Always show key strokes. - (let ((input-method-use-echo-area t)) - (while (or event unread-command-events) - (unless event - (setq event (pop unread-command-events))) - ;; `quail-input-method' seems to unread 'return instead of - ;; ?\n hence this conversion. - (when (eq event 'return) - (setq event ?\n)) - (when (characterp event) - (setq result (funcall im-func event)) - ;; This also works for portable character encoding. - (setq result - (encode-coding-string (concat result) - 'compound-text-with-extensions)) - (exwm-xim--make-request - (make-instance 'xim:commit-x-lookup-chars - :im-id (slot-value req 'im-id) - :ic-id (slot-value req 'ic-id) - :flag (logior xim:commit-flag:synchronous - xim:commit-flag:x-lookup-chars) - :length (length result) - :string result) - conn client-xwin) - (xcb:flush conn)) - (setq event nil)))) + (while (or (slot-value req 'event) unread-command-events) + (unless (slot-value req 'event) + (setq event (pop unread-command-events))) + (if (or (not im-func) + ;; `list' is the default method. + (eq im-func #'list) + (not event) + ;; Select only printable keys. + (not (integerp event)) (> #x20 event) (< #x7e event)) + ;; Either there is no active input method, or invalid key + ;; is detected. + (with-slots ((raw-event event) + im-id ic-id serial-number) + req + (if raw-event + (setq event raw-event) + (setq keysym (xcb:keysyms:event->keysym exwm-xim--conn event)) + (with-slots (detail state) key-event + (setf detail (xcb:keysyms:keysym->keycode exwm-xim--conn + (car keysym)) + state (cdr keysym))) + (setq event (let ((xcb:lsb lsb)) + (xcb:marshal key-event conn)))) + (when event + (exwm-xim--make-request + (make-instance 'xim:forward-event + :im-id im-id + :ic-id ic-id + :flag xim:commit-flag:synchronous + :serial-number serial-number + :event event) + conn client-xwin))) (when (eq exwm--selected-input-mode 'char-mode) - (exwm-input--release-keyboard)))))) + ;; Grab keyboard temporarily for char-mode. + (exwm-input--grab-keyboard)) + (unwind-protect + (with-temp-buffer + ;; Always show key strokes. + (let ((input-method-use-echo-area t)) + (setq result (funcall im-func event)) + ;; This also works for portable character encoding. + (setq result + (encode-coding-string (concat result) + 'compound-text-with-extensions)) + (exwm-xim--make-request + (make-instance 'xim:commit-x-lookup-chars + :im-id (slot-value req 'im-id) + :ic-id (slot-value req 'ic-id) + :flag (logior xim:commit-flag:synchronous + xim:commit-flag:x-lookup-chars) + :length (length result) + :string result) + conn client-xwin))) + (when (eq exwm--selected-input-mode 'char-mode) + (exwm-input--release-keyboard)))) + (xcb:flush conn) + (setf event nil + (slot-value req 'event) nil)))) (defun exwm-xim--make-request (req conn client-xwin) "Make an XIM request REQ via connection CONN. -- cgit 1.4.1 From 8a54504152220e660b7df33dacda3b58b7532563 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 9 Jun 2019 00:00:00 +0000 Subject: Handle (t . EVENT) format events in exwm-xim * exwm-xim.el (exwm-xim--handle-forward-event-request): Ditto. --- exwm-xim.el | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/exwm-xim.el b/exwm-xim.el index f6cd5299be..344f8c64cd 100644 --- a/exwm-xim.el +++ b/exwm-xim.el @@ -546,7 +546,11 @@ The actual XIM request is in client message data or a property." (logand state (lognot (cdr keysym))))))) (while (or (slot-value req 'event) unread-command-events) (unless (slot-value req 'event) - (setq event (pop unread-command-events))) + (setq event (pop unread-command-events)) + ;; Handle events in (t . EVENT) format. + (when (and (consp event) + (eq (car event) t)) + (setq event (cdr event)))) (if (or (not im-func) ;; `list' is the default method. (eq im-func #'list) -- cgit 1.4.1 From aa92c7be8cb92ae74617b8d3d431431d2aa7edac Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 16 Jun 2019 00:00:00 +0000 Subject: Avoid workspace switch loop with `mouse-autoselect-window' enabled * exwm-input.el (exwm-input--last-enter-notify-position): New variable storing last mouse position. (exwm-input--on-EnterNotify): Avoid switching workspace when mouse position is not changed (the event is a result of a workspace switch). --- exwm-input.el | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index eeea36e5a2..22c8002be6 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -219,6 +219,8 @@ ARGS are additional arguments to CALLBACK." (cdr exwm-input--timestamp-callback)) (setq exwm-input--timestamp-callback nil))))) +(defvar exwm-input--last-enter-notify-position nil) + (defun exwm-input--on-EnterNotify (data _synthetic) "Handle EnterNotify events." (let ((evt (make-instance 'xcb:EnterNotify)) @@ -228,7 +230,9 @@ ARGS are additional arguments to CALLBACK." (setq buffer (exwm--id->buffer event) window (get-buffer-window buffer t)) (exwm--log "buffer=%s; window=%s" buffer window) - (when (and buffer window (not (eq window (selected-window)))) + (when (and buffer window (not (eq window (selected-window))) + (not (equal exwm-input--last-enter-notify-position + (vector root-x root-y)))) (setq frame (window-frame window) frame-xid (frame-parameter frame 'exwm-id)) (unless (eq frame exwm-workspace--current) @@ -260,7 +264,8 @@ ARGS are additional arguments to CALLBACK." :destination frame-xid :event-mask xcb:EventMask:NoEvent :event (xcb:marshal fake-evt exwm--connection))) - (xcb:flush exwm--connection))))) + (xcb:flush exwm--connection)) + (setq exwm-input--last-enter-notify-position (vector root-x root-y))))) (defun exwm-input--on-keysyms-update () (exwm--log) -- cgit 1.4.1 From 605b0a9575564c5e864e3a6381d18e3dcbfbde04 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 16 Jun 2019 00:00:00 +0000 Subject: Allow panel to hide floating X windows * exwm.el (exwm--on-ClientMessage): Use `exwm-floating-hide' to hide X windows on receiving `WM_CHANGE_STATE' events. --- exwm.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/exwm.el b/exwm.el index 7d301ab4bd..ec49748c2d 100644 --- a/exwm.el +++ b/exwm.el @@ -555,7 +555,9 @@ (when (and (buffer-live-p buffer) (= (elt data 0) xcb:icccm:WM_STATE:IconicState)) (with-current-buffer buffer - (bury-buffer))))) + (if exwm--floating-frame + (call-interactively #'exwm-floating-hide) + (bury-buffer)))))) (t (exwm--log "Unhandled: %s(%d)" (x-get-atom-name type exwm-workspace--current) type))))) -- cgit 1.4.1 From fe8ee3c5786bf8c1c69cef1b2ad6bb6b2d785a6d Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 30 Jun 2019 00:00:00 +0000 Subject: Fix 'Attempt to delete a surrogate minibuffer frame' error * exwm-workspace.el (exwm-workspace--get-remove-frame-next-workspace): New function automatically moves X window elsewhere before removing a workspace; also returns the destination workspace. (exwm-workspace--prompt-delete, exwm-workspace-delete) (exwm-workspace--remove-frame-as-workspace): Use it. * exwm.el (exwm--on-ClientMessage): Use it. --- exwm-workspace.el | 36 ++++++++++++++++++++++++------------ exwm.el | 4 +++- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index cca4ed8c43..183d91a34c 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -240,6 +240,7 @@ NIL if FRAME is not a workspace" (when (and exwm-workspace--prompt-delete-allowed (< 1 (exwm-workspace--count))) (let ((frame (elt exwm-workspace--list (1- minibuffer-history-position)))) + (exwm-workspace--get-remove-frame-next-workspace frame) (if (eq frame exwm-workspace--current) ;; Abort the recursive minibuffer if deleting the current workspace. (progn @@ -778,10 +779,12 @@ INDEX must not exceed the current number of workspaces." (interactive) (exwm--log "%s" frame-or-index) (when (< 1 (exwm-workspace--count)) - (delete-frame - (if frame-or-index - (exwm-workspace--workspace-from-frame-or-index frame-or-index) - exwm-workspace--current)))) + (let ((frame (if frame-or-index + (exwm-workspace--workspace-from-frame-or-index + frame-or-index) + exwm-workspace--current))) + (exwm-workspace--get-remove-frame-next-workspace frame) + (delete-frame frame)))) (defun exwm-workspace--set-desktop (id) "Set _NET_WM_DESKTOP for X window ID." @@ -1330,23 +1333,32 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (exwm-workspace-switch frame t) (run-hooks 'exwm-workspace-list-change-hook))) +(defun exwm-workspace--get-remove-frame-next-workspace (frame) + "Return the next workspace if workspace FRAME is removed. + +All X windows currently on workspace FRAME will be automatically moved to +the next workspace." + (let* ((index (exwm-workspace--position frame)) + (lastp (= index (1- (exwm-workspace--count)))) + (nextw (elt exwm-workspace--list (+ index (if lastp -1 +1))))) + ;; Clients need to be moved to some other workspace before this being + ;; removed. + (dolist (pair exwm--id-buffer-alist) + (with-current-buffer (cdr pair) + (when (eq exwm--frame frame) + (exwm-workspace-move-window nextw exwm--id)))) + nextw)) + (defun exwm-workspace--remove-frame-as-workspace (frame) "Stop treating frame FRAME as a workspace." ;; TODO: restore all frame parameters (e.g. exwm-workspace, buffer-predicate, ;; etc) (exwm--log "Removing frame `%s' as workspace" frame) (let* ((index (exwm-workspace--position frame)) - (lastp (= index (1- (exwm-workspace--count)))) - (nextw (elt exwm-workspace--list (+ index (if lastp -1 +1))))) + (nextw (exwm-workspace--get-remove-frame-next-workspace frame))) ;; Need to remove the workspace from the list in order for ;; the correct calculation of indexes. (setq exwm-workspace--list (delete frame exwm-workspace--list)) - ;; Clients need to be moved to some other workspace before this is being - ;; removed. - (dolist (pair exwm--id-buffer-alist) - (with-current-buffer (cdr pair) - (when (eq exwm--frame frame) - (exwm-workspace-move-window nextw exwm--id)))) ;; Update the _NET_WM_DESKTOP property of each X window affected. (dolist (pair exwm--id-buffer-alist) (when (<= (1- index) diff --git a/exwm.el b/exwm.el index ec49748c2d..9ebfabf9e8 100644 --- a/exwm.el +++ b/exwm.el @@ -432,7 +432,9 @@ (make-frame)) ((and (> current requested) (> current 1)) - (delete-frame (car (last exwm-workspace--list))))))) + (let ((frame (car (last exwm-workspace--list)))) + (exwm-workspace--get-remove-frame-next-workspace frame) + (delete-frame frame)))))) ;; _NET_CURRENT_DESKTOP. ((= type xcb:Atom:_NET_CURRENT_DESKTOP) (exwm-workspace-switch (elt data 0))) -- cgit 1.4.1 From 0a3dde042a6ff671bd728bf1943fd3a743371161 Mon Sep 17 00:00:00 2001 From: Sebastian Wålinder Date: Sun, 30 Jun 2019 18:21:24 +0200 Subject: Added option to have a key that ends exwm-input-send-next-key * exwm-input.el (exwm-input-send-next-key): Accept an optional end key. --- exwm-input.el | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 22c8002be6..351820ca7e 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -845,11 +845,11 @@ button event." (xcb:flush exwm--connection))) ;;;###autoload -(cl-defun exwm-input-send-next-key (times) +(cl-defun exwm-input-send-next-key (times &optional end-key) "Send next key to client window. EXWM will prompt for the key to send. This command can be prefixed to send -multiple keys." +multiple keys. If END-KEY is non-nil, stop sending keys if it's pressed." (interactive "p") (exwm--log) (unless (derived-mode-p 'exwm-mode) @@ -861,11 +861,17 @@ multiple keys." (let ((exwm-input-line-mode-passthrough t)) (catch 'break (while t - (setq key (read-key (format "Send key: %s (%d/%d)" + (setq key (read-key (format "Send key: %s (%d/%d) %s" (key-description keys) - (1+ i) times))) + (1+ i) times + (if end-key + (concat "To exit, press: " + (key-description + (list end-key))) + "")))) (unless (listp key) (throw 'break nil))))) (setq keys (vconcat keys (vector key))) + (when (eq key end-key) (cl-return-from exwm-input-send-next-key)) (exwm-input--fake-key key)))) (defun exwm-input--set-simulation-keys (simulation-keys &optional no-refresh) -- cgit 1.4.1 From c5794765362460b5c18fdedb3f77617fb34ee804 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 28 Jul 2019 00:00:00 +0000 Subject: Fix `after-focus-change-function' not working * exwm-workspace.el (exwm-workspace--original-handle-focus-in) (exwm-workspace--original-handle-focus-out): Store the original `handle-focus-{in,out}'. (exwm-workspace-switch): Now that `handle-focus-{in,out}' has been updated to call other stuffs like `after-focus-change-function', we can no longer run `focus-{in,out}-hook' only. --- exwm-workspace.el | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 183d91a34c..a4ccc2f059 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -106,6 +106,11 @@ Please manually run the hook `exwm-workspace-list-change-hook' afterwards.") (defvar exwm-workspace--minibuffer nil "The minibuffer frame shared among all frames.") +(defvar exwm-workspace--original-handle-focus-in + (symbol-function #'handle-focus-in)) +(defvar exwm-workspace--original-handle-focus-out + (symbol-function #'handle-focus-out)) + (defvar exwm-workspace--prompt-add-allowed nil "Non-nil to allow adding workspace from the prompt.") @@ -648,8 +653,9 @@ for internal use only." (when (and (not (eq frame old-frame)) (frame-live-p old-frame)) (with-selected-frame old-frame - (run-hooks 'focus-out-hook))) - (run-hooks 'focus-in-hook) + (funcall exwm-workspace--original-handle-focus-out + (list 'focus-out frame)))) + (funcall exwm-workspace--original-handle-focus-in (list 'focus-in frame)) (run-hooks 'exwm-workspace-switch-hook))) ;;;###autoload -- cgit 1.4.1 From 2c0dcc46cdf4a51aa7f082492290d9fb5a3537bf Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 5 Aug 2019 00:00:00 +0000 Subject: Remove hard-coded keys in menu * exwm-core.el (exwm-mode-menu): Avoid hard-coding keys in `exwm-mode-menu'. --- exwm-core.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 39928e1565..287fc6f71b 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -251,7 +251,7 @@ One of `line-mode' or `char-mode'.") "---" "*Resizing*" "---" - ["Toggle mode-line" exwm-layout-toggle-mode-line :keys "C-c C-t C-m"] + ["Toggle mode-line" exwm-layout-toggle-mode-line] ["Enlarge window vertically" exwm-layout-enlarge-window] ["Enlarge window horizontally" exwm-layout-enlarge-window-horizontally] ["Shrink window vertically" exwm-layout-shrink-window] @@ -289,7 +289,7 @@ One of `line-mode' or `char-mode'.") ["Delete current workspace" exwm-workspace-delete] ["Move workspace to" exwm-workspace-move] ["Swap workspaces" exwm-workspace-swap] - ["Move X window to" exwm-workspace-move-window :keys "C-c C-m"] + ["Move X window to" exwm-workspace-move-window] ["Move X window from" exwm-workspace-switch-to-buffer] ["Toggle minibuffer" exwm-workspace-toggle-minibuffer] ["Switch workspace" exwm-workspace-switch] -- cgit 1.4.1 From 37098a400994948fe99a2bb944fc2c66e0c71b6a Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Mon, 5 Aug 2019 00:00:00 +0000 Subject: Fix detection of modifier keys in Emacs events * exwm-input.el (exwm-input--grab-global-prefix-keys) (exwm-input--fake-key): * exwm-xim.el (exwm-xim--handle-forward-event-request): X11 allows multiple combinations of KEYSYM-MODIFIERS to generate a same KEYSYM, thus the result of an Emacs event to KEYSYM-MODIFIERS conversion is not necessarily unique. Previously the result of `xcb:keysyms:event->keysym' is misused as the modifiers returned is actually the ones should be consumed. --- exwm-input.el | 39 +++++++++++++++++++++------------------ exwm-xim.el | 8 ++++---- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 351820ca7e..ababbb6e78 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -488,23 +488,26 @@ ARGS are additional arguments to CALLBACK." :key nil :pointer-mode xcb:GrabMode:Async :keyboard-mode xcb:GrabMode:Async)) - keysym keycode) + keysyms keycode alt-modifier) (dolist (k exwm-input--global-prefix-keys) - (setq keysym (xcb:keysyms:event->keysym exwm--connection k) + (setq keysyms (xcb:keysyms:event->keysyms exwm--connection k) keycode (xcb:keysyms:keysym->keycode exwm--connection - (car keysym))) - (exwm--log "Grabbing key=%s (keysym=%s keycode=%s)" - (single-key-description k) keysym keycode) - (setf (slot-value req 'modifiers) (cdr keysym) - (slot-value req 'key) keycode) - (dolist (xwin xwins) - (setf (slot-value req 'grab-window) xwin) - (xcb:+request exwm--connection req) + (caar keysyms))) + (exwm--log "Grabbing key=%s (keysyms=%s keycode=%s)" + (single-key-description k) keysyms keycode) + (dolist (keysym keysyms) + (setf (slot-value req 'modifiers) (cdr keysym) + (slot-value req 'key) keycode) ;; Also grab this key with num-lock mask set. - (when (/= 0 xcb:keysyms:num-lock-mask) - (setf (slot-value req 'modifiers) - (logior (cdr keysym) xcb:keysyms:num-lock-mask)) - (xcb:+request exwm--connection req)))) + (when (and (/= 0 xcb:keysyms:num-lock-mask) + (= 0 (logand (cdr keysym) xcb:keysyms:num-lock-mask))) + (setf alt-modifier (logior (cdr keysym) xcb:keysyms:num-lock-mask))) + (dolist (xwin xwins) + (setf (slot-value req 'grab-window) xwin) + (xcb:+request exwm--connection req) + (when alt-modifier + (setf (slot-value req 'modifiers) alt-modifier) + (xcb:+request exwm--connection req))))) (xcb:flush exwm--connection))) (defun exwm-input--set-key (key command) @@ -817,12 +820,12 @@ button event." (defun exwm-input--fake-key (event) "Fake a key event equivalent to Emacs event EVENT." - (let* ((keysym (xcb:keysyms:event->keysym exwm--connection event)) + (let* ((keysyms (xcb:keysyms:event->keysyms exwm--connection event)) keycode id) - (when (= 0 (car keysym)) + (when (= 0 (caar keysyms)) (user-error "[EXWM] Invalid key: %s" (single-key-description event))) (setq keycode (xcb:keysyms:keysym->keycode exwm--connection - (car keysym))) + (caar keysyms))) (when (/= 0 keycode) (setq id (exwm--buffer->id (window-buffer (selected-window)))) (exwm--log "id=#x%x event=%s keycode" id event keycode) @@ -839,7 +842,7 @@ button event." :child 0 :root-x 0 :root-y 0 :event-x 0 :event-y 0 - :state (cdr keysym) + :state (cdar keysyms) :same-screen 1) exwm--connection))))) (xcb:flush exwm--connection))) diff --git a/exwm-xim.el b/exwm-xim.el index 344f8c64cd..dc22f82fc4 100644 --- a/exwm-xim.el +++ b/exwm-xim.el @@ -530,7 +530,7 @@ The actual XIM request is in client message data or a property." (defun exwm-xim--handle-forward-event-request (req lsb conn client-xwin) (let ((im-func (with-current-buffer (window-buffer) input-method-function)) - key-event keysym event result) + key-event keysym keysyms event result) ;; Note: The flag slot is ignored. ;; Do conversion in client's byte-order. (let ((xcb:lsb lsb)) @@ -564,11 +564,11 @@ The actual XIM request is in client message data or a property." req (if raw-event (setq event raw-event) - (setq keysym (xcb:keysyms:event->keysym exwm-xim--conn event)) + (setq keysyms (xcb:keysyms:event->keysyms exwm-xim--conn event)) (with-slots (detail state) key-event (setf detail (xcb:keysyms:keysym->keycode exwm-xim--conn - (car keysym)) - state (cdr keysym))) + (caar keysyms)) + state (cdar keysyms))) (setq event (let ((xcb:lsb lsb)) (xcb:marshal key-event conn)))) (when event -- cgit 1.4.1 From dd6f5c36edf62f8c9d3c98a77b0d22b141663f6c Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 11 Aug 2019 00:00:00 +0000 Subject: Inform user about making a frame a workspace * exwm-workspace.el (exwm-workspace--add-frame-as-workspace): Add a message. (exwm-workspace--init): Exclude initial workspaces. --- exwm-workspace.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index a4ccc2f059..f536bc5a81 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1337,6 +1337,7 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (if exwm-workspace--create-silently (setq exwm-workspace--switch-history-outdated t) (exwm-workspace-switch frame t) + (message "Added %s as workspace %d" frame exwm-workspace-current-index) (run-hooks 'exwm-workspace-list-change-hook))) (defun exwm-workspace--get-remove-frame-next-workspace (frame) @@ -1629,8 +1630,9 @@ applied to all subsequently created X frames." (nconc initial-workspaces (list (make-frame '((window-system . x) (client . nil)))))) ;; Configure workspaces - (dolist (i initial-workspaces) - (exwm-workspace--add-frame-as-workspace i))) + (let ((exwm-workspace--create-silently t)) + (dolist (i initial-workspaces) + (exwm-workspace--add-frame-as-workspace i)))) (xcb:flush exwm--connection) ;; We have to advice `x-create-frame' or every call to it would hang EXWM (advice-add 'x-create-frame :around #'exwm-workspace--x-create-frame) -- cgit 1.4.1 From 1f2bd54c117b3eeb88b6af1e8627030f00e37898 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 11 Aug 2019 00:00:00 +0000 Subject: Fix a regression with systemtray * exwm-systemtray.el (exwm-systemtray--on-workspace-switch) (exwm-systemtray--on-randr-refresh): Instead of retrieving the real frame height, manually calculate it with workarea height and menu-bar/tool-bar size. --- exwm-systemtray.el | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/exwm-systemtray.el b/exwm-systemtray.el index 80505c22a5..a095bfa8de 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -331,27 +331,37 @@ You shall use the default value if using auto-hide minibuffer." "Reparent/Refresh the system tray in `exwm-workspace-switch-hook'." (exwm--log) (unless (exwm-workspace--minibuffer-own-frame-p) - (xcb:+request exwm-systemtray--connection - (make-instance 'xcb:ReparentWindow - :window exwm-systemtray--embedder-window - :parent (string-to-number - (frame-parameter exwm-workspace--current - 'window-id)) - :x 0 - :y (- (frame-pixel-height exwm-workspace--current) - exwm-systemtray-height)))) + (let ((geometry (frame-geometry exwm-workspace--current))) + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:ReparentWindow + :window exwm-systemtray--embedder-window + :parent (string-to-number + (frame-parameter exwm-workspace--current + 'window-id)) + :x 0 + :y (- (elt (elt exwm-workspace--workareas + exwm-workspace-current-index) + 3) + (or (cddr (assq 'menu-bar-size geometry)) 0) + (or (cddr (assq 'tool-bar-size geometry)) 0) + exwm-systemtray-height))))) (exwm-systemtray--refresh)) (defun exwm-systemtray--on-randr-refresh () "Reposition/Refresh the system tray in `exwm-randr-refresh-hook'." (exwm--log) (unless (exwm-workspace--minibuffer-own-frame-p) - (xcb:+request exwm-systemtray--connection - (make-instance 'xcb:ConfigureWindow - :window exwm-systemtray--embedder-window - :value-mask xcb:ConfigWindow:Y - :y (- (frame-pixel-height exwm-workspace--current) - exwm-systemtray-height)))) + (let ((geometry (frame-geometry exwm-workspace--current))) + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:ConfigureWindow + :window exwm-systemtray--embedder-window + :value-mask xcb:ConfigWindow:Y + :y (- (elt (elt exwm-workspace--workareas + exwm-workspace-current-index) + 3) + (or (cddr (assq 'menu-bar-size geometry)) 0) + (or (cddr (assq 'tool-bar-size geometry)) 0) + exwm-systemtray-height))))) (exwm-systemtray--refresh)) (defalias 'exwm-systemtray--on-struts-update -- cgit 1.4.1 From a1cf0d9b85e1d1d4f3b50a9d51980688b7b816df Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 11 Aug 2019 00:00:00 +0000 Subject: Avoid checking `*temp*' buffers * exwm-input.el (exwm-input--on-buffer-list-update): The way of detecting a switch from a `*temp*' buffer does not always work. Disable it until we find a better way. --- exwm-input.el | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index ababbb6e78..2b421d02b6 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -277,11 +277,7 @@ ARGS are additional arguments to CALLBACK." (when (and (not (eq this-command #'handle-switch-frame)) (not exwm-input--skip-buffer-list-update) (not (exwm-workspace--client-p)) - ;; The following conditions filter out events relating to temp - ;; buffers. - (eq (current-buffer) (window-buffer)) - (not (string-prefix-p " *temp*" - (buffer-name (car (last (buffer-list))))))) + (eq (current-buffer) (window-buffer))) (exwm--log "current-buffer=%S selected-window=%S" (current-buffer) (selected-window)) (redirect-frame-focus (selected-frame) nil) -- cgit 1.4.1 From d78c562f9a90055b595cb783928af43597b5ca6c Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 11 Aug 2019 00:00:00 +0000 Subject: Make `exwm-manage-configurations' more user friendly * exwm-manage.el (exwm-manage-configurations): Specify type for each configuration option. --- exwm-manage.el | 78 ++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index 6f89fff4f0..797fc15cc6 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -74,28 +74,64 @@ criterion would be applied." :type '(alist :key-type (sexp :tag "Matching criterion" nil) :value-type (plist :tag "Configurations" - :key-type - (choice - (const :tag "Floating" floating) - (const :tag "X" x) - (const :tag "Y" y) - (const :tag "Width" width) - (const :tag "Height" height) - (const :tag "Border width" border-width) - (const :tag "Fullscreen" fullscreen) - (const :tag "Floating mode-line" floating-mode-line) - (const :tag "Tiling mode-line" tiling-mode-line) - (const :tag "Floating header-line" - floating-header-line) - (const :tag "Tiling header-line" tiling-header-line) - (const :tag "Char-mode" char-mode) - (const :tag "Prefix keys" prefix-keys) - (const :tag "Simulation keys" simulation-keys) - (const :tag "Workspace" workspace) - (const :tag "Managed" managed) + :options + (((const :tag "Floating" floating) boolean) + ((const :tag "X" x) number) + ((const :tag "Y" y) number) + ((const :tag "Width" width) number) + ((const :tag "Height" height) number) + ((const :tag "Border width" border-width) integer) + ((const :tag "Fullscreen" fullscreen) boolean) + ((const :tag "Floating mode-line" floating-mode-line) + sexp) + ((const :tag "Tiling mode-line" tiling-mode-line) sexp) + ((const :tag "Floating header-line" + floating-header-line) + sexp) + ((const :tag "Tiling header-line" tiling-header-line) + sexp) + ((const :tag "Char-mode" char-mode) boolean) + ((const :tag "Prefix keys" prefix-keys) + (repeat key-sequence)) + ((const :tag "Simulation keys" simulation-keys) + (alist :key-type (key-sequence :tag "From") + :value-type (key-sequence :tag "To"))) + ((const :tag "Workspace" workspace) integer) + ((const :tag "Managed" managed) boolean) ;; For forward compatibility. - (other)) - :value-type (sexp :tag "Value" nil)))) + ((other) sexp)))) + ;; TODO: This is admittedly ugly. We'd be better off with an event type. + :get (lambda (symbol) + (mapcar (lambda (pair) + (let* ((match (car pair)) + (config (cdr pair)) + (prefix-keys (plist-get config 'prefix-keys))) + (when prefix-keys + (setq config (copy-tree config) + config (plist-put config 'prefix-keys + (mapcar (lambda (i) + (if (sequencep i) + i + (vector i))) + prefix-keys)))) + (cons match config))) + (default-value symbol))) + :set (lambda (symbol value) + (set symbol + (mapcar (lambda (pair) + (let* ((match (car pair)) + (config (cdr pair)) + (prefix-keys (plist-get config 'prefix-keys))) + (when prefix-keys + (setq config (copy-tree config) + config (plist-put config 'prefix-keys + (mapcar (lambda (i) + (if (sequencep i) + (aref i 0) + i)) + prefix-keys)))) + (cons match config))) + value)))) ;; FIXME: Make the following values as small as possible. (defconst exwm-manage--height-delta-min 5) -- cgit 1.4.1 From 5b9f4b0851d27e8132a930dfbfec0b94099a4c9f Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 18 Aug 2019 00:00:00 +0000 Subject: Make it possible to answer questions from Emacs when in char-mode * exwm-input.el (exwm-input--echo-area-timer): New variable storing the timer used for detecting echo area messages. (exwm-input--on-minibuffer-setup, exwm-input--on-minibuffer-exit) (exwm-input--on-echo-area-dirty, exwm-input--on-echo-area-clear): New functions for grabbing/releasing keyboard when minibuffer/echo becomes active/inactive. (exwm-input--init, exwm-input--exit): Register/Unregister them. (exwm-input--grab-keyboard, exwm-input--release-keyboard): Validate buffers. --- exwm-input.el | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 2b421d02b6..52370bf8f4 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -140,6 +140,8 @@ defined in `exwm-mode-map' here." (defvar exwm-input--update-focus-window nil "The (Emacs) window to be focused. This value should always be overwritten.") +(defvar exwm-input--echo-area-timer nil "Timer for detecting echo area dirty.") + (defvar exwm-input--event-hook nil "Hook to run when EXWM receives an event.") @@ -760,8 +762,10 @@ button event." :pointer-mode xcb:GrabMode:Async :keyboard-mode xcb:GrabMode:Sync)) (exwm--log "Failed to grab keyboard for #x%x" id)) - (with-current-buffer (exwm--id->buffer id) - (setq exwm--input-mode 'line-mode)))) + (let ((buffer (exwm--id->buffer id))) + (when buffer + (with-current-buffer buffer + (setq exwm--input-mode 'line-mode)))))) (defun exwm-input--release-keyboard (&optional id) "Ungrab all key events on window ID." @@ -775,8 +779,10 @@ button event." :modifiers xcb:ModMask:Any)) (exwm--log "Failed to release keyboard for #x%x" id)) (exwm-input--grab-global-prefix-keys id) - (with-current-buffer (exwm--id->buffer id) - (setq exwm--input-mode 'char-mode)))) + (let ((buffer (exwm--id->buffer id))) + (when buffer + (with-current-buffer buffer + (setq exwm--input-mode 'char-mode)))))) ;;;###autoload (defun exwm-input-grab-keyboard (&optional id) @@ -1027,6 +1033,39 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences." "Run in `post-command-hook'." (setq exwm-input--during-command nil)) +(defun exwm-input--on-minibuffer-setup () + "Run in `minibuffer-setup-hook' to grab keyboard if necessary." + (exwm--log) + (with-current-buffer + (window-buffer (frame-selected-window exwm-workspace--current)) + (when (and (derived-mode-p 'exwm-mode) + (eq exwm--selected-input-mode 'char-mode)) + (exwm-input--grab-keyboard exwm--id)))) + +(defun exwm-input--on-minibuffer-exit () + "Run in `minibuffer-exit-hook' to release keyboard if necessary." + (exwm--log) + (with-current-buffer + (window-buffer (frame-selected-window exwm-workspace--current)) + (when (and (derived-mode-p 'exwm-mode) + (eq exwm--selected-input-mode 'char-mode) + (eq exwm--input-mode 'line-mode)) + (exwm-input--release-keyboard exwm--id)))) + +(defun exwm-input--on-echo-area-dirty () + "Run when new message arrives to grab keyboard if necessary." + (exwm--log) + (when (and (not (active-minibuffer-window)) + (not (exwm-workspace--client-p)) + cursor-in-echo-area) + (exwm-input--on-minibuffer-setup))) + +(defun exwm-input--on-echo-area-clear () + "Run in `echo-area-clear-hook' to release keyboard if necessary." + (exwm--log) + (unless (current-message) + (exwm-input--on-minibuffer-exit))) + (defun exwm-input--init () "Initialize the keyboard module." (exwm--log) @@ -1075,6 +1114,12 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences." ;; Control `exwm-input--during-command' (add-hook 'pre-command-hook #'exwm-input--on-pre-command) (add-hook 'post-command-hook #'exwm-input--on-post-command) + ;; Grab/Release keyboard when minibuffer/echo becomes active/inactive. + (add-hook 'minibuffer-setup-hook #'exwm-input--on-minibuffer-setup) + (add-hook 'minibuffer-exit-hook #'exwm-input--on-minibuffer-exit) + (setq exwm-input--echo-area-timer + (run-with-idle-timer 0 t #'exwm-input--on-echo-area-dirty)) + (add-hook 'echo-area-clear-hook #'exwm-input--on-echo-area-clear) ;; Update focus when buffer list updates (add-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update)) @@ -1089,6 +1134,12 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences." (exwm-input--unset-simulation-keys) (remove-hook 'pre-command-hook #'exwm-input--on-pre-command) (remove-hook 'post-command-hook #'exwm-input--on-post-command) + (remove-hook 'minibuffer-setup-hook #'exwm-input--on-minibuffer-setup) + (remove-hook 'minibuffer-exit-hook #'exwm-input--on-minibuffer-exit) + (when exwm-input--echo-area-timer + (cancel-timer exwm-input--echo-area-timer) + (setq exwm-input--echo-area-timer nil)) + (remove-hook 'echo-area-clear-hook #'exwm-input--on-echo-area-clear) (remove-hook 'buffer-list-update-hook #'exwm-input--on-buffer-list-update) (when exwm-input--update-focus-defer-timer (cancel-timer exwm-input--update-focus-defer-timer)) -- cgit 1.4.1 From 5505cff826b361d5d0090a6314891005595e3279 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 25 Aug 2019 00:00:00 +0000 Subject: Fix a lock issue with _NET_CLOSE_WINDOW and WM_DELETE_WINDOW * exwm.el (exwm--on-ClientMessage): Calling `kill-buffer' directly from an event context won't work since the DestroyNotify event for a WM_DELETE_WINDOW request won't be handled until the current event context terminates. * exwm-manage.el (exwm-manage--kill-buffer-query-function): Avoid potential side effects with MapWindow. --- exwm-manage.el | 6 ++++-- exwm.el | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/exwm-manage.el b/exwm-manage.el index 797fc15cc6..deb475d53a 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -513,8 +513,10 @@ manager is shutting down." (catch 'return (when (or (not exwm--id) (xcb:+request-checked+request-check exwm--connection - (make-instance 'xcb:MapWindow - :window exwm--id))) + (make-instance 'xcb:ChangeWindowAttributes + :window exwm--id + :value-mask xcb:CW:EventMask + :event-mask (exwm--get-client-event-mask)))) ;; The X window is no longer alive so just close the buffer. (when exwm--floating-frame (let ((window (frame-parameter exwm--floating-frame 'exwm-outer-id)) diff --git a/exwm.el b/exwm.el index 9ebfabf9e8..8ea4a7636f 100644 --- a/exwm.el +++ b/exwm.el @@ -420,7 +420,8 @@ (setq type (slot-value obj 'type) id (slot-value obj 'window) data (slot-value (slot-value obj 'data) 'data32)) - (exwm--log "atom=%s(%s)" (x-get-atom-name type exwm-workspace--current) type) + (exwm--log "atom=%s(%s)" (x-get-atom-name type exwm-workspace--current) + type) (cond ;; _NET_NUMBER_OF_DESKTOPS. ((= type xcb:Atom:_NET_NUMBER_OF_DESKTOPS) @@ -459,7 +460,7 @@ ((= type xcb:Atom:_NET_CLOSE_WINDOW) (let ((buffer (exwm--id->buffer id))) (when (buffer-live-p buffer) - (kill-buffer buffer)))) + (exwm--defer 0 #'kill-buffer buffer)))) ;; _NET_WM_MOVERESIZE ((= type xcb:Atom:_NET_WM_MOVERESIZE) (let ((direction (elt data 2)) -- cgit 1.4.1 From ddc22f2feddd99ff488f5f1c03b4f059e353056a Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 25 Aug 2019 00:00:00 +0000 Subject: Clear echo area for input methods * exwm-xim.el (exwm-xim--handle-forward-event-request): exwm-xim enforces `input-method-use-echo-area' but this breaks some input methods. This change clear garbage printed by such input methods. --- exwm-xim.el | 2 ++ 1 file changed, 2 insertions(+) diff --git a/exwm-xim.el b/exwm-xim.el index dc22f82fc4..5530c87c14 100644 --- a/exwm-xim.el +++ b/exwm-xim.el @@ -588,6 +588,8 @@ The actual XIM request is in client message data or a property." ;; Always show key strokes. (let ((input-method-use-echo-area t)) (setq result (funcall im-func event)) + ;; Clear echo area for the input method. + (message nil) ;; This also works for portable character encoding. (setq result (encode-coding-string (concat result) -- cgit 1.4.1 From d63dc6a82a44c1e918b93c4f50e5d4274ad6d21a Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 25 Aug 2019 00:00:00 +0000 Subject: Ignore non-`exwm-mode' buffers in `exwm-workspace-move-window' * exwm-workspace.el (exwm-workspace-move-window): Ignore non-`exwm-mode' buffers. --- exwm-workspace.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index f536bc5a81..8aa04ddf2a 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -804,7 +804,7 @@ INDEX must not exceed the current number of workspaces." :data desktop))))) ;;;###autoload -(defun exwm-workspace-move-window (frame-or-index &optional id) +(cl-defun exwm-workspace-move-window (frame-or-index &optional id) "Move window ID to workspace FRAME-OR-INDEX." (interactive (list (cond @@ -819,6 +819,8 @@ INDEX must not exceed the current number of workspaces." (let ((frame (exwm-workspace--workspace-from-frame-or-index frame-or-index)) old-frame container) (unless id (setq id (exwm--buffer->id (window-buffer)))) + (unless id + (cl-return-from exwm-workspace-move-window)) (exwm--log "Moving #x%x to %s" id frame-or-index) (with-current-buffer (exwm--id->buffer id) (unless (eq exwm--frame frame) -- cgit 1.4.1 From f303517c297a9ca9e6a814897f96303b5d0f02be Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 25 Aug 2019 00:00:00 +0000 Subject: * exwm-core.el (exwm-mode-menu): Clarify simulation keys in menu. --- exwm-core.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exwm-core.el b/exwm-core.el index 287fc6f71b..d3edb146ec 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -271,7 +271,8 @@ One of `line-mode' or `char-mode'.") (when (sequencep key) (setq result (append result `([ - ,(key-description value) + ,(format "Send '%s'" + (key-description value)) (lambda () (interactive) (dolist (i ',value) -- cgit 1.4.1 From 2d36241a10b4b705e1482322383f135bca7ac184 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 8 Sep 2019 00:00:00 +0000 Subject: Add timestamps to `exwm-debug' logs * exwm-core.el (exwm-debug-log-time-function): New user option for choosing the style of timestamps in debug logs. (exwm-debug-log-uptime, exwm-debug-log-time): Possible candidates for it. (exwm--log): Use it. --- exwm-core.el | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/exwm-core.el b/exwm-core.el index d3edb146ec..ed9a523448 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -33,6 +33,27 @@ (require 'xcb-ewmh) (require 'xcb-debug) +(defcustom exwm-debug-log-time-function #'exwm-debug-log-uptime + "Function used for generating timestamps in `exwm-debug' logs. + +Here are some predefined candidates: +`exwm-debug-log-uptime': Display the uptime of this Emacs instance. +`exwm-debug-log-time': Display time of day. +`nil': Disable timestamp." + :group 'exwm + :type `(choice (const :tag "Emacs uptime" ,#'exwm-debug-log-uptime) + (const :tag "Time of day" ,#'exwm-debug-log-time) + (const :tag "Off" nil) + (function :tag "Other"))) + +(defun exwm-debug-log-uptime () + "Add uptime to `exwm-debug' logs." + (emacs-uptime "[%.2h:%.2m:%.2s] ")) + +(defun exwm-debug-log-time () + "Add time of day to `exwm-debug' logs." + (format-time-string "[%T] ")) + (defvar exwm--connection nil "X connection.") (defvar exwm--wmsn-window nil @@ -79,7 +100,10 @@ FORMAT-STRING is a string specifying the message to output, as in `format'. The OBJECTS arguments specify the substitutions." (unless format-string (setq format-string "")) `(when exwm-debug - (xcb-debug:message ,(concat "%s:\t" format-string "\n") + (xcb-debug:message ,(concat "%s%s:\t" format-string "\n") + (if exwm-debug-log-time-function + (funcall exwm-debug-log-time-function) + "") (xcb-debug:compile-time-function-name) ,@objects) nil)) -- cgit 1.4.1 From 6593236366afdaedefcaa8ad1a9fed9888ca9dfe Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 8 Sep 2019 00:00:00 +0000 Subject: Handle and skip problematic per-application configurations * exwm-manage.el (exwm-manage--get-configurations): Select the first usable configuration. --- exwm-manage.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exwm-manage.el b/exwm-manage.el index deb475d53a..a06768f6dc 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -242,7 +242,8 @@ criterion would be applied." (when (derived-mode-p 'exwm-mode) (dolist (i exwm-manage-configurations) (save-current-buffer - (when (eval (car i) t) + (when (with-demoted-errors "Problematic configuration: %S" + (eval (car i) t)) (cl-return-from exwm-manage--get-configurations (cdr i))))))) (defun exwm-manage--manage-window (id) -- cgit 1.4.1 From 323e919876e90a101c66f485a4b925dd49ae0a26 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 8 Sep 2019 00:00:00 +0000 Subject: ; Improve the docstring of `exwm-manage-configurations' --- exwm-manage.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/exwm-manage.el b/exwm-manage.el index a06768f6dc..d657b4d4f5 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -70,7 +70,9 @@ possible choices: For each X window managed for the first time, matching criteria (sexps) are evaluated sequentially and the first configuration with a non-nil matching -criterion would be applied." +criterion would be applied. Apart from generic forms, one would typically +want to match against EXWM internal variables such as `exwm-title', +`exwm-class-name' and `exwm-instance-name'." :type '(alist :key-type (sexp :tag "Matching criterion" nil) :value-type (plist :tag "Configurations" -- cgit 1.4.1 From ccc4cce0e0df426f4eccb81479c4a773c488155d Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 8 Sep 2019 00:00:00 +0000 Subject: Fix floating X window size with menu-bar/tool-bar enabled * exwm-floating.el (exwm-floating--set-floating) exwm-floating--do-moveresize) exwm-layout.el (exwm-layout--show): Take menu-bar/tool-bar into consideration. --- exwm-floating.el | 31 +++++++++++++++++++++---------- exwm-layout.el | 8 ++++++-- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index c478e75c77..f4557ebd57 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -211,10 +211,14 @@ This is also used by X window containers.") ;; The frame will be made visible by `select-frame-set-input-focus'. (make-frame-invisible frame) (let* ((edges (window-inside-pixel-edges window)) + (geometry (frame-geometry frame)) (frame-width (+ width (- (frame-pixel-width frame) (- (elt edges 2) (elt edges 0))))) (frame-height (+ height (- (frame-pixel-height frame) - (- (elt edges 3) (elt edges 1))))) + (- (elt edges 3) (elt edges 1))) + ;; Use `frame-outer-height' in the future. + (or (cddr (assq 'menu-bar-size geometry)) 0) + (or (cddr (assq 'tool-bar-size geometry)) 0))) (floating-mode-line (plist-get exwm--configurations 'floating-mode-line)) (floating-header-line (plist-get exwm--configurations @@ -466,7 +470,7 @@ This is also used by X window containers.") height (frame-pixel-height frame)) (unless type ;; Determine the resize type according to the pointer position - ;; Clicking the center 1/3 part to resize has not effect + ;; Clicking the center 1/3 part to resize has no effect (setq x (/ (* 3 win-x) (float width)) y (/ (* 3 win-y) (float height)) type (cond ((and (< x 1) (< y 1)) @@ -603,7 +607,8 @@ This is also used by X window containers.") "Perform move/resize." (when exwm-floating--moveresize-calculate (let* ((obj (make-instance 'xcb:MotionNotify)) - result value-mask x y width height buffer-or-id container-or-id) + result value-mask x y width height buffer-or-id container-or-id + geometry y-offset) (xcb:unmarshal obj data) (setq result (funcall exwm-floating--moveresize-calculate (slot-value obj 'root-x) (slot-value obj 'root-y)) @@ -613,13 +618,19 @@ This is also used by X window containers.") y (aref result 3) width (max 1 (aref result 4)) height (max 1 (aref result 5))) - (setq container-or-id - (if (bufferp buffer-or-id) - ;; Managed. - (with-current-buffer buffer-or-id - (frame-parameter exwm--floating-frame 'exwm-container)) - ;; Unmanaged. - buffer-or-id)) + (if (not (bufferp buffer-or-id)) + ;; Unmanaged. + (setq container-or-id buffer-or-id) + ;; Managed. + (setq container-or-id + (with-current-buffer buffer-or-id + (frame-parameter exwm--floating-frame 'exwm-container)) + geometry (frame-geometry exwm--floating-frame) + ;; Use `frame-outer-height' in the future. + y-offset (+ (or (cddr (assq 'menu-bar-size geometry)) 0) + (or (cddr (assq 'tool-bar-size geometry)) 0)) + y (- y y-offset) + height (+ height y-offset))) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window container-or-id diff --git a/exwm-layout.el b/exwm-layout.el index 9ef516bd06..5644dbadc1 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -102,11 +102,15 @@ (y (pop edges)) (width (- (pop edges) x)) (height (- (pop edges) y)) - frame-x frame-y frame-width frame-height) + geometry frame-x frame-y frame-width frame-height) (with-current-buffer (exwm--id->buffer id) (when exwm--floating-frame (setq frame-width (frame-pixel-width exwm--floating-frame) - frame-height (frame-pixel-height exwm--floating-frame)) + geometry (frame-geometry exwm--floating-frame) + frame-height (+ (frame-pixel-height exwm--floating-frame) + ;; Use `frame-outer-height' in the future. + (or (cddr (assq 'menu-bar-size geometry)) 0) + (or (cddr (assq 'tool-bar-size geometry)) 0))) (when exwm--floating-frame-position (setq frame-x (elt exwm--floating-frame-position 0) frame-y (elt exwm--floating-frame-position 1) -- cgit 1.4.1 From 5fbf20ba16c87a110587b513b479a5f1f90d3db6 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 8 Sep 2019 00:00:00 +0000 Subject: ; Improve messages for automatically created workspaces --- exwm-workspace.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 8aa04ddf2a..7b18cde5c1 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1338,8 +1338,10 @@ Please check `exwm-workspace--minibuffer-own-frame-p' first." (exwm-workspace--update-ewmh-props) (if exwm-workspace--create-silently (setq exwm-workspace--switch-history-outdated t) - (exwm-workspace-switch frame t) - (message "Added %s as workspace %d" frame exwm-workspace-current-index) + (let ((original-index exwm-workspace-current-index)) + (exwm-workspace-switch frame t) + (message "Created %s as workspace %d; switched from %d" + frame exwm-workspace-current-index original-index)) (run-hooks 'exwm-workspace-list-change-hook))) (defun exwm-workspace--get-remove-frame-next-workspace (frame) -- cgit 1.4.1 From cb9607814f3cfcfa3c5597370edc5300c259119e Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 8 Sep 2019 00:00:00 +0000 Subject: ; Autoload `exwm-enable' --- exwm.el | 1 + 1 file changed, 1 insertion(+) diff --git a/exwm.el b/exwm.el index 8ea4a7636f..87ce18642d 100644 --- a/exwm.el +++ b/exwm.el @@ -872,6 +872,7 @@ manager. If t, replace it, if nil, abort and ask the user if `ask'." (xcb:disconnect exwm--connection)) (setq exwm--connection nil)) +;;;###autoload (defun exwm-enable (&optional undo) "Enable/Disable EXWM." (exwm--log "%s" undo) -- cgit 1.4.1 From 48b15e25ad9009b1135db6344de156ef4154758f Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 13 Sep 2019 00:00:00 +0000 Subject: Improve user options * exwm-floating.el (exwm-floating-border-color) (exwm-floating-border-width): Make changes take effect w/o restart. (exwm-floating--init-border): Refactored out from `exwm-floating--init'. * exwm-workspace.el (exwm-workspace-minibuffer-position): Clarify a restart is required. --- exwm-floating.el | 111 +++++++++++++++++++++++++++++++++++++++++------------- exwm-workspace.el | 4 +- 2 files changed, 87 insertions(+), 28 deletions(-) diff --git a/exwm-floating.el b/exwm-floating.el index f4557ebd57..27ee574ad7 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -44,20 +44,70 @@ context of the corresponding buffer." context of the corresponding buffer." :type 'hook) +(defvar exwm-floating--border-pixel nil + "Border pixel drawn around floating X windows.") + (defcustom exwm-floating-border-color "navy" "Border color of floating windows." - :type 'color) + :type 'color + :initialize #'custom-initialize-default + :set (lambda (symbol value) + (set-default symbol value) + ;; Change border color for all floating X windows. + (exwm-floating--init-border) + (dolist (pair exwm--id-buffer-alist) + (with-current-buffer (cdr pair) + (when exwm--floating-frame + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window + (frame-parameter exwm--floating-frame + 'exwm-container) + :value-mask xcb:CW:BorderPixel + :border-pixel + exwm-floating--border-pixel))))) + (when exwm--connection + (xcb:flush exwm--connection)))) (defcustom exwm-floating-border-width 1 "Border width of floating windows." - :type 'integer) + :type '(integer + :validate (lambda (widget) + (when (< (widget-value widget) 0) + (widget-put widget :error "Border width is at least 0") + widget))) + :initialize #'custom-initialize-default + :set (lambda (symbol value) + (let ((delta (- value exwm-floating-border-width)) + container) + (set-default symbol value) + ;; Change border width for all floating X windows. + (dolist (pair exwm--id-buffer-alist) + (with-current-buffer (cdr pair) + (when exwm--floating-frame + (setq container (frame-parameter exwm--floating-frame + 'exwm-container)) + (with-slots (x y) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetGeometry + :drawable container)) + (xcb:+request exwm--connection + (make-instance 'xcb:ConfigureWindow + :window container + :value-mask + (logior xcb:ConfigWindow:X + xcb:ConfigWindow:Y + xcb:ConfigWindow:BorderWidth) + :border-width value + :x (- x delta) + :y (- y delta))))))) + (when exwm--connection + (xcb:flush exwm--connection))))) (defvar exwm-floating--border-colormap nil "Colormap used by the border pixel. This is also used by X window containers.") -(defvar exwm-floating--border-pixel nil - "Border pixel drawn around floating X windows.") ;; Cursors for moving/resizing a window (defvar exwm-floating--cursor-move nil) @@ -679,32 +729,39 @@ Both DELTA-X and DELTA-Y default to 1. This command should be bound locally." nil nil)) (xcb:flush exwm--connection))) +(defun exwm-floating--init-border () + "Initialize border colormap and pixel." + (exwm--log) + ;; Use the default colormap. + (unless exwm-floating--border-colormap + (with-slots (roots) (xcb:get-setup exwm--connection) + (with-slots (default-colormap) (car roots) + (setq exwm-floating--border-colormap default-colormap)))) + ;; Free any previously allocated pixel. + (when exwm-floating--border-pixel + (xcb:+request exwm--connection + (make-instance 'xcb:FreeColors + :cmap exwm-floating--border-colormap + :plane-mask 0 + :pixels (vector exwm-floating--border-pixel))) + (setq exwm-floating--border-pixel nil)) + ;; Allocate new pixel. + (let ((color (x-color-values (or exwm-floating-border-color ""))) + reply) + (when color + (setq reply (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:AllocColor + :cmap exwm-floating--border-colormap + :red (pop color) + :green (pop color) + :blue (pop color)))) + (when reply + (setq exwm-floating--border-pixel (slot-value reply 'pixel)))))) + (defun exwm-floating--init () "Initialize floating module." (exwm--log) - ;; Check border width. - (unless (and (integerp exwm-floating-border-width) - (> exwm-floating-border-width 0)) - (setq exwm-floating-border-width 0)) - ;; Initialize border pixel. - (when (> exwm-floating-border-width 0) - (setq exwm-floating--border-colormap - (slot-value (car (slot-value - (xcb:get-setup exwm--connection) 'roots)) - 'default-colormap)) - (unless (stringp exwm-floating-border-color) - (setq exwm-floating-border-color "")) - (let* ((color (x-color-values exwm-floating-border-color)) - reply) - (when color - (setq reply (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:AllocColor - :cmap exwm-floating--border-colormap - :red (pop color) - :green (pop color) - :blue (pop color)))) - (when reply - (setq exwm-floating--border-pixel (slot-value reply 'pixel)))))) + (exwm-floating--init-border) ;; Initialize cursors for moving/resizing a window (xcb:cursor:init exwm--connection) (setq exwm-floating--cursor-move diff --git a/exwm-workspace.el b/exwm-workspace.el index 7b18cde5c1..ac4cb982da 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -62,7 +62,9 @@ By default `number-to-string' is applied which yields 0 1 2 ... ." :type 'function) (defcustom exwm-workspace-minibuffer-position nil - "Position of the minibuffer frame." + "Position of the minibuffer frame. + +A restart is required for this change to take effect." :type '(choice (const :tag "Bottom (fixed)" nil) (const :tag "Bottom (auto-hide)" bottom) (const :tag "Top (auto-hide)" top))) -- cgit 1.4.1 From eb49e57f762ab47e874c821c12e0b641d3dd9d8e Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Fri, 13 Sep 2019 00:00:00 +0000 Subject: Fix hiding floating X windows * exwm-layout.el (exwm-layout--floating-hidden-position): A place far enough from the origin to actually hide floating X window containers. (exwm-layout--show, exwm-layout--hide): Use it. --- exwm-layout.el | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index 5644dbadc1..0b5adeb2f4 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -40,6 +40,9 @@ "Non-nil to allow switching to buffers on other workspaces." :type 'boolean) +(defconst exwm-layout--floating-hidden-position -101 + "Where to place hidden floating X windows.") + (defvar exwm-layout--other-buffer-exclude-buffers nil "List of buffers that should not be selected by `other-buffer'.") @@ -114,9 +117,8 @@ (when exwm--floating-frame-position (setq frame-x (elt exwm--floating-frame-position 0) frame-y (elt exwm--floating-frame-position 1) - ;; The frame was placed at (-1, -1). - x (+ x frame-x 1) - y (+ y frame-y 1)) + x (+ x frame-x (- exwm-layout--floating-hidden-position)) + y (+ y frame-y (- exwm-layout--floating-hidden-position))) (setq exwm--floating-frame-position nil)) (exwm--set-geometry (frame-parameter exwm--floating-frame 'exwm-container) @@ -152,7 +154,10 @@ :drawable container)))) (setq exwm--floating-frame-position (vector (slot-value geometry 'x) (slot-value geometry 'y))) - (exwm--set-geometry container -1 -1 1 1))) + (exwm--set-geometry container exwm-layout--floating-hidden-position + exwm-layout--floating-hidden-position + 1 + 1))) (xcb:+request exwm--connection (make-instance 'xcb:ChangeWindowAttributes :window id :value-mask xcb:CW:EventMask -- cgit 1.4.1 From f167bc979c76edb43bc63cffebec095601446477 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 14 Sep 2019 00:00:00 +0000 Subject: Replace `frame-geometry' * exwm-workspace.el (exwm-workspace--frame-y-offset) exwm-workspace--window-y-offset, exwm-workspace--update-offsets): New variables & function for the calculation of Emacs frame offsets, as `frame-geometry' is not available in Emacs 24. * exwm-floating.el (exwm-floating--set-floating) (exwm-floating--do-moveresize): * exwm-layout.el (exwm-layout--show): * exwm-systemtray.el (exwm-systemtray--on-workspace-switch) (exwm-systemtray--on-randr-refresh, exwm-systemtray--init): Use them. * exwm-systemtray.el (exwm-systemtray--refresh-all): Renamed from `exwm-systemtray--on-randr-refresh'. (exwm-systemtray--init, exwm-systemtray--exit): Use it. * exwm-floating.el (exwm-floating--stop-moveresize): Send a ConfigureNotify event to floating frame to update its position (seems required by Emacs 24). --- exwm-config.el | 4 +-- exwm-core.el | 4 +-- exwm-floating.el | 63 ++++++++++++++++++++++++++++++----------- exwm-layout.el | 7 ++--- exwm-systemtray.el | 83 +++++++++++++++++++++++++++--------------------------- exwm-workspace.el | 27 ++++++++++++++++++ 6 files changed, 122 insertions(+), 66 deletions(-) diff --git a/exwm-config.el b/exwm-config.el index 6635e4345d..4454ea5a68 100644 --- a/exwm-config.el +++ b/exwm-config.el @@ -47,8 +47,8 @@ ([?\s-w] . exwm-workspace-switch) ;; 's-&': Launch application. ([?\s-&] . (lambda (command) - (interactive (list (read-shell-command "$ "))) - (start-process-shell-command command nil command))) + (interactive (list (read-shell-command "$ "))) + (start-process-shell-command command nil command))) ;; 's-N': Switch to certain workspace. ,@(mapcar (lambda (i) `(,(kbd (format "s-%d" i)) . diff --git a/exwm-core.el b/exwm-core.el index ed9a523448..b0a683999d 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -168,8 +168,8 @@ The action is to call FUNCTION with arguments ARGS. If Emacs is not idle, defer the action until Emacs is idle. Otherwise, defer the action until at least SECS seconds later." `(run-with-idle-timer (+ (float-time (or (current-idle-time) - (seconds-to-time (- ,secs)))) - ,secs) + (seconds-to-time (- ,secs)))) + ,secs) nil ,function ,@args)) diff --git a/exwm-floating.el b/exwm-floating.el index 27ee574ad7..189acc40be 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -124,12 +124,15 @@ This is also used by X window containers.") "Calculate move/resize parameters [buffer event-mask x y width height].") (defvar exwm-workspace--current) +(defvar exwm-workspace--frame-y-offset) +(defvar exwm-workspace--window-y-offset) (defvar exwm-workspace--workareas) (declare-function exwm-layout--hide "exwm-layout.el" (id)) (declare-function exwm-layout--iconic-state-p "exwm-layout.el" (&optional id)) (declare-function exwm-layout--refresh "exwm-layout.el" ()) (declare-function exwm-layout--show "exwm-layout.el" (id &optional window)) (declare-function exwm-workspace--position "exwm-workspace.el" (frame)) +(declare-function exwm-workspace--update-offsets "exwm-workspace.el" ()) (defun exwm-floating--set-allowed-actions (id tilling) "Set _NET_WM_ALLOWED_ACTIONS." @@ -166,8 +169,8 @@ This is also used by X window containers.") (get-buffer "*scratch*"))) (make-frame `((minibuffer . ,(minibuffer-window exwm--frame)) - (left . ,(* window-min-width -100)) - (top . ,(* window-min-height -100)) + (left . ,(* window-min-width -10000)) + (top . ,(* window-min-height -10000)) (width . ,window-min-width) (height . ,window-min-height) (unsplittable . t))))) ;and fix the size later @@ -179,6 +182,9 @@ This is also used by X window containers.") (y (slot-value exwm--geometry 'y)) (width (slot-value exwm--geometry 'width)) (height (slot-value exwm--geometry 'height))) + ;; Force drawing menu-bar & tool-bar. + (redisplay t) + (exwm-workspace--update-offsets) (exwm--log "Floating geometry (original): %dx%d%+d%+d" width height x y) ;; Save frame parameters. (set-frame-parameter frame 'exwm-outer-id outer-id) @@ -261,14 +267,12 @@ This is also used by X window containers.") ;; The frame will be made visible by `select-frame-set-input-focus'. (make-frame-invisible frame) (let* ((edges (window-inside-pixel-edges window)) - (geometry (frame-geometry frame)) (frame-width (+ width (- (frame-pixel-width frame) (- (elt edges 2) (elt edges 0))))) (frame-height (+ height (- (frame-pixel-height frame) (- (elt edges 3) (elt edges 1))) ;; Use `frame-outer-height' in the future. - (or (cddr (assq 'menu-bar-size geometry)) 0) - (or (cddr (assq 'tool-bar-size geometry)) 0))) + exwm-workspace--frame-y-offset)) (floating-mode-line (plist-get exwm--configurations 'floating-mode-line)) (floating-header-line (plist-get exwm--configurations @@ -293,7 +297,7 @@ This is also used by X window containers.") 'floating-header-line)) exwm--mwm-hints-decorations) (setq header-line-format nil) - ;; The header-line need to be hidden in floating header. + ;; The header-line need to be hidden in floating mode. (setq frame-height (- frame-height (window-header-line-height (frame-root-window frame))) header-line-format nil))) @@ -304,8 +308,8 @@ This is also used by X window containers.") :depth 0 :wid frame-container :parent exwm--root - :x (- x (elt edges 0)) - :y (- y (elt edges 1)) + :x x + :y (- y exwm-workspace--window-y-offset) :width width :height height :border-width @@ -377,7 +381,7 @@ This is also used by X window containers.") (with-current-buffer (exwm--id->buffer id) (run-hooks 'exwm-floating-setup-hook)) ;; Redraw the frame. - (redisplay)) + (redisplay t)) (defun exwm-floating--unset-floating (id) "Make window ID non-floating." @@ -644,11 +648,38 @@ This is also used by X window containers.") (xcb:+request exwm--connection (make-instance 'xcb:UngrabPointer :time xcb:Time:CurrentTime)) (when exwm-floating--moveresize-calculate - (let (result buffer-or-id) + (let (result buffer-or-id outer-id container-id) (setq result (funcall exwm-floating--moveresize-calculate 0 0) buffer-or-id (aref result 0)) (when (bufferp buffer-or-id) (with-current-buffer buffer-or-id + (setq outer-id (frame-parameter exwm--floating-frame 'exwm-outer-id) + container-id (frame-parameter exwm--floating-frame + 'exwm-container)) + (with-slots (x y width height border-width) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetGeometry + :drawable container-id)) + ;; Notify Emacs frame about this the position change. + (xcb:+request exwm--connection + (make-instance 'xcb:SendEvent + :propagate 0 + :destination outer-id + :event-mask xcb:EventMask:StructureNotify + :event + (xcb:marshal + (make-instance 'xcb:ConfigureNotify + :event outer-id + :window outer-id + :above-sibling xcb:Window:None + :x (+ x border-width) + :y (+ y border-width) + :width width + :height height + :border-width 0 + :override-redirect 0) + exwm--connection))) + (xcb:flush exwm--connection)) (exwm-layout--show exwm--id (frame-root-window exwm--floating-frame))))) (setq exwm-floating--moveresize-calculate nil))) @@ -657,8 +688,7 @@ This is also used by X window containers.") "Perform move/resize." (when exwm-floating--moveresize-calculate (let* ((obj (make-instance 'xcb:MotionNotify)) - result value-mask x y width height buffer-or-id container-or-id - geometry y-offset) + result value-mask x y width height buffer-or-id container-or-id) (xcb:unmarshal obj data) (setq result (funcall exwm-floating--moveresize-calculate (slot-value obj 'root-x) (slot-value obj 'root-y)) @@ -675,12 +705,11 @@ This is also used by X window containers.") (setq container-or-id (with-current-buffer buffer-or-id (frame-parameter exwm--floating-frame 'exwm-container)) - geometry (frame-geometry exwm--floating-frame) + x (- x exwm-floating-border-width) ;; Use `frame-outer-height' in the future. - y-offset (+ (or (cddr (assq 'menu-bar-size geometry)) 0) - (or (cddr (assq 'tool-bar-size geometry)) 0)) - y (- y y-offset) - height (+ height y-offset))) + y (- y exwm-floating-border-width + exwm-workspace--window-y-offset) + height (+ height exwm-workspace--window-y-offset))) (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow :window container-or-id diff --git a/exwm-layout.el b/exwm-layout.el index 0b5adeb2f4..9f1e0cf748 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -52,6 +52,7 @@ (defvar exwm-layout--timer nil "Timer used to track echo area changes.") (defvar exwm-workspace--current) +(defvar exwm-workspace--frame-y-offset) (declare-function exwm-input--release-keyboard "exwm-input.el") (declare-function exwm-input--grab-keyboard "exwm-input.el") (declare-function exwm-input-grab-keyboard "exwm-input.el") @@ -105,15 +106,13 @@ (y (pop edges)) (width (- (pop edges) x)) (height (- (pop edges) y)) - geometry frame-x frame-y frame-width frame-height) + frame-x frame-y frame-width frame-height) (with-current-buffer (exwm--id->buffer id) (when exwm--floating-frame (setq frame-width (frame-pixel-width exwm--floating-frame) - geometry (frame-geometry exwm--floating-frame) frame-height (+ (frame-pixel-height exwm--floating-frame) ;; Use `frame-outer-height' in the future. - (or (cddr (assq 'menu-bar-size geometry)) 0) - (or (cddr (assq 'tool-bar-size geometry)) 0))) + exwm-workspace--frame-y-offset)) (when exwm--floating-frame-position (setq frame-x (elt exwm--floating-frame-position 0) frame-y (elt exwm--floating-frame-position 1) diff --git a/exwm-systemtray.el b/exwm-systemtray.el index a095bfa8de..35010ee341 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -331,42 +331,37 @@ You shall use the default value if using auto-hide minibuffer." "Reparent/Refresh the system tray in `exwm-workspace-switch-hook'." (exwm--log) (unless (exwm-workspace--minibuffer-own-frame-p) - (let ((geometry (frame-geometry exwm-workspace--current))) - (xcb:+request exwm-systemtray--connection - (make-instance 'xcb:ReparentWindow - :window exwm-systemtray--embedder-window - :parent (string-to-number - (frame-parameter exwm-workspace--current - 'window-id)) - :x 0 - :y (- (elt (elt exwm-workspace--workareas - exwm-workspace-current-index) - 3) - (or (cddr (assq 'menu-bar-size geometry)) 0) - (or (cddr (assq 'tool-bar-size geometry)) 0) - exwm-systemtray-height))))) + (exwm-workspace--update-offsets) + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:ReparentWindow + :window exwm-systemtray--embedder-window + :parent (string-to-number + (frame-parameter exwm-workspace--current + 'window-id)) + :x 0 + :y (- (elt (elt exwm-workspace--workareas + exwm-workspace-current-index) + 3) + exwm-workspace--frame-y-offset + exwm-systemtray-height)))) (exwm-systemtray--refresh)) -(defun exwm-systemtray--on-randr-refresh () - "Reposition/Refresh the system tray in `exwm-randr-refresh-hook'." +(defun exwm-systemtray--refresh-all () + "Reposition/Refresh the system tray." (exwm--log) (unless (exwm-workspace--minibuffer-own-frame-p) - (let ((geometry (frame-geometry exwm-workspace--current))) - (xcb:+request exwm-systemtray--connection - (make-instance 'xcb:ConfigureWindow - :window exwm-systemtray--embedder-window - :value-mask xcb:ConfigWindow:Y - :y (- (elt (elt exwm-workspace--workareas - exwm-workspace-current-index) - 3) - (or (cddr (assq 'menu-bar-size geometry)) 0) - (or (cddr (assq 'tool-bar-size geometry)) 0) - exwm-systemtray-height))))) + (exwm-workspace--update-offsets) + (xcb:+request exwm-systemtray--connection + (make-instance 'xcb:ConfigureWindow + :window exwm-systemtray--embedder-window + :value-mask xcb:ConfigWindow:Y + :y (- (elt (elt exwm-workspace--workareas + exwm-workspace-current-index) + 3) + exwm-workspace--frame-y-offset + exwm-systemtray-height)))) (exwm-systemtray--refresh)) -(defalias 'exwm-systemtray--on-struts-update - #'exwm-systemtray--on-randr-refresh) - (cl-defun exwm-systemtray--init () "Initialize system tray module." (exwm--log) @@ -452,11 +447,14 @@ You shall use the default value if using auto-hide minibuffer." (- (line-pixel-height) exwm-systemtray-height) ;; Vertically centered. (/ (- (line-pixel-height) exwm-systemtray-height) 2))) - (let ((workarea (elt exwm-workspace--workareas - exwm-workspace-current-index))) - (setq frame exwm-workspace--current - ;; Bottom aligned. - y (- (aref workarea 3) exwm-systemtray-height)))) + (exwm-workspace--update-offsets) + (setq frame exwm-workspace--current + ;; Bottom aligned. + y (- (elt (elt exwm-workspace--workareas + exwm-workspace-current-index) + 3) + exwm-workspace--frame-y-offset + exwm-systemtray-height))) (setq parent (string-to-number (frame-parameter frame 'window-id)) depth (slot-value (xcb:+request-unchecked+reply exwm-systemtray--connection @@ -501,12 +499,14 @@ You shall use the default value if using auto-hide minibuffer." ;; Add hook to move/reparent the embedder. (add-hook 'exwm-workspace-switch-hook #'exwm-systemtray--on-workspace-switch) (add-hook 'exwm-workspace--update-workareas-hook - #'exwm-systemtray--on-struts-update) + #'exwm-systemtray--refresh-all) + (add-hook 'menu-bar-mode-hook #'exwm-systemtray--refresh-all) + (add-hook 'tool-bar-mode-hook #'exwm-systemtray--refresh-all) (when (boundp 'exwm-randr-refresh-hook) - (add-hook 'exwm-randr-refresh-hook #'exwm-systemtray--on-randr-refresh)) + (add-hook 'exwm-randr-refresh-hook #'exwm-systemtray--refresh-all)) ;; The struts can be updated already. (when exwm-workspace--workareas - (exwm-systemtray--on-struts-update))) + (exwm-systemtray--refresh-all))) (defun exwm-systemtray--exit () "Exit the systemtray module." @@ -532,10 +532,11 @@ You shall use the default value if using auto-hide minibuffer." (remove-hook 'exwm-workspace-switch-hook #'exwm-systemtray--on-workspace-switch) (remove-hook 'exwm-workspace--update-workareas-hook - #'exwm-systemtray--on-struts-update) + #'exwm-systemtray--refresh-all) + (remove-hook 'menu-bar-mode-hook #'exwm-systemtray--refresh-all) + (remove-hook 'tool-bar-mode-hook #'exwm-systemtray--refresh-all) (when (boundp 'exwm-randr-refresh-hook) - (remove-hook 'exwm-randr-refresh-hook - #'exwm-systemtray--on-randr-refresh)))) + (remove-hook 'exwm-randr-refresh-hook #'exwm-systemtray--refresh-all)))) (defun exwm-systemtray-enable () "Enable system tray support for EXWM." diff --git a/exwm-workspace.el b/exwm-workspace.el index ac4cb982da..41343187ed 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -134,6 +134,11 @@ Please manually run the hook `exwm-workspace-list-change-hook' afterwards.") (defvar exwm-workspace--workareas nil "Workareas (struts excluded).") +(defvar exwm-workspace--frame-y-offset 0 + "Offset between Emacs inner & outer frame in Y.") +(defvar exwm-workspace--window-y-offset 0 + "Offset between Emacs first window & outer frame in Y.") + (defvar exwm-input--during-command) (defvar exwm-input--event-hook) (defvar exwm-layout-show-all-buffers) @@ -397,6 +402,28 @@ NIL if FRAME is not a workspace" (exwm--log "%s" exwm-workspace--workareas) (run-hooks 'exwm-workspace--update-workareas-hook)) +(defun exwm-workspace--update-offsets () + "Update `exwm-workspace--frame-y-offset'/`exwm-workspace--window-y-offset'." + (exwm--log) + (if (not (and exwm-workspace--list + (or menu-bar-mode tool-bar-mode))) + (setq exwm-workspace--frame-y-offset 0 + exwm-workspace--window-y-offset 0) + (redisplay t) + (let* ((frame (elt exwm-workspace--list 0)) + (edges (window-inside-absolute-pixel-edges (frame-first-window + frame)))) + (with-slots (y) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetGeometry + :drawable (frame-parameter frame 'exwm-outer-id))) + (with-slots ((y* y)) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetGeometry + :drawable (frame-parameter frame 'exwm-id))) + (setq exwm-workspace--frame-y-offset (- y* y) + exwm-workspace--window-y-offset (- (elt edges 1) y))))))) + (defun exwm-workspace--set-active (frame active) "Make frame FRAME active on its monitor." (exwm--log "active=%s; frame=%s" frame active) -- cgit 1.4.1 From 10766e232b607755252fb22e212fb91efa00c6b9 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 14 Sep 2019 00:00:00 +0000 Subject: Support starting EXWM from Emacs server not in daemon mode * exwm.el (exwm-enable): Start EXWM from `window-setup-hook' or `after-make-frame-functions', whichever comes first. --- exwm.el | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/exwm.el b/exwm.el index 87ce18642d..9d60d5cd08 100644 --- a/exwm.el +++ b/exwm.el @@ -892,10 +892,11 @@ manager. If t, replace it, if nil, abort and ask the user if `ask'." ;; Ignore unrecognized command line arguments. This can be helpful ;; when EXWM is launched by some session manager. (push #'vector command-line-functions) - (add-hook (if (daemonp) - 'after-make-frame-functions ;emacsclient - 'window-setup-hook) ;emacs - #'exwm-init t) + ;; In case EXWM is to be started from a graphical Emacs instance. + (add-hook 'window-setup-hook #'exwm-init t) + ;; In case EXWM is to be started with emacsclient. + (add-hook 'after-make-frame-functions #'exwm-init t) + ;; Manage the subordinate Emacs server. (add-hook 'kill-emacs-hook #'exwm--server-stop) (dolist (i exwm-blocking-subrs) (advice-add i :around #'exwm--server-eval-at))))) -- cgit 1.4.1 From 559e56f4732d47ba459fb5cdc4246cb22d77d484 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sat, 14 Sep 2019 00:00:00 +0000 Subject: Bump version to 0.23 --- exwm.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exwm.el b/exwm.el index 9d60d5cd08..e5b603e025 100644 --- a/exwm.el +++ b/exwm.el @@ -4,8 +4,8 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0.22.1 -;; Package-Requires: ((xelb "0.16")) +;; Version: 0.23 +;; Package-Requires: ((xelb "0.18")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From 16d643dd3b2b8e468dbbfb2ad03d7abe41fdf94d Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 15 Sep 2019 00:00:00 +0000 Subject: Fix timestamp for debug logs * exwm-core.el (exwm-debug-log-time-function): Moved to `exwm-debug' group and set `xcb-debug:log-time-function' automatically. (exwm-debug-log-uptime, exwm-debug-log-time): Reuse the definition from XELB. --- exwm-core.el | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index b0a683999d..9e2829165c 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -40,19 +40,22 @@ Here are some predefined candidates: `exwm-debug-log-uptime': Display the uptime of this Emacs instance. `exwm-debug-log-time': Display time of day. `nil': Disable timestamp." - :group 'exwm + :group 'exwm-debug :type `(choice (const :tag "Emacs uptime" ,#'exwm-debug-log-uptime) (const :tag "Time of day" ,#'exwm-debug-log-time) (const :tag "Off" nil) - (function :tag "Other"))) - -(defun exwm-debug-log-uptime () - "Add uptime to `exwm-debug' logs." - (emacs-uptime "[%.2h:%.2m:%.2s] ")) - -(defun exwm-debug-log-time () - "Add time of day to `exwm-debug' logs." - (format-time-string "[%T] ")) + (function :tag "Other")) + :set (lambda (symbol value) + (set-default symbol value) + ;; Also change the format for XELB to make logs consistent + ;; (as they share the same buffer). + (setq xcb-debug:log-time-function value))) + +(defalias 'exwm-debug-log-uptime 'xcb-debug:log-uptime + "Add uptime to `exwm-debug' logs.") + +(defalias 'exwm-debug-log-time 'xcb-debug:log-time + "Add time of day to `exwm-debug' logs.") (defvar exwm--connection nil "X connection.") -- cgit 1.4.1 From 988f983233275b84ee3587af8a449a44d95c6752 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 15 Sep 2019 00:00:00 +0000 Subject: Fix `exwm-layout-enlarge-window' for floating X windows * exwm-layout.el (exwm-layout-enlarge-window): Correct the height of outer frame. --- exwm-layout.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm-layout.el b/exwm-layout.el index 9f1e0cf748..6275b794bd 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -468,7 +468,7 @@ windows." :width width)) (xcb:flush exwm--connection)))) (t - (let* ((height (frame-pixel-height)) + (let* ((height (+ (frame-pixel-height) exwm-workspace--frame-y-offset)) (edges (window-inside-pixel-edges)) (inner-height (- (elt edges 3) (elt edges 1))) (margin (- height inner-height))) -- cgit 1.4.1 From 9c85f172e5697f85bdea67ef8b23819448abd31a Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 22 Sep 2019 00:00:00 +0000 Subject: Skip global keys unavailable in X server * exwm-input.el (exwm-input--grab-global-prefix-keys): Some global keys might not be available in X server (perhaps due to misconfiguration). --- exwm-input.el | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 52370bf8f4..c5eedc9e1d 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -488,24 +488,27 @@ ARGS are additional arguments to CALLBACK." :keyboard-mode xcb:GrabMode:Async)) keysyms keycode alt-modifier) (dolist (k exwm-input--global-prefix-keys) - (setq keysyms (xcb:keysyms:event->keysyms exwm--connection k) - keycode (xcb:keysyms:keysym->keycode exwm--connection - (caar keysyms))) - (exwm--log "Grabbing key=%s (keysyms=%s keycode=%s)" - (single-key-description k) keysyms keycode) - (dolist (keysym keysyms) - (setf (slot-value req 'modifiers) (cdr keysym) - (slot-value req 'key) keycode) - ;; Also grab this key with num-lock mask set. - (when (and (/= 0 xcb:keysyms:num-lock-mask) - (= 0 (logand (cdr keysym) xcb:keysyms:num-lock-mask))) - (setf alt-modifier (logior (cdr keysym) xcb:keysyms:num-lock-mask))) - (dolist (xwin xwins) - (setf (slot-value req 'grab-window) xwin) - (xcb:+request exwm--connection req) - (when alt-modifier - (setf (slot-value req 'modifiers) alt-modifier) - (xcb:+request exwm--connection req))))) + (setq keysyms (xcb:keysyms:event->keysyms exwm--connection k)) + (if (not keysyms) + (warn "Key unavailable: %s" (key-description (vector k))) + (setq keycode (xcb:keysyms:keysym->keycode exwm--connection + (caar keysyms))) + (exwm--log "Grabbing key=%s (keysyms=%s keycode=%s)" + (single-key-description k) keysyms keycode) + (dolist (keysym keysyms) + (setf (slot-value req 'modifiers) (cdr keysym) + (slot-value req 'key) keycode) + ;; Also grab this key with num-lock mask set. + (when (and (/= 0 xcb:keysyms:num-lock-mask) + (= 0 (logand (cdr keysym) xcb:keysyms:num-lock-mask))) + (setf alt-modifier (logior (cdr keysym) + xcb:keysyms:num-lock-mask))) + (dolist (xwin xwins) + (setf (slot-value req 'grab-window) xwin) + (xcb:+request exwm--connection req) + (when alt-modifier + (setf (slot-value req 'modifiers) alt-modifier) + (xcb:+request exwm--connection req)))))) (xcb:flush exwm--connection))) (defun exwm-input--set-key (key command) -- cgit 1.4.1 From 2005fa5c5dcfdcc49e1ce0b631a1793e9c25604f Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 22 Sep 2019 00:00:00 +0000 Subject: Make _NET_ACTIVE_WINDOW working for floating X windows * exwm.el (exwm--on-ClientMessage): Select floating frames for floating X windows on receiving _NET_ACTIVE_WINDOW. --- exwm.el | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/exwm.el b/exwm.el index e5b603e025..36dbcb677f 100644 --- a/exwm.el +++ b/exwm.el @@ -446,16 +446,18 @@ (when (buffer-live-p buffer) (with-current-buffer buffer (when (eq exwm--frame exwm-workspace--current) - (setq iconic (exwm-layout--iconic-state-p)) - (when iconic - ;; State change: iconic => normal. - (set-window-buffer (frame-selected-window exwm--frame) - (current-buffer))) - ;; Focus transfer. - (setq window (get-buffer-window nil t)) - (when (or iconic - (not (eq window (selected-window)))) - (select-window window))))))) + (if exwm--floating-frame + (select-frame exwm--floating-frame) + (setq iconic (exwm-layout--iconic-state-p)) + (when iconic + ;; State change: iconic => normal. + (set-window-buffer (frame-selected-window exwm--frame) + (current-buffer))) + ;; Focus transfer. + (setq window (get-buffer-window nil t)) + (when (or iconic + (not (eq window (selected-window)))) + (select-window window)))))))) ;; _NET_CLOSE_WINDOW. ((= type xcb:Atom:_NET_CLOSE_WINDOW) (let ((buffer (exwm--id->buffer id))) -- cgit 1.4.1 From 9bea3c9bcd02dc2647881feaaacbf71f6c25ff64 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 22 Sep 2019 00:00:00 +0000 Subject: Make input focus continue to work after EXWM exits * exwm-input.el (exwm-input--exit): Set input focus 'revert-to' to 'PointerRoot' so that user can set input focus to X windows with pointer after EXWM exits (and there's no other WM). --- exwm-input.el | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/exwm-input.el b/exwm-input.el index c5eedc9e1d..c8cf01817c 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -1147,7 +1147,14 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences." (when exwm-input--update-focus-defer-timer (cancel-timer exwm-input--update-focus-defer-timer)) (when exwm-input--update-focus-timer - (cancel-timer exwm-input--update-focus-timer))) + (cancel-timer exwm-input--update-focus-timer)) + ;; Make input focus working even without a WM. + (xcb:+request exwm--connection + (make-instance 'xcb:SetInputFocus + :revert-to xcb:InputFocus:PointerRoot + :focus exwm--root + :time xcb:Time:CurrentTime)) + (xcb:flush exwm--connection)) -- cgit 1.4.1 From 7778766af824326d89cf4bce7541823218d43adc Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 2 Oct 2019 00:00:00 +0000 Subject: Allow customizing undetectable commands * exwm-input.el (exwm-input-pre-post-command-blacklist): List of commands undetectable with `post-command-hook'. (exwm-input--on-pre-command): Use it. --- exwm-input.el | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/exwm-input.el b/exwm-input.el index c8cf01817c..7833130d78 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -960,6 +960,10 @@ Notes: (set symbol value) (exwm-input--set-simulation-keys value))) +(defcustom exwm-input-pre-post-command-blacklist '(exit-minibuffer) + "Commands impossible to detect with `post-command-hook'." + :type '(repeat function)) + (cl-defun exwm-input--read-keys (prompt stop-key) (let ((cursor-in-echo-area t) keys key) @@ -1029,7 +1033,7 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences." (defun exwm-input--on-pre-command () "Run in `pre-command-hook'." - (unless (eq this-command #'exit-minibuffer) + (unless (memq this-command exwm-input-pre-post-command-blacklist) (setq exwm-input--during-command t))) (defun exwm-input--on-post-command () -- cgit 1.4.1 From 9eed52ee2b5a1366dfa5fdd3ad45328c554b356c Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 2 Oct 2019 00:00:00 +0000 Subject: Avoid calling `x-focus-frame' on non-graphical frames * exwm-workspace.el (exwm-workspace--client-p): Also account for non-graphical frames. * exwm-input.el (exwm-input--on-minibuffer-setup) (exwm-input--on-minibuffer-exit): Exclude emacsclient frames. --- exwm-input.el | 2 ++ exwm-workspace.el | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/exwm-input.el b/exwm-input.el index 7833130d78..2d3b481898 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -1046,6 +1046,7 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences." (with-current-buffer (window-buffer (frame-selected-window exwm-workspace--current)) (when (and (derived-mode-p 'exwm-mode) + (not (exwm-workspace--client-p)) (eq exwm--selected-input-mode 'char-mode)) (exwm-input--grab-keyboard exwm--id)))) @@ -1055,6 +1056,7 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences." (with-current-buffer (window-buffer (frame-selected-window exwm-workspace--current)) (when (and (derived-mode-p 'exwm-mode) + (not (exwm-workspace--client-p)) (eq exwm--selected-input-mode 'char-mode) (eq exwm--input-mode 'line-mode)) (exwm-input--release-keyboard exwm--id)))) diff --git a/exwm-workspace.el b/exwm-workspace.el index 41343187ed..736f46f82c 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -167,7 +167,8 @@ NIL if FRAME is not a workspace" (defsubst exwm-workspace--client-p (&optional frame) "Return non-nil if FRAME is an emacsclient frame." - (frame-parameter frame 'client)) + (or (frame-parameter frame 'client) + (not (display-graphic-p frame)))) (defvar exwm-workspace--switch-map nil "Keymap used for interactively selecting workspace.") -- cgit 1.4.1 From 1e9be0de384d0750277b350c77e5e4a5d8a8ae10 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Wed, 2 Oct 2019 00:00:00 +0000 Subject: Fix detection of `exwm-workspace--window-y-offset' * exwm-workspace.el (exwm-workspace--update-offsets): Explicitly request the geometry of the container for the first workspace as it may not align with the top of the root X window. --- exwm-workspace.el | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 736f46f82c..705da308f2 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -417,13 +417,19 @@ NIL if FRAME is not a workspace" (with-slots (y) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GetGeometry - :drawable (frame-parameter frame 'exwm-outer-id))) + :drawable (frame-parameter frame + 'exwm-container))) (with-slots ((y* y)) (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GetGeometry - :drawable (frame-parameter frame 'exwm-id))) - (setq exwm-workspace--frame-y-offset (- y* y) - exwm-workspace--window-y-offset (- (elt edges 1) y))))))) + :drawable (frame-parameter frame + 'exwm-outer-id))) + (with-slots ((y** y)) + (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetGeometry + :drawable (frame-parameter frame 'exwm-id))) + (setq exwm-workspace--frame-y-offset (- y** y*) + exwm-workspace--window-y-offset (- (elt edges 1) y)))))))) (defun exwm-workspace--set-active (frame active) "Make frame FRAME active on its monitor." -- cgit 1.4.1 From f14bd2a110af927293c23e8ff7aa1e2287120b0d Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 6 Oct 2019 08:28:03 +0900 Subject: Set the current buffer before handling key events There's no guarantee that the global current buffer matches the selected window's buffer. For example, the following will output "*Messages*" regardless of the actual current buffer: (progn (run-at "1 sec" nil (lambda () (with-current-buffer (get-buffer "*Messages*") (sit-for 5)))) (run-at "2 sec" nil (lambda () (message (buffer-name))))) * exwm-input.el (exwm-input--on-KeyPress): Set the current buffer to selected window's current buffer. --- exwm-input.el | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 2d3b481898..8cd54c69b8 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -441,18 +441,19 @@ ARGS are additional arguments to CALLBACK." (defun exwm-input--on-KeyPress (data _synthetic) "Handle KeyPress event." - (let ((obj (make-instance 'xcb:KeyPress))) - (xcb:unmarshal obj data) - (exwm--log "major-mode=%s buffer=%s" - major-mode (buffer-name (current-buffer))) - (if (derived-mode-p 'exwm-mode) - (cl-case exwm--input-mode - (line-mode - (exwm-input--on-KeyPress-line-mode obj data)) - (char-mode - (exwm-input--on-KeyPress-char-mode obj data))) - (exwm-input--on-KeyPress-char-mode obj))) - (run-hooks 'exwm-input--event-hook)) + (with-current-buffer (window-buffer (selected-window)) + (let ((obj (make-instance 'xcb:KeyPress))) + (xcb:unmarshal obj data) + (exwm--log "major-mode=%s buffer=%s" + major-mode (buffer-name (current-buffer))) + (if (derived-mode-p 'exwm-mode) + (cl-case exwm--input-mode + (line-mode + (exwm-input--on-KeyPress-line-mode obj data)) + (char-mode + (exwm-input--on-KeyPress-char-mode obj data))) + (exwm-input--on-KeyPress-char-mode obj))) + (run-hooks 'exwm-input--event-hook))) (defun exwm-input--on-CreateNotify (data _synthetic) "Handle CreateNotify events." -- cgit 1.4.1 From 5a43dbecc759fccdd1adcaae5c073bdb4d88d856 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 6 Oct 2019 08:39:36 +0900 Subject: Add minibuffer-keyboard-quit to the default pre-post-command-blacklist * exwm-input.el (exwm-input-pre-post-command-blacklist): Add `minibuffer-keyboard-quit' to the blacklist. This is invoked when the user aborts a the minibuffer with C-g. --- exwm-input.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/exwm-input.el b/exwm-input.el index 8cd54c69b8..e648219388 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -961,7 +961,9 @@ Notes: (set symbol value) (exwm-input--set-simulation-keys value))) -(defcustom exwm-input-pre-post-command-blacklist '(exit-minibuffer) +(defcustom exwm-input-pre-post-command-blacklist '(exit-minibuffer + abort-recursive-edit + minibuffer-keyboard-quit) "Commands impossible to detect with `post-command-hook'." :type '(repeat function)) -- cgit 1.4.1 From 3c8b39505825eff7e2b61e09833374d904926724 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 13 Oct 2019 00:00:00 +0000 Subject: Fix `exwm-input-set-simulation-key' * exwm-input.el (exwm-input--read-keys): Return nil on empty input so that `exwm-input-set-simulation-key' can ignore it. (exwm-input-set-simulation-key): Improve prompt. --- exwm-input.el | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index e648219388..34fe2221b7 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -976,7 +976,8 @@ Notes: (key-description (vector stop-key)) (key-description keys))) keys (vconcat keys (vector key)))) - (substring keys 0 -1))) + (when (> (length keys) 1) + (substring keys 0 -1)))) ;;;###autoload (defun exwm-input-set-simulation-key (original-key simulated-key) @@ -987,10 +988,11 @@ ends unless it's specifically saved in the Customize interface for `exwm-input-simulation-keys'." (interactive (let (original simulated) - (setq original (exwm-input--read-keys "Original keys" ?\C-g)) + (setq original (exwm-input--read-keys "Translate from" ?\C-g)) (when original (setq simulated (exwm-input--read-keys - (format "Simulate %s as" (key-description original)) + (format "Translate from %s to" + (key-description original)) ?\C-g))) (list original simulated))) (exwm--log "original: %s, simulated: %s" original-key simulated-key) -- cgit 1.4.1 From 1772b984522c800b1ecd02a5f7fd2b44ce15f3de Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 27 Oct 2019 00:00:00 +0000 Subject: Loosen the requirements for input focus update * exwm-input.el (exwm-input--on-buffer-list-update): No more check on current buffer (the check on buffer name was dropped in a1cf0d9b8). --- exwm-input.el | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 34fe2221b7..da242e9a6a 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -278,8 +278,7 @@ ARGS are additional arguments to CALLBACK." "Run in `buffer-list-update-hook' to track input focus." (when (and (not (eq this-command #'handle-switch-frame)) (not exwm-input--skip-buffer-list-update) - (not (exwm-workspace--client-p)) - (eq (current-buffer) (window-buffer))) + (not (exwm-workspace--client-p))) (exwm--log "current-buffer=%S selected-window=%S" (current-buffer) (selected-window)) (redirect-frame-focus (selected-frame) nil) -- cgit 1.4.1 From 3420cd24d107bf9a0396089bb00396e6ab35447e Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 3 Nov 2019 00:00:00 +0000 Subject: Add support for accessing keymaps in char-mode * exwm-input.el (exwm-input-invoke-factory): New macro for generating new commands that invoke corresponding prefix keys. --- exwm-input.el | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/exwm-input.el b/exwm-input.el index da242e9a6a..4ba39f7ae4 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -1035,6 +1035,21 @@ where both ORIGINAL-KEY and SIMULATED-KEY are key sequences." (dolist (key keys) (exwm-input--fake-key key))))) +;;;###autoload +(defmacro exwm-input-invoke-factory (keys) + "Make a command that invokes KEYS when called. + +One use is to access the keymap bound to KEYS (as prefix keys) in char-mode." + (let* ((keys (kbd keys)) + (description (key-description keys))) + `(defun ,(intern (concat "exwm-input--invoke--" description)) () + ,(format "Invoke `%s'." description) + (interactive) + (mapc (lambda (key) + (exwm-input--cache-event key t) + (exwm-input--unread-event key)) + ',(listify-key-sequence keys))))) + (defun exwm-input--on-pre-command () "Run in `pre-command-hook'." (unless (memq this-command exwm-input-pre-post-command-blacklist) -- cgit 1.4.1 From 6a3e9b2c64d644ed468684a2a867c535e50ef66a Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 1 Dec 2019 00:00:00 +0000 Subject: Avoid redundant input focus transfer * exwm-input.el (exwm-input--set-focus): Check for current focused X window before making an input focus transfer. (exwm-input--on-buffer-list-update): Remove the `this-command' check. --- exwm-input.el | 71 ++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 4ba39f7ae4..b493b9357d 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -164,32 +164,48 @@ This value should always be overwritten.") (defun exwm-input--set-focus (id) "Set input focus to window ID in a proper way." - (when (exwm--id->buffer id) - (exwm--log "id=#x%x" id) - (with-current-buffer (exwm--id->buffer id) - (exwm-input--update-timestamp - (lambda (timestamp id send-input-focus wm-take-focus) - (when send-input-focus - (xcb:+request exwm--connection - (make-instance 'xcb:SetInputFocus - :revert-to xcb:InputFocus:Parent - :focus id - :time timestamp))) - (when wm-take-focus - (let ((event (make-instance 'xcb:icccm:WM_TAKE_FOCUS - :window id - :time timestamp))) - (setq event (xcb:marshal event exwm--connection)) + (let ((from (slot-value (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:GetInputFocus)) + 'focus)) + tree) + (if (or (exwm--id->buffer from) + (eq from id)) + (exwm--log "#x%x => #x%x" (or from 0) (or id 0)) + ;; Attempt to find the top-level X window for a 'focus proxy'. + (unless (= from xcb:Window:None) + (setq tree (xcb:+request-unchecked+reply exwm--connection + (make-instance 'xcb:QueryTree + :window from))) + (when tree + (setq from (slot-value tree 'parent)))) + (exwm--log "#x%x (corrected) => #x%x" (or from 0) (or id 0))) + (when (and (exwm--id->buffer id) + ;; Avoid redundant input focus transfer. + (not (eq from id))) + (with-current-buffer (exwm--id->buffer id) + (exwm-input--update-timestamp + (lambda (timestamp id send-input-focus wm-take-focus) + (when send-input-focus (xcb:+request exwm--connection - (make-instance 'xcb:icccm:SendEvent - :destination id - :event event)))) - (exwm-input--set-active-window id) - (xcb:flush exwm--connection)) - id - (or exwm--hints-input - (not (memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols))) - (memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols))))) + (make-instance 'xcb:SetInputFocus + :revert-to xcb:InputFocus:Parent + :focus id + :time timestamp))) + (when wm-take-focus + (let ((event (make-instance 'xcb:icccm:WM_TAKE_FOCUS + :window id + :time timestamp))) + (setq event (xcb:marshal event exwm--connection)) + (xcb:+request exwm--connection + (make-instance 'xcb:icccm:SendEvent + :destination id + :event event)))) + (exwm-input--set-active-window id) + (xcb:flush exwm--connection)) + id + (or exwm--hints-input + (not (memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols))) + (memq xcb:Atom:WM_TAKE_FOCUS exwm--protocols)))))) (defun exwm-input--update-timestamp (callback &rest args) "Fetch the latest timestamp from the server and feed it to CALLBACK. @@ -276,9 +292,8 @@ ARGS are additional arguments to CALLBACK." (defun exwm-input--on-buffer-list-update () "Run in `buffer-list-update-hook' to track input focus." - (when (and (not (eq this-command #'handle-switch-frame)) - (not exwm-input--skip-buffer-list-update) - (not (exwm-workspace--client-p))) + (when (and (not (exwm-workspace--client-p)) + (not exwm-input--skip-buffer-list-update)) (exwm--log "current-buffer=%S selected-window=%S" (current-buffer) (selected-window)) (redirect-frame-focus (selected-frame) nil) -- cgit 1.4.1 From 8b05c2a30d112e5deaf1196fccfcc2d1eb273756 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 8 Dec 2019 00:00:00 +0000 Subject: Eliminate a compile warning * exwm-workspace.el (exwm-workspace-move-window): Replace the obsolete `run-window-configuration-change-hook'. --- exwm-workspace.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 705da308f2..8d11126b51 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -872,7 +872,7 @@ INDEX must not exceed the current number of workspaces." ;; Tiling. (if (get-buffer-window nil frame) (when (eq frame exwm-workspace--current) - (run-window-configuration-change-hook frame)) + (exwm-layout--refresh frame)) (set-window-buffer (get-buffer-window nil t) (other-buffer nil t)) (unless (eq frame exwm-workspace--current) -- cgit 1.4.1 From 65ec749bb074b51824576834d1f85c3c4301e999 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 15 Dec 2019 00:00:00 +0000 Subject: Fix moving Emacs frames * exwm.el (exwm--on-ClientMessage): Avoid moving workspace frames; Move floating X window when trying to move the floating frame. --- exwm.el | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/exwm.el b/exwm.el index 36dbcb677f..a2fbd03439 100644 --- a/exwm.el +++ b/exwm.el @@ -479,7 +479,33 @@ ) ((= direction xcb:ewmh:_NET_WM_MOVERESIZE_CANCEL) (exwm-floating--stop-moveresize)) - (t (exwm-floating--start-moveresize id direction)))))) + ;; In case it's a workspace frame. + ((and (not buffer) + (catch 'break + (dolist (f exwm-workspace--list) + (when (or (eq id (frame-parameter f 'exwm-outer-id)) + (eq id (frame-parameter f 'exwm-id))) + (throw 'break t))) + nil))) + (t + ;; In case it's a floating frame, + ;; move the corresponding X window instead. + (unless buffer + (catch 'break + (dolist (pair exwm--id-buffer-alist) + (with-current-buffer (cdr pair) + (when + (and exwm--floating-frame + (or (eq id + (frame-parameter exwm--floating-frame + 'exwm-outer-id)) + (eq id + (frame-parameter exwm--floating-frame + 'exwm-id)))) + (setq id exwm--id) + (throw 'break nil)))))) + ;; Start to move it. + (exwm-floating--start-moveresize id direction)))))) ;; _NET_REQUEST_FRAME_EXTENTS ((= type xcb:Atom:_NET_REQUEST_FRAME_EXTENTS) (let ((buffer (exwm--id->buffer id)) -- cgit 1.4.1 From 27a884e9472485e15a9b7dc7b62985a956c5f20d Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 2 Feb 2020 00:00:00 +0000 Subject: Update copyright year to 2020 --- exwm-cm.el | 2 +- exwm-config.el | 2 +- exwm-core.el | 2 +- exwm-floating.el | 2 +- exwm-input.el | 2 +- exwm-layout.el | 2 +- exwm-manage.el | 2 +- exwm-randr.el | 2 +- exwm-systemtray.el | 2 +- exwm-workspace.el | 2 +- exwm-xim.el | 2 +- exwm.el | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/exwm-cm.el b/exwm-cm.el index 1c33f875a6..9228477858 100644 --- a/exwm-cm.el +++ b/exwm-cm.el @@ -1,6 +1,6 @@ ;;; exwm-cm.el --- Compositing Manager for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2016-2019 Free Software Foundation, Inc. +;; Copyright (C) 2016-2020 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-config.el b/exwm-config.el index 4454ea5a68..b47a9ba832 100644 --- a/exwm-config.el +++ b/exwm-config.el @@ -1,6 +1,6 @@ ;;; exwm-config.el --- Predefined configurations -*- lexical-binding: t -*- -;; Copyright (C) 2015-2019 Free Software Foundation, Inc. +;; Copyright (C) 2015-2020 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-core.el b/exwm-core.el index 9e2829165c..7e37a71b6c 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -1,6 +1,6 @@ ;;; exwm-core.el --- Core definitions -*- lexical-binding: t -*- -;; Copyright (C) 2015-2019 Free Software Foundation, Inc. +;; Copyright (C) 2015-2020 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-floating.el b/exwm-floating.el index 189acc40be..115dd3b17b 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -1,6 +1,6 @@ ;;; exwm-floating.el --- Floating Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015-2019 Free Software Foundation, Inc. +;; Copyright (C) 2015-2020 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-input.el b/exwm-input.el index b493b9357d..17ee6ec9a2 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -1,6 +1,6 @@ ;;; exwm-input.el --- Input Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015-2019 Free Software Foundation, Inc. +;; Copyright (C) 2015-2020 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-layout.el b/exwm-layout.el index 6275b794bd..cb7ad56ed0 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -1,6 +1,6 @@ ;;; exwm-layout.el --- Layout Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015-2019 Free Software Foundation, Inc. +;; Copyright (C) 2015-2020 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-manage.el b/exwm-manage.el index d657b4d4f5..a7866f1ef8 100644 --- a/exwm-manage.el +++ b/exwm-manage.el @@ -1,7 +1,7 @@ ;;; exwm-manage.el --- Window Management Module for -*- lexical-binding: t -*- ;;; EXWM -;; Copyright (C) 2015-2019 Free Software Foundation, Inc. +;; Copyright (C) 2015-2020 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-randr.el b/exwm-randr.el index c028c30296..7acceb1324 100644 --- a/exwm-randr.el +++ b/exwm-randr.el @@ -1,6 +1,6 @@ ;;; exwm-randr.el --- RandR Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015-2019 Free Software Foundation, Inc. +;; Copyright (C) 2015-2020 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-systemtray.el b/exwm-systemtray.el index 35010ee341..3d0990ec81 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -1,7 +1,7 @@ ;;; exwm-systemtray.el --- System Tray Module for -*- lexical-binding: t -*- ;;; EXWM -;; Copyright (C) 2016-2019 Free Software Foundation, Inc. +;; Copyright (C) 2016-2020 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-workspace.el b/exwm-workspace.el index 8d11126b51..4da97d9282 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -1,6 +1,6 @@ ;;; exwm-workspace.el --- Workspace Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2015-2019 Free Software Foundation, Inc. +;; Copyright (C) 2015-2020 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm-xim.el b/exwm-xim.el index 5530c87c14..6a3b888b47 100644 --- a/exwm-xim.el +++ b/exwm-xim.el @@ -1,6 +1,6 @@ ;;; exwm-xim.el --- XIM Module for EXWM -*- lexical-binding: t -*- -;; Copyright (C) 2019 Free Software Foundation, Inc. +;; Copyright (C) 2019-2020 Free Software Foundation, Inc. ;; Author: Chris Feng diff --git a/exwm.el b/exwm.el index a2fbd03439..112f42db1f 100644 --- a/exwm.el +++ b/exwm.el @@ -1,6 +1,6 @@ ;;; exwm.el --- Emacs X Window Manager -*- lexical-binding: t -*- -;; Copyright (C) 2015-2019 Free Software Foundation, Inc. +;; Copyright (C) 2015-2020 Free Software Foundation, Inc. ;; Author: Chris Feng ;; Maintainer: Chris Feng -- cgit 1.4.1 From 36d2f0056eff396d115d4cbf5777221fc5bb9c4c Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 2 Feb 2020 00:00:00 +0000 Subject: Refactor color-related code * exwm-core.el (exwm--color->pixel): New function for converting color to TrueColor pixel. * exwm-floating.el (exwm-floating--border-pixel) (exwm-floating--border-colormap, exwm-floating--init-border): Removed. (exwm-floating-border-color, exwm-floating--set-floating): Use `exwm--color->pixel' and only support TrueColor. --- exwm-core.el | 9 +++++++ exwm-floating.el | 78 ++++++++++++++------------------------------------------ 2 files changed, 28 insertions(+), 59 deletions(-) diff --git a/exwm-core.el b/exwm-core.el index 7e37a71b6c..553fb4b4e3 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -184,6 +184,15 @@ least SECS seconds later." (if mouse-autoselect-window xcb:EventMask:EnterWindow 0))) +(defun exwm--color->pixel (color) + "Convert COLOR to PIXEL (index in TrueColor colormap)." + (when (and color + (eq (x-display-visual-class) 'true-color)) + (let ((rgb (x-color-values color))) + (logior (lsh (lsh (pop rgb) -8) 16) + (lsh (lsh (pop rgb) -8) 8) + (lsh (pop rgb) -8))))) + ;; Internal variables (defvar-local exwm--id nil) ;window ID (defvar-local exwm--configurations nil) ;initial configurations. diff --git a/exwm-floating.el b/exwm-floating.el index 115dd3b17b..d1882cf746 100644 --- a/exwm-floating.el +++ b/exwm-floating.el @@ -44,9 +44,6 @@ context of the corresponding buffer." context of the corresponding buffer." :type 'hook) -(defvar exwm-floating--border-pixel nil - "Border pixel drawn around floating X windows.") - (defcustom exwm-floating-border-color "navy" "Border color of floating windows." :type 'color @@ -54,20 +51,20 @@ context of the corresponding buffer." :set (lambda (symbol value) (set-default symbol value) ;; Change border color for all floating X windows. - (exwm-floating--init-border) - (dolist (pair exwm--id-buffer-alist) - (with-current-buffer (cdr pair) - (when exwm--floating-frame - (xcb:+request exwm--connection - (make-instance 'xcb:ChangeWindowAttributes - :window - (frame-parameter exwm--floating-frame - 'exwm-container) - :value-mask xcb:CW:BorderPixel - :border-pixel - exwm-floating--border-pixel))))) (when exwm--connection - (xcb:flush exwm--connection)))) + (let ((border-pixel (exwm--color->pixel value))) + (when border-pixel + (dolist (pair exwm--id-buffer-alist) + (with-current-buffer (cdr pair) + (when exwm--floating-frame + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window + (frame-parameter exwm--floating-frame + 'exwm-container) + :value-mask xcb:CW:BorderPixel + :border-pixel border-pixel))))) + (xcb:flush exwm--connection)))))) (defcustom exwm-floating-border-width 1 "Border width of floating windows." @@ -104,11 +101,6 @@ context of the corresponding buffer." (when exwm--connection (xcb:flush exwm--connection))))) -(defvar exwm-floating--border-colormap nil - "Colormap used by the border pixel. - -This is also used by X window containers.") - ;; Cursors for moving/resizing a window (defvar exwm-floating--cursor-move nil) (defvar exwm-floating--cursor-top-left nil) @@ -276,7 +268,8 @@ This is also used by X window containers.") (floating-mode-line (plist-get exwm--configurations 'floating-mode-line)) (floating-header-line (plist-get exwm--configurations - 'floating-header-line))) + 'floating-header-line)) + (border-pixel (exwm--color->pixel exwm-floating-border-color))) (if floating-mode-line (setq exwm--mode-line-format (or exwm--mode-line-format mode-line-format) @@ -323,15 +316,12 @@ This is also used by X window containers.") :class xcb:WindowClass:InputOutput :visual 0 :value-mask (logior xcb:CW:BackPixmap - (if exwm-floating--border-pixel + (if border-pixel xcb:CW:BorderPixel 0) - xcb:CW:OverrideRedirect - (if exwm-floating--border-colormap - xcb:CW:Colormap 0)) + xcb:CW:OverrideRedirect) :background-pixmap xcb:BackPixmap:ParentRelative - :border-pixel exwm-floating--border-pixel - :override-redirect 1 - :colormap exwm-floating--border-colormap)) + :border-pixel border-pixel + :override-redirect 1)) (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_NAME :window frame-container @@ -758,39 +748,9 @@ Both DELTA-X and DELTA-Y default to 1. This command should be bound locally." nil nil)) (xcb:flush exwm--connection))) -(defun exwm-floating--init-border () - "Initialize border colormap and pixel." - (exwm--log) - ;; Use the default colormap. - (unless exwm-floating--border-colormap - (with-slots (roots) (xcb:get-setup exwm--connection) - (with-slots (default-colormap) (car roots) - (setq exwm-floating--border-colormap default-colormap)))) - ;; Free any previously allocated pixel. - (when exwm-floating--border-pixel - (xcb:+request exwm--connection - (make-instance 'xcb:FreeColors - :cmap exwm-floating--border-colormap - :plane-mask 0 - :pixels (vector exwm-floating--border-pixel))) - (setq exwm-floating--border-pixel nil)) - ;; Allocate new pixel. - (let ((color (x-color-values (or exwm-floating-border-color ""))) - reply) - (when color - (setq reply (xcb:+request-unchecked+reply exwm--connection - (make-instance 'xcb:AllocColor - :cmap exwm-floating--border-colormap - :red (pop color) - :green (pop color) - :blue (pop color)))) - (when reply - (setq exwm-floating--border-pixel (slot-value reply 'pixel)))))) - (defun exwm-floating--init () "Initialize floating module." (exwm--log) - (exwm-floating--init-border) ;; Initialize cursors for moving/resizing a window (xcb:cursor:init exwm--connection) (setq exwm-floating--cursor-move -- cgit 1.4.1 From 48db94f48bea1137132345abfe8256cfc6219248 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 2 Feb 2020 00:00:00 +0000 Subject: Add solid background support to systemtray * exwm-systemtray.el (exwm-systemtray-background-color): New user option for configuring systemtray background color. (exwm-systemtray--init): Configure background color for systemtray. --- exwm-systemtray.el | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/exwm-systemtray.el b/exwm-systemtray.el index 3d0990ec81..20dc5226cf 100644 --- a/exwm-systemtray.el +++ b/exwm-systemtray.el @@ -67,13 +67,44 @@ You shall use the default value if using auto-hide minibuffer." "Gap between icons." :type 'integer) +(defvar exwm-systemtray--embedder-window nil "The embedder window.") + +(defcustom exwm-systemtray-background-color nil + "Background color of systemtray. + +This should be a color, or nil for transparent background." + :type '(choice (const :tag "Transparent" nil) + (color)) + :initialize #'custom-initialize-default + :set (lambda (symbol value) + (set-default symbol value) + ;; Change the background color for embedder. + (when (and exwm--connection + exwm-systemtray--embedder-window) + (let ((background-pixel (exwm--color->pixel value))) + (xcb:+request exwm--connection + (make-instance 'xcb:ChangeWindowAttributes + :window exwm-systemtray--embedder-window + :value-mask (logior xcb:CW:BackPixmap + (if background-pixel + xcb:CW:BackPixel 0)) + :background-pixmap + xcb:BackPixmap:ParentRelative + :background-pixel background-pixel)) + ;; Unmap & map to take effect immediately. + (xcb:+request exwm--connection + (make-instance 'xcb:UnmapWindow + :window exwm-systemtray--embedder-window)) + (xcb:+request exwm--connection + (make-instance 'xcb:MapWindow + :window exwm-systemtray--embedder-window)) + (xcb:flush exwm--connection))))) + ;; GTK icons require at least 16 pixels to show normally. (defconst exwm-systemtray--icon-min-size 16 "Minimum icon size.") (defvar exwm-systemtray--connection nil "The X connection.") -(defvar exwm-systemtray--embedder-window nil "The embedder window.") - (defvar exwm-systemtray--list nil "The icon list.") (defvar exwm-systemtray--selection-owner-window nil @@ -438,6 +469,7 @@ You shall use the default value if using auto-hide minibuffer." :data xcb:systemtray:ORIENTATION:HORZ))) ;; Create the embedder. (let ((id (xcb:generate-id exwm-systemtray--connection)) + (background-pixel (exwm--color->pixel exwm-systemtray-background-color)) frame parent depth y) (setq exwm-systemtray--embedder-window id) (if (exwm-workspace--minibuffer-own-frame-p) @@ -473,8 +505,12 @@ You shall use the default value if using auto-hide minibuffer." :border-width 0 :class xcb:WindowClass:InputOutput :visual 0 - :value-mask (logior xcb:CW:BackPixmap xcb:CW:EventMask) + :value-mask (logior xcb:CW:BackPixmap + (if background-pixel + xcb:CW:BackPixel 0) + xcb:CW:EventMask) :background-pixmap xcb:BackPixmap:ParentRelative + :background-pixel background-pixel :event-mask xcb:EventMask:SubstructureNotify)) ;; Set _NET_WM_NAME. (xcb:+request exwm-systemtray--connection -- cgit 1.4.1 From ed0f63327dbfa7ef2ebe1dc37bfc38d40d531c29 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 1 Mar 2020 00:00:00 +0000 Subject: Make exwm-mode buffers work with winner-mode * exwm-input.el (exwm-input--noop): New placeholder command. (exwm-input--on-pre-command, exwm-input--on-post-command): Ignore this command. (exwm-input--on-KeyPress-line-mode): Set `last-command' to make winner-undo start over from the newest config; run `post-command-hook' to make winner-mode save configs; run `pre-command-hook' in case required by some other package. --- exwm-input.el | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 17ee6ec9a2..82ce82a4e7 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -649,6 +649,10 @@ Current buffer must be an `exwm-mode' buffer." (lookup-key (current-local-map) (vector event)) (gethash event exwm-input--simulation-keys))) +(defun exwm-input--noop (&rest _args) + "A placeholder command." + (interactive)) + (defun exwm-input--on-KeyPress-line-mode (key-press raw-data) "Parse X KeyPress event to Emacs key event and then feed the command loop." (with-slots (detail state) key-press @@ -678,10 +682,15 @@ Current buffer must be an `exwm-mode' buffer." :destination (slot-value key-press 'event) :event-mask xcb:EventMask:NoEvent :event raw-data))) - ;; Make Emacs aware of this event when defining keyboard macros. - (when (and defining-kbd-macro event) - (set-transient-map '(keymap (t . (lambda () (interactive))))) - (exwm-input--unread-event event))) + (if defining-kbd-macro + (when event + ;; Make Emacs aware of this event when defining keyboard macros. + (set-transient-map `(keymap (t . ,#'exwm-input--noop))) + (exwm-input--unread-event event)) + ;; Fool some packages into thinking there is a change in the buffer. + (setq last-command #'exwm-input--noop) + (run-hooks 'pre-command-hook) + (run-hooks 'post-command-hook))) (xcb:+request exwm--connection (make-instance 'xcb:AllowEvents :mode mode @@ -1067,12 +1076,14 @@ One use is to access the keymap bound to KEYS (as prefix keys) in char-mode." (defun exwm-input--on-pre-command () "Run in `pre-command-hook'." - (unless (memq this-command exwm-input-pre-post-command-blacklist) + (unless (or (eq this-command #'exwm-input--noop) + (memq this-command exwm-input-pre-post-command-blacklist)) (setq exwm-input--during-command t))) (defun exwm-input--on-post-command () "Run in `post-command-hook'." - (setq exwm-input--during-command nil)) + (unless (eq this-command #'exwm-input--noop) + (setq exwm-input--during-command nil))) (defun exwm-input--on-minibuffer-setup () "Run in `minibuffer-setup-hook' to grab keyboard if necessary." -- cgit 1.4.1 From dc5cc1dead101f7a06e00513fd2a1821ab1f3384 Mon Sep 17 00:00:00 2001 From: Curiosidad-Racional Date: Sat, 21 Mar 2020 13:51:09 +0100 Subject: Fixed fail update when current buffer differs from window Local variable `exwm--input-mode' from different buffer when current buffer don't contains #id window. `with-current-buffer' changes buffer after `cl-case' with local `exwm--input-mode' --- exwm-input.el | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 82ce82a4e7..2368331cd0 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -749,20 +749,20 @@ button event." "Update the propertized `mode-line-process' for window ID." (exwm--log "#x%x" id) (let (help-echo cmd mode) - (cl-case exwm--input-mode - (line-mode - (setq mode "line" - help-echo "mouse-1: Switch to char-mode" - cmd (lambda () - (interactive) - (exwm-input-release-keyboard id)))) - (char-mode - (setq mode "char" - help-echo "mouse-1: Switch to line-mode" - cmd (lambda () - (interactive) - (exwm-input-grab-keyboard id))))) (with-current-buffer (exwm--id->buffer id) + (cl-case exwm--input-mode + (line-mode + (setq mode "line" + help-echo "mouse-1: Switch to char-mode" + cmd (lambda () + (interactive) + (exwm-input-release-keyboard id)))) + (char-mode + (setq mode "char" + help-echo "mouse-1: Switch to line-mode" + cmd (lambda () + (interactive) + (exwm-input-grab-keyboard id))))) (setq mode-line-process `(": " (:propertize ,mode -- cgit 1.4.1 From 5f9ba9772f483729c479c316d456fa87888c062f Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 29 Mar 2020 00:00:00 +0000 Subject: Additional fix for winner-mode * exwm-input.el (exwm-input--on-KeyPress-line-mode): Check incomplete key presses (which generate no valid events) before running `pre-command-hook' and `post-command-hook'. --- exwm-input.el | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 82ce82a4e7..a891710da2 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -688,9 +688,10 @@ Current buffer must be an `exwm-mode' buffer." (set-transient-map `(keymap (t . ,#'exwm-input--noop))) (exwm-input--unread-event event)) ;; Fool some packages into thinking there is a change in the buffer. - (setq last-command #'exwm-input--noop) - (run-hooks 'pre-command-hook) - (run-hooks 'post-command-hook))) + (when event + (setq last-command #'exwm-input--noop) + (run-hooks 'pre-command-hook) + (run-hooks 'post-command-hook)))) (xcb:+request exwm--connection (make-instance 'xcb:AllowEvents :mode mode -- cgit 1.4.1 From 3dbf1b1bed96d9ac3ee2718134ded619f46951bb Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 29 Mar 2020 00:00:00 +0000 Subject: Add support for _NET_WM_STATE_HIDDEN * exwm-layout.el (exwm-layout--set-ewmh-state): New function for setting _NET_WM_STATE (according to local state). * exwm.el (exwm--init-icccm-ewmh): Declare the support for this atom. --- exwm-layout.el | 23 ++++++++++++++++------- exwm.el | 2 +- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index cb7ad56ed0..170c2bece2 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -80,6 +80,14 @@ (buffer-local-value 'exwm-state (exwm--id->buffer id)) exwm-state))) +(defun exwm-layout--set-ewmh-state (xwin) + "Set _NET_WM_STATE." + (with-current-buffer (exwm--id->buffer xwin) + (xcb:+request exwm--connection + (make-instance 'xcb:ewmh:set-_NET_WM_STATE + :window exwm--id + :data exwm--ewmh-state)))) + (defun exwm-layout--fullscreen-p () (when (derived-mode-p 'exwm-mode) (memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state))) @@ -135,6 +143,9 @@ (exwm--set-geometry id x y width height) (xcb:+request exwm--connection (make-instance 'xcb:MapWindow :window id)) (exwm-layout--set-state id xcb:icccm:WM_STATE:NormalState) + (setq exwm--ewmh-state + (delq xcb:Atom:_NET_WM_STATE_HIDDEN exwm--ewmh-state)) + (exwm-layout--set-ewmh-state id) (exwm-layout--auto-iconify))) (xcb:flush exwm--connection)) @@ -168,6 +179,8 @@ :window id :value-mask xcb:CW:EventMask :event-mask (exwm--get-client-event-mask))) (exwm-layout--set-state id xcb:icccm:WM_STATE:IconicState) + (cl-pushnew xcb:Atom:_NET_WM_STATE_HIDDEN exwm--ewmh-state) + (exwm-layout--set-ewmh-state id) (exwm-layout--auto-iconify) (xcb:flush exwm--connection)))) @@ -191,13 +204,10 @@ xcb:ConfigWindow:StackMode) :border-width 0 :stack-mode xcb:StackMode:Above)) - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_WM_STATE - :window exwm--id - :data (vector xcb:Atom:_NET_WM_STATE_FULLSCREEN))) + (cl-pushnew xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) + (exwm-layout--set-ewmh-state id) (xcb:flush exwm--connection) (set-window-dedicated-p (get-buffer-window) t) - (cl-pushnew xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) (exwm-input--release-keyboard exwm--id))) ;;;###autoload @@ -223,8 +233,7 @@ (let ((window (get-buffer-window nil t))) (when window (exwm-layout--show exwm--id window)))) - (xcb:+request exwm--connection - (make-instance 'xcb:ewmh:set-_NET_WM_STATE :window exwm--id :data [])) + (exwm-layout--set-ewmh-state id) (xcb:flush exwm--connection) (set-window-dedicated-p (get-buffer-window) nil) (when (eq 'line-mode exwm--selected-input-mode) diff --git a/exwm.el b/exwm.el index 112f42db1f..09192faec4 100644 --- a/exwm.el +++ b/exwm.el @@ -673,7 +673,7 @@ ;; xcb:Atom:_NET_WM_STATE_SHADED ;; xcb:Atom:_NET_WM_STATE_SKIP_TASKBAR ;; xcb:Atom:_NET_WM_STATE_SKIP_PAGER - ;; xcb:Atom:_NET_WM_STATE_HIDDEN + xcb:Atom:_NET_WM_STATE_HIDDEN xcb:Atom:_NET_WM_STATE_FULLSCREEN ;; xcb:Atom:_NET_WM_STATE_ABOVE ;; xcb:Atom:_NET_WM_STATE_BELOW -- cgit 1.4.1 From cddb3429d2219ed5e15761e4b7b61be1a14970bd Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Wed, 15 Apr 2020 00:00:00 +0000 Subject: Abort recursive edit before switching workspaces * exwm-workspace.el (exwm-workspace-switch): Abort recursive edit before switching to other workspace. This avoids the usual `set-window-configuration' calls (e.g., by `eval-expression') to switch *us back to the previous workspace. --- exwm-workspace.el | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 4da97d9282..1f832a66fd 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -587,6 +587,16 @@ for internal use only." (when (or force (not (eq frame exwm-workspace--current))) (unless (window-live-p window) (setq window (frame-selected-window frame))) + ;; Close the (possible) active minibuffer. Aborting the recursive edit + ;; level will abort the execution of this very command. Schedule it to + ;; run immediately afterward. This must all be done before the new + ;; workspace is selected in the new workspace, in order to avoid the focus + ;; to go back to the previous frame due to resetting the window + ;; configuration (e.g. `eval-expression'). + (when (active-minibuffer-window) + (exwm--defer 0 (lambda () + (exwm-workspace-switch frame-or-index force))) + (abort-recursive-edit)) ;; Raise this frame. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow @@ -635,12 +645,6 @@ for internal use only." (select-window window) (x-focus-frame (window-frame window)) ;The real input focus. (set-frame-parameter frame 'exwm-selected-window nil) - ;; Close the (possible) active minibuffer - (when (active-minibuffer-window) - (exwm--defer 0 (lambda () - ;; Might be aborted by then. - (when (active-minibuffer-window) - (abort-recursive-edit))))) (if (exwm-workspace--minibuffer-own-frame-p) ;; Resize the minibuffer frame. (exwm-workspace--resize-minibuffer-frame) -- cgit 1.4.1 From 373eda99bd01e5bf53abc46c8a3c096bceeff72c Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Wed, 15 Apr 2020 00:00:00 +0000 Subject: Stop aborting recursive edit upon switching workspaces. * exwm-workspace.el (exwm-workspace-switch): Stop aborting recursive edit upon switching workspaces. Users should handle it just like in regular Emacs (possibly customizing `enable-recursive-minibuffers'). --- exwm-workspace.el | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index 1f832a66fd..f63a0a36eb 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -587,16 +587,6 @@ for internal use only." (when (or force (not (eq frame exwm-workspace--current))) (unless (window-live-p window) (setq window (frame-selected-window frame))) - ;; Close the (possible) active minibuffer. Aborting the recursive edit - ;; level will abort the execution of this very command. Schedule it to - ;; run immediately afterward. This must all be done before the new - ;; workspace is selected in the new workspace, in order to avoid the focus - ;; to go back to the previous frame due to resetting the window - ;; configuration (e.g. `eval-expression'). - (when (active-minibuffer-window) - (exwm--defer 0 (lambda () - (exwm-workspace-switch frame-or-index force))) - (abort-recursive-edit)) ;; Raise this frame. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow -- cgit 1.4.1 From 007916c64763b53f37fb7c759a145857f63c1c76 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Wed, 15 Apr 2020 00:00:00 +0000 Subject: Fix window manager name * exwm.el (exwm--init-icccm-ewmh): Correct _NET_WM_NAME to the name of the window manager on the _NET_SUPPORTING_WM_CHECK. --- exwm.el | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/exwm.el b/exwm.el index 09192faec4..e94b283a63 100644 --- a/exwm.el +++ b/exwm.el @@ -730,10 +730,11 @@ :visual 0 :value-mask xcb:CW:OverrideRedirect :override-redirect 1)) - ;; Set _NET_WM_NAME + ;; Set _NET_WM_NAME. Must be set to the name of the window manager, as + ;; required by wm-spec. (xcb:+request exwm--connection (make-instance 'xcb:ewmh:set-_NET_WM_NAME - :window new-id :data "EXWM: exwm--guide-window")) + :window new-id :data "EXWM")) (dolist (i (list exwm--root new-id)) ;; Set _NET_SUPPORTING_WM_CHECK (xcb:+request exwm--connection -- cgit 1.4.1 From ce2625d050b1a4aa5815dc488880b85d0270d743 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Fri, 17 Apr 2020 00:00:00 +0000 Subject: Run hook upon input mode change * exwm-input.el (exwm-input-input-mode-change-hook): Add new hook for code to run upon input mode change. (exwm-input--grab-keyboard, exwm-input--release-keyboard): Run it. --- exwm-input.el | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 9ba3502a4d..2d784073e7 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -145,6 +145,10 @@ This value should always be overwritten.") (defvar exwm-input--event-hook nil "Hook to run when EXWM receives an event.") +(defvar exwm-input-input-mode-change-hook nil + "Hook to run when an input mode changes on an `exwm-mode' buffer. +Current buffer will be the `exwm-mode' buffer when this hook runs.") + (defvar exwm-workspace--current) (declare-function exwm-floating--do-moveresize "exwm-floating.el" (data _synthetic)) @@ -793,7 +797,8 @@ button event." (let ((buffer (exwm--id->buffer id))) (when buffer (with-current-buffer buffer - (setq exwm--input-mode 'line-mode)))))) + (setq exwm--input-mode 'line-mode) + (run-hooks 'exwm-input-input-mode-change-hook)))))) (defun exwm-input--release-keyboard (&optional id) "Ungrab all key events on window ID." @@ -810,7 +815,8 @@ button event." (let ((buffer (exwm--id->buffer id))) (when buffer (with-current-buffer buffer - (setq exwm--input-mode 'char-mode)))))) + (setq exwm--input-mode 'char-mode) + (run-hooks 'exwm-input-input-mode-change-hook)))))) ;;;###autoload (defun exwm-input-grab-keyboard (&optional id) -- cgit 1.4.1 From c7c42582b7342f20c22b8a040eab72d3ce9f37b5 Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Wed, 6 May 2020 00:00:00 +0000 Subject: Prevent jumping back to previous workspace * exwm-workspace.el (exwm-workspace-switch): Focus out old frame before switching to the new one to prevent jumping back to previous workspace. --- exwm-workspace.el | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/exwm-workspace.el b/exwm-workspace.el index f63a0a36eb..cff17f3a11 100644 --- a/exwm-workspace.el +++ b/exwm-workspace.el @@ -587,6 +587,11 @@ for internal use only." (when (or force (not (eq frame exwm-workspace--current))) (unless (window-live-p window) (setq window (frame-selected-window frame))) + (when (and (not (eq frame old-frame)) + (frame-live-p old-frame)) + (with-selected-frame old-frame + (funcall exwm-workspace--original-handle-focus-out + (list 'focus-out frame)))) ;; Raise this frame. (xcb:+request exwm--connection (make-instance 'xcb:ConfigureWindow @@ -680,11 +685,6 @@ for internal use only." :dst-x (/ (frame-pixel-width frame) 2) :dst-y (/ (frame-pixel-height frame) 2))) (xcb:flush exwm--connection)))) - (when (and (not (eq frame old-frame)) - (frame-live-p old-frame)) - (with-selected-frame old-frame - (funcall exwm-workspace--original-handle-focus-out - (list 'focus-out frame)))) (funcall exwm-workspace--original-handle-focus-in (list 'focus-in frame)) (run-hooks 'exwm-workspace-switch-hook))) -- cgit 1.4.1 From 1d0df9144ef40b4d7dac5d73e5a4103e00343a6b Mon Sep 17 00:00:00 2001 From: Adrián Medraño Calvo Date: Wed, 6 May 2020 00:00:00 +0000 Subject: ; Upcase menu item --- exwm-core.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm-core.el b/exwm-core.el index 553fb4b4e3..76454894ab 100644 --- a/exwm-core.el +++ b/exwm-core.el @@ -334,7 +334,7 @@ One of `line-mode' or `char-mode'.") ("Switch to" :filter (lambda (&rest _args) (mapcar (lambda (i) - `[,(format "workspace %d" i) + `[,(format "Workspace %d" i) (lambda () (interactive) (exwm-workspace-switch ,i)) -- cgit 1.4.1 From 7f4b9cdb0f4122f561d26dcb39d405deeadf931e Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 10 May 2020 00:00:00 +0000 Subject: Make return key work in exwm-xim * exwm-xim.el (exwm-xim--handle-forward-event-request): Send all key events to Emacs while invoking an input method (don't rely on its keymap definition). --- exwm-xim.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exwm-xim.el b/exwm-xim.el index 6a3b888b47..acf718e27f 100644 --- a/exwm-xim.el +++ b/exwm-xim.el @@ -586,7 +586,8 @@ The actual XIM request is in client message data or a property." (unwind-protect (with-temp-buffer ;; Always show key strokes. - (let ((input-method-use-echo-area t)) + (let ((input-method-use-echo-area t) + (exwm-input-line-mode-passthrough t)) (setq result (funcall im-func event)) ;; Clear echo area for the input method. (message nil) -- cgit 1.4.1 From 624a30212cca1af99fda1417fd81c31eeba4cca8 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 10 May 2020 00:00:00 +0000 Subject: Clarify the use of `exwm-config-default' * exwm-config.el (exwm-config-example): Renamed from `exwm-config-default' to make it clear it's just an example. --- exwm-config.el | 5 ++++- exwm.el | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/exwm-config.el b/exwm-config.el index b47a9ba832..bb8258a714 100644 --- a/exwm-config.el +++ b/exwm-config.el @@ -28,7 +28,10 @@ (require 'exwm) (require 'ido) -(defun exwm-config-default () +(define-obsolete-function-alias 'exwm-config-default + #'exwm-config-example "27.1") + +(defun exwm-config-example () "Default configuration of EXWM." ;; Set the initial workspace number. (unless (get 'exwm-workspace-number 'saved-value) diff --git a/exwm.el b/exwm.el index e94b283a63..98736ac91e 100644 --- a/exwm.el +++ b/exwm.el @@ -46,7 +46,7 @@ ;; ;; (require 'exwm) ;; (require 'exwm-config) -;; (exwm-config-default) +;; (exwm-config-example) ;; ;; 3. Link or copy the file 'xinitrc' to '~/.xinitrc'. ;; 4. Launch EXWM in a console (e.g. tty1) with -- cgit 1.4.1 From 9f286212778c52a709302c7151e76b836db79ef5 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 24 May 2020 00:00:00 +0000 Subject: Bump version to 0.24 --- exwm.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exwm.el b/exwm.el index 98736ac91e..fc96ac7509 100644 --- a/exwm.el +++ b/exwm.el @@ -4,7 +4,7 @@ ;; Author: Chris Feng ;; Maintainer: Chris Feng -;; Version: 0.23 +;; Version: 0.24 ;; Package-Requires: ((xelb "0.18")) ;; Keywords: unix ;; URL: https://github.com/ch11ng/exwm -- cgit 1.4.1 From edb930005b0ba83051ca8a59b493e9a3c8ef580a Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 14 Jun 2020 00:00:00 +0000 Subject: Fix fullscreen states * exwm-layout.el (exwm-layout-set-fullscreen, exwm-layout-unset-fullscreen): Use `exwm--id' for interactive use. (exwm-layout-unset-fullscreen): Mandatorily clear fullscreen states. --- exwm-layout.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/exwm-layout.el b/exwm-layout.el index 170c2bece2..79d0c95bcd 100644 --- a/exwm-layout.el +++ b/exwm-layout.el @@ -205,7 +205,7 @@ :border-width 0 :stack-mode xcb:StackMode:Above)) (cl-pushnew xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state) - (exwm-layout--set-ewmh-state id) + (exwm-layout--set-ewmh-state exwm--id) (xcb:flush exwm--connection) (set-window-dedicated-p (get-buffer-window) t) (exwm-input--release-keyboard exwm--id))) @@ -233,7 +233,9 @@ (let ((window (get-buffer-window nil t))) (when window (exwm-layout--show exwm--id window)))) - (exwm-layout--set-ewmh-state id) + (setq exwm--ewmh-state + (delq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)) + (exwm-layout--set-ewmh-state exwm--id) (xcb:flush exwm--connection) (set-window-dedicated-p (get-buffer-window) nil) (when (eq 'line-mode exwm--selected-input-mode) -- cgit 1.4.1 From 0368127976bda29d35eed788edfe74644ecd3845 Mon Sep 17 00:00:00 2001 From: Chris Feng Date: Sun, 12 Jul 2020 00:00:00 +0000 Subject: Make button events working with winner-mode * exwm-input.el (exwm-input--fake-last-command): Extracted for reuse. (exwm-input--on-ButtonPress, exwm-input--on-KeyPress-line-mode): Use it. --- exwm-input.el | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/exwm-input.el b/exwm-input.el index 2d784073e7..decfc8128c 100644 --- a/exwm-input.el +++ b/exwm-input.el @@ -396,7 +396,7 @@ ARGS are additional arguments to CALLBACK." "Handle ButtonPress event." (let ((obj (make-instance 'xcb:ButtonPress)) (mode xcb:Allow:SyncPointer) - button-event window buffer frame) + button-event window buffer frame fake-last-command) (xcb:unmarshal obj data) (exwm--log "major-mode=%s buffer=%s" major-mode (buffer-name (current-buffer))) @@ -423,6 +423,7 @@ ARGS are additional arguments to CALLBACK." (exwm-floating--start-moveresize event)) (buffer ;; Click to focus + (setq fake-last-command t) (unless (eq window (selected-window)) (setq frame (window-frame window)) (unless (eq frame exwm-workspace--current) @@ -451,7 +452,10 @@ ARGS are additional arguments to CALLBACK." (setq mode (exwm-input--on-ButtonPress-char-mode))))))) (t ;; Replay this event by default. + (setq fake-last-command t) (setq mode xcb:Allow:ReplayPointer)))) + (when fake-last-command + (exwm-input--fake-last-command)) (xcb:+request exwm--connection (make-instance 'xcb:AllowEvents :mode mode :time xcb:Time:CurrentTime)) (xcb:flush exwm--connection)) @@ -657,6 +661,12 @@ Current buffer must be an `exwm-mode' buffer." "A placeholder command." (interactive)) +(defun exwm-input--fake-last-command () + "Fool some packages into thinking there is a change in the buffer." + (setq last-command #'exwm-input--noop) + (run-hooks 'pre-command-hook) + (run-hooks 'post-command-hook)) + (defun exwm-input--on-KeyPress-line-mode (key-press raw-data) "Parse X KeyPress event to Emacs key event and then feed the command loop." (with-slots (detail state) key-press @@ -686,16 +696,12 @@ Current buffer must be an `exwm-mode' buffer." :destination (slot-value key-press 'event) :event-mask xcb:EventMask:NoEvent :event raw-data))) - (if defining-kbd-macro - (when event - ;; Make Emacs aware of this event when defining keyboard macros. - (set-transient-map `(keymap (t . ,#'exwm-input--noop))) - (exwm-input--unread-event event)) - ;; Fool some packages into thinking there is a change in the buffer. - (when event - (setq last-command #'exwm-input--noop) - (run-hooks 'pre-command-hook) - (run-hooks 'post-command-hook)))) + (when event + (if (not defining-kbd-macro) + (exwm-input--fake-last-command) + ;; Make Emacs aware of this event when defining keyboard macros. + (set-transient-map `(keymap (t . ,#'exwm-input--noop))) + (exwm-input--unread-event event)))) (xcb:+request exwm--connection (make-instance 'xcb:AllowEvents :mode mode -- cgit 1.4.1