mirror of
https://github.com/ch11ng/exwm.git
synced 2024-11-23 11:47:58 +01:00
Moved to https://github.com/emacs-exwm/exwm
This commit is contained in:
parent
798dc60a9b
commit
4755cb6ee1
18 changed files with 19 additions and 9724 deletions
|
@ -1 +0,0 @@
|
||||||
README.md
|
|
10
.github/issue_template.md
vendored
Normal file
10
.github/issue_template.md
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# Project moved
|
||||||
|
|
||||||
|
EXWM has moved to the new location https://github.com/emacs-exwm/exwm. The move was
|
||||||
|
necessary since the EXWM author Chris Feng has been missing for a few years and
|
||||||
|
new maintainers were added to the EXWM project.
|
||||||
|
|
||||||
|
Please file new issues to [EXWM Issue
|
||||||
|
Tracker](https://github.com/emacs-exwm/exwm/issues).
|
||||||
|
|
||||||
|
Issues already in this tracker will continue to be handled here.
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,3 +0,0 @@
|
||||||
*.elc
|
|
||||||
*-pkg.el
|
|
||||||
*-autoloads.el
|
|
674
LICENSE
674
LICENSE
|
@ -1,674 +0,0 @@
|
||||||
GNU GENERAL PUBLIC LICENSE
|
|
||||||
Version 3, 29 June 2007
|
|
||||||
|
|
||||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
|
||||||
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.
|
|
||||||
|
|
||||||
{one line to give the program's name and a brief idea of what it does.}
|
|
||||||
Copyright (C) {year} {name of author}
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
{project} Copyright (C) {year} {fullname}
|
|
||||||
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
|
|
||||||
<http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
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
|
|
||||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
|
27
README.md
27
README.md
|
@ -1,21 +1,12 @@
|
||||||
# Emacs X Window Manager
|
# Project moved
|
||||||
|
|
||||||
EXWM (Emacs X Window Manager) is a full-featured tiling X window manager
|
EXWM has moved to the new location https://github.com/emacs-exwm/exwm. The move was
|
||||||
for Emacs built on top of [XELB](https://github.com/ch11ng/xelb).
|
necessary since the EXWM author Chris Feng has been missing for a few years and
|
||||||
It features:
|
new maintainers were added to the EXWM project.
|
||||||
+ Fully keyboard-driven operations
|
|
||||||
+ Hybrid layout modes (tiling & stacking)
|
|
||||||
+ Dynamic workspace support
|
|
||||||
+ ICCCM/EWMH compliance
|
|
||||||
+ (Optional) RandR (multi-monitor) support
|
|
||||||
+ (Optional) Builtin system tray
|
|
||||||
+ (Optional) Builtin input method
|
|
||||||
|
|
||||||
Please check out the
|
Please find the new repositories and wiki at the following locations:
|
||||||
[screenshots](https://github.com/ch11ng/exwm/wiki/Screenshots)
|
|
||||||
to get an overview of what EXWM is capable of,
|
* [XELB Repository](https://github.com/emacs-exwm/xelb)
|
||||||
and the [user guide](https://github.com/ch11ng/exwm/wiki)
|
* [EXWM Repository](https://github.com/emacs-exwm/exwm)
|
||||||
for a detailed explanation of its usage.
|
* [EXWM Wiki](https://github.com/emacs-exwm/exwm/wiki)
|
||||||
|
|
||||||
**Note**: If you install EXWM from source, it's recommended to install
|
|
||||||
XELB also from source (otherwise install both from GNU ELPA).
|
|
||||||
|
|
|
@ -1,199 +0,0 @@
|
||||||
;;; exwm-background.el --- X Background Module for EXWM -*- lexical-binding: t -*-
|
|
||||||
|
|
||||||
;; Copyright (C) 2022-2024 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
;; Author: Steven Allen <steven@stebalien.com>
|
|
||||||
|
|
||||||
;; 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
;;; Commentary:
|
|
||||||
|
|
||||||
;; This module adds X background color setting support to EXWM.
|
|
||||||
|
|
||||||
;; To use this module, load and enable it as follows:
|
|
||||||
;; (require 'exwm-background)
|
|
||||||
;; (exwm-background-enable)
|
|
||||||
;;
|
|
||||||
;; By default, this will apply the theme's background color. However, that
|
|
||||||
;; color can be customized via the `exwm-background-color' setting.
|
|
||||||
|
|
||||||
;;; Code:
|
|
||||||
|
|
||||||
(require 'exwm-core)
|
|
||||||
|
|
||||||
(defcustom exwm-background-color nil
|
|
||||||
"Background color for Xorg."
|
|
||||||
:type '(choice
|
|
||||||
(color :tag "Background Color")
|
|
||||||
(const :tag "Default" nil))
|
|
||||||
:group 'exwm
|
|
||||||
:initialize #'custom-initialize-default
|
|
||||||
:set (lambda (symbol value)
|
|
||||||
(set-default-toplevel-value symbol value)
|
|
||||||
(exwm-background--update)))
|
|
||||||
|
|
||||||
(defconst exwm-background--properties '("_XROOTPMAP_ID" "_XSETROOT_ID" "ESETROOT_PMAP_ID")
|
|
||||||
"The background properties to set.
|
|
||||||
We can't need to set these so that compositing window managers can correctly display the background
|
|
||||||
color.")
|
|
||||||
|
|
||||||
(defvar exwm-background--connection nil
|
|
||||||
"The X connection used for setting the background.
|
|
||||||
We use a separate connection as other background-setting tools may kill this connection when they
|
|
||||||
replace it.")
|
|
||||||
|
|
||||||
(defvar exwm-background--pixmap nil
|
|
||||||
"Cached background pixmap.")
|
|
||||||
|
|
||||||
(defvar exwm-background--atoms nil
|
|
||||||
"Cached background atoms.")
|
|
||||||
|
|
||||||
(defun exwm-background--update (&rest _)
|
|
||||||
"Update the EXWM background."
|
|
||||||
|
|
||||||
;; Always reconnect as any tool that sets the background may have disconnected us (to force X to
|
|
||||||
;; free resources).
|
|
||||||
(exwm-background--connect)
|
|
||||||
|
|
||||||
(let ((gc (xcb:generate-id exwm-background--connection))
|
|
||||||
(color (exwm--color->pixel (or exwm-background-color
|
|
||||||
(face-background 'default)))))
|
|
||||||
;; Fill the pixmap.
|
|
||||||
(xcb:+request exwm-background--connection
|
|
||||||
(make-instance 'xcb:CreateGC
|
|
||||||
:cid gc :drawable exwm-background--pixmap
|
|
||||||
:value-mask (logior xcb:GC:Foreground
|
|
||||||
xcb:GC:GraphicsExposures)
|
|
||||||
:foreground color
|
|
||||||
:graphics-exposures 0))
|
|
||||||
|
|
||||||
(xcb:+request exwm-background--connection
|
|
||||||
(make-instance 'xcb:PolyFillRectangle
|
|
||||||
:gc gc :drawable exwm-background--pixmap
|
|
||||||
:rectangles
|
|
||||||
(list
|
|
||||||
(make-instance
|
|
||||||
'xcb:RECTANGLE
|
|
||||||
:x 0 :y 0 :width 1 :height 1))))
|
|
||||||
(xcb:+request exwm-background--connection (make-instance 'xcb:FreeGC :gc gc)))
|
|
||||||
|
|
||||||
;; Reapply it to force an update (also clobber anyone else who may have set it).
|
|
||||||
(xcb:+request exwm-background--connection
|
|
||||||
(make-instance 'xcb:ChangeWindowAttributes
|
|
||||||
:window exwm--root
|
|
||||||
:value-mask xcb:CW:BackPixmap
|
|
||||||
:background-pixmap exwm-background--pixmap))
|
|
||||||
|
|
||||||
(let (old)
|
|
||||||
;; Collect old pixmaps so we can kill other background clients (all the background setting tools
|
|
||||||
;; seem to do this).
|
|
||||||
(dolist (atom exwm-background--atoms)
|
|
||||||
(when-let* ((reply (xcb:+request-unchecked+reply exwm-background--connection
|
|
||||||
(make-instance 'xcb:GetProperty
|
|
||||||
:delete 0
|
|
||||||
:window exwm--root
|
|
||||||
:property atom
|
|
||||||
:type xcb:Atom:PIXMAP
|
|
||||||
:long-offset 0
|
|
||||||
:long-length 1)))
|
|
||||||
(value (vconcat (slot-value reply 'value)))
|
|
||||||
((length= value 4))
|
|
||||||
(pixmap (funcall (if xcb:lsb #'xcb:-unpack-u4-lsb #'xcb:-unpack-u4)
|
|
||||||
value 0))
|
|
||||||
((not (or (= pixmap exwm-background--pixmap)
|
|
||||||
(member pixmap old)))))
|
|
||||||
(push pixmap old)))
|
|
||||||
|
|
||||||
;; Change the background.
|
|
||||||
(dolist (atom exwm-background--atoms)
|
|
||||||
(xcb:+request exwm-background--connection
|
|
||||||
(make-instance 'xcb:ChangeProperty
|
|
||||||
:window exwm--root
|
|
||||||
:property atom
|
|
||||||
:type xcb:Atom:PIXMAP
|
|
||||||
:format 32
|
|
||||||
:mode xcb:PropMode:Replace
|
|
||||||
:data-len 1
|
|
||||||
:data
|
|
||||||
(funcall (if xcb:lsb
|
|
||||||
#'xcb:-pack-u4-lsb
|
|
||||||
#'xcb:-pack-u4)
|
|
||||||
exwm-background--pixmap))))
|
|
||||||
|
|
||||||
;; Kill the old background clients.
|
|
||||||
(dolist (pixmap old)
|
|
||||||
(xcb:+request exwm-background--connection
|
|
||||||
(make-instance 'xcb:KillClient :resource pixmap))))
|
|
||||||
|
|
||||||
(xcb:flush exwm-background--connection))
|
|
||||||
|
|
||||||
(defun exwm-background--connected-p ()
|
|
||||||
(and exwm-background--connection
|
|
||||||
(process-live-p (slot-value exwm-background--connection 'process))))
|
|
||||||
|
|
||||||
(defun exwm-background--connect ()
|
|
||||||
(unless (exwm-background--connected-p)
|
|
||||||
(setq exwm-background--connection (xcb:connect))
|
|
||||||
;;prevent query message on exit
|
|
||||||
(set-process-query-on-exit-flag (slot-value exwm-background--connection 'process) nil)
|
|
||||||
|
|
||||||
;; Intern the background property atoms.
|
|
||||||
(setq exwm-background--atoms
|
|
||||||
(mapcar
|
|
||||||
(lambda (prop) (exwm--intern-atom prop exwm-background--connection))
|
|
||||||
exwm-background--properties))
|
|
||||||
|
|
||||||
;; Create the pixmap.
|
|
||||||
(setq exwm-background--pixmap (xcb:generate-id exwm-background--connection))
|
|
||||||
(xcb:+request exwm-background--connection
|
|
||||||
(make-instance 'xcb:CreatePixmap
|
|
||||||
:depth
|
|
||||||
(slot-value
|
|
||||||
(xcb:+request-unchecked+reply exwm-background--connection
|
|
||||||
(make-instance 'xcb:GetGeometry :drawable exwm--root))
|
|
||||||
'depth)
|
|
||||||
:pid exwm-background--pixmap
|
|
||||||
:drawable exwm--root
|
|
||||||
:width 1 :height 1))))
|
|
||||||
|
|
||||||
(defun exwm-background--init ()
|
|
||||||
"Initialize background module."
|
|
||||||
(exwm--log)
|
|
||||||
(add-hook 'enable-theme-functions 'exwm-background--update)
|
|
||||||
(add-hook 'disable-theme-functions 'exwm-background--update)
|
|
||||||
(exwm-background--update))
|
|
||||||
|
|
||||||
(defun exwm-background--exit ()
|
|
||||||
"Uninitialize the background module."
|
|
||||||
(exwm--log)
|
|
||||||
(remove-hook 'enable-theme-functions 'exwm-background--update)
|
|
||||||
(remove-hook 'disable-theme-functions 'exwm-background--update)
|
|
||||||
(when (and exwm-background--connection
|
|
||||||
(slot-value exwm-background--connection 'connected))
|
|
||||||
(xcb:disconnect exwm-background--connection))
|
|
||||||
(setq exwm-background--pixmap nil
|
|
||||||
exwm-background--connection nil
|
|
||||||
exwm-background--atoms nil))
|
|
||||||
|
|
||||||
(defun exwm-background-enable ()
|
|
||||||
"Enable background support for EXWM."
|
|
||||||
(exwm--log)
|
|
||||||
(add-hook 'exwm-init-hook #'exwm-background--init)
|
|
||||||
(add-hook 'exwm-exit-hook #'exwm-background--exit))
|
|
||||||
|
|
||||||
(provide 'exwm-background)
|
|
||||||
|
|
||||||
;;; exwm-background.el ends here
|
|
131
exwm-config.el
131
exwm-config.el
|
@ -1,131 +0,0 @@
|
||||||
;;; exwm-config.el --- Predefined configurations -*- lexical-binding: t -*-
|
|
||||||
|
|
||||||
;; Copyright (C) 2015-2024 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
;; Author: Chris Feng <chris.w.feng@gmail.com>
|
|
||||||
|
|
||||||
;; 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
;;; Commentary:
|
|
||||||
|
|
||||||
;; This module contains typical (yet minimal) configurations of EXWM.
|
|
||||||
|
|
||||||
;;; Code:
|
|
||||||
|
|
||||||
(require 'exwm)
|
|
||||||
(require 'ido)
|
|
||||||
|
|
||||||
(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)
|
|
||||||
(setq exwm-workspace-number 4))
|
|
||||||
;; Make class name the buffer name
|
|
||||||
(add-hook 'exwm-update-class-hook
|
|
||||||
(lambda ()
|
|
||||||
(exwm-workspace-rename-buffer exwm-class-name)))
|
|
||||||
;; 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
|
|
||||||
'(([?\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
|
|
||||||
(exwm-config-ido)
|
|
||||||
;; Other configurations
|
|
||||||
(exwm-config-misc))
|
|
||||||
|
|
||||||
(defun exwm-config--fix/ido-buffer-window-other-frame ()
|
|
||||||
"Fix `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 (derived-mode-p 'exwm-mode)
|
|
||||||
exwm--floating-frame)
|
|
||||||
;; Switch from a floating frame.
|
|
||||||
(with-current-buffer buffer
|
|
||||||
(if (and (derived-mode-p '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 (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.
|
|
||||||
(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."
|
|
||||||
(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))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(provide 'exwm-config)
|
|
||||||
|
|
||||||
;;; exwm-config.el ends here
|
|
408
exwm-core.el
408
exwm-core.el
|
@ -1,408 +0,0 @@
|
||||||
;;; exwm-core.el --- Core definitions -*- lexical-binding: t -*-
|
|
||||||
|
|
||||||
;; Copyright (C) 2015-2024 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
;; Author: Chris Feng <chris.w.feng@gmail.com>
|
|
||||||
|
|
||||||
;; 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
;;; Commentary:
|
|
||||||
|
|
||||||
;; This module includes core definitions of variables, macros, functions, etc
|
|
||||||
;; shared by various other modules.
|
|
||||||
|
|
||||||
;;; Code:
|
|
||||||
|
|
||||||
(require 'kmacro)
|
|
||||||
|
|
||||||
(require 'xcb)
|
|
||||||
(require 'xcb-icccm)
|
|
||||||
(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-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"))
|
|
||||||
: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.")
|
|
||||||
|
|
||||||
(defvar exwm--terminal nil
|
|
||||||
"Terminal corresponding to `exwm--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--guide-window nil
|
|
||||||
"An X window separating workspaces and X windows.")
|
|
||||||
|
|
||||||
(defvar exwm--id-buffer-alist nil "Alist of (<X window ID> . <Emacs buffer>).")
|
|
||||||
|
|
||||||
(defvar exwm--root nil "Root window.")
|
|
||||||
|
|
||||||
(defvar exwm-input--global-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))
|
|
||||||
(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))
|
|
||||||
|
|
||||||
(define-minor-mode exwm-debug
|
|
||||||
"Debug-logging enabled if non-nil."
|
|
||||||
:global t
|
|
||||||
:group 'exwm-debug)
|
|
||||||
|
|
||||||
(defmacro exwm--debug (&rest forms)
|
|
||||||
"Evaluate FORMS if mode `exwm-debug' is active."
|
|
||||||
(when exwm-debug `(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."
|
|
||||||
(unless format-string (setq format-string ""))
|
|
||||||
`(when exwm-debug
|
|
||||||
(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))
|
|
||||||
|
|
||||||
(defsubst exwm--id->buffer (id)
|
|
||||||
"X window ID => Emacs buffer."
|
|
||||||
(declare (indent defun))
|
|
||||||
(cdr (assoc id exwm--id-buffer-alist)))
|
|
||||||
|
|
||||||
(defsubst exwm--buffer->id (buffer)
|
|
||||||
"Emacs buffer BUFFER => X window ID."
|
|
||||||
(declare (indent defun))
|
|
||||||
(car (rassoc buffer exwm--id-buffer-alist)))
|
|
||||||
|
|
||||||
(defun exwm--lock (&rest _args)
|
|
||||||
"Lock (disable all events)."
|
|
||||||
(exwm--log)
|
|
||||||
(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)."
|
|
||||||
(exwm--log)
|
|
||||||
(xcb:+request exwm--connection
|
|
||||||
(make-instance 'xcb:ChangeWindowAttributes
|
|
||||||
:window exwm--root
|
|
||||||
:value-mask xcb:CW:EventMask
|
|
||||||
:event-mask (eval-when-compile
|
|
||||||
(logior xcb:EventMask:SubstructureRedirect
|
|
||||||
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)))
|
|
||||||
|
|
||||||
(defun exwm--intern-atom (atom &optional conn)
|
|
||||||
"Intern X11 ATOM.
|
|
||||||
If CONN is non-nil, use it instead of the value of the variable
|
|
||||||
`exwm--connection'."
|
|
||||||
(slot-value (xcb:+request-unchecked+reply (or conn 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.
|
|
||||||
|
|
||||||
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)
|
|
||||||
nil
|
|
||||||
,function
|
|
||||||
,@args))
|
|
||||||
|
|
||||||
(defsubst exwm--terminal-p (&optional frame)
|
|
||||||
"Return t when FRAME's terminal is EXWM's terminal.
|
|
||||||
If FRAME is null, use selected frame."
|
|
||||||
(declare (indent defun))
|
|
||||||
(eq exwm--terminal (frame-terminal frame)))
|
|
||||||
|
|
||||||
(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)))
|
|
||||||
|
|
||||||
(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 (ash (ash (pop rgb) -8) 16)
|
|
||||||
(ash (ash (pop rgb) -8) 8)
|
|
||||||
(ash (pop rgb) -8)))))
|
|
||||||
|
|
||||||
(defun exwm--get-visual-depth-colormap (conn id)
|
|
||||||
"Get visual, depth and colormap from X window ID.
|
|
||||||
Return a three element list with the respective results.
|
|
||||||
|
|
||||||
If CONN is non-nil, use it instead of the value of the variable
|
|
||||||
`exwm--connection'."
|
|
||||||
(let (ret-visual ret-depth ret-colormap)
|
|
||||||
(with-slots (visual colormap)
|
|
||||||
(xcb:+request-unchecked+reply conn
|
|
||||||
(make-instance 'xcb:GetWindowAttributes :window id))
|
|
||||||
(setq ret-visual visual)
|
|
||||||
(setq ret-colormap colormap))
|
|
||||||
(with-slots (depth)
|
|
||||||
(xcb:+request-unchecked+reply conn
|
|
||||||
(make-instance 'xcb:GetGeometry :drawable id))
|
|
||||||
(setq ret-depth depth))
|
|
||||||
(list ret-visual ret-depth ret-colormap)))
|
|
||||||
|
|
||||||
;; 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
|
|
||||||
(defvar-local exwm--floating-frame-position nil) ;set when hidden.
|
|
||||||
(defvar-local exwm--fixed-size nil) ;fixed size
|
|
||||||
(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.")
|
|
||||||
(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 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)
|
|
||||||
(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)
|
|
||||||
(defvar-local exwm--hints-urgency nil)
|
|
||||||
;; _MOTIF_WM_HINTS
|
|
||||||
(defvar-local exwm--mwm-hints-decorations t)
|
|
||||||
|
|
||||||
(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)
|
|
||||||
(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)
|
|
||||||
(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'.")
|
|
||||||
|
|
||||||
(defvar exwm--kmacro-map
|
|
||||||
(let ((map (make-sparse-keymap)))
|
|
||||||
(define-key map [t]
|
|
||||||
(lambda ()
|
|
||||||
(interactive)
|
|
||||||
(cond
|
|
||||||
((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)
|
|
||||||
(memq last-input-event exwm-input-prefix-keys)
|
|
||||||
(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)))
|
|
||||||
(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.
|
|
||||||
(easy-menu-define exwm-mode-menu exwm-mode-map
|
|
||||||
"Menu for `exwm-mode'."
|
|
||||||
'("EXWM"
|
|
||||||
"---"
|
|
||||||
"*General*"
|
|
||||||
"---"
|
|
||||||
["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*"
|
|
||||||
"---"
|
|
||||||
["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]
|
|
||||||
["Shrink window horizontally" exwm-layout-shrink-window-horizontally]
|
|
||||||
|
|
||||||
"---"
|
|
||||||
"*Keyboard*"
|
|
||||||
"---"
|
|
||||||
["Toggle keyboard mode" exwm-input-toggle-keyboard]
|
|
||||||
["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)
|
|
||||||
(let (result)
|
|
||||||
(maphash
|
|
||||||
(lambda (key value)
|
|
||||||
(when (sequencep key)
|
|
||||||
(setq result (append result
|
|
||||||
`([
|
|
||||||
,(format "Send '%s'"
|
|
||||||
(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]
|
|
||||||
|
|
||||||
"---"
|
|
||||||
"*Workspace*"
|
|
||||||
"---"
|
|
||||||
["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]
|
|
||||||
["Move X window from" exwm-workspace-switch-to-buffer]
|
|
||||||
["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
|
|
||||||
(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--count))))))))
|
|
||||||
|
|
||||||
(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
|
|
||||||
#'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
|
|
||||||
cursor-type nil
|
|
||||||
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
|
|
781
exwm-floating.el
781
exwm-floating.el
|
@ -1,781 +0,0 @@
|
||||||
;;; exwm-floating.el --- Floating Module for EXWM -*- lexical-binding: t -*-
|
|
||||||
|
|
||||||
;; Copyright (C) 2015-2024 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
;; Author: Chris Feng <chris.w.feng@gmail.com>
|
|
||||||
|
|
||||||
;; 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
;;; Commentary:
|
|
||||||
|
|
||||||
;; This module deals with the conversion between floating and non-floating
|
|
||||||
;; states and implements moving/resizing operations on floating windows.
|
|
||||||
|
|
||||||
;;; Code:
|
|
||||||
|
|
||||||
(require 'xcb-cursor)
|
|
||||||
(require 'exwm-core)
|
|
||||||
|
|
||||||
(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
|
|
||||||
:initialize #'custom-initialize-default
|
|
||||||
:set (lambda (symbol value)
|
|
||||||
(set-default symbol value)
|
|
||||||
;; Change border color for all floating X windows.
|
|
||||||
(when 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."
|
|
||||||
: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)))))
|
|
||||||
|
|
||||||
;; 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)
|
|
||||||
|
|
||||||
(defvar exwm-floating--moveresize-calculate nil
|
|
||||||
"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)
|
|
||||||
(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" ())
|
|
||||||
(declare-function exwm-workspace--workarea "exwm-workspace.el" (frame))
|
|
||||||
|
|
||||||
(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
|
|
||||||
: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)))))
|
|
||||||
|
|
||||||
(defun exwm-floating--set-floating (id)
|
|
||||||
"Make window ID floating."
|
|
||||||
(let ((window (get-buffer-window (exwm--id->buffer id))))
|
|
||||||
(when window
|
|
||||||
;; Hide the non-floating X window first.
|
|
||||||
(set-window-buffer window (other-buffer nil t))))
|
|
||||||
(let* ((original-frame (buffer-local-value 'exwm--frame
|
|
||||||
(exwm--id->buffer id)))
|
|
||||||
;; Create new frame
|
|
||||||
(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 exwm--frame))
|
|
||||||
(tab-bar-lines . 0)
|
|
||||||
(tab-bar-lines-keep-state . t)
|
|
||||||
(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
|
|
||||||
(outer-id (string-to-number (frame-parameter frame 'outer-window-id)))
|
|
||||||
(window-id (string-to-number (frame-parameter frame 'window-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))
|
|
||||||
(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)
|
|
||||||
(set-frame-parameter frame 'exwm-id window-id)
|
|
||||||
(set-frame-parameter frame 'exwm-container frame-container)
|
|
||||||
;; Fix illegal parameters
|
|
||||||
;; FIXME: check normal hints restrictions
|
|
||||||
(with-slots ((x* x) (y* y) (width* width) (height* height))
|
|
||||||
(exwm-workspace--workarea original-frame)
|
|
||||||
;; Center floating windows
|
|
||||||
(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)))
|
|
||||||
(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 (+ 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 (/ (- 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)))))
|
|
||||||
;; 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)
|
|
||||||
;; Fit frame to client
|
|
||||||
;; 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))
|
|
||||||
(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.
|
|
||||||
exwm-workspace--frame-y-offset))
|
|
||||||
(floating-mode-line (plist-get exwm--configurations
|
|
||||||
'floating-mode-line))
|
|
||||||
(floating-header-line (plist-get exwm--configurations
|
|
||||||
'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)
|
|
||||||
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)))
|
|
||||||
(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 mode.
|
|
||||||
(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
|
|
||||||
(make-instance 'xcb:CreateWindow
|
|
||||||
:depth 0
|
|
||||||
:wid frame-container
|
|
||||||
:parent exwm--root
|
|
||||||
:x x
|
|
||||||
:y (- y exwm-workspace--window-y-offset)
|
|
||||||
:width width
|
|
||||||
:height height
|
|
||||||
: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
|
|
||||||
(if border-pixel
|
|
||||||
xcb:CW:BorderPixel 0)
|
|
||||||
xcb:CW:OverrideRedirect)
|
|
||||||
:background-pixmap xcb:BackPixmap:ParentRelative
|
|
||||||
:border-pixel border-pixel
|
|
||||||
:override-redirect 1))
|
|
||||||
(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))
|
|
||||||
;; 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))
|
|
||||||
(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--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
|
|
||||||
(add-hook 'window-configuration-change-hook #'exwm-layout--refresh)
|
|
||||||
(set-window-dedicated-p window t)
|
|
||||||
(exwm-layout--show id window))
|
|
||||||
(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.
|
|
||||||
(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))
|
|
||||||
;; Redraw the frame.
|
|
||||||
(redisplay t))
|
|
||||||
|
|
||||||
(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
|
|
||||||
;; The X window is already mapped.
|
|
||||||
;; 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--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
|
|
||||||
'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))
|
|
||||||
;; Also destroy its container.
|
|
||||||
(xcb:+request exwm--connection
|
|
||||||
(make-instance 'xcb:DestroyWindow :window 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 id
|
|
||||||
:value-mask (logior xcb:ConfigWindow:BorderWidth
|
|
||||||
xcb:ConfigWindow:Sibling
|
|
||||||
xcb:ConfigWindow:StackMode)
|
|
||||||
:border-width 0
|
|
||||||
: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)
|
|
||||||
;; 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)
|
|
||||||
(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)))
|
|
||||||
(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)))
|
|
||||||
(with-current-buffer (exwm--id->buffer id)
|
|
||||||
(run-hooks 'exwm-floating-exit-hook)))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(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)
|
|
||||||
(if exwm--floating-frame
|
|
||||||
(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)
|
|
||||||
(exwm--log)
|
|
||||||
(when (and (derived-mode-p 'exwm-mode)
|
|
||||||
exwm--floating-frame)
|
|
||||||
(exwm-layout--hide exwm--id)
|
|
||||||
(select-frame-set-input-focus exwm-workspace--current)))
|
|
||||||
|
|
||||||
(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)
|
|
||||||
;; Managed.
|
|
||||||
(with-current-buffer buffer-or-id
|
|
||||||
(setq frame exwm--floating-frame
|
|
||||||
container-or-id (frame-parameter exwm--floating-frame
|
|
||||||
'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-or-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)))
|
|
||||||
(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 no 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-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)))))
|
|
||||||
((= 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)))))
|
|
||||||
((= 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)))))
|
|
||||||
((= 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))))
|
|
||||||
((= 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))))))
|
|
||||||
((= 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))))))
|
|
||||||
((= 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))))))
|
|
||||||
((= 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)))))
|
|
||||||
;; Select events and change cursor (should always succeed)
|
|
||||||
(xcb:+request-unchecked+reply exwm--connection
|
|
||||||
(make-instance 'xcb:GrabPointer
|
|
||||||
:owner-events 0 :grab-window container-or-id
|
|
||||||
: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
|
|
||||||
:cursor cursor
|
|
||||||
:time xcb:Time:CurrentTime)))))))
|
|
||||||
|
|
||||||
(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
|
|
||||||
(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)))
|
|
||||||
|
|
||||||
(defun exwm-floating--do-moveresize (data _synthetic)
|
|
||||||
"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)
|
|
||||||
(xcb:unmarshal obj data)
|
|
||||||
(setq result (funcall exwm-floating--moveresize-calculate
|
|
||||||
(slot-value obj 'root-x) (slot-value obj 'root-y))
|
|
||||||
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)))
|
|
||||||
(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))
|
|
||||||
x (- x exwm-floating-border-width)
|
|
||||||
;; Use `frame-outer-height' in the future.
|
|
||||||
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
|
|
||||||
:value-mask (aref result 1)
|
|
||||||
:x x
|
|
||||||
:y y
|
|
||||||
:width width
|
|
||||||
:height height))
|
|
||||||
(when (bufferp buffer-or-id)
|
|
||||||
;; Managed.
|
|
||||||
(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)
|
|
||||||
"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))
|
|
||||||
(unless delta-y (setq delta-y 1))
|
|
||||||
(unless (and (= 0 delta-x) (= 0 delta-y))
|
|
||||||
(let* ((floating-container (frame-parameter exwm--floating-frame
|
|
||||||
'exwm-container))
|
|
||||||
(geometry (xcb:+request-unchecked+reply exwm--connection
|
|
||||||
(make-instance 'xcb:GetGeometry
|
|
||||||
:drawable floating-container)))
|
|
||||||
(edges (window-inside-absolute-pixel-edges)))
|
|
||||||
(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 ()
|
|
||||||
"Initialize floating module."
|
|
||||||
(exwm--log)
|
|
||||||
;; 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")))
|
|
||||||
|
|
||||||
(defun exwm-floating--exit ()
|
|
||||||
"Exit the floating module."
|
|
||||||
(exwm--log))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(provide 'exwm-floating)
|
|
||||||
|
|
||||||
;;; exwm-floating.el ends here
|
|
1271
exwm-input.el
1271
exwm-input.el
File diff suppressed because it is too large
Load diff
631
exwm-layout.el
631
exwm-layout.el
|
@ -1,631 +0,0 @@
|
||||||
;;; exwm-layout.el --- Layout Module for EXWM -*- lexical-binding: t -*-
|
|
||||||
|
|
||||||
;; Copyright (C) 2015-2024 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
;; Author: Chris Feng <chris.w.feng@gmail.com>
|
|
||||||
|
|
||||||
;; 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
;;; Commentary:
|
|
||||||
|
|
||||||
;; This module is responsible for keeping X client window properly displayed.
|
|
||||||
|
|
||||||
;;; Code:
|
|
||||||
|
|
||||||
(require 'exwm-core)
|
|
||||||
|
|
||||||
(defgroup exwm-layout nil
|
|
||||||
"Layout."
|
|
||||||
: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)
|
|
||||||
|
|
||||||
(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'.")
|
|
||||||
|
|
||||||
(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)
|
|
||||||
(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")
|
|
||||||
(declare-function exwm-workspace--active-p "exwm-workspace.el" (frame))
|
|
||||||
(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--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 of X window ID to 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))
|
|
||||||
(with-current-buffer (exwm--id->buffer id)
|
|
||||||
(setq exwm-state state)))
|
|
||||||
|
|
||||||
(defun exwm-layout--iconic-state-p (&optional id)
|
|
||||||
"Check whether X window ID is in iconic state."
|
|
||||||
(= xcb:icccm:WM_STATE:IconicState
|
|
||||||
(if id
|
|
||||||
(buffer-local-value 'exwm-state (exwm--id->buffer id))
|
|
||||||
exwm-state)))
|
|
||||||
|
|
||||||
(defun exwm-layout--set-ewmh-state (id)
|
|
||||||
"Set _NET_WM_STATE of X window ID to the value of variable `exwm--ewmh-state'."
|
|
||||||
(with-current-buffer (exwm--id->buffer id)
|
|
||||||
(xcb:+request exwm--connection
|
|
||||||
(make-instance 'xcb:ewmh:set-_NET_WM_STATE
|
|
||||||
:window exwm--id
|
|
||||||
:data exwm--ewmh-state))))
|
|
||||||
|
|
||||||
(defun exwm-layout--fullscreen-p ()
|
|
||||||
"Check whether current `exwm-mode' buffer is in fullscreen state."
|
|
||||||
(when (derived-mode-p 'exwm-mode)
|
|
||||||
(memq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state)))
|
|
||||||
|
|
||||||
(defun exwm-layout--auto-iconify ()
|
|
||||||
"Helper function to iconify unused X windows.
|
|
||||||
See variable `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)
|
|
||||||
(let* ((edges (window-inside-absolute-pixel-edges window))
|
|
||||||
(x (pop edges))
|
|
||||||
(y (pop edges))
|
|
||||||
(width (- (pop edges) x))
|
|
||||||
(height (- (pop edges) y))
|
|
||||||
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)
|
|
||||||
;; Use `frame-outer-height' in the future.
|
|
||||||
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)
|
|
||||||
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)
|
|
||||||
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)
|
|
||||||
(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))
|
|
||||||
|
|
||||||
(defun exwm-layout--hide (id)
|
|
||||||
"Hide window ID."
|
|
||||||
(with-current-buffer (exwm--id->buffer id)
|
|
||||||
(unless (or (exwm-layout--iconic-state-p)
|
|
||||||
(and exwm--floating-frame
|
|
||||||
(eq 4294967295. exwm--desktop)))
|
|
||||||
(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 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
|
|
||||||
: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--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))))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(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))
|
|
||||||
(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)
|
|
||||||
(exwm--set-geometry exwm--id x y width height))
|
|
||||||
;; Raise the X window.
|
|
||||||
(xcb:+request exwm--connection
|
|
||||||
(make-instance 'xcb:ConfigureWindow
|
|
||||||
:window exwm--id
|
|
||||||
:value-mask (logior xcb:ConfigWindow:BorderWidth
|
|
||||||
xcb:ConfigWindow:StackMode)
|
|
||||||
:border-width 0
|
|
||||||
:stack-mode xcb:StackMode:Above))
|
|
||||||
(cl-pushnew 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) t)
|
|
||||||
(exwm-input--release-keyboard exwm--id)))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(cl-defun exwm-layout-unset-fullscreen (&optional id)
|
|
||||||
"Restore X window ID 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))
|
|
||||||
(with-current-buffer (if id (exwm--id->buffer id) (window-buffer))
|
|
||||||
;; `exwm-layout--show' relies on `exwm--ewmh-state' to decide whether to
|
|
||||||
;; fullscreen the window.
|
|
||||||
(setq exwm--ewmh-state
|
|
||||||
(delq xcb:Atom:_NET_WM_STATE_FULLSCREEN exwm--ewmh-state))
|
|
||||||
(exwm-layout--set-ewmh-state exwm--id)
|
|
||||||
(if exwm--floating-frame
|
|
||||||
(exwm-layout--show exwm--id (frame-root-window exwm--floating-frame))
|
|
||||||
(xcb:+request exwm--connection
|
|
||||||
(make-instance 'xcb:ConfigureWindow
|
|
||||||
:window exwm--id
|
|
||||||
:value-mask (logior xcb:ConfigWindow:Sibling
|
|
||||||
xcb:ConfigWindow:StackMode)
|
|
||||||
: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:flush exwm--connection)
|
|
||||||
(set-window-dedicated-p (get-buffer-window) nil)
|
|
||||||
(when (eq 'line-mode exwm--selected-input-mode)
|
|
||||||
(exwm-input--grab-keyboard exwm--id))))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(cl-defun exwm-layout-toggle-fullscreen (&optional id)
|
|
||||||
"Toggle fullscreen mode of X window ID."
|
|
||||||
(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
|
|
||||||
(with-current-buffer (exwm--id->buffer id)
|
|
||||||
(if (exwm-layout--fullscreen-p)
|
|
||||||
(exwm-layout-unset-fullscreen id)
|
|
||||||
(exwm-layout-set-fullscreen id)))))
|
|
||||||
|
|
||||||
(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'.
|
|
||||||
|
|
||||||
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 (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.
|
|
||||||
(not (get-buffer-window buffer t)))))
|
|
||||||
|
|
||||||
(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))
|
|
||||||
(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 (&optional frame)
|
|
||||||
"Refresh layout of FRAME.
|
|
||||||
If FRAME is nil, refresh layout of selected frame."
|
|
||||||
;; `window-size-change-functions' sets this argument while
|
|
||||||
;; `window-configuration-change-hook' makes the frame selected.
|
|
||||||
(unless frame
|
|
||||||
(setq frame (selected-frame)))
|
|
||||||
(exwm--log "frame=%s" 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)
|
|
||||||
(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."
|
|
||||||
(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.
|
|
||||||
(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 (;; 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)
|
|
||||||
;; 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
|
|
||||||
;; `exwm-mode' buffer.
|
|
||||||
(setq vacated-windows
|
|
||||||
(append vacated-windows (remove
|
|
||||||
window
|
|
||||||
(get-buffer-window-list
|
|
||||||
(current-buffer) 'nomini t))))
|
|
||||||
;; 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
|
|
||||||
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)
|
|
||||||
(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))))
|
|
||||||
(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)))
|
|
||||||
|
|
||||||
(defun exwm-layout--on-minibuffer-setup ()
|
|
||||||
"Refresh layout when minibuffer grows."
|
|
||||||
(exwm--log)
|
|
||||||
;; Only when active minibuffer's frame is an EXWM frame.
|
|
||||||
(let* ((mini-window (active-minibuffer-window))
|
|
||||||
(frame (window-frame mini-window)))
|
|
||||||
(when (exwm-workspace--workspace-p frame)
|
|
||||||
(exwm--defer 0 (lambda ()
|
|
||||||
(when (< 1 (window-height mini-window))
|
|
||||||
(exwm-layout--refresh frame)))))))
|
|
||||||
|
|
||||||
(defun exwm-layout--on-echo-area-change (&optional dirty)
|
|
||||||
"Run when message arrives or in `echo-area-clear-hook' to refresh layout.
|
|
||||||
If DIRTY is non-nil, refresh layout immediately."
|
|
||||||
(let ((frame (window-frame (active-minibuffer-window)))
|
|
||||||
(msg (current-message)))
|
|
||||||
;; Check whether the frame where current window's minibuffer resides (not
|
|
||||||
;; current window's frame for floating windows!) must be adjusted.
|
|
||||||
(when (and msg
|
|
||||||
(exwm-workspace--workspace-p frame)
|
|
||||||
(or (cl-position ?\n msg)
|
|
||||||
(> (length msg) (frame-width frame))))
|
|
||||||
(exwm--log)
|
|
||||||
(if dirty
|
|
||||||
(exwm-layout--refresh exwm-workspace--current)
|
|
||||||
(exwm--defer 0 #'exwm-layout--refresh exwm-workspace--current)))))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(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")
|
|
||||||
(exwm--log)
|
|
||||||
(cond
|
|
||||||
((zerop delta)) ;no operation
|
|
||||||
((window-minibuffer-p)) ;avoid resize minibuffer-window
|
|
||||||
((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))
|
|
||||||
(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 (and width (> width 0))
|
|
||||||
(setf (slot-value exwm--geometry 'width) width)
|
|
||||||
(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:+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) exwm-workspace--frame-y-offset))
|
|
||||||
(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 (and height (> height 0))
|
|
||||||
(setf (slot-value exwm--geometry 'height) height)
|
|
||||||
(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:+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
|
|
||||||
(defun exwm-layout-enlarge-window-horizontally (delta)
|
|
||||||
"Make the selected window DELTA pixels wider.
|
|
||||||
|
|
||||||
See also `exwm-layout-enlarge-window'."
|
|
||||||
(interactive "p")
|
|
||||||
(exwm--log "%s" delta)
|
|
||||||
(exwm-layout-enlarge-window delta t))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun exwm-layout-shrink-window (delta)
|
|
||||||
"Make the selected window DELTA pixels lower.
|
|
||||||
|
|
||||||
See also `exwm-layout-enlarge-window'."
|
|
||||||
(interactive "p")
|
|
||||||
(exwm--log "%s" delta)
|
|
||||||
(exwm-layout-enlarge-window (- delta)))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun exwm-layout-shrink-window-horizontally (delta)
|
|
||||||
"Make the selected window DELTA pixels narrower.
|
|
||||||
|
|
||||||
See also `exwm-layout-enlarge-window'."
|
|
||||||
(interactive "p")
|
|
||||||
(exwm--log "%s" delta)
|
|
||||||
(exwm-layout-enlarge-window (- delta) t))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(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
|
|
||||||
(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)))))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(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)
|
|
||||||
(if (not exwm--floating-frame)
|
|
||||||
(exwm-layout--show exwm--id)
|
|
||||||
(set-frame-height exwm--floating-frame
|
|
||||||
(+ (frame-pixel-height exwm--floating-frame)
|
|
||||||
(window-mode-line-height (frame-root-window
|
|
||||||
exwm--floating-frame)))
|
|
||||||
nil t)
|
|
||||||
(call-interactively #'exwm-input-grab-keyboard))
|
|
||||||
(force-mode-line-update)))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(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)
|
|
||||||
(exwm-layout-show-mode-line))))
|
|
||||||
|
|
||||||
(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)
|
|
||||||
(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)
|
|
||||||
(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."
|
|
||||||
(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))
|
|
||||||
(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)
|
|
||||||
|
|
||||||
;;; exwm-layout.el ends here
|
|
820
exwm-manage.el
820
exwm-manage.el
|
@ -1,820 +0,0 @@
|
||||||
;;; exwm-manage.el --- Window Management Module for -*- lexical-binding: t -*-
|
|
||||||
;;; EXWM
|
|
||||||
|
|
||||||
;; Copyright (C) 2015-2024 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
;; Author: Chris Feng <chris.w.feng@gmail.com>
|
|
||||||
|
|
||||||
;; 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
;;; Commentary:
|
|
||||||
|
|
||||||
;; This is the fundamental module of EXWM that deals with window management.
|
|
||||||
|
|
||||||
;;; Code:
|
|
||||||
|
|
||||||
(require 'exwm-core)
|
|
||||||
|
|
||||||
(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.
|
|
||||||
This hook runs in the context of the corresponding `exwm-mode' 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)
|
|
||||||
|
|
||||||
(defcustom exwm-manage-ping-timeout 3
|
|
||||||
"Seconds to wait before killing a client."
|
|
||||||
:type 'integer)
|
|
||||||
|
|
||||||
(defcustom exwm-manage-configurations nil
|
|
||||||
"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. 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"
|
|
||||||
: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) 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)
|
|
||||||
(defconst exwm-manage--width-delta-min 5)
|
|
||||||
|
|
||||||
;; The _MOTIF_WM_HINTS atom (see <Xm/MwmUtil.h> 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-input--skip-buffer-list-update)
|
|
||||||
(defvar exwm-input-prefix-keys)
|
|
||||||
(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-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))
|
|
||||||
(declare-function exwm-input-grab-keyboard "exwm-input.el" (&optional id))
|
|
||||||
(declare-function exwm-input-release-keyboard "exwm-input.el" (&optional id))
|
|
||||||
(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-layout-set-fullscreen "exwm-layout.el" (&optional id))
|
|
||||||
(declare-function exwm-workspace--get-geometry "exwm-workspace.el" (frame))
|
|
||||||
(declare-function exwm-workspace--position "exwm-workspace.el" (frame))
|
|
||||||
(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--workarea "exwm-workspace.el" (frame))
|
|
||||||
|
|
||||||
(defun exwm-manage--update-geometry (id &optional force)
|
|
||||||
"Update geometry of X window ID.
|
|
||||||
Override current geometry if FORCE is non-nil."
|
|
||||||
(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
|
|
||||||
(make-instance 'xcb:GetGeometry :drawable id))))
|
|
||||||
(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 of X window ID."
|
|
||||||
(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
|
|
||||||
(make-instance 'xcb:ewmh:get-_NET_WM_STATE
|
|
||||||
:window id))))
|
|
||||||
(when reply
|
|
||||||
(setq exwm--ewmh-state (append (slot-value reply 'value) nil)))))))
|
|
||||||
|
|
||||||
(defun exwm-manage--update-mwm-hints (id &optional force)
|
|
||||||
"Update _MOTIF_WM_HINTS of X window ID.
|
|
||||||
Override current hinds if FORCE is non-nil."
|
|
||||||
(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
|
|
||||||
(make-instance 'xcb:icccm:-GetProperty
|
|
||||||
:window id
|
|
||||||
:property exwm-manage--_MOTIF_WM_HINTS
|
|
||||||
:type exwm-manage--_MOTIF_WM_HINTS
|
|
||||||
:long-length 5))))
|
|
||||||
(when reply
|
|
||||||
;; Check MotifWmHints.decorations.
|
|
||||||
(with-slots (value) reply
|
|
||||||
(setq value (append value nil))
|
|
||||||
(when (and value
|
|
||||||
;; See <Xm/MwmUtil.h> 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))))))))
|
|
||||||
|
|
||||||
(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
|
|
||||||
:data (vconcat (mapcar #'car exwm--id-buffer-alist)))))
|
|
||||||
|
|
||||||
(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
|
|
||||||
(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)
|
|
||||||
"Manage window ID."
|
|
||||||
(exwm--log "Try to manage #x%x" id)
|
|
||||||
(catch 'return
|
|
||||||
;; 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--get-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 (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)))))
|
|
||||||
(exwm-mode)
|
|
||||||
(setq exwm--id id
|
|
||||||
exwm--frame exwm-workspace--current)
|
|
||||||
(exwm--update-window-type id)
|
|
||||||
(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)
|
|
||||||
(exwm--update-title id)
|
|
||||||
(exwm--update-protocols id)
|
|
||||||
(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)
|
|
||||||
(exwm--update-struts id))
|
|
||||||
;; Remove all events
|
|
||||||
(xcb:+request exwm--connection
|
|
||||||
(make-instance 'xcb:ChangeWindowAttributes
|
|
||||||
:window id :value-mask xcb:CW:EventMask
|
|
||||||
:event-mask
|
|
||||||
(if (memq xcb:Atom:_NET_WM_WINDOW_TYPE_DOCK
|
|
||||||
exwm-window-type)
|
|
||||||
;; Listen for PropertyChange (struts) and
|
|
||||||
;; UnmapNotify/DestroyNotify event of the dock.
|
|
||||||
(exwm--get-client-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
|
|
||||||
;; Center window of type _NET_WM_WINDOW_TYPE_SPLASH
|
|
||||||
(when (memq xcb:Atom:_NET_WM_WINDOW_TYPE_SPLASH exwm-window-type)
|
|
||||||
(with-slots ((x* x) (y* y) (width* width) (height* height))
|
|
||||||
(exwm-workspace--workarea exwm--frame)
|
|
||||||
(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.
|
|
||||||
(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)
|
|
||||||
(exwm-input--skip-buffer-list-update t))
|
|
||||||
(kill-buffer (current-buffer)))
|
|
||||||
(throw 'return 'ignored))
|
|
||||||
(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
|
|
||||||
(make-instance 'xcb:ConfigureWindow
|
|
||||||
:window id :value-mask xcb:ConfigWindow:BorderWidth
|
|
||||||
:border-width 0))
|
|
||||||
(dolist (button ;grab buttons to set focus / move / resize
|
|
||||||
(list xcb:ButtonIndex:1 xcb:ButtonIndex:2 xcb:ButtonIndex:3))
|
|
||||||
(xcb:+request 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)))
|
|
||||||
(exwm-manage--set-client-list)
|
|
||||||
(xcb:flush exwm--connection)
|
|
||||||
(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)
|
|
||||||
(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
|
|
||||||
(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)
|
|
||||||
(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))
|
|
||||||
(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 (or (plist-get exwm--configurations 'fullscreen)
|
|
||||||
(exwm-layout--fullscreen-p))
|
|
||||||
(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)))))
|
|
||||||
|
|
||||||
(defun exwm-manage--unmanage-window (id &optional withdraw-only)
|
|
||||||
"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)
|
|
||||||
(setq exwm--id-buffer-alist (assq-delete-all id exwm--id-buffer-alist))
|
|
||||||
;; Update workspaces when a dock is destroyed.
|
|
||||||
(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)
|
|
||||||
(exwm-workspace--update-workareas)
|
|
||||||
(dolist (f exwm-workspace--list)
|
|
||||||
(exwm-workspace--set-fullscreen f)))
|
|
||||||
(when (and (buffer-live-p buffer)
|
|
||||||
;; Invoked from `exwm-manage--exit' upon disconnection.
|
|
||||||
(slot-value exwm--connection 'connected))
|
|
||||||
(with-current-buffer buffer
|
|
||||||
;; Unmap the X window.
|
|
||||||
(xcb:+request exwm--connection
|
|
||||||
(make-instance 'xcb:UnmapWindow :window id))
|
|
||||||
;;
|
|
||||||
(setq exwm-workspace--switch-history-outdated t)
|
|
||||||
;;
|
|
||||||
(when withdraw-only
|
|
||||||
(xcb:+request exwm--connection
|
|
||||||
(make-instance 'xcb:ChangeWindowAttributes
|
|
||||||
:window id :value-mask xcb:CW:EventMask
|
|
||||||
:event-mask xcb:EventMask:NoEvent))
|
|
||||||
;; Delete WM_STATE property
|
|
||||||
(xcb:+request exwm--connection
|
|
||||||
(make-instance 'xcb:DeleteProperty
|
|
||||||
:window id :property xcb:Atom:WM_STATE))
|
|
||||||
(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)))))
|
|
||||||
(when exwm--floating-frame
|
|
||||||
;; 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))
|
|
||||||
(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
|
|
||||||
(lambda (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))))))
|
|
||||||
|
|
||||||
(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)))
|
|
||||||
reply)
|
|
||||||
(dolist (i (slot-value tree 'children))
|
|
||||||
(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)))))))
|
|
||||||
|
|
||||||
(defun exwm-manage--kill-buffer-query-function ()
|
|
||||||
"Run in `kill-buffer-query-functions'."
|
|
||||||
(exwm--log "id=#x%x; buffer=%s" (or exwm--id 0) (current-buffer))
|
|
||||||
(catch 'return
|
|
||||||
(when (or (not exwm--connection)
|
|
||||||
(not (slot-value exwm--connection 'connected)))
|
|
||||||
(throw 'return t))
|
|
||||||
(when (or (not exwm--id)
|
|
||||||
(xcb:+request-checked+request-check exwm--connection
|
|
||||||
(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))
|
|
||||||
(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))
|
|
||||||
(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.
|
|
||||||
(xcb:+request exwm--connection
|
|
||||||
(make-instance 'xcb:DestroyWindow :window exwm--id))
|
|
||||||
(xcb:flush exwm--connection)
|
|
||||||
;; Wait for DestroyNotify event.
|
|
||||||
(throw 'return nil))
|
|
||||||
(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)))
|
|
||||||
(xcb:flush exwm--connection)
|
|
||||||
;;
|
|
||||||
(unless (memq xcb:Atom:_NET_WM_PING exwm--protocols)
|
|
||||||
;; 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)
|
|
||||||
(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 (y-or-n-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)))))
|
|
||||||
|
|
||||||
(defun exwm-manage--kill-client (&optional id)
|
|
||||||
"Kill X client ID.
|
|
||||||
If ID is nil, kill X window corresponding to current buffer."
|
|
||||||
(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)))
|
|
||||||
(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--add-frame (frame)
|
|
||||||
"Run in `after-make-frame-functions'.
|
|
||||||
FRAME is the newly created frame."
|
|
||||||
(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'.
|
|
||||||
FRAME is the frame to be deleted."
|
|
||||||
(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))
|
|
||||||
exwm-manage--frame-outer-id-list))))
|
|
||||||
|
|
||||||
(defun exwm-manage--on-ConfigureRequest (data _synthetic)
|
|
||||||
"Handle ConfigureRequest event.
|
|
||||||
DATA contains unmarshalled ConfigureRequest event data."
|
|
||||||
(exwm--log)
|
|
||||||
(let ((obj (make-instance 'xcb:ConfigureRequest))
|
|
||||||
buffer edges width-delta height-delta)
|
|
||||||
(xcb:unmarshal obj data)
|
|
||||||
(with-slots (window x y width height
|
|
||||||
border-width sibling stack-mode value-mask)
|
|
||||||
obj
|
|
||||||
(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)
|
|
||||||
(if (and (setq buffer (exwm--id->buffer window))
|
|
||||||
(with-current-buffer buffer
|
|
||||||
(or (exwm-layout--fullscreen-p)
|
|
||||||
;; 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
|
|
||||||
(if (exwm-layout--fullscreen-p)
|
|
||||||
(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)
|
|
||||||
(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))))
|
|
||||||
(if buffer
|
|
||||||
(with-current-buffer buffer
|
|
||||||
(exwm--log "ConfigureWindow (resize floating X window)")
|
|
||||||
(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)))
|
|
||||||
(exwm--log "ConfigureWindow (preserve geometry)")
|
|
||||||
;; Configure the unmanaged window.
|
|
||||||
;; 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))
|
|
||||||
|
|
||||||
(defun exwm-manage--on-MapRequest (data _synthetic)
|
|
||||||
"Handle MapRequest event.
|
|
||||||
DATA contains unmarshalled MapRequest event data."
|
|
||||||
(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)
|
|
||||||
;; State change: iconic => normal.
|
|
||||||
(when (eq exwm--frame exwm-workspace--current)
|
|
||||||
(pop-to-buffer-same-window (current-buffer)))
|
|
||||||
(exwm--log "#x%x is already managed" window)))
|
|
||||||
(if (/= exwm--root parent)
|
|
||||||
(progn (xcb:+request exwm--connection
|
|
||||||
(make-instance 'xcb:MapWindow :window window))
|
|
||||||
(xcb:flush exwm--connection))
|
|
||||||
(exwm--log "#x%x" window)
|
|
||||||
(exwm-manage--manage-window window))))))
|
|
||||||
|
|
||||||
(defun exwm-manage--on-UnmapNotify (data _synthetic)
|
|
||||||
"Handle UnmapNotify event.
|
|
||||||
DATA contains unmarshalled UnmapNotify event data."
|
|
||||||
(let ((obj (make-instance 'xcb:UnmapNotify)))
|
|
||||||
(xcb:unmarshal obj data)
|
|
||||||
(with-slots (window) obj
|
|
||||||
(exwm--log "id=#x%x" window)
|
|
||||||
(exwm-manage--unmanage-window window t))))
|
|
||||||
|
|
||||||
(defun exwm-manage--on-MapNotify (data _synthetic)
|
|
||||||
"Handle MapNotify event.
|
|
||||||
DATA contains unmarshalled MapNotify event data."
|
|
||||||
(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.
|
|
||||||
(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)
|
|
||||||
"Handle DestroyNotify event.
|
|
||||||
DATA contains unmarshalled DestroyNotify event data.
|
|
||||||
SYNTHETIC indicates whether the event is a synthetic event."
|
|
||||||
(unless synthetic
|
|
||||||
(exwm--log)
|
|
||||||
(let ((obj (make-instance 'xcb:DestroyNotify)))
|
|
||||||
(xcb:unmarshal obj data)
|
|
||||||
(exwm--log "#x%x" (slot-value obj 'window))
|
|
||||||
(exwm-manage--unmanage-window (slot-value obj 'window)))))
|
|
||||||
|
|
||||||
(defun exwm-manage--init ()
|
|
||||||
"Initialize manage module."
|
|
||||||
;; Intern _MOTIF_WM_HINTS
|
|
||||||
(exwm--log)
|
|
||||||
(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
|
|
||||||
#'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))
|
|
||||||
|
|
||||||
(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)
|
|
||||||
(remove-hook 'delete-frame-functions #'exwm-manage--remove-frame)
|
|
||||||
(setq exwm-manage--_MOTIF_WM_HINTS nil))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(provide 'exwm-manage)
|
|
||||||
|
|
||||||
;;; exwm-manage.el ends here
|
|
377
exwm-randr.el
377
exwm-randr.el
|
@ -1,377 +0,0 @@
|
||||||
;;; exwm-randr.el --- RandR Module for EXWM -*- lexical-binding: t -*-
|
|
||||||
|
|
||||||
;; Copyright (C) 2015-2024 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
;; Author: Chris Feng <chris.w.feng@gmail.com>
|
|
||||||
|
|
||||||
;; 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
;;; 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, load, enable it and configure
|
|
||||||
;; `exwm-randr-workspace-monitor-plist' and `exwm-randr-screen-change-hook'
|
|
||||||
;; as follows:
|
|
||||||
;;
|
|
||||||
;; (require 'exwm-randr)
|
|
||||||
;; (setq exwm-randr-workspace-monitor-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)
|
|
||||||
;;
|
|
||||||
;; With above lines, workspace 0 should be assigned to the output named "VGA1",
|
|
||||||
;; 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)
|
|
||||||
|
|
||||||
;;; Code:
|
|
||||||
|
|
||||||
(require 'xcb-randr)
|
|
||||||
|
|
||||||
(require 'exwm-core)
|
|
||||||
(require 'exwm-workspace)
|
|
||||||
|
|
||||||
(declare-function x-get-atom-name "C source code" (VALUE &optional FRAME))
|
|
||||||
|
|
||||||
(defgroup exwm-randr nil
|
|
||||||
"RandR."
|
|
||||||
:version "25.3"
|
|
||||||
:group 'exwm)
|
|
||||||
|
|
||||||
(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-monitor-plist nil
|
|
||||||
"Plist mapping workspaces to monitors.
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
xrandr --setmonitor *LVDS-1-L 640/135x800/163+0+0 LVDS-1
|
|
||||||
xrandr --setmonitor LVDS-1-R 640/135x800/163+640+0 none
|
|
||||||
|
|
||||||
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-randr--last-timestamp 0 "Used for debouncing events.")
|
|
||||||
|
|
||||||
(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 1.5 monitors."
|
|
||||||
(exwm--log)
|
|
||||||
(let (monitor-name geometry monitor-geometry-alist primary-monitor)
|
|
||||||
(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)
|
|
||||||
geometry (make-instance 'xcb:RECTANGLE
|
|
||||||
:x x
|
|
||||||
:y y
|
|
||||||
:width width
|
|
||||||
:height height)
|
|
||||||
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-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))
|
|
||||||
(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))))))
|
|
||||||
monitor-alias-alist))
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(defun exwm-randr-refresh ()
|
|
||||||
"Refresh workspaces according to the updated RandR info."
|
|
||||||
(interactive)
|
|
||||||
(exwm--log)
|
|
||||||
(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))
|
|
||||||
container-monitor-alist container-frame-alist)
|
|
||||||
(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 (cdr (assoc monitor monitor-geometry-alist)))
|
|
||||||
(frame (elt exwm-workspace--list i))
|
|
||||||
(container (frame-parameter frame 'exwm-container)))
|
|
||||||
(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 (cdr (assoc primary-monitor
|
|
||||||
monitor-geometry-alist))))
|
|
||||||
(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-monitor monitor)
|
|
||||||
(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))
|
|
||||||
;; 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 ((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))))
|
|
||||||
|
|
||||||
(define-obsolete-function-alias 'exwm-randr--refresh #'exwm-randr-refresh
|
|
||||||
"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)
|
|
||||||
(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.
|
|
||||||
|
|
||||||
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)
|
|
||||||
(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."
|
|
||||||
(exwm--log)
|
|
||||||
(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))
|
|
||||||
(unless (assq i frameset-filter-alist)
|
|
||||||
(push (cons i :never) frameset-filter-alist))))
|
|
||||||
|
|
||||||
(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))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(provide 'exwm-randr)
|
|
||||||
|
|
||||||
;;; exwm-randr.el ends here
|
|
|
@ -1,702 +0,0 @@
|
||||||
;;; exwm-systemtray.el --- System Tray Module for -*- lexical-binding: t -*-
|
|
||||||
;;; EXWM
|
|
||||||
|
|
||||||
;; Copyright (C) 2016-2024 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
;; Author: Chris Feng <chris.w.feng@gmail.com>
|
|
||||||
|
|
||||||
;; 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
;;; 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-ewmh)
|
|
||||||
(require 'xcb-icccm)
|
|
||||||
(require 'xcb-xembed)
|
|
||||||
(require 'xcb-systemtray)
|
|
||||||
|
|
||||||
(require 'exwm-core)
|
|
||||||
(require 'exwm-workspace)
|
|
||||||
|
|
||||||
(declare-function exwm-workspace--workarea "exwm-workspace.el" (frame))
|
|
||||||
|
|
||||||
(defclass exwm-systemtray--icon ()
|
|
||||||
((width :initarg :width)
|
|
||||||
(height :initarg :height)
|
|
||||||
(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.")
|
|
||||||
|
|
||||||
(defgroup exwm-systemtray nil
|
|
||||||
"System tray."
|
|
||||||
:version "25.3"
|
|
||||||
:group 'exwm)
|
|
||||||
|
|
||||||
(defcustom exwm-systemtray-height nil
|
|
||||||
"System tray height.
|
|
||||||
|
|
||||||
You shall use the default value if using auto-hide minibuffer."
|
|
||||||
:type 'integer)
|
|
||||||
|
|
||||||
(defcustom exwm-systemtray-icon-gap 2
|
|
||||||
"Gap between icons."
|
|
||||||
:type 'integer)
|
|
||||||
|
|
||||||
(defvar exwm-systemtray--connection nil "The X connection.")
|
|
||||||
|
|
||||||
(defvar exwm-systemtray--embedder-window nil "The embedder window.")
|
|
||||||
(defvar exwm-systemtray--embedder-window-depth nil
|
|
||||||
"The embedder window's depth.")
|
|
||||||
|
|
||||||
(defcustom exwm-systemtray-background-color 'workspace-background
|
|
||||||
"Background color of systemtray.
|
|
||||||
This should be a color, the symbol `workspace-background' for the background
|
|
||||||
color of current workspace frame, or the symbol `transparent' for transparent
|
|
||||||
background.
|
|
||||||
|
|
||||||
Transparent background is not yet supported when Emacs uses 32-bit depth
|
|
||||||
visual, as reported by `x-display-planes'. The X resource \"Emacs.visualClass:
|
|
||||||
TrueColor-24\" can be used to force Emacs to use 24-bit depth."
|
|
||||||
:type '(choice (const :tag "Transparent" transparent)
|
|
||||||
(const :tag "Frame background" workspace-background)
|
|
||||||
(color :tag "Color"))
|
|
||||||
:initialize #'custom-initialize-default
|
|
||||||
:set (lambda (symbol value)
|
|
||||||
(when (and (eq value 'transparent)
|
|
||||||
(not (exwm-systemtray--transparency-supported-p)))
|
|
||||||
(display-warning 'exwm-systemtray
|
|
||||||
"Transparent background is not supported yet when \
|
|
||||||
using 32-bit depth. Using `workspace-background' instead.")
|
|
||||||
(setq value 'workspace-background))
|
|
||||||
(set-default symbol value)
|
|
||||||
(when (and exwm-systemtray--connection
|
|
||||||
exwm-systemtray--embedder-window)
|
|
||||||
;; Change the background color for embedder.
|
|
||||||
(exwm-systemtray--set-background-color)
|
|
||||||
;; Unmap & map to take effect immediately.
|
|
||||||
(xcb:+request exwm-systemtray--connection
|
|
||||||
(make-instance 'xcb:UnmapWindow
|
|
||||||
:window exwm-systemtray--embedder-window))
|
|
||||||
(xcb:+request exwm-systemtray--connection
|
|
||||||
(make-instance 'xcb:MapWindow
|
|
||||||
:window exwm-systemtray--embedder-window))
|
|
||||||
(xcb:flush exwm-systemtray--connection))))
|
|
||||||
|
|
||||||
;; GTK icons require at least 16 pixels to show normally.
|
|
||||||
(defconst exwm-systemtray--icon-min-size 16 "Minimum icon size.")
|
|
||||||
|
|
||||||
(defvar exwm-systemtray--list nil "The icon list.")
|
|
||||||
|
|
||||||
(defvar exwm-systemtray--selection-owner-window nil
|
|
||||||
"The selection owner window.")
|
|
||||||
|
|
||||||
(defvar xcb:Atom:_NET_SYSTEM_TRAY_S0)
|
|
||||||
|
|
||||||
(defun exwm-systemtray--embed (icon)
|
|
||||||
"Embed an 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 "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 "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
|
|
||||||
:window icon
|
|
||||||
:parent exwm-systemtray--embedder-window
|
|
||||||
: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: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
|
|
||||||
(/= 0 (logand (slot-value info 'flags) xcb:xembed:MAPPED)))
|
|
||||||
;; Default to visible.
|
|
||||||
(setq visible t))
|
|
||||||
(when visible
|
|
||||||
(exwm--log "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-window
|
|
||||||
: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 "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."
|
|
||||||
(exwm--log)
|
|
||||||
;; Make sure to redraw the embedder.
|
|
||||||
(xcb:+request exwm-systemtray--connection
|
|
||||||
(make-instance 'xcb:UnmapWindow
|
|
||||||
:window exwm-systemtray--embedder-window))
|
|
||||||
(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)))
|
|
||||||
(let ((workarea (exwm-workspace--workarea exwm-workspace-current-index)))
|
|
||||||
(xcb:+request exwm-systemtray--connection
|
|
||||||
(make-instance 'xcb:ConfigureWindow
|
|
||||||
:window exwm-systemtray--embedder-window
|
|
||||||
:value-mask (logior xcb:ConfigWindow:X
|
|
||||||
xcb:ConfigWindow:Width)
|
|
||||||
:x (- (slot-value workarea 'width) x)
|
|
||||||
:width x)))
|
|
||||||
(when map
|
|
||||||
(xcb:+request exwm-systemtray--connection
|
|
||||||
(make-instance 'xcb:MapWindow
|
|
||||||
:window exwm-systemtray--embedder-window))))
|
|
||||||
(xcb:flush exwm-systemtray--connection))
|
|
||||||
|
|
||||||
(defun exwm-systemtray--refresh-background-color (&optional remap)
|
|
||||||
"Refresh background color after theme change or workspace switch.
|
|
||||||
If REMAP is not nil, map and unmap the embedder window so that the background is
|
|
||||||
redrawn."
|
|
||||||
;; Only `workspace-background' is dependent on current theme and workspace.
|
|
||||||
(when (eq 'workspace-background exwm-systemtray-background-color)
|
|
||||||
(exwm-systemtray--set-background-color)
|
|
||||||
(when remap
|
|
||||||
(xcb:+request exwm-systemtray--connection
|
|
||||||
(make-instance 'xcb:UnmapWindow
|
|
||||||
:window exwm-systemtray--embedder-window))
|
|
||||||
(xcb:+request exwm-systemtray--connection
|
|
||||||
(make-instance 'xcb:MapWindow
|
|
||||||
:window exwm-systemtray--embedder-window))
|
|
||||||
(xcb:flush exwm-systemtray--connection))))
|
|
||||||
|
|
||||||
(defun exwm-systemtray--set-background-color ()
|
|
||||||
"Change the background color of the embedder.
|
|
||||||
The color is set according to `exwm-systemtray-background-color'.
|
|
||||||
|
|
||||||
Note that this function does not change the current contents of the embedder
|
|
||||||
window; unmap & map are necessary for the background color to take effect."
|
|
||||||
(when (and exwm-systemtray--connection
|
|
||||||
exwm-systemtray--embedder-window)
|
|
||||||
(let* ((color (cl-case exwm-systemtray-background-color
|
|
||||||
((transparent nil) ; nil means transparent as well
|
|
||||||
(if (exwm-systemtray--transparency-supported-p)
|
|
||||||
nil
|
|
||||||
(message "%s" "[EXWM] system tray does not support \
|
|
||||||
`transparent' background; using `workspace-background' instead")
|
|
||||||
(face-background 'default exwm-workspace--current)))
|
|
||||||
(workspace-background
|
|
||||||
(face-background 'default exwm-workspace--current))
|
|
||||||
(t exwm-systemtray-background-color)))
|
|
||||||
(background-pixel (exwm--color->pixel color)))
|
|
||||||
(xcb:+request exwm-systemtray--connection
|
|
||||||
(make-instance 'xcb:ChangeWindowAttributes
|
|
||||||
:window exwm-systemtray--embedder-window
|
|
||||||
;; Either-or. A `background-pixel' of nil
|
|
||||||
;; means simulate transparency. We use
|
|
||||||
;; `xcb:CW:BackPixmap' together with
|
|
||||||
;; `xcb:BackPixmap:ParentRelative' do that,
|
|
||||||
;; but this only works when the parent
|
|
||||||
;; window's visual (Emacs') has the same
|
|
||||||
;; visual depth.
|
|
||||||
:value-mask (if background-pixel
|
|
||||||
xcb:CW:BackPixel
|
|
||||||
xcb:CW:BackPixmap)
|
|
||||||
;; Due to the :value-mask above,
|
|
||||||
;; :background-pixmap only takes effect when
|
|
||||||
;; `transparent' is requested and supported
|
|
||||||
;; (visual depth of Emacs and of system tray
|
|
||||||
;; are equal). Setting
|
|
||||||
;; `xcb:BackPixmap:ParentRelative' when
|
|
||||||
;; that's not the case would produce an
|
|
||||||
;; `xcb:Match' error.
|
|
||||||
:background-pixmap xcb:BackPixmap:ParentRelative
|
|
||||||
:background-pixel background-pixel)))))
|
|
||||||
|
|
||||||
(defun exwm-systemtray--transparency-supported-p ()
|
|
||||||
"Check whether transparent background is supported.
|
|
||||||
EXWM system tray supports transparency when the visual depth of the system tray
|
|
||||||
window matches that of Emacs. The visual depth of the system tray window is the
|
|
||||||
default visual depth of the display.
|
|
||||||
|
|
||||||
Sections \"Visual and background pixmap handling\" and
|
|
||||||
\"_NET_SYSTEM_TRAY_VISUAL\" of the System Tray Protocol Specification
|
|
||||||
\(https://specifications.freedesktop.org/systemtray-spec/systemtray-spec-latest.html#visuals)
|
|
||||||
indicate how to support actual transparency."
|
|
||||||
(let ((planes (x-display-planes)))
|
|
||||||
(if exwm-systemtray--embedder-window-depth
|
|
||||||
(= planes exwm-systemtray--embedder-window-depth)
|
|
||||||
(<= planes 24))))
|
|
||||||
|
|
||||||
(defun exwm-systemtray--on-DestroyNotify (data _synthetic)
|
|
||||||
"Unembed icons on DestroyNotify.
|
|
||||||
Argument DATA contains the raw event data."
|
|
||||||
(exwm--log)
|
|
||||||
(let ((obj (make-instance 'xcb:DestroyNotify)))
|
|
||||||
(xcb:unmarshal obj data)
|
|
||||||
(with-slots (window) obj
|
|
||||||
(when (assoc window exwm-systemtray--list)
|
|
||||||
(exwm-systemtray--unembed window)))))
|
|
||||||
|
|
||||||
(defun exwm-systemtray--on-ReparentNotify (data _synthetic)
|
|
||||||
"Unembed icons on ReparentNotify.
|
|
||||||
Argument DATA contains the raw event data."
|
|
||||||
(exwm--log)
|
|
||||||
(let ((obj (make-instance 'xcb:ReparentNotify)))
|
|
||||||
(xcb:unmarshal obj data)
|
|
||||||
(with-slots (window parent) obj
|
|
||||||
(when (and (/= parent exwm-systemtray--embedder-window)
|
|
||||||
(assoc window exwm-systemtray--list))
|
|
||||||
(exwm-systemtray--unembed window)))))
|
|
||||||
|
|
||||||
(defun exwm-systemtray--on-ResizeRequest (data _synthetic)
|
|
||||||
"Resize the tray icon on ResizeRequest.
|
|
||||||
Argument DATA contains the raw event data."
|
|
||||||
(exwm--log)
|
|
||||||
(let ((obj (make-instance 'xcb:ResizeRequest))
|
|
||||||
attr)
|
|
||||||
(xcb:unmarshal obj data)
|
|
||||||
(with-slots (window width height) obj
|
|
||||||
(when (setq attr (cdr (assoc 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.
|
|
||||||
Argument DATA contains the raw event data."
|
|
||||||
(exwm--log)
|
|
||||||
(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 (cdr (assoc 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 "#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.
|
|
||||||
Argument DATA contains the raw event data."
|
|
||||||
(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))
|
|
||||||
(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))))
|
|
||||||
;; Not implemented (rarely used nowadays).
|
|
||||||
((or (= opcode xcb:systemtray:opcode:BEGIN-MESSAGE)
|
|
||||||
(= opcode xcb:systemtray:opcode:CANCEL-MESSAGE)))
|
|
||||||
(t
|
|
||||||
(exwm--log "Unknown opcode message: %s" obj)))))))
|
|
||||||
|
|
||||||
(defun exwm-systemtray--on-KeyPress (data _synthetic)
|
|
||||||
"Forward all KeyPress events to Emacs frame.
|
|
||||||
Argument DATA contains the raw event data."
|
|
||||||
(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.
|
|
||||||
(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'."
|
|
||||||
(exwm--log)
|
|
||||||
(unless (exwm-workspace--minibuffer-own-frame-p)
|
|
||||||
(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 (- (slot-value (exwm-workspace--workarea
|
|
||||||
exwm-workspace-current-index)
|
|
||||||
'height)
|
|
||||||
exwm-workspace--frame-y-offset
|
|
||||||
exwm-systemtray-height))))
|
|
||||||
(exwm-systemtray--refresh-background-color)
|
|
||||||
(exwm-systemtray--refresh))
|
|
||||||
|
|
||||||
(defun exwm-systemtray--on-theme-change (_theme)
|
|
||||||
"Refresh system tray upon theme change."
|
|
||||||
(exwm-systemtray--refresh-background-color 'remap))
|
|
||||||
|
|
||||||
(defun exwm-systemtray--refresh-all ()
|
|
||||||
"Reposition/Refresh the system tray."
|
|
||||||
(exwm--log)
|
|
||||||
(unless (exwm-workspace--minibuffer-own-frame-p)
|
|
||||||
(exwm-workspace--update-offsets)
|
|
||||||
(xcb:+request exwm-systemtray--connection
|
|
||||||
(make-instance 'xcb:ConfigureWindow
|
|
||||||
:window exwm-systemtray--embedder-window
|
|
||||||
:value-mask xcb:ConfigWindow:Y
|
|
||||||
:y (- (slot-value (exwm-workspace--workarea
|
|
||||||
exwm-workspace-current-index)
|
|
||||||
'height)
|
|
||||||
exwm-workspace--frame-y-offset
|
|
||||||
exwm-systemtray-height))))
|
|
||||||
(exwm-systemtray--refresh))
|
|
||||||
|
|
||||||
(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))
|
|
||||||
(cl-assert (not exwm-systemtray--embedder-window))
|
|
||||||
(unless exwm-systemtray-height
|
|
||||||
(setq exwm-systemtray-height (max exwm-systemtray--icon-min-size
|
|
||||||
(with-selected-window (minibuffer-window)
|
|
||||||
(line-pixel-height)))))
|
|
||||||
;; Create a new connection.
|
|
||||||
(setq exwm-systemtray--connection (xcb:connect))
|
|
||||||
(set-process-query-on-exit-flag (slot-value exwm-systemtray--connection
|
|
||||||
'process)
|
|
||||||
nil)
|
|
||||||
;; Initialize XELB modules.
|
|
||||||
(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
|
|
||||||
(make-instance 'xcb:GetSelectionOwner
|
|
||||||
: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)))
|
|
||||||
(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))
|
|
||||||
;; 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
|
|
||||||
: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
|
|
||||||
:window id
|
|
||||||
:data xcb:systemtray:ORIENTATION:HORZ)))
|
|
||||||
;; Create the embedder.
|
|
||||||
(let ((id (xcb:generate-id exwm-systemtray--connection))
|
|
||||||
frame parent embedder-depth embedder-visual embedder-colormap y)
|
|
||||||
(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)
|
|
||||||
;; Bottom aligned.
|
|
||||||
(- (line-pixel-height) exwm-systemtray-height)
|
|
||||||
;; Vertically centered.
|
|
||||||
(/ (- (line-pixel-height) exwm-systemtray-height) 2)))
|
|
||||||
(exwm-workspace--update-offsets)
|
|
||||||
(setq frame exwm-workspace--current
|
|
||||||
;; Bottom aligned.
|
|
||||||
y (- (slot-value (exwm-workspace--workarea
|
|
||||||
exwm-workspace-current-index)
|
|
||||||
'height)
|
|
||||||
exwm-workspace--frame-y-offset
|
|
||||||
exwm-systemtray-height)))
|
|
||||||
(setq parent (string-to-number (frame-parameter frame 'window-id)))
|
|
||||||
;; Use default depth, visual and colormap (from root window), instead of
|
|
||||||
;; Emacs frame's. See Section "Visual and background pixmap handling" in
|
|
||||||
;; "System Tray Protocol Specification 0.3".
|
|
||||||
(let* ((vdc (exwm--get-visual-depth-colormap exwm-systemtray--connection
|
|
||||||
exwm--root)))
|
|
||||||
(setq embedder-visual (car vdc))
|
|
||||||
(setq embedder-depth (cadr vdc))
|
|
||||||
(setq embedder-colormap (caddr vdc)))
|
|
||||||
;; Note down the embedder window's depth. It will be used to check whether
|
|
||||||
;; we can use xcb:BackPixmap:ParentRelative to emulate transparency.
|
|
||||||
(setq exwm-systemtray--embedder-window-depth embedder-depth)
|
|
||||||
(xcb:+request exwm-systemtray--connection
|
|
||||||
(make-instance 'xcb:CreateWindow
|
|
||||||
:depth embedder-depth
|
|
||||||
:wid id
|
|
||||||
:parent parent
|
|
||||||
:x 0
|
|
||||||
:y y
|
|
||||||
:width 1
|
|
||||||
:height exwm-systemtray-height
|
|
||||||
:border-width 0
|
|
||||||
:class xcb:WindowClass:InputOutput
|
|
||||||
:visual embedder-visual
|
|
||||||
:colormap embedder-colormap
|
|
||||||
:value-mask (logior xcb:CW:BorderPixel
|
|
||||||
xcb:CW:Colormap
|
|
||||||
xcb:CW:EventMask)
|
|
||||||
:border-pixel 0
|
|
||||||
:event-mask xcb:EventMask:SubstructureNotify))
|
|
||||||
(exwm-systemtray--set-background-color)
|
|
||||||
;; Set _NET_WM_NAME.
|
|
||||||
(xcb:+request exwm-systemtray--connection
|
|
||||||
(make-instance 'xcb:ewmh:set-_NET_WM_NAME
|
|
||||||
:window id
|
|
||||||
:data "EXWM: exwm-systemtray--embedder-window"))
|
|
||||||
;; Set _NET_WM_WINDOW_TYPE.
|
|
||||||
(xcb:+request exwm-systemtray--connection
|
|
||||||
(make-instance 'xcb:ewmh:set-_NET_WM_WINDOW_TYPE
|
|
||||||
:window id
|
|
||||||
:data (vector xcb:Atom:_NET_WM_WINDOW_TYPE_DOCK)))
|
|
||||||
;; Set _NET_SYSTEM_TRAY_VISUAL.
|
|
||||||
(xcb:+request exwm-systemtray--connection
|
|
||||||
(make-instance 'xcb:xembed:set-_NET_SYSTEM_TRAY_VISUAL
|
|
||||||
:window exwm-systemtray--selection-owner-window
|
|
||||||
:data embedder-visual)))
|
|
||||||
(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)
|
|
||||||
(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)
|
|
||||||
(add-hook 'exwm-workspace--update-workareas-hook
|
|
||||||
#'exwm-systemtray--refresh-all)
|
|
||||||
;; Add hook to update background colors.
|
|
||||||
(add-hook 'enable-theme-functions #'exwm-systemtray--on-theme-change)
|
|
||||||
(add-hook 'disable-theme-functions #'exwm-systemtray--on-theme-change)
|
|
||||||
(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--refresh-all))
|
|
||||||
;; The struts can be updated already.
|
|
||||||
(when exwm-workspace--workareas
|
|
||||||
(exwm-systemtray--refresh-all)))
|
|
||||||
|
|
||||||
(defun exwm-systemtray--exit ()
|
|
||||||
"Exit the systemtray module."
|
|
||||||
(exwm--log)
|
|
||||||
(when exwm-systemtray--connection
|
|
||||||
(when (slot-value exwm-systemtray--connection 'connected)
|
|
||||||
;; 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-window))
|
|
||||||
(xcb:+request exwm-systemtray--connection
|
|
||||||
(make-instance 'xcb:ReparentWindow
|
|
||||||
:window exwm-systemtray--embedder-window
|
|
||||||
:parent exwm--root
|
|
||||||
:x 0
|
|
||||||
:y 0))
|
|
||||||
(xcb:disconnect exwm-systemtray--connection))
|
|
||||||
(setq exwm-systemtray--connection nil
|
|
||||||
exwm-systemtray--list nil
|
|
||||||
exwm-systemtray--selection-owner-window nil
|
|
||||||
exwm-systemtray--embedder-window nil
|
|
||||||
exwm-systemtray--embedder-window-depth nil)
|
|
||||||
(remove-hook 'exwm-workspace-switch-hook
|
|
||||||
#'exwm-systemtray--on-workspace-switch)
|
|
||||||
(remove-hook 'exwm-workspace--update-workareas-hook
|
|
||||||
#'exwm-systemtray--refresh-all)
|
|
||||||
(remove-hook 'enable-theme-functions #'exwm-systemtray--on-theme-change)
|
|
||||||
(remove-hook 'disable-theme-functions #'exwm-systemtray--on-theme-change)
|
|
||||||
(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--refresh-all))))
|
|
||||||
|
|
||||||
(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))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(provide 'exwm-systemtray)
|
|
||||||
|
|
||||||
;;; exwm-systemtray.el ends here
|
|
1769
exwm-workspace.el
1769
exwm-workspace.el
File diff suppressed because it is too large
Load diff
810
exwm-xim.el
810
exwm-xim.el
|
@ -1,810 +0,0 @@
|
||||||
;;; exwm-xim.el --- XIM Module for EXWM -*- lexical-binding: t -*-
|
|
||||||
|
|
||||||
;; Copyright (C) 2019-2024 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
;; Author: Chris Feng <chris.w.feng@gmail.com>
|
|
||||||
|
|
||||||
;; 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
;;; 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:
|
|
||||||
|
|
||||||
(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.")
|
|
||||||
|
|
||||||
;; 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)
|
|
||||||
|
|
||||||
(defvar exwm-xim-buffer-p nil
|
|
||||||
"Whether current buffer is used by exwm-xim.")
|
|
||||||
(make-variable-buffer-local 'exwm-xim-buffer-p)
|
|
||||||
|
|
||||||
(defun exwm-xim--on-SelectionRequest (data _synthetic)
|
|
||||||
"Handle SelectionRequest events on IMS window.
|
|
||||||
DATA contains unmarshalled SelectionRequest event data.
|
|
||||||
|
|
||||||
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)
|
|
||||||
(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))
|
|
||||||
(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--handle-forward-event-request (req lsb conn client-xwin)
|
|
||||||
(let ((im-func (with-current-buffer (window-buffer)
|
|
||||||
input-method-function))
|
|
||||||
key-event keysym keysyms 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)))))))
|
|
||||||
(while (or (slot-value req 'event) unread-command-events)
|
|
||||||
(unless (slot-value req 'event)
|
|
||||||
(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)
|
|
||||||
(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 keysyms (xcb:keysyms:event->keysyms exwm-xim--conn event))
|
|
||||||
(with-slots (detail state) key-event
|
|
||||||
(setf detail (xcb:keysyms:keysym->keycode exwm-xim--conn
|
|
||||||
(caar keysyms))
|
|
||||||
state (cdar keysyms)))
|
|
||||||
(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)
|
|
||||||
;; Grab keyboard temporarily for char-mode.
|
|
||||||
(exwm-input--grab-keyboard))
|
|
||||||
(unwind-protect
|
|
||||||
(with-temp-buffer
|
|
||||||
;; This variable is used to test whether exwm-xim is enabled.
|
|
||||||
;; Used by e.g. pyim-probe.
|
|
||||||
(setq-local exwm-xim-buffer-p t)
|
|
||||||
;; Always show key strokes.
|
|
||||||
(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)
|
|
||||||
;; 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.
|
|
||||||
|
|
||||||
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)
|
|
||||||
(when (slot-value (elt i 0) 'connected)
|
|
||||||
(xcb:disconnect (elt i 0)))))
|
|
||||||
exwm-xim--server-client-plist)
|
|
||||||
;; Close the IMS connection.
|
|
||||||
(unless (and exwm-xim--conn
|
|
||||||
(slot-value exwm-xim--conn 'connected))
|
|
||||||
(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
|
|
20
xinitrc
20
xinitrc
|
@ -1,20 +0,0 @@
|
||||||
# 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
|
|
||||||
|
|
||||||
# Set default cursor.
|
|
||||||
xsetroot -cursor_name left_ptr
|
|
||||||
|
|
||||||
# Set keyboard repeat rate.
|
|
||||||
xset r rate 200 60
|
|
||||||
|
|
||||||
# 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
|
|
Loading…
Reference in a new issue