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 times
|
||||||
import std/os
|
|
||||||
import base
|
|
||||||
|
|
||||||
proc get_hour(hr: int): string
|
import ../model/info
|
||||||
proc get_minute(min: int): string
|
import ../output
|
||||||
|
|
||||||
proc get_fuzzytime(): string =
|
proc getHour(hr: int): 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 =
|
|
||||||
case hr:
|
case hr:
|
||||||
of 1, 13:
|
of 1, 13:
|
||||||
return "one"
|
return "one"
|
||||||
|
@ -51,10 +32,10 @@ proc get_hour(hr: int): string =
|
||||||
else:
|
else:
|
||||||
return "error"
|
return "error"
|
||||||
|
|
||||||
proc get_minute(min: int): string =
|
proc getMinute(min: int): string =
|
||||||
case min:
|
case min:
|
||||||
of 58,59,0,1,2:
|
of 58,59,0,1,2:
|
||||||
return "o'clock"
|
return "oclock"
|
||||||
of 3,4,5,6,7,53,54,55,56,57:
|
of 3,4,5,6,7,53,54,55,56,57:
|
||||||
return "five"
|
return "five"
|
||||||
of 8,9,10,11,12,48,49,50,51,52:
|
of 8,9,10,11,12,48,49,50,51,52:
|
||||||
|
@ -70,29 +51,41 @@ proc get_minute(min: int): string =
|
||||||
else:
|
else:
|
||||||
return "error"
|
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 =
|
proc getObject(time: string): Info =
|
||||||
var data = Info()
|
var data = newInfo("Furry Time")
|
||||||
data.title = "Fuzzy Time :"
|
|
||||||
data.full_text = time
|
data.full_text = time
|
||||||
data.color = foreground
|
|
||||||
data.border = lightblue
|
|
||||||
data.selected_background = lightblue
|
|
||||||
data.selected_color = black
|
|
||||||
return data
|
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() =
|
proc go*() =
|
||||||
var last_time = ""
|
let time = getFuzzyTime()
|
||||||
while true:
|
show(time)
|
||||||
let time = get_fuzzytime()
|
|
||||||
if time != last_time:
|
|
||||||
let data = getObject(time)
|
|
||||||
outputJSON(data)
|
|
||||||
if stoploop:
|
|
||||||
break
|
|
||||||
last_time = 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