Compare commits
No commits in common. "master" and "main" have entirely different histories.
74 changed files with 4084 additions and 1769 deletions
0
.gitignore
vendored
Normal file → Executable file
0
.gitignore
vendored
Normal file → Executable file
674
LICENSE
674
LICENSE
|
@ -1,674 +0,0 @@
|
|||
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>.
|
41
README.MD
41
README.MD
|
@ -1,41 +0,0 @@
|
|||
# A selection of information output tools for dmenu
|
||||
|
||||
These are a selection of independant tools for displaying various information
|
||||
about system status in dmenu. Some of them i.e. `volume` have options (up, down, mute...)
|
||||
which are selectable options in dmenu.
|
||||
|
||||
Example in dmenu:
|
||||
|
||||
https://user-images.githubusercontent.com/31094984/166115207-c24e9ec8-136e-4956-9842-bcde496bb743.mp4
|
||||
|
||||
|
||||
Example in rofi:
|
||||
|
||||
|
||||
https://user-images.githubusercontent.com/31094984/166115213-511fdaaa-4c04-461a-9976-38a2e9bd83fe.mp4
|
||||
|
||||
|
||||
They are compiled separately, for example:
|
||||
```nim
|
||||
nim c pingclock
|
||||
```
|
||||
and then run with
|
||||
```sh
|
||||
./pingclock dmenu
|
||||
or
|
||||
./pingclock rofi
|
||||
```
|
||||
it can also be run without any arguments to receive a i3bar compatible json string
|
||||
|
||||
Personally, I have these bound to key combinations in i3. In fact, I have a seperate `bindsym` mode in which all these tools are accessible i.e. `$mod+i` to get to "info" mode then `p` to show pingclock.
|
||||
|
||||
You can also set the volume and brightness levels by typing a numeric figure into the dmenu/rofi input box
|
||||
|
||||
There's also an i3bar_tools_threaded folder with tools for use with i3bar/i3blocks which continously update. Currently these are not working as I recently switched from this as primary, to a secondary choice.
|
||||
|
||||
These have some configuration variables explicit to me, you'll need to change them for you for them to be useful I imagine.
|
||||
|
||||
### Dependencies
|
||||
- dmenu, rofi, or i3bar (with i3blocks)
|
||||
|
||||
I'm aware my code is messy. I'm aware my code is undocumented. But hopefully these things are simple enough to work out.
|
16
README.md
Normal file
16
README.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
# Requirements
|
||||
A non-exhaustive list of system requirements
|
||||
|
||||
General:
|
||||
- rofi or
|
||||
- rofi-lbonn-wayland for wayland support
|
||||
|
||||
Screenshurr:
|
||||
- maim (for X)
|
||||
- xsel (for X)
|
||||
- grim (for wayland)
|
||||
- slurp (for wayland)
|
||||
- wl-clipboard (for wayland)
|
||||
|
||||
Brightnurrs:
|
||||
- acpilight
|
129
base.nim
129
base.nim
|
@ -1,129 +0,0 @@
|
|||
import std/[os,osproc,strutils]
|
||||
import std/json
|
||||
import std/rdstdin
|
||||
import marshal
|
||||
|
||||
type
|
||||
Info* = object
|
||||
title*: string
|
||||
full_text*: string
|
||||
html_text*: string
|
||||
short_text*: string
|
||||
border*: string
|
||||
color*: string
|
||||
selected_color*: string
|
||||
background*: string
|
||||
selected_background*: string
|
||||
|
||||
type
|
||||
i3BarInput* = object
|
||||
button*: int
|
||||
x*: int
|
||||
y*: int
|
||||
|
||||
const background* = "#000000"
|
||||
const backgroundalt* = "#bb222222"
|
||||
const backgroundalt2* = "#bb333333"
|
||||
const foreground* = "#dfdfdf"
|
||||
const foregroundalt* = "#777"
|
||||
const foregroundalt2* = "#ccc"
|
||||
const black* = "#000000"
|
||||
const white* = "#FFFFFF"
|
||||
const yellow* = "#ffb52a"
|
||||
const red* = "#e60053"
|
||||
const purple* = "#9f78e1"
|
||||
const blue* = "#0a6cf5"
|
||||
const lightblue* = "#7296EF"
|
||||
const lighterblue* = "#B5DDF7"
|
||||
const green* = "#4b9901"
|
||||
const lightgreen* = "#00ff00"
|
||||
const grey* = "#dfdfdf"
|
||||
const darkgrey* = "#444"
|
||||
const primary* = yellow
|
||||
const secondary* = red
|
||||
const alert* = "#bd2c40"
|
||||
var loop* = true
|
||||
var stoploop* = false
|
||||
var dmenu = false
|
||||
var rofi = false
|
||||
|
||||
proc newInfo*(): Info =
|
||||
return Info(
|
||||
title: "Info : ",
|
||||
full_text: "",
|
||||
short_text: "",
|
||||
color: foreground,
|
||||
selected_color: background,
|
||||
border: white,
|
||||
selected_background: white,
|
||||
background: black
|
||||
)
|
||||
proc debugLog*(str: string) =
|
||||
let f = open("/tmp/debug.txt",fmAppend)
|
||||
defer: f.close()
|
||||
f.writeLine(str)
|
||||
|
||||
proc parseInput*(): i3BarInput =
|
||||
let input = readLineFromStdin("")
|
||||
try:
|
||||
let jsonNode = parseJson(input)
|
||||
let i3input = to(jsonNode, i3BarInput)
|
||||
return i3input
|
||||
except:
|
||||
return i3BarInput()
|
||||
|
||||
proc clearInput*(count: int = 1) =
|
||||
for x in countup(1, count):
|
||||
discard readLineFromStdin("")
|
||||
|
||||
proc getArguments*(): seq[string] =
|
||||
let args = commandLineParams()
|
||||
return args
|
||||
|
||||
proc runDMenu*(data: Info, opts: varargs[string], rofi: bool = false): string =
|
||||
discard execCmd("i3-msg mode \"default\"")
|
||||
var cmd = "echo -e \"" & $data.full_text & "\n"
|
||||
for opt in opts:
|
||||
cmd = cmd & opt & "\n"
|
||||
cmd.removeSuffix("\n")
|
||||
if not rofi:
|
||||
cmd = cmd & "\" | dmenu"
|
||||
else:
|
||||
cmd = cmd & "\" | rofi -dmenu"
|
||||
cmd = cmd & " -l " & $(len(opts) + 1)
|
||||
cmd = cmd & " -p \"" & $data.title & "\""
|
||||
cmd = cmd & " -nb \"" & $background & "\""
|
||||
cmd = cmd & " -nf \"" & $foreground & "\""
|
||||
cmd = cmd & " -sb \"" & $data.selected_background & "\""
|
||||
cmd = cmd & " -sf \"" & $data.selected_color & "\""
|
||||
cmd = cmd & " -fn Hermit-10"
|
||||
#echo "Dmenu :", cmd
|
||||
let output = execCmdEx(cmd)
|
||||
let option:string = strip(output[0])
|
||||
return option
|
||||
|
||||
proc outputJSON*(data: Info, args: varargs[string]): string {.discardable.} =
|
||||
var output = ""
|
||||
if dmenu:
|
||||
output = runDMenu(data, args)
|
||||
elif rofi:
|
||||
output = runDmenu(data,args, rofi = true)
|
||||
else:
|
||||
var j_data = data
|
||||
if j_data.html_text != "":
|
||||
j_data.full_text = j_data.html_text
|
||||
echo $$j_data
|
||||
return output
|
||||
|
||||
let args = getArguments()
|
||||
for arg in args:
|
||||
if arg == "noloop":
|
||||
stoploop = true
|
||||
if arg == "dmenu":
|
||||
stoploop = true
|
||||
dmenu = true
|
||||
if arg == "rofi":
|
||||
stoploop = true
|
||||
rofi = true
|
||||
|
||||
|
101
battery.nim
101
battery.nim
|
@ -1,101 +0,0 @@
|
|||
import base
|
||||
import strutils
|
||||
import std/os
|
||||
|
||||
proc battery_exists(): bool =
|
||||
let state = strip(readFile("/sys/class/power_supply/BAT0/present"))
|
||||
if state == "1":
|
||||
return true
|
||||
return false
|
||||
|
||||
proc is_charging(): bool =
|
||||
let state = strip(readFile("/sys/class/power_supply/BAT0/status"))
|
||||
case state:
|
||||
of "Charging":
|
||||
return true
|
||||
else:
|
||||
return false
|
||||
|
||||
proc get_charge(): int =
|
||||
var charge = 0
|
||||
let chg = strip(readFile("/sys/class/power_supply/BAT0/capacity"))
|
||||
if chg != "":
|
||||
charge = parseInt(chg)
|
||||
return charge
|
||||
|
||||
proc get_design(charge: int, state: bool): (string, string, string, string, string) =
|
||||
var icon = " "
|
||||
var icon_colour = lightgreen
|
||||
var col = foreground
|
||||
var bg = black
|
||||
var border = lightgreen
|
||||
if is_charging():
|
||||
icon = " "
|
||||
else:
|
||||
case charge:
|
||||
of 0..5:
|
||||
icon_colour = black
|
||||
col = black
|
||||
bg = red
|
||||
of 6..19:
|
||||
icon_colour = alert
|
||||
border = alert
|
||||
of 20..39:
|
||||
icon_colour = yellow
|
||||
border = yellow
|
||||
icon = " "
|
||||
of 40..59:
|
||||
icon_colour = green
|
||||
border= green
|
||||
icon = " "
|
||||
of 60..79:
|
||||
icon_colour = green
|
||||
border= green
|
||||
icon = " "
|
||||
of 80..100:
|
||||
icon_colour = lightgreen
|
||||
border = lightgreen
|
||||
icon = " "
|
||||
else:
|
||||
icon = "x "
|
||||
|
||||
let main_text = icon & " " & $charge & "%"
|
||||
let text = "<span foreground=\"" & icon_colour & "\">" & icon & "</span>" & $charge & "%"
|
||||
return (text,main_text, col, bg, border)
|
||||
|
||||
proc get_output(charge: int, state: bool): Info =
|
||||
let (text,main_text,col,bg_col,bord_col) = get_design(charge, state)
|
||||
let data = Info(
|
||||
title: "Battery : ",
|
||||
full_text: main_text,
|
||||
html_text: text,
|
||||
color: col,
|
||||
border: bord_col,
|
||||
background: bg_col,
|
||||
selected_background: bord_col,
|
||||
selected_color: black
|
||||
)
|
||||
return data
|
||||
|
||||
|
||||
proc get_battery_info() =
|
||||
var last_charge = -1
|
||||
var last_state = false
|
||||
while true:
|
||||
let charge = get_charge()
|
||||
let state = is_charging()
|
||||
if charge != last_charge or state != last_state:
|
||||
let data = get_output(charge, state)
|
||||
outputJSON(data)
|
||||
last_charge = charge
|
||||
last_state = state
|
||||
if stoploop:
|
||||
break
|
||||
sleep(1000)
|
||||
|
||||
proc main() =
|
||||
if battery_exists():
|
||||
get_battery_info()
|
||||
|
||||
main()
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
import std/os
|
||||
import strutils
|
||||
import std/osproc
|
||||
import std/math
|
||||
import base
|
||||
|
||||
proc getLimit(): int
|
||||
|
||||
let limit = getLimit()
|
||||
|
||||
proc getLimit(): int =
|
||||
let limit = parseInt(strip(readFile("/sys/class/backlight/intel_backlight/max_brightness")))
|
||||
return limit
|
||||
|
||||
proc getDesign(pcnt: float): string =
|
||||
var icon = "🌑"
|
||||
case pcnt:
|
||||
of 85..100:
|
||||
icon = "🌕"
|
||||
of 75..85:
|
||||
icon = "🌖"
|
||||
of 50..75:
|
||||
icon = "🌗"
|
||||
of 35..50:
|
||||
icon = "🌘"
|
||||
else:
|
||||
icon = "🌑"
|
||||
let percent = toInt(round(pcnt,0))
|
||||
let text = icon & " " & $percent & "%"
|
||||
return text
|
||||
|
||||
proc get_brightness*(run_once: bool = false) =
|
||||
var last_pcnt: float = 0
|
||||
while true:
|
||||
let current = parseInt(strip(readFile("/sys/class/backlight/intel_backlight/actual_brightness")))
|
||||
let pcnt = (current/limit)*100
|
||||
if pcnt != last_pcnt:
|
||||
let text = getDesign(pcnt)
|
||||
var data = newInfo()
|
||||
data.title = "Brightness : "
|
||||
data.full_text = text
|
||||
data.border = yellow
|
||||
data.selected_background = yellow
|
||||
data.selected_color = black
|
||||
let args = @["up", "down"]
|
||||
let option = outputJSON(data,args)
|
||||
if option in args:
|
||||
case option:
|
||||
of "up":
|
||||
discard execCmd("xbacklight -inc 5")
|
||||
get_brightness(true)
|
||||
of "down":
|
||||
discard execCmd("xbacklight -dec 5")
|
||||
get_brightness(true)
|
||||
else:
|
||||
try:
|
||||
let i = parseInt(option)
|
||||
discard execCmd("xbacklight -set " & $i)
|
||||
get_brightness(true)
|
||||
except:
|
||||
echo getCurrentExceptionMsg()
|
||||
|
||||
if run_once:
|
||||
break
|
||||
if stoploop:
|
||||
break
|
||||
last_pcnt = pcnt
|
||||
sleep(1000)
|
||||
|
||||
proc main() =
|
||||
get_brightness()
|
||||
|
||||
if isMainModule:
|
||||
main()
|
88
date.nim
88
date.nim
|
@ -1,88 +0,0 @@
|
|||
import std/os
|
||||
import std/times
|
||||
import std/osproc
|
||||
import std/re
|
||||
import base
|
||||
|
||||
|
||||
proc getObject(date: string): Info =
|
||||
var data = newInfo()
|
||||
data.title = "Date : "
|
||||
data.full_text = date
|
||||
data.border = blue
|
||||
data.selected_background = blue
|
||||
data.selected_color = white
|
||||
return data
|
||||
|
||||
#proc openCalendar(datestr: string) =
|
||||
proc newCalendar(): string =
|
||||
var c = """yad --calendar \
|
||||
--undecorated --fixed --close-on-unfocus --no-buttons \
|
||||
--width="222" --height="193" \
|
||||
--posx="%pos_x" --posy="%pos_y" \
|
||||
--title="yad-calendar" --borders 0 > /dev/null
|
||||
"""
|
||||
return c
|
||||
proc openCalendar*(input: i3barInput) =
|
||||
var c = newCalendar()
|
||||
|
||||
c = replace(c,re"%pos_x", $(input.x - 111))
|
||||
c = replace(c,re"%pos_y", $input.y)
|
||||
|
||||
discard execCmd(c)
|
||||
|
||||
proc dmenuCalendar() =
|
||||
var c = newCalendar()
|
||||
|
||||
c = replace(c,re"%pos_x", "100")
|
||||
c = replace(c,re"%pos_y", "0")
|
||||
|
||||
discard execCmd(c)
|
||||
|
||||
|
||||
proc getDate*() =
|
||||
var last_date = ""
|
||||
while true:
|
||||
let now = now()
|
||||
let d = now.format("yyyy-MM-dd")
|
||||
if d != last_date:
|
||||
let data = getObject(d)
|
||||
let args = @["open calendar"]
|
||||
case outputJSON(data, args):
|
||||
of "open calendar":
|
||||
dmenuCalendar()
|
||||
last_date = d
|
||||
if stoploop:
|
||||
break
|
||||
sleep(5000)
|
||||
|
||||
proc main() =
|
||||
getDate()
|
||||
|
||||
|
||||
if isMainModule:
|
||||
main()
|
||||
|
||||
#DATE="$(date +"%a %d %H:%M")"
|
||||
#if [ "$(xdotool getwindowfocus getwindowname)" = "yad-calendar" ]; then
|
||||
# exit 0
|
||||
#fi
|
||||
#eval "$(xdotool getmouselocation --shell)"
|
||||
#eval "$(xdotool getdisplaygeometry --shell)"
|
||||
## X
|
||||
#if [ "$((X + YAD_WIDTH / 2 + BORDER_SIZE))" -gt "$WIDTH" ]; then #Right side
|
||||
# : $((pos_x = WIDTH - YAD_WIDTH - BORDER_SIZE))
|
||||
#elif [ "$((X - YAD_WIDTH / 2 - BORDER_SIZE))" -lt 0 ]; then #Left side
|
||||
# : $((pos_x = BORDER_SIZE))
|
||||
#else #Center
|
||||
# : $((pos_x = X - YAD_WIDTH / 2))
|
||||
#fi
|
||||
## Y
|
||||
#if [ "$Y" -gt "$((HEIGHT / 2))" ]; then #Bottom
|
||||
# : $((pos_y = HEIGHT - YAD_HEIGHT - BAR_HEIGHT - BORDER_SIZE))
|
||||
#else #Top
|
||||
# : $((pos_y = BAR_HEIGHT + BORDER_SIZE))
|
||||
#fi
|
||||
#yad --calendar --undecorated --fixed --close-on-unfocus --no-buttons \
|
||||
# --width="$YAD_WIDTH" --height="$YAD_HEIGHT" --posx="$pos_x" --posy="$pos_y" \
|
||||
# --title="yad-calendar" --borders=0 >/dev/null &
|
1
emojis.json
Normal file
1
emojis.json
Normal file
File diff suppressed because one or more lines are too long
1567
examples/i3-tree-example.txt
Normal file
1567
examples/i3-tree-example.txt
Normal file
File diff suppressed because it is too large
Load diff
BIN
examples/urr_example.mp4
Normal file
BIN
examples/urr_example.mp4
Normal file
Binary file not shown.
BIN
examples/urr_example.png
Normal file
BIN
examples/urr_example.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
107
i3bar_tides.nim
107
i3bar_tides.nim
|
@ -1,107 +0,0 @@
|
|||
#curl https://www.tidetimes.org.uk/exmouth-dock-tide-times-20190101 | grep -E -o ">((High|Low)|([0-9]+:[0-9]+)|([0-9]+\.[0-9]+m))"
|
||||
import i3bar_base
|
||||
import std/re
|
||||
import std/httpclient
|
||||
import std/os
|
||||
import std/times
|
||||
|
||||
const url* = "https://www.tidetimes.org.uk/%LOC-tide-times"
|
||||
const loc* = "exmouth-dock"
|
||||
const icon: string = "🌊"
|
||||
|
||||
type
|
||||
Tide = ref object
|
||||
State: string
|
||||
Time: string
|
||||
Height: string
|
||||
Tomorrow: bool
|
||||
TideList = ref object
|
||||
Tides: seq[Tide]
|
||||
LastUpdated: DateTime
|
||||
|
||||
proc sortTides(tides: seq[Tide]): seq[Tide] =
|
||||
let timenow = now()
|
||||
var reltides: seq[Tide]
|
||||
var count = 0
|
||||
for tide in tides:
|
||||
if timenow.format("HH:MM") <= tide.Time or tide.Tomorrow:
|
||||
reltides.add(tide)
|
||||
count += 1
|
||||
if count >= 2:
|
||||
break
|
||||
return reltides
|
||||
|
||||
|
||||
proc getTideData(gettomorrow: bool = false): seq[Tide] =
|
||||
var tides: seq[Tide]
|
||||
let fnd = re">((High|Low)|([0-9]+:[0-9]+)|([0-9]+\.[0-9]+m))"
|
||||
var client = newHttpClient()
|
||||
var link = replace(url,re"\%LOC",loc)
|
||||
if gettomorrow:
|
||||
let tomdate = now() + initTimeInterval(days = 1)
|
||||
link &= "-" & tomdate.format("yyyyMMdd")
|
||||
try:
|
||||
let data = client.getContent(link)
|
||||
echo "Data : " & data
|
||||
let times = findAll(data,fnd)
|
||||
var tide: Tide
|
||||
var count: int = 0
|
||||
for time in times:
|
||||
let l = len(time) - 1
|
||||
if time == ">High" or time == ">Low":
|
||||
tide = Tide()
|
||||
tide.State = time[1..l]
|
||||
count = 1
|
||||
continue
|
||||
else:
|
||||
count += 1
|
||||
if count == 2:
|
||||
tide.Time = time[1..l]
|
||||
elif count == 3:
|
||||
tide.Height = time[1..l]
|
||||
if gettomorrow:
|
||||
tide.Tomorrow = true
|
||||
tides.add(tide)
|
||||
except:
|
||||
sleep(5000)
|
||||
return getTideData(false)
|
||||
if not gettomorrow:
|
||||
let tomtides = getTideData(true)
|
||||
for tide in tomtides:
|
||||
tides.add(tide)
|
||||
return tides
|
||||
|
||||
proc getDesign(tides: seq[Tide]): i3barData =
|
||||
var size = ""
|
||||
if len(tides) > 1:
|
||||
size = "small"
|
||||
let text = icon & tides[0].State[0] & " " & tides[0].Time & " " & tides[0].Height & "\r" &
|
||||
icon & tides[1].State[0] & " " & tides[1].Time & " " & tides[1].Height
|
||||
let t2 = "<span size=\"" & size & "\">" & text & "</span>"
|
||||
var data = newi3barData()
|
||||
data.full_text = t2
|
||||
data.border = black
|
||||
return data
|
||||
|
||||
|
||||
proc getTides*() {.gcsafe.}=
|
||||
var mytides = TideList()
|
||||
var last_data = "-1"
|
||||
while true:
|
||||
if len(mytides.Tides) == 0 or mytides.LastUpdated < now() - initTimeInterval(hours = 1):
|
||||
mytides.Tides = getTideData()
|
||||
mytides.LastUpdated = now()
|
||||
let data = getDesign(sortTides(mytides.Tides))
|
||||
if $data != last_data:
|
||||
let args: seq[string] = @[]
|
||||
outputJSON(data,args)
|
||||
last_data = $data
|
||||
if stoploop:
|
||||
break
|
||||
sleep(10000)
|
||||
|
||||
proc main() =
|
||||
getTides()
|
||||
|
||||
if isMainModule:
|
||||
main()
|
|
@ -1,27 +0,0 @@
|
|||
import i3bar_base
|
||||
import i3bar_brightness
|
||||
import std/threadpool
|
||||
import std/osproc
|
||||
|
||||
proc await_click_info() =
|
||||
while true:
|
||||
let input = parseInput()
|
||||
case input.button:
|
||||
of 1,4:
|
||||
let state = execCmd("xbacklight -inc 5")
|
||||
get_brightness(true)
|
||||
of 3,5:
|
||||
let state = execCmd("xbacklight -dec 5")
|
||||
get_brightness(true)
|
||||
else:
|
||||
let no = false
|
||||
|
||||
clearInput(2)
|
||||
|
||||
proc main() =
|
||||
spawn get_brightness()
|
||||
spawn await_click_info()
|
||||
sync()
|
||||
|
||||
if isMainModule:
|
||||
main()
|
|
@ -1,18 +0,0 @@
|
|||
import std/threadpool
|
||||
import i3bar_date
|
||||
import i3bar_base
|
||||
|
||||
proc await_click_info() =
|
||||
while true:
|
||||
let input = parseInput()
|
||||
if input.button == 1:
|
||||
openCalendar(input)
|
||||
|
||||
proc main() =
|
||||
spawn getDate()
|
||||
spawn await_click_info()
|
||||
sync()
|
||||
|
||||
|
||||
if isMainModule:
|
||||
main()
|
|
@ -1,25 +0,0 @@
|
|||
import i3bar_base
|
||||
import i3bar_nic
|
||||
import std/os
|
||||
import std/osproc
|
||||
import std/threadpool
|
||||
import strutils
|
||||
|
||||
proc await_click_info() =
|
||||
while true:
|
||||
let input = parseInput()
|
||||
if input.button == 1:
|
||||
discard execCmd("alacritty -e nmtui-connect")
|
||||
|
||||
|
||||
proc main() =
|
||||
let mynic = get_nic()
|
||||
if dirExists("/sys/class/net/" & mynic):
|
||||
spawn get_net_info(mynic)
|
||||
spawn await_click_info()
|
||||
sync()
|
||||
else:
|
||||
echo "No NIC"
|
||||
|
||||
if isMainModule:
|
||||
main()
|
|
@ -1,21 +0,0 @@
|
|||
import i3bar_tides
|
||||
import i3bar_base
|
||||
import std/re
|
||||
import std/threadpool
|
||||
import std/osproc
|
||||
|
||||
proc await_click_info() =
|
||||
while true:
|
||||
let input = parseInput()
|
||||
case input.button:
|
||||
of 1:
|
||||
let state = execCmd("xdg-open " & replace(url,re"\%LOC",loc))
|
||||
else:
|
||||
let no = false
|
||||
|
||||
proc main() =
|
||||
spawn getTides()
|
||||
spawn await_click_info()
|
||||
sync()
|
||||
|
||||
main()
|
|
@ -1,34 +0,0 @@
|
|||
import i3bar_base
|
||||
import i3bar_volume
|
||||
import std/threadpool
|
||||
import std/osproc
|
||||
|
||||
proc await_click_info() =
|
||||
while true:
|
||||
let input = parseInput()
|
||||
case input.button:
|
||||
of 4:
|
||||
discard execCmd("pamixer -i 5")
|
||||
get_volume(true)
|
||||
clearInput(2)
|
||||
of 5:
|
||||
discard execCmd("pamixer -d 5")
|
||||
get_volume(true)
|
||||
clearInput(2)
|
||||
of 1:
|
||||
discard execCmd("pamixer -t")
|
||||
get_volume(true)
|
||||
of 3:
|
||||
discard execCmd("alacritty -e ncpamixer")
|
||||
get_volume(true)
|
||||
else:
|
||||
let no = false
|
||||
|
||||
|
||||
proc main() =
|
||||
spawn get_volume()
|
||||
spawn await_click_info()
|
||||
sync()
|
||||
|
||||
if isMainModule:
|
||||
main()
|
|
@ -1,22 +0,0 @@
|
|||
import i3bar_base
|
||||
import i3bar_wlan
|
||||
import std/[os,osproc]
|
||||
import std/threadpool
|
||||
|
||||
|
||||
proc await_click_info() =
|
||||
while true:
|
||||
let input = parseInput()
|
||||
if input.button == 1:
|
||||
discard execCmd("alacritty -e nmtui-connect")
|
||||
|
||||
proc main() =
|
||||
if dirExists("/sys/class/net/" & wlan_nic):
|
||||
spawn get_wifi_info()
|
||||
spawn await_click_info()
|
||||
sync()
|
||||
else:
|
||||
echo "No WLAN"
|
||||
|
||||
if isMainModule:
|
||||
main()
|
5
install.sh
Executable file
5
install.sh
Executable file
|
@ -0,0 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
nimble install -y
|
||||
mkdir -p "$HOME/.local/bin"
|
||||
cp -v "wmtools" "$HOME/.local/bin/wmtools"
|
66
nic.nim
66
nic.nim
|
@ -1,66 +0,0 @@
|
|||
import base
|
||||
import std/os
|
||||
import std/osproc
|
||||
import strutils
|
||||
|
||||
const nics: seq[string] = @["wlan0", "enp3s0","wlp2s0","enp0s20f0u3"]
|
||||
|
||||
proc get_ip(nic: string): string =
|
||||
let cmd = "ifconfig " & nic & " | grep inet | awk -F\" \" '{print $2}' | head -1 | awk '{print $1}'"
|
||||
let ip = execCmdEx(cmd)
|
||||
return strip(ip.output)
|
||||
|
||||
proc get_online_state(nic: string): string =
|
||||
let oper = readFile("/sys/class/net/" & nic & "/operstate")
|
||||
let state = strip(oper)
|
||||
return "[" & state & "]"
|
||||
|
||||
proc get_net(nic: string): (string, string) =
|
||||
let state = get_online_state(nic)
|
||||
let ip = get_ip(nic)
|
||||
if state == "[down]" or ip == "":
|
||||
return ("disconnected", state)
|
||||
return (ip, state)
|
||||
|
||||
proc getObject(conn: string, nic: string): Info =
|
||||
var data = newInfo()
|
||||
data.title = "IP :"
|
||||
data.full_text = conn
|
||||
data.border = purple
|
||||
data.selected_background = purple
|
||||
data.selected_color = black
|
||||
return data
|
||||
|
||||
proc get_net_info*(nic: string) =
|
||||
var last_ip = ""
|
||||
var last_state = ""
|
||||
while true:
|
||||
let (ip, state) = get_net(nic)
|
||||
if ip != last_ip or state != last_state:
|
||||
let data = getObject(state & " " & ip, nic)
|
||||
let args = @["nmtui-connect"]
|
||||
let option = outputJSON(data, args)
|
||||
case option:
|
||||
of "nmtui-connect":
|
||||
discard execCmd("alacritty -e nmtui-connect")
|
||||
last_ip = ip
|
||||
last_state = state
|
||||
if stoploop:
|
||||
break
|
||||
sleep(1000)
|
||||
|
||||
|
||||
proc get_nic*(): string =
|
||||
for nic in nics:
|
||||
if dirExists("/sys/class/net/" & nic):
|
||||
return nic
|
||||
return "no-nic"
|
||||
|
||||
proc main() =
|
||||
let mynic = get_nic()
|
||||
if dirExists("/sys/class/net/" & mynic):
|
||||
get_net_info(mynic)
|
||||
else:
|
||||
echo "No NIC"
|
||||
|
||||
main()
|
1
nim.cfg
Normal file
1
nim.cfg
Normal file
|
@ -0,0 +1 @@
|
|||
-d:ssl
|
|
@ -1,68 +0,0 @@
|
|||
import std/os
|
||||
import std/osproc
|
||||
import std/re
|
||||
import strutils
|
||||
import base
|
||||
|
||||
const host: string = "web.wilde.cloud"
|
||||
const cmd: string = "ping -4 -c 1 " & host
|
||||
const time_secs: int = 4
|
||||
|
||||
let ping_re = re(r"time=[0-9.]+")
|
||||
|
||||
proc get_ping(): float =
|
||||
var ping: float = -1
|
||||
let cmdOut = execCmdEx(cmd)
|
||||
let lines = splitLines(cmdOut.output)
|
||||
let ping_line = lines[1]
|
||||
let bounds = findBounds(ping_line, ping_re)
|
||||
if bounds.first > 0:
|
||||
let png = ping_line[bounds.first+5..bounds.last]
|
||||
ping = parseFloat(png)
|
||||
return ping
|
||||
|
||||
proc getObject(ping: float): Info =
|
||||
let pingstr = split($ping,".")
|
||||
let niceping = pingstr[0] & "." & pingstr[1][0]
|
||||
var text = "🏓 " & niceping & " ms"
|
||||
var col = foreground
|
||||
if ping < 0:
|
||||
text = "❌ No Pong"
|
||||
col = yellow
|
||||
else:
|
||||
case ping:
|
||||
of 0..100:
|
||||
col = foreground
|
||||
of 101..400:
|
||||
col = yellow
|
||||
of 401..1000:
|
||||
col = alert
|
||||
else:
|
||||
col = red
|
||||
|
||||
var data = newInfo()
|
||||
data.title = "Ping Clock:"
|
||||
data.full_text = text
|
||||
data.color = col
|
||||
data.border = blue
|
||||
data.background = black
|
||||
data.selected_background = blue
|
||||
data.selected_color = white
|
||||
return data
|
||||
|
||||
|
||||
proc main() =
|
||||
var last_ping: float = 0
|
||||
while loop:
|
||||
let ping = get_ping()
|
||||
if ping != last_ping:
|
||||
let data = getObject(ping)
|
||||
outputJSON(data)
|
||||
last_ping = ping
|
||||
loop = not stoploop
|
||||
if loop:
|
||||
sleep(time_secs * 1000)
|
||||
|
||||
|
||||
if isMainModule:
|
||||
main()
|
10
src/common.nim
Normal file
10
src/common.nim
Normal file
|
@ -0,0 +1,10 @@
|
|||
import model/config
|
||||
import model/tool
|
||||
import model/info
|
||||
|
||||
export config
|
||||
export tool
|
||||
export info
|
||||
|
||||
var myConfig* = newConfig()
|
||||
|
22
src/common/colours.nim
Normal file
22
src/common/colours.nim
Normal file
|
@ -0,0 +1,22 @@
|
|||
|
||||
const background* = "#000000"
|
||||
const backgroundalt* = "#bb222222"
|
||||
const backgroundalt2* = "#bb333333"
|
||||
const foreground* = "#dfdfdf"
|
||||
const foregroundalt* = "#777"
|
||||
const foregroundalt2* = "#ccc"
|
||||
const black* = "#000000"
|
||||
const white* = "#FFFFFF"
|
||||
const yellow* = "#ffb52a"
|
||||
const red* = "#e60053"
|
||||
const purple* = "#9f78e1"
|
||||
const blue* = "#0a6cf5"
|
||||
const lightblue* = "#7296EF"
|
||||
const lighterblue* = "#B5DDF7"
|
||||
const green* = "#4b9901"
|
||||
const lightgreen* = "#00ff00"
|
||||
const grey* = "#dfdfdf"
|
||||
const darkgrey* = "#444"
|
||||
const primary* = yellow
|
||||
const secondary* = red
|
||||
const alert* = "#bd2c40"
|
9
src/common/display.nim
Normal file
9
src/common/display.nim
Normal file
|
@ -0,0 +1,9 @@
|
|||
import std/envvars
|
||||
|
||||
const XDG_SESSION_TYPE = "XDG_SESSION_TYPE"
|
||||
const WAYLAND = "wayland"
|
||||
|
||||
proc isWayland*(): bool =
|
||||
if existsEnv(XDG_SESSION_TYPE) and getEnv(XDG_SESSION_TYPE) == WAYLAND:
|
||||
return true
|
||||
return false
|
14
src/common/http.nim
Normal file
14
src/common/http.nim
Normal file
|
@ -0,0 +1,14 @@
|
|||
import httpclient
|
||||
|
||||
proc download*(link: string): string =
|
||||
var client = newHttpClient(timeout = 10000)
|
||||
try:
|
||||
let resp = client.get(link)
|
||||
if resp.status == $Http200:
|
||||
return resp.body
|
||||
except:
|
||||
echo getCurrentExceptionMsg()
|
||||
return ""
|
||||
|
||||
proc save*(file: string, content: string) =
|
||||
writeFile(file,content)
|
58
src/dispatcher.nim
Normal file
58
src/dispatcher.nim
Normal file
|
@ -0,0 +1,58 @@
|
|||
|
||||
import common
|
||||
import util/furrytime
|
||||
import util/pingclock
|
||||
import util/batturry
|
||||
import util/volurrme
|
||||
import util/netwurrk
|
||||
import util/wirelurrs
|
||||
import util/emurrji
|
||||
import util/calendurr
|
||||
import util/remminurr
|
||||
import util/passwurrd
|
||||
import util/pw_generaturr
|
||||
import util/temperaturr
|
||||
import util/screenshurrt
|
||||
import util/calculaturr
|
||||
import util/brightnurrs
|
||||
import util/tideurrl
|
||||
import util/wallpapurr
|
||||
|
||||
proc dispatch*(cfg: Config) =
|
||||
case cfg.run
|
||||
of FurryTime:
|
||||
furrytime.go()
|
||||
of PingClock:
|
||||
pingclock.go()
|
||||
of Batturry:
|
||||
batturry.go()
|
||||
of Volurrme:
|
||||
volurrme.go()
|
||||
of Netwurrk:
|
||||
netwurrk.go()
|
||||
of Wirelurrs:
|
||||
wirelurrs.go()
|
||||
of Emurrji:
|
||||
emurrji.go()
|
||||
of Calendurr:
|
||||
calendurr.go()
|
||||
of Remminurr:
|
||||
remminurr.go()
|
||||
of Passwurrd:
|
||||
passwurrd.go()
|
||||
of PasswurrdGeneraturr:
|
||||
pw_generaturr.go()
|
||||
of Temperaturr:
|
||||
temperaturr.go()
|
||||
of Screenshurrt:
|
||||
screenshurrt.go()
|
||||
of Calculaturr:
|
||||
calculaturr.go()
|
||||
of Brightnurrs:
|
||||
brightnurrs.go()
|
||||
of Tideurrl:
|
||||
tideurrl.go()
|
||||
of Wallpapurr:
|
||||
wallpapurr.go()
|
||||
else:
|
||||
echo "No valid run command given"
|
33
src/lib/emurrjilist.nim
Normal file
33
src/lib/emurrjilist.nim
Normal file
|
@ -0,0 +1,33 @@
|
|||
import jsony
|
||||
|
||||
type
|
||||
Emoji* = object
|
||||
emoji*: string
|
||||
name*: string
|
||||
group*: string
|
||||
subgroup*: string
|
||||
|
||||
proc `$`(e: Emoji): string =
|
||||
return e.emoji & " : " & e.name & " : " & e.group & " : " & e.subgroup
|
||||
|
||||
const include_groups = ["Smileys & Emotion"]
|
||||
#, "People & Body"]
|
||||
#, "Animals & Nature"]
|
||||
#, "Food & Drink"]
|
||||
#, "Travel & Places", "Activities", "Objects"]
|
||||
const ignore_subgroups = ["religion"]
|
||||
|
||||
proc getEmojis(): seq[string] =
|
||||
let file_emojis = staticRead("../../emojis.json")
|
||||
let emojis = file_emojis.fromJson(seq[Emoji])
|
||||
var list: seq[string] = @[]
|
||||
for e in emojis:
|
||||
if e.group in include_groups and e.subgroup notin ignore_subgroups:
|
||||
list.add($e)
|
||||
return list
|
||||
|
||||
const emoji_list = getEmojis()
|
||||
|
||||
proc getEmoji*(): seq[string] =
|
||||
return emoji_list
|
||||
|
55
src/lib/refresh_emoji.nim
Normal file
55
src/lib/refresh_emoji.nim
Normal file
|
@ -0,0 +1,55 @@
|
|||
import jsony
|
||||
import strutils
|
||||
import re
|
||||
import httpclient
|
||||
import emurrjilist
|
||||
|
||||
var emojis: seq[Emoji] = @[]
|
||||
let e_re = re"E[0-9\.]+"
|
||||
const group_line = "# group: "
|
||||
const subgroup_line = "# subgroup: "
|
||||
|
||||
proc parse(body: string) =
|
||||
var current_group = ""
|
||||
var current_subgroup = ""
|
||||
for line in body.split("\n"):
|
||||
if line.startsWith(group_line):
|
||||
let g = line.replace(group_line, "")
|
||||
if current_group != g:
|
||||
current_group = g
|
||||
continue
|
||||
if line.startsWith(subgroup_line):
|
||||
let sub_g = line.replace(subgroup_line, "")
|
||||
if current_subgroup != sub_g:
|
||||
current_subgroup = sub_g
|
||||
continue
|
||||
if line == "" or (line.len > 0 and line[0] == '#'):
|
||||
continue
|
||||
try:
|
||||
let parts = line.split("#")
|
||||
let emo = parts[1].split(e_re)
|
||||
var emoji = Emoji()
|
||||
emoji.emoji = emo[0].strip(chars={' '})
|
||||
emoji.name = emo[1].strip()
|
||||
emoji.group = current_group
|
||||
emoji.subgroup = current_subgroup
|
||||
emojis.add(emoji)
|
||||
except:
|
||||
echo getCurrentExceptionMsg()
|
||||
|
||||
proc getUnicodeOrgEmoji() =
|
||||
const url = "https://unicode.org/Public/emoji/latest/emoji-test.txt"
|
||||
var client = newHttpClient()
|
||||
let page = client.get(url)
|
||||
parse(page.body)
|
||||
|
||||
proc save(emojis: seq[Emoji]) =
|
||||
let file = "emojis.json"
|
||||
writeFile(file, emojis.toJson())
|
||||
|
||||
proc getEmojis() =
|
||||
getUnicodeOrgEmoji()
|
||||
save(emojis)
|
||||
|
||||
when isMainModule:
|
||||
getEmojis()
|
6
src/model/brightness.nim
Normal file
6
src/model/brightness.nim
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
type
|
||||
BrightnessArg* = enum
|
||||
None,
|
||||
BrightUp,
|
||||
BrightDown
|
57
src/model/config.nim
Normal file
57
src/model/config.nim
Normal file
|
@ -0,0 +1,57 @@
|
|||
import os
|
||||
import strutils
|
||||
import parsetoml
|
||||
|
||||
import tool
|
||||
import screenshot
|
||||
|
||||
type
|
||||
Config* = ref object
|
||||
exec*: string
|
||||
run*: Tool
|
||||
max_lines*: int
|
||||
prepend*: bool
|
||||
to_stdout*: bool
|
||||
screenshot_tool*: ScreenshotTool
|
||||
unsplash_key*: string
|
||||
bg_dir*: string
|
||||
|
||||
let config_dir* = getHomeDir() & ".config/wm_tools/"
|
||||
let config_file* = config_dir & "config.toml"
|
||||
|
||||
proc `$`(c: Config): string =
|
||||
var str = "exec = \"" & c.exec & "\"\n"
|
||||
str &= "prepend = " & $c.prepend & "\n"
|
||||
str &= "screenshot_tool = \"" & $c.screenshot_tool & "\"\n"
|
||||
str &= "max_lines = " & $c.max_lines
|
||||
str &= "\n"
|
||||
return str
|
||||
|
||||
proc newConfig*(): Config =
|
||||
var cfg = Config()
|
||||
cfg.exec = "rofi -dmenu"
|
||||
cfg.prepend = true
|
||||
cfg.screenshot_tool = Maim
|
||||
cfg.max_lines = 20
|
||||
|
||||
discard existsOrCreateDir(config_dir)
|
||||
if not fileExists(config_file):
|
||||
writeFile(config_file,$cfg)
|
||||
else:
|
||||
let content = readFile(config_file)
|
||||
try:
|
||||
let toml = parseString(content)
|
||||
if toml.hasKey("exec"):
|
||||
cfg.exec = toml["exec"].getStr
|
||||
if toml.hasKey("max_lines"):
|
||||
cfg.max_lines = toml["max_lines"].getInt
|
||||
if toml.hasKey("screenshot_tool"):
|
||||
cfg.screenshot_tool = toml["screenshot_tool"].getStr.toScreenshotTool
|
||||
if toml.hasKey("unsplash_key"):
|
||||
cfg.unsplash_key = toml["unsplash_key"].getStr
|
||||
if toml.hasKey("bg_dir"):
|
||||
cfg.bg_dir = toml["bg_dir"].getStr.replace("$HOME",getHomeDir())
|
||||
except:
|
||||
echo "Error with Config File:"
|
||||
echo getCurrentExceptionMsg()
|
||||
return cfg
|
11
src/model/info.nim
Normal file
11
src/model/info.nim
Normal file
|
@ -0,0 +1,11 @@
|
|||
type
|
||||
Info* = object
|
||||
title*: string
|
||||
full_text*: string
|
||||
html_text*: string
|
||||
short_text*: string
|
||||
args*: seq[string]
|
||||
|
||||
proc newInfo*(str: string = "Info"): Info =
|
||||
var title = str
|
||||
return Info(title: title)
|
14
src/model/pwgen.nim
Normal file
14
src/model/pwgen.nim
Normal file
|
@ -0,0 +1,14 @@
|
|||
|
||||
type
|
||||
PWGen* = object
|
||||
to_terminal*: bool
|
||||
word_len*: int
|
||||
digits*: int
|
||||
number*: int
|
||||
|
||||
proc newPWGen*(): PWGen =
|
||||
var pw = PWGen()
|
||||
pw.word_len = 5
|
||||
pw.digits = 4
|
||||
pw.number = 10
|
||||
return pw
|
81
src/model/screenshot.nim
Normal file
81
src/model/screenshot.nim
Normal file
|
@ -0,0 +1,81 @@
|
|||
|
||||
import sequtils
|
||||
import strutils
|
||||
|
||||
type
|
||||
Screenshot* = object
|
||||
size*: ScreenshotSize
|
||||
tool*: ScreenshotTool
|
||||
ScreenshotSize* = enum
|
||||
None = ""
|
||||
Region = "region",
|
||||
Full = "fullscreen",
|
||||
Window = "window"
|
||||
ScreenshotTool* = enum
|
||||
None = ""
|
||||
Maim = "maim"
|
||||
Grim = "grim"
|
||||
|
||||
proc newScreenshot*(): Screenshot =
|
||||
var ss = Screenshot()
|
||||
ss.tool = Maim
|
||||
ss.size = None
|
||||
return ss
|
||||
|
||||
proc toScreenshotSize*(str: string): ScreenshotSize =
|
||||
case str
|
||||
of "region": return Region
|
||||
of "fullscreen": return Full
|
||||
of "window": return Window
|
||||
else: return None
|
||||
|
||||
proc isScreenshotSize*(str: string): bool =
|
||||
return str.toScreenshotSize != None
|
||||
|
||||
proc ScreenshotSizes*(): seq[string] =
|
||||
var sizes: seq[string] = @[]
|
||||
for item in ScreenshotSize.toSeq:
|
||||
if item != None:
|
||||
sizes.add($item)
|
||||
return sizes
|
||||
|
||||
proc toScreenshotTool*(str: string): ScreenshotTool =
|
||||
case str
|
||||
of "maim": return Maim
|
||||
of "grim": return Grim
|
||||
else: return None
|
||||
|
||||
proc isScreenshotTool*(str: string): bool =
|
||||
return str.toScreenshotTool != None
|
||||
|
||||
proc command*(tool: ScreenshotTool): string =
|
||||
case tool
|
||||
of Maim: return "maim -u %s --format png %f"
|
||||
of Grim: return "grim %s %f"
|
||||
else: return ""
|
||||
|
||||
proc activeWindowCommand*(tool: ScreenshotTool): string =
|
||||
var cmd = tool.command()
|
||||
# where %s is an extra flag or process, i.e. xdotool for getting active window
|
||||
case tool
|
||||
of Maim:
|
||||
cmd = cmd.replace("%s","-i $(xdotool getactivewindow)")
|
||||
of Grim:
|
||||
echo "Not currently Implemented"
|
||||
quit(1)
|
||||
else: return cmd
|
||||
return cmd
|
||||
|
||||
proc regionCommand*(tool: ScreenshotTool): string =
|
||||
var cmd = tool.command()
|
||||
case tool
|
||||
of Maim:
|
||||
cmd = cmd.replace("%s","-s")
|
||||
of Grim:
|
||||
cmd = cmd.replace("%s","-g \"$(slurp)\"")
|
||||
else: return cmd
|
||||
return cmd
|
||||
|
||||
const X_CLIPBOARD_CMD* = "xclip -selection clipboard -t image/png"
|
||||
const WL_CLIPBOARD_CMD* = "wl-copy"
|
||||
|
22
src/model/tides.nim
Normal file
22
src/model/tides.nim
Normal file
|
@ -0,0 +1,22 @@
|
|||
import times
|
||||
|
||||
const TIDE_URL* = "https://www.tidetimes.org.uk/%LOC-tide-times"
|
||||
const DEFAULT_LOC* = "exmouth-dock"
|
||||
|
||||
type
|
||||
Tide* = ref object
|
||||
state*: string
|
||||
time*: string
|
||||
height*: string
|
||||
tomorrow*: bool
|
||||
TideList* = ref object
|
||||
tides*: seq[Tide]
|
||||
url*: string
|
||||
last_updated*: DateTime
|
||||
location*: string
|
||||
|
||||
proc newTideList*(): TideList =
|
||||
var tl = TideList()
|
||||
tl.url = TIDE_URL
|
||||
tl.location = DEFAULT_LOC
|
||||
return tl
|
21
src/model/tool.nim
Normal file
21
src/model/tool.nim
Normal file
|
@ -0,0 +1,21 @@
|
|||
|
||||
type
|
||||
Tool* = enum
|
||||
None,
|
||||
FurryTime,
|
||||
PingClock,
|
||||
Batturry,
|
||||
Volurrme,
|
||||
Netwurrk,
|
||||
Wirelurrs,
|
||||
Emurrji,
|
||||
Calendurr,
|
||||
Remminurr,
|
||||
Passwurrd,
|
||||
PasswurrdGeneraturr,
|
||||
Temperaturr,
|
||||
Screenshurrt,
|
||||
Calculaturr,
|
||||
Brightnurrs,
|
||||
Tideurrl,
|
||||
Wallpapurr
|
4
src/model/volume.nim
Normal file
4
src/model/volume.nim
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
type
|
||||
VolArg* = enum
|
||||
None, VolUp, VolDown, VolMute
|
6
src/model/wallpapurr.nim
Normal file
6
src/model/wallpapurr.nim
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
type
|
||||
WPArgs* = object
|
||||
query*: string
|
||||
last*: bool
|
||||
from_unsplash*: bool
|
38
src/notify.nim
Normal file
38
src/notify.nim
Normal file
|
@ -0,0 +1,38 @@
|
|||
import strutils
|
||||
import osproc
|
||||
|
||||
type
|
||||
Note* = object
|
||||
urgency*: Urgency
|
||||
title*: string
|
||||
content*: string
|
||||
timeout*: int
|
||||
Urgency* = enum
|
||||
Normal = "normal"
|
||||
Low = "low"
|
||||
Urgent = "urgent"
|
||||
Critical = "critical"
|
||||
|
||||
proc newNote*(): Note =
|
||||
return Note(urgency: Normal, title: "Notification", content: "Hello, I am a notifications", timeout: 2000)
|
||||
|
||||
proc `$`*(n: Note): string =
|
||||
let str = "notify-send -u $U $T $C -t $N"
|
||||
.replace("$U", $n.urgency)
|
||||
.replace("$T", n.title.escape)
|
||||
.replace("$C", n.content.escape)
|
||||
.replace("$N", $n.timeout)
|
||||
return str
|
||||
|
||||
proc send*(n: Note) =
|
||||
discard execCmdEx($n)
|
||||
|
||||
proc send*(s: varargs[string,`$`]) =
|
||||
var n = newNote()
|
||||
n.title = s[0]
|
||||
if s.len > 1:
|
||||
n.content = s[1..^1].join("")
|
||||
else:
|
||||
n.content = ""
|
||||
send(n)
|
||||
|
76
src/output.nim
Normal file
76
src/output.nim
Normal file
|
@ -0,0 +1,76 @@
|
|||
import osproc
|
||||
import strutils
|
||||
|
||||
import common
|
||||
|
||||
proc stripQuotes(str: string): string =
|
||||
var text = replace(str,"\"",""")
|
||||
return text
|
||||
|
||||
proc quote*(str: string): string =
|
||||
var text = str
|
||||
# May need to put some further work to escape some special chars here
|
||||
text = stripQuotes(text)
|
||||
|
||||
# Put leading and ending quote marks in
|
||||
return " \"" & text & "\" "
|
||||
# ^ Add a spaces ^ so the previous flag isn't touching
|
||||
|
||||
proc markup(str: string): string =
|
||||
# Placeholder proc for future use
|
||||
var text = stripQuotes(str)
|
||||
return text
|
||||
|
||||
proc copyToClipboard*(str: string): bool {.discardable.} =
|
||||
let ok = execCmd("echo -n " & quote(str) & " | xclip -selection clipboard")
|
||||
return ok == 0
|
||||
|
||||
proc listify(data:Info, opts: varargs[string]): string =
|
||||
var text = data.full_text
|
||||
if opts.len > 0:
|
||||
text &= "\n"
|
||||
for opt in opts:
|
||||
text &= markup text
|
||||
text &= "\n"
|
||||
return text
|
||||
|
||||
proc genMenuCmd(data: Info, opts: varargs[string]): string =
|
||||
var cmd = ""
|
||||
# length of the list of opts, plus the full text
|
||||
var x_lines = len(opts) + 1
|
||||
# if the text is empty, we don't want to create a menu item of it
|
||||
if data.full_text != "":
|
||||
let text = markup data.full_text
|
||||
cmd &= text & "\n"
|
||||
else:
|
||||
x_lines -= 1
|
||||
for opt in opts:
|
||||
let text = markup opt
|
||||
cmd &= text & "\n"
|
||||
|
||||
cmd.removeSuffix("\n")
|
||||
|
||||
if x_lines > myConfig.max_lines: x_lines = myConfig.max_lines
|
||||
|
||||
if myConfig.prepend:
|
||||
cmd = "echo -e" & quote(cmd) & "| "
|
||||
cmd &= myConfig.exec
|
||||
cmd &= " -i" # set case insensitive
|
||||
cmd &= " -p" & quote(data.title)
|
||||
cmd &= "-l " & $x_lines
|
||||
echo "Sending command:\n" & cmd
|
||||
return cmd
|
||||
|
||||
proc runExec(data: Info, opts: varargs[string]): string =
|
||||
if not myConfig.to_stdout:
|
||||
let cmd = genMenuCmd(data, opts)
|
||||
var output = execCmdEx(cmd)
|
||||
echo "Output:\n" & $output
|
||||
output.output.stripLineEnd()
|
||||
return output.output
|
||||
else:
|
||||
stdout.writeLine listify(data,opts)
|
||||
|
||||
proc outputData*(data: Info, args: varargs[string,`$`]): string {.discardable.} =
|
||||
var output = runExec(data,args)
|
||||
return output
|
217
src/parser.nim
Normal file
217
src/parser.nim
Normal file
|
@ -0,0 +1,217 @@
|
|||
import os
|
||||
|
||||
import argparse
|
||||
|
||||
import common
|
||||
import common/display
|
||||
import model/pwgen
|
||||
import model/volume
|
||||
import model/brightness
|
||||
import model/screenshot
|
||||
import model/tides
|
||||
import model/wallpapurr
|
||||
|
||||
proc parseArgs*() =
|
||||
let params = commandLineParams()
|
||||
var p = newParser:
|
||||
help("WMTools : a set of tools to output option to your program of choice i.e. Rofi")
|
||||
arg("input",help="the tool to run, i.e. furrytime, pingclock, volurrme, etc.")
|
||||
arg("others", nargs = -1)
|
||||
flag("-o","--output",help="outputs to stdout instead of something else")
|
||||
try:
|
||||
var opts = p.parse(params)
|
||||
# TODO sort this but out, handle args for all modules, etc.
|
||||
myConfig.to_stdout = opts.output
|
||||
case opts.input
|
||||
of "furrytime", "fuzzytime", "time":
|
||||
myConfig.run = FurryTime
|
||||
of "pingclock", "pingclurrk", "ping":
|
||||
myConfig.run = PingClock
|
||||
of "batturry", "battery", "bat":
|
||||
myConfig.run = Batturry
|
||||
of "volurrme", "volume", "vol":
|
||||
myConfig.run = Volurrme
|
||||
of "netwurrk", "network", "net":
|
||||
myConfig.run = Netwurrk
|
||||
of "wirelurrs", "wireless", "wifi":
|
||||
myConfig.run = Wirelurrs
|
||||
of "emurrji", "emoji":
|
||||
myConfig.run = Emurrji
|
||||
of "calendurr", "calender", "cal":
|
||||
myConfig.run = Calendurr
|
||||
of "remminurr", "remmina", "rdp", "rem":
|
||||
myConfig.run = Remminurr
|
||||
of "passwurrd", "password", "pw":
|
||||
myConfig.run = Passwurrd
|
||||
of "passwurrdgeneraturr", "passwordgenerator", "pwgen":
|
||||
myConfig.run = PasswurrdGeneraturr
|
||||
of "temperaturr", "temperature", "temp":
|
||||
myConfig.run = Temperaturr
|
||||
of "screenshurrt", "screenshot", "screeny":
|
||||
myConfig.run = Screenshurrt
|
||||
of "calculaturr", "calculator", "calc":
|
||||
myConfig.run = Calculaturr
|
||||
of "brightnurrs", "brightness", "bright":
|
||||
myConfig.run = Brightnurrs
|
||||
of "tideurrl", "tides":
|
||||
myConfig.run = Tideurrl
|
||||
of "wallpapurr", "wallpaper":
|
||||
myConfig.run = Wallpapurr
|
||||
else:
|
||||
echo p.help
|
||||
quit(1)
|
||||
except ShortCircuit as err:
|
||||
if err.flag == "argparse_help":
|
||||
echo err.help
|
||||
quit(1)
|
||||
except UsageError:
|
||||
stderr.writeLine getCurrentExceptionMsg()
|
||||
quit(1)
|
||||
|
||||
proc parseVolArgs*(): VolArg =
|
||||
var arg: VolArg
|
||||
let params = commandLineParams()
|
||||
var p = newParser:
|
||||
help("Args for volurrme")
|
||||
arg("volurrme",help="can only ever be 'volurrme' as you won't have gotten this far otherwise")
|
||||
arg("adjust",help="up, down, or mute",default=some(""))
|
||||
try:
|
||||
var opts = p.parse(params)
|
||||
case opts.adjust
|
||||
of "volup", "up":
|
||||
arg = VolUp
|
||||
of "voldown", "down":
|
||||
arg = VolDown
|
||||
of "mute","volmute":
|
||||
arg = VolMute
|
||||
except ShortCircuit as err:
|
||||
if err.flag == "argparse_help":
|
||||
echo err.help
|
||||
quit(1)
|
||||
except UsageError:
|
||||
stderr.writeLine getCurrentExceptionMsg()
|
||||
quit(1)
|
||||
return arg
|
||||
|
||||
proc parseBrightnessArgs*(): BrightnessArg =
|
||||
var arg: BrightnessArg
|
||||
let params = commandLineParams()
|
||||
var p = newParser:
|
||||
help("Args for volurrme")
|
||||
arg("brightnurrs",help="can only ever be 'brightnurrs' as you won't have gotten this far otherwise")
|
||||
arg("adjust",help="up, down, or mute",default=some(""))
|
||||
try:
|
||||
var opts = p.parse(params)
|
||||
case opts.adjust
|
||||
of "up":
|
||||
arg = BrightUp
|
||||
of "down":
|
||||
arg = BrightDown
|
||||
except ShortCircuit as err:
|
||||
if err.flag == "argparse_help":
|
||||
echo err.help
|
||||
quit(1)
|
||||
except UsageError:
|
||||
stderr.writeLine getCurrentExceptionMsg()
|
||||
quit(1)
|
||||
return arg
|
||||
|
||||
proc parsePWGenArgs*(): PWGen =
|
||||
var gen = newPWGen()
|
||||
let params = commandLineParams()
|
||||
var p = newParser:
|
||||
help("Args for pw_generaturr")
|
||||
arg("pwgen",help="can only ever be 'pwgen' as you won't have gotten this far otherwise")
|
||||
flag("-o","--output",help="outputs to terminal instead of something else")
|
||||
option("-l","--length",help="Length of the word part of the password")
|
||||
option("-d","--digits",help="Length of the number part of the password",default=some(""))
|
||||
option("-n","--number",help="Number of passwords to return")
|
||||
try:
|
||||
var opts = p.parse(params)
|
||||
if opts.length != "":
|
||||
gen.word_len = parseInt(opts.length)
|
||||
if opts.digits != "":
|
||||
gen.digits = parseInt(opts.digits)
|
||||
if opts.number != "":
|
||||
gen.number = parseInt(opts.number)
|
||||
gen.to_terminal = not opts.output
|
||||
except ShortCircuit as err:
|
||||
if err.flag == "argparse_help":
|
||||
echo err.help
|
||||
quit(1)
|
||||
except UsageError:
|
||||
stderr.writeLine getCurrentExceptionMsg()
|
||||
quit(1)
|
||||
return gen
|
||||
|
||||
proc parseScreenshotArgs*(): Screenshot =
|
||||
var ss = newScreenshot()
|
||||
ss.tool = myConfig.screenshot_tool
|
||||
let params = commandLineParams()
|
||||
var p = newParser:
|
||||
help("Args for screenshurrt")
|
||||
arg("screenshurrt",help="can only ever be 'screenshurrt' as you won't have gotten this far otherwise")
|
||||
option("-s","--size",help="size/region i.e. region, fullscreen or window")
|
||||
option("-t","--tool",help="the tool to take the screenshot, i.e. maim or grim")
|
||||
try:
|
||||
var opts = p.parse(params)
|
||||
if opts.size != "":
|
||||
ss.size = opts.size.toScreenshotSize()
|
||||
if opts.tool != "":
|
||||
ss.tool = opts.tool.toScreenshotTool()
|
||||
elif isWayland():
|
||||
ss.tool = GRIM
|
||||
except ShortCircuit as err:
|
||||
if err.flag == "argparse_help":
|
||||
echo err.help
|
||||
quit(1)
|
||||
except UsageError:
|
||||
stderr.writeLine getCurrentExceptionMsg()
|
||||
quit(1)
|
||||
return ss
|
||||
|
||||
proc parseTideurrlArgs*(): TideList =
|
||||
var t = newTideList()
|
||||
let params = commandLineParams()
|
||||
var p = newParser:
|
||||
help("Args for tideurrl")
|
||||
arg("tideurrl",help="can only ever be 'tideurrl' as you won't have gotten this far otherwise")
|
||||
option("-l","--loc",help="location name")
|
||||
try:
|
||||
var opts = p.parse(params)
|
||||
if opts.loc != "":
|
||||
t.location = opts.loc
|
||||
except ShortCircuit as err:
|
||||
if err.flag == "argparse_help":
|
||||
echo err.help
|
||||
quit(1)
|
||||
except UsageError:
|
||||
stderr.writeLine getCurrentExceptionMsg()
|
||||
quit(1)
|
||||
return t
|
||||
|
||||
proc parseWallpapurrArgs*(): WPArgs =
|
||||
var args = WPArgs()
|
||||
let params = commandLineParams()
|
||||
var p = newParser:
|
||||
help("Args for wallpapurr")
|
||||
arg("wallpapurr",help="can only ever be 'wallpapurr' as you won't have gotten this far otherwise")
|
||||
option("-q","--query",help="query name")
|
||||
flag("-l","--last",help="last image")
|
||||
flag("-n","--unsplash",help="get from unsplash")
|
||||
try:
|
||||
var opts = p.parse(params)
|
||||
if opts.query != "":
|
||||
args.query = opts.query
|
||||
if opts.last:
|
||||
args.last = true
|
||||
if opts.unsplash:
|
||||
args.from_unsplash = true
|
||||
except ShortCircuit as err:
|
||||
if err.flag == "argparse_help":
|
||||
echo err.help
|
||||
quit(1)
|
||||
except UsageError:
|
||||
stderr.writeLine getCurrentExceptionMsg()
|
||||
quit(1)
|
||||
return args
|
31
src/util/.archived/alurrm/src/timurr.nim
Normal file
31
src/util/.archived/alurrm/src/timurr.nim
Normal file
|
@ -0,0 +1,31 @@
|
|||
import ../../globurrl
|
||||
import argparse
|
||||
|
||||
const DURATION_FMT="HH:mm:ss"
|
||||
|
||||
var parser = newParser:
|
||||
flag "-s", "--show", help="Show current timers"
|
||||
flag "-a", "--add", help="Add a new timer, requires -d,--duration"
|
||||
option "-d", "--duration", help="Duration in HH:mm:ss format", default=some("")
|
||||
|
||||
proc show_timurr() =
|
||||
echo "Hello"
|
||||
|
||||
when isMainModule:
|
||||
echo("This is still a work in progress")
|
||||
try:
|
||||
var opts = parser.parse()
|
||||
if opts.show:
|
||||
show_timurr()
|
||||
elif opts.add:
|
||||
if opts.duration != "":
|
||||
echo "hi"
|
||||
else:
|
||||
stderr.writeLine "Cannot add (-a) without durations (-d)"
|
||||
except ShortCircuit as e:
|
||||
if e.flag == "argparse_help":
|
||||
echo parser.help
|
||||
quit(1)
|
||||
except UsageError:
|
||||
stderr.writeLine getCurrentExceptionMsg()
|
||||
quit(1)
|
14
src/util/.archived/alurrm/timurr.nimble
Normal file
14
src/util/.archived/alurrm/timurr.nimble
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Package
|
||||
|
||||
version = "0.1.0"
|
||||
author = "Paul Wilde"
|
||||
description = "A timer/alarm tool"
|
||||
license = "GPL-3.0-or-later"
|
||||
srcDir = "src"
|
||||
bin = @["timurr"]
|
||||
|
||||
|
||||
# Dependencies
|
||||
|
||||
requires "nim >= 1.6.6"
|
||||
requires "argparse"
|
14
src/util/.archived/burrkmarks/burrkmarks.nimble
Normal file
14
src/util/.archived/burrkmarks/burrkmarks.nimble
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Package
|
||||
|
||||
version = "0.1.0"
|
||||
author = "Paul Wilde"
|
||||
description = "A dmenu/rofi bookmark manager"
|
||||
license = "GPL-2.0-or-later"
|
||||
srcDir = "src"
|
||||
bin = @["burrkmarks"]
|
||||
|
||||
|
||||
# Dependencies
|
||||
|
||||
requires "nim >= 1.6.6"
|
||||
requires "parsetoml >= 0.6.0"
|
1
src/util/.archived/burrkmarks/nim.cfg
Normal file
1
src/util/.archived/burrkmarks/nim.cfg
Normal file
|
@ -0,0 +1 @@
|
|||
-d:ssl
|
110
src/util/.archived/burrkmarks/src/burrkmarks.nim
Normal file
110
src/util/.archived/burrkmarks/src/burrkmarks.nim
Normal file
|
@ -0,0 +1,110 @@
|
|||
import std/[json,os,marshal,osproc,re]
|
||||
import std/[asyncdispatch, httpclient]
|
||||
import ../../globurrl
|
||||
|
||||
type
|
||||
Bookmark = object
|
||||
name: string
|
||||
shortname: string
|
||||
url: string
|
||||
|
||||
const TITLE = "Burrkmarks"
|
||||
let title_re = re("<title>(.*?)<\\/title>", {reMultiLine})
|
||||
let title_rem_re = re("<\\/?title>")
|
||||
let https_re = re("https?:\\/\\/")
|
||||
let bookmarks_file = getSyncDir() & "bookmarks.json"
|
||||
var bookmarks: seq[Bookmark] = @[]
|
||||
|
||||
proc `$`(bookmark: Bookmark): string =
|
||||
let show_name = if bookmark.shortname != "": bookmark.shortname else: bookmark.name
|
||||
let x = show_name & " - [" & bookmark.url & "]"
|
||||
return x
|
||||
|
||||
proc getTitle(bookmark: Bookmark): Future[string] {.async.} =
|
||||
var client = newAsyncHttpClient()
|
||||
try:
|
||||
let html = await client.getContent(bookmark.url)
|
||||
let titles = html.findAll(title_re)
|
||||
if len(titles) > 0:
|
||||
var title = titles[0]
|
||||
title = title.replace(title_rem_re,"")
|
||||
return title
|
||||
except:
|
||||
echo getCurrentExceptionMsg()
|
||||
return ""
|
||||
|
||||
proc save(bookmarks: seq[Bookmark]): bool {.discardable.} =
|
||||
let data = pretty(%*bookmarks)
|
||||
writeFile(bookmarks_file, data)
|
||||
|
||||
proc get(bookmarks: seq[Bookmark], str: string): Bookmark =
|
||||
for bookmark in bookmarks:
|
||||
if str == $bookmark:
|
||||
return bookmark
|
||||
return Bookmark()
|
||||
|
||||
proc checkFile(): bool =
|
||||
if not fileExists(bookmarks_file):
|
||||
writeFile(bookmarks_file,"")
|
||||
if fileExists(bookmarks_file):
|
||||
return true
|
||||
return false
|
||||
|
||||
proc getBookmarks(): seq[Bookmark] =
|
||||
let f = open(bookmarks_file)
|
||||
try:
|
||||
let nodes = parseJson(f.readAll())
|
||||
for node in nodes:
|
||||
var bookmark = Bookmark()
|
||||
bookmark.name = node.getOrDefault("name").getStr()
|
||||
bookmark.shortname = node.getOrDefault("shortname").getStr()
|
||||
bookmark.url = node.getOrDefault("url").getStr()
|
||||
bookmarks.add(bookmark)
|
||||
except:
|
||||
echo getCurrentExceptionMsg()
|
||||
return bookmarks
|
||||
|
||||
proc addBookmark(link: string) =
|
||||
var url = link
|
||||
var bookmark = Bookmark()
|
||||
if not url.contains(https_re):
|
||||
url = "https://" & url
|
||||
bookmark.url = url
|
||||
bookmark.name = waitFor bookmark.getTitle()
|
||||
bookmarks.add(bookmark)
|
||||
bookmarks.save()
|
||||
|
||||
proc goToBookmark(bookmark: string) =
|
||||
let bm = bookmarks.get(bookmark)
|
||||
discard execCmd("xdg-open " & bm.url)
|
||||
|
||||
proc toStrList(bookmarks: seq[Bookmark]): seq[string] =
|
||||
var list: seq[string] = @[]
|
||||
for bookmark in bookmarks:
|
||||
list.add($bookmark)
|
||||
return list
|
||||
|
||||
proc showBookmarks() =
|
||||
let info = newInfo(TITLE)
|
||||
let args = bookmarks.toStrList()
|
||||
let option = outputData(info, args)
|
||||
if option == "":
|
||||
echo "Empty input, closing..."
|
||||
return
|
||||
elif option notin args:
|
||||
echo "Adding bookmark: ", option
|
||||
addBookmark(option)
|
||||
showBookmarks()
|
||||
elif option in args:
|
||||
echo "Opening bookmark: ", option
|
||||
goToBookmark(option)
|
||||
|
||||
proc start() =
|
||||
if checkfile():
|
||||
bookmarks = getBookmarks()
|
||||
showBookmarks()
|
||||
else:
|
||||
echo "File : ", bookmarks_file, " does not exist."
|
||||
|
||||
when isMainModule:
|
||||
start()
|
13
src/util/.archived/noteurr/noteurr.nimble
Normal file
13
src/util/.archived/noteurr/noteurr.nimble
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Package
|
||||
|
||||
version = "0.1.0"
|
||||
author = "Paul Wilde"
|
||||
description = "A notes app for dmenu"
|
||||
license = "GPL-3.0-or-later"
|
||||
srcDir = "src"
|
||||
bin = @["noteurr"]
|
||||
|
||||
|
||||
# Dependencies
|
||||
|
||||
requires "nim >= 1.6.6"
|
167
src/util/.archived/noteurr/src/noteurr.nim
Normal file
167
src/util/.archived/noteurr/src/noteurr.nim
Normal file
|
@ -0,0 +1,167 @@
|
|||
import ../../globurrl
|
||||
import std/[os,sequtils,times]
|
||||
|
||||
const note_dir = WM_TOOLS_DIR & ".notes.wm_tools/" # Putting it in Nextcloud so it can sync :-)
|
||||
const default_bg = white
|
||||
const default_fg = black
|
||||
|
||||
type
|
||||
Note = object
|
||||
id: string
|
||||
text: string
|
||||
|
||||
proc startNotes()
|
||||
|
||||
proc displayOptionMenu(option: string)
|
||||
|
||||
var notes: seq[Note] = @[]
|
||||
|
||||
proc readNotes(): seq[Note] =
|
||||
if len(notes) == 0:
|
||||
if existsOrCreateDir(note_dir):
|
||||
for note_file in walkDir(note_dir):
|
||||
try:
|
||||
var note = Note()
|
||||
note.id = note_file[1]
|
||||
note.text = stripQuotes(readFile(note_file[1]))
|
||||
notes.add(note)
|
||||
except:
|
||||
echo "Unable to read note : ", note_file, " : ", getCurrentExceptionMsg()
|
||||
return notes
|
||||
|
||||
proc removeNote(note: Note) =
|
||||
try:
|
||||
removeFile(note.id)
|
||||
for idx, a_note in notes:
|
||||
if note.id == a_note.id:
|
||||
notes.delete(idx)
|
||||
except:
|
||||
echo "Unable to delete file : ", note.id, " : ", getCurrentExceptionMsg()
|
||||
|
||||
proc getNoteStrings(): seq[string] =
|
||||
var note_list: seq[string] = @[]
|
||||
if len(notes) == 0:
|
||||
discard readNotes()
|
||||
for note in notes:
|
||||
note_list.add(note.text)
|
||||
return note_list
|
||||
|
||||
proc writeNote(note: Note, is_new: bool = false) =
|
||||
if note.text == "":
|
||||
return
|
||||
try:
|
||||
|
||||
writeFile(note.id, stripQuotes(note.text))
|
||||
if is_new:
|
||||
var new_note = note
|
||||
new_note.text = stripQuotes(new_note.text)
|
||||
notes.add(note)
|
||||
else:
|
||||
for idx, a_note in notes:
|
||||
if a_note.id == note.id:
|
||||
notes[idx].text = stripQuotes(note.text)
|
||||
except:
|
||||
echo "write_note, Unable to write note : ", note.id, " : ", getCurrentExceptionMsg()
|
||||
|
||||
proc getNotes(): (Info, seq[string]) =
|
||||
var info = newInfo("Noteurr")
|
||||
info.selected_bg = default_bg
|
||||
info.selected_fg = default_fg
|
||||
let notes = getNoteStrings()
|
||||
return (info,notes)
|
||||
|
||||
proc getNote(text: string): Note =
|
||||
for note in notes:
|
||||
if note.text == text:
|
||||
return note
|
||||
return Note()
|
||||
|
||||
proc displayDeleteConfirmationMenu(note: string) =
|
||||
var yes_no = newInfo("Confirm Delete note : " & note)
|
||||
yes_no.selected_bg = default_bg
|
||||
yes_no.selected_fg = default_fg
|
||||
let args = @["yes", "no"]
|
||||
let choice = outputData(yes_no, args)
|
||||
case choice:
|
||||
of "yes":
|
||||
let rm_note = getNote(note)
|
||||
if rm_note.text == note:
|
||||
removeNote(rm_note)
|
||||
of "no":
|
||||
displayOptionMenu(note)
|
||||
|
||||
proc replaceNoteConfirmationMenu(note_idx: int, new_text: string): bool =
|
||||
let old_note = notes[note_idx]
|
||||
var diag = newInfo("Replace Note : '" & old_note.text & "', with '" & new_text & "' ?")
|
||||
diag.selected_bg = default_bg
|
||||
diag.selected_fg = default_fg
|
||||
let args = @["yes", "no"]
|
||||
let choice = outputData(diag,args)
|
||||
case choice:
|
||||
of "yes":
|
||||
echo "here"
|
||||
return true
|
||||
else:
|
||||
return false
|
||||
|
||||
proc replaceNote(new_text: string, old_text: string) =
|
||||
for idx, note in notes:
|
||||
if note.text == old_text:
|
||||
if replaceNoteConfirmationMenu(idx,new_text):
|
||||
var new_note = note
|
||||
new_note.text = new_text
|
||||
notes[idx] = new_note
|
||||
writeNote(new_note)
|
||||
return
|
||||
|
||||
proc displayOptionMenu(option: string) =
|
||||
var select = newInfo("Noteurr")
|
||||
select.selected_bg = default_bg
|
||||
select.selected_fg = default_fg
|
||||
let note_choices = @["rm","back"]
|
||||
let args = concat(@[option],note_choices)
|
||||
let chosen = outputData(select,args)
|
||||
if chosen in note_choices:
|
||||
case chosen:
|
||||
of "rm":
|
||||
displayDeleteConfirmationMenu(option)
|
||||
startNotes()
|
||||
of "back":
|
||||
startNotes()
|
||||
elif chosen != "" and chosen != option:
|
||||
replaceNote(chosen, option)
|
||||
displayOptionMenu(chosen)
|
||||
else:
|
||||
displayOptionMenu(option)
|
||||
|
||||
proc newNote(text: string) =
|
||||
var new_note = Note()
|
||||
let note_id = times.now().format("'{'yyyyMMdd-HHmmss-ffffff'}'")
|
||||
new_note.id = note_dir & note_id
|
||||
new_note.text = text
|
||||
writeNote(new_note, is_new = true)
|
||||
|
||||
proc startNotes() =
|
||||
let (info,all_notes) = getNotes()
|
||||
let all_choices = @["exit"]
|
||||
let args = concat(all_notes, all_choices)
|
||||
let option = outputData(info, args)
|
||||
if option in all_choices:
|
||||
case option:
|
||||
of "exit":
|
||||
return
|
||||
if option in all_notes:
|
||||
displayOptionMenu(option)
|
||||
elif option != "":
|
||||
newNote(option)
|
||||
startNotes()
|
||||
|
||||
proc main() =
|
||||
echo "Note dir : ", note_dir
|
||||
if tool != "dmenu" and tool != "rofi":
|
||||
echo "Can only be run in dmenu or rofi mode. Exiting..."
|
||||
return
|
||||
startNotes()
|
||||
|
||||
if isMainModule:
|
||||
main()
|
1
src/util/.archived/translaturr/nim.cfg
Normal file
1
src/util/.archived/translaturr/nim.cfg
Normal file
|
@ -0,0 +1 @@
|
|||
-d:ssl
|
126
src/util/.archived/translaturr/src/translaturr.nim
Normal file
126
src/util/.archived/translaturr/src/translaturr.nim
Normal file
|
@ -0,0 +1,126 @@
|
|||
import ../../globurrl
|
||||
import std/[re,httpclient,json,strutils,tables]
|
||||
|
||||
|
||||
# TODO:
|
||||
# Query available languages, maybe have selection panel
|
||||
# Maybe store last used so future translations are more fluent
|
||||
|
||||
const LIBRETRANSLATE_URL = "https://libretranslate.pussthecat.org/"
|
||||
const HOME = "en"
|
||||
let LANG_RE = re"\w+>\w+"
|
||||
let DETECT_RE = re"\bd(etect)?\b"
|
||||
var current = @["de",HOME]
|
||||
var detected = {"detected":"0","confidence":"0","language":"en"}.toTable
|
||||
|
||||
type
|
||||
Request = object
|
||||
source: string
|
||||
target: string
|
||||
q: string
|
||||
format: string
|
||||
|
||||
proc main(messages: varargs[string] = @[])
|
||||
|
||||
proc newRequest(): Request =
|
||||
return Request(source:current[0],
|
||||
target:current[1],
|
||||
q: "",
|
||||
format: "text",)
|
||||
|
||||
proc parseReq(req: string): Request =
|
||||
var langs = findAll(req,LANG_RE)
|
||||
var r = newRequest()
|
||||
let query = replace(req,LANG_RE,"")
|
||||
r.q = query
|
||||
if len(langs) > 0:
|
||||
let lang = langs[0]
|
||||
langs = lang.split(re">")
|
||||
current[0] = langs[0].toLowerAscii()
|
||||
current[1] = langs[1].toLowerAscii()
|
||||
r.source = current[0].toLowerAscii()
|
||||
r.target = current[1].toLowerAscii()
|
||||
if query == "":
|
||||
main()
|
||||
return r
|
||||
|
||||
proc answerTranslate(response: string, req: Request) =
|
||||
let r = parseJson(response)
|
||||
try:
|
||||
let ans = r["translatedText"].getStr()
|
||||
main(ans)
|
||||
except:
|
||||
main("Error : " & r["error"].getStr())
|
||||
|
||||
proc goTranslate(req: string) =
|
||||
let r = parseReq(req)
|
||||
if r.q != "":
|
||||
var client = newHTTPClient()
|
||||
client.headers = newHttpHeaders({"Content-Type":"application/json"})
|
||||
let body = %*r
|
||||
let response = client.request(LIBRETRANSLATE_URL & "translate", httpMethod = HttpPost, body = $body)
|
||||
if response.status != "":
|
||||
answerTranslate(response.body, r)
|
||||
|
||||
proc flipCurrent() =
|
||||
current = @[current[1],current[0]]
|
||||
main()
|
||||
|
||||
proc detectLanguage(str: string) =
|
||||
let term = replace(str,DETECT_RE,"")
|
||||
echo "detecting ", term
|
||||
if term != "":
|
||||
var client = newHTTPClient()
|
||||
client.headers = newHttpHeaders({"Content-Type":"application/json"})
|
||||
let body = %*Request(q:term)
|
||||
let response = client.request(LIBRETRANSLATE_URL & "detect", httpMethod = HttpPost, body = $body)
|
||||
if response.status != "":
|
||||
let r = parseJson(response.body)
|
||||
echo r
|
||||
try:
|
||||
detected["confidence"] = $r[0]["confidence"].getFloat()
|
||||
detected["language"] = r[0]["language"].getStr()
|
||||
detected["detected"] = "1"
|
||||
current[0] = detected["language"]
|
||||
current[1] = HOME
|
||||
goTranslate(term)
|
||||
except:
|
||||
try:
|
||||
main("Error : " & r["error"].getStr())
|
||||
except:
|
||||
main("Error Parsing Json")
|
||||
return
|
||||
|
||||
proc main(messages:varargs[string] = @[]) =
|
||||
var info = newInfo("Translaturr")
|
||||
info.selected_bg = green
|
||||
var args: seq[string] = @[]
|
||||
for msg in messages:
|
||||
if msg != "":
|
||||
args.add(msg)
|
||||
args.add(current[0] & " > " & current[1])
|
||||
if detected["detected"] == "1":
|
||||
args.add("detected language : " & detected["language"])
|
||||
args.add("detected confidence : " & detected["confidence"])
|
||||
detected["detected"] = "0"
|
||||
if len(messages) > 0:
|
||||
args.add("back")
|
||||
args.add("exit")
|
||||
let output = outputData(info,args)
|
||||
if output == "exit" or output == "":
|
||||
return
|
||||
elif output == "back":
|
||||
main()
|
||||
elif output == current[0] & " > " & current[1]:
|
||||
flipCurrent()
|
||||
elif re.startsWith(output,DETECT_RE):
|
||||
detectLanguage(output)
|
||||
elif output in messages:
|
||||
copyToClipboard(output)
|
||||
else:
|
||||
goTranslate(output)
|
||||
return
|
||||
|
||||
if isMainModule:
|
||||
main()
|
||||
|
13
src/util/.archived/translaturr/translaturr.nimble
Normal file
13
src/util/.archived/translaturr/translaturr.nimble
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Package
|
||||
|
||||
version = "0.1.0"
|
||||
author = "Paul Wilde"
|
||||
description = "Query libretranslate with dmenu"
|
||||
license = "GPL-3.0-or-later"
|
||||
srcDir = "src"
|
||||
bin = @["translaturr"]
|
||||
|
||||
|
||||
# Dependencies
|
||||
|
||||
requires "nim >= 1.6.6"
|
108
src/util/batturry.nim
Normal file
108
src/util/batturry.nim
Normal file
|
@ -0,0 +1,108 @@
|
|||
import strutils
|
||||
|
||||
import ../common
|
||||
import ../common/colours
|
||||
import ../output
|
||||
|
||||
const battery = "BAT0"
|
||||
const ok_fg = lightgreen
|
||||
const default_fg = white
|
||||
const default_bg = black
|
||||
const warning_fg = black
|
||||
const warning_bg = red
|
||||
const low_bg = black
|
||||
const low_fg = red
|
||||
const alert_fg = black
|
||||
const alert_bg = yellow
|
||||
const med_fg = green
|
||||
#const med_bg = black
|
||||
|
||||
|
||||
proc batteryExists(): bool =
|
||||
try:
|
||||
let state = strip(readFile("/sys/class/power_supply/" & battery & "/present"))
|
||||
if state == "1":
|
||||
return true
|
||||
except:
|
||||
echo "Error getting battery : " & getCurrentExceptionMsg()
|
||||
return false
|
||||
|
||||
proc isCharging(): bool =
|
||||
try:
|
||||
let state = strip(readFile("/sys/class/power_supply/" & battery & "/status"))
|
||||
if state == "Charging":
|
||||
return true
|
||||
except:
|
||||
echo "Error getting charging status : " & getCurrentExceptionMsg()
|
||||
return false
|
||||
|
||||
proc getCharge(): int =
|
||||
var charge = 0
|
||||
try:
|
||||
let chg = strip(readFile("/sys/class/power_supply/" & battery & "/capacity"))
|
||||
if chg != "":
|
||||
charge = parseInt(chg)
|
||||
except:
|
||||
echo "Error getting battery level : " & getCurrentExceptionMsg()
|
||||
return charge
|
||||
|
||||
proc getDesign(charge: int, state: bool): string =
|
||||
var icon = " "
|
||||
var icon_colour = ok_fg
|
||||
var col = default_fg
|
||||
var bg = default_bg
|
||||
var border = ok_fg
|
||||
if isCharging():
|
||||
icon = " "
|
||||
else:
|
||||
case charge:
|
||||
of 0..5:
|
||||
icon_colour = warning_fg
|
||||
col = warning_fg
|
||||
bg = warning_bg
|
||||
of 6..19:
|
||||
icon_colour = low_fg
|
||||
border = low_bg
|
||||
bg = default_bg
|
||||
of 20..39:
|
||||
icon_colour = alert_fg
|
||||
border = alert_bg
|
||||
icon = " "
|
||||
of 40..59:
|
||||
icon_colour = med_fg
|
||||
border= med_fg
|
||||
icon = " "
|
||||
of 60..79:
|
||||
icon_colour = med_fg
|
||||
border = med_fg
|
||||
icon = " "
|
||||
of 80..100:
|
||||
icon_colour = ok_fg
|
||||
border = ok_fg
|
||||
icon = " "
|
||||
else:
|
||||
icon = "x "
|
||||
|
||||
let main_text = icon & " " & $charge & "%"
|
||||
|
||||
return main_text
|
||||
|
||||
proc getOutput(charge: int, state: bool): Info =
|
||||
let main_text = get_design(charge, state)
|
||||
var data = newInfo("Batturry")
|
||||
# TODO check if html text works with rofi
|
||||
data.full_text = main_text
|
||||
return data
|
||||
|
||||
|
||||
proc getBatteryInfo() =
|
||||
let charge = getCharge()
|
||||
let state = isCharging()
|
||||
let data = getoutput(charge, state)
|
||||
outputData(data)
|
||||
|
||||
proc go*() =
|
||||
if batteryExists():
|
||||
getBatteryInfo()
|
||||
|
||||
|
104
src/util/brightnurrs.nim
Normal file
104
src/util/brightnurrs.nim
Normal file
|
@ -0,0 +1,104 @@
|
|||
import os
|
||||
import strutils
|
||||
import osproc
|
||||
import math
|
||||
import tables
|
||||
|
||||
import ../common
|
||||
import ../parser
|
||||
import ../model/brightness
|
||||
import ../output
|
||||
|
||||
const UP: Table[string,string] = {"linux": "xbacklight -inc %v", "freebsd": "backlight incr %v"}.toTable # %v is amount by
|
||||
const DOWN: Table[string,string] = {"linux": "xbacklight -dec %v", "freebsd": "backlight decr %v"}.toTable() # %v is amount by
|
||||
const SET: Table[string,string] = {"linux": "xbacklight -set %v", "freebsd": "backlight %v"}.toTable() # %v is amount by
|
||||
const default_value = "5"
|
||||
|
||||
proc getBacklight(): string =
|
||||
if hostOS == "freebsd": return ""
|
||||
for dir in walkDir("/sys/class/backlight"):
|
||||
echo dir.path
|
||||
var bl = dir.path.replace("/sys/class/backlight/","")
|
||||
echo bl
|
||||
return bl
|
||||
|
||||
proc getLimit(backlight: string): int =
|
||||
try:
|
||||
if backlight != "":
|
||||
let back_l = readFile("/sys/class/backlight/" & backlight & "/max_brightness")
|
||||
return parseInt(strip(back_l))
|
||||
except:
|
||||
echo "Error getting backlight max : ", getCurrentExceptionMsg()
|
||||
return -1
|
||||
|
||||
|
||||
proc getDesign(pcnt: float): string =
|
||||
var icon = "🌑"
|
||||
case pcnt:
|
||||
of 85..100:
|
||||
icon = "🌕"
|
||||
of 75..85:
|
||||
icon = "🌖"
|
||||
of 50..75:
|
||||
icon = "🌗"
|
||||
of 35..50:
|
||||
icon = "🌘"
|
||||
else:
|
||||
icon = "🌑"
|
||||
let percent = toInt(round(pcnt,0))
|
||||
let text = icon & " " & $percent & "%"
|
||||
return text
|
||||
|
||||
proc brightnessUp() =
|
||||
let cmd = replace(UP[hostOS],"%v",default_value)
|
||||
discard execCmd(cmd)
|
||||
proc brightnessDown() =
|
||||
let cmd = replace(DOWN[hostOS],"%v",default_value)
|
||||
discard execCmd(cmd)
|
||||
|
||||
proc getBrightness*(backlight: string) =
|
||||
var data = newInfo("Brightnurrs")
|
||||
if hostOS == "freebsd":
|
||||
let bl = execCmdEx("backlight")
|
||||
let pcnt = bl.output.replace("brightness: ","").strip.parseFloat
|
||||
let text = getDesign(pcnt)
|
||||
data.full_text = text
|
||||
elif backlight == "":
|
||||
data.full_text = "No Backlight Found"
|
||||
discard outputData(data)
|
||||
quit(1)
|
||||
else:
|
||||
let limit = getLimit(backlight)
|
||||
let current = parseInt(strip(readFile("/sys/class/backlight/" & backlight & "/actual_brightness")))
|
||||
let pcnt = (current/limit)*100
|
||||
let text = getDesign(pcnt)
|
||||
data.full_text = text
|
||||
let args = @["up", "down"]
|
||||
let option = outputData(data,args)
|
||||
if option in args:
|
||||
case option:
|
||||
of "up":
|
||||
brightnessUp()
|
||||
backlight.getBrightness()
|
||||
of "down":
|
||||
brightnessDown()
|
||||
backlight.getBrightness()
|
||||
else:
|
||||
try:
|
||||
let i = parseInt(option)
|
||||
let cmd = replace(SET[hostOS],"%v",$i)
|
||||
discard execCmd(cmd)
|
||||
backlight.getBrightness()
|
||||
except:
|
||||
echo getCurrentExceptionMsg()
|
||||
|
||||
proc go*() =
|
||||
let backlight = getBacklight()
|
||||
let barg = parseBrightnessArgs()
|
||||
case barg:
|
||||
of BrightUp:
|
||||
brightnessUp()
|
||||
of BrightDown:
|
||||
brightnessDown()
|
||||
else:
|
||||
backlight.getBrightness()
|
38
src/util/calculaturr.nim
Normal file
38
src/util/calculaturr.nim
Normal file
|
@ -0,0 +1,38 @@
|
|||
import osproc
|
||||
import strutils
|
||||
import sequtils
|
||||
|
||||
import ../common
|
||||
import ../output
|
||||
|
||||
# using qalc as it has a lot of nice inbuilt features
|
||||
# may be nice to make it totally non-dependant on other things though
|
||||
|
||||
const exitSeq = @["---","exit"]
|
||||
proc doCalculation(calc: string) =
|
||||
var info = newInfo("Calculaturr")
|
||||
let ans_cmd_terse = "echo \"" & calc & "\" | qalc +u8 -terse -color=never | awk '!/^>/ && !/^$/ {gsub(/^[ \\t]+|[ \\t]+$/, \"\", $0); print}'"
|
||||
let ans_cmd_full = "echo \"" & calc & "\" | qalc +u8 -color=never | awk '!/^>/ && !/^$/ {gsub(/^[ \\t]+|[ \\t]+$/, \"\", $0); print}'"
|
||||
var ans_full = execCmdEx(ans_cmd_full)
|
||||
ans_full.output.stripLineEnd()
|
||||
var ans_terse = execCmdEx(ans_cmd_terse)
|
||||
ans_terse.output.stripLineEnd()
|
||||
let answers = @[strip(ans_full.output),
|
||||
strip(ans_terse.output)]
|
||||
let args = concat(answers,exitSeq)
|
||||
var cmd = outputData(info, args)
|
||||
if cmd in answers:
|
||||
copyToClipboard(cmd)
|
||||
elif cmd in exitSeq or cmd == "":
|
||||
return
|
||||
else:
|
||||
doCalculation(cmd)
|
||||
|
||||
proc go*() =
|
||||
var info = newInfo("Calculaturr")
|
||||
let args = @["exit"]
|
||||
let cmd = outputData(info, args)
|
||||
if cmd != "":
|
||||
doCalculation(cmd)
|
||||
return
|
||||
|
42
src/util/calendurr.nim
Normal file
42
src/util/calendurr.nim
Normal file
|
@ -0,0 +1,42 @@
|
|||
import times
|
||||
import osproc
|
||||
import re
|
||||
|
||||
import ../common
|
||||
import ../output
|
||||
|
||||
const default_format = "yyyy-MM-dd"
|
||||
const cal_pos_x = "20"
|
||||
const cal_pos_y = "20"
|
||||
|
||||
proc getObject(date: string): Info =
|
||||
var data = newInfo("Calendurr")
|
||||
data.full_text = date
|
||||
return data
|
||||
|
||||
proc newCalendar(): string =
|
||||
var c = """yad --calendar \
|
||||
--undecorated --fixed --close-on-unfocus --no-buttons \
|
||||
--width="222" --height="193" \
|
||||
--posx="%pos_x" --posy="%pos_y" \
|
||||
--title="yad-calendar" --borders 0 > /dev/null
|
||||
"""
|
||||
return c
|
||||
|
||||
proc openCalendar() =
|
||||
var c = newCalendar()
|
||||
c = replace(c,re"%pos_x", cal_pos_x)
|
||||
c = replace(c,re"%pos_y", cal_pos_y)
|
||||
discard execCmd(c)
|
||||
|
||||
proc getDate*() =
|
||||
let date_today = times.now().format(default_format)
|
||||
let data = getObject(date_today)
|
||||
let output = outputData(data)
|
||||
if output == date_today:
|
||||
openCalendar()
|
||||
|
||||
proc go*() =
|
||||
getDate()
|
||||
|
||||
|
20
src/util/emurrji.nim
Normal file
20
src/util/emurrji.nim
Normal file
|
@ -0,0 +1,20 @@
|
|||
import ../lib/emurrjilist
|
||||
import ../common
|
||||
import ../output
|
||||
|
||||
import re
|
||||
|
||||
proc go*() =
|
||||
var info = newInfo("Emurrji")
|
||||
var args = getEmoji()
|
||||
args.add("exit")
|
||||
let output = outputData(info,args)
|
||||
if output == "exit" or output == "":
|
||||
return
|
||||
else:
|
||||
let e = re.findAll(output,re(".+ :"))
|
||||
if len(e) > 0:
|
||||
let emoji = re.replace(e[0], re(" :"),"")
|
||||
copyToClipboard(emoji)
|
||||
return
|
||||
|
|
@ -1,28 +1,9 @@
|
|||
import std/times
|
||||
import std/os
|
||||
import base
|
||||
import times
|
||||
|
||||
proc get_hour(hr: int): string
|
||||
proc get_minute(min: int): string
|
||||
import ../model/info
|
||||
import ../output
|
||||
|
||||
proc get_fuzzytime(): string =
|
||||
let tm = now()
|
||||
var hr = tm.hour()
|
||||
let min = tm.minute()
|
||||
var link = "past"
|
||||
if min > 32 :
|
||||
link = "to"
|
||||
case hr:
|
||||
of 23:
|
||||
hr = 0
|
||||
else:
|
||||
hr = hr + 1
|
||||
if min >= 58 or min <= 02:
|
||||
return get_hour(hr) & " " & get_minute(min)
|
||||
else:
|
||||
return get_minute(min) & " " & link & " " & get_hour(hr)
|
||||
|
||||
proc get_hour(hr: int): string =
|
||||
proc getHour(hr: int): string =
|
||||
case hr:
|
||||
of 1, 13:
|
||||
return "one"
|
||||
|
@ -51,10 +32,10 @@ proc get_hour(hr: int): string =
|
|||
else:
|
||||
return "error"
|
||||
|
||||
proc get_minute(min: int): string =
|
||||
proc getMinute(min: int): string =
|
||||
case min:
|
||||
of 58,59,0,1,2:
|
||||
return "o'clock"
|
||||
return "oclock"
|
||||
of 3,4,5,6,7,53,54,55,56,57:
|
||||
return "five"
|
||||
of 8,9,10,11,12,48,49,50,51,52:
|
||||
|
@ -70,29 +51,41 @@ proc get_minute(min: int): string =
|
|||
else:
|
||||
return "error"
|
||||
|
||||
proc getFuzzyTime(): string =
|
||||
let tm = now()
|
||||
var hr = tm.hour()
|
||||
let min = tm.minute()
|
||||
var link = "past"
|
||||
if min > 32 :
|
||||
link = "to"
|
||||
case hr:
|
||||
of 23:
|
||||
hr = 0
|
||||
else:
|
||||
hr = hr + 1
|
||||
if min >= 58 or min <= 02:
|
||||
return getHour(hr) & " " & getMinute(min)
|
||||
else:
|
||||
return getMinute(min) & " " & link & " " & getHour(hr)
|
||||
|
||||
proc getObject(time: string): Info =
|
||||
var data = Info()
|
||||
data.title = "Fuzzy Time :"
|
||||
var data = newInfo("Furry Time")
|
||||
data.full_text = time
|
||||
data.color = foreground
|
||||
data.border = lightblue
|
||||
data.selected_background = lightblue
|
||||
data.selected_color = black
|
||||
return data
|
||||
|
||||
proc show(time: string, next_fuzzy: bool = false) =
|
||||
let data = getObject(time)
|
||||
let x = outputData(data)
|
||||
if x == time:
|
||||
case next_fuzzy:
|
||||
of true:
|
||||
let t = getFuzzyTime()
|
||||
show(t, false)
|
||||
else:
|
||||
let t = now().format("HH:mm:ss")
|
||||
show(t, true)
|
||||
|
||||
proc main() =
|
||||
var last_time = ""
|
||||
while true:
|
||||
let time = get_fuzzytime()
|
||||
if time != last_time:
|
||||
let data = getObject(time)
|
||||
outputJSON(data)
|
||||
if stoploop:
|
||||
break
|
||||
last_time = time
|
||||
proc go*() =
|
||||
let time = getFuzzyTime()
|
||||
show(time)
|
||||
|
||||
sleep(2000)
|
||||
|
||||
if isMainModule:
|
||||
main()
|
62
src/util/netwurrk.nim
Normal file
62
src/util/netwurrk.nim
Normal file
|
@ -0,0 +1,62 @@
|
|||
import os
|
||||
import osproc
|
||||
import strutils
|
||||
import sequtils
|
||||
|
||||
import ../common
|
||||
import ../output
|
||||
|
||||
const mng_cmd = "alacritty -e nmtui-connect"
|
||||
|
||||
proc getIP(nic: string): string =
|
||||
let cmd = "ifconfig " & nic & " | grep inet | awk -F\" \" '{print $2}' | head -1 | awk '{print $1}'"
|
||||
let ip = execCmdEx(cmd)
|
||||
return strip(ip.output)
|
||||
|
||||
proc getOnlineState(nic: string): string =
|
||||
try:
|
||||
let oper = readFile("/sys/class/net/" & nic & "/operstate")
|
||||
let state = strip(oper)
|
||||
return "[" & state & "]"
|
||||
except:
|
||||
echo "Error getting operstate for " & nic & " : ", getCurrentExceptionMsg()
|
||||
return ""
|
||||
|
||||
proc getConnState(nic: string): (string, string) =
|
||||
let state = getOnlineState(nic)
|
||||
let ip = getIP(nic)
|
||||
if state == "[down]" or ip == "":
|
||||
return ("disconnected", state)
|
||||
return (ip, state)
|
||||
|
||||
proc getObject(): Info =
|
||||
var data = newInfo("Netwurrk")
|
||||
return data
|
||||
|
||||
proc getNetInfo*(my_nics: seq[string]) =
|
||||
var my_nic_states: seq[string] = @[]
|
||||
for nic in my_nics:
|
||||
let (ip, state) = getConnState(nic)
|
||||
my_nic_states.add(nic & ":" & state & " " & ip)
|
||||
let data = getObject()
|
||||
let args = concat(my_nic_states,@["manage"])
|
||||
let option = outputData(data, args)
|
||||
if option in my_nic_states:
|
||||
discard execCmd(mng_cmd)
|
||||
case option:
|
||||
of "manage":
|
||||
discard execCmd(mng_cmd)
|
||||
|
||||
proc getNics*(): seq[string] =
|
||||
var my_nics: seq[string] = @[]
|
||||
for nic in os.walkDir("/sys/class/net/"):
|
||||
let n = replace(nic.path, "/sys/class/net/", "")
|
||||
my_nics.add(n)
|
||||
|
||||
if len(my_nics) > 0:
|
||||
return my_nics
|
||||
return @["no-nic"]
|
||||
|
||||
proc go*() =
|
||||
getNetInfo(getNics())
|
||||
|
33
src/util/passwurrd.nim
Normal file
33
src/util/passwurrd.nim
Normal file
|
@ -0,0 +1,33 @@
|
|||
import os
|
||||
import osproc
|
||||
import re
|
||||
import strutils
|
||||
|
||||
import ../common
|
||||
import ../output
|
||||
|
||||
const pw_store = getHomeDir() & ".password-store/"
|
||||
var passwords: seq[string] = @[]
|
||||
let gpg_re = re("(" & pw_store & "|\\.gpg)")
|
||||
|
||||
proc parseFiles(path: string) =
|
||||
for file in walkDir(path):
|
||||
if file.path.endsWith(".gpg"):
|
||||
let pw = replace(file.path, gpg_re,"")
|
||||
passwords.add(pw)
|
||||
elif file.kind == pcDir:
|
||||
parseFiles(file.path)
|
||||
|
||||
proc getPasswords(): seq[string] =
|
||||
parseFiles(pw_store)
|
||||
return passwords
|
||||
|
||||
proc passwordToClipboard(password: string) =
|
||||
discard execCmd("pass show -c " & password)
|
||||
|
||||
proc go*() =
|
||||
var info = newInfo("Passwurrd")
|
||||
var pws = getPasswords()
|
||||
let output = outputData(info,pws)
|
||||
if output in passwords:
|
||||
passwordToClipboard(output)
|
55
src/util/pingclock.nim
Normal file
55
src/util/pingclock.nim
Normal file
|
@ -0,0 +1,55 @@
|
|||
import osproc
|
||||
import re
|
||||
import strutils
|
||||
|
||||
import ../common
|
||||
import ../output
|
||||
|
||||
const host: string = "9.9.9.9"
|
||||
|
||||
let ping_re = re(r"time=[0-9.]+")
|
||||
const ping_cmd: string = "ping -4 -c 1 " & host
|
||||
|
||||
proc getPing(): float =
|
||||
var ping: float = -1
|
||||
let cmdOut = execCmdEx(ping_cmd)
|
||||
let lines = splitLines(cmdOut.output)
|
||||
let ping_line = lines[1]
|
||||
let bounds = findBounds(ping_line, ping_re)
|
||||
if bounds.first > 0:
|
||||
let png = ping_line[bounds.first+5..bounds.last]
|
||||
ping = parseFloat(png)
|
||||
return ping
|
||||
|
||||
proc getObject(ping: float): Info =
|
||||
let pingstr = split($ping,".")
|
||||
let niceping = pingstr[0] & "." & pingstr[1][0]
|
||||
var text = "🏓 " & niceping & " ms"
|
||||
var state = 0
|
||||
if ping < 0:
|
||||
text = "❌ No Pong"
|
||||
state = 1
|
||||
else:
|
||||
case ping:
|
||||
of 0..100:
|
||||
state = 0
|
||||
of 101..400:
|
||||
state = 1
|
||||
of 401..1000:
|
||||
state = 2
|
||||
else:
|
||||
state = 9
|
||||
|
||||
var data = newInfo("Ping Clurrk")
|
||||
data.full_text = text
|
||||
# i3bar stuff
|
||||
return data
|
||||
|
||||
|
||||
proc go*() =
|
||||
let ping = get_ping()
|
||||
let data = getObject(ping)
|
||||
let output = outputData(data)
|
||||
if output == data.full_text:
|
||||
go()
|
||||
|
65
src/util/pw_generaturr.nim
Normal file
65
src/util/pw_generaturr.nim
Normal file
|
@ -0,0 +1,65 @@
|
|||
|
||||
import httpclient
|
||||
import json
|
||||
import strutils
|
||||
import random
|
||||
|
||||
import ../parser
|
||||
import ../model/pwgen
|
||||
import ../common
|
||||
import ../output
|
||||
|
||||
var passwords: seq[string]
|
||||
proc getNumber(size: int = 4): string =
|
||||
var num = ""
|
||||
for _ in countup(1,size):
|
||||
randomize()
|
||||
let roll = rand(0..9)
|
||||
num &= $roll
|
||||
return num
|
||||
|
||||
proc parsePasswords(body: string, digits: int) =
|
||||
passwords = @[]
|
||||
let j = body.parseJson
|
||||
for pass in j.getElems:
|
||||
var p = pass.getStr.capitalizeAscii
|
||||
p &= getNumber(digits)
|
||||
passwords.add(p)
|
||||
|
||||
proc getPasswords(pwgen: PWGen) =
|
||||
var c = newHttpClient()
|
||||
try:
|
||||
let url = "https://random-word-api.herokuapp.com/word?number=" & $pwgen.number & "&length=" & $pwgen.word_len
|
||||
let resp = c.get(url)
|
||||
if resp.status == $Http200:
|
||||
parsePasswords(resp.body, pwgen.digits)
|
||||
except:
|
||||
stderr.writeLine getCurrentExceptionMsg()
|
||||
|
||||
proc getOutput(): Info =
|
||||
var data = newInfo("PW Generaturr")
|
||||
data.full_text = "Refresh"
|
||||
return data
|
||||
|
||||
proc goOutput(args: PWGen) =
|
||||
let data = getoutput()
|
||||
let selected = outputData(data,passwords)
|
||||
if selected in passwords:
|
||||
copyToClipboard(selected)
|
||||
elif selected == "Refresh":
|
||||
getPasswords(args)
|
||||
goOutput(args)
|
||||
|
||||
proc go*() =
|
||||
echo "Getting passwords..."
|
||||
let args = parsePWGenArgs()
|
||||
getPasswords(args)
|
||||
if args.to_terminal:
|
||||
for pw in passwords:
|
||||
echo pw
|
||||
else:
|
||||
echo "Outputting to app"
|
||||
goOutput(args)
|
||||
|
||||
if isMainModule:
|
||||
go()
|
68
src/util/remminurr.nim
Normal file
68
src/util/remminurr.nim
Normal file
|
@ -0,0 +1,68 @@
|
|||
import os
|
||||
import osproc
|
||||
import tables
|
||||
import algorithm
|
||||
|
||||
import configparser
|
||||
|
||||
import ../common
|
||||
import ../output
|
||||
|
||||
const REMMINA_DIR = getHomeDir() & ".local/share/remmina"
|
||||
|
||||
var sessions = initTable[string,string]()
|
||||
var names: seq[string] = @[]
|
||||
|
||||
proc getRemminaFiles(): seq[string] =
|
||||
if len(names) < 1:
|
||||
for file in walkFiles(REMMINA_DIR & "/*.remmina"):
|
||||
let content = readFile(file)
|
||||
let ini = parseIni(content)
|
||||
let group = ini.getProperty("remmina","group")
|
||||
let name = ini.getProperty("remmina","name")
|
||||
let server = ini.getProperty("remmina","server")
|
||||
if name != "" and server != "":
|
||||
let slug = group & " : " & name & " : (" & server & ")"
|
||||
sessions[slug] = file
|
||||
names.add(slug)
|
||||
names.sort()
|
||||
return names
|
||||
|
||||
proc editRemmina(conn: string) =
|
||||
let session = sessions[conn]
|
||||
discard execCmd("remmina -e " & quote(session))
|
||||
|
||||
proc startRemmina(conn: string) =
|
||||
let session = sessions[conn]
|
||||
discard execCmd("remmina -c " & quote(session))
|
||||
|
||||
proc go*() # adding as need to refer back to it in selectRemmina
|
||||
|
||||
proc selectRemmina(conn: string) =
|
||||
var info = newInfo("Remmina Choosurr : " & conn)
|
||||
let args = @["connect", "edit", "back"]
|
||||
let output = outputData(info,args)
|
||||
if output in args:
|
||||
case output:
|
||||
of "connect":
|
||||
startRemmina(conn)
|
||||
#switchWorkspace()
|
||||
of "edit":
|
||||
editRemmina(conn)
|
||||
#switchWorkspace()
|
||||
of "back":
|
||||
go()
|
||||
|
||||
proc go*() =
|
||||
var info = newInfo("Remmina Choosurr")
|
||||
var args: seq[string] = getRemminaFiles()
|
||||
args.add("new")
|
||||
args.add("exit")
|
||||
let output = outputData(info,args)
|
||||
if output == "exit" or output == "":
|
||||
return
|
||||
elif output == "new":
|
||||
discard execCmd("remmina --new")
|
||||
elif output in names:
|
||||
selectRemmina(output)
|
||||
return
|
98
src/util/screenshurrt.nim
Normal file
98
src/util/screenshurrt.nim
Normal file
|
@ -0,0 +1,98 @@
|
|||
import times
|
||||
import os
|
||||
import osproc
|
||||
import strutils
|
||||
import sequtils
|
||||
|
||||
import ../common
|
||||
import ../common/display
|
||||
import ../output
|
||||
import ../parser
|
||||
import ../model/screenshot
|
||||
|
||||
const DATE_FORMAT = "yyyyMMdd-hhmmss"
|
||||
const FILENAME = "Screenshot-%d.png"
|
||||
const TEMP_DIR = "/tmp/"
|
||||
let DATE_STR = now().format(DATE_FORMAT)
|
||||
|
||||
let CLIPBOARD_CMD = if isWayland(): WL_CLIPBOARD_CMD else: X_CLIPBOARD_CMD
|
||||
|
||||
proc saveToClipboard(filename: string) =
|
||||
let cmd = "cat " & filename & " | " & CLIPBOARD_CMD
|
||||
let status = execCmd(cmd)
|
||||
if status == 0 and fileExists(filename):
|
||||
removeFile(filename)
|
||||
return
|
||||
|
||||
proc saveToFile(filename: string) =
|
||||
if fileExists(filename):
|
||||
let new_filename = filename.replace("/tmp/", getHomeDir() & "Screenshots/")
|
||||
copyFile(filename, new_filename)
|
||||
if fileExists(new_filename):
|
||||
removeFile(filename)
|
||||
return
|
||||
|
||||
proc openFile(filename: string) =
|
||||
let cmd = "xdg-open " & filename
|
||||
discard execCmd(cmd)
|
||||
return
|
||||
|
||||
proc showScreenshotSaveSel(filename: string) =
|
||||
let info = newInfo("Screenshurrt")
|
||||
let args = @["clipboard", "save", "open", "---", "exit"]
|
||||
let choice = outputData(info,args)
|
||||
if choice == "---":
|
||||
showScreenshotSaveSel(filename)
|
||||
elif choice == "exit":
|
||||
return
|
||||
elif choice in args:
|
||||
case choice:
|
||||
of "clipboard":
|
||||
saveToClipboard(filename)
|
||||
of "save":
|
||||
saveToFile(filename)
|
||||
of "open":
|
||||
openFile(filename)
|
||||
return
|
||||
|
||||
proc showScreenshotSizeSel(): ScreenshotSize =
|
||||
let info = newInfo("Screenshurrt type")
|
||||
let args = concat(ScreenshotSizes(),@["---","exit"])
|
||||
let choice = outputData(info,args)
|
||||
if choice.isScreenshotSize():
|
||||
return choice.toScreenshotSize()
|
||||
elif choice == "---":
|
||||
return showScreenshotSizeSel()
|
||||
elif choice == "exit":
|
||||
quit(0)
|
||||
else:
|
||||
quit(0)
|
||||
|
||||
proc takeScreenshot(ss: Screenshot) =
|
||||
let filename = TEMP_DIR & FILENAME.replace("%d",DATE_STR)
|
||||
var cmd = ss.tool.command
|
||||
case ss.size:
|
||||
of Window:
|
||||
cmd = ss.tool.activeWindowCommand()
|
||||
of Region:
|
||||
cmd = ss.tool.regionCommand()
|
||||
else: #fullscreen
|
||||
cmd = cmd.replace("%s","")
|
||||
# sleep for a bit otherwise the screen shot could grabs dmenu as well
|
||||
sleep(1*500)
|
||||
cmd = cmd.replace("%f",filename)
|
||||
echo "Running command:\n" & cmd
|
||||
let status = execCmd(cmd)
|
||||
if status == 0:
|
||||
showScreenshotSaveSel(filename)
|
||||
return
|
||||
|
||||
proc go*() =
|
||||
var ss = parseScreenshotArgs()
|
||||
if ss.size == None:
|
||||
ss.size = showScreenshotSizeSel()
|
||||
if ss.size != None:
|
||||
ss.takeScreenshot()
|
||||
|
||||
if isMainModule:
|
||||
go()
|
54
src/util/temperaturr.nim
Normal file
54
src/util/temperaturr.nim
Normal file
|
@ -0,0 +1,54 @@
|
|||
import os
|
||||
import re
|
||||
import math
|
||||
import strutils
|
||||
|
||||
import ../common
|
||||
import ../output
|
||||
|
||||
proc getThermalZones(): seq[string] =
|
||||
var zones: seq[string] = @[]
|
||||
let dirname_re = re("thermal_zone[\\d]+")
|
||||
let enabled_re = re("enabled")
|
||||
for file in walkDir("/sys/class/thermal/"):
|
||||
if file.path.contains(dirname_re):
|
||||
let state = readFile(file.path & "/mode")
|
||||
if state.contains(enabled_re):
|
||||
zones.add(file.path)
|
||||
return zones
|
||||
|
||||
proc getTemp(zone: string): int =
|
||||
let temp = strip(readFile(zone & "/temp"))
|
||||
return parseInt(temp)
|
||||
|
||||
proc getAverageTemp(zones: seq[string]): float =
|
||||
var temps: int = 0
|
||||
for zone in zones:
|
||||
let temp = getTemp(zone)
|
||||
temps += temp
|
||||
let avgtemp = ceil((temps / len(zones))/1000)
|
||||
return round(avgtemp,2)
|
||||
|
||||
proc getObject(temp: float): Info =
|
||||
var icon = ""
|
||||
case temp:
|
||||
of 40..59:
|
||||
icon = ""
|
||||
of 60.. 200:
|
||||
icon = ""
|
||||
else:
|
||||
icon = ""
|
||||
let main_text = icon & " " & $temp & "°C"
|
||||
var data = newInfo("Temperaturr")
|
||||
data.full_text = main_text
|
||||
return data
|
||||
|
||||
proc go*() =
|
||||
let zones = getThermalZones()
|
||||
let temp = getAverageTemp(zones)
|
||||
let data = getObject(temp)
|
||||
let option = outputData(data)
|
||||
if option == data.full_text:
|
||||
# Refresh
|
||||
go()
|
||||
|
110
src/util/tideurrl.nim
Normal file
110
src/util/tideurrl.nim
Normal file
|
@ -0,0 +1,110 @@
|
|||
#curl https://www.tidetimes.org.uk/exmouth-dock-tide-times-20190101 | grep -E -o ">((High|Low)|([0-9]+:[0-9]+)|([0-9]+\.[0-9]+m))"
|
||||
|
||||
import re
|
||||
import httpclient
|
||||
import times
|
||||
import osproc
|
||||
import sequtils
|
||||
|
||||
import ../common
|
||||
import ../parser
|
||||
import ../output
|
||||
import ../model/tides
|
||||
|
||||
const icon: string = "🌊 "
|
||||
|
||||
proc sortTides(tides: seq[Tide], is_tomorrow: bool = false): seq[Tide] =
|
||||
let timenow = now()
|
||||
var reltides: seq[Tide]
|
||||
for tide in tides:
|
||||
if timenow.format("HH:MM") <= tide.time and not is_tomorrow:
|
||||
reltides.add(tide)
|
||||
elif is_tomorrow:
|
||||
reltides.add(tide)
|
||||
return reltides
|
||||
|
||||
|
||||
proc getTideData(mytides: TideList, get_tomorrow: bool = false): seq[Tide] =
|
||||
var tides: seq[Tide]
|
||||
let fnd = re">((High|Low)|([0-9]+:[0-9]+)|([0-9]+\.[0-9]+m))"
|
||||
var client = newHttpClient(timeout = 10000)
|
||||
var link = replace(mytides.url,re"\%LOC",mytides.location)
|
||||
if get_tomorrow:
|
||||
let tomdate = now() + initTimeInterval(days = 1)
|
||||
link &= "-" & tomdate.format("yyyyMMdd")
|
||||
try:
|
||||
# Remember to compile with -d:ssl else this won't work
|
||||
let resp = client.request(link)
|
||||
if resp.status != $Http200 or resp.body == "":
|
||||
var data = newInfo("Tideurrl")
|
||||
data.full_text = "Error Response: " & resp.status & ":\nBody:" & resp.body
|
||||
discard outputData(data)
|
||||
return @[]
|
||||
let data = resp.body
|
||||
let times = findAll(data,fnd)
|
||||
var tide: Tide
|
||||
var column = 0
|
||||
for idx,time in times:
|
||||
if idx == 12:
|
||||
break
|
||||
let l = len(time) - 1
|
||||
if time == ">High" or time == ">Low":
|
||||
tide = Tide()
|
||||
tide.state = time[1..l]
|
||||
column = 1
|
||||
continue
|
||||
elif column == 1:
|
||||
tide.time = time[1..l]
|
||||
column = 2
|
||||
continue
|
||||
elif column == 2:
|
||||
tide.height = time[1..l]
|
||||
tides.add(tide)
|
||||
column = 0
|
||||
continue
|
||||
except:
|
||||
var data = newInfo("Tideurrl")
|
||||
data.full_text = "Unable to get Tide Data : " & getCurrentExceptionMsg()
|
||||
discard outputData(data)
|
||||
return tides
|
||||
|
||||
proc getDesign(tl: TideList): Info =
|
||||
var my_tides: seq[string] = @[]
|
||||
my_tides.add(tl.location)
|
||||
let tides = tl.tides
|
||||
for tide in tides:
|
||||
let str = icon & tide.state[0] & " " & tide.time & " " & tide.height
|
||||
my_tides.add(str)
|
||||
var data = newInfo("Tideurrl")
|
||||
data.args = my_tides
|
||||
return data
|
||||
|
||||
proc getTides*(mytides: var TideList, get_tomorrow: bool = false) =
|
||||
mytides.tides = mytides.getTideData(get_tomorrow)
|
||||
mytides.tides = sortTides(mytides.tides, get_tomorrow)
|
||||
if len(mytides.tides) == 0:
|
||||
return
|
||||
let data = getDesign(mytides)
|
||||
var opt_tomorrow = "tomorrow"
|
||||
if get_tomorrow:
|
||||
opt_tomorrow = "back"
|
||||
let args = concat(data.args,@["---",opt_tomorrow])
|
||||
let output = outputData(data,args)
|
||||
if output == "tomorrow":
|
||||
getTides(mytides,true)
|
||||
elif output == "back":
|
||||
getTides(mytides)
|
||||
elif output == "---" or output == "":
|
||||
return
|
||||
elif output in args:
|
||||
let url = replace(mytides.url,re"\%LOC",mytides.location)
|
||||
discard execCmd("xdg-open " & url)
|
||||
else:
|
||||
mytides.location = output
|
||||
getTides(mytides)
|
||||
|
||||
|
||||
proc go*() =
|
||||
var mytides = parseTideurrlArgs()
|
||||
getTides(mytides)
|
||||
|
115
src/util/volurrme.nim
Normal file
115
src/util/volurrme.nim
Normal file
|
@ -0,0 +1,115 @@
|
|||
import os
|
||||
import strutils
|
||||
import sequtils
|
||||
import osproc
|
||||
|
||||
import ../common
|
||||
import ../output
|
||||
import ../parser
|
||||
import ../model/volume
|
||||
|
||||
const audio_tools = @["ncpamixer", "pavucontrol"]
|
||||
const vol_cmd = "pamixer"
|
||||
const vol_up = vol_cmd & " -i %v" # where %v is amount by
|
||||
const vol_down = vol_cmd & " -d %v" # where %v is amount by
|
||||
const vol_set = vol_cmd & " --set-volume %v" # where %v is amount by
|
||||
const vol_mute = vol_cmd & " -t"
|
||||
const vol_default_by = "5"
|
||||
const vol_get = vol_cmd & " --get-volume"
|
||||
const vol_get_mute = vol_cmd & " --get-mute"
|
||||
|
||||
|
||||
proc getCurrentVolume(): string =
|
||||
let mute = execCmdEx(vol_get_mute)
|
||||
if strip(mute.output) == "true":
|
||||
return "muted"
|
||||
let vol = execCmdEx(vol_get)
|
||||
return vol.output
|
||||
|
||||
proc checkVolume(volume: string): string =
|
||||
var vol = volume
|
||||
if strip(vol) == "Connection error":
|
||||
sleep(1000)
|
||||
vol = getCurrentVolume()
|
||||
vol = checkVolume(vol)
|
||||
return vol
|
||||
|
||||
proc getDesign(volume: string): string =
|
||||
let vol = checkVolume(volume)
|
||||
var icon = " "
|
||||
if vol == "muted":
|
||||
return icon & "muted"
|
||||
let pcnt = parseInt(strip(vol))
|
||||
case pcnt:
|
||||
of 85..100:
|
||||
icon = " "
|
||||
of 55..84:
|
||||
icon = " "
|
||||
of 35..54:
|
||||
icon = " "
|
||||
of 10..34:
|
||||
icon = " "
|
||||
else:
|
||||
icon = " "
|
||||
let main_text = icon & $pcnt & "%"
|
||||
return main_text
|
||||
|
||||
proc volumeUp() =
|
||||
let cmd = replace(vol_up, "%v", vol_default_by)
|
||||
discard execCmd(cmd)
|
||||
|
||||
proc volumeDown() =
|
||||
let cmd = replace(vol_down, "%v", vol_default_by)
|
||||
discard execCmd(cmd)
|
||||
|
||||
proc volumeMute() =
|
||||
discard execCmd(vol_mute)
|
||||
|
||||
proc getVolume*() =
|
||||
let vol = getCurrentVolume()
|
||||
let main_text = getDesign(vol)
|
||||
var data = newInfo("Volurrme")
|
||||
data.full_text = main_text
|
||||
let args = concat(@["up", "down", "mute", "---", "exit", "---"],audio_tools)
|
||||
let option = outputData(data,args)
|
||||
if option == "":
|
||||
return
|
||||
elif option in args:
|
||||
if option in audio_tools:
|
||||
discard(execCmd(option))
|
||||
return
|
||||
case option:
|
||||
of "up":
|
||||
volumeUp()
|
||||
getVolume()
|
||||
of "down":
|
||||
volumeDown()
|
||||
getVolume()
|
||||
of "mute":
|
||||
volumeMute()
|
||||
getVolume()
|
||||
of "---":
|
||||
getVolume()
|
||||
of "exit":
|
||||
return
|
||||
else:
|
||||
try:
|
||||
let vol = parseInt(option)
|
||||
let cmd = replace(vol_set, "%v", $vol)
|
||||
discard execCmd(cmd)
|
||||
getVolume()
|
||||
except:
|
||||
echo getCurrentExceptionMsg()
|
||||
getVolume()
|
||||
|
||||
proc go*() =
|
||||
let arg = parseVolArgs()
|
||||
case arg
|
||||
of VolUp:
|
||||
volumeUp()
|
||||
of VolDown:
|
||||
volumeDown()
|
||||
of VolMute:
|
||||
volumeMute()
|
||||
else:
|
||||
getVolume()
|
140
src/util/wallpapurr.nim
Normal file
140
src/util/wallpapurr.nim
Normal file
|
@ -0,0 +1,140 @@
|
|||
import os
|
||||
import json
|
||||
import osproc
|
||||
import random
|
||||
import strutils
|
||||
import sequtils
|
||||
import httpclient
|
||||
|
||||
import ../common
|
||||
import ../common/http
|
||||
import ../common/display
|
||||
import ../parser
|
||||
import ../output
|
||||
import ../notify
|
||||
|
||||
var UNSPLASH_KEY = ""
|
||||
var BG_DIR = "/tmp/"
|
||||
var LAST_FILE = ""
|
||||
var LAST = ""
|
||||
|
||||
const UNSPLASH_URL = "https://api.unsplash.com/photos/random?query=$QUERY&orientation=landscape"
|
||||
|
||||
proc getFromUnsplash(q: var string): string =
|
||||
let dir = BG_DIR & "/unsplash/" & q & "/"
|
||||
createDir(dir)
|
||||
notify.send("Getting from Unsplash",q)
|
||||
q = q.replace(" ","%20")
|
||||
let uri = UNSPLASH_URL.replace("$QUERY",q)
|
||||
var client = newHttpClient(timeout = 10000)
|
||||
let id = "Client-ID " & UNSPLASH_KEY
|
||||
client.headers = newHttpHeaders({"Authorization": id})
|
||||
try:
|
||||
let resp = client.get(uri)
|
||||
if resp.status == $Http200:
|
||||
let j = parseJson(resp.body)
|
||||
let link = j["links"]["download"].getStr
|
||||
let filename = dir & j["slug"].getStr & ".jpg"
|
||||
let img = download(link)
|
||||
filename.save(img)
|
||||
return filename
|
||||
except:
|
||||
echo getCurrentExceptionMsg()
|
||||
|
||||
proc getFiles(dir: string): seq[string] =
|
||||
var files: seq[string] = @[]
|
||||
for file in walkDir(dir):
|
||||
if file.path.endsWith(".jpg"): files.add(file.path)
|
||||
elif file.kind == pcDir:
|
||||
files = files.concat(getFiles(file.path))
|
||||
return files
|
||||
|
||||
proc getLast() =
|
||||
LAST = readFile(LAST_FILE).strip()
|
||||
|
||||
proc setLastFileName(file: string) =
|
||||
if file.len == 0:
|
||||
echo "no file"
|
||||
return
|
||||
writeFile(LAST_FILE, file)
|
||||
LAST = file
|
||||
|
||||
proc getImageFromDir(): string =
|
||||
notify.send("Getting Random file from:",BG_DIR)
|
||||
var img_files = getFiles(BG_DIR).filter(proc(f: string): bool = f != LAST)
|
||||
randomize()
|
||||
img_files.shuffle()
|
||||
if img_files.len > 0:
|
||||
let img_file = img_files[0]
|
||||
notify.send("Found : ", img_file)
|
||||
return img_file
|
||||
notify.send("No Background Images Found")
|
||||
return ""
|
||||
|
||||
proc getCurrSwayBGPID(): string =
|
||||
let pid = execCmdEx("pgrep swaybg")
|
||||
return pid.output
|
||||
|
||||
proc killCurrSwayBGPID(pid: string) =
|
||||
sleep 2000
|
||||
discard execCmd("kill " & pid)
|
||||
|
||||
|
||||
proc setImage(img: string) =
|
||||
notify.send("Setting Background to:",img)
|
||||
if isWayland():
|
||||
let pid = getCurrSwayBGPID()
|
||||
let swaybg = "swaybg -m fill -i " & img.escape & " &"
|
||||
discard execCmd(swaybg)
|
||||
killCurrSwayBGPID(pid)
|
||||
else:
|
||||
let feh = "feh --bg-fill " & img.escape
|
||||
discard execCmdEx(feh)
|
||||
|
||||
proc setLast() =
|
||||
notify.send("Setting Background to Last", LAST)
|
||||
if isWayland():
|
||||
let pid = getCurrSwayBGPID()
|
||||
let swaybg = "swaybg -m fill -i " & LAST.escape & " &"
|
||||
discard execCmd(swaybg)
|
||||
killCurrSwayBGPID(pid)
|
||||
else:
|
||||
let feh = "feh --bg-fill " & LAST.escape
|
||||
discard execCmdEx(feh)
|
||||
|
||||
proc getDesign(): Info =
|
||||
var data = newInfo("Wallpapurr")
|
||||
return data
|
||||
|
||||
proc queryPrompt(): string =
|
||||
let data = getDesign()
|
||||
let output = data.outputData()
|
||||
return output
|
||||
|
||||
proc go*() =
|
||||
var args = parseWallpapurrArgs()
|
||||
UNSPLASH_KEY = myConfig.unsplash_key
|
||||
BG_DIR = myConfig.bg_dir
|
||||
LAST_FILE = BG_DIR & "/last.txt"
|
||||
|
||||
if args.from_unsplash and args.query == "":
|
||||
args.query = queryPrompt()
|
||||
|
||||
var img = ""
|
||||
if args.query != "" or args.from_unsplash:
|
||||
echo "Query: ", args.query
|
||||
img = getFromUnsplash(args.query)
|
||||
elif args.last:
|
||||
getLast()
|
||||
setLast()
|
||||
quit(0)
|
||||
else:
|
||||
img = getImageFromDir()
|
||||
setImage(img)
|
||||
setLastFilename(img)
|
||||
echo img
|
||||
|
||||
|
||||
|
||||
|
||||
|
69
src/util/wirelurrs.nim
Normal file
69
src/util/wirelurrs.nim
Normal file
|
@ -0,0 +1,69 @@
|
|||
import os
|
||||
import osproc
|
||||
import strutils
|
||||
import sequtils
|
||||
|
||||
import ../common
|
||||
import ../output
|
||||
|
||||
const get_ssid_cmd = "iwgetid -r"
|
||||
const mng_cmd = "alacritty -e nmtui-connect"
|
||||
|
||||
proc getSsid(): string =
|
||||
let ssid = execCmdEx(get_ssid_cmd)
|
||||
return strip(ssid.output)
|
||||
|
||||
proc getSignalQuality(): string =
|
||||
let wl = readFile("/proc/net/wireless")
|
||||
let ln = splitLines(wl)[2]
|
||||
let links = split(ln," ")
|
||||
var qual = strip(links[1])
|
||||
qual = replace(qual,".","")
|
||||
return "[" & qual & "]"
|
||||
|
||||
proc getWifi(nic: string): (string, string) =
|
||||
let ssid = getSsid()
|
||||
if ssid == "":
|
||||
return ("disconnected", "disconnected")
|
||||
let quality = getSignalQuality()
|
||||
return (ssid, quality)
|
||||
|
||||
proc getObject(): Info =
|
||||
var data = newInfo("Wirelurrs")
|
||||
return data
|
||||
|
||||
proc getWifiInfo*(nics: seq[string]) =
|
||||
var lst: seq[string] = @[]
|
||||
for nic in nics:
|
||||
let (essid, quality) = getWifi(nic)
|
||||
lst.add(nic & ":" & quality & " " & essid)
|
||||
let data = getObject()
|
||||
let args = concat(lst,@["---", "manage","exit"])
|
||||
let output = outputData(data, args)
|
||||
if output in lst:
|
||||
discard execCmd(mng_cmd)
|
||||
case output:
|
||||
of "manage":
|
||||
discard execCmd(mng_cmd)
|
||||
of "exit":
|
||||
return
|
||||
of "---":
|
||||
return
|
||||
|
||||
proc getWiFiNICs(): seq[string] =
|
||||
var my_nics: seq[string] = @[]
|
||||
for path in walkDir("/sys/class/net/"):
|
||||
if dirExists(path.path & "/wireless"):
|
||||
let nic = path.path.replace("/sys/class/net/","")
|
||||
my_nics.add(nic)
|
||||
return my_nics
|
||||
|
||||
proc go*() =
|
||||
let my_nics = getWiFiNICs()
|
||||
if len(my_nics) > 0:
|
||||
getWifiInfo(my_nics)
|
||||
else:
|
||||
var data = getObject()
|
||||
data.full_text = "No WLAN"
|
||||
discard outputData(data)
|
||||
|
11
src/wmtools.nim
Normal file
11
src/wmtools.nim
Normal file
|
@ -0,0 +1,11 @@
|
|||
# This is just an example to get you started. A typical binary package
|
||||
# uses this file as the main entry point of the application.
|
||||
#
|
||||
|
||||
import common
|
||||
import parser
|
||||
import dispatcher
|
||||
|
||||
when isMainModule:
|
||||
parseArgs()
|
||||
dispatch myConfig
|
|
@ -1,68 +0,0 @@
|
|||
import std/os
|
||||
import std/re
|
||||
import std/math
|
||||
import strutils
|
||||
import base
|
||||
|
||||
|
||||
proc getThermalZones(): seq[string] =
|
||||
var zones: seq[string] = @[]
|
||||
let dirname = re("thermal_zone[\\d]+")
|
||||
for file in walkDir("/sys/class/thermal/"):
|
||||
if contains(file.path,dirname):
|
||||
let state = readFile(file.path & "/mode")
|
||||
if contains(state,re"enabled"):
|
||||
zones.add(file.path)
|
||||
return zones
|
||||
|
||||
proc getTemp(zone: string): int =
|
||||
let temp = strip(readFile(zone & "/temp"))
|
||||
return parseInt(temp)
|
||||
|
||||
proc getAverageTemp(zones: seq[string]): int =
|
||||
var temps: int = 0
|
||||
for zone in zones:
|
||||
let temp = getTemp(zone)
|
||||
temps += temp
|
||||
let avgtemp = ceil((temps / len(zones))/1000)
|
||||
return toInt(round(avgtemp,0))
|
||||
|
||||
proc getObject(temp: int): Info =
|
||||
var icon = ""
|
||||
var colour = foreground
|
||||
case temp:
|
||||
of 40..59:
|
||||
colour = yellow
|
||||
icon = ""
|
||||
of 60.. 200:
|
||||
colour = red
|
||||
icon = ""
|
||||
else:
|
||||
colour = green
|
||||
icon = ""
|
||||
let text = "<span foreground='" & colour & "'>" & icon & "</span> " & $temp & "°C"
|
||||
let main_text = icon & " " & $temp & "°C"
|
||||
var data = newInfo()
|
||||
data.title = "Temp : "
|
||||
data.full_text = main_text
|
||||
data.html_text = text
|
||||
data.color = foreground
|
||||
data.border = colour
|
||||
data.selected_background = colour
|
||||
data.selected_color = black
|
||||
return data
|
||||
|
||||
proc main() =
|
||||
let zones = getThermalZones()
|
||||
var last_temp = 0
|
||||
while true:
|
||||
let temp = getAverageTemp(zones)
|
||||
if temp != last_temp:
|
||||
let data = getObject(temp)
|
||||
outputJSON(data)
|
||||
last_temp = temp
|
||||
if stoploop:
|
||||
break
|
||||
sleep(10000)
|
||||
|
||||
main()
|
99
volume.nim
99
volume.nim
|
@ -1,99 +0,0 @@
|
|||
import std/os
|
||||
import strutils
|
||||
import std/osproc
|
||||
import std/math
|
||||
import base
|
||||
|
||||
proc get_current_volume(): string {.gcsafe.}
|
||||
|
||||
proc check_volume(volume: string): string =
|
||||
var vol = volume
|
||||
if strip(vol) == "Connection error":
|
||||
discard execCmdEx("pulseaudio -k; sleep 5; pulseaudio -D")
|
||||
vol = get_current_volume()
|
||||
vol = check_volume(vol)
|
||||
return vol
|
||||
|
||||
proc getDesign(volume: string): (string,string) =
|
||||
let vol = check_volume(volume)
|
||||
var icon = " "
|
||||
if vol == "muted":
|
||||
return (icon & "muted", "")
|
||||
let pcnt = parseInt(strip(vol))
|
||||
case pcnt:
|
||||
of 85..100:
|
||||
icon = " "
|
||||
of 55..84:
|
||||
icon = " "
|
||||
of 35..54:
|
||||
icon = " "
|
||||
of 10..34:
|
||||
icon = " "
|
||||
else:
|
||||
icon = " "
|
||||
let main_text = icon & $pcnt & "%"
|
||||
let text = "<span size=\"x-large\">" & icon & "</span>" & $pcnt & "%"
|
||||
return (text, main_text)
|
||||
|
||||
proc get_current_volume(): string =
|
||||
let mute = execCmdEx("pamixer --get-mute")
|
||||
if strip(mute.output) == "true":
|
||||
return "muted"
|
||||
let vol = execCmdEx("pamixer --get-volume")
|
||||
return vol.output
|
||||
|
||||
proc get_volume*(run_once: bool = false) =
|
||||
var last_vol: string = ""
|
||||
while true:
|
||||
let vol = get_current_volume()
|
||||
if vol != last_vol or true:
|
||||
let (text, main_text) = getDesign(vol)
|
||||
var data = Info()
|
||||
data.title = "Volume : "
|
||||
data.html_text = text
|
||||
data.full_text = main_text
|
||||
data.color = foreground
|
||||
data.border = green
|
||||
data.background = black
|
||||
data.selected_background = green
|
||||
data.selected_color = black
|
||||
let args = @["up", "down", "mute", "ncpamixer", "pavucontrol"]
|
||||
let option = outputJSON(data,args)
|
||||
if option in args:
|
||||
case option:
|
||||
of "up":
|
||||
discard execCmd("pamixer -i 5")
|
||||
get_volume()
|
||||
of "down":
|
||||
discard execCmd("pamixer -d 5")
|
||||
get_volume()
|
||||
of "mute":
|
||||
discard execCmd("pamixer -t")
|
||||
get_volume()
|
||||
of "ncpamixer":
|
||||
discard execCmd("ncpamixer")
|
||||
of "pavucontrol":
|
||||
discard execCmd("pavucontrol")
|
||||
else:
|
||||
try:
|
||||
let vol = parseInt(option)
|
||||
discard execCmd("pamixer --set-volume " & $vol)
|
||||
get_volume()
|
||||
except:
|
||||
echo getCurrentExceptionMsg()
|
||||
|
||||
|
||||
|
||||
|
||||
if run_once:
|
||||
break
|
||||
last_vol = vol
|
||||
sleep(1000)
|
||||
if stoploop:
|
||||
break
|
||||
|
||||
proc main() =
|
||||
get_volume()
|
||||
|
||||
if isMainModule:
|
||||
main()
|
62
wlan.nim
62
wlan.nim
|
@ -1,62 +0,0 @@
|
|||
import base
|
||||
import std/os
|
||||
import std/osproc
|
||||
import strutils
|
||||
|
||||
const wlan_nic*: string ="wlan0"
|
||||
|
||||
# /sys/class/net/wlp2s0/operstate up or down if connected
|
||||
|
||||
proc get_essid(): string =
|
||||
let essid = execCmdEx("iwgetid -r")
|
||||
return strip(essid.output)
|
||||
|
||||
proc get_signal_quality(): string =
|
||||
let wl = readFile("/proc/net/wireless")
|
||||
let ln = splitLines(wl)[2]
|
||||
let links = split(ln," ")
|
||||
var qual = strip(links[1])
|
||||
qual = replace(qual,".","")
|
||||
return "[" & qual & "]"
|
||||
|
||||
proc get_wifi(): (string, string) =
|
||||
let essid = get_essid()
|
||||
if essid == "":
|
||||
return ("disconnected", "")
|
||||
let quality = get_signal_quality()
|
||||
return (essid, quality)
|
||||
|
||||
proc getObject(conn: string): Info =
|
||||
var data = newInfo()
|
||||
data.title = "WiFi : "
|
||||
data.full_text = conn
|
||||
data.border = purple
|
||||
data.selected_background = purple
|
||||
data.selected_color = black
|
||||
return data
|
||||
|
||||
proc get_wifi_info*() =
|
||||
var last_qual = ""
|
||||
while true:
|
||||
let (essid, quality) = get_wifi()
|
||||
if quality != last_qual:
|
||||
let data = getObject(quality & " " & essid)
|
||||
let args = @["nmtui-connect"]
|
||||
let output = outputJSON(data, args)
|
||||
echo output
|
||||
case output:
|
||||
of "nmtui-connect":
|
||||
discard execCmd("alacritty -e nmtui-connect")
|
||||
last_qual = quality
|
||||
if stoploop:
|
||||
break
|
||||
sleep(1000)
|
||||
|
||||
proc main() =
|
||||
if dirExists("/sys/class/net/" & wlan_nic):
|
||||
get_wifi_info()
|
||||
else:
|
||||
echo "No WLAN"
|
||||
|
||||
if isMainModule:
|
||||
main()
|
20
wm_tools.nimble
Normal file
20
wm_tools.nimble
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Package
|
||||
|
||||
version = "2.0.8"
|
||||
author = "Paul Wilde"
|
||||
description = "A set of informational tools"
|
||||
license = "AGPL-3.0-or-later"
|
||||
srcDir = "src"
|
||||
bin = @["wmtools"]
|
||||
|
||||
|
||||
# Dependencies
|
||||
|
||||
requires "nim >= 2.0.0"
|
||||
requires "parsetoml >= 0.7.1"
|
||||
requires "argparse"
|
||||
requires "configparser"
|
||||
requires "jsony"
|
||||
|
||||
task refresh_emoji, "Refresh Emoji Library file":
|
||||
exec "nim c -r src/lib/refresh_emoji.nim"
|
Loading…
Reference in a new issue