From 5eb6165d6806f5656802a6f58feea9469dad6141 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Mon, 7 Oct 2019 16:39:46 +0200 Subject: Reorganise, change license, portable makefile, and reference radharc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- .gitignore | 14 +- COPYING | 15 - LICENSE | 683 +------------------------------------ Makefile | 164 ++++----- README | 2 +- arg.h | 65 ++++ cg-base.c | 910 ++++++++++++++++++++++++++++++++++++++++++++++++++ cg-base.h | 237 +++++++++++++ cg-brilliance.1 | 115 +++++++ cg-brilliance.c | 296 ++++++++++++++++ cg-darkroom.1 | 108 ++++++ cg-darkroom.c | 299 +++++++++++++++++ cg-gamma.1 | 140 ++++++++ cg-gamma.c | 524 +++++++++++++++++++++++++++++ cg-icc.1 | 125 +++++++ cg-icc.c | 928 +++++++++++++++++++++++++++++++++++++++++++++++++++ cg-limits.1 | 161 +++++++++ cg-limits.c | 714 +++++++++++++++++++++++++++++++++++++++ cg-linear.1 | 123 +++++++ cg-linear.c | 263 +++++++++++++++ cg-negative.1 | 112 +++++++ cg-negative.c | 238 +++++++++++++ cg-query.1 | 72 ++++ cg-query.c | 497 +++++++++++++++++++++++++++ cg-rainbow.1 | 114 +++++++ cg-rainbow.c | 262 +++++++++++++++ cg-remove.1 | 60 ++++ cg-remove.c | 386 +++++++++++++++++++++ cg-shallow.1 | 127 +++++++ cg-shallow.c | 262 +++++++++++++++ cg-sleepmode.1 | 133 ++++++++ cg-sleepmode.c | 443 ++++++++++++++++++++++++ cg-tools.7 | 46 +++ config.mk | 8 + man/cg-brilliance.1 | 118 ------- man/cg-darkroom.1 | 111 ------ man/cg-gamma.1 | 143 -------- man/cg-icc.1 | 128 ------- man/cg-limits.1 | 164 --------- man/cg-linear.1 | 126 ------- man/cg-negative.1 | 115 ------- man/cg-query.1 | 75 ----- man/cg-rainbow.1 | 117 ------- man/cg-remove.1 | 63 ---- man/cg-shallow.1 | 130 -------- man/cg-sleepmode.1 | 136 -------- man/cg-tools.7 | 48 --- src/arg.h | 65 ---- src/cg-base.c | 927 --------------------------------------------------- src/cg-base.h | 254 -------------- src/cg-brilliance.c | 313 ----------------- src/cg-darkroom.c | 316 ------------------ src/cg-gamma.c | 541 ------------------------------ src/cg-icc.c | 945 ---------------------------------------------------- src/cg-limits.c | 731 ---------------------------------------- src/cg-linear.c | 280 ---------------- src/cg-negative.c | 255 -------------- src/cg-query.c | 514 ---------------------------- src/cg-rainbow.c | 279 ---------------- src/cg-remove.c | 403 ---------------------- src/cg-shallow.c | 279 ---------------- src/cg-sleepmode.c | 460 ------------------------- 62 files changed, 7847 insertions(+), 8835 deletions(-) delete mode 100644 COPYING create mode 100644 arg.h create mode 100644 cg-base.c create mode 100644 cg-base.h create mode 100644 cg-brilliance.1 create mode 100644 cg-brilliance.c create mode 100644 cg-darkroom.1 create mode 100644 cg-darkroom.c create mode 100644 cg-gamma.1 create mode 100644 cg-gamma.c create mode 100644 cg-icc.1 create mode 100644 cg-icc.c create mode 100644 cg-limits.1 create mode 100644 cg-limits.c create mode 100644 cg-linear.1 create mode 100644 cg-linear.c create mode 100644 cg-negative.1 create mode 100644 cg-negative.c create mode 100644 cg-query.1 create mode 100644 cg-query.c create mode 100644 cg-rainbow.1 create mode 100644 cg-rainbow.c create mode 100644 cg-remove.1 create mode 100644 cg-remove.c create mode 100644 cg-shallow.1 create mode 100644 cg-shallow.c create mode 100644 cg-sleepmode.1 create mode 100644 cg-sleepmode.c create mode 100644 cg-tools.7 create mode 100644 config.mk delete mode 100644 man/cg-brilliance.1 delete mode 100644 man/cg-darkroom.1 delete mode 100644 man/cg-gamma.1 delete mode 100644 man/cg-icc.1 delete mode 100644 man/cg-limits.1 delete mode 100644 man/cg-linear.1 delete mode 100644 man/cg-negative.1 delete mode 100644 man/cg-query.1 delete mode 100644 man/cg-rainbow.1 delete mode 100644 man/cg-remove.1 delete mode 100644 man/cg-shallow.1 delete mode 100644 man/cg-sleepmode.1 delete mode 100644 man/cg-tools.7 delete mode 100644 src/arg.h delete mode 100644 src/cg-base.c delete mode 100644 src/cg-base.h delete mode 100644 src/cg-brilliance.c delete mode 100644 src/cg-darkroom.c delete mode 100644 src/cg-gamma.c delete mode 100644 src/cg-icc.c delete mode 100644 src/cg-limits.c delete mode 100644 src/cg-linear.c delete mode 100644 src/cg-negative.c delete mode 100644 src/cg-query.c delete mode 100644 src/cg-rainbow.c delete mode 100644 src/cg-remove.c delete mode 100644 src/cg-shallow.c delete mode 100644 src/cg-sleepmode.c diff --git a/.gitignore b/.gitignore index aee9d82..cf5779d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,8 @@ -bin/ -obj/ -\#*\# -.\#* +/cg-* +!*.h +!*.c +*\#* *~ -*.bak -*.orig -*.swo -*.swp *.o -*.out *.su +*.out diff --git a/COPYING b/COPYING deleted file mode 100644 index 890de82..0000000 --- a/COPYING +++ /dev/null @@ -1,15 +0,0 @@ -cg-tools -- Cooperative gamma-enabled tools -Copyright (C) 2016, 2018 Mattias Andrée (maandree@kth.se) - -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 . diff --git a/LICENSE b/LICENSE index 94a9ed0..bb494bb 100644 --- a/LICENSE +++ b/LICENSE @@ -1,674 +1,15 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 +ISC License - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. +© 2016, 2018, 2019 Mattias Andrée - Preamble +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile b/Makefile index 1861df5..487d6f7 100644 --- a/Makefile +++ b/Makefile @@ -1,112 +1,70 @@ -PREFIX = /usr -BINDIR = $(PREFIX)/bin -DATADIR = $(PREFIX)/share -MANDIR = $(DATADIR)/man -MAN1DIR = $(MANDIR)/man1 -MAN7DIR = $(MANDIR)/man7 -LICENSEDIR = $(DATADIR)/licenses +.POSIX: +CONFIGFILE = config.mk +include $(CONFIGFILE) PKGNAME = cg-tools - -BIN = bin/cg-brilliance \ - bin/cg-darkroom \ - bin/cg-gamma \ - bin/cg-icc \ - bin/cg-limits \ - bin/cg-linear \ - bin/cg-negative \ - bin/cg-query \ - bin/cg-rainbow \ - bin/cg-remove \ - bin/cg-sleepmode \ - bin/cg-shallow - -MAN1 = man/cg-brilliance.1 \ - man/cg-darkroom.1 \ - man/cg-gamma.1 \ - man/cg-icc.1 \ - man/cg-limits.1 \ - man/cg-linear.1 \ - man/cg-negative.1 \ - man/cg-query.1 \ - man/cg-rainbow.1 \ - man/cg-remove.1 \ - man/cg-sleepmode.1 \ - man/cg-shallow.1 - -MAN7 = man/cg-tools.7 - - -OPTIMISE = -O2 - -WARN = -Wall -Wextra - -DEF = -D'PKGNAME="$(PKGNAME)"' -D_DEFAULT_SOURCE -D_BSD_SOURCE - - -all: $(BIN) - -bin/%: obj/%.o obj/cg-base.o - @mkdir -p -- "$$(dirname -- "$@")" - $(CC) -std=c99 $(OPTIMISE) $(WARN) $(DEF) -o $@ $^ $(LDFLAGS) -lm -lcoopgamma - -bin/cg-query: obj/cg-query.o - @mkdir -p -- "$$(dirname -- "$@")" - $(CC) -std=c99 $(OPTIMISE) $(WARN) $(DEF) -o $@ $^ $(LDFLAGS) -lm -lcoopgamma - -bin/cg-remove: obj/cg-remove.o - @mkdir -p -- "$$(dirname -- "$@")" - $(CC) -std=c99 $(OPTIMISE) $(WARN) $(DEF) -o $@ $^ $(LDFLAGS) -lm -lcoopgamma - -obj/%.o: src/%.c src/*.h - @mkdir -p -- "$$(dirname -- "$@")" - $(CC) -std=c99 $(OPTIMISE) $(WARN) $(DEF) -c -o $@ $< $(CFLAGS) $(CPPFLAGS) - - -install: install-base install-doc - -install-base: install-cmd install-copyright - -install-copyright: install-license install-copying - -install-doc: install-man - -install-man: install-man1 install-man7 - -install-cmd: $(BIN) - mkdir -p -- "$(DESTDIR)$(BINDIR)" - install -m755 -- $(BIN) "$(DESTDIR)$(BINDIR)" - -install-license: - mkdir -p -- "$(DESTDIR)$(LICENSEDIR)/$(PKGNAME)" - install -m644 -- LICENSE "$(DESTDIR)$(LICENSEDIR)/$(PKGNAME)/LICENSE" - -install-copying: - mkdir -p -- "$(DESTDIR)$(LICENSEDIR)/$(PKGNAME)" - install -m644 -- COPYING "$(DESTDIR)$(LICENSEDIR)/$(PKGNAME)/COPYING" - -install-man1: - mkdir -p -- "$(DESTDIR)$(MAN1DIR)" - install -m644 -- $(MAN1) "$(DESTDIR)$(MAN1DIR)" - -install-man7: - mkdir -p -- "$(DESTDIR)$(MAN7DIR)" - install -m644 -- $(MAN7) "$(DESTDIR)$(MAN7DIR)" - +XOUT =\ + cg-brilliance\ + cg-darkroom\ + cg-gamma\ + cg-icc\ + cg-limits\ + cg-linear\ + cg-negative\ + cg-rainbow\ + cg-sleepmode\ + cg-shallow + +XBIN =\ + cg-query\ + cg-remove + +HDR =\ + arg.h\ + cg-base.h + +BIN = $(XBIN) $(XOUT) +OUT = $(XOUT:=.out) +OBJ = $(BIN:=.o) cg-base.o +MAN1 = $(BIN:=.1) +MAN7 = cg-tools.7 + +all: $(XBIN) $(OUT) +$(OBJ): $(@:.o=.c) $(HDR) +$(OUT): $(@:.out=.o) cg-base.o + +.c.o: + $(CC) -c -o $@ $< $(CPPFLAGS) $(CFLAGS) + +.o.out: + $(CC) -o $@ $< cg-base.o $(LDFLAGS) + +cg-query: cg-query.o + $(CC) -o $@ $@.o $(LDFLAGS) + +cg-remove: cg-remove.o + $(CC) -o $@ $@.o $(LDFLAGS) + +install: $(XBIN) $(OUT) + mkdir -p -- "$(DESTDIR)$(PREFIX)/bin" + mkdir -p -- "$(DESTDIR)$(MANPREFIX)/man1" + mkdir -p -- "$(DESTDIR)$(MANPREFIX)/man7" + cp -- $(XBIN) "$(DESTDIR)$(PREFIX)/bin" + for x in $(XOUT); do cp -- "$$x.out" "$(DESTDIR)$(PREFIX)/bin/$$x" || exit 1; done + cp -- $(MAN1) "$(DESTDIR)$(MANPREFIX)/man1" + cp -- $(MAN7) "$(DESTDIR)$(MANPREFIX)/man7" uninstall: - -cd "$(DESTDIR)$(BINDIR)" && rm -- $$(printf -- '%s\n' $(BIN) | cut -d / -f 2-) - -rm -- "$(DESTDIR)$(LICENSEDIR)/$(PKGNAME)/LICENSE" - -rm -- "$(DESTDIR)$(LICENSEDIR)/$(PKGNAME)/COPYING" - -rmdir -- "$(DESTDIR)$(LICENSEDIR)/$(PKGNAME)" - -cd "$(DESTDIR)$(MAN1DIR)" && rm -- $$(printf -- '%s\n' $(MAN1) | cut -d / -f 2-) - -cd "$(DESTDIR)$(MAN7DIR)" && rm -- $$(printf -- '%s\n' $(MAN7) | cut -d / -f 2-) - + -cd -- "$(DESTDIR)$(PREFIX)/bin" && rm -f -- $(XBIN) $(XOUT) + -cd -- "$(DESTDIR)$(MANPREFIX)/man1" && rm -f -- $(MAN1) + -cd -- "$(DESTDIR)$(MANPREFIX)/man7" && rm -f -- $(MAN7) clean: - -rm -r -- bin obj + -rm -f -- $(BIN) *.o *.su *.out + +.SUFFIXES: +.SUFFIXES: .c .o .out -.PHONY: all clean install install-base install-doc install-cmd install-copyright \ - install-license install-copying install-man install-man1 install-man7 uninstall +.PHONY: all install uninstall clean install diff --git a/README b/README index 0ded1a8..e2be4bb 100644 --- a/README +++ b/README @@ -42,4 +42,4 @@ UTILITIES Gradually fade out the monitors, and gradually fade in on exit. SEE ALSO - libcoopgamma(7), coopgammad(1) + libcoopgamma(7), coopgammad(1), radharc(1) diff --git a/arg.h b/arg.h new file mode 100644 index 0000000..0b23c53 --- /dev/null +++ b/arg.h @@ -0,0 +1,65 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef ARG_H__ +#define ARG_H__ + +extern char *argv0; + +/* use main(int argc, char *argv[]) */ +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][0] == '-'\ + && argv[0][1];\ + argc--, argv++) {\ + char argc_;\ + char **argv_;\ + int brk_;\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + for (brk_ = 0, argv[0]++, argv_ = argv;\ + argv[0][0] && !brk_;\ + argv[0]++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][0];\ + switch (argc_) + +/* Handles obsolete -NUM syntax */ +#define ARGNUM case '0':\ + case '1':\ + case '2':\ + case '3':\ + case '4':\ + case '5':\ + case '6':\ + case '7':\ + case '8':\ + case '9' + +#define ARGEND }\ + } + +#define ARGC() argc_ + +#define ARGNUMF() (brk_ = 1, estrtonum(argv[0], 0, INT_MAX)) + +#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define LNGARG() &argv[0][0] + +#endif diff --git a/cg-base.c b/cg-base.c new file mode 100644 index 0000000..33d9c4d --- /dev/null +++ b/cg-base.c @@ -0,0 +1,910 @@ +/* See LICENSE file for copyright and license details. */ +#include "cg-base.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + + +/** + * The process's name + */ +const char* argv0 = NULL; + +/** + * The libcoopgamma context + */ +libcoopgamma_context_t cg; + +/** + * The names of the selected CRTC:s + */ +char** crtcs = NULL; + +/** + * Gamma ramp updates for each CRTC + */ +filter_update_t* crtc_updates = NULL; + +/** + * CRTC and monitor information about + * each selected CRTC and connect monitor + */ +libcoopgamma_crtc_info_t* crtc_info = NULL; + +/** + * The number of selected CRTC:s + */ +size_t crtcs_n = 0; + +/** + * The number of filters + */ +size_t filters_n = 0; + + +/** + * Contexts for asynchronous ramp updates + */ +static libcoopgamma_async_context_t* asyncs = NULL; + +/** + * The number of pending receives + */ +static size_t pending_recvs = 0; + +/** + * Whether message must be flushed + */ +static int flush_pending = 0; + + + +/** + * Data used to sort CRTC:s + */ +struct crtc_sort_data +{ + /** + * The gamma ramp type + */ + libcoopgamma_depth_t depth; + + /** + * Should be 0 + */ + int __padding; + + /** + * The size of the red gamma ramp + */ + size_t red_size; + + /** + * The size of the green gamma ramp + */ + size_t green_size; + + /** + * The size of the blue gamma ramp + */ + size_t blue_size; + + /** + * The index of the CRTC + */ + size_t index; +}; + + + +/** + * Compare two strings + * + * @param a Return -1 if this string is `NULL` or less than `b` + * @param b Return +1 if this string is less than `a` + * @return See `a` and `b`, 0 is returned if `a` and `b` are equal + */ +static int nulstrcmp(const char *a, const char *b) +{ + return (a == NULL) ? -1 : strcmp(a, b); +} + + +/** + * Compare two instances of `crtc_sort_data` + * + * @param a_ Return -1 if this one is lower + * @param b_ Return +1 if this one is higher + * @return See `a_` and `b_`, only -1 or +1 can be returned + */ +static int crtc_sort_data_cmp(const void* a_, const void* b_) +{ + const struct crtc_sort_data* a = a_; + const struct crtc_sort_data* b = b_; + int cmp = memcmp(a, b, sizeof(*a) - sizeof(a->index)); + return cmp ? cmp : a->index < b->index ? -1 : +1; +} + + +/** + * Make elements in `crtc_updates` slaves where appropriate + * + * @return Zero on success, -1 on error + */ +int make_slaves(void) +{ + struct crtc_sort_data* data; + size_t i, j, n = 0, master = 0, master_i; + + data = alloca(filters_n * sizeof(*data)); + memset(data, 0, filters_n * sizeof(*data)); + for (i = 0; i < filters_n; i++) + { + if (!(crtc_info[crtc_updates[i].crtc].supported)) + continue; + + data[n].depth = crtc_updates[i].filter.depth; + data[n].red_size = crtc_updates[i].filter.ramps.u8.red_size; + data[n].green_size = crtc_updates[i].filter.ramps.u8.green_size; + data[n].blue_size = crtc_updates[i].filter.ramps.u8.blue_size; + data[n].index = i; + n++; + } + + qsort(data, n, sizeof(*data), crtc_sort_data_cmp); + if (n == 0) + return 0; + + master_i = data[0].index; + for (i = 1; i < n; i++) + if (memcmp(data + i, data + master, sizeof(*data) - sizeof(data->index))) + { + if (master + 1 < i) + { + crtc_updates[master_i].slaves = calloc(i - master, sizeof(size_t)); + if (crtc_updates[master_i].slaves == NULL) + return -1; + for (j = 1; master + j < i; j++) + crtc_updates[master_i].slaves[j - 1] = data[master + j].index; + } + master = i; + master_i = data[master].index; + } + else + { + libcoopgamma_ramps_destroy(&(crtc_updates[data[i].index].filter.ramps.u8)); + crtc_updates[data[i].index].master = 0; + crtc_updates[data[i].index].filter.ramps.u8 = crtc_updates[master_i].filter.ramps.u8; + } + + if (master + 1 < i) + { + crtc_updates[master_i].slaves = calloc(i - master, sizeof(size_t)); + if (crtc_updates[master_i].slaves == NULL) + return -1; + for (j = 1; master + j < i; j++) + crtc_updates[master_i].slaves[j - 1] = data[master + j].index; + } + + return 0; +} + + +/** + * Update a filter and synchronise calls + * + * @param index The index of the CRTC + * @param timeout The number of milliseconds a call to `poll` may block, + * -1 if it may block forever + * @return 1: Success, no pending synchronisations + * 0: Success, with still pending synchronisations + * -1: Error, `errno` set + * -2: Error, `cg.error` set + * + * @throws EINTR Call to `poll` was interrupted by a signal + * @throws EAGAIN Call to `poll` timed out + */ +int update_filter(size_t index, int timeout) +{ + filter_update_t* filter = crtc_updates + index; + + if (!(filter->synced) || filter->failed) + abort(); + + pending_recvs += 1; + + if (libcoopgamma_set_gamma_send(&(filter->filter), &cg, asyncs + index) < 0) + switch (errno) + { + case EINTR: + case EAGAIN: +#if EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif + flush_pending = 1; + break; + default: + return -1; + } + + filter->synced = 0; + return synchronise(timeout); +} + + +/** + * Synchronised calls + * + * @param timeout The number of milliseconds a call to `poll` may block, + * -1 if it may block forever + * @return 1: Success, no pending synchronisations + * 0: Success, with still pending synchronisations + * -1: Error, `errno` set + * -2: Error, `cg.error` set + * + * @throws EINTR Call to `poll` was interrupted by a signal + * @throws EAGAIN Call to `poll` timed out + */ +int synchronise(int timeout) +{ + struct pollfd pollfd; + size_t selected; + + pollfd.fd = cg.fd; + pollfd.events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI; + if (flush_pending > 0) + pollfd.events |= POLLOUT; + + pollfd.revents = 0; + if (poll(&pollfd, (nfds_t)1, timeout) < 0) + return -1; + + if (pollfd.revents & (POLLOUT | POLLERR | POLLHUP | POLLNVAL)) + { + if (libcoopgamma_flush(&cg) < 0) + goto sync; + flush_pending = 0; + } + + if ((timeout < 0) && (pending_recvs > 0)) + if (!(pollfd.revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI))) + { + pollfd.revents = 0; + if (poll(&pollfd, (nfds_t)1, -1) < 0) + return -1; + } + + sync: + if (pollfd.revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI | POLLERR | POLLHUP | POLLNVAL)) + for (;;) + { + if (libcoopgamma_synchronise(&cg, asyncs, filters_n, &selected) < 0) + { + if (errno == 0) + continue; + else + goto fail; + } + if (crtc_updates[selected].synced) + continue; + crtc_updates[selected].synced = 1; + pending_recvs -= 1; + if (libcoopgamma_set_gamma_recv(&cg, asyncs + selected) < 0) + { + if (cg.error.server_side) + { + crtc_updates[selected].error = cg.error; + crtc_updates[selected].failed = 1; + memset(&(cg.error), 0, sizeof(cg.error)); + } + else + goto cg_fail; + } + } + + return pending_recvs == 0; + cg_fail: + return -2; + fail: + switch (errno) + { + case EINTR: + case EAGAIN: +#if EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif + return pending_recvs == 0; + default: + return -1; + } +} + + +/** + * Initialise the process, specifically + * reset the signal mask and signal handlers + * + * @return Zero on success, -1 on error + */ +static int initialise_proc(void) +{ + sigset_t sigmask; + int sig; + + for (sig = 1; sig < _NSIG; sig++) + if (signal(sig, SIG_DFL) == SIG_ERR) + if (sig == SIGCHLD) + return -1; + + if (sigemptyset(&sigmask) < 0) + return -1; + if (sigprocmask(SIG_SETMASK, &sigmask, NULL) < 0) + return -1; + + return 0; +} + + +/** + * Print, to stdout, a list of all + * recognised adjustment methods + * + * @return Zero on success, -1 on error + */ +static int list_methods(void) +{ + char** list; + size_t i; + + list = libcoopgamma_get_methods(); + if (list == NULL) + return -1; + for (i = 0; list[i]; i++) + printf("%s\n", list[i]); + free(list); + if (fflush(stdout) < 0) + return -1; + + return 0; +} + + +/** + * Print, to stdout, a list of all CRTC:s + * + * A connection to the coopgamma server + * must have been made + * + * @return Zero on success, -1 on error, -2 + * on libcoopgamma error + */ +static int list_crtcs(void) +{ + char** list; + size_t i; + + list = libcoopgamma_get_crtcs_sync(&cg); + if (list == NULL) + return -2; + for (i = 0; list[i]; i++) + printf("%s\n", list[i]); + free(list); + if (fflush(stdout) < 0) + return -1; + + return 0; +} + + +/** + * Fill the list of CRTC information + * + * @return Zero on success, -1 on error, -2 + * on libcoopgamma error + */ +static int get_crtc_info(void) +{ + size_t i, unsynced = 0, selected; + char* synced; + int need_flush = 0; + struct pollfd pollfd; + + synced = alloca(crtcs_n * sizeof(*synced)); + memset(synced, 0, crtcs_n * sizeof(*synced)); + + i = 0; + pollfd.fd = cg.fd; + pollfd.events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI; + + while ((unsynced > 0) || (i < crtcs_n)) + { + wait: + if (i < crtcs_n) + pollfd.events |= POLLOUT; + else + pollfd.events &= ~POLLOUT; + + pollfd.revents = 0; + if (poll(&pollfd, (nfds_t)1, -1) < 0) + goto fail; + + if (pollfd.revents & (POLLOUT | POLLERR | POLLHUP | POLLNVAL)) + { + if (need_flush && (libcoopgamma_flush(&cg) < 0)) + goto send_fail; + need_flush = 0; + for (; i < crtcs_n; i++) + if (unsynced++, libcoopgamma_get_gamma_info_send(crtcs[i], &cg, asyncs + i) < 0) + goto send_fail; + goto send_done; + send_fail: + switch (errno) + { + case EINTR: + case EAGAIN: +#if EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif + i++; + need_flush = 1; + break; + default: + goto fail; + } + } + send_done: + + if ((unsynced == 0) && (i == crtcs_n)) + break; + + if (pollfd.revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) + while (unsynced > 0) + switch (libcoopgamma_synchronise(&cg, asyncs, i, &selected)) + { + case 0: + if (synced[selected]) + { + libcoopgamma_skip_message(&cg); + break; + } + synced[selected] = 1; + unsynced -= 1; + if (libcoopgamma_get_gamma_info_recv(crtc_info + selected, &cg, asyncs + selected) < 0) + goto cg_fail; + break; + case -1: + switch (errno) + { + case 0: + break; + case EINTR: + case EAGAIN: +#if EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif + goto wait; + default: + goto fail; + } + break; + } + } + + return 0; + fail: + return -1; + cg_fail: + return -2; +} + + +/** + * -M METHOD + * Select adjustment method. If METHOD is "?", + * available methods will be printed to stdout. + * + * -S SITE + * Select site (display server instance). + * + * -c CRTC + * Select CRT controller. If CRTC is "?", CRTC:s + * will be printed to stdout. + * + * This option can be used multiple times. If it + * is not used at all, all CRTC:s will be selected. + * + * -p PRIORITY + * Select the priority for the filter, this should + * be a signed two's-complement integer. If + * PRIORITY is "?", the default priority for the + * program is printed to stdout. + * + * -R RULE + * The rule of the filter, that is, the last part + * of the class which is its identifier. If RULE + * is "?" the default rule is printed to stdout, + * if RULE is "??" the default class is printed + * to stdout. + * + * @param argc The number of command line arguments + * @param argv The command line arguments + * @return 0 on success, 1 on error + */ +int main(int argc, char* argv[]) +{ + int stage = 0; + int dealloc_crtcs = 0; + int rc = 0; + char* method = NULL; + char* site = NULL; + size_t crtc_i = 0; + int64_t priority = default_priority; + char* prio = NULL; + char* rule = NULL; + char* class = default_class; + char** classes = NULL; + size_t classes_n = 0; + int explicit_crtcs = 0; + int have_crtc_q = 0; + size_t i, filter_i; + + argv0 = *argv++, argc--; + + if (initialise_proc() < 0) + goto fail; + + crtcs = alloca(argc * sizeof(*crtcs)); + + for (; *argv; argv++, argc--) + { + char* args = *argv; + char opt[3]; + if (!strcmp(args, "--")) + { + argv++, argc--; + break; + } + opt[0] = *args++; + opt[2] = '\0'; + if ((*opt != '-') && (*opt != '+')) + break; + while (*args) + { + char* arg; + int at_end; + opt[1] = *args++; + arg = args; + if ((at_end = !*arg)) + arg = argv[1]; + if (!strcmp(opt, "-M")) + { + if ((method != NULL) || ((method = arg) == NULL)) + usage(); + } + else if (!strcmp(opt, "-S")) + { + if ((site != NULL) || ((site = arg) == NULL)) + usage(); + } + else if (!strcmp(opt, "-c")) + { + if (arg == NULL) + usage(); + crtcs[crtc_i++] = arg; + explicit_crtcs = 1; + if (!have_crtc_q && !strcmp(arg, "?")) + have_crtc_q = 1; + } + else if (!strcmp(opt, "-p")) + { + if ((prio != NULL) || ((prio = arg) == NULL)) + usage(); + } + else if (!strcmp(opt, "-R")) + { + if ((rule != NULL) || ((rule = arg) == NULL)) + usage(); + } + else + switch (handle_opt(opt, arg)) + { + case 0: + goto next_opt; + case 1: + break; + default: + goto fail; + } + argv += at_end; + argc -= at_end; + break; + next_opt:; + } + } + + crtcs_n = crtc_i; + crtcs[crtc_i] = NULL; + if (!have_crtc_q && nulstrcmp(method, "?") && + nulstrcmp(rule, "?") && nulstrcmp(rule, "??") && + ((default_priority == NO_DEFAULT_PRIORITY) || nulstrcmp(prio, "?"))) + if (handle_args(argc, argv, prio) < 0) + goto fail; + + if (default_priority != NO_DEFAULT_PRIORITY) + { + if (!nulstrcmp(prio, "?")) + { + printf("%" PRIi64 "\n", priority); + return 0; + } + else if (prio != NULL) + { + char *end; + errno = 0; + priority = (int64_t)strtoll(prio, &end, 10); + if (errno || *end || !*prio) + usage(); + } + } + + if (!nulstrcmp(rule, "??")) + { + size_t i; + if (*class_suffixes == NULL) + printf("%s\n", class); + else + for (i = 0; class_suffixes[i] != NULL; i++) + printf("%s%s\n", class, class_suffixes[i]); + return 0; + } + else if (!nulstrcmp(rule, "?")) + { + printf("%s\n", strstr(strstr(class, "::") + 2, "::") + 2); + return 0; + } + else if (rule != NULL) + { + char* p = strstr(strstr(class, "::") + 2, "::") + 2; + size_t n = (size_t)(p - class); + class = alloca(strlen(rule) + n + (size_t)1); + memcpy(class, default_class, n); + strcpy(class + n, rule); + if (strchr(class, '\n')) + { + fprintf(stderr, "%s: LF character is not allowed in the filter's class\n", argv0); + goto custom_fail; + } + } + + if (!nulstrcmp(method, "?")) + { + if (list_methods() < 0) + goto fail; + return 0; + } + + if (libcoopgamma_context_initialise(&cg) < 0) + goto fail; + stage++; + if (libcoopgamma_connect(method, site, &cg) < 0) + { + fprintf(stderr, "%s: server failed to initialise\n", argv0); + goto custom_fail; + } + stage++; + + if (have_crtc_q) + switch (list_crtcs()) + { + case 0: + goto done; + case -1: + goto fail; + default: + goto cg_fail; + } + + if (crtcs_n == 0) + { + crtcs = libcoopgamma_get_crtcs_sync(&cg); + if (crtcs == NULL) + goto cg_fail; + dealloc_crtcs = 1; + for (; crtcs[crtcs_n] != NULL; crtcs_n++); + } + + if (crtcs_n == 0) + { + fprintf(stderr, "%s: no CRTC:s are available\n", argv0); + goto custom_fail; + } + + if (*class_suffixes == NULL) + { + classes = &class; + classes_n = 1; + } + else + { + size_t len = strlen(class); + while (class_suffixes[classes_n]) + classes_n++; + classes = alloca(classes_n * sizeof(*classes)); + for (i = 0; i < classes_n; i++) + { + classes[i] = alloca(len + strlen(class_suffixes[i]) + sizeof(":")); + stpcpy(stpcpy(stpcpy(classes[i], class), ":"), class_suffixes[i]); + } + } + filters_n = classes_n * crtcs_n; + + crtc_info = alloca(crtcs_n * sizeof(*crtc_info)); + memset(crtc_info, 0, crtcs_n * sizeof(*crtc_info)); + for (crtc_i = 0; crtc_i < crtcs_n; crtc_i++) + if (libcoopgamma_crtc_info_initialise(crtc_info + crtc_i) < 0) + goto cg_fail; + + if (libcoopgamma_set_nonblocking(&cg, 1) < 0) + goto fail; + + asyncs = alloca(filters_n * sizeof(*asyncs)); + memset(asyncs, 0, filters_n * sizeof(*asyncs)); + for (filter_i = 0; filter_i < filters_n; filter_i++) + if (libcoopgamma_async_context_initialise(asyncs + filter_i) < 0) + goto fail; + + switch (get_crtc_info()) + { + case 0: + break; + case -1: + goto fail; + case -2: + goto cg_fail; + } + + for (crtc_i = 0; crtc_i < crtcs_n; crtc_i++) + { + if (explicit_crtcs && !(crtc_info[crtc_i].supported)) + fprintf(stderr, "%s: warning: gamma adjustments not supported on CRTC: %s\n", + argv0, crtcs[crtc_i]); + if (crtc_info[crtc_i].cooperative == 0) + fprintf(stderr, "%s: warning: cooperative gamma server not running for CRTC: %s\n", + argv0, crtcs[crtc_i]); + } + + crtc_updates = alloca(filters_n * sizeof(*crtc_updates)); + memset(crtc_updates, 0, filters_n * sizeof(*crtc_updates)); + for (filter_i = i = 0; i < classes_n; i++) + for (crtc_i = 0; crtc_i < crtcs_n; crtc_i++, filter_i++) + { + if (libcoopgamma_filter_initialise(&(crtc_updates[filter_i].filter)) < 0) + goto fail; + if (libcoopgamma_error_initialise(&(crtc_updates[filter_i].error)) < 0) + goto fail; + crtc_updates[filter_i].crtc = crtc_i; + crtc_updates[filter_i].synced = 1; + crtc_updates[filter_i].failed = 0; + crtc_updates[filter_i].master = 1; + crtc_updates[filter_i].slaves = NULL; + crtc_updates[filter_i].filter.crtc = crtcs[crtc_i]; + crtc_updates[filter_i].filter.class = classes[i]; + crtc_updates[filter_i].filter.priority = priority; + crtc_updates[filter_i].filter.depth = crtc_info[crtc_i].depth; + crtc_updates[filter_i].filter.ramps.u8.red_size = crtc_info[crtc_i].red_size; + crtc_updates[filter_i].filter.ramps.u8.green_size = crtc_info[crtc_i].green_size; + crtc_updates[filter_i].filter.ramps.u8.blue_size = crtc_info[crtc_i].blue_size; + switch (crtc_updates[filter_i].filter.depth) + { +#define X(CONST, MEMBER, MAX, TYPE)\ + case CONST:\ + libcoopgamma_ramps_initialise(&(crtc_updates[filter_i].filter.ramps.MEMBER));\ + libclut_start_over(&(crtc_updates[filter_i].filter.ramps.MEMBER), MAX, TYPE, 1, 1, 1);\ + break; +LIST_DEPTHS +#undef X + default: + fprintf(stderr, "%s: internal error: gamma ramp type is unrecognised: %i\n", + argv0, crtc_updates[filter_i].filter.depth); + goto custom_fail; + } + } + + switch (start()) + { + case 0: + break; + case -1: + goto fail; + case -2: + goto cg_fail; + case -3: + goto custom_fail; + } + + for (filter_i = 0; filter_i < filters_n; filter_i++) + if (crtc_updates[filter_i].failed) + { + const char* side = cg.error.server_side ? "server" : "client"; + const char* crtc = crtc_updates[filter_i].filter.crtc; + if (cg.error.custom) + { + if ((cg.error.number != 0) && (cg.error.description != NULL)) + fprintf(stderr, "%s: %s-side error number %" PRIu64 " for CRTC %s: %s\n", + argv0, side, cg.error.number, crtc, cg.error.description); + else if (cg.error.number != 0) + fprintf(stderr, "%s: %s-side error number %" PRIu64 " for CRTC %s\n", + argv0, side, cg.error.number, crtc); + else if (cg.error.description != NULL) + fprintf(stderr, "%s: %s-side error for CRTC %s: %s\n", argv0, side, crtc, cg.error.description); + } + else if (cg.error.description != NULL) + fprintf(stderr, "%s: %s-side error for CRTC %s: %s\n", argv0, side, crtc, cg.error.description); + else + fprintf(stderr, "%s: %s-side error for CRTC %s: %s\n", argv0, side, crtc, strerror(cg.error.number)); + } + + done: + if (dealloc_crtcs) + free(crtcs); + if (crtc_info != NULL) + for (crtc_i = 0; crtc_i < crtcs_n; crtc_i++) + libcoopgamma_crtc_info_destroy(crtc_info + crtc_i); + if (asyncs != NULL) + for (filter_i = 0; filter_i < filters_n; filter_i++) + libcoopgamma_async_context_destroy(asyncs + filter_i); + if (stage >= 1) + libcoopgamma_context_destroy(&cg, stage >= 2); + if (crtc_updates != NULL) + for (filter_i = 0; filter_i < filters_n; filter_i++) + { + if (crtc_updates[filter_i].master == 0) + memset(&(crtc_updates[filter_i].filter.ramps.u8), 0, sizeof(crtc_updates[filter_i].filter.ramps.u8)); + crtc_updates[filter_i].filter.crtc = NULL; + crtc_updates[filter_i].filter.class = NULL; + libcoopgamma_filter_destroy(&(crtc_updates[filter_i].filter)); + libcoopgamma_error_destroy(&(crtc_updates[filter_i].error)); + free(crtc_updates[filter_i].slaves); + } + return rc; + + custom_fail: + rc = 1; + goto done; + + fail: + rc = 1; + if (errno) + perror(argv0); + goto done; + + cg_fail: + rc = 1; + { + const char* side = cg.error.server_side ? "server" : "client"; + if (cg.error.custom) + { + if ((cg.error.number != 0) && (cg.error.description != NULL)) + fprintf(stderr, "%s: %s-side error number %" PRIu64 ": %s\n", + argv0, side, cg.error.number, cg.error.description); + else if (cg.error.number != 0) + fprintf(stderr, "%s: %s-side error number %" PRIu64 "\n", argv0, side, cg.error.number); + else if (cg.error.description != NULL) + fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, cg.error.description); + } + else if (cg.error.description != NULL) + fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, cg.error.description); + else + fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, strerror(cg.error.number)); + } + goto done; +} diff --git a/cg-base.h b/cg-base.h new file mode 100644 index 0000000..a9ef65c --- /dev/null +++ b/cg-base.h @@ -0,0 +1,237 @@ +/* See LICENSE file for copyright and license details. */ +#include + +#include + + + +/** + * Value of `default_priority` that indicates + * that there is no default priority + */ +#define NO_DEFAULT_PRIORITY INT64_MAX + + + +/** + * X-macro that list all gamma ramp types + * + * X will be expanded with 4 arguments: + * 1) The libcoopgamma constant that identifies the type + * 2) The member in `union libcoopgamma_ramps` that + * corresponds to the type + * 3) The max value for the ramp stops + * 4) The type of the ramp stops + */ +#define LIST_DEPTHS\ + X(LIBCOOPGAMMA_UINT8, u8, UINT8_MAX, uint8_t)\ + X(LIBCOOPGAMMA_UINT16, u16, UINT16_MAX, uint16_t)\ + X(LIBCOOPGAMMA_UINT32, u32, UINT32_MAX, uint32_t)\ + X(LIBCOOPGAMMA_UINT64, u64, UINT64_MAX, uint64_t)\ + X(LIBCOOPGAMMA_FLOAT, f, ((float)1), float)\ + X(LIBCOOPGAMMA_DOUBLE, d, ((double)1), double) + + + +/** + * Information (except asynchronous call context) + * required to update the gamma ramps on a CRTC. + */ +typedef struct filter_update +{ + /** + * The filter to update + * + * `.filter.crtc`, `.filter.class`, and + * `.filter.priority` (unless `default_priority` + * is `NO_DEFAULT_PRIORITY`), `.filter.depth` + * are preconfigured, and `.filter.ramps` + * is preinitialised and preset to an + * identity ramp + */ + libcoopgamma_filter_t filter; + + /** + * The index of the CRTC + */ + size_t crtc; + + /** + * Has the update been synchronised? + */ + int synced; + + /** + * Did the update fail? + */ + int failed; + + /** + * Error description if `.failed` is true + */ + libcoopgamma_error_t error; + + /** + * If zero, the ramps in `.filter` shall + * neither be modified nor freed + */ + int master; + + /** + * 0-terminated list of elements in + * `.crtc_updates` which shares gamma + * ramps with this instance + * + * This will only be set if `.master` + * is true + */ + size_t* slaves; + +} filter_update_t; + + + +/** + * The process's name + */ +extern const char* argv0; + +/** + * The libcoopgamma context + */ +extern libcoopgamma_context_t cg; + +/** + * The names of the selected CRTC:s + */ +extern char** crtcs; + +/** + * Gamma ramp updates for each CRTC + */ +extern filter_update_t* crtc_updates; + +/** + * CRTC and monitor information about + * each selected CRTC and connect monitor + */ +extern libcoopgamma_crtc_info_t* crtc_info; + +/** + * The number of selected CRTC:s + */ +extern size_t crtcs_n; + +/** + * The number of filters + */ +extern size_t filters_n; + + + +/** + * The default filter priority for the program + */ +extern const int64_t default_priority; + +/** + * The default class for the program + */ +extern char default_class[]; + +/** + * Class suffixes + */ +extern const char* const* class_suffixes; + + + +/** + * Make elements in `crtc_updates` slaves where appropriate + * + * @return Zero on success, -1 on error + */ +int make_slaves(void); + +/** + * Update a filter and synchronise calls + * + * @param index The index of the CRTC + * @param timeout The number of milliseconds a call to `poll` may block, + * -1 if it may block forever + * @return 1: Success, no pending synchronisations + * 0: Success, with still pending synchronisations + * -1: Error, `errno` set + * -2: Error, `cg.error` set + * + * @throws EINTR Call to `poll` was interrupted by a signal + * @throws EAGAIN Call to `poll` timed out + */ +int update_filter(size_t index, int timeout); + +/** + * Synchronised calls + * + * @param timeout The number of milliseconds a call to `poll` may block, + * -1 if it may block forever + * @return 1: Success, no pending synchronisations + * 0: Success, with still pending synchronisations + * -1: Error, `errno` set + * -2: Error, `cg.error` set + * + * @throws EINTR Call to `poll` was interrupted by a signal + * @throws EAGAIN Call to `poll` timed out + */ +int synchronise(int timeout); + + +/** + * Print usage information and exit + */ +#if defined(__GNUC__) +__attribute__((__noreturn__)) +#endif +extern void usage(void); + +/** + * Handle a command line option + * + * @param opt The option, it is a NUL-terminate two-character + * string starting with either '-' or '+', if the + * argument is not recognised, call `usage`. This + * string will not be "-M", "-S", "-c", "-p", or "-R". + * @param arg The argument associated with `opt`, + * `NULL` there is no next argument, if this + * parameter is `NULL` but needed, call `usage` + * @return 0 if `arg` was not used, + * 1 if `arg` was used, + * -1 on error + */ +#if defined(__GNUC__) +__attribute__((__nonnull__(1))) +#endif +extern int handle_opt(char* opt, char* arg); + +/** + * This function is called after the last + * call to `handle_opt` + * + * @param argc The number of unparsed arguments + * @param argv `NULL` terminated list of unparsed arguments + * @param prio The argument associated with the "-p" option + * @return Zero on success, -1 on error + */ +#if defined(__GNUC__) +__attribute__((__nonnull__(2))) +#endif +extern int handle_args(int argc, char* argv[], char* prio); + +/** + * The main function for the program-specific code + * + * @return 0: Success + * -1: Error, `errno` set + * -2: Error, `cg.error` set + * -3: Error, message already printed + */ +extern int start(void); diff --git a/cg-brilliance.1 b/cg-brilliance.1 new file mode 100644 index 0000000..4f35feb --- /dev/null +++ b/cg-brilliance.1 @@ -0,0 +1,115 @@ +.TH CG-BRILLIANCE 1 CG-TOOLS +.SH NAME +cg-brilliance - Set the brilliance on the monitors +.SH SYNOPSIS +.B cg-brilliance +.RB [ \-M +.IR method ] +.RB [ \-S +.IR site ] +.RB [ \-c +.IR crtc "]... ["\fB\-R\fP +.IR rule ] +.RB ( \-x +| +.RB [ \-p +.IR priority ] +.RB [ \-d ] +.RI ( all +| +.I red +.I green +.IR blue )) +.SH DESCRIPTION +.B cg-brilliance +changes the brilliance on the monitors, to +.I all +on all channel, or to +.IR red , +.IR green , +and +.I blue +to the red, green, and blue channels independently, respectively. +A value of 0 means complete darkness, and 1 means normal brilliance. +.SH OPTIONS +.TP +.BR \-c " "\fIcrtc\fP +Apply the filter to the CRTC with the monitor whose EDID is +.IR crtc . +By default, the filter is applied to all monitors. + +If +.I crtc +is +.RB ' ? ', +all available CRTC's are listed. +.TP +.B \-d +Keep the process alive and remove the filter on death. +.TP +.BR \-M " "\fImethod\fP +Adjustment method name or number. Recognised names include: +.TS +tab(:); +l l. +\fBdummy\fP:Dummy method +\fBrandr\fP:X RAndR +\fBvidmode\fP:X VidMode +\fBdrm\fP:Linux DRM +\fBgdi\fP:Windows GDI +\fBquartz\fP:Quartz Core Graphics +.TE + +The adjustment methods are supported via +.BR libgamma (7). +Only methods that were enabled when +.B libgamma +was compiled will be supported. + +If +.I method +is +.RB ' ? ', +all available adjustment methods are printed. +.TP +.BR \-p " "\fIpriority\fP +Set the priority of the filter. Filters with higher priority +are applied before filters with lower priority. The value +must be a signed 64-bit integer (between \-9223372036854775807 +and 9223372036854775807). +.BR cg-brilliance 's +default priority is 2305843009213693952. + +If +.I priority +is +.RB ' ? ', +the utility's default priority is printed. +.TP +.BR \-R " "\fIrule\fP +Set the rule of of the filter to +.IR rule . +This is the last part of the filter's identifier (class). +The default rule is +.BR standard . + +If +.I rule +is +.RB ' ? ' +the utility's default rule is printed. If +.I rule +is +.RB ' ?? ' +the utility's default class is printed. +.TP +.BR \-S " "\fIsite\fP +Select the site to which to connect. For example +.RB ' :0 ', +for local display 0 when using +.BR X . +.TP +.B \-x +Remove the currently applied filter. +.SH SEE ALSO +.BR cg-tools (7) diff --git a/cg-brilliance.c b/cg-brilliance.c new file mode 100644 index 0000000..8ed3124 --- /dev/null +++ b/cg-brilliance.c @@ -0,0 +1,296 @@ +/* See LICENSE file for copyright and license details. */ +#include "cg-base.h" + +#include + +#include +#include +#include +#include +#include + + + +/** + * The default filter priority for the program + */ +const int64_t default_priority = (int64_t)1 << 61; + +/** + * The default class for the program + */ +char default_class[] = PKGNAME "::cg-brilliance::standard"; + +/** + * Class suffixes + */ +const char* const* class_suffixes = (const char* const[]){NULL}; + + + +/** + * -d: keep process alive and remove filter on death + */ +static int dflag = 0; + +/** + * -x: remove filter rather than adding it + */ +static int xflag = 0; + +/** + * The brilliance of the red channel + */ +static double rvalue = 0; + +/** + * The brilliance of the green channel + */ +static double gvalue = 0; + +/** + * The brilliance of the blue channel + */ +static double bvalue = 0; + + + +/** + * Print usage information and exit + */ +void usage(void) +{ + fprintf(stderr, + "Usage: %s [-M method] [-S site] [-c crtc]... [-R rule] " + "(-x | [-p priority] [-d] (all | red green blue))\n", + argv0); + exit(1); +} + + +/** + * Handle a command line option + * + * @param opt The option, it is a NUL-terminate two-character + * string starting with either '-' or '+', if the + * argument is not recognised, call `usage`. This + * string will not be "-M", "-S", "-c", "-p", or "-R". + * @param arg The argument associated with `opt`, + * `NULL` there is no next argument, if this + * parameter is `NULL` but needed, call `usage` + * @return 0 if `arg` was not used, + * 1 if `arg` was used, + * -1 on error + */ +int handle_opt(char* opt, char* arg) +{ + if (opt[0] == '-') + switch (opt[1]) + { + case 'd': + if (dflag || xflag) + usage(); + dflag = 1; + break; + case 'x': + if (xflag || dflag) + usage(); + xflag = 1; + break; + default: + usage(); + } + else + usage(); + return 0; + (void) arg; +} + + +/** + * Parse a non-negative double encoded as a string + * + * @param out Output parameter for the value + * @param str The string + * @return Zero on success, -1 if the string is invalid + */ +static int parse_double(double* restrict out, const char* restrict str) +{ + char* end; + errno = 0; + *out = strtod(str, &end); + if (errno || (*out < 0) || isinf(*out) || isnan(*out) || *end) + return -1; + if (!*str || !strchr("0123456789.", *str)) + return -1; + return 0; +} + + +/** + * This function is called after the last + * call to `handle_opt` + * + * @param argc The number of unparsed arguments + * @param argv `NULL` terminated list of unparsed arguments + * @param prio The argument associated with the "-p" option + * @return Zero on success, -1 on error + */ +int handle_args(int argc, char* argv[], char* prio) +{ + char* red = NULL; + char* green = NULL; + char* blue = NULL; + int q = xflag + dflag; + if ((q > 1) || (xflag && (prio != NULL || argc))) + usage(); + if (argc == 1) + red = green = blue = argv[0]; + else if (argc == 3) + { + red = argv[0]; + green = argv[1]; + blue = argv[2]; + } + else if (argc || !xflag) + usage(); + if (argc) + { + if (parse_double(&rvalue, red) < 0) + usage(); + if (parse_double(&gvalue, blue) < 0) + usage(); + if (parse_double(&bvalue, green) < 0) + usage(); + } + return 0; +} + + +/** + * Fill a filter + * + * @param filter The filter to fill + */ +static void fill_filter(libcoopgamma_filter_t* restrict filter) +{ + size_t i; + switch (filter->depth) + { +#define X(CONST, MAX, TYPE, MEMBER) \ + case CONST: \ + for (i = 0; i < filter->ramps.MEMBER.red_size; i++) \ + { \ + double val = (double)(filter->ramps.MEMBER.red[i]); \ + val *= rvalue; \ + if (val < 0) filter->ramps.MEMBER.red[i] = 0; \ + else if (val > (double)(MAX)) filter->ramps.MEMBER.red[i] = MAX; \ + else filter->ramps.MEMBER.red[i] = (TYPE)val; \ + } \ + for (i = 0; i < filter->ramps.MEMBER.green_size; i++) \ + { \ + double val = (double)(filter->ramps.MEMBER.green[i]); \ + val *= gvalue; \ + if (val < 0) filter->ramps.MEMBER.green[i] = 0; \ + else if (val > (double)(MAX)) filter->ramps.MEMBER.green[i] = MAX; \ + else filter->ramps.MEMBER.green[i] = (TYPE)val; \ + } \ + for (i = 0; i < filter->ramps.MEMBER.blue_size; i++) \ + { \ + double val = (double)(filter->ramps.MEMBER.blue[i]); \ + val *= bvalue; \ + if (val < 0) filter->ramps.MEMBER.blue[i] = 0; \ + else if (val > (double)(MAX)) filter->ramps.MEMBER.blue[i] = MAX; \ + else filter->ramps.MEMBER.blue[i] = (TYPE)val; \ + } \ + break + X(LIBCOOPGAMMA_UINT8, UINT8_MAX, uint8_t, u8); + X(LIBCOOPGAMMA_UINT16, UINT16_MAX, uint16_t, u16); + X(LIBCOOPGAMMA_UINT32, UINT32_MAX, uint32_t, u32); + X(LIBCOOPGAMMA_UINT64, UINT64_MAX, uint64_t, u64); +#undef X + case LIBCOOPGAMMA_FLOAT: + libclut_rgb_brightness(&(filter->ramps.f), (float)1, float, rvalue, gvalue, bvalue); + libclut_clip(&(filter->ramps.f), (float)1, float, 1, 1, 1); + break; + case LIBCOOPGAMMA_DOUBLE: + libclut_rgb_brightness(&(filter->ramps.d), (double)1, double, rvalue, gvalue, bvalue); + libclut_clip(&(filter->ramps.d), (double)1, double, 1, 1, 1); + break; + default: + abort(); + } +} + + +/** + * The main function for the program-specific code + * + * @return 0: Success + * -1: Error, `errno` set + * -2: Error, `cg.error` set + * -3: Error, message already printed + */ +int start(void) +{ + int r; + size_t i, j; + + if (xflag) + for (i = 0; i < filters_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_REMOVE; + else if (dflag) + for (i = 0; i < filters_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_DEATH; + else + for (i = 0; i < filters_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_REMOVAL; + + if (!xflag) + if ((r = make_slaves()) < 0) + return r; + + for (i = 0, r = 1; i < filters_n; i++) + { + if (!(crtc_updates[i].master) || !(crtc_info[crtc_updates[i].crtc].supported)) + continue; + if (!xflag) + fill_filter(&(crtc_updates[i].filter)); + r = update_filter(i, 0); + if ((r == -2) || ((r == -1) && (errno != EAGAIN))) + return r; + if (crtc_updates[i].slaves != NULL) + for (j = 0; crtc_updates[i].slaves[j] != 0; j++) + { + r = update_filter(crtc_updates[i].slaves[j], 0); + if ((r == -2) || ((r == -1) && (errno != EAGAIN))) + return r; + } + } + + while (r != 1) + if ((r = synchronise(-1)) < 0) + return r; + + if (!dflag) + return 0; + + if (libcoopgamma_set_nonblocking(&cg, 0) < 0) + return -1; + for (;;) + if (libcoopgamma_synchronise(&cg, NULL, 0, &j) < 0) + switch (errno) + { + case 0: + break; + case ENOTRECOVERABLE: + goto enotrecoverable; + default: + return -1; + } + + enotrecoverable: + for (;;) + if (pause() < 0) + return -1; +} diff --git a/cg-darkroom.1 b/cg-darkroom.1 new file mode 100644 index 0000000..ad812ff --- /dev/null +++ b/cg-darkroom.1 @@ -0,0 +1,108 @@ +.TH CG-DARKROOM 1 CG-TOOLS +.SH NAME +cg-darkroom - Invert colour on the monitors and make them dark red +.SH SYNOPSIS +.B cg-darkroom +.RB [ \-M +.IR method ] +.RB [ \-S +.IR site ] +.RB [ \-c +.IR crtc "]... ["\fB\-R\fP +.IR rule ] +.RB ( \-x +| +.RB [ \-p +.IR priority ] +.RB [ \-d ] +.RI [ brightness ]) +.SH DESCRIPTION +.B cg-darkroom +inverts the colours on the monitors, dim them, and turns of +the green and blue channels. The monitors are dimmed to the +brilliance of +.IR brightness , +or 0.25 if not specified. A value of 0 means complete darkness, +and 1 means normal brilliance. +.SH OPTIONS +.TP +.BR \-c " "\fIcrtc\fP +Apply the filter to the CRTC with the monitor whose EDID is +.IR crtc . +By default, the filter is applied to all monitors. + +If +.I crtc +is +.RB ' ? ', +all available CRTC's are listed. +.TP +.B \-d +Keep the process alive and remove the filter on death. +.TP +.BR \-M " "\fImethod\fP +Adjustment method name or number. Recognised names include: +.TS +tab(:); +l l. +\fBdummy\fP:Dummy method +\fBrandr\fP:X RAndR +\fBvidmode\fP:X VidMode +\fBdrm\fP:Linux DRM +\fBgdi\fP:Windows GDI +\fBquartz\fP:Quartz Core Graphics +.TE + +The adjustment methods are supported via +.BR libgamma (7). +Only methods that were enabled when +.B libgamma +was compiled will be supported. + +If +.I method +is +.RB ' ? ', +all available adjustment methods are printed. +.TP +.BR \-p " "\fIpriority\fP +Set the priority of the filter. Filters with higher priority +are applied before filters with lower priority. The value +must be a signed 64-bit integer (between \-9223372036854775807 +and 9223372036854775807). +.BR cg-darkroom 's +default priority is 6917529027641081856. + +If +.I priority +is +.RB ' ? ', +the utility's default priority is printed. +.TP +.BR \-R " "\fIrule\fP +Set the rule of of the filter to +.IR rule . +This is the last part of the filter's identifier (class). +The default rule is +.BR standard . + +If +.I rule +is +.RB ' ? ' +the utility's default rule is printed. If +.I rule +is +.RB ' ?? ' +the utility's default class is printed. +.TP +.BR \-S " "\fIsite\fP +Select the site to which to connect. For example +.RB ' :0 ', +for local display 0 when using +.BR X . +.TP +.B \-x +Remove the currently applied filter. +.SH SEE ALSO +.BR cg-tools (7) diff --git a/cg-darkroom.c b/cg-darkroom.c new file mode 100644 index 0000000..b9b79f1 --- /dev/null +++ b/cg-darkroom.c @@ -0,0 +1,299 @@ +/* See LICENSE file for copyright and license details. */ +#include "cg-base.h" + +#include + +#include +#include +#include +#include +#include + + + +/** + * The default filter priority for the program + */ +const int64_t default_priority = (int64_t)3 << 61; + +/** + * The default class for the program + */ +char default_class[] = PKGNAME "::cg-darkroom::standard"; + +/** + * Class suffixes + */ +const char* const* class_suffixes = (const char* const[]){NULL}; + + + +/** + * -d: keep process alive and remove filter on death + */ +static int dflag = 0; + +/** + * -x: remove filter rather than adding it + */ +static int xflag = 0; + +/** + * The brilliance of the red channel + */ +static double value = 0.25; + + + +/** + * Print usage information and exit + */ +void usage(void) +{ + fprintf(stderr, + "Usage: %s [-M method] [-S site] [-c crtc]... [-R rule] " + "(-x | [-p priority] [-d] [brightness])\n", + argv0); + exit(1); +} + + +/** + * Handle a command line option + * + * @param opt The option, it is a NUL-terminate two-character + * string starting with either '-' or '+', if the + * argument is not recognised, call `usage`. This + * string will not be "-M", "-S", "-c", "-p", or "-R". + * @param arg The argument associated with `opt`, + * `NULL` there is no next argument, if this + * parameter is `NULL` but needed, call `usage` + * @return 0 if `arg` was not used, + * 1 if `arg` was used, + * -1 on error + */ +int handle_opt(char* opt, char* arg) +{ + if (opt[0] == '-') + switch (opt[1]) + { + case 'd': + if (dflag || xflag) + usage(); + dflag = 1; + break; + case 'x': + if (xflag || dflag) + usage(); + xflag = 1; + break; + default: + usage(); + } + else + usage(); + return 0; + (void) arg; +} + + +/** + * Parse a non-negative double encoded as a string + * + * @param out Output parameter for the value + * @param str The string + * @return Zero on success, -1 if the string is invalid + */ +static int parse_double(double* restrict out, const char* restrict str) +{ + char* end; + errno = 0; + *out = strtod(str, &end); + if (errno || (*out < 0) || isinf(*out) || isnan(*out) || *end) + return -1; + if (!*str || !strchr("0123456789.", *str)) + return -1; + return 0; +} + + +/** + * This function is called after the last + * call to `handle_opt` + * + * @param argc The number of unparsed arguments + * @param argv `NULL` terminated list of unparsed arguments + * @param prio The argument associated with the "-p" option + * @return Zero on success, -1 on error + */ +int handle_args(int argc, char* argv[], char* prio) +{ + int q = xflag + dflag; + if ((q > 1) || (xflag && (prio != NULL || argc))) + usage(); + if (argc == 1) + { + if (parse_double(&value, argv[0]) < 0) + usage(); + } + else if (argc) + usage(); + return 0; +} + + +/** + * Fill a filter + * + * @param filter The filter to fill + * @return Zero on success, -1 on error + */ +static int fill_filter(libcoopgamma_filter_t* restrict filter) +{ + union libcoopgamma_ramps dramps; + size_t size; + + if ((0 <= value) && (value <= 1)) + { + switch (filter->depth) + { +#define X(CONST, MEMBER, MAX, TYPE)\ + case CONST:\ + libclut_negative(&(filter->ramps.MEMBER), MAX, TYPE, 1, 0, 0);\ + libclut_rgb_brightness(&(filter->ramps.MEMBER), MAX, TYPE, 1, 0, 0);\ + libclut_cie_brightness(&(filter->ramps.MEMBER), MAX, TYPE, value, value, value);\ + break; +LIST_DEPTHS +#undef X + default: + abort(); + } + return 0; + } + if (filter->depth == LIBCOOPGAMMA_DOUBLE) + { + libclut_negative(&(filter->ramps.d), (double)1, double, 1, 0, 0); + libclut_rgb_brightness(&(filter->ramps.d), (double)1, double, 1, 0, 0); + libclut_cie_brightness(&(filter->ramps.d), (double)1, double, value, value, value); + libclut_clip(&(filter->ramps.d), (double)1, double, 1, 0, 0); + return 0; + } + if (filter->depth == LIBCOOPGAMMA_FLOAT) + { + libclut_negative(&(filter->ramps.f), (float)1, float, 1, 0, 0); + libclut_rgb_brightness(&(filter->ramps.f), (float)1, float, 1, 0, 0); + libclut_cie_brightness(&(filter->ramps.f), (float)1, float, value, value, value); + libclut_clip(&(filter->ramps.f), (float)1, float, 1, 0, 0); + return 0; + } + + size = dramps.d.red_size = filter->ramps.d.red_size; + size += dramps.d.green_size = filter->ramps.d.green_size; + size += dramps.d.blue_size = filter->ramps.d.blue_size; + dramps.d.red = calloc(size, sizeof(double)); + if (dramps.d.red == NULL) + return -1; + dramps.d.green = dramps.d.red + dramps.d.red_size; + dramps.d.blue = dramps.d.green + dramps.d.green_size; + + libclut_start_over(&(dramps.d), (double)1, double, 1, 0, 0); + libclut_negative(&(dramps.d), (double)1, double, 1, 0, 0); + libclut_rgb_brightness(&(dramps.d), (double)1, double, 1, 0, 0); + libclut_cie_brightness(&(dramps.d), (double)1, double, value, value, value); + libclut_clip(&(dramps.d), (double)1, double, 1, 0, 0); + + switch (filter->depth) + { + case LIBCOOPGAMMA_UINT8: + libclut_translate(&(filter->ramps.u8), UINT8_MAX, uint8_t, &(dramps.d), (double)1, double); + break; + case LIBCOOPGAMMA_UINT16: + libclut_translate(&(filter->ramps.u16), UINT16_MAX, uint16_t, &(dramps.d), (double)1, double); + break; + case LIBCOOPGAMMA_UINT32: + libclut_translate(&(filter->ramps.u32), UINT32_MAX, uint32_t, &(dramps.d), (double)1, double); + break; + case LIBCOOPGAMMA_UINT64: + libclut_translate(&(filter->ramps.u64), UINT64_MAX, uint64_t, &(dramps.d), (double)1, double); + break; + default: + abort(); + } + + free(dramps.d.red); + return 0; +} + + +/** + * The main function for the program-specific code + * + * @return 0: Success + * -1: Error, `errno` set + * -2: Error, `cg.error` set + * -3: Error, message already printed + */ +int start(void) +{ + int r; + size_t i, j; + + if (xflag) + for (i = 0; i < filters_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_REMOVE; + else if (dflag) + for (i = 0; i < filters_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_DEATH; + else + for (i = 0; i < filters_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_REMOVAL; + + if (!xflag) + if ((r = make_slaves()) < 0) + return r; + + for (i = 0, r = 1; i < filters_n; i++) + { + if (!(crtc_updates[i].master) || !(crtc_info[crtc_updates[i].crtc].supported)) + continue; + if (!xflag) + if ((r = fill_filter(&(crtc_updates[i].filter))) < 0) + return r; + r = update_filter(i, 0); + if ((r == -2) || ((r == -1) && (errno != EAGAIN))) + return r; + if (crtc_updates[i].slaves != NULL) + for (j = 0; crtc_updates[i].slaves[j] != 0; j++) + { + r = update_filter(crtc_updates[i].slaves[j], 0); + if ((r == -2) || ((r == -1) && (errno != EAGAIN))) + return r; + } + } + + while (r != 1) + if ((r = synchronise(-1)) < 0) + return r; + + if (!dflag) + return 0; + + if (libcoopgamma_set_nonblocking(&cg, 0) < 0) + return -1; + for (;;) + if (libcoopgamma_synchronise(&cg, NULL, 0, &j) < 0) + switch (errno) + { + case 0: + break; + case ENOTRECOVERABLE: + goto enotrecoverable; + default: + return -1; + } + + enotrecoverable: + for (;;) + if (pause() < 0) + return -1; +} diff --git a/cg-gamma.1 b/cg-gamma.1 new file mode 100644 index 0000000..28ccb81 --- /dev/null +++ b/cg-gamma.1 @@ -0,0 +1,140 @@ +.TH CG-GAMMA 1 CG-TOOLS +.SH NAME +cg-gamma - Adjust the gamma curves on the monitors +.SH SYNOPSIS +.B cg-gamma +.RB [ \-M +.IR method ] +.RB [ \-S +.IR site ] +.RB [ \-c +.IR crtc "]... ["\fB\-R\fP +.IR rule ] +.RB ( \-x +| +.RB [ \-p +.IR priority ] +.RB [ \-d ] +.RB [ \-f +.I file +| +.I all +| +.I red +.I green +.IR blue ]) +.SH DESCRIPTION +.B cg-gamma +sets the gamma on the monitors to +.I all +on all channels, or +.IR red , +.IR green , +and +.IR blue +to the red, green, and blue channels, respectively. Alternatively, +.B cg-gamma +can change the gamma on each monitor independently by using +reading the values from +.I file +or a default file. +.P +The file is a 4-column text file where empty lines and lines +starting with a +.RB ' # ', +after any whitespace, are ignored. +The values in the columns should be, in order, a monitor's +EDID, that monitor's red gamma value, green gamma value, and +blue gamma value. +.SH OPTIONS +.TP +.BR \-f " "\fIfile\fP +Read the gamma values from the selected file. +.TP +.BR \-c " "\fIcrtc\fP +Apply the filter to the CRTC with the monitor whose EDID is +.IR crtc . +By default, the filter is applied to all monitors. + +If +.I crtc +is +.RB ' ? ', +all available CRTC's are listed. +.TP +.B \-d +Keep the process alive and remove the filter on death. +.TP +.BR \-M " "\fImethod\fP +Adjustment method name or number. Recognised names include: +.TS +tab(:); +l l. +\fBdummy\fP:Dummy method +\fBrandr\fP:X RAndR +\fBvidmode\fP:X VidMode +\fBdrm\fP:Linux DRM +\fBgdi\fP:Windows GDI +\fBquartz\fP:Quartz Core Graphics +.TE + +The adjustment methods are supported via +.BR libgamma (7). +Only methods that were enabled when +.B libgamma +was compiled will be supported. + +If +.I method +is +.RB ' ? ', +all available adjustment methods are printed. +.TP +.BR \-p " "\fIpriority\fP +Set the priority of the filter. Filters with higher priority +are applied before filters with lower priority. The value +must be a signed 64-bit integer (between \-9223372036854775807 +and 9223372036854775807). +.BR cg-gamma 's +default priority is 0. + +If +.I priority +is +.RB ' ? ', +the utility's default priority is printed. +.TP +.BR \-R " "\fIrule\fP +Set the rule of of the filter to +.IR rule . +This is the last part of the filter's identifier (class). +The default rule is +.BR standard . + +If +.I rule +is +.RB ' ? ' +the utility's default rule is printed. If +.I rule +is +.RB ' ?? ' +the utility's default class is printed. +.TP +.BR \-S " "\fIsite\fP +Select the site to which to connect. For example +.RB ' :0 ', +for local display 0 when using +.BR X . +.TP +.B \-x +Remove the currently applied filter. +.SH FILES +.TP +.B ~/.config/gamma +The default gamma table file. +.TP +.B /etc/gamma +The fallback gamma table file. +.SH SEE ALSO +.BR cg-tools (7) diff --git a/cg-gamma.c b/cg-gamma.c new file mode 100644 index 0000000..cc625bd --- /dev/null +++ b/cg-gamma.c @@ -0,0 +1,524 @@ +/* See LICENSE file for copyright and license details. */ +#include "cg-base.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + + +/** + * The default filter priority for the program + */ +const int64_t default_priority = 0; + +/** + * The default class for the program + */ +char default_class[] = PKGNAME "::cg-gamma::standard"; + +/** + * Class suffixes + */ +const char* const* class_suffixes = (const char* const[]){NULL}; + + + +/** + * -d: keep process alive and remove filter on death + */ +static int dflag = 0; + +/** + * -x: remove filter rather than adding it + */ +static int xflag = 0; + +/** + * -f: gamma listing file + */ +static char* fflag = NULL; + +/** + * The gamma of the red channel + */ +static double rgamma = 1; + +/** + * The gamma of the green channel + */ +static double ggamma = 1; + +/** + * The gamma of the blue channel + */ +static double bgamma = 1; + +/** + * `NULL`-terminated list of output + * names listed in the configuration file + */ +static char** names = NULL; + +/** + * The gamma of the red channel on monitor + * with same index in `names` + */ +static double* rgammas = NULL; + +/** + * The gamma of the green channel on monitor + * with same index in `names` + */ +static double* ggammas = NULL; + +/** + * The gamma of the blue channel on monitor + * with same index in `names` + */ +static double* bgammas = NULL; + + + +/** + * Print usage information and exit + */ +void usage(void) +{ + fprintf(stderr, + "Usage: %s [-M method] [-S site] [-c crtc]... [-R rule] " + "(-x | [-p priority] [-d] [-f file | all | red green blue])\n", + argv0); + exit(1); +} + + +/** + * Perform cleanup so valgrind output is clean + * + * @param ret The value to return + * @return `ret` is returned as is + */ +static int cleanup(int ret) +{ + int saved_errno = errno; + if (names != NULL) + { + char** p = names; + while (*p) + free(*p++); + } + free(names); + free(rgammas); + free(ggammas); + free(bgammas); + errno = saved_errno; + return ret; +} + + +/** + * Handle a command line option + * + * @param opt The option, it is a NUL-terminate two-character + * string starting with either '-' or '+', if the + * argument is not recognised, call `usage`. This + * string will not be "-M", "-S", "-c", "-p", or "-R". + * @param arg The argument associated with `opt`, + * `NULL` there is no next argument, if this + * parameter is `NULL` but needed, call `usage` + * @return 0 if `arg` was not used, + * 1 if `arg` was used, + * -1 on error + */ +int handle_opt(char* opt, char* arg) +{ + if (opt[0] == '-') + switch (opt[1]) + { + case 'd': + if (dflag || xflag) + usage(); + dflag = 1; + break; + case 'x': + if (xflag || dflag) + usage(); + xflag = 1; + break; + case 'f': + if (fflag || !(fflag = arg)) + usage(); + return 1; + default: + usage(); + } + return 0; +} + + +/** + * Get the pathname of a configuration file + * + * @param confname The filename (excluding directory) of the configuration file + * @return The full pathname of the configuration file, `NULL` on error + */ +static char* get_conf_file(const char* restrict confname) +{ + struct passwd* pw; + char* path; + + pw = getpwuid(getuid()); + if ((pw == NULL) || (pw->pw_dir == NULL)) + return NULL; + + path = malloc(strlen(pw->pw_dir) + strlen(confname) + sizeof("/.config/")); + if (path == NULL) + return NULL; + + sprintf(path, "%s/.config/%s", pw->pw_dir, confname); + + if (access(path, F_OK) < 0) + sprintf(path, "/etc/%s", confname); + + return path; +} + + +/** + * Parse a non-negative double encoded as a string + * + * @param out Output parameter for the value + * @param str The string + * @return Zero on success, -1 if the string is invalid + */ +static int parse_double(double* restrict out, const char* restrict str) +{ + char* end; + errno = 0; + *out = strtod(str, &end); + if (errno || (*out < 0) || isinf(*out) || isnan(*out) || *end) + return -1; + if (!*str || !strchr("0123456789.", *str)) + return -1; + return 0; +} + + +/** + * Parse gamma configuration file + * + * @param pathname The pathname of the file + * @return Zero on success, -1 on error + */ +static int parse_gamma_file(const char* restrict pathname) +{ + int fd, saved_errno; + char* line = NULL; + size_t size = 0, lineno = 0, ptr = 0, alloc = 0; + ssize_t n; + FILE* f = NULL; + char* p; + char* q; + char* r; + char* g; + char* b; + + fd = open(pathname, O_RDONLY); + if (fd == -1) + return -1; + + f = fdopen(fd, "rb"); + if (f == NULL) + goto fail; + + while (n = getline(&line, &size, f), n >= 0) + { + lineno += 1; + + if ((n > 0) && (line[n - 1] == '\n')) + line[n - 1] = '\0'; + p = line; + while ((*p == ' ') || (*p == '\t')) p++; + if ((!*p) || (*p == '#')) + continue; + + r = strpbrk(line, " \t"); + if (r == NULL) + goto bad; + while (r[1] == ' ' || r[1] == '\t') r++; + g = strpbrk(r + 1, " \t"); + if (g == NULL) + goto bad; + while (g[1] == ' ' || g[1] == '\t') g++; + b = strpbrk(g + 1, " \t"); + if (b == NULL) + goto bad; + while (b[1] == ' ' || b[1] == '\t') b++; + + for (;;) + { + q = strpbrk(b + 1, " \t"); + if (q == NULL) + break; + while (q[1] == ' ' || q[1] == '\t') q++; + if (!*q) + break; + r = g, g = b, b = q; + } + + *r++ = '\0'; + *g++ = '\0'; + *b++ = '\0'; + + q = strpbrk(r, " \t"); + if (q != NULL) + *q = '\0'; + q = strpbrk(g, " \t"); + if (q != NULL) + *q = '\0'; + q = strpbrk(b, " \t"); + if (q != NULL) + *q = '\0'; + + q = strchr(p, '\0'); + while ((q != p) && ((q[-1] == ' ') || (q[-1] == '\t'))) + q--; + *q = '\0'; + + if (ptr == alloc) + { + void* new; + size_t new_size = alloc ? (alloc << 1) : 4; + + new = realloc(rgammas, new_size * sizeof(*rgammas)); + if (new == NULL) + goto fail; + rgammas = new; + + new = realloc(ggammas, new_size * sizeof(*ggammas)); + if (new == NULL) + goto fail; + ggammas = new; + + new = realloc(bgammas, new_size * sizeof(*bgammas)); + if (new == NULL) + goto fail; + bgammas = new; + + new = realloc(names, (new_size + 1) * sizeof(*names)); + if (new == NULL) + goto fail; + names = new; + memset(names + alloc, 0, (new_size + 1 - alloc) * sizeof(*names)); + + alloc = new_size; + } + + if ((parse_double(rgammas + ptr, r) < 0) || + (parse_double(ggammas + ptr, g) < 0) || + (parse_double(bgammas + ptr, b) < 0)) + goto bad; + names[ptr] = malloc(strlen(p) + 1); + if (names[ptr] == NULL) + goto fail; + strcpy(names[ptr], p); + ptr++; + + continue; + bad: + fprintf(stderr, "%s: ignoring malformatted line in %s: %zu\n", argv0, pathname, lineno); + } + + if (fclose(f) < 0) + { + f = NULL; + goto fail; + } + close(fd); + free(line); + return 0; + fail: + saved_errno = errno; + free(line); + if (f != NULL) + fclose(f); + if (fd >= 0) + close(fd); + errno = saved_errno; + return -1; +} + + +/** + * This function is called after the last + * call to `handle_opt` + * + * @param argc The number of unparsed arguments + * @param argv `NULL` terminated list of unparsed arguments + * @param prio The argument associated with the "-p" option + * @return Zero on success, -1 on error + */ +int handle_args(int argc, char* argv[], char* prio) +{ + int free_fflag = 0, saved_errno; + int q = xflag + dflag; + if ((q > 1) || (fflag && argc) || (xflag && ((fflag != NULL) || (argc > 0) || (prio != NULL)))) + usage(); + if (argc == 1) + { + if (parse_double(&rgamma, argv[0]) < 0) + usage(); + bgamma = ggamma = rgamma; + } + else if (argc == 3) + { + if (parse_double(&rgamma, argv[0]) < 0) + usage(); + if (parse_double(&ggamma, argv[1]) < 0) + usage(); + if (parse_double(&bgamma, argv[2]) < 0) + usage(); + } + else if (argc) + usage(); + if (!argc && !fflag && !xflag) + { + fflag = get_conf_file("gamma"); + if (fflag == NULL) + return -1; + free_fflag = 1; + } + if (fflag) + if (parse_gamma_file(fflag) < 0) + goto fail; + if (free_fflag) + free(fflag), fflag = NULL; + return 0; + fail: + saved_errno = errno; + if (free_fflag) + free(fflag), fflag = NULL; + errno = saved_errno; + return cleanup(-1); +} + + +/** + * Fill a filter + * + * @param filter The filter to fill + * @param r The red gamma + * @param g The green gamma + * @param b The blue gamma + */ +static void fill_filter(libcoopgamma_filter_t* restrict filter, double r, double g, double b) +{ + switch (filter->depth) + { +#define X(CONST, MEMBER, MAX, TYPE)\ + case CONST:\ + libclut_gamma(&(filter->ramps.MEMBER), MAX, TYPE, r, g, b);\ + break; +LIST_DEPTHS +#undef X + default: + abort(); + } +} + + +/** + * The main function for the program-specific code + * + * @return 0: Success + * -1: Error, `errno` set + * -2: Error, `cg.error` set + * -3: Error, message already printed + */ +int start(void) +{ + int r; + size_t i, j; + + if (xflag) + for (i = 0; i < filters_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_REMOVE; + else if (dflag) + for (i = 0; i < filters_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_DEATH; + else + for (i = 0; i < filters_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_REMOVAL; + + if (!xflag && (names != NULL)) + if ((r = make_slaves()) < 0) + return cleanup(r); + + if (names == NULL) + for (i = 0, r = 1; i < filters_n; i++) + { + if (!(crtc_updates[i].master) || !(crtc_info[crtc_updates[i].crtc].supported)) + continue; + if (!xflag) + fill_filter(&(crtc_updates[i].filter), rgamma, ggamma, bgamma); + r = update_filter(i, 0); + if ((r == -2) || ((r == -1) && (errno != EAGAIN))) + return cleanup(r); + if (crtc_updates[i].slaves != NULL) + for (j = 0; crtc_updates[i].slaves[j] != 0; j++) + { + r = update_filter(crtc_updates[i].slaves[j], 0); + if ((r == -2) || ((r == -1) && (errno != EAGAIN))) + return cleanup(r); + } + } + else + for (i = 0, r = 1; i < filters_n; i++) + { + if (!(crtc_info[crtc_updates[i].crtc].supported)) + continue; + for (j = 0; names[j] != NULL; j++) + if (!strcasecmp(crtc_updates[i].filter.crtc, names[j])) + { + fill_filter(&(crtc_updates[i].filter), rgammas[j], ggammas[j], bgammas[j]); + r = update_filter(i, 0); + if ((r == -2) || ((r == -1) && (errno != EAGAIN))) + return cleanup(r); + break; + } + } + + while (r != 1) + if ((r = synchronise(-1)) < 0) + return cleanup(r); + + if (!dflag) + return cleanup(0); + + if (libcoopgamma_set_nonblocking(&cg, 0) < 0) + return cleanup(-1); + for (;;) + if (libcoopgamma_synchronise(&cg, NULL, 0, &j) < 0) + switch (errno) + { + case 0: + break; + case ENOTRECOVERABLE: + goto enotrecoverable; + default: + return cleanup(-1); + } + + enotrecoverable: + for (;;) + if (pause() < 0) + return cleanup(-1); +} diff --git a/cg-icc.1 b/cg-icc.1 new file mode 100644 index 0000000..c2f1d5b --- /dev/null +++ b/cg-icc.1 @@ -0,0 +1,125 @@ +.TH CG-ICC 1 CG-TOOLS +.SH NAME +cg-icc - Apply ICC profiles to the monitors +.SH SYNOPSIS +.B cg-icc +.RB [ \-M +.IR method ] +.RB [ \-S +.IR site ] +.RB [ \-c +.IR crtc "]... ["\fB\-R\fP +.IR rule ] +.RB ( \-x +| +.RB [ \-p +.IR priority ] +.RB [ \-d ] +.RI [ file ]) +.SH DESCRIPTION +.B cg-icc +applies the ICC profile +.I file +to the monitors. If +.I file +is not specified, +.B cg-icc +reads a default ICC profile table file and applies +the appropriate profiles to each monitor, which as a +profile, independently. +.P +The file is a 2-column text file where empty lines and lines +starting with a +.RB ' # ', +after any whitespace, are ignored. The +first value is a monitor's EDID, and the second value is the +ICC file for that monitor. +.SH OPTIONS +.TP +.BR \-c " "\fIcrtc\fP +Apply the filter to the CRTC with the monitor whose EDID is +.IR crtc . +By default, the filter is applied to all monitors. + +If +.I crtc +is +.RB ' ? ', +all available CRTC's are listed. +.TP +.B \-d +Keep the process alive and remove the filter on death. +.TP +.BR \-M " "\fImethod\fP +Adjustment method name or number. Recognised names include: +.TS +tab(:); +l l. +\fBdummy\fP:Dummy method +\fBrandr\fP:X RAndR +\fBvidmode\fP:X VidMode +\fBdrm\fP:Linux DRM +\fBgdi\fP:Windows GDI +\fBquartz\fP:Quartz Core Graphics +.TE + +The adjustment methods are supported via +.BR libgamma (7). +Only methods that were enabled when +.B libgamma +was compiled will be supported. + +If +.I method +is +.RB ' ? ', +all available adjustment methods are printed. +.TP +.BR \-p " "\fIpriority\fP +Set the priority of the filter. Filters with higher priority +are applied before filters with lower priority. The value +must be a signed 64-bit integer (between \-9223372036854775807 +and 9223372036854775807). +.BR cg-icc 's +default priority is 0. + +If +.I priority +is +.RB ' ? ', +the utility's default priority is printed. +.TP +.BR \-R " "\fIrule\fP +Set the rule of of the filter to +.IR rule . +This is the last part of the filter's identifier (class). +The default rule is +.BR standard . + +If +.I rule +is +.RB ' ? ' +the utility's default rule is printed. If +.I rule +is +.RB ' ?? ' +the utility's default class is printed. +.TP +.BR \-S " "\fIsite\fP +Select the site to which to connect. For example +.RB ' :0 ', +for local display 0 when using +.BR X . +.TP +.B \-x +Remove the currently applied filter. +.SH FILES +.TP +.B ~/.config/icctab +The default ICC profile table file. +.TP +.B /etc/icctab +The fallback ICC profile table file. +.SH SEE ALSO +.BR cg-tools (7) diff --git a/cg-icc.c b/cg-icc.c new file mode 100644 index 0000000..61bcdb6 --- /dev/null +++ b/cg-icc.c @@ -0,0 +1,928 @@ +/* See LICENSE file for copyright and license details. */ +#include "cg-base.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + + +/* Note, that EDID:s are 256 hexadecimals long, and + * a filename can only be 255 characters long. */ + + + +/** + * Magic number for dual-byte precision lookup table based profiles + */ +#define MLUT_TAG 0x6D4C5554L + +/** + * Magic number for gamma–brightness–contrast based profiles + * and for variable precision lookup table profiles + */ +#define VCGT_TAG 0x76636774L + +/** + * The filename of the configuration file + */ +#define ICCTAB "icctab" + + + +/** + * The default filter priority for the program + */ +const int64_t default_priority = 0; + +/** + * The default class for the program + */ +char default_class[] = PKGNAME "::cg-icc::standard"; + +/** + * Class suffixes + */ +const char* const* class_suffixes = (const char* const[]){NULL}; + + + +/** + * -d: keep process alive and remove filter on death + */ +static int dflag = 0; + +/** + * -x: remove filter rather than adding it + */ +static int xflag = 0; + +/** + * The panhame of the selected ICC profile + */ +static const char* icc_pathname = NULL; + +/** + * Gamma ramps loaded from `icc_pathname` + */ +static libcoopgamma_ramps_t uniramps; + +/** + * The datatype of the stops in the ramps of `uniramps` + */ +static libcoopgamma_depth_t unidepth = 0; + +/** + * Parsed ICC profiles for each CRTC + */ +static libcoopgamma_ramps_t* rampses = NULL; + +/** + * The datatype of the stops in the ramps of + * corresponding element in `rampses` + */ +static libcoopgamma_depth_t* depths = NULL; + +/** + * File descriptor for configuration directory + */ +static int confdirfd = -1; + +/** + * List of CRTC:s + */ +static char** crtc_icc_keys = NULL; + +/** + * List of ICC profile pathnames for corresponding + * CRTC in `crtc_icc_keys` + */ +static char** crtc_icc_values = NULL; + + + +/** + * Print usage information and exit + */ +void usage(void) +{ + fprintf(stderr, + "Usage: %s [-M method] [-S site] [-c crtc]... [-R rule] " + "(-x | [-p priority] [-d] [file])\n", + argv0); + exit(1); +} + + +/** + * Perform cleanup so valgrind output is clean + * + * @param ret The value to return + * @return `ret` is returned as is + */ +static int cleanup(int ret) +{ + int saved_errno = errno; + size_t i; + libcoopgamma_ramps_destroy(&uniramps); + if (confdirfd >= 0) + close(confdirfd); + if (rampses != NULL) + for (i = 0; i < crtcs_n; i++) + libcoopgamma_ramps_destroy(rampses + i); + free(rampses); + free(depths); + if (crtc_icc_keys != NULL) + for (i = 0; crtc_icc_keys[i] != NULL; i++) + free(crtc_icc_keys[i]); + free(crtc_icc_keys); + if (crtc_icc_values != NULL) + for (i = 0; crtc_icc_values[i] != NULL; i++) + free(crtc_icc_values[i]); + free(crtc_icc_values); + errno = saved_errno; + return ret; +} + + +/** + * Handle a command line option + * + * @param opt The option, it is a NUL-terminate two-character + * string starting with either '-' or '+', if the + * argument is not recognised, call `usage`. This + * string will not be "-M", "-S", "-c", "-p", or "-R". + * @param arg The argument associated with `opt`, + * `NULL` there is no next argument, if this + * parameter is `NULL` but needed, call `usage` + * @return 0 if `arg` was not used, + * 1 if `arg` was used, + * -1 on error + */ +int handle_opt(char* opt, char* arg) +{ + if (opt[0] == '-') + switch (opt[1]) + { + case 'd': + if (dflag || xflag) + usage(); + dflag = 1; + break; + case 'x': + if (xflag || dflag) + usage(); + xflag = 1; + break; + default: + usage(); + } + return 0; + (void) arg; +} + + +/** + * Populate `crtc_icc_keys` and `crtc_icc_value` + * + * @path fd File descriptor for the ICC profile table + * @path dirname The dirname of the ICC profile table + * @return Zero on success, -1 on error + */ +static int load_icc_table(int fd, const char *dirname) +{ + FILE *fp; + ssize_t len; + size_t lineno = 1, size = 0; + char *p, *q, *line = NULL; + int saved_errno; + size_t ptr = 0, siz = 0; + void *new; + size_t dirname_len = strlen(dirname); + fp = fdopen(fd, "rb"); + if (fp == NULL) + return -1; + for (; len = getline(&line, &size, fp), len >= 0; lineno++) + { + if (len && line[len - 1] == '\n') + line[--len] = '\0'; + p = line + strspn(line, " \t"); + if (!*p || (*p == '#')) + continue; + q = p + strspn(p, "0123456789abcdefABCDEF"); + if ((*q != ' ' && *q != '\t')) + { + fprintf(stderr, "%s: warning: line %zu is malformated in %s/%s\n", + argv0, lineno, dirname, ICCTAB); + continue; + } + *q = '\0'; + if ((size_t)(q - p) != 256) + fprintf(stderr, "%s: warning: EDID on line %zu in %s/%s looks to be of wrong length: %s\n", + argv0, lineno, dirname, ICCTAB, p); + q++; + q += strspn(p, " \t"); + if (!*q) + { + fprintf(stderr, "%s: warning: line %zu is malformated in %s/%s\n", + argv0, lineno, dirname, ICCTAB); + continue; + } + if (strchr(" \t", strchr(q, '\0')[-1])) + fprintf(stderr, "%s: warning: filename on line %zu in %s/%s ends with white space: %s\n", + argv0, lineno, dirname, ICCTAB, q); + if (ptr == siz) + { + new = realloc(crtc_icc_keys, (siz + 5) * sizeof(*crtc_icc_keys)); + if (new == NULL) + goto fail; + crtc_icc_keys = new; + new = realloc(crtc_icc_values, (siz + 5) * sizeof(*crtc_icc_values)); + if (new == NULL) + goto fail; + crtc_icc_values = new; + siz += 4; + } + crtc_icc_values[ptr] = malloc((*q == '/' ? 1 : dirname_len + sizeof("/")) + strlen(q)); + if (crtc_icc_values[ptr] == NULL) + goto fail; + if (*q == '/') + strcpy(crtc_icc_values[ptr], q); + else + stpcpy(stpcpy(stpcpy(crtc_icc_values[ptr], dirname), "/"), q); + crtc_icc_keys[ptr] = malloc(strlen(p) + 1); + if (crtc_icc_keys[ptr] == NULL) + { + ptr++; + goto fail; + } + strcpy(crtc_icc_keys[ptr], p); + ptr++; + } + if (ferror(fp)) + goto fail; + if (crtc_icc_keys != NULL) + crtc_icc_keys[ptr] = NULL; + if (crtc_icc_values != NULL) + crtc_icc_values[ptr] = NULL; + fclose(fp); + free(line); + return 0; + fail: + saved_errno = errno; + if (crtc_icc_keys != NULL) + crtc_icc_keys[ptr] = NULL; + if (crtc_icc_values != NULL) + crtc_icc_values[ptr] = NULL; + fclose(fp); + free(line); + errno = saved_errno; + return -1; +} + + +/** + * This function is called after the last + * call to `handle_opt` + * + * @param argc The number of unparsed arguments + * @param argv `NULL` terminated list of unparsed arguments + * @param prio The argument associated with the "-p" option + * @return Zero on success, -1 on error + */ +int handle_args(int argc, char* argv[], char* prio) +{ + struct passwd* pw; + char* path = NULL; + int saved_errno; + int fd = -1, q = xflag + dflag; + if ((q > 1) || (xflag && ((argc > 0) || (prio != NULL))) || (argc > 1)) + usage(); + icc_pathname = *argv; + memset(&uniramps, 0, sizeof(uniramps)); + if (!xflag && (icc_pathname == NULL)) + { + pw = getpwuid(getuid()); + if ((pw == NULL) || (pw->pw_dir == NULL)) + goto fail; + + path = malloc(strlen(pw->pw_dir) + sizeof("/.config")); + if (path == NULL) + goto fail; + + sprintf(path, "%s/.config", pw->pw_dir); + + if (access(path, F_OK) < 0) + sprintf(path, "/etc"); + + confdirfd = open(path, O_DIRECTORY); + if (confdirfd < 0) + goto fail; + + fd = openat(confdirfd, ICCTAB, O_RDONLY); + if (fd < 0) + goto fail; + + if (load_icc_table(fd, path) < 0) + goto fail; + + free(path), path = NULL; + close(fd), fd = -1; + } + return 0; + fail: + saved_errno = errno; + free(path), path = NULL; + if (fd >= 0) + close(fd); + errno = saved_errno; + return cleanup(-1); +} + + +/** + * Read an unsigned 64-bit integer + * + * @param content The beginning of the encoded integer + * @return The integer, decoded + */ +static uint64_t icc_uint64(const char* restrict content) +{ + uint64_t rc; + rc = (uint64_t)(unsigned char)(content[0]), rc <<= 8; + rc |= (uint64_t)(unsigned char)(content[1]), rc <<= 8; + rc |= (uint64_t)(unsigned char)(content[2]), rc <<= 8; + rc |= (uint64_t)(unsigned char)(content[3]), rc <<= 8; + rc |= (uint64_t)(unsigned char)(content[4]), rc <<= 8; + rc |= (uint64_t)(unsigned char)(content[5]), rc <<= 8; + rc |= (uint64_t)(unsigned char)(content[6]), rc <<= 8; + rc |= (uint64_t)(unsigned char)(content[7]); + return rc; +} + + +/** + * Read an unsigned 32-bit integer + * + * @param content The beginning of the encoded integer + * @return The integer, decoded + */ +static uint32_t icc_uint32(const char* restrict content) +{ + uint32_t rc; + rc = (uint32_t)(unsigned char)(content[0]), rc <<= 8; + rc |= (uint32_t)(unsigned char)(content[1]), rc <<= 8; + rc |= (uint32_t)(unsigned char)(content[2]), rc <<= 8; + rc |= (uint32_t)(unsigned char)(content[3]); + return rc; +} + + +/** + * Read an unsigned 16-bit integer + * + * @param content The beginning of the encoded integer + * @return The integer, decoded + */ +static uint16_t icc_uint16(const char* restrict content) +{ + uint16_t rc; + rc = (uint16_t)(unsigned char)(content[0]), rc <<= 8; + rc |= (uint16_t)(unsigned char)(content[1]); + return rc; +} + + +/** + * Read an unsigned 8-bit integer + * + * @param content The beginning of the encoded integer + * @return The integer, decoded + */ +static uint16_t icc_uint8(const char* restrict content) +{ + return (uint8_t)(content[0]); +} + + +/** + * Read a floating-point value + * + * @param content The beginning of the encoded value + * @param width The number of bytes with which the value is encoded + * @return The value, decoded + */ +static double icc_double(const char* restrict content, size_t width) +{ + double ret = 0; + size_t i; + for (i = 0; i < width; i++) + { + ret /= 256; + ret += (double)(unsigned char)(content[width - 1 - i]); + } + ret /= 255; + return ret; +} + + +/** + * Parse an ICC profile + * + * @param content The content of the ICC profile file + * @param n The byte-size of `content` + * @param ramps Output parameter for the filter stored in the ICC profile, + * `.red_size`, `.green_size`, `.blue_size` should already be + * set (these values can however be modified.) + * @param depth Output parameter for ramps stop value type + * @return Zero on success, -1 on error, -2 if no usable data is + * available in the profile. + */ +static int parse_icc(const char* restrict content, size_t n, libcoopgamma_ramps_t* ramps, + libcoopgamma_depth_t* depth) +{ + uint32_t i_tag, n_tags; + size_t i, ptr = 0, xptr; + + /* Skip header */ + if (n - ptr < 128) + return -2; + ptr += 128; + + /* Get the number of tags */ + if (n - ptr < 4) + return -2; + n_tags = icc_uint32(content + ptr), ptr += 4; + + for (i_tag = 0, xptr = ptr; i_tag < n_tags; i_tag++, ptr = xptr) + { + uint32_t tag_name, tag_offset, tag_size, gamma_type; + + /* Get profile encoding type, offset to the profile and the encoding size of its data */ + if (n - ptr < 12) + return -2; + tag_name = icc_uint32(content + ptr), ptr += 4; + tag_offset = icc_uint32(content + ptr), ptr += 4; + tag_size = icc_uint32(content + ptr), ptr += 4; + xptr = ptr; + + /* Jump to the profile data */ + if (tag_offset > INT32_MAX - tag_size) + return -2; + if (tag_offset + tag_size > n) + return -2; + ptr = tag_offset; + + if (tag_name == MLUT_TAG) + { + /* The profile is encododed as an dual-byte precision lookup table */ + + /* Initialise ramps */ + *depth = LIBCOOPGAMMA_UINT16; + ramps->u16.red_size = 256; + ramps->u16.green_size = 256; + ramps->u16.blue_size = 256; + if (libcoopgamma_ramps_initialise(&(ramps->u16)) < 0) + return -1; + + /* Get the lookup table */ + if (n - ptr < 3 * 256 * 2) + continue; + for (i = 0; i < 256; i++) + ramps->u16.red[i] = icc_uint16(content + ptr), ptr += 2; + for (i = 0; i < 256; i++) + ramps->u16.green[i] = icc_uint16(content + ptr), ptr += 2; + for (i = 0; i < 256; i++) + ramps->u16.blue[i] = icc_uint16(content + ptr), ptr += 2; + + return 0; + } + else if (tag_name == VCGT_TAG) + { + /* The profile is encoded as with gamma, brightness and contrast values + * or as a variable precision lookup table profile */ + + /* VCGT profiles starts where their magic number */ + if (n - ptr < 4) + continue; + tag_name = icc_uint32(content + ptr), ptr += 4; + if (tag_name != VCGT_TAG) + continue; + + /* Skip four bytes */ + if (n - ptr < 4) + continue; + ptr += 4; + + /* Get the actual encoding type */ + if (n - ptr < 4) + continue; + gamma_type = icc_uint32(content + ptr), ptr += 4; + + if (gamma_type == 0) + { + /* The profile is encoded as a variable precision lookup table */ + uint16_t n_channels, n_entries, entry_size; + + /* Get metadata */ + if (n - ptr < 3 * 4) + continue; + n_channels = icc_uint16(content + ptr), ptr += 2; + n_entries = icc_uint16(content + ptr), ptr += 2; + entry_size = icc_uint16(content + ptr), ptr += 2; + if (tag_size == 1584) + n_channels = 3, n_entries = 256, entry_size = 2; + if (n_channels != 3) + /* Assuming sRGB, can only be an correct assumption if there are exactly three channels */ + continue; + + /* Check data availability */ + if (n_channels > SIZE_MAX / n_entries) + continue; + if (entry_size > SIZE_MAX / (n_entries * n_channels)) + continue; + if (n - ptr < (size_t)n_channels * (size_t)n_entries * (size_t)entry_size) + continue; + + /* Initialise ramps */ + ramps->u8.red_size = (size_t)n_entries; + ramps->u8.green_size = (size_t)n_entries; + ramps->u8.blue_size = (size_t)n_entries; + switch (entry_size) + { + case 1: + *depth = LIBCOOPGAMMA_UINT8; + if (libcoopgamma_ramps_initialise(&(ramps->u8)) < 0) + return -1; + break; + case 2: + *depth = LIBCOOPGAMMA_UINT16; + if (libcoopgamma_ramps_initialise(&(ramps->u16)) < 0) + return -1; + break; + case 4: + *depth = LIBCOOPGAMMA_UINT32; + if (libcoopgamma_ramps_initialise(&(ramps->u32)) < 0) + return -1; + break; + case 8: + *depth = LIBCOOPGAMMA_UINT64; + if (libcoopgamma_ramps_initialise(&(ramps->u64)) < 0) + return -1; + break; + default: + *depth = LIBCOOPGAMMA_DOUBLE; + if (libcoopgamma_ramps_initialise(&(ramps->d)) < 0) + return -1; + break; + } + + /* Get the lookup table */ + switch (*depth) + { + case LIBCOOPGAMMA_UINT8: + for (i = 0; i < ramps->u8.red_size; i++) + ramps->u8.red[i] = icc_uint8(content + ptr), ptr += 1; + for (i = 0; i < ramps->u8.green_size; i++) + ramps->u8.green[i] = icc_uint8(content + ptr), ptr += 1; + for (i = 0; i < ramps->u8.blue_size; i++) + ramps->u8.blue[i] = icc_uint8(content + ptr), ptr += 1; + break; + case LIBCOOPGAMMA_UINT16: + for (i = 0; i < ramps->u16.red_size; i++) + ramps->u16.red[i] = icc_uint16(content + ptr), ptr += 2; + for (i = 0; i < ramps->u16.green_size; i++) + ramps->u16.green[i] = icc_uint16(content + ptr), ptr += 2; + for (i = 0; i < ramps->u16.blue_size; i++) + ramps->u16.blue[i] = icc_uint16(content + ptr), ptr += 2; + break; + case LIBCOOPGAMMA_UINT32: + for (i = 0; i < ramps->u32.red_size; i++) + ramps->u32.red[i] = icc_uint32(content + ptr), ptr += 4; + for (i = 0; i < ramps->u32.green_size; i++) + ramps->u32.green[i] = icc_uint32(content + ptr), ptr += 4; + for (i = 0; i < ramps->u32.blue_size; i++) + ramps->u32.blue[i] = icc_uint32(content + ptr), ptr += 4; + break; + case LIBCOOPGAMMA_UINT64: + for (i = 0; i < ramps->u64.red_size; i++) + ramps->u64.red[i] = icc_uint64(content + ptr), ptr += 8; + for (i = 0; i < ramps->u64.green_size; i++) + ramps->u64.green[i] = icc_uint64(content + ptr), ptr += 8; + for (i = 0; i < ramps->u64.blue_size; i++) + ramps->u64.blue[i] = icc_uint64(content + ptr), ptr += 8; + break; + default: + for (i = 0; i < ramps->d.red_size; i++) + ramps->d.red[i] = icc_double(content + ptr, entry_size), ptr += entry_size; + for (i = 0; i < ramps->d.green_size; i++) + ramps->d.green[i] = icc_double(content + ptr, entry_size), ptr += entry_size; + for (i = 0; i < ramps->d.blue_size; i++) + ramps->d.blue[i] = icc_double(content + ptr, entry_size), ptr += entry_size; + break; + } + + return 0; + } + else if (gamma_type == 1) + { + /* The profile is encoded with gamma, brightness and contrast values */ + double r_gamma, r_min, r_max, g_gamma, g_min, g_max, b_gamma, b_min, b_max; + + /* Get the gamma, brightness and contrast */ + if (n - ptr < 9 * 4) + continue; + r_gamma = icc_uint32(content + ptr), r_gamma /= 65536L, ptr += 4; + r_min = icc_uint32(content + ptr), r_min /= 65536L, ptr += 4; + r_max = icc_uint32(content + ptr), r_max /= 65536L, ptr += 4; + g_gamma = icc_uint32(content + ptr), g_gamma /= 65536L, ptr += 4; + g_min = icc_uint32(content + ptr), g_min /= 65536L, ptr += 4; + g_max = icc_uint32(content + ptr), g_max /= 65536L, ptr += 4; + b_gamma = icc_uint32(content + ptr), b_gamma /= 65536L, ptr += 4; + b_min = icc_uint32(content + ptr), b_min /= 65536L, ptr += 4; + b_max = icc_uint32(content + ptr), b_max /= 65536L, ptr += 4; + + /* Initialise ramps */ + *depth = LIBCOOPGAMMA_DOUBLE; + if (libcoopgamma_ramps_initialise(&(ramps->d)) < 0) + return -1; + + /* Set ramps */ + libclut_start_over(&(ramps->d), (double)1, double, 1, 1, 1); + libclut_gamma(&(ramps->d), (double)1, double, r_gamma, g_gamma, b_gamma); + libclut_rgb_limits(&(ramps->d), (double)1, double, r_min, r_max, g_min, g_max, b_min, b_max); + + return 0; + } + } + } + + return -2; +} + + +/** + * Load an ICC profile + * + * @param file The ICC-profile file + * @param ramps Output parameter for the filter stored in the ICC profile, + * `.red_size`, `.green_size`, `.blue_size` should already be + * set (these values can however be modified.) + * @param depth Output parameter for ramps stop value type + * @return Zero on success, -1 on error, -2 if no usable data is + * available in the profile. + */ +static int load_icc(const char* file, libcoopgamma_ramps_t* ramps, libcoopgamma_depth_t* depth) +{ + char* content = NULL; + size_t ptr = 0, size = 0; + ssize_t got; + int fd = -1, r = -1, saved_errno; + + fd = open(file, O_RDONLY); + if (fd < 0) + { + if (errno == ENOENT) + { + fprintf(stderr, "%s: %s: %s\n", argv0, strerror(ENOENT), file); + errno = 0; + } + goto fail; + } + + for (;;) + { + if (ptr == size) + { + size_t new_size = size ? (size << 1) : 4098; + void* new = realloc(content, new_size); + if (new == NULL) + goto fail; + content = new; + size = new_size; + } + got = read(fd, content + ptr, size - ptr); + if (got < 0) + { + if (errno == EINTR) + continue; + goto fail; + } + if (got == 0) + break; + ptr += (size_t)got; + } + + close(fd), fd = -1; + + r = parse_icc(content, ptr, ramps, depth); + fail: + saved_errno = errno; + if (fd >= 0) + close(fd); + free(content); + errno = saved_errno; + return r; +} + + +/** + * Get the pathname of the ICC profile for a CRTC + * + * @param crtc The CRTC name + * @return The ICC profile file + */ +static const char* get_icc(const char* crtc) +{ + size_t i; + if (crtc_icc_keys != NULL) + for (i = 0; crtc_icc_keys[i] != NULL; i++) + if (!strcasecmp(crtc, crtc_icc_keys[i])) + return crtc_icc_values[i]; + return NULL; +} + + +/** + * Fill a filter + * + * @param filter The filter to fill + * @param ramps The prototype filter + * @param depth The prototype filter's stop datatype + */ +static void fill_filter(libcoopgamma_filter_t* filter, const libcoopgamma_ramps_t* ramps, + libcoopgamma_depth_t depth) +{ + switch (filter->depth) + { +#define X(CONST, MEMBER, MAX, TYPE)\ + case CONST:\ + switch (depth)\ + {\ + case LIBCOOPGAMMA_UINT8:\ + libclut_translate(&(filter->ramps.MEMBER), MAX, TYPE, &(ramps->u8), UINT8_MAX, uint8_t);\ + break;\ + case LIBCOOPGAMMA_UINT16:\ + libclut_translate(&(filter->ramps.MEMBER), MAX, TYPE, &(ramps->u16), UINT16_MAX, uint16_t);\ + break;\ + case LIBCOOPGAMMA_UINT32:\ + libclut_translate(&(filter->ramps.MEMBER), MAX, TYPE, &(ramps->u32), UINT32_MAX, uint32_t);\ + break;\ + case LIBCOOPGAMMA_UINT64:\ + libclut_translate(&(filter->ramps.MEMBER), MAX, TYPE, &(ramps->u64), UINT64_MAX, uint64_t);\ + break;\ + case LIBCOOPGAMMA_FLOAT:\ + libclut_translate(&(filter->ramps.MEMBER), MAX, TYPE, &(ramps->f), (float)1, float);\ + break;\ + case LIBCOOPGAMMA_DOUBLE:\ + libclut_translate(&(filter->ramps.MEMBER), MAX, TYPE, &(ramps->d), (double)1, double);\ + break;\ + }\ + break; +LIST_DEPTHS +#undef X + default: + abort(); + } +} + + +/** + * The main function for the program-specific code + * + * @return 0: Success + * -1: Error, `errno` set + * -2: Error, `cg.error` set + * -3: Error, message already printed + */ +int start(void) +{ + int r; + size_t i, j; + const char* path; + + if (xflag) + for (i = 0; i < crtcs_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_REMOVE; + else if (dflag) + for (i = 0; i < crtcs_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_DEATH; + else + for (i = 0; i < crtcs_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_REMOVAL; + + if (!xflag && (icc_pathname == NULL)) + if ((r = make_slaves()) < 0) + return cleanup(r); + + if (icc_pathname != NULL) + { + uniramps.u8.red_size = uniramps.u8.green_size = uniramps.u8.blue_size = 1; + for (i = 0; i < crtcs_n; i++) + { + if (uniramps.u8.red_size < crtc_updates[i].filter.ramps.u8.red_size) + uniramps. u8.red_size = crtc_updates[i].filter.ramps.u8.red_size; + if (uniramps.u8.green_size < crtc_updates[i].filter.ramps.u8.green_size) + uniramps. u8.green_size = crtc_updates[i].filter.ramps.u8.green_size; + if (uniramps.u8.blue_size < crtc_updates[i].filter.ramps.u8.blue_size) + uniramps. u8.blue_size = crtc_updates[i].filter.ramps.u8.blue_size; + } + switch (load_icc(icc_pathname, &uniramps, &unidepth)) + { + case 0: + break; + case -1: + return cleanup(-1); + case -2: + fprintf(stderr, "%s: unusable ICC profile: %s\n", argv0, icc_pathname); + return cleanup(-3); + } + } + else + { + rampses = calloc(crtcs_n, sizeof(*rampses)); + if (rampses == NULL) + return cleanup(-1); + depths = malloc(crtcs_n * sizeof(*depths)); + if (depths == NULL) + return cleanup(-1); + for (i = 0; i < crtcs_n; i++) + { + rampses[i].u8.red_size = crtc_updates[i].filter.ramps.u8.red_size; + rampses[i].u8.green_size = crtc_updates[i].filter.ramps.u8.green_size; + rampses[i].u8.blue_size = crtc_updates[i].filter.ramps.u8.blue_size; + path = get_icc(crtc_updates[i].filter.crtc); + if (path == NULL) + { + /* TODO remove CRTC */ + } + else + switch (load_icc(path, rampses + i, depths + i)) + { + case 0: + break; + case -1: + return cleanup(-1); + case -2: + fprintf(stderr, "%s: unusable ICC profile: %s\n", argv0, path); + return cleanup(-3); + } + } + } + + for (i = 0, r = 1; i < crtcs_n; i++) + { + if (!(crtc_updates[i].master) || !(crtc_info[i].supported)) + continue; + if (!xflag) + { + if (icc_pathname != NULL) + fill_filter(&(crtc_updates[i].filter), &uniramps, unidepth); + else + fill_filter(&(crtc_updates[i].filter), rampses + i, depths[i]); + } + r = update_filter(i, 0); + if ((r == -2) || ((r == -1) && (errno != EAGAIN))) + return cleanup(r); + if (crtc_updates[i].slaves != NULL) + for (j = 0; crtc_updates[i].slaves[j] != 0; j++) + { + r = update_filter(crtc_updates[i].slaves[j], 0); + if ((r == -2) || ((r == -1) && (errno != EAGAIN))) + return cleanup(r); + } + } + + while (r != 1) + if ((r = synchronise(-1)) < 0) + return cleanup(r); + + if (!dflag) + return cleanup(0); + + if (libcoopgamma_set_nonblocking(&cg, 0) < 0) + return cleanup(-1); + for (;;) + if (libcoopgamma_synchronise(&cg, NULL, 0, &j) < 0) + switch (errno) + { + case 0: + break; + case ENOTRECOVERABLE: + goto enotrecoverable; + default: + return cleanup(-1); + } + + enotrecoverable: + for (;;) + if (pause() < 0) + return cleanup(-1); +} diff --git a/cg-limits.1 b/cg-limits.1 new file mode 100644 index 0000000..9ac16b4 --- /dev/null +++ b/cg-limits.1 @@ -0,0 +1,161 @@ +.TH CG-LIMITS 1 CG-TOOLS +.SH NAME +cg-limits - Adjust the brightness and contrast on the monitors +.SH SYNOPSIS +.B cg-limits +.RB [ \-M +.IR method ] +.RB [ \-S +.IR site ] +.RB [ \-c +.IR crtc "]... ["\fB\-R\fP +.IR rule ] +.RB ( \-x +| +.RB [ \-p +.IR priority ] +.RB [ \-d ] +.RB ([ \-B +.IR brightness-file ] +.RB [ \-C +.IR contrast-file ] +| +.IB brightness-all : contrast-all +| +.IB brightness-red : contrast-red +.IB brightness-green : contrast-green +.IR brightness-blue \fB:\fP contrast-blue )) +.SH DESCRIPTION +.B cg-limits +sets the brightness (the brightness of black) on the monitors to +.I brightness-all +and the contrast (the brightness of white) on the monitors to +.I contrast-all +on all channels, or the brightness to +.IR brightness-red , +.IR brightness-green , +and +.I brightness-blue +to the red, green, and blue channels, respectively, and the +contrasts to +.IR contrast-red , +.IR contrast-green , +and +.IR contrast-blue . +Alternatively, +.B cg-limits +can change the brightness on each monitor independently by +using reading the values from +.I brightness-file +and the contrast on each monitor independently by +using reading the values from +.I contrast-file +or default files. +.P +The files are 4-column text files where empty lines and lines +starting with a +.RB ' # ', +after any whitespace, are ignored. +The values in the columns should be, in order, a monitor's +EDID, that monitor's red value, green value, and blue value. +.SH OPTIONS +.TP +.B \-B " "\fIbrightness-file\fP +Read the brightness values from the selected file. +.TP +.B \-C " "\fIbrightness-file\fP +Read the contrast values from the selected file. +.TP +.BR \-c " "\fIcrtc\fP +Apply the filter to the CRTC with the monitor whose EDID is +.IR crtc . +By default, the filter is applied to all monitors. + +If +.I crtc +is +.RB ' ? ', +all available CRTC's are listed. +.TP +.B \-d +Keep the process alive and remove the filter on death. +.TP +.BR \-M " "\fImethod\fP +Adjustment method name or number. Recognised names include: +.TS +tab(:); +l l. +\fBdummy\fP:Dummy method +\fBrandr\fP:X RAndR +\fBvidmode\fP:X VidMode +\fBdrm\fP:Linux DRM +\fBgdi\fP:Windows GDI +\fBquartz\fP:Quartz Core Graphics +.TE + +The adjustment methods are supported via +.BR libgamma (7). +Only methods that were enabled when +.B libgamma +was compiled will be supported. + +If +.I method +is +.RB ' ? ', +all available adjustment methods are printed. +.TP +.BR \-p " "\fIpriority\fP +Set the priority of the filter. Filters with higher priority +are applied before filters with lower priority. The value +must be a signed 64-bit integer (between \-9223372036854775807 +and 9223372036854775807). +.BR cg-limits 's +default priority is \-4611686018427387904. + +If +.I priority +is +.RB ' ? ', +the utility's default priority is printed. +.TP +.BR \-R " "\fIrule\fP +Set the rule of of the filter to +.IR rule . +This is the last part of the filter's identifier (class). +The default rule is +.BR standard . + +If +.I rule +is +.RB ' ? ' +the utility's default rule is printed. If +.I rule +is +.RB ' ?? ' +the utility's default class is printed. +.TP +.BR \-S " "\fIsite\fP +Select the site to which to connect. For example +.RB ' :0 ', +for local display 0 when using +.BR X . +.TP +.B \-x +Remove the currently applied filter. +.SH FILES +.TP +.B ~/.config/brightness +The default brightness table file. +.TP +.B ~/.config/contrast +The default contrast table file. +.TP +.B /etc/brightness +The fallback brightness table file. +.TP +.B /etc/contrast +The fallback contrast table file. +.SH SEE ALSO +.BR cg-tools (7) diff --git a/cg-limits.c b/cg-limits.c new file mode 100644 index 0000000..896505f --- /dev/null +++ b/cg-limits.c @@ -0,0 +1,714 @@ +/* See LICENSE file for copyright and license details. */ +#include "cg-base.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + + +/** + * The default filter priority for the program + */ +const int64_t default_priority = -((int64_t)1 << 62); + +/** + * The default class for the program + */ +char default_class[] = PKGNAME "::cg-limits::standard"; + +/** + * Class suffixes + */ +const char* const* class_suffixes = (const char* const[]){NULL}; + + + +/** + * -d: keep process alive and remove filter on death + */ +static int dflag = 0; + +/** + * -x: remove filter rather than adding it + */ +static int xflag = 0; + +/** + * -B: brightness listing file + */ +static char* Bflag = NULL; + +/** + * -C: constrat listing file + */ +static char* Cflag = NULL; + +/** + * The brightness of the red channel + */ +static double rbrightness = 0; + +/** + * The brightness of the green channel + */ +static double gbrightness = 0; + +/** + * The brightness of the blue channel + */ +static double bbrightness = 0; + +/** + * The contrast of the red channel + */ +static double rcontrast = 1; + +/** + * The contrast of the green channel + */ +static double gcontrast = 1; + +/** + * The contrast of the blue channel + */ +static double bcontrast = 1; + +/** + * `NULL`-terminated list of output names + * listed in the brightness configuration file + */ +static char** brightness_names = NULL; + +/** + * The brightness of the red channel on monitor + * with same index in `brightness_names` + */ +static double* rbrightnesses = NULL; + +/** + * The brightness of the green channel on monitor + * with same index in `brightness_names` + */ +static double* gbrightnesses = NULL; + +/** + * The brightness of the blue channel on monitor + * with same index in `brightness_names` + */ +static double* bbrightnesses = NULL; + +/** + * `NULL`-terminated list of output names + * listed in the contrast configuration file + */ +static char** contrast_names = NULL; + +/** + * The contrast of the red channel on monitor + * with same index in `contrast_names` + */ +static double* rcontrasts = NULL; + +/** + * The contrast of the green channel on monitor + * with same index in `contrast_names` + */ +static double* gcontrasts = NULL; + +/** + * The contrast of the blue channel on monitor + * with same index in `contrast_names` + */ +static double* bcontrasts = NULL; + + + +/** + * Print usage information and exit + */ +void usage(void) +{ + fprintf(stderr, + "Usage: %s [-M method] [-S site] [-c crtc]... [-R rule] (-x | [-p priority] [-d] " + "([-B brightness-file] [-C contrast-file] | brightness-all:contrast-all | " + "brightness-red:contrast-red brightness-green:contrast-green brightness-blue:contrast-blue))\n", + argv0); + exit(1); +} + + +/** + * Perform cleanup so valgrind output is clean + * + * @param ret The value to return + * @return `ret` is returned as is + */ +static int cleanup(int ret) +{ + int saved_errno = errno; + if (brightness_names != NULL) + { + char** p = brightness_names; + while (*p) + free(*p++); + } + free(brightness_names); + free(rbrightnesses); + free(gbrightnesses); + free(bbrightnesses); + if (contrast_names != NULL) + { + char** p = contrast_names; + while (*p) + free(*p++); + } + free(contrast_names); + free(rcontrasts); + free(gcontrasts); + free(bcontrasts); + errno = saved_errno; + return ret; +} + + +/** + * Handle a command line option + * + * @param opt The option, it is a NUL-terminate two-character + * string starting with either '-' or '+', if the + * argument is not recognised, call `usage`. This + * string will not be "-M", "-S", "-c", "-p", or "-R". + * @param arg The argument associated with `opt`, + * `NULL` there is no next argument, if this + * parameter is `NULL` but needed, call `usage` + * @return 0 if `arg` was not used, + * 1 if `arg` was used, + * -1 on error + */ +int handle_opt(char* opt, char* arg) +{ + if (opt[0] == '-') + switch (opt[1]) + { + case 'd': + if (dflag || xflag) + usage(); + dflag = 1; + break; + case 'x': + if (xflag || dflag) + usage(); + xflag = 1; + break; + case 'B': + if (Bflag || !(Bflag = arg)) + usage(); + return 1; + case 'C': + if (Cflag || !(Cflag = arg)) + usage(); + return 1; + default: + usage(); + } + return 0; +} + + +/** + * Get the pathname of a configuration file + * + * @param confname The filename (excluding directory) of the configuration file + * @return The full pathname of the configuration file, `NULL` on error + */ +static char* get_conf_file(const char* restrict confname) +{ + struct passwd* pw; + char* path; + + pw = getpwuid(getuid()); + if ((pw == NULL) || (pw->pw_dir == NULL)) + return NULL; + + path = malloc(strlen(pw->pw_dir) + strlen(confname) + sizeof("/.config/")); + if (path == NULL) + return NULL; + + sprintf(path, "%s/.config/%s", pw->pw_dir, confname); + + if (access(path, F_OK) < 0) + sprintf(path, "/etc/%s", confname); + + return path; +} + + +/** + * Parse a double encoded as a string + * + * @param out Output parameter for the value + * @param str The string + * @return Zero on success, -1 if the string is invalid + */ +static int parse_double(double* restrict out, const char* restrict str) +{ + char* end; + errno = 0; + *out = strtod(str, &end); + if (errno || isinf(*out) || isnan(*out) || *end) + return -1; + if (!*str || !strchr("-0123456789.", *str)) + return -1; + return 0; +} + + +/** + * Parse two doubles encoded as a "%lf:%lf" string + * + * @param left Output parameter for the first value + * @param right Output parameter for the second value + * @param str The string + * @return Zero on success, -1 if the string is invalid + */ +static int parse_twidouble(double* restrict left, double* restrict right, const char* restrict str) +{ + char* p = strchr(str, ':'); + int r; + if (p == NULL) + return -1; + *p = '\0'; + r = -((parse_double(left, str) < 0) || (parse_double(right, p + 1) < 0)); + *p = ':'; + return r; +} + + +/** + * Parse configuration file + * + * @param pathname The pathname of the file + * @param names Reference to the list of names + * @param rs Reference to the list of red values + * @param gs Reference to the list of green values + * @param bs Reference to the list of blue values + * @return Zero on success, -1 on error + */ +static int parse_conf_file(const char* restrict pathname, char*** restrict names, + double** restrict rs, double** restrict gs, double** restrict bs) +{ + int fd, saved_errno; + char* line = NULL; + size_t size = 0, lineno = 0, ptr = 0, alloc = 0; + ssize_t n; + FILE* f = NULL; + char* p; + char* q; + char* r; + char* g; + char* b; + + fd = open(pathname, O_RDONLY); + if (fd == -1) + return -1; + + f = fdopen(fd, "rb"); + if (f == NULL) + goto fail; + + while (n = getline(&line, &size, f), n >= 0) + { + lineno += 1; + + if ((n > 0) && (line[n - 1] == '\n')) + line[n - 1] = '\0'; + p = line; + while ((*p == ' ') || (*p == '\t')) p++; + if ((!*p) || (*p == '#')) + continue; + + r = strpbrk(line, " \t"); + if (r == NULL) + goto bad; + while (r[1] == ' ' || r[1] == '\t') r++; + g = strpbrk(r + 1, " \t"); + if (g == NULL) + goto bad; + while (g[1] == ' ' || g[1] == '\t') g++; + b = strpbrk(g + 1, " \t"); + if (b == NULL) + goto bad; + while (b[1] == ' ' || b[1] == '\t') b++; + + for (;;) + { + q = strpbrk(b + 1, " \t"); + if (q == NULL) + break; + while (q[1] == ' ' || q[1] == '\t') q++; + if (!*q) + break; + r = g, g = b, b = q; + } + + *r++ = '\0'; + *g++ = '\0'; + *b++ = '\0'; + + q = strpbrk(r, " \t"); + if (q != NULL) + *q = '\0'; + q = strpbrk(g, " \t"); + if (q != NULL) + *q = '\0'; + q = strpbrk(b, " \t"); + if (q != NULL) + *q = '\0'; + + q = strchr(p, '\0'); + while ((q != p) && ((q[-1] == ' ') || (q[-1] == '\t'))) + q--; + *q = '\0'; + + if (ptr == alloc) + { + void* new; + size_t new_size = alloc ? (alloc << 1) : 4; + + new = realloc(*rs, new_size * sizeof(**rs)); + if (new == NULL) + goto fail; + *rs = new; + + new = realloc(*gs, new_size * sizeof(**gs)); + if (new == NULL) + goto fail; + *gs = new; + + new = realloc(*bs, new_size * sizeof(**bs)); + if (new == NULL) + goto fail; + *bs = new; + + new = realloc(*names, (new_size + 1) * sizeof(**names)); + if (new == NULL) + goto fail; + *names = new; + memset(*names + alloc, 0, (new_size + 1 - alloc) * sizeof(**names)); + + alloc = new_size; + } + + if ((parse_double((*rs) + ptr, r) < 0) || + (parse_double((*gs) + ptr, g) < 0) || + (parse_double((*bs) + ptr, b) < 0)) + goto bad; + (*names)[ptr] = malloc(strlen(p) + 1); + if ((*names)[ptr] == NULL) + goto fail; + strcpy((*names)[ptr], p); + ptr++; + + continue; + bad: + fprintf(stderr, "%s: ignoring malformatted line in %s: %zu\n", argv0, pathname, lineno); + } + + if (fclose(f) < 0) + { + f = NULL; + goto fail; + } + close(fd); + free(line); + return 0; + fail: + saved_errno = errno; + free(line); + if (f != NULL) + fclose(f); + if (fd >= 0) + close(fd); + errno = saved_errno; + return -1; +} + + +/** + * Parse brightness configuration file + * + * @param pathname The pathname of the file + * @return Zero on success, -1 on error + */ +static int parse_brightness_file(const char* restrict pathname) +{ + return parse_conf_file(pathname, &brightness_names, &rbrightnesses, &gbrightnesses, &bbrightnesses); +} + + +/** + * Parse contrast configuration file + * + * @param pathname The pathname of the file + * @return Zero on success, -1 on error + */ +static int parse_contrast_file(const char* restrict pathname) +{ + return parse_conf_file(pathname, &contrast_names, &rcontrasts, &gcontrasts, &bcontrasts); +} + + +/** + * This function is called after the last + * call to `handle_opt` + * + * @param argc The number of unparsed arguments + * @param argv `NULL` terminated list of unparsed arguments + * @param prio The argument associated with the "-p" option + * @return Zero on success, -1 on error + */ +int handle_args(int argc, char* argv[], char* prio) +{ + int free_Bflag = 0, free_Cflag = 0, saved_errno; + int q = xflag + dflag; + if ((q > 1) || (xflag && ((Bflag != NULL) || (Cflag != NULL) || (argc > 0) || (prio != NULL)))) + usage(); + if ((Bflag || Cflag) && argc) + usage(); + + if (argc == 1) + { + if (parse_twidouble(&rbrightness, &rcontrast, argv[0]) < 0) + usage(); + bbrightness = gbrightness = rbrightness; + bcontrast = gcontrast = rcontrast; + } + else if (argc == 3) + { + if (parse_twidouble(&rbrightness, &rcontrast, argv[0]) < 0) + usage(); + if (parse_twidouble(&gbrightness, &gcontrast, argv[1]) < 0) + usage(); + if (parse_twidouble(&bbrightness, &bcontrast, argv[2]) < 0) + usage(); + } + else if (argc) + usage(); + + if (!argc && !Bflag && !xflag) + { + Bflag = get_conf_file("brightness"); + if (Bflag == NULL) + return -1; + free_Bflag = 1; + } + if (Bflag) + if (parse_brightness_file(Bflag) < 0) + goto fail; + if (free_Bflag) + free(Bflag), Bflag = NULL; + + if (!argc && !Cflag && !xflag) + { + Cflag = get_conf_file("contrast"); + if (Cflag == NULL) + return -1; + free_Cflag = 1; + } + if (Cflag) + if (parse_contrast_file(Cflag) < 0) + goto fail; + if (free_Cflag) + free(Cflag), Cflag = NULL; + + return 0; + fail: + saved_errno = errno; + if (free_Bflag) + free(Bflag), Bflag = NULL; + if (free_Cflag) + free(Cflag), Cflag = NULL; + errno = saved_errno; + return cleanup(-1); +} + + +/** + * Fill a filter + * + * @param filter The filter to fill + * @param rb The red brightness + * @param rc The red contrast + * @param gb The green brightness + * @param gc The green contrast + * @param bb The blue brightness + * @param bc The blue contrast + * @return Zero on success, -1 on error + */ +static int fill_filter(libcoopgamma_filter_t* restrict filter, + double rb, double rc, double gb, double gc, double bb, double bc) +{ + union libcoopgamma_ramps dramps; + size_t size; + + if (filter->depth == LIBCOOPGAMMA_DOUBLE) + { + libclut_rgb_limits(&(filter->ramps.d), (double)1, double, rb, rc, gb, gc, bb, bc); + libclut_clip(&(filter->ramps.d), (double)1, double, 1, 1, 1); + return 0; + } + if (filter->depth == LIBCOOPGAMMA_FLOAT) + { + libclut_rgb_limits(&(filter->ramps.f), (float)1, float, rb, rc, gb, gc, bb, bc); + libclut_clip(&(filter->ramps.f), (float)1, float, 1, 1, 1); + return 0; + } + + size = dramps.d.red_size = filter->ramps.d.red_size; + size += dramps.d.green_size = filter->ramps.d.green_size; + size += dramps.d.blue_size = filter->ramps.d.blue_size; + dramps.d.red = calloc(size, sizeof(double)); + if (dramps.d.red == NULL) + return -1; + dramps.d.green = dramps.d.red + dramps.d.red_size; + dramps.d.blue = dramps.d.green + dramps.d.green_size; + + libclut_start_over(&(dramps.d), (double)1, double, 1, 1, 1); + libclut_rgb_limits(&(dramps.d), (double)1, double, rb, rc, gb, gc, bb, bc); + libclut_clip(&(dramps.d), (double)1, double, 1, 1, 1); + + switch (filter->depth) + { + case LIBCOOPGAMMA_UINT8: + libclut_translate(&(filter->ramps.u8), UINT8_MAX, uint8_t, &(dramps.d), (double)1, double); + break; + case LIBCOOPGAMMA_UINT16: + libclut_translate(&(filter->ramps.u16), UINT16_MAX, uint16_t, &(dramps.d), (double)1, double); + break; + case LIBCOOPGAMMA_UINT32: + libclut_translate(&(filter->ramps.u32), UINT32_MAX, uint32_t, &(dramps.d), (double)1, double); + break; + case LIBCOOPGAMMA_UINT64: + libclut_translate(&(filter->ramps.u64), UINT64_MAX, uint64_t, &(dramps.d), (double)1, double); + break; + default: + abort(); + } + + free(dramps.d.red); + return 0; +} + + +/** + * The main function for the program-specific code + * + * @return 0: Success + * -1: Error, `errno` set + * -2: Error, `cg.error` set + * -3: Error, message already printed + */ +int start(void) +{ + int r; + size_t i, j, k; + + if (xflag) + for (i = 0; i < filters_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_REMOVE; + else if (dflag) + for (i = 0; i < filters_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_DEATH; + else + for (i = 0; i < filters_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_REMOVAL; + + if (!xflag && ((brightness_names != NULL) || (contrast_names != NULL))) + if ((r = make_slaves()) < 0) + return cleanup(r); + + if ((brightness_names == NULL) && (contrast_names == NULL)) + for (i = 0, r = 1; i < filters_n; i++) + { + if (!(crtc_updates[i].master) || !(crtc_info[crtc_updates[i].crtc].supported)) + continue; + if (!xflag) + if ((r = fill_filter(&(crtc_updates[i].filter), rbrightness, rcontrast, + gbrightness, gcontrast, bbrightness, bcontrast)) < 0) + return cleanup(r); + r = update_filter(i, 0); + if ((r == -2) || ((r == -1) && (errno != EAGAIN))) + return cleanup(r); + if (crtc_updates[i].slaves != NULL) + for (j = 0; crtc_updates[i].slaves[j] != 0; j++) + { + r = update_filter(crtc_updates[i].slaves[j], 0); + if ((r == -2) || ((r == -1) && (errno != EAGAIN))) + return cleanup(r); + } + } + else + { + char* empty = NULL; + char** bnames = brightness_names ? brightness_names : ∅ + char** cnames = contrast_names ? contrast_names : ∅ + for (i = 0, r = 1; i < filters_n; i++) + { + if (!(crtc_info[crtc_updates[i].crtc].supported)) + continue; + for (j = 0; bnames[j] != NULL; j++) + if (!strcasecmp(crtc_updates[i].filter.crtc, bnames[j])) + break; + for (k = 0; cnames[k] != NULL; k++) + if (!strcasecmp(crtc_updates[i].filter.crtc, cnames[k])) + break; + if ((bnames[j] != NULL) || (cnames[k] != NULL)) + { + double rb = 0, gb = 0, bb = 0, rc = 1, bc = 1, gc = 1; + if (bnames[j] != NULL) + rb = rbrightnesses[j], gb = gbrightnesses[j], bb = bbrightnesses[j]; + if (cnames[j] != NULL) + rc = rcontrasts[j], gc = gcontrasts[j], bc = bcontrasts[j]; + if ((r = fill_filter(&(crtc_updates[i].filter), rb, rc, gb, gc, bb, bc)) < 0) + return cleanup(r); + r = update_filter(i, 0); + if ((r == -2) || ((r == -1) && (errno != EAGAIN))) + return cleanup(r); + } + } + } + + while (r != 1) + if ((r = synchronise(-1)) < 0) + return cleanup(r); + + if (!dflag) + return cleanup(0); + + if (libcoopgamma_set_nonblocking(&cg, 0) < 0) + return cleanup(-1); + for (;;) + if (libcoopgamma_synchronise(&cg, NULL, 0, &j) < 0) + switch (errno) + { + case 0: + break; + case ENOTRECOVERABLE: + goto enotrecoverable; + default: + return cleanup(-1); + } + + enotrecoverable: + for (;;) + if (pause() < 0) + return cleanup(-1); +} diff --git a/cg-linear.1 b/cg-linear.1 new file mode 100644 index 0000000..3f1330f --- /dev/null +++ b/cg-linear.1 @@ -0,0 +1,123 @@ +.TH CG-LINEAR 1 CG-TOOLS +.SH NAME +cg-linear - Create a span where adjustments are over unencodec RGB +.SH SYNOPSIS +.B cg-linear +.RB [ \-M +.IR method ] +.RB [ \-S +.IR site ] +.RB [ \-c +.IR crtc "]... ["\fB\-R\fP +.IR rule-base ] +.RB ( \-x +| +.B \-p +.IB start-priority : stop-priority +.RB [ \-d ] +.RB [ \+rgb ]) +.SH DESCRIPTION +.B cg-linear +adds two filters to the CLUT, one filter that converts +all stops to unencoded RGB, and a second filter that +converts all stops to encodec RGB. +.P +Note that if this is applied multiple times, values +can be doubly encoded and doubly decode. +.SH OPTIONS +.TP +.BR \+r +Ignore the red channel. +.TP +.BR \+g +Ignore the green channel. +.TP +.BR \+b +Ignore the blue channel. +.TP +.BR \-c " "\fIcrtc\fP +Apply the filter to the CRTC with the monitor whose EDID is +.IR crtc . +By default, the filter is applied to all monitors. + +If +.I crtc +is +.RB ' ? ', +all available CRTC's are listed. +.TP +.B \-d +Keep the process alive and remove the filter on death. +.TP +.BR \-M " "\fImethod\fP +Adjustment method name or number. Recognised names include: +.TS +tab(:); +l l. +\fBdummy\fP:Dummy method +\fBrandr\fP:X RAndR +\fBvidmode\fP:X VidMode +\fBdrm\fP:Linux DRM +\fBgdi\fP:Windows GDI +\fBquartz\fP:Quartz Core Graphics +.TE + +The adjustment methods are supported via +.BR libgamma (7). +Only methods that were enabled when +.B libgamma +was compiled will be supported. + +If +.I method +is +.RB ' ? ', +all available adjustment methods are printed. +.TP +.BR \-p " "\fIstart-priority\fP\fB:\fP\fIstop-priority\fP +Set the priority of the linearisation filter to +.IR start-priority , +and the priority of the delinearisation filter to +.IR stop-priority . +Filters with higher priority are applied before filters with +lower priority. This means that normally you want the +.I start-priority +to be higher than +.IR stop-priority . +The value must be a signed 64-bit integer (between +\-9223372036854775807 and 9223372036854775807). +.TP +.BR \-R " "\fIrule-base\fP +Set the rule base of of the filters to +.IR rule-base . +.RB ' :start ' +and +.RB ' :stop ' +are appended to the +linearisation filter's rule and delinearisation filter's rule, +respectively. The rule is the last part of the filter's +identifier (class). The default rule base is +.BR standard . + +If +.I rule +is +.RB ' ? ' +the utility's default rule vase is printed. If +.I rule +is +.RB ' ?? ' +the utility's default classes is printed. The first printed +class is for the linearisation filter, and the second printed +class is for the delinearisation filter. +.TP +.BR \-S " "\fIsite\fP +Select the site to which to connect. For example +.RB ' :0 ', +for local display 0 when using +.BR X . +.TP +.B \-x +Remove the currently applied filter. +.SH SEE ALSO +.BR cg-tools (7) diff --git a/cg-linear.c b/cg-linear.c new file mode 100644 index 0000000..554478e --- /dev/null +++ b/cg-linear.c @@ -0,0 +1,263 @@ +/* See LICENSE file for copyright and license details. */ +#include "cg-base.h" + +#include + +#include +#include +#include +#include +#include + + + +/** + * The default filter priority for the program + */ +const int64_t default_priority = NO_DEFAULT_PRIORITY; + +/** + * The default class base for the program + */ +char default_class[] = PKGNAME "::cg-linear::standard"; + +/** + * Class suffixes + */ +const char* const* class_suffixes = (const char* const[]){":start", ":stop", NULL}; + + + +/** + * -d: keep process alive and remove filter on death + */ +static int dflag = 0; + +/** + * -x: remove filter rather than adding it + */ +static int xflag = 0; + +/** + * +r: do not touch the red channel + */ +static int rplus = 0; + +/** + * +g: do not touch the green channel + */ +static int gplus = 0; + +/** + * +b: do not touch the blue channel + */ +static int bplus = 0; + +/** + * The priority of the linearisation filter + */ +static int64_t start_priority; + +/** + * The priority of the delinearisation filter + */ +static int64_t stop_priority; + + + +/** + * Print usage information and exit + */ +void usage(void) +{ + fprintf(stderr, + "Usage: %s [-M method] [-S site] [-c crtc]... [-R rule-base] " + "(-x | -p start-priority:stop-priority [-d] [+rgb])\n", + argv0); + exit(1); +} + + +/** + * Handle a command line option + * + * @param opt The option, it is a NUL-terminate two-character + * string starting with either '-' or '+', if the + * argument is not recognised, call `usage`. This + * string will not be "-M", "-S", "-c", "-p", or "-R". + * @param arg The argument associated with `opt`, + * `NULL` there is no next argument, if this + * parameter is `NULL` but needed, call `usage` + * @return 0 if `arg` was not used, + * 1 if `arg` was used, + * -1 on error + */ +int handle_opt(char* opt, char* arg) +{ + if (opt[0] == '-') + switch (opt[1]) + { + case 'd': + if (dflag || xflag) + usage(); + dflag = 1; + break; + case 'x': + if (xflag || dflag) + usage(); + xflag = 1; + break; + default: + usage(); + } + else + switch (opt[1]) + { + case 'r': + if (rplus) + usage(); + rplus = 1; + break; + case 'g': + if (gplus) + usage(); + gplus = 1; + break; + case 'b': + if (bplus) + usage(); + bplus = 1; + break; + default: + usage(); + } + return 0; + (void) arg; +} + + +/** + * This function is called after the last + * call to `handle_opt` + * + * @param argc The number of unparsed arguments + * @param argv `NULL` terminated list of unparsed arguments + * @param prio The argument associated with the "-p" option + * @return Zero on success, -1 on error + */ +int handle_args(int argc, char* argv[], char* prio) +{ + int q = xflag + (dflag | rplus | gplus | bplus); + char *p, *end; + if (argc || (q > 1) || (xflag && (prio != NULL))) + usage(); + if (!xflag && (prio == NULL)) + usage(); + if (prio != NULL) + { + p = strchr(prio, ':'); + if (!p) + usage(); + *p++ = '\0'; + errno = 0; + start_priority = (size_t)strtoul(prio, &end, 10); + if (errno || *end || !*prio) + usage(); + stop_priority = (size_t)strtoul(p, &end, 10); + if (errno || *end || !*prio) + usage(); + p[-1] = ':'; + } + return 0; + (void) argv; +} + + +/** + * Fill a filter + * + * @param filter The filter to fill + * @param is_start If the fitler is a linearisation filter + */ +static void fill_filter(libcoopgamma_filter_t* restrict filter, int is_start) +{ + switch (filter->depth) + { +#define X(CONST, MEMBER, MAX, TYPE)\ + case CONST:\ + if (is_start)\ + libclut_linearise(&(filter->ramps.MEMBER), MAX, TYPE, !rplus, !gplus, !bplus);\ + else\ + libclut_standardise(&(filter->ramps.MEMBER), MAX, TYPE, !rplus, !gplus, !bplus);\ + break; +LIST_DEPTHS +#undef X + default: + abort(); + } +} + + +/** + * The main function for the program-specific code + * + * @return 0: Success + * -1: Error, `errno` set + * -2: Error, `cg.error` set + * -3: Error, message already printed + */ +int start(void) +{ + int r; + size_t i, j; + + if (xflag) + for (i = 0; i < filters_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_REMOVE; + else if (dflag) + for (i = 0; i < filters_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_DEATH; + else + for (i = 0; i < filters_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_REMOVAL; + + for (i = 0, r = 1; i < filters_n; i++) + { + if (!(crtc_info[crtc_updates[i].crtc].supported)) + continue; + if (!xflag) { + int is_start = strchr(crtc_updates[i].filter.class, '\0')[-1] == 't'; + fill_filter(&(crtc_updates[i].filter), is_start); + crtc_updates[i].filter.priority = is_start ? start_priority : stop_priority; + } + r = update_filter(i, 0); + if ((r == -2) || ((r == -1) && (errno != EAGAIN))) + return r; + } + + while (r != 1) + if ((r = synchronise(-1)) < 0) + return r; + + if (!dflag) + return 0; + + if (libcoopgamma_set_nonblocking(&cg, 0) < 0) + return -1; + for (;;) + if (libcoopgamma_synchronise(&cg, NULL, 0, &j) < 0) + switch (errno) + { + case 0: + break; + case ENOTRECOVERABLE: + goto enotrecoverable; + default: + return -1; + } + + enotrecoverable: + for (;;) + if (pause() < 0) + return -1; +} diff --git a/cg-negative.1 b/cg-negative.1 new file mode 100644 index 0000000..0eafdea --- /dev/null +++ b/cg-negative.1 @@ -0,0 +1,112 @@ +.TH CG-NEGATIVE 1 CG-TOOLS +.SH NAME +cg-negative - Invert colour on the monitors +.SH SYNOPSIS +.B cg-negative +.RB [ \-M +.IR method ] +.RB [ \-S +.IR site ] +.RB [ \-c +.IR crtc "]... ["\fB\-R\fP +.IR rule ] +.RB ( \-x +| +.RB [ \-p +.IR priority ] +.RB [ \-d ] +.RB [ \+rgb ]) +.SH DESCRIPTION +.B cg-negative +inverts the colours on the monitors. +.SH OPTIONS +.TP +.BR \+r +Do not invert the red channel. +.TP +.BR \+g +Do not invert the green channel. +.TP +.BR \+b +Do not invert the blue channel. +.TP +.BR \-c " "\fIcrtc\fP +Apply the filter to the CRTC with the monitor whose EDID is +.IR crtc . +By default, the filter is applied to all monitors. + +If +.I crtc +is +.RB ' ? ', +all available CRTC's are listed. +.TP +.B \-d +Keep the process alive and remove the filter on death. +.TP +.BR \-M " "\fImethod\fP +Adjustment method name or number. Recognised names include: +.TS +tab(:); +l l. +\fBdummy\fP:Dummy method +\fBrandr\fP:X RAndR +\fBvidmode\fP:X VidMode +\fBdrm\fP:Linux DRM +\fBgdi\fP:Windows GDI +\fBquartz\fP:Quartz Core Graphics +.TE + +The adjustment methods are supported via +.BR libgamma (7). +Only methods that were enabled when +.B libgamma +was compiled will be supported. + +If +.I method +is +.RB ' ? ', +all available adjustment methods are printed. +.TP +.BR \-p " "\fIpriority\fP +Set the priority of the filter. Filters with higher priority +are applied before filters with lower priority. The value +must be a signed 64-bit integer (between \-9223372036854775807 +and 9223372036854775807). +.BR cg-negative 's +default priority is 4611686018427387904. + +If +.I priority +is +.RB ' ? ', +the utility's default priority is printed. +.TP +.BR \-R " "\fIrule\fP +Set the rule of of the filter to +.IR rule . +This is the last part of the filter's identifier (class). +The default rule is +.BR standard . + +If +.I rule +is +.RB ' ? ' +the utility's default rule is printed. If +.I rule +is +.RB ' ?? ' +the utility's default class is printed. +.TP +.BR \-S " "\fIsite\fP +Select the site to which to connect. For example +.RB ' :0 ', +for local display 0 when using +.BR X . +.TP +.B \-x +Remove the currently applied filter. +.SH SEE ALSO +.BR cg-tools (7) diff --git a/cg-negative.c b/cg-negative.c new file mode 100644 index 0000000..be5f2a0 --- /dev/null +++ b/cg-negative.c @@ -0,0 +1,238 @@ +/* See LICENSE file for copyright and license details. */ +#include "cg-base.h" + +#include + +#include +#include +#include +#include +#include + + + +/** + * The default filter priority for the program + */ +const int64_t default_priority = (int64_t)1 << 62; + +/** + * The default class for the program + */ +char default_class[] = PKGNAME "::cg-negative::standard"; + +/** + * Class suffixes + */ +const char* const* class_suffixes = (const char* const[]){NULL}; + + + +/** + * -d: keep process alive and remove filter on death + */ +static int dflag = 0; + +/** + * -x: remove filter rather than adding it + */ +static int xflag = 0; + +/** + * +r: do not touch the red channel + */ +static int rplus = 0; + +/** + * +g: do not touch the green channel + */ +static int gplus = 0; + +/** + * +b: do not touch the blue channel + */ +static int bplus = 0; + + + +/** + * Print usage information and exit + */ +void usage(void) +{ + fprintf(stderr, + "Usage: %s [-M method] [-S site] [-c crtc]... [-R rule] (-x | [-p priority] [-d] [+rgb])\n", + argv0); + exit(1); +} + + +/** + * Handle a command line option + * + * @param opt The option, it is a NUL-terminate two-character + * string starting with either '-' or '+', if the + * argument is not recognised, call `usage`. This + * string will not be "-M", "-S", "-c", "-p", or "-R". + * @param arg The argument associated with `opt`, + * `NULL` there is no next argument, if this + * parameter is `NULL` but needed, call `usage` + * @return 0 if `arg` was not used, + * 1 if `arg` was used, + * -1 on error + */ +int handle_opt(char* opt, char* arg) +{ + if (opt[0] == '-') + switch (opt[1]) + { + case 'd': + if (dflag || xflag) + usage(); + dflag = 1; + break; + case 'x': + if (xflag || dflag) + usage(); + xflag = 1; + break; + default: + usage(); + } + else + switch (opt[1]) + { + case 'r': + if (rplus) + usage(); + rplus = 1; + break; + case 'g': + if (gplus) + usage(); + gplus = 1; + break; + case 'b': + if (bplus) + usage(); + bplus = 1; + break; + default: + usage(); + } + return 0; + (void) arg; +} + + +/** + * This function is called after the last + * call to `handle_opt` + * + * @param argc The number of unparsed arguments + * @param argv `NULL` terminated list of unparsed arguments + * @param prio The argument associated with the "-p" option + * @return Zero on success, -1 on error + */ +int handle_args(int argc, char* argv[], char* prio) +{ + int q = xflag + (dflag | rplus | gplus | bplus); + if (argc || (q > 1) || (xflag && (prio != NULL))) + usage(); + return 0; + (void) argv; +} + + +/** + * Fill a filter + * + * @param filter The filter to fill + */ +static void fill_filter(libcoopgamma_filter_t* restrict filter) +{ + switch (filter->depth) + { +#define X(CONST, MEMBER, MAX, TYPE)\ + case CONST:\ + libclut_negative(&(filter->ramps.MEMBER), MAX, TYPE, !rplus, !gplus, !bplus);\ + break; +LIST_DEPTHS +#undef X + default: + abort(); + } +} + + +/** + * The main function for the program-specific code + * + * @return 0: Success + * -1: Error, `errno` set + * -2: Error, `cg.error` set + * -3: Error, message already printed + */ +int start(void) +{ + int r; + size_t i, j; + + if (xflag) + for (i = 0; i < filters_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_REMOVE; + else if (dflag) + for (i = 0; i < filters_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_DEATH; + else + for (i = 0; i < filters_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_REMOVAL; + + if (!xflag) + if ((r = make_slaves()) < 0) + return r; + + for (i = 0, r = 1; i < filters_n; i++) + { + if (!(crtc_updates[i].master) || !(crtc_info[crtc_updates[i].crtc].supported)) + continue; + if (!xflag) + fill_filter(&(crtc_updates[i].filter)); + r = update_filter(i, 0); + if ((r == -2) || ((r == -1) && (errno != EAGAIN))) + return r; + if (crtc_updates[i].slaves != NULL) + for (j = 0; crtc_updates[i].slaves[j] != 0; j++) + { + r = update_filter(crtc_updates[i].slaves[j], 0); + if ((r == -2) || ((r == -1) && (errno != EAGAIN))) + return r; + } + } + + while (r != 1) + if ((r = synchronise(-1)) < 0) + return r; + + if (!dflag) + return 0; + + if (libcoopgamma_set_nonblocking(&cg, 0) < 0) + return -1; + for (;;) + if (libcoopgamma_synchronise(&cg, NULL, 0, &j) < 0) + switch (errno) + { + case 0: + break; + case ENOTRECOVERABLE: + goto enotrecoverable; + default: + return -1; + } + + enotrecoverable: + for (;;) + if (pause() < 0) + return -1; +} diff --git a/cg-query.1 b/cg-query.1 new file mode 100644 index 0000000..93ae3ff --- /dev/null +++ b/cg-query.1 @@ -0,0 +1,72 @@ +.TH CG-QUERY 1 CG-TOOLS +.SH NAME +cg-query - List output filters applied to a monitor +.SH SYNOPSIS +.B cg-query +.RB [ \-M +.IR method ] +.RB [ \-S +.IR site ] +.RB [ \-h +.IR high ] +.RB [ \-l +.IR low ] +.B \-c +.I crtc +.SH DESCRIPTION +.B cg-query +prints a list of all output filters applied to the monitor +whose EDID is +.IR crtc . +.SH OPTIONS +.TP +.BR \-h " "\fIhigh\fP +Do not list filter with higher priority than +.IR high . +.TP +.BR \-l " "\fIlow\fP +Do not list filter with lower priority than +.IR low . +.TP +.BR \-c " "\fIcrtc\fP +Apply the filter to the CRTC with the monitor whose EDID is +.IR crtc . + +If +.I crtc +is +.RB ' ? ', +all available CRTC's are listed. +.TP +.BR \-M " "\fImethod\fP +Adjustment method name or number. Recognised names include: +.TS +tab(:); +l l. +\fBdummy\fP:Dummy method +\fBrandr\fP:X RAndR +\fBvidmode\fP:X VidMode +\fBdrm\fP:Linux DRM +\fBgdi\fP:Windows GDI +\fBquartz\fP:Quartz Core Graphics +.TE + +The adjustment methods are supported via +.BR libgamma (7). +Only methods that were enabled when +.B libgamma +was compiled will be supported. + +If +.I method +is +.RB ' ? ', +all available adjustment methods are printed. +.TP +.BR \-S " "\fIsite\fP +Select the site to which to connect. For example +.RB ' :0 ', +for local display 0 when using +.BR X . +.SH SEE ALSO +.BR cg-tools (7) diff --git a/cg-query.c b/cg-query.c new file mode 100644 index 0000000..8532599 --- /dev/null +++ b/cg-query.c @@ -0,0 +1,497 @@ +/* See LICENSE file for copyright and license details. */ +#include "arg.h" + +#include + +#include +#include +#include +#include +#include +#include + + + +/** + * The libcoopgamma context + */ +static libcoopgamma_context_t cg; + +/** + * Filter query + */ +static libcoopgamma_filter_query_t query; + +/** + * The class of the filter to print + */ +static char* class = NULL; + + + +/** + * Print usage information and exit + */ +static void usage(void) +{ + fprintf(stderr, + "Usage: %s [-M method] [-S site] [-h high] [-l low] [-f class] -c crtc\n", + argv0); + exit(1); +} + + +/** + * Initialise the process, specifically + * reset the signal mask and signal handlers + * + * @return Zero on success, -1 on error + */ +static int initialise_proc(void) +{ + sigset_t sigmask; + int sig; + + for (sig = 1; sig < _NSIG; sig++) + if (signal(sig, SIG_DFL) == SIG_ERR) + if (sig == SIGCHLD) + return -1; + + if (sigemptyset(&sigmask) < 0) + return -1; + if (sigprocmask(SIG_SETMASK, &sigmask, NULL) < 0) + return -1; + + return 0; +} + + +/** + * Print, to stdout, a list of all + * recognised adjustment methods + * + * @return Zero on success, -1 on error + */ +static int list_methods(void) +{ + char** list; + size_t i; + + list = libcoopgamma_get_methods(); + if (list == NULL) + return -1; + for (i = 0; list[i]; i++) + printf("%s\n", list[i]); + free(list); + if (fflush(stdout) < 0) + return -1; + + return 0; +} + + +/** + * Print, to stdout, a list of all CRTC:s + * + * A connection to the coopgamma server + * must have been made + * + * @return Zero on success, -1 on error, -2 + * on libcoopgamma error + */ +static int list_crtcs(void) +{ + char** list; + size_t i; + + list = libcoopgamma_get_crtcs_sync(&cg); + if (list == NULL) + return -2; + for (i = 0; list[i]; i++) + printf("%s\n", list[i]); + free(list); + if (fflush(stdout) < 0) + return -1; + + return 0; +} + + +/** + * Print, to stdout, information about + * the selected CRTC + * + * @return Zero on success, -1 on error, -2 + * on libcoopgamma error, -3 on error + * with error message already printed + */ +static int print_info(void) +{ + libcoopgamma_crtc_info_t info; + libcoopgamma_filter_table_t table; + const char* str; + int saved_errno, ret = 0; + size_t i; + + if (libcoopgamma_crtc_info_initialise(&info) < 0) + return -1; + if (libcoopgamma_filter_table_initialise(&table) < 0) + { + saved_errno = errno; + libcoopgamma_crtc_info_destroy(&info); + errno = saved_errno; + return -1; + } + + if (libcoopgamma_get_gamma_info_sync(query.crtc, &info, &cg) < 0) + goto cg_fail; + + printf("Cooperative gamma server running: %s\n", + info.cooperative ? "yes" : "no"); + + printf("Gamma adjustments supported: %s\n", + info.supported == LIBCOOPGAMMA_MAYBE ? "maybe" : info.supported ? "yes" : "no"); + + printf("Gamma ramps stops (red green blue): %zu %zu %zu\n", + info.red_size, info.green_size, info.blue_size); + + switch (info.depth) + { + case LIBCOOPGAMMA_DOUBLE: str = "double-precision floating-point"; break; + case LIBCOOPGAMMA_FLOAT: str = "single-precision floating-point"; break; + case LIBCOOPGAMMA_UINT8: str = "unsigned 8-bit integer"; break; + case LIBCOOPGAMMA_UINT16: str = "unsigned 16-bit integer"; break; + case LIBCOOPGAMMA_UINT32: str = "unsigned 32-bit integer"; break; + case LIBCOOPGAMMA_UINT64: str = "unsigned 64-bit integer"; break; + default: + errno = EPROTO; + goto fail; + } + printf("Gamma ramps stops value type: %s\n", str); + + if (info.colourspace != LIBCOOPGAMMA_UNKNOWN) + { + switch (info.colourspace) + { + case LIBCOOPGAMMA_SRGB: str = "sRGB"; break; + case LIBCOOPGAMMA_RGB: str = "non-standard RGB"; break; + case LIBCOOPGAMMA_NON_RGB: str = "non-RGB multicolour"; break; + case LIBCOOPGAMMA_GREY: str = "monochrome or singlecolour scale"; break; + default: + errno = EPROTO; + goto fail; + } + printf("Monitor's colourspace: %s\n", str); + } + + if (info.have_gamut) + { + printf("Monitor's red colour (x, y): %lf, %lf\n", + info.red_x / (double)1024, info.red_y / (double)1024); + + printf("Monitor's green colour (x, y): %lf, %lf\n", + info.green_x / (double)1024, info.green_y / (double)1024); + + printf("Monitor's blue colour (x, y): %lf, %lf\n", + info.blue_x / (double)1024, info.blue_y / (double)1024); + + printf("Monitor's white point (x, y): %lf, %lf\n", + info.white_x / (double)1024, info.white_y / (double)1024); + } + + if (libcoopgamma_get_gamma_sync(&query, &table, &cg) < 0) + goto cg_fail; + + if ((table.red_size != info.red_size) || (table.green_size != info.green_size) || + (table.blue_size != info.blue_size) || (table.depth != info.depth)) + { + fprintf(stderr, "%s: gamma ramp structure changed between queries\n", argv0); + goto custom_fail; + } + + printf("Filters: %zu\n", table.filter_count); + for (i = 0; i < table.filter_count; i++) + { + printf(" Filter %zu:\n", i); + printf(" Priority: %" PRIi64 "\n", table.filters[i].priority); + printf(" Class: %s\n", table.filters[i].class); + } + + done: + saved_errno = errno; + libcoopgamma_crtc_info_destroy(&info); + libcoopgamma_filter_table_destroy(&table); + errno = saved_errno; + return ret; + fail: + ret = -1; + goto done; + cg_fail: + ret = -2; + goto done; + custom_fail: + ret = -3; + goto done; +} + + + +/** + * Print, to stdout, the ramps of the select + * filter on the select CRTC + * + * @return Zero on success, -1 on error, -2 + * on libcoopgamma error, -3 on error + * with error message already printed + */ +static int print_filter(void) +{ + libcoopgamma_filter_table_t table; + libcoopgamma_ramps_t* restrict ramps; + int saved_errno, ret = 0; + size_t i, n; + + if (libcoopgamma_filter_table_initialise(&table) < 0) + return -1; + + if (libcoopgamma_get_gamma_sync(&query, &table, &cg) < 0) + goto cg_fail; + + if (query.coalesce) + i = 0; + else + for (i = 0; i < table.filter_count; i++) + if (!strcmp(table.filters[i].class, class)) + break; + if (i == table.filter_count) + { + fprintf(stderr, "%s: selected filter does not exist on selected CRTC\n", argv0); + goto custom_fail; + } + ramps = &(table.filters[i].ramps); + + n = table.red_size; + if (n < table.green_size) + n = table.green_size; + if (n < table.blue_size) + n = table.blue_size; + + switch (table.depth) + { +#define X(CONST, MEMBER, TYPE, FORMAT, DASH) \ + case CONST: \ + for (i = 0; i < n; i++) \ + { \ + if (i < ramps->MEMBER.red_size) \ + printf("%" FORMAT " ", (TYPE)(ramps->MEMBER.red[i])); \ + else \ + printf(DASH " "); \ + if (i < ramps->MEMBER.green_size) \ + printf("%" FORMAT " ", (TYPE)(ramps->MEMBER.green[i])); \ + else \ + printf(DASH " "); \ + if (i < ramps->MEMBER.blue_size) \ + printf("%" FORMAT "\n", (TYPE)(ramps->MEMBER.blue[i])); \ + else \ + printf(DASH "\n"); \ + } \ + break + X(LIBCOOPGAMMA_DOUBLE, d, double, "lf", "----"); + X(LIBCOOPGAMMA_FLOAT, f, double, "lf", "----"); + X(LIBCOOPGAMMA_UINT8, u8, uint8_t, "02" PRIx8, "--"); + X(LIBCOOPGAMMA_UINT16, u16, uint16_t, "04" PRIx16, "----"); + X(LIBCOOPGAMMA_UINT32, u32, uint32_t, "08" PRIx32, "--------"); + X(LIBCOOPGAMMA_UINT64, u64, uint64_t, "016" PRIx64, "----------------"); +#undef X + default: + errno = EPROTO; + goto fail; + } + + done: + saved_errno = errno; + libcoopgamma_filter_table_destroy(&table); + errno = saved_errno; + return ret; + fail: + ret = -1; + goto done; + cg_fail: + ret = -2; + goto done; + custom_fail: + ret = -3; + goto done; +} + + +/** + * -M METHOD + * Select adjustment method. If METHOD is "?", + * available methods will be printed to stdout. + * + * -S SITE + * Select site (display server instance). + * + * -c CRTC + * Select CRT controller. If CRTC is "?", CRTC:s + * will be printed to stdout. + * + * -h HIGH + * Suppress filter with higher priority than HIGH. + * + * -l LOW + * Suppress filter with lower priority than LOW. + * + * -f CLASS + * Print gamma ramps of the filter with class CLASS + * on the selected CRTC. If CLASS is "*" all filters + * with a priority in [LOW, HIGH] are coalesced. + * + * @param argc The number of command line arguments + * @param argv The command line arguments + * @return 0 on success, 1 on error + */ +int main(int argc, char* argv[]) +{ + int stage = 0, haveh = 0, havel = 0; + int rc = 0; + char* method = NULL; + char* site = NULL; + + query.high_priority = INT64_MAX; + query.low_priority = INT64_MIN; + query.crtc = NULL; + query.coalesce = 0; + + ARGBEGIN + { + case 'M': + if (method != NULL) + usage(); + method = EARGF(usage()); + break; + case 'S': + if (site != NULL) + usage(); + site = EARGF(usage()); + break; + case 'c': + if (query.crtc != NULL) + usage(); + query.crtc = EARGF(usage()); + break; + case 'h': + if (haveh++) + usage(); + query.high_priority = (int64_t)atoll(EARGF(usage())); + break; + case 'l': + if (havel++) + usage(); + query.low_priority = (int64_t)atoll(EARGF(usage())); + break; + case 'f': + if (class != NULL) + usage(); + class = EARGF(usage()); + if ((class[0] == '*') && (class[1] == '\0')) + query.coalesce = 1; + break; + default: + usage(); + } + ARGEND; + + if (argc) + usage(); + + if (initialise_proc() < 0) + goto fail; + + if ((method != NULL) && !strcmp(method, "?")) + { + if ((site != NULL) || (query.crtc != NULL)) + usage(); + if (list_methods() < 0) + goto fail; + return 0; + } + + if (libcoopgamma_context_initialise(&cg) < 0) + goto fail; + stage++; + if (libcoopgamma_connect(method, site, &cg) < 0) + { + fprintf(stderr, "%s: server failed to initialise\n", argv0); + goto custom_fail; + } + stage++; + + if (!(query.crtc)) + usage(); + + if (!strcmp(query.crtc, "?")) + switch (list_crtcs()) + { + case 0: + goto done; + case -1: + goto fail; + default: + goto cg_fail; + } + + switch (class ? print_filter() : print_info()) + { + case 0: + goto done; + case -1: + goto fail; + case -2: + goto cg_fail; + default: + goto custom_fail; + } + + fflush(stdout); + if (ferror(stdout)) + goto fail; + if (fclose(stdout) < 0) + goto fail; + + done: + if (stage >= 1) + libcoopgamma_context_destroy(&cg, stage >= 2); + return rc; + + custom_fail: + rc = 1; + goto done; + + fail: + rc = 1; + perror(argv0); + goto done; + + cg_fail: + rc = 1; + { + const char* side = cg.error.server_side ? "server" : "client"; + if (cg.error.custom) + { + if ((cg.error.number != 0) || (cg.error.description != NULL)) + fprintf(stderr, "%s: %s-side error number %" PRIu64 ": %s\n", + argv0, side, cg.error.number, cg.error.description); + else if (cg.error.number != 0) + fprintf(stderr, "%s: %s-side error number %" PRIu64 "\n", argv0, side, cg.error.number); + else if (cg.error.description != NULL) + fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, cg.error.description); + } + else if (cg.error.description != NULL) + fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, cg.error.description); + else + fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, strerror(cg.error.number)); + } + goto done; +} diff --git a/cg-rainbow.1 b/cg-rainbow.1 new file mode 100644 index 0000000..9601843 --- /dev/null +++ b/cg-rainbow.1 @@ -0,0 +1,114 @@ +.TH CG-RAINBOW 1 CG-TOOLS +.SH NAME +cg-rainbow - Adds a rainbow cycle effect to the monitors +.SH SYNOPSIS +.B cg-rainbow +.RB [ \-M +.IR method ] +.RB [ \-S +.IR site ] +.RB [ \-c +.IR crtc "]... ["\fB\-R\fP +.IR rule ] +.RB [ \-p +.IR priority ] +.RB [ \-l +.IR luminosity ] +.RB [ \-s +.IR rainbowhz ] +.SH DESCRIPTION +.B cg-rainbow +cycles between a the rainbow's colors and use the colours +as the monitors' white colour. +.P +.B cg-rainbow +is a rewrite of +.BR xrainbow (1) +and +.BR grainbow (1). +.SH OPTIONS +.TP +.BR \-l " "\fIluminosity\fP +Select the luminosity of the rainbow. A value of 0 means +the rainbow is completely black, and a value of 1 means the +the rainbow is fully saturated, but not oversaturated. The +default luminosity is one third. +.TP +.BR \-s " "\fIrainbowhz\fP +Select the number of of rainbow-cycles per second. The +default frequency is one third. +.TP +.BR \-c " "\fIcrtc\fP +Apply the filter to the CRTC with the monitor whose EDID is +.IR crtc . +By default, the filter is applied to all monitors. + +If +.I crtc +is +.RB ' ? ', +all available CRTC's are listed. +.TP +.BR \-M " "\fImethod\fP +Adjustment method name or number. Recognised names include: +.TS +tab(:); +l l. +\fBdummy\fP:Dummy method +\fBrandr\fP:X RAndR +\fBvidmode\fP:X VidMode +\fBdrm\fP:Linux DRM +\fBgdi\fP:Windows GDI +\fBquartz\fP:Quartz Core Graphics +.TE + +The adjustment methods are supported via +.BR libgamma (7). +Only methods that were enabled when +.B libgamma +was compiled will be supported. + +If +.I method +is +.RB ' ? ', +all available adjustment methods are printed. +.TP +.BR \-p " "\fIpriority\fP +Set the priority of the filter. Filters with higher priority +are applied before filters with lower priority. The value +must be a signed 64-bit integer (between \-9223372036854775807 +and 9223372036854775807). +.BR cg-rainbow 's +default priority is 1152921504606846976. + +If +.I priority +is +.RB ' ? ', +the utility's default priority is printed. +.TP +.BR \-R " "\fIrule\fP +Set the rule of of the filter to +.IR rule . +This is the last part of the filter's identifier (class). +The default rule is +.BR standard . + +If +.I rule +is +.RB ' ? ' +the utility's default rule is printed. If +.I rule +is +.RB ' ?? ' +the utility's default class is printed. +.TP +.BR \-S " "\fIsite\fP +Select the site to which to connect. For example +.RB ' :0 ', +for local display 0 when using +.BR X . +.SH SEE ALSO +.BR cg-tools (7) diff --git a/cg-rainbow.c b/cg-rainbow.c new file mode 100644 index 0000000..4cf4797 --- /dev/null +++ b/cg-rainbow.c @@ -0,0 +1,262 @@ +/* See LICENSE file for copyright and license details. */ +#include "cg-base.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + + +/** + * The default filter priority for the program + */ +const int64_t default_priority = (int64_t)1 << 60; + +/** + * The default class for the program + */ +char default_class[] = PKGNAME "::cg-rainbow::standard"; + +/** + * Class suffixes + */ +const char* const* class_suffixes = (const char* const[]){NULL}; + + + +/** + * -s: rainbow-frequency in Hz + */ +static char* sflag = NULL; + +/** + * -l: base luminosity + */ +static char* lflag = NULL; + +/** + * The rainbow-frequency multiplied by 3 + */ +double rainbows_per_third_second = 1; + +/** + * The base luminosity + */ +double luminosity = (double)1 / 3; + + + +/** + * Print usage information and exit + */ +void usage(void) +{ + fprintf(stderr, + "Usage: %s [-M method] [-S site] [-c crtc]... [-R rule] [-p priority]" + " [-l luminosity] [-s rainbowhz]\n", + argv0); + exit(1); +} + + +/** + * Handle a command line option + * + * @param opt The option, it is a NUL-terminate two-character + * string starting with either '-' or '+', if the + * argument is not recognised, call `usage`. This + * string will not be "-M", "-S", "-c", "-p", or "-R". + * @param arg The argument associated with `opt`, + * `NULL` there is no next argument, if this + * parameter is `NULL` but needed, call `usage` + * @return 0 if `arg` was not used, + * 1 if `arg` was used, + * -1 on error + */ +int handle_opt(char* opt, char* arg) +{ + if (opt[0] == '-') + switch (opt[1]) + { + case 'l': + if (lflag || !(lflag = arg)) + usage(); + return 1; + case 's': + if (sflag || !(sflag = arg)) + usage(); + return 1; + default: + usage(); + } + else + usage(); + return 0; +} + + +/** + * Parse a non-negative double encoded as a string + * + * @param out Output parameter for the value + * @param str The string + * @return Zero on success, -1 if the string is invalid + */ +static int parse_double(double* restrict out, const char* restrict str) +{ + char* end; + errno = 0; + *out = strtod(str, &end); + if (errno || (*out < 0) || isinf(*out) || isnan(*out) || *end) + return -1; + if (!*str || !strchr("0123456789.", *str)) + return -1; + return 0; +} + + +/** + * This function is called after the last + * call to `handle_opt` + * + * @param argc The number of unparsed arguments + * @param argv `NULL` terminated list of unparsed arguments + * @param prio The argument associated with the "-p" option + * @return Zero on success, -1 on error + */ +int handle_args(int argc, char* argv[], char* prio) +{ + int q = (lflag || sflag); + if ((q > 1) || argc) + usage(); + if (sflag != NULL) + { + if (parse_double(&rainbows_per_third_second, sflag) < 0) + usage(); + rainbows_per_third_second *= 3; + } + if (lflag != NULL) + { + if (parse_double(&luminosity, lflag) < 0) + usage(); + } + return 0; + (void) argv; + (void) prio; +} + + +/** + * Fill a filter + * + * @param filter The filter to fill + * @param red The red brightness + * @param green The green brightness + * @param blue The blue brightness + */ +static void fill_filter(libcoopgamma_filter_t* restrict filter, double red, double green, double blue) +{ + switch (filter->depth) + { +#define X(CONST, MEMBER, MAX, TYPE)\ + case CONST:\ + libclut_start_over(&(filter->ramps.MEMBER), MAX, TYPE, 1, 1, 1);\ + libclut_rgb_brightness(&(filter->ramps.MEMBER), MAX, TYPE, red, green, blue);\ + break; +LIST_DEPTHS +#undef X + default: + abort(); + } +} + + +/** + * Get the current monotonic time as a double + * + * @param now Output parameter for the current time (monotonic) + * @return Zero on success, -1 on error + */ +static int double_time(double* restrict now) +{ +#ifndef CLOCK_MONOTONIC_RAW +# define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC +#endif + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) < 0) + return -1; + *now = (double)(ts.tv_nsec); + *now /= 1000000000L; + *now += (double)(ts.tv_sec); + return 0; +} + + +/** + * The main function for the program-specific code + * + * @return 0: Success + * -1: Error, `errno` set + * -2: Error, `cg.error` set + * -3: Error, message already printed + */ +int start(void) +{ + int r; + size_t i, j; + double pal[3]; + double t, starttime; + + for (i = 0; i < filters_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_DEATH; + + if ((r = make_slaves()) < 0) + return r; + + if ((r = double_time(&starttime)) < 0) + return r; + + for (;;) + { + if ((r = double_time(&t)) < 0) + return r; + t -= starttime; + t *= rainbows_per_third_second; + pal[0] = pal[1] = pal[2] = luminosity; + pal[((long)t) % 3] += 1 - fmod(t, 1); + pal[((long)t + 1) % 3] += fmod(t, 1); + if (pal[0] > 1) pal[0] = 1; + if (pal[1] > 1) pal[1] = 1; + if (pal[2] > 1) pal[2] = 1; + + for (i = 0, r = 1; i < filters_n; i++) + { + if (!(crtc_updates[i].master) || !(crtc_info[crtc_updates[i].crtc].supported)) + continue; + fill_filter(&(crtc_updates[i].filter), pal[0], pal[1], pal[2]); + r = update_filter(i, 0); + if ((r == -2) || ((r == -1) && (errno != EAGAIN))) + return r; + if (crtc_updates[i].slaves != NULL) + for (j = 0; crtc_updates[i].slaves[j] != 0; j++) + { + r = update_filter(crtc_updates[i].slaves[j], 0); + if ((r == -2) || ((r == -1) && (errno != EAGAIN))) + return r; + } + } + + while (r != 1) + if ((r = synchronise(-1)) < 0) + return r; + + sched_yield(); + } +} diff --git a/cg-remove.1 b/cg-remove.1 new file mode 100644 index 0000000..9181899 --- /dev/null +++ b/cg-remove.1 @@ -0,0 +1,60 @@ +.TH CG-REMOVE 1 CG-TOOLS +.SH NAME +cg-remove - Remove output filter from monitors +.SH SYNOPSIS +.B cg-remove +.RB [ \-M +.IR method ] +.RB [ \-S +.IR site ] +.RB [ \-c +.IR crtc "]... " class ... +.SH DESCRIPTION +.B cg-remove +remove the filters with the selected +.IR class es. +.SH OPTIONS +.TP +.BR \-c " "\fIcrtc\fP +Apply the filter to the CRTC with the monitor whose EDID is +.IR crtc . +By default, the filter is applied to all monitors. + +If +.I crtc +is +.RB ' ? ', +all available CRTC's are listed. +.TP +.BR \-M " "\fImethod\fP +Adjustment method name or number. Recognised names include: +.TS +tab(:); +l l. +\fBdummy\fP:Dummy method +\fBrandr\fP:X RAndR +\fBvidmode\fP:X VidMode +\fBdrm\fP:Linux DRM +\fBgdi\fP:Windows GDI +\fBquartz\fP:Quartz Core Graphics +.TE + +The adjustment methods are supported via +.BR libgamma (7). +Only methods that were enabled when +.B libgamma +was compiled will be supported. + +If +.I method +is +.RB ' ? ', +all available adjustment methods are printed. +.TP +.BR \-S " "\fIsite\fP +Select the site to which to connect. For example +.RB ' :0 ', +for local display 0 when using +.BR X . +.SH SEE ALSO +.BR cg-tools (7) diff --git a/cg-remove.c b/cg-remove.c new file mode 100644 index 0000000..0e81df0 --- /dev/null +++ b/cg-remove.c @@ -0,0 +1,386 @@ +/* See LICENSE file for copyright and license details. */ +#include "arg.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + + + +/** + * The libcoopgamma context + */ +static libcoopgamma_context_t cg; + + + +/** + * Print usage information and exit + */ +static void usage(void) +{ + fprintf(stderr, + "Usage: %s [-M method] [-S site] [-c crtc]... class...\n", + argv0); + exit(1); +} + + +/** + * Initialise the process, specifically + * reset the signal mask and signal handlers + * + * @return Zero on success, -1 on error + */ +static int initialise_proc(void) +{ + sigset_t sigmask; + int sig; + + for (sig = 1; sig < _NSIG; sig++) + if (signal(sig, SIG_DFL) == SIG_ERR) + if (sig == SIGCHLD) + return -1; + + if (sigemptyset(&sigmask) < 0) + return -1; + if (sigprocmask(SIG_SETMASK, &sigmask, NULL) < 0) + return -1; + + return 0; +} + + +/** + * Print, to stdout, a list of all + * recognised adjustment methods + * + * @return Zero on success, -1 on error + */ +static int list_methods(void) +{ + char** list; + size_t i; + + list = libcoopgamma_get_methods(); + if (list == NULL) + return -1; + for (i = 0; list[i]; i++) + printf("%s\n", list[i]); + free(list); + if (fflush(stdout) < 0) + return -1; + + return 0; +} + + +/** + * Print, to stdout, a list of all CRTC:s + * + * A connection to the coopgamma server + * must have been made + * + * @return Zero on success, -1 on error, -2 + * on libcoopgamma error + */ +static int list_crtcs(void) +{ + char** list; + size_t i; + + list = libcoopgamma_get_crtcs_sync(&cg); + if (list == NULL) + return -2; + for (i = 0; list[i]; i++) + printf("%s\n", list[i]); + free(list); + if (fflush(stdout) < 0) + return -1; + + return 0; +} + + +/** + * Remove selected filters from selected CRTC:s + * + * @param crtcs `NULL`-terminated list of CRTC names + * @param classes `NULL`-terminated list of filter classes + * @return Zero on success, -1 on error, -2 on + * libcoopgamma error + */ +static int remove_filters(char* const* restrict crtcs, char* const* restrict classes) +{ + size_t n = 0, unsynced = 0, selected, i, j; + char* synced = NULL; + libcoopgamma_async_context_t* asyncs = NULL; + int saved_errno, need_flush = 0, ret = 0; + struct pollfd pollfd; + libcoopgamma_filter_t command; + + for (i = 0; crtcs[i] != NULL; i++); + for (j = 0; classes[j] != NULL; j++); + synced = calloc(i, j * sizeof(*synced)); + if (synced == NULL) + goto fail; + asyncs = calloc(i, j * sizeof(*asyncs)); + if (asyncs == NULL) + goto fail; + + i = j = 0; + command.lifespan = LIBCOOPGAMMA_REMOVE; + pollfd.fd = cg.fd; + pollfd.events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI; + + while ((unsynced > 0) || (crtcs[i] != NULL)) + { + wait: + if (crtcs[i] != NULL) + pollfd.events |= POLLOUT; + else + pollfd.events &= ~POLLOUT; + + pollfd.revents = 0; + if (poll(&pollfd, (nfds_t)1, -1) < 0) + goto fail; + + if (pollfd.revents & (POLLOUT | POLLERR | POLLHUP | POLLNVAL)) + { + if (need_flush && (libcoopgamma_flush(&cg) < 0)) + goto send_fail; + need_flush = 0; + for (; crtcs[i] != NULL; i++, j = 0) + { + command.crtc = crtcs[i]; + while (classes[j] != NULL) + { + command.class = classes[j++]; + if (unsynced++, libcoopgamma_set_gamma_send(&command, &cg, asyncs + n++) < 0) + goto send_fail; + } + } + goto send_done; + send_fail: + switch (errno) + { + case EINTR: + case EAGAIN: +#if EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif + need_flush = 1; + if (classes[j] == NULL) + i++, j = 0; + break; + default: + goto fail; + } + } + send_done: + + if ((unsynced == 0) && (crtcs[i] == NULL)) + break; + + if (pollfd.revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) + while (unsynced > 0) + switch (libcoopgamma_synchronise(&cg, asyncs, n, &selected)) + { + case 0: + if (synced[selected]) + { + libcoopgamma_skip_message(&cg); + break; + } + synced[selected] = 1; + unsynced -= 1; + if (libcoopgamma_set_gamma_recv(&cg, asyncs + selected) < 0) + goto cg_fail; + break; + default: + switch (errno) + { + case 0: + break; + case EINTR: + case EAGAIN: +#if EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif + goto wait; + default: + goto fail; + } + break; + } + } + + done: + saved_errno = errno; + free(synced); + free(asyncs); + errno = saved_errno; + return ret; + fail: + ret = -1; + goto done; + cg_fail: + ret = -2; + goto done; +} + + +/** + * -M METHOD + * Select adjustment method. If METHOD is "?", + * available methods will be printed to stdout. + * + * -S SITE + * Select site (display server instance). + * + * -c CRTC + * Select CRT controller. If CRTC is "?", CRTC:s + * will be printed to stdout. + * + * Can be used multiple times. If not used, all + * CRTC:s are selected. + * + * @param argc The number of command line arguments + * @param argv The command line arguments + * @return 0 on success, 1 on error + */ +int main(int argc, char* argv[]) +{ + int stage = 0; + int rc = 0; + char* method = NULL; + char* site = NULL; + char** crtcs_ = NULL; + char** crtcs = alloca(argc * sizeof(char*)); + size_t i, crtcs_n = 0; + + ARGBEGIN + { + case 'M': + if (method != NULL) + usage(); + method = EARGF(usage()); + break; + case 'S': + if (site != NULL) + usage(); + site = EARGF(usage()); + break; + case 'c': + crtcs[crtcs_n++] = EARGF(usage()); + break; + default: + usage(); + } + ARGEND; + + if (initialise_proc() < 0) + goto fail; + + if ((method != NULL) && !strcmp(method, "?")) + { + if ((site != NULL) || (crtcs_n > 0) || (argc > 0)) + usage(); + if (list_methods() < 0) + goto fail; + return 0; + } + + if (libcoopgamma_context_initialise(&cg) < 0) + goto fail; + stage++; + if (libcoopgamma_connect(method, site, &cg) < 0) + { + fprintf(stderr, "%s: server failed to initialise\n", argv0); + goto custom_fail; + } + stage++; + + for (i = 0; i < crtcs_n; i++) + if (!strcmp(crtcs[i], "?")) + { + if (argc > 0) + usage(); + switch (list_crtcs()) + { + case 0: + goto done; + case -1: + goto fail; + default: + goto cg_fail; + } + } + + if (argc == 0) + usage(); + + if (crtcs_n == 0) + { + crtcs = crtcs_ = libcoopgamma_get_crtcs_sync(&cg); + if (crtcs == NULL) + goto cg_fail; + } + else + crtcs[crtcs_n] = NULL; + + if (libcoopgamma_set_nonblocking(&cg, 1) < 0) + goto fail; + + switch (remove_filters(crtcs, argv)) + { + case 0: + break; + case -1: + goto fail; + default: + goto cg_fail; + } + + done: + if (stage >= 1) + libcoopgamma_context_destroy(&cg, stage >= 2); + free(crtcs_); + return rc; + + custom_fail: + rc = 1; + goto done; + + fail: + rc = 1; + perror(argv0); + goto done; + + cg_fail: + rc = 1; + { + const char* side = cg.error.server_side ? "server" : "client"; + if (cg.error.custom) + { + if ((cg.error.number != 0) || (cg.error.description != NULL)) + fprintf(stderr, "%s: %s-side error number %" PRIu64 ": %s\n", + argv0, side, cg.error.number, cg.error.description); + else if (cg.error.number != 0) + fprintf(stderr, "%s: %s-side error number %" PRIu64 "\n", argv0, side, cg.error.number); + else if (cg.error.description != NULL) + fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, cg.error.description); + } + else if (cg.error.description != NULL) + fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, cg.error.description); + else + fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, strerror(cg.error.number)); + } + goto done; +} diff --git a/cg-shallow.1 b/cg-shallow.1 new file mode 100644 index 0000000..35e8470 --- /dev/null +++ b/cg-shallow.1 @@ -0,0 +1,127 @@ +.TH CG-SHALLOW 1 CG-TOOLS +.SH NAME +cg-shallow - Emulate low colour resolution on the monitors +.SH SYNOPSIS +.B cg-shallow +.RB [ \-M +.IR method ] +.RB [ \-S +.IR site ] +.RB [ \-c +.IR crtc "]... ["\fB\-R\fP +.IR rule ] +.RB ( \-x +| +.RB [ \-p +.IR priority ] +.RB [ \-d ] +.RI [ all +| +.I red +.I green +.IR blue ]) +.SH DESCRIPTION +.B cg-shallow +degrades the colour resolution on the monitors, to +.I all +values on each channel, or to +.IR red , +.IR green , +and +.I blue +to the red, green, and blue channels independently, respectively. +A value of 0 means the resolution will not be changes. The values +are encodingly equidistantly distributed with black and full colour +remaining as is, unless the value 1 is specified, in which case +it undefined which brightness the channel will be fixed at. +.PP +If neither +.I all +or +.IR red , +.IR green , +and +.I blue +are specified, the value 2 for all channels is selected by default. +.SH OPTIONS +.TP +.BR \-c " "\fIcrtc\fP +Apply the filter to the CRTC with the monitor whose EDID is +.IR crtc . +By default, the filter is applied to all monitors. + +If +.I crtc +is +.RB ' ? ', +all available CRTC's are listed. +.TP +.B \-d +Keep the process alive and remove the filter on death. +.TP +.BR \-M " "\fImethod\fP +Adjustment method name or number. Recognised names include: +.TS +tab(:); +l l. +\fBdummy\fP:Dummy method +\fBrandr\fP:X RAndR +\fBvidmode\fP:X VidMode +\fBdrm\fP:Linux DRM +\fBgdi\fP:Windows GDI +\fBquartz\fP:Quartz Core Graphics +.TE + +The adjustment methods are supported via +.BR libgamma (7). +Only methods that were enabled when +.B libgamma +was compiled will be supported. + +If +.I method +is +.RB ' ? ', +all available adjustment methods are printed. +.TP +.BR \-p " "\fIpriority\fP +Set the priority of the filter. Filters with higher priority +are applied before filters with lower priority. The value +must be a signed 64-bit integer (between \-9223372036854775807 +and 9223372036854775807). +.BR cg-brilliance 's +default priority is \-6917529027641081856. + +If +.I priority +is +.RB ' ? ', +the utility's default priority is printed. +.TP +.BR \-R " "\fIrule\fP +Set the rule of of the filter to +.IR rule . +This is the last part of the filter's identifier (class). +The default rule is +.BR standard . + +If +.I rule +is +.RB ' ? ' +the utility's default rule is printed. If +.I rule +is +.RB ' ?? ' +the utility's default class is printed. +.TP +.BR \-S " "\fIsite\fP +Select the site to which to connect. For example +.RB ' :0 ', +for local display 0 when using +.BR X . +.TP +.B \-x +Remove the currently applied filter. +.SH SEE ALSO +.BR cg-tools (7) diff --git a/cg-shallow.c b/cg-shallow.c new file mode 100644 index 0000000..5b65364 --- /dev/null +++ b/cg-shallow.c @@ -0,0 +1,262 @@ +/* See LICENSE file for copyright and license details. */ +#include "cg-base.h" + +#include + +#include +#include +#include +#include +#include +#include + + + +/** + * The default filter priority for the program + */ +const int64_t default_priority = -((int64_t)3 << 61); + +/** + * The default class for the program + */ +char default_class[] = PKGNAME "::cg-shallow::standard"; + +/** + * Class suffixes + */ +const char* const* class_suffixes = (const char* const[]){NULL}; + + + +/** + * -d: keep process alive and remove filter on death + */ +static int dflag = 0; + +/** + * -x: remove filter rather than adding it + */ +static int xflag = 0; + +/** + * The emulated red resolution, 0 for unchanged. + */ +static size_t rres = 2; + +/** + * The emulated green resolution, 0 for unchanged. + */ +static size_t gres = 2; + +/** + * The emulated blue resolution, 0 for unchanged. + */ +static size_t bres = 2; + + +/** + * Print usage information and exit + */ +void usage(void) +{ + fprintf(stderr, + "Usage: %s [-M method] [-S site] [-c crtc]... [-R rule] " + "(-x | [-p priority] [-d] [all | red green blue])\n", + argv0); + exit(1); +} + + +/** + * Handle a command line option + * + * @param opt The option, it is a NUL-terminate two-character + * string starting with either '-' or '+', if the + * argument is not recognised, call `usage`. This + * string will not be "-M", "-S", "-c", "-p", or "-R". + * @param arg The argument associated with `opt`, + * `NULL` there is no next argument, if this + * parameter is `NULL` but needed, call `usage` + * @return 0 if `arg` was not used, + * 1 if `arg` was used, + * -1 on error + */ +int handle_opt(char* opt, char* arg) +{ + if (opt[0] == '-') + switch (opt[1]) + { + case 'd': + if (dflag || xflag) + usage(); + dflag = 1; + break; + case 'x': + if (xflag || dflag) + usage(); + xflag = 1; + break; + default: + usage(); + } + else + usage(); + return 0; + (void) arg; +} + + +/** + * Parse a non-negative integer encoded as a string + * + * @param out Output parameter for the value + * @param str The string + * @return Zero on success, -1 if the string is invalid + */ +static int parse_int(size_t* restrict out, const char* restrict str) +{ + char* end; + errno = 0; + if (!isdigit(*str)) + return -1; + *out = strtoul(str, &end, 10); + if (errno || *end) + return -1; + return 0; +} + + +/** + * This function is called after the last + * call to `handle_opt` + * + * @param argc The number of unparsed arguments + * @param argv `NULL` terminated list of unparsed arguments + * @param prio The argument associated with the "-p" option + * @return Zero on success, -1 on error + */ +int handle_args(int argc, char* argv[], char* prio) +{ + char* red = NULL; + char* green = NULL; + char* blue = NULL; + int q = xflag + (dflag | (argc > 0)); + if ((q > 1) || (xflag && (prio != NULL))) + usage(); + if (argc == 1) + red = green = blue = argv[0]; + else if (argc == 3) + { + red = argv[0]; + green = argv[1]; + blue = argv[2]; + } + else if (argc && !xflag) + usage(); + if (argc) + { + if (parse_int(&rres, red) < 0) + usage(); + if (parse_int(&gres, blue) < 0) + usage(); + if (parse_int(&bres, green) < 0) + usage(); + } + return 0; + (void) argv; +} + + +/** + * Fill a filter + * + * @param filter The filter to fill + */ +static void fill_filter(libcoopgamma_filter_t* restrict filter) +{ + switch (filter->depth) + { +#define X(CONST, MEMBER, MAX, TYPE)\ + case CONST:\ + libclut_lower_resolution(&(filter->ramps.MEMBER), MAX, TYPE, 0, rres, 0, gres, 0, bres);\ + break; +LIST_DEPTHS +#undef X + default: + abort(); + } +} + + +/** + * The main function for the program-specific code + * + * @return 0: Success + * -1: Error, `errno` set + * -2: Error, `cg.error` set + * -3: Error, message already printed + */ +int start(void) +{ + int r; + size_t i, j; + + if (xflag) + for (i = 0; i < filters_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_REMOVE; + else if (dflag) + for (i = 0; i < filters_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_DEATH; + else + for (i = 0; i < filters_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_REMOVAL; + + if (!xflag) + if ((r = make_slaves()) < 0) + return r; + + for (i = 0, r = 1; i < filters_n; i++) + { + if (!(crtc_updates[i].master) || !(crtc_info[crtc_updates[i].crtc].supported)) + continue; + if (!xflag) + fill_filter(&(crtc_updates[i].filter)); + r = update_filter(i, 0); + if ((r == -2) || ((r == -1) && (errno != EAGAIN))) + return r; + if (crtc_updates[i].slaves != NULL) + for (j = 0; crtc_updates[i].slaves[j] != 0; j++) + { + r = update_filter(crtc_updates[i].slaves[j], 0); + if ((r == -2) || ((r == -1) && (errno != EAGAIN))) + return r; + } + } + + while (r != 1) + if ((r = synchronise(-1)) < 0) + return r; + + if (!dflag) + return 0; + + if (libcoopgamma_set_nonblocking(&cg, 0) < 0) + return -1; + for (;;) + if (libcoopgamma_synchronise(&cg, NULL, 0, &j) < 0) + switch (errno) + { + case 0: + break; + case ENOTRECOVERABLE: + goto enotrecoverable; + default: + return -1; + } + + enotrecoverable: + for (;;) + if (pause() < 0) + return -1; +} diff --git a/cg-sleepmode.1 b/cg-sleepmode.1 new file mode 100644 index 0000000..e95c813 --- /dev/null +++ b/cg-sleepmode.1 @@ -0,0 +1,133 @@ +.TH CG-SLEEPMODE 1 CG-TOOLS +.SH NAME +cg-sleepmode - Gradually fade out the monitors, and gradually fade in on exit +.SH SYNOPSIS +.B cg-sleepmode +.RB [ \-M +.IR method ] +.RB [ \-S +.IR site ] +.RB [ \-c +.IR crtc "]... ["\fB\-R\fP +.IR rule ] +.RB [ \-p +.IR priority ] +.RB [ \-r +.IR red-fadeout-time ] +.RB [ \-g +.IR green-fadeout-time ] +.RB [ \-b +.IR blue-fadeout-time ] +.RI [ red-luminosity +.RI [ green-luminosity +.RI [ blue-luminosity ]]] +.SH DESCRIPTION +.B cg-sleepmode +gradually fade out the red, green, and blue channels on the +monitors to the brilliances of +.IR red-luminosity , +.IR green-luminosity , +and +.IR blue-luminosity , +respectively, whose default values are 0.25, 0, and 0, +respectively. A value of 0 means complete darkness, a +value of 1 means normal brilliance. +.P +.B cg-sleepmode +does not exist after the channels have been faded out, +instead, it's waits until it's killed, and then fades the +channel's back in to normal brilliance. +.SH OPTIONS +.TP +.BR \-r " "\fIred-fadeout-time\fP +Set the fade-out time for the red channel to +\fIred-fadeout-time\fP seconds. The fade-in time is based +on this value. The default fadeout time for the red channel +is 3 seconds. +.TP +.BR \-g " "\fIgreen-fadeout-time\fP +Set the fade-out time for the green channel to +\fIgreen-fadeout-time\fP seconds. The fade-in time is based +on this value. The default fadeout time for the green channel +is 2 seconds. +.TP +.BR \-b " "\fIblue-fadeout-time\fP +Set the fade-out time for the blue channel to +\fIblue-fadeout-time\fP seconds. The fade-in time is based +on this value. The default fadeout time for the blue channel +is 1 second. +.TP +.BR \-c " "\fIcrtc\fP +Apply the filter to the CRTC with the monitor whose EDID is +.IR crtc . +By default, the filter is applied to all monitors. + +If +.I crtc +is +.RB ' ? ', +all available CRTC's are listed. +.TP +.BR \-M " "\fImethod\fP +Adjustment method name or number. Recognised names include: +.TS +tab(:); +l l. +\fBdummy\fP:Dummy method +\fBrandr\fP:X RAndR +\fBvidmode\fP:X VidMode +\fBdrm\fP:Linux DRM +\fBgdi\fP:Windows GDI +\fBquartz\fP:Quartz Core Graphics +.TE + +The adjustment methods are supported via +.BR libgamma (7). +Only methods that were enabled when +.B libgamma +was compiled will be supported. + +If +.I method +is +.RB ' ? ', +all available adjustment methods are printed. +.TP +.BR \-p " "\fIpriority\fP +Set the priority of the filter. Filters with higher priority +are applied before filters with lower priority. The value +must be a signed 64-bit integer (between \-9223372036854775807 +and 9223372036854775807). +.BR cg-sleepmode 's +default priority is 1729382256910270464. + +If +.I priority +is +.RB ' ? ', +the utility's default priority is printed. +.TP +.BR \-R " "\fIrule\fP +Set the rule of of the filter to +.IR rule . +This is the last part of the filter's identifier (class). +The default rule is +.BR standard . + +If +.I rule +is +.RB ' ? ' +the utility's default rule is printed. If +.I rule +is +.RB ' ?? ' +the utility's default class is printed. +.TP +.BR \-S " "\fIsite\fP +Select the site to which to connect. For example +.RB ' :0 ', +for local display 0 when using +.BR X . +.SH SEE ALSO +.BR cg-tools (7) diff --git a/cg-sleepmode.c b/cg-sleepmode.c new file mode 100644 index 0000000..67b5570 --- /dev/null +++ b/cg-sleepmode.c @@ -0,0 +1,443 @@ +/* See LICENSE file for copyright and license details. */ +#include "cg-base.h" + +#include + +#if defined(_GNU_SOURCE) +# undef _GNU_SOURCE +#endif +#include +#include +#include +#include +#include +#include +#include +#include + + + +/** + * The default filter priority for the program + */ +const int64_t default_priority = (int64_t)3 << 59; + +/** + * The default class for the program + */ +char default_class[] = PKGNAME "::cg-sleepmode::standard"; + +/** + * Class suffixes + */ +const char* const* class_suffixes = (const char* const[]){NULL}; + + + +/** + * -r: fade-out time for the red channel + */ +static char* rflag; + +/** + * -g: fade-out time for the green channel + */ +static char* gflag; + +/** + * -b: fade-out time for the blue channel + */ +static char* bflag; + +/** + * The duration, in seconds, of the red channel's fade out + */ +static double red_time = 3; + +/** + * The duration, in seconds, of the green channel's fade out + */ +static double green_time = 2; + +/** + * The duration, in seconds, of the blue channel's fade out + */ +static double blue_time = 1; + +/** + * The luminosity of red channel after the fade out + */ +static double red_target = 0.5; + +/** + * The luminosity of green channel after the fade out + */ +static double green_target = 0; + +/** + * The luminosity of blue channel after the fade out + */ +static double blue_target = 0; + +/** + * Time to fade in? + */ +static volatile sig_atomic_t received_int = 0; + + + +/** + * Print usage information and exit + */ +void usage(void) +{ + fprintf(stderr, + "Usage: %s [-M method] [-S site] [-c crtc]... [-R rule] [-p priority] " + "[-r red-fadeout-time] [-g green-fadeout-time] [-b blue-fadeout-time] " + "[red-luminosity [green-luminosity [blue-luminosity]]]\n", + argv0); + exit(1); +} + + +/** + * Called when a signal is received + * that tells the program to terminate + * + * @param signo The received signal + */ +static void sig_int(int signo) +{ + received_int = 1; + (void) signo; +} + + +/** + * Handle a command line option + * + * @param opt The option, it is a NUL-terminate two-character + * string starting with either '-' or '+', if the + * argument is not recognised, call `usage`. This + * string will not be "-M", "-S", "-c", "-p", or "-R". + * @param arg The argument associated with `opt`, + * `NULL` there is no next argument, if this + * parameter is `NULL` but needed, call `usage` + * @return 0 if `arg` was not used, + * 1 if `arg` was used, + * -1 on error + */ +int handle_opt(char* opt, char* arg) +{ + if (opt[0] == '-') + switch (opt[1]) + { + case 'r': + if (rflag || !(rflag = arg)) + usage(); + return 1; + case 'g': + if (gflag || !(gflag = arg)) + usage(); + return 1; + case 'b': + if (bflag || !(bflag = arg)) + usage(); + return 1; + default: + usage(); + } + else + usage(); + return 0; +} + + +/** + * Parse a non-negative double encoded as a string + * + * @param out Output parameter for the value + * @param str The string + * @return Zero on success, -1 if the string is invalid + */ +static int parse_double(double* restrict out, const char* restrict str) +{ + char* end; + errno = 0; + *out = strtod(str, &end); + if (errno || (*out < 0) || isinf(*out) || isnan(*out) || *end) + return -1; + if (!*str || !strchr("0123456789.", *str)) + return -1; + return 0; +} + + +/** + * This function is called after the last + * call to `handle_opt` + * + * @param argc The number of unparsed arguments + * @param argv `NULL` terminated list of unparsed arguments + * @param prio The argument associated with the "-p" option + * @return Zero on success, -1 on error + */ +int handle_args(int argc, char* argv[], char* prio) +{ + int q = (rflag || gflag || bflag || argc); + if ((q > 1) || (argc > 3)) + usage(); + if (rflag != NULL) + if (parse_double(&red_time, rflag) < 0) + usage(); + if (gflag != NULL) + if (parse_double(&green_time, gflag) < 0) + usage(); + if (bflag != NULL) + if (parse_double(&blue_time, bflag) < 0) + usage(); + if (argc >= 1) + if (parse_double(&red_target, argv[0]) < 0) + usage(); + if (argc >= 2) + if (parse_double(&green_target, argv[1]) < 0) + usage(); + if (argc >= 3) + if (parse_double(&blue_target, argv[2]) < 0) + usage(); + if (red_target >= 1) + red_time = 0; + if (green_target >= 1) + green_time = 0; + if (blue_target >= 1) + blue_time = 0; + return 0; + (void) prio; +} + + +/** + * Fill a filter + * + * @param filter The filter to fill + * @param red The red brightness + * @param green The green brightness + * @param blue The blue brightness + */ +static void fill_filter(libcoopgamma_filter_t* restrict filter, double red, double green, double blue) +{ + switch (filter->depth) + { +#define X(CONST, MEMBER, MAX, TYPE)\ + case CONST:\ + libclut_start_over(&(filter->ramps.MEMBER), MAX, TYPE, 1, 1, 1);\ + libclut_rgb_brightness(&(filter->ramps.MEMBER), MAX, TYPE, red, green, blue);\ + break; +LIST_DEPTHS +#undef X + default: + abort(); + } +} + + +/** + * Get the current monotonic time as a double + * + * @param now Output parameter for the current time (monotonic) + * @return Zero on success, -1 on error + */ +static int double_time(double* restrict now) +{ +#ifndef CLOCK_MONOTONIC_RAW +# define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC +#endif + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) < 0) + return -1; + *now = (double)(ts.tv_nsec); + *now /= 1000000000L; + *now += (double)(ts.tv_sec); + return 0; +} + + +/** + * The main function for the program-specific code + * + * @return 0: Success + * -1: Error, `errno` set + * -2: Error, `cg.error` set + * -3: Error, message already printed + */ +int start(void) +{ + int r, fade_red, fade_green, fade_blue; + size_t i, j; + double t, starttime, red, green, blue, redt, greent, bluet; + + redt = (red_target - 1) / red_time; + greent = (green_target - 1) / green_time; + bluet = (blue_target - 1) / blue_time; + fade_red = !isinf(redt) && !isnan(redt); + fade_green = !isinf(greent) && !isnan(greent); + fade_blue = !isinf(bluet) && !isnan(bluet); + + for (i = 0; i < filters_n; i++) + crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_DEATH; + + if ((r = make_slaves()) < 0) + return r; + + if ((r = double_time(&starttime)) < 0) + return r; + + red = red_target < 0 ? 0 : red_target > 1 ? 1 : red_target; + green = green_target < 0 ? 0 : green_target > 1 ? 1 : green_target; + blue = blue_target < 0 ? 0 : blue_target > 1 ? 1 : blue_target; + + for (;;) + { + if ((r = double_time(&t)) < 0) + return r; + t -= starttime; + if (fade_red) + {if (red = 1 + t * redt, red > 1) red = 1; else if (red < 0) red = 0;} + if (fade_green) + {if (green = 1 + t * greent, green > 1) green = 1; else if (green < 0) green = 0;} + if (fade_blue) + {if (blue = 1 + t * bluet, blue > 1) blue = 1; else if (blue < 0) blue = 0;} + + for (i = 0, r = 1; i < filters_n; i++) + { + if (!(crtc_updates[i].master) || !(crtc_info[crtc_updates[i].crtc].supported)) + continue; + fill_filter(&(crtc_updates[i].filter), red, green, blue); + r = update_filter(i, 0); + if ((r == -2) || ((r == -1) && (errno != EAGAIN))) + return r; + if (crtc_updates[i].slaves != NULL) + for (j = 0; crtc_updates[i].slaves[j] != 0; j++) + { + r = update_filter(crtc_updates[i].slaves[j], 0); + if ((r == -2) || ((r == -1) && (errno != EAGAIN))) + return r; + } + } + + while (r != 1) + if ((r = synchronise(-1)) < 0) + return r; + + sched_yield(); + + if ((t >= red_time) && (t >= green_time) && (t >= blue_time)) + break; + } + + if ((signal(SIGINT, sig_int) == SIG_ERR) || + (signal(SIGTERM, sig_int) == SIG_ERR) || + (signal(SIGHUP, sig_int) == SIG_ERR)) + return -1; + + if (libcoopgamma_set_nonblocking(&cg, 0) < 0) + return -1; + for (;;) + if (libcoopgamma_synchronise(&cg, NULL, 0, &j) < 0) + { + if (received_int) + goto fade_in; + switch (errno) + { + case 0: + break; + case ENOTRECOVERABLE: + goto enotrecoverable; + default: + return 1; + } + } + + fade_in: + if (libcoopgamma_set_nonblocking(&cg, 1) < 0) + return -1; + + t = red_time; + t = t > green_time ? t : green_time; + t = t > blue_time ? t : blue_time; + redt = t - red_time; + greent = t - green_time; + bluet = t - blue_time; + t = red_time + green_time + blue_time; + if (red_time > 0) + t = t < red_time ? t : red_time; + if (green_time > 0) + t = t < green_time ? t : green_time; + if (blue_time > 0) + t = t < blue_time ? t : blue_time; + red_time = t + redt; + green_time = t + greent; + blue_time = t + bluet; + + red = green = blue = 1; + + if ((r = double_time(&starttime)) < 0) + return r; + + for (;;) + { + if ((r = double_time(&t)) < 0) + return r; + t -= starttime; + redt = t / red_time; + greent = t / green_time; + bluet = t / blue_time; + if (!isinf(redt) && !isnan(redt)) + { + red = red_target * (1 - redt) + redt; + if (red > 1) red = 1; else if (red < 0) red = 0; + } + if (!isinf(greent) && !isnan(greent)) + { + green = green_target * (1 - greent) + greent; + if (green > 1) green = 1; else if (green < 0) green = 0; + } + if (!isinf(bluet) && !isnan(bluet)) + { + blue = blue_target * (1 - bluet) + bluet; + if (blue > 1) blue = 1; else if (blue < 0) blue = 0; + } + + for (i = 0, r = 1; i < filters_n; i++) + { + if (!(crtc_updates[i].master) || !(crtc_info[crtc_updates[i].crtc].supported)) + continue; + fill_filter(&(crtc_updates[i].filter), red, green, blue); + r = update_filter(i, 0); + if ((r == -2) || ((r == -1) && (errno != EAGAIN))) + return r; + if (crtc_updates[i].slaves != NULL) + for (j = 0; crtc_updates[i].slaves[j] != 0; j++) + { + r = update_filter(crtc_updates[i].slaves[j], 0); + if ((r == -2) || ((r == -1) && (errno != EAGAIN))) + return r; + } + } + + while (r != 1) + if ((r = synchronise(-1)) < 0) + return r; + + sched_yield(); + + if ((t >= red_time) && (t >= green_time) && (t >= blue_time)) + break; + } + + return 0; + enotrecoverable: + for (;;) + if (pause() < 0) + return -1; +} diff --git a/cg-tools.7 b/cg-tools.7 new file mode 100644 index 0000000..387a2c1 --- /dev/null +++ b/cg-tools.7 @@ -0,0 +1,46 @@ +.TH CG-TOOLS 7 CG-TOOLS +.SH NAME +cg-tools - Cooperative gamma tools +.SH DESCRIPTION +.B cg-tools +is a collection of utilities that change the colour output on monitors +without overriding each other. +.SH UTILITIES +.TP +.BR cg-brilliance (1) +Set the brilliance on the monitors. +.TP +.BR cg-darkroom (1) +Invert colour on the monitors and make them dark red. +.TP +.BR cg-gamma (1) +Adjust the gamma curves on the monitors. +.TP +.BR cg-icc (1) +Apply ICC profiles to the monitors. +.TP +.BR cg-limits (1) +Adjust the brightness and contrast on the monitors. +.TP +.BR cg-linear (1) +Create a span of priorities where adjustments are made +on unencoded (linear) RGB rather than encoded RGB. +.TP +.BR cg-negative (1) +Invert colour on the monitors. +.TP +.BR cg-query (1) +List output filters applied to a monitor. +.TP +.BR cg-rainbow (1) +Adds a rainbow cycle effect to the monitors. +.TP +.BR cg-remove (1) +Remove output filter from monitors. +.TP +.BR cg-sleepmode (1) +Gradually fade out the monitors, and gradually fade in on exit. +.SH SEE ALSO +.BR libcoopgamma (7), +.BR coopgammad (1), +.BR radharc (1) diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..a3ec1ce --- /dev/null +++ b/config.mk @@ -0,0 +1,8 @@ +PREFIX = /usr +MANPREFIX = $(PREFIX)/share/man + +PKGNAME = cg-tools + +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D'PKGNAME="$(PKGNAME)"' +CFLAGS = -std=c99 -Wall -O2 +LDFLAGS = -lcoopgamma -lm -s diff --git a/man/cg-brilliance.1 b/man/cg-brilliance.1 deleted file mode 100644 index a79583e..0000000 --- a/man/cg-brilliance.1 +++ /dev/null @@ -1,118 +0,0 @@ -.TH CG-BRILLIANCE 1 CG-TOOLS -.SH NAME -cg-brilliance - Set the brilliance on the monitors -.SH SYNOPSIS -.B cg-brilliance -.RB [ \-M -.IR method ] -.RB [ \-S -.IR site ] -.RB [ \-c -.IR crtc "]... ["\fB\-R\fP -.IR rule ] -.RB ( \-x -| -.RB [ \-p -.IR priority ] -.RB [ \-d ] -.RI ( all -| -.I red -.I green -.IR blue )) -.SH DESCRIPTION -.B cg-brilliance -changes the brilliance on the monitors, to -.I all -on all channel, or to -.IR red , -.IR green , -and -.I blue -to the red, green, and blue channels independently, respectively. -A value of 0 means complete darkness, and 1 means normal brilliance. -.SH OPTIONS -.TP -.BR \-c " "\fIcrtc\fP -Apply the filter to the CRTC with the monitor whose EDID is -.IR crtc . -By default, the filter is applied to all monitors. - -If -.I crtc -is -.RB ' ? ', -all available CRTC's are listed. -.TP -.B \-d -Keep the process alive and remove the filter on death. -.TP -.BR \-M " "\fImethod\fP -Adjustment method name or number. Recognised names include: -.TS -tab(:); -l l. -\fBdummy\fP:Dummy method -\fBrandr\fP:X RAndR -\fBvidmode\fP:X VidMode -\fBdrm\fP:Linux DRM -\fBgdi\fP:Windows GDI -\fBquartz\fP:Quartz Core Graphics -.TE - -The adjustment methods are supported via -.BR libgamma (7). -Only methods that were enabled when -.B libgamma -was compiled will be supported. - -If -.I method -is -.RB ' ? ', -all available adjustment methods are printed. -.TP -.BR \-p " "\fIpriority\fP -Set the priority of the filter. Filters with higher priority -are applied before filters with lower priority. The value -must be a signed 64-bit integer (between \-9223372036854775807 -and 9223372036854775807). -.BR cg-brilliance 's -default priority is 2305843009213693952. - -If -.I priority -is -.RB ' ? ', -the utility's default priority is printed. -.TP -.BR \-R " "\fIrule\fP -Set the rule of of the filter to -.IR rule . -This is the last part of the filter's identifier (class). -The default rule is -.BR standard . - -If -.I rule -is -.RB ' ? ' -the utility's default rule is printed. If -.I rule -is -.RB ' ?? ' -the utility's default class is printed. -.TP -.BR \-S " "\fIsite\fP -Select the site to which to connect. For example -.RB ' :0 ', -for local display 0 when using -.BR X . -.TP -.B \-x -Remove the currently applied filter. -.SH "SEE ALSO" -.BR cg-tools (7) -.SH BUGS -Please report bugs to https://github.com/maandree/cg-tools/issues -or to maandree@kth.se diff --git a/man/cg-darkroom.1 b/man/cg-darkroom.1 deleted file mode 100644 index 106f62f..0000000 --- a/man/cg-darkroom.1 +++ /dev/null @@ -1,111 +0,0 @@ -.TH CG-DARKROOM 1 CG-TOOLS -.SH NAME -cg-darkroom - Invert colour on the monitors and make them dark red -.SH SYNOPSIS -.B cg-darkroom -.RB [ \-M -.IR method ] -.RB [ \-S -.IR site ] -.RB [ \-c -.IR crtc "]... ["\fB\-R\fP -.IR rule ] -.RB ( \-x -| -.RB [ \-p -.IR priority ] -.RB [ \-d ] -.RI [ brightness ]) -.SH DESCRIPTION -.B cg-darkroom -inverts the colours on the monitors, dim them, and turns of -the green and blue channels. The monitors are dimmed to the -brilliance of -.IR brightness , -or 0.25 if not specified. A value of 0 means complete darkness, -and 1 means normal brilliance. -.SH OPTIONS -.TP -.BR \-c " "\fIcrtc\fP -Apply the filter to the CRTC with the monitor whose EDID is -.IR crtc . -By default, the filter is applied to all monitors. - -If -.I crtc -is -.RB ' ? ', -all available CRTC's are listed. -.TP -.B \-d -Keep the process alive and remove the filter on death. -.TP -.BR \-M " "\fImethod\fP -Adjustment method name or number. Recognised names include: -.TS -tab(:); -l l. -\fBdummy\fP:Dummy method -\fBrandr\fP:X RAndR -\fBvidmode\fP:X VidMode -\fBdrm\fP:Linux DRM -\fBgdi\fP:Windows GDI -\fBquartz\fP:Quartz Core Graphics -.TE - -The adjustment methods are supported via -.BR libgamma (7). -Only methods that were enabled when -.B libgamma -was compiled will be supported. - -If -.I method -is -.RB ' ? ', -all available adjustment methods are printed. -.TP -.BR \-p " "\fIpriority\fP -Set the priority of the filter. Filters with higher priority -are applied before filters with lower priority. The value -must be a signed 64-bit integer (between \-9223372036854775807 -and 9223372036854775807). -.BR cg-darkroom 's -default priority is 6917529027641081856. - -If -.I priority -is -.RB ' ? ', -the utility's default priority is printed. -.TP -.BR \-R " "\fIrule\fP -Set the rule of of the filter to -.IR rule . -This is the last part of the filter's identifier (class). -The default rule is -.BR standard . - -If -.I rule -is -.RB ' ? ' -the utility's default rule is printed. If -.I rule -is -.RB ' ?? ' -the utility's default class is printed. -.TP -.BR \-S " "\fIsite\fP -Select the site to which to connect. For example -.RB ' :0 ', -for local display 0 when using -.BR X . -.TP -.B \-x -Remove the currently applied filter. -.SH "SEE ALSO" -.BR cg-tools (7) -.SH BUGS -Please report bugs to https://github.com/maandree/cg-tools/issues -or to maandree@kth.se diff --git a/man/cg-gamma.1 b/man/cg-gamma.1 deleted file mode 100644 index 47681f7..0000000 --- a/man/cg-gamma.1 +++ /dev/null @@ -1,143 +0,0 @@ -.TH CG-GAMMA 1 CG-TOOLS -.SH NAME -cg-gamma - Adjust the gamma curves on the monitors -.SH SYNOPSIS -.B cg-gamma -.RB [ \-M -.IR method ] -.RB [ \-S -.IR site ] -.RB [ \-c -.IR crtc "]... ["\fB\-R\fP -.IR rule ] -.RB ( \-x -| -.RB [ \-p -.IR priority ] -.RB [ \-d ] -.RB [ \-f -.I file -| -.I all -| -.I red -.I green -.IR blue ]) -.SH DESCRIPTION -.B cg-gamma -sets the gamma on the monitors to -.I all -on all channels, or -.IR red , -.IR green , -and -.IR blue -to the red, green, and blue channels, respectively. Alternatively, -.B cg-gamma -can change the gamma on each monitor independently by using -reading the values from -.I file -or a default file. -.P -The file is a 4-column text file where empty lines and lines -starting with a -.RB ' # ', -after any whitespace, are ignored. -The values in the columns should be, in order, a monitor's -EDID, that monitor's red gamma value, green gamma value, and -blue gamma value. -.SH OPTIONS -.TP -.BR \-f " "\fIfile\fP -Read the gamma values from the selected file. -.TP -.BR \-c " "\fIcrtc\fP -Apply the filter to the CRTC with the monitor whose EDID is -.IR crtc . -By default, the filter is applied to all monitors. - -If -.I crtc -is -.RB ' ? ', -all available CRTC's are listed. -.TP -.B \-d -Keep the process alive and remove the filter on death. -.TP -.BR \-M " "\fImethod\fP -Adjustment method name or number. Recognised names include: -.TS -tab(:); -l l. -\fBdummy\fP:Dummy method -\fBrandr\fP:X RAndR -\fBvidmode\fP:X VidMode -\fBdrm\fP:Linux DRM -\fBgdi\fP:Windows GDI -\fBquartz\fP:Quartz Core Graphics -.TE - -The adjustment methods are supported via -.BR libgamma (7). -Only methods that were enabled when -.B libgamma -was compiled will be supported. - -If -.I method -is -.RB ' ? ', -all available adjustment methods are printed. -.TP -.BR \-p " "\fIpriority\fP -Set the priority of the filter. Filters with higher priority -are applied before filters with lower priority. The value -must be a signed 64-bit integer (between \-9223372036854775807 -and 9223372036854775807). -.BR cg-gamma 's -default priority is 0. - -If -.I priority -is -.RB ' ? ', -the utility's default priority is printed. -.TP -.BR \-R " "\fIrule\fP -Set the rule of of the filter to -.IR rule . -This is the last part of the filter's identifier (class). -The default rule is -.BR standard . - -If -.I rule -is -.RB ' ? ' -the utility's default rule is printed. If -.I rule -is -.RB ' ?? ' -the utility's default class is printed. -.TP -.BR \-S " "\fIsite\fP -Select the site to which to connect. For example -.RB ' :0 ', -for local display 0 when using -.BR X . -.TP -.B \-x -Remove the currently applied filter. -.SH FILES -.TP -.B ~/.config/gamma -The default gamma table file. -.TP -.B /etc/gamma -The fallback gamma table file. -.SH "SEE ALSO" -.BR cg-tools (7) -.SH BUGS -Please report bugs to https://github.com/maandree/cg-tools/issues -or to maandree@kth.se diff --git a/man/cg-icc.1 b/man/cg-icc.1 deleted file mode 100644 index 8de9b82..0000000 --- a/man/cg-icc.1 +++ /dev/null @@ -1,128 +0,0 @@ -.TH CG-ICC 1 CG-TOOLS -.SH NAME -cg-icc - Apply ICC profiles to the monitors -.SH SYNOPSIS -.B cg-icc -.RB [ \-M -.IR method ] -.RB [ \-S -.IR site ] -.RB [ \-c -.IR crtc "]... ["\fB\-R\fP -.IR rule ] -.RB ( \-x -| -.RB [ \-p -.IR priority ] -.RB [ \-d ] -.RI [ file ]) -.SH DESCRIPTION -.B cg-icc -applies the ICC profile -.I file -to the monitors. If -.I file -is not specified, -.B cg-icc -reads a default ICC profile table file and applies -the appropriate profiles to each monitor, which as a -profile, independently. -.P -The file is a 2-column text file where empty lines and lines -starting with a -.RB ' # ', -after any whitespace, are ignored. The -first value is a monitor's EDID, and the second value is the -ICC file for that monitor. -.SH OPTIONS -.TP -.BR \-c " "\fIcrtc\fP -Apply the filter to the CRTC with the monitor whose EDID is -.IR crtc . -By default, the filter is applied to all monitors. - -If -.I crtc -is -.RB ' ? ', -all available CRTC's are listed. -.TP -.B \-d -Keep the process alive and remove the filter on death. -.TP -.BR \-M " "\fImethod\fP -Adjustment method name or number. Recognised names include: -.TS -tab(:); -l l. -\fBdummy\fP:Dummy method -\fBrandr\fP:X RAndR -\fBvidmode\fP:X VidMode -\fBdrm\fP:Linux DRM -\fBgdi\fP:Windows GDI -\fBquartz\fP:Quartz Core Graphics -.TE - -The adjustment methods are supported via -.BR libgamma (7). -Only methods that were enabled when -.B libgamma -was compiled will be supported. - -If -.I method -is -.RB ' ? ', -all available adjustment methods are printed. -.TP -.BR \-p " "\fIpriority\fP -Set the priority of the filter. Filters with higher priority -are applied before filters with lower priority. The value -must be a signed 64-bit integer (between \-9223372036854775807 -and 9223372036854775807). -.BR cg-icc 's -default priority is 0. - -If -.I priority -is -.RB ' ? ', -the utility's default priority is printed. -.TP -.BR \-R " "\fIrule\fP -Set the rule of of the filter to -.IR rule . -This is the last part of the filter's identifier (class). -The default rule is -.BR standard . - -If -.I rule -is -.RB ' ? ' -the utility's default rule is printed. If -.I rule -is -.RB ' ?? ' -the utility's default class is printed. -.TP -.BR \-S " "\fIsite\fP -Select the site to which to connect. For example -.RB ' :0 ', -for local display 0 when using -.BR X . -.TP -.B \-x -Remove the currently applied filter. -.SH FILES -.TP -.B ~/.config/icctab -The default ICC profile table file. -.TP -.B /etc/icctab -The fallback ICC profile table file. -.SH "SEE ALSO" -.BR cg-tools (7) -.SH BUGS -Please report bugs to https://github.com/maandree/cg-tools/issues -or to maandree@kth.se diff --git a/man/cg-limits.1 b/man/cg-limits.1 deleted file mode 100644 index 363190d..0000000 --- a/man/cg-limits.1 +++ /dev/null @@ -1,164 +0,0 @@ -.TH CG-LIMITS 1 CG-TOOLS -.SH NAME -cg-limits - Adjust the brightness and contrast on the monitors -.SH SYNOPSIS -.B cg-limits -.RB [ \-M -.IR method ] -.RB [ \-S -.IR site ] -.RB [ \-c -.IR crtc "]... ["\fB\-R\fP -.IR rule ] -.RB ( \-x -| -.RB [ \-p -.IR priority ] -.RB [ \-d ] -.RB ([ \-B -.IR brightness-file ] -.RB [ \-C -.IR contrast-file ] -| -.IB brightness-all : contrast-all -| -.IB brightness-red : contrast-red -.IB brightness-green : contrast-green -.IR brightness-blue \fB:\fP contrast-blue )) -.SH DESCRIPTION -.B cg-limits -sets the brightness (the brightness of black) on the monitors to -.I brightness-all -and the contrast (the brightness of white) on the monitors to -.I contrast-all -on all channels, or the brightness to -.IR brightness-red , -.IR brightness-green , -and -.I brightness-blue -to the red, green, and blue channels, respectively, and the -contrasts to -.IR contrast-red , -.IR contrast-green , -and -.IR contrast-blue . -Alternatively, -.B cg-limits -can change the brightness on each monitor independently by -using reading the values from -.I brightness-file -and the contrast on each monitor independently by -using reading the values from -.I contrast-file -or default files. -.P -The files are 4-column text files where empty lines and lines -starting with a -.RB ' # ', -after any whitespace, are ignored. -The values in the columns should be, in order, a monitor's -EDID, that monitor's red value, green value, and blue value. -.SH OPTIONS -.TP -.B \-B " "\fIbrightness-file\fP -Read the brightness values from the selected file. -.TP -.B \-C " "\fIbrightness-file\fP -Read the contrast values from the selected file. -.TP -.BR \-c " "\fIcrtc\fP -Apply the filter to the CRTC with the monitor whose EDID is -.IR crtc . -By default, the filter is applied to all monitors. - -If -.I crtc -is -.RB ' ? ', -all available CRTC's are listed. -.TP -.B \-d -Keep the process alive and remove the filter on death. -.TP -.BR \-M " "\fImethod\fP -Adjustment method name or number. Recognised names include: -.TS -tab(:); -l l. -\fBdummy\fP:Dummy method -\fBrandr\fP:X RAndR -\fBvidmode\fP:X VidMode -\fBdrm\fP:Linux DRM -\fBgdi\fP:Windows GDI -\fBquartz\fP:Quartz Core Graphics -.TE - -The adjustment methods are supported via -.BR libgamma (7). -Only methods that were enabled when -.B libgamma -was compiled will be supported. - -If -.I method -is -.RB ' ? ', -all available adjustment methods are printed. -.TP -.BR \-p " "\fIpriority\fP -Set the priority of the filter. Filters with higher priority -are applied before filters with lower priority. The value -must be a signed 64-bit integer (between \-9223372036854775807 -and 9223372036854775807). -.BR cg-limits 's -default priority is \-4611686018427387904. - -If -.I priority -is -.RB ' ? ', -the utility's default priority is printed. -.TP -.BR \-R " "\fIrule\fP -Set the rule of of the filter to -.IR rule . -This is the last part of the filter's identifier (class). -The default rule is -.BR standard . - -If -.I rule -is -.RB ' ? ' -the utility's default rule is printed. If -.I rule -is -.RB ' ?? ' -the utility's default class is printed. -.TP -.BR \-S " "\fIsite\fP -Select the site to which to connect. For example -.RB ' :0 ', -for local display 0 when using -.BR X . -.TP -.B \-x -Remove the currently applied filter. -.SH FILES -.TP -.B ~/.config/brightness -The default brightness table file. -.TP -.B ~/.config/contrast -The default contrast table file. -.TP -.B /etc/brightness -The fallback brightness table file. -.TP -.B /etc/contrast -The fallback contrast table file. -.SH "SEE ALSO" -.BR cg-tools (7) -.SH BUGS -Please report bugs to https://github.com/maandree/cg-tools/issues -or to maandree@kth.se diff --git a/man/cg-linear.1 b/man/cg-linear.1 deleted file mode 100644 index cb19307..0000000 --- a/man/cg-linear.1 +++ /dev/null @@ -1,126 +0,0 @@ -.TH CG-LINEAR 1 CG-TOOLS -.SH NAME -cg-linear - Create a span where adjustments are over unencodec RGB -.SH SYNOPSIS -.B cg-linear -.RB [ \-M -.IR method ] -.RB [ \-S -.IR site ] -.RB [ \-c -.IR crtc "]... ["\fB\-R\fP -.IR rule-base ] -.RB ( \-x -| -.B \-p -.IB start-priority : stop-priority -.RB [ \-d ] -.RB [ \+rgb ]) -.SH DESCRIPTION -.B cg-linear -adds two filters to the CLUT, one filter that converts -all stops to unencoded RGB, and a second filter that -converts all stops to encodec RGB. -.P -Note that if this is applied multiple times, values -can be doubly encoded and doubly decode. -.SH OPTIONS -.TP -.BR \+r -Ignore the red channel. -.TP -.BR \+g -Ignore the green channel. -.TP -.BR \+b -Ignore the blue channel. -.TP -.BR \-c " "\fIcrtc\fP -Apply the filter to the CRTC with the monitor whose EDID is -.IR crtc . -By default, the filter is applied to all monitors. - -If -.I crtc -is -.RB ' ? ', -all available CRTC's are listed. -.TP -.B \-d -Keep the process alive and remove the filter on death. -.TP -.BR \-M " "\fImethod\fP -Adjustment method name or number. Recognised names include: -.TS -tab(:); -l l. -\fBdummy\fP:Dummy method -\fBrandr\fP:X RAndR -\fBvidmode\fP:X VidMode -\fBdrm\fP:Linux DRM -\fBgdi\fP:Windows GDI -\fBquartz\fP:Quartz Core Graphics -.TE - -The adjustment methods are supported via -.BR libgamma (7). -Only methods that were enabled when -.B libgamma -was compiled will be supported. - -If -.I method -is -.RB ' ? ', -all available adjustment methods are printed. -.TP -.BR \-p " "\fIstart-priority\fP\fB:\fP\fIstop-priority\fP -Set the priority of the linearisation filter to -.IR start-priority , -and the priority of the delinearisation filter to -.IR stop-priority . -Filters with higher priority are applied before filters with -lower priority. This means that normally you want the -.I start-priority -to be higher than -.IR stop-priority . -The value must be a signed 64-bit integer (between -\-9223372036854775807 and 9223372036854775807). -.TP -.BR \-R " "\fIrule-base\fP -Set the rule base of of the filters to -.IR rule-base . -.RB ' :start ' -and -.RB ' :stop ' -are appended to the -linearisation filter's rule and delinearisation filter's rule, -respectively. The rule is the last part of the filter's -identifier (class). The default rule base is -.BR standard . - -If -.I rule -is -.RB ' ? ' -the utility's default rule vase is printed. If -.I rule -is -.RB ' ?? ' -the utility's default classes is printed. The first printed -class is for the linearisation filter, and the second printed -class is for the delinearisation filter. -.TP -.BR \-S " "\fIsite\fP -Select the site to which to connect. For example -.RB ' :0 ', -for local display 0 when using -.BR X . -.TP -.B \-x -Remove the currently applied filter. -.SH "SEE ALSO" -.BR cg-tools (7) -.SH BUGS -Please report bugs to https://github.com/maandree/cg-tools/issues -or to maandree@kth.se diff --git a/man/cg-negative.1 b/man/cg-negative.1 deleted file mode 100644 index 098cc1b..0000000 --- a/man/cg-negative.1 +++ /dev/null @@ -1,115 +0,0 @@ -.TH CG-NEGATIVE 1 CG-TOOLS -.SH NAME -cg-negative - Invert colour on the monitors -.SH SYNOPSIS -.B cg-negative -.RB [ \-M -.IR method ] -.RB [ \-S -.IR site ] -.RB [ \-c -.IR crtc "]... ["\fB\-R\fP -.IR rule ] -.RB ( \-x -| -.RB [ \-p -.IR priority ] -.RB [ \-d ] -.RB [ \+rgb ]) -.SH DESCRIPTION -.B cg-negative -inverts the colours on the monitors. -.SH OPTIONS -.TP -.BR \+r -Do not invert the red channel. -.TP -.BR \+g -Do not invert the green channel. -.TP -.BR \+b -Do not invert the blue channel. -.TP -.BR \-c " "\fIcrtc\fP -Apply the filter to the CRTC with the monitor whose EDID is -.IR crtc . -By default, the filter is applied to all monitors. - -If -.I crtc -is -.RB ' ? ', -all available CRTC's are listed. -.TP -.B \-d -Keep the process alive and remove the filter on death. -.TP -.BR \-M " "\fImethod\fP -Adjustment method name or number. Recognised names include: -.TS -tab(:); -l l. -\fBdummy\fP:Dummy method -\fBrandr\fP:X RAndR -\fBvidmode\fP:X VidMode -\fBdrm\fP:Linux DRM -\fBgdi\fP:Windows GDI -\fBquartz\fP:Quartz Core Graphics -.TE - -The adjustment methods are supported via -.BR libgamma (7). -Only methods that were enabled when -.B libgamma -was compiled will be supported. - -If -.I method -is -.RB ' ? ', -all available adjustment methods are printed. -.TP -.BR \-p " "\fIpriority\fP -Set the priority of the filter. Filters with higher priority -are applied before filters with lower priority. The value -must be a signed 64-bit integer (between \-9223372036854775807 -and 9223372036854775807). -.BR cg-negative 's -default priority is 4611686018427387904. - -If -.I priority -is -.RB ' ? ', -the utility's default priority is printed. -.TP -.BR \-R " "\fIrule\fP -Set the rule of of the filter to -.IR rule . -This is the last part of the filter's identifier (class). -The default rule is -.BR standard . - -If -.I rule -is -.RB ' ? ' -the utility's default rule is printed. If -.I rule -is -.RB ' ?? ' -the utility's default class is printed. -.TP -.BR \-S " "\fIsite\fP -Select the site to which to connect. For example -.RB ' :0 ', -for local display 0 when using -.BR X . -.TP -.B \-x -Remove the currently applied filter. -.SH "SEE ALSO" -.BR cg-tools (7) -.SH BUGS -Please report bugs to https://github.com/maandree/cg-tools/issues -or to maandree@kth.se diff --git a/man/cg-query.1 b/man/cg-query.1 deleted file mode 100644 index f726ebd..0000000 --- a/man/cg-query.1 +++ /dev/null @@ -1,75 +0,0 @@ -.TH CG-QUERY 1 CG-TOOLS -.SH NAME -cg-query - List output filters applied to a monitor -.SH SYNOPSIS -.B cg-query -.RB [ \-M -.IR method ] -.RB [ \-S -.IR site ] -.RB [ \-h -.IR high ] -.RB [ \-l -.IR low ] -.B \-c -.I crtc -.SH DESCRIPTION -.B cg-query -prints a list of all output filters applied to the monitor -whose EDID is -.IR crtc . -.SH OPTIONS -.TP -.BR \-h " "\fIhigh\fP -Do not list filter with higher priority than -.IR high . -.TP -.BR \-l " "\fIlow\fP -Do not list filter with lower priority than -.IR low . -.TP -.BR \-c " "\fIcrtc\fP -Apply the filter to the CRTC with the monitor whose EDID is -.IR crtc . - -If -.I crtc -is -.RB ' ? ', -all available CRTC's are listed. -.TP -.BR \-M " "\fImethod\fP -Adjustment method name or number. Recognised names include: -.TS -tab(:); -l l. -\fBdummy\fP:Dummy method -\fBrandr\fP:X RAndR -\fBvidmode\fP:X VidMode -\fBdrm\fP:Linux DRM -\fBgdi\fP:Windows GDI -\fBquartz\fP:Quartz Core Graphics -.TE - -The adjustment methods are supported via -.BR libgamma (7). -Only methods that were enabled when -.B libgamma -was compiled will be supported. - -If -.I method -is -.RB ' ? ', -all available adjustment methods are printed. -.TP -.BR \-S " "\fIsite\fP -Select the site to which to connect. For example -.RB ' :0 ', -for local display 0 when using -.BR X . -.SH "SEE ALSO" -.BR cg-tools (7) -.SH BUGS -Please report bugs to https://github.com/maandree/cg-tools/issues -or to maandree@kth.se diff --git a/man/cg-rainbow.1 b/man/cg-rainbow.1 deleted file mode 100644 index cd397f8..0000000 --- a/man/cg-rainbow.1 +++ /dev/null @@ -1,117 +0,0 @@ -.TH CG-RAINBOW 1 CG-TOOLS -.SH NAME -cg-rainbow - Adds a rainbow cycle effect to the monitors -.SH SYNOPSIS -.B cg-rainbow -.RB [ \-M -.IR method ] -.RB [ \-S -.IR site ] -.RB [ \-c -.IR crtc "]... ["\fB\-R\fP -.IR rule ] -.RB [ \-p -.IR priority ] -.RB [ \-l -.IR luminosity ] -.RB [ \-s -.IR rainbowhz ] -.SH DESCRIPTION -.B cg-rainbow -cycles between a the rainbow's colors and use the colours -as the monitors' white colour. -.P -.B cg-rainbow -is a rewrite of -.BR xrainbow (1) -and -.BR grainbow (1). -.SH OPTIONS -.TP -.BR \-l " "\fIluminosity\fP -Select the luminosity of the rainbow. A value of 0 means -the rainbow is completely black, and a value of 1 means the -the rainbow is fully saturated, but not oversaturated. The -default luminosity is one third. -.TP -.BR \-s " "\fIrainbowhz\fP -Select the number of of rainbow-cycles per second. The -default frequency is one third. -.TP -.BR \-c " "\fIcrtc\fP -Apply the filter to the CRTC with the monitor whose EDID is -.IR crtc . -By default, the filter is applied to all monitors. - -If -.I crtc -is -.RB ' ? ', -all available CRTC's are listed. -.TP -.BR \-M " "\fImethod\fP -Adjustment method name or number. Recognised names include: -.TS -tab(:); -l l. -\fBdummy\fP:Dummy method -\fBrandr\fP:X RAndR -\fBvidmode\fP:X VidMode -\fBdrm\fP:Linux DRM -\fBgdi\fP:Windows GDI -\fBquartz\fP:Quartz Core Graphics -.TE - -The adjustment methods are supported via -.BR libgamma (7). -Only methods that were enabled when -.B libgamma -was compiled will be supported. - -If -.I method -is -.RB ' ? ', -all available adjustment methods are printed. -.TP -.BR \-p " "\fIpriority\fP -Set the priority of the filter. Filters with higher priority -are applied before filters with lower priority. The value -must be a signed 64-bit integer (between \-9223372036854775807 -and 9223372036854775807). -.BR cg-rainbow 's -default priority is 1152921504606846976. - -If -.I priority -is -.RB ' ? ', -the utility's default priority is printed. -.TP -.BR \-R " "\fIrule\fP -Set the rule of of the filter to -.IR rule . -This is the last part of the filter's identifier (class). -The default rule is -.BR standard . - -If -.I rule -is -.RB ' ? ' -the utility's default rule is printed. If -.I rule -is -.RB ' ?? ' -the utility's default class is printed. -.TP -.BR \-S " "\fIsite\fP -Select the site to which to connect. For example -.RB ' :0 ', -for local display 0 when using -.BR X . -.SH "SEE ALSO" -.BR cg-tools (7) -.SH BUGS -Please report bugs to https://github.com/maandree/cg-tools/issues -or to maandree@kth.se diff --git a/man/cg-remove.1 b/man/cg-remove.1 deleted file mode 100644 index bd68648..0000000 --- a/man/cg-remove.1 +++ /dev/null @@ -1,63 +0,0 @@ -.TH CG-REMOVE 1 CG-TOOLS -.SH NAME -cg-remove - Remove output filter from monitors -.SH SYNOPSIS -.B cg-remove -.RB [ \-M -.IR method ] -.RB [ \-S -.IR site ] -.RB [ \-c -.IR crtc "]... " class ... -.SH DESCRIPTION -.B cg-remove -remove the filters with the selected -.IR class es. -.SH OPTIONS -.TP -.BR \-c " "\fIcrtc\fP -Apply the filter to the CRTC with the monitor whose EDID is -.IR crtc . -By default, the filter is applied to all monitors. - -If -.I crtc -is -.RB ' ? ', -all available CRTC's are listed. -.TP -.BR \-M " "\fImethod\fP -Adjustment method name or number. Recognised names include: -.TS -tab(:); -l l. -\fBdummy\fP:Dummy method -\fBrandr\fP:X RAndR -\fBvidmode\fP:X VidMode -\fBdrm\fP:Linux DRM -\fBgdi\fP:Windows GDI -\fBquartz\fP:Quartz Core Graphics -.TE - -The adjustment methods are supported via -.BR libgamma (7). -Only methods that were enabled when -.B libgamma -was compiled will be supported. - -If -.I method -is -.RB ' ? ', -all available adjustment methods are printed. -.TP -.BR \-S " "\fIsite\fP -Select the site to which to connect. For example -.RB ' :0 ', -for local display 0 when using -.BR X . -.SH "SEE ALSO" -.BR cg-tools (7) -.SH BUGS -Please report bugs to https://github.com/maandree/cg-tools/issues -or to maandree@kth.se diff --git a/man/cg-shallow.1 b/man/cg-shallow.1 deleted file mode 100644 index 8b80263..0000000 --- a/man/cg-shallow.1 +++ /dev/null @@ -1,130 +0,0 @@ -.TH CG-SHALLOW 1 CG-TOOLS -.SH NAME -cg-shallow - Emulate low colour resolution on the monitors -.SH SYNOPSIS -.B cg-shallow -.RB [ \-M -.IR method ] -.RB [ \-S -.IR site ] -.RB [ \-c -.IR crtc "]... ["\fB\-R\fP -.IR rule ] -.RB ( \-x -| -.RB [ \-p -.IR priority ] -.RB [ \-d ] -.RI [ all -| -.I red -.I green -.IR blue ]) -.SH DESCRIPTION -.B cg-shallow -degrades the colour resolution on the monitors, to -.I all -values on each channel, or to -.IR red , -.IR green , -and -.I blue -to the red, green, and blue channels independently, respectively. -A value of 0 means the resolution will not be changes. The values -are encodingly equidistantly distributed with black and full colour -remaining as is, unless the value 1 is specified, in which case -it undefined which brightness the channel will be fixed at. -.PP -If neither -.I all -or -.IR red , -.IR green , -and -.I blue -are specified, the value 2 for all channels is selected by default. -.SH OPTIONS -.TP -.BR \-c " "\fIcrtc\fP -Apply the filter to the CRTC with the monitor whose EDID is -.IR crtc . -By default, the filter is applied to all monitors. - -If -.I crtc -is -.RB ' ? ', -all available CRTC's are listed. -.TP -.B \-d -Keep the process alive and remove the filter on death. -.TP -.BR \-M " "\fImethod\fP -Adjustment method name or number. Recognised names include: -.TS -tab(:); -l l. -\fBdummy\fP:Dummy method -\fBrandr\fP:X RAndR -\fBvidmode\fP:X VidMode -\fBdrm\fP:Linux DRM -\fBgdi\fP:Windows GDI -\fBquartz\fP:Quartz Core Graphics -.TE - -The adjustment methods are supported via -.BR libgamma (7). -Only methods that were enabled when -.B libgamma -was compiled will be supported. - -If -.I method -is -.RB ' ? ', -all available adjustment methods are printed. -.TP -.BR \-p " "\fIpriority\fP -Set the priority of the filter. Filters with higher priority -are applied before filters with lower priority. The value -must be a signed 64-bit integer (between \-9223372036854775807 -and 9223372036854775807). -.BR cg-brilliance 's -default priority is \-6917529027641081856. - -If -.I priority -is -.RB ' ? ', -the utility's default priority is printed. -.TP -.BR \-R " "\fIrule\fP -Set the rule of of the filter to -.IR rule . -This is the last part of the filter's identifier (class). -The default rule is -.BR standard . - -If -.I rule -is -.RB ' ? ' -the utility's default rule is printed. If -.I rule -is -.RB ' ?? ' -the utility's default class is printed. -.TP -.BR \-S " "\fIsite\fP -Select the site to which to connect. For example -.RB ' :0 ', -for local display 0 when using -.BR X . -.TP -.B \-x -Remove the currently applied filter. -.SH "SEE ALSO" -.BR cg-tools (7) -.SH BUGS -Please report bugs to https://github.com/maandree/cg-tools/issues -or to maandree@kth.se diff --git a/man/cg-sleepmode.1 b/man/cg-sleepmode.1 deleted file mode 100644 index c750ea0..0000000 --- a/man/cg-sleepmode.1 +++ /dev/null @@ -1,136 +0,0 @@ -.TH CG-SLEEPMODE 1 CG-TOOLS -.SH NAME -cg-sleepmode - Gradually fade out the monitors, and gradually fade in on exit -.SH SYNOPSIS -.B cg-sleepmode -.RB [ \-M -.IR method ] -.RB [ \-S -.IR site ] -.RB [ \-c -.IR crtc "]... ["\fB\-R\fP -.IR rule ] -.RB [ \-p -.IR priority ] -.RB [ \-r -.IR red-fadeout-time ] -.RB [ \-g -.IR green-fadeout-time ] -.RB [ \-b -.IR blue-fadeout-time ] -.RI [ red-luminosity -.RI [ green-luminosity -.RI [ blue-luminosity ]]] -.SH DESCRIPTION -.B cg-sleepmode -gradually fade out the red, green, and blue channels on the -monitors to the brilliances of -.IR red-luminosity , -.IR green-luminosity , -and -.IR blue-luminosity , -respectively, whose default values are 0.25, 0, and 0, -respectively. A value of 0 means complete darkness, a -value of 1 means normal brilliance. -.P -.B cg-sleepmode -does not exist after the channels have been faded out, -instead, it's waits until it's killed, and then fades the -channel's back in to normal brilliance. -.SH OPTIONS -.TP -.BR \-r " "\fIred-fadeout-time\fP -Set the fade-out time for the red channel to -\fIred-fadeout-time\fP seconds. The fade-in time is based -on this value. The default fadeout time for the red channel -is 3 seconds. -.TP -.BR \-g " "\fIgreen-fadeout-time\fP -Set the fade-out time for the green channel to -\fIgreen-fadeout-time\fP seconds. The fade-in time is based -on this value. The default fadeout time for the green channel -is 2 seconds. -.TP -.BR \-b " "\fIblue-fadeout-time\fP -Set the fade-out time for the blue channel to -\fIblue-fadeout-time\fP seconds. The fade-in time is based -on this value. The default fadeout time for the blue channel -is 1 second. -.TP -.BR \-c " "\fIcrtc\fP -Apply the filter to the CRTC with the monitor whose EDID is -.IR crtc . -By default, the filter is applied to all monitors. - -If -.I crtc -is -.RB ' ? ', -all available CRTC's are listed. -.TP -.BR \-M " "\fImethod\fP -Adjustment method name or number. Recognised names include: -.TS -tab(:); -l l. -\fBdummy\fP:Dummy method -\fBrandr\fP:X RAndR -\fBvidmode\fP:X VidMode -\fBdrm\fP:Linux DRM -\fBgdi\fP:Windows GDI -\fBquartz\fP:Quartz Core Graphics -.TE - -The adjustment methods are supported via -.BR libgamma (7). -Only methods that were enabled when -.B libgamma -was compiled will be supported. - -If -.I method -is -.RB ' ? ', -all available adjustment methods are printed. -.TP -.BR \-p " "\fIpriority\fP -Set the priority of the filter. Filters with higher priority -are applied before filters with lower priority. The value -must be a signed 64-bit integer (between \-9223372036854775807 -and 9223372036854775807). -.BR cg-sleepmode 's -default priority is 1729382256910270464. - -If -.I priority -is -.RB ' ? ', -the utility's default priority is printed. -.TP -.BR \-R " "\fIrule\fP -Set the rule of of the filter to -.IR rule . -This is the last part of the filter's identifier (class). -The default rule is -.BR standard . - -If -.I rule -is -.RB ' ? ' -the utility's default rule is printed. If -.I rule -is -.RB ' ?? ' -the utility's default class is printed. -.TP -.BR \-S " "\fIsite\fP -Select the site to which to connect. For example -.RB ' :0 ', -for local display 0 when using -.BR X . -.SH "SEE ALSO" -.BR cg-tools (7) -.SH BUGS -Please report bugs to https://github.com/maandree/cg-tools/issues -or to maandree@kth.se diff --git a/man/cg-tools.7 b/man/cg-tools.7 deleted file mode 100644 index 0a2c3af..0000000 --- a/man/cg-tools.7 +++ /dev/null @@ -1,48 +0,0 @@ -.TH CG-TOOLS 7 CG-TOOLS -.SH NAME -cg-tools - Cooperative gamma tools -.SH DESCRIPTION -.B cg-tools -is a collection of utilities that change the colour output on monitors -without overriding each other. -.SH UTILITIES -.TP -.BR cg-brilliance (1) -Set the brilliance on the monitors. -.TP -.BR cg-darkroom (1) -Invert colour on the monitors and make them dark red. -.TP -.BR cg-gamma (1) -Adjust the gamma curves on the monitors. -.TP -.BR cg-icc (1) -Apply ICC profiles to the monitors. -.TP -.BR cg-limits (1) -Adjust the brightness and contrast on the monitors. -.TP -.BR cg-linear (1) -Create a span of priorities where adjustments are made -on unencoded (linear) RGB rather than encoded RGB. -.TP -.BR cg-negative (1) -Invert colour on the monitors. -.TP -.BR cg-query (1) -List output filters applied to a monitor. -.TP -.BR cg-rainbow (1) -Adds a rainbow cycle effect to the monitors. -.TP -.BR cg-remove (1) -Remove output filter from monitors. -.TP -.BR cg-sleepmode (1) -Gradually fade out the monitors, and gradually fade in on exit. -.SH "SEE ALSO" -.BR libcoopgamma (7), -.BR coopgammad (1) -.SH BUGS -Please report bugs to https://github.com/maandree/cg-tools/issues -or to maandree@kth.se diff --git a/src/arg.h b/src/arg.h deleted file mode 100644 index 0b23c53..0000000 --- a/src/arg.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copy me if you can. - * by 20h - */ - -#ifndef ARG_H__ -#define ARG_H__ - -extern char *argv0; - -/* use main(int argc, char *argv[]) */ -#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ - argv[0] && argv[0][0] == '-'\ - && argv[0][1];\ - argc--, argv++) {\ - char argc_;\ - char **argv_;\ - int brk_;\ - if (argv[0][1] == '-' && argv[0][2] == '\0') {\ - argv++;\ - argc--;\ - break;\ - }\ - for (brk_ = 0, argv[0]++, argv_ = argv;\ - argv[0][0] && !brk_;\ - argv[0]++) {\ - if (argv_ != argv)\ - break;\ - argc_ = argv[0][0];\ - switch (argc_) - -/* Handles obsolete -NUM syntax */ -#define ARGNUM case '0':\ - case '1':\ - case '2':\ - case '3':\ - case '4':\ - case '5':\ - case '6':\ - case '7':\ - case '8':\ - case '9' - -#define ARGEND }\ - } - -#define ARGC() argc_ - -#define ARGNUMF() (brk_ = 1, estrtonum(argv[0], 0, INT_MAX)) - -#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ - ((x), abort(), (char *)0) :\ - (brk_ = 1, (argv[0][1] != '\0')?\ - (&argv[0][1]) :\ - (argc--, argv++, argv[0]))) - -#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ - (char *)0 :\ - (brk_ = 1, (argv[0][1] != '\0')?\ - (&argv[0][1]) :\ - (argc--, argv++, argv[0]))) - -#define LNGARG() &argv[0][0] - -#endif diff --git a/src/cg-base.c b/src/cg-base.c deleted file mode 100644 index 9f77937..0000000 --- a/src/cg-base.c +++ /dev/null @@ -1,927 +0,0 @@ -/** - * cg-tools -- Cooperative gamma-enabled tools - * Copyright (C) 2016, 2018 Mattias Andrée (maandree@kth.se) - * - * 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 . - */ -#include "cg-base.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - - - -/** - * The process's name - */ -const char* argv0 = NULL; - -/** - * The libcoopgamma context - */ -libcoopgamma_context_t cg; - -/** - * The names of the selected CRTC:s - */ -char** crtcs = NULL; - -/** - * Gamma ramp updates for each CRTC - */ -filter_update_t* crtc_updates = NULL; - -/** - * CRTC and monitor information about - * each selected CRTC and connect monitor - */ -libcoopgamma_crtc_info_t* crtc_info = NULL; - -/** - * The number of selected CRTC:s - */ -size_t crtcs_n = 0; - -/** - * The number of filters - */ -size_t filters_n = 0; - - -/** - * Contexts for asynchronous ramp updates - */ -static libcoopgamma_async_context_t* asyncs = NULL; - -/** - * The number of pending receives - */ -static size_t pending_recvs = 0; - -/** - * Whether message must be flushed - */ -static int flush_pending = 0; - - - -/** - * Data used to sort CRTC:s - */ -struct crtc_sort_data -{ - /** - * The gamma ramp type - */ - libcoopgamma_depth_t depth; - - /** - * Should be 0 - */ - int __padding; - - /** - * The size of the red gamma ramp - */ - size_t red_size; - - /** - * The size of the green gamma ramp - */ - size_t green_size; - - /** - * The size of the blue gamma ramp - */ - size_t blue_size; - - /** - * The index of the CRTC - */ - size_t index; -}; - - - -/** - * Compare two strings - * - * @param a Return -1 if this string is `NULL` or less than `b` - * @param b Return +1 if this string is less than `a` - * @return See `a` and `b`, 0 is returned if `a` and `b` are equal - */ -static int nulstrcmp(const char *a, const char *b) -{ - return (a == NULL) ? -1 : strcmp(a, b); -} - - -/** - * Compare two instances of `crtc_sort_data` - * - * @param a_ Return -1 if this one is lower - * @param b_ Return +1 if this one is higher - * @return See `a_` and `b_`, only -1 or +1 can be returned - */ -static int crtc_sort_data_cmp(const void* a_, const void* b_) -{ - const struct crtc_sort_data* a = a_; - const struct crtc_sort_data* b = b_; - int cmp = memcmp(a, b, sizeof(*a) - sizeof(a->index)); - return cmp ? cmp : a->index < b->index ? -1 : +1; -} - - -/** - * Make elements in `crtc_updates` slaves where appropriate - * - * @return Zero on success, -1 on error - */ -int make_slaves(void) -{ - struct crtc_sort_data* data; - size_t i, j, n = 0, master = 0, master_i; - - data = alloca(filters_n * sizeof(*data)); - memset(data, 0, filters_n * sizeof(*data)); - for (i = 0; i < filters_n; i++) - { - if (!(crtc_info[crtc_updates[i].crtc].supported)) - continue; - - data[n].depth = crtc_updates[i].filter.depth; - data[n].red_size = crtc_updates[i].filter.ramps.u8.red_size; - data[n].green_size = crtc_updates[i].filter.ramps.u8.green_size; - data[n].blue_size = crtc_updates[i].filter.ramps.u8.blue_size; - data[n].index = i; - n++; - } - - qsort(data, n, sizeof(*data), crtc_sort_data_cmp); - if (n == 0) - return 0; - - master_i = data[0].index; - for (i = 1; i < n; i++) - if (memcmp(data + i, data + master, sizeof(*data) - sizeof(data->index))) - { - if (master + 1 < i) - { - crtc_updates[master_i].slaves = calloc(i - master, sizeof(size_t)); - if (crtc_updates[master_i].slaves == NULL) - return -1; - for (j = 1; master + j < i; j++) - crtc_updates[master_i].slaves[j - 1] = data[master + j].index; - } - master = i; - master_i = data[master].index; - } - else - { - libcoopgamma_ramps_destroy(&(crtc_updates[data[i].index].filter.ramps.u8)); - crtc_updates[data[i].index].master = 0; - crtc_updates[data[i].index].filter.ramps.u8 = crtc_updates[master_i].filter.ramps.u8; - } - - if (master + 1 < i) - { - crtc_updates[master_i].slaves = calloc(i - master, sizeof(size_t)); - if (crtc_updates[master_i].slaves == NULL) - return -1; - for (j = 1; master + j < i; j++) - crtc_updates[master_i].slaves[j - 1] = data[master + j].index; - } - - return 0; -} - - -/** - * Update a filter and synchronise calls - * - * @param index The index of the CRTC - * @param timeout The number of milliseconds a call to `poll` may block, - * -1 if it may block forever - * @return 1: Success, no pending synchronisations - * 0: Success, with still pending synchronisations - * -1: Error, `errno` set - * -2: Error, `cg.error` set - * - * @throws EINTR Call to `poll` was interrupted by a signal - * @throws EAGAIN Call to `poll` timed out - */ -int update_filter(size_t index, int timeout) -{ - filter_update_t* filter = crtc_updates + index; - - if (!(filter->synced) || filter->failed) - abort(); - - pending_recvs += 1; - - if (libcoopgamma_set_gamma_send(&(filter->filter), &cg, asyncs + index) < 0) - switch (errno) - { - case EINTR: - case EAGAIN: -#if EAGAIN != EWOULDBLOCK - case EWOULDBLOCK: -#endif - flush_pending = 1; - break; - default: - return -1; - } - - filter->synced = 0; - return synchronise(timeout); -} - - -/** - * Synchronised calls - * - * @param timeout The number of milliseconds a call to `poll` may block, - * -1 if it may block forever - * @return 1: Success, no pending synchronisations - * 0: Success, with still pending synchronisations - * -1: Error, `errno` set - * -2: Error, `cg.error` set - * - * @throws EINTR Call to `poll` was interrupted by a signal - * @throws EAGAIN Call to `poll` timed out - */ -int synchronise(int timeout) -{ - struct pollfd pollfd; - size_t selected; - - pollfd.fd = cg.fd; - pollfd.events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI; - if (flush_pending > 0) - pollfd.events |= POLLOUT; - - pollfd.revents = 0; - if (poll(&pollfd, (nfds_t)1, timeout) < 0) - return -1; - - if (pollfd.revents & (POLLOUT | POLLERR | POLLHUP | POLLNVAL)) - { - if (libcoopgamma_flush(&cg) < 0) - goto sync; - flush_pending = 0; - } - - if ((timeout < 0) && (pending_recvs > 0)) - if (!(pollfd.revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI))) - { - pollfd.revents = 0; - if (poll(&pollfd, (nfds_t)1, -1) < 0) - return -1; - } - - sync: - if (pollfd.revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI | POLLERR | POLLHUP | POLLNVAL)) - for (;;) - { - if (libcoopgamma_synchronise(&cg, asyncs, filters_n, &selected) < 0) - { - if (errno == 0) - continue; - else - goto fail; - } - if (crtc_updates[selected].synced) - continue; - crtc_updates[selected].synced = 1; - pending_recvs -= 1; - if (libcoopgamma_set_gamma_recv(&cg, asyncs + selected) < 0) - { - if (cg.error.server_side) - { - crtc_updates[selected].error = cg.error; - crtc_updates[selected].failed = 1; - memset(&(cg.error), 0, sizeof(cg.error)); - } - else - goto cg_fail; - } - } - - return pending_recvs == 0; - cg_fail: - return -2; - fail: - switch (errno) - { - case EINTR: - case EAGAIN: -#if EAGAIN != EWOULDBLOCK - case EWOULDBLOCK: -#endif - return pending_recvs == 0; - default: - return -1; - } -} - - -/** - * Initialise the process, specifically - * reset the signal mask and signal handlers - * - * @return Zero on success, -1 on error - */ -static int initialise_proc(void) -{ - sigset_t sigmask; - int sig; - - for (sig = 1; sig < _NSIG; sig++) - if (signal(sig, SIG_DFL) == SIG_ERR) - if (sig == SIGCHLD) - return -1; - - if (sigemptyset(&sigmask) < 0) - return -1; - if (sigprocmask(SIG_SETMASK, &sigmask, NULL) < 0) - return -1; - - return 0; -} - - -/** - * Print, to stdout, a list of all - * recognised adjustment methods - * - * @return Zero on success, -1 on error - */ -static int list_methods(void) -{ - char** list; - size_t i; - - list = libcoopgamma_get_methods(); - if (list == NULL) - return -1; - for (i = 0; list[i]; i++) - printf("%s\n", list[i]); - free(list); - if (fflush(stdout) < 0) - return -1; - - return 0; -} - - -/** - * Print, to stdout, a list of all CRTC:s - * - * A connection to the coopgamma server - * must have been made - * - * @return Zero on success, -1 on error, -2 - * on libcoopgamma error - */ -static int list_crtcs(void) -{ - char** list; - size_t i; - - list = libcoopgamma_get_crtcs_sync(&cg); - if (list == NULL) - return -2; - for (i = 0; list[i]; i++) - printf("%s\n", list[i]); - free(list); - if (fflush(stdout) < 0) - return -1; - - return 0; -} - - -/** - * Fill the list of CRTC information - * - * @return Zero on success, -1 on error, -2 - * on libcoopgamma error - */ -static int get_crtc_info(void) -{ - size_t i, unsynced = 0, selected; - char* synced; - int need_flush = 0; - struct pollfd pollfd; - - synced = alloca(crtcs_n * sizeof(*synced)); - memset(synced, 0, crtcs_n * sizeof(*synced)); - - i = 0; - pollfd.fd = cg.fd; - pollfd.events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI; - - while ((unsynced > 0) || (i < crtcs_n)) - { - wait: - if (i < crtcs_n) - pollfd.events |= POLLOUT; - else - pollfd.events &= ~POLLOUT; - - pollfd.revents = 0; - if (poll(&pollfd, (nfds_t)1, -1) < 0) - goto fail; - - if (pollfd.revents & (POLLOUT | POLLERR | POLLHUP | POLLNVAL)) - { - if (need_flush && (libcoopgamma_flush(&cg) < 0)) - goto send_fail; - need_flush = 0; - for (; i < crtcs_n; i++) - if (unsynced++, libcoopgamma_get_gamma_info_send(crtcs[i], &cg, asyncs + i) < 0) - goto send_fail; - goto send_done; - send_fail: - switch (errno) - { - case EINTR: - case EAGAIN: -#if EAGAIN != EWOULDBLOCK - case EWOULDBLOCK: -#endif - i++; - need_flush = 1; - break; - default: - goto fail; - } - } - send_done: - - if ((unsynced == 0) && (i == crtcs_n)) - break; - - if (pollfd.revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) - while (unsynced > 0) - switch (libcoopgamma_synchronise(&cg, asyncs, i, &selected)) - { - case 0: - if (synced[selected]) - { - libcoopgamma_skip_message(&cg); - break; - } - synced[selected] = 1; - unsynced -= 1; - if (libcoopgamma_get_gamma_info_recv(crtc_info + selected, &cg, asyncs + selected) < 0) - goto cg_fail; - break; - case -1: - switch (errno) - { - case 0: - break; - case EINTR: - case EAGAIN: -#if EAGAIN != EWOULDBLOCK - case EWOULDBLOCK: -#endif - goto wait; - default: - goto fail; - } - break; - } - } - - return 0; - fail: - return -1; - cg_fail: - return -2; -} - - -/** - * -M METHOD - * Select adjustment method. If METHOD is "?", - * available methods will be printed to stdout. - * - * -S SITE - * Select site (display server instance). - * - * -c CRTC - * Select CRT controller. If CRTC is "?", CRTC:s - * will be printed to stdout. - * - * This option can be used multiple times. If it - * is not used at all, all CRTC:s will be selected. - * - * -p PRIORITY - * Select the priority for the filter, this should - * be a signed two's-complement integer. If - * PRIORITY is "?", the default priority for the - * program is printed to stdout. - * - * -R RULE - * The rule of the filter, that is, the last part - * of the class which is its identifier. If RULE - * is "?" the default rule is printed to stdout, - * if RULE is "??" the default class is printed - * to stdout. - * - * @param argc The number of command line arguments - * @param argv The command line arguments - * @return 0 on success, 1 on error - */ -int main(int argc, char* argv[]) -{ - int stage = 0; - int dealloc_crtcs = 0; - int rc = 0; - char* method = NULL; - char* site = NULL; - size_t crtc_i = 0; - int64_t priority = default_priority; - char* prio = NULL; - char* rule = NULL; - char* class = default_class; - char** classes = NULL; - size_t classes_n = 0; - int explicit_crtcs = 0; - int have_crtc_q = 0; - size_t i, filter_i; - - argv0 = *argv++, argc--; - - if (initialise_proc() < 0) - goto fail; - - crtcs = alloca(argc * sizeof(*crtcs)); - - for (; *argv; argv++, argc--) - { - char* args = *argv; - char opt[3]; - if (!strcmp(args, "--")) - { - argv++, argc--; - break; - } - opt[0] = *args++; - opt[2] = '\0'; - if ((*opt != '-') && (*opt != '+')) - break; - while (*args) - { - char* arg; - int at_end; - opt[1] = *args++; - arg = args; - if ((at_end = !*arg)) - arg = argv[1]; - if (!strcmp(opt, "-M")) - { - if ((method != NULL) || ((method = arg) == NULL)) - usage(); - } - else if (!strcmp(opt, "-S")) - { - if ((site != NULL) || ((site = arg) == NULL)) - usage(); - } - else if (!strcmp(opt, "-c")) - { - if (arg == NULL) - usage(); - crtcs[crtc_i++] = arg; - explicit_crtcs = 1; - if (!have_crtc_q && !strcmp(arg, "?")) - have_crtc_q = 1; - } - else if (!strcmp(opt, "-p")) - { - if ((prio != NULL) || ((prio = arg) == NULL)) - usage(); - } - else if (!strcmp(opt, "-R")) - { - if ((rule != NULL) || ((rule = arg) == NULL)) - usage(); - } - else - switch (handle_opt(opt, arg)) - { - case 0: - goto next_opt; - case 1: - break; - default: - goto fail; - } - argv += at_end; - argc -= at_end; - break; - next_opt:; - } - } - - crtcs_n = crtc_i; - crtcs[crtc_i] = NULL; - if (!have_crtc_q && nulstrcmp(method, "?") && - nulstrcmp(rule, "?") && nulstrcmp(rule, "??") && - ((default_priority == NO_DEFAULT_PRIORITY) || nulstrcmp(prio, "?"))) - if (handle_args(argc, argv, prio) < 0) - goto fail; - - if (default_priority != NO_DEFAULT_PRIORITY) - { - if (!nulstrcmp(prio, "?")) - { - printf("%" PRIi64 "\n", priority); - return 0; - } - else if (prio != NULL) - { - char *end; - errno = 0; - priority = (int64_t)strtoll(prio, &end, 10); - if (errno || *end || !*prio) - usage(); - } - } - - if (!nulstrcmp(rule, "??")) - { - size_t i; - if (*class_suffixes == NULL) - printf("%s\n", class); - else - for (i = 0; class_suffixes[i] != NULL; i++) - printf("%s%s\n", class, class_suffixes[i]); - return 0; - } - else if (!nulstrcmp(rule, "?")) - { - printf("%s\n", strstr(strstr(class, "::") + 2, "::") + 2); - return 0; - } - else if (rule != NULL) - { - char* p = strstr(strstr(class, "::") + 2, "::") + 2; - size_t n = (size_t)(p - class); - class = alloca(strlen(rule) + n + (size_t)1); - memcpy(class, default_class, n); - strcpy(class + n, rule); - if (strchr(class, '\n')) - { - fprintf(stderr, "%s: LF character is not allowed in the filter's class\n", argv0); - goto custom_fail; - } - } - - if (!nulstrcmp(method, "?")) - { - if (list_methods() < 0) - goto fail; - return 0; - } - - if (libcoopgamma_context_initialise(&cg) < 0) - goto fail; - stage++; - if (libcoopgamma_connect(method, site, &cg) < 0) - { - fprintf(stderr, "%s: server failed to initialise\n", argv0); - goto custom_fail; - } - stage++; - - if (have_crtc_q) - switch (list_crtcs()) - { - case 0: - goto done; - case -1: - goto fail; - default: - goto cg_fail; - } - - if (crtcs_n == 0) - { - crtcs = libcoopgamma_get_crtcs_sync(&cg); - if (crtcs == NULL) - goto cg_fail; - dealloc_crtcs = 1; - for (; crtcs[crtcs_n] != NULL; crtcs_n++); - } - - if (crtcs_n == 0) - { - fprintf(stderr, "%s: no CRTC:s are available\n", argv0); - goto custom_fail; - } - - if (*class_suffixes == NULL) - { - classes = &class; - classes_n = 1; - } - else - { - size_t len = strlen(class); - while (class_suffixes[classes_n]) - classes_n++; - classes = alloca(classes_n * sizeof(*classes)); - for (i = 0; i < classes_n; i++) - { - classes[i] = alloca(len + strlen(class_suffixes[i]) + sizeof(":")); - stpcpy(stpcpy(stpcpy(classes[i], class), ":"), class_suffixes[i]); - } - } - filters_n = classes_n * crtcs_n; - - crtc_info = alloca(crtcs_n * sizeof(*crtc_info)); - memset(crtc_info, 0, crtcs_n * sizeof(*crtc_info)); - for (crtc_i = 0; crtc_i < crtcs_n; crtc_i++) - if (libcoopgamma_crtc_info_initialise(crtc_info + crtc_i) < 0) - goto cg_fail; - - if (libcoopgamma_set_nonblocking(&cg, 1) < 0) - goto fail; - - asyncs = alloca(filters_n * sizeof(*asyncs)); - memset(asyncs, 0, filters_n * sizeof(*asyncs)); - for (filter_i = 0; filter_i < filters_n; filter_i++) - if (libcoopgamma_async_context_initialise(asyncs + filter_i) < 0) - goto fail; - - switch (get_crtc_info()) - { - case 0: - break; - case -1: - goto fail; - case -2: - goto cg_fail; - } - - for (crtc_i = 0; crtc_i < crtcs_n; crtc_i++) - { - if (explicit_crtcs && !(crtc_info[crtc_i].supported)) - fprintf(stderr, "%s: warning: gamma adjustments not supported on CRTC: %s\n", - argv0, crtcs[crtc_i]); - if (crtc_info[crtc_i].cooperative == 0) - fprintf(stderr, "%s: warning: cooperative gamma server not running for CRTC: %s\n", - argv0, crtcs[crtc_i]); - } - - crtc_updates = alloca(filters_n * sizeof(*crtc_updates)); - memset(crtc_updates, 0, filters_n * sizeof(*crtc_updates)); - for (filter_i = i = 0; i < classes_n; i++) - for (crtc_i = 0; crtc_i < crtcs_n; crtc_i++, filter_i++) - { - if (libcoopgamma_filter_initialise(&(crtc_updates[filter_i].filter)) < 0) - goto fail; - if (libcoopgamma_error_initialise(&(crtc_updates[filter_i].error)) < 0) - goto fail; - crtc_updates[filter_i].crtc = crtc_i; - crtc_updates[filter_i].synced = 1; - crtc_updates[filter_i].failed = 0; - crtc_updates[filter_i].master = 1; - crtc_updates[filter_i].slaves = NULL; - crtc_updates[filter_i].filter.crtc = crtcs[crtc_i]; - crtc_updates[filter_i].filter.class = classes[i]; - crtc_updates[filter_i].filter.priority = priority; - crtc_updates[filter_i].filter.depth = crtc_info[crtc_i].depth; - crtc_updates[filter_i].filter.ramps.u8.red_size = crtc_info[crtc_i].red_size; - crtc_updates[filter_i].filter.ramps.u8.green_size = crtc_info[crtc_i].green_size; - crtc_updates[filter_i].filter.ramps.u8.blue_size = crtc_info[crtc_i].blue_size; - switch (crtc_updates[filter_i].filter.depth) - { -#define X(CONST, MEMBER, MAX, TYPE)\ - case CONST:\ - libcoopgamma_ramps_initialise(&(crtc_updates[filter_i].filter.ramps.MEMBER));\ - libclut_start_over(&(crtc_updates[filter_i].filter.ramps.MEMBER), MAX, TYPE, 1, 1, 1);\ - break; -LIST_DEPTHS -#undef X - default: - fprintf(stderr, "%s: internal error: gamma ramp type is unrecognised: %i\n", - argv0, crtc_updates[filter_i].filter.depth); - goto custom_fail; - } - } - - switch (start()) - { - case 0: - break; - case -1: - goto fail; - case -2: - goto cg_fail; - case -3: - goto custom_fail; - } - - for (filter_i = 0; filter_i < filters_n; filter_i++) - if (crtc_updates[filter_i].failed) - { - const char* side = cg.error.server_side ? "server" : "client"; - const char* crtc = crtc_updates[filter_i].filter.crtc; - if (cg.error.custom) - { - if ((cg.error.number != 0) && (cg.error.description != NULL)) - fprintf(stderr, "%s: %s-side error number %" PRIu64 " for CRTC %s: %s\n", - argv0, side, cg.error.number, crtc, cg.error.description); - else if (cg.error.number != 0) - fprintf(stderr, "%s: %s-side error number %" PRIu64 " for CRTC %s\n", - argv0, side, cg.error.number, crtc); - else if (cg.error.description != NULL) - fprintf(stderr, "%s: %s-side error for CRTC %s: %s\n", argv0, side, crtc, cg.error.description); - } - else if (cg.error.description != NULL) - fprintf(stderr, "%s: %s-side error for CRTC %s: %s\n", argv0, side, crtc, cg.error.description); - else - fprintf(stderr, "%s: %s-side error for CRTC %s: %s\n", argv0, side, crtc, strerror(cg.error.number)); - } - - done: - if (dealloc_crtcs) - free(crtcs); - if (crtc_info != NULL) - for (crtc_i = 0; crtc_i < crtcs_n; crtc_i++) - libcoopgamma_crtc_info_destroy(crtc_info + crtc_i); - if (asyncs != NULL) - for (filter_i = 0; filter_i < filters_n; filter_i++) - libcoopgamma_async_context_destroy(asyncs + filter_i); - if (stage >= 1) - libcoopgamma_context_destroy(&cg, stage >= 2); - if (crtc_updates != NULL) - for (filter_i = 0; filter_i < filters_n; filter_i++) - { - if (crtc_updates[filter_i].master == 0) - memset(&(crtc_updates[filter_i].filter.ramps.u8), 0, sizeof(crtc_updates[filter_i].filter.ramps.u8)); - crtc_updates[filter_i].filter.crtc = NULL; - crtc_updates[filter_i].filter.class = NULL; - libcoopgamma_filter_destroy(&(crtc_updates[filter_i].filter)); - libcoopgamma_error_destroy(&(crtc_updates[filter_i].error)); - free(crtc_updates[filter_i].slaves); - } - return rc; - - custom_fail: - rc = 1; - goto done; - - fail: - rc = 1; - if (errno) - perror(argv0); - goto done; - - cg_fail: - rc = 1; - { - const char* side = cg.error.server_side ? "server" : "client"; - if (cg.error.custom) - { - if ((cg.error.number != 0) && (cg.error.description != NULL)) - fprintf(stderr, "%s: %s-side error number %" PRIu64 ": %s\n", - argv0, side, cg.error.number, cg.error.description); - else if (cg.error.number != 0) - fprintf(stderr, "%s: %s-side error number %" PRIu64 "\n", argv0, side, cg.error.number); - else if (cg.error.description != NULL) - fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, cg.error.description); - } - else if (cg.error.description != NULL) - fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, cg.error.description); - else - fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, strerror(cg.error.number)); - } - goto done; -} - diff --git a/src/cg-base.h b/src/cg-base.h deleted file mode 100644 index cd885e7..0000000 --- a/src/cg-base.h +++ /dev/null @@ -1,254 +0,0 @@ -/** - * cg-tools -- Cooperative gamma-enabled tools - * Copyright (C) 2016 Mattias Andrée (maandree@kth.se) - * - * 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 . - */ -#include - -#include - - - -/** - * Value of `default_priority` that indicates - * that there is no default priority - */ -#define NO_DEFAULT_PRIORITY INT64_MAX - - - -/** - * X-macro that list all gamma ramp types - * - * X will be expanded with 4 arguments: - * 1) The libcoopgamma constant that identifies the type - * 2) The member in `union libcoopgamma_ramps` that - * corresponds to the type - * 3) The max value for the ramp stops - * 4) The type of the ramp stops - */ -#define LIST_DEPTHS\ - X(LIBCOOPGAMMA_UINT8, u8, UINT8_MAX, uint8_t)\ - X(LIBCOOPGAMMA_UINT16, u16, UINT16_MAX, uint16_t)\ - X(LIBCOOPGAMMA_UINT32, u32, UINT32_MAX, uint32_t)\ - X(LIBCOOPGAMMA_UINT64, u64, UINT64_MAX, uint64_t)\ - X(LIBCOOPGAMMA_FLOAT, f, ((float)1), float)\ - X(LIBCOOPGAMMA_DOUBLE, d, ((double)1), double) - - - -/** - * Information (except asynchronous call context) - * required to update the gamma ramps on a CRTC. - */ -typedef struct filter_update -{ - /** - * The filter to update - * - * `.filter.crtc`, `.filter.class`, and - * `.filter.priority` (unless `default_priority` - * is `NO_DEFAULT_PRIORITY`), `.filter.depth` - * are preconfigured, and `.filter.ramps` - * is preinitialised and preset to an - * identity ramp - */ - libcoopgamma_filter_t filter; - - /** - * The index of the CRTC - */ - size_t crtc; - - /** - * Has the update been synchronised? - */ - int synced; - - /** - * Did the update fail? - */ - int failed; - - /** - * Error description if `.failed` is true - */ - libcoopgamma_error_t error; - - /** - * If zero, the ramps in `.filter` shall - * neither be modified nor freed - */ - int master; - - /** - * 0-terminated list of elements in - * `.crtc_updates` which shares gamma - * ramps with this instance - * - * This will only be set if `.master` - * is true - */ - size_t* slaves; - -} filter_update_t; - - - -/** - * The process's name - */ -extern const char* argv0; - -/** - * The libcoopgamma context - */ -extern libcoopgamma_context_t cg; - -/** - * The names of the selected CRTC:s - */ -extern char** crtcs; - -/** - * Gamma ramp updates for each CRTC - */ -extern filter_update_t* crtc_updates; - -/** - * CRTC and monitor information about - * each selected CRTC and connect monitor - */ -extern libcoopgamma_crtc_info_t* crtc_info; - -/** - * The number of selected CRTC:s - */ -extern size_t crtcs_n; - -/** - * The number of filters - */ -extern size_t filters_n; - - - -/** - * The default filter priority for the program - */ -extern const int64_t default_priority; - -/** - * The default class for the program - */ -extern char default_class[]; - -/** - * Class suffixes - */ -extern const char* const* class_suffixes; - - - -/** - * Make elements in `crtc_updates` slaves where appropriate - * - * @return Zero on success, -1 on error - */ -int make_slaves(void); - -/** - * Update a filter and synchronise calls - * - * @param index The index of the CRTC - * @param timeout The number of milliseconds a call to `poll` may block, - * -1 if it may block forever - * @return 1: Success, no pending synchronisations - * 0: Success, with still pending synchronisations - * -1: Error, `errno` set - * -2: Error, `cg.error` set - * - * @throws EINTR Call to `poll` was interrupted by a signal - * @throws EAGAIN Call to `poll` timed out - */ -int update_filter(size_t index, int timeout); - -/** - * Synchronised calls - * - * @param timeout The number of milliseconds a call to `poll` may block, - * -1 if it may block forever - * @return 1: Success, no pending synchronisations - * 0: Success, with still pending synchronisations - * -1: Error, `errno` set - * -2: Error, `cg.error` set - * - * @throws EINTR Call to `poll` was interrupted by a signal - * @throws EAGAIN Call to `poll` timed out - */ -int synchronise(int timeout); - - -/** - * Print usage information and exit - */ -#if defined(__GNUC__) -__attribute__((__noreturn__)) -#endif -extern void usage(void); - -/** - * Handle a command line option - * - * @param opt The option, it is a NUL-terminate two-character - * string starting with either '-' or '+', if the - * argument is not recognised, call `usage`. This - * string will not be "-M", "-S", "-c", "-p", or "-R". - * @param arg The argument associated with `opt`, - * `NULL` there is no next argument, if this - * parameter is `NULL` but needed, call `usage` - * @return 0 if `arg` was not used, - * 1 if `arg` was used, - * -1 on error - */ -#if defined(__GNUC__) -__attribute__((__nonnull__(1))) -#endif -extern int handle_opt(char* opt, char* arg); - -/** - * This function is called after the last - * call to `handle_opt` - * - * @param argc The number of unparsed arguments - * @param argv `NULL` terminated list of unparsed arguments - * @param prio The argument associated with the "-p" option - * @return Zero on success, -1 on error - */ -#if defined(__GNUC__) -__attribute__((__nonnull__(2))) -#endif -extern int handle_args(int argc, char* argv[], char* prio); - -/** - * The main function for the program-specific code - * - * @return 0: Success - * -1: Error, `errno` set - * -2: Error, `cg.error` set - * -3: Error, message already printed - */ -extern int start(void); - diff --git a/src/cg-brilliance.c b/src/cg-brilliance.c deleted file mode 100644 index 3541d03..0000000 --- a/src/cg-brilliance.c +++ /dev/null @@ -1,313 +0,0 @@ -/** - * cg-tools -- Cooperative gamma-enabled tools - * Copyright (C) 2016, 2018 Mattias Andrée (maandree@kth.se) - * - * 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 . - */ -#include "cg-base.h" - -#include - -#include -#include -#include -#include -#include - - - -/** - * The default filter priority for the program - */ -const int64_t default_priority = (int64_t)1 << 61; - -/** - * The default class for the program - */ -char default_class[] = PKGNAME "::cg-brilliance::standard"; - -/** - * Class suffixes - */ -const char* const* class_suffixes = (const char* const[]){NULL}; - - - -/** - * -d: keep process alive and remove filter on death - */ -static int dflag = 0; - -/** - * -x: remove filter rather than adding it - */ -static int xflag = 0; - -/** - * The brilliance of the red channel - */ -static double rvalue = 0; - -/** - * The brilliance of the green channel - */ -static double gvalue = 0; - -/** - * The brilliance of the blue channel - */ -static double bvalue = 0; - - - -/** - * Print usage information and exit - */ -void usage(void) -{ - fprintf(stderr, - "Usage: %s [-M method] [-S site] [-c crtc]... [-R rule] " - "(-x | [-p priority] [-d] (all | red green blue))\n", - argv0); - exit(1); -} - - -/** - * Handle a command line option - * - * @param opt The option, it is a NUL-terminate two-character - * string starting with either '-' or '+', if the - * argument is not recognised, call `usage`. This - * string will not be "-M", "-S", "-c", "-p", or "-R". - * @param arg The argument associated with `opt`, - * `NULL` there is no next argument, if this - * parameter is `NULL` but needed, call `usage` - * @return 0 if `arg` was not used, - * 1 if `arg` was used, - * -1 on error - */ -int handle_opt(char* opt, char* arg) -{ - if (opt[0] == '-') - switch (opt[1]) - { - case 'd': - if (dflag || xflag) - usage(); - dflag = 1; - break; - case 'x': - if (xflag || dflag) - usage(); - xflag = 1; - break; - default: - usage(); - } - else - usage(); - return 0; - (void) arg; -} - - -/** - * Parse a non-negative double encoded as a string - * - * @param out Output parameter for the value - * @param str The string - * @return Zero on success, -1 if the string is invalid - */ -static int parse_double(double* restrict out, const char* restrict str) -{ - char* end; - errno = 0; - *out = strtod(str, &end); - if (errno || (*out < 0) || isinf(*out) || isnan(*out) || *end) - return -1; - if (!*str || !strchr("0123456789.", *str)) - return -1; - return 0; -} - - -/** - * This function is called after the last - * call to `handle_opt` - * - * @param argc The number of unparsed arguments - * @param argv `NULL` terminated list of unparsed arguments - * @param prio The argument associated with the "-p" option - * @return Zero on success, -1 on error - */ -int handle_args(int argc, char* argv[], char* prio) -{ - char* red = NULL; - char* green = NULL; - char* blue = NULL; - int q = xflag + dflag; - if ((q > 1) || (xflag && (prio != NULL || argc))) - usage(); - if (argc == 1) - red = green = blue = argv[0]; - else if (argc == 3) - { - red = argv[0]; - green = argv[1]; - blue = argv[2]; - } - else if (argc || !xflag) - usage(); - if (argc) - { - if (parse_double(&rvalue, red) < 0) - usage(); - if (parse_double(&gvalue, blue) < 0) - usage(); - if (parse_double(&bvalue, green) < 0) - usage(); - } - return 0; -} - - -/** - * Fill a filter - * - * @param filter The filter to fill - */ -static void fill_filter(libcoopgamma_filter_t* restrict filter) -{ - size_t i; - switch (filter->depth) - { -#define X(CONST, MAX, TYPE, MEMBER) \ - case CONST: \ - for (i = 0; i < filter->ramps.MEMBER.red_size; i++) \ - { \ - double val = (double)(filter->ramps.MEMBER.red[i]); \ - val *= rvalue; \ - if (val < 0) filter->ramps.MEMBER.red[i] = 0; \ - else if (val > (double)(MAX)) filter->ramps.MEMBER.red[i] = MAX; \ - else filter->ramps.MEMBER.red[i] = (TYPE)val; \ - } \ - for (i = 0; i < filter->ramps.MEMBER.green_size; i++) \ - { \ - double val = (double)(filter->ramps.MEMBER.green[i]); \ - val *= gvalue; \ - if (val < 0) filter->ramps.MEMBER.green[i] = 0; \ - else if (val > (double)(MAX)) filter->ramps.MEMBER.green[i] = MAX; \ - else filter->ramps.MEMBER.green[i] = (TYPE)val; \ - } \ - for (i = 0; i < filter->ramps.MEMBER.blue_size; i++) \ - { \ - double val = (double)(filter->ramps.MEMBER.blue[i]); \ - val *= bvalue; \ - if (val < 0) filter->ramps.MEMBER.blue[i] = 0; \ - else if (val > (double)(MAX)) filter->ramps.MEMBER.blue[i] = MAX; \ - else filter->ramps.MEMBER.blue[i] = (TYPE)val; \ - } \ - break - X(LIBCOOPGAMMA_UINT8, UINT8_MAX, uint8_t, u8); - X(LIBCOOPGAMMA_UINT16, UINT16_MAX, uint16_t, u16); - X(LIBCOOPGAMMA_UINT32, UINT32_MAX, uint32_t, u32); - X(LIBCOOPGAMMA_UINT64, UINT64_MAX, uint64_t, u64); -#undef X - case LIBCOOPGAMMA_FLOAT: - libclut_rgb_brightness(&(filter->ramps.f), (float)1, float, rvalue, gvalue, bvalue); - libclut_clip(&(filter->ramps.f), (float)1, float, 1, 1, 1); - break; - case LIBCOOPGAMMA_DOUBLE: - libclut_rgb_brightness(&(filter->ramps.d), (double)1, double, rvalue, gvalue, bvalue); - libclut_clip(&(filter->ramps.d), (double)1, double, 1, 1, 1); - break; - default: - abort(); - } -} - - -/** - * The main function for the program-specific code - * - * @return 0: Success - * -1: Error, `errno` set - * -2: Error, `cg.error` set - * -3: Error, message already printed - */ -int start(void) -{ - int r; - size_t i, j; - - if (xflag) - for (i = 0; i < filters_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_REMOVE; - else if (dflag) - for (i = 0; i < filters_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_DEATH; - else - for (i = 0; i < filters_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_REMOVAL; - - if (!xflag) - if ((r = make_slaves()) < 0) - return r; - - for (i = 0, r = 1; i < filters_n; i++) - { - if (!(crtc_updates[i].master) || !(crtc_info[crtc_updates[i].crtc].supported)) - continue; - if (!xflag) - fill_filter(&(crtc_updates[i].filter)); - r = update_filter(i, 0); - if ((r == -2) || ((r == -1) && (errno != EAGAIN))) - return r; - if (crtc_updates[i].slaves != NULL) - for (j = 0; crtc_updates[i].slaves[j] != 0; j++) - { - r = update_filter(crtc_updates[i].slaves[j], 0); - if ((r == -2) || ((r == -1) && (errno != EAGAIN))) - return r; - } - } - - while (r != 1) - if ((r = synchronise(-1)) < 0) - return r; - - if (!dflag) - return 0; - - if (libcoopgamma_set_nonblocking(&cg, 0) < 0) - return -1; - for (;;) - if (libcoopgamma_synchronise(&cg, NULL, 0, &j) < 0) - switch (errno) - { - case 0: - break; - case ENOTRECOVERABLE: - goto enotrecoverable; - default: - return -1; - } - - enotrecoverable: - for (;;) - if (pause() < 0) - return -1; -} - diff --git a/src/cg-darkroom.c b/src/cg-darkroom.c deleted file mode 100644 index 9990aa3..0000000 --- a/src/cg-darkroom.c +++ /dev/null @@ -1,316 +0,0 @@ -/** - * cg-tools -- Cooperative gamma-enabled tools - * Copyright (C) 2016, 2018 Mattias Andrée (maandree@kth.se) - * - * 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 . - */ -#include "cg-base.h" - -#include - -#include -#include -#include -#include -#include - - - -/** - * The default filter priority for the program - */ -const int64_t default_priority = (int64_t)3 << 61; - -/** - * The default class for the program - */ -char default_class[] = PKGNAME "::cg-darkroom::standard"; - -/** - * Class suffixes - */ -const char* const* class_suffixes = (const char* const[]){NULL}; - - - -/** - * -d: keep process alive and remove filter on death - */ -static int dflag = 0; - -/** - * -x: remove filter rather than adding it - */ -static int xflag = 0; - -/** - * The brilliance of the red channel - */ -static double value = 0.25; - - - -/** - * Print usage information and exit - */ -void usage(void) -{ - fprintf(stderr, - "Usage: %s [-M method] [-S site] [-c crtc]... [-R rule] " - "(-x | [-p priority] [-d] [brightness])\n", - argv0); - exit(1); -} - - -/** - * Handle a command line option - * - * @param opt The option, it is a NUL-terminate two-character - * string starting with either '-' or '+', if the - * argument is not recognised, call `usage`. This - * string will not be "-M", "-S", "-c", "-p", or "-R". - * @param arg The argument associated with `opt`, - * `NULL` there is no next argument, if this - * parameter is `NULL` but needed, call `usage` - * @return 0 if `arg` was not used, - * 1 if `arg` was used, - * -1 on error - */ -int handle_opt(char* opt, char* arg) -{ - if (opt[0] == '-') - switch (opt[1]) - { - case 'd': - if (dflag || xflag) - usage(); - dflag = 1; - break; - case 'x': - if (xflag || dflag) - usage(); - xflag = 1; - break; - default: - usage(); - } - else - usage(); - return 0; - (void) arg; -} - - -/** - * Parse a non-negative double encoded as a string - * - * @param out Output parameter for the value - * @param str The string - * @return Zero on success, -1 if the string is invalid - */ -static int parse_double(double* restrict out, const char* restrict str) -{ - char* end; - errno = 0; - *out = strtod(str, &end); - if (errno || (*out < 0) || isinf(*out) || isnan(*out) || *end) - return -1; - if (!*str || !strchr("0123456789.", *str)) - return -1; - return 0; -} - - -/** - * This function is called after the last - * call to `handle_opt` - * - * @param argc The number of unparsed arguments - * @param argv `NULL` terminated list of unparsed arguments - * @param prio The argument associated with the "-p" option - * @return Zero on success, -1 on error - */ -int handle_args(int argc, char* argv[], char* prio) -{ - int q = xflag + dflag; - if ((q > 1) || (xflag && (prio != NULL || argc))) - usage(); - if (argc == 1) - { - if (parse_double(&value, argv[0]) < 0) - usage(); - } - else if (argc) - usage(); - return 0; -} - - -/** - * Fill a filter - * - * @param filter The filter to fill - * @return Zero on success, -1 on error - */ -static int fill_filter(libcoopgamma_filter_t* restrict filter) -{ - union libcoopgamma_ramps dramps; - size_t size; - - if ((0 <= value) && (value <= 1)) - { - switch (filter->depth) - { -#define X(CONST, MEMBER, MAX, TYPE)\ - case CONST:\ - libclut_negative(&(filter->ramps.MEMBER), MAX, TYPE, 1, 0, 0);\ - libclut_rgb_brightness(&(filter->ramps.MEMBER), MAX, TYPE, 1, 0, 0);\ - libclut_cie_brightness(&(filter->ramps.MEMBER), MAX, TYPE, value, value, value);\ - break; -LIST_DEPTHS -#undef X - default: - abort(); - } - return 0; - } - if (filter->depth == LIBCOOPGAMMA_DOUBLE) - { - libclut_negative(&(filter->ramps.d), (double)1, double, 1, 0, 0); - libclut_rgb_brightness(&(filter->ramps.d), (double)1, double, 1, 0, 0); - libclut_cie_brightness(&(filter->ramps.d), (double)1, double, value, value, value); - libclut_clip(&(filter->ramps.d), (double)1, double, 1, 0, 0); - return 0; - } - if (filter->depth == LIBCOOPGAMMA_FLOAT) - { - libclut_negative(&(filter->ramps.f), (float)1, float, 1, 0, 0); - libclut_rgb_brightness(&(filter->ramps.f), (float)1, float, 1, 0, 0); - libclut_cie_brightness(&(filter->ramps.f), (float)1, float, value, value, value); - libclut_clip(&(filter->ramps.f), (float)1, float, 1, 0, 0); - return 0; - } - - size = dramps.d.red_size = filter->ramps.d.red_size; - size += dramps.d.green_size = filter->ramps.d.green_size; - size += dramps.d.blue_size = filter->ramps.d.blue_size; - dramps.d.red = calloc(size, sizeof(double)); - if (dramps.d.red == NULL) - return -1; - dramps.d.green = dramps.d.red + dramps.d.red_size; - dramps.d.blue = dramps.d.green + dramps.d.green_size; - - libclut_start_over(&(dramps.d), (double)1, double, 1, 0, 0); - libclut_negative(&(dramps.d), (double)1, double, 1, 0, 0); - libclut_rgb_brightness(&(dramps.d), (double)1, double, 1, 0, 0); - libclut_cie_brightness(&(dramps.d), (double)1, double, value, value, value); - libclut_clip(&(dramps.d), (double)1, double, 1, 0, 0); - - switch (filter->depth) - { - case LIBCOOPGAMMA_UINT8: - libclut_translate(&(filter->ramps.u8), UINT8_MAX, uint8_t, &(dramps.d), (double)1, double); - break; - case LIBCOOPGAMMA_UINT16: - libclut_translate(&(filter->ramps.u16), UINT16_MAX, uint16_t, &(dramps.d), (double)1, double); - break; - case LIBCOOPGAMMA_UINT32: - libclut_translate(&(filter->ramps.u32), UINT32_MAX, uint32_t, &(dramps.d), (double)1, double); - break; - case LIBCOOPGAMMA_UINT64: - libclut_translate(&(filter->ramps.u64), UINT64_MAX, uint64_t, &(dramps.d), (double)1, double); - break; - default: - abort(); - } - - free(dramps.d.red); - return 0; -} - - -/** - * The main function for the program-specific code - * - * @return 0: Success - * -1: Error, `errno` set - * -2: Error, `cg.error` set - * -3: Error, message already printed - */ -int start(void) -{ - int r; - size_t i, j; - - if (xflag) - for (i = 0; i < filters_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_REMOVE; - else if (dflag) - for (i = 0; i < filters_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_DEATH; - else - for (i = 0; i < filters_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_REMOVAL; - - if (!xflag) - if ((r = make_slaves()) < 0) - return r; - - for (i = 0, r = 1; i < filters_n; i++) - { - if (!(crtc_updates[i].master) || !(crtc_info[crtc_updates[i].crtc].supported)) - continue; - if (!xflag) - if ((r = fill_filter(&(crtc_updates[i].filter))) < 0) - return r; - r = update_filter(i, 0); - if ((r == -2) || ((r == -1) && (errno != EAGAIN))) - return r; - if (crtc_updates[i].slaves != NULL) - for (j = 0; crtc_updates[i].slaves[j] != 0; j++) - { - r = update_filter(crtc_updates[i].slaves[j], 0); - if ((r == -2) || ((r == -1) && (errno != EAGAIN))) - return r; - } - } - - while (r != 1) - if ((r = synchronise(-1)) < 0) - return r; - - if (!dflag) - return 0; - - if (libcoopgamma_set_nonblocking(&cg, 0) < 0) - return -1; - for (;;) - if (libcoopgamma_synchronise(&cg, NULL, 0, &j) < 0) - switch (errno) - { - case 0: - break; - case ENOTRECOVERABLE: - goto enotrecoverable; - default: - return -1; - } - - enotrecoverable: - for (;;) - if (pause() < 0) - return -1; -} - diff --git a/src/cg-gamma.c b/src/cg-gamma.c deleted file mode 100644 index 1d24d07..0000000 --- a/src/cg-gamma.c +++ /dev/null @@ -1,541 +0,0 @@ -/** - * cg-tools -- Cooperative gamma-enabled tools - * Copyright (C) 2016, 2018 Mattias Andrée (maandree@kth.se) - * - * 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 . - */ -#include "cg-base.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - - - -/** - * The default filter priority for the program - */ -const int64_t default_priority = 0; - -/** - * The default class for the program - */ -char default_class[] = PKGNAME "::cg-gamma::standard"; - -/** - * Class suffixes - */ -const char* const* class_suffixes = (const char* const[]){NULL}; - - - -/** - * -d: keep process alive and remove filter on death - */ -static int dflag = 0; - -/** - * -x: remove filter rather than adding it - */ -static int xflag = 0; - -/** - * -f: gamma listing file - */ -static char* fflag = NULL; - -/** - * The gamma of the red channel - */ -static double rgamma = 1; - -/** - * The gamma of the green channel - */ -static double ggamma = 1; - -/** - * The gamma of the blue channel - */ -static double bgamma = 1; - -/** - * `NULL`-terminated list of output - * names listed in the configuration file - */ -static char** names = NULL; - -/** - * The gamma of the red channel on monitor - * with same index in `names` - */ -static double* rgammas = NULL; - -/** - * The gamma of the green channel on monitor - * with same index in `names` - */ -static double* ggammas = NULL; - -/** - * The gamma of the blue channel on monitor - * with same index in `names` - */ -static double* bgammas = NULL; - - - -/** - * Print usage information and exit - */ -void usage(void) -{ - fprintf(stderr, - "Usage: %s [-M method] [-S site] [-c crtc]... [-R rule] " - "(-x | [-p priority] [-d] [-f file | all | red green blue])\n", - argv0); - exit(1); -} - - -/** - * Perform cleanup so valgrind output is clean - * - * @param ret The value to return - * @return `ret` is returned as is - */ -static int cleanup(int ret) -{ - int saved_errno = errno; - if (names != NULL) - { - char** p = names; - while (*p) - free(*p++); - } - free(names); - free(rgammas); - free(ggammas); - free(bgammas); - errno = saved_errno; - return ret; -} - - -/** - * Handle a command line option - * - * @param opt The option, it is a NUL-terminate two-character - * string starting with either '-' or '+', if the - * argument is not recognised, call `usage`. This - * string will not be "-M", "-S", "-c", "-p", or "-R". - * @param arg The argument associated with `opt`, - * `NULL` there is no next argument, if this - * parameter is `NULL` but needed, call `usage` - * @return 0 if `arg` was not used, - * 1 if `arg` was used, - * -1 on error - */ -int handle_opt(char* opt, char* arg) -{ - if (opt[0] == '-') - switch (opt[1]) - { - case 'd': - if (dflag || xflag) - usage(); - dflag = 1; - break; - case 'x': - if (xflag || dflag) - usage(); - xflag = 1; - break; - case 'f': - if (fflag || !(fflag = arg)) - usage(); - return 1; - default: - usage(); - } - return 0; -} - - -/** - * Get the pathname of a configuration file - * - * @param confname The filename (excluding directory) of the configuration file - * @return The full pathname of the configuration file, `NULL` on error - */ -static char* get_conf_file(const char* restrict confname) -{ - struct passwd* pw; - char* path; - - pw = getpwuid(getuid()); - if ((pw == NULL) || (pw->pw_dir == NULL)) - return NULL; - - path = malloc(strlen(pw->pw_dir) + strlen(confname) + sizeof("/.config/")); - if (path == NULL) - return NULL; - - sprintf(path, "%s/.config/%s", pw->pw_dir, confname); - - if (access(path, F_OK) < 0) - sprintf(path, "/etc/%s", confname); - - return path; -} - - -/** - * Parse a non-negative double encoded as a string - * - * @param out Output parameter for the value - * @param str The string - * @return Zero on success, -1 if the string is invalid - */ -static int parse_double(double* restrict out, const char* restrict str) -{ - char* end; - errno = 0; - *out = strtod(str, &end); - if (errno || (*out < 0) || isinf(*out) || isnan(*out) || *end) - return -1; - if (!*str || !strchr("0123456789.", *str)) - return -1; - return 0; -} - - -/** - * Parse gamma configuration file - * - * @param pathname The pathname of the file - * @return Zero on success, -1 on error - */ -static int parse_gamma_file(const char* restrict pathname) -{ - int fd, saved_errno; - char* line = NULL; - size_t size = 0, lineno = 0, ptr = 0, alloc = 0; - ssize_t n; - FILE* f = NULL; - char* p; - char* q; - char* r; - char* g; - char* b; - - fd = open(pathname, O_RDONLY); - if (fd == -1) - return -1; - - f = fdopen(fd, "rb"); - if (f == NULL) - goto fail; - - while (n = getline(&line, &size, f), n >= 0) - { - lineno += 1; - - if ((n > 0) && (line[n - 1] == '\n')) - line[n - 1] = '\0'; - p = line; - while ((*p == ' ') || (*p == '\t')) p++; - if ((!*p) || (*p == '#')) - continue; - - r = strpbrk(line, " \t"); - if (r == NULL) - goto bad; - while (r[1] == ' ' || r[1] == '\t') r++; - g = strpbrk(r + 1, " \t"); - if (g == NULL) - goto bad; - while (g[1] == ' ' || g[1] == '\t') g++; - b = strpbrk(g + 1, " \t"); - if (b == NULL) - goto bad; - while (b[1] == ' ' || b[1] == '\t') b++; - - for (;;) - { - q = strpbrk(b + 1, " \t"); - if (q == NULL) - break; - while (q[1] == ' ' || q[1] == '\t') q++; - if (!*q) - break; - r = g, g = b, b = q; - } - - *r++ = '\0'; - *g++ = '\0'; - *b++ = '\0'; - - q = strpbrk(r, " \t"); - if (q != NULL) - *q = '\0'; - q = strpbrk(g, " \t"); - if (q != NULL) - *q = '\0'; - q = strpbrk(b, " \t"); - if (q != NULL) - *q = '\0'; - - q = strchr(p, '\0'); - while ((q != p) && ((q[-1] == ' ') || (q[-1] == '\t'))) - q--; - *q = '\0'; - - if (ptr == alloc) - { - void* new; - size_t new_size = alloc ? (alloc << 1) : 4; - - new = realloc(rgammas, new_size * sizeof(*rgammas)); - if (new == NULL) - goto fail; - rgammas = new; - - new = realloc(ggammas, new_size * sizeof(*ggammas)); - if (new == NULL) - goto fail; - ggammas = new; - - new = realloc(bgammas, new_size * sizeof(*bgammas)); - if (new == NULL) - goto fail; - bgammas = new; - - new = realloc(names, (new_size + 1) * sizeof(*names)); - if (new == NULL) - goto fail; - names = new; - memset(names + alloc, 0, (new_size + 1 - alloc) * sizeof(*names)); - - alloc = new_size; - } - - if ((parse_double(rgammas + ptr, r) < 0) || - (parse_double(ggammas + ptr, g) < 0) || - (parse_double(bgammas + ptr, b) < 0)) - goto bad; - names[ptr] = malloc(strlen(p) + 1); - if (names[ptr] == NULL) - goto fail; - strcpy(names[ptr], p); - ptr++; - - continue; - bad: - fprintf(stderr, "%s: ignoring malformatted line in %s: %zu\n", argv0, pathname, lineno); - } - - if (fclose(f) < 0) - { - f = NULL; - goto fail; - } - close(fd); - free(line); - return 0; - fail: - saved_errno = errno; - free(line); - if (f != NULL) - fclose(f); - if (fd >= 0) - close(fd); - errno = saved_errno; - return -1; -} - - -/** - * This function is called after the last - * call to `handle_opt` - * - * @param argc The number of unparsed arguments - * @param argv `NULL` terminated list of unparsed arguments - * @param prio The argument associated with the "-p" option - * @return Zero on success, -1 on error - */ -int handle_args(int argc, char* argv[], char* prio) -{ - int free_fflag = 0, saved_errno; - int q = xflag + dflag; - if ((q > 1) || (fflag && argc) || (xflag && ((fflag != NULL) || (argc > 0) || (prio != NULL)))) - usage(); - if (argc == 1) - { - if (parse_double(&rgamma, argv[0]) < 0) - usage(); - bgamma = ggamma = rgamma; - } - else if (argc == 3) - { - if (parse_double(&rgamma, argv[0]) < 0) - usage(); - if (parse_double(&ggamma, argv[1]) < 0) - usage(); - if (parse_double(&bgamma, argv[2]) < 0) - usage(); - } - else if (argc) - usage(); - if (!argc && !fflag && !xflag) - { - fflag = get_conf_file("gamma"); - if (fflag == NULL) - return -1; - free_fflag = 1; - } - if (fflag) - if (parse_gamma_file(fflag) < 0) - goto fail; - if (free_fflag) - free(fflag), fflag = NULL; - return 0; - fail: - saved_errno = errno; - if (free_fflag) - free(fflag), fflag = NULL; - errno = saved_errno; - return cleanup(-1); -} - - -/** - * Fill a filter - * - * @param filter The filter to fill - * @param r The red gamma - * @param g The green gamma - * @param b The blue gamma - */ -static void fill_filter(libcoopgamma_filter_t* restrict filter, double r, double g, double b) -{ - switch (filter->depth) - { -#define X(CONST, MEMBER, MAX, TYPE)\ - case CONST:\ - libclut_gamma(&(filter->ramps.MEMBER), MAX, TYPE, r, g, b);\ - break; -LIST_DEPTHS -#undef X - default: - abort(); - } -} - - -/** - * The main function for the program-specific code - * - * @return 0: Success - * -1: Error, `errno` set - * -2: Error, `cg.error` set - * -3: Error, message already printed - */ -int start(void) -{ - int r; - size_t i, j; - - if (xflag) - for (i = 0; i < filters_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_REMOVE; - else if (dflag) - for (i = 0; i < filters_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_DEATH; - else - for (i = 0; i < filters_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_REMOVAL; - - if (!xflag && (names != NULL)) - if ((r = make_slaves()) < 0) - return cleanup(r); - - if (names == NULL) - for (i = 0, r = 1; i < filters_n; i++) - { - if (!(crtc_updates[i].master) || !(crtc_info[crtc_updates[i].crtc].supported)) - continue; - if (!xflag) - fill_filter(&(crtc_updates[i].filter), rgamma, ggamma, bgamma); - r = update_filter(i, 0); - if ((r == -2) || ((r == -1) && (errno != EAGAIN))) - return cleanup(r); - if (crtc_updates[i].slaves != NULL) - for (j = 0; crtc_updates[i].slaves[j] != 0; j++) - { - r = update_filter(crtc_updates[i].slaves[j], 0); - if ((r == -2) || ((r == -1) && (errno != EAGAIN))) - return cleanup(r); - } - } - else - for (i = 0, r = 1; i < filters_n; i++) - { - if (!(crtc_info[crtc_updates[i].crtc].supported)) - continue; - for (j = 0; names[j] != NULL; j++) - if (!strcasecmp(crtc_updates[i].filter.crtc, names[j])) - { - fill_filter(&(crtc_updates[i].filter), rgammas[j], ggammas[j], bgammas[j]); - r = update_filter(i, 0); - if ((r == -2) || ((r == -1) && (errno != EAGAIN))) - return cleanup(r); - break; - } - } - - while (r != 1) - if ((r = synchronise(-1)) < 0) - return cleanup(r); - - if (!dflag) - return cleanup(0); - - if (libcoopgamma_set_nonblocking(&cg, 0) < 0) - return cleanup(-1); - for (;;) - if (libcoopgamma_synchronise(&cg, NULL, 0, &j) < 0) - switch (errno) - { - case 0: - break; - case ENOTRECOVERABLE: - goto enotrecoverable; - default: - return cleanup(-1); - } - - enotrecoverable: - for (;;) - if (pause() < 0) - return cleanup(-1); -} - diff --git a/src/cg-icc.c b/src/cg-icc.c deleted file mode 100644 index 5970930..0000000 --- a/src/cg-icc.c +++ /dev/null @@ -1,945 +0,0 @@ -/** - * cg-tools -- Cooperative gamma-enabled tools - * Copyright (C) 2016, 2018 Mattias Andrée (maandree@kth.se) - * - * 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 . - */ -#include "cg-base.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - - - -/* Note, that EDID:s are 256 hexadecimals long, and - * a filename can only be 255 characters long. */ - - - -/** - * Magic number for dual-byte precision lookup table based profiles - */ -#define MLUT_TAG 0x6D4C5554L - -/** - * Magic number for gamma–brightness–contrast based profiles - * and for variable precision lookup table profiles - */ -#define VCGT_TAG 0x76636774L - -/** - * The filename of the configuration file - */ -#define ICCTAB "icctab" - - - -/** - * The default filter priority for the program - */ -const int64_t default_priority = 0; - -/** - * The default class for the program - */ -char default_class[] = PKGNAME "::cg-icc::standard"; - -/** - * Class suffixes - */ -const char* const* class_suffixes = (const char* const[]){NULL}; - - - -/** - * -d: keep process alive and remove filter on death - */ -static int dflag = 0; - -/** - * -x: remove filter rather than adding it - */ -static int xflag = 0; - -/** - * The panhame of the selected ICC profile - */ -static const char* icc_pathname = NULL; - -/** - * Gamma ramps loaded from `icc_pathname` - */ -static libcoopgamma_ramps_t uniramps; - -/** - * The datatype of the stops in the ramps of `uniramps` - */ -static libcoopgamma_depth_t unidepth = 0; - -/** - * Parsed ICC profiles for each CRTC - */ -static libcoopgamma_ramps_t* rampses = NULL; - -/** - * The datatype of the stops in the ramps of - * corresponding element in `rampses` - */ -static libcoopgamma_depth_t* depths = NULL; - -/** - * File descriptor for configuration directory - */ -static int confdirfd = -1; - -/** - * List of CRTC:s - */ -static char** crtc_icc_keys = NULL; - -/** - * List of ICC profile pathnames for corresponding - * CRTC in `crtc_icc_keys` - */ -static char** crtc_icc_values = NULL; - - - -/** - * Print usage information and exit - */ -void usage(void) -{ - fprintf(stderr, - "Usage: %s [-M method] [-S site] [-c crtc]... [-R rule] " - "(-x | [-p priority] [-d] [file])\n", - argv0); - exit(1); -} - - -/** - * Perform cleanup so valgrind output is clean - * - * @param ret The value to return - * @return `ret` is returned as is - */ -static int cleanup(int ret) -{ - int saved_errno = errno; - size_t i; - libcoopgamma_ramps_destroy(&uniramps); - if (confdirfd >= 0) - close(confdirfd); - if (rampses != NULL) - for (i = 0; i < crtcs_n; i++) - libcoopgamma_ramps_destroy(rampses + i); - free(rampses); - free(depths); - if (crtc_icc_keys != NULL) - for (i = 0; crtc_icc_keys[i] != NULL; i++) - free(crtc_icc_keys[i]); - free(crtc_icc_keys); - if (crtc_icc_values != NULL) - for (i = 0; crtc_icc_values[i] != NULL; i++) - free(crtc_icc_values[i]); - free(crtc_icc_values); - errno = saved_errno; - return ret; -} - - -/** - * Handle a command line option - * - * @param opt The option, it is a NUL-terminate two-character - * string starting with either '-' or '+', if the - * argument is not recognised, call `usage`. This - * string will not be "-M", "-S", "-c", "-p", or "-R". - * @param arg The argument associated with `opt`, - * `NULL` there is no next argument, if this - * parameter is `NULL` but needed, call `usage` - * @return 0 if `arg` was not used, - * 1 if `arg` was used, - * -1 on error - */ -int handle_opt(char* opt, char* arg) -{ - if (opt[0] == '-') - switch (opt[1]) - { - case 'd': - if (dflag || xflag) - usage(); - dflag = 1; - break; - case 'x': - if (xflag || dflag) - usage(); - xflag = 1; - break; - default: - usage(); - } - return 0; - (void) arg; -} - - -/** - * Populate `crtc_icc_keys` and `crtc_icc_value` - * - * @path fd File descriptor for the ICC profile table - * @path dirname The dirname of the ICC profile table - * @return Zero on success, -1 on error - */ -static int load_icc_table(int fd, const char *dirname) -{ - FILE *fp; - ssize_t len; - size_t lineno = 1, size = 0; - char *p, *q, *line = NULL; - int saved_errno; - size_t ptr = 0, siz = 0; - void *new; - size_t dirname_len = strlen(dirname); - fp = fdopen(fd, "rb"); - if (fp == NULL) - return -1; - for (; len = getline(&line, &size, fp), len >= 0; lineno++) - { - if (len && line[len - 1] == '\n') - line[--len] = '\0'; - p = line + strspn(line, " \t"); - if (!*p || (*p == '#')) - continue; - q = p + strspn(p, "0123456789abcdefABCDEF"); - if ((*q != ' ' && *q != '\t')) - { - fprintf(stderr, "%s: warning: line %zu is malformated in %s/%s\n", - argv0, lineno, dirname, ICCTAB); - continue; - } - *q = '\0'; - if ((size_t)(q - p) != 256) - fprintf(stderr, "%s: warning: EDID on line %zu in %s/%s looks to be of wrong length: %s\n", - argv0, lineno, dirname, ICCTAB, p); - q++; - q += strspn(p, " \t"); - if (!*q) - { - fprintf(stderr, "%s: warning: line %zu is malformated in %s/%s\n", - argv0, lineno, dirname, ICCTAB); - continue; - } - if (strchr(" \t", strchr(q, '\0')[-1])) - fprintf(stderr, "%s: warning: filename on line %zu in %s/%s ends with white space: %s\n", - argv0, lineno, dirname, ICCTAB, q); - if (ptr == siz) - { - new = realloc(crtc_icc_keys, (siz + 5) * sizeof(*crtc_icc_keys)); - if (new == NULL) - goto fail; - crtc_icc_keys = new; - new = realloc(crtc_icc_values, (siz + 5) * sizeof(*crtc_icc_values)); - if (new == NULL) - goto fail; - crtc_icc_values = new; - siz += 4; - } - crtc_icc_values[ptr] = malloc((*q == '/' ? 1 : dirname_len + sizeof("/")) + strlen(q)); - if (crtc_icc_values[ptr] == NULL) - goto fail; - if (*q == '/') - strcpy(crtc_icc_values[ptr], q); - else - stpcpy(stpcpy(stpcpy(crtc_icc_values[ptr], dirname), "/"), q); - crtc_icc_keys[ptr] = malloc(strlen(p) + 1); - if (crtc_icc_keys[ptr] == NULL) - { - ptr++; - goto fail; - } - strcpy(crtc_icc_keys[ptr], p); - ptr++; - } - if (ferror(fp)) - goto fail; - if (crtc_icc_keys != NULL) - crtc_icc_keys[ptr] = NULL; - if (crtc_icc_values != NULL) - crtc_icc_values[ptr] = NULL; - fclose(fp); - free(line); - return 0; - fail: - saved_errno = errno; - if (crtc_icc_keys != NULL) - crtc_icc_keys[ptr] = NULL; - if (crtc_icc_values != NULL) - crtc_icc_values[ptr] = NULL; - fclose(fp); - free(line); - errno = saved_errno; - return -1; -} - - -/** - * This function is called after the last - * call to `handle_opt` - * - * @param argc The number of unparsed arguments - * @param argv `NULL` terminated list of unparsed arguments - * @param prio The argument associated with the "-p" option - * @return Zero on success, -1 on error - */ -int handle_args(int argc, char* argv[], char* prio) -{ - struct passwd* pw; - char* path = NULL; - int saved_errno; - int fd = -1, q = xflag + dflag; - if ((q > 1) || (xflag && ((argc > 0) || (prio != NULL))) || (argc > 1)) - usage(); - icc_pathname = *argv; - memset(&uniramps, 0, sizeof(uniramps)); - if (!xflag && (icc_pathname == NULL)) - { - pw = getpwuid(getuid()); - if ((pw == NULL) || (pw->pw_dir == NULL)) - goto fail; - - path = malloc(strlen(pw->pw_dir) + sizeof("/.config")); - if (path == NULL) - goto fail; - - sprintf(path, "%s/.config", pw->pw_dir); - - if (access(path, F_OK) < 0) - sprintf(path, "/etc"); - - confdirfd = open(path, O_DIRECTORY); - if (confdirfd < 0) - goto fail; - - fd = openat(confdirfd, ICCTAB, O_RDONLY); - if (fd < 0) - goto fail; - - if (load_icc_table(fd, path) < 0) - goto fail; - - free(path), path = NULL; - close(fd), fd = -1; - } - return 0; - fail: - saved_errno = errno; - free(path), path = NULL; - if (fd >= 0) - close(fd); - errno = saved_errno; - return cleanup(-1); -} - - -/** - * Read an unsigned 64-bit integer - * - * @param content The beginning of the encoded integer - * @return The integer, decoded - */ -static uint64_t icc_uint64(const char* restrict content) -{ - uint64_t rc; - rc = (uint64_t)(unsigned char)(content[0]), rc <<= 8; - rc |= (uint64_t)(unsigned char)(content[1]), rc <<= 8; - rc |= (uint64_t)(unsigned char)(content[2]), rc <<= 8; - rc |= (uint64_t)(unsigned char)(content[3]), rc <<= 8; - rc |= (uint64_t)(unsigned char)(content[4]), rc <<= 8; - rc |= (uint64_t)(unsigned char)(content[5]), rc <<= 8; - rc |= (uint64_t)(unsigned char)(content[6]), rc <<= 8; - rc |= (uint64_t)(unsigned char)(content[7]); - return rc; -} - - -/** - * Read an unsigned 32-bit integer - * - * @param content The beginning of the encoded integer - * @return The integer, decoded - */ -static uint32_t icc_uint32(const char* restrict content) -{ - uint32_t rc; - rc = (uint32_t)(unsigned char)(content[0]), rc <<= 8; - rc |= (uint32_t)(unsigned char)(content[1]), rc <<= 8; - rc |= (uint32_t)(unsigned char)(content[2]), rc <<= 8; - rc |= (uint32_t)(unsigned char)(content[3]); - return rc; -} - - -/** - * Read an unsigned 16-bit integer - * - * @param content The beginning of the encoded integer - * @return The integer, decoded - */ -static uint16_t icc_uint16(const char* restrict content) -{ - uint16_t rc; - rc = (uint16_t)(unsigned char)(content[0]), rc <<= 8; - rc |= (uint16_t)(unsigned char)(content[1]); - return rc; -} - - -/** - * Read an unsigned 8-bit integer - * - * @param content The beginning of the encoded integer - * @return The integer, decoded - */ -static uint16_t icc_uint8(const char* restrict content) -{ - return (uint8_t)(content[0]); -} - - -/** - * Read a floating-point value - * - * @param content The beginning of the encoded value - * @param width The number of bytes with which the value is encoded - * @return The value, decoded - */ -static double icc_double(const char* restrict content, size_t width) -{ - double ret = 0; - size_t i; - for (i = 0; i < width; i++) - { - ret /= 256; - ret += (double)(unsigned char)(content[width - 1 - i]); - } - ret /= 255; - return ret; -} - - -/** - * Parse an ICC profile - * - * @param content The content of the ICC profile file - * @param n The byte-size of `content` - * @param ramps Output parameter for the filter stored in the ICC profile, - * `.red_size`, `.green_size`, `.blue_size` should already be - * set (these values can however be modified.) - * @param depth Output parameter for ramps stop value type - * @return Zero on success, -1 on error, -2 if no usable data is - * available in the profile. - */ -static int parse_icc(const char* restrict content, size_t n, libcoopgamma_ramps_t* ramps, - libcoopgamma_depth_t* depth) -{ - uint32_t i_tag, n_tags; - size_t i, ptr = 0, xptr; - - /* Skip header */ - if (n - ptr < 128) - return -2; - ptr += 128; - - /* Get the number of tags */ - if (n - ptr < 4) - return -2; - n_tags = icc_uint32(content + ptr), ptr += 4; - - for (i_tag = 0, xptr = ptr; i_tag < n_tags; i_tag++, ptr = xptr) - { - uint32_t tag_name, tag_offset, tag_size, gamma_type; - - /* Get profile encoding type, offset to the profile and the encoding size of its data */ - if (n - ptr < 12) - return -2; - tag_name = icc_uint32(content + ptr), ptr += 4; - tag_offset = icc_uint32(content + ptr), ptr += 4; - tag_size = icc_uint32(content + ptr), ptr += 4; - xptr = ptr; - - /* Jump to the profile data */ - if (tag_offset > INT32_MAX - tag_size) - return -2; - if (tag_offset + tag_size > n) - return -2; - ptr = tag_offset; - - if (tag_name == MLUT_TAG) - { - /* The profile is encododed as an dual-byte precision lookup table */ - - /* Initialise ramps */ - *depth = LIBCOOPGAMMA_UINT16; - ramps->u16.red_size = 256; - ramps->u16.green_size = 256; - ramps->u16.blue_size = 256; - if (libcoopgamma_ramps_initialise(&(ramps->u16)) < 0) - return -1; - - /* Get the lookup table */ - if (n - ptr < 3 * 256 * 2) - continue; - for (i = 0; i < 256; i++) - ramps->u16.red[i] = icc_uint16(content + ptr), ptr += 2; - for (i = 0; i < 256; i++) - ramps->u16.green[i] = icc_uint16(content + ptr), ptr += 2; - for (i = 0; i < 256; i++) - ramps->u16.blue[i] = icc_uint16(content + ptr), ptr += 2; - - return 0; - } - else if (tag_name == VCGT_TAG) - { - /* The profile is encoded as with gamma, brightness and contrast values - * or as a variable precision lookup table profile */ - - /* VCGT profiles starts where their magic number */ - if (n - ptr < 4) - continue; - tag_name = icc_uint32(content + ptr), ptr += 4; - if (tag_name != VCGT_TAG) - continue; - - /* Skip four bytes */ - if (n - ptr < 4) - continue; - ptr += 4; - - /* Get the actual encoding type */ - if (n - ptr < 4) - continue; - gamma_type = icc_uint32(content + ptr), ptr += 4; - - if (gamma_type == 0) - { - /* The profile is encoded as a variable precision lookup table */ - uint16_t n_channels, n_entries, entry_size; - - /* Get metadata */ - if (n - ptr < 3 * 4) - continue; - n_channels = icc_uint16(content + ptr), ptr += 2; - n_entries = icc_uint16(content + ptr), ptr += 2; - entry_size = icc_uint16(content + ptr), ptr += 2; - if (tag_size == 1584) - n_channels = 3, n_entries = 256, entry_size = 2; - if (n_channels != 3) - /* Assuming sRGB, can only be an correct assumption if there are exactly three channels */ - continue; - - /* Check data availability */ - if (n_channels > SIZE_MAX / n_entries) - continue; - if (entry_size > SIZE_MAX / (n_entries * n_channels)) - continue; - if (n - ptr < (size_t)n_channels * (size_t)n_entries * (size_t)entry_size) - continue; - - /* Initialise ramps */ - ramps->u8.red_size = (size_t)n_entries; - ramps->u8.green_size = (size_t)n_entries; - ramps->u8.blue_size = (size_t)n_entries; - switch (entry_size) - { - case 1: - *depth = LIBCOOPGAMMA_UINT8; - if (libcoopgamma_ramps_initialise(&(ramps->u8)) < 0) - return -1; - break; - case 2: - *depth = LIBCOOPGAMMA_UINT16; - if (libcoopgamma_ramps_initialise(&(ramps->u16)) < 0) - return -1; - break; - case 4: - *depth = LIBCOOPGAMMA_UINT32; - if (libcoopgamma_ramps_initialise(&(ramps->u32)) < 0) - return -1; - break; - case 8: - *depth = LIBCOOPGAMMA_UINT64; - if (libcoopgamma_ramps_initialise(&(ramps->u64)) < 0) - return -1; - break; - default: - *depth = LIBCOOPGAMMA_DOUBLE; - if (libcoopgamma_ramps_initialise(&(ramps->d)) < 0) - return -1; - break; - } - - /* Get the lookup table */ - switch (*depth) - { - case LIBCOOPGAMMA_UINT8: - for (i = 0; i < ramps->u8.red_size; i++) - ramps->u8.red[i] = icc_uint8(content + ptr), ptr += 1; - for (i = 0; i < ramps->u8.green_size; i++) - ramps->u8.green[i] = icc_uint8(content + ptr), ptr += 1; - for (i = 0; i < ramps->u8.blue_size; i++) - ramps->u8.blue[i] = icc_uint8(content + ptr), ptr += 1; - break; - case LIBCOOPGAMMA_UINT16: - for (i = 0; i < ramps->u16.red_size; i++) - ramps->u16.red[i] = icc_uint16(content + ptr), ptr += 2; - for (i = 0; i < ramps->u16.green_size; i++) - ramps->u16.green[i] = icc_uint16(content + ptr), ptr += 2; - for (i = 0; i < ramps->u16.blue_size; i++) - ramps->u16.blue[i] = icc_uint16(content + ptr), ptr += 2; - break; - case LIBCOOPGAMMA_UINT32: - for (i = 0; i < ramps->u32.red_size; i++) - ramps->u32.red[i] = icc_uint32(content + ptr), ptr += 4; - for (i = 0; i < ramps->u32.green_size; i++) - ramps->u32.green[i] = icc_uint32(content + ptr), ptr += 4; - for (i = 0; i < ramps->u32.blue_size; i++) - ramps->u32.blue[i] = icc_uint32(content + ptr), ptr += 4; - break; - case LIBCOOPGAMMA_UINT64: - for (i = 0; i < ramps->u64.red_size; i++) - ramps->u64.red[i] = icc_uint64(content + ptr), ptr += 8; - for (i = 0; i < ramps->u64.green_size; i++) - ramps->u64.green[i] = icc_uint64(content + ptr), ptr += 8; - for (i = 0; i < ramps->u64.blue_size; i++) - ramps->u64.blue[i] = icc_uint64(content + ptr), ptr += 8; - break; - default: - for (i = 0; i < ramps->d.red_size; i++) - ramps->d.red[i] = icc_double(content + ptr, entry_size), ptr += entry_size; - for (i = 0; i < ramps->d.green_size; i++) - ramps->d.green[i] = icc_double(content + ptr, entry_size), ptr += entry_size; - for (i = 0; i < ramps->d.blue_size; i++) - ramps->d.blue[i] = icc_double(content + ptr, entry_size), ptr += entry_size; - break; - } - - return 0; - } - else if (gamma_type == 1) - { - /* The profile is encoded with gamma, brightness and contrast values */ - double r_gamma, r_min, r_max, g_gamma, g_min, g_max, b_gamma, b_min, b_max; - - /* Get the gamma, brightness and contrast */ - if (n - ptr < 9 * 4) - continue; - r_gamma = icc_uint32(content + ptr), r_gamma /= 65536L, ptr += 4; - r_min = icc_uint32(content + ptr), r_min /= 65536L, ptr += 4; - r_max = icc_uint32(content + ptr), r_max /= 65536L, ptr += 4; - g_gamma = icc_uint32(content + ptr), g_gamma /= 65536L, ptr += 4; - g_min = icc_uint32(content + ptr), g_min /= 65536L, ptr += 4; - g_max = icc_uint32(content + ptr), g_max /= 65536L, ptr += 4; - b_gamma = icc_uint32(content + ptr), b_gamma /= 65536L, ptr += 4; - b_min = icc_uint32(content + ptr), b_min /= 65536L, ptr += 4; - b_max = icc_uint32(content + ptr), b_max /= 65536L, ptr += 4; - - /* Initialise ramps */ - *depth = LIBCOOPGAMMA_DOUBLE; - if (libcoopgamma_ramps_initialise(&(ramps->d)) < 0) - return -1; - - /* Set ramps */ - libclut_start_over(&(ramps->d), (double)1, double, 1, 1, 1); - libclut_gamma(&(ramps->d), (double)1, double, r_gamma, g_gamma, b_gamma); - libclut_rgb_limits(&(ramps->d), (double)1, double, r_min, r_max, g_min, g_max, b_min, b_max); - - return 0; - } - } - } - - return -2; -} - - -/** - * Load an ICC profile - * - * @param file The ICC-profile file - * @param ramps Output parameter for the filter stored in the ICC profile, - * `.red_size`, `.green_size`, `.blue_size` should already be - * set (these values can however be modified.) - * @param depth Output parameter for ramps stop value type - * @return Zero on success, -1 on error, -2 if no usable data is - * available in the profile. - */ -static int load_icc(const char* file, libcoopgamma_ramps_t* ramps, libcoopgamma_depth_t* depth) -{ - char* content = NULL; - size_t ptr = 0, size = 0; - ssize_t got; - int fd = -1, r = -1, saved_errno; - - fd = open(file, O_RDONLY); - if (fd < 0) - { - if (errno == ENOENT) - { - fprintf(stderr, "%s: %s: %s\n", argv0, strerror(ENOENT), file); - errno = 0; - } - goto fail; - } - - for (;;) - { - if (ptr == size) - { - size_t new_size = size ? (size << 1) : 4098; - void* new = realloc(content, new_size); - if (new == NULL) - goto fail; - content = new; - size = new_size; - } - got = read(fd, content + ptr, size - ptr); - if (got < 0) - { - if (errno == EINTR) - continue; - goto fail; - } - if (got == 0) - break; - ptr += (size_t)got; - } - - close(fd), fd = -1; - - r = parse_icc(content, ptr, ramps, depth); - fail: - saved_errno = errno; - if (fd >= 0) - close(fd); - free(content); - errno = saved_errno; - return r; -} - - -/** - * Get the pathname of the ICC profile for a CRTC - * - * @param crtc The CRTC name - * @return The ICC profile file - */ -static const char* get_icc(const char* crtc) -{ - size_t i; - if (crtc_icc_keys != NULL) - for (i = 0; crtc_icc_keys[i] != NULL; i++) - if (!strcasecmp(crtc, crtc_icc_keys[i])) - return crtc_icc_values[i]; - return NULL; -} - - -/** - * Fill a filter - * - * @param filter The filter to fill - * @param ramps The prototype filter - * @param depth The prototype filter's stop datatype - */ -static void fill_filter(libcoopgamma_filter_t* filter, const libcoopgamma_ramps_t* ramps, - libcoopgamma_depth_t depth) -{ - switch (filter->depth) - { -#define X(CONST, MEMBER, MAX, TYPE)\ - case CONST:\ - switch (depth)\ - {\ - case LIBCOOPGAMMA_UINT8:\ - libclut_translate(&(filter->ramps.MEMBER), MAX, TYPE, &(ramps->u8), UINT8_MAX, uint8_t);\ - break;\ - case LIBCOOPGAMMA_UINT16:\ - libclut_translate(&(filter->ramps.MEMBER), MAX, TYPE, &(ramps->u16), UINT16_MAX, uint16_t);\ - break;\ - case LIBCOOPGAMMA_UINT32:\ - libclut_translate(&(filter->ramps.MEMBER), MAX, TYPE, &(ramps->u32), UINT32_MAX, uint32_t);\ - break;\ - case LIBCOOPGAMMA_UINT64:\ - libclut_translate(&(filter->ramps.MEMBER), MAX, TYPE, &(ramps->u64), UINT64_MAX, uint64_t);\ - break;\ - case LIBCOOPGAMMA_FLOAT:\ - libclut_translate(&(filter->ramps.MEMBER), MAX, TYPE, &(ramps->f), (float)1, float);\ - break;\ - case LIBCOOPGAMMA_DOUBLE:\ - libclut_translate(&(filter->ramps.MEMBER), MAX, TYPE, &(ramps->d), (double)1, double);\ - break;\ - }\ - break; -LIST_DEPTHS -#undef X - default: - abort(); - } -} - - -/** - * The main function for the program-specific code - * - * @return 0: Success - * -1: Error, `errno` set - * -2: Error, `cg.error` set - * -3: Error, message already printed - */ -int start(void) -{ - int r; - size_t i, j; - const char* path; - - if (xflag) - for (i = 0; i < crtcs_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_REMOVE; - else if (dflag) - for (i = 0; i < crtcs_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_DEATH; - else - for (i = 0; i < crtcs_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_REMOVAL; - - if (!xflag && (icc_pathname == NULL)) - if ((r = make_slaves()) < 0) - return cleanup(r); - - if (icc_pathname != NULL) - { - uniramps.u8.red_size = uniramps.u8.green_size = uniramps.u8.blue_size = 1; - for (i = 0; i < crtcs_n; i++) - { - if (uniramps.u8.red_size < crtc_updates[i].filter.ramps.u8.red_size) - uniramps. u8.red_size = crtc_updates[i].filter.ramps.u8.red_size; - if (uniramps.u8.green_size < crtc_updates[i].filter.ramps.u8.green_size) - uniramps. u8.green_size = crtc_updates[i].filter.ramps.u8.green_size; - if (uniramps.u8.blue_size < crtc_updates[i].filter.ramps.u8.blue_size) - uniramps. u8.blue_size = crtc_updates[i].filter.ramps.u8.blue_size; - } - switch (load_icc(icc_pathname, &uniramps, &unidepth)) - { - case 0: - break; - case -1: - return cleanup(-1); - case -2: - fprintf(stderr, "%s: unusable ICC profile: %s\n", argv0, icc_pathname); - return cleanup(-3); - } - } - else - { - rampses = calloc(crtcs_n, sizeof(*rampses)); - if (rampses == NULL) - return cleanup(-1); - depths = malloc(crtcs_n * sizeof(*depths)); - if (depths == NULL) - return cleanup(-1); - for (i = 0; i < crtcs_n; i++) - { - rampses[i].u8.red_size = crtc_updates[i].filter.ramps.u8.red_size; - rampses[i].u8.green_size = crtc_updates[i].filter.ramps.u8.green_size; - rampses[i].u8.blue_size = crtc_updates[i].filter.ramps.u8.blue_size; - path = get_icc(crtc_updates[i].filter.crtc); - if (path == NULL) - { - /* TODO remove CRTC */ - } - else - switch (load_icc(path, rampses + i, depths + i)) - { - case 0: - break; - case -1: - return cleanup(-1); - case -2: - fprintf(stderr, "%s: unusable ICC profile: %s\n", argv0, path); - return cleanup(-3); - } - } - } - - for (i = 0, r = 1; i < crtcs_n; i++) - { - if (!(crtc_updates[i].master) || !(crtc_info[i].supported)) - continue; - if (!xflag) - { - if (icc_pathname != NULL) - fill_filter(&(crtc_updates[i].filter), &uniramps, unidepth); - else - fill_filter(&(crtc_updates[i].filter), rampses + i, depths[i]); - } - r = update_filter(i, 0); - if ((r == -2) || ((r == -1) && (errno != EAGAIN))) - return cleanup(r); - if (crtc_updates[i].slaves != NULL) - for (j = 0; crtc_updates[i].slaves[j] != 0; j++) - { - r = update_filter(crtc_updates[i].slaves[j], 0); - if ((r == -2) || ((r == -1) && (errno != EAGAIN))) - return cleanup(r); - } - } - - while (r != 1) - if ((r = synchronise(-1)) < 0) - return cleanup(r); - - if (!dflag) - return cleanup(0); - - if (libcoopgamma_set_nonblocking(&cg, 0) < 0) - return cleanup(-1); - for (;;) - if (libcoopgamma_synchronise(&cg, NULL, 0, &j) < 0) - switch (errno) - { - case 0: - break; - case ENOTRECOVERABLE: - goto enotrecoverable; - default: - return cleanup(-1); - } - - enotrecoverable: - for (;;) - if (pause() < 0) - return cleanup(-1); -} - diff --git a/src/cg-limits.c b/src/cg-limits.c deleted file mode 100644 index 8390385..0000000 --- a/src/cg-limits.c +++ /dev/null @@ -1,731 +0,0 @@ -/** - * cg-tools -- Cooperative gamma-enabled tools - * Copyright (C) 2016, 2018 Mattias Andrée (maandree@kth.se) - * - * 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 . - */ -#include "cg-base.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - - - -/** - * The default filter priority for the program - */ -const int64_t default_priority = -((int64_t)1 << 62); - -/** - * The default class for the program - */ -char default_class[] = PKGNAME "::cg-limits::standard"; - -/** - * Class suffixes - */ -const char* const* class_suffixes = (const char* const[]){NULL}; - - - -/** - * -d: keep process alive and remove filter on death - */ -static int dflag = 0; - -/** - * -x: remove filter rather than adding it - */ -static int xflag = 0; - -/** - * -B: brightness listing file - */ -static char* Bflag = NULL; - -/** - * -C: constrat listing file - */ -static char* Cflag = NULL; - -/** - * The brightness of the red channel - */ -static double rbrightness = 0; - -/** - * The brightness of the green channel - */ -static double gbrightness = 0; - -/** - * The brightness of the blue channel - */ -static double bbrightness = 0; - -/** - * The contrast of the red channel - */ -static double rcontrast = 1; - -/** - * The contrast of the green channel - */ -static double gcontrast = 1; - -/** - * The contrast of the blue channel - */ -static double bcontrast = 1; - -/** - * `NULL`-terminated list of output names - * listed in the brightness configuration file - */ -static char** brightness_names = NULL; - -/** - * The brightness of the red channel on monitor - * with same index in `brightness_names` - */ -static double* rbrightnesses = NULL; - -/** - * The brightness of the green channel on monitor - * with same index in `brightness_names` - */ -static double* gbrightnesses = NULL; - -/** - * The brightness of the blue channel on monitor - * with same index in `brightness_names` - */ -static double* bbrightnesses = NULL; - -/** - * `NULL`-terminated list of output names - * listed in the contrast configuration file - */ -static char** contrast_names = NULL; - -/** - * The contrast of the red channel on monitor - * with same index in `contrast_names` - */ -static double* rcontrasts = NULL; - -/** - * The contrast of the green channel on monitor - * with same index in `contrast_names` - */ -static double* gcontrasts = NULL; - -/** - * The contrast of the blue channel on monitor - * with same index in `contrast_names` - */ -static double* bcontrasts = NULL; - - - -/** - * Print usage information and exit - */ -void usage(void) -{ - fprintf(stderr, - "Usage: %s [-M method] [-S site] [-c crtc]... [-R rule] (-x | [-p priority] [-d] " - "([-B brightness-file] [-C contrast-file] | brightness-all:contrast-all | " - "brightness-red:contrast-red brightness-green:contrast-green brightness-blue:contrast-blue))\n", - argv0); - exit(1); -} - - -/** - * Perform cleanup so valgrind output is clean - * - * @param ret The value to return - * @return `ret` is returned as is - */ -static int cleanup(int ret) -{ - int saved_errno = errno; - if (brightness_names != NULL) - { - char** p = brightness_names; - while (*p) - free(*p++); - } - free(brightness_names); - free(rbrightnesses); - free(gbrightnesses); - free(bbrightnesses); - if (contrast_names != NULL) - { - char** p = contrast_names; - while (*p) - free(*p++); - } - free(contrast_names); - free(rcontrasts); - free(gcontrasts); - free(bcontrasts); - errno = saved_errno; - return ret; -} - - -/** - * Handle a command line option - * - * @param opt The option, it is a NUL-terminate two-character - * string starting with either '-' or '+', if the - * argument is not recognised, call `usage`. This - * string will not be "-M", "-S", "-c", "-p", or "-R". - * @param arg The argument associated with `opt`, - * `NULL` there is no next argument, if this - * parameter is `NULL` but needed, call `usage` - * @return 0 if `arg` was not used, - * 1 if `arg` was used, - * -1 on error - */ -int handle_opt(char* opt, char* arg) -{ - if (opt[0] == '-') - switch (opt[1]) - { - case 'd': - if (dflag || xflag) - usage(); - dflag = 1; - break; - case 'x': - if (xflag || dflag) - usage(); - xflag = 1; - break; - case 'B': - if (Bflag || !(Bflag = arg)) - usage(); - return 1; - case 'C': - if (Cflag || !(Cflag = arg)) - usage(); - return 1; - default: - usage(); - } - return 0; -} - - -/** - * Get the pathname of a configuration file - * - * @param confname The filename (excluding directory) of the configuration file - * @return The full pathname of the configuration file, `NULL` on error - */ -static char* get_conf_file(const char* restrict confname) -{ - struct passwd* pw; - char* path; - - pw = getpwuid(getuid()); - if ((pw == NULL) || (pw->pw_dir == NULL)) - return NULL; - - path = malloc(strlen(pw->pw_dir) + strlen(confname) + sizeof("/.config/")); - if (path == NULL) - return NULL; - - sprintf(path, "%s/.config/%s", pw->pw_dir, confname); - - if (access(path, F_OK) < 0) - sprintf(path, "/etc/%s", confname); - - return path; -} - - -/** - * Parse a double encoded as a string - * - * @param out Output parameter for the value - * @param str The string - * @return Zero on success, -1 if the string is invalid - */ -static int parse_double(double* restrict out, const char* restrict str) -{ - char* end; - errno = 0; - *out = strtod(str, &end); - if (errno || isinf(*out) || isnan(*out) || *end) - return -1; - if (!*str || !strchr("-0123456789.", *str)) - return -1; - return 0; -} - - -/** - * Parse two doubles encoded as a "%lf:%lf" string - * - * @param left Output parameter for the first value - * @param right Output parameter for the second value - * @param str The string - * @return Zero on success, -1 if the string is invalid - */ -static int parse_twidouble(double* restrict left, double* restrict right, const char* restrict str) -{ - char* p = strchr(str, ':'); - int r; - if (p == NULL) - return -1; - *p = '\0'; - r = -((parse_double(left, str) < 0) || (parse_double(right, p + 1) < 0)); - *p = ':'; - return r; -} - - -/** - * Parse configuration file - * - * @param pathname The pathname of the file - * @param names Reference to the list of names - * @param rs Reference to the list of red values - * @param gs Reference to the list of green values - * @param bs Reference to the list of blue values - * @return Zero on success, -1 on error - */ -static int parse_conf_file(const char* restrict pathname, char*** restrict names, - double** restrict rs, double** restrict gs, double** restrict bs) -{ - int fd, saved_errno; - char* line = NULL; - size_t size = 0, lineno = 0, ptr = 0, alloc = 0; - ssize_t n; - FILE* f = NULL; - char* p; - char* q; - char* r; - char* g; - char* b; - - fd = open(pathname, O_RDONLY); - if (fd == -1) - return -1; - - f = fdopen(fd, "rb"); - if (f == NULL) - goto fail; - - while (n = getline(&line, &size, f), n >= 0) - { - lineno += 1; - - if ((n > 0) && (line[n - 1] == '\n')) - line[n - 1] = '\0'; - p = line; - while ((*p == ' ') || (*p == '\t')) p++; - if ((!*p) || (*p == '#')) - continue; - - r = strpbrk(line, " \t"); - if (r == NULL) - goto bad; - while (r[1] == ' ' || r[1] == '\t') r++; - g = strpbrk(r + 1, " \t"); - if (g == NULL) - goto bad; - while (g[1] == ' ' || g[1] == '\t') g++; - b = strpbrk(g + 1, " \t"); - if (b == NULL) - goto bad; - while (b[1] == ' ' || b[1] == '\t') b++; - - for (;;) - { - q = strpbrk(b + 1, " \t"); - if (q == NULL) - break; - while (q[1] == ' ' || q[1] == '\t') q++; - if (!*q) - break; - r = g, g = b, b = q; - } - - *r++ = '\0'; - *g++ = '\0'; - *b++ = '\0'; - - q = strpbrk(r, " \t"); - if (q != NULL) - *q = '\0'; - q = strpbrk(g, " \t"); - if (q != NULL) - *q = '\0'; - q = strpbrk(b, " \t"); - if (q != NULL) - *q = '\0'; - - q = strchr(p, '\0'); - while ((q != p) && ((q[-1] == ' ') || (q[-1] == '\t'))) - q--; - *q = '\0'; - - if (ptr == alloc) - { - void* new; - size_t new_size = alloc ? (alloc << 1) : 4; - - new = realloc(*rs, new_size * sizeof(**rs)); - if (new == NULL) - goto fail; - *rs = new; - - new = realloc(*gs, new_size * sizeof(**gs)); - if (new == NULL) - goto fail; - *gs = new; - - new = realloc(*bs, new_size * sizeof(**bs)); - if (new == NULL) - goto fail; - *bs = new; - - new = realloc(*names, (new_size + 1) * sizeof(**names)); - if (new == NULL) - goto fail; - *names = new; - memset(*names + alloc, 0, (new_size + 1 - alloc) * sizeof(**names)); - - alloc = new_size; - } - - if ((parse_double((*rs) + ptr, r) < 0) || - (parse_double((*gs) + ptr, g) < 0) || - (parse_double((*bs) + ptr, b) < 0)) - goto bad; - (*names)[ptr] = malloc(strlen(p) + 1); - if ((*names)[ptr] == NULL) - goto fail; - strcpy((*names)[ptr], p); - ptr++; - - continue; - bad: - fprintf(stderr, "%s: ignoring malformatted line in %s: %zu\n", argv0, pathname, lineno); - } - - if (fclose(f) < 0) - { - f = NULL; - goto fail; - } - close(fd); - free(line); - return 0; - fail: - saved_errno = errno; - free(line); - if (f != NULL) - fclose(f); - if (fd >= 0) - close(fd); - errno = saved_errno; - return -1; -} - - -/** - * Parse brightness configuration file - * - * @param pathname The pathname of the file - * @return Zero on success, -1 on error - */ -static int parse_brightness_file(const char* restrict pathname) -{ - return parse_conf_file(pathname, &brightness_names, &rbrightnesses, &gbrightnesses, &bbrightnesses); -} - - -/** - * Parse contrast configuration file - * - * @param pathname The pathname of the file - * @return Zero on success, -1 on error - */ -static int parse_contrast_file(const char* restrict pathname) -{ - return parse_conf_file(pathname, &contrast_names, &rcontrasts, &gcontrasts, &bcontrasts); -} - - -/** - * This function is called after the last - * call to `handle_opt` - * - * @param argc The number of unparsed arguments - * @param argv `NULL` terminated list of unparsed arguments - * @param prio The argument associated with the "-p" option - * @return Zero on success, -1 on error - */ -int handle_args(int argc, char* argv[], char* prio) -{ - int free_Bflag = 0, free_Cflag = 0, saved_errno; - int q = xflag + dflag; - if ((q > 1) || (xflag && ((Bflag != NULL) || (Cflag != NULL) || (argc > 0) || (prio != NULL)))) - usage(); - if ((Bflag || Cflag) && argc) - usage(); - - if (argc == 1) - { - if (parse_twidouble(&rbrightness, &rcontrast, argv[0]) < 0) - usage(); - bbrightness = gbrightness = rbrightness; - bcontrast = gcontrast = rcontrast; - } - else if (argc == 3) - { - if (parse_twidouble(&rbrightness, &rcontrast, argv[0]) < 0) - usage(); - if (parse_twidouble(&gbrightness, &gcontrast, argv[1]) < 0) - usage(); - if (parse_twidouble(&bbrightness, &bcontrast, argv[2]) < 0) - usage(); - } - else if (argc) - usage(); - - if (!argc && !Bflag && !xflag) - { - Bflag = get_conf_file("brightness"); - if (Bflag == NULL) - return -1; - free_Bflag = 1; - } - if (Bflag) - if (parse_brightness_file(Bflag) < 0) - goto fail; - if (free_Bflag) - free(Bflag), Bflag = NULL; - - if (!argc && !Cflag && !xflag) - { - Cflag = get_conf_file("contrast"); - if (Cflag == NULL) - return -1; - free_Cflag = 1; - } - if (Cflag) - if (parse_contrast_file(Cflag) < 0) - goto fail; - if (free_Cflag) - free(Cflag), Cflag = NULL; - - return 0; - fail: - saved_errno = errno; - if (free_Bflag) - free(Bflag), Bflag = NULL; - if (free_Cflag) - free(Cflag), Cflag = NULL; - errno = saved_errno; - return cleanup(-1); -} - - -/** - * Fill a filter - * - * @param filter The filter to fill - * @param rb The red brightness - * @param rc The red contrast - * @param gb The green brightness - * @param gc The green contrast - * @param bb The blue brightness - * @param bc The blue contrast - * @return Zero on success, -1 on error - */ -static int fill_filter(libcoopgamma_filter_t* restrict filter, - double rb, double rc, double gb, double gc, double bb, double bc) -{ - union libcoopgamma_ramps dramps; - size_t size; - - if (filter->depth == LIBCOOPGAMMA_DOUBLE) - { - libclut_rgb_limits(&(filter->ramps.d), (double)1, double, rb, rc, gb, gc, bb, bc); - libclut_clip(&(filter->ramps.d), (double)1, double, 1, 1, 1); - return 0; - } - if (filter->depth == LIBCOOPGAMMA_FLOAT) - { - libclut_rgb_limits(&(filter->ramps.f), (float)1, float, rb, rc, gb, gc, bb, bc); - libclut_clip(&(filter->ramps.f), (float)1, float, 1, 1, 1); - return 0; - } - - size = dramps.d.red_size = filter->ramps.d.red_size; - size += dramps.d.green_size = filter->ramps.d.green_size; - size += dramps.d.blue_size = filter->ramps.d.blue_size; - dramps.d.red = calloc(size, sizeof(double)); - if (dramps.d.red == NULL) - return -1; - dramps.d.green = dramps.d.red + dramps.d.red_size; - dramps.d.blue = dramps.d.green + dramps.d.green_size; - - libclut_start_over(&(dramps.d), (double)1, double, 1, 1, 1); - libclut_rgb_limits(&(dramps.d), (double)1, double, rb, rc, gb, gc, bb, bc); - libclut_clip(&(dramps.d), (double)1, double, 1, 1, 1); - - switch (filter->depth) - { - case LIBCOOPGAMMA_UINT8: - libclut_translate(&(filter->ramps.u8), UINT8_MAX, uint8_t, &(dramps.d), (double)1, double); - break; - case LIBCOOPGAMMA_UINT16: - libclut_translate(&(filter->ramps.u16), UINT16_MAX, uint16_t, &(dramps.d), (double)1, double); - break; - case LIBCOOPGAMMA_UINT32: - libclut_translate(&(filter->ramps.u32), UINT32_MAX, uint32_t, &(dramps.d), (double)1, double); - break; - case LIBCOOPGAMMA_UINT64: - libclut_translate(&(filter->ramps.u64), UINT64_MAX, uint64_t, &(dramps.d), (double)1, double); - break; - default: - abort(); - } - - free(dramps.d.red); - return 0; -} - - -/** - * The main function for the program-specific code - * - * @return 0: Success - * -1: Error, `errno` set - * -2: Error, `cg.error` set - * -3: Error, message already printed - */ -int start(void) -{ - int r; - size_t i, j, k; - - if (xflag) - for (i = 0; i < filters_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_REMOVE; - else if (dflag) - for (i = 0; i < filters_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_DEATH; - else - for (i = 0; i < filters_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_REMOVAL; - - if (!xflag && ((brightness_names != NULL) || (contrast_names != NULL))) - if ((r = make_slaves()) < 0) - return cleanup(r); - - if ((brightness_names == NULL) && (contrast_names == NULL)) - for (i = 0, r = 1; i < filters_n; i++) - { - if (!(crtc_updates[i].master) || !(crtc_info[crtc_updates[i].crtc].supported)) - continue; - if (!xflag) - if ((r = fill_filter(&(crtc_updates[i].filter), rbrightness, rcontrast, - gbrightness, gcontrast, bbrightness, bcontrast)) < 0) - return cleanup(r); - r = update_filter(i, 0); - if ((r == -2) || ((r == -1) && (errno != EAGAIN))) - return cleanup(r); - if (crtc_updates[i].slaves != NULL) - for (j = 0; crtc_updates[i].slaves[j] != 0; j++) - { - r = update_filter(crtc_updates[i].slaves[j], 0); - if ((r == -2) || ((r == -1) && (errno != EAGAIN))) - return cleanup(r); - } - } - else - { - char* empty = NULL; - char** bnames = brightness_names ? brightness_names : ∅ - char** cnames = contrast_names ? contrast_names : ∅ - for (i = 0, r = 1; i < filters_n; i++) - { - if (!(crtc_info[crtc_updates[i].crtc].supported)) - continue; - for (j = 0; bnames[j] != NULL; j++) - if (!strcasecmp(crtc_updates[i].filter.crtc, bnames[j])) - break; - for (k = 0; cnames[k] != NULL; k++) - if (!strcasecmp(crtc_updates[i].filter.crtc, cnames[k])) - break; - if ((bnames[j] != NULL) || (cnames[k] != NULL)) - { - double rb = 0, gb = 0, bb = 0, rc = 1, bc = 1, gc = 1; - if (bnames[j] != NULL) - rb = rbrightnesses[j], gb = gbrightnesses[j], bb = bbrightnesses[j]; - if (cnames[j] != NULL) - rc = rcontrasts[j], gc = gcontrasts[j], bc = bcontrasts[j]; - if ((r = fill_filter(&(crtc_updates[i].filter), rb, rc, gb, gc, bb, bc)) < 0) - return cleanup(r); - r = update_filter(i, 0); - if ((r == -2) || ((r == -1) && (errno != EAGAIN))) - return cleanup(r); - } - } - } - - while (r != 1) - if ((r = synchronise(-1)) < 0) - return cleanup(r); - - if (!dflag) - return cleanup(0); - - if (libcoopgamma_set_nonblocking(&cg, 0) < 0) - return cleanup(-1); - for (;;) - if (libcoopgamma_synchronise(&cg, NULL, 0, &j) < 0) - switch (errno) - { - case 0: - break; - case ENOTRECOVERABLE: - goto enotrecoverable; - default: - return cleanup(-1); - } - - enotrecoverable: - for (;;) - if (pause() < 0) - return cleanup(-1); -} - diff --git a/src/cg-linear.c b/src/cg-linear.c deleted file mode 100644 index 1ce9a5d..0000000 --- a/src/cg-linear.c +++ /dev/null @@ -1,280 +0,0 @@ -/** - * cg-tools -- Cooperative gamma-enabled tools - * Copyright (C) 2016, 2018 Mattias Andrée (maandree@kth.se) - * - * 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 . - */ -#include "cg-base.h" - -#include - -#include -#include -#include -#include -#include - - - -/** - * The default filter priority for the program - */ -const int64_t default_priority = NO_DEFAULT_PRIORITY; - -/** - * The default class base for the program - */ -char default_class[] = PKGNAME "::cg-linear::standard"; - -/** - * Class suffixes - */ -const char* const* class_suffixes = (const char* const[]){":start", ":stop", NULL}; - - - -/** - * -d: keep process alive and remove filter on death - */ -static int dflag = 0; - -/** - * -x: remove filter rather than adding it - */ -static int xflag = 0; - -/** - * +r: do not touch the red channel - */ -static int rplus = 0; - -/** - * +g: do not touch the green channel - */ -static int gplus = 0; - -/** - * +b: do not touch the blue channel - */ -static int bplus = 0; - -/** - * The priority of the linearisation filter - */ -static int64_t start_priority; - -/** - * The priority of the delinearisation filter - */ -static int64_t stop_priority; - - - -/** - * Print usage information and exit - */ -void usage(void) -{ - fprintf(stderr, - "Usage: %s [-M method] [-S site] [-c crtc]... [-R rule-base] " - "(-x | -p start-priority:stop-priority [-d] [+rgb])\n", - argv0); - exit(1); -} - - -/** - * Handle a command line option - * - * @param opt The option, it is a NUL-terminate two-character - * string starting with either '-' or '+', if the - * argument is not recognised, call `usage`. This - * string will not be "-M", "-S", "-c", "-p", or "-R". - * @param arg The argument associated with `opt`, - * `NULL` there is no next argument, if this - * parameter is `NULL` but needed, call `usage` - * @return 0 if `arg` was not used, - * 1 if `arg` was used, - * -1 on error - */ -int handle_opt(char* opt, char* arg) -{ - if (opt[0] == '-') - switch (opt[1]) - { - case 'd': - if (dflag || xflag) - usage(); - dflag = 1; - break; - case 'x': - if (xflag || dflag) - usage(); - xflag = 1; - break; - default: - usage(); - } - else - switch (opt[1]) - { - case 'r': - if (rplus) - usage(); - rplus = 1; - break; - case 'g': - if (gplus) - usage(); - gplus = 1; - break; - case 'b': - if (bplus) - usage(); - bplus = 1; - break; - default: - usage(); - } - return 0; - (void) arg; -} - - -/** - * This function is called after the last - * call to `handle_opt` - * - * @param argc The number of unparsed arguments - * @param argv `NULL` terminated list of unparsed arguments - * @param prio The argument associated with the "-p" option - * @return Zero on success, -1 on error - */ -int handle_args(int argc, char* argv[], char* prio) -{ - int q = xflag + (dflag | rplus | gplus | bplus); - char *p, *end; - if (argc || (q > 1) || (xflag && (prio != NULL))) - usage(); - if (!xflag && (prio == NULL)) - usage(); - if (prio != NULL) - { - p = strchr(prio, ':'); - if (!p) - usage(); - *p++ = '\0'; - errno = 0; - start_priority = (size_t)strtoul(prio, &end, 10); - if (errno || *end || !*prio) - usage(); - stop_priority = (size_t)strtoul(p, &end, 10); - if (errno || *end || !*prio) - usage(); - p[-1] = ':'; - } - return 0; - (void) argv; -} - - -/** - * Fill a filter - * - * @param filter The filter to fill - * @param is_start If the fitler is a linearisation filter - */ -static void fill_filter(libcoopgamma_filter_t* restrict filter, int is_start) -{ - switch (filter->depth) - { -#define X(CONST, MEMBER, MAX, TYPE)\ - case CONST:\ - if (is_start)\ - libclut_linearise(&(filter->ramps.MEMBER), MAX, TYPE, !rplus, !gplus, !bplus);\ - else\ - libclut_standardise(&(filter->ramps.MEMBER), MAX, TYPE, !rplus, !gplus, !bplus);\ - break; -LIST_DEPTHS -#undef X - default: - abort(); - } -} - - -/** - * The main function for the program-specific code - * - * @return 0: Success - * -1: Error, `errno` set - * -2: Error, `cg.error` set - * -3: Error, message already printed - */ -int start(void) -{ - int r; - size_t i, j; - - if (xflag) - for (i = 0; i < filters_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_REMOVE; - else if (dflag) - for (i = 0; i < filters_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_DEATH; - else - for (i = 0; i < filters_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_REMOVAL; - - for (i = 0, r = 1; i < filters_n; i++) - { - if (!(crtc_info[crtc_updates[i].crtc].supported)) - continue; - if (!xflag) { - int is_start = strchr(crtc_updates[i].filter.class, '\0')[-1] == 't'; - fill_filter(&(crtc_updates[i].filter), is_start); - crtc_updates[i].filter.priority = is_start ? start_priority : stop_priority; - } - r = update_filter(i, 0); - if ((r == -2) || ((r == -1) && (errno != EAGAIN))) - return r; - } - - while (r != 1) - if ((r = synchronise(-1)) < 0) - return r; - - if (!dflag) - return 0; - - if (libcoopgamma_set_nonblocking(&cg, 0) < 0) - return -1; - for (;;) - if (libcoopgamma_synchronise(&cg, NULL, 0, &j) < 0) - switch (errno) - { - case 0: - break; - case ENOTRECOVERABLE: - goto enotrecoverable; - default: - return -1; - } - - enotrecoverable: - for (;;) - if (pause() < 0) - return -1; -} - diff --git a/src/cg-negative.c b/src/cg-negative.c deleted file mode 100644 index 8a98959..0000000 --- a/src/cg-negative.c +++ /dev/null @@ -1,255 +0,0 @@ -/** - * cg-tools -- Cooperative gamma-enabled tools - * Copyright (C) 2016, 2018 Mattias Andrée (maandree@kth.se) - * - * 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 . - */ -#include "cg-base.h" - -#include - -#include -#include -#include -#include -#include - - - -/** - * The default filter priority for the program - */ -const int64_t default_priority = (int64_t)1 << 62; - -/** - * The default class for the program - */ -char default_class[] = PKGNAME "::cg-negative::standard"; - -/** - * Class suffixes - */ -const char* const* class_suffixes = (const char* const[]){NULL}; - - - -/** - * -d: keep process alive and remove filter on death - */ -static int dflag = 0; - -/** - * -x: remove filter rather than adding it - */ -static int xflag = 0; - -/** - * +r: do not touch the red channel - */ -static int rplus = 0; - -/** - * +g: do not touch the green channel - */ -static int gplus = 0; - -/** - * +b: do not touch the blue channel - */ -static int bplus = 0; - - - -/** - * Print usage information and exit - */ -void usage(void) -{ - fprintf(stderr, - "Usage: %s [-M method] [-S site] [-c crtc]... [-R rule] (-x | [-p priority] [-d] [+rgb])\n", - argv0); - exit(1); -} - - -/** - * Handle a command line option - * - * @param opt The option, it is a NUL-terminate two-character - * string starting with either '-' or '+', if the - * argument is not recognised, call `usage`. This - * string will not be "-M", "-S", "-c", "-p", or "-R". - * @param arg The argument associated with `opt`, - * `NULL` there is no next argument, if this - * parameter is `NULL` but needed, call `usage` - * @return 0 if `arg` was not used, - * 1 if `arg` was used, - * -1 on error - */ -int handle_opt(char* opt, char* arg) -{ - if (opt[0] == '-') - switch (opt[1]) - { - case 'd': - if (dflag || xflag) - usage(); - dflag = 1; - break; - case 'x': - if (xflag || dflag) - usage(); - xflag = 1; - break; - default: - usage(); - } - else - switch (opt[1]) - { - case 'r': - if (rplus) - usage(); - rplus = 1; - break; - case 'g': - if (gplus) - usage(); - gplus = 1; - break; - case 'b': - if (bplus) - usage(); - bplus = 1; - break; - default: - usage(); - } - return 0; - (void) arg; -} - - -/** - * This function is called after the last - * call to `handle_opt` - * - * @param argc The number of unparsed arguments - * @param argv `NULL` terminated list of unparsed arguments - * @param prio The argument associated with the "-p" option - * @return Zero on success, -1 on error - */ -int handle_args(int argc, char* argv[], char* prio) -{ - int q = xflag + (dflag | rplus | gplus | bplus); - if (argc || (q > 1) || (xflag && (prio != NULL))) - usage(); - return 0; - (void) argv; -} - - -/** - * Fill a filter - * - * @param filter The filter to fill - */ -static void fill_filter(libcoopgamma_filter_t* restrict filter) -{ - switch (filter->depth) - { -#define X(CONST, MEMBER, MAX, TYPE)\ - case CONST:\ - libclut_negative(&(filter->ramps.MEMBER), MAX, TYPE, !rplus, !gplus, !bplus);\ - break; -LIST_DEPTHS -#undef X - default: - abort(); - } -} - - -/** - * The main function for the program-specific code - * - * @return 0: Success - * -1: Error, `errno` set - * -2: Error, `cg.error` set - * -3: Error, message already printed - */ -int start(void) -{ - int r; - size_t i, j; - - if (xflag) - for (i = 0; i < filters_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_REMOVE; - else if (dflag) - for (i = 0; i < filters_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_DEATH; - else - for (i = 0; i < filters_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_REMOVAL; - - if (!xflag) - if ((r = make_slaves()) < 0) - return r; - - for (i = 0, r = 1; i < filters_n; i++) - { - if (!(crtc_updates[i].master) || !(crtc_info[crtc_updates[i].crtc].supported)) - continue; - if (!xflag) - fill_filter(&(crtc_updates[i].filter)); - r = update_filter(i, 0); - if ((r == -2) || ((r == -1) && (errno != EAGAIN))) - return r; - if (crtc_updates[i].slaves != NULL) - for (j = 0; crtc_updates[i].slaves[j] != 0; j++) - { - r = update_filter(crtc_updates[i].slaves[j], 0); - if ((r == -2) || ((r == -1) && (errno != EAGAIN))) - return r; - } - } - - while (r != 1) - if ((r = synchronise(-1)) < 0) - return r; - - if (!dflag) - return 0; - - if (libcoopgamma_set_nonblocking(&cg, 0) < 0) - return -1; - for (;;) - if (libcoopgamma_synchronise(&cg, NULL, 0, &j) < 0) - switch (errno) - { - case 0: - break; - case ENOTRECOVERABLE: - goto enotrecoverable; - default: - return -1; - } - - enotrecoverable: - for (;;) - if (pause() < 0) - return -1; -} - diff --git a/src/cg-query.c b/src/cg-query.c deleted file mode 100644 index a24ef11..0000000 --- a/src/cg-query.c +++ /dev/null @@ -1,514 +0,0 @@ -/** - * cg-tools -- Cooperative gamma-enabled tools - * Copyright (C) 2016, 2018 Mattias Andrée (maandree@kth.se) - * - * 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 . - */ -#include "arg.h" - -#include - -#include -#include -#include -#include -#include -#include - - - -/** - * The libcoopgamma context - */ -static libcoopgamma_context_t cg; - -/** - * Filter query - */ -static libcoopgamma_filter_query_t query; - -/** - * The class of the filter to print - */ -static char* class = NULL; - - - -/** - * Print usage information and exit - */ -static void usage(void) -{ - fprintf(stderr, - "Usage: %s [-M method] [-S site] [-h high] [-l low] [-f class] -c crtc\n", - argv0); - exit(1); -} - - -/** - * Initialise the process, specifically - * reset the signal mask and signal handlers - * - * @return Zero on success, -1 on error - */ -static int initialise_proc(void) -{ - sigset_t sigmask; - int sig; - - for (sig = 1; sig < _NSIG; sig++) - if (signal(sig, SIG_DFL) == SIG_ERR) - if (sig == SIGCHLD) - return -1; - - if (sigemptyset(&sigmask) < 0) - return -1; - if (sigprocmask(SIG_SETMASK, &sigmask, NULL) < 0) - return -1; - - return 0; -} - - -/** - * Print, to stdout, a list of all - * recognised adjustment methods - * - * @return Zero on success, -1 on error - */ -static int list_methods(void) -{ - char** list; - size_t i; - - list = libcoopgamma_get_methods(); - if (list == NULL) - return -1; - for (i = 0; list[i]; i++) - printf("%s\n", list[i]); - free(list); - if (fflush(stdout) < 0) - return -1; - - return 0; -} - - -/** - * Print, to stdout, a list of all CRTC:s - * - * A connection to the coopgamma server - * must have been made - * - * @return Zero on success, -1 on error, -2 - * on libcoopgamma error - */ -static int list_crtcs(void) -{ - char** list; - size_t i; - - list = libcoopgamma_get_crtcs_sync(&cg); - if (list == NULL) - return -2; - for (i = 0; list[i]; i++) - printf("%s\n", list[i]); - free(list); - if (fflush(stdout) < 0) - return -1; - - return 0; -} - - -/** - * Print, to stdout, information about - * the selected CRTC - * - * @return Zero on success, -1 on error, -2 - * on libcoopgamma error, -3 on error - * with error message already printed - */ -static int print_info(void) -{ - libcoopgamma_crtc_info_t info; - libcoopgamma_filter_table_t table; - const char* str; - int saved_errno, ret = 0; - size_t i; - - if (libcoopgamma_crtc_info_initialise(&info) < 0) - return -1; - if (libcoopgamma_filter_table_initialise(&table) < 0) - { - saved_errno = errno; - libcoopgamma_crtc_info_destroy(&info); - errno = saved_errno; - return -1; - } - - if (libcoopgamma_get_gamma_info_sync(query.crtc, &info, &cg) < 0) - goto cg_fail; - - printf("Cooperative gamma server running: %s\n", - info.cooperative ? "yes" : "no"); - - printf("Gamma adjustments supported: %s\n", - info.supported == LIBCOOPGAMMA_MAYBE ? "maybe" : info.supported ? "yes" : "no"); - - printf("Gamma ramps stops (red green blue): %zu %zu %zu\n", - info.red_size, info.green_size, info.blue_size); - - switch (info.depth) - { - case LIBCOOPGAMMA_DOUBLE: str = "double-precision floating-point"; break; - case LIBCOOPGAMMA_FLOAT: str = "single-precision floating-point"; break; - case LIBCOOPGAMMA_UINT8: str = "unsigned 8-bit integer"; break; - case LIBCOOPGAMMA_UINT16: str = "unsigned 16-bit integer"; break; - case LIBCOOPGAMMA_UINT32: str = "unsigned 32-bit integer"; break; - case LIBCOOPGAMMA_UINT64: str = "unsigned 64-bit integer"; break; - default: - errno = EPROTO; - goto fail; - } - printf("Gamma ramps stops value type: %s\n", str); - - if (info.colourspace != LIBCOOPGAMMA_UNKNOWN) - { - switch (info.colourspace) - { - case LIBCOOPGAMMA_SRGB: str = "sRGB"; break; - case LIBCOOPGAMMA_RGB: str = "non-standard RGB"; break; - case LIBCOOPGAMMA_NON_RGB: str = "non-RGB multicolour"; break; - case LIBCOOPGAMMA_GREY: str = "monochrome or singlecolour scale"; break; - default: - errno = EPROTO; - goto fail; - } - printf("Monitor's colourspace: %s\n", str); - } - - if (info.have_gamut) - { - printf("Monitor's red colour (x, y): %lf, %lf\n", - info.red_x / (double)1024, info.red_y / (double)1024); - - printf("Monitor's green colour (x, y): %lf, %lf\n", - info.green_x / (double)1024, info.green_y / (double)1024); - - printf("Monitor's blue colour (x, y): %lf, %lf\n", - info.blue_x / (double)1024, info.blue_y / (double)1024); - - printf("Monitor's white point (x, y): %lf, %lf\n", - info.white_x / (double)1024, info.white_y / (double)1024); - } - - if (libcoopgamma_get_gamma_sync(&query, &table, &cg) < 0) - goto cg_fail; - - if ((table.red_size != info.red_size) || (table.green_size != info.green_size) || - (table.blue_size != info.blue_size) || (table.depth != info.depth)) - { - fprintf(stderr, "%s: gamma ramp structure changed between queries\n", argv0); - goto custom_fail; - } - - printf("Filters: %zu\n", table.filter_count); - for (i = 0; i < table.filter_count; i++) - { - printf(" Filter %zu:\n", i); - printf(" Priority: %" PRIi64 "\n", table.filters[i].priority); - printf(" Class: %s\n", table.filters[i].class); - } - - done: - saved_errno = errno; - libcoopgamma_crtc_info_destroy(&info); - libcoopgamma_filter_table_destroy(&table); - errno = saved_errno; - return ret; - fail: - ret = -1; - goto done; - cg_fail: - ret = -2; - goto done; - custom_fail: - ret = -3; - goto done; -} - - - -/** - * Print, to stdout, the ramps of the select - * filter on the select CRTC - * - * @return Zero on success, -1 on error, -2 - * on libcoopgamma error, -3 on error - * with error message already printed - */ -static int print_filter(void) -{ - libcoopgamma_filter_table_t table; - libcoopgamma_ramps_t* restrict ramps; - int saved_errno, ret = 0; - size_t i, n; - - if (libcoopgamma_filter_table_initialise(&table) < 0) - return -1; - - if (libcoopgamma_get_gamma_sync(&query, &table, &cg) < 0) - goto cg_fail; - - if (query.coalesce) - i = 0; - else - for (i = 0; i < table.filter_count; i++) - if (!strcmp(table.filters[i].class, class)) - break; - if (i == table.filter_count) - { - fprintf(stderr, "%s: selected filter does not exist on selected CRTC\n", argv0); - goto custom_fail; - } - ramps = &(table.filters[i].ramps); - - n = table.red_size; - if (n < table.green_size) - n = table.green_size; - if (n < table.blue_size) - n = table.blue_size; - - switch (table.depth) - { -#define X(CONST, MEMBER, TYPE, FORMAT, DASH) \ - case CONST: \ - for (i = 0; i < n; i++) \ - { \ - if (i < ramps->MEMBER.red_size) \ - printf("%" FORMAT " ", (TYPE)(ramps->MEMBER.red[i])); \ - else \ - printf(DASH " "); \ - if (i < ramps->MEMBER.green_size) \ - printf("%" FORMAT " ", (TYPE)(ramps->MEMBER.green[i])); \ - else \ - printf(DASH " "); \ - if (i < ramps->MEMBER.blue_size) \ - printf("%" FORMAT "\n", (TYPE)(ramps->MEMBER.blue[i])); \ - else \ - printf(DASH "\n"); \ - } \ - break - X(LIBCOOPGAMMA_DOUBLE, d, double, "lf", "----"); - X(LIBCOOPGAMMA_FLOAT, f, double, "lf", "----"); - X(LIBCOOPGAMMA_UINT8, u8, uint8_t, "02" PRIx8, "--"); - X(LIBCOOPGAMMA_UINT16, u16, uint16_t, "04" PRIx16, "----"); - X(LIBCOOPGAMMA_UINT32, u32, uint32_t, "08" PRIx32, "--------"); - X(LIBCOOPGAMMA_UINT64, u64, uint64_t, "016" PRIx64, "----------------"); -#undef X - default: - errno = EPROTO; - goto fail; - } - - done: - saved_errno = errno; - libcoopgamma_filter_table_destroy(&table); - errno = saved_errno; - return ret; - fail: - ret = -1; - goto done; - cg_fail: - ret = -2; - goto done; - custom_fail: - ret = -3; - goto done; -} - - -/** - * -M METHOD - * Select adjustment method. If METHOD is "?", - * available methods will be printed to stdout. - * - * -S SITE - * Select site (display server instance). - * - * -c CRTC - * Select CRT controller. If CRTC is "?", CRTC:s - * will be printed to stdout. - * - * -h HIGH - * Suppress filter with higher priority than HIGH. - * - * -l LOW - * Suppress filter with lower priority than LOW. - * - * -f CLASS - * Print gamma ramps of the filter with class CLASS - * on the selected CRTC. If CLASS is "*" all filters - * with a priority in [LOW, HIGH] are coalesced. - * - * @param argc The number of command line arguments - * @param argv The command line arguments - * @return 0 on success, 1 on error - */ -int main(int argc, char* argv[]) -{ - int stage = 0, haveh = 0, havel = 0; - int rc = 0; - char* method = NULL; - char* site = NULL; - - query.high_priority = INT64_MAX; - query.low_priority = INT64_MIN; - query.crtc = NULL; - query.coalesce = 0; - - ARGBEGIN - { - case 'M': - if (method != NULL) - usage(); - method = EARGF(usage()); - break; - case 'S': - if (site != NULL) - usage(); - site = EARGF(usage()); - break; - case 'c': - if (query.crtc != NULL) - usage(); - query.crtc = EARGF(usage()); - break; - case 'h': - if (haveh++) - usage(); - query.high_priority = (int64_t)atoll(EARGF(usage())); - break; - case 'l': - if (havel++) - usage(); - query.low_priority = (int64_t)atoll(EARGF(usage())); - break; - case 'f': - if (class != NULL) - usage(); - class = EARGF(usage()); - if ((class[0] == '*') && (class[1] == '\0')) - query.coalesce = 1; - break; - default: - usage(); - } - ARGEND; - - if (argc) - usage(); - - if (initialise_proc() < 0) - goto fail; - - if ((method != NULL) && !strcmp(method, "?")) - { - if ((site != NULL) || (query.crtc != NULL)) - usage(); - if (list_methods() < 0) - goto fail; - return 0; - } - - if (libcoopgamma_context_initialise(&cg) < 0) - goto fail; - stage++; - if (libcoopgamma_connect(method, site, &cg) < 0) - { - fprintf(stderr, "%s: server failed to initialise\n", argv0); - goto custom_fail; - } - stage++; - - if (!(query.crtc)) - usage(); - - if (!strcmp(query.crtc, "?")) - switch (list_crtcs()) - { - case 0: - goto done; - case -1: - goto fail; - default: - goto cg_fail; - } - - switch (class ? print_filter() : print_info()) - { - case 0: - goto done; - case -1: - goto fail; - case -2: - goto cg_fail; - default: - goto custom_fail; - } - - fflush(stdout); - if (ferror(stdout)) - goto fail; - if (fclose(stdout) < 0) - goto fail; - - done: - if (stage >= 1) - libcoopgamma_context_destroy(&cg, stage >= 2); - return rc; - - custom_fail: - rc = 1; - goto done; - - fail: - rc = 1; - perror(argv0); - goto done; - - cg_fail: - rc = 1; - { - const char* side = cg.error.server_side ? "server" : "client"; - if (cg.error.custom) - { - if ((cg.error.number != 0) || (cg.error.description != NULL)) - fprintf(stderr, "%s: %s-side error number %" PRIu64 ": %s\n", - argv0, side, cg.error.number, cg.error.description); - else if (cg.error.number != 0) - fprintf(stderr, "%s: %s-side error number %" PRIu64 "\n", argv0, side, cg.error.number); - else if (cg.error.description != NULL) - fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, cg.error.description); - } - else if (cg.error.description != NULL) - fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, cg.error.description); - else - fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, strerror(cg.error.number)); - } - goto done; -} - diff --git a/src/cg-rainbow.c b/src/cg-rainbow.c deleted file mode 100644 index 067785f..0000000 --- a/src/cg-rainbow.c +++ /dev/null @@ -1,279 +0,0 @@ -/** - * cg-tools -- Cooperative gamma-enabled tools - * Copyright (C) 2016, 2018 Mattias Andrée (maandree@kth.se) - * - * 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 . - */ -#include "cg-base.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - - - -/** - * The default filter priority for the program - */ -const int64_t default_priority = (int64_t)1 << 60; - -/** - * The default class for the program - */ -char default_class[] = PKGNAME "::cg-rainbow::standard"; - -/** - * Class suffixes - */ -const char* const* class_suffixes = (const char* const[]){NULL}; - - - -/** - * -s: rainbow-frequency in Hz - */ -static char* sflag = NULL; - -/** - * -l: base luminosity - */ -static char* lflag = NULL; - -/** - * The rainbow-frequency multiplied by 3 - */ -double rainbows_per_third_second = 1; - -/** - * The base luminosity - */ -double luminosity = (double)1 / 3; - - - -/** - * Print usage information and exit - */ -void usage(void) -{ - fprintf(stderr, - "Usage: %s [-M method] [-S site] [-c crtc]... [-R rule] [-p priority]" - " [-l luminosity] [-s rainbowhz]\n", - argv0); - exit(1); -} - - -/** - * Handle a command line option - * - * @param opt The option, it is a NUL-terminate two-character - * string starting with either '-' or '+', if the - * argument is not recognised, call `usage`. This - * string will not be "-M", "-S", "-c", "-p", or "-R". - * @param arg The argument associated with `opt`, - * `NULL` there is no next argument, if this - * parameter is `NULL` but needed, call `usage` - * @return 0 if `arg` was not used, - * 1 if `arg` was used, - * -1 on error - */ -int handle_opt(char* opt, char* arg) -{ - if (opt[0] == '-') - switch (opt[1]) - { - case 'l': - if (lflag || !(lflag = arg)) - usage(); - return 1; - case 's': - if (sflag || !(sflag = arg)) - usage(); - return 1; - default: - usage(); - } - else - usage(); - return 0; -} - - -/** - * Parse a non-negative double encoded as a string - * - * @param out Output parameter for the value - * @param str The string - * @return Zero on success, -1 if the string is invalid - */ -static int parse_double(double* restrict out, const char* restrict str) -{ - char* end; - errno = 0; - *out = strtod(str, &end); - if (errno || (*out < 0) || isinf(*out) || isnan(*out) || *end) - return -1; - if (!*str || !strchr("0123456789.", *str)) - return -1; - return 0; -} - - -/** - * This function is called after the last - * call to `handle_opt` - * - * @param argc The number of unparsed arguments - * @param argv `NULL` terminated list of unparsed arguments - * @param prio The argument associated with the "-p" option - * @return Zero on success, -1 on error - */ -int handle_args(int argc, char* argv[], char* prio) -{ - int q = (lflag || sflag); - if ((q > 1) || argc) - usage(); - if (sflag != NULL) - { - if (parse_double(&rainbows_per_third_second, sflag) < 0) - usage(); - rainbows_per_third_second *= 3; - } - if (lflag != NULL) - { - if (parse_double(&luminosity, lflag) < 0) - usage(); - } - return 0; - (void) argv; - (void) prio; -} - - -/** - * Fill a filter - * - * @param filter The filter to fill - * @param red The red brightness - * @param green The green brightness - * @param blue The blue brightness - */ -static void fill_filter(libcoopgamma_filter_t* restrict filter, double red, double green, double blue) -{ - switch (filter->depth) - { -#define X(CONST, MEMBER, MAX, TYPE)\ - case CONST:\ - libclut_start_over(&(filter->ramps.MEMBER), MAX, TYPE, 1, 1, 1);\ - libclut_rgb_brightness(&(filter->ramps.MEMBER), MAX, TYPE, red, green, blue);\ - break; -LIST_DEPTHS -#undef X - default: - abort(); - } -} - - -/** - * Get the current monotonic time as a double - * - * @param now Output parameter for the current time (monotonic) - * @return Zero on success, -1 on error - */ -static int double_time(double* restrict now) -{ -#ifndef CLOCK_MONOTONIC_RAW -# define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC -#endif - struct timespec ts; - if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) < 0) - return -1; - *now = (double)(ts.tv_nsec); - *now /= 1000000000L; - *now += (double)(ts.tv_sec); - return 0; -} - - -/** - * The main function for the program-specific code - * - * @return 0: Success - * -1: Error, `errno` set - * -2: Error, `cg.error` set - * -3: Error, message already printed - */ -int start(void) -{ - int r; - size_t i, j; - double pal[3]; - double t, starttime; - - for (i = 0; i < filters_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_DEATH; - - if ((r = make_slaves()) < 0) - return r; - - if ((r = double_time(&starttime)) < 0) - return r; - - for (;;) - { - if ((r = double_time(&t)) < 0) - return r; - t -= starttime; - t *= rainbows_per_third_second; - pal[0] = pal[1] = pal[2] = luminosity; - pal[((long)t) % 3] += 1 - fmod(t, 1); - pal[((long)t + 1) % 3] += fmod(t, 1); - if (pal[0] > 1) pal[0] = 1; - if (pal[1] > 1) pal[1] = 1; - if (pal[2] > 1) pal[2] = 1; - - for (i = 0, r = 1; i < filters_n; i++) - { - if (!(crtc_updates[i].master) || !(crtc_info[crtc_updates[i].crtc].supported)) - continue; - fill_filter(&(crtc_updates[i].filter), pal[0], pal[1], pal[2]); - r = update_filter(i, 0); - if ((r == -2) || ((r == -1) && (errno != EAGAIN))) - return r; - if (crtc_updates[i].slaves != NULL) - for (j = 0; crtc_updates[i].slaves[j] != 0; j++) - { - r = update_filter(crtc_updates[i].slaves[j], 0); - if ((r == -2) || ((r == -1) && (errno != EAGAIN))) - return r; - } - } - - while (r != 1) - if ((r = synchronise(-1)) < 0) - return r; - - sched_yield(); - } -} - diff --git a/src/cg-remove.c b/src/cg-remove.c deleted file mode 100644 index d942f87..0000000 --- a/src/cg-remove.c +++ /dev/null @@ -1,403 +0,0 @@ -/** - * cg-tools -- Cooperative gamma-enabled tools - * Copyright (C) 2016, 2018 Mattias Andrée (maandree@kth.se) - * - * 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 . - */ -#include "arg.h" - -#include - -#include -#include -#include -#include -#include -#include -#include - - - -/** - * The libcoopgamma context - */ -static libcoopgamma_context_t cg; - - - -/** - * Print usage information and exit - */ -static void usage(void) -{ - fprintf(stderr, - "Usage: %s [-M method] [-S site] [-c crtc]... class...\n", - argv0); - exit(1); -} - - -/** - * Initialise the process, specifically - * reset the signal mask and signal handlers - * - * @return Zero on success, -1 on error - */ -static int initialise_proc(void) -{ - sigset_t sigmask; - int sig; - - for (sig = 1; sig < _NSIG; sig++) - if (signal(sig, SIG_DFL) == SIG_ERR) - if (sig == SIGCHLD) - return -1; - - if (sigemptyset(&sigmask) < 0) - return -1; - if (sigprocmask(SIG_SETMASK, &sigmask, NULL) < 0) - return -1; - - return 0; -} - - -/** - * Print, to stdout, a list of all - * recognised adjustment methods - * - * @return Zero on success, -1 on error - */ -static int list_methods(void) -{ - char** list; - size_t i; - - list = libcoopgamma_get_methods(); - if (list == NULL) - return -1; - for (i = 0; list[i]; i++) - printf("%s\n", list[i]); - free(list); - if (fflush(stdout) < 0) - return -1; - - return 0; -} - - -/** - * Print, to stdout, a list of all CRTC:s - * - * A connection to the coopgamma server - * must have been made - * - * @return Zero on success, -1 on error, -2 - * on libcoopgamma error - */ -static int list_crtcs(void) -{ - char** list; - size_t i; - - list = libcoopgamma_get_crtcs_sync(&cg); - if (list == NULL) - return -2; - for (i = 0; list[i]; i++) - printf("%s\n", list[i]); - free(list); - if (fflush(stdout) < 0) - return -1; - - return 0; -} - - -/** - * Remove selected filters from selected CRTC:s - * - * @param crtcs `NULL`-terminated list of CRTC names - * @param classes `NULL`-terminated list of filter classes - * @return Zero on success, -1 on error, -2 on - * libcoopgamma error - */ -static int remove_filters(char* const* restrict crtcs, char* const* restrict classes) -{ - size_t n = 0, unsynced = 0, selected, i, j; - char* synced = NULL; - libcoopgamma_async_context_t* asyncs = NULL; - int saved_errno, need_flush = 0, ret = 0; - struct pollfd pollfd; - libcoopgamma_filter_t command; - - for (i = 0; crtcs[i] != NULL; i++); - for (j = 0; classes[j] != NULL; j++); - synced = calloc(i, j * sizeof(*synced)); - if (synced == NULL) - goto fail; - asyncs = calloc(i, j * sizeof(*asyncs)); - if (asyncs == NULL) - goto fail; - - i = j = 0; - command.lifespan = LIBCOOPGAMMA_REMOVE; - pollfd.fd = cg.fd; - pollfd.events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI; - - while ((unsynced > 0) || (crtcs[i] != NULL)) - { - wait: - if (crtcs[i] != NULL) - pollfd.events |= POLLOUT; - else - pollfd.events &= ~POLLOUT; - - pollfd.revents = 0; - if (poll(&pollfd, (nfds_t)1, -1) < 0) - goto fail; - - if (pollfd.revents & (POLLOUT | POLLERR | POLLHUP | POLLNVAL)) - { - if (need_flush && (libcoopgamma_flush(&cg) < 0)) - goto send_fail; - need_flush = 0; - for (; crtcs[i] != NULL; i++, j = 0) - { - command.crtc = crtcs[i]; - while (classes[j] != NULL) - { - command.class = classes[j++]; - if (unsynced++, libcoopgamma_set_gamma_send(&command, &cg, asyncs + n++) < 0) - goto send_fail; - } - } - goto send_done; - send_fail: - switch (errno) - { - case EINTR: - case EAGAIN: -#if EAGAIN != EWOULDBLOCK - case EWOULDBLOCK: -#endif - need_flush = 1; - if (classes[j] == NULL) - i++, j = 0; - break; - default: - goto fail; - } - } - send_done: - - if ((unsynced == 0) && (crtcs[i] == NULL)) - break; - - if (pollfd.revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) - while (unsynced > 0) - switch (libcoopgamma_synchronise(&cg, asyncs, n, &selected)) - { - case 0: - if (synced[selected]) - { - libcoopgamma_skip_message(&cg); - break; - } - synced[selected] = 1; - unsynced -= 1; - if (libcoopgamma_set_gamma_recv(&cg, asyncs + selected) < 0) - goto cg_fail; - break; - default: - switch (errno) - { - case 0: - break; - case EINTR: - case EAGAIN: -#if EAGAIN != EWOULDBLOCK - case EWOULDBLOCK: -#endif - goto wait; - default: - goto fail; - } - break; - } - } - - done: - saved_errno = errno; - free(synced); - free(asyncs); - errno = saved_errno; - return ret; - fail: - ret = -1; - goto done; - cg_fail: - ret = -2; - goto done; -} - - -/** - * -M METHOD - * Select adjustment method. If METHOD is "?", - * available methods will be printed to stdout. - * - * -S SITE - * Select site (display server instance). - * - * -c CRTC - * Select CRT controller. If CRTC is "?", CRTC:s - * will be printed to stdout. - * - * Can be used multiple times. If not used, all - * CRTC:s are selected. - * - * @param argc The number of command line arguments - * @param argv The command line arguments - * @return 0 on success, 1 on error - */ -int main(int argc, char* argv[]) -{ - int stage = 0; - int rc = 0; - char* method = NULL; - char* site = NULL; - char** crtcs_ = NULL; - char** crtcs = alloca(argc * sizeof(char*)); - size_t i, crtcs_n = 0; - - ARGBEGIN - { - case 'M': - if (method != NULL) - usage(); - method = EARGF(usage()); - break; - case 'S': - if (site != NULL) - usage(); - site = EARGF(usage()); - break; - case 'c': - crtcs[crtcs_n++] = EARGF(usage()); - break; - default: - usage(); - } - ARGEND; - - if (initialise_proc() < 0) - goto fail; - - if ((method != NULL) && !strcmp(method, "?")) - { - if ((site != NULL) || (crtcs_n > 0) || (argc > 0)) - usage(); - if (list_methods() < 0) - goto fail; - return 0; - } - - if (libcoopgamma_context_initialise(&cg) < 0) - goto fail; - stage++; - if (libcoopgamma_connect(method, site, &cg) < 0) - { - fprintf(stderr, "%s: server failed to initialise\n", argv0); - goto custom_fail; - } - stage++; - - for (i = 0; i < crtcs_n; i++) - if (!strcmp(crtcs[i], "?")) - { - if (argc > 0) - usage(); - switch (list_crtcs()) - { - case 0: - goto done; - case -1: - goto fail; - default: - goto cg_fail; - } - } - - if (argc == 0) - usage(); - - if (crtcs_n == 0) - { - crtcs = crtcs_ = libcoopgamma_get_crtcs_sync(&cg); - if (crtcs == NULL) - goto cg_fail; - } - else - crtcs[crtcs_n] = NULL; - - if (libcoopgamma_set_nonblocking(&cg, 1) < 0) - goto fail; - - switch (remove_filters(crtcs, argv)) - { - case 0: - break; - case -1: - goto fail; - default: - goto cg_fail; - } - - done: - if (stage >= 1) - libcoopgamma_context_destroy(&cg, stage >= 2); - free(crtcs_); - return rc; - - custom_fail: - rc = 1; - goto done; - - fail: - rc = 1; - perror(argv0); - goto done; - - cg_fail: - rc = 1; - { - const char* side = cg.error.server_side ? "server" : "client"; - if (cg.error.custom) - { - if ((cg.error.number != 0) || (cg.error.description != NULL)) - fprintf(stderr, "%s: %s-side error number %" PRIu64 ": %s\n", - argv0, side, cg.error.number, cg.error.description); - else if (cg.error.number != 0) - fprintf(stderr, "%s: %s-side error number %" PRIu64 "\n", argv0, side, cg.error.number); - else if (cg.error.description != NULL) - fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, cg.error.description); - } - else if (cg.error.description != NULL) - fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, cg.error.description); - else - fprintf(stderr, "%s: %s-side error: %s\n", argv0, side, strerror(cg.error.number)); - } - goto done; -} - diff --git a/src/cg-shallow.c b/src/cg-shallow.c deleted file mode 100644 index 230da88..0000000 --- a/src/cg-shallow.c +++ /dev/null @@ -1,279 +0,0 @@ -/** - * cg-tools -- Cooperative gamma-enabled tools - * Copyright (C) 2016, 2018 Mattias Andrée (maandree@kth.se) - * - * 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 . - */ -#include "cg-base.h" - -#include - -#include -#include -#include -#include -#include -#include - - - -/** - * The default filter priority for the program - */ -const int64_t default_priority = -((int64_t)3 << 61); - -/** - * The default class for the program - */ -char default_class[] = PKGNAME "::cg-shallow::standard"; - -/** - * Class suffixes - */ -const char* const* class_suffixes = (const char* const[]){NULL}; - - - -/** - * -d: keep process alive and remove filter on death - */ -static int dflag = 0; - -/** - * -x: remove filter rather than adding it - */ -static int xflag = 0; - -/** - * The emulated red resolution, 0 for unchanged. - */ -static size_t rres = 2; - -/** - * The emulated green resolution, 0 for unchanged. - */ -static size_t gres = 2; - -/** - * The emulated blue resolution, 0 for unchanged. - */ -static size_t bres = 2; - - -/** - * Print usage information and exit - */ -void usage(void) -{ - fprintf(stderr, - "Usage: %s [-M method] [-S site] [-c crtc]... [-R rule] " - "(-x | [-p priority] [-d] [all | red green blue])\n", - argv0); - exit(1); -} - - -/** - * Handle a command line option - * - * @param opt The option, it is a NUL-terminate two-character - * string starting with either '-' or '+', if the - * argument is not recognised, call `usage`. This - * string will not be "-M", "-S", "-c", "-p", or "-R". - * @param arg The argument associated with `opt`, - * `NULL` there is no next argument, if this - * parameter is `NULL` but needed, call `usage` - * @return 0 if `arg` was not used, - * 1 if `arg` was used, - * -1 on error - */ -int handle_opt(char* opt, char* arg) -{ - if (opt[0] == '-') - switch (opt[1]) - { - case 'd': - if (dflag || xflag) - usage(); - dflag = 1; - break; - case 'x': - if (xflag || dflag) - usage(); - xflag = 1; - break; - default: - usage(); - } - else - usage(); - return 0; - (void) arg; -} - - -/** - * Parse a non-negative integer encoded as a string - * - * @param out Output parameter for the value - * @param str The string - * @return Zero on success, -1 if the string is invalid - */ -static int parse_int(size_t* restrict out, const char* restrict str) -{ - char* end; - errno = 0; - if (!isdigit(*str)) - return -1; - *out = strtoul(str, &end, 10); - if (errno || *end) - return -1; - return 0; -} - - -/** - * This function is called after the last - * call to `handle_opt` - * - * @param argc The number of unparsed arguments - * @param argv `NULL` terminated list of unparsed arguments - * @param prio The argument associated with the "-p" option - * @return Zero on success, -1 on error - */ -int handle_args(int argc, char* argv[], char* prio) -{ - char* red = NULL; - char* green = NULL; - char* blue = NULL; - int q = xflag + (dflag | (argc > 0)); - if ((q > 1) || (xflag && (prio != NULL))) - usage(); - if (argc == 1) - red = green = blue = argv[0]; - else if (argc == 3) - { - red = argv[0]; - green = argv[1]; - blue = argv[2]; - } - else if (argc && !xflag) - usage(); - if (argc) - { - if (parse_int(&rres, red) < 0) - usage(); - if (parse_int(&gres, blue) < 0) - usage(); - if (parse_int(&bres, green) < 0) - usage(); - } - return 0; - (void) argv; -} - - -/** - * Fill a filter - * - * @param filter The filter to fill - */ -static void fill_filter(libcoopgamma_filter_t* restrict filter) -{ - switch (filter->depth) - { -#define X(CONST, MEMBER, MAX, TYPE)\ - case CONST:\ - libclut_lower_resolution(&(filter->ramps.MEMBER), MAX, TYPE, 0, rres, 0, gres, 0, bres);\ - break; -LIST_DEPTHS -#undef X - default: - abort(); - } -} - - -/** - * The main function for the program-specific code - * - * @return 0: Success - * -1: Error, `errno` set - * -2: Error, `cg.error` set - * -3: Error, message already printed - */ -int start(void) -{ - int r; - size_t i, j; - - if (xflag) - for (i = 0; i < filters_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_REMOVE; - else if (dflag) - for (i = 0; i < filters_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_DEATH; - else - for (i = 0; i < filters_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_REMOVAL; - - if (!xflag) - if ((r = make_slaves()) < 0) - return r; - - for (i = 0, r = 1; i < filters_n; i++) - { - if (!(crtc_updates[i].master) || !(crtc_info[crtc_updates[i].crtc].supported)) - continue; - if (!xflag) - fill_filter(&(crtc_updates[i].filter)); - r = update_filter(i, 0); - if ((r == -2) || ((r == -1) && (errno != EAGAIN))) - return r; - if (crtc_updates[i].slaves != NULL) - for (j = 0; crtc_updates[i].slaves[j] != 0; j++) - { - r = update_filter(crtc_updates[i].slaves[j], 0); - if ((r == -2) || ((r == -1) && (errno != EAGAIN))) - return r; - } - } - - while (r != 1) - if ((r = synchronise(-1)) < 0) - return r; - - if (!dflag) - return 0; - - if (libcoopgamma_set_nonblocking(&cg, 0) < 0) - return -1; - for (;;) - if (libcoopgamma_synchronise(&cg, NULL, 0, &j) < 0) - switch (errno) - { - case 0: - break; - case ENOTRECOVERABLE: - goto enotrecoverable; - default: - return -1; - } - - enotrecoverable: - for (;;) - if (pause() < 0) - return -1; -} - diff --git a/src/cg-sleepmode.c b/src/cg-sleepmode.c deleted file mode 100644 index 3248d58..0000000 --- a/src/cg-sleepmode.c +++ /dev/null @@ -1,460 +0,0 @@ -/** - * cg-tools -- Cooperative gamma-enabled tools - * Copyright (C) 2016, 2018 Mattias Andrée (maandree@kth.se) - * - * 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 . - */ -#include "cg-base.h" - -#include - -#if defined(_GNU_SOURCE) -# undef _GNU_SOURCE -#endif -#include -#include -#include -#include -#include -#include -#include -#include - - - -/** - * The default filter priority for the program - */ -const int64_t default_priority = (int64_t)3 << 59; - -/** - * The default class for the program - */ -char default_class[] = PKGNAME "::cg-sleepmode::standard"; - -/** - * Class suffixes - */ -const char* const* class_suffixes = (const char* const[]){NULL}; - - - -/** - * -r: fade-out time for the red channel - */ -static char* rflag; - -/** - * -g: fade-out time for the green channel - */ -static char* gflag; - -/** - * -b: fade-out time for the blue channel - */ -static char* bflag; - -/** - * The duration, in seconds, of the red channel's fade out - */ -static double red_time = 3; - -/** - * The duration, in seconds, of the green channel's fade out - */ -static double green_time = 2; - -/** - * The duration, in seconds, of the blue channel's fade out - */ -static double blue_time = 1; - -/** - * The luminosity of red channel after the fade out - */ -static double red_target = 0.5; - -/** - * The luminosity of green channel after the fade out - */ -static double green_target = 0; - -/** - * The luminosity of blue channel after the fade out - */ -static double blue_target = 0; - -/** - * Time to fade in? - */ -static volatile sig_atomic_t received_int = 0; - - - -/** - * Print usage information and exit - */ -void usage(void) -{ - fprintf(stderr, - "Usage: %s [-M method] [-S site] [-c crtc]... [-R rule] [-p priority] " - "[-r red-fadeout-time] [-g green-fadeout-time] [-b blue-fadeout-time] " - "[red-luminosity [green-luminosity [blue-luminosity]]]\n", - argv0); - exit(1); -} - - -/** - * Called when a signal is received - * that tells the program to terminate - * - * @param signo The received signal - */ -static void sig_int(int signo) -{ - received_int = 1; - (void) signo; -} - - -/** - * Handle a command line option - * - * @param opt The option, it is a NUL-terminate two-character - * string starting with either '-' or '+', if the - * argument is not recognised, call `usage`. This - * string will not be "-M", "-S", "-c", "-p", or "-R". - * @param arg The argument associated with `opt`, - * `NULL` there is no next argument, if this - * parameter is `NULL` but needed, call `usage` - * @return 0 if `arg` was not used, - * 1 if `arg` was used, - * -1 on error - */ -int handle_opt(char* opt, char* arg) -{ - if (opt[0] == '-') - switch (opt[1]) - { - case 'r': - if (rflag || !(rflag = arg)) - usage(); - return 1; - case 'g': - if (gflag || !(gflag = arg)) - usage(); - return 1; - case 'b': - if (bflag || !(bflag = arg)) - usage(); - return 1; - default: - usage(); - } - else - usage(); - return 0; -} - - -/** - * Parse a non-negative double encoded as a string - * - * @param out Output parameter for the value - * @param str The string - * @return Zero on success, -1 if the string is invalid - */ -static int parse_double(double* restrict out, const char* restrict str) -{ - char* end; - errno = 0; - *out = strtod(str, &end); - if (errno || (*out < 0) || isinf(*out) || isnan(*out) || *end) - return -1; - if (!*str || !strchr("0123456789.", *str)) - return -1; - return 0; -} - - -/** - * This function is called after the last - * call to `handle_opt` - * - * @param argc The number of unparsed arguments - * @param argv `NULL` terminated list of unparsed arguments - * @param prio The argument associated with the "-p" option - * @return Zero on success, -1 on error - */ -int handle_args(int argc, char* argv[], char* prio) -{ - int q = (rflag || gflag || bflag || argc); - if ((q > 1) || (argc > 3)) - usage(); - if (rflag != NULL) - if (parse_double(&red_time, rflag) < 0) - usage(); - if (gflag != NULL) - if (parse_double(&green_time, gflag) < 0) - usage(); - if (bflag != NULL) - if (parse_double(&blue_time, bflag) < 0) - usage(); - if (argc >= 1) - if (parse_double(&red_target, argv[0]) < 0) - usage(); - if (argc >= 2) - if (parse_double(&green_target, argv[1]) < 0) - usage(); - if (argc >= 3) - if (parse_double(&blue_target, argv[2]) < 0) - usage(); - if (red_target >= 1) - red_time = 0; - if (green_target >= 1) - green_time = 0; - if (blue_target >= 1) - blue_time = 0; - return 0; - (void) prio; -} - - -/** - * Fill a filter - * - * @param filter The filter to fill - * @param red The red brightness - * @param green The green brightness - * @param blue The blue brightness - */ -static void fill_filter(libcoopgamma_filter_t* restrict filter, double red, double green, double blue) -{ - switch (filter->depth) - { -#define X(CONST, MEMBER, MAX, TYPE)\ - case CONST:\ - libclut_start_over(&(filter->ramps.MEMBER), MAX, TYPE, 1, 1, 1);\ - libclut_rgb_brightness(&(filter->ramps.MEMBER), MAX, TYPE, red, green, blue);\ - break; -LIST_DEPTHS -#undef X - default: - abort(); - } -} - - -/** - * Get the current monotonic time as a double - * - * @param now Output parameter for the current time (monotonic) - * @return Zero on success, -1 on error - */ -static int double_time(double* restrict now) -{ -#ifndef CLOCK_MONOTONIC_RAW -# define CLOCK_MONOTONIC_RAW CLOCK_MONOTONIC -#endif - struct timespec ts; - if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) < 0) - return -1; - *now = (double)(ts.tv_nsec); - *now /= 1000000000L; - *now += (double)(ts.tv_sec); - return 0; -} - - -/** - * The main function for the program-specific code - * - * @return 0: Success - * -1: Error, `errno` set - * -2: Error, `cg.error` set - * -3: Error, message already printed - */ -int start(void) -{ - int r, fade_red, fade_green, fade_blue; - size_t i, j; - double t, starttime, red, green, blue, redt, greent, bluet; - - redt = (red_target - 1) / red_time; - greent = (green_target - 1) / green_time; - bluet = (blue_target - 1) / blue_time; - fade_red = !isinf(redt) && !isnan(redt); - fade_green = !isinf(greent) && !isnan(greent); - fade_blue = !isinf(bluet) && !isnan(bluet); - - for (i = 0; i < filters_n; i++) - crtc_updates[i].filter.lifespan = LIBCOOPGAMMA_UNTIL_DEATH; - - if ((r = make_slaves()) < 0) - return r; - - if ((r = double_time(&starttime)) < 0) - return r; - - red = red_target < 0 ? 0 : red_target > 1 ? 1 : red_target; - green = green_target < 0 ? 0 : green_target > 1 ? 1 : green_target; - blue = blue_target < 0 ? 0 : blue_target > 1 ? 1 : blue_target; - - for (;;) - { - if ((r = double_time(&t)) < 0) - return r; - t -= starttime; - if (fade_red) - {if (red = 1 + t * redt, red > 1) red = 1; else if (red < 0) red = 0;} - if (fade_green) - {if (green = 1 + t * greent, green > 1) green = 1; else if (green < 0) green = 0;} - if (fade_blue) - {if (blue = 1 + t * bluet, blue > 1) blue = 1; else if (blue < 0) blue = 0;} - - for (i = 0, r = 1; i < filters_n; i++) - { - if (!(crtc_updates[i].master) || !(crtc_info[crtc_updates[i].crtc].supported)) - continue; - fill_filter(&(crtc_updates[i].filter), red, green, blue); - r = update_filter(i, 0); - if ((r == -2) || ((r == -1) && (errno != EAGAIN))) - return r; - if (crtc_updates[i].slaves != NULL) - for (j = 0; crtc_updates[i].slaves[j] != 0; j++) - { - r = update_filter(crtc_updates[i].slaves[j], 0); - if ((r == -2) || ((r == -1) && (errno != EAGAIN))) - return r; - } - } - - while (r != 1) - if ((r = synchronise(-1)) < 0) - return r; - - sched_yield(); - - if ((t >= red_time) && (t >= green_time) && (t >= blue_time)) - break; - } - - if ((signal(SIGINT, sig_int) == SIG_ERR) || - (signal(SIGTERM, sig_int) == SIG_ERR) || - (signal(SIGHUP, sig_int) == SIG_ERR)) - return -1; - - if (libcoopgamma_set_nonblocking(&cg, 0) < 0) - return -1; - for (;;) - if (libcoopgamma_synchronise(&cg, NULL, 0, &j) < 0) - { - if (received_int) - goto fade_in; - switch (errno) - { - case 0: - break; - case ENOTRECOVERABLE: - goto enotrecoverable; - default: - return 1; - } - } - - fade_in: - if (libcoopgamma_set_nonblocking(&cg, 1) < 0) - return -1; - - t = red_time; - t = t > green_time ? t : green_time; - t = t > blue_time ? t : blue_time; - redt = t - red_time; - greent = t - green_time; - bluet = t - blue_time; - t = red_time + green_time + blue_time; - if (red_time > 0) - t = t < red_time ? t : red_time; - if (green_time > 0) - t = t < green_time ? t : green_time; - if (blue_time > 0) - t = t < blue_time ? t : blue_time; - red_time = t + redt; - green_time = t + greent; - blue_time = t + bluet; - - red = green = blue = 1; - - if ((r = double_time(&starttime)) < 0) - return r; - - for (;;) - { - if ((r = double_time(&t)) < 0) - return r; - t -= starttime; - redt = t / red_time; - greent = t / green_time; - bluet = t / blue_time; - if (!isinf(redt) && !isnan(redt)) - { - red = red_target * (1 - redt) + redt; - if (red > 1) red = 1; else if (red < 0) red = 0; - } - if (!isinf(greent) && !isnan(greent)) - { - green = green_target * (1 - greent) + greent; - if (green > 1) green = 1; else if (green < 0) green = 0; - } - if (!isinf(bluet) && !isnan(bluet)) - { - blue = blue_target * (1 - bluet) + bluet; - if (blue > 1) blue = 1; else if (blue < 0) blue = 0; - } - - for (i = 0, r = 1; i < filters_n; i++) - { - if (!(crtc_updates[i].master) || !(crtc_info[crtc_updates[i].crtc].supported)) - continue; - fill_filter(&(crtc_updates[i].filter), red, green, blue); - r = update_filter(i, 0); - if ((r == -2) || ((r == -1) && (errno != EAGAIN))) - return r; - if (crtc_updates[i].slaves != NULL) - for (j = 0; crtc_updates[i].slaves[j] != 0; j++) - { - r = update_filter(crtc_updates[i].slaves[j], 0); - if ((r == -2) || ((r == -1) && (errno != EAGAIN))) - return r; - } - } - - while (r != 1) - if ((r = synchronise(-1)) < 0) - return r; - - sched_yield(); - - if ((t >= red_time) && (t >= green_time) && (t >= blue_time)) - break; - } - - return 0; - enotrecoverable: - for (;;) - if (pause() < 0) - return -1; -} - -- cgit v1.2.3-70-g09d2