mv from dotfiles
This commit is contained in:
parent
b3bf338e43
commit
3daa1af41e
5
dwl/.gitignore
vendored
Normal file
5
dwl/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
dwl
|
||||||
|
*.o
|
||||||
|
*-protocol.c
|
||||||
|
*-protocol.h
|
||||||
|
.ccls-cache
|
692
dwl/LICENSE
Normal file
692
dwl/LICENSE
Normal file
@ -0,0 +1,692 @@
|
|||||||
|
dwl - dwm for Wayland
|
||||||
|
|
||||||
|
Copyright © 2020 dwl team
|
||||||
|
|
||||||
|
See also the files LICENSE.tinywl, LICENSE.dwm and LICENSE.sway.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://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 <https://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:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
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
|
||||||
|
<https://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
|
||||||
|
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
39
dwl/LICENSE.dwm
Normal file
39
dwl/LICENSE.dwm
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
Portions of dwl based on dwm code are used under the following license:
|
||||||
|
|
||||||
|
MIT/X Consortium License
|
||||||
|
|
||||||
|
© 2006-2019 Anselm R Garbe <anselm@garbe.ca>
|
||||||
|
© 2006-2009 Jukka Salmi <jukka at salmi dot ch>
|
||||||
|
© 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
|
||||||
|
© 2007-2011 Peter Hartlich <sgkkr at hartlich dot com>
|
||||||
|
© 2007-2009 Szabolcs Nagy <nszabolcs at gmail dot com>
|
||||||
|
© 2007-2009 Christof Musik <christof at sendfax dot de>
|
||||||
|
© 2007-2009 Premysl Hruby <dfenze at gmail dot com>
|
||||||
|
© 2007-2008 Enno Gottox Boland <gottox at s01 dot de>
|
||||||
|
© 2008 Martin Hurton <martin dot hurton at gmail dot com>
|
||||||
|
© 2008 Neale Pickett <neale dot woozle dot org>
|
||||||
|
© 2009 Mate Nagy <mnagy at port70 dot net>
|
||||||
|
© 2010-2016 Hiltjo Posthuma <hiltjo@codemadness.org>
|
||||||
|
© 2010-2012 Connor Lane Smith <cls@lubutu.com>
|
||||||
|
© 2011 Christoph Lohmann <20h@r-36.net>
|
||||||
|
© 2015-2016 Quentin Rameau <quinq@fifth.space>
|
||||||
|
© 2015-2016 Eric Pruitt <eric.pruitt@gmail.com>
|
||||||
|
© 2016-2017 Markus Teich <markus.teich@stusta.mhn.de>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
19
dwl/LICENSE.sway
Normal file
19
dwl/LICENSE.sway
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2016-2017 Drew DeVault
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
127
dwl/LICENSE.tinywl
Normal file
127
dwl/LICENSE.tinywl
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
dwl is originally based on TinyWL, which is used under the following license:
|
||||||
|
|
||||||
|
This work is licensed under CC0, which effectively puts it in the public domain.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Creative Commons Legal Code
|
||||||
|
|
||||||
|
CC0 1.0 Universal
|
||||||
|
|
||||||
|
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||||
|
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||||
|
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||||
|
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||||
|
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||||
|
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||||
|
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||||
|
HEREUNDER.
|
||||||
|
|
||||||
|
Statement of Purpose
|
||||||
|
|
||||||
|
The laws of most jurisdictions throughout the world automatically confer
|
||||||
|
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||||
|
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||||
|
authorship and/or a database (each, a "Work").
|
||||||
|
|
||||||
|
Certain owners wish to permanently relinquish those rights to a Work for
|
||||||
|
the purpose of contributing to a commons of creative, cultural and
|
||||||
|
scientific works ("Commons") that the public can reliably and without fear
|
||||||
|
of later claims of infringement build upon, modify, incorporate in other
|
||||||
|
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||||
|
and for any purposes, including without limitation commercial purposes.
|
||||||
|
These owners may contribute to the Commons to promote the ideal of a free
|
||||||
|
culture and the further production of creative, cultural and scientific
|
||||||
|
works, or to gain reputation or greater distribution for their Work in
|
||||||
|
part through the use and efforts of others.
|
||||||
|
|
||||||
|
For these and/or other purposes and motivations, and without any
|
||||||
|
expectation of additional consideration or compensation, the person
|
||||||
|
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||||
|
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||||
|
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||||
|
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||||
|
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||||
|
|
||||||
|
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||||
|
protected by copyright and related or neighboring rights ("Copyright and
|
||||||
|
Related Rights"). Copyright and Related Rights include, but are not
|
||||||
|
limited to, the following:
|
||||||
|
|
||||||
|
i. the right to reproduce, adapt, distribute, perform, display,
|
||||||
|
communicate, and translate a Work;
|
||||||
|
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||||
|
iii. publicity and privacy rights pertaining to a person's image or
|
||||||
|
likeness depicted in a Work;
|
||||||
|
iv. rights protecting against unfair competition in regards to a Work,
|
||||||
|
subject to the limitations in paragraph 4(a), below;
|
||||||
|
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||||
|
in a Work;
|
||||||
|
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||||
|
European Parliament and of the Council of 11 March 1996 on the legal
|
||||||
|
protection of databases, and under any national implementation
|
||||||
|
thereof, including any amended or successor version of such
|
||||||
|
directive); and
|
||||||
|
vii. other similar, equivalent or corresponding rights throughout the
|
||||||
|
world based on applicable law or treaty, and any national
|
||||||
|
implementations thereof.
|
||||||
|
|
||||||
|
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||||
|
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||||
|
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||||
|
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||||
|
of action, whether now known or unknown (including existing as well as
|
||||||
|
future claims and causes of action), in the Work (i) in all territories
|
||||||
|
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||||
|
treaty (including future time extensions), (iii) in any current or future
|
||||||
|
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||||
|
including without limitation commercial, advertising or promotional
|
||||||
|
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||||
|
member of the public at large and to the detriment of Affirmer's heirs and
|
||||||
|
successors, fully intending that such Waiver shall not be subject to
|
||||||
|
revocation, rescission, cancellation, termination, or any other legal or
|
||||||
|
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||||
|
as contemplated by Affirmer's express Statement of Purpose.
|
||||||
|
|
||||||
|
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||||
|
be judged legally invalid or ineffective under applicable law, then the
|
||||||
|
Waiver shall be preserved to the maximum extent permitted taking into
|
||||||
|
account Affirmer's express Statement of Purpose. In addition, to the
|
||||||
|
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||||
|
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||||
|
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||||
|
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||||
|
maximum duration provided by applicable law or treaty (including future
|
||||||
|
time extensions), (iii) in any current or future medium and for any number
|
||||||
|
of copies, and (iv) for any purpose whatsoever, including without
|
||||||
|
limitation commercial, advertising or promotional purposes (the
|
||||||
|
"License"). The License shall be deemed effective as of the date CC0 was
|
||||||
|
applied by Affirmer to the Work. Should any part of the License for any
|
||||||
|
reason be judged legally invalid or ineffective under applicable law, such
|
||||||
|
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||||
|
of the License, and in such case Affirmer hereby affirms that he or she
|
||||||
|
will not (i) exercise any of his or her remaining Copyright and Related
|
||||||
|
Rights in the Work or (ii) assert any associated claims and causes of
|
||||||
|
action with respect to the Work, in either case contrary to Affirmer's
|
||||||
|
express Statement of Purpose.
|
||||||
|
|
||||||
|
4. Limitations and Disclaimers.
|
||||||
|
|
||||||
|
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||||
|
surrendered, licensed or otherwise affected by this document.
|
||||||
|
b. Affirmer offers the Work as-is and makes no representations or
|
||||||
|
warranties of any kind concerning the Work, express, implied,
|
||||||
|
statutory or otherwise, including without limitation warranties of
|
||||||
|
title, merchantability, fitness for a particular purpose, non
|
||||||
|
infringement, or the absence of latent or other defects, accuracy, or
|
||||||
|
the present or absence of errors, whether or not discoverable, all to
|
||||||
|
the greatest extent permissible under applicable law.
|
||||||
|
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||||
|
that may apply to the Work or any use thereof, including without
|
||||||
|
limitation any person's Copyright and Related Rights in the Work.
|
||||||
|
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||||
|
consents, permissions or other rights required for any use of the
|
||||||
|
Work.
|
||||||
|
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||||
|
party to this document and has no duty or obligation with respect to
|
||||||
|
this CC0 or use of the Work.
|
69
dwl/Makefile
Normal file
69
dwl/Makefile
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
.POSIX:
|
||||||
|
.SUFFIXES:
|
||||||
|
|
||||||
|
include config.mk
|
||||||
|
|
||||||
|
# flags for compiling
|
||||||
|
DWLCPPFLAGS = -I. -DWLR_USE_UNSTABLE -DVERSION=\"$(VERSION)\"
|
||||||
|
|
||||||
|
# Wayland utils
|
||||||
|
WAYLAND_PROTOCOLS = `pkg-config --variable=pkgdatadir wayland-protocols`
|
||||||
|
WAYLAND_SCANNER = `pkg-config --variable=wayland_scanner wayland-scanner`
|
||||||
|
|
||||||
|
# CFLAGS / LDFLAGS
|
||||||
|
PKGS = wlroots wayland-server xkbcommon libinput $(XLIBS)
|
||||||
|
DWLCFLAGS = `pkg-config --cflags $(PKGS)` $(DWLCPPFLAGS) $(CFLAGS) $(XWAYLAND)
|
||||||
|
LDLIBS = `pkg-config --libs $(PKGS)` $(LIBS)
|
||||||
|
|
||||||
|
# build rules
|
||||||
|
|
||||||
|
# wayland-scanner is a tool which generates C headers and rigging for Wayland
|
||||||
|
# protocols, which are specified in XML. wlroots requires you to rig these up
|
||||||
|
# to your build system yourself and provide them in the include path.
|
||||||
|
all: dwl
|
||||||
|
dwl: dwl.o util.o
|
||||||
|
$(CC) dwl.o util.o $(LDLIBS) $(LDFLAGS) -o $@
|
||||||
|
dwl.o: dwl.c config.mk config.h client.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h idle-protocol.h
|
||||||
|
util.o: util.c util.h
|
||||||
|
|
||||||
|
# wayland scanner rules to generate .h / .c files
|
||||||
|
xdg-shell-protocol.h:
|
||||||
|
$(WAYLAND_SCANNER) server-header \
|
||||||
|
$(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
|
||||||
|
wlr-layer-shell-unstable-v1-protocol.h:
|
||||||
|
$(WAYLAND_SCANNER) server-header \
|
||||||
|
protocols/wlr-layer-shell-unstable-v1.xml $@
|
||||||
|
idle-protocol.h:
|
||||||
|
$(WAYLAND_SCANNER) server-header \
|
||||||
|
protocols/idle.xml $@
|
||||||
|
|
||||||
|
config.h:
|
||||||
|
cp config.def.h $@
|
||||||
|
clean:
|
||||||
|
rm -f dwl *.o *-protocol.h
|
||||||
|
|
||||||
|
# distribution archive
|
||||||
|
dist: clean
|
||||||
|
mkdir -p dwl-$(VERSION)
|
||||||
|
cp -R LICENSE* Makefile README.md generate-version.sh client.h\
|
||||||
|
config.def.h config.mk protocols dwl.1 dwl.c util.c util.h\
|
||||||
|
dwl-$(VERSION)
|
||||||
|
echo "echo $(VERSION)" > dwl-$(VERSION)/generate-version.sh
|
||||||
|
tar -caf dwl-$(VERSION).tar.gz dwl-$(VERSION)
|
||||||
|
rm -rf dwl-$(VERSION)
|
||||||
|
|
||||||
|
# install rules
|
||||||
|
|
||||||
|
install: dwl
|
||||||
|
mkdir -p $(DESTDIR)$(PREFIX)/bin
|
||||||
|
cp -f dwl $(DESTDIR)$(PREFIX)/bin
|
||||||
|
chmod 755 $(DESTDIR)$(PREFIX)/bin/dwl
|
||||||
|
mkdir -p $(DESTDIR)$(MANDIR)/man1
|
||||||
|
cp -f dwl.1 $(DESTDIR)$(MANDIR)/man1
|
||||||
|
chmod 644 $(DESTDIR)$(MANDIR)/man1/dwl.1
|
||||||
|
uninstall:
|
||||||
|
rm -f $(DESTDIR)$(PREFIX)/bin/dwl $(DESTDIR)$(MANDIR)/man1/dwl.1
|
||||||
|
|
||||||
|
.SUFFIXES: .c .o
|
||||||
|
.c.o:
|
||||||
|
$(CC) $(CPPFLAGS) $(DWLCFLAGS) -c $<
|
98
dwl/README.md
Normal file
98
dwl/README.md
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
# dwl - dwm for Wayland
|
||||||
|
|
||||||
|
Join us on our [Discord server](https://discord.gg/jJxZnrGPWN)!
|
||||||
|
|
||||||
|
dwl is a compact, hackable compositor for Wayland based on [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots/). It is intended to fill the same space in the Wayland world that dwm does in X11, primarily in terms of philosophy, and secondarily in terms of functionality. Like dwm, dwl is:
|
||||||
|
|
||||||
|
- Easy to understand, hack on, and extend with patches
|
||||||
|
- One C source file (or a very small number) configurable via `config.h`
|
||||||
|
- Limited to 2000 SLOC to promote hackability
|
||||||
|
- Tied to as few external dependencies as possible
|
||||||
|
|
||||||
|
dwl is not meant to provide every feature under the sun. Instead, like dwm, it sticks to features which are necessary, simple, and straightforward to implement given the base on which it is built. Implemented default features are:
|
||||||
|
|
||||||
|
- Any features provided by dwm/Xlib: simple window borders, tags, keybindings, client rules, mouse move/resize. Providing a built-in status bar is an exception to this goal, to avoid dependencies on font rendering and/or drawing libraries when an external bar could work well.
|
||||||
|
- Configurable multi-monitor layout support, including position and rotation
|
||||||
|
- Configurable HiDPI/multi-DPI support
|
||||||
|
- Idle-inhibit protocol which lets applications such as mpv disable idle monitoring
|
||||||
|
- Provide information to external status bars via stdout/stdin
|
||||||
|
- Urgency hints via xdg-activate protocol
|
||||||
|
- Support screen lockers via input-inhibitor protocol
|
||||||
|
- Various Wayland protocols
|
||||||
|
- XWayland support as provided by wlroots (can be enabled in `config.mk`)
|
||||||
|
- Zero flickering - Wayland users naturally expect that "every frame is perfect"
|
||||||
|
- Layer shell popups (used by Waybar)
|
||||||
|
- Damage tracking provided by scenegraph API
|
||||||
|
|
||||||
|
Features under consideration (possibly as patches) are:
|
||||||
|
|
||||||
|
- Protocols made trivial by wlroots
|
||||||
|
- Implement the text-input and input-method protocols to support IME once ibus implements input-method v2 (see https://github.com/ibus/ibus/pull/2256 and https://github.com/djpohly/dwl/pull/12)
|
||||||
|
|
||||||
|
Feature *non-goals* for the main codebase include:
|
||||||
|
|
||||||
|
- Client-side decoration (any more than is necessary to tell the clients not to)
|
||||||
|
- Client-initiated window management, such as move, resize, and close, which can be done through the compositor
|
||||||
|
- Animations and visual effects
|
||||||
|
|
||||||
|
## Building dwl
|
||||||
|
|
||||||
|
dwl has only two dependencies: wlroots and wayland-protocols. Simply install these (and their `-devel` versions if your distro has separate development packages) and run `make`. If you wish to build against a Git version of wlroots, check out the [wlroots-next branch](https://github.com/djpohly/dwl/tree/wlroots-next).
|
||||||
|
|
||||||
|
To enable XWayland, you should also install xorg-xwayland and uncomment its flag in `config.mk`.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
All configuration is done by editing `config.h` and recompiling, in the same manner as dwm. There is no way to separately restart the window manager in Wayland without restarting the entire display server, so any changes will take effect the next time dwl is executed.
|
||||||
|
|
||||||
|
As in the dwm community, we encourage users to share patches they have created. Check out the [patches page on our wiki](https://github.com/djpohly/dwl/wiki/Patches)!
|
||||||
|
|
||||||
|
## Running dwl
|
||||||
|
|
||||||
|
dwl can be run on any of the backends supported by wlroots. This means you can run it as a separate window inside either an X11 or Wayland session, as well as directly from a VT console. Depending on your distro's setup, you may need to add your user to the `video` and `input` groups before you can run dwl on a VT.
|
||||||
|
|
||||||
|
When dwl is run with no arguments, it will launch the server and begin handling any shortcuts configured in `config.h`. There is no status bar or other decoration initially; these are instead clients that can be run within the Wayland session.
|
||||||
|
|
||||||
|
If you would like to run a script or command automatically at startup, you can specify the command using the `-s` option. This command will be executed as a shell command using `/bin/sh -c`. It serves a similar function to `.xinitrc`, but differs in that the display server will not shut down when this process terminates. Instead, dwl will send this process a SIGTERM at shutdown and wait for it to terminate (if it hasn't already). This makes it ideal for execing into a user service manager like [s6](https://skarnet.org/software/s6/), [anopa](https://jjacky.com/anopa/), [runit](http://smarden.org/runit/faq.html#userservices), or [`systemd --user`](https://wiki.archlinux.org/title/Systemd/User).
|
||||||
|
|
||||||
|
Note: The `-s` command is run as a *child process* of dwl, which means that it does not have the ability to affect the environment of dwl or of any processes that it spawns. If you need to set environment variables that affect the entire dwl session, these must be set prior to running dwl. For example, Wayland requires a valid `XDG_RUNTIME_DIR`, which is usually set up by a session manager such as `elogind` or `systemd-logind`. If your system doesn't do this automatically, you will need to configure it prior to launching `dwl`, e.g.:
|
||||||
|
|
||||||
|
export XDG_RUNTIME_DIR=/tmp/xdg-runtime-$(id -u)
|
||||||
|
mkdir -p $XDG_RUNTIME_DIR
|
||||||
|
dwl
|
||||||
|
|
||||||
|
### Status information
|
||||||
|
|
||||||
|
Information about selected layouts, current window title, and selected/occupied/urgent tags is written to the stdin of the `-s` command (see the `printstatus()` function for details). This information can be used to populate an external status bar with a script that parses the information. Failing to read this information will cause dwl to block, so if you do want to run a startup command that does not consume the status information, you can close standard input with the `<&-` shell redirection, for example:
|
||||||
|
|
||||||
|
dwl -s 'foot --server <&-'
|
||||||
|
|
||||||
|
If your startup command is a shell script, you can achieve the same inside the script with the line
|
||||||
|
|
||||||
|
exec <&-
|
||||||
|
|
||||||
|
Existing dwl-specific status bars and dwl-specific scripts for other status bars include:
|
||||||
|
- [somebar](https://sr.ht/~raphi/somebar/) status bar designed for dwl
|
||||||
|
- [dtaobarv2.sh](https://cdn.discordapp.com/attachments/792078050024095745/862428883423723560/dtaobarv2.sh) for use with [dtao](https://github.com/djpohly/dtao) (See "Pinned Messages" on the "customizations" channel of the [dwl Discord server](https://discord.gg/jJxZnrGPWN) for details.)
|
||||||
|
- [dwlbar.sh](https://cdn.discordapp.com/attachments/792078050024095745/810926218529472592/dwlbar.sh) for use with [waybar](https://github.com/Alexays/Waybar) (See "Pinned Messages" on the "customizations" channel of the [dwl Discord server](https://discord.gg/jJxZnrGPWN) for details.)
|
||||||
|
- [waybar-dwl](https://codeberg.org/fauxmight/waybar-dwl.git) for use with [waybar](https://github.com/Alexays/Waybar)
|
||||||
|
- [dwl-tags.sh](https://codeberg.org/novakane/yambar/src/branch/master/examples/scripts/dwl-tags.sh) for use with [yambar](https://codeberg.org/dnkl/yambar)
|
||||||
|
- [waybar-dwl.sh](https://gitee.com/guyuming76/personal/tree/dwl/gentoo/waybar-dwl) for use with [waybar](https://github.com/Alexays/Waybar) (ACCESS TO THIS SCRIPT REQUIRES gitee.com LOGIN!)
|
||||||
|
|
||||||
|
## Replacements for X applications
|
||||||
|
|
||||||
|
You can find a [list of Wayland applications on the sway wiki](https://github.com/swaywm/sway/wiki/i3-Migration-Guide).
|
||||||
|
|
||||||
|
## IRC channel
|
||||||
|
|
||||||
|
dwl's IRC channel is #dwl on irc.libera.chat.
|
||||||
|
|
||||||
|
## Acknowledgements
|
||||||
|
|
||||||
|
dwl began by extending the TinyWL example provided (CC0) by the sway/wlroots developers. This was made possible in many cases by looking at how sway accomplished something, then trying to do the same in as suckless a way as possible.
|
||||||
|
|
||||||
|
Many thanks to suckless.org and the dwm developers and community for the inspiration, and to the various contributors to the project, including:
|
||||||
|
|
||||||
|
- Alexander Courtis for the XWayland implementation
|
||||||
|
- Guido Cella for the layer-shell protocol implementation, patch maintenance, and for helping to keep the project running
|
||||||
|
- Stivvo for output management and fullscreen support, and patch maintenance
|
278
dwl/client.h
Normal file
278
dwl/client.h
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
/*
|
||||||
|
* Attempt to consolidate unavoidable suck into one file, away from dwl.c. This
|
||||||
|
* file is not meant to be pretty. We use a .h file with static inline
|
||||||
|
* functions instead of a separate .c module, or function pointers like sway, so
|
||||||
|
* that they will simply compile out if the chosen #defines leave them unused.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Leave these functions first; they're used in the others */
|
||||||
|
static inline int
|
||||||
|
client_is_x11(Client *c)
|
||||||
|
{
|
||||||
|
#ifdef XWAYLAND
|
||||||
|
return c->type == X11Managed || c->type == X11Unmanaged;
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct wlr_surface *
|
||||||
|
client_surface(Client *c)
|
||||||
|
{
|
||||||
|
#ifdef XWAYLAND
|
||||||
|
if (client_is_x11(c))
|
||||||
|
return c->surface.xwayland->surface;
|
||||||
|
#endif
|
||||||
|
return c->surface.xdg->surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The others */
|
||||||
|
static inline void
|
||||||
|
client_activate_surface(struct wlr_surface *s, int activated)
|
||||||
|
{
|
||||||
|
struct wlr_xdg_surface *surface;
|
||||||
|
#ifdef XWAYLAND
|
||||||
|
struct wlr_xwayland_surface *xsurface;
|
||||||
|
if (wlr_surface_is_xwayland_surface(s)
|
||||||
|
&& (xsurface = wlr_xwayland_surface_from_wlr_surface(s))) {
|
||||||
|
wlr_xwayland_surface_activate(xsurface, activated);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (wlr_surface_is_xdg_surface(s)
|
||||||
|
&& (surface = wlr_xdg_surface_from_wlr_surface(s))
|
||||||
|
&& surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL)
|
||||||
|
wlr_xdg_toplevel_set_activated(surface, activated);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
client_for_each_surface(Client *c, wlr_surface_iterator_func_t fn, void *data)
|
||||||
|
{
|
||||||
|
wlr_surface_for_each_surface(client_surface(c), fn, data);
|
||||||
|
#ifdef XWAYLAND
|
||||||
|
if (client_is_x11(c))
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
wlr_xdg_surface_for_each_popup_surface(c->surface.xdg, fn, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const char *
|
||||||
|
client_get_appid(Client *c)
|
||||||
|
{
|
||||||
|
#ifdef XWAYLAND
|
||||||
|
if (client_is_x11(c))
|
||||||
|
return c->surface.xwayland->class;
|
||||||
|
#endif
|
||||||
|
return c->surface.xdg->toplevel->app_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
client_get_geometry(Client *c, struct wlr_box *geom)
|
||||||
|
{
|
||||||
|
#ifdef XWAYLAND
|
||||||
|
if (client_is_x11(c)) {
|
||||||
|
geom->x = c->surface.xwayland->x;
|
||||||
|
geom->y = c->surface.xwayland->y;
|
||||||
|
geom->width = c->surface.xwayland->width;
|
||||||
|
geom->height = c->surface.xwayland->height;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
wlr_xdg_surface_get_geometry(c->surface.xdg, geom);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
client_get_size_hints(Client *c, struct wlr_box *max, struct wlr_box *min)
|
||||||
|
{
|
||||||
|
struct wlr_xdg_toplevel *toplevel;
|
||||||
|
struct wlr_xdg_toplevel_state *state;
|
||||||
|
#ifdef XWAYLAND
|
||||||
|
if (client_is_x11(c)) {
|
||||||
|
struct wlr_xwayland_surface_size_hints *size_hints;
|
||||||
|
size_hints = c->surface.xwayland->size_hints;
|
||||||
|
if (size_hints) {
|
||||||
|
max->width = size_hints->max_width;
|
||||||
|
max->height = size_hints->max_height;
|
||||||
|
min->width = size_hints->min_width;
|
||||||
|
min->height = size_hints->min_height;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
toplevel = c->surface.xdg->toplevel;
|
||||||
|
state = &toplevel->current;
|
||||||
|
max->width = state->max_width;
|
||||||
|
max->height = state->max_height;
|
||||||
|
min->width = state->min_width;
|
||||||
|
min->height = state->min_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline const char *
|
||||||
|
client_get_title(Client *c)
|
||||||
|
{
|
||||||
|
#ifdef XWAYLAND
|
||||||
|
if (client_is_x11(c))
|
||||||
|
return c->surface.xwayland->title;
|
||||||
|
#endif
|
||||||
|
return c->surface.xdg->toplevel->title;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
client_is_float_type(Client *c)
|
||||||
|
{
|
||||||
|
struct wlr_box min = {0}, max = {0};
|
||||||
|
client_get_size_hints(c, &max, &min);
|
||||||
|
|
||||||
|
#ifdef XWAYLAND
|
||||||
|
if (client_is_x11(c)) {
|
||||||
|
struct wlr_xwayland_surface *surface = c->surface.xwayland;
|
||||||
|
if (surface->modal)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < surface->window_type_len; i++)
|
||||||
|
if (surface->window_type[i] == netatom[NetWMWindowTypeDialog]
|
||||||
|
|| surface->window_type[i] == netatom[NetWMWindowTypeSplash]
|
||||||
|
|| surface->window_type[i] == netatom[NetWMWindowTypeToolbar]
|
||||||
|
|| surface->window_type[i] == netatom[NetWMWindowTypeUtility])
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return ((min.width > 0 || min.height > 0 || max.width > 0 || max.height > 0)
|
||||||
|
&& (min.width == max.width || min.height == max.height))
|
||||||
|
|| c->surface.xwayland->parent;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ((min.width > 0 || min.height > 0 || max.width > 0 || max.height > 0)
|
||||||
|
&& (min.width == max.width || min.height == max.height))
|
||||||
|
|| c->surface.xdg->toplevel->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
client_wants_fullscreen(Client *c)
|
||||||
|
{
|
||||||
|
#ifdef XWAYLAND
|
||||||
|
if (client_is_x11(c))
|
||||||
|
return c->surface.xwayland->fullscreen;
|
||||||
|
#endif
|
||||||
|
return c->surface.xdg->toplevel->requested.fullscreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
client_is_unmanaged(Client *c)
|
||||||
|
{
|
||||||
|
#ifdef XWAYLAND
|
||||||
|
return c->type == X11Unmanaged;
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
client_send_close(Client *c)
|
||||||
|
{
|
||||||
|
#ifdef XWAYLAND
|
||||||
|
if (client_is_x11(c)) {
|
||||||
|
wlr_xwayland_surface_close(c->surface.xwayland);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
wlr_xdg_toplevel_send_close(c->surface.xdg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
client_set_fullscreen(Client *c, int fullscreen)
|
||||||
|
{
|
||||||
|
#ifdef XWAYLAND
|
||||||
|
if (client_is_x11(c)) {
|
||||||
|
wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, fullscreen);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
wlr_xdg_toplevel_set_fullscreen(c->surface.xdg, fullscreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t
|
||||||
|
client_set_size(Client *c, uint32_t width, uint32_t height)
|
||||||
|
{
|
||||||
|
#ifdef XWAYLAND
|
||||||
|
if (client_is_x11(c)) {
|
||||||
|
wlr_xwayland_surface_configure(c->surface.xwayland,
|
||||||
|
c->geom.x, c->geom.y, width, height);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return wlr_xdg_toplevel_set_size(c->surface.xdg, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
client_set_tiled(Client *c, uint32_t edges)
|
||||||
|
{
|
||||||
|
#ifdef XWAYLAND
|
||||||
|
if (client_is_x11(c))
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
wlr_xdg_toplevel_set_tiled(c->surface.xdg, edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct wlr_surface *
|
||||||
|
client_surface_at(Client *c, double cx, double cy, double *sx, double *sy)
|
||||||
|
{
|
||||||
|
#ifdef XWAYLAND
|
||||||
|
if (client_is_x11(c))
|
||||||
|
return wlr_surface_surface_at(c->surface.xwayland->surface,
|
||||||
|
cx, cy, sx, sy);
|
||||||
|
#endif
|
||||||
|
return wlr_xdg_surface_surface_at(c->surface.xdg, cx, cy, sx, sy);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
client_restack_surface(Client *c)
|
||||||
|
{
|
||||||
|
#ifdef XWAYLAND
|
||||||
|
if (client_is_x11(c))
|
||||||
|
wlr_xwayland_surface_restack(c->surface.xwayland, NULL,
|
||||||
|
XCB_STACK_MODE_ABOVE);
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline Client *
|
||||||
|
client_from_wlr_surface(struct wlr_surface *s)
|
||||||
|
{
|
||||||
|
struct wlr_xdg_surface *surface;
|
||||||
|
|
||||||
|
#ifdef XWAYLAND
|
||||||
|
struct wlr_xwayland_surface *xsurface;
|
||||||
|
if (s->role_data && wlr_surface_is_xwayland_surface(s)
|
||||||
|
&& (xsurface = wlr_xwayland_surface_from_wlr_surface(s)))
|
||||||
|
return xsurface->data;
|
||||||
|
#endif
|
||||||
|
if (s->role_data && wlr_surface_is_xdg_surface(s)
|
||||||
|
&& (surface = wlr_xdg_surface_from_wlr_surface(s))
|
||||||
|
&& surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL)
|
||||||
|
return surface->data;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *
|
||||||
|
toplevel_from_popup(struct wlr_xdg_popup *popup)
|
||||||
|
{
|
||||||
|
struct wlr_xdg_surface *surface = popup->base;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
switch (surface->role) {
|
||||||
|
case WLR_XDG_SURFACE_ROLE_POPUP:
|
||||||
|
if (wlr_surface_is_layer_surface(surface->popup->parent))
|
||||||
|
return wlr_layer_surface_v1_from_wlr_surface(surface->popup->parent)->data;
|
||||||
|
else if (!wlr_surface_is_xdg_surface(surface->popup->parent))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
surface = wlr_xdg_surface_from_wlr_surface(surface->popup->parent);
|
||||||
|
break;
|
||||||
|
case WLR_XDG_SURFACE_ROLE_TOPLEVEL:
|
||||||
|
return surface->data;
|
||||||
|
case WLR_XDG_SURFACE_ROLE_NONE:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
162
dwl/config.def.h
Normal file
162
dwl/config.def.h
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
/* appearance */
|
||||||
|
static const int sloppyfocus = 1; /* focus follows mouse */
|
||||||
|
static const unsigned int borderpx = 1; /* border pixel of windows */
|
||||||
|
static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */
|
||||||
|
static const float rootcolor[] = {0.3, 0.3, 0.3, 1.0};
|
||||||
|
static const float bordercolor[] = {0.5, 0.5, 0.5, 1.0};
|
||||||
|
static const float focuscolor[] = {1.0, 0.0, 0.0, 1.0};
|
||||||
|
|
||||||
|
/* cursor warping */
|
||||||
|
static const bool cursor_warp = true;
|
||||||
|
|
||||||
|
/* tagging */
|
||||||
|
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
|
||||||
|
|
||||||
|
static const Rule rules[] = {
|
||||||
|
/* app_id title tags mask isfloating monitor */
|
||||||
|
/* examples:
|
||||||
|
{ "Gimp", NULL, 0, 1, -1 },
|
||||||
|
*/
|
||||||
|
{ "firefox", NULL, 1 << 8, 0, -1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* layout(s) */
|
||||||
|
static const Layout layouts[] = {
|
||||||
|
/* symbol arrange function */
|
||||||
|
{ "[]=", tile },
|
||||||
|
{ "><>", NULL }, /* no layout function means floating behavior */
|
||||||
|
{ "[M]", monocle },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* monitors
|
||||||
|
* The order in which monitors are defined determines their position.
|
||||||
|
* Non-configured monitors are always added to the left. */
|
||||||
|
static const MonitorRule monrules[] = {
|
||||||
|
/* name mfact nmaster scale layout rotate/reflect x y resx resy rate adaptive*/
|
||||||
|
/* example of a HiDPI laptop monitor at 120Hz:
|
||||||
|
{ "eDP-1", 0.5, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, 0, 0, 0, 0, 120.000, 1},
|
||||||
|
*/
|
||||||
|
/* defaults */
|
||||||
|
{ NULL, 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, 0, 0, 0, 0, 0, 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* keyboard */
|
||||||
|
static const struct xkb_rule_names xkb_rules = {
|
||||||
|
/* can specify fields: rules, model, layout, variant, options */
|
||||||
|
/* example:
|
||||||
|
.options = "ctrl:nocaps",
|
||||||
|
*/
|
||||||
|
.options = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int repeat_rate = 25;
|
||||||
|
static const int repeat_delay = 600;
|
||||||
|
|
||||||
|
/* Trackpad */
|
||||||
|
static const int tap_to_click = 1;
|
||||||
|
static const int tap_and_drag = 1;
|
||||||
|
static const int drag_lock = 1;
|
||||||
|
static const int natural_scrolling = 0;
|
||||||
|
static const int cursor_timeout = 5;
|
||||||
|
static const int disable_while_typing = 1;
|
||||||
|
static const int left_handed = 0;
|
||||||
|
static const int middle_button_emulation = 0;
|
||||||
|
/* You can choose between:
|
||||||
|
LIBINPUT_CONFIG_SCROLL_NO_SCROLL
|
||||||
|
LIBINPUT_CONFIG_SCROLL_2FG
|
||||||
|
LIBINPUT_CONFIG_SCROLL_EDGE
|
||||||
|
LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN
|
||||||
|
*/
|
||||||
|
static const enum libinput_config_scroll_method scroll_method = LIBINPUT_CONFIG_SCROLL_2FG;
|
||||||
|
|
||||||
|
/* You can choose between:
|
||||||
|
LIBINPUT_CONFIG_CLICK_METHOD_NONE
|
||||||
|
LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS
|
||||||
|
LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER
|
||||||
|
*/
|
||||||
|
static const enum libinput_config_click_method click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
|
||||||
|
|
||||||
|
/* You can choose between:
|
||||||
|
LIBINPUT_CONFIG_SEND_EVENTS_ENABLED
|
||||||
|
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED
|
||||||
|
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE
|
||||||
|
*/
|
||||||
|
static const uint32_t send_events_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
|
||||||
|
|
||||||
|
/* You can choose between:
|
||||||
|
LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT
|
||||||
|
LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE
|
||||||
|
*/
|
||||||
|
static const enum libinput_config_accel_profile accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
|
||||||
|
static const double accel_speed = 0.0;
|
||||||
|
|
||||||
|
/* Autostart */
|
||||||
|
static const char *const autostart[] = {
|
||||||
|
"sh", "-c", "swaybg --image /xap/local/background", NULL,
|
||||||
|
NULL /* terminate */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* If you want to use the windows key change this to WLR_MODIFIER_LOGO */
|
||||||
|
#define MODKEY WLR_MODIFIER_ALT
|
||||||
|
#define TAGKEYS(KEY,SKEY,TAG) \
|
||||||
|
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \
|
||||||
|
{ MODKEY|WLR_MODIFIER_CTRL, KEY, toggleview, {.ui = 1 << TAG} }, \
|
||||||
|
{ MODKEY|WLR_MODIFIER_SHIFT, SKEY, tag, {.ui = 1 << TAG} }, \
|
||||||
|
{ MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,SKEY,toggletag, {.ui = 1 << TAG} }
|
||||||
|
|
||||||
|
/* helper for spawning shell commands in the pre dwm-5.0 fashion */
|
||||||
|
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
|
||||||
|
|
||||||
|
/* commands */
|
||||||
|
static const char *termcmd[] = { "alacritty", NULL };
|
||||||
|
static const char *menucmd[] = { "bemenu-run", NULL };
|
||||||
|
|
||||||
|
static const Key keys[] = {
|
||||||
|
/* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */
|
||||||
|
/* modifier key function argument */
|
||||||
|
{ MODKEY, XKB_KEY_p, spawn, {.v = menucmd} },
|
||||||
|
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} },
|
||||||
|
{ MODKEY, XKB_KEY_j, focusstack, {.i = +1} },
|
||||||
|
{ MODKEY, XKB_KEY_k, focusstack, {.i = -1} },
|
||||||
|
{ MODKEY, XKB_KEY_i, incnmaster, {.i = +1} },
|
||||||
|
{ MODKEY, XKB_KEY_d, incnmaster, {.i = -1} },
|
||||||
|
{ MODKEY, XKB_KEY_h, setmfact, {.f = -0.05} },
|
||||||
|
{ MODKEY, XKB_KEY_l, setmfact, {.f = +0.05} },
|
||||||
|
{ MODKEY, XKB_KEY_Return, zoom, {0} },
|
||||||
|
{ MODKEY, XKB_KEY_Tab, view, {0} },
|
||||||
|
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_C, killclient, {0} },
|
||||||
|
{ MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} },
|
||||||
|
{ MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} },
|
||||||
|
{ MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} },
|
||||||
|
{ MODKEY, XKB_KEY_space, setlayout, {0} },
|
||||||
|
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
|
||||||
|
{ MODKEY, XKB_KEY_e, togglefullscreen, {0} },
|
||||||
|
{ MODKEY, XKB_KEY_0, view, {.ui = ~0} },
|
||||||
|
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} },
|
||||||
|
{ MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} },
|
||||||
|
{ MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} },
|
||||||
|
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} },
|
||||||
|
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmon, {.i = WLR_DIRECTION_RIGHT} },
|
||||||
|
TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0),
|
||||||
|
TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1),
|
||||||
|
TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2),
|
||||||
|
TAGKEYS( XKB_KEY_4, XKB_KEY_dollar, 3),
|
||||||
|
TAGKEYS( XKB_KEY_5, XKB_KEY_percent, 4),
|
||||||
|
TAGKEYS( XKB_KEY_6, XKB_KEY_asciicircum, 5),
|
||||||
|
TAGKEYS( XKB_KEY_7, XKB_KEY_ampersand, 6),
|
||||||
|
TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7),
|
||||||
|
TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8),
|
||||||
|
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Q, quit, {0} },
|
||||||
|
|
||||||
|
/* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */
|
||||||
|
{ WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} },
|
||||||
|
#define CHVT(n) { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_XF86Switch_VT_##n, chvt, {.ui = (n)} }
|
||||||
|
CHVT(1), CHVT(2), CHVT(3), CHVT(4), CHVT(5), CHVT(6),
|
||||||
|
CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const Button buttons[] = {
|
||||||
|
{ MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} },
|
||||||
|
{ MODKEY, BTN_MIDDLE, togglefloating, {0} },
|
||||||
|
{ MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} },
|
||||||
|
};
|
190
dwl/config.h
Normal file
190
dwl/config.h
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
#include <X11/XF86keysym.h>
|
||||||
|
/* appearance */
|
||||||
|
static const int sloppyfocus = 1; /* focus follows mouse */
|
||||||
|
static const unsigned int borderpx = 1; /* border pixel of windows */
|
||||||
|
static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */
|
||||||
|
static const float rootcolor[] = {0.3, 0.3, 0.3, 1.0};
|
||||||
|
static const float bordercolor[] = {0.5, 0.5, 0.5, 1.0};
|
||||||
|
static const float focuscolor[] = {1.0, 0.0, 0.0, 1.0};
|
||||||
|
|
||||||
|
/* cursor warping */
|
||||||
|
static const bool cursor_warp = true;
|
||||||
|
|
||||||
|
/* tagging */
|
||||||
|
static const char *tags[] = { "1", "2", "3", "4", "5" };
|
||||||
|
|
||||||
|
static const Rule rules[] = {
|
||||||
|
/* app_id title tags mask isfloating monitor */
|
||||||
|
/* examples:
|
||||||
|
{ "Gimp", NULL, 0, 1, -1 },
|
||||||
|
*/
|
||||||
|
// { "firefox", NULL, 1 << 8, 0, -1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* layout(s) */
|
||||||
|
static const Layout layouts[] = {
|
||||||
|
/* symbol arrange function */
|
||||||
|
{ "[]=", tile },
|
||||||
|
{ "><>", NULL }, /* no layout function means floating behavior */
|
||||||
|
{ "[M]", monocle },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* monitors */
|
||||||
|
static const MonitorRule monrules[] = {
|
||||||
|
/* name mfact nmaster scale layout rotate/reflect x y resx resy rate adaptive*/
|
||||||
|
/* example of a HiDPI laptop monitor at 120Hz:
|
||||||
|
{ "eDP-1", 0.5, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, 0, 0, 0, 0, 120.000, 1},
|
||||||
|
*/
|
||||||
|
/* defaults */
|
||||||
|
{ "eDP-1", 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, 1920, 0, 0, 0, 60, 1},
|
||||||
|
{ "HDMI-A-1", 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, 0, 0, 0, 0, 74.973, 1},
|
||||||
|
{ NULL, 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, 0, 0, 0, 0, 0, 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* keyboard */
|
||||||
|
static const struct xkb_rule_names xkb_rules = {
|
||||||
|
/* can specify fields: rules, model, layout, variant, options */
|
||||||
|
/* example:
|
||||||
|
.options = "ctrl:nocaps",
|
||||||
|
*/
|
||||||
|
.options = "caps:escape",
|
||||||
|
.options = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int repeat_rate = 30;
|
||||||
|
static const int repeat_delay = 300;
|
||||||
|
|
||||||
|
/* Trackpad */
|
||||||
|
static const int tap_to_click = 1;
|
||||||
|
static const int tap_and_drag = 1;
|
||||||
|
static const int drag_lock = 1;
|
||||||
|
static const int natural_scrolling = 0;
|
||||||
|
static const int cursor_timeout = 5;
|
||||||
|
static const int disable_while_typing = 1;
|
||||||
|
static const int left_handed = 0;
|
||||||
|
static const int middle_button_emulation = 0;
|
||||||
|
/* You can choose between:
|
||||||
|
LIBINPUT_CONFIG_SCROLL_NO_SCROLL
|
||||||
|
LIBINPUT_CONFIG_SCROLL_2FG
|
||||||
|
LIBINPUT_CONFIG_SCROLL_EDGE
|
||||||
|
LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN
|
||||||
|
*/
|
||||||
|
static const enum libinput_config_scroll_method scroll_method = LIBINPUT_CONFIG_SCROLL_2FG;
|
||||||
|
|
||||||
|
/* You can choose between:
|
||||||
|
LIBINPUT_CONFIG_CLICK_METHOD_NONE
|
||||||
|
LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS
|
||||||
|
LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER
|
||||||
|
*/
|
||||||
|
static const enum libinput_config_click_method click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
|
||||||
|
|
||||||
|
/* You can choose between:
|
||||||
|
LIBINPUT_CONFIG_SEND_EVENTS_ENABLED
|
||||||
|
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED
|
||||||
|
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE
|
||||||
|
*/
|
||||||
|
static const uint32_t send_events_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
|
||||||
|
|
||||||
|
/* You can choose between:
|
||||||
|
LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT
|
||||||
|
LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE
|
||||||
|
*/
|
||||||
|
static const enum libinput_config_accel_profile accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
|
||||||
|
static const double accel_speed = 0.0;
|
||||||
|
|
||||||
|
static const char bemenufont[] = "JetBrains Mono Nerd Font 14";
|
||||||
|
|
||||||
|
/* Autostart */
|
||||||
|
static const char *const autostart[] = {
|
||||||
|
"sh", "-c", "swaybg --image ~/.wallpaper.jpg", NULL,
|
||||||
|
"sh", "-c", "someblocks", NULL,
|
||||||
|
"sh", "-c", "dunst", NULL,
|
||||||
|
NULL /* terminate */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* If you want to use the windows key change this to WLR_MODIFIER_LOGO */
|
||||||
|
#define MODKEY WLR_MODIFIER_ALT
|
||||||
|
#define TAGKEYS(KEY,SKEY,TAG) \
|
||||||
|
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \
|
||||||
|
{ MODKEY|WLR_MODIFIER_CTRL, KEY, toggleview, {.ui = 1 << TAG} }, \
|
||||||
|
{ MODKEY|WLR_MODIFIER_SHIFT, SKEY, tag, {.ui = 1 << TAG} }, \
|
||||||
|
{ MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,SKEY,toggletag, {.ui = 1 << TAG} }
|
||||||
|
|
||||||
|
/* helper for spawning shell commands in the pre dwm-5.0 fashion */
|
||||||
|
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
|
||||||
|
|
||||||
|
/* commands */
|
||||||
|
static const char *termcmd[] = { "alacritty", NULL };
|
||||||
|
static const char *menucmd[] = { "bemenu_recent", "--fn", bemenufont, NULL };
|
||||||
|
static const char *volup[] = { "pactl", "set-sink-volume", "@DEFAULT_SINK@", "+5%", NULL };
|
||||||
|
static const char *voldown[] = { "pactl", "set-sink-volume", "@DEFAULT_SINK@", "-5%", NULL };
|
||||||
|
static const char *volmute[] = { "pactl", "set-sink-mute", "@DEFAULT_SINK@", "toggle", NULL };
|
||||||
|
static const char *micmute[] = { "pactl", "set-source-mute", "@DEFAULT_SOURCE@", "toggle", NULL };
|
||||||
|
|
||||||
|
static const char *brightnessup[] = { "light", "-A", "5.00", NULL };
|
||||||
|
static const char *brightnessdown[] = { "light", "-U", "5.00", NULL };
|
||||||
|
static const char *playpause[] = { "playerctl", "play-pause", NULL };
|
||||||
|
static const char *playnext[] = { "playerctl", "next", NULL };
|
||||||
|
static const char *playprev[] = { "playerctl", "previous", NULL };
|
||||||
|
|
||||||
|
static const Key keys[] = {
|
||||||
|
/* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */
|
||||||
|
/* modifier key function argument */
|
||||||
|
{ MODKEY, XKB_KEY_p, spawn, {.v = menucmd} },
|
||||||
|
{ MODKEY, XKB_KEY_Return, spawn, {.v = termcmd} },
|
||||||
|
{ MODKEY, XKB_KEY_n, focusstack, {.i = +1} },
|
||||||
|
{ MODKEY, XKB_KEY_e, focusstack, {.i = -1} },
|
||||||
|
{ MODKEY, XKB_KEY_minus, incnmaster, {.i = +1} },
|
||||||
|
{ MODKEY, XKB_KEY_equal, incnmaster, {.i = -1} },
|
||||||
|
{ MODKEY, XKB_KEY_h, setmfact, {.f = -0.05} },
|
||||||
|
{ MODKEY, XKB_KEY_i, setmfact, {.f = +0.05} },
|
||||||
|
{ MODKEY, XKB_KEY_grave, zoom, {0} },
|
||||||
|
{ MODKEY, XKB_KEY_Tab, view, {0} },
|
||||||
|
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_C, killclient, {0} },
|
||||||
|
{ MODKEY, XKB_KEY_q, killclient, {0} },
|
||||||
|
{ MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} },
|
||||||
|
{ MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} },
|
||||||
|
{ MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} },
|
||||||
|
{ MODKEY, XKB_KEY_space, setlayout, {0} },
|
||||||
|
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
|
||||||
|
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_E, togglefullscreen, {0} },
|
||||||
|
{ MODKEY, XKB_KEY_0, view, {.ui = ~0} },
|
||||||
|
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} },
|
||||||
|
{ MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} },
|
||||||
|
{ MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} },
|
||||||
|
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} },
|
||||||
|
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmon, {.i = WLR_DIRECTION_RIGHT} },
|
||||||
|
TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0),
|
||||||
|
TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1),
|
||||||
|
TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2),
|
||||||
|
TAGKEYS( XKB_KEY_4, XKB_KEY_dollar, 3),
|
||||||
|
TAGKEYS( XKB_KEY_5, XKB_KEY_percent, 4),
|
||||||
|
TAGKEYS( XKB_KEY_6, XKB_KEY_asciicircum, 5),
|
||||||
|
TAGKEYS( XKB_KEY_7, XKB_KEY_ampersand, 6),
|
||||||
|
TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7),
|
||||||
|
TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8),
|
||||||
|
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Q, quit, {0} },
|
||||||
|
|
||||||
|
{ 0, XF86XK_AudioPause, spawn, {.v = playpause } },
|
||||||
|
{ 0, XF86XK_AudioPlay, spawn, {.v = playpause } },
|
||||||
|
{ 0, XF86XK_AudioNext, spawn, {.v = playnext } },
|
||||||
|
{ 0, XF86XK_AudioPrev, spawn, {.v = playprev } },
|
||||||
|
{ 0, XF86XK_AudioRaiseVolume, spawn, {.v = volup } },
|
||||||
|
{ 0, XF86XK_AudioLowerVolume, spawn, {.v = voldown } },
|
||||||
|
{ 0, XF86XK_AudioMute, spawn, {.v = volmute } },
|
||||||
|
{ 0, XF86XK_AudioMicMute, spawn, {.v = micmute } },
|
||||||
|
{ 0, XF86XK_MonBrightnessUp, spawn, {.v = brightnessup } },
|
||||||
|
{ 0, XF86XK_MonBrightnessDown, spawn, {.v = brightnessdown } },
|
||||||
|
|
||||||
|
/* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */
|
||||||
|
{ WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} },
|
||||||
|
#define CHVT(n) { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_XF86Switch_VT_##n, chvt, {.ui = (n)} }
|
||||||
|
CHVT(1), CHVT(2), CHVT(3), CHVT(4), CHVT(5), CHVT(6),
|
||||||
|
CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const Button buttons[] = {
|
||||||
|
{ MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} },
|
||||||
|
{ MODKEY, BTN_MIDDLE, togglefloating, {0} },
|
||||||
|
{ MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} },
|
||||||
|
};
|
15
dwl/config.mk
Normal file
15
dwl/config.mk
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
_VERSION = 0.3.1
|
||||||
|
VERSION = `./generate-version.sh $(_VERSION)`
|
||||||
|
|
||||||
|
# paths
|
||||||
|
PREFIX = /usr/local
|
||||||
|
MANDIR = $(PREFIX)/share/man
|
||||||
|
|
||||||
|
# Compile flags that can be used
|
||||||
|
#CFLAGS = -pedantic -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-unused-function -Wno-unused-variable -Wno-unused-result -Wdeclaration-after-statement
|
||||||
|
|
||||||
|
# XWAYLAND =
|
||||||
|
# XLIBS =
|
||||||
|
# Uncomment to build XWayland support
|
||||||
|
XWAYLAND = -DXWAYLAND
|
||||||
|
XLIBS = xcb
|
151
dwl/dwl.1
Normal file
151
dwl/dwl.1
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
.Dd January 8, 2021
|
||||||
|
.Dt DWL 1
|
||||||
|
.Os
|
||||||
|
.Sh NAME
|
||||||
|
.Nm dwl
|
||||||
|
.Nd dwm for Wayland
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm
|
||||||
|
.Op Fl v
|
||||||
|
.Op Fl s Ar startup command
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
.Nm
|
||||||
|
is a Wayland compositor based on wlroots.
|
||||||
|
It is intended to fill the same space in the Wayland world that
|
||||||
|
.Nm dwm
|
||||||
|
does for X11.
|
||||||
|
.Pp
|
||||||
|
When given the
|
||||||
|
.Fl v
|
||||||
|
option,
|
||||||
|
.Nm
|
||||||
|
writes its name and version to standard error and exits unsuccessfully.
|
||||||
|
.Pp
|
||||||
|
When given the
|
||||||
|
.Fl s
|
||||||
|
option,
|
||||||
|
.Nm
|
||||||
|
starts a shell process running
|
||||||
|
.Ar command
|
||||||
|
when starting.
|
||||||
|
When stopping, it sends
|
||||||
|
.Dv SIGTERM
|
||||||
|
to the child process and waits for it to exit.
|
||||||
|
.Pp
|
||||||
|
Users are encouraged to customize
|
||||||
|
.Nm
|
||||||
|
by editing the sources, in particular
|
||||||
|
.Pa config.h .
|
||||||
|
The default key bindings are as follows:
|
||||||
|
.Bl -tag -width 20n -offset indent -compact
|
||||||
|
.It Mod-[1-9]
|
||||||
|
Show only all windows with a tag.
|
||||||
|
.It Mod-Ctrl-[1-9]
|
||||||
|
Show all windows with a tag.
|
||||||
|
.It Mod-Shift-[1-9]
|
||||||
|
Move window to a single tag.
|
||||||
|
.It Mod-Ctrl-Shift-[1-9]
|
||||||
|
Toggle tag for window.
|
||||||
|
.It Mod-p
|
||||||
|
Spawn
|
||||||
|
.Nm bemenu-run .
|
||||||
|
.It Mod-Shift-Return
|
||||||
|
Spawn
|
||||||
|
.Nm alacritty .
|
||||||
|
.It Mod-[jk]
|
||||||
|
Move focus down/up the stack.
|
||||||
|
.It Mod-[id]
|
||||||
|
Increase/decrease number of windows in master area.
|
||||||
|
.It Mod-[hl]
|
||||||
|
Decrease/increase master area.
|
||||||
|
.It Mod-Return
|
||||||
|
Move window on top of stack or switch top of stack with second window.
|
||||||
|
.It Mod-Tab
|
||||||
|
Show only all windows with previous tag.
|
||||||
|
.It Mod-Shift-c
|
||||||
|
Close window.
|
||||||
|
.It Mod-t
|
||||||
|
Switch to tabbed layout.
|
||||||
|
.It Mod-f
|
||||||
|
Switch to floating layout.
|
||||||
|
.It Mod-m
|
||||||
|
Switch to monocle layout.
|
||||||
|
.It Mod-Space
|
||||||
|
Switch to previous layout.
|
||||||
|
.It Mod-Shift-Space
|
||||||
|
Toggle floating state of window.
|
||||||
|
.It Mod-e
|
||||||
|
Toggle fullscreen state of window.
|
||||||
|
.It Mod-0
|
||||||
|
Show all windows.
|
||||||
|
.It Mod-Shift-0
|
||||||
|
Set all tags for window.
|
||||||
|
.It Mod-,
|
||||||
|
Move focus to previous monitor.
|
||||||
|
.It Mod-.
|
||||||
|
Move focus to next monitor.
|
||||||
|
.It Mod-Shift-,
|
||||||
|
Move window to previous monitor.
|
||||||
|
.It Mod-Shift-.
|
||||||
|
Move window to next monitor.
|
||||||
|
.It Mod-Shift-q
|
||||||
|
Quit
|
||||||
|
.Nm .
|
||||||
|
.El
|
||||||
|
These might differ depending on your keyboard layout.
|
||||||
|
.Sh ENVIRONMENT
|
||||||
|
These environment variables are used by
|
||||||
|
.Nm :
|
||||||
|
.Bl -tag -width XDG_RUNTIME_DIR
|
||||||
|
.It Ev XDG_RUNTIME_DIR
|
||||||
|
A directory where temporary user files, such as the Wayland socket,
|
||||||
|
are stored.
|
||||||
|
.It Ev XDG_CONFIG_DIR
|
||||||
|
A directory containung configuration of various programs and
|
||||||
|
libraries, including libxkbcommon.
|
||||||
|
.It Ev DISPLAY , WAYLAND_DISPLAY , WAYLAND_SOCKET
|
||||||
|
Tell how to connect to an underlying X11 or Wayland server.
|
||||||
|
.It Ev WLR_*
|
||||||
|
Various variables specific to wlroots.
|
||||||
|
.It Ev XKB_* , XLOCALEDIR , XCOMPOSEFILE
|
||||||
|
Various variables specific to libxkbcommon.
|
||||||
|
.It Ev XCURSOR_PATH
|
||||||
|
List of directories to search for XCursor themes in.
|
||||||
|
.It Ev HOME
|
||||||
|
A directory where there are always dear files there for you.
|
||||||
|
Waiting for you to clean them up.
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
These are set by
|
||||||
|
.Nm :
|
||||||
|
.Bl -tag -width WAYLAND_DISPLAY
|
||||||
|
.It Ev WAYLAND_DISPLAY
|
||||||
|
Tell how to connect to
|
||||||
|
.Nm .
|
||||||
|
.It Ev DISPLAY
|
||||||
|
If using
|
||||||
|
.Nm Xwayland ,
|
||||||
|
tell how to connect to the
|
||||||
|
.Nm Xwayland
|
||||||
|
server.
|
||||||
|
.El
|
||||||
|
.Sh EXAMPLES
|
||||||
|
Start
|
||||||
|
.Nm
|
||||||
|
with s6 in the background:
|
||||||
|
.Dl dwl -s 's6-svscan <&-'
|
||||||
|
.Sh SEE ALSO
|
||||||
|
.Xr alacritty 1 ,
|
||||||
|
.Xr bemenu 1 ,
|
||||||
|
.Xr dwm 1 ,
|
||||||
|
.Xr xkeyboard-config 7
|
||||||
|
.Sh CAVEATS
|
||||||
|
The child process's standard input is connected with a pipe to
|
||||||
|
.Nm .
|
||||||
|
If the child process neither reads from the pipe nor closes its
|
||||||
|
standard input,
|
||||||
|
.Nm
|
||||||
|
will freeze after a while due to it blocking when writing to the full
|
||||||
|
pipe buffer.
|
||||||
|
.Sh BUGS
|
||||||
|
All of them.
|
13
dwl/generate-version.sh
Executable file
13
dwl/generate-version.sh
Executable file
@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if git tag --contains HEAD | grep -q $1; then
|
||||||
|
echo $1
|
||||||
|
else
|
||||||
|
branch="$(git rev-parse --abbrev-ref HEAD)"
|
||||||
|
commit="$(git rev-parse --short HEAD)"
|
||||||
|
if [ "${branch}" != "main" ]; then
|
||||||
|
echo $1-$branch-$commit
|
||||||
|
else
|
||||||
|
echo $1-$commit
|
||||||
|
fi
|
||||||
|
fi
|
23
dwl/patches/alwayscenter.patch
Normal file
23
dwl/patches/alwayscenter.patch
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
From 9173fdaf0d1eb853ff194ea11767ac15a0d13560 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Guido Cella <guido@guidocella.xyz>
|
||||||
|
Date: Sat, 17 Apr 2021 08:59:55 +0200
|
||||||
|
Subject: [PATCH] Center floating windows
|
||||||
|
|
||||||
|
Credits to Benjamin Chausse for fixing this with multiple monitors.
|
||||||
|
---
|
||||||
|
dwl.c | 2 ++
|
||||||
|
1 file changed, 2 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/dwl.c b/dwl.c
|
||||||
|
index 22287fb3..80c16459 100644
|
||||||
|
--- a/dwl.c
|
||||||
|
+++ b/dwl.c
|
||||||
|
@@ -467,6 +467,8 @@ applyrules(Client *c)
|
||||||
|
mon = m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+ c->geom.x = (mon->w.width - c->geom.width) / 2 + mon->m.x;
|
||||||
|
+ c->geom.y = (mon->w.height - c->geom.height) / 2 + mon->m.y;
|
||||||
|
wlr_scene_node_reparent(c->scene, layers[c->isfloating ? LyrFloat : LyrTile]);
|
||||||
|
setmon(c, mon, newtags);
|
||||||
|
}
|
203
dwl/patches/autostart-wlroots-next.patch
Normal file
203
dwl/patches/autostart-wlroots-next.patch
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
From 03d03691d3c17307bc04f2ec7e2491d48699821a Mon Sep 17 00:00:00 2001
|
||||||
|
From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?=
|
||||||
|
<leohdz172@protonmail.com>
|
||||||
|
Date: Wed, 9 Feb 2022 07:02:47 -0600
|
||||||
|
Subject: [PATCH] apply autostart patch from dwm
|
||||||
|
|
||||||
|
https://dwm.suckless.org/patches/cool_autostart/
|
||||||
|
---
|
||||||
|
config.def.h | 6 ++++
|
||||||
|
dwl.c | 99 +++++++++++++++++++++++++++++++++++++++-------------
|
||||||
|
2 files changed, 81 insertions(+), 24 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/config.def.h b/config.def.h
|
||||||
|
index 190b0da4..bfff6041 100644
|
||||||
|
--- a/config.def.h
|
||||||
|
+++ b/config.def.h
|
||||||
|
@@ -51,6 +51,12 @@ static const int repeat_delay = 600;
|
||||||
|
static const int tap_to_click = 1;
|
||||||
|
static const int natural_scrolling = 0;
|
||||||
|
|
||||||
|
+/* Autostart */
|
||||||
|
+static const char *const autostart[] = {
|
||||||
|
+ "sh", "-c", "swaybg --image /xap/local/background", NULL,
|
||||||
|
+ NULL /* terminate */
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
/* If you want to use the windows key change this to WLR_MODIFIER_LOGO */
|
||||||
|
#define MODKEY WLR_MODIFIER_ALT
|
||||||
|
#define TAGKEYS(KEY,SKEY,TAG) \
|
||||||
|
diff --git a/dwl.c b/dwl.c
|
||||||
|
index b56abd7c..e342557b 100644
|
||||||
|
--- a/dwl.c
|
||||||
|
+++ b/dwl.c
|
||||||
|
@@ -202,6 +202,7 @@ static void arrange(Monitor *m);
|
||||||
|
static void arrangelayer(Monitor *m, struct wl_list *list,
|
||||||
|
struct wlr_box *usable_area, int exclusive);
|
||||||
|
static void arrangelayers(Monitor *m);
|
||||||
|
+static void autostartexec(void);
|
||||||
|
static void axisnotify(struct wl_listener *listener, void *data);
|
||||||
|
static void buttonpress(struct wl_listener *listener, void *data);
|
||||||
|
static void chvt(const Arg *arg);
|
||||||
|
@@ -264,6 +265,7 @@ static void setmon(Client *c, Monitor *m, unsigned int newtags);
|
||||||
|
static void setpsel(struct wl_listener *listener, void *data);
|
||||||
|
static void setsel(struct wl_listener *listener, void *data);
|
||||||
|
static void setup(void);
|
||||||
|
+static void sigchld(int unused);
|
||||||
|
static void spawn(const Arg *arg);
|
||||||
|
static void startdrag(struct wl_listener *listener, void *data);
|
||||||
|
static void tag(const Arg *arg);
|
||||||
|
@@ -351,7 +353,6 @@ static void configurex11(struct wl_listener *listener, void *data);
|
||||||
|
static void createnotifyx11(struct wl_listener *listener, void *data);
|
||||||
|
static Atom getatom(xcb_connection_t *xc, const char *name);
|
||||||
|
static void sethints(struct wl_listener *listener, void *data);
|
||||||
|
-static void sigchld(int unused);
|
||||||
|
static void xwaylandready(struct wl_listener *listener, void *data);
|
||||||
|
static struct wl_listener new_xwayland_surface = {.notify = createnotifyx11};
|
||||||
|
static struct wl_listener xwayland_ready = {.notify = xwaylandready};
|
||||||
|
@@ -368,6 +369,9 @@ static Atom netatom[NetLast];
|
||||||
|
/* compile-time check if all tags fit into an unsigned int bit array. */
|
||||||
|
struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
|
||||||
|
|
||||||
|
+static pid_t *autostart_pids;
|
||||||
|
+static size_t autostart_len;
|
||||||
|
+
|
||||||
|
/* function implementations */
|
||||||
|
void
|
||||||
|
applybounds(Client *c, struct wlr_box *bbox)
|
||||||
|
@@ -386,6 +390,30 @@ applybounds(Client *c, struct wlr_box *bbox)
|
||||||
|
c->geom.y = bbox->y;
|
||||||
|
}
|
||||||
|
|
||||||
|
+void
|
||||||
|
+autostartexec(void)
|
||||||
|
+{
|
||||||
|
+ const char *const *p;
|
||||||
|
+ size_t i = 0;
|
||||||
|
+
|
||||||
|
+ /* count entries */
|
||||||
|
+ for (p = autostart; *p; autostart_len++, p++)
|
||||||
|
+ while (*++p);
|
||||||
|
+
|
||||||
|
+ autostart_pids = calloc(autostart_len, sizeof(pid_t));
|
||||||
|
+ for (p = autostart; *p; i++, p++) {
|
||||||
|
+ if ((autostart_pids[i] = fork()) == 0) {
|
||||||
|
+ setsid();
|
||||||
|
+ execvp(*p, (char *const *)p);
|
||||||
|
+ fprintf(stderr, "dwl: execvp %s\n", *p);
|
||||||
|
+ perror(" failed");
|
||||||
|
+ _exit(EXIT_FAILURE);
|
||||||
|
+ }
|
||||||
|
+ /* skip arguments */
|
||||||
|
+ while (*++p);
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
void
|
||||||
|
applyrules(Client *c)
|
||||||
|
{
|
||||||
|
@@ -1535,6 +1563,16 @@ printstatus(void)
|
||||||
|
void
|
||||||
|
quit(const Arg *arg)
|
||||||
|
{
|
||||||
|
+ size_t i;
|
||||||
|
+
|
||||||
|
+ /* kill child processes */
|
||||||
|
+ for (i = 0; i < autostart_len; i++) {
|
||||||
|
+ if (0 < autostart_pids[i]) {
|
||||||
|
+ kill(autostart_pids[i], SIGTERM);
|
||||||
|
+ waitpid(autostart_pids[i], NULL, 0);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
wl_display_terminate(dpy);
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1630,6 +1668,7 @@ run(char *startup_cmd)
|
||||||
|
setenv("WAYLAND_DISPLAY", socket, 1);
|
||||||
|
|
||||||
|
/* Now that the socket exists, run the startup command */
|
||||||
|
+ autostartexec();
|
||||||
|
if (startup_cmd) {
|
||||||
|
int piperw[2];
|
||||||
|
if (pipe(piperw) < 0)
|
||||||
|
@@ -1816,11 +1855,7 @@ setup(void)
|
||||||
|
dpy = wl_display_create();
|
||||||
|
|
||||||
|
/* Set up signal handlers */
|
||||||
|
-#ifdef XWAYLAND
|
||||||
|
sigchld(0);
|
||||||
|
-#else
|
||||||
|
- signal(SIGCHLD, SIG_IGN);
|
||||||
|
-#endif
|
||||||
|
signal(SIGINT, quitsignal);
|
||||||
|
signal(SIGTERM, quitsignal);
|
||||||
|
|
||||||
|
@@ -1985,6 +2020,41 @@ setup(void)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
+void
|
||||||
|
+sigchld(int unused)
|
||||||
|
+{
|
||||||
|
+ siginfo_t in;
|
||||||
|
+ pid_t pid;
|
||||||
|
+ /* We should be able to remove this function in favor of a simple
|
||||||
|
+ * signal(SIGCHLD, SIG_IGN);
|
||||||
|
+ * but the Xwayland implementation in wlroots currently prevents us from
|
||||||
|
+ * setting our own disposition for SIGCHLD.
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+ if (signal(SIGCHLD, sigchld) == SIG_ERR)
|
||||||
|
+ die("can't install SIGCHLD handler:");
|
||||||
|
+ /* WNOWAIT leaves the child in a waitable state, in case this is the
|
||||||
|
+ * XWayland process
|
||||||
|
+ */
|
||||||
|
+ while (!waitid(P_ALL, 0, &in, WEXITED|WNOHANG|WNOWAIT) && in.si_pid
|
||||||
|
+ && in.si_pid != xwayland->server->pid) {
|
||||||
|
+ if (0 < (pid = waitpid(in.si_pid, NULL, WNOHANG))) {
|
||||||
|
+ pid_t *p, *lim;
|
||||||
|
+
|
||||||
|
+ if (!(p = autostart_pids))
|
||||||
|
+ continue;
|
||||||
|
+ lim = &p[autostart_len];
|
||||||
|
+
|
||||||
|
+ for (; p < lim; p++) {
|
||||||
|
+ if (*p == pid) {
|
||||||
|
+ *p = -1;
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
void
|
||||||
|
spawn(const Arg *arg)
|
||||||
|
{
|
||||||
|
@@ -2379,25 +2449,6 @@ sethints(struct wl_listener *listener, void *data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-void
|
||||||
|
-sigchld(int unused)
|
||||||
|
-{
|
||||||
|
- siginfo_t in;
|
||||||
|
- /* We should be able to remove this function in favor of a simple
|
||||||
|
- * signal(SIGCHLD, SIG_IGN);
|
||||||
|
- * but the Xwayland implementation in wlroots currently prevents us from
|
||||||
|
- * setting our own disposition for SIGCHLD.
|
||||||
|
- */
|
||||||
|
- if (signal(SIGCHLD, sigchld) == SIG_ERR)
|
||||||
|
- die("can't install SIGCHLD handler:");
|
||||||
|
- /* WNOWAIT leaves the child in a waitable state, in case this is the
|
||||||
|
- * XWayland process
|
||||||
|
- */
|
||||||
|
- while (!waitid(P_ALL, 0, &in, WEXITED|WNOHANG|WNOWAIT) && in.si_pid
|
||||||
|
- && in.si_pid != xwayland->server->pid)
|
||||||
|
- waitpid(in.si_pid, NULL, 0);
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
void
|
||||||
|
xwaylandready(struct wl_listener *listener, void *data)
|
||||||
|
{
|
145
dwl/patches/cursor_warp.patch
Normal file
145
dwl/patches/cursor_warp.patch
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
From 8703fcdeac789425fbea768c3efa30884518e72e Mon Sep 17 00:00:00 2001
|
||||||
|
From: Faerryn <alexandre.liao@gmail.com>
|
||||||
|
Date: Thu, 26 May 2022 23:18:59 -0400
|
||||||
|
Subject: [PATCH 1/3] cursor wrap
|
||||||
|
|
||||||
|
---
|
||||||
|
dwl.c | 10 ++++++++++
|
||||||
|
1 file changed, 10 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/dwl.c b/dwl.c
|
||||||
|
index d0f5afc8..7427fa06 100644
|
||||||
|
--- a/dwl.c
|
||||||
|
+++ b/dwl.c
|
||||||
|
@@ -1107,6 +1107,16 @@ focusclient(Client *c, int lift)
|
||||||
|
struct wlr_keyboard *kb;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
+ /* Warp cursor to center of client if it is outside */
|
||||||
|
+ if (c && (cursor->x < c->geom.x ||
|
||||||
|
+ cursor->x > c->geom.x + c->geom.width ||
|
||||||
|
+ cursor->y < c->geom.y ||
|
||||||
|
+ cursor->y > c->geom.y + c->geom.height))
|
||||||
|
+ wlr_cursor_warp_closest( cursor,
|
||||||
|
+ NULL,
|
||||||
|
+ c->geom.x + c->geom.width / 2.0,
|
||||||
|
+ c->geom.y + c->geom.height / 2.0);
|
||||||
|
+
|
||||||
|
/* Raise client in stacking order if requested */
|
||||||
|
if (c && lift)
|
||||||
|
wlr_scene_node_raise_to_top(c->scene);
|
||||||
|
|
||||||
|
From 9cd5b806a96823fc10784a39c86d57156e749d11 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Faerryn <alexandre.liao@gmail.com>
|
||||||
|
Date: Fri, 27 May 2022 14:51:09 -0400
|
||||||
|
Subject: [PATCH 2/3] both focusclient and arrange will warp cursor
|
||||||
|
|
||||||
|
---
|
||||||
|
dwl.c | 26 ++++++++++++++++++--------
|
||||||
|
1 file changed, 18 insertions(+), 8 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/dwl.c b/dwl.c
|
||||||
|
index 7427fa06..2bdcaa56 100644
|
||||||
|
--- a/dwl.c
|
||||||
|
+++ b/dwl.c
|
||||||
|
@@ -290,6 +290,7 @@ static void updatetitle(struct wl_listener *listener, void *data);
|
||||||
|
static void urgent(struct wl_listener *listener, void *data);
|
||||||
|
static void view(const Arg *arg);
|
||||||
|
static void virtualkeyboard(struct wl_listener *listener, void *data);
|
||||||
|
+static void warpcursor(const Client *c);
|
||||||
|
static Monitor *xytomon(double x, double y);
|
||||||
|
static struct wlr_scene_node *xytonode(double x, double y, struct wlr_surface **psurface,
|
||||||
|
Client **pc, LayerSurface **pl, double *nx, double *ny);
|
||||||
|
@@ -489,6 +490,9 @@ arrange(Monitor *m)
|
||||||
|
if (m->lt[m->sellt]->arrange)
|
||||||
|
m->lt[m->sellt]->arrange(m);
|
||||||
|
/* TODO recheck pointer focus here... or in resize()? */
|
||||||
|
+ c = selclient();
|
||||||
|
+ if (c)
|
||||||
|
+ warpcursor(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
@@ -1108,14 +1112,8 @@ focusclient(Client *c, int lift)
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Warp cursor to center of client if it is outside */
|
||||||
|
- if (c && (cursor->x < c->geom.x ||
|
||||||
|
- cursor->x > c->geom.x + c->geom.width ||
|
||||||
|
- cursor->y < c->geom.y ||
|
||||||
|
- cursor->y > c->geom.y + c->geom.height))
|
||||||
|
- wlr_cursor_warp_closest( cursor,
|
||||||
|
- NULL,
|
||||||
|
- c->geom.x + c->geom.width / 2.0,
|
||||||
|
- c->geom.y + c->geom.height / 2.0);
|
||||||
|
+ if (c)
|
||||||
|
+ warpcursor(c);
|
||||||
|
|
||||||
|
/* Raise client in stacking order if requested */
|
||||||
|
if (c && lift)
|
||||||
|
@@ -2375,6 +2373,18 @@ virtualkeyboard(struct wl_listener *listener, void *data)
|
||||||
|
createkeyboard(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
+void
|
||||||
|
+warpcursor(const Client *c) {
|
||||||
|
+ if (cursor->x < c->geom.x ||
|
||||||
|
+ cursor->x > c->geom.x + c->geom.width ||
|
||||||
|
+ cursor->y < c->geom.y ||
|
||||||
|
+ cursor->y > c->geom.y + c->geom.height)
|
||||||
|
+ wlr_cursor_warp_closest(cursor,
|
||||||
|
+ NULL,
|
||||||
|
+ c->geom.x + c->geom.width / 2.0,
|
||||||
|
+ c->geom.y + c->geom.height / 2.0);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
Monitor *
|
||||||
|
xytomon(double x, double y)
|
||||||
|
{
|
||||||
|
|
||||||
|
From 95a8e2c3372b2dc245533001164b1e427751d394 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Faerryn <alexandre.liao@gmail.com>
|
||||||
|
Date: Sat, 28 May 2022 12:35:32 -0400
|
||||||
|
Subject: [PATCH 3/3] add an option to config.h for cursor_warp
|
||||||
|
|
||||||
|
---
|
||||||
|
config.def.h | 3 +++
|
||||||
|
dwl.c | 4 ++--
|
||||||
|
2 files changed, 5 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/config.def.h b/config.def.h
|
||||||
|
index 4f131dda..f82dd582 100644
|
||||||
|
--- a/config.def.h
|
||||||
|
+++ b/config.def.h
|
||||||
|
@@ -6,6 +6,9 @@ static const float rootcolor[] = {0.3, 0.3, 0.3, 1.0};
|
||||||
|
static const float bordercolor[] = {0.5, 0.5, 0.5, 1.0};
|
||||||
|
static const float focuscolor[] = {1.0, 0.0, 0.0, 1.0};
|
||||||
|
|
||||||
|
+/* cursor warping */
|
||||||
|
+static const bool cursor_warp = true;
|
||||||
|
+
|
||||||
|
/* tagging */
|
||||||
|
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
|
||||||
|
|
||||||
|
diff --git a/dwl.c b/dwl.c
|
||||||
|
index 2bdcaa56..e593b152 100644
|
||||||
|
--- a/dwl.c
|
||||||
|
+++ b/dwl.c
|
||||||
|
@@ -491,7 +491,7 @@ arrange(Monitor *m)
|
||||||
|
m->lt[m->sellt]->arrange(m);
|
||||||
|
/* TODO recheck pointer focus here... or in resize()? */
|
||||||
|
c = selclient();
|
||||||
|
- if (c)
|
||||||
|
+ if (cursor_warp && c)
|
||||||
|
warpcursor(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1112,7 +1112,7 @@ focusclient(Client *c, int lift)
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Warp cursor to center of client if it is outside */
|
||||||
|
- if (c)
|
||||||
|
+ if (cursor_warp && c)
|
||||||
|
warpcursor(c);
|
||||||
|
|
||||||
|
/* Raise client in stacking order if requested */
|
162
dwl/patches/monfig.patch
Normal file
162
dwl/patches/monfig.patch
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
From bf8e50786ee0c7e20024b8aa8c82a744351b87b1 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Palanix <palanixyt@gmail.com>
|
||||||
|
Date: Mon, 4 Apr 2022 16:08:29 +0200
|
||||||
|
Subject: [PATCH] Updated patch now allowing setting x and y
|
||||||
|
|
||||||
|
---
|
||||||
|
config.def.h | 12 +++++----
|
||||||
|
dwl.c | 73 +++++++++++++++++++++++++++++++++++++++++++++-------
|
||||||
|
2 files changed, 71 insertions(+), 14 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/config.def.h b/config.def.h
|
||||||
|
index 4f131dda..9bd1b7d0 100644
|
||||||
|
--- a/config.def.h
|
||||||
|
+++ b/config.def.h
|
||||||
|
@@ -25,14 +25,16 @@ static const Layout layouts[] = {
|
||||||
|
{ "[M]", monocle },
|
||||||
|
};
|
||||||
|
|
||||||
|
-/* monitors */
|
||||||
|
+/* monitors
|
||||||
|
+ * The order in which monitors are defined determines their position.
|
||||||
|
+ * Non-configured monitors are always added to the left. */
|
||||||
|
static const MonitorRule monrules[] = {
|
||||||
|
- /* name mfact nmaster scale layout rotate/reflect */
|
||||||
|
- /* example of a HiDPI laptop monitor:
|
||||||
|
- { "eDP-1", 0.5, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL },
|
||||||
|
+ /* name mfact nmaster scale layout rotate/reflect x y resx resy rate adaptive*/
|
||||||
|
+ /* example of a HiDPI laptop monitor at 120Hz:
|
||||||
|
+ { "eDP-1", 0.5, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, 0, 0, 0, 0, 120.000, 1},
|
||||||
|
*/
|
||||||
|
/* defaults */
|
||||||
|
- { NULL, 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL },
|
||||||
|
+ { NULL, 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, 0, 0, 0, 0, 0, 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* keyboard */
|
||||||
|
diff --git a/dwl.c b/dwl.c
|
||||||
|
index d0f5afc8..5f306c59 100644
|
||||||
|
--- a/dwl.c
|
||||||
|
+++ b/dwl.c
|
||||||
|
@@ -191,6 +191,12 @@ typedef struct {
|
||||||
|
float scale;
|
||||||
|
const Layout *lt;
|
||||||
|
enum wl_output_transform rr;
|
||||||
|
+ int x;
|
||||||
|
+ int y;
|
||||||
|
+ int resx;
|
||||||
|
+ int resy;
|
||||||
|
+ int rate;
|
||||||
|
+ int adaptive_true;
|
||||||
|
} MonitorRule;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
@@ -263,6 +269,7 @@ static void requeststartdrag(struct wl_listener *listener, void *data);
|
||||||
|
static void resize(Client *c, int x, int y, int w, int h, int interact);
|
||||||
|
static void run(char *startup_cmd);
|
||||||
|
static Client *selclient(void);
|
||||||
|
+static void set_mode(struct wlr_output *output, int width, int height, float refresh_rate);
|
||||||
|
static void setcursor(struct wl_listener *listener, void *data);
|
||||||
|
static void setfloating(Client *c, int floating);
|
||||||
|
static void setfullscreen(Client *c, int fullscreen);
|
||||||
|
@@ -868,18 +875,51 @@ createlayersurface(struct wl_listener *listener, void *data)
|
||||||
|
wlr_layer_surface->current = old_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
+void
|
||||||
|
+set_mode(struct wlr_output *output, int width, int height,
|
||||||
|
+ float refresh_rate) {
|
||||||
|
+ // Not all floating point integers can be represented exactly
|
||||||
|
+ // as (int)(1000 * mHz / 1000.f)
|
||||||
|
+ // round() the result to avoid any error
|
||||||
|
+ struct wlr_output_mode *mode, *best = NULL;
|
||||||
|
+ int mhz = (int)((refresh_rate * 1000) + 0.5);
|
||||||
|
+
|
||||||
|
+ if (wl_list_empty(&output->modes)) {
|
||||||
|
+ wlr_output_set_custom_mode(output, width, height,
|
||||||
|
+ refresh_rate > 0 ? mhz : 0);
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ wl_list_for_each(mode, &output->modes, link) {
|
||||||
|
+ if (mode->width == width && mode->height == height) {
|
||||||
|
+ if (mode->refresh == mhz) {
|
||||||
|
+ best = mode;
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ if (best == NULL || mode->refresh > best->refresh) {
|
||||||
|
+ best = mode;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ if (!best) {
|
||||||
|
+ best = wlr_output_preferred_mode(output);
|
||||||
|
+ }
|
||||||
|
+ wlr_output_set_mode(output, best);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
void
|
||||||
|
createmon(struct wl_listener *listener, void *data)
|
||||||
|
{
|
||||||
|
/* This event is raised by the backend when a new output (aka a display or
|
||||||
|
* monitor) becomes available. */
|
||||||
|
struct wlr_output *wlr_output = data;
|
||||||
|
+ const struct wlr_output_mode *wlr_output_mode;
|
||||||
|
+ int32_t resx,resy,rate;
|
||||||
|
const MonitorRule *r;
|
||||||
|
Monitor *m = wlr_output->data = ecalloc(1, sizeof(*m));
|
||||||
|
m->wlr_output = wlr_output;
|
||||||
|
|
||||||
|
- wlr_output_init_render(wlr_output, alloc, drw);
|
||||||
|
-
|
||||||
|
+ wlr_output_set_mode(wlr_output, wlr_output_preferred_mode(wlr_output));
|
||||||
|
/* Initialize monitor state using configured rules */
|
||||||
|
for (size_t i = 0; i < LENGTH(m->layers); i++)
|
||||||
|
wl_list_init(&m->layers[i]);
|
||||||
|
@@ -892,16 +932,31 @@ createmon(struct wl_listener *listener, void *data)
|
||||||
|
wlr_xcursor_manager_load(cursor_mgr, r->scale);
|
||||||
|
m->lt[0] = m->lt[1] = r->lt;
|
||||||
|
wlr_output_set_transform(wlr_output, r->rr);
|
||||||
|
+
|
||||||
|
+ wlr_output_mode = wlr_output_preferred_mode(wlr_output);
|
||||||
|
+
|
||||||
|
+ if (r->rate)
|
||||||
|
+ rate = r->rate;
|
||||||
|
+ else
|
||||||
|
+ rate = wlr_output_mode->refresh;
|
||||||
|
+ if (r->resx)
|
||||||
|
+ resx = r->resx;
|
||||||
|
+ else
|
||||||
|
+ resx = wlr_output_mode->width;
|
||||||
|
+ if (r->resy)
|
||||||
|
+ resy = r->resy;
|
||||||
|
+ else
|
||||||
|
+ resy = wlr_output_mode->height;
|
||||||
|
+
|
||||||
|
+ set_mode(wlr_output, resx, resy, rate);
|
||||||
|
+
|
||||||
|
+ if (r->adaptive_true)
|
||||||
|
+ wlr_output_enable_adaptive_sync(wlr_output, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- /* The mode is a tuple of (width, height, refresh rate), and each
|
||||||
|
- * monitor supports only a specific set of modes. We just pick the
|
||||||
|
- * monitor's preferred mode; a more sophisticated compositor would let
|
||||||
|
- * the user configure it. */
|
||||||
|
- wlr_output_set_mode(wlr_output, wlr_output_preferred_mode(wlr_output));
|
||||||
|
- wlr_output_enable_adaptive_sync(wlr_output, 1);
|
||||||
|
+ wlr_output_init_render(wlr_output, alloc, drw);
|
||||||
|
|
||||||
|
/* Set up event listeners */
|
||||||
|
LISTEN(&wlr_output->events.frame, &m->frame, rendermon);
|
||||||
|
@@ -921,7 +976,7 @@ createmon(struct wl_listener *listener, void *data)
|
||||||
|
* output (such as DPI, scale factor, manufacturer, etc).
|
||||||
|
*/
|
||||||
|
m->scene_output = wlr_scene_output_create(scene, wlr_output);
|
||||||
|
- wlr_output_layout_add_auto(output_layout, wlr_output);
|
||||||
|
+ wlr_output_layout_add(output_layout, wlr_output,r->x, r->y);
|
||||||
|
|
||||||
|
/* If length == 1 we need update selmon.
|
||||||
|
* Maybe it will change in run(). */
|
168
dwl/patches/pertag.patch
Normal file
168
dwl/patches/pertag.patch
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
From 55e3bc247bb47ed61dbc404a8b3bf24abd517cac Mon Sep 17 00:00:00 2001
|
||||||
|
From: Guido Cella <guido@guidocella.xyz>
|
||||||
|
Date: Sun, 20 Mar 2022 21:22:13 +0100
|
||||||
|
Subject: [PATCH] Port pertag
|
||||||
|
|
||||||
|
---
|
||||||
|
dwl.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----
|
||||||
|
1 file changed, 72 insertions(+), 5 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/dwl.c b/dwl.c
|
||||||
|
index 01f51ad9..dc1266a7 100644
|
||||||
|
--- a/dwl.c
|
||||||
|
+++ b/dwl.c
|
||||||
|
@@ -87,6 +87,7 @@ typedef struct {
|
||||||
|
const Arg arg;
|
||||||
|
} Button;
|
||||||
|
|
||||||
|
+typedef struct Pertag Pertag;
|
||||||
|
typedef struct Monitor Monitor;
|
||||||
|
typedef struct {
|
||||||
|
/* Must be first */
|
||||||
|
@@ -173,6 +174,7 @@ struct Monitor {
|
||||||
|
struct wlr_box w; /* window area, layout-relative */
|
||||||
|
struct wl_list layers[4]; /* LayerSurface::link */
|
||||||
|
const Layout *lt[2];
|
||||||
|
+ Pertag *pertag;
|
||||||
|
unsigned int seltags;
|
||||||
|
unsigned int sellt;
|
||||||
|
unsigned int tagset[2];
|
||||||
|
@@ -359,6 +361,14 @@ static Atom netatom[NetLast];
|
||||||
|
/* attempt to encapsulate suck into one file */
|
||||||
|
#include "client.h"
|
||||||
|
|
||||||
|
+struct Pertag {
|
||||||
|
+ unsigned int curtag, prevtag; /* current and previous tag */
|
||||||
|
+ int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */
|
||||||
|
+ float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */
|
||||||
|
+ unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */
|
||||||
|
+ const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
/* compile-time check if all tags fit into an unsigned int bit array. */
|
||||||
|
struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
|
||||||
|
|
||||||
|
@@ -801,6 +811,7 @@ createmon(struct wl_listener *listener, void *data)
|
||||||
|
struct wlr_output *wlr_output = data;
|
||||||
|
const MonitorRule *r;
|
||||||
|
Monitor *m = wlr_output->data = calloc(1, sizeof(*m));
|
||||||
|
+ unsigned int i;
|
||||||
|
if (!m)
|
||||||
|
EBARF("createmon: calloc");
|
||||||
|
m->wlr_output = wlr_output;
|
||||||
|
@@ -841,6 +852,18 @@ createmon(struct wl_listener *listener, void *data)
|
||||||
|
wl_list_insert(&mons, &m->link);
|
||||||
|
printstatus();
|
||||||
|
|
||||||
|
+ m->pertag = calloc(1, sizeof(Pertag));
|
||||||
|
+ m->pertag->curtag = m->pertag->prevtag = 1;
|
||||||
|
+
|
||||||
|
+ for (i = 0; i <= LENGTH(tags); i++) {
|
||||||
|
+ m->pertag->nmasters[i] = m->nmaster;
|
||||||
|
+ m->pertag->mfacts[i] = m->mfact;
|
||||||
|
+
|
||||||
|
+ m->pertag->ltidxs[i][0] = m->lt[0];
|
||||||
|
+ m->pertag->ltidxs[i][1] = m->lt[1];
|
||||||
|
+ m->pertag->sellts[i] = m->sellt;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
/* Adds this to the output layout in the order it was configured in.
|
||||||
|
*
|
||||||
|
* The output layout utility automatically adds a wl_output global to the
|
||||||
|
@@ -1194,7 +1217,7 @@ focustop(Monitor *m)
|
||||||
|
void
|
||||||
|
incnmaster(const Arg *arg)
|
||||||
|
{
|
||||||
|
- selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
|
||||||
|
+ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0);
|
||||||
|
arrange(selmon);
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1765,9 +1788,9 @@ void
|
||||||
|
setlayout(const Arg *arg)
|
||||||
|
{
|
||||||
|
if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
|
||||||
|
- selmon->sellt ^= 1;
|
||||||
|
+ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag] ^= 1;
|
||||||
|
if (arg && arg->v)
|
||||||
|
- selmon->lt[selmon->sellt] = (Layout *)arg->v;
|
||||||
|
+ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v;
|
||||||
|
/* TODO change layout symbol? */
|
||||||
|
arrange(selmon);
|
||||||
|
printstatus();
|
||||||
|
@@ -1784,7 +1807,7 @@ setmfact(const Arg *arg)
|
||||||
|
f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
|
||||||
|
if (f < 0.1 || f > 0.9)
|
||||||
|
return;
|
||||||
|
- selmon->mfact = f;
|
||||||
|
+ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f;
|
||||||
|
arrange(selmon);
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -2107,9 +2130,30 @@ void
|
||||||
|
toggleview(const Arg *arg)
|
||||||
|
{
|
||||||
|
unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
|
||||||
|
+ int i;
|
||||||
|
|
||||||
|
if (newtagset) {
|
||||||
|
selmon->tagset[selmon->seltags] = newtagset;
|
||||||
|
+
|
||||||
|
+ if (newtagset == ~0) {
|
||||||
|
+ selmon->pertag->prevtag = selmon->pertag->curtag;
|
||||||
|
+ selmon->pertag->curtag = 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* test if the user did not select the same tag */
|
||||||
|
+ if (!(newtagset & 1 << (selmon->pertag->curtag - 1))) {
|
||||||
|
+ selmon->pertag->prevtag = selmon->pertag->curtag;
|
||||||
|
+ for (i = 0; !(newtagset & 1 << i); i++) ;
|
||||||
|
+ selmon->pertag->curtag = i + 1;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* apply settings for this view */
|
||||||
|
+ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
|
||||||
|
+ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
|
||||||
|
+ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
|
||||||
|
+ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
|
||||||
|
+ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
|
||||||
|
+
|
||||||
|
focusclient(focustop(selmon), 1);
|
||||||
|
arrange(selmon);
|
||||||
|
}
|
||||||
|
@@ -2219,11 +2263,34 @@ urgent(struct wl_listener *listener, void *data)
|
||||||
|
void
|
||||||
|
view(const Arg *arg)
|
||||||
|
{
|
||||||
|
+ int i;
|
||||||
|
+ unsigned int tmptag;
|
||||||
|
+
|
||||||
|
if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
|
||||||
|
return;
|
||||||
|
selmon->seltags ^= 1; /* toggle sel tagset */
|
||||||
|
- if (arg->ui & TAGMASK)
|
||||||
|
+ if (arg->ui & TAGMASK) {
|
||||||
|
selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
|
||||||
|
+ selmon->pertag->prevtag = selmon->pertag->curtag;
|
||||||
|
+
|
||||||
|
+ if (arg->ui == ~0)
|
||||||
|
+ selmon->pertag->curtag = 0;
|
||||||
|
+ else {
|
||||||
|
+ for (i = 0; !(arg->ui & 1 << i); i++) ;
|
||||||
|
+ selmon->pertag->curtag = i + 1;
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ tmptag = selmon->pertag->prevtag;
|
||||||
|
+ selmon->pertag->prevtag = selmon->pertag->curtag;
|
||||||
|
+ selmon->pertag->curtag = tmptag;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
|
||||||
|
+ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
|
||||||
|
+ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
|
||||||
|
+ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
|
||||||
|
+ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
|
||||||
|
+
|
||||||
|
focusclient(focustop(selmon), 1);
|
||||||
|
arrange(selmon);
|
||||||
|
printstatus();
|
135
dwl/patches/unclutter.patch
Normal file
135
dwl/patches/unclutter.patch
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
From 00e76d7ab7be57c8f514a8aa9d2337e55cd67b47 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Guido Cella <guido@guidocella.xyz>
|
||||||
|
Date: Sat, 12 Feb 2022 20:29:09 +0100
|
||||||
|
Subject: [PATCH] =?UTF-8?q?Hide=20the=20mouse=20cursor=20if=20it=20isn?=
|
||||||
|
=?UTF-8?q?=E2=80=99t=20being=20used?=
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
---
|
||||||
|
config.def.h | 2 ++
|
||||||
|
dwl.c | 41 +++++++++++++++++++++++++++++++++++++----
|
||||||
|
2 files changed, 39 insertions(+), 4 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/config.def.h b/config.def.h
|
||||||
|
index 84086594..10b463e1 100644
|
||||||
|
--- a/config.def.h
|
||||||
|
+++ b/config.def.h
|
||||||
|
@@ -51,6 +51,8 @@ static const int repeat_delay = 600;
|
||||||
|
static const int tap_to_click = 1;
|
||||||
|
static const int natural_scrolling = 0;
|
||||||
|
|
||||||
|
+static const int cursor_timeout = 5;
|
||||||
|
+
|
||||||
|
#define MODKEY WLR_MODIFIER_ALT
|
||||||
|
#define TAGKEYS(KEY,SKEY,TAG) \
|
||||||
|
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \
|
||||||
|
diff --git a/dwl.c b/dwl.c
|
||||||
|
index 01f51ad9..e6a5382b 100644
|
||||||
|
--- a/dwl.c
|
||||||
|
+++ b/dwl.c
|
||||||
|
@@ -232,6 +232,8 @@ static void focusmon(const Arg *arg);
|
||||||
|
static void focusstack(const Arg *arg);
|
||||||
|
static void fullscreennotify(struct wl_listener *listener, void *data);
|
||||||
|
static Client *focustop(Monitor *m);
|
||||||
|
+static void handlecursoractivity();
|
||||||
|
+static int hidecursor(void *data);
|
||||||
|
static void incnmaster(const Arg *arg);
|
||||||
|
static void inputdevice(struct wl_listener *listener, void *data);
|
||||||
|
static int keybinding(uint32_t mods, xkb_keysym_t sym);
|
||||||
|
@@ -310,6 +312,8 @@ static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr;
|
||||||
|
|
||||||
|
static struct wlr_cursor *cursor;
|
||||||
|
static struct wlr_xcursor_manager *cursor_mgr;
|
||||||
|
+static struct wl_event_source *hide_source;
|
||||||
|
+static bool cursor_hidden = false;
|
||||||
|
|
||||||
|
static struct wlr_seat *seat;
|
||||||
|
static struct wl_list keyboards;
|
||||||
|
@@ -603,6 +607,7 @@ axisnotify(struct wl_listener *listener, void *data)
|
||||||
|
* for example when you move the scroll wheel. */
|
||||||
|
struct wlr_event_pointer_axis *event = data;
|
||||||
|
wlr_idle_notify_activity(idle, seat);
|
||||||
|
+ handlecursoractivity();
|
||||||
|
/* Notify the client with pointer focus of the axis event. */
|
||||||
|
wlr_seat_pointer_notify_axis(seat,
|
||||||
|
event->time_msec, event->orientation, event->delta,
|
||||||
|
@@ -619,6 +624,7 @@ buttonpress(struct wl_listener *listener, void *data)
|
||||||
|
const Button *b;
|
||||||
|
|
||||||
|
wlr_idle_notify_activity(idle, seat);
|
||||||
|
+ handlecursoractivity();
|
||||||
|
|
||||||
|
switch (event->state) {
|
||||||
|
case WLR_BUTTON_PRESSED:
|
||||||
|
@@ -1191,6 +1197,25 @@ focustop(Monitor *m)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
+void
|
||||||
|
+handlecursoractivity()
|
||||||
|
+{
|
||||||
|
+ wl_event_source_timer_update(hide_source, cursor_timeout * 1000);
|
||||||
|
+ if (cursor_hidden) {
|
||||||
|
+ wlr_xcursor_manager_set_cursor_image(cursor_mgr, "left_ptr", cursor);
|
||||||
|
+ cursor_hidden = false;
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+int
|
||||||
|
+hidecursor(void *data)
|
||||||
|
+{
|
||||||
|
+ wlr_cursor_set_image(cursor, NULL, 0, 0, 0, 0, 0, 0);
|
||||||
|
+ wlr_seat_pointer_notify_clear_focus(seat);
|
||||||
|
+ cursor_hidden = true;
|
||||||
|
+ return 1;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
void
|
||||||
|
incnmaster(const Arg *arg)
|
||||||
|
{
|
||||||
|
@@ -1401,6 +1426,7 @@ motionnotify(uint32_t time)
|
||||||
|
/* time is 0 in internal calls meant to restore pointer focus. */
|
||||||
|
if (time) {
|
||||||
|
wlr_idle_notify_activity(idle, seat);
|
||||||
|
+ handlecursoractivity();
|
||||||
|
|
||||||
|
/* Update selmon (even while dragging a window) */
|
||||||
|
if (sloppyfocus)
|
||||||
|
@@ -1558,10 +1584,13 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy,
|
||||||
|
time = now.tv_sec * 1000 + now.tv_nsec / 1000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
- /* Let the client know that the mouse cursor has entered one
|
||||||
|
- * of its surfaces, and make keyboard focus follow if desired.
|
||||||
|
- * wlroots makes this a no-op if surface is already focused */
|
||||||
|
- wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
|
||||||
|
+ if (!cursor_hidden)
|
||||||
|
+ wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
|
||||||
|
+ // wlr_seat_pointer_notify_enter seems to always show the cursor when
|
||||||
|
+ // calling this to restore pointer focus while mapping and unmapping layer
|
||||||
|
+ // surfaces. Not sure how sway makes it work, but you're unlikely to care
|
||||||
|
+ // about which surface has pointer focus in this case as you don't know
|
||||||
|
+ // where you're clicking until you show the cursor again.
|
||||||
|
wlr_seat_pointer_notify_motion(seat, time, sx, sy);
|
||||||
|
|
||||||
|
}
|
||||||
|
@@ -1711,6 +1740,7 @@ run(char *startup_cmd)
|
||||||
|
* monitor when displayed here */
|
||||||
|
wlr_cursor_warp_closest(cursor, NULL, cursor->x, cursor->y);
|
||||||
|
wlr_xcursor_manager_set_cursor_image(cursor_mgr, "left_ptr", cursor);
|
||||||
|
+ handlecursoractivity();
|
||||||
|
|
||||||
|
/* Run the Wayland event loop. This does not return until you exit the
|
||||||
|
* compositor. Starting the backend rigged up all of the necessary event
|
||||||
|
@@ -1959,6 +1989,9 @@ setup(void)
|
||||||
|
wl_signal_add(&cursor->events.axis, &cursor_axis);
|
||||||
|
wl_signal_add(&cursor->events.frame, &cursor_frame);
|
||||||
|
|
||||||
|
+ hide_source = wl_event_loop_add_timer(wl_display_get_event_loop(dpy),
|
||||||
|
+ hidecursor, cursor);
|
||||||
|
+
|
||||||
|
/*
|
||||||
|
* Configures a seat, which is a single "seat" at which a user sits and
|
||||||
|
* operates the computer. This conceptually includes up to one keyboard,
|
49
dwl/protocols/idle.xml
Normal file
49
dwl/protocols/idle.xml
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<protocol name="idle">
|
||||||
|
<copyright><![CDATA[
|
||||||
|
Copyright (C) 2015 Martin Gräßlin
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
]]></copyright>
|
||||||
|
<interface name="org_kde_kwin_idle" version="1">
|
||||||
|
<description summary="User idle time manager">
|
||||||
|
This interface allows to monitor user idle time on a given seat. The interface
|
||||||
|
allows to register timers which trigger after no user activity was registered
|
||||||
|
on the seat for a given interval. It notifies when user activity resumes.
|
||||||
|
|
||||||
|
This is useful for applications wanting to perform actions when the user is not
|
||||||
|
interacting with the system, e.g. chat applications setting the user as away, power
|
||||||
|
management features to dim screen, etc..
|
||||||
|
</description>
|
||||||
|
<request name="get_idle_timeout">
|
||||||
|
<arg name="id" type="new_id" interface="org_kde_kwin_idle_timeout"/>
|
||||||
|
<arg name="seat" type="object" interface="wl_seat"/>
|
||||||
|
<arg name="timeout" type="uint" summary="The idle timeout in msec"/>
|
||||||
|
</request>
|
||||||
|
</interface>
|
||||||
|
<interface name="org_kde_kwin_idle_timeout" version="1">
|
||||||
|
<request name="release" type="destructor">
|
||||||
|
<description summary="release the timeout object"/>
|
||||||
|
</request>
|
||||||
|
<request name="simulate_user_activity">
|
||||||
|
<description summary="Simulates user activity for this timeout, behaves just like real user activity on the seat"/>
|
||||||
|
</request>
|
||||||
|
<event name="idle">
|
||||||
|
<description summary="Triggered when there has not been any user activity in the requested idle time interval"/>
|
||||||
|
</event>
|
||||||
|
<event name="resumed">
|
||||||
|
<description summary="Triggered on the first user activity after an idle event"/>
|
||||||
|
</event>
|
||||||
|
</interface>
|
||||||
|
</protocol>
|
390
dwl/protocols/wlr-layer-shell-unstable-v1.xml
Normal file
390
dwl/protocols/wlr-layer-shell-unstable-v1.xml
Normal file
@ -0,0 +1,390 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<protocol name="wlr_layer_shell_unstable_v1">
|
||||||
|
<copyright>
|
||||||
|
Copyright © 2017 Drew DeVault
|
||||||
|
|
||||||
|
Permission to use, copy, modify, distribute, and sell this
|
||||||
|
software and its documentation for any purpose is hereby granted
|
||||||
|
without fee, provided that the above copyright notice appear in
|
||||||
|
all copies and that both that copyright notice and this permission
|
||||||
|
notice appear in supporting documentation, and that the name of
|
||||||
|
the copyright holders not be used in advertising or publicity
|
||||||
|
pertaining to distribution of the software without specific,
|
||||||
|
written prior permission. The copyright holders make no
|
||||||
|
representations about the suitability of this software for any
|
||||||
|
purpose. It is provided "as is" without express or implied
|
||||||
|
warranty.
|
||||||
|
|
||||||
|
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||||
|
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||||
|
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||||
|
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||||
|
THIS SOFTWARE.
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<interface name="zwlr_layer_shell_v1" version="4">
|
||||||
|
<description summary="create surfaces that are layers of the desktop">
|
||||||
|
Clients can use this interface to assign the surface_layer role to
|
||||||
|
wl_surfaces. Such surfaces are assigned to a "layer" of the output and
|
||||||
|
rendered with a defined z-depth respective to each other. They may also be
|
||||||
|
anchored to the edges and corners of a screen and specify input handling
|
||||||
|
semantics. This interface should be suitable for the implementation of
|
||||||
|
many desktop shell components, and a broad number of other applications
|
||||||
|
that interact with the desktop.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="get_layer_surface">
|
||||||
|
<description summary="create a layer_surface from a surface">
|
||||||
|
Create a layer surface for an existing surface. This assigns the role of
|
||||||
|
layer_surface, or raises a protocol error if another role is already
|
||||||
|
assigned.
|
||||||
|
|
||||||
|
Creating a layer surface from a wl_surface which has a buffer attached
|
||||||
|
or committed is a client error, and any attempts by a client to attach
|
||||||
|
or manipulate a buffer prior to the first layer_surface.configure call
|
||||||
|
must also be treated as errors.
|
||||||
|
|
||||||
|
After creating a layer_surface object and setting it up, the client
|
||||||
|
must perform an initial commit without any buffer attached.
|
||||||
|
The compositor will reply with a layer_surface.configure event.
|
||||||
|
The client must acknowledge it and is then allowed to attach a buffer
|
||||||
|
to map the surface.
|
||||||
|
|
||||||
|
You may pass NULL for output to allow the compositor to decide which
|
||||||
|
output to use. Generally this will be the one that the user most
|
||||||
|
recently interacted with.
|
||||||
|
|
||||||
|
Clients can specify a namespace that defines the purpose of the layer
|
||||||
|
surface.
|
||||||
|
</description>
|
||||||
|
<arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/>
|
||||||
|
<arg name="surface" type="object" interface="wl_surface"/>
|
||||||
|
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
|
||||||
|
<arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/>
|
||||||
|
<arg name="namespace" type="string" summary="namespace for the layer surface"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<enum name="error">
|
||||||
|
<entry name="role" value="0" summary="wl_surface has another role"/>
|
||||||
|
<entry name="invalid_layer" value="1" summary="layer value is invalid"/>
|
||||||
|
<entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<enum name="layer">
|
||||||
|
<description summary="available layers for surfaces">
|
||||||
|
These values indicate which layers a surface can be rendered in. They
|
||||||
|
are ordered by z depth, bottom-most first. Traditional shell surfaces
|
||||||
|
will typically be rendered between the bottom and top layers.
|
||||||
|
Fullscreen shell surfaces are typically rendered at the top layer.
|
||||||
|
Multiple surfaces can share a single layer, and ordering within a
|
||||||
|
single layer is undefined.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<entry name="background" value="0"/>
|
||||||
|
<entry name="bottom" value="1"/>
|
||||||
|
<entry name="top" value="2"/>
|
||||||
|
<entry name="overlay" value="3"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<!-- Version 3 additions -->
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor" since="3">
|
||||||
|
<description summary="destroy the layer_shell object">
|
||||||
|
This request indicates that the client will not use the layer_shell
|
||||||
|
object any more. Objects that have been created through this instance
|
||||||
|
are not affected.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="zwlr_layer_surface_v1" version="4">
|
||||||
|
<description summary="layer metadata interface">
|
||||||
|
An interface that may be implemented by a wl_surface, for surfaces that
|
||||||
|
are designed to be rendered as a layer of a stacked desktop-like
|
||||||
|
environment.
|
||||||
|
|
||||||
|
Layer surface state (layer, size, anchor, exclusive zone,
|
||||||
|
margin, interactivity) is double-buffered, and will be applied at the
|
||||||
|
time wl_surface.commit of the corresponding wl_surface is called.
|
||||||
|
|
||||||
|
Attaching a null buffer to a layer surface unmaps it.
|
||||||
|
|
||||||
|
Unmapping a layer_surface means that the surface cannot be shown by the
|
||||||
|
compositor until it is explicitly mapped again. The layer_surface
|
||||||
|
returns to the state it had right after layer_shell.get_layer_surface.
|
||||||
|
The client can re-map the surface by performing a commit without any
|
||||||
|
buffer attached, waiting for a configure event and handling it as usual.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="set_size">
|
||||||
|
<description summary="sets the size of the surface">
|
||||||
|
Sets the size of the surface in surface-local coordinates. The
|
||||||
|
compositor will display the surface centered with respect to its
|
||||||
|
anchors.
|
||||||
|
|
||||||
|
If you pass 0 for either value, the compositor will assign it and
|
||||||
|
inform you of the assignment in the configure event. You must set your
|
||||||
|
anchor to opposite edges in the dimensions you omit; not doing so is a
|
||||||
|
protocol error. Both values are 0 by default.
|
||||||
|
|
||||||
|
Size is double-buffered, see wl_surface.commit.
|
||||||
|
</description>
|
||||||
|
<arg name="width" type="uint"/>
|
||||||
|
<arg name="height" type="uint"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="set_anchor">
|
||||||
|
<description summary="configures the anchor point of the surface">
|
||||||
|
Requests that the compositor anchor the surface to the specified edges
|
||||||
|
and corners. If two orthogonal edges are specified (e.g. 'top' and
|
||||||
|
'left'), then the anchor point will be the intersection of the edges
|
||||||
|
(e.g. the top left corner of the output); otherwise the anchor point
|
||||||
|
will be centered on that edge, or in the center if none is specified.
|
||||||
|
|
||||||
|
Anchor is double-buffered, see wl_surface.commit.
|
||||||
|
</description>
|
||||||
|
<arg name="anchor" type="uint" enum="anchor"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="set_exclusive_zone">
|
||||||
|
<description summary="configures the exclusive geometry of this surface">
|
||||||
|
Requests that the compositor avoids occluding an area with other
|
||||||
|
surfaces. The compositor's use of this information is
|
||||||
|
implementation-dependent - do not assume that this region will not
|
||||||
|
actually be occluded.
|
||||||
|
|
||||||
|
A positive value is only meaningful if the surface is anchored to one
|
||||||
|
edge or an edge and both perpendicular edges. If the surface is not
|
||||||
|
anchored, anchored to only two perpendicular edges (a corner), anchored
|
||||||
|
to only two parallel edges or anchored to all edges, a positive value
|
||||||
|
will be treated the same as zero.
|
||||||
|
|
||||||
|
A positive zone is the distance from the edge in surface-local
|
||||||
|
coordinates to consider exclusive.
|
||||||
|
|
||||||
|
Surfaces that do not wish to have an exclusive zone may instead specify
|
||||||
|
how they should interact with surfaces that do. If set to zero, the
|
||||||
|
surface indicates that it would like to be moved to avoid occluding
|
||||||
|
surfaces with a positive exclusive zone. If set to -1, the surface
|
||||||
|
indicates that it would not like to be moved to accommodate for other
|
||||||
|
surfaces, and the compositor should extend it all the way to the edges
|
||||||
|
it is anchored to.
|
||||||
|
|
||||||
|
For example, a panel might set its exclusive zone to 10, so that
|
||||||
|
maximized shell surfaces are not shown on top of it. A notification
|
||||||
|
might set its exclusive zone to 0, so that it is moved to avoid
|
||||||
|
occluding the panel, but shell surfaces are shown underneath it. A
|
||||||
|
wallpaper or lock screen might set their exclusive zone to -1, so that
|
||||||
|
they stretch below or over the panel.
|
||||||
|
|
||||||
|
The default value is 0.
|
||||||
|
|
||||||
|
Exclusive zone is double-buffered, see wl_surface.commit.
|
||||||
|
</description>
|
||||||
|
<arg name="zone" type="int"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="set_margin">
|
||||||
|
<description summary="sets a margin from the anchor point">
|
||||||
|
Requests that the surface be placed some distance away from the anchor
|
||||||
|
point on the output, in surface-local coordinates. Setting this value
|
||||||
|
for edges you are not anchored to has no effect.
|
||||||
|
|
||||||
|
The exclusive zone includes the margin.
|
||||||
|
|
||||||
|
Margin is double-buffered, see wl_surface.commit.
|
||||||
|
</description>
|
||||||
|
<arg name="top" type="int"/>
|
||||||
|
<arg name="right" type="int"/>
|
||||||
|
<arg name="bottom" type="int"/>
|
||||||
|
<arg name="left" type="int"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<enum name="keyboard_interactivity">
|
||||||
|
<description summary="types of keyboard interaction possible for a layer shell surface">
|
||||||
|
Types of keyboard interaction possible for layer shell surfaces. The
|
||||||
|
rationale for this is twofold: (1) some applications are not interested
|
||||||
|
in keyboard events and not allowing them to be focused can improve the
|
||||||
|
desktop experience; (2) some applications will want to take exclusive
|
||||||
|
keyboard focus.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<entry name="none" value="0">
|
||||||
|
<description summary="no keyboard focus is possible">
|
||||||
|
This value indicates that this surface is not interested in keyboard
|
||||||
|
events and the compositor should never assign it the keyboard focus.
|
||||||
|
|
||||||
|
This is the default value, set for newly created layer shell surfaces.
|
||||||
|
|
||||||
|
This is useful for e.g. desktop widgets that display information or
|
||||||
|
only have interaction with non-keyboard input devices.
|
||||||
|
</description>
|
||||||
|
</entry>
|
||||||
|
<entry name="exclusive" value="1">
|
||||||
|
<description summary="request exclusive keyboard focus">
|
||||||
|
Request exclusive keyboard focus if this surface is above the shell surface layer.
|
||||||
|
|
||||||
|
For the top and overlay layers, the seat will always give
|
||||||
|
exclusive keyboard focus to the top-most layer which has keyboard
|
||||||
|
interactivity set to exclusive. If this layer contains multiple
|
||||||
|
surfaces with keyboard interactivity set to exclusive, the compositor
|
||||||
|
determines the one receiving keyboard events in an implementation-
|
||||||
|
defined manner. In this case, no guarantee is made when this surface
|
||||||
|
will receive keyboard focus (if ever).
|
||||||
|
|
||||||
|
For the bottom and background layers, the compositor is allowed to use
|
||||||
|
normal focus semantics.
|
||||||
|
|
||||||
|
This setting is mainly intended for applications that need to ensure
|
||||||
|
they receive all keyboard events, such as a lock screen or a password
|
||||||
|
prompt.
|
||||||
|
</description>
|
||||||
|
</entry>
|
||||||
|
<entry name="on_demand" value="2" since="4">
|
||||||
|
<description summary="request regular keyboard focus semantics">
|
||||||
|
This requests the compositor to allow this surface to be focused and
|
||||||
|
unfocused by the user in an implementation-defined manner. The user
|
||||||
|
should be able to unfocus this surface even regardless of the layer
|
||||||
|
it is on.
|
||||||
|
|
||||||
|
Typically, the compositor will want to use its normal mechanism to
|
||||||
|
manage keyboard focus between layer shell surfaces with this setting
|
||||||
|
and regular toplevels on the desktop layer (e.g. click to focus).
|
||||||
|
Nevertheless, it is possible for a compositor to require a special
|
||||||
|
interaction to focus or unfocus layer shell surfaces (e.g. requiring
|
||||||
|
a click even if focus follows the mouse normally, or providing a
|
||||||
|
keybinding to switch focus between layers).
|
||||||
|
|
||||||
|
This setting is mainly intended for desktop shell components (e.g.
|
||||||
|
panels) that allow keyboard interaction. Using this option can allow
|
||||||
|
implementing a desktop shell that can be fully usable without the
|
||||||
|
mouse.
|
||||||
|
</description>
|
||||||
|
</entry>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<request name="set_keyboard_interactivity">
|
||||||
|
<description summary="requests keyboard events">
|
||||||
|
Set how keyboard events are delivered to this surface. By default,
|
||||||
|
layer shell surfaces do not receive keyboard events; this request can
|
||||||
|
be used to change this.
|
||||||
|
|
||||||
|
This setting is inherited by child surfaces set by the get_popup
|
||||||
|
request.
|
||||||
|
|
||||||
|
Layer surfaces receive pointer, touch, and tablet events normally. If
|
||||||
|
you do not want to receive them, set the input region on your surface
|
||||||
|
to an empty region.
|
||||||
|
|
||||||
|
Keyboard interactivity is double-buffered, see wl_surface.commit.
|
||||||
|
</description>
|
||||||
|
<arg name="keyboard_interactivity" type="uint" enum="keyboard_interactivity"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="get_popup">
|
||||||
|
<description summary="assign this layer_surface as an xdg_popup parent">
|
||||||
|
This assigns an xdg_popup's parent to this layer_surface. This popup
|
||||||
|
should have been created via xdg_surface::get_popup with the parent set
|
||||||
|
to NULL, and this request must be invoked before committing the popup's
|
||||||
|
initial state.
|
||||||
|
|
||||||
|
See the documentation of xdg_popup for more details about what an
|
||||||
|
xdg_popup is and how it is used.
|
||||||
|
</description>
|
||||||
|
<arg name="popup" type="object" interface="xdg_popup"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="ack_configure">
|
||||||
|
<description summary="ack a configure event">
|
||||||
|
When a configure event is received, if a client commits the
|
||||||
|
surface in response to the configure event, then the client
|
||||||
|
must make an ack_configure request sometime before the commit
|
||||||
|
request, passing along the serial of the configure event.
|
||||||
|
|
||||||
|
If the client receives multiple configure events before it
|
||||||
|
can respond to one, it only has to ack the last configure event.
|
||||||
|
|
||||||
|
A client is not required to commit immediately after sending
|
||||||
|
an ack_configure request - it may even ack_configure several times
|
||||||
|
before its next surface commit.
|
||||||
|
|
||||||
|
A client may send multiple ack_configure requests before committing, but
|
||||||
|
only the last request sent before a commit indicates which configure
|
||||||
|
event the client really is responding to.
|
||||||
|
</description>
|
||||||
|
<arg name="serial" type="uint" summary="the serial from the configure event"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the layer_surface">
|
||||||
|
This request destroys the layer surface.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="configure">
|
||||||
|
<description summary="suggest a surface change">
|
||||||
|
The configure event asks the client to resize its surface.
|
||||||
|
|
||||||
|
Clients should arrange their surface for the new states, and then send
|
||||||
|
an ack_configure request with the serial sent in this configure event at
|
||||||
|
some point before committing the new surface.
|
||||||
|
|
||||||
|
The client is free to dismiss all but the last configure event it
|
||||||
|
received.
|
||||||
|
|
||||||
|
The width and height arguments specify the size of the window in
|
||||||
|
surface-local coordinates.
|
||||||
|
|
||||||
|
The size is a hint, in the sense that the client is free to ignore it if
|
||||||
|
it doesn't resize, pick a smaller size (to satisfy aspect ratio or
|
||||||
|
resize in steps of NxM pixels). If the client picks a smaller size and
|
||||||
|
is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
|
||||||
|
surface will be centered on this axis.
|
||||||
|
|
||||||
|
If the width or height arguments are zero, it means the client should
|
||||||
|
decide its own window dimension.
|
||||||
|
</description>
|
||||||
|
<arg name="serial" type="uint"/>
|
||||||
|
<arg name="width" type="uint"/>
|
||||||
|
<arg name="height" type="uint"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="closed">
|
||||||
|
<description summary="surface should be closed">
|
||||||
|
The closed event is sent by the compositor when the surface will no
|
||||||
|
longer be shown. The output may have been destroyed or the user may
|
||||||
|
have asked for it to be removed. Further changes to the surface will be
|
||||||
|
ignored. The client should destroy the resource after receiving this
|
||||||
|
event, and create a new surface if they so choose.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<enum name="error">
|
||||||
|
<entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
|
||||||
|
<entry name="invalid_size" value="1" summary="size is invalid"/>
|
||||||
|
<entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
|
||||||
|
<entry name="invalid_keyboard_interactivity" value="3" summary="keyboard interactivity is invalid"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<enum name="anchor" bitfield="true">
|
||||||
|
<entry name="top" value="1" summary="the top edge of the anchor rectangle"/>
|
||||||
|
<entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/>
|
||||||
|
<entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
|
||||||
|
<entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<!-- Version 2 additions -->
|
||||||
|
|
||||||
|
<request name="set_layer" since="2">
|
||||||
|
<description summary="change the layer of the surface">
|
||||||
|
Change the layer that the surface is rendered on.
|
||||||
|
|
||||||
|
Layer is double-buffered, see wl_surface.commit.
|
||||||
|
</description>
|
||||||
|
<arg name="layer" type="uint" enum="zwlr_layer_shell_v1.layer" summary="layer to move this surface to"/>
|
||||||
|
</request>
|
||||||
|
</interface>
|
||||||
|
</protocol>
|
35
dwl/util.c
Normal file
35
dwl/util.c
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/* See LICENSE.dwm file for copyright and license details. */
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
void *
|
||||||
|
ecalloc(size_t nmemb, size_t size)
|
||||||
|
{
|
||||||
|
void *p;
|
||||||
|
|
||||||
|
if (!(p = calloc(nmemb, size)))
|
||||||
|
die("calloc:");
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
die(const char *fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
|
||||||
|
fputc(' ', stderr);
|
||||||
|
perror(NULL);
|
||||||
|
} else {
|
||||||
|
fputc('\n', stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(1);
|
||||||
|
}
|
4
dwl/util.h
Normal file
4
dwl/util.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/* See LICENSE.dwm file for copyright and license details. */
|
||||||
|
|
||||||
|
void die(const char *fmt, ...);
|
||||||
|
void *ecalloc(size_t nmemb, size_t size);
|
37
dwm/LICENSE
Normal file
37
dwm/LICENSE
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
MIT/X Consortium License
|
||||||
|
|
||||||
|
© 2006-2019 Anselm R Garbe <anselm@garbe.ca>
|
||||||
|
© 2006-2009 Jukka Salmi <jukka at salmi dot ch>
|
||||||
|
© 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
|
||||||
|
© 2007-2011 Peter Hartlich <sgkkr at hartlich dot com>
|
||||||
|
© 2007-2009 Szabolcs Nagy <nszabolcs at gmail dot com>
|
||||||
|
© 2007-2009 Christof Musik <christof at sendfax dot de>
|
||||||
|
© 2007-2009 Premysl Hruby <dfenze at gmail dot com>
|
||||||
|
© 2007-2008 Enno Gottox Boland <gottox at s01 dot de>
|
||||||
|
© 2008 Martin Hurton <martin dot hurton at gmail dot com>
|
||||||
|
© 2008 Neale Pickett <neale dot woozle dot org>
|
||||||
|
© 2009 Mate Nagy <mnagy at port70 dot net>
|
||||||
|
© 2010-2016 Hiltjo Posthuma <hiltjo@codemadness.org>
|
||||||
|
© 2010-2012 Connor Lane Smith <cls@lubutu.com>
|
||||||
|
© 2011 Christoph Lohmann <20h@r-36.net>
|
||||||
|
© 2015-2016 Quentin Rameau <quinq@fifth.space>
|
||||||
|
© 2015-2016 Eric Pruitt <eric.pruitt@gmail.com>
|
||||||
|
© 2016-2017 Markus Teich <markus.teich@stusta.mhn.de>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
51
dwm/Makefile
Normal file
51
dwm/Makefile
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# dwm - dynamic window manager
|
||||||
|
# See LICENSE file for copyright and license details.
|
||||||
|
|
||||||
|
include config.mk
|
||||||
|
|
||||||
|
SRC = drw.c dwm.c util.c
|
||||||
|
OBJ = ${SRC:.c=.o}
|
||||||
|
|
||||||
|
all: options dwm
|
||||||
|
|
||||||
|
options:
|
||||||
|
@echo dwm build options:
|
||||||
|
@echo "CFLAGS = ${CFLAGS}"
|
||||||
|
@echo "LDFLAGS = ${LDFLAGS}"
|
||||||
|
@echo "CC = ${CC}"
|
||||||
|
|
||||||
|
.c.o:
|
||||||
|
${CC} -c ${CFLAGS} $<
|
||||||
|
|
||||||
|
${OBJ}: config.h config.mk
|
||||||
|
|
||||||
|
config.h:
|
||||||
|
cp config.def.h $@
|
||||||
|
|
||||||
|
dwm: ${OBJ}
|
||||||
|
${CC} -o $@ ${OBJ} ${LDFLAGS}
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz
|
||||||
|
|
||||||
|
dist: clean
|
||||||
|
mkdir -p dwm-${VERSION}
|
||||||
|
cp -R LICENSE Makefile README config.def.h config.mk\
|
||||||
|
dwm.1 drw.h util.h ${SRC} dwm.png transient.c dwm-${VERSION}
|
||||||
|
tar -cf dwm-${VERSION}.tar dwm-${VERSION}
|
||||||
|
gzip dwm-${VERSION}.tar
|
||||||
|
rm -rf dwm-${VERSION}
|
||||||
|
|
||||||
|
install: all
|
||||||
|
mkdir -p ${DESTDIR}${PREFIX}/bin
|
||||||
|
cp -f dwm ${DESTDIR}${PREFIX}/bin
|
||||||
|
chmod 755 ${DESTDIR}${PREFIX}/bin/dwm
|
||||||
|
mkdir -p ${DESTDIR}${MANPREFIX}/man1
|
||||||
|
sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1
|
||||||
|
chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
rm -f ${DESTDIR}${PREFIX}/bin/dwm\
|
||||||
|
${DESTDIR}${MANPREFIX}/man1/dwm.1
|
||||||
|
|
||||||
|
.PHONY: all options clean dist install uninstall
|
48
dwm/README
Normal file
48
dwm/README
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
dwm - dynamic window manager
|
||||||
|
============================
|
||||||
|
dwm is an extremely fast, small, and dynamic window manager for X.
|
||||||
|
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
In order to build dwm you need the Xlib header files.
|
||||||
|
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
Edit config.mk to match your local setup (dwm is installed into
|
||||||
|
the /usr/local namespace by default).
|
||||||
|
|
||||||
|
Afterwards enter the following command to build and install dwm (if
|
||||||
|
necessary as root):
|
||||||
|
|
||||||
|
make clean install
|
||||||
|
|
||||||
|
|
||||||
|
Running dwm
|
||||||
|
-----------
|
||||||
|
Add the following line to your .xinitrc to start dwm using startx:
|
||||||
|
|
||||||
|
exec dwm
|
||||||
|
|
||||||
|
In order to connect dwm to a specific display, make sure that
|
||||||
|
the DISPLAY environment variable is set correctly, e.g.:
|
||||||
|
|
||||||
|
DISPLAY=foo.bar:1 exec dwm
|
||||||
|
|
||||||
|
(This will start dwm on display :1 of the host foo.bar.)
|
||||||
|
|
||||||
|
In order to display status info in the bar, you can do something
|
||||||
|
like this in your .xinitrc:
|
||||||
|
|
||||||
|
while xsetroot -name "`date` `uptime | sed 's/.*,//'`"
|
||||||
|
do
|
||||||
|
sleep 1
|
||||||
|
done &
|
||||||
|
exec dwm
|
||||||
|
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
-------------
|
||||||
|
The configuration of dwm is done by creating a custom config.h
|
||||||
|
and (re)compiling the source code.
|
116
dwm/config.def.h
Normal file
116
dwm/config.def.h
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
|
||||||
|
/* appearance */
|
||||||
|
static const unsigned int borderpx = 1; /* border pixel of windows */
|
||||||
|
static const unsigned int snap = 32; /* snap pixel */
|
||||||
|
static const int showbar = 1; /* 0 means no bar */
|
||||||
|
static const int topbar = 1; /* 0 means bottom bar */
|
||||||
|
static const char *fonts[] = { "monospace:size=10" };
|
||||||
|
static const char dmenufont[] = "monospace:size=10";
|
||||||
|
static const char col_gray1[] = "#222222";
|
||||||
|
static const char col_gray2[] = "#444444";
|
||||||
|
static const char col_gray3[] = "#bbbbbb";
|
||||||
|
static const char col_gray4[] = "#eeeeee";
|
||||||
|
static const char col_cyan[] = "#005577";
|
||||||
|
static const char *colors[][3] = {
|
||||||
|
/* fg bg border */
|
||||||
|
[SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
|
||||||
|
[SchemeSel] = { col_gray4, col_cyan, col_cyan },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* tagging */
|
||||||
|
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
|
||||||
|
|
||||||
|
static const Rule rules[] = {
|
||||||
|
/* xprop(1):
|
||||||
|
* WM_CLASS(STRING) = instance, class
|
||||||
|
* WM_NAME(STRING) = title
|
||||||
|
*/
|
||||||
|
/* class instance title tags mask isfloating monitor */
|
||||||
|
{ "Gimp", NULL, NULL, 0, 1, -1 },
|
||||||
|
{ "Firefox", NULL, NULL, 1 << 8, 0, -1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* layout(s) */
|
||||||
|
static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */
|
||||||
|
static const int nmaster = 1; /* number of clients in master area */
|
||||||
|
static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */
|
||||||
|
static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */
|
||||||
|
|
||||||
|
static const Layout layouts[] = {
|
||||||
|
/* symbol arrange function */
|
||||||
|
{ "[]=", tile }, /* first entry is default */
|
||||||
|
{ "><>", NULL }, /* no layout function means floating behavior */
|
||||||
|
{ "[M]", monocle },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* key definitions */
|
||||||
|
#define MODKEY Mod1Mask
|
||||||
|
#define TAGKEYS(KEY,TAG) \
|
||||||
|
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \
|
||||||
|
{ MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \
|
||||||
|
{ MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \
|
||||||
|
{ MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} },
|
||||||
|
|
||||||
|
/* helper for spawning shell commands in the pre dwm-5.0 fashion */
|
||||||
|
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
|
||||||
|
|
||||||
|
/* commands */
|
||||||
|
static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */
|
||||||
|
static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
|
||||||
|
static const char *termcmd[] = { "st", NULL };
|
||||||
|
|
||||||
|
static Key keys[] = {
|
||||||
|
/* modifier key function argument */
|
||||||
|
{ MODKEY, XK_p, spawn, {.v = dmenucmd } },
|
||||||
|
{ MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
|
||||||
|
{ MODKEY, XK_b, togglebar, {0} },
|
||||||
|
{ MODKEY, XK_j, focusstack, {.i = +1 } },
|
||||||
|
{ MODKEY, XK_k, focusstack, {.i = -1 } },
|
||||||
|
{ MODKEY, XK_i, incnmaster, {.i = +1 } },
|
||||||
|
{ MODKEY, XK_d, incnmaster, {.i = -1 } },
|
||||||
|
{ MODKEY, XK_h, setmfact, {.f = -0.05} },
|
||||||
|
{ MODKEY, XK_l, setmfact, {.f = +0.05} },
|
||||||
|
{ MODKEY, XK_Return, zoom, {0} },
|
||||||
|
{ MODKEY, XK_Tab, view, {0} },
|
||||||
|
{ MODKEY|ShiftMask, XK_c, killclient, {0} },
|
||||||
|
{ MODKEY, XK_t, setlayout, {.v = &layouts[0]} },
|
||||||
|
{ MODKEY, XK_f, setlayout, {.v = &layouts[1]} },
|
||||||
|
{ MODKEY, XK_m, setlayout, {.v = &layouts[2]} },
|
||||||
|
{ MODKEY, XK_space, setlayout, {0} },
|
||||||
|
{ MODKEY|ShiftMask, XK_space, togglefloating, {0} },
|
||||||
|
{ MODKEY, XK_0, view, {.ui = ~0 } },
|
||||||
|
{ MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } },
|
||||||
|
{ MODKEY, XK_comma, focusmon, {.i = -1 } },
|
||||||
|
{ MODKEY, XK_period, focusmon, {.i = +1 } },
|
||||||
|
{ MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
|
||||||
|
{ MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
|
||||||
|
TAGKEYS( XK_1, 0)
|
||||||
|
TAGKEYS( XK_2, 1)
|
||||||
|
TAGKEYS( XK_3, 2)
|
||||||
|
TAGKEYS( XK_4, 3)
|
||||||
|
TAGKEYS( XK_5, 4)
|
||||||
|
TAGKEYS( XK_6, 5)
|
||||||
|
TAGKEYS( XK_7, 6)
|
||||||
|
TAGKEYS( XK_8, 7)
|
||||||
|
TAGKEYS( XK_9, 8)
|
||||||
|
{ MODKEY|ShiftMask, XK_q, quit, {0} },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* button definitions */
|
||||||
|
/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
|
||||||
|
static Button buttons[] = {
|
||||||
|
/* click event mask button function argument */
|
||||||
|
{ ClkLtSymbol, 0, Button1, setlayout, {0} },
|
||||||
|
{ ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },
|
||||||
|
{ ClkWinTitle, 0, Button2, zoom, {0} },
|
||||||
|
{ ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
|
||||||
|
{ ClkClientWin, MODKEY, Button1, movemouse, {0} },
|
||||||
|
{ ClkClientWin, MODKEY, Button2, togglefloating, {0} },
|
||||||
|
{ ClkClientWin, MODKEY, Button3, resizemouse, {0} },
|
||||||
|
{ ClkTagBar, 0, Button1, view, {0} },
|
||||||
|
{ ClkTagBar, 0, Button3, toggleview, {0} },
|
||||||
|
{ ClkTagBar, MODKEY, Button1, tag, {0} },
|
||||||
|
{ ClkTagBar, MODKEY, Button3, toggletag, {0} },
|
||||||
|
};
|
||||||
|
|
149
dwm/config.h
Normal file
149
dwm/config.h
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <X11/XF86keysym.h>
|
||||||
|
|
||||||
|
/* appearance */
|
||||||
|
static const unsigned int borderpx = 1; /* border pixel of windows */
|
||||||
|
static const unsigned int snap = 32; /* snap pixel */
|
||||||
|
static const int showbar = 1; /* 0 means no bar */
|
||||||
|
static const int topbar = 1; /* 0 means bottom bar */
|
||||||
|
static const char *fonts[] = {
|
||||||
|
"JetBrains Mono Nerd Font:pixelsize=14:antialias=true:autohint=true",
|
||||||
|
"Symbola:pixelsize=12:antialias=true:autohint=true",
|
||||||
|
"Inconsolata for Powerline:pixelsize=12:antialias=true:autohint=true",
|
||||||
|
"Liberation Mono:pixelsize=14:antialias=true:autohint=true",
|
||||||
|
};
|
||||||
|
static const char dmenufont[] = "JetBrains Mono Nerd Font:pixelsize=14:antialias=true:autohint=true";
|
||||||
|
static const char col_gray1[] = "#282C34";
|
||||||
|
static const char col_gray2[] = "#444444";
|
||||||
|
static const char col_gray3[] = "#BBBBBB";
|
||||||
|
static const char col_gray4[] = "#DFDFDF";
|
||||||
|
static const char col_cyan[] = "#6E98EB";
|
||||||
|
static const char col_cyan1[] = "#6E98EB";
|
||||||
|
static const char *colors[][3] = {
|
||||||
|
/* fg bg border */
|
||||||
|
[SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
|
||||||
|
[SchemeSel] = { col_gray4, col_cyan, col_cyan1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* tagging */
|
||||||
|
static const char *tags[] = { "1", "2", "3", "4", "5"};
|
||||||
|
|
||||||
|
static const Rule rules[] = {
|
||||||
|
/* xprop(1):
|
||||||
|
* WM_CLASS(STRING) = instance, class
|
||||||
|
* WM_NAME(STRING) = title
|
||||||
|
*/
|
||||||
|
/* class instance title tags mask isfloating monitor */
|
||||||
|
{ "Gimp", NULL, NULL, 0, 1, -1 },
|
||||||
|
{ "Firefox", NULL, NULL, 1 << 8, 0, -1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* layout(s) */
|
||||||
|
static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */
|
||||||
|
static const int nmaster = 1; /* number of clients in master area */
|
||||||
|
static const int resizehints = 0; /* 1 means respect size hints in tiled resizals */
|
||||||
|
static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */
|
||||||
|
|
||||||
|
static const Layout layouts[] = {
|
||||||
|
/* symbol arrange function */
|
||||||
|
{ "[]=", tile }, /* first entry is default */
|
||||||
|
{ "><>", NULL }, /* no layout function means floating behavior */
|
||||||
|
{ "[M]", monocle },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* key definitions */
|
||||||
|
#define MODKEY Mod1Mask
|
||||||
|
// #define ALT Mod1Mask
|
||||||
|
#define SUPER Mod4Mask
|
||||||
|
#define TAGKEYS(KEY,TAG) \
|
||||||
|
{ MODKEY, KEY, view, {.ui = 1 << TAG} }, \
|
||||||
|
{ MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \
|
||||||
|
{ MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \
|
||||||
|
{ MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} },
|
||||||
|
|
||||||
|
/* helper for spawning shell commands in the pre dwm-5.0 fashion */
|
||||||
|
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
|
||||||
|
|
||||||
|
/* commands */
|
||||||
|
static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */
|
||||||
|
static const char *dmenucmd[] = { "dmenu_recent", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
|
||||||
|
static const char *termcmd[] = { "st", NULL };
|
||||||
|
static const char *scrot[] = { "dmenu_scrot", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
|
||||||
|
static const char *media_controls[] = { "dmenu_media", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
|
||||||
|
static const char *volup[] = { "pactl", "set-sink-volume", "@DEFAULT_SINK@", "+5%", NULL };
|
||||||
|
static const char *voldown[] = { "pactl", "set-sink-volume", "@DEFAULT_SINK@", "-5%", NULL };
|
||||||
|
static const char *volmute[] = { "pactl", "set-sink-mute", "@DEFAULT_SINK@", "toggle", NULL };
|
||||||
|
static const char *micmute[] = { "pactl", "set-source-mute", "@DEFAULT_SOURCE@", "toggle", NULL };
|
||||||
|
static const char *brightnessup[] = { "light", "-A", "5.00", NULL };
|
||||||
|
static const char *brightnessdown[] = { "light", "-U", "5.00", NULL };
|
||||||
|
static const char *playpause[] = { "playerctl", "play-pause", NULL };
|
||||||
|
static const char *playnext[] = { "playerctl", "next", NULL };
|
||||||
|
static const char *playprev[] = { "playerctl", "previous", NULL };
|
||||||
|
|
||||||
|
static Key keys[] = {
|
||||||
|
/* modifier key function argument */
|
||||||
|
{ MODKEY, XK_p, spawn, {.v = dmenucmd } },
|
||||||
|
{ MODKEY, XK_Return, spawn, {.v = termcmd } },
|
||||||
|
{ MODKEY, XK_a, spawn, {.v = media_controls } },
|
||||||
|
{ MODKEY, XK_b, togglebar, {0} },
|
||||||
|
{ MODKEY, XK_n, focusstack, {.i = +1 } },
|
||||||
|
{ MODKEY, XK_e, focusstack, {.i = -1 } },
|
||||||
|
{ MODKEY, XK_minus, incnmaster, {.i = +1 } },
|
||||||
|
{ MODKEY, XK_equal, incnmaster, {.i = -1 } },
|
||||||
|
{ MODKEY, XK_h, setmfact, {.f = -0.05} },
|
||||||
|
{ MODKEY, XK_i, setmfact, {.f = +0.05} },
|
||||||
|
{ MODKEY, XK_grave, zoom, {0} },
|
||||||
|
{ MODKEY, XK_Tab, view, {0} },
|
||||||
|
{ MODKEY|ShiftMask, XK_c, killclient, {0} },
|
||||||
|
{ MODKEY, XK_q, killclient, {0} },
|
||||||
|
{ MODKEY, XK_t, setlayout, {.v = &layouts[0]} },
|
||||||
|
{ MODKEY, XK_f, setlayout, {.v = &layouts[1]} },
|
||||||
|
{ MODKEY, XK_m, setlayout, {.v = &layouts[2]} },
|
||||||
|
{ MODKEY, XK_space, setlayout, {0} },
|
||||||
|
{ MODKEY|ShiftMask, XK_space, togglefloating, {0} },
|
||||||
|
{ MODKEY, XK_0, view, {.ui = ~0 } },
|
||||||
|
{ MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } },
|
||||||
|
{ MODKEY, XK_comma, focusmon, {.i = -1 } },
|
||||||
|
{ MODKEY, XK_period, focusmon, {.i = +1 } },
|
||||||
|
{ MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
|
||||||
|
{ MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
|
||||||
|
TAGKEYS( XK_1, 0)
|
||||||
|
TAGKEYS( XK_2, 1)
|
||||||
|
TAGKEYS( XK_3, 2)
|
||||||
|
TAGKEYS( XK_4, 3)
|
||||||
|
TAGKEYS( XK_5, 4)
|
||||||
|
TAGKEYS( XK_6, 5)
|
||||||
|
TAGKEYS( XK_7, 6)
|
||||||
|
TAGKEYS( XK_8, 7)
|
||||||
|
TAGKEYS( XK_9, 8)
|
||||||
|
{ MODKEY|ShiftMask, XK_q, quit, {0} },
|
||||||
|
{ 0, XF86XK_AudioPause, spawn, {.v = playpause } },
|
||||||
|
{ 0, XF86XK_AudioPlay, spawn, {.v = playpause } },
|
||||||
|
{ 0, XF86XK_AudioNext, spawn, {.v = playnext } },
|
||||||
|
{ 0, XF86XK_AudioPrev, spawn, {.v = playprev } },
|
||||||
|
{ 0, XF86XK_AudioRaiseVolume, spawn, {.v = volup } },
|
||||||
|
{ 0, XF86XK_AudioLowerVolume, spawn, {.v = voldown } },
|
||||||
|
{ 0, XF86XK_AudioMute, spawn, {.v = volmute } },
|
||||||
|
{ 0, XF86XK_AudioMicMute, spawn, {.v = micmute } },
|
||||||
|
{ 0, XF86XK_MonBrightnessUp, spawn, {.v = brightnessup } },
|
||||||
|
{ 0, XF86XK_MonBrightnessDown, spawn, {.v = brightnessdown } },
|
||||||
|
{ 0, XK_Print, spawn, {.v = scrot } },
|
||||||
|
};
|
||||||
|
|
||||||
|
/* button definitions */
|
||||||
|
/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
|
||||||
|
static Button buttons[] = {
|
||||||
|
/* click event mask button function argument */
|
||||||
|
{ ClkLtSymbol, 0, Button1, setlayout, {0} },
|
||||||
|
{ ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },
|
||||||
|
{ ClkWinTitle, 0, Button2, zoom, {0} },
|
||||||
|
{ ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
|
||||||
|
{ ClkClientWin, MODKEY, Button1, movemouse, {0} },
|
||||||
|
{ ClkClientWin, MODKEY, Button2, togglefloating, {0} },
|
||||||
|
{ ClkClientWin, MODKEY, Button3, resizemouse, {0} },
|
||||||
|
{ ClkTagBar, 0, Button1, view, {0} },
|
||||||
|
{ ClkTagBar, 0, Button3, toggleview, {0} },
|
||||||
|
{ ClkTagBar, MODKEY, Button1, tag, {0} },
|
||||||
|
{ ClkTagBar, MODKEY, Button3, toggletag, {0} },
|
||||||
|
};
|
||||||
|
|
38
dwm/config.mk
Normal file
38
dwm/config.mk
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# dwm version
|
||||||
|
VERSION = 6.3
|
||||||
|
|
||||||
|
# Customize below to fit your system
|
||||||
|
|
||||||
|
# paths
|
||||||
|
PREFIX = /usr/local
|
||||||
|
MANPREFIX = ${PREFIX}/share/man
|
||||||
|
|
||||||
|
X11INC = /usr/X11R6/include
|
||||||
|
X11LIB = /usr/X11R6/lib
|
||||||
|
|
||||||
|
# Xinerama, comment if you don't want it
|
||||||
|
XINERAMALIBS = -lXinerama
|
||||||
|
XINERAMAFLAGS = -DXINERAMA
|
||||||
|
|
||||||
|
# freetype
|
||||||
|
FREETYPELIBS = -lfontconfig -lXft
|
||||||
|
FREETYPEINC = /usr/include/freetype2
|
||||||
|
# OpenBSD (uncomment)
|
||||||
|
#FREETYPEINC = ${X11INC}/freetype2
|
||||||
|
|
||||||
|
# includes and libs
|
||||||
|
INCS = -I${X11INC} -I${FREETYPEINC}
|
||||||
|
LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
|
||||||
|
|
||||||
|
# flags
|
||||||
|
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
|
||||||
|
#CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS}
|
||||||
|
CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS}
|
||||||
|
LDFLAGS = ${LIBS}
|
||||||
|
|
||||||
|
# Solaris
|
||||||
|
#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\"
|
||||||
|
#LDFLAGS = ${LIBS}
|
||||||
|
|
||||||
|
# compiler and linker
|
||||||
|
CC = cc
|
436
dwm/drw.c
Normal file
436
dwm/drw.c
Normal file
@ -0,0 +1,436 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xft/Xft.h>
|
||||||
|
|
||||||
|
#include "drw.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#define UTF_INVALID 0xFFFD
|
||||||
|
#define UTF_SIZ 4
|
||||||
|
|
||||||
|
static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
|
||||||
|
static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
|
||||||
|
static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};
|
||||||
|
static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
|
||||||
|
|
||||||
|
static long
|
||||||
|
utf8decodebyte(const char c, size_t *i)
|
||||||
|
{
|
||||||
|
for (*i = 0; *i < (UTF_SIZ + 1); ++(*i))
|
||||||
|
if (((unsigned char)c & utfmask[*i]) == utfbyte[*i])
|
||||||
|
return (unsigned char)c & ~utfmask[*i];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
utf8validate(long *u, size_t i)
|
||||||
|
{
|
||||||
|
if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
|
||||||
|
*u = UTF_INVALID;
|
||||||
|
for (i = 1; *u > utfmax[i]; ++i)
|
||||||
|
;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
utf8decode(const char *c, long *u, size_t clen)
|
||||||
|
{
|
||||||
|
size_t i, j, len, type;
|
||||||
|
long udecoded;
|
||||||
|
|
||||||
|
*u = UTF_INVALID;
|
||||||
|
if (!clen)
|
||||||
|
return 0;
|
||||||
|
udecoded = utf8decodebyte(c[0], &len);
|
||||||
|
if (!BETWEEN(len, 1, UTF_SIZ))
|
||||||
|
return 1;
|
||||||
|
for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
|
||||||
|
udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
|
||||||
|
if (type)
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
if (j < len)
|
||||||
|
return 0;
|
||||||
|
*u = udecoded;
|
||||||
|
utf8validate(u, len);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
Drw *
|
||||||
|
drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
|
||||||
|
{
|
||||||
|
Drw *drw = ecalloc(1, sizeof(Drw));
|
||||||
|
|
||||||
|
drw->dpy = dpy;
|
||||||
|
drw->screen = screen;
|
||||||
|
drw->root = root;
|
||||||
|
drw->w = w;
|
||||||
|
drw->h = h;
|
||||||
|
drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
|
||||||
|
drw->gc = XCreateGC(dpy, root, 0, NULL);
|
||||||
|
XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
|
||||||
|
|
||||||
|
return drw;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
drw_resize(Drw *drw, unsigned int w, unsigned int h)
|
||||||
|
{
|
||||||
|
if (!drw)
|
||||||
|
return;
|
||||||
|
|
||||||
|
drw->w = w;
|
||||||
|
drw->h = h;
|
||||||
|
if (drw->drawable)
|
||||||
|
XFreePixmap(drw->dpy, drw->drawable);
|
||||||
|
drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
drw_free(Drw *drw)
|
||||||
|
{
|
||||||
|
XFreePixmap(drw->dpy, drw->drawable);
|
||||||
|
XFreeGC(drw->dpy, drw->gc);
|
||||||
|
drw_fontset_free(drw->fonts);
|
||||||
|
free(drw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function is an implementation detail. Library users should use
|
||||||
|
* drw_fontset_create instead.
|
||||||
|
*/
|
||||||
|
static Fnt *
|
||||||
|
xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
|
||||||
|
{
|
||||||
|
Fnt *font;
|
||||||
|
XftFont *xfont = NULL;
|
||||||
|
FcPattern *pattern = NULL;
|
||||||
|
|
||||||
|
if (fontname) {
|
||||||
|
/* Using the pattern found at font->xfont->pattern does not yield the
|
||||||
|
* same substitution results as using the pattern returned by
|
||||||
|
* FcNameParse; using the latter results in the desired fallback
|
||||||
|
* behaviour whereas the former just results in missing-character
|
||||||
|
* rectangles being drawn, at least with some fonts. */
|
||||||
|
if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) {
|
||||||
|
fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!(pattern = FcNameParse((FcChar8 *) fontname))) {
|
||||||
|
fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname);
|
||||||
|
XftFontClose(drw->dpy, xfont);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
} else if (fontpattern) {
|
||||||
|
if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
|
||||||
|
fprintf(stderr, "error, cannot load font from pattern.\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
die("no font specified.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do not allow using color fonts. This is a workaround for a BadLength
|
||||||
|
* error from Xft with color glyphs. Modelled on the Xterm workaround. See
|
||||||
|
* https://bugzilla.redhat.com/show_bug.cgi?id=1498269
|
||||||
|
* https://lists.suckless.org/dev/1701/30932.html
|
||||||
|
* https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349
|
||||||
|
* and lots more all over the internet.
|
||||||
|
*/
|
||||||
|
FcBool iscol;
|
||||||
|
if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) {
|
||||||
|
XftFontClose(drw->dpy, xfont);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
font = ecalloc(1, sizeof(Fnt));
|
||||||
|
font->xfont = xfont;
|
||||||
|
font->pattern = pattern;
|
||||||
|
font->h = xfont->ascent + xfont->descent;
|
||||||
|
font->dpy = drw->dpy;
|
||||||
|
|
||||||
|
return font;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
xfont_free(Fnt *font)
|
||||||
|
{
|
||||||
|
if (!font)
|
||||||
|
return;
|
||||||
|
if (font->pattern)
|
||||||
|
FcPatternDestroy(font->pattern);
|
||||||
|
XftFontClose(font->dpy, font->xfont);
|
||||||
|
free(font);
|
||||||
|
}
|
||||||
|
|
||||||
|
Fnt*
|
||||||
|
drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount)
|
||||||
|
{
|
||||||
|
Fnt *cur, *ret = NULL;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (!drw || !fonts)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
for (i = 1; i <= fontcount; i++) {
|
||||||
|
if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) {
|
||||||
|
cur->next = ret;
|
||||||
|
ret = cur;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (drw->fonts = ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
drw_fontset_free(Fnt *font)
|
||||||
|
{
|
||||||
|
if (font) {
|
||||||
|
drw_fontset_free(font->next);
|
||||||
|
xfont_free(font);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
|
||||||
|
{
|
||||||
|
if (!drw || !dest || !clrname)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
|
||||||
|
DefaultColormap(drw->dpy, drw->screen),
|
||||||
|
clrname, dest))
|
||||||
|
die("error, cannot allocate color '%s'", clrname);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wrapper to create color schemes. The caller has to call free(3) on the
|
||||||
|
* returned color scheme when done using it. */
|
||||||
|
Clr *
|
||||||
|
drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
Clr *ret;
|
||||||
|
|
||||||
|
/* need at least two colors for a scheme */
|
||||||
|
if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor))))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
for (i = 0; i < clrcount; i++)
|
||||||
|
drw_clr_create(drw, &ret[i], clrnames[i]);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
drw_setfontset(Drw *drw, Fnt *set)
|
||||||
|
{
|
||||||
|
if (drw)
|
||||||
|
drw->fonts = set;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
drw_setscheme(Drw *drw, Clr *scm)
|
||||||
|
{
|
||||||
|
if (drw)
|
||||||
|
drw->scheme = scm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert)
|
||||||
|
{
|
||||||
|
if (!drw || !drw->scheme)
|
||||||
|
return;
|
||||||
|
XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel);
|
||||||
|
if (filled)
|
||||||
|
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
|
||||||
|
else
|
||||||
|
XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
|
||||||
|
{
|
||||||
|
char buf[1024];
|
||||||
|
int ty;
|
||||||
|
unsigned int ew;
|
||||||
|
XftDraw *d = NULL;
|
||||||
|
Fnt *usedfont, *curfont, *nextfont;
|
||||||
|
size_t i, len;
|
||||||
|
int utf8strlen, utf8charlen, render = x || y || w || h;
|
||||||
|
long utf8codepoint = 0;
|
||||||
|
const char *utf8str;
|
||||||
|
FcCharSet *fccharset;
|
||||||
|
FcPattern *fcpattern;
|
||||||
|
FcPattern *match;
|
||||||
|
XftResult result;
|
||||||
|
int charexists = 0;
|
||||||
|
|
||||||
|
if (!drw || (render && !drw->scheme) || !text || !drw->fonts)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!render) {
|
||||||
|
w = ~w;
|
||||||
|
} else {
|
||||||
|
XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
|
||||||
|
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
|
||||||
|
d = XftDrawCreate(drw->dpy, drw->drawable,
|
||||||
|
DefaultVisual(drw->dpy, drw->screen),
|
||||||
|
DefaultColormap(drw->dpy, drw->screen));
|
||||||
|
x += lpad;
|
||||||
|
w -= lpad;
|
||||||
|
}
|
||||||
|
|
||||||
|
usedfont = drw->fonts;
|
||||||
|
while (1) {
|
||||||
|
utf8strlen = 0;
|
||||||
|
utf8str = text;
|
||||||
|
nextfont = NULL;
|
||||||
|
while (*text) {
|
||||||
|
utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
|
||||||
|
for (curfont = drw->fonts; curfont; curfont = curfont->next) {
|
||||||
|
charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
|
||||||
|
if (charexists) {
|
||||||
|
if (curfont == usedfont) {
|
||||||
|
utf8strlen += utf8charlen;
|
||||||
|
text += utf8charlen;
|
||||||
|
} else {
|
||||||
|
nextfont = curfont;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!charexists || nextfont)
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
charexists = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (utf8strlen) {
|
||||||
|
drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL);
|
||||||
|
/* shorten text if necessary */
|
||||||
|
for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--)
|
||||||
|
drw_font_getexts(usedfont, utf8str, len, &ew, NULL);
|
||||||
|
|
||||||
|
if (len) {
|
||||||
|
memcpy(buf, utf8str, len);
|
||||||
|
buf[len] = '\0';
|
||||||
|
if (len < utf8strlen)
|
||||||
|
for (i = len; i && i > len - 3; buf[--i] = '.')
|
||||||
|
; /* NOP */
|
||||||
|
|
||||||
|
if (render) {
|
||||||
|
ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
|
||||||
|
XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
|
||||||
|
usedfont->xfont, x, ty, (XftChar8 *)buf, len);
|
||||||
|
}
|
||||||
|
x += ew;
|
||||||
|
w -= ew;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!*text) {
|
||||||
|
break;
|
||||||
|
} else if (nextfont) {
|
||||||
|
charexists = 0;
|
||||||
|
usedfont = nextfont;
|
||||||
|
} else {
|
||||||
|
/* Regardless of whether or not a fallback font is found, the
|
||||||
|
* character must be drawn. */
|
||||||
|
charexists = 1;
|
||||||
|
|
||||||
|
fccharset = FcCharSetCreate();
|
||||||
|
FcCharSetAddChar(fccharset, utf8codepoint);
|
||||||
|
|
||||||
|
if (!drw->fonts->pattern) {
|
||||||
|
/* Refer to the comment in xfont_create for more information. */
|
||||||
|
die("the first font in the cache must be loaded from a font string.");
|
||||||
|
}
|
||||||
|
|
||||||
|
fcpattern = FcPatternDuplicate(drw->fonts->pattern);
|
||||||
|
FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
|
||||||
|
FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
|
||||||
|
FcPatternAddBool(fcpattern, FC_COLOR, FcFalse);
|
||||||
|
|
||||||
|
FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
|
||||||
|
FcDefaultSubstitute(fcpattern);
|
||||||
|
match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result);
|
||||||
|
|
||||||
|
FcCharSetDestroy(fccharset);
|
||||||
|
FcPatternDestroy(fcpattern);
|
||||||
|
|
||||||
|
if (match) {
|
||||||
|
usedfont = xfont_create(drw, NULL, match);
|
||||||
|
if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) {
|
||||||
|
for (curfont = drw->fonts; curfont->next; curfont = curfont->next)
|
||||||
|
; /* NOP */
|
||||||
|
curfont->next = usedfont;
|
||||||
|
} else {
|
||||||
|
xfont_free(usedfont);
|
||||||
|
usedfont = drw->fonts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (d)
|
||||||
|
XftDrawDestroy(d);
|
||||||
|
|
||||||
|
return x + (render ? w : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
|
||||||
|
{
|
||||||
|
if (!drw)
|
||||||
|
return;
|
||||||
|
|
||||||
|
XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
|
||||||
|
XSync(drw->dpy, False);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int
|
||||||
|
drw_fontset_getwidth(Drw *drw, const char *text)
|
||||||
|
{
|
||||||
|
if (!drw || !drw->fonts || !text)
|
||||||
|
return 0;
|
||||||
|
return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h)
|
||||||
|
{
|
||||||
|
XGlyphInfo ext;
|
||||||
|
|
||||||
|
if (!font || !text)
|
||||||
|
return;
|
||||||
|
|
||||||
|
XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
|
||||||
|
if (w)
|
||||||
|
*w = ext.xOff;
|
||||||
|
if (h)
|
||||||
|
*h = font->h;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cur *
|
||||||
|
drw_cur_create(Drw *drw, int shape)
|
||||||
|
{
|
||||||
|
Cur *cur;
|
||||||
|
|
||||||
|
if (!drw || !(cur = ecalloc(1, sizeof(Cur))))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
cur->cursor = XCreateFontCursor(drw->dpy, shape);
|
||||||
|
|
||||||
|
return cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
drw_cur_free(Drw *drw, Cur *cursor)
|
||||||
|
{
|
||||||
|
if (!cursor)
|
||||||
|
return;
|
||||||
|
|
||||||
|
XFreeCursor(drw->dpy, cursor->cursor);
|
||||||
|
free(cursor);
|
||||||
|
}
|
57
dwm/drw.h
Normal file
57
dwm/drw.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Cursor cursor;
|
||||||
|
} Cur;
|
||||||
|
|
||||||
|
typedef struct Fnt {
|
||||||
|
Display *dpy;
|
||||||
|
unsigned int h;
|
||||||
|
XftFont *xfont;
|
||||||
|
FcPattern *pattern;
|
||||||
|
struct Fnt *next;
|
||||||
|
} Fnt;
|
||||||
|
|
||||||
|
enum { ColFg, ColBg, ColBorder }; /* Clr scheme index */
|
||||||
|
typedef XftColor Clr;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned int w, h;
|
||||||
|
Display *dpy;
|
||||||
|
int screen;
|
||||||
|
Window root;
|
||||||
|
Drawable drawable;
|
||||||
|
GC gc;
|
||||||
|
Clr *scheme;
|
||||||
|
Fnt *fonts;
|
||||||
|
} Drw;
|
||||||
|
|
||||||
|
/* Drawable abstraction */
|
||||||
|
Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h);
|
||||||
|
void drw_resize(Drw *drw, unsigned int w, unsigned int h);
|
||||||
|
void drw_free(Drw *drw);
|
||||||
|
|
||||||
|
/* Fnt abstraction */
|
||||||
|
Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount);
|
||||||
|
void drw_fontset_free(Fnt* set);
|
||||||
|
unsigned int drw_fontset_getwidth(Drw *drw, const char *text);
|
||||||
|
void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h);
|
||||||
|
|
||||||
|
/* Colorscheme abstraction */
|
||||||
|
void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
|
||||||
|
Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
|
||||||
|
|
||||||
|
/* Cursor abstraction */
|
||||||
|
Cur *drw_cur_create(Drw *drw, int shape);
|
||||||
|
void drw_cur_free(Drw *drw, Cur *cursor);
|
||||||
|
|
||||||
|
/* Drawing context manipulation */
|
||||||
|
void drw_setfontset(Drw *drw, Fnt *set);
|
||||||
|
void drw_setscheme(Drw *drw, Clr *scm);
|
||||||
|
|
||||||
|
/* Drawing functions */
|
||||||
|
void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert);
|
||||||
|
int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert);
|
||||||
|
|
||||||
|
/* Map functions */
|
||||||
|
void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);
|
176
dwm/dwm.1
Normal file
176
dwm/dwm.1
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
.TH DWM 1 dwm\-VERSION
|
||||||
|
.SH NAME
|
||||||
|
dwm \- dynamic window manager
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B dwm
|
||||||
|
.RB [ \-v ]
|
||||||
|
.SH DESCRIPTION
|
||||||
|
dwm is a dynamic window manager for X. It manages windows in tiled, monocle
|
||||||
|
and floating layouts. Either layout can be applied dynamically, optimising the
|
||||||
|
environment for the application in use and the task performed.
|
||||||
|
.P
|
||||||
|
In tiled layouts windows are managed in a master and stacking area. The master
|
||||||
|
area on the left contains one window by default, and the stacking area on the
|
||||||
|
right contains all other windows. The number of master area windows can be
|
||||||
|
adjusted from zero to an arbitrary number. In monocle layout all windows are
|
||||||
|
maximised to the screen size. In floating layout windows can be resized and
|
||||||
|
moved freely. Dialog windows are always managed floating, regardless of the
|
||||||
|
layout applied.
|
||||||
|
.P
|
||||||
|
Windows are grouped by tags. Each window can be tagged with one or multiple
|
||||||
|
tags. Selecting certain tags displays all windows with these tags.
|
||||||
|
.P
|
||||||
|
Each screen contains a small status bar which displays all available tags, the
|
||||||
|
layout, the title of the focused window, and the text read from the root window
|
||||||
|
name property, if the screen is focused. A floating window is indicated with an
|
||||||
|
empty square and a maximised floating window is indicated with a filled square
|
||||||
|
before the windows title. The selected tags are indicated with a different
|
||||||
|
color. The tags of the focused window are indicated with a filled square in the
|
||||||
|
top left corner. The tags which are applied to one or more windows are
|
||||||
|
indicated with an empty square in the top left corner.
|
||||||
|
.P
|
||||||
|
dwm draws a small border around windows to indicate the focus state.
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
.B \-v
|
||||||
|
prints version information to stderr, then exits.
|
||||||
|
.SH USAGE
|
||||||
|
.SS Status bar
|
||||||
|
.TP
|
||||||
|
.B X root window name
|
||||||
|
is read and displayed in the status text area. It can be set with the
|
||||||
|
.BR xsetroot (1)
|
||||||
|
command.
|
||||||
|
.TP
|
||||||
|
.B Button1
|
||||||
|
click on a tag label to display all windows with that tag, click on the layout
|
||||||
|
label toggles between tiled and floating layout.
|
||||||
|
.TP
|
||||||
|
.B Button3
|
||||||
|
click on a tag label adds/removes all windows with that tag to/from the view.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-Button1
|
||||||
|
click on a tag label applies that tag to the focused window.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-Button3
|
||||||
|
click on a tag label adds/removes that tag to/from the focused window.
|
||||||
|
.SS Keyboard commands
|
||||||
|
.TP
|
||||||
|
.B Mod1\-Shift\-Return
|
||||||
|
Start
|
||||||
|
.BR st(1).
|
||||||
|
.TP
|
||||||
|
.B Mod1\-p
|
||||||
|
Spawn
|
||||||
|
.BR dmenu(1)
|
||||||
|
for launching other programs.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-,
|
||||||
|
Focus previous screen, if any.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-.
|
||||||
|
Focus next screen, if any.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-Shift\-,
|
||||||
|
Send focused window to previous screen, if any.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-Shift\-.
|
||||||
|
Send focused window to next screen, if any.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-b
|
||||||
|
Toggles bar on and off.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-t
|
||||||
|
Sets tiled layout.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-f
|
||||||
|
Sets floating layout.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-m
|
||||||
|
Sets monocle layout.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-space
|
||||||
|
Toggles between current and previous layout.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-j
|
||||||
|
Focus next window.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-k
|
||||||
|
Focus previous window.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-i
|
||||||
|
Increase number of windows in master area.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-d
|
||||||
|
Decrease number of windows in master area.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-l
|
||||||
|
Increase master area size.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-h
|
||||||
|
Decrease master area size.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-Return
|
||||||
|
Zooms/cycles focused window to/from master area (tiled layouts only).
|
||||||
|
.TP
|
||||||
|
.B Mod1\-Shift\-c
|
||||||
|
Close focused window.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-Shift\-space
|
||||||
|
Toggle focused window between tiled and floating state.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-Tab
|
||||||
|
Toggles to the previously selected tags.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-Shift\-[1..n]
|
||||||
|
Apply nth tag to focused window.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-Shift\-0
|
||||||
|
Apply all tags to focused window.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-Control\-Shift\-[1..n]
|
||||||
|
Add/remove nth tag to/from focused window.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-[1..n]
|
||||||
|
View all windows with nth tag.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-0
|
||||||
|
View all windows with any tag.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-Control\-[1..n]
|
||||||
|
Add/remove all windows with nth tag to/from the view.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-Shift\-q
|
||||||
|
Quit dwm.
|
||||||
|
.SS Mouse commands
|
||||||
|
.TP
|
||||||
|
.B Mod1\-Button1
|
||||||
|
Move focused window while dragging. Tiled windows will be toggled to the floating state.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-Button2
|
||||||
|
Toggles focused window between floating and tiled state.
|
||||||
|
.TP
|
||||||
|
.B Mod1\-Button3
|
||||||
|
Resize focused window while dragging. Tiled windows will be toggled to the floating state.
|
||||||
|
.SH CUSTOMIZATION
|
||||||
|
dwm is customized by creating a custom config.h and (re)compiling the source
|
||||||
|
code. This keeps it fast, secure and simple.
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR dmenu (1),
|
||||||
|
.BR st (1)
|
||||||
|
.SH ISSUES
|
||||||
|
Java applications which use the XToolkit/XAWT backend may draw grey windows
|
||||||
|
only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early
|
||||||
|
JDK 1.6 versions, because it assumes a reparenting window manager. Possible workarounds
|
||||||
|
are using JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or setting the
|
||||||
|
environment variable
|
||||||
|
.BR AWT_TOOLKIT=MToolkit
|
||||||
|
(to use the older Motif backend instead) or running
|
||||||
|
.B xprop -root -f _NET_WM_NAME 32a -set _NET_WM_NAME LG3D
|
||||||
|
or
|
||||||
|
.B wmname LG3D
|
||||||
|
(to pretend that a non-reparenting window manager is running that the
|
||||||
|
XToolkit/XAWT backend can recognize) or when using OpenJDK setting the environment variable
|
||||||
|
.BR _JAVA_AWT_WM_NONREPARENTING=1 .
|
||||||
|
.SH BUGS
|
||||||
|
Send all bug reports with a patch to hackers@suckless.org.
|
BIN
dwm/dwm.png
Normal file
BIN
dwm/dwm.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 373 B |
12
dwm/patches/dwm-alwayscenter-20200625-f04cac6.diff
Normal file
12
dwm/patches/dwm-alwayscenter-20200625-f04cac6.diff
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
diff -up dwm/dwm.c dwmmod/dwm.c
|
||||||
|
--- dwm/dwm.c 2020-06-25 00:21:30.383692180 -0300
|
||||||
|
+++ dwmmod/dwm.c 2020-06-25 00:20:35.643692330 -0300
|
||||||
|
@@ -1057,6 +1057,8 @@ manage(Window w, XWindowAttributes *wa)
|
||||||
|
updatewindowtype(c);
|
||||||
|
updatesizehints(c);
|
||||||
|
updatewmhints(c);
|
||||||
|
+ c->x = c->mon->mx + (c->mon->mw - WIDTH(c)) / 2;
|
||||||
|
+ c->y = c->mon->my + (c->mon->mh - HEIGHT(c)) / 2;
|
||||||
|
XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
|
||||||
|
grabbuttons(c, 0);
|
||||||
|
if (!c->isfloating)
|
32
dwm/patches/dwm-cursorwarp-20210222-61bb8b2.diff
Normal file
32
dwm/patches/dwm-cursorwarp-20210222-61bb8b2.diff
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
From 1e4936416fa5517fe447e3388e1d44c913e29f56 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Markus Dam <markus.dam123@gmail.com>
|
||||||
|
Date: Mon, 22 Feb 2021 13:40:13 +0100
|
||||||
|
Subject: [PATCH] Add cursor warping
|
||||||
|
|
||||||
|
---
|
||||||
|
dwm.c | 3 +++
|
||||||
|
1 file changed, 3 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/dwm.c b/dwm.c
|
||||||
|
index 331a309..c80ef8a 100644
|
||||||
|
--- a/dwm.c
|
||||||
|
+++ b/dwm.c
|
||||||
|
@@ -882,6 +882,8 @@ focusmon(const Arg *arg)
|
||||||
|
unfocus(selmon->sel, 0);
|
||||||
|
selmon = m;
|
||||||
|
focus(NULL);
|
||||||
|
+ if (selmon->sel)
|
||||||
|
+ XWarpPointer(dpy, None, selmon->sel->win, 0, 0, 0, 0, selmon->sel->w/2, selmon->sel->h/2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
@@ -906,6 +908,7 @@ focusstack(const Arg *arg)
|
||||||
|
if (c) {
|
||||||
|
focus(c);
|
||||||
|
restack(selmon);
|
||||||
|
+ XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w/2, c->h/2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
--
|
||||||
|
2.30.1
|
177
dwm/patches/dwm-pertag-20200914-61bb8b2.diff
Normal file
177
dwm/patches/dwm-pertag-20200914-61bb8b2.diff
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
diff --git a/dwm.c b/dwm.c
|
||||||
|
index 664c527..ac8e4ec 100644
|
||||||
|
--- a/dwm.c
|
||||||
|
+++ b/dwm.c
|
||||||
|
@@ -111,6 +111,7 @@ typedef struct {
|
||||||
|
void (*arrange)(Monitor *);
|
||||||
|
} Layout;
|
||||||
|
|
||||||
|
+typedef struct Pertag Pertag;
|
||||||
|
struct Monitor {
|
||||||
|
char ltsymbol[16];
|
||||||
|
float mfact;
|
||||||
|
@@ -130,6 +131,7 @@ struct Monitor {
|
||||||
|
Monitor *next;
|
||||||
|
Window barwin;
|
||||||
|
const Layout *lt[2];
|
||||||
|
+ Pertag *pertag;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
@@ -272,6 +274,15 @@ static Window root, wmcheckwin;
|
||||||
|
/* configuration, allows nested code to access above variables */
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
+struct Pertag {
|
||||||
|
+ unsigned int curtag, prevtag; /* current and previous tag */
|
||||||
|
+ int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */
|
||||||
|
+ float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */
|
||||||
|
+ unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */
|
||||||
|
+ const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */
|
||||||
|
+ int showbars[LENGTH(tags) + 1]; /* display bar for the current tag */
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
/* compile-time check if all tags fit into an unsigned int bit array. */
|
||||||
|
struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
|
||||||
|
|
||||||
|
@@ -632,6 +643,7 @@ Monitor *
|
||||||
|
createmon(void)
|
||||||
|
{
|
||||||
|
Monitor *m;
|
||||||
|
+ unsigned int i;
|
||||||
|
|
||||||
|
m = ecalloc(1, sizeof(Monitor));
|
||||||
|
m->tagset[0] = m->tagset[1] = 1;
|
||||||
|
@@ -642,6 +654,20 @@ createmon(void)
|
||||||
|
m->lt[0] = &layouts[0];
|
||||||
|
m->lt[1] = &layouts[1 % LENGTH(layouts)];
|
||||||
|
strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
|
||||||
|
+ m->pertag = ecalloc(1, sizeof(Pertag));
|
||||||
|
+ m->pertag->curtag = m->pertag->prevtag = 1;
|
||||||
|
+
|
||||||
|
+ for (i = 0; i <= LENGTH(tags); i++) {
|
||||||
|
+ m->pertag->nmasters[i] = m->nmaster;
|
||||||
|
+ m->pertag->mfacts[i] = m->mfact;
|
||||||
|
+
|
||||||
|
+ m->pertag->ltidxs[i][0] = m->lt[0];
|
||||||
|
+ m->pertag->ltidxs[i][1] = m->lt[1];
|
||||||
|
+ m->pertag->sellts[i] = m->sellt;
|
||||||
|
+
|
||||||
|
+ m->pertag->showbars[i] = m->showbar;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -967,7 +993,7 @@ grabkeys(void)
|
||||||
|
void
|
||||||
|
incnmaster(const Arg *arg)
|
||||||
|
{
|
||||||
|
- selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
|
||||||
|
+ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0);
|
||||||
|
arrange(selmon);
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1502,9 +1528,9 @@ void
|
||||||
|
setlayout(const Arg *arg)
|
||||||
|
{
|
||||||
|
if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
|
||||||
|
- selmon->sellt ^= 1;
|
||||||
|
+ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag] ^= 1;
|
||||||
|
if (arg && arg->v)
|
||||||
|
- selmon->lt[selmon->sellt] = (Layout *)arg->v;
|
||||||
|
+ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v;
|
||||||
|
strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);
|
||||||
|
if (selmon->sel)
|
||||||
|
arrange(selmon);
|
||||||
|
@@ -1523,7 +1549,7 @@ setmfact(const Arg *arg)
|
||||||
|
f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
|
||||||
|
if (f < 0.05 || f > 0.95)
|
||||||
|
return;
|
||||||
|
- selmon->mfact = f;
|
||||||
|
+ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f;
|
||||||
|
arrange(selmon);
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1702,7 +1728,7 @@ tile(Monitor *m)
|
||||||
|
void
|
||||||
|
togglebar(const Arg *arg)
|
||||||
|
{
|
||||||
|
- selmon->showbar = !selmon->showbar;
|
||||||
|
+ selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar;
|
||||||
|
updatebarpos(selmon);
|
||||||
|
XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
|
||||||
|
arrange(selmon);
|
||||||
|
@@ -1741,9 +1767,33 @@ void
|
||||||
|
toggleview(const Arg *arg)
|
||||||
|
{
|
||||||
|
unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
|
||||||
|
+ int i;
|
||||||
|
|
||||||
|
if (newtagset) {
|
||||||
|
selmon->tagset[selmon->seltags] = newtagset;
|
||||||
|
+
|
||||||
|
+ if (newtagset == ~0) {
|
||||||
|
+ selmon->pertag->prevtag = selmon->pertag->curtag;
|
||||||
|
+ selmon->pertag->curtag = 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* test if the user did not select the same tag */
|
||||||
|
+ if (!(newtagset & 1 << (selmon->pertag->curtag - 1))) {
|
||||||
|
+ selmon->pertag->prevtag = selmon->pertag->curtag;
|
||||||
|
+ for (i = 0; !(newtagset & 1 << i); i++) ;
|
||||||
|
+ selmon->pertag->curtag = i + 1;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* apply settings for this view */
|
||||||
|
+ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
|
||||||
|
+ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
|
||||||
|
+ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
|
||||||
|
+ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
|
||||||
|
+ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
|
||||||
|
+
|
||||||
|
+ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag])
|
||||||
|
+ togglebar(NULL);
|
||||||
|
+
|
||||||
|
focus(NULL);
|
||||||
|
arrange(selmon);
|
||||||
|
}
|
||||||
|
@@ -2038,11 +2088,37 @@ updatewmhints(Client *c)
|
||||||
|
void
|
||||||
|
view(const Arg *arg)
|
||||||
|
{
|
||||||
|
+ int i;
|
||||||
|
+ unsigned int tmptag;
|
||||||
|
+
|
||||||
|
if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
|
||||||
|
return;
|
||||||
|
selmon->seltags ^= 1; /* toggle sel tagset */
|
||||||
|
- if (arg->ui & TAGMASK)
|
||||||
|
+ if (arg->ui & TAGMASK) {
|
||||||
|
selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
|
||||||
|
+ selmon->pertag->prevtag = selmon->pertag->curtag;
|
||||||
|
+
|
||||||
|
+ if (arg->ui == ~0)
|
||||||
|
+ selmon->pertag->curtag = 0;
|
||||||
|
+ else {
|
||||||
|
+ for (i = 0; !(arg->ui & 1 << i); i++) ;
|
||||||
|
+ selmon->pertag->curtag = i + 1;
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ tmptag = selmon->pertag->prevtag;
|
||||||
|
+ selmon->pertag->prevtag = selmon->pertag->curtag;
|
||||||
|
+ selmon->pertag->curtag = tmptag;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag];
|
||||||
|
+ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag];
|
||||||
|
+ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag];
|
||||||
|
+ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt];
|
||||||
|
+ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1];
|
||||||
|
+
|
||||||
|
+ if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag])
|
||||||
|
+ togglebar(NULL);
|
||||||
|
+
|
||||||
|
focus(NULL);
|
||||||
|
arrange(selmon);
|
||||||
|
}
|
42
dwm/transient.c
Normal file
42
dwm/transient.c
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/* cc transient.c -o transient -lX11 */
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xutil.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
Display *d;
|
||||||
|
Window r, f, t = None;
|
||||||
|
XSizeHints h;
|
||||||
|
XEvent e;
|
||||||
|
|
||||||
|
d = XOpenDisplay(NULL);
|
||||||
|
if (!d)
|
||||||
|
exit(1);
|
||||||
|
r = DefaultRootWindow(d);
|
||||||
|
|
||||||
|
f = XCreateSimpleWindow(d, r, 100, 100, 400, 400, 0, 0, 0);
|
||||||
|
h.min_width = h.max_width = h.min_height = h.max_height = 400;
|
||||||
|
h.flags = PMinSize | PMaxSize;
|
||||||
|
XSetWMNormalHints(d, f, &h);
|
||||||
|
XStoreName(d, f, "floating");
|
||||||
|
XMapWindow(d, f);
|
||||||
|
|
||||||
|
XSelectInput(d, f, ExposureMask);
|
||||||
|
while (1) {
|
||||||
|
XNextEvent(d, &e);
|
||||||
|
|
||||||
|
if (t == None) {
|
||||||
|
sleep(5);
|
||||||
|
t = XCreateSimpleWindow(d, r, 50, 50, 100, 100, 0, 0, 0);
|
||||||
|
XSetTransientForHint(d, t, f);
|
||||||
|
XStoreName(d, t, "transient");
|
||||||
|
XMapWindow(d, t);
|
||||||
|
XSelectInput(d, t, ExposureMask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XCloseDisplay(d);
|
||||||
|
exit(0);
|
||||||
|
}
|
35
dwm/util.c
Normal file
35
dwm/util.c
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
void *
|
||||||
|
ecalloc(size_t nmemb, size_t size)
|
||||||
|
{
|
||||||
|
void *p;
|
||||||
|
|
||||||
|
if (!(p = calloc(nmemb, size)))
|
||||||
|
die("calloc:");
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
die(const char *fmt, ...) {
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
|
||||||
|
fputc(' ', stderr);
|
||||||
|
perror(NULL);
|
||||||
|
} else {
|
||||||
|
fputc('\n', stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit(1);
|
||||||
|
}
|
8
dwm/util.h
Normal file
8
dwm/util.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
|
||||||
|
#define MAX(A, B) ((A) > (B) ? (A) : (B))
|
||||||
|
#define MIN(A, B) ((A) < (B) ? (A) : (B))
|
||||||
|
#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B))
|
||||||
|
|
||||||
|
void die(const char *fmt, ...);
|
||||||
|
void *ecalloc(size_t nmemb, size_t size);
|
40
slstatus/LICENSE
Normal file
40
slstatus/LICENSE
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
ISC License
|
||||||
|
|
||||||
|
Copyright 2016-2020 Aaron Marcher <me@drkhsh.at>
|
||||||
|
|
||||||
|
Copyright 2016 Roy Freytag <rfreytag@hs-mittweida.de>
|
||||||
|
Copyright 2016 Vincent Loupmon <vincentloupmon@gmail.com>
|
||||||
|
Copyright 2016 Daniel Walter <d.walter@0x90.at>
|
||||||
|
Copyright 2016-2018 Ali H. Fardan <raiz@firemail.cc>
|
||||||
|
Copyright 2016 Jody Leonard <me@jodyleonard.com>
|
||||||
|
Copyright 2016-2018 Quentin Rameau <quinq@fifth.space>
|
||||||
|
Copyright 2016 Mike Coddington <mike@coddington.us>
|
||||||
|
Copyright 2016-2018 parazyd <parazyd@dyne.org>
|
||||||
|
Copyright 2017 Tobias Stoeckmann <tobias@stoeckmann.org>
|
||||||
|
Copyright 2017-2018 Laslo Hunhold <dev@frign.de>
|
||||||
|
Copyright 2018 Darron Anderson <darronanderson@protonmail.com>
|
||||||
|
Copyright 2018 Josuah Demangeon <mail@josuah.net>
|
||||||
|
Copyright 2018 Tobias Tschinkowitz <tobias@he4d.net>
|
||||||
|
Copyright 2018 David Demelier <markand@malikania.fr>
|
||||||
|
Copyright 2018-2019 Michael Buch <michaelbuch12@gmail.com>
|
||||||
|
Copyright 2018 Ian Remmler <ian@remmler.org>
|
||||||
|
Copyright 2016-2019 Joerg Jung <jung@openbsd.org>
|
||||||
|
Copyright 2019 Ryan Kes <alrayyes@gmail.com>
|
||||||
|
Copyright 2019 Cem Keylan <cem@ckyln.com>
|
||||||
|
Copyright 2019 dsp <dsp@2f30.org>
|
||||||
|
Copyright 2019-2020 Ingo Feinerer <feinerer@logic.at>
|
||||||
|
Copyright 2020 Alexandre Ratchov <alex@caoua.org>
|
||||||
|
Copyright 2020 Mart Lubbers <mart@martlubbers.net>
|
||||||
|
Copyright 2020 Daniel Moch <daniel@danielmoch.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
68
slstatus/Makefile
Normal file
68
slstatus/Makefile
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
# See LICENSE file for copyright and license details
|
||||||
|
# slstatus - suckless status monitor
|
||||||
|
.POSIX:
|
||||||
|
|
||||||
|
include config.mk
|
||||||
|
|
||||||
|
REQ = util
|
||||||
|
COM =\
|
||||||
|
components/battery\
|
||||||
|
components/cpu\
|
||||||
|
components/datetime\
|
||||||
|
components/disk\
|
||||||
|
components/entropy\
|
||||||
|
components/hostname\
|
||||||
|
components/ip\
|
||||||
|
components/kernel_release\
|
||||||
|
components/keyboard_indicators\
|
||||||
|
components/keymap\
|
||||||
|
components/load_avg\
|
||||||
|
components/netspeeds\
|
||||||
|
components/num_files\
|
||||||
|
components/ram\
|
||||||
|
components/run_command\
|
||||||
|
components/separator\
|
||||||
|
components/swap\
|
||||||
|
components/temperature\
|
||||||
|
components/uptime\
|
||||||
|
components/user\
|
||||||
|
components/volume\
|
||||||
|
components/wifi
|
||||||
|
|
||||||
|
all: slstatus
|
||||||
|
|
||||||
|
$(COM:=.o): config.mk $(REQ:=.h)
|
||||||
|
slstatus.o: slstatus.c slstatus.h arg.h config.h config.mk $(REQ:=.h)
|
||||||
|
|
||||||
|
.c.o:
|
||||||
|
$(CC) -o $@ -c $(CPPFLAGS) $(CFLAGS) $<
|
||||||
|
|
||||||
|
config.h:
|
||||||
|
cp config.def.h $@
|
||||||
|
|
||||||
|
slstatus: slstatus.o $(COM:=.o) $(REQ:=.o)
|
||||||
|
$(CC) -o $@ $(LDFLAGS) $(COM:=.o) $(REQ:=.o) slstatus.o $(LDLIBS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f slstatus slstatus.o $(COM:=.o) $(REQ:=.o)
|
||||||
|
|
||||||
|
dist:
|
||||||
|
rm -rf "slstatus-$(VERSION)"
|
||||||
|
mkdir -p "slstatus-$(VERSION)/components"
|
||||||
|
cp -R LICENSE Makefile README config.mk config.def.h \
|
||||||
|
arg.h slstatus.c $(COM:=.c) $(REQ:=.c) $(REQ:=.h) \
|
||||||
|
slstatus.1 "slstatus-$(VERSION)"
|
||||||
|
tar -cf - "slstatus-$(VERSION)" | gzip -c > "slstatus-$(VERSION).tar.gz"
|
||||||
|
rm -rf "slstatus-$(VERSION)"
|
||||||
|
|
||||||
|
install: all
|
||||||
|
mkdir -p "$(DESTDIR)$(PREFIX)/bin"
|
||||||
|
cp -f slstatus "$(DESTDIR)$(PREFIX)/bin"
|
||||||
|
chmod 755 "$(DESTDIR)$(PREFIX)/bin/slstatus"
|
||||||
|
mkdir -p "$(DESTDIR)$(MANPREFIX)/man1"
|
||||||
|
cp -f slstatus.1 "$(DESTDIR)$(MANPREFIX)/man1"
|
||||||
|
chmod 644 "$(DESTDIR)$(MANPREFIX)/man1/slstatus.1"
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
rm -f "$(DESTDIR)$(PREFIX)/bin/slstatus"
|
||||||
|
rm -f "$(DESTDIR)$(MANPREFIX)/man1/slstatus.1"
|
65
slstatus/README
Normal file
65
slstatus/README
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
slstatus - suckless status
|
||||||
|
==========================
|
||||||
|
slstatus is a suckless status monitor for window managers that use WM_NAME
|
||||||
|
(e.g. dwm) or stdin to fill the status bar.
|
||||||
|
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
- Battery percentage/state/time left
|
||||||
|
- CPU usage
|
||||||
|
- CPU frequency
|
||||||
|
- Custom shell commands
|
||||||
|
- Date and time
|
||||||
|
- Disk status (free storage, percentage, total storage and used storage)
|
||||||
|
- Available entropy
|
||||||
|
- Username/GID/UID
|
||||||
|
- Hostname
|
||||||
|
- IP address (IPv4 and IPv6)
|
||||||
|
- Kernel version
|
||||||
|
- Keyboard indicators
|
||||||
|
- Keymap
|
||||||
|
- Load average
|
||||||
|
- Network speeds (RX and TX)
|
||||||
|
- Number of files in a directory (hint: Maildir)
|
||||||
|
- Memory status (free memory, percentage, total memory and used memory)
|
||||||
|
- Swap status (free swap, percentage, total swap and used swap)
|
||||||
|
- Temperature
|
||||||
|
- Uptime
|
||||||
|
- Volume percentage
|
||||||
|
- WiFi signal percentage and ESSID
|
||||||
|
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
Currently slstatus works on FreeBSD, Linux and OpenBSD.
|
||||||
|
In order to build slstatus you need the Xlib header files.
|
||||||
|
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
Edit config.mk to match your local setup (slstatus is installed into the
|
||||||
|
/usr/local namespace by default).
|
||||||
|
|
||||||
|
Afterwards enter the following command to build and install slstatus (if
|
||||||
|
necessary as root):
|
||||||
|
|
||||||
|
make clean install
|
||||||
|
|
||||||
|
|
||||||
|
Running slstatus
|
||||||
|
----------------
|
||||||
|
See the man page for details.
|
||||||
|
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
-------------
|
||||||
|
slstatus can be customized by creating a custom config.h and (re)compiling the
|
||||||
|
source code. This keeps it fast, secure and simple.
|
||||||
|
|
||||||
|
|
||||||
|
Upcoming
|
||||||
|
--------
|
||||||
|
|
||||||
|
A release (v1.0) will come soon... ;)
|
||||||
|
After a long phase of inactivity, development has been continued!
|
33
slstatus/arg.h
Normal file
33
slstatus/arg.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#ifndef ARG_H
|
||||||
|
#define ARG_H
|
||||||
|
|
||||||
|
extern char *argv0;
|
||||||
|
|
||||||
|
/* int main(int argc, char *argv[]) */
|
||||||
|
#define ARGBEGIN for (argv0 = *argv, *argv ? (argc--, argv++) : ((void *)0); \
|
||||||
|
*argv && (*argv)[0] == '-' && (*argv)[1]; argc--, argv++) { \
|
||||||
|
int i_, argused_; \
|
||||||
|
if ((*argv)[1] == '-' && !(*argv)[2]) { \
|
||||||
|
argc--, argv++; \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
for (i_ = 1, argused_ = 0; (*argv)[i_]; i_++) { \
|
||||||
|
switch((*argv)[i_])
|
||||||
|
#define ARGEND if (argused_) { \
|
||||||
|
if ((*argv)[i_ + 1]) { \
|
||||||
|
break; \
|
||||||
|
} else { \
|
||||||
|
argc--, argv++; \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
#define ARGC() ((*argv)[i_])
|
||||||
|
#define ARGF_(x) (((*argv)[i_ + 1]) ? (argused_ = 1, &((*argv)[i_ + 1])) : \
|
||||||
|
(*(argv + 1)) ? (argused_ = 1, *(argv + 1)) : (x))
|
||||||
|
#define EARGF(x) ARGF_(((x), exit(1), (char *)0))
|
||||||
|
#define ARGF() ARGF_((char *)0)
|
||||||
|
|
||||||
|
#endif
|
252
slstatus/components/battery.c
Normal file
252
slstatus/components/battery.c
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
pick(const char *bat, const char *f1, const char *f2, char *path,
|
||||||
|
size_t length)
|
||||||
|
{
|
||||||
|
if (esnprintf(path, length, f1, bat) > 0 &&
|
||||||
|
access(path, R_OK) == 0) {
|
||||||
|
return f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (esnprintf(path, length, f2, bat) > 0 &&
|
||||||
|
access(path, R_OK) == 0) {
|
||||||
|
return f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
battery_perc(const char *bat)
|
||||||
|
{
|
||||||
|
int perc;
|
||||||
|
char path[PATH_MAX];
|
||||||
|
|
||||||
|
if (esnprintf(path, sizeof(path),
|
||||||
|
"/sys/class/power_supply/%s/capacity", bat) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (pscanf(path, "%d", &perc) != 1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bprintf("%d", perc);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
battery_state(const char *bat)
|
||||||
|
{
|
||||||
|
static struct {
|
||||||
|
char *state;
|
||||||
|
char *symbol;
|
||||||
|
} map[] = {
|
||||||
|
{ "Charging", "+" },
|
||||||
|
{ "Discharging", "-" },
|
||||||
|
{ "Full", "o" },
|
||||||
|
};
|
||||||
|
size_t i;
|
||||||
|
char path[PATH_MAX], state[12];
|
||||||
|
|
||||||
|
if (esnprintf(path, sizeof(path),
|
||||||
|
"/sys/class/power_supply/%s/status", bat) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (pscanf(path, "%12s", state) != 1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < LEN(map); i++) {
|
||||||
|
if (!strcmp(map[i].state, state)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (i == LEN(map)) ? "?" : map[i].symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
battery_remaining(const char *bat)
|
||||||
|
{
|
||||||
|
uintmax_t charge_now, current_now, m, h;
|
||||||
|
double timeleft;
|
||||||
|
char path[PATH_MAX], state[12];
|
||||||
|
|
||||||
|
if (esnprintf(path, sizeof(path),
|
||||||
|
"/sys/class/power_supply/%s/status", bat) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (pscanf(path, "%12s", state) != 1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pick(bat, "/sys/class/power_supply/%s/charge_now",
|
||||||
|
"/sys/class/power_supply/%s/energy_now", path,
|
||||||
|
sizeof(path)) ||
|
||||||
|
pscanf(path, "%ju", &charge_now) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(state, "Discharging")) {
|
||||||
|
if (!pick(bat, "/sys/class/power_supply/%s/current_now",
|
||||||
|
"/sys/class/power_supply/%s/power_now", path,
|
||||||
|
sizeof(path)) ||
|
||||||
|
pscanf(path, "%ju", ¤t_now) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_now == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
timeleft = (double)charge_now / (double)current_now;
|
||||||
|
h = timeleft;
|
||||||
|
m = (timeleft - (double)h) * 60;
|
||||||
|
|
||||||
|
return bprintf("%juh %jum", h, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <machine/apmvar.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static int
|
||||||
|
load_apm_power_info(struct apm_power_info *apm_info)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
fd = open("/dev/apm", O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
warn("open '/dev/apm':");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(apm_info, 0, sizeof(struct apm_power_info));
|
||||||
|
if (ioctl(fd, APM_IOC_GETPOWER, apm_info) < 0) {
|
||||||
|
warn("ioctl 'APM_IOC_GETPOWER':");
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return close(fd), 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
battery_perc(const char *unused)
|
||||||
|
{
|
||||||
|
struct apm_power_info apm_info;
|
||||||
|
|
||||||
|
if (load_apm_power_info(&apm_info)) {
|
||||||
|
return bprintf("%d", apm_info.battery_life);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
battery_state(const char *unused)
|
||||||
|
{
|
||||||
|
struct {
|
||||||
|
unsigned int state;
|
||||||
|
char *symbol;
|
||||||
|
} map[] = {
|
||||||
|
{ APM_AC_ON, "+" },
|
||||||
|
{ APM_AC_OFF, "-" },
|
||||||
|
};
|
||||||
|
struct apm_power_info apm_info;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (load_apm_power_info(&apm_info)) {
|
||||||
|
for (i = 0; i < LEN(map); i++) {
|
||||||
|
if (map[i].state == apm_info.ac_state) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (i == LEN(map)) ? "?" : map[i].symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
battery_remaining(const char *unused)
|
||||||
|
{
|
||||||
|
struct apm_power_info apm_info;
|
||||||
|
|
||||||
|
if (load_apm_power_info(&apm_info)) {
|
||||||
|
if (apm_info.ac_state != APM_AC_ON) {
|
||||||
|
return bprintf("%uh %02um",
|
||||||
|
apm_info.minutes_left / 60,
|
||||||
|
apm_info.minutes_left % 60);
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#elif defined(__FreeBSD__)
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
|
||||||
|
const char *
|
||||||
|
battery_perc(const char *unused)
|
||||||
|
{
|
||||||
|
int cap;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
len = sizeof(cap);
|
||||||
|
if (sysctlbyname("hw.acpi.battery.life", &cap, &len, NULL, 0) == -1
|
||||||
|
|| !len)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return bprintf("%d", cap);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
battery_state(const char *unused)
|
||||||
|
{
|
||||||
|
int state;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
len = sizeof(state);
|
||||||
|
if (sysctlbyname("hw.acpi.battery.state", &state, &len, NULL, 0) == -1
|
||||||
|
|| !len)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
switch(state) {
|
||||||
|
case 0:
|
||||||
|
case 2:
|
||||||
|
return "+";
|
||||||
|
case 1:
|
||||||
|
return "-";
|
||||||
|
default:
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
battery_remaining(const char *unused)
|
||||||
|
{
|
||||||
|
int rem;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
len = sizeof(rem);
|
||||||
|
if (sysctlbyname("hw.acpi.battery.time", &rem, &len, NULL, 0) == -1
|
||||||
|
|| !len
|
||||||
|
|| rem == -1)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return bprintf("%uh %02um", rem / 60, rem % 60);
|
||||||
|
}
|
||||||
|
#endif
|
164
slstatus/components/cpu.c
Normal file
164
slstatus/components/cpu.c
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
const char *
|
||||||
|
cpu_freq(void)
|
||||||
|
{
|
||||||
|
uintmax_t freq;
|
||||||
|
|
||||||
|
/* in kHz */
|
||||||
|
if (pscanf("/sys/devices/system/cpu/cpu0/cpufreq/"
|
||||||
|
"scaling_cur_freq", "%ju", &freq) != 1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt_human(freq * 1000, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
cpu_perc(void)
|
||||||
|
{
|
||||||
|
static long double a[7];
|
||||||
|
long double b[7], sum;
|
||||||
|
|
||||||
|
memcpy(b, a, sizeof(b));
|
||||||
|
/* cpu user nice system idle iowait irq softirq */
|
||||||
|
if (pscanf("/proc/stat", "%*s %Lf %Lf %Lf %Lf %Lf %Lf %Lf",
|
||||||
|
&a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6])
|
||||||
|
!= 7) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (b[0] == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
sum = (b[0] + b[1] + b[2] + b[3] + b[4] + b[5] + b[6]) -
|
||||||
|
(a[0] + a[1] + a[2] + a[3] + a[4] + a[5] + a[6]);
|
||||||
|
|
||||||
|
if (sum == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bprintf("%d", (int)(100 *
|
||||||
|
((b[0] + b[1] + b[2] + b[5] + b[6]) -
|
||||||
|
(a[0] + a[1] + a[2] + a[5] + a[6])) / sum));
|
||||||
|
}
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/sched.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
|
||||||
|
const char *
|
||||||
|
cpu_freq(void)
|
||||||
|
{
|
||||||
|
int freq, mib[2];
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
mib[0] = CTL_HW;
|
||||||
|
mib[1] = HW_CPUSPEED;
|
||||||
|
|
||||||
|
size = sizeof(freq);
|
||||||
|
|
||||||
|
/* in MHz */
|
||||||
|
if (sysctl(mib, 2, &freq, &size, NULL, 0) < 0) {
|
||||||
|
warn("sysctl 'HW_CPUSPEED':");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt_human(freq * 1E6, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
cpu_perc(void)
|
||||||
|
{
|
||||||
|
int mib[2];
|
||||||
|
static uintmax_t a[CPUSTATES];
|
||||||
|
uintmax_t b[CPUSTATES], sum;
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
mib[0] = CTL_KERN;
|
||||||
|
mib[1] = KERN_CPTIME;
|
||||||
|
|
||||||
|
size = sizeof(a);
|
||||||
|
|
||||||
|
memcpy(b, a, sizeof(b));
|
||||||
|
if (sysctl(mib, 2, &a, &size, NULL, 0) < 0) {
|
||||||
|
warn("sysctl 'KERN_CPTIME':");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (b[0] == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
sum = (a[CP_USER] + a[CP_NICE] + a[CP_SYS] + a[CP_INTR] + a[CP_IDLE]) -
|
||||||
|
(b[CP_USER] + b[CP_NICE] + b[CP_SYS] + b[CP_INTR] + b[CP_IDLE]);
|
||||||
|
|
||||||
|
if (sum == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bprintf("%d", 100 *
|
||||||
|
((a[CP_USER] + a[CP_NICE] + a[CP_SYS] +
|
||||||
|
a[CP_INTR]) -
|
||||||
|
(b[CP_USER] + b[CP_NICE] + b[CP_SYS] +
|
||||||
|
b[CP_INTR])) / sum);
|
||||||
|
}
|
||||||
|
#elif defined(__FreeBSD__)
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#include <devstat.h>
|
||||||
|
|
||||||
|
const char *
|
||||||
|
cpu_freq(void)
|
||||||
|
{
|
||||||
|
int freq;
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
size = sizeof(freq);
|
||||||
|
/* in MHz */
|
||||||
|
if (sysctlbyname("hw.clockrate", &freq, &size, NULL, 0) == -1
|
||||||
|
|| !size) {
|
||||||
|
warn("sysctlbyname 'hw.clockrate':");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt_human(freq * 1E6, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
cpu_perc(void)
|
||||||
|
{
|
||||||
|
size_t size;
|
||||||
|
static long a[CPUSTATES];
|
||||||
|
long b[CPUSTATES], sum;
|
||||||
|
|
||||||
|
size = sizeof(a);
|
||||||
|
memcpy(b, a, sizeof(b));
|
||||||
|
if (sysctlbyname("kern.cp_time", &a, &size, NULL, 0) == -1
|
||||||
|
|| !size) {
|
||||||
|
warn("sysctlbyname 'kern.cp_time':");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (b[0] == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
sum = (a[CP_USER] + a[CP_NICE] + a[CP_SYS] + a[CP_INTR] + a[CP_IDLE]) -
|
||||||
|
(b[CP_USER] + b[CP_NICE] + b[CP_SYS] + b[CP_INTR] + b[CP_IDLE]);
|
||||||
|
|
||||||
|
if (sum == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bprintf("%d", 100 *
|
||||||
|
((a[CP_USER] + a[CP_NICE] + a[CP_SYS] +
|
||||||
|
a[CP_INTR]) -
|
||||||
|
(b[CP_USER] + b[CP_NICE] + b[CP_SYS] +
|
||||||
|
b[CP_INTR])) / sum);
|
||||||
|
}
|
||||||
|
#endif
|
19
slstatus/components/datetime.c
Normal file
19
slstatus/components/datetime.c
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
const char *
|
||||||
|
datetime(const char *fmt)
|
||||||
|
{
|
||||||
|
time_t t;
|
||||||
|
|
||||||
|
t = time(NULL);
|
||||||
|
if (!strftime(buf, sizeof(buf), fmt, localtime(&t))) {
|
||||||
|
warn("strftime: Result string exceeds buffer size");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
58
slstatus/components/disk.c
Normal file
58
slstatus/components/disk.c
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/statvfs.h>
|
||||||
|
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
const char *
|
||||||
|
disk_free(const char *path)
|
||||||
|
{
|
||||||
|
struct statvfs fs;
|
||||||
|
|
||||||
|
if (statvfs(path, &fs) < 0) {
|
||||||
|
warn("statvfs '%s':", path);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt_human(fs.f_frsize * fs.f_bavail, 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
disk_perc(const char *path)
|
||||||
|
{
|
||||||
|
struct statvfs fs;
|
||||||
|
|
||||||
|
if (statvfs(path, &fs) < 0) {
|
||||||
|
warn("statvfs '%s':", path);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bprintf("%d", (int)(100 *
|
||||||
|
(1.0f - ((float)fs.f_bavail / (float)fs.f_blocks))));
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
disk_total(const char *path)
|
||||||
|
{
|
||||||
|
struct statvfs fs;
|
||||||
|
|
||||||
|
if (statvfs(path, &fs) < 0) {
|
||||||
|
warn("statvfs '%s':", path);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt_human(fs.f_frsize * fs.f_blocks, 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
disk_used(const char *path)
|
||||||
|
{
|
||||||
|
struct statvfs fs;
|
||||||
|
|
||||||
|
if (statvfs(path, &fs) < 0) {
|
||||||
|
warn("statvfs '%s':", path);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt_human(fs.f_frsize * (fs.f_blocks - fs.f_bfree), 1024);
|
||||||
|
}
|
27
slstatus/components/entropy.c
Normal file
27
slstatus/components/entropy.c
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#if defined(__linux__)
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
const char *
|
||||||
|
entropy(void)
|
||||||
|
{
|
||||||
|
uintmax_t num;
|
||||||
|
|
||||||
|
if (pscanf("/proc/sys/kernel/random/entropy_avail", "%ju", &num)
|
||||||
|
!= 1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bprintf("%ju", num);
|
||||||
|
}
|
||||||
|
#elif defined(__OpenBSD__) | defined(__FreeBSD__)
|
||||||
|
const char *
|
||||||
|
entropy(void)
|
||||||
|
{
|
||||||
|
/* Unicode Character 'INFINITY' (U+221E) */
|
||||||
|
return "\xe2\x88\x9e";
|
||||||
|
}
|
||||||
|
#endif
|
16
slstatus/components/hostname.c
Normal file
16
slstatus/components/hostname.c
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
const char *
|
||||||
|
hostname(void)
|
||||||
|
{
|
||||||
|
if (gethostname(buf, sizeof(buf)) < 0) {
|
||||||
|
warn("gethostbyname:");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
60
slstatus/components/ip.c
Normal file
60
slstatus/components/ip.c
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <ifaddrs.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#if defined(__OpenBSD__)
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#elif defined(__FreeBSD__)
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
ip(const char *interface, unsigned short sa_family)
|
||||||
|
{
|
||||||
|
struct ifaddrs *ifaddr, *ifa;
|
||||||
|
int s;
|
||||||
|
char host[NI_MAXHOST];
|
||||||
|
|
||||||
|
if (getifaddrs(&ifaddr) < 0) {
|
||||||
|
warn("getifaddrs:");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
|
||||||
|
if (!ifa->ifa_addr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6),
|
||||||
|
host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
|
||||||
|
if (!strcmp(ifa->ifa_name, interface) &&
|
||||||
|
(ifa->ifa_addr->sa_family == sa_family)) {
|
||||||
|
freeifaddrs(ifaddr);
|
||||||
|
if (s != 0) {
|
||||||
|
warn("getnameinfo: %s", gai_strerror(s));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return bprintf("%s", host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
freeifaddrs(ifaddr);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
ipv4(const char *interface)
|
||||||
|
{
|
||||||
|
return ip(interface, AF_INET);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
ipv6(const char *interface)
|
||||||
|
{
|
||||||
|
return ip(interface, AF_INET6);
|
||||||
|
}
|
18
slstatus/components/kernel_release.c
Normal file
18
slstatus/components/kernel_release.c
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <sys/utsname.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
const char *
|
||||||
|
kernel_release(void)
|
||||||
|
{
|
||||||
|
struct utsname udata;
|
||||||
|
|
||||||
|
if (uname(&udata) < 0) {
|
||||||
|
warn("uname:");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bprintf("%s", udata.release);
|
||||||
|
}
|
48
slstatus/components/keyboard_indicators.c
Normal file
48
slstatus/components/keyboard_indicators.c
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fmt consists of uppercase or lowercase 'c' for caps lock and/or 'n' for num
|
||||||
|
* lock, each optionally followed by '?', in the order of indicators desired.
|
||||||
|
* If followed by '?', the letter with case preserved is included in the output
|
||||||
|
* if the corresponding indicator is on. Otherwise, the letter is always
|
||||||
|
* included, lowercase when off and uppercase when on.
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
keyboard_indicators(const char *fmt)
|
||||||
|
{
|
||||||
|
Display *dpy;
|
||||||
|
XKeyboardState state;
|
||||||
|
size_t fmtlen, i, n;
|
||||||
|
int togglecase, isset;
|
||||||
|
char key;
|
||||||
|
|
||||||
|
if (!(dpy = XOpenDisplay(NULL))) {
|
||||||
|
warn("XOpenDisplay: Failed to open display");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
XGetKeyboardControl(dpy, &state);
|
||||||
|
XCloseDisplay(dpy);
|
||||||
|
|
||||||
|
fmtlen = strnlen(fmt, 4);
|
||||||
|
for (i = n = 0; i < fmtlen; i++) {
|
||||||
|
key = tolower(fmt[i]);
|
||||||
|
if (key != 'c' && key != 'n') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
togglecase = (i + 1 >= fmtlen || fmt[i + 1] != '?');
|
||||||
|
isset = (state.led_mask & (1 << (key == 'n')));
|
||||||
|
if (togglecase) {
|
||||||
|
buf[n++] = isset ? toupper(key) : key;
|
||||||
|
} else if (isset) {
|
||||||
|
buf[n++] = fmt[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf[n] = 0;
|
||||||
|
return buf;
|
||||||
|
}
|
87
slstatus/components/keymap.c
Normal file
87
slstatus/components/keymap.c
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <X11/XKBlib.h>
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
static int
|
||||||
|
valid_layout_or_variant(char *sym)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
/* invalid symbols from xkb rules config */
|
||||||
|
static const char *invalid[] = { "evdev", "inet", "pc", "base" };
|
||||||
|
|
||||||
|
for (i = 0; i < LEN(invalid); i++) {
|
||||||
|
if (!strncmp(sym, invalid[i], strlen(invalid[i]))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
get_layout(char *syms, int grp_num)
|
||||||
|
{
|
||||||
|
char *tok, *layout;
|
||||||
|
int grp;
|
||||||
|
|
||||||
|
layout = NULL;
|
||||||
|
tok = strtok(syms, "+:");
|
||||||
|
for (grp = 0; tok && grp <= grp_num; tok = strtok(NULL, "+:")) {
|
||||||
|
if (!valid_layout_or_variant(tok)) {
|
||||||
|
continue;
|
||||||
|
} else if (strlen(tok) == 1 && isdigit(tok[0])) {
|
||||||
|
/* ignore :2, :3, :4 (additional layout groups) */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
layout = tok;
|
||||||
|
grp++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
keymap(void)
|
||||||
|
{
|
||||||
|
Display *dpy;
|
||||||
|
XkbDescRec *desc;
|
||||||
|
XkbStateRec state;
|
||||||
|
char *symbols, *layout;
|
||||||
|
|
||||||
|
layout = NULL;
|
||||||
|
|
||||||
|
if (!(dpy = XOpenDisplay(NULL))) {
|
||||||
|
warn("XOpenDisplay: Failed to open display");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!(desc = XkbAllocKeyboard())) {
|
||||||
|
warn("XkbAllocKeyboard: Failed to allocate keyboard");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
if (XkbGetNames(dpy, XkbSymbolsNameMask, desc)) {
|
||||||
|
warn("XkbGetNames: Failed to retrieve key symbols");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
if (XkbGetState(dpy, XkbUseCoreKbd, &state)) {
|
||||||
|
warn("XkbGetState: Failed to retrieve keyboard state");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
if (!(symbols = XGetAtomName(dpy, desc->names->symbols))) {
|
||||||
|
warn("XGetAtomName: Failed to get atom name");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
layout = (char *)bprintf("%s", get_layout(symbols, state.group));
|
||||||
|
XFree(symbols);
|
||||||
|
end:
|
||||||
|
XkbFreeKeyboard(desc, XkbSymbolsNameMask, 1);
|
||||||
|
if (XCloseDisplay(dpy)) {
|
||||||
|
warn("XCloseDisplay: Failed to close display");
|
||||||
|
}
|
||||||
|
|
||||||
|
return layout;
|
||||||
|
}
|
18
slstatus/components/load_avg.c
Normal file
18
slstatus/components/load_avg.c
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
const char *
|
||||||
|
load_avg(void)
|
||||||
|
{
|
||||||
|
double avgs[3];
|
||||||
|
|
||||||
|
if (getloadavg(avgs, 3) < 0) {
|
||||||
|
warn("getloadavg: Failed to obtain load average");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bprintf("%.2f %.2f %.2f", avgs[0], avgs[1], avgs[2]);
|
||||||
|
}
|
139
slstatus/components/netspeeds.c
Normal file
139
slstatus/components/netspeeds.c
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
const char *
|
||||||
|
netspeed_rx(const char *interface)
|
||||||
|
{
|
||||||
|
uintmax_t oldrxbytes;
|
||||||
|
static uintmax_t rxbytes;
|
||||||
|
extern const unsigned int interval;
|
||||||
|
char path[PATH_MAX];
|
||||||
|
|
||||||
|
oldrxbytes = rxbytes;
|
||||||
|
|
||||||
|
if (esnprintf(path, sizeof(path),
|
||||||
|
"/sys/class/net/%s/statistics/rx_bytes",
|
||||||
|
interface) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (pscanf(path, "%ju", &rxbytes) != 1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (oldrxbytes == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt_human((rxbytes - oldrxbytes) * 1000 / interval,
|
||||||
|
1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
netspeed_tx(const char *interface)
|
||||||
|
{
|
||||||
|
uintmax_t oldtxbytes;
|
||||||
|
static uintmax_t txbytes;
|
||||||
|
extern const unsigned int interval;
|
||||||
|
char path[PATH_MAX];
|
||||||
|
|
||||||
|
oldtxbytes = txbytes;
|
||||||
|
|
||||||
|
if (esnprintf(path, sizeof(path),
|
||||||
|
"/sys/class/net/%s/statistics/tx_bytes",
|
||||||
|
interface) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (pscanf(path, "%ju", &txbytes) != 1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (oldtxbytes == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt_human((txbytes - oldtxbytes) * 1000 / interval,
|
||||||
|
1024);
|
||||||
|
}
|
||||||
|
#elif defined(__OpenBSD__) | defined(__FreeBSD__)
|
||||||
|
#include <string.h>
|
||||||
|
#include <ifaddrs.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
|
||||||
|
const char *
|
||||||
|
netspeed_rx(const char *interface)
|
||||||
|
{
|
||||||
|
struct ifaddrs *ifal, *ifa;
|
||||||
|
struct if_data *ifd;
|
||||||
|
uintmax_t oldrxbytes;
|
||||||
|
static uintmax_t rxbytes;
|
||||||
|
extern const unsigned int interval;
|
||||||
|
int if_ok = 0;
|
||||||
|
|
||||||
|
oldrxbytes = rxbytes;
|
||||||
|
|
||||||
|
if (getifaddrs(&ifal) == -1) {
|
||||||
|
warn("getifaddrs failed");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
rxbytes = 0;
|
||||||
|
for (ifa = ifal; ifa; ifa = ifa->ifa_next) {
|
||||||
|
if (!strcmp(ifa->ifa_name, interface) &&
|
||||||
|
(ifd = (struct if_data *)ifa->ifa_data)) {
|
||||||
|
rxbytes += ifd->ifi_ibytes, if_ok = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
freeifaddrs(ifal);
|
||||||
|
if (!if_ok) {
|
||||||
|
warn("reading 'if_data' failed");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (oldrxbytes == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt_human((rxbytes - oldrxbytes) * 1000 / interval,
|
||||||
|
1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
netspeed_tx(const char *interface)
|
||||||
|
{
|
||||||
|
struct ifaddrs *ifal, *ifa;
|
||||||
|
struct if_data *ifd;
|
||||||
|
uintmax_t oldtxbytes;
|
||||||
|
static uintmax_t txbytes;
|
||||||
|
extern const unsigned int interval;
|
||||||
|
int if_ok = 0;
|
||||||
|
|
||||||
|
oldtxbytes = txbytes;
|
||||||
|
|
||||||
|
if (getifaddrs(&ifal) == -1) {
|
||||||
|
warn("getifaddrs failed");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
txbytes = 0;
|
||||||
|
for (ifa = ifal; ifa; ifa = ifa->ifa_next) {
|
||||||
|
if (!strcmp(ifa->ifa_name, interface) &&
|
||||||
|
(ifd = (struct if_data *)ifa->ifa_data)) {
|
||||||
|
txbytes += ifd->ifi_obytes, if_ok = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
freeifaddrs(ifal);
|
||||||
|
if (!if_ok) {
|
||||||
|
warn("reading 'if_data' failed");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (oldtxbytes == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt_human((txbytes - oldtxbytes) * 1000 / interval,
|
||||||
|
1024);
|
||||||
|
}
|
||||||
|
#endif
|
31
slstatus/components/num_files.c
Normal file
31
slstatus/components/num_files.c
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
const char *
|
||||||
|
num_files(const char *path)
|
||||||
|
{
|
||||||
|
struct dirent *dp;
|
||||||
|
DIR *fd;
|
||||||
|
int num;
|
||||||
|
|
||||||
|
if (!(fd = opendir(path))) {
|
||||||
|
warn("opendir '%s':", path);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
num = 0;
|
||||||
|
while ((dp = readdir(fd))) {
|
||||||
|
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) {
|
||||||
|
continue; /* skip self and parent */
|
||||||
|
}
|
||||||
|
num++;
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(fd);
|
||||||
|
|
||||||
|
return bprintf("%d", num);
|
||||||
|
}
|
222
slstatus/components/ram.c
Normal file
222
slstatus/components/ram.c
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
const char *
|
||||||
|
ram_free(void)
|
||||||
|
{
|
||||||
|
uintmax_t free;
|
||||||
|
|
||||||
|
if (pscanf("/proc/meminfo",
|
||||||
|
"MemTotal: %ju kB\n"
|
||||||
|
"MemFree: %ju kB\n"
|
||||||
|
"MemAvailable: %ju kB\n",
|
||||||
|
&free, &free, &free) != 3) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt_human(free * 1024, 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
ram_perc(void)
|
||||||
|
{
|
||||||
|
uintmax_t total, free, buffers, cached;
|
||||||
|
|
||||||
|
if (pscanf("/proc/meminfo",
|
||||||
|
"MemTotal: %ju kB\n"
|
||||||
|
"MemFree: %ju kB\n"
|
||||||
|
"MemAvailable: %ju kB\n"
|
||||||
|
"Buffers: %ju kB\n"
|
||||||
|
"Cached: %ju kB\n",
|
||||||
|
&total, &free, &buffers, &buffers, &cached) != 5) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (total == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bprintf("%d", 100 * ((total - free) - (buffers + cached))
|
||||||
|
/ total);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
ram_total(void)
|
||||||
|
{
|
||||||
|
uintmax_t total;
|
||||||
|
|
||||||
|
if (pscanf("/proc/meminfo", "MemTotal: %ju kB\n", &total)
|
||||||
|
!= 1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt_human(total * 1024, 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
ram_used(void)
|
||||||
|
{
|
||||||
|
uintmax_t total, free, buffers, cached;
|
||||||
|
|
||||||
|
if (pscanf("/proc/meminfo",
|
||||||
|
"MemTotal: %ju kB\n"
|
||||||
|
"MemFree: %ju kB\n"
|
||||||
|
"MemAvailable: %ju kB\n"
|
||||||
|
"Buffers: %ju kB\n"
|
||||||
|
"Cached: %ju kB\n",
|
||||||
|
&total, &free, &buffers, &buffers, &cached) != 5) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt_human((total - free - buffers - cached) * 1024,
|
||||||
|
1024);
|
||||||
|
}
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define LOG1024 10
|
||||||
|
#define pagetok(size, pageshift) (size_t)(size << (pageshift - LOG1024))
|
||||||
|
|
||||||
|
inline int
|
||||||
|
load_uvmexp(struct uvmexp *uvmexp)
|
||||||
|
{
|
||||||
|
int uvmexp_mib[] = {CTL_VM, VM_UVMEXP};
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
size = sizeof(*uvmexp);
|
||||||
|
|
||||||
|
if (sysctl(uvmexp_mib, 2, uvmexp, &size, NULL, 0) >= 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
ram_free(void)
|
||||||
|
{
|
||||||
|
struct uvmexp uvmexp;
|
||||||
|
int free_pages;
|
||||||
|
|
||||||
|
if (load_uvmexp(&uvmexp)) {
|
||||||
|
free_pages = uvmexp.npages - uvmexp.active;
|
||||||
|
return fmt_human(pagetok(free_pages, uvmexp.pageshift) *
|
||||||
|
1024, 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
ram_perc(void)
|
||||||
|
{
|
||||||
|
struct uvmexp uvmexp;
|
||||||
|
int percent;
|
||||||
|
|
||||||
|
if (load_uvmexp(&uvmexp)) {
|
||||||
|
percent = uvmexp.active * 100 / uvmexp.npages;
|
||||||
|
return bprintf("%d", percent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
ram_total(void)
|
||||||
|
{
|
||||||
|
struct uvmexp uvmexp;
|
||||||
|
|
||||||
|
if (load_uvmexp(&uvmexp)) {
|
||||||
|
return fmt_human(pagetok(uvmexp.npages,
|
||||||
|
uvmexp.pageshift) * 1024,
|
||||||
|
1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
ram_used(void)
|
||||||
|
{
|
||||||
|
struct uvmexp uvmexp;
|
||||||
|
|
||||||
|
if (load_uvmexp(&uvmexp)) {
|
||||||
|
return fmt_human(pagetok(uvmexp.active,
|
||||||
|
uvmexp.pageshift) * 1024,
|
||||||
|
1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#elif defined(__FreeBSD__)
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#include <sys/vmmeter.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <vm/vm_param.h>
|
||||||
|
|
||||||
|
const char *
|
||||||
|
ram_free(void) {
|
||||||
|
struct vmtotal vm_stats;
|
||||||
|
int mib[] = {CTL_VM, VM_TOTAL};
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
len = sizeof(struct vmtotal);
|
||||||
|
if (sysctl(mib, 2, &vm_stats, &len, NULL, 0) == -1
|
||||||
|
|| !len)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return fmt_human(vm_stats.t_free * getpagesize(), 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
ram_total(void) {
|
||||||
|
long npages;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
len = sizeof(npages);
|
||||||
|
if (sysctlbyname("vm.stats.vm.v_page_count", &npages, &len, NULL, 0) == -1
|
||||||
|
|| !len)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return fmt_human(npages * getpagesize(), 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
ram_perc(void) {
|
||||||
|
long npages;
|
||||||
|
long active;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
len = sizeof(npages);
|
||||||
|
if (sysctlbyname("vm.stats.vm.v_page_count", &npages, &len, NULL, 0) == -1
|
||||||
|
|| !len)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (sysctlbyname("vm.stats.vm.v_active_count", &active, &len, NULL, 0) == -1
|
||||||
|
|| !len)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return bprintf("%d", active * 100 / npages);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
ram_used(void) {
|
||||||
|
long active;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
len = sizeof(active);
|
||||||
|
if (sysctlbyname("vm.stats.vm.v_active_count", &active, &len, NULL, 0) == -1
|
||||||
|
|| !len)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return fmt_human(active * getpagesize(), 1024);
|
||||||
|
}
|
||||||
|
#endif
|
30
slstatus/components/run_command.c
Normal file
30
slstatus/components/run_command.c
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
const char *
|
||||||
|
run_command(const char *cmd)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
if (!(fp = popen(cmd, "r"))) {
|
||||||
|
warn("popen '%s':", cmd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
p = fgets(buf, sizeof(buf) - 1, fp);
|
||||||
|
if (pclose(fp) < 0) {
|
||||||
|
warn("pclose '%s':", cmd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!p) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if ((p = strrchr(buf, '\n'))) {
|
||||||
|
p[0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf[0] ? buf : NULL;
|
||||||
|
}
|
10
slstatus/components/separator.c
Normal file
10
slstatus/components/separator.c
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
const char *
|
||||||
|
separator(const char *separator)
|
||||||
|
{
|
||||||
|
return separator;
|
||||||
|
}
|
284
slstatus/components/swap.c
Normal file
284
slstatus/components/swap.c
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
static int
|
||||||
|
get_swap_info(long *s_total, long *s_free, long *s_cached)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
struct {
|
||||||
|
const char *name;
|
||||||
|
const size_t len;
|
||||||
|
long *var;
|
||||||
|
} ent[] = {
|
||||||
|
{ "SwapTotal", sizeof("SwapTotal") - 1, s_total },
|
||||||
|
{ "SwapFree", sizeof("SwapFree") - 1, s_free },
|
||||||
|
{ "SwapCached", sizeof("SwapCached") - 1, s_cached },
|
||||||
|
};
|
||||||
|
size_t line_len = 0, i, left;
|
||||||
|
char *line = NULL;
|
||||||
|
|
||||||
|
/* get number of fields we want to extract */
|
||||||
|
for (i = 0, left = 0; i < LEN(ent); i++) {
|
||||||
|
if (ent[i].var) {
|
||||||
|
left++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(fp = fopen("/proc/meminfo", "r"))) {
|
||||||
|
warn("fopen '/proc/meminfo':");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read file line by line and extract field information */
|
||||||
|
while (left > 0 && getline(&line, &line_len, fp) >= 0) {
|
||||||
|
for (i = 0; i < LEN(ent); i++) {
|
||||||
|
if (ent[i].var &&
|
||||||
|
!strncmp(line, ent[i].name, ent[i].len)) {
|
||||||
|
sscanf(line + ent[i].len + 1,
|
||||||
|
"%ld kB\n", ent[i].var);
|
||||||
|
left--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(line);
|
||||||
|
if (ferror(fp)) {
|
||||||
|
warn("getline '/proc/meminfo':");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
swap_free(void)
|
||||||
|
{
|
||||||
|
long free;
|
||||||
|
|
||||||
|
if (get_swap_info(NULL, &free, NULL)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt_human(free * 1024, 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
swap_perc(void)
|
||||||
|
{
|
||||||
|
long total, free, cached;
|
||||||
|
|
||||||
|
if (get_swap_info(&total, &free, &cached) || total == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bprintf("%d", 100 * (total - free - cached) / total);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
swap_total(void)
|
||||||
|
{
|
||||||
|
long total;
|
||||||
|
|
||||||
|
if (get_swap_info(&total, NULL, NULL)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt_human(total * 1024, 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
swap_used(void)
|
||||||
|
{
|
||||||
|
long total, free, cached;
|
||||||
|
|
||||||
|
if (get_swap_info(&total, &free, &cached)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt_human((total - free - cached) * 1024, 1024);
|
||||||
|
}
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/swap.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static int
|
||||||
|
getstats(int *total, int *used)
|
||||||
|
{
|
||||||
|
struct swapent *sep, *fsep;
|
||||||
|
int rnswap, nswap, i;
|
||||||
|
|
||||||
|
if ((nswap = swapctl(SWAP_NSWAP, 0, 0)) < 1) {
|
||||||
|
warn("swaptctl 'SWAP_NSWAP':");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!(fsep = sep = calloc(nswap, sizeof(*sep)))) {
|
||||||
|
warn("calloc 'nswap':");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if ((rnswap = swapctl(SWAP_STATS, (void *)sep, nswap)) < 0) {
|
||||||
|
warn("swapctl 'SWAP_STATA':");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (nswap != rnswap) {
|
||||||
|
warn("getstats: SWAP_STATS != SWAP_NSWAP");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*total = 0;
|
||||||
|
*used = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < rnswap; i++) {
|
||||||
|
*total += sep->se_nblks >> 1;
|
||||||
|
*used += sep->se_inuse >> 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(fsep);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
swap_free(void)
|
||||||
|
{
|
||||||
|
int total, used;
|
||||||
|
|
||||||
|
if (getstats(&total, &used)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt_human((total - used) * 1024, 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
swap_perc(void)
|
||||||
|
{
|
||||||
|
int total, used;
|
||||||
|
|
||||||
|
if (getstats(&total, &used)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (total == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bprintf("%d", 100 * used / total);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
swap_total(void)
|
||||||
|
{
|
||||||
|
int total, used;
|
||||||
|
|
||||||
|
if (getstats(&total, &used)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt_human(total * 1024, 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
swap_used(void)
|
||||||
|
{
|
||||||
|
int total, used;
|
||||||
|
|
||||||
|
if (getstats(&total, &used)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt_human(used * 1024, 1024);
|
||||||
|
}
|
||||||
|
#elif defined(__FreeBSD__)
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <kvm.h>
|
||||||
|
|
||||||
|
static int getswapinfo(struct kvm_swap *swap_info, size_t size)
|
||||||
|
{
|
||||||
|
kvm_t *kd;
|
||||||
|
|
||||||
|
kd = kvm_openfiles(NULL, "/dev/null", NULL, 0, NULL);
|
||||||
|
if(kd == NULL) {
|
||||||
|
warn("kvm_openfiles '/dev/null':");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(kvm_getswapinfo(kd, swap_info, size, 0 /* Unused flags */) == -1) {
|
||||||
|
warn("kvm_getswapinfo:");
|
||||||
|
kvm_close(kd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
kvm_close(kd);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
swap_free(void)
|
||||||
|
{
|
||||||
|
struct kvm_swap swap_info[1];
|
||||||
|
long used, total;
|
||||||
|
|
||||||
|
if(!getswapinfo(swap_info, 1))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
total = swap_info[0].ksw_total;
|
||||||
|
used = swap_info[0].ksw_used;
|
||||||
|
|
||||||
|
return fmt_human((total - used) * getpagesize(), 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
swap_perc(void)
|
||||||
|
{
|
||||||
|
struct kvm_swap swap_info[1];
|
||||||
|
long used, total;
|
||||||
|
|
||||||
|
if(!getswapinfo(swap_info, 1))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
total = swap_info[0].ksw_total;
|
||||||
|
used = swap_info[0].ksw_used;
|
||||||
|
|
||||||
|
return bprintf("%d", used * 100 / total);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
swap_total(void)
|
||||||
|
{
|
||||||
|
struct kvm_swap swap_info[1];
|
||||||
|
long total;
|
||||||
|
|
||||||
|
if(!getswapinfo(swap_info, 1))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
total = swap_info[0].ksw_total;
|
||||||
|
|
||||||
|
return fmt_human(total * getpagesize(), 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
swap_used(void)
|
||||||
|
{
|
||||||
|
struct kvm_swap swap_info[1];
|
||||||
|
long used;
|
||||||
|
|
||||||
|
if(!getswapinfo(swap_info, 1))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
used = swap_info[0].ksw_used;
|
||||||
|
|
||||||
|
return fmt_human(used * getpagesize(), 1024);
|
||||||
|
}
|
||||||
|
#endif
|
71
slstatus/components/temperature.c
Normal file
71
slstatus/components/temperature.c
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
const char *
|
||||||
|
temp(const char *file)
|
||||||
|
{
|
||||||
|
uintmax_t temp;
|
||||||
|
|
||||||
|
if (pscanf(file, "%ju", &temp) != 1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bprintf("%ju", temp / 1000);
|
||||||
|
}
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/time.h> /* before <sys/sensors.h> for struct timeval */
|
||||||
|
#include <sys/sensors.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
|
||||||
|
const char *
|
||||||
|
temp(const char *unused)
|
||||||
|
{
|
||||||
|
int mib[5];
|
||||||
|
size_t size;
|
||||||
|
struct sensor temp;
|
||||||
|
|
||||||
|
mib[0] = CTL_HW;
|
||||||
|
mib[1] = HW_SENSORS;
|
||||||
|
mib[2] = 0; /* cpu0 */
|
||||||
|
mib[3] = SENSOR_TEMP;
|
||||||
|
mib[4] = 0; /* temp0 */
|
||||||
|
|
||||||
|
size = sizeof(temp);
|
||||||
|
|
||||||
|
if (sysctl(mib, 5, &temp, &size, NULL, 0) < 0) {
|
||||||
|
warn("sysctl 'SENSOR_TEMP':");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* kelvin to celsius */
|
||||||
|
return bprintf("%d", (int)((float)(temp.value-273150000) / 1E6));
|
||||||
|
}
|
||||||
|
#elif defined(__FreeBSD__)
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
|
||||||
|
const char *
|
||||||
|
temp(const char *zone)
|
||||||
|
{
|
||||||
|
char buf[256];
|
||||||
|
int temp;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
len = sizeof(temp);
|
||||||
|
snprintf(buf, sizeof(buf), "hw.acpi.thermal.%s.temperature", zone);
|
||||||
|
if (sysctlbyname(buf, &temp, &len, NULL, 0) == -1
|
||||||
|
|| !len)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* kelvin to decimal celcius */
|
||||||
|
return bprintf("%d.%d", (temp - 2731) / 10, abs((temp - 2731) % 10));
|
||||||
|
}
|
||||||
|
#endif
|
33
slstatus/components/uptime.c
Normal file
33
slstatus/components/uptime.c
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
#if defined(CLOCK_BOOTTIME)
|
||||||
|
#define UPTIME_FLAG CLOCK_BOOTTIME
|
||||||
|
#elif defined(CLOCK_UPTIME)
|
||||||
|
#define UPTIME_FLAG CLOCK_UPTIME
|
||||||
|
#else
|
||||||
|
#define UPTIME_FLAG CLOCK_MONOTONIC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const char *
|
||||||
|
uptime(void)
|
||||||
|
{
|
||||||
|
char warn_buf[256];
|
||||||
|
uintmax_t h, m;
|
||||||
|
struct timespec uptime;
|
||||||
|
|
||||||
|
if (clock_gettime(UPTIME_FLAG, &uptime) < 0) {
|
||||||
|
snprintf(warn_buf, 256, "clock_gettime %d", UPTIME_FLAG);
|
||||||
|
warn(warn_buf);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
h = uptime.tv_sec / 3600;
|
||||||
|
m = uptime.tv_sec % 3600 / 60;
|
||||||
|
|
||||||
|
return bprintf("%juh %jum", h, m);
|
||||||
|
}
|
32
slstatus/components/user.c
Normal file
32
slstatus/components/user.c
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
const char *
|
||||||
|
gid(void)
|
||||||
|
{
|
||||||
|
return bprintf("%d", getgid());
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
username(void)
|
||||||
|
{
|
||||||
|
struct passwd *pw;
|
||||||
|
|
||||||
|
if (!(pw = getpwuid(geteuid()))) {
|
||||||
|
warn("getpwuid '%d':", geteuid());
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bprintf("%s", pw->pw_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
uid(void)
|
||||||
|
{
|
||||||
|
return bprintf("%d", geteuid());
|
||||||
|
}
|
217
slstatus/components/volume.c
Normal file
217
slstatus/components/volume.c
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
#if defined(__OpenBSD__)
|
||||||
|
#include <sys/queue.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <sndio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
struct control {
|
||||||
|
LIST_ENTRY(control) next;
|
||||||
|
unsigned int addr;
|
||||||
|
#define CTRL_NONE 0
|
||||||
|
#define CTRL_LEVEL 1
|
||||||
|
#define CTRL_MUTE 2
|
||||||
|
unsigned int type;
|
||||||
|
unsigned int maxval;
|
||||||
|
unsigned int val;
|
||||||
|
};
|
||||||
|
|
||||||
|
static LIST_HEAD(, control) controls = LIST_HEAD_INITIALIZER(controls);
|
||||||
|
static struct pollfd *pfds;
|
||||||
|
static struct sioctl_hdl *hdl;
|
||||||
|
static int initialized;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call-back to obtain the description of all audio controls.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
ondesc(void *unused, struct sioctl_desc *desc, int val)
|
||||||
|
{
|
||||||
|
struct control *c, *ctmp;
|
||||||
|
unsigned int type = CTRL_NONE;
|
||||||
|
|
||||||
|
if (desc == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Delete existing audio control with the same address. */
|
||||||
|
LIST_FOREACH_SAFE(c, &controls, next, ctmp) {
|
||||||
|
if (desc->addr == c->addr) {
|
||||||
|
LIST_REMOVE(c, next);
|
||||||
|
free(c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only match output.level and output.mute audio controls. */
|
||||||
|
if (desc->group[0] != 0 ||
|
||||||
|
strcmp(desc->node0.name, "output") != 0)
|
||||||
|
return;
|
||||||
|
if (desc->type == SIOCTL_NUM &&
|
||||||
|
strcmp(desc->func, "level") == 0)
|
||||||
|
type = CTRL_LEVEL;
|
||||||
|
else if (desc->type == SIOCTL_SW &&
|
||||||
|
strcmp(desc->func, "mute") == 0)
|
||||||
|
type = CTRL_MUTE;
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
c = malloc(sizeof(struct control));
|
||||||
|
if (c == NULL) {
|
||||||
|
warn("sndio: failed to allocate audio control\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
c->addr = desc->addr;
|
||||||
|
c->type = type;
|
||||||
|
c->maxval = desc->maxval;
|
||||||
|
c->val = val;
|
||||||
|
LIST_INSERT_HEAD(&controls, c, next);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call-back invoked whenever an audio control changes.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
onval(void *unused, unsigned int addr, unsigned int val)
|
||||||
|
{
|
||||||
|
struct control *c;
|
||||||
|
|
||||||
|
LIST_FOREACH(c, &controls, next) {
|
||||||
|
if (c->addr == addr)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
c->val = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cleanup(void)
|
||||||
|
{
|
||||||
|
struct control *c;
|
||||||
|
|
||||||
|
if (hdl) {
|
||||||
|
sioctl_close(hdl);
|
||||||
|
hdl = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(pfds);
|
||||||
|
pfds = NULL;
|
||||||
|
|
||||||
|
while (!LIST_EMPTY(&controls)) {
|
||||||
|
c = LIST_FIRST(&controls);
|
||||||
|
LIST_REMOVE(c, next);
|
||||||
|
free(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
init(void)
|
||||||
|
{
|
||||||
|
hdl = sioctl_open(SIO_DEVANY, SIOCTL_READ, 0);
|
||||||
|
if (hdl == NULL) {
|
||||||
|
warn("sndio: cannot open device");
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sioctl_ondesc(hdl, ondesc, NULL)) {
|
||||||
|
warn("sndio: cannot set control description call-back");
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sioctl_onval(hdl, onval, NULL)) {
|
||||||
|
warn("sndio: cannot set control values call-back");
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
pfds = calloc(sioctl_nfds(hdl), sizeof(struct pollfd));
|
||||||
|
if (pfds == NULL) {
|
||||||
|
warn("sndio: cannot allocate pollfd structures");
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
failed:
|
||||||
|
cleanup();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
vol_perc(const char *unused)
|
||||||
|
{
|
||||||
|
struct control *c;
|
||||||
|
int n, v, value;
|
||||||
|
|
||||||
|
if (!initialized)
|
||||||
|
initialized = init();
|
||||||
|
|
||||||
|
if (hdl == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
n = sioctl_pollfd(hdl, pfds, POLLIN);
|
||||||
|
if (n > 0) {
|
||||||
|
n = poll(pfds, n, 0);
|
||||||
|
if (n > 0) {
|
||||||
|
if (sioctl_revents(hdl, pfds) & POLLHUP) {
|
||||||
|
warn("sndio: disconnected");
|
||||||
|
cleanup();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value = 100;
|
||||||
|
LIST_FOREACH(c, &controls, next) {
|
||||||
|
if (c->type == CTRL_MUTE && c->val == 1)
|
||||||
|
value = 0;
|
||||||
|
else if (c->type == CTRL_LEVEL) {
|
||||||
|
v = (c->val * 100 + c->maxval / 2) / c->maxval;
|
||||||
|
/* For multiple channels return the minimum. */
|
||||||
|
if (v < value)
|
||||||
|
value = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bprintf("%d", value);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#include <sys/soundcard.h>
|
||||||
|
|
||||||
|
const char *
|
||||||
|
vol_perc(const char *card)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
int v, afd, devmask;
|
||||||
|
char *vnames[] = SOUND_DEVICE_NAMES;
|
||||||
|
|
||||||
|
if ((afd = open(card, O_RDONLY | O_NONBLOCK)) < 0) {
|
||||||
|
warn("open '%s':", card);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(afd, (int)SOUND_MIXER_READ_DEVMASK, &devmask) < 0) {
|
||||||
|
warn("ioctl 'SOUND_MIXER_READ_DEVMASK':");
|
||||||
|
close(afd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
for (i = 0; i < LEN(vnames); i++) {
|
||||||
|
if (devmask & (1 << i) && !strcmp("vol", vnames[i])) {
|
||||||
|
if (ioctl(afd, MIXER_READ(i), &v) < 0) {
|
||||||
|
warn("ioctl 'MIXER_READ(%ld)':", i);
|
||||||
|
close(afd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(afd);
|
||||||
|
|
||||||
|
return bprintf("%d", v & 0xff);
|
||||||
|
}
|
||||||
|
#endif
|
272
slstatus/components/wifi.c
Normal file
272
slstatus/components/wifi.c
Normal file
@ -0,0 +1,272 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <ifaddrs.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "../util.h"
|
||||||
|
|
||||||
|
#define RSSI_TO_PERC(rssi) \
|
||||||
|
rssi >= -50 ? 100 : \
|
||||||
|
(rssi <= -100 ? 0 : \
|
||||||
|
(2 * (rssi + 100)))
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
#include <limits.h>
|
||||||
|
#include <linux/wireless.h>
|
||||||
|
|
||||||
|
const char *
|
||||||
|
wifi_perc(const char *interface)
|
||||||
|
{
|
||||||
|
int cur;
|
||||||
|
size_t i;
|
||||||
|
char *p, *datastart;
|
||||||
|
char path[PATH_MAX];
|
||||||
|
char status[5];
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
if (esnprintf(path, sizeof(path), "/sys/class/net/%s/operstate",
|
||||||
|
interface) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!(fp = fopen(path, "r"))) {
|
||||||
|
warn("fopen '%s':", path);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
p = fgets(status, 5, fp);
|
||||||
|
fclose(fp);
|
||||||
|
if (!p || strcmp(status, "up\n") != 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(fp = fopen("/proc/net/wireless", "r"))) {
|
||||||
|
warn("fopen '/proc/net/wireless':");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 3; i++) {
|
||||||
|
if (!(p = fgets(buf, sizeof(buf) - 1, fp)))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
if (i < 2 || !p) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(datastart = strstr(buf, interface))) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
datastart = (datastart+(strlen(interface)+1));
|
||||||
|
sscanf(datastart + 1, " %*d %d %*d %*d\t\t %*d\t "
|
||||||
|
"%*d\t\t%*d\t\t %*d\t %*d\t\t %*d", &cur);
|
||||||
|
|
||||||
|
/* 70 is the max of /proc/net/wireless */
|
||||||
|
return bprintf("%d", (int)((float)cur / 70 * 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
wifi_essid(const char *interface)
|
||||||
|
{
|
||||||
|
static char id[IW_ESSID_MAX_SIZE+1];
|
||||||
|
int sockfd;
|
||||||
|
struct iwreq wreq;
|
||||||
|
|
||||||
|
memset(&wreq, 0, sizeof(struct iwreq));
|
||||||
|
wreq.u.essid.length = IW_ESSID_MAX_SIZE+1;
|
||||||
|
if (esnprintf(wreq.ifr_name, sizeof(wreq.ifr_name), "%s",
|
||||||
|
interface) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||||
|
warn("socket 'AF_INET':");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
wreq.u.essid.pointer = id;
|
||||||
|
if (ioctl(sockfd,SIOCGIWESSID, &wreq) < 0) {
|
||||||
|
warn("ioctl 'SIOCGIWESSID':");
|
||||||
|
close(sockfd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(sockfd);
|
||||||
|
|
||||||
|
if (!strcmp(id, "")) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <net/if_media.h>
|
||||||
|
#include <net80211/ieee80211.h>
|
||||||
|
#include <sys/select.h> /* before <sys/ieee80211_ioctl.h> for NBBY */
|
||||||
|
#include <net80211/ieee80211_ioctl.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
static int
|
||||||
|
load_ieee80211_nodereq(const char *interface, struct ieee80211_nodereq *nr)
|
||||||
|
{
|
||||||
|
struct ieee80211_bssid bssid;
|
||||||
|
int sockfd;
|
||||||
|
uint8_t zero_bssid[IEEE80211_ADDR_LEN];
|
||||||
|
|
||||||
|
memset(&bssid, 0, sizeof(bssid));
|
||||||
|
memset(nr, 0, sizeof(struct ieee80211_nodereq));
|
||||||
|
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||||
|
warn("socket 'AF_INET':");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
strlcpy(bssid.i_name, interface, sizeof(bssid.i_name));
|
||||||
|
if ((ioctl(sockfd, SIOCG80211BSSID, &bssid)) < 0) {
|
||||||
|
warn("ioctl 'SIOCG80211BSSID':");
|
||||||
|
close(sockfd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memset(&zero_bssid, 0, sizeof(zero_bssid));
|
||||||
|
if (memcmp(bssid.i_bssid, zero_bssid,
|
||||||
|
IEEE80211_ADDR_LEN) == 0) {
|
||||||
|
close(sockfd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
strlcpy(nr->nr_ifname, interface, sizeof(nr->nr_ifname));
|
||||||
|
memcpy(&nr->nr_macaddr, bssid.i_bssid, sizeof(nr->nr_macaddr));
|
||||||
|
if ((ioctl(sockfd, SIOCG80211NODE, nr)) < 0 && nr->nr_rssi) {
|
||||||
|
warn("ioctl 'SIOCG80211NODE':");
|
||||||
|
close(sockfd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return close(sockfd), 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
wifi_perc(const char *interface)
|
||||||
|
{
|
||||||
|
struct ieee80211_nodereq nr;
|
||||||
|
int q;
|
||||||
|
|
||||||
|
if (load_ieee80211_nodereq(interface, &nr)) {
|
||||||
|
if (nr.nr_max_rssi) {
|
||||||
|
q = IEEE80211_NODEREQ_RSSI(&nr);
|
||||||
|
} else {
|
||||||
|
q = RSSI_TO_PERC(nr.nr_rssi);
|
||||||
|
}
|
||||||
|
return bprintf("%d", q);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
wifi_essid(const char *interface)
|
||||||
|
{
|
||||||
|
struct ieee80211_nodereq nr;
|
||||||
|
|
||||||
|
if (load_ieee80211_nodereq(interface, &nr)) {
|
||||||
|
return bprintf("%s", nr.nr_nwid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#elif defined(__FreeBSD__)
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <net80211/ieee80211_ioctl.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
load_ieee80211req(int sock, const char *interface, void *data, int type, size_t *len)
|
||||||
|
{
|
||||||
|
char warn_buf[256];
|
||||||
|
struct ieee80211req ireq;
|
||||||
|
memset(&ireq, 0, sizeof(ireq));
|
||||||
|
ireq.i_type = type;
|
||||||
|
ireq.i_data = (caddr_t) data;
|
||||||
|
ireq.i_len = *len;
|
||||||
|
|
||||||
|
strlcpy(ireq.i_name, interface, sizeof(ireq.i_name));
|
||||||
|
if (ioctl(sock, SIOCG80211, &ireq) < 0) {
|
||||||
|
snprintf(warn_buf, sizeof(warn_buf),
|
||||||
|
"ioctl: 'SIOCG80211': %d", type);
|
||||||
|
warn(warn_buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*len = ireq.i_len;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
wifi_perc(const char *interface)
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
struct ieee80211req_sta_req sta;
|
||||||
|
uint8_t buf[24 * 1024];
|
||||||
|
} info;
|
||||||
|
uint8_t bssid[IEEE80211_ADDR_LEN];
|
||||||
|
int rssi_dbm;
|
||||||
|
int sockfd;
|
||||||
|
size_t len;
|
||||||
|
const char *fmt;
|
||||||
|
|
||||||
|
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||||
|
warn("socket 'AF_INET':");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Retreive MAC address of interface */
|
||||||
|
len = IEEE80211_ADDR_LEN;
|
||||||
|
fmt = NULL;
|
||||||
|
if (load_ieee80211req(sockfd, interface, &bssid, IEEE80211_IOC_BSSID, &len))
|
||||||
|
{
|
||||||
|
/* Retrieve info on station with above BSSID */
|
||||||
|
memset(&info, 0, sizeof(info));
|
||||||
|
memcpy(info.sta.is_u.macaddr, bssid, sizeof(bssid));
|
||||||
|
|
||||||
|
len = sizeof(info);
|
||||||
|
if (load_ieee80211req(sockfd, interface, &info, IEEE80211_IOC_STA_INFO, &len)) {
|
||||||
|
rssi_dbm = info.sta.info[0].isi_noise +
|
||||||
|
info.sta.info[0].isi_rssi / 2;
|
||||||
|
|
||||||
|
fmt = bprintf("%d", RSSI_TO_PERC(rssi_dbm));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(sockfd);
|
||||||
|
return fmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
wifi_essid(const char *interface)
|
||||||
|
{
|
||||||
|
char ssid[IEEE80211_NWID_LEN + 1];
|
||||||
|
size_t len;
|
||||||
|
int sockfd;
|
||||||
|
const char *fmt;
|
||||||
|
|
||||||
|
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||||
|
warn("socket 'AF_INET':");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt = NULL;
|
||||||
|
len = sizeof(ssid);
|
||||||
|
memset(&ssid, 0, len);
|
||||||
|
if (load_ieee80211req(sockfd, interface, &ssid, IEEE80211_IOC_SSID, &len )) {
|
||||||
|
if (len < sizeof(ssid))
|
||||||
|
len += 1;
|
||||||
|
else
|
||||||
|
len = sizeof(ssid);
|
||||||
|
|
||||||
|
ssid[len - 1] = '\0';
|
||||||
|
fmt = bprintf("%s", ssid);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(sockfd);
|
||||||
|
return fmt;
|
||||||
|
}
|
||||||
|
#endif
|
73
slstatus/config.def.h
Normal file
73
slstatus/config.def.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
|
||||||
|
/* interval between updates (in ms) */
|
||||||
|
const unsigned int interval = 1000;
|
||||||
|
|
||||||
|
/* text to show if no value can be retrieved */
|
||||||
|
static const char unknown_str[] = "n/a";
|
||||||
|
|
||||||
|
/* maximum output string length */
|
||||||
|
#define MAXLEN 2048
|
||||||
|
|
||||||
|
/*
|
||||||
|
* function description argument (example)
|
||||||
|
*
|
||||||
|
* battery_perc battery percentage battery name (BAT0)
|
||||||
|
* NULL on OpenBSD/FreeBSD
|
||||||
|
* battery_state battery charging state battery name (BAT0)
|
||||||
|
* NULL on OpenBSD/FreeBSD
|
||||||
|
* battery_remaining battery remaining HH:MM battery name (BAT0)
|
||||||
|
* NULL on OpenBSD/FreeBSD
|
||||||
|
* cpu_perc cpu usage in percent NULL
|
||||||
|
* cpu_freq cpu frequency in MHz NULL
|
||||||
|
* datetime date and time format string (%F %T)
|
||||||
|
* disk_free free disk space in GB mountpoint path (/)
|
||||||
|
* disk_perc disk usage in percent mountpoint path (/)
|
||||||
|
* disk_total total disk space in GB mountpoint path (/")
|
||||||
|
* disk_used used disk space in GB mountpoint path (/)
|
||||||
|
* entropy available entropy NULL
|
||||||
|
* gid GID of current user NULL
|
||||||
|
* hostname hostname NULL
|
||||||
|
* ipv4 IPv4 address interface name (eth0)
|
||||||
|
* ipv6 IPv6 address interface name (eth0)
|
||||||
|
* kernel_release `uname -r` NULL
|
||||||
|
* keyboard_indicators caps/num lock indicators format string (c?n?)
|
||||||
|
* see keyboard_indicators.c
|
||||||
|
* keymap layout (variant) of current NULL
|
||||||
|
* keymap
|
||||||
|
* load_avg load average NULL
|
||||||
|
* netspeed_rx receive network speed interface name (wlan0)
|
||||||
|
* netspeed_tx transfer network speed interface name (wlan0)
|
||||||
|
* num_files number of files in a directory path
|
||||||
|
* (/home/foo/Inbox/cur)
|
||||||
|
* ram_free free memory in GB NULL
|
||||||
|
* ram_perc memory usage in percent NULL
|
||||||
|
* ram_total total memory size in GB NULL
|
||||||
|
* ram_used used memory in GB NULL
|
||||||
|
* run_command custom shell command command (echo foo)
|
||||||
|
* separator string to echo NULL
|
||||||
|
* swap_free free swap in GB NULL
|
||||||
|
* swap_perc swap usage in percent NULL
|
||||||
|
* swap_total total swap size in GB NULL
|
||||||
|
* swap_used used swap in GB NULL
|
||||||
|
* temp temperature in degree celsius sensor file
|
||||||
|
* (/sys/class/thermal/...)
|
||||||
|
* NULL on OpenBSD
|
||||||
|
* thermal zone on FreeBSD
|
||||||
|
* (tz0, tz1, etc.)
|
||||||
|
* uid UID of current user NULL
|
||||||
|
* uptime system uptime NULL
|
||||||
|
* username username of current user NULL
|
||||||
|
* vol_perc OSS/ALSA volume in percent mixer file (/dev/mixer)
|
||||||
|
* NULL on OpenBSD
|
||||||
|
* wifi_perc WiFi signal in percent interface name (wlan0)
|
||||||
|
* wifi_essid WiFi ESSID interface name (wlan0)
|
||||||
|
*/
|
||||||
|
static const struct arg args[] = {
|
||||||
|
/* function format argument */
|
||||||
|
{ vol_perc, "%s%|", "%H:%M" },
|
||||||
|
{ wifi_perc, "%s%|", "%H:%M" },
|
||||||
|
{ battery_perc, "%s%|", "BAT0" },
|
||||||
|
{ battery_perc, "%s%|", "BAT1" },
|
||||||
|
{ datetime, "%s", "%H:%M" },
|
||||||
|
};
|
73
slstatus/config.h
Normal file
73
slstatus/config.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
|
||||||
|
/* interval between updates (in ms) */
|
||||||
|
const unsigned int interval = 1000;
|
||||||
|
|
||||||
|
/* text to show if no value can be retrieved */
|
||||||
|
static const char unknown_str[] = "n/a";
|
||||||
|
|
||||||
|
/* maximum output string length */
|
||||||
|
#define MAXLEN 2048
|
||||||
|
|
||||||
|
/*
|
||||||
|
* function description argument (example)
|
||||||
|
*
|
||||||
|
* battery_perc battery percentage battery name (BAT0)
|
||||||
|
* NULL on OpenBSD/FreeBSD
|
||||||
|
* battery_state battery charging state battery name (BAT0)
|
||||||
|
* NULL on OpenBSD/FreeBSD
|
||||||
|
* battery_remaining battery remaining HH:MM battery name (BAT0)
|
||||||
|
* NULL on OpenBSD/FreeBSD
|
||||||
|
* cpu_perc cpu usage in percent NULL
|
||||||
|
* cpu_freq cpu frequency in MHz NULL
|
||||||
|
* datetime date and time format string (%F %T)
|
||||||
|
* disk_free free disk space in GB mountpoint path (/)
|
||||||
|
* disk_perc disk usage in percent mountpoint path (/)
|
||||||
|
* disk_total total disk space in GB mountpoint path (/")
|
||||||
|
* disk_used used disk space in GB mountpoint path (/)
|
||||||
|
* entropy available entropy NULL
|
||||||
|
* gid GID of current user NULL
|
||||||
|
* hostname hostname NULL
|
||||||
|
* ipv4 IPv4 address interface name (eth0)
|
||||||
|
* ipv6 IPv6 address interface name (eth0)
|
||||||
|
* kernel_release `uname -r` NULL
|
||||||
|
* keyboard_indicators caps/num lock indicators format string (c?n?)
|
||||||
|
* see keyboard_indicators.c
|
||||||
|
* keymap layout (variant) of current NULL
|
||||||
|
* keymap
|
||||||
|
* load_avg load average NULL
|
||||||
|
* netspeed_rx receive network speed interface name (wlan0)
|
||||||
|
* netspeed_tx transfer network speed interface name (wlan0)
|
||||||
|
* num_files number of files in a directory path
|
||||||
|
* (/home/foo/Inbox/cur)
|
||||||
|
* ram_free free memory in GB NULL
|
||||||
|
* ram_perc memory usage in percent NULL
|
||||||
|
* ram_total total memory size in GB NULL
|
||||||
|
* ram_used used memory in GB NULL
|
||||||
|
* run_command custom shell command command (echo foo)
|
||||||
|
* separator string to echo NULL
|
||||||
|
* swap_free free swap in GB NULL
|
||||||
|
* swap_perc swap usage in percent NULL
|
||||||
|
* swap_total total swap size in GB NULL
|
||||||
|
* swap_used used swap in GB NULL
|
||||||
|
* temp temperature in degree celsius sensor file
|
||||||
|
* (/sys/class/thermal/...)
|
||||||
|
* NULL on OpenBSD
|
||||||
|
* thermal zone on FreeBSD
|
||||||
|
* (tz0, tz1, etc.)
|
||||||
|
* uid UID of current user NULL
|
||||||
|
* uptime system uptime NULL
|
||||||
|
* username username of current user NULL
|
||||||
|
* vol_perc OSS/ALSA volume in percent mixer file (/dev/mixer)
|
||||||
|
* NULL on OpenBSD
|
||||||
|
* wifi_perc WiFi signal in percent interface name (wlan0)
|
||||||
|
* wifi_essid WiFi ESSID interface name (wlan0)
|
||||||
|
*/
|
||||||
|
static const struct arg args[] = {
|
||||||
|
/* function format argument */
|
||||||
|
{ run_command, " 墳 %s% | ", "pamixer --get-volume" },
|
||||||
|
{ temp, "Coffee: %s°C | ", "/sys/class/hwmon/hwmon5/temp1_input" },
|
||||||
|
{ battery_perc, " %s% | ", "BAT0" },
|
||||||
|
{ battery_perc, " %s% | ", "BAT1" },
|
||||||
|
{ datetime, "%s ", " %d/%m | %H:%M" },
|
||||||
|
};
|
22
slstatus/config.mk
Normal file
22
slstatus/config.mk
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# slstatus version
|
||||||
|
VERSION = 0
|
||||||
|
|
||||||
|
# customize below to fit your system
|
||||||
|
|
||||||
|
# paths
|
||||||
|
PREFIX = /usr/local
|
||||||
|
MANPREFIX = $(PREFIX)/share/man
|
||||||
|
|
||||||
|
X11INC = /usr/X11R6/include
|
||||||
|
X11LIB = /usr/X11R6/lib
|
||||||
|
|
||||||
|
# flags
|
||||||
|
CPPFLAGS = -I$(X11INC) -D_DEFAULT_SOURCE
|
||||||
|
CFLAGS = -std=c99 -pedantic -Wall -Wextra -Os
|
||||||
|
LDFLAGS = -L$(X11LIB) -s
|
||||||
|
# OpenBSD: add -lsndio
|
||||||
|
# FreeBSD: add -lkvm
|
||||||
|
LDLIBS = -lX11
|
||||||
|
|
||||||
|
# compiler and linker
|
||||||
|
CC = cc
|
28
slstatus/slstatus.1
Normal file
28
slstatus/slstatus.1
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
.Dd 2020-06-23
|
||||||
|
.Dt SLSTATUS 1
|
||||||
|
.Os
|
||||||
|
.Sh NAME
|
||||||
|
.Nm slstatus
|
||||||
|
.Nd suckless status monitor
|
||||||
|
.Sh SYNOPSIS
|
||||||
|
.Nm
|
||||||
|
.Op Fl s
|
||||||
|
.Op Fl 1
|
||||||
|
.Sh DESCRIPTION
|
||||||
|
.Nm
|
||||||
|
is a suckless status monitor for window managers that use WM_NAME (e.g. dwm) or
|
||||||
|
stdin to fill the status bar.
|
||||||
|
By default,
|
||||||
|
.Nm
|
||||||
|
outputs to WM_NAME.
|
||||||
|
.Sh OPTIONS
|
||||||
|
.Bl -tag -width Ds
|
||||||
|
.It Fl s
|
||||||
|
Write to stdout instead of WM_NAME.
|
||||||
|
.It Fl 1
|
||||||
|
Write once to stdout and quit.
|
||||||
|
.El
|
||||||
|
.Sh CUSTOMIZATION
|
||||||
|
.Nm
|
||||||
|
can be customized by creating a custom config.h and (re)compiling the source
|
||||||
|
code. This keeps it fast, secure and simple.
|
141
slstatus/slstatus.c
Normal file
141
slstatus/slstatus.c
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <errno.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
|
||||||
|
#include "arg.h"
|
||||||
|
#include "slstatus.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
struct arg {
|
||||||
|
const char *(*func)();
|
||||||
|
const char *fmt;
|
||||||
|
const char *args;
|
||||||
|
};
|
||||||
|
|
||||||
|
char buf[1024];
|
||||||
|
static volatile sig_atomic_t done;
|
||||||
|
static Display *dpy;
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
static void
|
||||||
|
terminate(const int signo)
|
||||||
|
{
|
||||||
|
if (signo != SIGUSR1)
|
||||||
|
done = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
difftimespec(struct timespec *res, struct timespec *a, struct timespec *b)
|
||||||
|
{
|
||||||
|
res->tv_sec = a->tv_sec - b->tv_sec - (a->tv_nsec < b->tv_nsec);
|
||||||
|
res->tv_nsec = a->tv_nsec - b->tv_nsec +
|
||||||
|
(a->tv_nsec < b->tv_nsec) * 1E9;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
usage(void)
|
||||||
|
{
|
||||||
|
die("usage: %s [-s] [-1]", argv0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
struct sigaction act;
|
||||||
|
struct timespec start, current, diff, intspec, wait;
|
||||||
|
size_t i, len;
|
||||||
|
int sflag, ret;
|
||||||
|
char status[MAXLEN];
|
||||||
|
const char *res;
|
||||||
|
|
||||||
|
sflag = 0;
|
||||||
|
ARGBEGIN {
|
||||||
|
case '1':
|
||||||
|
done = 1;
|
||||||
|
/* fallthrough */
|
||||||
|
case 's':
|
||||||
|
sflag = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage();
|
||||||
|
} ARGEND
|
||||||
|
|
||||||
|
if (argc) {
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&act, 0, sizeof(act));
|
||||||
|
act.sa_handler = terminate;
|
||||||
|
sigaction(SIGINT, &act, NULL);
|
||||||
|
sigaction(SIGTERM, &act, NULL);
|
||||||
|
act.sa_flags |= SA_RESTART;
|
||||||
|
sigaction(SIGUSR1, &act, NULL);
|
||||||
|
|
||||||
|
if (!sflag && !(dpy = XOpenDisplay(NULL))) {
|
||||||
|
die("XOpenDisplay: Failed to open display");
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (clock_gettime(CLOCK_MONOTONIC, &start) < 0) {
|
||||||
|
die("clock_gettime:");
|
||||||
|
}
|
||||||
|
|
||||||
|
status[0] = '\0';
|
||||||
|
for (i = len = 0; i < LEN(args); i++) {
|
||||||
|
if (!(res = args[i].func(args[i].args))) {
|
||||||
|
res = unknown_str;
|
||||||
|
}
|
||||||
|
if ((ret = esnprintf(status + len, sizeof(status) - len,
|
||||||
|
args[i].fmt, res)) < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
len += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sflag) {
|
||||||
|
puts(status);
|
||||||
|
fflush(stdout);
|
||||||
|
if (ferror(stdout))
|
||||||
|
die("puts:");
|
||||||
|
} else {
|
||||||
|
if (XStoreName(dpy, DefaultRootWindow(dpy), status)
|
||||||
|
< 0) {
|
||||||
|
die("XStoreName: Allocation failed");
|
||||||
|
}
|
||||||
|
XFlush(dpy);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!done) {
|
||||||
|
if (clock_gettime(CLOCK_MONOTONIC, ¤t) < 0) {
|
||||||
|
die("clock_gettime:");
|
||||||
|
}
|
||||||
|
difftimespec(&diff, ¤t, &start);
|
||||||
|
|
||||||
|
intspec.tv_sec = interval / 1000;
|
||||||
|
intspec.tv_nsec = (interval % 1000) * 1E6;
|
||||||
|
difftimespec(&wait, &intspec, &diff);
|
||||||
|
|
||||||
|
if (wait.tv_sec >= 0) {
|
||||||
|
if (nanosleep(&wait, NULL) < 0 &&
|
||||||
|
errno != EINTR) {
|
||||||
|
die("nanosleep:");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (!done);
|
||||||
|
|
||||||
|
if (!sflag) {
|
||||||
|
XStoreName(dpy, DefaultRootWindow(dpy), NULL);
|
||||||
|
if (XCloseDisplay(dpy) < 0) {
|
||||||
|
die("XCloseDisplay: Failed to close display");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
84
slstatus/slstatus.h
Normal file
84
slstatus/slstatus.h
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
|
||||||
|
/* battery */
|
||||||
|
const char *battery_perc(const char *);
|
||||||
|
const char *battery_state(const char *);
|
||||||
|
const char *battery_remaining(const char *);
|
||||||
|
|
||||||
|
/* cpu */
|
||||||
|
const char *cpu_freq(void);
|
||||||
|
const char *cpu_perc(void);
|
||||||
|
|
||||||
|
/* datetime */
|
||||||
|
const char *datetime(const char *fmt);
|
||||||
|
|
||||||
|
/* disk */
|
||||||
|
const char *disk_free(const char *path);
|
||||||
|
const char *disk_perc(const char *path);
|
||||||
|
const char *disk_total(const char *path);
|
||||||
|
const char *disk_used(const char *path);
|
||||||
|
|
||||||
|
/* entropy */
|
||||||
|
const char *entropy(void);
|
||||||
|
|
||||||
|
/* hostname */
|
||||||
|
const char *hostname(void);
|
||||||
|
|
||||||
|
/* ip */
|
||||||
|
const char *ipv4(const char *interface);
|
||||||
|
const char *ipv6(const char *interface);
|
||||||
|
|
||||||
|
/* kernel_release */
|
||||||
|
const char *kernel_release(void);
|
||||||
|
|
||||||
|
/* keyboard_indicators */
|
||||||
|
const char *keyboard_indicators(void);
|
||||||
|
|
||||||
|
/* keymap */
|
||||||
|
const char *keymap(void);
|
||||||
|
|
||||||
|
/* load_avg */
|
||||||
|
const char *load_avg(void);
|
||||||
|
|
||||||
|
/* netspeeds */
|
||||||
|
const char *netspeed_rx(const char *interface);
|
||||||
|
const char *netspeed_tx(const char *interface);
|
||||||
|
|
||||||
|
/* num_files */
|
||||||
|
const char *num_files(const char *path);
|
||||||
|
|
||||||
|
/* ram */
|
||||||
|
const char *ram_free(void);
|
||||||
|
const char *ram_perc(void);
|
||||||
|
const char *ram_total(void);
|
||||||
|
const char *ram_used(void);
|
||||||
|
|
||||||
|
/* run_command */
|
||||||
|
const char *run_command(const char *cmd);
|
||||||
|
|
||||||
|
/* separator */
|
||||||
|
const char *separator(const char *separator);
|
||||||
|
|
||||||
|
/* swap */
|
||||||
|
const char *swap_free(void);
|
||||||
|
const char *swap_perc(void);
|
||||||
|
const char *swap_total(void);
|
||||||
|
const char *swap_used(void);
|
||||||
|
|
||||||
|
/* temperature */
|
||||||
|
const char *temp(const char *);
|
||||||
|
|
||||||
|
/* uptime */
|
||||||
|
const char *uptime(void);
|
||||||
|
|
||||||
|
/* user */
|
||||||
|
const char *gid(void);
|
||||||
|
const char *username(void);
|
||||||
|
const char *uid(void);
|
||||||
|
|
||||||
|
/* volume */
|
||||||
|
const char *vol_perc(const char *card);
|
||||||
|
|
||||||
|
/* wifi */
|
||||||
|
const char *wifi_perc(const char *interface);
|
||||||
|
const char *wifi_essid(const char *interface);
|
146
slstatus/util.c
Normal file
146
slstatus/util.c
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
char *argv0;
|
||||||
|
|
||||||
|
static void
|
||||||
|
verr(const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
if (argv0 && strncmp(fmt, "usage", sizeof("usage") - 1)) {
|
||||||
|
fprintf(stderr, "%s: ", argv0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
|
||||||
|
if (fmt[0] && fmt[strlen(fmt) - 1] == ':') {
|
||||||
|
fputc(' ', stderr);
|
||||||
|
perror(NULL);
|
||||||
|
} else {
|
||||||
|
fputc('\n', stderr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
warn(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
verr(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
die(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
verr(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
evsnprintf(char *str, size_t size, const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = vsnprintf(str, size, fmt, ap);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
warn("vsnprintf:");
|
||||||
|
return -1;
|
||||||
|
} else if ((size_t)ret >= size) {
|
||||||
|
warn("vsnprintf: Output truncated");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
esnprintf(char *str, size_t size, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
ret = evsnprintf(str, size, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
bprintf(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
ret = evsnprintf(buf, sizeof(buf), fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
return (ret < 0) ? NULL : buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
fmt_human(uintmax_t num, int base)
|
||||||
|
{
|
||||||
|
double scaled;
|
||||||
|
size_t i, prefixlen;
|
||||||
|
const char **prefix;
|
||||||
|
const char *prefix_1000[] = { "", "k", "M", "G", "T", "P", "E", "Z",
|
||||||
|
"Y" };
|
||||||
|
const char *prefix_1024[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei",
|
||||||
|
"Zi", "Yi" };
|
||||||
|
|
||||||
|
switch (base) {
|
||||||
|
case 1000:
|
||||||
|
prefix = prefix_1000;
|
||||||
|
prefixlen = LEN(prefix_1000);
|
||||||
|
break;
|
||||||
|
case 1024:
|
||||||
|
prefix = prefix_1024;
|
||||||
|
prefixlen = LEN(prefix_1024);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
warn("fmt_human: Invalid base");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
scaled = num;
|
||||||
|
for (i = 0; i < prefixlen && scaled >= base; i++) {
|
||||||
|
scaled /= base;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bprintf("%.1f %s", scaled, prefix[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
pscanf(const char *path, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
va_list ap;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
if (!(fp = fopen(path, "r"))) {
|
||||||
|
warn("fopen '%s':", path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
va_start(ap, fmt);
|
||||||
|
n = vfscanf(fp, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
return (n == EOF) ? -1 : n;
|
||||||
|
}
|
16
slstatus/util.h
Normal file
16
slstatus/util.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/* See LICENSE file for copyright and license details. */
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
extern char buf[1024];
|
||||||
|
|
||||||
|
#define LEN(x) (sizeof (x) / sizeof *(x))
|
||||||
|
|
||||||
|
extern char *argv0;
|
||||||
|
|
||||||
|
void warn(const char *, ...);
|
||||||
|
void die(const char *, ...);
|
||||||
|
|
||||||
|
int esnprintf(char *str, size_t size, const char *fmt, ...);
|
||||||
|
const char *bprintf(const char *fmt, ...);
|
||||||
|
const char *fmt_human(uintmax_t num, int base);
|
||||||
|
int pscanf(const char *path, const char *fmt, ...);
|
19
somebar/.builds/archlinux.yml
Normal file
19
somebar/.builds/archlinux.yml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
image: archlinux
|
||||||
|
packages:
|
||||||
|
- base-devel
|
||||||
|
- meson
|
||||||
|
- wayland
|
||||||
|
- wayland-protocols
|
||||||
|
- cairo
|
||||||
|
- pango
|
||||||
|
sources:
|
||||||
|
- https://git.sr.ht/~raphi/somebar
|
||||||
|
tasks:
|
||||||
|
- setup: |
|
||||||
|
cd somebar
|
||||||
|
meson build --fatal-meson-warnings
|
||||||
|
cp src/config.def.hpp src/config.hpp
|
||||||
|
- build: |
|
||||||
|
cd somebar/build
|
||||||
|
ninja
|
||||||
|
sudo ninja install
|
20
somebar/.builds/freebsd.yml
Normal file
20
somebar/.builds/freebsd.yml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
image: freebsd/latest
|
||||||
|
packages:
|
||||||
|
- devel/evdev-proto
|
||||||
|
- devel/meson
|
||||||
|
- devel/pkgconf
|
||||||
|
- graphics/cairo
|
||||||
|
- graphics/wayland
|
||||||
|
- graphics/wayland-protocols
|
||||||
|
- x11-toolkits/pango
|
||||||
|
sources:
|
||||||
|
- https://git.sr.ht/~raphi/somebar
|
||||||
|
tasks:
|
||||||
|
- setup: |
|
||||||
|
cd somebar
|
||||||
|
meson build --fatal-meson-warnings
|
||||||
|
cp src/config.def.hpp src/config.hpp
|
||||||
|
- build: |
|
||||||
|
cd somebar/build
|
||||||
|
ninja
|
||||||
|
sudo ninja install
|
5
somebar/.editorconfig
Normal file
5
somebar/.editorconfig
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = tab
|
||||||
|
indent_brace_style = K&R
|
3
somebar/.gitignore
vendored
Normal file
3
somebar/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
compile_commands.json
|
||||||
|
build
|
||||||
|
.cache
|
20
somebar/LICENSE
Normal file
20
somebar/LICENSE
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
Copyright (c) 2021 Raphael Robatsch
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice (including the next
|
||||||
|
paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
109
somebar/README.md
Normal file
109
somebar/README.md
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
# somebar - dwm-like bar for dwl
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The mailing list for this project is
|
||||||
|
[~raphi/public-inbox@lists.sr.ht](mailto:~raphi/public-inbox@lists.sr.ht).
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
* c++ compiler, meson, and ninja
|
||||||
|
* wayland-scanner
|
||||||
|
* libwayland-client
|
||||||
|
* libwayland-cursor
|
||||||
|
* libcairo
|
||||||
|
* libpango
|
||||||
|
* libpangocairo
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo apt install build-essential meson ninja-build \
|
||||||
|
libwayland-bin libwayland-client0 libwayland-cursor0 libwayland-dev \
|
||||||
|
libcairo2 libcairo2-dev \
|
||||||
|
libpango-1.0-0 libpango1.0-dev libpangocairo-1.0-0
|
||||||
|
|
||||||
|
# or
|
||||||
|
|
||||||
|
sudo pacman -S base-devel meson \
|
||||||
|
wayland wayland-protocols cairo pango
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Copy `src/config.def.hpp` to `src/config.hpp`, and adjust if needed.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
```
|
||||||
|
cp src/config.def.hpp src/config.hpp
|
||||||
|
meson setup build
|
||||||
|
ninja -C build
|
||||||
|
sudo ninja -C build install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
You must start somebar using dwl's `-s` flag, e.g. `dwl -s somebar`.
|
||||||
|
|
||||||
|
Somebar can be controlled by writing to `$XDG_RUNTIME_DIR/somebar-0`
|
||||||
|
or the path defined by `-s` argument.
|
||||||
|
The following commands are supported:
|
||||||
|
|
||||||
|
* `status TEXT`: Updates the status bar
|
||||||
|
* `hide MONITOR` Hides somebar on the specified monitor
|
||||||
|
* `show MONITOR` Shows somebar on the specified monitor
|
||||||
|
* `toggle MONITOR` Toggles somebar on the specified monitor
|
||||||
|
|
||||||
|
MONITOR is an zxdg_output_v1 name, which can be determined e.g. using `weston-info`.
|
||||||
|
Additionally, MONITOR can be `all` (all monitors) or `selected` (the monitor with focus).
|
||||||
|
|
||||||
|
Commands can be sent either by writing to the file name above, or equivalently by calling
|
||||||
|
somebar with the `-c` argument. For example: `somebar -c toggle all`. This is recommended
|
||||||
|
for shell scripts, as there is no race-free way to write to a file only if it exists.
|
||||||
|
|
||||||
|
The maintainer of somebar also maintains
|
||||||
|
[someblocks](https://git.sr.ht/~raphi/someblocks/),
|
||||||
|
a fork of [dwmblocks](https://github.com/torrinfail/dwmblocks) that outputs
|
||||||
|
to somebar instead of dwm's bar.
|
||||||
|
|
||||||
|
## IPC
|
||||||
|
|
||||||
|
Out of the box, somebar cannot control dwl. Clicking on the tag bar has no
|
||||||
|
effect, because there is no communication channel from somebar back to dwl.
|
||||||
|
|
||||||
|
If you apply the patch `contrib/ipc.patch`, then somebar will
|
||||||
|
|
||||||
|
1. Not read stdin anymore, and instead use a wayland extension to read dwl's
|
||||||
|
state. This means you must close stdin yourself, if you choose to launch
|
||||||
|
somebar using dwl's -s flag.
|
||||||
|
2. somebar can use the same wayland extension to send commands back to dwl.
|
||||||
|
This means that clicking on tags will switch to that tag (this can of course
|
||||||
|
be customized in config.h).
|
||||||
|
|
||||||
|
If you apply the IPC patch to somebar, then
|
||||||
|
**dwl must have the [wayland-ipc patch](https://git.sr.ht/~raphi/dwl/blob/master/patches/wayland-ipc.patch) applied too**,
|
||||||
|
since dwl must implement the wayland extension too.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
somebar - dwm-like bar for dwl
|
||||||
|
|
||||||
|
Copyright (c) 2021 Raphael Robatsch
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice (including the next
|
||||||
|
paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
504
somebar/contrib/ipc.patch
Normal file
504
somebar/contrib/ipc.patch
Normal file
@ -0,0 +1,504 @@
|
|||||||
|
Replaces somebar's channel to dwl from stdin to a wayland extension.
|
||||||
|
|
||||||
|
Normally, somebar reads dwl's state by reading from stdin. This requires
|
||||||
|
that somebar is started from dwl, and does not allow sending commands back
|
||||||
|
to dwl.
|
||||||
|
|
||||||
|
This patch replaces this channel with a wayland protocol extension. somebar
|
||||||
|
can use this protocol extension to observe and update the dwl window management
|
||||||
|
state.
|
||||||
|
|
||||||
|
Important! dwl must have the wayland-ipc patch applied for this to work!
|
||||||
|
https://git.sr.ht/~raphi/dwl/blob/master/patches/wayland-ipc.patch
|
||||||
|
diff --git a/protocols/meson.build b/protocols/meson.build
|
||||||
|
index 7bd222b..5752608 100644
|
||||||
|
--- a/protocols/meson.build
|
||||||
|
+++ b/protocols/meson.build
|
||||||
|
@@ -15,6 +15,7 @@ wayland_xmls = [
|
||||||
|
wl_protocol_dir + '/stable/xdg-shell/xdg-shell.xml',
|
||||||
|
wl_protocol_dir + '/unstable/xdg-output/xdg-output-unstable-v1.xml',
|
||||||
|
'wlr-layer-shell-unstable-v1.xml',
|
||||||
|
+ 'net-tapesoftware-dwl-wm-unstable-v1.xml',
|
||||||
|
]
|
||||||
|
wayland_sources = [
|
||||||
|
wayland_scanner_code.process(wayland_xmls),
|
||||||
|
diff --git a/protocols/net-tapesoftware-dwl-wm-unstable-v1.xml b/protocols/net-tapesoftware-dwl-wm-unstable-v1.xml
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..390f5a1
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/protocols/net-tapesoftware-dwl-wm-unstable-v1.xml
|
||||||
|
@@ -0,0 +1,164 @@
|
||||||
|
+<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
+<protocol name="net_tapesoftware_dwl_wm_unstable_v1">
|
||||||
|
+ <copyright>
|
||||||
|
+ Copyright (c) 2021 Raphael Robatsch
|
||||||
|
+
|
||||||
|
+ Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
+ copy of this software and associated documentation files (the
|
||||||
|
+ "Software"), to deal in the Software without restriction, including
|
||||||
|
+ without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
+ distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
+ permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
+ the following conditions:
|
||||||
|
+
|
||||||
|
+ The above copyright notice and this permission notice (including the
|
||||||
|
+ next paragraph) shall be included in all copies or substantial portions
|
||||||
|
+ of the Software.
|
||||||
|
+
|
||||||
|
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
+ </copyright>
|
||||||
|
+
|
||||||
|
+ <interface name="znet_tapesoftware_dwl_wm_v1" version="1">
|
||||||
|
+ <description summary="control the dwl state">
|
||||||
|
+ This interface is exposed as a global in the wl_registry.
|
||||||
|
+
|
||||||
|
+ Clients can use this protocol to receive updates of the window manager
|
||||||
|
+ state (active tags, active layout, and focused window).
|
||||||
|
+ Clients can also control this state.
|
||||||
|
+
|
||||||
|
+ After binding, the client will receive the available tags and layouts
|
||||||
|
+ with the 'tag' and 'layout' events. These can be used in subsequent
|
||||||
|
+ dwl_wm_monitor_v1.set_tags/set_layout requests, and to interpret the
|
||||||
|
+ dwl_wm_monitor_v1.layout/tag events.
|
||||||
|
+ </description>
|
||||||
|
+
|
||||||
|
+ <request name="release" type="destructor">
|
||||||
|
+ <description summary="release dwl_wm">
|
||||||
|
+ This request indicates that the client will not use the dwl_wm
|
||||||
|
+ object any more. Objects that have been created through this instance
|
||||||
|
+ are not affected.
|
||||||
|
+ </description>
|
||||||
|
+ </request>
|
||||||
|
+
|
||||||
|
+ <request name="get_monitor">
|
||||||
|
+ <description summary="gets a dwl monitor from an output">
|
||||||
|
+ Gets a dwl monitor for the specified output. The window manager
|
||||||
|
+ state on the output can be controlled using the monitor.
|
||||||
|
+ </description>
|
||||||
|
+ <arg name="id" type="new_id" interface="znet_tapesoftware_dwl_wm_monitor_v1" />
|
||||||
|
+ <arg name="output" type="object" interface="wl_output" />
|
||||||
|
+ </request>
|
||||||
|
+
|
||||||
|
+ <event name="tag">
|
||||||
|
+ <description summary="announces the presence of a tag">
|
||||||
|
+ This event is sent immediately after binding.
|
||||||
|
+ A roundtrip after binding guarantees that the client has received all tags.
|
||||||
|
+ </description>
|
||||||
|
+ <arg name="name" type="string"/>
|
||||||
|
+ </event>
|
||||||
|
+
|
||||||
|
+ <event name="layout">
|
||||||
|
+ <description summary="announces the presence of a layout">
|
||||||
|
+ This event is sent immediately after binding.
|
||||||
|
+ A roundtrip after binding guarantees that the client has received all layouts.
|
||||||
|
+ </description>
|
||||||
|
+ <arg name="name" type="string"/>
|
||||||
|
+ </event>
|
||||||
|
+ </interface>
|
||||||
|
+
|
||||||
|
+ <interface name="znet_tapesoftware_dwl_wm_monitor_v1" version="1">
|
||||||
|
+ <description summary="control one monitor">
|
||||||
|
+ Observes and controls one monitor.
|
||||||
|
+
|
||||||
|
+ Events are double-buffered: Clients should cache all events and only
|
||||||
|
+ redraw themselves once the 'frame' event is sent.
|
||||||
|
+
|
||||||
|
+ Requests are not double-buffered: The compositor will update itself
|
||||||
|
+ immediately.
|
||||||
|
+ </description>
|
||||||
|
+
|
||||||
|
+ <enum name="tag_state">
|
||||||
|
+ <entry name="none" value="0" summary="no state"/>
|
||||||
|
+ <entry name="active" value="1" summary="tag is active"/>
|
||||||
|
+ <entry name="urgent" value="2" summary="tag has at least one urgent client"/>
|
||||||
|
+ </enum>
|
||||||
|
+
|
||||||
|
+ <request name="release" type="destructor">
|
||||||
|
+ <description summary="release dwl_monitor">
|
||||||
|
+ This request indicates that the client is done with this dwl_monitor.
|
||||||
|
+ All further requests are ignored.
|
||||||
|
+ </description>
|
||||||
|
+ </request>
|
||||||
|
+
|
||||||
|
+ <event name="selected">
|
||||||
|
+ <description summary="updates the selected state of the monitor">
|
||||||
|
+ If 'selected' is nonzero, this monitor is the currently selected one.
|
||||||
|
+ </description>
|
||||||
|
+ <arg name="selected" type="uint"/>
|
||||||
|
+ </event>
|
||||||
|
+
|
||||||
|
+ <event name="tag">
|
||||||
|
+ <description summary="updates the state of one tag">
|
||||||
|
+ Announces the update of a tag. num_clients and focused_client can be
|
||||||
|
+ used to draw client indicators.
|
||||||
|
+ </description>
|
||||||
|
+ <arg name="tag" type="uint" summary="index of a tag received by the dwl_wm_v1.tag event." />
|
||||||
|
+ <arg name="state" type="uint" enum="tag_state"/>
|
||||||
|
+ <arg name="num_clients" type="uint" summary="number of clients on this tag"/>
|
||||||
|
+ <arg name="focused_client" type="int" summary="out of num_clients. -1 if there is no focused client"/>
|
||||||
|
+ </event>
|
||||||
|
+
|
||||||
|
+ <event name="layout">
|
||||||
|
+ <description summary="updates the selected layout">
|
||||||
|
+ Announces the update of the selected layout.
|
||||||
|
+ </description>
|
||||||
|
+ <arg name="layout" type="uint" summary="index of a layout received by the dwl_wm_v1.layout event."/>
|
||||||
|
+ </event>
|
||||||
|
+
|
||||||
|
+ <event name="title">
|
||||||
|
+ <description summary="updates the focused client">
|
||||||
|
+ Announces the update of the selected client.
|
||||||
|
+ </description>
|
||||||
|
+ <arg name="title" type="string"/>
|
||||||
|
+ </event>
|
||||||
|
+
|
||||||
|
+ <event name="frame">
|
||||||
|
+ <description summary="end of status update sequence">
|
||||||
|
+ Sent after all other events belonging to the status update has been sent.
|
||||||
|
+ Clients should redraw themselves now.
|
||||||
|
+ </description>
|
||||||
|
+ </event>
|
||||||
|
+
|
||||||
|
+ <request name="set_tags">
|
||||||
|
+ <description summary="sets the active tags on this monitor.">
|
||||||
|
+ Changes are applied immediately.
|
||||||
|
+ </description>
|
||||||
|
+ <arg name="tagmask" type="uint" summary="bitmask of the tags that should be set."/>
|
||||||
|
+ <arg name="toggle_tagset" type="uint"/>
|
||||||
|
+ </request>
|
||||||
|
+
|
||||||
|
+ <request name="set_client_tags">
|
||||||
|
+ <description summary="updates the tags of the focused client.">
|
||||||
|
+ tags are updated as follows:
|
||||||
|
+ new_tags = (current_tags AND and_tags) XOR xor_tags
|
||||||
|
+
|
||||||
|
+ Changes are applied immediately.
|
||||||
|
+ </description>
|
||||||
|
+ <arg name="and_tags" type="uint"/>
|
||||||
|
+ <arg name="xor_tags" type="uint"/>
|
||||||
|
+ </request>
|
||||||
|
+
|
||||||
|
+ <request name="set_layout">
|
||||||
|
+ <description summary="sets the active layout on this monitor.">
|
||||||
|
+ Changes are applied immediately.
|
||||||
|
+ </description>
|
||||||
|
+ <arg name="layout" type="uint" summary="index of a layout received by the dwl_wm_v1.layout event."/>
|
||||||
|
+ </request>
|
||||||
|
+ </interface>
|
||||||
|
+</protocol>
|
||||||
|
diff --git a/src/common.hpp b/src/common.hpp
|
||||||
|
index aed4480..12a3e2e 100644
|
||||||
|
--- a/src/common.hpp
|
||||||
|
+++ b/src/common.hpp
|
||||||
|
@@ -10,6 +10,7 @@
|
||||||
|
#include <cairo/cairo.h>
|
||||||
|
#include <pango/pango.h>
|
||||||
|
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||||
|
+#include "net-tapesoftware-dwl-wm-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
|
struct Color {
|
||||||
|
Color() {}
|
||||||
|
@@ -38,7 +39,14 @@ extern wl_display* display;
|
||||||
|
extern wl_compositor* compositor;
|
||||||
|
extern wl_shm* shm;
|
||||||
|
extern zwlr_layer_shell_v1* wlrLayerShell;
|
||||||
|
+extern std::vector<std::string> tagNames;
|
||||||
|
+extern std::vector<std::string> layoutNames;
|
||||||
|
|
||||||
|
+void view(Monitor& m, const Arg& arg);
|
||||||
|
+void toggleview(Monitor& m, const Arg& arg);
|
||||||
|
+void setlayout(Monitor& m, const Arg& arg);
|
||||||
|
+void tag(Monitor& m, const Arg& arg);
|
||||||
|
+void toggletag(Monitor& m, const Arg& arg);
|
||||||
|
void spawn(Monitor&, const Arg& arg);
|
||||||
|
void setCloexec(int fd);
|
||||||
|
[[noreturn]] void die(const char* why);
|
||||||
|
@@ -59,6 +67,7 @@ WL_DELETER(wl_output, wl_output_release);
|
||||||
|
WL_DELETER(wl_pointer, wl_pointer_release);
|
||||||
|
WL_DELETER(wl_seat, wl_seat_release);
|
||||||
|
WL_DELETER(wl_surface, wl_surface_destroy);
|
||||||
|
+WL_DELETER(znet_tapesoftware_dwl_wm_monitor_v1, znet_tapesoftware_dwl_wm_monitor_v1_release);
|
||||||
|
WL_DELETER(zwlr_layer_surface_v1, zwlr_layer_surface_v1_destroy);
|
||||||
|
|
||||||
|
WL_DELETER(cairo_t, cairo_destroy);
|
||||||
|
diff --git a/src/config.def.hpp b/src/config.def.hpp
|
||||||
|
index 40a8c95..a9560cb 100644
|
||||||
|
--- a/src/config.def.hpp
|
||||||
|
+++ b/src/config.def.hpp
|
||||||
|
@@ -16,12 +16,11 @@ constexpr ColorScheme colorInactive = {Color(0xbb, 0xbb, 0xbb), Color(0x22, 0x22
|
||||||
|
constexpr ColorScheme colorActive = {Color(0xee, 0xee, 0xee), Color(0x00, 0x55, 0x77)};
|
||||||
|
constexpr const char* termcmd[] = {"foot", nullptr};
|
||||||
|
|
||||||
|
-static std::vector<std::string> tagNames = {
|
||||||
|
- "1", "2", "3",
|
||||||
|
- "4", "5", "6",
|
||||||
|
- "7", "8", "9",
|
||||||
|
-};
|
||||||
|
-
|
||||||
|
constexpr Button buttons[] = {
|
||||||
|
+ { ClkTagBar, BTN_LEFT, view, {0} },
|
||||||
|
+ { ClkTagBar, BTN_RIGHT, tag, {0} },
|
||||||
|
+ { ClkTagBar, BTN_MIDDLE, toggletag, {0} },
|
||||||
|
+ { ClkLayoutSymbol, BTN_LEFT, setlayout, {.ui = 0} },
|
||||||
|
+ { ClkLayoutSymbol, BTN_RIGHT, setlayout, {.ui = 2} },
|
||||||
|
{ ClkStatusText, BTN_RIGHT, spawn, {.v = termcmd} },
|
||||||
|
};
|
||||||
|
diff --git a/src/main.cpp b/src/main.cpp
|
||||||
|
index 6198d8b..9e7549a 100644
|
||||||
|
--- a/src/main.cpp
|
||||||
|
+++ b/src/main.cpp
|
||||||
|
@@ -3,7 +3,6 @@
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdio>
|
||||||
|
-#include <sstream>
|
||||||
|
#include <list>
|
||||||
|
#include <optional>
|
||||||
|
#include <utility>
|
||||||
|
@@ -21,8 +20,8 @@
|
||||||
|
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||||
|
#include "xdg-output-unstable-v1-client-protocol.h"
|
||||||
|
#include "xdg-shell-client-protocol.h"
|
||||||
|
+#include "net-tapesoftware-dwl-wm-unstable-v1-client-protocol.h"
|
||||||
|
#include "common.hpp"
|
||||||
|
-#include "config.hpp"
|
||||||
|
#include "bar.hpp"
|
||||||
|
#include "line_buffer.hpp"
|
||||||
|
|
||||||
|
@@ -34,6 +33,7 @@ struct Monitor {
|
||||||
|
bool desiredVisibility {true};
|
||||||
|
bool hasData;
|
||||||
|
uint32_t tags;
|
||||||
|
+ wl_unique_ptr<znet_tapesoftware_dwl_wm_monitor_v1> dwlMonitor;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SeatPointer {
|
||||||
|
@@ -54,8 +54,6 @@ static void updatemon(Monitor &mon);
|
||||||
|
static void onReady();
|
||||||
|
static void setupStatusFifo();
|
||||||
|
static void onStatus();
|
||||||
|
-static void onStdin();
|
||||||
|
-static void handleStdin(const std::string& line);
|
||||||
|
static void updateVisibility(const std::string& name, bool(*updater)(bool));
|
||||||
|
static void onGlobalAdd(void*, wl_registry* registry, uint32_t name, const char* interface, uint32_t version);
|
||||||
|
static void onGlobalRemove(void*, wl_registry* registry, uint32_t name);
|
||||||
|
@@ -67,6 +65,9 @@ wl_display* display;
|
||||||
|
wl_compositor* compositor;
|
||||||
|
wl_shm* shm;
|
||||||
|
zwlr_layer_shell_v1* wlrLayerShell;
|
||||||
|
+znet_tapesoftware_dwl_wm_v1* dwlWm;
|
||||||
|
+std::vector<std::string> tagNames;
|
||||||
|
+std::vector<std::string> layoutNames;
|
||||||
|
static xdg_wm_base* xdgWmBase;
|
||||||
|
static zxdg_output_manager_v1* xdgOutputManager;
|
||||||
|
static wl_surface* cursorSurface;
|
||||||
|
@@ -85,6 +86,26 @@ static int statusFifoFd {-1};
|
||||||
|
static int statusFifoWriter {-1};
|
||||||
|
static bool quitting {false};
|
||||||
|
|
||||||
|
+void view(Monitor& m, const Arg& arg)
|
||||||
|
+{
|
||||||
|
+ znet_tapesoftware_dwl_wm_monitor_v1_set_tags(m.dwlMonitor.get(), arg.ui, 1);
|
||||||
|
+}
|
||||||
|
+void toggleview(Monitor& m, const Arg& arg)
|
||||||
|
+{
|
||||||
|
+ znet_tapesoftware_dwl_wm_monitor_v1_set_tags(m.dwlMonitor.get(), m.tags ^ arg.ui, 0);
|
||||||
|
+}
|
||||||
|
+void setlayout(Monitor& m, const Arg& arg)
|
||||||
|
+{
|
||||||
|
+ znet_tapesoftware_dwl_wm_monitor_v1_set_layout(m.dwlMonitor.get(), arg.ui);
|
||||||
|
+}
|
||||||
|
+void tag(Monitor& m, const Arg& arg)
|
||||||
|
+{
|
||||||
|
+ znet_tapesoftware_dwl_wm_monitor_v1_set_client_tags(m.dwlMonitor.get(), 0, arg.ui);
|
||||||
|
+}
|
||||||
|
+void toggletag(Monitor& m, const Arg& arg)
|
||||||
|
+{
|
||||||
|
+ znet_tapesoftware_dwl_wm_monitor_v1_set_client_tags(m.dwlMonitor.get(), ~0, arg.ui);
|
||||||
|
+}
|
||||||
|
void spawn(Monitor&, const Arg& arg)
|
||||||
|
{
|
||||||
|
if (fork() == 0) {
|
||||||
|
@@ -189,11 +210,62 @@ static const struct wl_seat_listener seatListener = {
|
||||||
|
.name = [](void*, wl_seat*, const char* name) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
+static const struct znet_tapesoftware_dwl_wm_v1_listener dwlWmListener = {
|
||||||
|
+ .tag = [](void*, znet_tapesoftware_dwl_wm_v1*, const char* name) {
|
||||||
|
+ tagNames.push_back(name);
|
||||||
|
+ },
|
||||||
|
+ .layout = [](void*, znet_tapesoftware_dwl_wm_v1*, const char* name) {
|
||||||
|
+ layoutNames.push_back(name);
|
||||||
|
+ },
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static const struct znet_tapesoftware_dwl_wm_monitor_v1_listener dwlWmMonitorListener {
|
||||||
|
+ .selected = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*, uint32_t selected) {
|
||||||
|
+ auto mon = static_cast<Monitor*>(mv);
|
||||||
|
+ if (selected) {
|
||||||
|
+ selmon = mon;
|
||||||
|
+ } else if (selmon == mon) {
|
||||||
|
+ selmon = nullptr;
|
||||||
|
+ }
|
||||||
|
+ mon->bar.setSelected(selected);
|
||||||
|
+ },
|
||||||
|
+ .tag = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*, uint32_t tag, uint32_t state, uint32_t numClients, int32_t focusedClient) {
|
||||||
|
+ auto mon = static_cast<Monitor*>(mv);
|
||||||
|
+ int tagState = TagState::None;
|
||||||
|
+ if (state & ZNET_TAPESOFTWARE_DWL_WM_MONITOR_V1_TAG_STATE_ACTIVE)
|
||||||
|
+ tagState |= TagState::Active;
|
||||||
|
+ if (state & ZNET_TAPESOFTWARE_DWL_WM_MONITOR_V1_TAG_STATE_URGENT)
|
||||||
|
+ tagState |= TagState::Urgent;
|
||||||
|
+ mon->bar.setTag(tag, tagState, numClients, focusedClient);
|
||||||
|
+ uint32_t mask = 1 << tag;
|
||||||
|
+ if (tagState & TagState::Active) {
|
||||||
|
+ mon->tags |= mask;
|
||||||
|
+ } else {
|
||||||
|
+ mon->tags &= ~mask;
|
||||||
|
+ }
|
||||||
|
+ },
|
||||||
|
+ .layout = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*, uint32_t layout) {
|
||||||
|
+ auto mon = static_cast<Monitor*>(mv);
|
||||||
|
+ mon->bar.setLayout(layoutNames[layout]);
|
||||||
|
+ },
|
||||||
|
+ .title = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*, const char* title) {
|
||||||
|
+ auto mon = static_cast<Monitor*>(mv);
|
||||||
|
+ mon->bar.setTitle(title);
|
||||||
|
+ },
|
||||||
|
+ .frame = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*) {
|
||||||
|
+ auto mon = static_cast<Monitor*>(mv);
|
||||||
|
+ mon->hasData = true;
|
||||||
|
+ updatemon(*mon);
|
||||||
|
+ }
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
void setupMonitor(uint32_t name, wl_output* output) {
|
||||||
|
auto& monitor = monitors.emplace_back(Monitor {name, {}, wl_unique_ptr<wl_output> {output}});
|
||||||
|
monitor.bar.setStatus(lastStatus);
|
||||||
|
auto xdgOutput = zxdg_output_manager_v1_get_xdg_output(xdgOutputManager, monitor.wlOutput.get());
|
||||||
|
zxdg_output_v1_add_listener(xdgOutput, &xdgOutputListener, &monitor);
|
||||||
|
+ monitor.dwlMonitor.reset(znet_tapesoftware_dwl_wm_v1_get_monitor(dwlWm, monitor.wlOutput.get()));
|
||||||
|
+ znet_tapesoftware_dwl_wm_monitor_v1_add_listener(monitor.dwlMonitor.get(), &dwlWmMonitorListener, &monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updatemon(Monitor& mon)
|
||||||
|
@@ -219,6 +291,7 @@ void onReady()
|
||||||
|
requireGlobal(shm, "wl_shm");
|
||||||
|
requireGlobal(wlrLayerShell, "zwlr_layer_shell_v1");
|
||||||
|
requireGlobal(xdgOutputManager, "zxdg_output_manager_v1");
|
||||||
|
+ requireGlobal(dwlWm, "znet_tapesoftware_dwl_wm_v1");
|
||||||
|
setupStatusFifo();
|
||||||
|
wl_display_roundtrip(display); // roundtrip so we receive all dwl tags etc.
|
||||||
|
|
||||||
|
@@ -226,7 +299,6 @@ void onReady()
|
||||||
|
for (auto output : uninitializedOutputs) {
|
||||||
|
setupMonitor(output.first, output.second);
|
||||||
|
}
|
||||||
|
- wl_display_roundtrip(display); // wait for xdg_output names before we read stdin
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupStatusFifo()
|
||||||
|
@@ -259,66 +331,6 @@ void setupStatusFifo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-static LineBuffer<512> stdinBuffer;
|
||||||
|
-static void onStdin()
|
||||||
|
-{
|
||||||
|
- auto res = stdinBuffer.readLines(
|
||||||
|
- [](void* p, size_t size) { return read(0, p, size); },
|
||||||
|
- [](char* p, size_t size) { handleStdin({p, size}); });
|
||||||
|
- if (res == 0) {
|
||||||
|
- quitting = true;
|
||||||
|
- }
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static void handleStdin(const std::string& line)
|
||||||
|
-{
|
||||||
|
- // this parses the lines that dwl sends in printstatus()
|
||||||
|
- std::string monName, command;
|
||||||
|
- auto stream = std::istringstream {line};
|
||||||
|
- stream >> monName >> command;
|
||||||
|
- if (!stream.good()) {
|
||||||
|
- return;
|
||||||
|
- }
|
||||||
|
- auto mon = std::find_if(begin(monitors), end(monitors), [&](const Monitor& mon) {
|
||||||
|
- return mon.xdgName == monName;
|
||||||
|
- });
|
||||||
|
- if (mon == end(monitors))
|
||||||
|
- return;
|
||||||
|
- if (command == "title") {
|
||||||
|
- auto title = std::string {};
|
||||||
|
- std::getline(stream, title);
|
||||||
|
- mon->bar.setTitle(title);
|
||||||
|
- } else if (command == "selmon") {
|
||||||
|
- uint32_t selected;
|
||||||
|
- stream >> selected;
|
||||||
|
- mon->bar.setSelected(selected);
|
||||||
|
- if (selected) {
|
||||||
|
- selmon = &*mon;
|
||||||
|
- } else if (selmon == &*mon) {
|
||||||
|
- selmon = nullptr;
|
||||||
|
- }
|
||||||
|
- } else if (command == "tags") {
|
||||||
|
- uint32_t occupied, tags, clientTags, urgent;
|
||||||
|
- stream >> occupied >> tags >> clientTags >> urgent;
|
||||||
|
- for (auto i=0u; i<tagNames.size(); i++) {
|
||||||
|
- auto tagMask = 1 << i;
|
||||||
|
- int state = TagState::None;
|
||||||
|
- if (tags & tagMask)
|
||||||
|
- state |= TagState::Active;
|
||||||
|
- if (urgent & tagMask)
|
||||||
|
- state |= TagState::Urgent;
|
||||||
|
- mon->bar.setTag(i, state, occupied & tagMask ? 1 : 0, clientTags & tagMask ? 0 : -1);
|
||||||
|
- }
|
||||||
|
- mon->tags = tags;
|
||||||
|
- } else if (command == "layout") {
|
||||||
|
- auto layout = std::string {};
|
||||||
|
- std::getline(stream, layout);
|
||||||
|
- mon->bar.setLayout(layout);
|
||||||
|
- }
|
||||||
|
- mon->hasData = true;
|
||||||
|
- updatemon(*mon);
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
const std::string prefixStatus = "status ";
|
||||||
|
const std::string prefixShow = "show ";
|
||||||
|
const std::string prefixHide = "hide ";
|
||||||
|
@@ -393,6 +405,10 @@ void onGlobalAdd(void*, wl_registry* registry, uint32_t name, const char* interf
|
||||||
|
xdg_wm_base_add_listener(xdgWmBase, &xdgWmBaseListener, nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
+ if (reg.handle(dwlWm, znet_tapesoftware_dwl_wm_v1_interface, 1)) {
|
||||||
|
+ znet_tapesoftware_dwl_wm_v1_add_listener(dwlWm, &dwlWmListener, nullptr);
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
if (wl_seat* wlSeat; reg.handle(wlSeat, wl_seat_interface, 7)) {
|
||||||
|
auto& seat = seats.emplace_back(Seat {name, wl_unique_ptr<wl_seat> {wlSeat}});
|
||||||
|
wl_seat_add_listener(wlSeat, &seatListener, &seat);
|
||||||
|
@@ -494,10 +510,6 @@ int main(int argc, char* argv[])
|
||||||
|
.fd = displayFd,
|
||||||
|
.events = POLLIN,
|
||||||
|
});
|
||||||
|
- pollfds.push_back({
|
||||||
|
- .fd = STDIN_FILENO,
|
||||||
|
- .events = POLLIN,
|
||||||
|
- });
|
||||||
|
if (fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) < 0) {
|
||||||
|
diesys("fcntl F_SETFL");
|
||||||
|
}
|
||||||
|
@@ -522,8 +534,6 @@ int main(int argc, char* argv[])
|
||||||
|
ev.events = POLLIN;
|
||||||
|
waylandFlush();
|
||||||
|
}
|
||||||
|
- } else if (ev.fd == STDIN_FILENO && (ev.revents & POLLIN)) {
|
||||||
|
- onStdin();
|
||||||
|
} else if (ev.fd == statusFifoFd && (ev.revents & POLLIN)) {
|
||||||
|
onStatus();
|
||||||
|
} else if (ev.fd == signalSelfPipe[0] && (ev.revents & POLLIN)) {
|
31
somebar/meson.build
Normal file
31
somebar/meson.build
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
project('somebar', ['c', 'cpp'],
|
||||||
|
version: '0.1.0',
|
||||||
|
default_options: [
|
||||||
|
'cpp_std=c++17',
|
||||||
|
'cpp_args=-Wno-parentheses',
|
||||||
|
])
|
||||||
|
|
||||||
|
wayland_dep = dependency('wayland-client')
|
||||||
|
wayland_cursor_dep = dependency('wayland-cursor')
|
||||||
|
cairo_dep = dependency('cairo')
|
||||||
|
pango_dep = dependency('pango')
|
||||||
|
pangocairo_dep = dependency('pangocairo')
|
||||||
|
|
||||||
|
subdir('protocols')
|
||||||
|
|
||||||
|
executable('somebar',
|
||||||
|
'src/main.cpp',
|
||||||
|
'src/shm_buffer.cpp',
|
||||||
|
'src/bar.cpp',
|
||||||
|
wayland_sources,
|
||||||
|
dependencies: [
|
||||||
|
wayland_dep,
|
||||||
|
wayland_cursor_dep,
|
||||||
|
cairo_dep,
|
||||||
|
pango_dep,
|
||||||
|
pangocairo_dep,
|
||||||
|
],
|
||||||
|
install: true,
|
||||||
|
cpp_args: '-DSOMEBAR_VERSION="@0@"'.format(meson.project_version()))
|
||||||
|
|
||||||
|
install_man('somebar.1')
|
22
somebar/protocols/meson.build
Normal file
22
somebar/protocols/meson.build
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# adapted from https://github.com/swaywm/swayidle/blob/0467c1e03a5780ed8e3ba611f099a838822ab550/meson.build
|
||||||
|
wayland_scanner = find_program('wayland-scanner')
|
||||||
|
wayland_protos_dep = dependency('wayland-protocols')
|
||||||
|
wl_protocol_dir = wayland_protos_dep.get_pkgconfig_variable('pkgdatadir')
|
||||||
|
wayland_scanner_code = generator(
|
||||||
|
wayland_scanner,
|
||||||
|
output: '@BASENAME@-protocol.c',
|
||||||
|
arguments: ['private-code', '@INPUT@', '@OUTPUT@'])
|
||||||
|
wayland_scanner_client = generator(
|
||||||
|
wayland_scanner,
|
||||||
|
output: '@BASENAME@-client-protocol.h',
|
||||||
|
arguments: ['client-header', '@INPUT@', '@OUTPUT@'])
|
||||||
|
|
||||||
|
wayland_xmls = [
|
||||||
|
wl_protocol_dir + '/stable/xdg-shell/xdg-shell.xml',
|
||||||
|
wl_protocol_dir + '/unstable/xdg-output/xdg-output-unstable-v1.xml',
|
||||||
|
'wlr-layer-shell-unstable-v1.xml',
|
||||||
|
]
|
||||||
|
wayland_sources = [
|
||||||
|
wayland_scanner_code.process(wayland_xmls),
|
||||||
|
wayland_scanner_client.process(wayland_xmls),
|
||||||
|
]
|
390
somebar/protocols/wlr-layer-shell-unstable-v1.xml
Normal file
390
somebar/protocols/wlr-layer-shell-unstable-v1.xml
Normal file
@ -0,0 +1,390 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<protocol name="wlr_layer_shell_unstable_v1">
|
||||||
|
<copyright>
|
||||||
|
Copyright © 2017 Drew DeVault
|
||||||
|
|
||||||
|
Permission to use, copy, modify, distribute, and sell this
|
||||||
|
software and its documentation for any purpose is hereby granted
|
||||||
|
without fee, provided that the above copyright notice appear in
|
||||||
|
all copies and that both that copyright notice and this permission
|
||||||
|
notice appear in supporting documentation, and that the name of
|
||||||
|
the copyright holders not be used in advertising or publicity
|
||||||
|
pertaining to distribution of the software without specific,
|
||||||
|
written prior permission. The copyright holders make no
|
||||||
|
representations about the suitability of this software for any
|
||||||
|
purpose. It is provided "as is" without express or implied
|
||||||
|
warranty.
|
||||||
|
|
||||||
|
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||||
|
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||||
|
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||||
|
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||||
|
THIS SOFTWARE.
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<interface name="zwlr_layer_shell_v1" version="4">
|
||||||
|
<description summary="create surfaces that are layers of the desktop">
|
||||||
|
Clients can use this interface to assign the surface_layer role to
|
||||||
|
wl_surfaces. Such surfaces are assigned to a "layer" of the output and
|
||||||
|
rendered with a defined z-depth respective to each other. They may also be
|
||||||
|
anchored to the edges and corners of a screen and specify input handling
|
||||||
|
semantics. This interface should be suitable for the implementation of
|
||||||
|
many desktop shell components, and a broad number of other applications
|
||||||
|
that interact with the desktop.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="get_layer_surface">
|
||||||
|
<description summary="create a layer_surface from a surface">
|
||||||
|
Create a layer surface for an existing surface. This assigns the role of
|
||||||
|
layer_surface, or raises a protocol error if another role is already
|
||||||
|
assigned.
|
||||||
|
|
||||||
|
Creating a layer surface from a wl_surface which has a buffer attached
|
||||||
|
or committed is a client error, and any attempts by a client to attach
|
||||||
|
or manipulate a buffer prior to the first layer_surface.configure call
|
||||||
|
must also be treated as errors.
|
||||||
|
|
||||||
|
After creating a layer_surface object and setting it up, the client
|
||||||
|
must perform an initial commit without any buffer attached.
|
||||||
|
The compositor will reply with a layer_surface.configure event.
|
||||||
|
The client must acknowledge it and is then allowed to attach a buffer
|
||||||
|
to map the surface.
|
||||||
|
|
||||||
|
You may pass NULL for output to allow the compositor to decide which
|
||||||
|
output to use. Generally this will be the one that the user most
|
||||||
|
recently interacted with.
|
||||||
|
|
||||||
|
Clients can specify a namespace that defines the purpose of the layer
|
||||||
|
surface.
|
||||||
|
</description>
|
||||||
|
<arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/>
|
||||||
|
<arg name="surface" type="object" interface="wl_surface"/>
|
||||||
|
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
|
||||||
|
<arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/>
|
||||||
|
<arg name="name_space" type="string" summary="namespace for the layer surface"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<enum name="error">
|
||||||
|
<entry name="role" value="0" summary="wl_surface has another role"/>
|
||||||
|
<entry name="invalid_layer" value="1" summary="layer value is invalid"/>
|
||||||
|
<entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<enum name="layer">
|
||||||
|
<description summary="available layers for surfaces">
|
||||||
|
These values indicate which layers a surface can be rendered in. They
|
||||||
|
are ordered by z depth, bottom-most first. Traditional shell surfaces
|
||||||
|
will typically be rendered between the bottom and top layers.
|
||||||
|
Fullscreen shell surfaces are typically rendered at the top layer.
|
||||||
|
Multiple surfaces can share a single layer, and ordering within a
|
||||||
|
single layer is undefined.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<entry name="background" value="0"/>
|
||||||
|
<entry name="bottom" value="1"/>
|
||||||
|
<entry name="top" value="2"/>
|
||||||
|
<entry name="overlay" value="3"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<!-- Version 3 additions -->
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor" since="3">
|
||||||
|
<description summary="destroy the layer_shell object">
|
||||||
|
This request indicates that the client will not use the layer_shell
|
||||||
|
object any more. Objects that have been created through this instance
|
||||||
|
are not affected.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="zwlr_layer_surface_v1" version="4">
|
||||||
|
<description summary="layer metadata interface">
|
||||||
|
An interface that may be implemented by a wl_surface, for surfaces that
|
||||||
|
are designed to be rendered as a layer of a stacked desktop-like
|
||||||
|
environment.
|
||||||
|
|
||||||
|
Layer surface state (layer, size, anchor, exclusive zone,
|
||||||
|
margin, interactivity) is double-buffered, and will be applied at the
|
||||||
|
time wl_surface.commit of the corresponding wl_surface is called.
|
||||||
|
|
||||||
|
Attaching a null buffer to a layer surface unmaps it.
|
||||||
|
|
||||||
|
Unmapping a layer_surface means that the surface cannot be shown by the
|
||||||
|
compositor until it is explicitly mapped again. The layer_surface
|
||||||
|
returns to the state it had right after layer_shell.get_layer_surface.
|
||||||
|
The client can re-map the surface by performing a commit without any
|
||||||
|
buffer attached, waiting for a configure event and handling it as usual.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="set_size">
|
||||||
|
<description summary="sets the size of the surface">
|
||||||
|
Sets the size of the surface in surface-local coordinates. The
|
||||||
|
compositor will display the surface centered with respect to its
|
||||||
|
anchors.
|
||||||
|
|
||||||
|
If you pass 0 for either value, the compositor will assign it and
|
||||||
|
inform you of the assignment in the configure event. You must set your
|
||||||
|
anchor to opposite edges in the dimensions you omit; not doing so is a
|
||||||
|
protocol error. Both values are 0 by default.
|
||||||
|
|
||||||
|
Size is double-buffered, see wl_surface.commit.
|
||||||
|
</description>
|
||||||
|
<arg name="width" type="uint"/>
|
||||||
|
<arg name="height" type="uint"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="set_anchor">
|
||||||
|
<description summary="configures the anchor point of the surface">
|
||||||
|
Requests that the compositor anchor the surface to the specified edges
|
||||||
|
and corners. If two orthogonal edges are specified (e.g. 'top' and
|
||||||
|
'left'), then the anchor point will be the intersection of the edges
|
||||||
|
(e.g. the top left corner of the output); otherwise the anchor point
|
||||||
|
will be centered on that edge, or in the center if none is specified.
|
||||||
|
|
||||||
|
Anchor is double-buffered, see wl_surface.commit.
|
||||||
|
</description>
|
||||||
|
<arg name="anchor" type="uint" enum="anchor"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="set_exclusive_zone">
|
||||||
|
<description summary="configures the exclusive geometry of this surface">
|
||||||
|
Requests that the compositor avoids occluding an area with other
|
||||||
|
surfaces. The compositor's use of this information is
|
||||||
|
implementation-dependent - do not assume that this region will not
|
||||||
|
actually be occluded.
|
||||||
|
|
||||||
|
A positive value is only meaningful if the surface is anchored to one
|
||||||
|
edge or an edge and both perpendicular edges. If the surface is not
|
||||||
|
anchored, anchored to only two perpendicular edges (a corner), anchored
|
||||||
|
to only two parallel edges or anchored to all edges, a positive value
|
||||||
|
will be treated the same as zero.
|
||||||
|
|
||||||
|
A positive zone is the distance from the edge in surface-local
|
||||||
|
coordinates to consider exclusive.
|
||||||
|
|
||||||
|
Surfaces that do not wish to have an exclusive zone may instead specify
|
||||||
|
how they should interact with surfaces that do. If set to zero, the
|
||||||
|
surface indicates that it would like to be moved to avoid occluding
|
||||||
|
surfaces with a positive exclusive zone. If set to -1, the surface
|
||||||
|
indicates that it would not like to be moved to accommodate for other
|
||||||
|
surfaces, and the compositor should extend it all the way to the edges
|
||||||
|
it is anchored to.
|
||||||
|
|
||||||
|
For example, a panel might set its exclusive zone to 10, so that
|
||||||
|
maximized shell surfaces are not shown on top of it. A notification
|
||||||
|
might set its exclusive zone to 0, so that it is moved to avoid
|
||||||
|
occluding the panel, but shell surfaces are shown underneath it. A
|
||||||
|
wallpaper or lock screen might set their exclusive zone to -1, so that
|
||||||
|
they stretch below or over the panel.
|
||||||
|
|
||||||
|
The default value is 0.
|
||||||
|
|
||||||
|
Exclusive zone is double-buffered, see wl_surface.commit.
|
||||||
|
</description>
|
||||||
|
<arg name="zone" type="int"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="set_margin">
|
||||||
|
<description summary="sets a margin from the anchor point">
|
||||||
|
Requests that the surface be placed some distance away from the anchor
|
||||||
|
point on the output, in surface-local coordinates. Setting this value
|
||||||
|
for edges you are not anchored to has no effect.
|
||||||
|
|
||||||
|
The exclusive zone includes the margin.
|
||||||
|
|
||||||
|
Margin is double-buffered, see wl_surface.commit.
|
||||||
|
</description>
|
||||||
|
<arg name="top" type="int"/>
|
||||||
|
<arg name="right" type="int"/>
|
||||||
|
<arg name="bottom" type="int"/>
|
||||||
|
<arg name="left" type="int"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<enum name="keyboard_interactivity">
|
||||||
|
<description summary="types of keyboard interaction possible for a layer shell surface">
|
||||||
|
Types of keyboard interaction possible for layer shell surfaces. The
|
||||||
|
rationale for this is twofold: (1) some applications are not interested
|
||||||
|
in keyboard events and not allowing them to be focused can improve the
|
||||||
|
desktop experience; (2) some applications will want to take exclusive
|
||||||
|
keyboard focus.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<entry name="none" value="0">
|
||||||
|
<description summary="no keyboard focus is possible">
|
||||||
|
This value indicates that this surface is not interested in keyboard
|
||||||
|
events and the compositor should never assign it the keyboard focus.
|
||||||
|
|
||||||
|
This is the default value, set for newly created layer shell surfaces.
|
||||||
|
|
||||||
|
This is useful for e.g. desktop widgets that display information or
|
||||||
|
only have interaction with non-keyboard input devices.
|
||||||
|
</description>
|
||||||
|
</entry>
|
||||||
|
<entry name="exclusive" value="1">
|
||||||
|
<description summary="request exclusive keyboard focus">
|
||||||
|
Request exclusive keyboard focus if this surface is above the shell surface layer.
|
||||||
|
|
||||||
|
For the top and overlay layers, the seat will always give
|
||||||
|
exclusive keyboard focus to the top-most layer which has keyboard
|
||||||
|
interactivity set to exclusive. If this layer contains multiple
|
||||||
|
surfaces with keyboard interactivity set to exclusive, the compositor
|
||||||
|
determines the one receiving keyboard events in an implementation-
|
||||||
|
defined manner. In this case, no guarantee is made when this surface
|
||||||
|
will receive keyboard focus (if ever).
|
||||||
|
|
||||||
|
For the bottom and background layers, the compositor is allowed to use
|
||||||
|
normal focus semantics.
|
||||||
|
|
||||||
|
This setting is mainly intended for applications that need to ensure
|
||||||
|
they receive all keyboard events, such as a lock screen or a password
|
||||||
|
prompt.
|
||||||
|
</description>
|
||||||
|
</entry>
|
||||||
|
<entry name="on_demand" value="2" since="4">
|
||||||
|
<description summary="request regular keyboard focus semantics">
|
||||||
|
This requests the compositor to allow this surface to be focused and
|
||||||
|
unfocused by the user in an implementation-defined manner. The user
|
||||||
|
should be able to unfocus this surface even regardless of the layer
|
||||||
|
it is on.
|
||||||
|
|
||||||
|
Typically, the compositor will want to use its normal mechanism to
|
||||||
|
manage keyboard focus between layer shell surfaces with this setting
|
||||||
|
and regular toplevels on the desktop layer (e.g. click to focus).
|
||||||
|
Nevertheless, it is possible for a compositor to require a special
|
||||||
|
interaction to focus or unfocus layer shell surfaces (e.g. requiring
|
||||||
|
a click even if focus follows the mouse normally, or providing a
|
||||||
|
keybinding to switch focus between layers).
|
||||||
|
|
||||||
|
This setting is mainly intended for desktop shell components (e.g.
|
||||||
|
panels) that allow keyboard interaction. Using this option can allow
|
||||||
|
implementing a desktop shell that can be fully usable without the
|
||||||
|
mouse.
|
||||||
|
</description>
|
||||||
|
</entry>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<request name="set_keyboard_interactivity">
|
||||||
|
<description summary="requests keyboard events">
|
||||||
|
Set how keyboard events are delivered to this surface. By default,
|
||||||
|
layer shell surfaces do not receive keyboard events; this request can
|
||||||
|
be used to change this.
|
||||||
|
|
||||||
|
This setting is inherited by child surfaces set by the get_popup
|
||||||
|
request.
|
||||||
|
|
||||||
|
Layer surfaces receive pointer, touch, and tablet events normally. If
|
||||||
|
you do not want to receive them, set the input region on your surface
|
||||||
|
to an empty region.
|
||||||
|
|
||||||
|
Keyboard interactivity is double-buffered, see wl_surface.commit.
|
||||||
|
</description>
|
||||||
|
<arg name="keyboard_interactivity" type="uint" enum="keyboard_interactivity"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="get_popup">
|
||||||
|
<description summary="assign this layer_surface as an xdg_popup parent">
|
||||||
|
This assigns an xdg_popup's parent to this layer_surface. This popup
|
||||||
|
should have been created via xdg_surface::get_popup with the parent set
|
||||||
|
to NULL, and this request must be invoked before committing the popup's
|
||||||
|
initial state.
|
||||||
|
|
||||||
|
See the documentation of xdg_popup for more details about what an
|
||||||
|
xdg_popup is and how it is used.
|
||||||
|
</description>
|
||||||
|
<arg name="popup" type="object" interface="xdg_popup"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="ack_configure">
|
||||||
|
<description summary="ack a configure event">
|
||||||
|
When a configure event is received, if a client commits the
|
||||||
|
surface in response to the configure event, then the client
|
||||||
|
must make an ack_configure request sometime before the commit
|
||||||
|
request, passing along the serial of the configure event.
|
||||||
|
|
||||||
|
If the client receives multiple configure events before it
|
||||||
|
can respond to one, it only has to ack the last configure event.
|
||||||
|
|
||||||
|
A client is not required to commit immediately after sending
|
||||||
|
an ack_configure request - it may even ack_configure several times
|
||||||
|
before its next surface commit.
|
||||||
|
|
||||||
|
A client may send multiple ack_configure requests before committing, but
|
||||||
|
only the last request sent before a commit indicates which configure
|
||||||
|
event the client really is responding to.
|
||||||
|
</description>
|
||||||
|
<arg name="serial" type="uint" summary="the serial from the configure event"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the layer_surface">
|
||||||
|
This request destroys the layer surface.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="configure">
|
||||||
|
<description summary="suggest a surface change">
|
||||||
|
The configure event asks the client to resize its surface.
|
||||||
|
|
||||||
|
Clients should arrange their surface for the new states, and then send
|
||||||
|
an ack_configure request with the serial sent in this configure event at
|
||||||
|
some point before committing the new surface.
|
||||||
|
|
||||||
|
The client is free to dismiss all but the last configure event it
|
||||||
|
received.
|
||||||
|
|
||||||
|
The width and height arguments specify the size of the window in
|
||||||
|
surface-local coordinates.
|
||||||
|
|
||||||
|
The size is a hint, in the sense that the client is free to ignore it if
|
||||||
|
it doesn't resize, pick a smaller size (to satisfy aspect ratio or
|
||||||
|
resize in steps of NxM pixels). If the client picks a smaller size and
|
||||||
|
is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
|
||||||
|
surface will be centered on this axis.
|
||||||
|
|
||||||
|
If the width or height arguments are zero, it means the client should
|
||||||
|
decide its own window dimension.
|
||||||
|
</description>
|
||||||
|
<arg name="serial" type="uint"/>
|
||||||
|
<arg name="width" type="uint"/>
|
||||||
|
<arg name="height" type="uint"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="closed">
|
||||||
|
<description summary="surface should be closed">
|
||||||
|
The closed event is sent by the compositor when the surface will no
|
||||||
|
longer be shown. The output may have been destroyed or the user may
|
||||||
|
have asked for it to be removed. Further changes to the surface will be
|
||||||
|
ignored. The client should destroy the resource after receiving this
|
||||||
|
event, and create a new surface if they so choose.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<enum name="error">
|
||||||
|
<entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
|
||||||
|
<entry name="invalid_size" value="1" summary="size is invalid"/>
|
||||||
|
<entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
|
||||||
|
<entry name="invalid_keyboard_interactivity" value="3" summary="keyboard interactivity is invalid"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<enum name="anchor" bitfield="true">
|
||||||
|
<entry name="top" value="1" summary="the top edge of the anchor rectangle"/>
|
||||||
|
<entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/>
|
||||||
|
<entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
|
||||||
|
<entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<!-- Version 2 additions -->
|
||||||
|
|
||||||
|
<request name="set_layer" since="2">
|
||||||
|
<description summary="change the layer of the surface">
|
||||||
|
Change the layer that the surface is rendered on.
|
||||||
|
|
||||||
|
Layer is double-buffered, see wl_surface.commit.
|
||||||
|
</description>
|
||||||
|
<arg name="layer" type="uint" enum="zwlr_layer_shell_v1.layer" summary="layer to move this surface to"/>
|
||||||
|
</request>
|
||||||
|
</interface>
|
||||||
|
</protocol>
|
BIN
somebar/screenshot.png
Normal file
BIN
somebar/screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.6 KiB |
55
somebar/somebar.1
Normal file
55
somebar/somebar.1
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
.TH somebar 1 somebar\-1.0
|
||||||
|
.SH NAME
|
||||||
|
somebar \- dwm-like bar for dwl
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B somebar
|
||||||
|
.RB [ \-h ]
|
||||||
|
.RB [ \-v ]
|
||||||
|
.RB [ \-s
|
||||||
|
.IR path ]
|
||||||
|
.RB [ \-c
|
||||||
|
.IR command
|
||||||
|
arguments... ]
|
||||||
|
.SH DESCRIPTION
|
||||||
|
somebar is a status bar for dwl, visually and functionally resembling the
|
||||||
|
dwm bar.
|
||||||
|
.SH USAGE
|
||||||
|
You must start somebar using dwl's `-s` flag, e.g. `dwl -s somebar`.
|
||||||
|
|
||||||
|
Somebar can be controlled by writing to $XDG_RUNTIME_DIR/somebar-0, or the path
|
||||||
|
defined by the `-s` argument. The following commands are supported:
|
||||||
|
.TP
|
||||||
|
.B status TEXT
|
||||||
|
Updates the status bar
|
||||||
|
.TP
|
||||||
|
.B hide MONITOR
|
||||||
|
Hides somebar on the specified monitor
|
||||||
|
.TP
|
||||||
|
.B show MONITOR
|
||||||
|
Shows somebar on the specified monitor
|
||||||
|
.TP
|
||||||
|
.B toggle MONITOR
|
||||||
|
Toggles somebar on the specified monitor
|
||||||
|
.P
|
||||||
|
MONITOR is an zxdg_output_v1 name, which can be determined e.g. using `weston-info`.
|
||||||
|
Additionally, MONITOR can be `all` (all monitors) or `selected` (the monitor with focus).
|
||||||
|
|
||||||
|
Commands can be sent either by writing to the file name above, or equivalently by calling
|
||||||
|
somebar with the `-c` argument. For example: `somebar -c toggle all`. This is recommended
|
||||||
|
for shell scripts, as there is no race-free way to write to a file only if it exists.
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
.B \-h
|
||||||
|
Displays a short help text and exits
|
||||||
|
.TP
|
||||||
|
.B \-v
|
||||||
|
Displays version information and exits
|
||||||
|
.TP
|
||||||
|
.B \-s
|
||||||
|
Sets the path to the somebar control FIFO. The default value is
|
||||||
|
$XDG_RUNTIME_DIR/somebar-0
|
||||||
|
.TP
|
||||||
|
.B \-c
|
||||||
|
Sends a command to the control FIFO. See the USAGE section.
|
||||||
|
.SH BUGS
|
||||||
|
Send bug reports to ~raphi/public-inbox@lists.sr.ht
|
315
somebar/src/bar.cpp
Normal file
315
somebar/src/bar.cpp
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
// somebar - dwl barbar
|
||||||
|
// See LICENSE file for copyright and license details.
|
||||||
|
|
||||||
|
#include <wayland-client-protocol.h>
|
||||||
|
#include <pango/pangocairo.h>
|
||||||
|
#include "bar.hpp"
|
||||||
|
#include "cairo.h"
|
||||||
|
#include "config.hpp"
|
||||||
|
#include "pango/pango-font.h"
|
||||||
|
#include "pango/pango-fontmap.h"
|
||||||
|
#include "pango/pango-layout.h"
|
||||||
|
|
||||||
|
const zwlr_layer_surface_v1_listener Bar::_layerSurfaceListener = {
|
||||||
|
[](void* owner, zwlr_layer_surface_v1*, uint32_t serial, uint32_t width, uint32_t height)
|
||||||
|
{
|
||||||
|
static_cast<Bar*>(owner)->layerSurfaceConfigure(serial, width, height);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const wl_callback_listener Bar::_frameListener = {
|
||||||
|
[](void* owner, wl_callback* cb, uint32_t)
|
||||||
|
{
|
||||||
|
static_cast<Bar*>(owner)->render();
|
||||||
|
wl_callback_destroy(cb);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Font {
|
||||||
|
PangoFontDescription* description;
|
||||||
|
int height {0};
|
||||||
|
};
|
||||||
|
static Font getFont()
|
||||||
|
{
|
||||||
|
auto fontMap = pango_cairo_font_map_get_default();
|
||||||
|
if (!fontMap) {
|
||||||
|
die("pango_cairo_font_map_get_default");
|
||||||
|
}
|
||||||
|
auto fontDesc = pango_font_description_from_string(font);
|
||||||
|
if (!fontDesc) {
|
||||||
|
die("pango_font_description_from_string");
|
||||||
|
}
|
||||||
|
auto tempContext = pango_font_map_create_context(fontMap);
|
||||||
|
if (!tempContext) {
|
||||||
|
die("pango_font_map_create_context");
|
||||||
|
}
|
||||||
|
auto font = pango_font_map_load_font(fontMap, tempContext, fontDesc);
|
||||||
|
if (!font) {
|
||||||
|
die("pango_font_map_load_font");
|
||||||
|
}
|
||||||
|
auto metrics = pango_font_get_metrics(font, pango_language_get_default());
|
||||||
|
if (!metrics) {
|
||||||
|
die("pango_font_get_metrics");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto res = Font {};
|
||||||
|
res.description = fontDesc;
|
||||||
|
res.height = PANGO_PIXELS(pango_font_metrics_get_height(metrics));
|
||||||
|
|
||||||
|
pango_font_metrics_unref(metrics);
|
||||||
|
g_object_unref(font);
|
||||||
|
g_object_unref(tempContext);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
static Font barfont = getFont();
|
||||||
|
|
||||||
|
BarComponent::BarComponent() { }
|
||||||
|
BarComponent::BarComponent(wl_unique_ptr<PangoLayout> layout)
|
||||||
|
: pangoLayout {std::move(layout)}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int BarComponent::width() const
|
||||||
|
{
|
||||||
|
int w, h;
|
||||||
|
pango_layout_get_size(pangoLayout.get(), &w, &h);
|
||||||
|
return PANGO_PIXELS(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BarComponent::setText(const std::string& text)
|
||||||
|
{
|
||||||
|
_text = std::make_unique<std::string>(text);
|
||||||
|
pango_layout_set_text(pangoLayout.get(), _text->c_str(), _text->size());
|
||||||
|
}
|
||||||
|
|
||||||
|
Bar::Bar()
|
||||||
|
{
|
||||||
|
_pangoContext.reset(pango_font_map_create_context(pango_cairo_font_map_get_default()));
|
||||||
|
if (!_pangoContext) {
|
||||||
|
die("pango_font_map_create_context");
|
||||||
|
}
|
||||||
|
for (const auto& tagName : tagNames) {
|
||||||
|
_tags.push_back({ TagState::None, 0, 0, createComponent(tagName) });
|
||||||
|
}
|
||||||
|
_layoutCmp = createComponent();
|
||||||
|
_titleCmp = createComponent();
|
||||||
|
_statusCmp = createComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
const wl_surface* Bar::surface() const
|
||||||
|
{
|
||||||
|
return _surface.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Bar::visible() const
|
||||||
|
{
|
||||||
|
return _surface.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bar::show(wl_output* output)
|
||||||
|
{
|
||||||
|
if (visible()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_surface.reset(wl_compositor_create_surface(compositor));
|
||||||
|
_layerSurface.reset(zwlr_layer_shell_v1_get_layer_surface(wlrLayerShell,
|
||||||
|
_surface.get(), output, ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "net.tapesoftware.Somebar"));
|
||||||
|
zwlr_layer_surface_v1_add_listener(_layerSurface.get(), &_layerSurfaceListener, this);
|
||||||
|
auto anchor = topbar ? ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP : ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
|
||||||
|
zwlr_layer_surface_v1_set_anchor(_layerSurface.get(),
|
||||||
|
anchor | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT);
|
||||||
|
|
||||||
|
auto barSize = barfont.height + paddingY * 2;
|
||||||
|
zwlr_layer_surface_v1_set_size(_layerSurface.get(), 0, barSize);
|
||||||
|
zwlr_layer_surface_v1_set_exclusive_zone(_layerSurface.get(), barSize);
|
||||||
|
wl_surface_commit(_surface.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bar::hide()
|
||||||
|
{
|
||||||
|
if (!visible()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_layerSurface.reset();
|
||||||
|
_surface.reset();
|
||||||
|
_bufs.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bar::setTag(int tag, int state, int numClients, int focusedClient)
|
||||||
|
{
|
||||||
|
auto& t = _tags[tag];
|
||||||
|
t.state = state;
|
||||||
|
t.numClients = numClients;
|
||||||
|
t.focusedClient = focusedClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bar::setSelected(bool selected)
|
||||||
|
{
|
||||||
|
_selected = selected;
|
||||||
|
}
|
||||||
|
void Bar::setLayout(const std::string& layout)
|
||||||
|
{
|
||||||
|
_layoutCmp.setText(layout);
|
||||||
|
}
|
||||||
|
void Bar::setTitle(const std::string& title)
|
||||||
|
{
|
||||||
|
_titleCmp.setText(title);
|
||||||
|
}
|
||||||
|
void Bar::setStatus(const std::string& status)
|
||||||
|
{
|
||||||
|
_statusCmp.setText(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bar::invalidate()
|
||||||
|
{
|
||||||
|
if (_invalid || !visible()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_invalid = true;
|
||||||
|
auto frame = wl_surface_frame(_surface.get());
|
||||||
|
wl_callback_add_listener(frame, &_frameListener, this);
|
||||||
|
wl_surface_commit(_surface.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bar::click(Monitor* mon, int x, int, int btn)
|
||||||
|
{
|
||||||
|
Arg arg = {0};
|
||||||
|
Arg* argp = nullptr;
|
||||||
|
int control = ClkNone;
|
||||||
|
if (x > _statusCmp.x) {
|
||||||
|
control = ClkStatusText;
|
||||||
|
} else if (x > _titleCmp.x) {
|
||||||
|
control = ClkWinTitle;
|
||||||
|
} else if (x > _layoutCmp.x) {
|
||||||
|
control = ClkLayoutSymbol;
|
||||||
|
} else for (auto tag = _tags.size()-1; tag >= 0; tag--) {
|
||||||
|
if (x > _tags[tag].component.x) {
|
||||||
|
control = ClkTagBar;
|
||||||
|
arg.ui = 1<<tag;
|
||||||
|
argp = &arg;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto& button : buttons) {
|
||||||
|
if (button.control == control && button.btn == btn) {
|
||||||
|
button.func(*mon, *(argp ? argp : &button.arg));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bar::layerSurfaceConfigure(uint32_t serial, uint32_t width, uint32_t height)
|
||||||
|
{
|
||||||
|
zwlr_layer_surface_v1_ack_configure(_layerSurface.get(), serial);
|
||||||
|
if (width == _bufs->width && height == _bufs->height) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_bufs.emplace(width, height, WL_SHM_FORMAT_XRGB8888);
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bar::render()
|
||||||
|
{
|
||||||
|
if (!_bufs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto img = wl_unique_ptr<cairo_surface_t> {cairo_image_surface_create_for_data(
|
||||||
|
_bufs->data(),
|
||||||
|
CAIRO_FORMAT_ARGB32,
|
||||||
|
_bufs->width,
|
||||||
|
_bufs->height,
|
||||||
|
_bufs->stride
|
||||||
|
)};
|
||||||
|
auto painter = wl_unique_ptr<cairo_t> {cairo_create(img.get())};
|
||||||
|
_painter = painter.get();
|
||||||
|
pango_cairo_update_context(_painter, _pangoContext.get());
|
||||||
|
_x = 0;
|
||||||
|
|
||||||
|
renderTags();
|
||||||
|
setColorScheme(_selected ? colorActive : colorInactive);
|
||||||
|
renderComponent(_layoutCmp);
|
||||||
|
renderComponent(_titleCmp);
|
||||||
|
renderStatus();
|
||||||
|
|
||||||
|
_painter = nullptr;
|
||||||
|
wl_surface_attach(_surface.get(), _bufs->buffer(), 0, 0);
|
||||||
|
wl_surface_damage(_surface.get(), 0, 0, _bufs->width, _bufs->height);
|
||||||
|
wl_surface_commit(_surface.get());
|
||||||
|
_bufs->flip();
|
||||||
|
_invalid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bar::renderTags()
|
||||||
|
{
|
||||||
|
for (auto &tag : _tags) {
|
||||||
|
setColorScheme(
|
||||||
|
tag.state & TagState::Active ? colorActive : colorInactive,
|
||||||
|
tag.state & TagState::Urgent);
|
||||||
|
renderComponent(tag.component);
|
||||||
|
auto indicators = std::min(tag.numClients, static_cast<int>(_bufs->height/2));
|
||||||
|
for (auto ind = 0; ind < indicators; ind++) {
|
||||||
|
auto w = ind == tag.focusedClient ? 7 : 1;
|
||||||
|
cairo_move_to(_painter, tag.component.x, ind*2+0.5);
|
||||||
|
cairo_rel_line_to(_painter, w, 0);
|
||||||
|
cairo_close_path(_painter);
|
||||||
|
cairo_set_line_width(_painter, 5);
|
||||||
|
cairo_stroke(_painter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bar::renderStatus()
|
||||||
|
{
|
||||||
|
pango_cairo_update_layout(_painter, _statusCmp.pangoLayout.get());
|
||||||
|
beginBg();
|
||||||
|
auto start = _bufs->width - _statusCmp.width() - paddingX*2;
|
||||||
|
cairo_rectangle(_painter, _x, 0, _bufs->width-_x+start, _bufs->height);
|
||||||
|
cairo_fill(_painter);
|
||||||
|
|
||||||
|
_x = start;
|
||||||
|
renderComponent(_statusCmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bar::setColorScheme(const ColorScheme& scheme, bool invert)
|
||||||
|
{
|
||||||
|
_colorScheme = invert
|
||||||
|
? ColorScheme {scheme.bg, scheme.fg}
|
||||||
|
: ColorScheme {scheme.fg, scheme.bg};
|
||||||
|
}
|
||||||
|
static void setColor(cairo_t* painter, const Color& color)
|
||||||
|
{
|
||||||
|
cairo_set_source_rgba(painter,
|
||||||
|
color.r/255.0, color.g/255.0, color.b/255.0, color.a/255.0);
|
||||||
|
}
|
||||||
|
void Bar::beginFg()
|
||||||
|
{
|
||||||
|
setColor(_painter, _colorScheme.fg);
|
||||||
|
}
|
||||||
|
void Bar::beginBg()
|
||||||
|
{
|
||||||
|
setColor(_painter, _colorScheme.bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bar::renderComponent(BarComponent& component)
|
||||||
|
{
|
||||||
|
pango_cairo_update_layout(_painter, component.pangoLayout.get());
|
||||||
|
auto size = component.width() + paddingX*2;
|
||||||
|
component.x = _x;
|
||||||
|
|
||||||
|
beginBg();
|
||||||
|
cairo_rectangle(_painter, _x, 0, size, _bufs->height);
|
||||||
|
cairo_fill(_painter);
|
||||||
|
cairo_move_to(_painter, _x+paddingX, paddingY);
|
||||||
|
|
||||||
|
beginFg();
|
||||||
|
pango_cairo_show_layout(_painter, component.pangoLayout.get());
|
||||||
|
_x += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
BarComponent Bar::createComponent(const std::string &initial)
|
||||||
|
{
|
||||||
|
auto layout = pango_layout_new(_pangoContext.get());
|
||||||
|
pango_layout_set_font_description(layout, barfont.description);
|
||||||
|
auto res = BarComponent {wl_unique_ptr<PangoLayout> {layout}};
|
||||||
|
res.setText(initial);
|
||||||
|
return res;
|
||||||
|
}
|
74
somebar/src/bar.hpp
Normal file
74
somebar/src/bar.hpp
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// somebar - dwl bar
|
||||||
|
// See LICENSE file for copyright and license details.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "shm_buffer.hpp"
|
||||||
|
|
||||||
|
class BarComponent {
|
||||||
|
std::unique_ptr<std::string> _text;
|
||||||
|
public:
|
||||||
|
BarComponent();
|
||||||
|
explicit BarComponent(wl_unique_ptr<PangoLayout> layout);
|
||||||
|
int width() const;
|
||||||
|
void setText(const std::string& text);
|
||||||
|
wl_unique_ptr<PangoLayout> pangoLayout;
|
||||||
|
int x {0};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Tag {
|
||||||
|
int state;
|
||||||
|
int numClients;
|
||||||
|
int focusedClient;
|
||||||
|
BarComponent component;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Monitor;
|
||||||
|
class Bar {
|
||||||
|
static const zwlr_layer_surface_v1_listener _layerSurfaceListener;
|
||||||
|
static const wl_callback_listener _frameListener;
|
||||||
|
|
||||||
|
wl_unique_ptr<wl_surface> _surface;
|
||||||
|
wl_unique_ptr<zwlr_layer_surface_v1> _layerSurface;
|
||||||
|
wl_unique_ptr<PangoContext> _pangoContext;
|
||||||
|
std::optional<ShmBuffer> _bufs;
|
||||||
|
std::vector<Tag> _tags;
|
||||||
|
BarComponent _layoutCmp, _titleCmp, _statusCmp;
|
||||||
|
bool _selected;
|
||||||
|
bool _invalid {false};
|
||||||
|
|
||||||
|
// only vaild during render()
|
||||||
|
cairo_t* _painter {nullptr};
|
||||||
|
int _x;
|
||||||
|
ColorScheme _colorScheme;
|
||||||
|
|
||||||
|
void layerSurfaceConfigure(uint32_t serial, uint32_t width, uint32_t height);
|
||||||
|
void render();
|
||||||
|
void renderTags();
|
||||||
|
void renderStatus();
|
||||||
|
|
||||||
|
// low-level rendering
|
||||||
|
void setColorScheme(const ColorScheme& scheme, bool invert = false);
|
||||||
|
void beginFg();
|
||||||
|
void beginBg();
|
||||||
|
void renderComponent(BarComponent& component);
|
||||||
|
BarComponent createComponent(const std::string& initial = {});
|
||||||
|
public:
|
||||||
|
Bar();
|
||||||
|
const wl_surface* surface() const;
|
||||||
|
bool visible() const;
|
||||||
|
void show(wl_output* output);
|
||||||
|
void hide();
|
||||||
|
void setTag(int tag, int state, int numClients, int focusedClient);
|
||||||
|
void setSelected(bool selected);
|
||||||
|
void setLayout(const std::string& layout);
|
||||||
|
void setTitle(const std::string& title);
|
||||||
|
void setStatus(const std::string& status);
|
||||||
|
void invalidate();
|
||||||
|
void click(Monitor* mon, int x, int y, int btn);
|
||||||
|
};
|
70
somebar/src/common.hpp
Normal file
70
somebar/src/common.hpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// somebar - dwl bar
|
||||||
|
// See LICENSE file for copyright and license details.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include <linux/input-event-codes.h>
|
||||||
|
#include <cairo/cairo.h>
|
||||||
|
#include <pango/pango.h>
|
||||||
|
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
|
struct Color {
|
||||||
|
Color() {}
|
||||||
|
constexpr Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a=255) : r(r), g(g), b(b), a(a) { }
|
||||||
|
uint8_t r, g, b, a {255};
|
||||||
|
};
|
||||||
|
struct ColorScheme {
|
||||||
|
Color fg, bg;
|
||||||
|
};
|
||||||
|
union Arg {
|
||||||
|
unsigned int ui;
|
||||||
|
const void* v;
|
||||||
|
};
|
||||||
|
struct Monitor;
|
||||||
|
|
||||||
|
enum TagState { None, Active = 0x01, Urgent = 0x02 };
|
||||||
|
enum Control { ClkNone, ClkTagBar, ClkLayoutSymbol, ClkWinTitle, ClkStatusText };
|
||||||
|
struct Button {
|
||||||
|
int control;
|
||||||
|
int btn; // <linux/input-event-codes.h>
|
||||||
|
void (*func)(Monitor& mon, const Arg& arg);
|
||||||
|
const Arg arg;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern wl_display* display;
|
||||||
|
extern wl_compositor* compositor;
|
||||||
|
extern wl_shm* shm;
|
||||||
|
extern zwlr_layer_shell_v1* wlrLayerShell;
|
||||||
|
|
||||||
|
void spawn(Monitor&, const Arg& arg);
|
||||||
|
void setCloexec(int fd);
|
||||||
|
[[noreturn]] void die(const char* why);
|
||||||
|
[[noreturn]] void diesys(const char* why);
|
||||||
|
|
||||||
|
// wayland smart pointers
|
||||||
|
template<typename T>
|
||||||
|
struct WlDeleter;
|
||||||
|
#define WL_DELETER(type, fn) template<> struct WlDeleter<type> { \
|
||||||
|
void operator()(type* v) { if(v) fn(v); } \
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using wl_unique_ptr = std::unique_ptr<T, WlDeleter<T>>;
|
||||||
|
|
||||||
|
WL_DELETER(wl_buffer, wl_buffer_destroy);
|
||||||
|
WL_DELETER(wl_output, wl_output_release);
|
||||||
|
WL_DELETER(wl_pointer, wl_pointer_release);
|
||||||
|
WL_DELETER(wl_seat, wl_seat_release);
|
||||||
|
WL_DELETER(wl_surface, wl_surface_destroy);
|
||||||
|
WL_DELETER(zwlr_layer_surface_v1, zwlr_layer_surface_v1_destroy);
|
||||||
|
|
||||||
|
WL_DELETER(cairo_t, cairo_destroy);
|
||||||
|
WL_DELETER(cairo_surface_t, cairo_surface_destroy);
|
||||||
|
|
||||||
|
WL_DELETER(PangoContext, g_object_unref);
|
||||||
|
WL_DELETER(PangoLayout, g_object_unref);
|
||||||
|
|
||||||
|
#undef WL_DELETER
|
27
somebar/src/config.def.hpp
Normal file
27
somebar/src/config.def.hpp
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// somebar - dwl bar
|
||||||
|
// See LICENSE file for copyright and license details.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
constexpr bool topbar = true;
|
||||||
|
|
||||||
|
constexpr int paddingX = 10;
|
||||||
|
constexpr int paddingY = 3;
|
||||||
|
|
||||||
|
// See https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html
|
||||||
|
constexpr const char* font = "Sans 12";
|
||||||
|
|
||||||
|
constexpr ColorScheme colorInactive = {Color(0xbb, 0xbb, 0xbb), Color(0x22, 0x22, 0x22)};
|
||||||
|
constexpr ColorScheme colorActive = {Color(0xee, 0xee, 0xee), Color(0x00, 0x55, 0x77)};
|
||||||
|
constexpr const char* termcmd[] = {"foot", nullptr};
|
||||||
|
|
||||||
|
static std::vector<std::string> tagNames = {
|
||||||
|
"1", "2", "3",
|
||||||
|
"4", "5", "6",
|
||||||
|
"7", "8", "9",
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr Button buttons[] = {
|
||||||
|
{ ClkStatusText, BTN_RIGHT, spawn, {.v = termcmd} },
|
||||||
|
};
|
29
somebar/src/config.hpp
Normal file
29
somebar/src/config.hpp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// somebar - dwl bar
|
||||||
|
// See LICENSE file for copyright and license details.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
constexpr bool topbar = false;
|
||||||
|
|
||||||
|
constexpr int paddingX = 10;
|
||||||
|
constexpr int paddingY = 3;
|
||||||
|
|
||||||
|
// See https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html
|
||||||
|
constexpr const char* font = "JetBrains Mono Nerd Font 12";
|
||||||
|
// constexpr const char* font = "Sans 12";
|
||||||
|
|
||||||
|
constexpr ColorScheme colorInactive = {Color(0xbb, 0xbb, 0xbb), Color(0x22, 0x22, 0x22)};
|
||||||
|
constexpr ColorScheme colorActive = {Color(0xee, 0xee, 0xee), Color(0x00, 0x55, 0x77)};
|
||||||
|
constexpr const char* termcmd[] = {"alacritty", nullptr};
|
||||||
|
|
||||||
|
static std::vector<std::string> tagNames = {
|
||||||
|
"1", "2", "3",
|
||||||
|
"4", "5",
|
||||||
|
// "6",
|
||||||
|
// "7", "8", "9",
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr Button buttons[] = {
|
||||||
|
{ ClkStatusText, BTN_RIGHT, spawn, {.v = termcmd} },
|
||||||
|
};
|
71
somebar/src/line_buffer.hpp
Normal file
71
somebar/src/line_buffer.hpp
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
// somebar - dwl bar
|
||||||
|
// See LICENSE file for copyright and license details.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <array>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
// reads data from Reader, and passes complete lines to Consumer.
|
||||||
|
template<size_t BufSize>
|
||||||
|
class LineBuffer {
|
||||||
|
using Iterator = typename std::array<char, BufSize>::iterator;
|
||||||
|
std::array<char, BufSize> _buffer;
|
||||||
|
Iterator _bufferedTo;
|
||||||
|
Iterator _consumedTo;
|
||||||
|
bool _discardLine {false};
|
||||||
|
public:
|
||||||
|
LineBuffer()
|
||||||
|
: _bufferedTo {_buffer.begin()}
|
||||||
|
, _consumedTo {_buffer.begin()}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Reader, typename Consumer>
|
||||||
|
ssize_t readLines(const Reader& reader, const Consumer& consumer)
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
auto bytesRead = reader(_bufferedTo, _buffer.end() - _bufferedTo);
|
||||||
|
if (bytesRead <= 0) {
|
||||||
|
return bytesRead;
|
||||||
|
}
|
||||||
|
_bufferedTo += bytesRead;
|
||||||
|
dispatchLines(consumer);
|
||||||
|
resetBuffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
template<typename Consumer>
|
||||||
|
void dispatchLines(const Consumer& consumer)
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
auto separator = std::find(_consumedTo, _bufferedTo, '\n');
|
||||||
|
if (separator == _bufferedTo) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
size_t lineLength = separator - _consumedTo;
|
||||||
|
if (!_discardLine) {
|
||||||
|
consumer(_consumedTo, lineLength);
|
||||||
|
}
|
||||||
|
_consumedTo = separator + 1;
|
||||||
|
_discardLine = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetBuffer()
|
||||||
|
{
|
||||||
|
size_t bytesRemaining = _bufferedTo - _consumedTo;
|
||||||
|
if (bytesRemaining == _buffer.size()) {
|
||||||
|
// line too long
|
||||||
|
_discardLine = true;
|
||||||
|
_consumedTo = _buffer.begin();
|
||||||
|
_bufferedTo = _buffer.begin();
|
||||||
|
} else {
|
||||||
|
// move the last partial message to the front of the buffer, so a full-sized
|
||||||
|
// message will fit
|
||||||
|
std::copy(_consumedTo, _bufferedTo, _buffer.begin());
|
||||||
|
_consumedTo = _buffer.begin();
|
||||||
|
_bufferedTo = _consumedTo + bytesRemaining;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
611
somebar/src/main.cpp
Normal file
611
somebar/src/main.cpp
Normal file
@ -0,0 +1,611 @@
|
|||||||
|
// somebar - dwl bar
|
||||||
|
// See LICENSE file for copyright and license details.
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <sstream>
|
||||||
|
#include <list>
|
||||||
|
#include <optional>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <linux/input-event-codes.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include <wayland-cursor.h>
|
||||||
|
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||||
|
#include "xdg-output-unstable-v1-client-protocol.h"
|
||||||
|
#include "xdg-shell-client-protocol.h"
|
||||||
|
#include "common.hpp"
|
||||||
|
#include "config.hpp"
|
||||||
|
#include "bar.hpp"
|
||||||
|
#include "line_buffer.hpp"
|
||||||
|
|
||||||
|
struct Monitor {
|
||||||
|
uint32_t registryName;
|
||||||
|
std::string xdgName;
|
||||||
|
wl_unique_ptr<wl_output> wlOutput;
|
||||||
|
Bar bar;
|
||||||
|
bool desiredVisibility {true};
|
||||||
|
bool hasData;
|
||||||
|
uint32_t tags;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SeatPointer {
|
||||||
|
wl_unique_ptr<wl_pointer> wlPointer;
|
||||||
|
Monitor* focusedMonitor;
|
||||||
|
int x, y;
|
||||||
|
std::vector<int> btns;
|
||||||
|
};
|
||||||
|
struct Seat {
|
||||||
|
uint32_t name;
|
||||||
|
wl_unique_ptr<wl_seat> wlSeat;
|
||||||
|
std::optional<SeatPointer> pointer;
|
||||||
|
};
|
||||||
|
|
||||||
|
static Monitor* monitorFromSurface(const wl_surface* surface);
|
||||||
|
static void setupMonitor(uint32_t name, wl_output* output);
|
||||||
|
static void updatemon(Monitor &mon);
|
||||||
|
static void onReady();
|
||||||
|
static void setupStatusFifo();
|
||||||
|
static void onStatus();
|
||||||
|
static void onStdin();
|
||||||
|
static void handleStdin(const std::string& line);
|
||||||
|
static void updateVisibility(const std::string& name, bool(*updater)(bool));
|
||||||
|
static void onGlobalAdd(void*, wl_registry* registry, uint32_t name, const char* interface, uint32_t version);
|
||||||
|
static void onGlobalRemove(void*, wl_registry* registry, uint32_t name);
|
||||||
|
static void requireGlobal(const void* p, const char* name);
|
||||||
|
static void waylandFlush();
|
||||||
|
static void cleanup();
|
||||||
|
|
||||||
|
wl_display* display;
|
||||||
|
wl_compositor* compositor;
|
||||||
|
wl_shm* shm;
|
||||||
|
zwlr_layer_shell_v1* wlrLayerShell;
|
||||||
|
static xdg_wm_base* xdgWmBase;
|
||||||
|
static zxdg_output_manager_v1* xdgOutputManager;
|
||||||
|
static wl_surface* cursorSurface;
|
||||||
|
static wl_cursor_image* cursorImage;
|
||||||
|
static bool ready;
|
||||||
|
static std::list<Monitor> monitors;
|
||||||
|
static std::vector<std::pair<uint32_t, wl_output*>> uninitializedOutputs;
|
||||||
|
static std::list<Seat> seats;
|
||||||
|
static Monitor* selmon;
|
||||||
|
static std::string lastStatus;
|
||||||
|
static std::string statusFifoName;
|
||||||
|
static std::vector<pollfd> pollfds;
|
||||||
|
static std::array<int, 2> signalSelfPipe;
|
||||||
|
static int displayFd {-1};
|
||||||
|
static int statusFifoFd {-1};
|
||||||
|
static int statusFifoWriter {-1};
|
||||||
|
static bool quitting {false};
|
||||||
|
|
||||||
|
void spawn(Monitor&, const Arg& arg)
|
||||||
|
{
|
||||||
|
if (fork() == 0) {
|
||||||
|
auto argv = static_cast<char* const*>(arg.v);
|
||||||
|
setsid();
|
||||||
|
execvp(argv[0], argv);
|
||||||
|
fprintf(stderr, "somebar: execvp %s ", argv[0]);
|
||||||
|
perror(" failed");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct xdg_wm_base_listener xdgWmBaseListener = {
|
||||||
|
[](void*, xdg_wm_base* sender, uint32_t serial) {
|
||||||
|
xdg_wm_base_pong(sender, serial);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct zxdg_output_v1_listener xdgOutputListener = {
|
||||||
|
.logical_position = [](void*, zxdg_output_v1*, int, int) { },
|
||||||
|
.logical_size = [](void*, zxdg_output_v1*, int, int) { },
|
||||||
|
.done = [](void*, zxdg_output_v1*) { },
|
||||||
|
.name = [](void* mp, zxdg_output_v1* xdgOutput, const char* name) {
|
||||||
|
auto& monitor = *static_cast<Monitor*>(mp);
|
||||||
|
monitor.xdgName = name;
|
||||||
|
zxdg_output_v1_destroy(xdgOutput);
|
||||||
|
},
|
||||||
|
.description = [](void*, zxdg_output_v1*, const char*) { },
|
||||||
|
};
|
||||||
|
|
||||||
|
Monitor* monitorFromSurface(const wl_surface* surface)
|
||||||
|
{
|
||||||
|
auto mon = std::find_if(begin(monitors), end(monitors), [surface](const Monitor& mon) {
|
||||||
|
return mon.bar.surface() == surface;
|
||||||
|
});
|
||||||
|
return mon != end(monitors) ? &*mon : nullptr;
|
||||||
|
}
|
||||||
|
static const struct wl_pointer_listener pointerListener = {
|
||||||
|
.enter = [](void* sp, wl_pointer* pointer, uint32_t serial,
|
||||||
|
wl_surface* surface, wl_fixed_t x, wl_fixed_t y)
|
||||||
|
{
|
||||||
|
auto& seat = *static_cast<Seat*>(sp);
|
||||||
|
seat.pointer->focusedMonitor = monitorFromSurface(surface);
|
||||||
|
if (!cursorImage) {
|
||||||
|
auto cursorTheme = wl_cursor_theme_load(nullptr, 24, shm);
|
||||||
|
cursorImage = wl_cursor_theme_get_cursor(cursorTheme, "left_ptr")->images[0];
|
||||||
|
cursorSurface = wl_compositor_create_surface(compositor);
|
||||||
|
wl_surface_attach(cursorSurface, wl_cursor_image_get_buffer(cursorImage), 0, 0);
|
||||||
|
wl_surface_commit(cursorSurface);
|
||||||
|
}
|
||||||
|
wl_pointer_set_cursor(pointer, serial, cursorSurface,
|
||||||
|
cursorImage->hotspot_x, cursorImage->hotspot_y);
|
||||||
|
},
|
||||||
|
.leave = [](void* sp, wl_pointer*, uint32_t serial, wl_surface*) {
|
||||||
|
auto& seat = *static_cast<Seat*>(sp);
|
||||||
|
seat.pointer->focusedMonitor = nullptr;
|
||||||
|
},
|
||||||
|
.motion = [](void* sp, wl_pointer*, uint32_t, wl_fixed_t x, wl_fixed_t y) {
|
||||||
|
auto& seat = *static_cast<Seat*>(sp);
|
||||||
|
seat.pointer->x = wl_fixed_to_int(x);
|
||||||
|
seat.pointer->y = wl_fixed_to_int(y);
|
||||||
|
},
|
||||||
|
.button = [](void* sp, wl_pointer*, uint32_t, uint32_t, uint32_t button, uint32_t pressed) {
|
||||||
|
auto& seat = *static_cast<Seat*>(sp);
|
||||||
|
auto it = std::find(begin(seat.pointer->btns), end(seat.pointer->btns), button);
|
||||||
|
if (pressed == WL_POINTER_BUTTON_STATE_PRESSED && it == end(seat.pointer->btns)) {
|
||||||
|
seat.pointer->btns.push_back(button);
|
||||||
|
} else if (pressed == WL_POINTER_BUTTON_STATE_RELEASED && it != end(seat.pointer->btns)) {
|
||||||
|
seat.pointer->btns.erase(it);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.axis = [](void* sp, wl_pointer*, uint32_t, uint32_t, wl_fixed_t) { },
|
||||||
|
.frame = [](void* sp, wl_pointer*) {
|
||||||
|
auto& seat = *static_cast<Seat*>(sp);
|
||||||
|
auto mon = seat.pointer->focusedMonitor;
|
||||||
|
if (!mon) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (auto btn : seat.pointer->btns) {
|
||||||
|
mon->bar.click(mon, seat.pointer->x, seat.pointer->y, btn);
|
||||||
|
}
|
||||||
|
seat.pointer->btns.clear();
|
||||||
|
},
|
||||||
|
.axis_source = [](void*, wl_pointer*, uint32_t) { },
|
||||||
|
.axis_stop = [](void*, wl_pointer*, uint32_t, uint32_t) { },
|
||||||
|
.axis_discrete = [](void*, wl_pointer*, uint32_t, int32_t) { },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct wl_seat_listener seatListener = {
|
||||||
|
.capabilities = [](void* sp, wl_seat*, uint32_t cap)
|
||||||
|
{
|
||||||
|
auto& seat = *static_cast<Seat*>(sp);
|
||||||
|
auto hasPointer = cap & WL_SEAT_CAPABILITY_POINTER;
|
||||||
|
if (!seat.pointer && hasPointer) {
|
||||||
|
auto &pointer = seat.pointer.emplace();
|
||||||
|
pointer.wlPointer = wl_unique_ptr<wl_pointer> {wl_seat_get_pointer(seat.wlSeat.get())};
|
||||||
|
wl_pointer_add_listener(seat.pointer->wlPointer.get(), &pointerListener, &seat);
|
||||||
|
} else if (seat.pointer && !hasPointer) {
|
||||||
|
seat.pointer.reset();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.name = [](void*, wl_seat*, const char* name) { }
|
||||||
|
};
|
||||||
|
|
||||||
|
void setupMonitor(uint32_t name, wl_output* output) {
|
||||||
|
auto& monitor = monitors.emplace_back(Monitor {name, {}, wl_unique_ptr<wl_output> {output}});
|
||||||
|
monitor.bar.setStatus(lastStatus);
|
||||||
|
auto xdgOutput = zxdg_output_manager_v1_get_xdg_output(xdgOutputManager, monitor.wlOutput.get());
|
||||||
|
zxdg_output_v1_add_listener(xdgOutput, &xdgOutputListener, &monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updatemon(Monitor& mon)
|
||||||
|
{
|
||||||
|
if (!mon.hasData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mon.desiredVisibility) {
|
||||||
|
if (mon.bar.visible()) {
|
||||||
|
mon.bar.invalidate();
|
||||||
|
} else {
|
||||||
|
mon.bar.show(mon.wlOutput.get());
|
||||||
|
}
|
||||||
|
} else if (mon.bar.visible()) {
|
||||||
|
mon.bar.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// called after we have received the initial batch of globals
|
||||||
|
void onReady()
|
||||||
|
{
|
||||||
|
requireGlobal(compositor, "wl_compositor");
|
||||||
|
requireGlobal(shm, "wl_shm");
|
||||||
|
requireGlobal(wlrLayerShell, "zwlr_layer_shell_v1");
|
||||||
|
requireGlobal(xdgOutputManager, "zxdg_output_manager_v1");
|
||||||
|
setupStatusFifo();
|
||||||
|
wl_display_roundtrip(display); // roundtrip so we receive all dwl tags etc.
|
||||||
|
|
||||||
|
ready = true;
|
||||||
|
for (auto output : uninitializedOutputs) {
|
||||||
|
setupMonitor(output.first, output.second);
|
||||||
|
}
|
||||||
|
wl_display_roundtrip(display); // wait for xdg_output names before we read stdin
|
||||||
|
}
|
||||||
|
|
||||||
|
bool createFifo(std::string path)
|
||||||
|
{
|
||||||
|
auto result = mkfifo(path.c_str(), 0666);
|
||||||
|
if (result == 0) {
|
||||||
|
auto fd = open(path.c_str(), O_CLOEXEC | O_NONBLOCK | O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
diesys("open status fifo reader");
|
||||||
|
}
|
||||||
|
statusFifoName = path;
|
||||||
|
statusFifoFd = fd;
|
||||||
|
|
||||||
|
fd = open(path.c_str(), O_CLOEXEC | O_WRONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
diesys("open status fifo writer");
|
||||||
|
}
|
||||||
|
statusFifoWriter = fd;
|
||||||
|
|
||||||
|
pollfds.push_back({
|
||||||
|
.fd = statusFifoFd,
|
||||||
|
.events = POLLIN,
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
} else if (errno != EEXIST) {
|
||||||
|
diesys("mkfifo");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupStatusFifo()
|
||||||
|
{
|
||||||
|
if (!statusFifoName.empty()) {
|
||||||
|
createFifo(statusFifoName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto i=0; i<100; i++) {
|
||||||
|
auto path = std::string{getenv("XDG_RUNTIME_DIR")} + "/somebar-" + std::to_string(i);
|
||||||
|
if (createFifo(path)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static LineBuffer<512> stdinBuffer;
|
||||||
|
static void onStdin()
|
||||||
|
{
|
||||||
|
auto res = stdinBuffer.readLines(
|
||||||
|
[](void* p, size_t size) { return read(0, p, size); },
|
||||||
|
[](char* p, size_t size) { handleStdin({p, size}); });
|
||||||
|
if (res == 0) {
|
||||||
|
quitting = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handleStdin(const std::string& line)
|
||||||
|
{
|
||||||
|
// this parses the lines that dwl sends in printstatus()
|
||||||
|
std::string monName, command;
|
||||||
|
auto stream = std::istringstream {line};
|
||||||
|
stream >> monName >> command;
|
||||||
|
if (!stream.good()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto mon = std::find_if(begin(monitors), end(monitors), [&](const Monitor& mon) {
|
||||||
|
return mon.xdgName == monName;
|
||||||
|
});
|
||||||
|
if (mon == end(monitors))
|
||||||
|
return;
|
||||||
|
if (command == "title") {
|
||||||
|
auto title = std::string {};
|
||||||
|
std::getline(stream, title);
|
||||||
|
mon->bar.setTitle(title);
|
||||||
|
} else if (command == "selmon") {
|
||||||
|
uint32_t selected;
|
||||||
|
stream >> selected;
|
||||||
|
mon->bar.setSelected(selected);
|
||||||
|
if (selected) {
|
||||||
|
selmon = &*mon;
|
||||||
|
} else if (selmon == &*mon) {
|
||||||
|
selmon = nullptr;
|
||||||
|
}
|
||||||
|
} else if (command == "tags") {
|
||||||
|
uint32_t occupied, tags, clientTags, urgent;
|
||||||
|
stream >> occupied >> tags >> clientTags >> urgent;
|
||||||
|
for (auto i=0u; i<tagNames.size(); i++) {
|
||||||
|
auto tagMask = 1 << i;
|
||||||
|
int state = TagState::None;
|
||||||
|
if (tags & tagMask)
|
||||||
|
state |= TagState::Active;
|
||||||
|
if (urgent & tagMask)
|
||||||
|
state |= TagState::Urgent;
|
||||||
|
mon->bar.setTag(i, state, occupied & tagMask ? 1 : 0, clientTags & tagMask ? 0 : -1);
|
||||||
|
}
|
||||||
|
mon->tags = tags;
|
||||||
|
} else if (command == "layout") {
|
||||||
|
auto layout = std::string {};
|
||||||
|
std::getline(stream, layout);
|
||||||
|
mon->bar.setLayout(layout);
|
||||||
|
}
|
||||||
|
mon->hasData = true;
|
||||||
|
updatemon(*mon);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string prefixStatus = "status ";
|
||||||
|
const std::string prefixShow = "show ";
|
||||||
|
const std::string prefixHide = "hide ";
|
||||||
|
const std::string prefixToggle = "toggle ";
|
||||||
|
const std::string argAll = "all";
|
||||||
|
const std::string argSelected = "selected";
|
||||||
|
|
||||||
|
static LineBuffer<512> statusBuffer;
|
||||||
|
void onStatus()
|
||||||
|
{
|
||||||
|
statusBuffer.readLines(
|
||||||
|
[](void* p, size_t size) {
|
||||||
|
return read(statusFifoFd, p, size);
|
||||||
|
},
|
||||||
|
[](const char* buffer, size_t n) {
|
||||||
|
auto str = std::string {buffer, n};
|
||||||
|
if (str.rfind(prefixStatus, 0) == 0) {
|
||||||
|
lastStatus = str.substr(prefixStatus.size());
|
||||||
|
for (auto &monitor : monitors) {
|
||||||
|
monitor.bar.setStatus(lastStatus);
|
||||||
|
monitor.bar.invalidate();
|
||||||
|
}
|
||||||
|
} else if (str.rfind(prefixShow, 0) == 0) {
|
||||||
|
updateVisibility(str.substr(prefixShow.size()), [](bool) { return true; });
|
||||||
|
} else if (str.rfind(prefixHide, 0) == 0) {
|
||||||
|
updateVisibility(str.substr(prefixHide.size()), [](bool) { return false; });
|
||||||
|
} else if (str.rfind(prefixToggle, 0) == 0) {
|
||||||
|
updateVisibility(str.substr(prefixToggle.size()), [](bool vis) { return !vis; });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateVisibility(const std::string& name, bool(*updater)(bool))
|
||||||
|
{
|
||||||
|
auto isCurrent = name == argSelected;
|
||||||
|
auto isAll = name == argAll;
|
||||||
|
for (auto& mon : monitors) {
|
||||||
|
if (isAll ||
|
||||||
|
isCurrent && &mon == selmon ||
|
||||||
|
mon.xdgName == name) {
|
||||||
|
auto newVisibility = updater(mon.desiredVisibility);
|
||||||
|
if (newVisibility != mon.desiredVisibility) {
|
||||||
|
mon.desiredVisibility = newVisibility;
|
||||||
|
updatemon(mon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HandleGlobalHelper {
|
||||||
|
wl_registry* registry;
|
||||||
|
uint32_t name;
|
||||||
|
const char* interface;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
bool handle(T& store, const wl_interface& iface, int version) {
|
||||||
|
if (strcmp(interface, iface.name)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
store = static_cast<T>(wl_registry_bind(registry, name, &iface, version));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
void onGlobalAdd(void*, wl_registry* registry, uint32_t name, const char* interface, uint32_t version)
|
||||||
|
{
|
||||||
|
auto reg = HandleGlobalHelper { registry, name, interface };
|
||||||
|
if (reg.handle(compositor, wl_compositor_interface, 4)) return;
|
||||||
|
if (reg.handle(shm, wl_shm_interface, 1)) return;
|
||||||
|
if (reg.handle(wlrLayerShell, zwlr_layer_shell_v1_interface, 4)) return;
|
||||||
|
if (reg.handle(xdgOutputManager, zxdg_output_manager_v1_interface, 3)) return;
|
||||||
|
if (reg.handle(xdgWmBase, xdg_wm_base_interface, 2)) {
|
||||||
|
xdg_wm_base_add_listener(xdgWmBase, &xdgWmBaseListener, nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (wl_seat* wlSeat; reg.handle(wlSeat, wl_seat_interface, 7)) {
|
||||||
|
auto& seat = seats.emplace_back(Seat {name, wl_unique_ptr<wl_seat> {wlSeat}});
|
||||||
|
wl_seat_add_listener(wlSeat, &seatListener, &seat);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (wl_output* output; reg.handle(output, wl_output_interface, 1)) {
|
||||||
|
if (ready) {
|
||||||
|
setupMonitor(name, output);
|
||||||
|
} else {
|
||||||
|
uninitializedOutputs.push_back({name, output});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void onGlobalRemove(void*, wl_registry* registry, uint32_t name)
|
||||||
|
{
|
||||||
|
monitors.remove_if([name](const Monitor &mon) { return mon.registryName == name; });
|
||||||
|
seats.remove_if([name](const Seat &seat) { return seat.name == name; });
|
||||||
|
}
|
||||||
|
static const struct wl_registry_listener registry_listener = {
|
||||||
|
.global = onGlobalAdd,
|
||||||
|
.global_remove = onGlobalRemove,
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
int opt;
|
||||||
|
while ((opt = getopt(argc, argv, "chvs:")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 's':
|
||||||
|
statusFifoName = optarg;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
printf("Usage: %s [-h] [-v] [-s path to the fifo] [-c command]\n", argv[0]);
|
||||||
|
printf(" -h: Show this help\n");
|
||||||
|
printf(" -v: Show somebar version\n");
|
||||||
|
printf(" -s: Change path to the fifo (default is \"$XDG_RUNTIME_DIR/somebar-0\")\n");
|
||||||
|
printf(" -c: Sends a command to sombar. See README for details.\n");
|
||||||
|
printf("If any of these are specified (except -s), somebar exits after the action.\n");
|
||||||
|
printf("Otherwise, somebar will display itself.\n");
|
||||||
|
exit(0);
|
||||||
|
case 'v':
|
||||||
|
printf("somebar " SOMEBAR_VERSION "\n");
|
||||||
|
exit(0);
|
||||||
|
case 'c':
|
||||||
|
if (optind >= argc) {
|
||||||
|
die("Expected command");
|
||||||
|
}
|
||||||
|
if (statusFifoName.empty()) {
|
||||||
|
statusFifoName = std::string {getenv("XDG_RUNTIME_DIR")} + "/somebar-0";
|
||||||
|
}
|
||||||
|
statusFifoWriter = open(statusFifoName.c_str(), O_WRONLY | O_CLOEXEC);
|
||||||
|
if (statusFifoWriter < 0) {
|
||||||
|
fprintf(stderr, "could not open %s: ", statusFifoName.c_str());
|
||||||
|
perror("");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
auto str = std::string {};
|
||||||
|
for (auto i = optind; i<argc; i++) {
|
||||||
|
if (i > optind) str += " ";
|
||||||
|
str += argv[i];
|
||||||
|
}
|
||||||
|
str += "\n";
|
||||||
|
write(statusFifoWriter, str.c_str(), str.size());
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pipe(signalSelfPipe.data()) < 0) {
|
||||||
|
diesys("pipe");
|
||||||
|
}
|
||||||
|
setCloexec(signalSelfPipe[0]);
|
||||||
|
setCloexec(signalSelfPipe[1]);
|
||||||
|
|
||||||
|
struct sigaction sighandler = {};
|
||||||
|
sighandler.sa_handler = [](int) {
|
||||||
|
if (write(signalSelfPipe[1], "0", 1) < 0) {
|
||||||
|
diesys("write");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (sigaction(SIGTERM, &sighandler, nullptr) < 0) {
|
||||||
|
diesys("sigaction");
|
||||||
|
}
|
||||||
|
if (sigaction(SIGINT, &sighandler, nullptr) < 0) {
|
||||||
|
diesys("sigaction");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sigaction chld_handler = {};
|
||||||
|
chld_handler.sa_handler = SIG_IGN;
|
||||||
|
if (sigaction(SIGCHLD, &chld_handler, nullptr) < 0) {
|
||||||
|
die("sigaction");
|
||||||
|
}
|
||||||
|
|
||||||
|
pollfds.push_back({
|
||||||
|
.fd = signalSelfPipe[0],
|
||||||
|
.events = POLLIN,
|
||||||
|
});
|
||||||
|
|
||||||
|
display = wl_display_connect(nullptr);
|
||||||
|
if (!display) {
|
||||||
|
die("Failed to connect to Wayland display");
|
||||||
|
}
|
||||||
|
displayFd = wl_display_get_fd(display);
|
||||||
|
|
||||||
|
auto registry = wl_display_get_registry(display);
|
||||||
|
wl_registry_add_listener(registry, ®istry_listener, nullptr);
|
||||||
|
wl_display_roundtrip(display);
|
||||||
|
onReady();
|
||||||
|
|
||||||
|
pollfds.push_back({
|
||||||
|
.fd = displayFd,
|
||||||
|
.events = POLLIN,
|
||||||
|
});
|
||||||
|
pollfds.push_back({
|
||||||
|
.fd = STDIN_FILENO,
|
||||||
|
.events = POLLIN,
|
||||||
|
});
|
||||||
|
if (fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) < 0) {
|
||||||
|
diesys("fcntl F_SETFL");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!quitting) {
|
||||||
|
waylandFlush();
|
||||||
|
if (poll(pollfds.data(), pollfds.size(), -1) < 0) {
|
||||||
|
if (errno != EINTR) {
|
||||||
|
diesys("poll");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (auto& ev : pollfds) {
|
||||||
|
if (ev.revents & POLLNVAL) {
|
||||||
|
die("poll revents contains POLLNVAL");
|
||||||
|
} else if (ev.fd == displayFd) {
|
||||||
|
if (ev.revents & POLLIN) {
|
||||||
|
if (wl_display_dispatch(display) < 0) {
|
||||||
|
die("wl_display_dispatch");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ev.revents & POLLOUT) {
|
||||||
|
ev.events = POLLIN;
|
||||||
|
waylandFlush();
|
||||||
|
}
|
||||||
|
} else if (ev.fd == STDIN_FILENO && (ev.revents & POLLIN)) {
|
||||||
|
onStdin();
|
||||||
|
} else if (ev.fd == statusFifoFd && (ev.revents & POLLIN)) {
|
||||||
|
onStatus();
|
||||||
|
} else if (ev.fd == signalSelfPipe[0] && (ev.revents & POLLIN)) {
|
||||||
|
quitting = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void requireGlobal(const void* p, const char* name)
|
||||||
|
{
|
||||||
|
if (p) return;
|
||||||
|
fprintf(stderr, "Wayland compositor does not export required global %s, aborting.\n", name);
|
||||||
|
cleanup();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void waylandFlush()
|
||||||
|
{
|
||||||
|
wl_display_dispatch_pending(display);
|
||||||
|
if (wl_display_flush(display) < 0 && errno == EAGAIN) {
|
||||||
|
for (auto& ev : pollfds) {
|
||||||
|
if (ev.fd == displayFd) {
|
||||||
|
ev.events |= POLLOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCloexec(int fd)
|
||||||
|
{
|
||||||
|
int flags = fcntl(fd, F_GETFD);
|
||||||
|
if (flags == -1) {
|
||||||
|
diesys("fcntl FD_GETFD");
|
||||||
|
}
|
||||||
|
if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
|
||||||
|
diesys("fcntl FD_SETFD");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cleanup() {
|
||||||
|
if (!statusFifoName.empty()) {
|
||||||
|
unlink(statusFifoName.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void die(const char* why) {
|
||||||
|
fprintf(stderr, "error: %s failed, aborting\n", why);
|
||||||
|
cleanup();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void diesys(const char* why) {
|
||||||
|
perror(why);
|
||||||
|
cleanup();
|
||||||
|
exit(1);
|
||||||
|
}
|
85
somebar/src/shm_buffer.cpp
Normal file
85
somebar/src/shm_buffer.cpp
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
// somebar - dwl bar
|
||||||
|
// See LICENSE file for copyright and license details.
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "shm_buffer.hpp"
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
static int createAnonShm();
|
||||||
|
constexpr int n = 2;
|
||||||
|
|
||||||
|
ShmBuffer::ShmBuffer(int w, int h, wl_shm_format format)
|
||||||
|
: width(w)
|
||||||
|
, height(h)
|
||||||
|
, stride(w*4)
|
||||||
|
{
|
||||||
|
auto oneSize = stride*size_t(h);
|
||||||
|
auto totalSize = oneSize * n;
|
||||||
|
auto fd = createAnonShm();
|
||||||
|
if (fd < 0) {
|
||||||
|
diesys("memfd_create");
|
||||||
|
}
|
||||||
|
if (ftruncate(fd, totalSize) < 0) {
|
||||||
|
diesys("ftruncate");
|
||||||
|
}
|
||||||
|
auto pool = wl_shm_create_pool(shm, fd, totalSize);
|
||||||
|
auto ptr = reinterpret_cast<uint8_t*>(mmap(nullptr, totalSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
|
||||||
|
if (!ptr) {
|
||||||
|
diesys("mmap");
|
||||||
|
}
|
||||||
|
_mapping = MemoryMapping {ptr, totalSize};
|
||||||
|
close(fd);
|
||||||
|
for (auto i=0; i<n; i++) {
|
||||||
|
auto offset = oneSize*i;
|
||||||
|
_buffers[i] = {
|
||||||
|
ptr+offset,
|
||||||
|
wl_unique_ptr<wl_buffer> { wl_shm_pool_create_buffer(pool, offset, width, height, stride, format) },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
wl_shm_pool_destroy(pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* ShmBuffer::data()
|
||||||
|
{
|
||||||
|
return _buffers[_current].data;
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_buffer* ShmBuffer::buffer()
|
||||||
|
{
|
||||||
|
return _buffers[_current].buffer.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShmBuffer::flip()
|
||||||
|
{
|
||||||
|
_current = 1-_current;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
int createAnonShm() {
|
||||||
|
return memfd_create("wl_shm", MFD_CLOEXEC);
|
||||||
|
}
|
||||||
|
#elif defined(__FreeBSD__)
|
||||||
|
int createAnonShm() {
|
||||||
|
auto fd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
|
||||||
|
setCloexec(fd);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
int createAnonShm() {
|
||||||
|
char name[] = "/wl_shm-XXXXXX";
|
||||||
|
auto fd = shm_mkstemp(name);
|
||||||
|
if (fd >= 0) {
|
||||||
|
auto res = shm_unlink(name);
|
||||||
|
if (res < 0) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setCloexec(fd);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#error "your system has no sane method of creating an anonymous shared memory object. no, calling shm_open in a loop is not sane."
|
||||||
|
#endif
|
45
somebar/src/shm_buffer.hpp
Normal file
45
somebar/src/shm_buffer.hpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// somebar - dwl bar
|
||||||
|
// See LICENSE file for copyright and license details.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <array>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include "common.hpp"
|
||||||
|
|
||||||
|
class MemoryMapping {
|
||||||
|
void* _ptr {nullptr};
|
||||||
|
size_t _size {0};
|
||||||
|
public:
|
||||||
|
MemoryMapping() { }
|
||||||
|
explicit MemoryMapping(void* ptr, size_t size) : _ptr(ptr), _size(size) { }
|
||||||
|
MemoryMapping(const MemoryMapping&) = delete;
|
||||||
|
MemoryMapping(MemoryMapping&& other) { swap(other); }
|
||||||
|
MemoryMapping& operator=(const MemoryMapping& other) = delete;
|
||||||
|
MemoryMapping& operator=(MemoryMapping&& other) { swap(other); return *this; }
|
||||||
|
~MemoryMapping() { if (_ptr) munmap(_ptr, _size); }
|
||||||
|
void swap(MemoryMapping &other) {
|
||||||
|
using std::swap;
|
||||||
|
swap(_ptr, other._ptr);
|
||||||
|
swap(_size, other._size);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// double buffered shm
|
||||||
|
// format is must be 32-bit
|
||||||
|
class ShmBuffer {
|
||||||
|
struct Buf {
|
||||||
|
uint8_t* data {nullptr};
|
||||||
|
wl_unique_ptr<wl_buffer> buffer;
|
||||||
|
};
|
||||||
|
std::array<Buf, 2> _buffers;
|
||||||
|
int _current {0};
|
||||||
|
MemoryMapping _mapping;
|
||||||
|
public:
|
||||||
|
const uint32_t width, height, stride;
|
||||||
|
|
||||||
|
explicit ShmBuffer(int width, int height, wl_shm_format format);
|
||||||
|
uint8_t* data();
|
||||||
|
wl_buffer* buffer();
|
||||||
|
void flip();
|
||||||
|
};
|
55
someblocks/.gitignore
vendored
Normal file
55
someblocks/.gitignore
vendored
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# Custom blocks file
|
||||||
|
|
||||||
|
# Prerequisites
|
||||||
|
*.d
|
||||||
|
|
||||||
|
# Object files
|
||||||
|
*.o
|
||||||
|
*.ko
|
||||||
|
*.obj
|
||||||
|
*.elf
|
||||||
|
|
||||||
|
# Linker output
|
||||||
|
*.ilk
|
||||||
|
*.map
|
||||||
|
*.exp
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Libraries
|
||||||
|
*.lib
|
||||||
|
*.a
|
||||||
|
*.la
|
||||||
|
*.lo
|
||||||
|
|
||||||
|
# Shared objects (inc. Windows DLLs)
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
||||||
|
*.i*86
|
||||||
|
*.x86_64
|
||||||
|
*.hex
|
||||||
|
someblocks
|
||||||
|
|
||||||
|
# Debug files
|
||||||
|
*.dSYM/
|
||||||
|
*.su
|
||||||
|
*.idb
|
||||||
|
*.pdb
|
||||||
|
|
||||||
|
# Kernel Module Compile Results
|
||||||
|
*.mod*
|
||||||
|
*.cmd
|
||||||
|
.tmp_versions/
|
||||||
|
modules.order
|
||||||
|
Module.symvers
|
||||||
|
Mkfile.old
|
||||||
|
dkms.conf
|
8
someblocks/LICENSE
Normal file
8
someblocks/LICENSE
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
ISC License (ISC)
|
||||||
|
|
||||||
|
Copyright 2020 torrinfail
|
||||||
|
Copyright 2021 Raphael Robatsch
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
20
someblocks/Makefile
Normal file
20
someblocks/Makefile
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
PREFIX ?= /usr/local
|
||||||
|
MANPREFIX ?= $(PREFIX)/share/man
|
||||||
|
CC ?= cc
|
||||||
|
|
||||||
|
output: someblocks.c blocks.def.h blocks.h
|
||||||
|
${CC} someblocks.c $(LDFLAGS) -o someblocks
|
||||||
|
blocks.h:
|
||||||
|
cp blocks.def.h $@
|
||||||
|
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o *.gch someblocks
|
||||||
|
install: output
|
||||||
|
mkdir -p $(DESTDIR)$(PREFIX)/bin
|
||||||
|
install -m 0755 someblocks $(DESTDIR)$(PREFIX)/bin/someblocks
|
||||||
|
mkdir -p $(DESTDIR)$(MANPREFIX)/man1
|
||||||
|
install -m 0644 someblocks.1 $(DESTDIR)$(MANPREFIX)/man1/someblocks.1
|
||||||
|
uninstall:
|
||||||
|
rm -f $(DESTDIR)$(PREFIX)/bin/someblocks
|
||||||
|
rm -f $(DESTDIR)$(MANPREFIX)/man1/someblocks.1
|
19
someblocks/README.md
Normal file
19
someblocks/README.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# someblocks
|
||||||
|
Modular status bar for [somebar](https://git.sr.ht/~raphi/somebar) written in c.
|
||||||
|
|
||||||
|
This is a fork of [dwmblocks](https://github.com/torrinfail/dwmblocks), modified
|
||||||
|
to connect to somebar instead of dwm.
|
||||||
|
|
||||||
|
The mailing list for this project is
|
||||||
|
[~raphi/public-inbox@lists.sr.ht](mailto:~raphi/public-inbox@lists.sr.ht).
|
||||||
|
# usage
|
||||||
|
To use someblocks first run 'make' and then install it with 'sudo make install'.
|
||||||
|
After that you can put someblocks in your startup script to have it start with dwl/somebar.
|
||||||
|
# modifying blocks
|
||||||
|
The statusbar is made from text output from commandline programs.
|
||||||
|
Blocks are added and removed by editing the blocks.h header file.
|
||||||
|
By default the blocks.h header file is created the first time you run make which copies the default config from blocks.def.h.
|
||||||
|
This is so you can edit your status bar commands and they will not get overwritten in a future update.
|
||||||
|
# patches
|
||||||
|
Here are some patches to someblocks that add features that I either don't want to merge in, or that require a dwl/somebar patch to work.
|
||||||
|
I do not maintain these but I will take pull requests to update them.
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user