[
  {
    "path": ".github/FUNDING.yml",
    "content": "ko_fi: felixkratz\n"
  },
  {
    "path": ".gitignore",
    "content": "test\nbin\ntodo.md\nnotifications.md\n.ccls-cache\n.DS_Store\n.ccls\n.cache\n.cmake\nCMakeCache.txt\nCMakeFiles/\ncmake_install.cmake\ncompile_commands.json\n"
  },
  {
    "path": "LICENSE.md",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n  <img src=\"images/Sbar.svg\" />\n</p>\n\n<p align=\"center\">\n<a href=\"https://felixkratz.github.io/SketchyBar/setup\">Install</a>\n<span> • </span>\n<a href=\"https://felixkratz.github.io/SketchyBar/config/bar\">Documentation</a> \n<span> • </span> \n<a href=\"https://github.com/FelixKratz/SketchyBar/discussions/47?sort=top\">Setups</a>\n<span> • </span> \n<a href=\"https://github.com/FelixKratz/SketchyBar/discussions/12?sort=top\">Plugins</a>\n</p>\n\n<p align=\"center\">\n<a href=\"https://opensource.org/licenses/\"><img src=\"https://img.shields.io/badge/License-GPL%20v3-blue.svg\"></a>\n<a href=\"https://github.com/FelixKratz/SketchyBar/releases\"><img src=\"https://img.shields.io/github/v/release/FelixKratz/SketchyBar.svg?style=flat&color=orange\" /></a>\n<a href=\"https://github.com/FelixKratz/SketchyBar/releases\"><img src=\"https://img.shields.io/github/commits-since/FelixKratz/SketchyBar/latest.svg?color=orange\"></a>\n<a href=\"https://en.wikipedia.org/wiki/Free_and_open-source_software\"><img src=\"https://img.shields.io/badge/FOSS-100%25-green.svg?style=flat\"></a>\n</p>\n\n<p align=\"center\">\nThis bar project aims to create a highly flexible, customizable, fast and powerful status bar replacement for people that like playing with\nshell scripts.\n</p>\n\n![](images/example.png)\n<p align=\"center\">\n<a href=\"https://github.com/FelixKratz/SketchyBar/discussions/47?sort=top\">More Setups</a>\n</p>\n\n\n\n## Features\n* Full *configurability* at any time\n* Dynamic *animation* system\n* Powerful *scripting* and *event* system\n* Optimized to be *fast* and *efficient*\n* Interactive *mouse* support\n* Support for displaying macOS menu bar apps (*aliases*)\n* Can draw arbitrary *graphs*\n* On-demand *popup* menus\n\nThe main design principle of this project is that *all* elements of the bar can\nbe added, removed and freely changed at any point in time. Thus, the\nconfiguration of the bar is not *static*, rather it is possible to adapt the\nappearance of the bar completely dynamically with the help of a powerful\nevent-driven scripting system at any point in time using the highly\nconfigurable basic building blocks SketchyBar offers.\n\n## Getting Started\nRefer to the installation guide in the [documentation](https://felixkratz.github.io/SketchyBar/setup) to get the program set up.\nOnce this is sorted you can start to become familiar with the syntax of sketchybar by going through the default [*sketchybarrc*](https://github.com/FelixKratz/SketchyBar/blob/master/sketchybarrc) file and the default [*plugin scripts*](https://github.com/FelixKratz/SketchyBar/blob/master/plugins),\nwhich are located in `~/.config/sketchybar/` and look like this:\n\n![](images/default.png)\n\nAll commands and options are explained in detail in the relevant sections\nof the configuration [documentation](https://felixkratz.github.io/SketchyBar/config/bar). You can try the commands directly from\nthe commandline to see which affect they have and how they alter the bar. Once you have become familiar with the syntax you can\nlook for a config to start from [here](https://github.com/FelixKratz/SketchyBar/discussions/47?sort=top) or start from scratch and customize\neverything to your liking.\n\nYou might also enjoy looking at the [Tips & Tricks](https://felixkratz.github.io/SketchyBar/config/tricks) section\nfor some further tips on your journey. If you are searching for functional items you might want to check the\n[plugins](https://github.com/FelixKratz/SketchyBar/discussions/12?sort=top) section if someone has already created what you are looking for.\n\nShould you encounter things not working as you expect them to, please *do not* hesitate to open an [issue](https://github.com/FelixKratz/SketchyBar/issues), as\nthis is either a bug or a documentation problem and relevant in any case.\n\n## Documentation\nFor the full documentation of all commands and properties please refer to the [website](https://felixkratz.github.io/SketchyBar/config/bar).\n\nIf questions remain, feel free to consult the [Q&A](https://github.com/FelixKratz/SketchyBar/discussions/categories/q-a) section.\n\n## Supporting\n*You* can support this project is many ways:\n- By *creating* issues and pull-requests if you encounter problems\n- By *sharing* your [plugins](https://github.com/FelixKratz/SketchyBar/discussions/12) and [setups](https://github.com/FelixKratz/SketchyBar/discussions/47)\n- By *starring* the project on GitHub\n- If this project has value to you, consider quantifying it and *donating* to a charity of your choice. If you want to let me know about your donation, you\ncan contact me via [email](mailto:felix.kratz@tu-dortmund.de?Subject=Donation).\n- If you want to support me directly, you can do so via [ko-fi](https://ko-fi.com/felixkratz)\n\n## Credits\nThis project was forked from *[spacebar](https://github.com/cmacrae/spacebar)* and completely reimagined and rewritten. <br>\nThe original idea is based on the status bar that was included in *[yabai](https://github.com/koekeishiya/yabai)* before getting removed.\n\n\n## Related Projects\n- [SbarLua](https://github.com/FelixKratz/SbarLua): A Lua API for SketchyBar\n- [sketchybar-app-font](https://github.com/kvndrsslr/sketchybar-app-font): A symbol font for SketchyBar\n- [SketchyBarHelper](https://github.com/FelixKratz/SketchyBarHelper): A header for C/C++ to directly communicate with SketchyBar\n\n## Some animation examples\n\nhttps://user-images.githubusercontent.com/22680421/211198711-45318f04-e96f-4aa1-a0ba-c7f30f050902.mp4\n\n\n"
  },
  {
    "path": "makefile",
    "content": "CFLAGS   = -std=c99 -Wall -O3 -ffast-math -fvisibility=hidden -fno-common\n\nLIBS     = -framework Carbon \\\n\t\t\t\t\t -framework AppKit \\\n\t\t\t\t\t -framework CoreAudio \\\n\t\t\t\t\t -framework CoreWLAN \\\n\t\t\t\t\t -framework CoreVideo \\\n\t\t\t\t\t -framework IOKit \\\n\t\t\t\t\t -F/System/Library/PrivateFrameworks \\\n\t\t\t\t\t -framework SkyLight \\\n\t\t\t\t\t -framework DisplayServices \\\n\t\t\t\t\t -framework MediaRemote\n\nODIR     = bin\nSRC      = src\n\n_OBJ = alias.o background.o bar_item.o custom_events.o event.o graph.o \\\n\t\t\t image.o mouse.o shadow.o font.o text.o message.o mouse.o bar.o color.o \\\n\t\t\t window.o bar_manager.o display.o group.o mach.o popup.o \\\n\t\t\t animation.o workspace.om volume.o slider.o power.o wifi.om media.om \\\n\t\t\t hotload.o app_windows.o\n\nOBJ  = $(patsubst %, $(ODIR)/%, $(_OBJ))\n\n.PHONY: all clean arm x86 profile leak universal\n\nall: clean universal\n\nleak: CFLAGS=-std=c99 -Wall -g\nleak: clean arm64\nleak:\n\t/usr/libexec/PlistBuddy -c \"Add :com.apple.security.get-task-allow bool true\" bin/tmp.entitlements\n\tcodesign -s - --entitlements bin/tmp.entitlements -f ./bin/sketchybar\n\tleaks -atExit -- ./bin/sketchybar\n\nx86: CFLAGS+=-target x86_64-apple-macos10.13\nx86: $(ODIR)/sketchybar\n\narm64: CFLAGS+=-target arm64-apple-macos11\narm64: $(ODIR)/sketchybar\n\nuniversal:\n\t$(MAKE) x86\n\tmv $(ODIR)/sketchybar $(ODIR)/sketchybar_x86\n\trm -rf $(ODIR)/*.o*\n\t$(MAKE) arm64\n\tmv $(ODIR)/sketchybar $(ODIR)/sketchybar_arm64\n\tlipo -create -output $(ODIR)/sketchybar $(ODIR)/sketchybar_x86 $(ODIR)/sketchybar_arm64\n\ndebug: CFLAGS=-std=c99 -Wall -g\ndebug: arm64\n\nasan: CFLAGS=-std=c99 -Wall -g -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer\nasan: clean arm64\n\t./bin/sketchybar\n\n$(ODIR)/sketchybar: $(SRC)/sketchybar.c $(OBJ) | $(ODIR)\n\t$(CC) $(CFLAGS) $^ -o $@ $(LIBS)\n\n$(ODIR)/%.o: $(SRC)/%.c $(SRC)/%.h | $(ODIR)\n\t$(CC) -c -o $@ $< $(CFLAGS)\n\n$(ODIR)/%.om: $(SRC)/%.m $(SRC)/%.h | $(ODIR)\n\t$(CC) -c -o $@ $< $(CFLAGS)\n\n$(ODIR):\n\tmkdir $(ODIR)\n\nclean:\n\trm -rf $(ODIR)\n"
  },
  {
    "path": "plugins/battery.sh",
    "content": "#!/bin/sh\n\nPERCENTAGE=\"$(pmset -g batt | grep -Eo \"\\d+%\" | cut -d% -f1)\"\nCHARGING=\"$(pmset -g batt | grep 'AC Power')\"\n\nif [ \"$PERCENTAGE\" = \"\" ]; then\n  exit 0\nfi\n\ncase \"${PERCENTAGE}\" in\n  9[0-9]|100) ICON=\"\"\n  ;;\n  [6-8][0-9]) ICON=\"\"\n  ;;\n  [3-5][0-9]) ICON=\"\"\n  ;;\n  [1-2][0-9]) ICON=\"\"\n  ;;\n  *) ICON=\"\"\nesac\n\nif [[ \"$CHARGING\" != \"\" ]]; then\n  ICON=\"\"\nfi\n\n# The item invoking this script (name $NAME) will get its icon and label\n# updated with the current battery status\nsketchybar --set \"$NAME\" icon=\"$ICON\" label=\"${PERCENTAGE}%\"\n"
  },
  {
    "path": "plugins/clock.sh",
    "content": "#!/bin/sh\n\n# The $NAME variable is passed from sketchybar and holds the name of\n# the item invoking this script:\n# https://felixkratz.github.io/SketchyBar/config/events#events-and-scripting\n\nsketchybar --set \"$NAME\" label=\"$(date '+%d/%m %H:%M')\"\n\n"
  },
  {
    "path": "plugins/front_app.sh",
    "content": "#!/bin/sh\n\n# Some events send additional information specific to the event in the $INFO\n# variable. E.g. the front_app_switched event sends the name of the newly\n# focused application in the $INFO variable:\n# https://felixkratz.github.io/SketchyBar/config/events#events-and-scripting\n\nif [ \"$SENDER\" = \"front_app_switched\" ]; then\n  sketchybar --set \"$NAME\" label=\"$INFO\"\nfi\n"
  },
  {
    "path": "plugins/space.sh",
    "content": "#!/bin/sh\n\n# The $SELECTED variable is available for space components and indicates if\n# the space invoking this script (with name: $NAME) is currently selected:\n# https://felixkratz.github.io/SketchyBar/config/components#space----associate-mission-control-spaces-with-an-item\n\nsketchybar --set \"$NAME\" background.drawing=\"$SELECTED\"\n"
  },
  {
    "path": "plugins/volume.sh",
    "content": "#!/bin/sh\n\n# The volume_change event supplies a $INFO variable in which the current volume\n# percentage is passed to the script.\n\nif [ \"$SENDER\" = \"volume_change\" ]; then\n  VOLUME=\"$INFO\"\n\n  case \"$VOLUME\" in\n    [6-9][0-9]|100) ICON=\"󰕾\"\n    ;;\n    [3-5][0-9]) ICON=\"󰖀\"\n    ;;\n    [1-9]|[1-2][0-9]) ICON=\"󰕿\"\n    ;;\n    *) ICON=\"󰖁\"\n  esac\n\n  sketchybar --set \"$NAME\" icon=\"$ICON\" label=\"$VOLUME%\"\nfi\n"
  },
  {
    "path": "sketchybarrc",
    "content": "# This is a demo config to showcase some of the most important commands.\n# It is meant to be changed and configured, as it is intentionally kept sparse.\n# For a (much) more advanced configuration example see my dotfiles:\n# https://github.com/FelixKratz/dotfiles\n\nPLUGIN_DIR=\"$CONFIG_DIR/plugins\"\n\n##### Bar Appearance #####\n# Configuring the general appearance of the bar.\n# These are only some of the options available. For all options see:\n# https://felixkratz.github.io/SketchyBar/config/bar\n# If you are looking for other colors, see the color picker:\n# https://felixkratz.github.io/SketchyBar/config/tricks#color-picker\n\nsketchybar --bar position=top height=40 blur_radius=30 color=0x40000000\n\n##### Changing Defaults #####\n# We now change some default values, which are applied to all further items.\n# For a full list of all available item properties see:\n# https://felixkratz.github.io/SketchyBar/config/items\n\ndefault=(\n  padding_left=5\n  padding_right=5\n  icon.font=\"Hack Nerd Font:Bold:17.0\"\n  label.font=\"Hack Nerd Font:Bold:14.0\"\n  icon.color=0xffffffff\n  label.color=0xffffffff\n  icon.padding_left=4\n  icon.padding_right=4\n  label.padding_left=4\n  label.padding_right=4\n)\nsketchybar --default \"${default[@]}\"\n\n##### Adding Mission Control Space Indicators #####\n# Let's add some mission control spaces:\n# https://felixkratz.github.io/SketchyBar/config/components#space----associate-mission-control-spaces-with-an-item\n# to indicate active and available mission control spaces.\n\nSPACE_ICONS=(\"1\" \"2\" \"3\" \"4\" \"5\" \"6\" \"7\" \"8\" \"9\" \"10\")\nfor i in \"${!SPACE_ICONS[@]}\"\ndo\n  sid=\"$(($i+1))\"\n  space=(\n    space=\"$sid\"\n    icon=\"${SPACE_ICONS[i]}\"\n    icon.padding_left=7\n    icon.padding_right=7\n    background.color=0x40ffffff\n    background.corner_radius=5\n    background.height=25\n    label.drawing=off\n    script=\"$PLUGIN_DIR/space.sh\"\n    click_script=\"yabai -m space --focus $sid\"\n  )\n  sketchybar --add space space.\"$sid\" left --set space.\"$sid\" \"${space[@]}\"\ndone\n\n##### Adding Left Items #####\n# We add some regular items to the left side of the bar, where\n# only the properties deviating from the current defaults need to be set\n\nsketchybar --add item chevron left \\\n           --set chevron icon= label.drawing=off \\\n           --add item front_app left \\\n           --set front_app icon.drawing=off script=\"$PLUGIN_DIR/front_app.sh\" \\\n           --subscribe front_app front_app_switched\n\n##### Adding Right Items #####\n# In the same way as the left items we can add items to the right side.\n# Additional position (e.g. center) are available, see:\n# https://felixkratz.github.io/SketchyBar/config/items#adding-items-to-sketchybar\n\n# Some items refresh on a fixed cycle, e.g. the clock runs its script once\n# every 10s. Other items respond to events they subscribe to, e.g. the\n# volume.sh script is only executed once an actual change in system audio\n# volume is registered. More info about the event system can be found here:\n# https://felixkratz.github.io/SketchyBar/config/events\n\nsketchybar --add item clock right \\\n           --set clock update_freq=10 icon=  script=\"$PLUGIN_DIR/clock.sh\" \\\n           --add item volume right \\\n           --set volume script=\"$PLUGIN_DIR/volume.sh\" \\\n           --subscribe volume volume_change \\\n           --add item battery right \\\n           --set battery update_freq=120 script=\"$PLUGIN_DIR/battery.sh\" \\\n           --subscribe battery system_woke power_source_change\n\n##### Force all scripts to run the first time (never do this in a script) #####\nsketchybar --update\n"
  },
  {
    "path": "src/alias.c",
    "content": "#include \"alias.h\"\n#include \"misc/helpers.h\"\n#include <CoreFoundation/CFBase.h>\n#include <CoreFoundation/CoreFoundation.h>\n\nvoid print_all_menu_items(FILE* rsp) {\n#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 110000\n  if (__builtin_available(macOS 11.0, *)) {\n    if (!CGRequestScreenCaptureAccess()) {\n      respond(rsp, \"[!] Query (default_menu_items): Screen Recording \"\n                   \"Permissions not given. Restart SketchyBar after granting \"\n                   \"permissions.\\n\");\n      return;\n    }\n  }\n\n#endif\n  CFArrayRef window_list = CGWindowListCopyWindowInfo(kCGWindowListOptionAll,\n                                                      kCGNullWindowID        );\n  int window_count = CFArrayGetCount(window_list);\n\n  float x_pos[window_count];\n  char* owner[window_count];\n  char* name[window_count];\n  memset(owner, 0, sizeof(owner));\n  memset(name, 0, sizeof(name));\n\n  int item_count = 0;\n  for (int i = 0; i < window_count; ++i) {\n    x_pos[i] = -9999.f;\n    CFDictionaryRef dictionary = CFArrayGetValueAtIndex(window_list, i);\n    if (!dictionary) continue;\n\n    CFStringRef owner_ref = CFDictionaryGetValue(dictionary,\n                                                 kCGWindowOwnerName);\n\n    CFNumberRef owner_pid_ref = CFDictionaryGetValue(dictionary,\n                                                     kCGWindowOwnerPID);\n\n    CFStringRef name_ref = CFDictionaryGetValue(dictionary, kCGWindowName);\n    CFNumberRef layer_ref = CFDictionaryGetValue(dictionary, kCGWindowLayer);\n    CFDictionaryRef bounds_ref = CFDictionaryGetValue(dictionary,\n                                                      kCGWindowBounds);\n\n    if (!name_ref || !owner_ref || !owner_pid_ref || !layer_ref || !bounds_ref)\n      continue;\n\n    long long int layer = 0;\n    CFNumberGetValue(layer_ref, CFNumberGetType(layer_ref), &layer);\n    uint64_t owner_pid = 0;\n    CFNumberGetValue(owner_pid_ref,\n                     CFNumberGetType(owner_pid_ref),\n                     &owner_pid                     );\n\n    if (layer != MENUBAR_LAYER) continue;\n    CGRect bounds = CGRectNull;\n    if (!CGRectMakeWithDictionaryRepresentation(bounds_ref, &bounds)) continue;\n    char* owner_copy = cfstring_copy(owner_ref);\n    if (string_equals(owner_copy, \"Window Server\")) {\n      free(owner_copy);\n      continue;\n    }\n    owner[item_count] = owner_copy;\n    name[item_count] = cfstring_copy(name_ref);\n    x_pos[item_count++] = bounds.origin.x;\n  }\n\n  if (item_count > 0) {\n    fprintf(rsp, \"[\\n\");\n    int counter = 0;\n    for (int i = 0; i < item_count; i++) {\n      float current_pos = x_pos[0];\n      uint32_t current_pos_id = 0;\n      for (int j = 0; j < window_count; j++) {\n        if (!name[j] || !owner[j]) continue;\n        if (x_pos[j] > current_pos) {\n          current_pos = x_pos[j];\n          current_pos_id = j;\n        }\n      }\n\n      if (!name[current_pos_id] || !owner[current_pos_id]) continue;\n      if (strcmp(name[current_pos_id], \"\") != 0) {\n        if (counter++ > 0) {\n          fprintf(rsp, \", \\n\");\n        }\n        fprintf(rsp, \"\\t\\\"%s,%s\\\"\", owner[current_pos_id],\n                                    name[current_pos_id]  );\n      }\n      x_pos[current_pos_id] = -9999.f;\n    }\n    fprintf(rsp, \"\\n]\\n\");\n    for (int i = 0; i < window_count; i++) {\n      if (owner[i]) free(owner[i]);\n      if (name[i]) free(name[i]);\n    }\n  }\n  CFRelease(window_list);\n}\n\nvoid alias_get_permission(struct alias* alias) { \n#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 110000 \n  if (__builtin_available(macOS 11.0, *)) {\n    alias->permission = CGRequestScreenCaptureAccess();\n  }\n#else\n  if(__builtin_available(macos 10.15, *)) {\n    CGImageRef img = CGWindowListCreateImage(CGRectMake(0, 0, 1, 1),\n                                             kCGWindowListOptionOnScreenOnly,\n                                             kCGNullWindowID,\n                                             kCGWindowImageDefault           );\n\n    CFRelease(img);\n  }\n#endif\n}\n\nvoid alias_init(struct alias* alias) {\n  alias->name = NULL;\n  alias->owner = NULL;\n  alias->color_override = false;\n  color_init(&alias->color, 0xffff0000);\n  alias->update_frequency = 1;\n  alias->counter = 0;\n\n  window_init(&alias->window);\n  image_init(&alias->image);\n}\n\nstatic void alias_find_window(struct alias* alias) {\n  CFArrayRef window_list = CGWindowListCopyWindowInfo(kCGWindowListOptionAll,\n                                                      kCGNullWindowID        );\n  int window_count = CFArrayGetCount(window_list);\n\n  for (int i = 0; i < window_count; ++i) {\n    CFDictionaryRef dictionary = CFArrayGetValueAtIndex(window_list, i);\n    if (!dictionary) continue;\n\n    CFStringRef owner_ref = CFDictionaryGetValue(dictionary,\n                                                 kCGWindowOwnerName);\n\n    CFNumberRef owner_pid_ref = CFDictionaryGetValue(dictionary,\n                                                     kCGWindowOwnerPID);\n\n    CFStringRef name_ref = CFDictionaryGetValue(dictionary, kCGWindowName);\n    if (!name_ref) continue;\n    if (!owner_ref) continue;\n    char* owner = cfstring_copy(owner_ref);\n    char* name = cfstring_copy(name_ref);\n\n    if (!(alias->owner && strcmp(alias->owner, owner) == 0\n          && ((alias->name && strcmp(alias->name, name) == 0)\n              || (!alias->name && strcmp(name, \"\") != 0)     ))) {\n      free(owner);\n      free(name);\n      continue;\n    }\n    free(owner);\n    free(name);\n\n    CFNumberRef layer_ref = CFDictionaryGetValue(dictionary, kCGWindowLayer);\n    if (!layer_ref) continue;\n\n    uint64_t layer = 0;\n    CFNumberGetValue(layer_ref, CFNumberGetType(layer_ref), &layer);\n    if (layer != MENUBAR_LAYER) continue;\n\n    CFNumberGetValue(owner_pid_ref,\n                     CFNumberGetType(owner_pid_ref),\n                     &alias->pid                    );\n\n    CFNumberRef window_id_ref = CFDictionaryGetValue(dictionary,\n                                                     kCGWindowNumber);\n\n    if (!window_id_ref) continue;\n    CFDictionaryRef bounds_ref = CFDictionaryGetValue(dictionary, kCGWindowBounds);\n    if (!bounds_ref) continue;\n\n    CGRect bounds;\n    CGRectMakeWithDictionaryRepresentation(bounds_ref, &bounds);\n\n    uint64_t wid;\n    CFNumberGetValue(window_id_ref,\n                     CFNumberGetType(window_id_ref),\n                     &wid                           );\n\n    alias->window.id = (uint32_t)wid;\n    alias->window.frame.size = bounds.size;\n    alias->window.origin = bounds.origin;\n\n    CFRelease(window_list);\n    return;\n  }\n  alias->window.id = 0;\n  CFRelease(window_list);\n}\n\nstatic bool alias_update_image(struct alias* alias, bool forced) {\n  if (alias->window.id == 0) alias_find_window(alias);\n  if (alias->window.id == 0) return false;\n\n  bool disabled = false;\n  CGImageRef image_ref = window_capture(&alias->window, &disabled);\n\n  if (!image_ref) {\n    if (!disabled) {\n      alias->window.id = 0;\n      image_destroy(&alias->image);\n    }\n    return false;\n  }\n\n  return image_set_image(&alias->image,\n                         image_ref,\n                         alias->window.frame,\n                         forced              );\n}\n\nvoid alias_setup(struct alias* alias, char* owner, char* name) {\n  alias->name = name;\n  alias->owner = owner;\n  alias_get_permission(alias);\n  alias_update_image(alias, true);\n}\n\nuint32_t alias_get_length(struct alias* alias) {\n  if (alias->image.image_ref) return alias->image.bounds.size.width;\n  return 0;\n}\n\nuint32_t alias_get_height(struct alias* alias) {\n  if (alias->image.image_ref) return alias->image.bounds.size.height;\n  return 0;\n}\n\nbool alias_update(struct alias* alias, bool forced) {\n  if (alias->update_frequency == 0) return false;\n\n  alias->counter++;\n  if (forced || alias->counter >= alias->update_frequency) {\n    alias->counter = 0;\n    if (alias_update_image(alias, forced)) {\n      return true;\n    }\n  }\n  return false;\n}\n\nvoid alias_draw(struct alias* alias, CGContextRef context) {\n  if (alias->color_override) {\n    CGContextSaveGState(context);\n    image_draw(&alias->image, context);\n    CGContextClipToMask(context, alias->image.bounds, alias->image.image_ref);\n    CGContextSetRGBFillColor(context,\n                             alias->color.r,\n                             alias->color.g,\n                             alias->color.b,\n                             alias->color.a );\n\n    CGContextFillRect(context, alias->image.bounds);\n    CGContextRestoreGState(context);\n  }\n  else {\n    image_draw(&alias->image, context);\n  }\n}\n\nvoid alias_destroy(struct alias* alias) {\n  image_destroy(&alias->image);\n  if (alias->name) free(alias->name);\n  if (alias->owner) free(alias->owner);\n  alias->name = NULL;\n  alias->owner = NULL;\n}\n\nvoid alias_calculate_bounds(struct alias* alias, uint32_t x, uint32_t y) {\n  image_calculate_bounds(&alias->image, x, y);\n}\n\nbool alias_parse_sub_domain(struct alias* alias, FILE* rsp, struct token property, char* message) {\n  struct key_value_pair key_value_pair = get_key_value_pair(property.text,'.');\n  if (key_value_pair.key && key_value_pair.value) {\n    struct token subdom = { key_value_pair.key, strlen(key_value_pair.key) };\n    struct token entry = { key_value_pair.value, strlen(key_value_pair.value)};\n    if (token_equals(subdom, SUB_DOMAIN_SHADOW))\n      return shadow_parse_sub_domain(&alias->image.shadow,\n                                     rsp,\n                                     entry,\n                                     message              );\n    else if (token_equals(subdom, SUB_DOMAIN_COLOR)) {\n      bool changed = !alias->color_override;\n      alias->color_override = true;\n      return color_parse_sub_domain(&alias->color, rsp, entry, message)\n            || changed;\n    }\n    else {\n      respond(rsp, \"[!] Alias: Invalid subdomain '%s'\\n\", subdom.text);\n    }\n  }\n  else if (token_equals(property, PROPERTY_COLOR)) {\n    color_set_hex(&alias->color, token_to_uint32t(get_token(&message)));\n    alias->color_override = true;\n    return true;\n  } else if (token_equals(property, PROPERTY_SCALE)) {\n    return image_set_scale(&alias->image, token_to_float(get_token(&message)));\n  } else if (token_equals(property, PROPERTY_UPDATE_FREQ)) {\n    alias->update_frequency = token_to_uint32t(get_token(&message));\n    return false;\n  } else {\n    respond(rsp, \"[!] Alias: Invalid property '%s' \\n\", property.text);\n  }\n  return false;\n}\n"
  },
  {
    "path": "src/alias.h",
    "content": "#pragma once\n#include <stdbool.h>\n#include \"misc/helpers.h\"\n#include \"window.h\"\n#include \"image.h\"\n\n#define MENUBAR_LAYER 0x19\n\nstruct alias {\n  bool permission;\n  uint32_t update_frequency;\n  uint32_t counter;\n\n  char* name;\n  char* owner;\n\n  pid_t pid;\n\n  struct window window;\n\n  bool color_override;\n  struct color color;\n  struct image image;\n};\n\nvoid alias_init(struct alias* alias);\nvoid alias_setup(struct alias* alias, char* owner, char* name);\nuint32_t alias_get_length(struct alias* alias);\nuint32_t alias_get_height(struct alias* alias);\n\nvoid alias_calculate_bounds(struct alias* alias, uint32_t x, uint32_t y);\nvoid alias_draw(struct alias* alias, CGContextRef context);\nbool alias_update(struct alias* alias, bool forced);\nvoid alias_destroy(struct alias* alias);\n\nvoid print_all_menu_items(FILE* rsp);\n\nbool alias_parse_sub_domain(struct alias* alias, FILE* rsp, struct token property, char* message);\n"
  },
  {
    "path": "src/animation.c",
    "content": "#include \"animation.h\"\n#include \"event.h\"\n\nstatic CVReturn animation_frame_callback(CVDisplayLinkRef display_link, const CVTimeStamp* now, const CVTimeStamp* output_time, CVOptionFlags flags, CVOptionFlags* flags_out, void* context) {\n  uint64_t hostTime = output_time->hostTime;\n  dispatch_async(dispatch_get_main_queue(), ^{\n    struct event event = { (void*)hostTime, ANIMATOR_REFRESH };\n    event_post(&event);\n  });\n  return kCVReturnSuccess;\n}\n\nstruct animation* animation_create() {\n  struct animation* animation = malloc(sizeof(struct animation));\n  memset(animation, 0, sizeof(struct animation));\n\n  return animation;\n}\n\nstatic void animation_destroy(struct animation* animation) {\n  if (animation) free(animation);\n}\n\nstatic void animation_lock(struct animation* animation) {\n  animation->locked = true;\n}\n\nvoid animation_setup(struct animation* animation, void* target, animator_function* update_function, int initial_value, int final_value, uint32_t duration, char interp_function) {\n  // The animation duration is represented as a frame count equivalent on a\n  // 60Hz display. E.g. 120frames = 2 seconds\n  animation->duration = (double)duration / 60.0;\n  animation->initial_value = initial_value;\n  animation->final_value = final_value;\n  animation->update_function = update_function;\n  animation->target = target;\n  animation->separate_bytes = false;\n  animation->as_float = false;\n\n  if (interp_function == INTERP_FUNCTION_TANH) {\n    animation->interp_function = &function_tanh;\n  } else if (interp_function == INTERP_FUNCTION_SIN) {\n    animation->interp_function = &function_sin;\n  } else if (interp_function == INTERP_FUNCTION_QUADRATIC) {\n    animation->interp_function = &function_square;\n  } else if (interp_function == INTERP_FUNCTION_EXP) {\n    animation->interp_function = &function_exp;\n  } else if (interp_function == INTERP_FUNCTION_CIRC) {\n    animation->interp_function = &function_circ;\n  } else {\n    animation->interp_function = &function_linear;\n  }\n}\n\nstatic bool animation_update(struct animation* animation, uint64_t time, uint64_t clock) {\n  if (!animation->target\n      || !animation->update_function\n      || animation->waiting         ) {\n    return false;\n  }\n\n  if (!animation->initial_time) animation->initial_time = time;\n  double t = animation->duration > 0\n             ? ((double)(time - animation->initial_time)\n               / (double)(animation->duration * clock))\n             : 1.0;\n\n  bool final_frame = t >= 1.0;\n  if (t < 0.0) t = 0.0;\n  if (t > 1.0) t = 1.0;\n\n  double slider = final_frame ? 1.0 : animation->interp_function(t);\n\n  int value;\n  if (animation->separate_bytes) {\n    for (int i = 0; i < 4; i++) {\n      unsigned char byte_i = *((unsigned char*)&animation->initial_value + i);\n      unsigned char byte_f = *((unsigned char*)&animation->final_value + i);\n\n      unsigned char byte_val = (1. - slider) * byte_i + slider * byte_f;\n      *((unsigned char*)&value + i) = byte_val;\n    }\n  } else if (animation->as_float) {\n    *((float*)&value) = (1. - slider) * *(float*)&animation->initial_value\n             + slider * *(float*)&animation->final_value;\n\n  } else {\n    value = (1. - slider) * animation->initial_value\n            + slider * animation->final_value\n            + 0.5;\n    if (final_frame) value = animation->final_value;\n  }\n\n  bool needs_update;\n  if (animation->as_float) {\n    needs_update =\n      ((bool (*)(void*, float))animation->update_function)(animation->target,\n                                                           *((float*)&value) );\n  } else {\n    needs_update = animation->update_function(animation->target, value);\n  }\n\n  bool found_item = false;\n  for (int i = 0; i < g_bar_manager.bar_item_count; i++) {\n    if (needs_update\n        && (animation->target >= (void*)g_bar_manager.bar_items[i])\n        && (animation->target < ((void*)g_bar_manager.bar_items[i]\n                                 + sizeof(struct bar_item)         ))) {\n\n      bar_item_needs_update(g_bar_manager.bar_items[i]);\n      found_item = true;\n    }\n  }\n\n  if (!found_item && needs_update) g_bar_manager.bar_needs_update = true;\n\n  animation->finished = final_frame;\n  if (animation->finished && animation->next) {\n    animation->next->previous = NULL;\n    animation->next->waiting = false;\n    animation->next = NULL;\n  }\n  return needs_update;\n}\n\nvoid animator_init(struct animator* animator) {\n  animator->animations = NULL;\n  animator->animation_count = 0;\n  animator->interp_function = 0;\n  animator->duration = 0;\n  animator->display_link = NULL;\n\n  animator_renew_display_link(animator);\n}\n\nvoid animator_renew_display_link(struct animator* animator) {\n  animator_destroy_display_link(animator);\n  CVDisplayLinkCreateWithActiveCGDisplays(&animator->display_link);\n\n  CVDisplayLinkSetOutputCallback(animator->display_link,\n                                 animation_frame_callback,\n                                 animator                 );\n\n  animator->clock = CVGetHostClockFrequency();\n  CVDisplayLinkStart(animator->display_link);\n}\n\nvoid animator_destroy_display_link(struct animator* animator) {\n  if (animator->display_link) {\n    CVDisplayLinkStop(animator->display_link);\n    CVDisplayLinkRelease(animator->display_link);\n    animator->display_link = NULL;\n  }\n}\n\nvoid animator_lock(struct animator* animator) {\n  for (int i = 0; i < animator->animation_count; i++) {\n     animation_lock(animator->animations[i]);\n  }\n}\n\nstatic void animator_calculate_offset_for_animation(struct animator* animator, struct animation* animation) {\n  if (animator->animation_count < 1) return;\n\n  struct animation* previous = NULL;\n  for (int i = animator->animation_count - 1; i >= 0; i--) {\n    struct animation* current = animator->animations[i];\n    if (current->target == animation->target\n        && current->update_function == animation->update_function) {\n      previous = current;\n      break;\n    }\n  }\n\n  if (previous) {\n    animation->initial_value = previous->final_value;\n    previous->next = animation;\n    animation->previous = previous;\n    animation->waiting = true;\n  }\n}\n\nvoid animator_add(struct animator* animator, struct animation* animation) {\n  animator_calculate_offset_for_animation(animator, animation);\n  animator->animations = realloc(animator->animations,\n                                 sizeof(struct animation*)\n                                        * ++animator->animation_count);\n  animator->animations[animator->animation_count - 1] = animation;\n\n  if (!animator->display_link) animator_renew_display_link(animator);\n}\n\nstatic void animator_remove(struct animator* animator, struct animation* animation) {\n  if (animator->animation_count == 1) {\n    free(animator->animations);\n    animator->animations = NULL;\n    animator->animation_count = 0;\n  } else {\n    struct animation* tmp[animator->animation_count - 1];\n    int count = 0;\n    for (int i = 0; i < animator->animation_count; i++) {\n      if (animator->animations[i] == animation) continue;\n      tmp[count++] = animator->animations[i];\n    }\n    animator->animation_count--;\n    animator->animations = realloc(animator->animations,\n                                   sizeof(struct animation*)\n                                          *animator->animation_count);\n\n    memcpy(animator->animations,\n           tmp,\n           sizeof(struct animation*)*animator->animation_count);\n  }\n\n  if (animation->previous) animation->previous->next = NULL;\n  if (animation->next) animation->next->previous = NULL;\n\n  animation_destroy(animation);\n}\n\nvoid animator_cancel_locked(struct animator* animator, void* target, animator_function* function) {\n  struct animation* remove[animator->animation_count];\n  memset(remove, 0, animator->animation_count);\n  uint32_t remove_count = 0;\n\n  for (int i = 0; i < animator->animation_count; i++) {\n    struct animation* animation = animator->animations[i];\n    if (animation->locked\n        && animation->target == target\n        && animation->update_function == function) {\n      remove[remove_count++] = animation;\n    }\n  }\n\n  for (uint32_t i = 0; i < remove_count; i++) {\n    animator_remove(animator, remove[i]);\n  }\n}\n\nbool animator_cancel(struct animator* animator, void* target, animator_function* function) {\n  bool needs_update = false;\n\n  struct animation* remove[animator->animation_count];\n  memset(remove, 0, animator->animation_count);\n  uint32_t remove_count = 0;\n\n  for (int i = 0; i < animator->animation_count; i++) {\n    struct animation* animation = animator->animations[i];\n    if (animation->target == target\n        && animation->update_function == function) {\n      needs_update |= function(animation->target, animation->final_value);\n      remove[remove_count++] = animation;\n    }\n  }\n\n  for (uint32_t i = 0; i < remove_count; i++) {\n    animator_remove(animator, remove[i]);\n  }\n\n  return needs_update;\n}\n\nbool animator_update(struct animator* animator, uint64_t time) {\n  bool needs_refresh = false;\n  struct animation* remove[animator->animation_count];\n  memset(remove, 0, animator->animation_count);\n  uint32_t remove_count = 0;\n\n  for (uint32_t i = 0; i < animator->animation_count; i++) {\n    needs_refresh |= animation_update(animator->animations[i],\n                                      time,\n                                      animator->clock         );\n\n    if (animator->animations[i]->finished) {\n      remove[remove_count++] = animator->animations[i];\n    }\n  }\n\n  for (uint32_t i = 0; i < remove_count; i++) {\n    animator_remove(animator, remove[i]);\n  }\n\n  if (animator->animation_count == 0) animator_destroy_display_link(animator);\n  return needs_refresh;\n}\n\nvoid animator_destroy(struct animator* animator) {\n  if (animator->animation_count > 0) {\n    if (animator->display_link)\n      CVDisplayLinkStop(animator->display_link);\n    CVDisplayLinkRelease(animator->display_link);\n    animator->display_link = NULL;\n\n    for (int i = 0; i < animator->animation_count; i++) {\n      animation_destroy(animator->animations[i]);\n    }\n  }\n\n  if (animator->animations) free(animator->animations);\n}\n"
  },
  {
    "path": "src/animation.h",
    "content": "#pragma once\n#include <CoreVideo/CoreVideo.h>\n#include \"misc/helpers.h\"\n\nextern struct bar_manager g_bar_manager;\n\n#define ANIMATE(f, o, p, t) \\\n{\\\n  if (g_bar_manager.animator.duration > 0) { \\\n    animator_cancel_locked(&g_bar_manager.animator, (void*)o, (bool (*)(void*, int))&f); \\\n    struct animation* animation = animation_create(); \\\n    animation_setup(animation, \\\n                    (void*)o, \\\n                    (bool (*)(void*, int))&f, \\\n                    p, \\\n                    t, \\\n                    g_bar_manager.animator.duration, \\\n                    g_bar_manager.animator.interp_function ); \\\n    animator_add(&g_bar_manager.animator, animation); \\\n  } else { \\\n    needs_refresh = animator_cancel(&g_bar_manager.animator, (void*)o, (bool (*)(void*, int))&f); \\\n    needs_refresh |= f(o, t); \\\n  } \\\n}\n\n#define ANIMATE_FLOAT(f, o, p, t) \\\n{\\\n  if (g_bar_manager.animator.duration > 0) { \\\n    animator_cancel_locked(&g_bar_manager.animator, (void*)o, (bool (*)(void*, int))&f); \\\n    struct animation* animation = animation_create(); \\\n    float initial_value = p; \\\n    float final_value = t; \\\n    animation_setup(animation, \\\n                    (void*)o, \\\n                    (bool (*)(void*, int))&f, \\\n                    *(int*)&initial_value, \\\n                    *(int*)&final_value, \\\n                    g_bar_manager.animator.duration, \\\n                    g_bar_manager.animator.interp_function ); \\\n    animation->as_float = true; \\\n    animator_add(&g_bar_manager.animator, animation); \\\n  } else { \\\n    needs_refresh = animator_cancel(&g_bar_manager.animator, (void*)o, (bool (*)(void*, int))&f); \\\n    needs_refresh |= f(o, t); \\\n  } \\\n}\n\n#define ANIMATE_BYTES(f, o, p, t) \\\n{\\\n  if (g_bar_manager.animator.duration > 0) { \\\n    animator_cancel_locked(&g_bar_manager.animator, (void*)o, (bool (*)(void*, int))&f); \\\n    struct animation* animation = animation_create(); \\\n    animation_setup(animation, \\\n                    (void*)o, \\\n                    (bool (*)(void*, int))&f, \\\n                    p, \\\n                    t, \\\n                    g_bar_manager.animator.duration, \\\n                    g_bar_manager.animator.interp_function ); \\\n    animation->separate_bytes = true; \\\n    animator_add(&g_bar_manager.animator, animation); \\\n  } else { \\\n    needs_refresh = animator_cancel(&g_bar_manager.animator, (void*)o, (bool (*)(void*, int))&f); \\\n    needs_refresh |= f(o, t); \\\n  } \\\n}\n\n#define ANIMATOR_FUNCTION(name) bool name(void* target, int value);\ntypedef ANIMATOR_FUNCTION(animator_function);\n\n#define ANIMATION_FUNCTION(name) double name(double x);\ntypedef ANIMATION_FUNCTION(animation_function);\n\n#define INTERP_FUNCTION_LINEAR    'l'\n#define INTERP_FUNCTION_QUADRATIC 'q'\n#define INTERP_FUNCTION_SIN       's'\n#define INTERP_FUNCTION_TANH      't'\n#define INTERP_FUNCTION_CIRC      'c'\n#define INTERP_FUNCTION_BOUNCE    'b'\n#define INTERP_FUNCTION_EXP       'e'\n#define INTERP_FUNCTION_OVERSHOOT 'o'\n\n\nstruct animation {\n  bool separate_bytes;\n  bool as_float;\n  bool locked;\n  bool finished;\n  bool waiting;\n\n  uint64_t initial_time;\n  double duration;\n\n  int initial_value;\n  int final_value;\n  animation_function* interp_function;\n\n  void* target;\n  animator_function* update_function;\n\n  struct animation* next;\n  struct animation* previous;\n};\n\nstruct animation* animation_create();\nvoid animation_setup(struct animation* animation, void* target, animator_function* update_function, int initial_value, int final_value, uint32_t duration, char interp_function);\n\nstruct animator {\n  CVDisplayLinkRef display_link;\n\n  double clock;\n  uint32_t interp_function;\n  uint32_t duration;\n  struct animation** animations;\n  uint32_t animation_count;\n};\n\nvoid animator_init(struct animator* animator);\nvoid animator_add(struct animator* animator, struct animation* animation);\n\nbool animator_cancel(struct animator* animator, void* target, animator_function* function);\nvoid animator_cancel_locked(struct animator* animator, void* target, animator_function* function);\n\nbool animator_update(struct animator* animator, uint64_t time);\nvoid animator_lock(struct animator* animator);\nvoid animator_destroy(struct animator* animator);\n\nvoid animator_renew_display_link(struct animator* animator);\nvoid animator_destroy_display_link(struct animator* animator);\n"
  },
  {
    "path": "src/app_windows.c",
    "content": "#include \"app_windows.h\"\n#include \"workspace.h\"\n#include \"misc/helpers.h\"\n#include \"event.h\"\n\nextern pid_t g_pid;\nstruct app_windows g_windows = { 0 };\nstruct app_windows g_hidden_windows = { 0 };\nbool g_space_window_events = false;\n\nstatic bool iterator_window_suitable(CFTypeRef iterator) {\n  uint64_t tags = SLSWindowIteratorGetTags(iterator);\n  uint64_t attributes = SLSWindowIteratorGetAttributes(iterator);\n  uint32_t parent_wid = SLSWindowIteratorGetParentID(iterator);\n  if (((parent_wid == 0)\n        && ((attributes & 0x2)\n          || (tags & 0x400000000000000))\n        && (((tags & 0x1))\n          || ((tags & 0x2)\n            && (tags & 0x80000000))))) {\n    return true;\n  }\n  return false;\n}\n\nvoid app_window_clear(struct app_window* window) {\n  memset(window, 0, sizeof(struct app_window));\n}\n\nvoid app_windows_add(struct app_windows* windows, struct app_window* window) {\n  for (int i = 0; i < windows->num_windows; i++) {\n    if (!windows->windows[i].wid) {\n      windows->windows[i] = *window;\n      return;\n    }\n  }\n\n  windows->windows = realloc(windows->windows,\n                             sizeof(struct app_window)\n                             * ++windows->num_windows);\n\n  windows->windows[windows->num_windows - 1] = *window;\n}\n\nvoid app_windows_clear_space(struct app_windows* windows, uint64_t sid) {\n  for (int i = 0; i < windows->num_windows; i++) {\n    if (windows->windows[i].sid == sid) app_window_clear(windows->windows + i);\n  }\n}\n\nvoid app_windows_register_notifications() {\n  uint32_t window_count = 0;\n  uint32_t wid_list[g_windows.num_windows + g_hidden_windows.num_windows];\n  for (int i = 0; i < g_windows.num_windows; i++) {\n    if (g_windows.windows[i].wid)\n      wid_list[window_count++] = g_windows.windows[i].wid;\n  }\n\n  for (int i = 0; i < g_hidden_windows.num_windows; i++) {\n    if (g_hidden_windows.windows[i].wid)\n      wid_list[window_count++] = g_hidden_windows.windows[i].wid;\n  }\n  SLSRequestNotificationsForWindows(g_connection, wid_list, window_count);\n}\n\nbool app_windows_find(struct app_windows* windows, struct app_window* window) {\n  for (int i = 0; i < windows->num_windows; i++) {\n    if (windows->windows[i].wid == window->wid\n        && windows->windows[i].sid == window->sid) {\n      return true;\n    }\n  }\n  return false;\n}\n\nstruct app_window* app_windows_find_by_wid(struct app_windows* windows, uint32_t wid) {\n  for (int i = 0; i < windows->num_windows; i++) {\n    if (windows->windows[i].wid == wid) return &windows->windows[i];\n  }\n  return NULL;\n}\n\nstatic bool app_window_suitable(struct app_window* window) {\n  CFArrayRef target_ref = cfarray_of_cfnumbers(&window->wid,\n                                               sizeof(uint32_t),\n                                               1,\n                                               kCFNumberSInt32Type);\n\n  if (!target_ref) return false;\n\n  bool suitable = false;\n  CFTypeRef query = SLSWindowQueryWindows(g_connection, target_ref, 0x0);\n  if (query) {\n    CFTypeRef iterator = SLSWindowQueryResultCopyWindows(query);\n    if (iterator && SLSWindowIteratorGetCount(iterator) > 0) {\n      if (SLSWindowIteratorAdvance(iterator)) {\n        if (iterator_window_suitable(iterator)) suitable = true;\n      }\n    }\n    if (iterator) CFRelease(iterator);\n    CFRelease(query);\n  }\n  CFRelease(target_ref);\n  return suitable;\n}\n\nstatic void app_windows_post_event_for_space(struct app_windows* windows, uint64_t sid) {\n  int index = mission_control_index(sid);\n  pid_t pid_list[windows->num_windows];\n  uint32_t pid_count[windows->num_windows];\n  char* pid_name[windows->num_windows];\n\n  memset(&pid_list, 0, sizeof(pid_t)*windows->num_windows);\n  memset(&pid_count, 0, sizeof(uint32_t)*windows->num_windows);\n  memset(&pid_name, 0, sizeof(char*)*windows->num_windows);\n\n  uint32_t length = 64;\n  for (int i = 0; i < windows->num_windows; i++) {\n    for (int j = 0; j < windows->num_windows; j++) {\n      if ((!pid_list[j] || pid_list[j] == windows->windows[i].pid)\n          && windows->windows[i].sid == sid) {\n        pid_list[j] = windows->windows[i].pid;\n        pid_count[j]++;\n        if (!pid_name[j]) {\n          pid_name[j] = workspace_copy_app_name_for_pid(pid_list[j]);\n          length += pid_name[j] ? (strlen(pid_name[j]) + 16) : 0;\n        }\n        break;\n      }\n    }\n  }\n\n  for (int i = 0; i < windows->num_windows; i++) {\n    for (int j = i + 1; j < windows->num_windows; j++) {\n      if (pid_name[i]\n          && pid_name[j]\n          && strcmp(pid_name[i], pid_name[j]) == 0) {\n        free(pid_name[j]);\n        pid_name[j] = NULL;\n\n        pid_count[i] += pid_count[j];\n      }\n    }\n  }\n\n  char payload[length];\n  memset(payload, 0, length);\n  snprintf(payload, length, \"{\\n\"\n                            \"\\t\\\"space\\\": %d,\\n\"\n                            \"\\t\\\"apps\\\": {\\n\",\n                            index               );\n\n  char* cursor = payload + strlen(payload);\n  bool first = true;\n  for (int i = 0; i < windows->num_windows; i++) {\n    if (!pid_list[i]) break; \n    if (!pid_name[i]) continue;\n    if (!first) {\n      snprintf(cursor, length - (cursor - payload), \",\\n\");\n      cursor = payload + strlen(payload);\n    } else first = false;\n    snprintf(cursor,\n             length - (cursor - payload),\n             \"\\t\\t\\\"%s\\\": %d\",\n             pid_name[i],\n             pid_count[i]                );\n\n    free(pid_name[i]);\n    cursor = payload + strlen(payload);\n  }\n\n  snprintf(cursor, length - (cursor - payload), \"\\n\\t}\\n}\\n\");\n  struct event event = { payload, SPACE_WINDOWS_CHANGED };\n  event_post(&event);\n}\n\nstatic void app_windows_update_space(struct app_windows* windows, uint64_t sid, bool silent) {\n  app_windows_clear_space(windows, sid);\n  CFArrayRef space_list_ref = cfarray_of_cfnumbers(&sid,\n                                                   sizeof(uint64_t),\n                                                   1,\n                                                   kCFNumberSInt64Type);\n\n  uint64_t set_tags = 1;\n  uint64_t clear_tags = 0;\n  CFArrayRef window_list = SLSCopyWindowsWithOptionsAndTags(g_connection,\n                                                            0,\n                                                            space_list_ref,\n                                                            0x2,\n                                                            &set_tags,\n                                                            &clear_tags    );\n\n  if (window_list) {\n    uint32_t window_count = CFArrayGetCount(window_list);\n    if (window_count > 0) {\n      CFTypeRef query = SLSWindowQueryWindows(g_connection, window_list, 0x0);\n      if (query) {\n        CFTypeRef iterator = SLSWindowQueryResultCopyWindows(query);\n        if (iterator) {\n          while(SLSWindowIteratorAdvance(iterator)) {\n            if (iterator_window_suitable(iterator)) {\n              uint32_t wid = SLSWindowIteratorGetWindowID(iterator);\n              int wid_cid = 0;\n              SLSGetWindowOwner(g_connection, wid, &wid_cid);\n\n              pid_t pid = 0;\n              SLSConnectionGetPID(wid_cid, &pid);\n              struct app_window window = {.wid = wid, .sid = sid, .pid = pid};\n              app_windows_add(windows, &window);\n            }\n          }\n          CFRelease(iterator);\n        }\n        CFRelease(query);\n      }\n    }\n    CFRelease(window_list);\n  }\n\n  CFRelease(space_list_ref);\n  if (!silent) app_windows_post_event_for_space(windows, sid);\n  app_windows_register_notifications();\n}\n\nstruct window_spawn_data {\n  uint64_t sid;\n  uint32_t wid;\n};\n\nstatic void window_spawn_handler(uint32_t event, struct window_spawn_data* data, size_t _, int cid) {\n  uint32_t wid = data->wid;\n  uint64_t sid = data->sid;\n\n  if (!wid || !sid) return;\n\n  struct app_window window = { .wid = wid, .sid = sid, .pid = 0 };\n\n  if (event == 1325 && app_window_suitable(&window)) {\n    app_windows_update_space(&g_windows, sid, false);\n  } else if (event == 1326 && app_windows_find(&g_windows, &window)) {\n    app_windows_update_space(&g_windows, sid, false);\n    struct app_window* window = app_windows_find_by_wid(&g_hidden_windows,\n                                                        wid               );\n    if (window) app_window_clear(window);\n  }\n}\n\nstatic void window_hide_handler(uint32_t event, uint32_t* window_id, size_t _, int cid) {\n  uint32_t wid = *window_id;\n\n  if (event == 816) {\n    struct app_window* window = app_windows_find_by_wid(&g_windows, wid);\n    if (window) {\n      if (!app_windows_find(&g_hidden_windows, window)) {\n        app_windows_add(&g_hidden_windows, window);\n      }\n      app_windows_update_space(&g_windows, window->sid, false);\n    }\n  } else if (event == 815) {\n    struct app_window* window = app_windows_find_by_wid(&g_hidden_windows, wid);\n    if (window) {\n      app_windows_update_space(&g_windows, window->sid, false);\n      app_window_clear(window);\n      app_windows_register_notifications();\n    }\n  }\n}\n\nstatic void update_all_spaces(struct app_windows* windows, bool silent) {\n  uint32_t display_count = 0;\n  uint32_t* displays = display_active_display_list(&display_count);\n  for (int i = 0; i < display_count; i++) {\n    int space_count = 0;\n    uint64_t* spaces = display_space_list(displays[i], &space_count);\n    for (int j = 0; j < space_count; j++) {\n      app_windows_update_space(windows, spaces[j], silent);\n    }\n    if (spaces) free(spaces);\n  }\n  if (displays) free(displays);\n}\n\nstatic void space_handler(uint32_t event, void* data, size_t data_length, void* context) {\n  update_all_spaces(&g_windows, event == 1401);\n}\n\nvoid forced_space_windows_event() {\n  if (g_space_window_events) update_all_spaces(&g_windows, false);\n}\n\nvoid begin_receiving_space_window_events() {\n  if (!g_space_window_events) {\n    SLSRegisterNotifyProc(window_spawn_handler,\n                          1325,\n                          (void*)(intptr_t)g_connection);\n\n    SLSRegisterNotifyProc(window_spawn_handler,\n                          1326,\n                          (void*)(intptr_t)g_connection);\n\n    SLSRegisterNotifyProc(window_hide_handler,\n                          815,\n                          (void*)(intptr_t)g_connection);\n\n    SLSRegisterNotifyProc(window_hide_handler,\n                          816,\n                          (void*)(intptr_t)g_connection);\n\n    SLSRegisterNotifyProc((void*)space_handler, 1401, NULL);\n    if (__builtin_available(macOS 13.0, *)) {\n      SLSRegisterNotifyProc((void*)space_handler, 1327, NULL);\n      SLSRegisterNotifyProc((void*)space_handler, 1328, NULL);\n    }\n\n    g_space_window_events = true;\n    update_all_spaces(&g_windows, true);\n  }\n}\n"
  },
  {
    "path": "src/app_windows.h",
    "content": "#pragma once\n#include \"event.h\"\n\nstruct app_window {\n  uint32_t wid;\n  uint64_t sid;\n  pid_t pid;\n};\n\nstruct app_windows {\n  struct app_window* windows;\n  uint32_t num_windows;\n};\n\nvoid begin_receiving_space_window_events();\nvoid forced_space_windows_event();\n"
  },
  {
    "path": "src/background.c",
    "content": "#include \"background.h\"\n#include \"image.h\"\n#include \"misc/helpers.h\"\n#include \"shadow.h\"\n#include \"animation.h\"\n#include \"bar_manager.h\"\n\nvoid background_init(struct background* background) {\n  background->enabled = false;\n  background->clip = 0.f;\n  background->clips = NULL;\n  background->num_clips = 0;\n  background->overrides_height = false;\n\n  background->bounds.size.height = 0;\n  background->bounds.size.width = 0;\n  background->border_width = 0;\n  background->padding_left = 0;\n  background->padding_right = 0;\n  background->corner_radius = 0;\n  background->x_offset = 0;\n  background->y_offset = 0;\n\n  color_init(&background->color, 0x00000000);\n  color_init(&background->border_color, 0x00000000);\n  shadow_init(&background->shadow);\n  image_init(&background->image);\n}\n\nbool background_set_height(struct background* background, uint32_t height) {\n  if (background->bounds.size.height == height) return false;\n  background->bounds.size.height = height;\n  background->overrides_height = height != 0;\n  return true;\n}\n\nstatic void background_reset_clip(struct background* background) {\n  for (uint32_t i = 0; i < background->num_clips; i++)\n    free(background->clips[i]);\n\n  if (background->clips) free(background->clips);\n  background->clips = NULL;\n  background->num_clips = 0;\n}\n\nbool background_set_enabled(struct background* background, bool enabled) {\n  if (background->enabled == enabled) return false;\n\n  if (background_clips_bar(background)) {\n    background_reset_clip(background);\n    g_bar_manager.bar_needs_update = true;\n  }\n\n  background->enabled = enabled;\n\n  return true;\n}\n\nbool background_set_color(struct background* background, uint32_t color) {\n  bool changed = background_set_enabled(background, true);\n  return color_set_hex(&background->color, color) || changed;\n}\n\nstatic bool background_set_clip(struct background* background, float clip) {\n  if (background->clip == clip) return false;\n  background->clip = clip;\n  g_bar_manager.bar_needs_update = true;\n  g_bar_manager.might_need_clipping = true;\n  if (clip > 0.f) background_set_enabled(background, true);\n  return true;\n}\n\nstatic bool background_set_border_color(struct background* background, uint32_t color) {\n  return color_set_hex(&background->border_color, color);\n}\n\nstatic bool background_set_border_width(struct background* background, uint32_t border_width) {\n  if (background->border_width == border_width) return false;\n  background->border_width = border_width;\n  return true;\n}\n\nstatic bool background_set_corner_radius(struct background* background, uint32_t corner_radius) {\n  if (background->corner_radius == corner_radius) return false;\n  background->corner_radius = corner_radius;\n  return true;\n}\n\nstatic bool background_set_xoffset(struct background* background, int offset) {\n  if (background->x_offset == offset) return false;\n  background->x_offset = offset;\n  return true;\n}\n\nstatic bool background_set_yoffset(struct background* background, int offset) {\n  if (background->y_offset == offset) return false;\n  background->y_offset = offset;\n  return true;\n}\n\nbool background_set_padding_left(struct background* background, uint32_t pad) {\n  if (background->padding_left == pad) return false;\n  background->padding_left = pad;\n  return true;\n}\n\nbool background_set_padding_right(struct background* background, uint32_t pad) {\n  if (background->padding_right == pad) return false;\n  background->padding_right = pad;\n  return true;\n}\n\nbool background_clip_needs_update(struct background* background, struct bar* bar) {\n  if (background->clip == 0.f || !background->enabled) return false;\n  struct background* clip = background_get_clip(background, bar->adid);\n  if (!CGRectEqualToRect(background->bounds, clip->bounds)) return true;\n  if (background->corner_radius != clip->corner_radius) return true;\n  if (background->x_offset != clip->x_offset) return true;\n  if (background->y_offset != clip->y_offset) return true;\n  return false;\n}\n\nstatic void background_update_clip(struct background* background, struct background* clip) {\n  memcpy(clip, background, sizeof(struct background));\n  background_clear_pointers(clip);\n}\n\nstruct background* background_get_clip(struct background* background, uint32_t adid) {\n  if (adid < 1) return NULL;\n  if (background->num_clips < adid) {\n    background->clips = (struct background**) realloc(background->clips,\n                                                sizeof(struct background*)*adid);\n    memset(background->clips + background->num_clips,\n           0,\n           sizeof(struct background*) * (adid - background->num_clips));\n\n    background->num_clips = adid;\n  }\n\n  if (!background->clips[adid - 1]) {\n    struct background* clip = malloc(sizeof(struct background));\n    background->clips[adid - 1] = clip;\n    background_init(clip);\n    background_update_clip(background, clip);\n    clip->bounds.origin = g_nirvana;\n  }\n\n  return background->clips[adid - 1];\n}\n\nbool background_clips_bar(struct background* background) {\n  return background->enabled && (background->clip > 0.f);\n}\n\nvoid background_clip_bar(struct background* background, int offset, struct bar* bar) {\n  if (!background_clips_bar(background)) return;\n\n  struct background* clip = background_get_clip(background, bar->adid);\n  background_update_clip(background, clip);\n\n  CGRect background_bounds = background->bounds;\n  background_bounds.origin.x += offset + background->x_offset;\n  background_bounds.origin.y += background->y_offset;\n\n  clip_rect(bar->window.context,\n            background_bounds,\n            background->clip,\n            background->corner_radius);\n}\n\nvoid background_calculate_bounds(struct background* background, uint32_t x, uint32_t y, uint32_t width, uint32_t height) {\n  background->bounds.origin.x = x;\n  background->bounds.origin.y = y - height / 2;\n  background->bounds.size.width = width;\n  background->bounds.size.height = height;\n\n  if (background->image.enabled)\n    image_calculate_bounds(&background->image, x, y);\n}\n\nstatic void draw_rect(CGContextRef context, CGRect region, struct color* fill_color, uint32_t corner_radius, uint32_t line_width, struct color* stroke_color) {\n  CGContextSetLineWidth(context, line_width);\n  if (stroke_color) CGContextSetRGBStrokeColor(context, stroke_color->r, stroke_color->g, stroke_color->b, stroke_color->a);\n  CGContextSetRGBFillColor(context, fill_color->r, fill_color->g, fill_color->b, fill_color->a);\n\n  CGMutablePathRef path = CGPathCreateMutable();\n  CGRect inset_region = CGRectInset(region, (float)(line_width) / 2.f, (float)(line_width) / 2.f);\n  if (corner_radius > inset_region.size.height / 2.f || corner_radius > inset_region.size.width / 2.f)\n    corner_radius = inset_region.size.height > inset_region.size.width ? inset_region.size.width / 2.f : inset_region.size.height / 2.f; \n  CGPathAddRoundedRect(path, NULL, inset_region, corner_radius, corner_radius);\n  CGContextAddPath(context, path);\n  CGContextDrawPath(context, kCGPathFillStroke);\n  CFRelease(path);\n}\n\nvoid background_draw(struct background* background, CGContextRef context) {\n  if (!background->enabled) return;\n\n  if ((background->border_color.a == 0 || background->border_width == 0)\n      && (background->color.a == 0)\n      && !background->shadow.enabled\n      && !background->image.enabled                                      ) {\n    // The background is enabled but has no content.\n    return;\n  }\n\n  CGRect background_bounds = background->bounds;\n  background_bounds.origin.x += background->x_offset;\n  background_bounds.origin.y += background->y_offset;\n  if (background->shadow.enabled) {\n    CGRect bounds = shadow_get_bounds(&background->shadow, background_bounds);\n    draw_rect(context,\n              bounds,\n              &background->shadow.color,\n              background->corner_radius,\n              background->border_width,\n              &background->shadow.color);\n  }\n\n  draw_rect(context,\n            background_bounds,\n            &background->color,\n            background->corner_radius,\n            background->border_width,\n            &background->border_color);\n\n  if (background->image.enabled)\n    image_draw(&background->image, context);\n\n}\n\nvoid background_clear_pointers(struct background* background) {\n  background->clips = NULL;\n  background->num_clips = 0;\n  image_clear_pointers(&background->image);\n}\n\nvoid background_destroy(struct background* background) {\n  for (uint32_t i = 0; i < background->num_clips; i++)\n    free(background->clips[i]);\n\n  if (background->clips) free(background->clips);\n\n  image_destroy(&background->image);\n  background_clear_pointers(background);\n}\n\nvoid background_serialize(struct background* background, char* indent, FILE* rsp, bool detailed) {\n  fprintf(rsp, \"%s\\\"drawing\\\": \\\"%s\\\",\\n\"\n               \"%s\\\"color\\\": \\\"0x%x\\\",\\n\"\n               \"%s\\\"border_color\\\": \\\"0x%x\\\",\\n\"\n               \"%s\\\"border_width\\\": %u,\\n\"\n               \"%s\\\"height\\\": %u,\\n\"\n               \"%s\\\"corner_radius\\\": %u,\\n\"\n               \"%s\\\"padding_left\\\": %d,\\n\"\n               \"%s\\\"padding_right\\\": %d,\\n\"\n               \"%s\\\"x_offset\\\": %d,\\n\"\n               \"%s\\\"y_offset\\\": %d,\\n\"\n               \"%s\\\"clip\\\": %f,\\n\",\n               indent, format_bool(background->enabled),\n               indent, background->color.hex,\n               indent, background->border_color.hex,\n               indent, background->border_width,\n               indent, background->overrides_height ? (int)background->bounds.size.height : 0,\n               indent, background->corner_radius,\n               indent, background->padding_left,\n               indent, background->padding_right,\n               indent, background->x_offset,\n               indent, background->y_offset,\n               indent, background->clip                                                       );\n\n  char deeper_indent[strlen(indent) + 2];\n  snprintf(deeper_indent, strlen(indent) + 2, \"%s\\t\", indent);\n\n  fprintf(rsp, \"%s\\\"image\\\": {\\n\", indent);\n  image_serialize(&background->image, deeper_indent, rsp);\n  fprintf(rsp, \"\\n%s}\", indent);\n\n  if (!detailed) return;\n\n  fprintf(rsp, \",\\n%s\\\"shadow\\\": {\\n\", indent);\n  shadow_serialize(&background->shadow, deeper_indent, rsp);\n  fprintf(rsp, \"\\n%s}\", indent);\n}\n\nbool background_parse_sub_domain(struct background* background, FILE* rsp, struct token property, char* message) {\n  bool needs_refresh = false;\n  if (token_equals(property, PROPERTY_DRAWING))\n    return background_set_enabled(background,\n                                  evaluate_boolean_state(get_token(&message),\n                                                         background->enabled));\n  else if (token_equals(property, PROPERTY_CLIP)) {\n    struct token token = get_token(&message);\n    ANIMATE_FLOAT(background_set_clip,\n                  background,\n                  background->clip,\n                  token_to_float(token));\n  } else if (token_equals(property, PROPERTY_HEIGHT)) {\n    struct token token = get_token(&message);\n    ANIMATE(background_set_height,\n            background,\n            background->bounds.size.height,\n            token_to_int(token)            );\n  }\n  else if (token_equals(property, PROPERTY_CORNER_RADIUS)) {\n    struct token token = get_token(&message);\n    ANIMATE(background_set_corner_radius,\n            background,\n            background->corner_radius,\n            token_to_int(token)          );\n  }\n  else if (token_equals(property, PROPERTY_BORDER_WIDTH)) {\n    struct token token = get_token(&message);\n    ANIMATE(background_set_border_width,\n            background,\n            background->border_width,\n            token_to_int(token)         );\n  }\n  else if (token_equals(property, PROPERTY_COLOR)) {\n    struct token token = get_token(&message);\n    ANIMATE_BYTES(background_set_color,\n                  background,\n                  background->color.hex,\n                  token_to_int(token)   );\n  }\n  else if (token_equals(property, PROPERTY_BORDER_COLOR)) {\n    struct token token = get_token(&message);\n    ANIMATE_BYTES(background_set_border_color,\n                  background,\n                  background->border_color.hex,\n                  token_to_int(token)          );\n  }\n  else if (token_equals(property, PROPERTY_PADDING_LEFT)) {\n    struct token token = get_token(&message);\n    ANIMATE(background_set_padding_left,\n            background,\n            background->padding_left,\n            token_to_int(token)         );\n  }\n  else if (token_equals(property, PROPERTY_PADDING_RIGHT)) {\n    struct token token = get_token(&message);\n    ANIMATE(background_set_padding_right,\n            background,\n            background->padding_right,\n            token_to_int(token)         );\n  }\n  else if (token_equals(property, PROPERTY_XOFFSET)) {\n    struct token token = get_token(&message);\n    ANIMATE(background_set_xoffset,\n            background,\n            background->x_offset,\n            token_to_int(token)    );\n  }\n  else if (token_equals(property, PROPERTY_YOFFSET)) {\n    struct token token = get_token(&message);\n    ANIMATE(background_set_yoffset,\n            background,\n            background->y_offset,\n            token_to_int(token)    );\n  }\n  else if (token_equals(property, SUB_DOMAIN_IMAGE)) {\n    return image_load(&background->image,\n                      token_to_string(get_token(&message)),\n                      rsp                                  );\n  }\n  else {\n    struct key_value_pair key_value_pair = get_key_value_pair(property.text,\n                                                              '.'           );\n    if (key_value_pair.key && key_value_pair.value) {\n      struct token subdom = {key_value_pair.key,strlen(key_value_pair.key)};\n      struct token entry = {key_value_pair.value,strlen(key_value_pair.value)};\n      if (token_equals(subdom, SUB_DOMAIN_SHADOW))\n        return shadow_parse_sub_domain(&background->shadow,\n                                       rsp,\n                                       entry,\n                                       message             );\n      else if (token_equals(subdom, SUB_DOMAIN_IMAGE)) {\n        return image_parse_sub_domain(&background->image, rsp, entry, message);\n      }\n      else if (token_equals(subdom, SUB_DOMAIN_COLOR)) {\n        return color_parse_sub_domain(&background->color, rsp, entry, message);\n      }\n      else if (token_equals(subdom, SUB_DOMAIN_BORDER_COLOR)) {\n        return color_parse_sub_domain(&background->border_color,\n                                      rsp,\n                                      entry,\n                                      message                   );\n      }\n      else {\n        respond(rsp, \"[!] Background: Invalid subdomain '%s'\\n\", subdom.text);\n      }\n    }\n    else {\n      respond(rsp, \"[!] Background: Invalid property '%s'\\n\", property.text);\n    }\n  }\n  return needs_refresh;\n}\n"
  },
  {
    "path": "src/background.h",
    "content": "#pragma once\n#include \"image.h\"\n\nstruct background {\n  bool enabled;\n  float clip;\n  bool overrides_height;\n\n  int padding_left;\n  int padding_right;\n  int x_offset;\n  int y_offset;\n  uint32_t border_width;\n  uint32_t corner_radius;\n\n  CGRect bounds;\n  struct image image;\n  struct shadow shadow;\n  struct color color;\n  struct color border_color;\n\n  struct background** clips;\n  uint32_t num_clips;\n};\n\nstruct bar;\n\nvoid background_init(struct background* background);\nvoid background_calculate_bounds(struct background* background, uint32_t x, uint32_t y, uint32_t width, uint32_t height);\n\nbool background_set_enabled(struct background* background, bool enabled);\nbool background_set_color(struct background* background, uint32_t color);\nbool background_set_height(struct background* background, uint32_t height);\nbool background_set_padding_left(struct background* background, uint32_t pad);\nbool background_set_padding_right(struct background* background, uint32_t pad);\n\nvoid background_draw(struct background* background, CGContextRef context);\n\nstruct background* background_get_clip(struct background* background, uint32_t adid);\nvoid background_clip_bar(struct background* background, int offset, struct bar* bar);\nbool background_clip_needs_update(struct background* background, struct bar* bar);\nbool background_clips_bar(struct background* background);\n\nvoid background_clear_pointers(struct background* background);\nvoid background_destroy(struct background* background);\n\nvoid background_serialize(struct background* background, char* indent, FILE* rsp, bool detailed);\nbool background_parse_sub_domain(struct background* background, FILE* rsp, struct token property, char* message);\n"
  },
  {
    "path": "src/bar.c",
    "content": "#include \"bar.h\"\n#include \"bar_manager.h\"\n#include \"event.h\"\n#include \"display.h\"\n#include \"misc/helpers.h\"\n#include \"window.h\"\n\n#define MAX_RENDER_THREADS 10\nstatic pthread_t g_render_threads[MAX_RENDER_THREADS];\nstatic uint32_t g_used_threads = 0;\n\nvoid join_render_threads() {\n  for (int i = 0; i < g_used_threads; i++)\n    pthread_join(g_render_threads[i], NULL);\n  g_used_threads = 0;\n}\n\nbool bar_draws_item(struct bar* bar, struct bar_item* bar_item) {\n    if (!bar_item->drawing || !bar->shown || bar->hidden) return false;\n\n    if (((bar_item->associated_display > 0\n          && (!(bar_item->associated_display & (1 << bar->adid))))\n        || (bar_item->associated_to_active_display\n            && (bar->adid != g_bar_manager.active_adid)))\n        && !bar_item->ignore_association)\n      return false;\n\n    if (bar_item->associated_space > 0\n        && (!(bar_item->associated_space & (1 << bar->sid))\n            && !bar_item->ignore_association)\n        && (bar_item->type != BAR_COMPONENT_SPACE)        )\n      return false;\n\n    if (bar_item->position == POSITION_POPUP\n        && (!bar_item->parent\n            || !bar_item->parent->popup.drawing\n            || (bar->adid != g_bar_manager.active_adid)))\n      return false;\n\n    return true;\n}\n\nstatic void bar_calculate_popup_anchor_for_bar_item(struct bar* bar, struct bar_item* bar_item) {\n  if (bar->adid != g_bar_manager.active_adid) return;\n  struct window* window = bar_item_get_window(bar_item, bar->adid);\n\n  if (!bar_item->popup.overrides_cell_size) {\n    if (g_bar_manager.position == POSITION_LEFT\n        || g_bar_manager.position == POSITION_RIGHT) {\n      bar_item->popup.cell_size = window->frame.size.width;\n    } else {\n      bar_item->popup.cell_size = window->frame.size.height;\n    }\n  }\n\n  popup_calculate_bounds(&bar_item->popup, bar);\n\n  CGPoint anchor = window->origin;\n\n  if (g_bar_manager.position == POSITION_LEFT\n      || g_bar_manager.position == POSITION_RIGHT) {\n    if (bar_item->popup.align == POSITION_CENTER) {\n      anchor.y += (window->frame.size.height\n                   - bar_item->popup.background.bounds.size.height) / 2;\n    } else if (bar_item->popup.align == POSITION_LEFT) {\n      anchor.y -= bar_item->background.padding_left;\n    } else {\n      anchor.y += window->frame.size.height\n                  - bar_item->popup.background.bounds.size.height;\n    }\n    anchor.x += (g_bar_manager.position == POSITION_RIGHT\n                ? (- bar_item->popup.background.bounds.size.width)\n                : window->frame.size.width);\n  } else {\n    if (bar_item->popup.align == POSITION_CENTER) {\n      anchor.x += (window->frame.size.width\n                   - bar_item->popup.background.bounds.size.width) / 2;\n    } else if (bar_item->popup.align == POSITION_LEFT) {\n      anchor.x -= bar_item->background.padding_left;\n    } else {\n      anchor.x += window->frame.size.width\n                  - bar_item->popup.background.bounds.size.width;\n    }\n    anchor.y += (g_bar_manager.position == POSITION_BOTTOM\n                ? (- bar_item->popup.background.bounds.size.height)\n                : window->frame.size.height);\n  }\n  popup_set_anchor(&bar_item->popup, anchor, bar->adid);\n  popup_calculate_bounds(&bar_item->popup, bar);\n}\n\nvoid bar_order_item_windows(struct bar* bar) {\n  if (bar->sid < 1 || bar->adid < 1 || !bar->shown) return;\n  window_set_level(&bar->window, g_bar_manager.window_level);\n  window_order(&bar->window, NULL, W_ABOVE);\n\n  struct window* previous_window = NULL;\n  struct window* first_window = NULL;\n  for (int i = 0; i < g_bar_manager.bar_item_count; i++) {\n    struct bar_item* bar_item = g_bar_manager.bar_items[i];\n    if (bar_item->position == POSITION_POPUP) continue;\n\n    struct window* window = bar_item_get_window(bar_item, bar->adid);\n    window_set_level(window, g_bar_manager.window_level);\n\n    if (!first_window) first_window = window;\n\n    if (bar_item->type == BAR_COMPONENT_GROUP) {\n      if (first_window)\n        window_order(window, first_window, W_BELOW);\n      else\n        window_order(window, &bar->window, W_ABOVE);\n      continue;\n    }\n\n    if (previous_window) window_order(window, previous_window, W_ABOVE);\n    else window_order(window, &bar->window, W_ABOVE);\n\n    previous_window = window;\n  }\n}\n\nstatic void bar_check_for_clip_updates(struct bar* bar) {\n  if (!g_bar_manager.bar_needs_update) {\n    for (int i = 0; i < g_bar_manager.bar_item_count; i++) {\n      struct bar_item* bar_item = g_bar_manager.bar_items[i];\n      struct window* window = bar_item_get_window(bar_item, bar->adid);\n\n      bool clips_bar = bar_item_clips_bar(bar_item);\n      if (!clips_bar || (!bar_draws_item(bar, bar_item))) {\n\n        if (clips_bar && !CGPointEqualToPoint(window->origin, g_nirvana)) {\n          g_bar_manager.bar_needs_update = true;\n          break;\n        }\n\n        continue;\n      }\n\n      g_bar_manager.bar_needs_update\n                       |= window->needs_move\n                          || window->needs_resize\n                          || bar_item_clip_needs_update_for_bar(bar_item, bar);\n\n      if (g_bar_manager.bar_needs_update) break;\n    }\n  }\n}\n\nvoid bar_draw(struct bar* bar, bool forced, bool threaded) {\n  if (bar->sid < 1 || bar->adid < 1) return;\n\n  if (g_bar_manager.might_need_clipping)\n    bar_check_for_clip_updates(bar);\n\n  if (g_bar_manager.bar_needs_update) {\n    struct background background = g_bar_manager.background;\n    background.bounds = bar->window.frame;\n    background.bounds.origin.y -= background.y_offset;\n    background.shadow.enabled = false;\n    background.enabled = true;\n    windows_freeze();\n    CGContextClearRect(bar->window.context, bar->window.frame);\n    background_draw(&background, bar->window.context);\n  }\n\n  for (int i = 0; i < g_bar_manager.bar_item_count; i++) {\n    struct bar_item* bar_item = g_bar_manager.bar_items[i];\n    struct window* window = bar_item_get_window(bar_item, bar->adid);\n\n    if (!bar_draws_item(bar, bar_item)){\n      if (!CGPointEqualToPoint(window->origin, g_nirvana)) {\n        window_move(window, g_nirvana);\n      }\n\n      bar_item_remove_associated_bar(bar_item, bar->adid);\n      continue;\n    }\n\n    bar_item_append_associated_bar(bar_item, bar->adid);\n\n    if (bar_item->popup.drawing && bar->adid == g_bar_manager.active_adid)\n      popup_draw(&bar_item->popup);\n\n    if (g_bar_manager.bar_needs_update) {\n      bar_item_clip_bar(bar_item,\n                        window->origin.x - bar->window.origin.x,\n                        bar                                     );\n    }\n\n    if (!window_apply_frame(window, forced) && !bar_item->needs_update)\n      continue;\n\n    if (bar_item->update_mask & UPDATE_MOUSE_ENTERED\n        || bar_item->update_mask & UPDATE_MOUSE_EXITED) {\n      window_assign_mouse_tracking_area(window, window->frame);\n    }\n\n    windows_freeze();\n    if (threaded && g_used_threads < MAX_RENDER_THREADS) {\n      uint32_t thread_id = g_used_threads++;\n      struct draw_item_payload {\n        struct window* window;\n        struct bar_item bar_item;\n      }* context = malloc(sizeof(struct draw_item_payload));\n      // We need to perform a shallow copy of the item here because\n      // the cached bounds will be invalidated when drawing the next bar.\n      context->bar_item = *bar_item;\n      context->window = window;\n      pthread_create(&g_render_threads[thread_id],\n                     NULL,\n                     draw_item_proc,\n                     context        );\n    } else {\n      CGContextClearRect(window->context, window->frame);\n      bar_item_draw(bar_item, window->context);\n      CGContextFlush(window->context);\n      window_flush(window);\n    }\n  }\n\n  if (g_bar_manager.bar_needs_update) {\n    CGContextFlush(bar->window.context);\n    window_flush(&bar->window);\n  }\n}\n\nstatic void bar_calculate_bounds_top_bottom(struct bar* bar) {\n  bool is_builtin = CGDisplayIsBuiltin(bar->did);\n  uint32_t notch_width = is_builtin ? g_bar_manager.notch_width : 0;\n\n  uint32_t center_length = bar_manager_length_for_bar_side(&g_bar_manager,\n                                                           bar,\n                                                           POSITION_CENTER);\n\n  uint32_t bar_left_first_item_x = max(g_bar_manager.background.padding_left,\n                                       0                                     );\n\n  uint32_t bar_right_first_item_x = bar->window.frame.size.width\n                                   -max(g_bar_manager.background.padding_right,\n                                          0                                  );\n\n  uint32_t bar_center_first_item_x = (bar->window.frame.size.width\n                                      - center_length) / 2;\n\n  uint32_t bar_center_right_first_item_x = (bar->window.frame.size.width\n                                            + notch_width) / 2;\n\n  uint32_t bar_center_left_first_item_x = (bar->window.frame.size.width\n                                           - notch_width) / 2;\n\n  uint32_t* next_position = NULL;\n  uint32_t y = bar->window.frame.size.height / 2;\n\n  for (int i = 0; i < g_bar_manager.bar_item_count; i++) {\n    struct bar_item* bar_item = g_bar_manager.bar_items[i];\n\n    if (!bar_draws_item(bar, bar_item)\n        || bar_item->type == BAR_COMPONENT_GROUP\n        || bar_item->position == POSITION_POPUP ) {\n      continue;\n    } \n\n    uint32_t bar_item_display_length = bar_item_get_length(bar_item, true);\n    bool rtl = false;\n\n    if (bar_item->position == POSITION_LEFT)\n      next_position = &bar_left_first_item_x;\n    else if (bar_item->position == POSITION_CENTER)\n      next_position = &bar_center_first_item_x;\n    else if (bar_item->position == POSITION_RIGHT)\n      next_position = &bar_right_first_item_x, rtl = true;\n    else if (bar_item->position == POSITION_CENTER_RIGHT)\n      next_position = &bar_center_right_first_item_x;\n    else if (bar_item->position == POSITION_CENTER_LEFT)\n      next_position = &bar_center_left_first_item_x, rtl = true;\n    else continue;\n\n    if (bar_item->position == POSITION_RIGHT\n        || bar_item->position == POSITION_CENTER_LEFT) {\n      *next_position = min(*next_position - bar_item_display_length\n                           - bar_item->background.padding_right,\n                           bar->window.frame.size.width\n                           - bar_item_display_length               );\n    }\n    else {\n      *next_position += max((int)-*next_position,\n                            bar_item->background.padding_left);\n    }\n\n    bar_item->graph.rtl = rtl;\n\n    CGPoint shadow_offsets = bar_item_calculate_shadow_offsets(bar_item);\n    uint32_t bar_item_length = bar_item_calculate_bounds(bar_item,\n                                 bar->window.frame.size.height\n                                 - (g_bar_manager.background.border_width + 1),\n                                 max(shadow_offsets.x, 0),\n                                 y                                           );\n\n    CGRect frame = {{bar->window.origin.x + *next_position\n                    - max(shadow_offsets.x, 0),\n                     bar->window.origin.y                 },\n                    {bar_item_display_length\n                      + shadow_offsets.x\n                      + shadow_offsets.y,\n                     bar->window.frame.size.height}         };\n\n    window_set_frame(bar_item_get_window(bar_item, bar->adid), frame);\n\n    if (bar_item->popup.drawing)\n      bar_calculate_popup_anchor_for_bar_item(bar, bar_item);\n\n    if (bar_item->position == POSITION_RIGHT\n        || bar_item->position == POSITION_CENTER_LEFT) {\n      *next_position += bar_item->has_const_width\n                        ? bar_item_display_length\n                          + bar_item->background.padding_right\n                          - bar_item->custom_width\n                        : (- bar_item->background.padding_left);\n    } else {\n      *next_position += bar_item->has_const_width\n                        ? bar_item->custom_width\n                          - bar_item->background.padding_left\n                        : (bar_item_length\n                           + bar_item->background.padding_right);\n    }\n  }\n\n  for (int i = 0; i < g_bar_manager.bar_item_count; i++) {\n    struct bar_item* bar_item = g_bar_manager.bar_items[i];\n\n    if (bar_item->type != BAR_COMPONENT_GROUP\n        || bar_item->position == POSITION_POPUP\n        || !bar_draws_item(bar, bar_item)) {\n      continue;\n    }\n\n    group_calculate_bounds(bar_item->group, bar, y);\n    window_set_frame(bar_item_get_window(bar_item->group->members[0],\n                                         bar->adid                   ),\n                     bar_item->group->bounds                           );\n\n    if (bar_item->popup.drawing)\n      bar_calculate_popup_anchor_for_bar_item(bar, bar_item);\n  }\n}\n\nstatic void bar_calculate_bounds_left_right(struct bar* bar) {\n  uint32_t notch_width = 0;\n\n  uint32_t center_length = bar_manager_length_for_bar_side(&g_bar_manager,\n                                                           bar,\n                                                           POSITION_CENTER);\n\n  uint32_t bar_left_first_item_y = max(g_bar_manager.background.padding_left,\n                                       0                                     );\n\n  uint32_t bar_right_first_item_y = bar->window.frame.size.height\n                                   -max(g_bar_manager.background.padding_right,\n                                          0                                  );\n\n  uint32_t bar_center_first_item_y = (bar->window.frame.size.height\n                                      - 2*g_bar_manager.margin\n                                      - center_length) / 2 - 1;\n\n  uint32_t bar_center_right_first_item_y = (bar->window.frame.size.height\n                                            + notch_width) / 2;\n\n  uint32_t bar_center_left_first_item_y = (bar->window.frame.size.height\n                                           - notch_width) / 2 ;\n\n  uint32_t* next_position = NULL;\n\n  for (int i = 0; i < g_bar_manager.bar_item_count; i++) {\n    struct bar_item* bar_item = g_bar_manager.bar_items[i];\n\n    if (!bar_draws_item(bar, bar_item)\n        || bar_item->type == BAR_COMPONENT_GROUP\n        || bar_item->position == POSITION_POPUP ) {\n      continue;\n    } \n\n    uint32_t bar_item_display_height = bar_item_get_height(bar_item);\n    uint32_t bar_item_display_length = bar_item_get_length(bar_item, true);\n    uint32_t x =  0;\n    bool rtl = false;\n\n    if (bar_item->position == POSITION_LEFT)\n      next_position = &bar_left_first_item_y;\n    else if (bar_item->position == POSITION_CENTER)\n      next_position = &bar_center_first_item_y;\n    else if (bar_item->position == POSITION_RIGHT)\n      next_position = &bar_right_first_item_y, rtl = true;\n    else if (bar_item->position == POSITION_CENTER_RIGHT)\n      next_position = &bar_center_right_first_item_y;\n    else if (bar_item->position == POSITION_CENTER_LEFT)\n      next_position = &bar_center_left_first_item_y, rtl = true;\n    else continue;\n\n    if (bar_item->position == POSITION_RIGHT\n        || bar_item->position == POSITION_CENTER_LEFT) {\n\n      *next_position = min(*next_position - bar_item_display_height\n                           - bar_item->background.padding_right,\n                           bar->window.frame.size.height\n                           - bar_item_display_height               );\n    }\n    else {\n      *next_position += max((int)-*next_position,\n                            bar_item->background.padding_left);\n    }\n\n    bar_item->graph.rtl = rtl;\n\n    CGPoint shadow_offsets = bar_item_calculate_shadow_offsets(bar_item);\n    bar_item_calculate_bounds(bar_item,\n                              bar_item_display_height,\n                              (g_bar_manager.background.bounds.size.height\n                               - bar_item_display_length) / 2.\n                              + max(shadow_offsets.x, 0),\n                              bar_item_display_height / 2.);\n\n\n    CGRect frame = {{bar->window.origin.x + x\n                     - max(shadow_offsets.x, 0),\n                     bar->window.origin.y\n                     + *next_position\n                     + -max(-bar_item->y_offset, 0)},\n                    {g_bar_manager.background.bounds.size.height,\n                     bar_item_display_height + abs(bar_item->y_offset)}};\n\n    window_set_frame(bar_item_get_window(bar_item, bar->adid), frame);\n\n    if (bar_item->popup.drawing)\n      bar_calculate_popup_anchor_for_bar_item(bar, bar_item);\n\n    if (bar_item->position == POSITION_RIGHT\n        || bar_item->position == POSITION_CENTER_LEFT) {\n      *next_position += bar_item->has_const_width\n                        ? bar_item_display_height\n                          + bar_item->background.padding_right\n                          - bar_item->custom_width\n                        : - bar_item->background.padding_left;\n    } else {\n      *next_position += bar_item->has_const_width\n                        ? bar_item->custom_width\n                          - bar_item->background.padding_left\n                        : (bar_item_display_height\n                           + bar_item->background.padding_right);\n    }\n  }\n}\n\nvoid bar_calculate_bounds(struct bar* bar) {\n  if (bar->sid < 1 || bar->adid < 1) return;\n\n  if (g_bar_manager.position == POSITION_LEFT\n      || g_bar_manager.position == POSITION_RIGHT) {\n    bar_calculate_bounds_left_right(bar);\n  } else {\n    bar_calculate_bounds_top_bottom(bar);\n  }\n}\n\nstatic CGRect bar_get_frame(struct bar *bar) {\n  bool is_builtin = CGDisplayIsBuiltin(bar->did);\n  int notch_offset = is_builtin ? g_bar_manager.notch_offset : 0;\n  int notch_display_height = is_builtin ? g_bar_manager.notch_display_height : 0;\n\n\n  CGRect bounds = display_bounds(bar->did);\n  CGPoint origin = bounds.origin;\n\n  if (g_bar_manager.position == POSITION_LEFT\n      || g_bar_manager.position == POSITION_RIGHT) {\n    bounds.size.height -= 2*g_bar_manager.background.y_offset;\n\n    origin.x += (g_bar_manager.position == POSITION_RIGHT\n                 ? (bounds.size.width\n                    - g_bar_manager.background.bounds.size.height\n                    - g_bar_manager.margin)\n                 : g_bar_manager.margin);\n\n    origin.y += g_bar_manager.background.y_offset;\n\n    if (display_menu_bar_visible() && !g_bar_manager.topmost) {\n      CGRect menu = display_menu_bar_rect(bar->did);\n      origin.y += menu.size.height;\n      bounds.size.height -= menu.size.height;\n    }\n\n    return (CGRect) {{origin.x, origin.y},\n                     {g_bar_manager.background.bounds.size.height,\n                      bounds.size.height                          }};\n  } else {\n    bounds.size.width -= 2*g_bar_manager.margin;\n    CGPoint origin = bounds.origin;\n    origin.x += g_bar_manager.margin;\n    origin.y += g_bar_manager.background.y_offset + notch_offset;\n\n\n    if (g_bar_manager.position == POSITION_BOTTOM) {\n      origin.y = CGRectGetMaxY(bounds)\n                 - g_bar_manager.background.bounds.size.height\n                 - 2*(g_bar_manager.background.y_offset) - notch_offset;\n    } else if (display_menu_bar_visible() && !g_bar_manager.topmost) {\n      CGRect menu = display_menu_bar_rect(bar->did);\n      origin.y += menu.size.height;\n    }\n\n\n    if (notch_display_height > 0) {\n      return (CGRect) {{origin.x, origin.y},\n                        {bounds.size.width,\n                        g_bar_manager.notch_display_height}};\n    }\n    \n    return (CGRect) {{origin.x, origin.y},\n                      {bounds.size.width,\n                      g_bar_manager.background.bounds.size.height}};\n  }\n}\n\nvoid bar_resize(struct bar* bar) {\n  if (bar->hidden || !bar->shown) {\n    window_move(&bar->window, g_nirvana);\n    return;\n  };\n  window_set_frame(&bar->window, bar_get_frame(bar));\n  if (window_apply_frame(&bar->window, false)) {\n    window_assign_mouse_tracking_area(&bar->window, bar->window.frame);\n    g_bar_manager.bar_needs_update = true;\n  }\n}\n\nvoid bar_set_hidden(struct bar* bar, bool hidden) {\n  if (bar->hidden == hidden) return;\n  bar->hidden = hidden;\n  \n  if (hidden) window_move(&bar->window, g_nirvana);\n  else bar_resize(bar);\n}\n\nstatic void bar_create_window(struct bar* bar) {\n  window_init(&bar->window);\n  window_create(&bar->window, bar_get_frame(bar));\n  window_assign_mouse_tracking_area(&bar->window, bar->window.frame);\n  window_set_blur_radius(&bar->window, g_bar_manager.blur_radius);\n  if (!g_bar_manager.shadow) window_disable_shadow(&bar->window);\n\n  context_set_font_smoothing(bar->window.context, g_bar_manager.font_smoothing);\n}\n\nvoid bar_change_space(struct bar* bar, uint64_t dsid) {\n  if (bar->adid < 1) return;\n\n  for (uint32_t i = 0; i < g_bar_manager.bar_item_count; i++) {\n    struct bar_item* bar_item = g_bar_manager.bar_items[i];\n    bar_item_change_space(bar_item, dsid, bar->adid);\n  }\n\n  window_send_to_space(&bar->window, dsid);\n}\n\nstruct bar *bar_create(uint32_t did) {\n  struct bar *bar = malloc(sizeof(struct bar));\n  memset(bar, 0, sizeof(struct bar));\n  bar->hidden = false;\n  bar->mouse_over = false;\n  bar->did = did;\n  bar->dsid = display_space_id(did);\n  bar->sid = mission_control_index(bar->dsid);\n  bar->shown = SLSSpaceGetType(g_connection, bar->dsid) != 4;\n  g_bar_manager.bar_needs_update = true;\n  bar_create_window(bar);\n  return bar;\n}\n\nvoid bar_destroy(struct bar *bar) {\n  window_close(&bar->window);\n  free(bar);\n}\n"
  },
  {
    "path": "src/bar.h",
    "content": "#pragma once\n#include \"bar_item.h\"\n#include \"misc/helpers.h\"\n#include \"window.h\"\n\nstruct bar {\n  bool shown;\n  bool hidden;\n  bool mouse_over;\n\n  uint32_t sid;\n  uint32_t dsid;\n  uint32_t did;\n  uint32_t adid;\n\n  struct window window;\n};\n\nstruct bar *bar_create(uint32_t did);\nvoid bar_close_window(struct bar* bar);\nvoid bar_destroy(struct bar* bar);\nvoid bar_set_hidden(struct bar* bar, bool hidden);\nvoid bar_calculate_bounds(struct bar* bar);\nvoid bar_resize(struct bar* bar);\nvoid bar_draw(struct bar* bar, bool forced, bool threaded);\nvoid bar_order_item_windows(struct bar* bar);\n\nbool bar_draws_item(struct bar* bar, struct bar_item* bar_item);\n\nvoid bar_change_space(struct bar* bar, uint64_t dsid);\n\nvoid context_set_font_smoothing(CGContextRef context, bool smoothing);\nvoid join_render_threads();\n"
  },
  {
    "path": "src/bar_item.c",
    "content": "#include \"bar_item.h\"\n#include \"bar_manager.h\"\n#include \"event.h\"\n#include \"volume.h\"\n#include \"power.h\"\n#include \"media.h\"\n#include \"app_windows.h\"\n\nstruct bar_item* bar_item_create() {\n  struct bar_item* bar_item = malloc(sizeof(struct bar_item));\n  memset(bar_item, 0, sizeof(struct bar_item));\n  return bar_item;\n}\n\nvoid bar_item_init(struct bar_item* bar_item, struct bar_item* default_item) {\n  bar_item->needs_update = true;\n  bar_item->lazy = true;\n  bar_item->drawing = true;\n  bar_item->updates = true;\n  bar_item->updates_only_when_shown = false;\n  bar_item->selected = false;\n  bar_item->ignore_association = false;\n  bar_item->overrides_association = false;\n  bar_item->counter = 0;\n  bar_item->type = BAR_ITEM;\n  bar_item->update_frequency = 0;\n  bar_item->position = POSITION_LEFT;\n  bar_item->align = POSITION_LEFT;\n  bar_item->associated_to_active_display = false;\n  bar_item->associated_display = 0;\n  bar_item->associated_space = 0;\n  bar_item->associated_bar = 0;\n  bar_item->blur_radius = 0;\n  bar_item->event_port = 0;\n  bar_item->shadow = false;\n  bar_item->scroll_texts = false;\n  bar_item->mouse_over = false;\n\n  bar_item->has_const_width = false;\n  bar_item->custom_width = 0;\n\n  bar_item->y_offset = 0;\n  \n  bar_item->has_alias = false;\n  bar_item->has_graph = false;\n\n  bar_item->name = NULL;\n  bar_item->script = NULL;\n  bar_item->click_script = NULL;\n\n  bar_item->group = NULL;\n  bar_item->parent = NULL;\n\n  text_init(&bar_item->icon);\n  text_init(&bar_item->label);\n  background_init(&bar_item->background);\n  env_vars_init(&bar_item->signal_args.env_vars);\n  popup_init(&bar_item->popup, bar_item);\n  graph_init(&bar_item->graph);\n  alias_init(&bar_item->alias);\n  slider_init(&bar_item->slider);\n  \n  if (default_item) bar_item_inherit_from_item(bar_item, default_item);\n}\n\nvoid bar_item_append_associated_space(struct bar_item* bar_item, uint32_t bit) {\n  if (bar_item->associated_space & bit) return;\n  bar_item->associated_space |= bit;\n  if (bar_item->type == BAR_COMPONENT_SPACE) {\n    bar_item->associated_space = bit;\n    char sid_str[32];\n    snprintf(sid_str, 32, \"%u\", get_set_bit_position(bit));\n    env_vars_set(&bar_item->signal_args.env_vars,\n                 string_copy(\"SID\"),\n                 string_copy(sid_str)            );\n  }\n}\n\nvoid bar_item_append_associated_display(struct bar_item* bar_item, uint32_t bit) {\n  if (bar_item->associated_display & bit) return;\n  bar_item->associated_display |= bit;\n  if (bar_item->type == BAR_COMPONENT_SPACE) {\n    bar_item->overrides_association = true;\n    bar_item->associated_display = bit;\n    char did_str[32];\n    snprintf(did_str, 32, \"%u\", get_set_bit_position(bit));\n    env_vars_set(&bar_item->signal_args.env_vars,\n                 string_copy(\"DID\"),\n                 string_copy(did_str)            );\n  }\n}\n\nbool bar_item_is_shown(struct bar_item* bar_item) {\n  if (bar_item->associated_bar & UINT32_MAX) return true;\n  else return false;\n}\n\nvoid bar_item_append_associated_bar(struct bar_item* bar_item, uint32_t adid) {\n  bar_item->associated_bar |= (1 << (adid - 1));\n}\n\nvoid bar_item_remove_associated_bar(struct bar_item* bar_item, uint32_t adid) {\n  bar_item->associated_bar &= ~(1 << (adid - 1)); \n}\n\nvoid bar_item_reset_associated_bar(struct bar_item* bar_item) {\n  bar_item->associated_bar = 0;\n}\n\nbool bar_item_update(struct bar_item* bar_item, char* sender, bool forced, struct env_vars* env_vars) {\n  bool is_shown = bar_item_is_shown(bar_item);\n  if (is_shown && bar_item->scroll_texts && (bar_item->counter % 15 == 0)) {\n    text_animate_scroll(&bar_item->icon);\n    text_animate_scroll(&bar_item->label);\n    if (bar_item->type == BAR_COMPONENT_SLIDER)\n      text_animate_scroll(&bar_item->slider.knob);\n  }\n\n  bar_item->counter++;\n\n  if ((!bar_item->updates || (bar_item->update_frequency == 0 && !sender))\n      && !forced                                                          ) {\n    return false;\n  }\n\n  bool scheduled_update_needed = bar_item->update_frequency\n                                  <= bar_item->counter;\n\n  bool should_update = bar_item->updates_only_when_shown\n                       ? is_shown\n                       : true;\n\n  if (((scheduled_update_needed || sender) && should_update) || forced) {\n    bar_item->counter = 0;\n\n    if ((bar_item->script && strlen(bar_item->script) > 0)\n         || bar_item->event_port                          ) {\n      if (!env_vars)\n        env_vars = &bar_item->signal_args.env_vars;\n      else {\n        for (int i = 0; i < bar_item->signal_args.env_vars.count; i++) {\n          env_vars_set(env_vars,\n                       string_copy(bar_item->signal_args.env_vars.vars[i]->key),\n                       string_copy(bar_item->signal_args.env_vars.vars[i]->value));\n        }\n        env_vars_set(env_vars,\n                     string_copy(\"NAME\"),\n                     string_copy(bar_item->name));\n      }\n\n      if (sender)\n        env_vars_set(env_vars, string_copy(\"SENDER\"), string_copy(sender));\n      else\n        env_vars_set(env_vars,\n                     string_copy(\"SENDER\"),\n                     string_copy(forced ? \"forced\" : \"routine\"));\n    }\n    // Script Update\n    if (bar_item->script && strlen(bar_item->script) > 0) {\n      fork_exec(bar_item->script, env_vars);\n    }\n\n    // Mach events\n    if (bar_item->event_port) {\n      uint32_t len = 0;\n      char* message = env_vars_copy_serialized_representation(env_vars, &len);\n\n      mach_send_message(bar_item->event_port, message, len, false);\n      free(message);\n    }\n  }\n\n  return false;\n}\n\nvoid bar_item_needs_update(struct bar_item* bar_item) {\n  bar_item->needs_update = true;\n}\n\nvoid bar_item_cancel_drag(struct bar_item* bar_item) {\n  if (bar_item->has_slider) {\n    char perc_str[8];\n    snprintf(perc_str, 8, \"%d\", bar_item->slider.percentage);\n    env_vars_set(&bar_item->signal_args.env_vars,\n                 string_copy(\"PERCENTAGE\"),\n                 string_copy(perc_str)           );\n\n    slider_cancel_drag(&bar_item->slider);\n  }\n}\n\nvoid bar_item_on_drag(struct bar_item* bar_item, CGPoint point) {\n  if (bar_item->has_slider) {\n    if (slider_handle_drag(&bar_item->slider, point)) {\n      bar_item_needs_update(bar_item);\n    }\n  }\n}\n\nvoid bar_item_on_click(struct bar_item* bar_item, uint32_t type, uint32_t mouse_button_code, uint32_t modifier, CGPoint point) {\n  if (!bar_item) return;\n\n  struct env_vars env_vars;\n  env_vars_init(&env_vars);\n  char info_str[256];\n  snprintf(info_str, 256, \"{\\n\"\n                         \"\\t\\\"button\\\": \\\"%s\\\",\\n\"\n                         \"\\t\\\"button_code\\\": %u,\\n\"\n                         \"\\t\\\"modifier\\\": \\\"%s\\\",\\n\"\n                         \"\\t\\\"modfier_code\\\": %u\\n\"\n                         \"}\\n\",\n                         get_type_description(type),\n                         mouse_button_code,\n                         get_modifier_description(modifier),\n                         modifier                           );\n\n  env_vars_set(&env_vars, string_copy(\"INFO\"), string_copy(info_str));\n\n  env_vars_set(&env_vars,\n               string_copy(\"BUTTON\"),\n               string_copy(get_type_description(type)));\n\n  env_vars_set(&env_vars,\n               string_copy(\"MODIFIER\"),\n               string_copy(get_modifier_description(modifier)));\n\n  if (bar_item->has_slider) {\n    if (bar_item->slider.is_dragged\n        || CGRectContainsPoint(bar_item->slider.background.bounds, point)) {\n      if (slider_handle_drag(&bar_item->slider, point)) {\n        bar_item_needs_update(bar_item);\n      }\n\n      bar_item_cancel_drag(bar_item);\n    } else {\n      env_vars_destroy(&env_vars);\n      return;\n    }\n  }\n\n  if (bar_item->click_script && strlen(bar_item->click_script) > 0) {\n    for (int i = 0; i < bar_item->signal_args.env_vars.count; i++) {\n      env_vars_set(&env_vars,\n                   string_copy(bar_item->signal_args.env_vars.vars[i]->key),\n                   string_copy(bar_item->signal_args.env_vars.vars[i]->value));\n    }\n\n    fork_exec(bar_item->click_script, &env_vars);\n  }\n  if (bar_item->update_mask & UPDATE_MOUSE_CLICKED)\n    bar_item_update(bar_item,\n                    COMMAND_SUBSCRIBE_MOUSE_CLICKED,\n                    true,\n                    &env_vars                       );\n\n  env_vars_destroy(&env_vars);\n}\n\nvoid bar_item_on_scroll(struct bar_item* bar_item, int scroll_delta, uint32_t modifier) {\n  if (!bar_item) return;\n\n  struct env_vars env_vars;\n  env_vars_init(&env_vars);\n  char info_str[256];\n  snprintf(info_str, 256, \"{\\n\"\n                          \"\\t\\\"delta\\\": %d,\\n\"\n                          \"\\t\\\"modifier\\\": \\\"%s\\\",\\n\"\n                          \"\\t\\\"modfier_code\\\": %u\\n\"\n                          \"}\\n\",\n                          scroll_delta,\n                          get_modifier_description(modifier),\n                          modifier                           );\n\n  env_vars_set(&env_vars, string_copy(\"INFO\"), string_copy(info_str));\n\n  char delta_ver_str[32];\n  snprintf(delta_ver_str, 32, \"%d\", scroll_delta);\n  env_vars_set(&env_vars,\n               string_copy(\"SCROLL_DELTA\"),\n               string_copy(delta_ver_str));\n\n  env_vars_set(&env_vars,\n               string_copy(\"MODIFIER\"),\n               string_copy(get_modifier_description(modifier)));\n\n\n  if (bar_item->update_mask & UPDATE_MOUSE_SCROLLED)\n    bar_item_update(bar_item,\n                    COMMAND_SUBSCRIBE_MOUSE_SCROLLED,\n                    true,\n                    &env_vars                        );\n\n\n  env_vars_destroy(&env_vars);\n}\n\nvoid bar_item_mouse_entered(struct bar_item* bar_item) {\n  if (bar_item->update_mask & UPDATE_MOUSE_ENTERED && !bar_item->mouse_over) {\n    bar_item_update(bar_item, COMMAND_SUBSCRIBE_MOUSE_ENTERED, true, NULL);\n  }\n  bar_item->mouse_over = true;\n}\n\nvoid bar_item_mouse_exited(struct bar_item* bar_item) {\n  if (bar_item->update_mask & UPDATE_MOUSE_EXITED) {\n    bar_item_update(bar_item, COMMAND_SUBSCRIBE_MOUSE_EXITED, true, NULL); \n  }\n  bar_item->mouse_over = false;\n}\n\nstatic bool bar_item_set_drawing(struct bar_item* bar_item, bool state) {\n  if (bar_item->drawing == state) return false;\n  bar_item->drawing = state;\n  return true;\n}\n\nstatic void bar_item_set_script(struct bar_item* bar_item, char* script) {\n  if (!script) return;\n\n  if (bar_item->script && strcmp(bar_item->script, script) == 0) {\n    free(script);\n    return;\n  }\n\n  if (script != bar_item->script && bar_item->script)\n    free(bar_item->script);\n\n  char* path = resolve_path(script);\n  if (path) bar_item->script = path;\n}\n\nstatic void bar_item_set_click_script(struct bar_item* bar_item, char* script) {\n  if (!script) return;\n\n  if (bar_item->click_script && strcmp(bar_item->click_script, script) == 0) {\n    free(script);\n    return;\n  }\n\n  if (script != bar_item->click_script && bar_item->click_script)\n    free(bar_item->click_script);\n\n  char* path = resolve_path(script);\n  if (path) bar_item->click_script = path;\n}\n\nstatic bool bar_item_set_yoffset(struct bar_item* bar_item, int offset) {\n  if (bar_item->y_offset == offset) return false;\n  bar_item->y_offset = offset;\n  return true;\n}\n\nstatic bool bar_item_set_blur_radius(struct bar_item* bar_item, uint32_t radius) {\n  if (bar_item->blur_radius == radius) return false;\n  bar_item->blur_radius = radius;\n  for (int i = 0; i < bar_item->num_windows; i++) {\n    if (!bar_item->windows[i]) continue;\n    window_set_blur_radius(bar_item->windows[i], radius);\n  }\n  return true;\n}\n\nstatic bool bar_item_set_width(struct bar_item* bar_item, int width) {\n  if (width < 0) {\n    bool prev = bar_item->has_const_width;\n    bar_item->has_const_width = false;\n    return prev != bar_item->has_const_width;\n  }\n  if (bar_item->custom_width == width && bar_item->has_const_width)\n    return false;\n\n  bar_item->custom_width = width;\n  bar_item->has_const_width = true;\n  return true;\n}\n\nstatic void bar_item_set_event_port(struct bar_item* bar_item, char* bs_name) {\n  mach_port_t port = mach_get_bs_port(bs_name);\n  bar_item->event_port = port;\n}\n\nbool bar_item_set_name(struct bar_item* bar_item, char* name) {\n  if (!name) return false;\n\n  if (bar_item->name && strcmp(bar_item->name, name) == 0) {\n    free(name);\n    return true;\n  }\n\n  if (strlen(name) == 0) return false;\n\n  if (name != bar_item->name && bar_item->name) free(bar_item->name);\n  bar_item->name = name;\n  env_vars_set(&bar_item->signal_args.env_vars,\n               string_copy(\"NAME\"),\n               string_copy(name)               );\n  return true;\n}\n\nbool bar_item_set_type(struct bar_item* bar_item, char* type) {\n  bool success = true;\n\n  if (string_equals(type, TYPE_SPACE)) {\n    bar_item->type = BAR_COMPONENT_SPACE;\n  } else if (string_equals(type, TYPE_ALIAS)) {\n    bar_item->type = BAR_COMPONENT_ALIAS;\n  } else if (string_equals(type, TYPE_GROUP)) {\n    bar_item->type = BAR_COMPONENT_GROUP;\n  } else if (string_equals(type, TYPE_GRAPH)) {\n    bar_item->type = BAR_COMPONENT_GRAPH;\n  } else if (string_equals(type, TYPE_SLIDER)) {\n    bar_item->type = BAR_COMPONENT_SLIDER;\n  } else {\n    bar_item->type = BAR_ITEM;\n    success = string_equals(type, TYPE_ITEM);\n  }\n\n  if (bar_item->type == BAR_COMPONENT_SPACE) {\n    if (!bar_item->script) { \n        bar_item_set_script(bar_item,\n            string_copy(\"sketchybar -m --set $NAME icon.highlight=$SELECTED\"));\n    }\n\n    bar_item->update_mask |= UPDATE_SPACE_CHANGE;\n    bar_item->updates = false;\n    bar_item->updates_only_when_shown = false;\n    env_vars_set(&bar_item->signal_args.env_vars,\n                 string_copy(\"SELECTED\"),\n                 string_copy(\"false\")            );\n\n    env_vars_set(&bar_item->signal_args.env_vars,\n                 string_copy(\"SID\"),\n                 string_copy(\"0\")                );\n\n    env_vars_set(&bar_item->signal_args.env_vars,\n                 string_copy(\"DID\"),\n                 string_copy(\"0\")                );\n  }\n  else if (bar_item->type == BAR_COMPONENT_ALIAS) {\n    bar_item->has_alias = true;\n  }\n  else if (bar_item->type == BAR_COMPONENT_GRAPH) {\n    bar_item->has_graph = true;\n  }\n  else if (bar_item->type == BAR_COMPONENT_SLIDER) {\n    bar_item->has_slider = true;\n  }\n  else if (bar_item->type == BAR_COMPONENT_GROUP) {\n    bar_item->group = group_create();\n    group_init(bar_item->group);\n    group_add_member(bar_item->group, bar_item);\n  }\n  return success;\n}\n\nbool bar_item_set_position(struct bar_item* bar_item, char* position) {\n  if (!position || strlen(position) == 0) return false;\n\n  switch (position[0]) {\n    case POSITION_LEFT:\n      break;\n    case POSITION_CENTER_LEFT:\n      break;\n    case POSITION_CENTER:\n      break;\n    case POSITION_CENTER_RIGHT:\n      break;\n    case POSITION_RIGHT:\n      break;\n    case POSITION_POPUP:\n      break;\n    default:\n      return false;\n  }\n\n  if (bar_item->parent != NULL){\n    popup_remove_item(&bar_item->parent->popup,bar_item);\n  }\n\n  bar_item->position = position[0];\n  if (position[0] != POSITION_POPUP)\n    bar_item->align = position[0];\n  return true;\n}\n\nbool bar_item_set_media_cover(struct bar_item* bar_item, struct image* image) {\n  bool linked = (bar_item->background.image.link == image\n                 ||bar_item->icon.background.image.link == image\n                 ||bar_item->label.background.image.link == image);\n  if (linked) bar_item_needs_update(bar_item);\n  return linked && bar_item_is_shown(bar_item);\n}\n\nstatic uint32_t bar_item_get_content_length(struct bar_item* bar_item) {\n  int length = text_get_length(&bar_item->icon, false)\n         + text_get_length(&bar_item->label, false)\n         + (bar_item->has_graph  ? graph_get_length(&bar_item->graph) : 0)\n         + (bar_item->has_slider ? slider_get_length(&bar_item->slider) : 0)\n         + (bar_item->has_alias  ? alias_get_length(&bar_item->alias) : 0);\n \n  return max(length, 0);\n}\n\nuint32_t bar_item_get_length(struct bar_item* bar_item, bool ignore_override) {\n  uint32_t content_length = bar_item_get_content_length(bar_item);\n  if (bar_item->background.enabled && bar_item->background.image.enabled) {\n    CGSize image_size = image_get_size(&bar_item->background.image);\n\n    if (image_size.width > content_length) content_length = image_size.width;\n  }\n\n  if (bar_item->has_const_width && (!ignore_override\n                                    || bar_item->custom_width\n                                       > content_length      )) {\n    return bar_item->custom_width;\n  }\n\n  return content_length;\n}\n\nuint32_t bar_item_get_height(struct bar_item* bar_item) {\n  uint32_t label_height = text_get_height(&bar_item->label);\n  uint32_t icon_height = text_get_height(&bar_item->icon);\n  uint32_t alias_height = alias_get_height(&bar_item->alias);\n  \n  uint32_t text_height = max(label_height, icon_height);\n  uint32_t item_height = max(text_height, alias_height);\n\n  uint32_t background_height = 0;\n  if (bar_item->background.enabled) {\n    uint32_t image_height = bar_item->background.image.enabled\n                            ? image_get_size(&bar_item->background.image).height\n                            : 0;\n\n    background_height = max(image_height,\n                            bar_item->background.bounds.size.height);\n  }\n\n  return max(item_height, background_height);\n}\n\nstruct window* bar_item_get_window(struct bar_item* bar_item, uint32_t adid) {\n  if (adid < 1 || !bar_item) return NULL;\n  if (bar_item->num_windows < adid) {\n    bar_item->windows = (struct window**) realloc(bar_item->windows,\n                                                  sizeof(struct window*)*adid);\n    memset(bar_item->windows + bar_item->num_windows,\n           0,\n           sizeof(struct window*) * (adid - bar_item->num_windows));\n\n    bar_item->num_windows = adid;\n  }\n\n  if (!bar_item->windows[adid - 1]) {\n    bar_item->windows[adid - 1] = malloc(sizeof(struct window));\n    window_init(bar_item->windows[adid - 1]);\n    window_create(bar_item->windows[adid - 1],\n                  (CGRect){{g_nirvana.x,g_nirvana.y}, {1, 1}});\n    if (!bar_item->shadow) window_disable_shadow(bar_item->windows[adid - 1]);\n    window_set_blur_radius(bar_item->windows[adid - 1], bar_item->blur_radius);\n    context_set_font_smoothing(bar_item->windows[adid - 1]->context,\n                               g_bar_manager.font_smoothing         );\n    \n    if (bar_item->parent)\n      bar_item->parent->popup.needs_ordering = true;\n    else\n      g_bar_manager.needs_ordering = true;\n  }\n\n  return bar_item->windows[adid - 1];\n}\n\nvoid bar_item_remove_window(struct bar_item* bar_item, uint32_t adid) {\n  if (bar_item->num_windows >= adid && bar_item->windows[adid - 1]) {\n    window_close(bar_item->windows[adid - 1]);\n    free(bar_item->windows[adid - 1]);\n    bar_item->windows[adid - 1] = NULL;\n  }\n}\n\nCGPoint bar_item_calculate_shadow_offsets(struct bar_item* bar_item) {\n  CGPoint offset;\n  offset.x = (int)((bar_item->background.shadow.enabled\n                ? max(-bar_item->background.shadow.offset.x, 0)\n                : 0)\n             + (bar_item->icon.shadow.enabled\n                ? max(-bar_item->icon.shadow.offset.x, 0)\n                : 0)\n             + (bar_item->icon.background.shadow.enabled\n                ? max(-bar_item->icon.background.shadow.offset.x, 0)\n                : 0)\n             + (bar_item->label.background.shadow.enabled\n                ? max(-bar_item->label.background.shadow.offset.x, 0)\n                : 0)\n             + (bar_item->label.shadow.enabled\n                ? max(-bar_item->label.shadow.offset.x, 0)\n                : 0)\n             + (bar_item->background.enabled\n                ? max(-bar_item->background.x_offset, 0)\n                : 0));\n\n  offset.y = (int)((bar_item->background.shadow.enabled\n                 ? max(bar_item->background.shadow.offset.x,0)\n                 : 0)\n              + (bar_item->icon.shadow.enabled\n                 ? max(bar_item->icon.shadow.offset.x, 0)\n                 : 0)\n              + (bar_item->icon.background.shadow.enabled\n                 ? max(bar_item->icon.background.shadow.offset.x, 0)\n                 : 0)\n              + (bar_item->label.background.shadow.enabled\n                 ? max(bar_item->label.background.shadow.offset.x, 0)\n                 : 0)\n              + (bar_item->label.shadow.enabled\n                 ? max(bar_item->label.shadow.offset.x, 0)\n                 : 0)\n              + (bar_item->background.enabled\n                 ? max(bar_item->background.x_offset, 0)\n                 : 0));\n  return offset;\n}\n\nuint32_t bar_item_calculate_bounds(struct bar_item* bar_item, uint32_t bar_height, uint32_t x, uint32_t y) {\n  uint32_t content_x = x;\n  uint32_t content_y = y;\n\n  uint32_t bar_item_length = bar_item_get_length(bar_item, false);\n  uint32_t bar_item_content_length = bar_item_get_content_length(bar_item);\n  if (bar_item_length > bar_item_content_length) {\n    if (bar_item->align == POSITION_CENTER)\n      content_x += (bar_item_length - bar_item_content_length) / 2;\n    else if (bar_item->align == POSITION_RIGHT)\n      content_x += bar_item_length - bar_item_content_length;\n  }\n\n  uint32_t icon_position = content_x;\n  uint32_t label_position = icon_position + text_get_length(&bar_item->icon,\n                                                            false           );\n\n  uint32_t sandwich_position = label_position;\n  if (bar_item->has_graph) {\n    label_position += graph_get_length(&bar_item->graph);\n  } else if (bar_item->has_alias) {\n    label_position += alias_get_length(&bar_item->alias); \n  } else if (bar_item->has_slider) {\n    label_position += slider_get_length(&bar_item->slider); \n  }\n\n\n  text_calculate_bounds(&bar_item->icon,\n                        icon_position,\n                        content_y + bar_item->y_offset);\n\n  text_calculate_bounds(&bar_item->label,\n                        label_position,\n                        content_y + bar_item->y_offset);\n\n  if (bar_item->has_alias)\n    alias_calculate_bounds(&bar_item->alias,\n                           sandwich_position,\n                           content_y + bar_item->y_offset);\n\n  if (bar_item->has_slider)\n    slider_calculate_bounds(&bar_item->slider,\n                            sandwich_position,\n                            content_y + bar_item->y_offset);\n\n  if (bar_item->has_graph) {\n    uint32_t height = bar_item->background.enabled\n                      ? (bar_item->background.bounds.size.height\n                         - bar_item->background.border_width - 1)\n                      : (bar_height\n                         - (g_bar_manager.background.border_width + 1));\n\n    graph_calculate_bounds(&bar_item->graph,\n                           sandwich_position,\n                           content_y + bar_item->y_offset,\n                           height                         );\n  }\n\n  if (bar_item->background.enabled) {\n    uint32_t height = bar_item->background.overrides_height\n                      ? bar_item->background.bounds.size.height\n                      : (bar_height\n                         - (g_bar_manager.background.border_width + 1));\n\n    background_calculate_bounds(&bar_item->background,\n                                x,\n                                content_y + bar_item->y_offset,\n                                bar_item_length,\n                                height                         );\n  }\n\n  return bar_item_length;\n}\n\nbool bar_item_clip_needs_update_for_bar(struct bar_item* bar_item, struct bar* bar) {\n  bool needs_update = false;\n\n  needs_update |= background_clip_needs_update(&bar_item->background, bar)\n               || background_clip_needs_update(&bar_item->icon.background, bar)\n               || background_clip_needs_update(&bar_item->label.background, bar);\n\n  return needs_update;\n}\n\nbool bar_item_clips_bar(struct bar_item* bar_item) {\n  return background_clips_bar(&bar_item->background)\n         || background_clips_bar(&bar_item->icon.background)\n         || background_clips_bar(&bar_item->label.background);\n}\n\nvoid bar_item_clip_bar(struct bar_item* bar_item, int offset, struct bar* bar) {\n  background_clip_bar(&bar_item->background, offset, bar);\n  background_clip_bar(&bar_item->icon.background, offset, bar);\n  background_clip_bar(&bar_item->label.background, offset, bar);\n}\n\nvoid* draw_item_proc(void* context) {\n  struct { struct window* window; struct bar_item bar_item; }* info = context;\n  CGContextClearRect(info->window->context, info->window->frame);\n  bar_item_draw(&info->bar_item, info->window->context);\n  CGContextFlush(info->window->context);\n  window_flush(info->window);\n  free(context);\n  return NULL;\n}\n\nvoid bar_item_draw(struct bar_item* bar_item, CGContextRef context) {\n  background_draw(&bar_item->background, context);\n  if (bar_item->type == BAR_COMPONENT_GROUP) return;\n\n  text_draw(&bar_item->icon, context);\n  text_draw(&bar_item->label, context);\n\n  if (bar_item->has_alias) alias_draw(&bar_item->alias, context);\n  if (bar_item->has_graph) graph_draw(&bar_item->graph, context);\n  if (bar_item->has_slider) slider_draw(&bar_item->slider, context);\n}\n\nvoid bar_item_change_space(struct bar_item* bar_item, uint64_t dsid, uint32_t adid) {\n  if (bar_item->num_windows >= adid && bar_item->windows[adid - 1]) {\n    window_send_to_space(bar_item->windows[adid - 1], dsid);\n  }\n  popup_change_space(&bar_item->popup, dsid, adid);\n}\n\nstatic void bar_item_clear_pointers(struct bar_item* bar_item) {\n  bar_item->name = NULL;\n  bar_item->script = NULL;\n  bar_item->click_script = NULL;\n  bar_item->group = NULL;\n  bar_item->signal_args.env_vars.vars = NULL;\n  bar_item->signal_args.env_vars.count = 0;\n  bar_item->windows = NULL;\n  bar_item->num_windows = 0;\n  text_clear_pointers(&bar_item->icon);\n  text_clear_pointers(&bar_item->label);\n  background_clear_pointers(&bar_item->background);\n  slider_clear_pointers(&bar_item->slider);\n  popup_clear_pointers(&bar_item->popup);\n  bar_item->popup.host = bar_item;\n}\n\nvoid bar_item_inherit_from_item(struct bar_item* bar_item, struct bar_item* ancestor) {\n  text_destroy(&bar_item->icon);\n  text_destroy(&bar_item->label);\n  text_destroy(&bar_item->slider.knob);\n  \n  char* name = bar_item->name;\n  char* script = bar_item->script;\n  char* click_script = bar_item->click_script;\n\n  memcpy(bar_item, ancestor, sizeof(struct bar_item));\n  bar_item_clear_pointers(bar_item);\n\n  bar_item->name = name;\n  bar_item->script = script;\n  bar_item->click_script = click_script;\n\n  text_copy(&bar_item->icon, &ancestor->icon);\n  text_copy(&bar_item->label, &ancestor->label);\n  text_copy(&bar_item->slider.knob, &ancestor->slider.knob);\n\n  if (ancestor->script)\n    bar_item_set_script(bar_item, string_copy(ancestor->script));\n  if (ancestor->click_script)\n    bar_item_set_click_script(bar_item, string_copy(ancestor->click_script));\n\n  image_copy(&bar_item->background.image,\n             ancestor->background.image.image_ref);\n\n  image_copy(&bar_item->icon.background.image,\n             ancestor->icon.background.image.image_ref);\n\n  image_copy(&bar_item->label.background.image,\n             ancestor->label.background.image.image_ref);\n\n  if (bar_item->type == BAR_COMPONENT_SPACE) {\n    env_vars_set(&bar_item->signal_args.env_vars,\n                 string_copy(\"SELECTED\"),\n                 string_copy(\"false\")            );\n\n    env_vars_set(&bar_item->signal_args.env_vars,\n                 string_copy(\"SID\"),\n                 string_copy(env_vars_get_value_for_key(&ancestor->signal_args.env_vars,\n                                                        \"DID\")                          ));\n    env_vars_set(&bar_item->signal_args.env_vars,\n                 string_copy(\"DID\"),\n                 string_copy(env_vars_get_value_for_key(&ancestor->signal_args.env_vars,\n                                                        \"DID\")                          ));\n  }\n}\n\nvoid bar_item_destroy(struct bar_item* bar_item, bool free_memory) {\n  if (bar_item->name) free(bar_item->name);\n  if (bar_item->script) free(bar_item->script);\n  if (bar_item->click_script) free(bar_item->click_script);\n\n  text_destroy(&bar_item->icon);\n  text_destroy(&bar_item->label);\n\n  graph_destroy(&bar_item->graph);\n  alias_destroy(&bar_item->alias);\n  slider_destroy(&bar_item->slider);\n\n  if (bar_item->group && bar_item->type == BAR_COMPONENT_GROUP)\n    group_destroy(bar_item->group);\n  else if (bar_item->group)\n    group_remove_member(bar_item->group, bar_item);\n\n  env_vars_destroy(&bar_item->signal_args.env_vars);\n  popup_destroy(&bar_item->popup);\n  background_destroy(&bar_item->background);\n\n  for (int j = 1; j <= bar_item->num_windows; j++) {\n    bar_item_remove_window(bar_item, j);\n  }\n  if (bar_item->windows) free(bar_item->windows);\n\n  if (free_memory) free(bar_item);\n}\n\nvoid bar_item_serialize(struct bar_item* bar_item, FILE* rsp) {\n  char type[32] = { 0 };\n  switch (bar_item->type) {\n    case BAR_ITEM:\n      snprintf(type, 32, \"item\");\n      break;\n    case BAR_COMPONENT_ALIAS:\n      snprintf(type, 32, \"alias\");\n      break;\n    case BAR_COMPONENT_GROUP:\n      snprintf(type, 32, \"bracket\");\n      break;\n    case BAR_COMPONENT_SLIDER:\n      snprintf(type, 32, \"slider\");\n      break;\n    case BAR_COMPONENT_GRAPH:\n      snprintf(type, 32, \"graph\");\n      break;\n    case BAR_COMPONENT_SPACE:\n      snprintf(type, 32, \"space\");\n      break;\n    default:\n      snprintf(type, 32, \"invalid\");\n      break;\n  }\n\n  char position[32] = { 0 };\n  switch (bar_item->position) {\n    case POSITION_LEFT:\n      snprintf(position, 32, \"left\");\n      break;\n    case POSITION_RIGHT:\n      snprintf(position, 32, \"right\");\n      break;\n    case POSITION_CENTER:\n      snprintf(position, 32, \"center\");\n      break;\n    case POSITION_CENTER_LEFT:\n      snprintf(position, 32, \"q\");\n      break;\n    case POSITION_CENTER_RIGHT:\n      snprintf(position, 32, \"e\");\n      break;\n    case POSITION_POPUP:\n      snprintf(position, 32, \"popup\");\n      break;\n    default:\n      snprintf(position, 32, \"invalid\");\n      break;\n  }\n\n  fprintf(rsp, \"{\\n\"\n               \"\\t\\\"name\\\": \\\"%s\\\",\\n\"\n               \"\\t\\\"type\\\": \\\"%s\\\",\\n\"\n               \"\\t\\\"geometry\\\": {\\n\"\n               \"\\t\\t\\\"drawing\\\": \\\"%s\\\",\\n\"\n               \"\\t\\t\\\"position\\\": \\\"%s\\\",\\n\"\n               \"\\t\\t\\\"associated_space_mask\\\": %u,\\n\"\n               \"\\t\\t\\\"associated_display_mask\\\": %u,\\n\"\n               \"\\t\\t\\\"ignore_association\\\": \\\"%s\\\",\\n\"\n               \"\\t\\t\\\"y_offset\\\": %d,\\n\"\n               \"\\t\\t\\\"padding_left\\\": %d,\\n\"\n               \"\\t\\t\\\"padding_right\\\": %d,\\n\"\n               \"\\t\\t\\\"scroll_texts\\\": \\\"%s\\\",\\n\"\n               \"\\t\\t\\\"width\\\": %d,\\n\"\n               \"\\t\\t\\\"background\\\": {\\n\",\n               bar_item->name,\n               type,\n               format_bool(bar_item->drawing),\n               position,\n               bar_item->associated_space,\n               bar_item->associated_display,\n               format_bool(bar_item->ignore_association),\n               bar_item->y_offset,\n               bar_item->background.padding_left,\n               bar_item->background.padding_right,\n               format_bool(bar_item->scroll_texts),\n               bar_item->has_const_width ? bar_item->custom_width : -1);\n\n  background_serialize(&bar_item->background, \"\\t\\t\\t\", rsp, true);\n  fprintf(rsp, \"\\n\\t\\t}\\n\\t},\\n\");\n\n  fprintf(rsp, \"\\t\\\"icon\\\": {\\n\");\n  text_serialize(&bar_item->icon, \"\\t\\t\", rsp);\n  fprintf(rsp, \"\\n\\t},\\n\");\n\n  fprintf(rsp, \"\\t\\\"label\\\": {\\n\");\n  text_serialize(&bar_item->label, \"\\t\\t\", rsp);\n  fprintf(rsp, \"\\n\\t},\\n\");\n\n\n  char* escaped_script = escape_string(bar_item->script);\n  char* escaped_click_script = escape_string(bar_item->click_script);\n  fprintf(rsp, \"\\t\\\"scripting\\\": {\\n\"\n               \"\\t\\t\\\"script\\\": \\\"%s\\\",\\n\"\n               \"\\t\\t\\\"click_script\\\": \\\"%s\\\",\\n\"\n               \"\\t\\t\\\"update_freq\\\": %u,\\n\"\n               \"\\t\\t\\\"update_mask\\\": %llu,\\n\"\n               \"\\t\\t\\\"updates\\\": \\\"%s\\\"\\n\\t},\\n\",\n               escaped_script,\n               escaped_click_script,\n               bar_item->update_frequency,\n               bar_item->update_mask,\n               bar_item->updates_only_when_shown\n                ? \"when_shown\"\n                : format_bool(bar_item->updates) );\n\n  if (escaped_script) free(escaped_script);\n  if (escaped_click_script) free(escaped_click_script);\n\n  fprintf(rsp, \"\\t\\\"bounding_rects\\\": {\\n\");\n  int counter = 0;\n  for (int i = 0; i < bar_item->num_windows; i++) {\n    if (!bar_item->windows[i]) continue;\n    if (counter++ > 0) fprintf(rsp, \",\\n\");\n    fprintf(rsp, \"\\t\\t\\\"display-%d\\\": {\\n\"\n            \"\\t\\t\\t\\\"origin\\\": [ %f, %f ],\\n\"\n            \"\\t\\t\\t\\\"size\\\": [ %f, %f ]\\n\\t\\t}\",\n            i + 1,\n            bar_item->windows[i]->origin.x,\n            bar_item->windows[i]->origin.y,\n            bar_item->windows[i]->frame.size.width,\n            bar_item->windows[i]->frame.size.height);\n  } \n  fprintf(rsp, \"\\n\\t}\");\n\n  if (bar_item->popup.num_items > 0) {\n    fprintf(rsp, \",\\n\\t\\\"popup\\\": {\\n\");\n    popup_serialize(&bar_item->popup, \"\\t\\t\", rsp);\n    fprintf(rsp, \"\\n\\t}\");\n  }\n\n  if (bar_item->type == BAR_COMPONENT_GROUP && bar_item->group) {\n    fprintf(rsp, \",\\n\\t\\\"bracket\\\": [\\n\");\n    group_serialize(bar_item->group, \"\\t\\t\", rsp);\n    fprintf(rsp, \"\\n\\t]\");\n  } else if (bar_item->type == BAR_COMPONENT_GRAPH) {\n    fprintf(rsp, \",\\n\\t\\\"graph\\\": {\\n\");\n    graph_serialize(&bar_item->graph, \"\\t\\t\", rsp);\n    fprintf(rsp, \"\\n\\t}\");\n  } else if (bar_item->type == BAR_COMPONENT_SLIDER) {\n    fprintf(rsp, \",\\n\\t\\\"slider\\\": {\\n\");\n    slider_serialize(&bar_item->slider, \"\\t\\t\", rsp);\n    fprintf(rsp, \"\\n\\t}\");\n  }\n\n  fprintf(rsp, \"\\n}\\n\");\n}\n\nvoid bar_item_parse_set_message(struct bar_item* bar_item, char* message, FILE* rsp) {\n  bool needs_refresh = false;\n  struct token property = get_token(&message);\n\n  struct key_value_pair key_value_pair = get_key_value_pair(property.text,'.');\n  if (key_value_pair.key && key_value_pair.value) {\n    struct token subdom = { key_value_pair.key, strlen(key_value_pair.key) };\n    struct token entry = { key_value_pair.value, strlen(key_value_pair.value)};\n    if (token_equals(subdom, SUB_DOMAIN_ICON)) {\n      needs_refresh = text_parse_sub_domain(&bar_item->icon,\n                                            rsp,\n                                            entry,\n                                            message         );\n    }\n    else if (token_equals(subdom, SUB_DOMAIN_LABEL)) {\n      needs_refresh = text_parse_sub_domain(&bar_item->label,\n                                            rsp,\n                                            entry,\n                                            message          );\n    }\n    else if (token_equals(subdom, SUB_DOMAIN_BACKGROUND)) {\n      needs_refresh = background_parse_sub_domain(&bar_item->background,\n                                                  rsp,\n                                                  entry,\n                                                  message               );\n    }\n    else if (token_equals(subdom, SUB_DOMAIN_POPUP)) {\n      needs_refresh = popup_parse_sub_domain(&bar_item->popup,\n                                             rsp,\n                                             entry,\n                                             message          );\n    }\n    else if (token_equals(subdom, SUB_DOMAIN_GRAPH)) {\n      if (bar_item->has_graph || bar_item == &g_bar_manager.default_item) {\n        needs_refresh = graph_parse_sub_domain(&bar_item->graph,\n                                               rsp,\n                                               entry,\n                                               message          );\n      } else {\n        respond(rsp, \"[!] Item (%s): Trying to set a graph property on a non-graph item\\n\", bar_item->name);\n      }\n    }\n    else if (token_equals(subdom, SUB_DOMAIN_ALIAS)) {\n      if (bar_item->has_alias || bar_item == &g_bar_manager.default_item) {\n        needs_refresh = alias_parse_sub_domain(&bar_item->alias,\n                                               rsp,\n                                               entry,\n                                               message          );\n      } else {\n        respond(rsp, \"[!] Item (%s): Trying to set an alias property on a non-alias item\\n\", bar_item->name);\n      }\n    }\n    else if (token_equals(subdom, SUB_DOMAIN_SLIDER)) {\n      if (bar_item->has_slider || bar_item == &g_bar_manager.default_item) {\n        needs_refresh = slider_parse_sub_domain(&bar_item->slider,\n                                                rsp,\n                                                entry,\n                                                message           );\n      } else {\n        respond(rsp, \"[!] Item (%s): Trying to set a slider property on a non-slider item\\n\", bar_item->name);\n      }\n    }\n    else {\n      respond(rsp, \"[!] Item (%s): Invalid subdomain '%s'\\n\", bar_item->name, subdom.text);\n    }\n  }\n  else if (token_equals(property, PROPERTY_ICON)) {\n    struct token dummy = { PROPERTY_STRING, strlen(PROPERTY_STRING)};\n    needs_refresh = text_parse_sub_domain(&bar_item->icon,\n                                          rsp,\n                                          dummy,\n                                          message         );\n\n  } else if (token_equals(property, PROPERTY_LABEL)) {\n    struct token dummy = { PROPERTY_STRING, strlen(PROPERTY_STRING)};\n    needs_refresh = text_parse_sub_domain(&bar_item->label,\n                                          rsp,\n                                          dummy,\n                                          message          );\n\n  } else if (token_equals(property, PROPERTY_UPDATES)) {\n    struct token token = get_token(&message);\n    if (token_equals(token, ARGUMENT_UPDATES_WHEN_SHOWN)) {\n      bar_item->updates = true;\n      bar_item->updates_only_when_shown = true;\n    }\n    else {\n      bar_item->updates = evaluate_boolean_state(token, bar_item->updates);\n      bar_item->updates_only_when_shown = false;\n    }\n  } else if (token_equals(property, PROPERTY_DRAWING)) {\n    needs_refresh = bar_item_set_drawing(bar_item,\n                                         evaluate_boolean_state(get_token(&message),\n                                                                bar_item->drawing   ));\n  } else if (token_equals(property, PROPERTY_SCROLL_TEXTS)) {\n    bar_item->scroll_texts = evaluate_boolean_state(get_token(&message),\n                                                    bar_item->scroll_texts);\n  } else if (token_equals(property, PROPERTY_WIDTH)) {\n    struct token token = get_token(&message);\n    if (token_equals(token, ARGUMENT_DYNAMIC)) {\n      ANIMATE(bar_item_set_width,\n              bar_item,\n              bar_item->custom_width,\n              bar_item_get_length(bar_item, true)\n              + bar_item->background.padding_left\n              + bar_item->background.padding_right);\n\n      struct animation* animation = animation_create();\n      animation_setup(animation,\n                      bar_item,\n                      (bool (*)(void*, int))&bar_item_set_width,\n                      bar_item->custom_width,\n                      -1,\n                      0,\n                      INTERP_FUNCTION_LINEAR               );\n      animator_add(&g_bar_manager.animator, animation);\n    }\n    else {\n      ANIMATE(bar_item_set_width,\n              bar_item,\n              bar_item_get_length(bar_item, false)\n              + (bar_item->has_const_width\n              ? 0\n              : (bar_item->background.padding_left\n                 + bar_item->background.padding_right)),\n              token_to_int(token)                       );\n    }\n  } else if (token_equals(property, PROPERTY_SCRIPT)) {\n    bar_item_set_script(bar_item, token_to_string(get_token(&message)));\n  } else if (token_equals(property, PROPERTY_CLICK_SCRIPT)) {\n    bar_item_set_click_script(bar_item, token_to_string(get_token(&message)));\n  } else if (token_equals(property, PROPERTY_UPDATE_FREQ)) {\n    bar_item->update_frequency = token_to_uint32t(get_token(&message));\n  } else if (token_equals(property, PROPERTY_POSITION)) {\n    struct token position = get_token(&message);\n    bar_item_set_position(bar_item, position.text);\n    struct key_value_pair key_value_pair = get_key_value_pair(position.text,\n                                                              '.'           );\n\n    if (key_value_pair.key && key_value_pair.value) {\n      if (key_value_pair.key[0] == POSITION_POPUP) {\n        int item_index_for_name = bar_manager_get_item_index_for_name(&g_bar_manager,\n                                                                      key_value_pair.value);\n        if (item_index_for_name < 0) {\n          respond(rsp, \"[!] Item Position (%s): Item '%s' is not a valid popup host\\n\", bar_item->name, key_value_pair.value);\n          return;\n        }\n        struct bar_item* target_item = g_bar_manager.bar_items[item_index_for_name];\n        popup_add_item(&target_item->popup, bar_item);\n      } else {\n        bar_item->parent = NULL;\n      }\n    }\n    needs_refresh = true;\n  } else if (token_equals(property, PROPERTY_ALIGN)) {\n    struct token position = get_token(&message);\n    if (bar_item->align != position.text[0]) {\n      bar_item->align = position.text[0];\n      needs_refresh = true;\n    }\n  } else if (token_equals(property, PROPERTY_ASSOCIATED_SPACE)\n             || token_equals(property, PROPERTY_SPACE)        ) {\n    struct token token = get_token(&message);\n    uint32_t prev = bar_item->associated_space;\n    bar_item->associated_space = 0;\n    uint32_t count;\n    char** list = token_split(token, ',', &count);\n    if (list && count > 0) {\n      for (int i = 0; i < count; i++) {\n        bar_item_append_associated_space(bar_item,\n                                         1 << strtoul(list[i],\n                                                      NULL,\n                                                      0       ));\n      }\n      free(list);\n    }\n    needs_refresh = (prev != bar_item->associated_space);\n  } else if (token_equals(property, PROPERTY_ASSOCIATED_DISPLAY)\n             || token_equals(property, PROPERTY_DISPLAY)        ) {\n    struct token token = get_token(&message);\n    uint32_t prev = bar_item->associated_display;\n    bar_item->associated_display = 0;\n    bar_item->associated_to_active_display = false;\n    uint32_t count;\n    char** list = token_split(token, ',', &count);\n    if (list && count > 0) {\n      for (int i = 0; i < count; i++) {\n        if (strcmp(list[i], \"active\") == 0) {\n          bar_item->associated_to_active_display = true;\n        }\n        else {\n          bar_item_append_associated_display(bar_item,\n                                             1 << strtoul(list[i],\n                                                          NULL,\n                                                          0       ));\n        }\n      }\n      free(list);\n    }\n    needs_refresh = (prev != bar_item->associated_display);\n  } else if (token_equals(property, PROPERTY_YOFFSET)) {\n    struct token token = get_token(&message);\n    ANIMATE(bar_item_set_yoffset,\n            bar_item,\n            bar_item->y_offset,\n            token_to_int(token)  );\n\n  } else if (token_equals(property, PROPERTY_PADDING_LEFT)) {\n    struct token token = get_token(&message);\n    ANIMATE(background_set_padding_left,\n            &bar_item->background,\n            bar_item->background.padding_left,\n            token_to_int(token)               );\n\n  } else if (token_equals(property, PROPERTY_PADDING_RIGHT)) {\n    struct token token = get_token(&message);\n    ANIMATE(background_set_padding_right,\n            &bar_item->background,\n            bar_item->background.padding_right,\n            token_to_int(token)                );\n\n  } else if (token_equals(property, PROPERTY_BLUR_RADIUS)) {\n    struct token token = get_token(&message);\n    ANIMATE(bar_item_set_blur_radius,\n            bar_item,\n            bar_item->blur_radius,\n            token_to_int(token)      );\n\n  } else if (token_equals(property, PROPERTY_SHADOW)) {\n    bool prev = bar_item->shadow;\n    bar_item->shadow = evaluate_boolean_state(get_token(&message),\n                                              bar_item->shadow    );\n    if (prev != bar_item->shadow) {\n      for (int i = 1; i <= bar_item->num_windows; i++) {\n        bar_item_remove_window(bar_item, i);\n      }\n      needs_refresh = true;\n    }\n  } else if (token_equals(property, PROPERTY_IGNORE_ASSOCIATION)) {\n    bar_item->ignore_association = evaluate_boolean_state(get_token(&message),\n                                                          bar_item->ignore_association);\n    needs_refresh = true;\n  } else if (token_equals(property, COMMAND_DEFAULT_RESET)) {\n    bar_item_init(&g_bar_manager.default_item, NULL);\n  } else if (token_equals(property, PROPERTY_EVENT_PORT)) {\n    struct token token = get_token(&message);\n    if (token.text && token.length > 0)\n      bar_item_set_event_port(bar_item, token.text);\n  } else {\n    respond(rsp, \"[!] Item (%s): Invalid property '%s' \\n\", bar_item->name, property.text);\n  }\n\n  if (needs_refresh) bar_item_needs_update(bar_item);\n}\n\nvoid bar_item_parse_subscribe_message(struct bar_item* bar_item, char* message, FILE* rsp) {\n  struct token event = get_token(&message);\n\n  while (event.text && event.length > 0) {\n    uint64_t event_flag = custom_events_get_flag_for_name(&g_bar_manager.custom_events,\n                                                          event.text                   );\n\n    if (event_flag & UPDATE_VOLUME_CHANGE) {\n      begin_receiving_volume_events();\n    }\n\n    if (event_flag & UPDATE_BRIGHTNESS_CHANGE) {\n      begin_receiving_brightness_events();\n    }\n\n    if (event_flag & UPDATE_MEDIA_CHANGE) {\n      begin_receiving_media_events();\n    }\n\n    if (event_flag & UPDATE_SPACE_WINDOWS_CHANGE) {\n      begin_receiving_space_window_events();\n    }\n\n    bar_item->update_mask |= event_flag;\n\n    if (!event_flag) {\n      respond(rsp, \"[?] Event: '%s' not found\\n\", event.text);\n    }\n\n    event = get_token(&message);\n  }\n}\n"
  },
  {
    "path": "src/bar_item.h",
    "content": "#pragma once\n\n#include \"alias.h\"\n#include \"custom_events.h\"\n#include \"graph.h\"\n#include \"group.h\"\n#include \"misc/env_vars.h\"\n#include \"misc/helpers.h\"\n#include \"popup.h\"\n#include \"text.h\"\n#include \"slider.h\"\n\n#define BAR_ITEM             'i'\n#define BAR_COMPONENT_GRAPH  'g'\n#define BAR_COMPONENT_SPACE  's'\n#define BAR_COMPONENT_ALIAS  'a'\n#define BAR_COMPONENT_GROUP  'b'\n#define BAR_COMPONENT_SLIDER 't'\n#define BAR_PLUGIN           'p'\n\nstruct bar_item {\n  char type;\n  char* name;\n\n  // Update Modifiers\n  uint32_t counter;\n  bool needs_update;\n  bool updates;\n  bool updates_only_when_shown;\n  bool lazy;\n  bool selected;\n  bool mouse_over;\n  bool ignore_association;\n  bool overrides_association;\n\n  // Drawing Modifiers\n  bool drawing;\n  bool shadow;\n  bool has_const_width;\n  bool scroll_texts;\n  char align;\n  uint32_t custom_width;\n  uint32_t blur_radius;\n\n  // These are 32bit masks where the ith bit represents the ith screen/display/bar association\n  bool associated_to_active_display;\n  uint32_t associated_bar;\n  uint32_t associated_display;\n  uint32_t associated_space;\n  uint32_t update_frequency;\n\n  char* script;\n  char* click_script;\n  struct signal_args signal_args;\n  \n  // The position in the bar: l,r,c\n  char position;\n  int y_offset;\n\n  // Background\n  struct background background;\n\n  // Icon properties\n  struct text icon;\n\n  // Label properties\n  struct text label;\n\n  // Graph Data\n  bool has_graph;\n  struct graph graph;\n\n  // Alias Data\n  bool has_alias;\n  struct alias alias;\n\n  // Slider Properties\n  bool has_slider;\n  struct slider slider;\n\n  // Group Properties\n  struct group* group;\n\n  // Update Events\n  uint64_t update_mask;\n\n  // Windows\n  uint32_t num_windows;\n  struct window** windows;\n\n  // Popup\n  struct popup popup;\n  struct bar_item* parent;\n\n  // Mach\n  mach_port_t event_port;\n};\n\nstruct bar_item* bar_item_create();\nvoid bar_item_inherit_from_item(struct bar_item* bar_item, struct bar_item* ancestor);\nvoid bar_item_init(struct bar_item* bar_item, struct bar_item* default_item);\nvoid bar_item_serialize(struct bar_item* bar_item, FILE* rsp);\nvoid bar_item_destroy(struct bar_item* bar_item, bool free_memory);\n\nbool bar_item_is_shown(struct bar_item* bar_item);\nvoid bar_item_needs_update(struct bar_item* bar_item);\nbool bar_item_update(struct bar_item* bar_item, char* sender, bool forced, struct env_vars* env_vars);\n\nvoid bar_item_on_click(struct bar_item* bar_item, uint32_t type, uint32_t mouse_button_code, uint32_t modifier, CGPoint point);\nvoid bar_item_on_scroll(struct bar_item* bar_item, int scroll_delta, uint32_t modifier);\nvoid bar_item_on_drag(struct bar_item* bar_item, CGPoint point);\nvoid bar_item_mouse_entered(struct bar_item* bar_item);\nvoid bar_item_mouse_exited(struct bar_item* bar_item);\nvoid bar_item_cancel_drag(struct bar_item* bar_item);\n\nvoid bar_item_append_associated_space(struct bar_item* bar_item, uint32_t bit);\nvoid bar_item_append_associated_display(struct bar_item* bar_item, uint32_t bit);\nvoid bar_item_append_associated_bar(struct bar_item* bar_item, uint32_t adid);\nvoid bar_item_remove_associated_bar(struct bar_item* bar_item, uint32_t adid);\nvoid bar_item_reset_associated_bar(struct bar_item* bar_item);\n\nbool bar_item_set_name(struct bar_item* bar_item, char* name);\nbool bar_item_set_type(struct bar_item* bar_item, char* type);\nbool bar_item_set_position(struct bar_item* bar_item, char* position);\nbool bar_item_set_media_cover(struct bar_item* bar_item, struct image* image);\n\nuint32_t bar_item_get_length(struct bar_item* bar_item, bool ignore_override);\nuint32_t bar_item_get_height(struct bar_item* bar_item);\n\nstruct window* bar_item_get_window(struct bar_item* bar_item, uint32_t adid);\nvoid bar_item_remove_window(struct bar_item* bar_item, uint32_t adid);\n\nCGPoint bar_item_calculate_shadow_offsets(struct bar_item* bar_item);\nuint32_t bar_item_calculate_bounds(struct bar_item* bar_item, uint32_t bar_height, uint32_t x, uint32_t y);\nvoid* draw_item_proc(void* context);\nvoid bar_item_draw(struct bar_item* bar_item, CGContextRef context);\nbool bar_item_clip_needs_update_for_bar(struct bar_item* bar_item, struct bar* bar);\nvoid bar_item_clip_bar(struct bar_item* bar_item, int offset, struct bar* bar);\nbool bar_item_clips_bar(struct bar_item* bar_item);\n\nvoid bar_item_change_space(struct bar_item* bar_item, uint64_t dsid, uint32_t adid);\n\nvoid bar_item_parse_set_message(struct bar_item* bar_item, char* message, FILE* rsp);\nvoid bar_item_parse_subscribe_message(struct bar_item* bar_item, char* message, FILE* rsp);\n"
  },
  {
    "path": "src/bar_manager.c",
    "content": "#include \"bar_manager.h\"\n#include \"bar.h\"\n#include \"bar_item.h\"\n#include \"event.h\"\n#include \"misc/env_vars.h\"\n#include \"misc/helpers.h\"\n#include \"wifi.h\"\n#include \"volume.h\"\n#include \"power.h\"\n#include \"mouse.h\"\n#include \"media.h\"\n#include \"app_windows.h\"\n\nextern void forced_front_app_event();\n\nstatic CLOCK_CALLBACK(clock_handler) {\n  struct event event = { NULL, SHELL_REFRESH };\n  event_post(&event);\n}\n\nvoid bar_manager_init(struct bar_manager* bar_manager) {\n  bar_manager->font_smoothing = false;\n  bar_manager->any_bar_hidden = false;\n  bar_manager->needs_ordering = false;\n  bar_manager->bar_needs_update = false;\n  bar_manager->bars = NULL;\n  bar_manager->bar_count = 0;\n  bar_manager->bar_items = NULL;\n  bar_manager->bar_item_count = 0;\n  bar_manager->displays = DISPLAY_ALL_PATTERN;\n  bar_manager->position = POSITION_TOP;\n  bar_manager->shadow = false;\n  bar_manager->blur_radius = 0;\n  bar_manager->margin = 0;\n  bar_manager->frozen = false;\n  bar_manager->sleeps = false;\n  bar_manager->window_level = kCGBackstopMenuLevel;\n  bar_manager->topmost = false;\n  bar_manager->notch_width = 200;\n  bar_manager->notch_offset = 0;\n  bar_manager->notch_display_height = 0;\n  bar_manager->active_adid = display_active_display_adid();\n  bar_manager->might_need_clipping = false;\n\n  bar_manager->sticky = true;\n\n  bar_manager->show_in_fullscreen = false;\n\n  image_init(&bar_manager->current_artwork);\n  background_init(&bar_manager->background);\n  bar_manager->background.bounds.size.height = 25;\n  bar_manager->background.overrides_height = true;\n  bar_manager->background.padding_left = 20;\n  bar_manager->background.padding_right = 20;\n\n  color_set_hex(&bar_manager->background.border_color, 0xffff0000);\n  color_set_hex(&bar_manager->background.color, 0x44000000);\n\n  bar_item_init(&bar_manager->default_item, NULL);\n  bar_item_set_name(&bar_manager->default_item, string_copy(\"defaults\"));\n  custom_events_init(&bar_manager->custom_events);\n\n  animator_init(&bar_manager->animator);\n\n  int shell_refresh_frequency = 1;\n\n  bar_manager->clock = CFRunLoopTimerCreate(NULL,\n                                            CFAbsoluteTimeGetCurrent()\n                                            + shell_refresh_frequency,\n                                            shell_refresh_frequency,\n                                            0,\n                                            0,\n                                            clock_handler,\n                                            NULL                      );\n\n  CFRunLoopAddTimer(CFRunLoopGetMain(),\n                    bar_manager->clock,\n                    kCFRunLoopCommonModes);\n}\n\nvoid bar_manager_sort(struct bar_manager* bar_manager, struct bar_item** ordering, uint32_t count) {\n  int index = 0;\n  for (int i = 0; i < bar_manager->bar_item_count; i++) {\n    for (int j = 0; j < count; j++) {\n      if (bar_manager->bar_items[i] == ordering[j]\n          && bar_manager->bar_items[i] != ordering[index]) {\n        bar_manager->bar_items[i] = ordering[index];\n        index++;\n        bar_item_needs_update(bar_manager->bar_items[i]);\n        break;\n      }\n      else if (bar_manager->bar_items[i] == ordering[j]\n               && bar_manager->bar_items[i] == ordering[index]) {\n        index++;\n        break;\n      }\n    }\n  }\n  bar_manager->needs_ordering = true;\n}\n\nint bar_manager_get_item_index_for_name(struct bar_manager* bar_manager, char* name) {\n  for (int i = 0; i < bar_manager->bar_item_count; i++) {\n    if (strcmp(bar_manager->bar_items[i]->name, name) == 0) {\n      return i;\n    }\n  }\n  return -1;\n}\n\nint bar_manager_get_item_index_by_address(struct bar_manager* bar_manager, struct bar_item* bar_item) {\n  for (int i = 0; i < bar_manager->bar_item_count; i++) {\n    if (bar_manager->bar_items[i] == bar_item) {\n      return i;\n    }\n  }\n  return -1;\n}\n\nvoid bar_manager_move_item(struct bar_manager* bar_manager, struct bar_item* item, struct bar_item* reference, bool before) {\n  if (bar_manager->bar_item_count <= 0) return;\n  struct bar_item* tmp[bar_manager->bar_item_count];\n  int count = 0;\n  for (int i = 0; i < bar_manager->bar_item_count; i++) {\n    if (bar_manager->bar_items[i] == item) continue;\n    if (bar_manager->bar_items[i] == reference && before) {\n      tmp[count++] = item;\n      tmp[count++] = bar_manager->bar_items[i];\n      continue;\n    } else if (bar_manager->bar_items[i] == reference && !before) {\n      tmp[count++] = bar_manager->bar_items[i];\n      tmp[count++] = item;\n      continue;\n    }\n    tmp[count++] = bar_manager->bar_items[i];\n  }\n  bar_manager->bar_items = realloc(\n                        bar_manager->bar_items,\n                        sizeof(struct bar_item*)*bar_manager->bar_item_count);\n\n  memcpy(bar_manager->bar_items,\n         tmp,\n         sizeof(struct bar_item*)*bar_manager->bar_item_count);\n\n  bar_manager->needs_ordering = true;\n}\n\nvoid bar_manager_remove_item(struct bar_manager* bar_manager, struct bar_item* bar_item) {\n  if (bar_manager->bar_item_count <= 0 || !bar_item\n      || bar_manager_get_item_index_by_address(bar_manager, bar_item) < 0) {\n    return;\n  }\n\n  if (bar_item->position == POSITION_POPUP) {\n    for (int i = 0; i < bar_manager->bar_item_count; i++) {\n      popup_remove_item(&bar_manager->bar_items[i]->popup, bar_item);\n    }\n  }\n  if (bar_manager->bar_item_count == 1) {\n    free(bar_manager->bar_items);\n    bar_manager->bar_items = NULL;\n    bar_manager->bar_item_count = 0;\n  } else {\n    struct bar_item* tmp[bar_manager->bar_item_count - 1];\n    int count = 0;\n    for (int i = 0; i < bar_manager->bar_item_count; i++) {\n      if (bar_manager->bar_items[i] == bar_item) continue;\n      tmp[count++] = bar_manager->bar_items[i];\n    }\n    bar_manager->bar_item_count--;\n    bar_manager->bar_items = realloc(\n                         bar_manager->bar_items,\n                         sizeof(struct bar_item*)*bar_manager->bar_item_count);\n\n    memcpy(bar_manager->bar_items,\n           tmp,\n           sizeof(struct bar_item*)*bar_manager->bar_item_count);\n  }\n\n  bar_item_destroy(bar_item, true);\n}\n\nbool bar_manager_set_margin(struct bar_manager* bar_manager, int margin) {\n  if (bar_manager->margin == margin) return false;\n  bar_manager->margin = margin;\n  bar_manager->bar_needs_resize = true;\n  return true;\n}\n\nbool bar_manager_set_y_offset(struct bar_manager* bar_manager, int y_offset) {\n  if (bar_manager->background.y_offset == y_offset) return false;\n  bar_manager->background.y_offset = y_offset;\n  bar_manager->bar_needs_resize = true;\n  return true;\n}\n\nbool bar_manager_set_bar_height(struct bar_manager* bar_manager, int height) {\n  bar_manager->bar_needs_resize |= background_set_height(&bar_manager->background, height);\n  return bar_manager->bar_needs_resize;\n}\n\nbool bar_manager_set_background_blur(struct bar_manager* bar_manager, uint32_t radius) {\n  if (bar_manager->blur_radius == radius) return false;\n  bar_manager->blur_radius = radius;\n  for (int i = 0; i < bar_manager->bar_count; i++) {\n    window_set_blur_radius(&bar_manager->bars[i]->window, radius);\n  }\n  return false;\n}\n\nbool bar_manager_set_position(struct bar_manager* bar_manager, char pos) {\n  if (bar_manager->position == pos) return false;\n  bar_manager->position = pos;\n  bar_manager->bar_needs_resize = true;\n  return true;\n}\n\nbool bar_manager_set_displays(struct bar_manager* bar_manager, uint32_t displays) {\n  if (bar_manager->displays == displays) return false;\n  bar_manager->displays = displays;\n\n  bar_manager_reset(bar_manager);\n  return true;\n}\n\nbool bar_manager_set_shadow(struct bar_manager* bar_manager, bool shadow) {\n  if (bar_manager->shadow == shadow) return false;\n  bar_manager->shadow = shadow;\n  bar_manager_reset(bar_manager);\n  return true;\n}\n\nbool bar_manager_set_notch_width(struct bar_manager* bar_manager, uint32_t width) {\n  if (bar_manager->notch_width == width) return false;\n\n  bar_manager->notch_width = width;\n  return true;\n}\n\nbool bar_manager_set_notch_offset(struct bar_manager* bar_manager, uint32_t offset) {\n  if (bar_manager->notch_offset == offset) return false;\n\n  bar_manager->notch_offset = offset;\n  bar_manager->bar_needs_resize = true;\n  return true;\n}\n\nbool bar_manager_set_notch_display_height(struct bar_manager* bar_manager, uint32_t offset) {\n  if (bar_manager->notch_display_height == offset) return false;\n\n  bar_manager->notch_display_height = offset;\n  bar_manager->bar_needs_resize = true;\n  return true;\n}\n\nbool bar_manager_set_font_smoothing(struct bar_manager* bar_manager, bool smoothing) {\n  if (bar_manager->font_smoothing == smoothing) return false;\n  bar_manager->font_smoothing = smoothing;\n  for (int i = 0; i < bar_manager->bar_count; i++)\n    context_set_font_smoothing(bar_manager->bars[i]->window.context, smoothing);\n  return true;\n}\n\nbool bar_manager_set_show_in_fullscreen(struct bar_manager* bar_manager, bool show_in_fullscreen) {\n    if (bar_manager->show_in_fullscreen == show_in_fullscreen) return false;\n    bar_manager->show_in_fullscreen = show_in_fullscreen;\n    return true;\n}\n\nbool bar_manager_set_hidden(struct bar_manager *bar_manager, uint32_t adid, bool hidden) {\n  bar_manager->any_bar_hidden = false;\n  if (adid > 0) {\n    bar_set_hidden(bar_manager->bars[adid - 1], hidden);\n    bar_manager->any_bar_hidden |= hidden;\n  }\n  else {\n    for (int i = 0; i < bar_manager->bar_count; i++) {\n      bar_set_hidden(bar_manager->bars[i], hidden);\n      bar_manager->any_bar_hidden |= hidden;\n    }\n  }\n\n  if (hidden) {\n    for (int i = 0; i < bar_manager->bar_item_count; i++) {\n      popup_set_drawing(&bar_manager->bar_items[i]->popup, false);\n    }\n  }\n\n  bar_manager->bar_needs_update = true;\n  return true;\n}\n\nbool bar_manager_set_topmost(struct bar_manager *bar_manager, char level, bool topmost) {\n  if (topmost) {\n    if (level == TOPMOST_LEVEL_WINDOW) {\n      bar_manager->window_level = kCGFloatingWindowLevel;\n    } else if (level == TOPMOST_LEVEL_ALL) {\n      bar_manager->window_level = kCGStatusWindowLevel;\n    }\n  } else {\n    bar_manager->window_level = kCGBackstopMenuLevel;\n  }\n\n  bar_manager_reset(bar_manager);\n  bar_manager->topmost = topmost;\n  return true;\n}\n\nbool bar_manager_set_sticky(struct bar_manager *bar_manager, bool sticky) {\n  if (sticky == bar_manager->sticky) return false;\n\n  bar_manager->sticky = sticky;\n  bar_manager_reset(bar_manager);\n  return true;\n}\n\nvoid bar_manager_freeze(struct bar_manager *bar_manager) {\n  bar_manager->frozen = true;\n}\n\nvoid bar_manager_unfreeze(struct bar_manager *bar_manager) {\n  bar_manager->frozen = false;\n}\n\nuint32_t bar_manager_length_for_bar_side(struct bar_manager* bar_manager, struct bar* bar, char side) {\n  uint32_t total_length = 0;\n  for (int i = 0; i < bar_manager->bar_item_count; i++) {\n    struct bar_item* bar_item = bar_manager->bar_items[i];\n    if (bar_item->position == side\n        && bar_item->type != BAR_COMPONENT_GROUP\n        && bar_draws_item(bar, bar_item)        ) {\n      int item_length = (bar_manager->position == POSITION_LEFT\n                         || bar_manager->position == POSITION_RIGHT)\n                        ? bar_item_get_height(bar_item)\n                        : bar_item_get_length(bar_item, false);\n\n      total_length += item_length + (bar_item->has_const_width\n                                     ? 0\n                                     : bar_item->background.padding_left\n                                       + bar_item->background.padding_right);\n    }\n  }\n  return total_length;\n}\n\nbool bar_manager_bar_needs_redraw(struct bar_manager* bar_manager, struct bar* bar) {\n  if (bar_manager->bar_needs_update) return true;\n\n  uint32_t bar_mask = 1 << bar->adid;\n  for (int i = 0; i < bar_manager->bar_item_count; i++) {\n    struct bar_item* bar_item = bar_manager->bar_items[i];\n    bool draws_item = bar_draws_item(bar, bar_item);\n\n    bool regular_update = bar_item->needs_update\n                          && draws_item;\n\n    if (regular_update) return true;\n\n    bool disabled_item_drawn_on_bar = !bar_item->drawing\n                                      && (bar_item->associated_bar != 0);\n\n    if (disabled_item_drawn_on_bar) return true;\n\n    if (bar_item->ignore_association) continue;\n\n    bool not_drawn_on_associated_display =\n      (draws_item\n       && (bar_item->associated_display > 0)\n       && (bar_item->associated_display & bar_mask)\n       && !(((bar_item->associated_bar << 1) & bar_mask)))\n      || (draws_item\n          && bar_item->associated_to_active_display\n          && bar_manager->active_adid == bar->adid\n          && !((bar_item->associated_bar << 1) & bar_mask));\n\n    if (not_drawn_on_associated_display) return true;\n\n    bool drawn_on_non_associated_display =\n      (!bar_item->associated_to_active_display\n       && (bar_item->associated_display > 0)\n       && !(bar_item->associated_display & bar_mask)\n       && (((bar_item->associated_bar << 1) & bar_mask)))\n      || (bar_item->drawing\n       && bar_item->associated_to_active_display\n       && (((bar_item->associated_bar << 1) & bar_mask))\n       && (bar->adid != bar_manager->active_adid));\n\n    if (drawn_on_non_associated_display) return true;\n\n    if (bar_item->type == BAR_COMPONENT_SPACE) continue;\n\n    bool drawn_on_non_associated_space = bar_item->associated_space > 0\n                                         && !(bar_item->associated_space\n                                              & (1 << bar->sid))\n                                         && ((bar_item->associated_bar << 1)\n                                             & bar_mask);\n\n    if (drawn_on_non_associated_space) return true;\n\n    bool not_drawn_on_associated_space = draws_item\n                                         && bar_item->associated_space > 0\n                                         && (bar_item->associated_space\n                                             & (1 << bar->sid))\n                                         && !((bar_item->associated_bar << 1)\n                                              & bar_mask);\n\n    if (not_drawn_on_associated_space) return true;\n\n  }\n  return false;\n}\n\nvoid bar_manager_clear_needs_update(struct bar_manager* bar_manager) {\n  for (int i = 0; i < bar_manager->bar_item_count; i++)\n    bar_manager->bar_items[i]->needs_update = false;\n\n  bar_manager->needs_ordering = false;\n  bar_manager->bar_needs_update = false;\n}\n\nvoid bar_manager_reset_bar_association(struct bar_manager* bar_manager) {\n  for (int i = 0; i < bar_manager->bar_item_count; i++)\n    bar_item_reset_associated_bar(bar_manager->bar_items[i]);\n}\n\nvoid bar_manager_refresh(struct bar_manager* bar_manager, bool forced, bool threaded) {\n  if (bar_manager->frozen) return;\n  if (forced) {\n    bar_manager_reset_bar_association(bar_manager);\n    for (int j = 0; j < bar_manager->bar_item_count; j++) {\n      bar_item_needs_update(bar_manager->bar_items[j]);\n    }\n  }\n\n  if (forced || bar_manager->bar_needs_resize) bar_manager_resize(bar_manager);\n\n  for (int i = 0; i < bar_manager->bar_count; ++i) {\n    if (forced\n        || bar_manager_bar_needs_redraw(bar_manager, bar_manager->bars[i])) {\n      bar_calculate_bounds(bar_manager->bars[i]);\n      bar_draw(bar_manager->bars[i], false, threaded);\n      if (bar_manager->needs_ordering) {\n        bar_order_item_windows(bar_manager->bars[i]);\n      }\n    }\n  }\n\n  bar_manager_clear_needs_update(bar_manager);\n  if (threaded) join_render_threads();\n}\n\nvoid bar_manager_resize(struct bar_manager* bar_manager) {\n  for (int i = 0; i < bar_manager->bar_count; ++i)\n    bar_resize(bar_manager->bars[i]);\n\n  bar_manager->bar_needs_resize = false;\n}\n\nstruct bar_item* bar_manager_create_item(struct bar_manager* bar_manager) {\n  bar_manager->bar_items = (struct bar_item**) realloc(\n                 bar_manager->bar_items,\n                 sizeof(struct bar_item*) * (bar_manager->bar_item_count + 1));\n\n  bar_manager->bar_item_count += 1;\n  struct bar_item* bar_item = bar_item_create();\n  bar_item_init(bar_item, &bar_manager->default_item);\n  bar_manager->bar_items[bar_manager->bar_item_count - 1] = bar_item;\n  bar_manager->needs_ordering = true;\n  return bar_item;\n}\n\nvoid bar_manager_update_alias_components(struct bar_manager* bar_manager, bool forced) {\n  for (int i = 0; i < bar_manager->bar_item_count; i++) {\n    if ((!bar_item_is_shown(bar_manager->bar_items[i]) && !forced)\n        || bar_manager->bar_items[i]->type != BAR_COMPONENT_ALIAS ) {\n      continue;\n    }\n\n    alias_update(&bar_manager->bar_items[i]->alias, forced);\n  }\n}\n\nvoid bar_manager_update_space_components(struct bar_manager* bar_manager, bool forced) {\n  for (int i = 0; i < bar_manager->bar_item_count; i++) {\n    struct bar_item* bar_item = bar_manager->bar_items[i];\n    if (bar_item->type != BAR_COMPONENT_SPACE) continue;\n\n    if (!bar_item->overrides_association) {\n      uint32_t space = get_set_bit_position(bar_item->associated_space);\n      uint32_t space_did = display_id_for_space(space);\n      if (space_did) {\n        bar_item->associated_display = 1 << (display_arrangement(space_did));\n      }\n      else {\n        bar_item->associated_display = 1 << 30;\n      }\n    }\n    for (int j = 0; j < bar_manager->bar_count; j++) {\n      struct bar* bar = bar_manager->bars[j];\n      uint32_t did = bar->adid;\n\n      if ((1 << did) & bar_item->associated_display) {\n        uint32_t sid = bar->sid;\n        if (sid == 0) continue;\n        if ((!bar_item->selected || forced)\n            && bar_item->associated_space & (1 << sid)) {\n          bar_item->selected = true;\n          bar_item->updates = true;\n          env_vars_set(&bar_item->signal_args.env_vars,\n                       string_copy(\"SELECTED\"),\n                       string_copy(\"true\")             );\n        }\n        else if ((bar_item->selected || forced)\n                 && !(bar_item->associated_space & (1 << sid))) {\n          bar_item->selected = false;\n          bar_item->updates = true;\n          env_vars_set(&bar_item->signal_args.env_vars,\n                       string_copy(\"SELECTED\"),\n                       string_copy(\"false\")            );\n        }\n        else {\n          bar_item->updates = false;\n        }\n      }\n    }\n  }\n}\n\nvoid bar_manager_animator_refresh(struct bar_manager* bar_manager, uint64_t time) {\n  bar_manager_freeze(bar_manager);\n  if (animator_update(&bar_manager->animator, time)) {\n    bar_manager_unfreeze(bar_manager);\n\n    if (bar_manager->bar_needs_resize) bar_manager_resize(bar_manager);\n    bar_manager_refresh(bar_manager, false, false);\n  }\n  bar_manager_unfreeze(bar_manager);\n}\n\nvoid bar_manager_update(struct bar_manager* bar_manager, bool forced) {\n  if ((bar_manager->frozen && !forced) || bar_manager->sleeps) return;\n\n  if (forced) {\n    bar_manager_handle_space_change(bar_manager, true);\n    forced_network_event();\n    forced_volume_event();\n    forced_brightness_event();\n    forced_power_event();\n    forced_front_app_event();\n    forced_media_change_event();\n    forced_space_windows_event();\n  }\n\n  bool needs_refresh = false;\n  for (int i = 0; i < bar_manager->bar_item_count; i++) {\n    struct bar_item* bar_item = bar_manager->bar_items[i];\n    needs_refresh |= bar_item_update(bar_item, NULL, forced, NULL);\n\n    if (bar_item->has_alias\n        && bar_item_is_shown(bar_item)\n        && alias_update(&bar_item->alias, false)) {\n      bar_item_needs_update(bar_item);\n      needs_refresh = true;\n    }\n  }\n\n  if (needs_refresh || forced) bar_manager_refresh(bar_manager, forced, false);\n}\n\nvoid bar_manager_reset(struct bar_manager* bar_manager) {\n  bar_manager_reset_bar_association(bar_manager);\n  for (int i = 0; i < bar_manager->bar_count; i++) {\n    for (int j = 0; j < bar_manager->bar_item_count; j++) {\n      struct bar_item* bar_item = bar_manager->bar_items[j];\n      bar_item_remove_window(bar_item, bar_manager->bars[i]->adid);\n    }\n\n    bar_destroy(bar_manager->bars[i]);\n    bar_manager->bars[i] = NULL;\n  }\n  bar_manager->bar_count = 0;\n\n  bar_manager_begin(bar_manager);\n}\n\nvoid bar_manager_begin(struct bar_manager* bar_manager) {\n  if (bar_manager->displays == DISPLAY_MAIN_PATTERN) {\n    uint32_t did = display_main_display_id();\n    bar_manager->bar_count = 1;\n    bar_manager->bars = (struct bar **) realloc(\n                                bar_manager->bars,\n                                sizeof(struct bar *) * bar_manager->bar_count);\n\n    memset(bar_manager->bars, 0, sizeof(struct bar*) * bar_manager->bar_count);\n    bar_manager->bars[0] = bar_create(did);\n    bar_manager->bars[0]->adid = display_arrangement(did);\n  }\n  else {\n    uint32_t display_count = display_active_display_count();\n    uint32_t bar_count = 0;\n    for (uint32_t index = 1; index <= display_count; index++) {\n      if (!(bar_manager->displays & 1 << (index - 1))) continue;\n      bar_count++;\n    }\n\n    bar_manager->bar_count = bar_count;\n    bar_manager->bars = (struct bar **) realloc(\n                                bar_manager->bars,\n                                sizeof(struct bar *) * bar_manager->bar_count);\n\n    memset(bar_manager->bars, 0, sizeof(struct bar*) * bar_manager->bar_count);\n\n    uint32_t bar_index = 0;\n    for (uint32_t index = 1; index <= display_count; index++) {\n      if (!(bar_manager->displays & 1 << (index - 1))) continue;\n      uint32_t did = display_arrangement_display_id(index);\n      bar_manager->bars[bar_index] = bar_create(did);\n      bar_manager->bars[bar_index]->adid = index;\n      if (bar_manager->any_bar_hidden)\n        bar_set_hidden(bar_manager->bars[bar_index], true);\n\n      bar_index++;\n    }\n  }\n\n  bar_manager->active_displays = 0;\n  for (int i = 0; i < bar_manager->bar_count; i++) {\n    bar_manager->active_displays |= 1 << bar_manager->bars[i]->adid;\n  }\n\n  bar_manager->active_adid = display_active_display_adid();\n  bar_manager->needs_ordering = true;\n}\n\nstruct bar_item* bar_manager_get_item_by_point(struct bar_manager* bar_manager, CGPoint point, struct window** window_out) {\n  for (int i = 0; i < bar_manager->bar_item_count; i++) {\n    struct bar_item* bar_item = bar_manager->bar_items[i];\n    if (!bar_item->drawing) continue;\n\n    for (int adid = 1; adid <= bar_item->num_windows; adid++) {\n      struct window* window = bar_item_get_window(bar_item, adid);\n      if (!window) continue;\n\n      CGRect frame = window->frame;\n      frame.origin = window->origin;\n      if (cgrect_contains_point(&frame, &point)) {\n        if (window_out) *window_out = window;\n        return bar_item;\n      }\n    }\n  }\n  return NULL;\n}\n\nstruct bar_item* bar_manager_get_item_by_wid(struct bar_manager* bar_manager, uint32_t wid, struct window** window_out) {\n  for (int i = 0; i < bar_manager->bar_item_count; i++) {\n    struct bar_item* bar_item = bar_manager->bar_items[i];\n    if (!bar_item->drawing) continue;\n\n    for (int adid = 1; adid <= bar_item->num_windows; adid++) {\n      struct window* window = bar_item_get_window(bar_item, adid);\n      if (!window) continue;\n\n      if (window->id == wid) {\n        if (window_out) *window_out = window;\n        return bar_item;\n      }\n    }\n  }\n  return NULL;\n}\n\nstruct bar* bar_manager_get_bar_by_wid(struct bar_manager* bar_manager, uint32_t wid) {\n  for (int i = 0; i < bar_manager->bar_count; i++) {\n    if (bar_manager->bars[i]->window.id == wid) {\n      return bar_manager->bars[i];\n    }\n  }\n  return NULL;\n}\n\nbool bar_manager_mouse_over_any_bar(struct bar_manager* bar_manager) {\n  for (int i = 0; i < bar_manager->bar_count; i++) {\n    if (bar_manager->bars[i]->mouse_over) return true;\n  }\n  return false;\n}\n\nstruct popup* bar_manager_get_popup_by_wid(struct bar_manager* bar_manager, uint32_t wid) {\n  for (int i = 0; i < bar_manager->bar_item_count; i++) {\n    struct bar_item* bar_item = bar_manager->bar_items[i];\n    if (!bar_item->drawing || !bar_item->popup.drawing) {\n      continue;\n    }\n\n    struct window* window = &bar_item->popup.window;\n\n    if (window->id == wid) {\n      return &bar_item->popup;\n    }\n  }\n  return NULL;\n}\n\nstruct popup* bar_manager_get_popup_by_point(struct bar_manager* bar_manager, CGPoint point) {\n  for (int i = 0; i < bar_manager->bar_item_count; i++) {\n    struct bar_item* bar_item = bar_manager->bar_items[i];\n    if (!bar_item->drawing || !bar_item->popup.drawing) {\n      continue;\n    }\n\n    struct window* window = &bar_item->popup.window;\n\n    CGRect frame = window->frame;\n    frame.origin = window->origin;\n\n    if (CGRectContainsPoint(frame, point)) return &bar_item->popup;\n  }\n  return NULL;\n\n}\n\nstruct bar* bar_manager_get_bar_by_point(struct bar_manager* bar_manager, CGPoint point) {\n  for (int i = 0; i < bar_manager->bar_count; i++) {\n    struct window* window = &bar_manager->bars[i]->window;\n\n    CGRect frame = window->frame;\n    frame.origin = window->origin;\n\n    if (CGRectContainsPoint(frame, point)) return bar_manager->bars[i];\n  }\n  return NULL;\n}\n\nbool bar_manager_mouse_over_any_popup(struct bar_manager* bar_manager) {\n  for (int i = 0; i < bar_manager->bar_item_count; i++) {\n    struct bar_item* bar_item = bar_manager->bar_items[i];\n    if (!bar_item->drawing || !bar_item->popup.drawing) {\n      continue;\n    }\n    if (bar_item->popup.mouse_over) return true;\n  }\n  return false;\n}\n\nvoid bar_manager_custom_events_trigger(struct bar_manager* bar_manager, char* name, struct env_vars* env_vars) {\n  uint64_t flag = custom_events_get_flag_for_name(&bar_manager->custom_events,\n                                                  name                       );\n\n  for (int i = 0; i < bar_manager->bar_item_count; i++) {\n    struct bar_item* bar_item = bar_manager->bar_items[i];\n    if (bar_item->update_mask & flag)\n      bar_item_update(bar_item, name, false, env_vars);\n  }\n}\n\nvoid bar_manager_display_resized(struct bar_manager* bar_manager, uint32_t did) {\n  bar_manager_display_changed(bar_manager);\n}\n\nvoid bar_manager_display_moved(struct bar_manager* bar_manager, uint32_t did) {\n  bar_manager_display_changed(bar_manager);\n}\n\nvoid bar_manager_display_removed(struct bar_manager* bar_manager, uint32_t did) {\n  bar_manager_display_changed(bar_manager);\n}\n\nvoid bar_manager_display_added(struct bar_manager* bar_manager, uint32_t did) {\n  bar_manager_display_changed(bar_manager);\n}\n\nvoid bar_manager_display_changed(struct bar_manager* bar_manager) {\n  bar_manager->active_adid = display_active_display_adid();\n\n  bar_manager_freeze(bar_manager);\n  bar_manager_reset(bar_manager);\n  bar_manager_unfreeze(bar_manager);\n  bar_manager_refresh(bar_manager, true, false);\n\n  bar_manager_handle_display_change(bar_manager);\n  bar_manager_handle_space_change(bar_manager, true);\n  animator_renew_display_link(&bar_manager->animator);\n}\n\nvoid bar_manager_cancel_drag(struct bar_manager* bar_manager) {\n  for (uint32_t i = 0; i < bar_manager->bar_item_count; i++) {\n    bar_item_cancel_drag(bar_manager->bar_items[i]);\n  }\n}\n\nvoid bar_manager_handle_mouse_entered_global(struct bar_manager* bar_manager) {\n  bar_manager_custom_events_trigger(bar_manager,\n                                    COMMAND_SUBSCRIBE_MOUSE_ENTERED_GLOBAL,\n                                    NULL                                   );\n}\n\nvoid bar_manager_handle_mouse_exited_global(struct bar_manager* bar_manager) {\n  bar_manager_custom_events_trigger(bar_manager,\n                                    COMMAND_SUBSCRIBE_MOUSE_EXITED_GLOBAL,\n                                    NULL                                  );\n\n  bar_manager_handle_mouse_exited(bar_manager, NULL);\n}\n\nvoid bar_manager_handle_mouse_scrolled_global(struct bar_manager* bar_manager, int scroll_delta, uint32_t adid, uint32_t modifier) {\n  struct env_vars env_vars;\n  env_vars_init(&env_vars);\n  char delta_ver_str[32];\n  snprintf(delta_ver_str, 32, \"%d\", scroll_delta);\n  env_vars_set(&env_vars,\n               string_copy(\"SCROLL_DELTA\"),\n               string_copy(delta_ver_str));\n\n  char info_str[256];\n  snprintf(info_str, 256, \"{\\n\"\n                          \"\\t\\\"delta\\\": %d,\\n\"\n                          \"\\t\\\"modifier\\\": \\\"%s\\\",\\n\"\n                          \"\\t\\\"modfier_code\\\": %u\\n\"\n                          \"}\\n\",\n                          scroll_delta,\n                          get_modifier_description(modifier),\n                          modifier                           );\n\n  env_vars_set(&env_vars, string_copy(\"INFO\"), string_copy(info_str));\n\n  char adid_str[32];\n  snprintf(adid_str, 32, \"%u\", adid);\n  env_vars_set(&env_vars,\n               string_copy(\"DID\"),\n               string_copy(adid_str));\n\n  env_vars_set(&env_vars,\n               string_copy(\"MODIFIER\"),\n               string_copy(get_modifier_description(modifier)));\n\n  bar_manager_custom_events_trigger(bar_manager,\n                                    COMMAND_SUBSCRIBE_MOUSE_SCROLLED_GLOBAL,\n                                    &env_vars                               );\n  env_vars_destroy(&env_vars);\n}\n\nvoid bar_manager_handle_mouse_entered(struct bar_manager* bar_manager, struct bar_item* bar_item) {\n  if (!bar_item) return;\n  bar_item_mouse_entered(bar_item);\n}\n\nvoid bar_manager_handle_mouse_exited(struct bar_manager* bar_manager, struct bar_item* bar_item) {\n  if (!bar_item) {\n    for (int i = 0; i < bar_manager->bar_item_count; i++)\n      bar_item_mouse_exited(bar_manager->bar_items[i]);\n  } else {\n    bar_item_mouse_exited(bar_item);\n  }\n}\n\nvoid bar_manager_handle_volume_change(struct bar_manager* bar_manager, float volume) {\n  struct env_vars env_vars;\n  env_vars_init(&env_vars);\n  char volume_str[16];\n  snprintf(volume_str, 16, \"%d\", (int)(volume*100. + 0.5));\n  env_vars_set(&env_vars, string_copy(\"INFO\"), string_copy(volume_str));\n  bar_manager_custom_events_trigger(bar_manager,\n                                    COMMAND_SUBSCRIBE_VOLUME_CHANGE,\n                                    &env_vars                       );\n  env_vars_destroy(&env_vars);\n}\n\nvoid bar_manager_handle_wifi_change(struct bar_manager* bar_manager, char* ssid) {\n  struct env_vars env_vars;\n  env_vars_init(&env_vars);\n  env_vars_set(&env_vars, string_copy(\"INFO\"), string_copy(ssid));\n  bar_manager_custom_events_trigger(bar_manager,\n                                    COMMAND_SUBSCRIBE_WIFI_CHANGE,\n                                    &env_vars                     );\n  env_vars_destroy(&env_vars);\n}\n\nvoid bar_manager_handle_brightness_change(struct bar_manager* bar_manager, float brightness) {\n  struct env_vars env_vars;\n  env_vars_init(&env_vars);\n  char brightness_str[16];\n  snprintf(brightness_str, 16, \"%d\", (int)(brightness*100. + 0.5));\n  env_vars_set(&env_vars, string_copy(\"INFO\"), string_copy(brightness_str));\n  bar_manager_custom_events_trigger(bar_manager,\n                                    COMMAND_SUBSCRIBE_BRIGHTNESS_CHANGE,\n                                    &env_vars                           );\n  env_vars_destroy(&env_vars);\n}\n\nvoid bar_manager_handle_power_source_change(struct bar_manager* bar_manager, char* state) {\n  struct env_vars env_vars;\n  env_vars_init(&env_vars);\n  env_vars_set(&env_vars, string_copy(\"INFO\"), string_copy(state));\n  bar_manager_custom_events_trigger(bar_manager,\n                                    COMMAND_SUBSCRIBE_POWER_SOURCE_CHANGE,\n                                    &env_vars                             );\n  env_vars_destroy(&env_vars);\n}\n\nvoid bar_manager_handle_media_change(struct bar_manager* bar_manager, char* info) {\n  struct env_vars env_vars;\n  env_vars_init(&env_vars);\n  env_vars_set(&env_vars, string_copy(\"INFO\"), string_copy(info));\n  bar_manager_custom_events_trigger(bar_manager,\n                                    COMMAND_SUBSCRIBE_MEDIA_CHANGE,\n                                    &env_vars                      );\n  env_vars_destroy(&env_vars);\n}\n\nvoid bar_manager_handle_media_cover_change(struct bar_manager* bar_manager, CGImageRef image) {\n  bool needs_refresh = false;\n  if (image_set_image(&bar_manager->current_artwork, image, CGRectNull, false)){\n    for (int i = 0; i < bar_manager->bar_item_count; i++) {\n      struct bar_item* bar_item = bar_manager->bar_items[i];\n      needs_refresh |= bar_item_set_media_cover(bar_item,\n                                                &bar_manager->current_artwork);\n    }\n  }\n\n  if (needs_refresh) bar_manager_refresh(bar_manager, false, false);\n}\n\nvoid bar_manager_handle_front_app_switch(struct bar_manager* bar_manager, char* info) {\n  struct env_vars env_vars;\n  env_vars_init(&env_vars);\n  if (info) env_vars_set(&env_vars, string_copy(\"INFO\"), info);\n  bar_manager_custom_events_trigger(bar_manager,\n                                    COMMAND_SUBSCRIBE_FRONT_APP_SWITCHED,\n                                    &env_vars                            );\n  env_vars_destroy(&env_vars);\n}\n\nvoid bar_manager_handle_space_windows_change(struct bar_manager* bar_manager, char* info) {\n  struct env_vars env_vars;\n  env_vars_init(&env_vars);\n  if (info) env_vars_set(&env_vars, string_copy(\"INFO\"), string_copy(info));\n  bar_manager_custom_events_trigger(bar_manager,\n                                    COMMAND_SUBSCRIBE_SPACE_WINDOWS_CHANGE,\n                                    &env_vars                              );\n  env_vars_destroy(&env_vars);\n}\n\nvoid bar_manager_handle_space_change(struct bar_manager* bar_manager, bool forced) {\n  struct env_vars env_vars;\n  env_vars_init(&env_vars);\n  char info[19 * bar_manager->bar_count + 4];\n  info[0] = '{';\n  info[1] = '\\n';\n  uint32_t cursor = 2;\n  char separator[] = \",\";\n  bar_manager_freeze(bar_manager);\n  bool force_refresh = false;\n  for (int i = 0; i < bar_manager->bar_count; i++) {\n    uint64_t dsid = display_space_id(bar_manager->bars[i]->did);\n    bar_manager->bars[i]->sid = mission_control_index(dsid);\n\n    bool was_shown = bar_manager->bars[i]->shown;\n    bar_manager->bars[i]->shown = SLSSpaceGetType(g_connection, dsid) != 4 || bar_manager->show_in_fullscreen;\n\n    bar_manager->needs_ordering |= !was_shown && bar_manager->bars[i]->shown;\n    force_refresh |= !was_shown && bar_manager->bars[i]->shown;\n    force_refresh |= !bar_manager->bars[i]->shown && was_shown;\n\n    if (bar_manager->bars[i]->dsid != dsid) {\n      bar_manager->bars[i]->dsid = dsid;\n      if (!bar_manager->sticky && bar_manager->bars[i]->shown)\n        bar_change_space(bar_manager->bars[i], dsid);\n    }\n    if (i == bar_manager->bar_count - 1)\n      separator[0] = '\\0';\n\n    snprintf(info + cursor, 19 * bar_manager->bar_count + 4 - cursor,\n                            \"\\t\\\"display-%d\\\": %d%s\\n\",\n                            bar_manager->bars[i]->adid,\n                            bar_manager->bars[i]->sid,\n                            separator                                );\n    cursor = strlen(info);\n  }\n\n  info[cursor] = '}';\n  info[cursor + 1] = '\\0';\n  env_vars_set(&env_vars, string_copy(\"INFO\"), string_copy(info));\n\n  bar_manager_update_space_components(bar_manager, forced);\n  bar_manager_custom_events_trigger(bar_manager,\n                                    COMMAND_SUBSCRIBE_SPACE_CHANGE,\n                                    &env_vars                      );\n\n\n  bar_manager_unfreeze(bar_manager);\n  bar_manager_refresh(bar_manager, force_refresh, false);\n  env_vars_destroy(&env_vars);\n}\n\nvoid bar_manager_poll_active_display(struct bar_manager* bar_manager) {\n  uint32_t aadid = display_active_display_adid();\n  if (aadid != bar_manager->active_adid) {\n    bar_manager_handle_display_change(bar_manager);\n  }\n}\n\nvoid bar_manager_handle_display_change(struct bar_manager* bar_manager) {\n  bar_manager->active_adid = display_active_display_adid();\n  struct env_vars env_vars;\n  env_vars_init(&env_vars);\n  char adid_str[3];\n  snprintf(adid_str, 3, \"%d\", bar_manager->active_adid);\n  env_vars_set(&env_vars, string_copy(\"INFO\"), string_copy(adid_str));\n\n  bar_manager_custom_events_trigger(bar_manager,\n                                    COMMAND_SUBSCRIBE_DISPLAY_CHANGE,\n                                    &env_vars                        );\n\n  bar_manager_refresh(bar_manager, false, false);\n  env_vars_destroy(&env_vars);\n}\n\nvoid bar_manager_handle_system_will_sleep(struct bar_manager* bar_manager) {\n  bar_manager_custom_events_trigger(bar_manager,\n                                    COMMAND_SUBSCRIBE_SYSTEM_WILL_SLEEP,\n                                    NULL                                );\n  animator_destroy_display_link(&bar_manager->animator);\n  bar_manager->sleeps = true;\n}\n\nvoid bar_manager_handle_system_woke(struct bar_manager* bar_manager) {\n  if (bar_manager->sleeps) {\n    bar_manager->sleeps = false;\n\n    // Sometimes the system wake notification precedes the display layout\n    // changes, so we queue a second wake event slightly later.\n    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{\n      usleep(500000);\n      dispatch_async(dispatch_get_main_queue(), ^{\n        struct event event = { NULL, SYSTEM_WOKE };\n        event_post(&event);\n      });\n    });\n  }\n\n  bar_manager_display_changed(bar_manager);\n  bar_manager_custom_events_trigger(bar_manager,\n                                    COMMAND_SUBSCRIBE_SYSTEM_WOKE,\n                                    NULL                          );\n}\n\nvoid bar_manager_handle_notification(struct bar_manager* bar_manager, struct notification* notification) {\n  char* name = custom_events_get_name_for_notification(&bar_manager->custom_events, notification->name);\n  if (!name) {\n    notification_destroy(notification);\n    return;\n  }\n\n  struct env_vars env_vars;\n  env_vars_init(&env_vars);\n  if (notification->info) env_vars_set(&env_vars,\n                                       string_copy(\"INFO\"),\n                                       string_copy(notification->info));\n\n  bar_manager_custom_events_trigger(bar_manager, name, &env_vars);\n  env_vars_destroy(&env_vars);\n  notification_destroy(notification);\n}\n\nvoid bar_manager_destroy(struct bar_manager* bar_manager) {\n  for (int i = 0; i < bar_manager->bar_item_count; i++) {\n    struct bar_item* bar_item = bar_manager->bar_items[i];\n    if (bar_item->event_port) {\n      mach_send_message(bar_item->event_port, \"k\", 2, false);\n    }\n  }\n\n  animator_destroy(&bar_manager->animator);\n\n  while (bar_manager->bar_item_count > 0) {\n    bar_manager_remove_item(bar_manager, bar_manager->bar_items[0]);\n  }\n\n  if (bar_manager->bar_items) free(bar_manager->bar_items);\n  for (int i = 0; i < bar_manager->bar_count; i++) {\n    bar_destroy(bar_manager->bars[i]);\n  }\n\n  bar_item_destroy(&bar_manager->default_item, false);\n  custom_events_destroy(&bar_manager->custom_events);\n  background_destroy(&bar_manager->background);\n\n  if (bar_manager->bars) free(bar_manager->bars);\n  CFRunLoopRemoveTimer(CFRunLoopGetMain(),\n                       bar_manager->clock,\n                       kCFRunLoopCommonModes);\n\n  CFRunLoopTimerInvalidate(bar_manager->clock);\n  CFRelease(bar_manager->clock);\n  image_destroy(&bar_manager->current_artwork);\n}\n\nvoid bar_manager_serialize(struct bar_manager* bar_manager, FILE* rsp) {\n  char indent[] = { \"\\t\" };\n  fprintf(rsp, \"{\\n\"\n               \"%s\\\"position\\\": \\\"%s\\\",\\n\"\n               \"%s\\\"topmost\\\": \\\"%s\\\",\\n\"\n               \"%s\\\"sticky\\\": \\\"%s\\\",\\n\"\n               \"%s\\\"hidden\\\": \\\"%s\\\",\\n\"\n               \"%s\\\"shadow\\\": \\\"%s\\\",\\n\"\n               \"%s\\\"font_smoothing\\\": \\\"%s\\\",\\n\"\n               \"%s\\\"show_in_fullscreen\\\": \\\"%s\\\",\\n\"\n               \"%s\\\"blur_radius\\\": %u,\\n\"\n               \"%s\\\"margin\\\": %d,\\n\",\n               indent, bar_manager->position == POSITION_BOTTOM\n                                              ? \"bottom\" : \"top\",\n               indent, format_bool(bar_manager->topmost),\n               indent, format_bool(bar_manager->sticky),\n               indent, format_bool(bar_manager->any_bar_hidden),\n               indent, format_bool(bar_manager->shadow),\n               indent, format_bool(bar_manager->font_smoothing),\n               indent, format_bool(bar_manager->show_in_fullscreen),\n               indent, bar_manager->blur_radius,\n               indent, bar_manager->margin         );\n\n  background_serialize(&bar_manager->background, indent, rsp, false);\n\n  fprintf(rsp, \",\\n%s\\\"items\\\": [\\n\", indent);\n  for (int i = 0; i < bar_manager->bar_item_count; i++) {\n    fprintf(rsp, \"%s\\t \\\"%s\\\"\", indent, bar_manager->bar_items[i]->name);\n    if (i < bar_manager->bar_item_count - 1) fprintf(rsp, \",\\n\");\n  }\n  fprintf(rsp, \"\\n%s]\\n}\\n\", indent);\n}\n"
  },
  {
    "path": "src/bar_manager.h",
    "content": "#pragma once\n#include \"bar.h\"\n#include \"bar_item.h\"\n#include \"animation.h\"\n\n#define CLOCK_CALLBACK(name) void name(CFRunLoopTimerRef timer, void *context)\ntypedef CLOCK_CALLBACK(clock_callback);\n\n#define DISPLAY_MAIN_PATTERN 0\n#define DISPLAY_ALL_PATTERN  UINT32_MAX\n\n#define TOPMOST_LEVEL_WINDOW 'w'\n#define TOPMOST_LEVEL_ALL    'a'\n\nstruct bar_manager {\n  CFRunLoopTimerRef clock;\n\n  bool frozen;\n  bool sleeps;\n  bool shadow;\n  bool topmost;\n  bool sticky;\n  bool font_smoothing;\n  bool any_bar_hidden;\n  bool needs_ordering;\n  bool might_need_clipping;\n  bool bar_needs_update;\n  bool bar_needs_resize;\n  bool show_in_fullscreen;\n\n  uint32_t displays;\n  char position;\n\n  int margin;\n  uint32_t blur_radius;\n  uint32_t notch_width;\n  uint32_t notch_offset;\n  uint32_t notch_display_height;\n  uint32_t active_adid;\n  uint32_t window_level;\n\n  struct bar** bars;\n  uint32_t bar_count;\n  uint32_t active_displays;\n\n  struct bar_item** bar_items;\n  struct bar_item default_item;\n  uint32_t bar_item_count;\n\n  struct background background;\n  struct custom_events custom_events;\n\n  struct animator animator;\n  struct image current_artwork;\n};\n\nvoid bar_manager_init(struct bar_manager* bar_manager);\nvoid bar_manager_begin(struct bar_manager* bar_manager);\nvoid bar_manager_reset(struct bar_manager* bar_manager);\n\nstruct bar_item* bar_manager_create_item(struct bar_manager* bar_manager);\nvoid bar_manager_remove_item(struct bar_manager* bar_manager, struct bar_item* bar_item);\nvoid bar_manager_move_item(struct bar_manager* bar_manager, struct bar_item* item, struct bar_item* reference, bool before);\nvoid bar_manager_handle_notification(struct bar_manager* bar_manager, struct notification* notification);\n\nvoid bar_manager_animator_refresh(struct bar_manager* bar_manager, uint64_t time);\nvoid bar_manager_update(struct bar_manager* bar_manager, bool forced);\nvoid bar_manager_update_space_components(struct bar_manager* bar_manager, bool forced);\nbool bar_manager_set_margin(struct bar_manager* bar_manager, int margin);\nbool bar_manager_set_y_offset(struct bar_manager* bar_manager, int y_offset);\nbool bar_manager_set_bar_height(struct bar_manager* bar_manager, int height);\nbool bar_manager_set_background_blur(struct bar_manager* bar_manager, uint32_t radius);\nbool bar_manager_set_position(struct bar_manager* bar_manager, char pos);\nbool bar_manager_set_spaces(struct bar_manager* bar_manager, bool value);\nbool bar_manager_set_spaces_for_all_displays(struct bar_manager* bar_manager, bool value);\nbool bar_manager_set_displays(struct bar_manager* bar_manager, uint32_t displays);\nbool bar_manager_set_hidden(struct bar_manager* bar_manager, uint32_t sid, bool hidden);\nbool bar_manager_set_topmost(struct bar_manager* bar_manager, char level, bool topmost);\nbool bar_manager_set_sticky(struct bar_manager *bar_manager, bool sticky);\nbool bar_manager_set_shadow(struct bar_manager* bar_manager, bool shadow);\nbool bar_manager_set_font_smoothing(struct bar_manager* bar_manager, bool smoothing);\nbool bar_manager_set_show_in_fullscreen(struct bar_manager* bar_manager, bool show_in_fullscreen);\nbool bar_manager_set_notch_width(struct bar_manager* bar_manager, uint32_t width);\nbool bar_manager_set_notch_offset(struct bar_manager* bar_manager, uint32_t offset);\nbool bar_manager_set_notch_display_height(struct bar_manager* bar_manager, uint32_t offset);\nvoid bar_manager_sort(struct bar_manager* bar_manager, struct bar_item** ordering, uint32_t count);\n\nstruct bar_item* bar_manager_get_item_by_point(struct bar_manager* bar_manager, CGPoint point, struct window** window_out);\nstruct bar* bar_manager_get_bar_by_point(struct bar_manager* bar_manager, CGPoint point);\nstruct popup* bar_manager_get_popup_by_point(struct bar_manager* bar_manager, CGPoint point);\nstruct bar_item* bar_manager_get_item_by_wid(struct bar_manager* bar_manager, uint32_t wid, struct window** window_out);\nstruct popup* bar_manager_get_popup_by_wid(struct bar_manager* bar_manager, uint32_t wid);\nstruct bar* bar_manager_get_bar_by_wid(struct bar_manager* bar_manager, uint32_t wid);\nint bar_manager_get_item_index_for_name(struct bar_manager* bar_manager, char* name);\nuint32_t bar_manager_length_for_bar_side(struct bar_manager* bar_manager, struct bar* bar, char side);\nbool bar_manager_mouse_over_any_popup(struct bar_manager* bar_manager);\nbool bar_manager_mouse_over_any_bar(struct bar_manager* bar_manager);\n\nvoid bar_manager_freeze(struct bar_manager* bar_manager);\nvoid bar_manager_unfreeze(struct bar_manager* bar_manager);\n\nvoid bar_manager_display_changed(struct bar_manager* bar_manager);\nvoid bar_manager_display_resized(struct bar_manager* bar_manager, uint32_t did);\nvoid bar_manager_display_moved(struct bar_manager* bar_manager, uint32_t did);\nvoid bar_manager_display_removed(struct bar_manager* bar_manager, uint32_t did);\nvoid bar_manager_display_added(struct bar_manager* bar_manager, uint32_t did);\nvoid bar_manager_refresh(struct bar_manager* bar_manager, bool forced, bool threaded);\nvoid bar_manager_resize(struct bar_manager* bar_manager);\n\nvoid bar_manager_poll_active_display(struct bar_manager* bar_manager);\nvoid bar_manager_handle_mouse_entered_global(struct bar_manager* bar_manager);\nvoid bar_manager_handle_mouse_exited_global(struct bar_manager* bar_manager);\nvoid bar_manager_handle_mouse_scrolled_global(struct bar_manager* bar_manager, int scroll_delta, uint32_t did, uint32_t modifier);\nvoid bar_manager_handle_mouse_entered(struct bar_manager* bar_manager, struct bar_item* bar_item);\nvoid bar_manager_handle_mouse_exited(struct bar_manager* bar_manager, struct bar_item* bar_item);\nvoid bar_manager_handle_front_app_switch(struct bar_manager* bar_manager, char* info);\nvoid bar_manager_handle_space_change(struct bar_manager* bar_manager, bool forced);\nvoid bar_manager_handle_display_change(struct bar_manager* bar_manager);\nvoid bar_manager_handle_system_woke(struct bar_manager* bar_manager);\nvoid bar_manager_handle_system_will_sleep(struct bar_manager* bar_manager);\nvoid bar_manager_handle_volume_change(struct bar_manager* bar_manager, float volume);\nvoid bar_manager_handle_wifi_change(struct bar_manager* bar_manager, char* ssid);\nvoid bar_manager_handle_brightness_change(struct bar_manager* bar_manager, float brightness);\nvoid bar_manager_handle_power_source_change(struct bar_manager* bar_manager, char* state);\nvoid bar_manager_handle_media_change(struct bar_manager* bar_manager, char* info);\nvoid bar_manager_handle_media_cover_change(struct bar_manager* bar_manager, CGImageRef image);\nvoid bar_manager_handle_space_windows_change(struct bar_manager* bar_manager, char* info);\nvoid bar_manager_custom_events_trigger(struct bar_manager* bar_manager, char* name, struct env_vars* env_vars);\n\nvoid bar_manager_destroy(struct bar_manager* bar_manager);\n\nvoid bar_manager_serialize(struct bar_manager* bar_manager, FILE* rsp);\n"
  },
  {
    "path": "src/color.c",
    "content": "#include \"color.h\"\n#include \"bar_manager.h\"\n#include \"animation.h\"\n\nstatic bool color_update_hex(struct color* color) {\n  uint32_t prev = color->hex;\n  color->hex = (((uint32_t)(color->a * 255.f)) << 24)\n               + (((uint32_t)(color->r * 255.f)) << 16)\n               + (((uint32_t)(color->g * 255.f)) << 8)\n               + (((uint32_t)(color->b * 255.f)) << 0);\n\n  return prev != color->hex;\n}\n\nvoid color_init(struct color* color, uint32_t hex) {\n  color_set_hex(color, hex);\n}\n\nbool color_set_hex(struct color* color, uint32_t hex) {\n  color->a = clamp(((hex >> 24) & 0xff) / 255.f, 0.f, 1.f);\n  color->r = clamp(((hex >> 16) & 0xff) / 255.f, 0.f, 1.f);\n  color->g = clamp(((hex >> 8) & 0xff) / 255.f, 0.f, 1.f);\n  color->b = clamp(((hex >> 0) & 0xff) / 255.f, 0.f, 1.f);\n  return color_update_hex(color);\n}\n\nbool color_set_alpha(struct color* color, float alpha) {\n  color->a = clamp(alpha, 0.f, 1.f);\n  return color_update_hex(color);\n}\n\nbool color_set_r(struct color* color, float red) {\n  color->r = clamp(red, 0.f, 1.f);\n  return color_update_hex(color);\n}\n\nbool color_set_g(struct color* color, float green) {\n  color->g = clamp(green, 0.f, 1.f);\n  return color_update_hex(color);\n}\n\nbool color_set_b(struct color* color, float blue) {\n  color->b = clamp(blue, 0.f, 1.f);\n  return color_update_hex(color);\n}\n\nbool color_parse_sub_domain(struct color* color, FILE* rsp, struct token property, char* message) {\n  bool needs_refresh = false;\n\n  if (token_equals(property, PROPERTY_COLOR_HEX)) {\n    ANIMATE_BYTES(color_set_hex,\n                  color,\n                  color->hex,\n                  token_to_int(get_token(&message)));\n  }\n  else if (token_equals(property, PROPERTY_COLOR_ALPHA)) {\n    ANIMATE_FLOAT(color_set_alpha,\n                  color,\n                  color->a,\n                  token_to_float(get_token(&message)));\n  }\n  else if (token_equals(property, PROPERTY_COLOR_RED)) {\n    ANIMATE_FLOAT(color_set_r,\n                  color,\n                  color->r,\n                  token_to_float(get_token(&message)));\n  }\n  else if (token_equals(property, PROPERTY_COLOR_GREEN)) {\n    ANIMATE_FLOAT(color_set_g,\n                  color,\n                  color->g,\n                  token_to_float(get_token(&message)));\n  }\n  else if (token_equals(property, PROPERTY_COLOR_BLUE)) {\n    ANIMATE_FLOAT(color_set_b,\n                  color,\n                  color->b,\n                  token_to_float(get_token(&message)));\n  } else {\n    respond(rsp, \"[?] Color: Invalid property '%s'\\n\", property);\n  }\n\n  return needs_refresh;\n}\n"
  },
  {
    "path": "src/color.h",
    "content": "#pragma once\n#include <stdbool.h>\n#include <stdint.h>\n#include \"misc/helpers.h\"\n\nstruct color {\n  float r;\n  float g;\n  float b;\n  float a;\n  uint32_t hex;\n};\n\nstatic struct color g_transparent = { 0 };\n\nvoid color_init(struct color* color, uint32_t hex);\nbool color_set_hex(struct color* color, uint32_t hex);\nbool color_set_alpha(struct color* color, float alpha);\nbool color_set_r(struct color* color, float red);\nbool color_set_g(struct color* color, float green);\nbool color_set_b(struct color* color, float blue);\n\nbool color_parse_sub_domain(struct color* color, FILE* rsp, struct token property, char* message);\n"
  },
  {
    "path": "src/custom_events.c",
    "content": "#include \"custom_events.h\"\n\nstatic struct custom_event* custom_event_create(void) {\n  return malloc(sizeof(struct custom_event));\n}\n\nvoid custom_event_init(struct custom_event* custom_event, char* name, char* notification) {\n  custom_event->name = name;\n  custom_event->notification = notification;\n}\n\nvoid custom_event_destroy(struct custom_event* custom_event) {\n  if (custom_event->name) free(custom_event->name);\n  if (custom_event->notification) free(custom_event->notification);\n  free(custom_event);\n}\n\nvoid custom_events_init(struct custom_events* custom_events) {\n  custom_events->count = 0;\n  custom_events->events = NULL;\n\n  // System Events\n  custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_FRONT_APP_SWITCHED), NULL);\n  custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_SPACE_CHANGE), NULL);\n  custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_DISPLAY_CHANGE), NULL);\n  custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_SYSTEM_WOKE), NULL);\n  custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_MOUSE_ENTERED), NULL);\n  custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_MOUSE_EXITED), NULL);\n  custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_MOUSE_CLICKED), NULL);\n  custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_MOUSE_SCROLLED), NULL);\n  custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_SYSTEM_WILL_SLEEP), NULL);\n  custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_MOUSE_ENTERED_GLOBAL), NULL);\n  custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_MOUSE_EXITED_GLOBAL), NULL);\n  custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_MOUSE_SCROLLED_GLOBAL), NULL);\n  custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_VOLUME_CHANGE), NULL);\n  custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_BRIGHTNESS_CHANGE), NULL);\n  custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_POWER_SOURCE_CHANGE), NULL);\n  custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_WIFI_CHANGE), NULL);\n  custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_MEDIA_CHANGE), NULL);\n  custom_events_append(custom_events, string_copy(COMMAND_SUBSCRIBE_SPACE_WINDOWS_CHANGE), NULL);\n}\n\nvoid custom_events_append(struct custom_events* custom_events, char* name, char* notification) {\n  if (custom_events_get_flag_for_name(custom_events, name) > 0) { \n    if (name) free(name);\n    if (notification) free(notification);\n    return; \n  }\n  custom_events->count++;\n  custom_events->events = (struct custom_event**) realloc(\n                          custom_events->events,\n                          sizeof(struct custom_event*) * custom_events->count);\n\n  custom_events->events[custom_events->count - 1] = custom_event_create();\n  custom_events->events[custom_events->count - 1]->name = name;\n  custom_events->events[custom_events->count - 1]->notification = notification;\n  if (notification)\n    workspace_create_custom_observer(&g_workspace_context, notification);\n}\n\nuint64_t custom_events_get_flag_for_name(struct custom_events* custom_events, char* name) {\n  for (int i = 0; i < custom_events->count; i++) {\n    if (strcmp(name, custom_events->events[i]->name) == 0) {\n      return 1ULL << i;\n    }\n  }\n  return 0;\n}\n\nchar* custom_events_get_name_for_notification(struct custom_events* custom_events, char* notification) {\n  for (int i = 0; i < custom_events->count; i++) {\n    if (!custom_events->events[i]->notification) continue;\n    if (strcmp(notification, custom_events->events[i]->notification) == 0) {\n      return custom_events->events[i]->name;\n    }\n  }\n  return NULL;\n}\n\nvoid custom_events_destroy(struct custom_events* custom_events) {\n  for (int i = 0; i < custom_events->count; i++) {\n    custom_event_destroy(custom_events->events[i]);\n  }\n  free(custom_events->events);\n}\n\nvoid custom_events_serialize(struct custom_events* custom_events, FILE* rsp) {\n  fprintf(rsp, \"{\\n\");\n  for (int i = 0; i < custom_events->count; i++) {\n    fprintf(rsp, \"\\t\\\"%s\\\": {\\n\"\n                 \"\\t\\t\\\"bit\\\": %llu,\\n\"\n                 \"\\t\\t\\\"notification\\\": \\\"%s\\\"\\n\",\n                 custom_events->events[i]->name,\n                 1ULL << i,\n                 custom_events->events[i]->notification);\n    if (i < custom_events->count - 1) fprintf(rsp, \"\\t},\\n\");\n  }\n  fprintf(rsp, \"\\t}\\n}\\n\");\n}\n"
  },
  {
    "path": "src/custom_events.h",
    "content": "#pragma once\n#include \"misc/helpers.h\"\n\n#define UPDATE_FRONT_APP_SWITCHED   1ULL\n#define UPDATE_SPACE_CHANGE         (1ULL << 1)\n#define UPDATE_DISPLAY_CHANGE       (1ULL << 2)\n#define UPDATE_SYSTEM_WOKE          (1ULL << 3)\n#define UPDATE_MOUSE_ENTERED        (1ULL << 4)\n#define UPDATE_MOUSE_EXITED         (1ULL << 5)\n#define UPDATE_MOUSE_CLICKED        (1ULL << 6)\n#define UPDATE_MOUSE_SCROLLED       (1ULL << 7)\n#define UPDATE_SYSTEM_WILL_SLEEP    (1ULL << 8)\n#define UPDATE_ENTERED_GLOBAL       (1ULL << 9)\n#define UPDATE_EXITED_GLOBAL        (1ULL << 10)\n#define UPDATE_SCROLLED_GLOBAL      (1ULL << 11)\n#define UPDATE_VOLUME_CHANGE        (1ULL << 12)\n#define UPDATE_BRIGHTNESS_CHANGE    (1ULL << 13)\n#define UPDATE_POWER_SOURCE_CHANGE  (1ULL << 14)\n#define UPDATE_WIFI_CHANGE          (1ULL << 15)\n#define UPDATE_MEDIA_CHANGE         (1ULL << 16)\n#define UPDATE_SPACE_WINDOWS_CHANGE (1ULL << 17)\n\nextern void* g_workspace_context;\nextern void workspace_create_custom_observer(void** context, char* name);\n\nstruct custom_event {\n  char* name;\n  char* notification;\n};\n\nvoid custom_event_init(struct custom_event* custom_event, char* name, char* notification);\n\nstruct custom_events {\n  uint32_t count;\n  struct custom_event** events;\n};\n\nvoid custom_events_init(struct custom_events* custom_events);\nvoid custom_events_append(struct custom_events* custom_events, char* name, char* notification);\nuint64_t custom_events_get_flag_for_name(struct custom_events* custom_events, char* name);\nchar* custom_events_get_name_for_notification(struct custom_events* custom_events, char* notification);\nvoid custom_events_destroy(struct custom_events* custom_events);\n\nvoid custom_events_serialize(struct custom_events* custom_events, FILE* rsp);\n"
  },
  {
    "path": "src/display.c",
    "content": "#include \"display.h\"\n#include \"misc/helpers.h\"\n\nextern int workspace_display_notch_height(uint32_t did);\nextern int g_connection;\nextern int g_space_management_mode;\nextern bool g_brightness_events;\n\n\nfloat g_last_brightness = -1.f;\nstatic void brightness_handler(void* notification_center, uint32_t did, void* name, const void* sender, CFDictionaryRef info) {\n  float b = 0;\n  float* brightness = &b;\n  DisplayServicesGetBrightness(did, brightness);\n  if (g_last_brightness < *brightness - 1e-2\n     || g_last_brightness > *brightness + 1e-2) {\n    g_last_brightness = *brightness;\n    struct event event = { (void*) brightness, BRIGHTNESS_CHANGED };\n    event_post(&event);\n  }\n}\n\nstatic DISPLAY_EVENT_HANDLER(display_handler) {\n  if (flags & kCGDisplayAddFlag) {\n    struct event event = { (void *)(intptr_t) did, DISPLAY_ADDED };\n    event_post(&event);\n\n    if (g_brightness_events && DisplayServicesCanChangeBrightness(did))\n      DisplayServicesRegisterForBrightnessChangeNotifications(did, did, (void*)brightness_handler);\n  } else if (flags & kCGDisplayRemoveFlag) {\n    struct event event = { (void *)(intptr_t) did, DISPLAY_REMOVED };\n    event_post(&event);\n\n    if (g_brightness_events && DisplayServicesCanChangeBrightness(did))\n      DisplayServicesUnregisterForBrightnessChangeNotifications(did, did);\n  } else if (flags & kCGDisplayMovedFlag) {\n    struct event event = { (void *)(intptr_t) did, DISPLAY_MOVED };\n    event_post(&event);\n  } else if (flags & kCGDisplayDesktopShapeChangedFlag) {\n    struct event event = { (void *)(intptr_t) did, DISPLAY_RESIZED };\n    event_post(&event);\n  }\n}\n\nCFStringRef display_uuid(uint32_t did) {\n  CFUUIDRef uuid_ref = CGDisplayCreateUUIDFromDisplayID(did);\n  if (!uuid_ref) return NULL;\n\n  CFStringRef uuid_str = CFUUIDCreateString(NULL, uuid_ref);\n  CFRelease(uuid_ref);\n\n  return uuid_str;\n}\n\nCGRect display_bounds(uint32_t did) {\n  return CGDisplayBounds(did);\n}\n\nuint64_t display_space_id(uint32_t did) {\n  CFStringRef uuid = display_uuid(did);\n  if (!uuid) return 0;\n\n  uint64_t sid = SLSManagedDisplayGetCurrentSpace(g_connection, uuid);\n  CFRelease(uuid);\n  return sid;\n}\n\nuint64_t *display_space_list(uint32_t did, int *count) {\n  CFStringRef uuid = display_uuid(did);\n  if (!uuid) return NULL;\n\n  CFArrayRef display_spaces_ref = SLSCopyManagedDisplaySpaces(g_connection);\n  if (!display_spaces_ref) return NULL;\n\n  uint64_t *space_list = NULL;\n  int display_spaces_count = CFArrayGetCount(display_spaces_ref);\n\n  for (int i = 0; i < display_spaces_count; ++i) {\n    CFDictionaryRef display_ref = CFArrayGetValueAtIndex(display_spaces_ref, i);\n    CFStringRef identifier = CFDictionaryGetValue(display_ref,\n                                                  CFSTR(\"Display Identifier\"));\n\n    if (!CFEqual(uuid, identifier)) continue;\n\n    CFArrayRef spaces_ref = CFDictionaryGetValue(display_ref, CFSTR(\"Spaces\"));\n    int spaces_count = CFArrayGetCount(spaces_ref);\n\n    space_list = malloc(sizeof(uint64_t) * spaces_count);\n    *count = spaces_count;\n\n    for (int j = 0; j < spaces_count; ++j) {\n      CFDictionaryRef space_ref = CFArrayGetValueAtIndex(spaces_ref, j);\n      CFNumberRef sid_ref = CFDictionaryGetValue(space_ref, CFSTR(\"id64\"));\n      CFNumberGetValue(sid_ref, CFNumberGetType(sid_ref), &space_list[j]);\n    }\n  }\n  CFRelease(display_spaces_ref);\n  CFRelease(uuid);\n\n  return space_list;\n}\n\nint display_arrangement(uint32_t did) {\n  if (display_active_display_count() == 1) {\n    uint32_t result = 0;\n    uint32_t count = 0;\n    CGGetActiveDisplayList(1, &result, &count);\n    if (did == result && count == 1) return 1;\n    else return 0;\n  }\n\n  CFStringRef uuid = display_uuid(did);\n  if (!uuid) return 0;\n\n  CFArrayRef displays = SLSCopyManagedDisplays(g_connection);\n  if (!displays) {\n    CFRelease(uuid);\n    return 0;\n  }\n\n  int result = 0;\n  int displays_count = CFArrayGetCount(displays);\n\n  for (int i = 0; i < displays_count; ++i) {\n    if (CFEqual(CFArrayGetValueAtIndex(displays, i), uuid)) {\n      result = i + 1;\n      break;\n    }\n  }\n  CFRelease(displays);\n  CFRelease(uuid);\n  return result;\n}\n\nuint32_t display_main_display_id(void) {\n  return CGMainDisplayID();\n}\n\nstatic CFStringRef display_active_display_uuid(void) {\n  if (g_space_management_mode != 1) {\n    CGEventRef event = CGEventCreate(NULL);\n    if (!event) return NULL;\n    CGPoint cursor = CGEventGetLocation(event);\n    uint32_t count = 0;\n    uint32_t* dids = display_active_display_list(&count);\n    uint32_t mouse_did = 0;\n    for (uint32_t i = 0; i < count; i++) {\n      if (CGRectContainsPoint(CGDisplayBounds(dids[i]), cursor)) {\n        mouse_did = dids[i];\n        break;\n      }\n    }\n    if (dids) free(dids);\n    CFRelease(event);\n\n    return display_uuid(mouse_did);\n  } else return SLSCopyActiveMenuBarDisplayIdentifier(g_connection);\n}\n\nuint32_t display_active_display_id(void) {\n  if (display_active_display_count() == 1) {\n    uint32_t did = 0;\n    uint32_t count = 0;\n    CGGetActiveDisplayList(1, &did, &count);\n    if (count == 1) return did;\n    else {\n      printf(\"ERROR (id): No active display detected!\\n\");\n      return 0;\n    }\n  }\n\n  CFStringRef uuid = display_active_display_uuid();\n  if (!uuid) return 0;\n  CFUUIDRef uuid_ref = CFUUIDCreateFromString(NULL, uuid);\n  uint32_t result = CGDisplayGetDisplayIDFromUUID(uuid_ref);\n  CFRelease(uuid_ref);\n  CFRelease(uuid);\n  return result;\n}\n\nCFStringRef display_arrangement_display_uuid(int arrangement) {\n  CFStringRef result = NULL;\n  CFArrayRef displays = SLSCopyManagedDisplays(g_connection);\n\n  int displays_count = CFArrayGetCount(displays);\n  for (int i = 0; i < displays_count; ++i) {\n    if ((i+1) != arrangement) continue;\n    result = CFRetain(CFArrayGetValueAtIndex(displays, i));\n    break;\n  }\n\n  CFRelease(displays);\n  return result;\n}\n\nuint32_t display_arrangement_display_id(int arrangement) {\n  uint32_t result = 0;\n  CFArrayRef displays = SLSCopyManagedDisplays(g_connection);\n\n  int displays_count = CFArrayGetCount(displays);\n  for (int i = 0; i < displays_count; ++i) {\n    if ((i+1) != arrangement) continue;\n    CFUUIDRef uuid_ref = CFUUIDCreateFromString(NULL,\n                                                CFArrayGetValueAtIndex(displays, i));\n    result = CGDisplayGetDisplayIDFromUUID(uuid_ref);\n    CFRelease(uuid_ref);\n    break;\n  }\n\n  CFRelease(displays);\n  return result;\n}\n\nuint32_t display_active_display_adid(void) {\n  if (display_active_display_count() == 1) return 1;\n\n  CFStringRef uuid = display_active_display_uuid();\n  if (!uuid) return 0;\n  CFArrayRef displays = SLSCopyManagedDisplays(g_connection);\n  if (!displays) {\n    CFRelease(uuid);\n    return 0;\n  }\n\n  int result = 0;\n  int displays_count = CFArrayGetCount(displays);\n\n  for (int i = 0; i < displays_count; ++i) {\n    if (CFEqual(CFArrayGetValueAtIndex(displays, i), uuid)) {\n      result = i + 1;\n      break;\n    }\n  }\n  CFRelease(displays);\n  CFRelease(uuid);\n  return result;\n}\n\nbool display_menu_bar_visible(void) {\n  int status = 0;\n  SLSGetMenuBarAutohideEnabled(g_connection, &status);\n  return !status;\n}\n\nCGRect display_menu_bar_rect(uint32_t did) {\n  CGRect bounds = {};\n\n  #ifdef __x86_64__\n  SLSGetRevealedMenuBarBounds(&bounds, g_connection, display_space_id(did));\n  #elif __arm64__\n  int notch_height = workspace_display_notch_height(did);\n  if (notch_height) {\n    bounds.size.height = notch_height + 6;\n  } else {\n    bounds.size.height = 24;\n  }\n\n  bounds.size.width = CGDisplayPixelsWide(did);\n  #endif\n\n  return bounds;\n}\n\nuint32_t display_active_display_count(void) {\n  uint32_t count;\n  CGGetActiveDisplayList(0, NULL, &count);\n  return count;\n}\n\nuint32_t *display_active_display_list(uint32_t *count) {\n  int display_count = display_active_display_count();\n  uint32_t *result = malloc(sizeof(uint32_t) * display_count);\n  CGGetActiveDisplayList(display_count, result, count);\n  return result;\n}\n\nbool display_begin() {\n  return CGDisplayRegisterReconfigurationCallback(display_handler, NULL)\n          == kCGErrorSuccess;\n}\n\nbool display_end() {\n  return CGDisplayRemoveReconfigurationCallback(display_handler, NULL)\n          == kCGErrorSuccess;\n}\n\nvoid forced_brightness_event() {\n  g_last_brightness = -1.f;\n  brightness_handler(NULL, display_active_display_id(), NULL, NULL, NULL);\n}\n\nvoid begin_receiving_brightness_events() {\n  if (g_brightness_events) return;\n  g_brightness_events = true;\n  uint32_t count;\n  uint32_t* result = display_active_display_list(&count);\n  for (int i = 0; i < count; i++) {\n    uint32_t did = *(result + i);\n    if (DisplayServicesCanChangeBrightness(did)) {\n      DisplayServicesRegisterForBrightnessChangeNotifications(did, did, (void*)brightness_handler);\n    }\n  }\n}\n\nvoid display_serialize(FILE* rsp) {\n  uint32_t count = 0;\n  uint32_t* display_ids = display_active_display_list(&count);\n  if (!display_ids) return;\n\n  fprintf(rsp, \"[\\n\");\n  for (int i = 0; i < count; i++) {\n    fprintf(rsp, \"\\t{\\n\");\n\n    uint32_t did = display_arrangement_display_id(i + 1);\n    CFStringRef uuid_ref = display_uuid(did);\n    CGRect frame = CGDisplayBounds(did);\n    char* uuid = NULL;\n    if (uuid_ref) {\n      uuid = cfstring_copy(uuid_ref);\n      CFRelease(uuid_ref);\n    }\n\n    fprintf(rsp, \"\\t\\t\\\"arrangement-id\\\":%d,\\n\", display_arrangement(did));\n    fprintf(rsp, \"\\t\\t\\\"DirectDisplayID\\\":%d,\\n\", did);\n    fprintf(rsp, \"\\t\\t\\\"UUID\\\":\\\"%s\\\",\\n\", uuid ? uuid : \"<unknown>\");\n    fprintf(rsp, \"\\t\\t\\\"frame\\\":{\\n\\t\\t\\\"x\\\":%.4f,\\n\\t\\t\\\"y\\\":%.4f,\\n\\t\\t\\\"w\\\":%.4f,\\n\\t\\t\\\"h\\\":%.4f\\n\\t\\t}\\n\", frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);\n\n    if (i == count - 1)\n      fprintf(rsp, \"\\t}\\n\");\n    else\n      fprintf(rsp, \"\\t},\\n\");\n\n    if (uuid) free(uuid);\n  }\n  fprintf(rsp, \"]\\n\");\n  free(display_ids);\n}\n"
  },
  {
    "path": "src/display.h",
    "content": "#pragma once\n#include \"event.h\"\n#include \"misc/helpers.h\"\n\n#define DISPLAY_EVENT_HANDLER(name) void name(uint32_t did, CGDisplayChangeSummaryFlags flags, void *context)\ntypedef DISPLAY_EVENT_HANDLER(display_callback);\n\nuint32_t display_main_display_id(void);\nuint32_t display_active_display_id(void);\nuint32_t display_active_display_adid(void);\nuint32_t display_arrangement_display_id(int arrangement);\nbool display_menu_bar_visible(void);\nCGRect display_menu_bar_rect(uint32_t did);\nuint32_t display_active_display_count(void);\nuint32_t* display_active_display_list(uint32_t* count);\nbool display_begin(void);\nbool display_end(void);\n\nCFStringRef display_uuid(uint32_t did);\nCGRect display_bounds(uint32_t did);\nuint64_t display_space_id(uint32_t did);\nuint64_t* display_space_list(uint32_t did, int* count);\nint display_arrangement(uint32_t did);\n\nvoid forced_brightness_event();\nvoid begin_receiving_brightness_events();\n\nvoid display_serialize(FILE* rsp);\n"
  },
  {
    "path": "src/event.c",
    "content": "#include \"event.h\"\n#include \"bar_manager.h\"\n#include \"custom_events.h\"\n#include \"hotload.h\"\n\nextern struct bar_manager g_bar_manager;\nextern int g_connection;\nextern int g_space_management_mode;\n\nstatic void event_distributed_notification(void* context) {\n  bar_manager_handle_notification(&g_bar_manager, context);\n}\n\nstatic void event_application_front_switched(void* context) {\n  bar_manager_handle_front_app_switch(&g_bar_manager, context);\n}\n\nstatic void event_space_changed(void* context) {\n  bar_manager_handle_space_change(&g_bar_manager, false);\n}\n\nstatic void event_display_changed(void* context) {\n  bar_manager_handle_display_change(&g_bar_manager);\n}\n\nstatic void event_display_added(void* context) {\n  uint32_t did = (uint32_t)(intptr_t)context;\n  bar_manager_display_added(&g_bar_manager, did);\n}\n\nstatic void event_display_removed(void* context) {\n  uint32_t did = (uint32_t)(intptr_t)context;\n  bar_manager_display_removed(&g_bar_manager, did);\n}\n\nstatic void event_display_moved(void* context) {\n  uint32_t did = (uint32_t)(intptr_t)context;\n  bar_manager_display_moved(&g_bar_manager, did);\n}\n\nstatic void event_display_resized(void* context) {\n  uint32_t did = (uint32_t)(intptr_t)context;\n  bar_manager_display_resized(&g_bar_manager, did);\n}\n\nstatic void event_menu_bar_hidden_changed(void* context) {\n  bar_manager_resize(&g_bar_manager);\n  g_bar_manager.bar_needs_update = true;\n  bar_manager_refresh(&g_bar_manager, false, false);\n}\n\nstatic void event_system_woke(void* context) {\n  bar_manager_handle_system_woke(&g_bar_manager);\n}\n\nstatic void event_system_will_sleep(void* context) {\n  bar_manager_handle_system_will_sleep(&g_bar_manager);\n}\n\nstatic void event_shell_refresh(void* context) {\n  bar_manager_update(&g_bar_manager, false);\n}\n\nstatic void event_animator_refresh(void* context) {\n  bar_manager_animator_refresh(&g_bar_manager, (uint64_t)context);\n}\n\nstatic void event_mach_message(void* context) {\n  handle_message_mach(context);\n}\n\nstatic void event_mouse_up(void* context) {\n  CGPoint point = CGEventGetLocation(context);\n  uint32_t wid = get_wid_from_cg_event(context);\n  CGEventType type = CGEventGetType(context);\n  uint32_t mouse_button_code = CGEventGetIntegerValueField(context, kCGMouseEventButtonNumber);\n  uint32_t modifier_keys = CGEventGetFlags(context);\n\n  struct window* window = NULL;\n  struct bar_item* bar_item = bar_manager_get_item_by_wid(&g_bar_manager,\n                                                          wid,\n                                                          &window        );\n\n  if (!bar_item || bar_item->type == BAR_COMPONENT_GROUP) {\n    bar_item = bar_manager_get_item_by_point(&g_bar_manager, point, &window);\n  }\n\n  struct bar* bar = bar_manager_get_bar_by_wid(&g_bar_manager, wid);\n  struct popup* popup = bar_manager_get_popup_by_wid(&g_bar_manager, wid);\n  if (!bar_item && !popup && !bar) return;\n\n  CGPoint point_in_window_coords = CGPointZero;\n  if (bar_item && window) {\n    point_in_window_coords.x = point.x - window->origin.x;\n    point_in_window_coords.y = point.y - window->origin.y;\n  }\n\n  bar_item_on_click(bar_item,\n                    type,\n                    mouse_button_code,\n                    modifier_keys,\n                    point_in_window_coords);\n\n  if (bar_item && bar_item->needs_update)\n    bar_manager_refresh(&g_bar_manager, false, false);\n}\n\nstatic void event_mouse_dragged(void* context) {\n  CGPoint point = CGEventGetLocation(context);\n  uint32_t wid = get_wid_from_cg_event(context);\n\n  struct window* window = NULL;\n  struct bar_item* bar_item = bar_manager_get_item_by_wid(&g_bar_manager,\n                                                          wid,\n                                                          &window        );\n\n  if (!bar_item || !bar_item->has_slider) return;\n\n  CGPoint point_in_window_coords = CGPointZero;\n  if (bar_item && window) {\n    point_in_window_coords.x = point.x - window->origin.x;\n    point_in_window_coords.y = point.y - window->origin.y;\n  }\n\n  bar_item_on_drag(bar_item, point_in_window_coords);\n\n  if (bar_item->needs_update)\n    bar_manager_refresh(&g_bar_manager, false, false);\n}\n\nstatic void event_mouse_entered(void* context) {\n  uint32_t wid = get_wid_from_cg_event(context);\n\n  struct bar* bar = bar_manager_get_bar_by_wid(&g_bar_manager, wid);\n  if (bar) {\n    // Handle global mouse entered event\n    if (!bar->mouse_over\n        && !bar_manager_mouse_over_any_popup(&g_bar_manager)) {\n      bar->mouse_over = true;\n      bar_manager_handle_mouse_entered_global(&g_bar_manager);\n    }\n    return;\n  }\n\n  struct popup* popup = bar_manager_get_popup_by_wid(&g_bar_manager, wid);\n  if (popup) {\n    // Handle global mouse entered event\n    if (!popup->mouse_over\n        && !bar_manager_mouse_over_any_bar(&g_bar_manager)) {\n      popup->mouse_over = true;\n      bar_manager_handle_mouse_entered_global(&g_bar_manager);\n    }\n    return;\n  }\n\n  struct bar_item* bar_item = bar_manager_get_item_by_wid(&g_bar_manager,\n                                                          wid,\n                                                          NULL          );\n\n  bar_manager_handle_mouse_entered(&g_bar_manager, bar_item);\n}\n\nstatic void event_mouse_exited(void* context) {\n  uint32_t wid = get_wid_from_cg_event(context);\n\n  struct bar* bar = NULL,* bar_target = NULL;\n  struct popup* popup,* popup_target = NULL;\n  struct window* origin_window = NULL;\n  bool over_target = false;\n\n  CGPoint point = CGEventGetLocation(context);\n  if ((bar = bar_manager_get_bar_by_wid(&g_bar_manager, wid))) {\n    origin_window = &bar->window;\n    popup_target = bar_manager_get_popup_by_point(&g_bar_manager,\n                                                  point          );\n    over_target = (popup_target != NULL);\n  }\n  else if ((popup = bar_manager_get_popup_by_wid(&g_bar_manager, wid))) {\n    origin_window = &popup->window;\n    bar_target = bar_manager_get_bar_by_point(&g_bar_manager, point);\n    over_target = (bar_target != NULL);\n  }\n\n  if (bar || popup) {\n    // Handle global mouse exited event\n    CGRect frame = origin_window->frame;\n    frame.origin = origin_window->origin;\n    frame = CGRectInset(frame, 1, 1);\n\n    bool over_origin = CGRectContainsPoint(frame, point);\n\n    if (!over_origin && !over_target) {\n      if (bar) bar->mouse_over = false;\n      else if (popup) popup->mouse_over = false;\n      bar_manager_handle_mouse_exited_global(&g_bar_manager);\n    } else if (!over_origin && over_target) {\n      if (bar) {\n        bar->mouse_over = false;\n        if (popup_target) popup_target->mouse_over = true;\n      }\n      else {\n        if (bar_target) bar_target->mouse_over = true;\n        if (popup) {\n          popup->mouse_over = false;\n          bool has_complex_mask\n                  = popup->host->update_mask & (UPDATE_MOUSE_EXITED\n                                                | UPDATE_EXITED_GLOBAL);\n          if (has_complex_mask\n              && bar_manager_get_item_by_point(&g_bar_manager, point, NULL)\n                  != popup->host) {\n            bar_manager_handle_mouse_exited(&g_bar_manager, popup->host);\n          }\n        }\n      }\n    }\n    return;\n  }\n\n  struct window* window = NULL;\n  struct bar_item* bar_item = bar_manager_get_item_by_wid(&g_bar_manager,\n                                                          wid,\n                                                          &window        );\n\n  if (bar_item\n      && bar_item->update_mask & UPDATE_EXITED_GLOBAL\n      && bar_manager_get_popup_by_point(&g_bar_manager, point)\n         == &bar_item->popup) {\n    return;\n  }\n  if (bar_item) bar_manager_handle_mouse_exited(&g_bar_manager, bar_item);\n}\n\n#define SCROLL_TIMEOUT 150000000\nstruct {\n  uint64_t timestamp;\n  int delta_y;\n} g_scroll_info;\n\nstatic void event_mouse_scrolled(void* context) {\n  CGPoint point = CGEventGetLocation(context);\n  uint32_t wid = get_wid_from_cg_event(context);\n  int scroll_delta\n    = CGEventGetIntegerValueField(context,\n        kCGScrollWheelEventDeltaAxis1);\n  uint32_t modifier_keys = CGEventGetFlags(context);\n\n  uint64_t event_time = clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW_APPROX);\n  if (g_scroll_info.timestamp + SCROLL_TIMEOUT > event_time) {\n    g_scroll_info.delta_y += scroll_delta;\n    return;\n  } else {\n    if (g_scroll_info.timestamp + 2*SCROLL_TIMEOUT < event_time)\n      g_scroll_info.delta_y = 0;\n    g_scroll_info.timestamp\n      = clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW_APPROX);\n  }\n\n  struct bar_item* bar_item = bar_manager_get_item_by_wid(&g_bar_manager,\n                                                          wid,\n                                                          NULL           );\n\n  if (!bar_item || bar_item->type == BAR_COMPONENT_GROUP) {\n    bar_item = bar_manager_get_item_by_point(&g_bar_manager, point, NULL);\n  }\n\n  if (!bar_item) {\n    struct bar* bar = bar_manager_get_bar_by_wid(&g_bar_manager, wid);\n    if (bar) {\n      // Handle global mouse scrolled event\n      if (bar->mouse_over\n          && !bar_manager_mouse_over_any_popup(&g_bar_manager)) {\n        bar_manager_handle_mouse_scrolled_global(&g_bar_manager,\n                                                 scroll_delta\n                                                 + g_scroll_info.delta_y,\n                                                 bar->adid,\n                                                 modifier_keys           );\n      }\n\n      g_scroll_info.delta_y = 0;\n      return;\n    }\n\n    struct popup* popup = bar_manager_get_popup_by_wid(&g_bar_manager, wid);\n    if (popup) {\n      // Handle global mouse scrolled event\n      if (popup->mouse_over\n          && !bar_manager_mouse_over_any_bar(&g_bar_manager)) {\n        bar_manager_handle_mouse_scrolled_global(&g_bar_manager,\n                                                 scroll_delta\n                                                 + g_scroll_info.delta_y,\n                                                 popup->adid,\n                                                 modifier_keys           );\n      }\n\n      g_scroll_info.delta_y = 0;\n      return;\n    }\n  }\n\n  bar_item_on_scroll(bar_item,\n                     scroll_delta + g_scroll_info.delta_y,\n                     modifier_keys                        );\n\n  if (bar_item && bar_item->needs_update)\n    bar_manager_refresh(&g_bar_manager, false, false);\n\n  g_scroll_info.delta_y = 0;\n}\n\n\nstatic void event_volume_changed(void* context) {\n  bar_manager_handle_volume_change(&g_bar_manager, *(float*)context);\n}\n\nstatic void event_wifi_changed(void* context) {\n  bar_manager_handle_wifi_change(&g_bar_manager, (char*)context);\n}\n\nstatic void event_brightness_changed(void* context) {\n  bar_manager_handle_brightness_change(&g_bar_manager, *(float*)context);\n}\n\nstatic void event_power_source_changed(void* context) {\n  bar_manager_handle_power_source_change(&g_bar_manager, (char*)context);\n}\n\nstatic void event_media_changed(void* context) {\n  bar_manager_handle_media_change(&g_bar_manager, (char*)context);\n}\n\nstatic void event_cover_changed(void* context) {\n  bar_manager_handle_media_cover_change(&g_bar_manager, (CGImageRef)context);\n}\n\nstatic void event_space_windows_changed(void* context) {\n  bar_manager_handle_space_windows_change(&g_bar_manager, (char*)context);\n}\n\nstatic void event_hotload(void* context) {\n  bar_manager_destroy(&g_bar_manager);\n  bar_manager_init(&g_bar_manager);\n  bar_manager_begin(&g_bar_manager);\n  exec_config_file();\n}\n\ntypedef void callback_type(void*);\nstatic callback_type* event_handler[] = {\n  [APPLICATION_FRONT_SWITCHED] = event_application_front_switched,\n  [SPACE_CHANGED]              = event_space_changed,\n  [DISPLAY_ADDED]              = event_display_added,\n  [DISPLAY_REMOVED]            = event_display_removed,\n  [DISPLAY_MOVED]              = event_display_moved,\n  [DISPLAY_RESIZED]            = event_display_resized,\n  [DISPLAY_CHANGED]            = event_display_changed,\n  [MOUSE_UP]                   = event_mouse_up,\n  [MOUSE_DRAGGED]              = event_mouse_dragged,\n  [MOUSE_ENTERED]              = event_mouse_entered,\n  [MOUSE_EXITED]               = event_mouse_exited,\n  [MOUSE_SCROLLED]             = event_mouse_scrolled,\n  [VOLUME_CHANGED]             = event_volume_changed,\n  [WIFI_CHANGED]               = event_wifi_changed,\n  [BRIGHTNESS_CHANGED]         = event_brightness_changed,\n  [POWER_SOURCE_CHANGED]       = event_power_source_changed,\n  [MEDIA_CHANGED]              = event_media_changed,\n  [COVER_CHANGED]              = event_cover_changed,\n  [DISTRIBUTED_NOTIFICATION]   = event_distributed_notification,\n  [MENU_BAR_HIDDEN_CHANGED]    = event_menu_bar_hidden_changed,\n  [SYSTEM_WOKE]                = event_system_woke,\n  [SYSTEM_WILL_SLEEP]          = event_system_will_sleep,\n  [SHELL_REFRESH]              = event_shell_refresh,\n  [ANIMATOR_REFRESH]           = event_animator_refresh,\n  [MACH_MESSAGE]               = event_mach_message,\n  [HOTLOAD]                    = event_hotload,\n  [SPACE_WINDOWS_CHANGED]      = event_space_windows_changed,\n};\n\nvoid event_post(struct event *event) {\n  if (event->type == EVENT_TYPE_UNKNOWN) return;\n\n  static bool initialized = false;\n  static pthread_mutex_t event_mutex;\n\n  if (!initialized && event->type == INIT_MUTEX) {\n    pthread_mutexattr_t mattr;\n    pthread_mutexattr_init(&mattr);\n    pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE);\n    pthread_mutex_init(&event_mutex, &mattr);\n    initialized = true;\n    return;\n  } else if (event->type == INIT_MUTEX) {\n    error(\"Trying to reinitialize the event mutex! abort..\\n\");\n  } else if (!initialized) error(\"The event mutex is not ready! abort..\\n\");\n\n  if (event->type == ANIMATOR_REFRESH) {\n    // We try to lock the mutex up to 1ms and then concede (skip the frame) to\n    // avoid deadlocking occuring due to the CVDisplayLink.\n    int locked;\n    for (int i = 0; i < 10; i++) {\n      if ((locked = pthread_mutex_trylock(&event_mutex)) == 0) break;\n      usleep(100);\n    }\n    if (locked != 0) return;\n  } else {\n    pthread_mutex_lock(&event_mutex);\n    if (g_space_management_mode != 1) {\n      bar_manager_poll_active_display(&g_bar_manager);\n    }\n  }\n\n  event_handler[event->type](event->context);\n  windows_unfreeze();\n  pthread_mutex_unlock(&event_mutex);\n}\n"
  },
  {
    "path": "src/event.h",
    "content": "#pragma once\n#include \"bar_manager.h\"\n#include \"message.h\"\n\nenum event_type {\n  EVENT_TYPE_UNKNOWN,\n  APPLICATION_FRONT_SWITCHED,\n  SPACE_CHANGED,\n  DISPLAY_ADDED,\n  DISPLAY_REMOVED,\n  DISPLAY_MOVED,\n  DISPLAY_RESIZED,\n  DISPLAY_CHANGED,\n  MENU_BAR_HIDDEN_CHANGED,\n  SYSTEM_WOKE,\n  SYSTEM_WILL_SLEEP,\n  SHELL_REFRESH,\n  ANIMATOR_REFRESH,\n  MACH_MESSAGE,\n  MOUSE_UP,\n  MOUSE_DRAGGED,\n  MOUSE_ENTERED,\n  MOUSE_EXITED,\n  MOUSE_SCROLLED,\n  VOLUME_CHANGED,\n  WIFI_CHANGED,\n  BRIGHTNESS_CHANGED,\n  POWER_SOURCE_CHANGED,\n  MEDIA_CHANGED,\n  COVER_CHANGED,\n  SPACE_WINDOWS_CHANGED,\n  DISTRIBUTED_NOTIFICATION,\n  HOTLOAD,\n\n  INIT_MUTEX,\n  EVENT_TYPE_COUNT\n};\n\nstruct event {\n  void* context;\n  enum event_type type;\n};\n\nvoid event_post(struct event *event);\n"
  },
  {
    "path": "src/font.c",
    "content": "#include \"font.h\"\n#include \"animation.h\"\n#include \"bar_manager.h\"\n\nstruct feature_mapping {\n  char opentype_tag[5];\n  int truetype_feature;\n  int truetype_selector;\n};\n\n// Non-exhaustive list of OpenType tags and corresponding TrueType features and selectors\nstruct feature_mapping feature_mappings[] = {\n  {\"liga\", kLigaturesType, kCommonLigaturesOnSelector},\n  {\"dlig\", kLigaturesType, kRareLigaturesOnSelector},\n\n  {\"tnum\", kNumberSpacingType, kMonospacedNumbersSelector}, \n  {\"pnum\", kNumberSpacingType, kProportionalNumbersSelector}, \n\n  {\"smcp\", kLowerCaseType, kLowerCaseSmallCapsSelector},\n  {\"c2sc\", kUpperCaseType, kUpperCaseSmallCapsSelector},\n\n  {\"onum\", kNumberCaseType, kLowerCaseNumbersSelector},\n  {\"lnum\", kNumberCaseType, kUpperCaseNumbersSelector},\n\n  {\"afrc\", kFractionsType, kVerticalFractionsSelector},\n  {\"frac\", kFractionsType, kDiagonalFractionsSelector},\n\n  {\"subs\", kVerticalPositionType, kInferiorsSelector},\n  {\"sups\", kVerticalPositionType, kSuperiorsSelector},\n\n  {\"zero\", kTypographicExtrasType, kSlashedZeroOnSelector},\n\n  {\"swsh\", kContextualAlternatesType, kSwashAlternatesOnSelector},\n  {\"cswh\", kContextualAlternatesType, kContextualSwashAlternatesOnSelector},\n\n  {\"calt\", kContextualAlternatesType, kContextualAlternatesOnSelector},\n\n  // kStylisticAlternativesType = 35\n  {\"salt\", 35,  2},\n  {\"ss01\", 35,  2}, {\"ss02\", 35, 4},  {\"ss03\", 35,  6}, {\"ss04\", 35,  8},\n  {\"ss05\", 35, 10}, {\"ss06\", 35, 12}, {\"ss07\", 35, 14}, {\"ss08\", 35, 16},\n  {\"ss09\", 35, 18}, {\"ss10\", 35, 20}, {\"ss11\", 35, 22}, {\"ss12\", 35, 24},\n  {\"ss13\", 35, 26}, {\"ss14\", 35, 28}, {\"ss15\", 35, 30}, {\"ss16\", 35, 32},\n  {\"ss17\", 35, 34}, {\"ss18\", 35, 36}, {\"ss19\", 35, 38}, {\"ss20\", 35, 40},\n\n  {\"\", 0, 0}\n};\n\nvoid get_truetype_feature(const char* opentype_tag, int* truetype_feature, int* truetype_selector) {\n  for (int i = 0; feature_mappings[i].opentype_tag[0] != '\\0'; ++i) {\n    if (strcmp(feature_mappings[i].opentype_tag, opentype_tag) == 0) {\n      *truetype_feature = feature_mappings[i].truetype_feature;\n      *truetype_selector = feature_mappings[i].truetype_selector;\n      return;\n    }\n  }\n}\n\nvoid font_register(char* font_path) {\n  CFStringRef url_string = CFStringCreateWithCString(kCFAllocatorDefault,\n                                                     font_path,\n                                                     kCFStringEncodingUTF8);\n  if (url_string) {\n    CFURLRef url_ref = CFURLCreateWithString(kCFAllocatorDefault,\n                                             url_string,\n                                             NULL                );\n    if (url_ref) {\n      CTFontManagerRegisterFontsForURL(url_ref,\n                                       kCTFontManagerScopeProcess,\n                                       NULL                       );\n      CFRelease(url_ref);\n    }\n    CFRelease(url_string);\n  }\n  free(font_path);\n}\n\nvoid font_create_ctfont(struct font* font) {\n\n  CFStringRef family_ref = CFStringCreateWithCString(NULL,\n                                                     font->family,\n                                                     kCFStringEncodingUTF8);\n\n  CFStringRef style_ref = CFStringCreateWithCString(NULL,\n                                                    font->style,\n                                                    kCFStringEncodingUTF8);\n\n  CFNumberRef size_ref = CFNumberCreate(NULL,\n                                        kCFNumberFloat32Type,\n                                        &font->size          );\n\n  const void *keys[] = { kCTFontFamilyNameAttribute,\n                         kCTFontStyleNameAttribute,\n                         kCTFontSizeAttribute       };\n\n  const void *values[] = { family_ref, style_ref, size_ref };\n  CFDictionaryRef attr = CFDictionaryCreate(NULL,\n                                            keys,\n                                            values,\n                                            array_count(keys),\n                                            &kCFTypeDictionaryKeyCallBacks,\n                                            &kCFTypeDictionaryValueCallBacks);\n\n  CTFontDescriptorRef descriptor = CTFontDescriptorCreateWithAttributes(attr);\n\n  if (font->ct_font) CFRelease(font->ct_font);\n\n  if (font->features) {\n    char* features_copy = string_copy(font->features);\n    char* feature = strtok(features_copy, \",\");\n\n    while (feature) {\n      int feature_name = 0;\n      int feature_selector = 0;\n\n      bool valid_feature = false;\n\n      if (sscanf(feature, \"%d:%d\", &feature_name, &feature_selector) == 2) {\n        valid_feature = true;\n      } else if (strlen(feature) == 4) {\n        get_truetype_feature(feature, &feature_name, &feature_selector);\n\n        if (feature_name != 0) {\n          valid_feature = true;\n        }\n      }\n\n      if (valid_feature) {\n        CFNumberRef name = CFNumberCreate(NULL, kCFNumberIntType, &feature_name);\n        CFNumberRef value = CFNumberCreate(NULL, kCFNumberIntType, &feature_selector);\n\n        CTFontDescriptorRef new_descriptor = CTFontDescriptorCreateCopyWithFeature(descriptor, name, value);\n        CFRelease(descriptor);\n        descriptor = new_descriptor;\n\n        CFRelease(name);\n        CFRelease(value);\n      }\n\n      feature = strtok(NULL, \",\");\n    }\n\n    free(features_copy);\n  }\n\n  font->ct_font = CTFontCreateWithFontDescriptor(descriptor, 0.0, NULL);\n\n  CFRelease(descriptor);\n  CFRelease(attr);\n  CFRelease(size_ref);\n  CFRelease(style_ref);\n  CFRelease(family_ref);\n}\n\nvoid font_init(struct font* font) {\n  font->size = 14.f;\n  font->style = string_copy(\"Bold\");\n  font->family = string_copy(\"Hack Nerd Font\");\n  font_create_ctfont(font);\n}\n\nbool font_set_style(struct font* font, char* style, bool forced) {\n  if (!style) return false;\n  if (!forced && font->style && string_equals(font->style, style)) {\n    free(style);\n    return false;\n  }\n  if (font->style && style != font->style) free(font->style);\n  font->style = style;\n  font->font_changed = true;\n\n  return true;\n}\n\nbool font_set_family(struct font* font, char* family, bool forced) {\n  if (!family) return false;\n  if (!forced && font->family && string_equals(font->family, family)) {\n    free(family);\n    return false;\n  }\n  if (font->family) free(font->family);\n  font->family = family;\n  font->font_changed = true;\n\n  return true;\n}\n\nbool font_set_size(struct font* font, float size) {\n  if (font->size == size) return false;\n\n  font->size = size;\n  font->font_changed = true;\n\n  return true;\n}\n\nbool font_set_features(struct font* font, char* features) {\n  if (!features) return false;\n  if (font->features && string_equals(font->features, features)) {\n    free(features);\n    return false;\n  }\n  if (font->features) free(font->features);\n  font->features = features;\n  font->font_changed = true;\n\n  return true;\n}\n\nbool font_set(struct font* font, char* font_string, bool forced) {\n  if (!font_string) return false;\n\n  float size = 10.0f;\n  char font_properties[2][255] = { {}, {} };\n  sscanf(font_string,\n         \"%254[^:]:%254[^:]:%f\",\n         font_properties[0],\n         font_properties[1],\n         &size                  );\n\n  free(font_string);\n\n  bool change = font_set_family(font, string_copy(font_properties[0]), forced);\n  change |= font_set_style(font, string_copy(font_properties[1]), forced);\n  change |= font_set_size(font, size);\n\n  return change;\n}\n\nvoid font_clear_pointers(struct font* font) {\n  font->ct_font = NULL;\n  font->family = NULL;\n  font->style = NULL;\n  font->features = NULL;\n}\n\nvoid font_destroy(struct font* font) {\n  if (font->style) free(font->style);\n  if (font->family) free(font->family);\n  if (font->features) free(font->features);\n  if (font->ct_font) CFRelease(font->ct_font);\n  font_clear_pointers(font);\n}\n\nbool font_parse_sub_domain(struct font* font, FILE* rsp, struct token property, char* message) {\n  bool needs_refresh = false;\n  if (token_equals(property, PROPERTY_FONT_SIZE)) {\n    struct token token = get_token(&message);\n    ANIMATE_FLOAT(font_set_size,\n                  font,\n                  font->size,\n                  token_to_float(token));\n  } else if (token_equals(property, PROPERTY_FONT_FAMILY)) {\n    struct token token = get_token(&message);\n    needs_refresh = font_set_family(font, token_to_string(token), false);\n  } else if (token_equals(property, PROPERTY_FONT_STYLE)) {\n    struct token token = get_token(&message);\n    needs_refresh = font_set_style(font, token_to_string(token), false);\n  } else if (token_equals(property, PROPERTY_FONT_FEATURES)) {\n    struct token token = get_token(&message);\n    needs_refresh = font_set_features(font, token_to_string(token));\n  } else {\n    respond(rsp, \"[!] Text: Invalid property '%s'\\n\", property.text);\n  }\n\n  return needs_refresh;\n}\n"
  },
  {
    "path": "src/font.h",
    "content": "#pragma once\n#include <CoreText/CoreText.h>\n#include \"misc/helpers.h\"\n\nstruct font {\n  CTFontRef ct_font;\n\n  bool font_changed;\n  float size;\n  char* family;\n  char* style;\n  char* features;\n};\n\nvoid font_register(char* font_path);\n\nvoid font_init(struct font* font);\nvoid font_destroy(struct font* font);\nbool font_set(struct font* font, char* font_string, bool forced);\nbool font_set_size(struct font* font, float size);\nbool font_set_family(struct font* font, char* family, bool forced);\nbool font_set_style(struct font* font, char* style, bool forced);\nvoid font_create_ctfont(struct font* font);\nvoid font_clear_pointers(struct font* font);\n\nbool font_parse_sub_domain(struct font* font, FILE* rsp, struct token property, char* message);\n"
  },
  {
    "path": "src/graph.c",
    "content": "#include \"graph.h\"\n\nvoid graph_init(struct graph* graph) {\n  graph->width = 0;\n  graph->cursor = 0;\n\n  graph->line_width = 0.5;\n  graph->fill = true;\n  graph->overrides_fill_color = false;\n  graph->enabled = true;\n\n  color_init(&graph->line_color, 0xffcccccc);\n  color_init(&graph->fill_color, 0xffcccccc);\n}\n\nvoid graph_setup(struct graph* graph, uint32_t width) {\n  graph->width = width;\n  graph->y = malloc(sizeof(float) * width);\n  memset(graph->y, 0, sizeof(float) * width);\n}\n\nfloat graph_get_y(struct graph* graph, uint32_t i) {\n  if (!graph->enabled) return 0.f;\n  return graph->y[ (graph->cursor + i)%graph->width ];\n}\n\nvoid graph_push_back(struct graph* graph, float y) {\n  if (!graph->enabled) return;\n  graph->y[graph->cursor] = y;\n\n  ++graph->cursor;\n  graph->cursor %= graph->width;\n}\n\nuint32_t graph_get_length(struct graph* graph) {\n  if (graph->enabled) return graph->width;\n  return 0;\n}\n\nvoid graph_calculate_bounds(struct graph* graph, uint32_t x, uint32_t y, uint32_t height) {\n  graph->bounds.size.height = height;\n  graph->bounds.origin.x = x;\n  graph->bounds.origin.y = y - graph->bounds.size.height / 2\n                           + graph->line_width;\n}\n\nvoid graph_draw(struct graph* graph, CGContextRef context) {\n  uint32_t x =  graph->bounds.origin.x + (graph->rtl ? graph->width : 0);\n  uint32_t y = graph->bounds.origin.y;\n  uint32_t height = graph->bounds.size.height;\n\n  uint32_t sample_width = 1;\n  bool fill = graph->fill;\n  CGContextSaveGState(context);\n  CGContextSetRGBStrokeColor(context,\n                             graph->line_color.r,\n                             graph->line_color.g,\n                             graph->line_color.b,\n                             graph->line_color.a );\n\n  if (graph->overrides_fill_color)\n    CGContextSetRGBFillColor(context,\n                             graph->fill_color.r,\n                             graph->fill_color.g,\n                             graph->fill_color.b,\n                             graph->fill_color.a );\n  else\n    CGContextSetRGBFillColor(context,\n                             graph->line_color.r,\n                             graph->line_color.g,\n                             graph->line_color.b,\n                             0.2 * graph->line_color.a);\n\n  CGContextSetLineWidth(context, graph->line_width);\n  CGMutablePathRef p = CGPathCreateMutable();\n  uint32_t start_x = x;\n  if (graph->rtl) {\n    CGPathMoveToPoint(p,\n                      NULL,\n                      x,\n                      y + graph_get_y(graph, graph->width - 1) * height);\n\n    for (int i = graph->width - 1; i > 0; --i, x -= sample_width) {\n      CGPathAddLineToPoint(p, NULL, x, y + graph_get_y(graph, i) * height);\n    }\n  }\n  else {\n    CGPathMoveToPoint(p, NULL, x, y + graph_get_y(graph, 0) * height);\n    for (int i = graph->width - 1; i > 0; --i, x += sample_width) {\n      CGPathAddLineToPoint(p, NULL, x, y + graph_get_y(graph, i) * height);\n    }\n  }\n  CGContextAddPath(context, p);\n  CGContextStrokePath(context);\n  if (fill) {\n    if (graph->rtl) {\n      CGPathAddLineToPoint(p, NULL, x + sample_width, y);\n    }\n    else {\n      CGPathAddLineToPoint(p, NULL, x - sample_width, y);\n    }\n    CGPathAddLineToPoint(p, NULL, start_x, y);\n    CGPathCloseSubpath(p);\n    CGContextAddPath(context, p);\n    CGContextFillPath(context);\n  }\n  CGPathRelease(p);\n  CGContextRestoreGState(context);\n}\n\nvoid graph_serialize(struct graph* graph, char* indent, FILE* rsp) {\n    fprintf(rsp, \"%s\\\"color\\\": \\\"0x%x\\\",\\n\"\n                 \"%s\\\"fill_color\\\": \\\"0x%x\\\",\\n\"\n                 \"%s\\\"line_width\\\": \\\"%f\\\",\\n\"\n                 \"%s\\\"data\\\": [\\n\",\n                 indent, graph->line_color.hex,\n                 indent, graph->fill_color.hex,\n                 indent, graph->line_width, indent);\n    int counter = 0;\n    for (int i = 0; i < graph->width; i++) {\n      if (counter++ > 0) fprintf(rsp, \",\\n\");\n      fprintf(rsp, \"%s\\t\\\"%f\\\"\", indent, graph->y[i]);\n    }\n    fprintf(rsp, \"\\n%s]\", indent);\n}\n\nvoid graph_destroy(struct graph* graph) {\n  if (!graph->enabled) return;\n  if (graph->y) free(graph->y);\n  graph->y = NULL;\n}\n\nbool graph_parse_sub_domain(struct graph* graph, FILE* rsp, struct token property, char* message) {\n  if (token_equals(property, PROPERTY_COLOR)) {\n    return color_set_hex(&graph->line_color,\n                         token_to_uint32t(get_token(&message)));\n  } else if (token_equals(property, PROPERTY_FILL_COLOR)) {\n    graph->overrides_fill_color = true;\n    return color_set_hex(&graph->fill_color,\n                         token_to_uint32t(get_token(&message)));\n  } else if (token_equals(property, PROPERTY_LINE_WIDTH)) {\n    graph->line_width = token_to_float(get_token(&message));\n    return true;\n  } \n  else {\n    struct key_value_pair key_value_pair = get_key_value_pair(property.text,\n                                                              '.'           );\n    if (key_value_pair.key && key_value_pair.value) {\n      struct token subdom = {key_value_pair.key,strlen(key_value_pair.key)};\n      struct token entry = {key_value_pair.value,strlen(key_value_pair.value)};\n      if (token_equals(subdom, SUB_DOMAIN_COLOR)) {\n        return color_parse_sub_domain(&graph->line_color, rsp, entry, message);\n      }\n      else if (token_equals(subdom, SUB_DOMAIN_FILL_COLOR)) {\n        return color_parse_sub_domain(&graph->fill_color, rsp, entry, message);\n      }\n      else {\n        respond(rsp, \"[!] Graph: Invalid subdomain '%s'\\n\", subdom.text);\n      }\n    }\n    else {\n      respond(rsp, \"[!] Graph: Invalid property '%s'\\n\", property.text);\n    }\n  }\n  return false;\n}\n"
  },
  {
    "path": "src/graph.h",
    "content": "#pragma once\n#include \"misc/helpers.h\"\n#include \"color.h\"\n\nstruct graph {\n  bool rtl;\n  bool fill;\n  bool enabled;\n  bool overrides_fill_color;\n\n  float* y;\n  uint32_t width;\n  uint32_t cursor;\n  float line_width;\n\n  CGRect bounds;\n  struct color line_color;\n  struct color fill_color;\n};\n\nvoid graph_init(struct graph* graph);\nvoid graph_setup(struct graph* graph, uint32_t width);\nvoid graph_push_back(struct graph* graph, float y);\nfloat graph_get_y(struct graph* graph, uint32_t i);\nuint32_t graph_get_length(struct graph* graph);\n\nvoid graph_calculate_bounds(struct graph* graph, uint32_t x, uint32_t y, uint32_t height);\nvoid graph_draw(struct graph* graph, CGContextRef context);\nvoid graph_destroy(struct graph* graph);\n\nvoid graph_serialize(struct graph* graph, char* indent, FILE* rsp);\nbool graph_parse_sub_domain(struct graph* graph, FILE* rsp, struct token property, char* message);\n"
  },
  {
    "path": "src/group.c",
    "content": "#include \"group.h\"\n#include \"bar.h\"\n\nstatic struct bar_item* group_get_first_member(struct group* group, struct bar* bar) {\n  if (group->num_members == 1) return NULL;\n\n  int min = INT32_MAX;\n  struct bar_item* first_item = NULL;\n\n  for (int i = 1; i < group->num_members; i++) {\n    struct bar_item* member = group->members[i];\n    if (bar_draws_item(bar, member)) {\n      struct window* window = bar_item_get_window(member, bar->adid);\n      if (window->origin.x < min) {\n        min = window->origin.x;\n        first_item = member;\n      }\n    }\n  }\n\n  return first_item;\n}\n\nstatic struct bar_item* group_get_last_member(struct group* group, struct bar* bar) {\n  if (group->num_members == 1) return NULL;\n\n  int max = INT32_MIN;\n  struct bar_item* last_item = NULL;\n\n  for (int i = 1; i < group->num_members; i++) {\n    struct bar_item* member = group->members[i];\n    if (bar_draws_item(bar, member)) {\n      struct window* window = bar_item_get_window(member, bar->adid);\n      if (window->origin.x + window->frame.size.width > max) {\n        max = window->origin.x + window->frame.size.width;\n        last_item = member;\n      }\n    }\n  }\n\n  return last_item;\n}\n\nstruct group* group_create() {\n  struct group* group = malloc(sizeof(struct group));\n  memset(group, 0, sizeof(struct group));\n  return group;\n}\n\nvoid group_init(struct group* group) {\n  group->num_members = 0;\n  group->members = NULL;\n}\n\nbool group_is_item_member(struct group* group, struct bar_item* item) {\n  for (uint32_t i = 0; i < group->num_members; i++) {\n    if (group->members[i] == item) return true;\n  }\n  return false;\n}\n\nvoid group_add_member(struct group* group, struct bar_item* item) {\n  if (group_is_item_member(group, item)) return;\n  if (item->group && item->group->members && item->group->members[0] == item) {\n    for (int i = 1; i < item->group->num_members; i++) {\n      group_add_member(group, item->group->members[i]);\n    }\n  } else {\n    group->num_members++;\n    group->members = realloc(group->members,\n                             sizeof(struct bar_item*)*group->num_members);\n    group->members[group->num_members - 1] = item;\n    item->group = group;\n  }\n}\n\nuint32_t group_get_length(struct group* group, struct bar* bar) {\n  int len = group->last_window->origin.x\n            + group->last_window->frame.size.width\n            + group->last_item->background.padding_right\n            + group->first_item->background.padding_left\n            - group->first_window->origin.x;\n\n  return max(len, 0);\n}\n\nvoid group_remove_member(struct group* group, struct bar_item* bar_item) {\n  if (group->num_members <= 0) return;\n  struct bar_item* tmp[group->num_members - 1];\n  int count = 0;\n  for (int i = 0; i < group->num_members; i++) {\n    if (group->members[i] == bar_item) continue;\n    tmp[count++] = group->members[i];\n  }\n  group->num_members--;\n  group->members = realloc(group->members,\n                           sizeof(struct bar_item*)*group->num_members);\n  memcpy(group->members, tmp, sizeof(struct bar_item*)*group->num_members);\n}\n\nvoid group_destroy(struct group* group) {\n  for (int i = 0; i < group->num_members; i++) {\n    group->members[i]->group = NULL;\n  }\n  if (group->members) free(group->members);\n  free(group);\n}\n\nvoid group_calculate_bounds(struct group* group, struct bar* bar, uint32_t y) {\n  group->first_item = group_get_first_member(group, bar);\n  group->first_window = bar_item_get_window(group->first_item, bar->adid);\n\n  group->last_item = group_get_last_member(group, bar);\n  group->last_window = bar_item_get_window(group->last_item, bar->adid);\n\n  if (!group->first_window || !group->last_window) {\n    group->bounds.origin = g_nirvana;\n    return;\n  }\n\n  uint32_t group_length = group_get_length(group, bar);\n  CGPoint shadow_offsets = bar_item_calculate_shadow_offsets(group->members[0]);\n  \n\n  group->bounds = (CGRect){{group->first_window->origin.x\n                            - group->first_item->background.padding_left,\n                            group->first_window->origin.y},\n                           {group_length\n                            + shadow_offsets.x\n                            + shadow_offsets.y,\n                           group->first_window->frame.size.height}};\n  \n  background_calculate_bounds(&group->members[0]->background,\n                              max(shadow_offsets.x, 0),\n                              y + group->members[0]->y_offset,\n                              group_get_length(group, bar),\n                              group->members[0]->background.bounds.size.height);\n}\n\nvoid group_serialize(struct group* group, char* indent, FILE* rsp) {\n    int counter = 0;\n    for (int i = 1; i < group->num_members; i++) {\n      if (!group->members[i]) continue;\n      if (counter++ > 0) fprintf(rsp, \",\\n\");\n      fprintf(rsp, \"%s\\\"%s\\\"\", indent, group->members[i]->name);\n    }\n}\n\n"
  },
  {
    "path": "src/group.h",
    "content": "#pragma once\n#include \"bar_item.h\"\n\nstruct bar;\n\nstruct group {\n  CGRect bounds;\n\n  struct window* first_window;\n  struct window* last_window;\n\n  struct bar_item* first_item;\n  struct bar_item* last_item;\n\n  uint32_t num_members;\n  struct bar_item** members;\n};\n\nstruct group* group_create();\nvoid group_init(struct group* group);\nvoid group_set_name(struct group* group, char* _name);\nvoid group_add_member(struct group* group, struct bar_item* item);\nvoid group_remove_member(struct group* group, struct bar_item* bar_item);\nuint32_t group_get_length(struct group* group, struct bar* bar);\n\nvoid group_calculate_bounds(struct group* group, struct bar* bar, uint32_t y);\nvoid group_destroy(struct group* group);\n\nvoid group_serialize(struct group* group, char* indent, FILE* rsp);\n"
  },
  {
    "path": "src/hotload.c",
    "content": "#include \"bar_manager.h\"\n#include \"event.h\"\n#include <ApplicationServices/ApplicationServices.h>\n#include <libgen.h>\n\nextern char g_config_file[4096];\nextern char g_name[256];\nbool g_hotload = false;\nint64_t g_last_hotload = 0;\n\nvoid hotload_set_state(int state) {\n  g_hotload = state;\n}\n\nint hotload_get_state() {\n  return g_hotload;\n}\n\nbool set_config_file_path(char* file) {\n  char* path = realpath(file, NULL);\n  if (path) {\n    snprintf(g_config_file, sizeof(g_config_file), \"%s\", path);\n    free(path);\n    return true;\n  }\n  return false;\n}\n\nstatic bool get_config_file(char *restrict filename, char *restrict buffer, int buffer_size) {\n  char *xdg_home = getenv(\"XDG_CONFIG_HOME\");\n  if (xdg_home && *xdg_home) {\n    snprintf(buffer, buffer_size, \"%s/%s/%s\", xdg_home, g_name, filename);\n    if (file_exists(buffer)) return true;\n  }\n\n  char *home = getenv(\"HOME\");\n  if (!home) return false;\n\n  snprintf(buffer, buffer_size, \"%s/.config/%s/%s\", home, g_name, filename);\n  if (file_exists(buffer)) return true;\n\n  snprintf(buffer, buffer_size, \"%s/.%s\", home, filename);\n  return file_exists(buffer);\n}\n\nvoid exec_config_file() {\n  if (!*g_config_file\n    && !get_config_file(\"sketchybarrc\", g_config_file, sizeof(g_config_file))) {\n    printf(\"could not locate config file..\\n\");\n    return;\n  }\n\n  if (!file_exists(g_config_file)) {\n    printf(\"file '%s' does not exist..\\n\", g_config_file);\n    return;\n  }\n\n  setenv(\"CONFIG_DIR\", dirname(g_config_file), 1);\n  chdir(dirname(g_config_file));\n\n  if (!ensure_executable_permission(g_config_file)) {\n    printf(\"could not set the executable permission bit for '%s'\\n\", g_config_file);\n    return;\n  }\n\n  if (!fork_exec(g_config_file, NULL)) {\n    printf(\"failed to execute file '%s'\\n\", g_config_file);\n    return;\n  }\n}\n\nstatic void handler(ConstFSEventStreamRef stream, void* context, size_t count, void* paths, const FSEventStreamEventFlags* flags, const FSEventStreamEventId* ids) {\n  if (g_hotload && count > 0) {\n    // Limit the hotload rate to avoid locking up the system on a hotload loop\n    int64_t time = clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW_APPROX);\n    if (time - g_last_hotload > (1ULL << 30)) {\n      g_last_hotload = time;\n      struct event event = { NULL, HOTLOAD };\n      event_post(&event);\n    }\n  }\n}\n\nint begin_receiving_config_change_events() {\n  char* file = dirname(g_config_file);\n  CFStringRef file_ref = CFStringCreateWithCString(\n                             kCFAllocatorDefault, file, kCFStringEncodingUTF8);\n\n  CFArrayRef paths = CFArrayCreate(NULL,\n                                   (const void**)&file_ref,\n                                   1,\n                                   &kCFTypeArrayCallBacks);\n\n  FSEventStreamRef stream = FSEventStreamCreate(\n                                         kCFAllocatorDefault,\n                                         handler,\n                                         NULL,\n                                         paths,\n                                         kFSEventStreamEventIdSinceNow,\n                                         0.5,\n                                         kFSEventStreamCreateFlagNoDefer\n                                         | kFSEventStreamCreateFlagFileEvents);\n\n  CFRelease(file_ref);\n  CFRelease(paths);\n\n  FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(),\n                                           kCFRunLoopDefaultMode);\n\n  FSEventStreamStart(stream);\n  return 0;\n}\n"
  },
  {
    "path": "src/hotload.h",
    "content": "#include <stdbool.h>\n\n#define HOTLOAD_STATE_ENABLED true\n#define HOTLOAD_STATE_DISABLED false\n\nvoid exec_config_file();\nvoid begin_receiving_config_change_events();\nvoid hotload_set_state(int state);\nint hotload_get_state();\nbool set_config_file_path(char* file);\n"
  },
  {
    "path": "src/image.c",
    "content": "#include \"image.h\"\n#include \"misc/helpers.h\"\n#include \"shadow.h\"\n#include \"workspace.h\"\n#include \"media.h\"\n\nvoid image_init(struct image* image) {\n  image->enabled = false;\n  image->image_ref = NULL;\n  image->data_ref = NULL;\n  image->bounds = CGRectNull;\n  image->size = CGSizeZero;\n  image->scale = 1.0;\n  image->path = NULL;\n  image->corner_radius = 0;\n  image->border_width = 0;\n  image->y_offset = 0;\n  image->padding_left = 0;\n  image->padding_right = 0;\n  image->link = NULL;\n\n  shadow_init(&image->shadow);\n  color_init(&image->border_color, 0xcccccccc);\n}\n\nbool image_set_enabled(struct image* image, bool enabled) {\n  if (image->enabled == enabled) return false;\n  image->enabled = enabled;\n  return true;\n}\n\nbool image_set_link(struct image* image, struct image* link) {\n  if (image->link == link) return false;\n  image->link = link;\n  if (link) image->enabled = true;\n  return true;\n}\n\nbool image_load(struct image* image, char* path, FILE* rsp) {\n  if (!path) return false;\n  char* app = string_copy(path);\n  if (image->path) free(image->path);\n  image->path = string_copy(path);\n  char* res_path = resolve_path(path);\n  CGImageRef new_image_ref = NULL;\n  float scale = 1.f;\n\n  struct key_value_pair app_kv = get_key_value_pair(app, '.');\n  if (app_kv.key && app_kv.value && strcmp(app_kv.key, \"app\") == 0) {\n    CGImageRef app_icon = workspace_icon_for_app(app_kv.value);\n    scale = workspace_get_scale();\n    scale *= scale;\n    if (app_icon) new_image_ref = app_icon;\n    else {\n      respond(rsp, \"[!] Image: Invalid application name: '%s'\\n\", app_kv.value);\n      free(res_path);\n      free(app);\n      return false;\n    }\n  } else if (app_kv.key && app_kv.value && strcmp(app_kv.key, \"space\") == 0) {\n    uint32_t sid = atoi(app_kv.value);\n    CGImageRef space_img = space_capture(sid);\n    if (space_img) new_image_ref = space_img;\n    else {\n      respond(rsp, \"[!] Image: Invalid Space ID: '%s'\\n\", app_kv.value);\n      free(res_path);\n      free(app);\n      return false;\n    }\n  } else if (strcmp(path, \"media.artwork\") == 0) {\n    free(res_path);\n    free(app);\n    begin_receiving_media_events();\n    return image_set_link(image, &g_bar_manager.current_artwork);\n  } else if (file_exists(res_path)) {\n    CGDataProviderRef data_provider = CGDataProviderCreateWithFilename(res_path);\n    if (data_provider) {\n      if (strlen(res_path) > 3 && string_equals(&res_path[strlen(res_path) - 4], \".png\"))\n        new_image_ref = CGImageCreateWithPNGDataProvider(data_provider,\n                                                         NULL,\n                                                         false,\n                                                         kCGRenderingIntentDefault);\n      else {\n        new_image_ref = CGImageCreateWithJPEGDataProvider(data_provider,\n                                                          NULL,\n                                                          false,\n                                                          kCGRenderingIntentDefault);\n      }\n      CFRelease(data_provider);\n    } else {\n      respond(rsp, \"[!] Image: Invalid Image Format: '%s'\\n\", app_kv.value);\n      free(res_path);\n      free(app);\n      return false;\n    }\n  }\n  else if (strlen(res_path) == 0) {\n    image_destroy(image);\n    free(res_path);\n    free(app);\n    return false;\n  } else {\n    respond(rsp, \"[!] Image: File '%s' not found\\n\", res_path);\n    free(res_path);\n    free(app);\n    return false;\n  }\n\n  if (new_image_ref) {\n    image_set_image(image,\n                    new_image_ref,\n                    (CGRect){{0,0},\n                             {CGImageGetWidth(new_image_ref) / scale,\n                              CGImageGetHeight(new_image_ref) / scale }},\n                    true                                        );\n  }\n  else {\n    if (new_image_ref) CFRelease(new_image_ref);\n    printf(\"Could not open image file at: %s\\n\", res_path);\n    fprintf(rsp, \"Could not open image file at: %s\\n\", res_path);\n  }\n\n  free(res_path);\n  free(app);\n  return true;\n}\n\nstatic bool image_data_equals(struct image* image, CFDataRef new_data_ref) {\n  bool equals = false;\n  if (image->image_ref && image->data_ref) {\n    uint32_t old_len = CFDataGetLength(image->data_ref);\n    uint32_t new_len = CFDataGetLength(new_data_ref);\n    if (old_len == new_len)\n      equals = memcmp(CFDataGetBytePtr(image->data_ref),\n                      CFDataGetBytePtr(new_data_ref),\n                      old_len                           )\n               == 0;\n  }\n\n  return equals;\n}\n\nvoid image_copy(struct image* image, CGImageRef source) {\n  if (source) image->image_ref = CGImageCreateCopy(source);\n}\n\nbool image_set_image(struct image* image, CGImageRef new_image_ref, CGRect bounds, bool forced) {\n  if (!new_image_ref) {\n    if (image->image_ref) CGImageRelease(image->image_ref);\n    if (image->data_ref) CFRelease(image->data_ref);\n    image->image_ref = NULL;\n    image->data_ref = NULL;\n    return false;\n  }\n  if (image->link) image_set_link(image, NULL);\n  CFDataRef new_data_ref = CGDataProviderCopyData(CGImageGetDataProvider(new_image_ref));\n\n  if (!forced && image_data_equals(image, new_data_ref)\n      && CGSizeEqualToSize(image->size, bounds.size)   ) {\n    CFRelease(new_data_ref);\n    CGImageRelease(new_image_ref);\n    return false;\n  }\n\n  if (image->image_ref) CGImageRelease(image->image_ref);\n  if (image->data_ref) CFRelease(image->data_ref);\n\n  image->size = bounds.size;\n  image->bounds = (CGRect){{0, 0},\n                           {bounds.size.width * image->scale,\n                            bounds.size.height * image->scale}};\n\n  image->image_ref = new_image_ref;\n  image->data_ref = new_data_ref;\n  image->enabled = true;\n  return true;\n}\n\nbool image_set_scale(struct image* image, float scale) {\n  if (scale == image->scale) return false;\n  image->scale = scale;\n  image->bounds = (CGRect){{image->bounds.origin.x, image->bounds.origin.y},\n                           {image->size.width * image->scale,\n                            image->size.height * image->scale}};\n  return true;\n}\n\nbool image_set_corner_radius(struct image* image, uint32_t corner_radius) {\n  if (image->corner_radius == corner_radius) return false;\n\n  image->corner_radius = corner_radius;\n  return true;\n}\n\nbool image_set_border_width(struct image* image, float border_width) {\n  if (image->border_width == border_width) return false;\n\n  image->border_width = border_width;\n  return true;\n}\n\nbool image_set_border_color(struct image* image, uint32_t color) {\n  return color_set_hex(&image->border_color, color);\n}\n\nbool image_set_padding_left(struct image* image, int padding_left) {\n  if (image->padding_left == padding_left) return false;\n\n  image->padding_left = padding_left;\n  return true;\n}\n\nbool image_set_padding_right(struct image* image, int padding_right) {\n  if (image->padding_right == padding_right) return false;\n\n  image->padding_right = padding_right;\n  return true;\n}\n\nbool image_set_yoffset(struct image* image, int yoffset) {\n  if (image->y_offset == yoffset) return false;\n\n  image->y_offset = yoffset;\n  return true;\n}\n\nCGSize image_get_size(struct image* image) {\n  return (CGSize){ .width = image->bounds.size.width\n                            + image->padding_left\n                            + image->padding_right\n                            + (image->shadow.enabled\n                               ? image->shadow.offset.x : 0),\n                   .height = image->bounds.size.height\n                             + 2*abs(image->y_offset) };\n}\n\nvoid image_calculate_bounds(struct image* image, uint32_t x, uint32_t y) {\n  if (image->link && image->link->image_ref) {\n    float internal_scale = 32.f / CGImageGetHeight(image->link->image_ref);\n    CGRect bounds = (CGRect){{0,0},\n                  { CGImageGetWidth(image->link->image_ref) * internal_scale,\n                    32.f                                                    }};\n\n    image->size = bounds.size;\n    image->bounds = (CGRect){{0, 0},\n                             {bounds.size.width * image->scale,\n                              bounds.size.height * image->scale}};\n  }\n\n  image->bounds.origin.x = x + image->padding_left;\n  image->bounds.origin.y = y - image->bounds.size.height / 2 + image->y_offset;\n}\n\nvoid image_draw(struct image* image, CGContextRef context) {\n  if ((!image->link && !image->image_ref)\n      || (image->link && !image->link->image_ref)) return;\n\n  if (image->shadow.enabled) {\n    CGContextSaveGState(context);\n    CGRect sbounds = shadow_get_bounds(&image->shadow, image->bounds);\n    CGContextSetRGBFillColor(context, image->shadow.color.r, image->shadow.color.g, image->shadow.color.b, image->shadow.color.a);\n    CGMutablePathRef path = CGPathCreateMutable();\n    CGPathAddRoundedRect(path,\n                         NULL,\n                         sbounds,\n                         image->corner_radius,\n                         image->corner_radius);\n\n    CGContextAddPath(context, path);\n    CGContextDrawPath(context, kCGPathFillStroke);\n    CFRelease(path);\n    CGContextRestoreGState(context);\n  } \n\n  CGContextSaveGState(context);\n  if (image->bounds.size.height > 2*image->corner_radius\n      && image->bounds.size.width > 2*image->corner_radius) {\n    CGMutablePathRef path = CGPathCreateMutable();\n    CGPathAddRoundedRect(path,\n                         NULL,\n                         image->bounds,\n                         image->corner_radius,\n                         image->corner_radius);\n\n    CGContextAddPath(context, path);\n    CGContextClip(context);\n    CFRelease(path);\n  }\n\n  CGContextDrawImage(context,\n                     image->bounds,\n                     image->link ? image->link->image_ref : image->image_ref);\n\n  if (image->bounds.size.height > 2*image->corner_radius\n      && image->bounds.size.width > 2*image->corner_radius) {\n    CGContextSetLineWidth(context, 2*image->border_width);\n    CGContextSetRGBStrokeColor(context,\n                               image->border_color.r,\n                               image->border_color.g,\n                               image->border_color.b,\n                               image->border_color.a);\n\n    CGContextSetRGBFillColor(context, 0, 0, 0, 0);\n    CGMutablePathRef path = CGPathCreateMutable();\n    CGPathAddRoundedRect(path,\n                         NULL,\n                         image->bounds,\n                         image->corner_radius,\n                         image->corner_radius);\n\n    CGContextAddPath(context, path);\n    CGContextDrawPath(context, kCGPathFillStroke);\n    CFRelease(path);\n  }\n  CGContextRestoreGState(context);\n}\n\nvoid image_clear_pointers(struct image* image) {\n  image->image_ref = NULL;\n  image->data_ref = NULL;\n  image->path = NULL;\n}\n\nvoid image_destroy(struct image* image) {\n  CGImageRelease(image->image_ref);\n  if (image->data_ref) CFRelease(image->data_ref);\n  if (image->path) free(image->path);\n  image_clear_pointers(image);\n}\n\nvoid image_serialize(struct image* image, char* indent, FILE* rsp) {\n  fprintf(rsp, \"%s\\\"value\\\": \\\"%s\\\",\\n\"\n               \"%s\\\"drawing\\\": \\\"%s\\\",\\n\"\n               \"%s\\\"scale\\\": %f\",\n               indent, image->path,\n               indent, format_bool(image->enabled),\n               indent, image->scale                );\n}\n\nbool image_parse_sub_domain(struct image* image, FILE* rsp, struct token property, char* message) {\n  bool needs_refresh = false;\n  if (token_equals(property, PROPERTY_STRING)) {\n    return image_load(image, token_to_string(get_token(&message)), rsp);\n  }\n  else if (token_equals(property, PROPERTY_DRAWING)) {\n    return image_set_enabled(image,\n                             evaluate_boolean_state(get_token(&message),\n                             image->enabled)                            );\n  }\n  else if (token_equals(property, PROPERTY_SCALE)) {\n    ANIMATE_FLOAT(image_set_scale,\n                  image,\n                  image->scale,\n                  token_to_float(get_token(&message)));\n  }\n  else if (token_equals(property, PROPERTY_CORNER_RADIUS)) {\n    ANIMATE(image_set_corner_radius,\n            image,\n            image->corner_radius,\n            token_to_uint32t(get_token(&message)));\n  }\n  else if (token_equals(property, PROPERTY_PADDING_LEFT)) {\n    ANIMATE(image_set_padding_left,\n            image,\n            image->padding_left,\n            token_to_int(get_token(&message)));\n  }\n  else if (token_equals(property, PROPERTY_PADDING_RIGHT)) {\n    ANIMATE(image_set_padding_right,\n            image,\n            image->padding_right,\n            token_to_int(get_token(&message)));\n  }\n  else if (token_equals(property, PROPERTY_YOFFSET)) {\n    ANIMATE(image_set_yoffset,\n            image,\n            image->y_offset,\n            token_to_int(get_token(&message)));\n  }\n  else if (token_equals(property, PROPERTY_BORDER_WIDTH)) {\n    ANIMATE_FLOAT(image_set_border_width,\n                  image,\n                  image->border_width,\n                  token_to_float(get_token(&message)));\n  }\n  else if (token_equals(property, PROPERTY_BORDER_COLOR)) {\n    struct token token = get_token(&message);\n    ANIMATE_BYTES(image_set_border_color,\n                  image,\n                  image->border_color.hex,\n                  token_to_int(token));\n  }\n  else {\n    struct key_value_pair key_value_pair = get_key_value_pair(property.text,\n                                                              '.'           );\n    if (key_value_pair.key && key_value_pair.value) {\n      struct token subdom = {key_value_pair.key,strlen(key_value_pair.key)};\n      struct token entry = {key_value_pair.value,strlen(key_value_pair.value)};\n      if (token_equals(subdom, SUB_DOMAIN_BORDER_COLOR)) {\n        return color_parse_sub_domain(&image->border_color,\n                                      rsp,\n                                      entry,\n                                      message);\n      }\n      else if (token_equals(subdom, SUB_DOMAIN_SHADOW)) {\n        return shadow_parse_sub_domain(&image->shadow,\n                                       rsp,\n                                       entry,\n                                       message             );\n      }\n      else {\n        respond(rsp, \"[?] Image: Invalid subdomain: %s \\n\", property.text);\n      }\n    }\n    else {\n        respond(rsp, \"[?] Image: Unknown property: %s \\n\", property.text);\n    }\n  }\n  return needs_refresh;\n}\n"
  },
  {
    "path": "src/image.h",
    "content": "#pragma once\n#include \"shadow.h\"\n#include \"misc/defines.h\"\n\nextern CGImageRef workspace_icon_for_app(char* app);\n\nstruct image {\n  bool enabled;\n\n  float scale;\n  CGSize size;\n  CGRect bounds;\n  \n  char* path;\n\n  CGImageRef image_ref;\n  CFDataRef data_ref;\n\n  struct shadow shadow;\n\n  struct color border_color;\n  float border_width;\n  uint32_t corner_radius;\n\n  int padding_left;\n  int padding_right;\n  int y_offset;\n\n  struct image* link;\n};\n\nvoid image_init(struct image* image);\nbool image_set_enabled(struct image* image, bool enabled);\nvoid image_copy(struct image* image, CGImageRef source);\nbool image_set_image(struct image* image, CGImageRef new_image_ref, CGRect bounds, bool forced);\nbool image_load(struct image* image, char* path, FILE* rsp);\nbool image_set_scale(struct image* image, float scale);\n\nCGSize image_get_size(struct image* image);\nvoid image_calculate_bounds(struct image* image, uint32_t x, uint32_t y);\nvoid image_draw(struct image* image, CGContextRef context);\nvoid image_clear_pointers(struct image* image);\nvoid image_destroy(struct image* image);\n\nvoid image_serialize(struct image* image, char* indent, FILE* rsp);\nbool image_parse_sub_domain(struct image* image, FILE* rsp, struct token property, char* message);\n"
  },
  {
    "path": "src/mach.c",
    "content": "#include \"mach.h\"\n#include <mach/mach_port.h>\n#include <mach/message.h>\n#include <stdint.h>\n#include <CoreFoundation/CoreFoundation.h>\n\nmach_port_t mach_get_bs_port(char* bs_name) {\n  mach_port_name_t task = mach_task_self();\n\n  mach_port_t bs_port;\n  if (task_get_special_port(task,\n                            TASK_BOOTSTRAP_PORT,\n                            &bs_port            ) != KERN_SUCCESS) {\n    return 0;\n  }\n\n  mach_port_t port;\n  if (bootstrap_look_up(bs_port,\n                        bs_name,\n                        &port   ) != KERN_SUCCESS) {\n    return 0;\n  }\n\n  return port;\n}\n\nvoid mach_receive_message(mach_port_t port, struct mach_buffer* buffer, bool timeout) {\n  *buffer = (struct mach_buffer) { 0 };\n  mach_msg_return_t msg_return;\n  if (timeout)\n    msg_return = mach_msg(&buffer->message.header,\n                          MACH_RCV_MSG | MACH_RCV_TIMEOUT,\n                          0,\n                          sizeof(struct mach_buffer),\n                          port,\n                          100,\n                          MACH_PORT_NULL                  );\n  else \n    msg_return = mach_msg(&buffer->message.header,\n                          MACH_RCV_MSG,\n                          0,\n                          sizeof(struct mach_buffer),\n                          port,\n                          MACH_MSG_TIMEOUT_NONE,\n                          MACH_PORT_NULL            );\n\n  if (msg_return != MACH_MSG_SUCCESS) {\n    buffer->message.descriptor.address = NULL;\n  }\n}\n\nchar* mach_send_message(mach_port_t port, char* message, uint32_t len, bool await_response) {\n  if (!message || !port) return NULL;\n\n  mach_port_t response_port;\n    mach_port_name_t task = mach_task_self();\n  if (await_response) {\n    if (mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE,\n                                 &response_port          ) != KERN_SUCCESS) {\n      return NULL;\n    }\n\n    if (mach_port_insert_right(task, response_port,\n                                     response_port,\n                                     MACH_MSG_TYPE_MAKE_SEND)!= KERN_SUCCESS) {\n      return NULL;\n    }\n  }\n\n  struct mach_message msg = { 0 };\n  msg.header.msgh_remote_port = port;\n  if (await_response) {\n    msg.header.msgh_local_port = response_port;\n    msg.header.msgh_id = response_port;\n    msg.header.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND,\n                                              MACH_MSG_TYPE_MAKE_SEND,\n                                              0,\n                                              MACH_MSGH_BITS_COMPLEX  );\n  } else {\n    msg.header.msgh_bits = MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND\n                                              & MACH_MSGH_BITS_REMOTE_MASK,\n                                              0,\n                                              0,\n                                              MACH_MSGH_BITS_COMPLEX       );\n  }\n\n  msg.header.msgh_size = sizeof(struct mach_message);\n\n  msg.msgh_descriptor_count = 1;\n  msg.descriptor.address = message;\n  msg.descriptor.size = len * sizeof(char);\n  msg.descriptor.copy = MACH_MSG_VIRTUAL_COPY;\n  msg.descriptor.deallocate = false;\n  msg.descriptor.type = MACH_MSG_OOL_DESCRIPTOR;\n\n  mach_msg(&msg.header,\n           MACH_SEND_MSG,\n           sizeof(struct mach_message),\n           0,\n           MACH_PORT_NULL,\n           MACH_MSG_TIMEOUT_NONE,\n           MACH_PORT_NULL             );\n\n  if (await_response) {\n    struct mach_buffer buffer = { 0 };\n    mach_receive_message(response_port, &buffer, true);\n    char* rsp = NULL;\n    if (buffer.message.descriptor.address) {\n      rsp = malloc(strlen(buffer.message.descriptor.address) + 1);\n      memcpy(rsp, buffer.message.descriptor.address,\n                  strlen(buffer.message.descriptor.address) + 1);\n    } else {\n      rsp = malloc(1);\n      *rsp = '\\0';\n    }\n\n    mach_msg_destroy(&buffer.message.header);\n    mach_port_mod_refs(task, response_port, MACH_PORT_RIGHT_RECEIVE, -1);\n    mach_port_deallocate(task, response_port);\n\n    return rsp;\n  }\n\n  return NULL;\n}\n\nvoid mach_message_callback(CFMachPortRef port, void* message, CFIndex size, void* context) {\n  struct mach_server* mach_server = context;\n  struct mach_buffer buffer;\n  buffer.message = *(struct mach_message*)message;\n  mach_server->handler(&buffer);\n  mach_msg_destroy(&buffer.message.header);\n}\n\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\nextern char g_name[256];\nbool mach_server_begin(struct mach_server* mach_server, mach_handler handler) {\n  mach_server->task = mach_task_self();\n\n  if (mach_port_allocate(mach_server->task,\n                         MACH_PORT_RIGHT_RECEIVE,\n                         &mach_server->port      ) != KERN_SUCCESS) {\n    return false;\n  }\n\n  struct mach_port_limits limits = {};\n  limits.mpl_qlimit = MACH_PORT_QLIMIT_LARGE;\n\n  if (mach_port_set_attributes(mach_server->task,\n                               mach_server->port,\n                               MACH_PORT_LIMITS_INFO,\n                               (mach_port_info_t)&limits,\n                               MACH_PORT_LIMITS_INFO_COUNT) != KERN_SUCCESS) {\n    return false;\n  }\n\n  if (mach_port_insert_right(mach_server->task,\n                             mach_server->port,\n                             mach_server->port,\n                             MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS) {\n    return false;\n  }\n\n  if (task_get_special_port(mach_server->task,\n                            TASK_BOOTSTRAP_PORT,\n                            &mach_server->bs_port) != KERN_SUCCESS) {\n    return false;\n  }\n\n  char bs_name[256];\n  snprintf(bs_name, 256, MACH_BS_NAME_FMT, g_name);\n\n  if (bootstrap_register(mach_server->bs_port,\n                         bs_name,\n                         mach_server->port    ) != KERN_SUCCESS) {\n    return false;\n  }\n\n  mach_server->handler = handler;\n  mach_server->is_running = true;\n\n  CFMachPortContext context = {0, (void*)mach_server};\n\n  CFMachPortRef cf_mach_port = CFMachPortCreateWithPort(NULL,\n                                                        mach_server->port,\n                                                        mach_message_callback,\n                                                        &context,\n                                                        false                );\n\n  CFRunLoopSourceRef source = CFMachPortCreateRunLoopSource(NULL,\n                                                            cf_mach_port,\n                                                            0            );\n\n  CFRunLoopAddSource(CFRunLoopGetMain(), source, kCFRunLoopDefaultMode);\n  CFRelease(source);\n  CFRelease(cf_mach_port);\n  return true;\n}\n#pragma clang diagnostic pop\n"
  },
  {
    "path": "src/mach.h",
    "content": "#pragma once\n#include <bootstrap.h>\n#include <mach/mach.h>\n#include <mach/message.h>\n#include <pthread.h>\n#include <stdlib.h>\n#include <stdio.h>\n\n#define MACH_BS_NAME_FMT \"git.felix.%s\"\n\nstruct mach_message {\n  mach_msg_header_t header;\n  mach_msg_size_t msgh_descriptor_count;\n  mach_msg_ool_descriptor_t descriptor;\n};\n\nstruct mach_buffer {\n  struct mach_message message;\n  mach_msg_trailer_t trailer;\n};\n\n#define MACH_HANDLER(name) void name(struct mach_buffer* message)\ntypedef MACH_HANDLER(mach_handler);\n\nstruct mach_server {\n  bool is_running;\n  mach_port_name_t task;\n  mach_port_t port;\n  mach_port_t bs_port;\n\n  mach_handler* handler;\n};\n\nbool mach_server_begin(struct mach_server* mach_server, mach_handler handler);\nchar* mach_send_message(mach_port_t port, char* message, uint32_t len, bool await_response);\nmach_port_t mach_get_bs_port(char* bs_name);\n"
  },
  {
    "path": "src/media.h",
    "content": "void initialize_media_events();\nvoid begin_receiving_media_events();\nvoid forced_media_change_event();\n"
  },
  {
    "path": "src/media.m",
    "content": "#include \"event.h\"\n#include <Foundation/Foundation.h>\n\n// The media remote private framework was locked for use on macOS 15.3.\n\nextern void MRMediaRemoteRegisterForNowPlayingNotifications(dispatch_queue_t queue);\nextern void MRMediaRemoteGetNowPlayingInfo(dispatch_queue_t queue, void (^block)(NSDictionary* dict));\nextern void MRMediaRemoteGetNowPlayingApplicationIsPlaying(dispatch_queue_t queue, void (^block)(BOOL playing));\nextern void MRMediaRemoteGetNowPlayingApplicationDisplayName(int null, dispatch_queue_t queue, void (^block)(CFStringRef name));\n\nextern NSString* kMRMediaRemoteNowPlayingApplicationIsPlayingDidChangeNotification;\nextern NSString* kMRMediaRemoteNowPlayingInfoDidChangeNotification;\nextern NSString* kMRMediaRemoteNowPlayingApplicationDidChangeNotification;\n\nextern NSString* kMRMediaRemoteNowPlayingInfoAlbum;\nextern NSString* kMRMediaRemoteNowPlayingInfoArtist;\nextern NSString* kMRMediaRemoteNowPlayingInfoTitle;\nextern NSString* kMRMediaRemoteNowPlayingInfoArtworkMIMEType;\nextern NSString* kMRMediaRemoteNowPlayingInfoArtworkData;\nextern NSString* kMRMediaRemoteNowPlayingApplicationDisplayNameUserInfoKey;\n\n@interface media_context : NSObject {}\n  @property const char* app;\n  @property const char* artist;\n  @property const char* title;\n  @property const char* album;\n  @property CGImageRef artwork;\n  @property BOOL playing;\n- (id)init;\n@end\n\n@implementation media_context\n- (id)init {\n    if ((self = [super init])) {\n      [NSNotificationCenter.defaultCenter addObserver:self\n                                          selector:@selector(media_change:)\n                                          name:kMRMediaRemoteNowPlayingInfoDidChangeNotification\n                                          object:nil];\n\n      [NSNotificationCenter.defaultCenter addObserver:self\n                                          selector:@selector(playing_change:)\n                                          name:kMRMediaRemoteNowPlayingApplicationIsPlayingDidChangeNotification\n                                          object:nil];\n\n      [NSNotificationCenter.defaultCenter addObserver:self\n                                          selector:@selector(media_change:)\n                                          name:kMRMediaRemoteNowPlayingApplicationDidChangeNotification\n                                          object:nil];\n      self.app = NULL;\n      self.artist = NULL;\n      self.title = NULL;\n      self.album = NULL;\n    }\n\n    return self;\n}\n\nchar* g_media_info = NULL;\nbool g_media_events = false;\n- (void) update {\n  @autoreleasepool {\n    if (self.app && self.artist && self.title && self.album) {\n      char* escaped_artist = escape_string((char*)self.artist);\n      char* escaped_title = escape_string((char*)self.title);\n      char* escaped_album = escape_string((char*)self.album);\n\n      uint32_t info_len = strlen(self.app)\n                          + strlen(escaped_artist)\n                          + strlen(escaped_title)\n                          + strlen(escaped_album) + 256;\n\n      char info[info_len];\n      snprintf(info, info_len, \"{\\n\"\n                               \"\\t\\\"state\\\": \\\"%s\\\",\\n\"\n                               \"\\t\\\"title\\\": \\\"%s\\\",\\n\"\n                               \"\\t\\\"album\\\": \\\"%s\\\",\\n\"\n                               \"\\t\\\"artist\\\": \\\"%s\\\",\\n\"\n                               \"\\t\\\"app\\\": \\\"%s\\\"\\n}\",\n                               self.playing ? \"playing\" : \"paused\",\n                               escaped_title,\n                               escaped_album,\n                               escaped_artist,\n                               self.app                            );\n\n      free(escaped_artist);\n      free(escaped_title);\n      free(escaped_album);\n\n      if (self.artwork) {\n        struct event cover_event = { self.artwork, COVER_CHANGED };\n        event_post(&cover_event);\n      }\n\n      if (!g_media_info || strcmp(info, g_media_info) != 0) {\n        g_media_info = realloc(g_media_info, info_len);\n        memcpy(g_media_info, info, info_len);\n\n        char payload_info[info_len];\n        memcpy(payload_info, info, info_len);\n\n        struct event event = { payload_info, MEDIA_CHANGED };\n        event_post(&event);\n      }\n    }\n  }\n}\n\n- (void)playing_change:(NSNotification *)notification {\n  if (!g_media_events) return;\n  MRMediaRemoteGetNowPlayingApplicationIsPlaying(dispatch_get_main_queue(), ^(BOOL playing) {\n    self.playing = playing;\n    [self media_change:notification];\n  });\n}\n\n- (void)media_change:(NSNotification *)notification {\n  if (!g_media_events) return;\n  MRMediaRemoteGetNowPlayingApplicationDisplayName(0, dispatch_get_main_queue(), ^(CFStringRef name_nr) {\n    if (!name_nr) return;\n\n    CFStringRef name = CFStringCreateCopy(CFAllocatorGetDefault(), name_nr);\n    @autoreleasepool {\n      MRMediaRemoteGetNowPlayingInfo(dispatch_get_main_queue(), ^(NSDictionary* dict) {\n        @autoreleasepool {\n          if (dict && name) {\n            NSString* app = (NSString*)name;\n            NSString* artist = [dict objectForKey:kMRMediaRemoteNowPlayingInfoArtist];\n            NSString* title = [dict objectForKey:kMRMediaRemoteNowPlayingInfoTitle];\n            NSString* album = [dict objectForKey:kMRMediaRemoteNowPlayingInfoAlbum];\n            if (artist && title && album && name) {\n              self.app = (char*)[app UTF8String];\n              self.artist = (char*)[artist UTF8String];\n              self.title = (char*)[title UTF8String];\n              self.album = (char*)[album UTF8String];\n\n              NSString* mime_type = [dict objectForKey:kMRMediaRemoteNowPlayingInfoArtworkMIMEType];\n              NSData* ns_data = [dict objectForKey:kMRMediaRemoteNowPlayingInfoArtworkData];\n              CGImageRef image = NULL;\n              if (mime_type && ns_data) {\n                CFDataRef data = CFDataCreate(NULL,\n                                              [ns_data bytes],\n                                              [ns_data length]);\n\n                CGDataProviderRef provider\n                                        = CGDataProviderCreateWithCFData(data);\n\n                if (provider) {\n                  CGImageSourceRef source\n                         = CGImageSourceCreateWithDataProvider(provider, NULL);\n                  if (source) {\n                    image = CGImageSourceCreateImageAtIndex(source, 0, NULL);\n                    CFRelease(source);\n                  }\n                  CFRelease(provider);\n                }\n                CFRelease(data);\n              }\n              self.artwork = image;\n              [self update];\n            }\n          }\n        }\n\n        CFRelease(name);\n      });\n    }\n  });\n}\n@end\n\nmedia_context* g_media_context = NULL;\nvoid initialize_media_events() {\n  MRMediaRemoteRegisterForNowPlayingNotifications(dispatch_get_main_queue());\n\n  g_media_context = [media_context alloc];\n  [g_media_context init];\n}\n\nvoid begin_receiving_media_events() {\n  g_media_events = true;\n}\n\nvoid forced_media_change_event() {\n  if (g_media_info) free(g_media_info);\n  g_media_info = NULL;\n  [g_media_context playing_change:NULL];\n}\n"
  },
  {
    "path": "src/message.c",
    "content": "#include \"message.h\"\n#include \"app_windows.h\"\n#include \"bar_manager.h\"\n#include \"misc/defines.h\"\n#include \"hotload.h\"\n#include \"misc/helpers.h\"\n#include \"volume.h\"\n#include \"media.h\"\n#include \"wifi.h\"\n#include \"power.h\"\n\nextern struct bar_manager g_bar_manager;\n\nstatic struct bar_item** get_bar_items_for_regex(struct token reg, FILE* rsp, uint32_t* count) {\n  struct bar_item** bar_items = NULL;\n\n  char* regstring = malloc(sizeof(char)*(reg.length - 1));\n  memcpy(regstring, &reg.text[1], reg.length - 2);\n  regstring[reg.length - 2] = '\\0';\n  regex_t regex;\n  int reti;\n  reti = regcomp(&regex, regstring, 0);\n  free(regstring);\n  if (reti) {\n    respond(rsp, \"[!] Regex: Could not compile regex '%s'\\n\", reg.text);\n    return NULL;\n  }\n\n  for (int i = 0; i < g_bar_manager.bar_item_count; i++) {\n    struct bar_item* bar_item = g_bar_manager.bar_items[i];\n\n    reti = regexec(&regex, bar_item->name, 0, NULL, 0);\n    if (!reti) {\n      ++*count;\n      bar_items = realloc(bar_items, sizeof(struct bar_item*)* *count);\n      bar_items[*count - 1] = bar_item;\n    }\n    else if (reti != REG_NOMATCH) {\n      char buf[1024];\n      regerror(reti, &regex, buf, sizeof(buf));\n      respond(rsp, \"[!] Regex: Regex match failed '%s'\\n\", buf);\n      return NULL;\n    }\n  }\n  if (!bar_items) {\n    respond(rsp, \"[?] Regex: No match found for regex '%s'\\n\", reg.text);\n  }\n  regfree(&regex);\n  return bar_items;\n}\n\nstatic void handle_domain_subscribe(FILE* rsp, struct token domain, char* message) {\n  struct token name = get_token(&message);\n\n  int item_index_for_name = bar_manager_get_item_index_for_name(&g_bar_manager,\n                                                                name.text    );\n  if (item_index_for_name < 0) {\n    respond(rsp, \"[!] Subscribe: Item not found '%s'\\n\", name.text);\n    return;\n  }\n  struct bar_item* bar_item = g_bar_manager.bar_items[item_index_for_name];\n\n  bar_item_parse_subscribe_message(bar_item, message, rsp);\n}\n\nstatic void handle_domain_trigger(FILE* rsp, struct token domain, char* message) {\n  struct token event = get_token(&message);\n  struct env_vars env_vars;\n  env_vars_init(&env_vars);\n  struct token token = get_token(&message);\n  while (token.text && token.length > 0) {\n    struct key_value_pair key_value_pair = get_key_value_pair(token.text, '=');\n\n    if (key_value_pair.key && key_value_pair.value) {\n      env_vars_set(&env_vars,\n                   string_copy(key_value_pair.key),\n                   string_copy(key_value_pair.value));\n    }\n\n    token = get_token(&message);\n  }\n\n  if (token_equals(event, COMMAND_SUBSCRIBE_SPACE_CHANGE)) {\n    bar_manager_handle_space_change(&g_bar_manager, true);\n  } else if (token_equals(event, COMMAND_SUBSCRIBE_DISPLAY_CHANGE)) {\n    bar_manager_handle_display_change(&g_bar_manager);\n  } else if (token_equals(event, COMMAND_SUBSCRIBE_SPACE_WINDOWS_CHANGE)) {\n    forced_space_windows_event();\n  } else if (token_equals(event, COMMAND_SUBSCRIBE_VOLUME_CHANGE)) {\n    forced_volume_event();\n  } else if (token_equals(event, COMMAND_SUBSCRIBE_MEDIA_CHANGE)) {\n    forced_media_change_event();\n  } else if (token_equals(event, COMMAND_SUBSCRIBE_WIFI_CHANGE)) {\n    forced_network_event();\n  } else if (token_equals(event, COMMAND_SUBSCRIBE_POWER_SOURCE_CHANGE)) {\n    forced_power_event();\n  } else {\n    bar_manager_custom_events_trigger(&g_bar_manager, event.text, &env_vars);\n  }\n  env_vars_destroy(&env_vars);\n}\n\nstatic void handle_domain_push(FILE* rsp, struct token domain, char* message) {\n  struct token name = get_token(&message);\n\n  int item_index_for_name = bar_manager_get_item_index_for_name(&g_bar_manager,\n                                                                name.text    );\n\n  if (item_index_for_name < 0) {\n    respond(rsp, \"[!] Push: Item '%s' not found\\n\", name.text);\n    return;\n  }\n  struct bar_item* bar_item = g_bar_manager.bar_items[item_index_for_name];\n  if (bar_item->type != BAR_COMPONENT_GRAPH) {\n    respond(rsp, \"[!] Push: Item '%s' not a graph\\n\", name.text);\n    return;\n  }\n  struct token y = get_token(&message);\n  while (y.text && y.length > 0) {\n    graph_push_back(&bar_item->graph, token_to_float(y));\n    y = get_token(&message);\n  }\n  bar_item_needs_update(bar_item);\n}\n\nstatic void handle_domain_rename(FILE* rsp, struct token domain, char* message) {\n  struct token old_name  = get_token(&message);\n  struct token new_name  = get_token(&message);\n  int item_index_for_old_name = bar_manager_get_item_index_for_name(&g_bar_manager,\n                                                                    old_name.text);\n  int item_index_for_new_name = bar_manager_get_item_index_for_name(&g_bar_manager,\n                                                                    new_name.text);\n  if (item_index_for_old_name < 0 || item_index_for_new_name >= 0) {\n    respond(rsp, \"[!] Rename: Failed to rename item: %s -> %s\\n\", old_name.text,\n                                                                  new_name.text);\n    return;\n  }\n  bar_item_set_name(g_bar_manager.bar_items[item_index_for_old_name],\n                    token_to_string(new_name)                        );\n}\n\nstatic void handle_domain_clone(FILE* rsp, struct token domain, char* message) {\n  struct token name = get_token(&message);\n  struct token parent = get_token(&message);\n  struct token modifier = get_token(&message);\n  struct bar_item* parent_item = NULL;\n\n  int parent_index = bar_manager_get_item_index_for_name(&g_bar_manager,\n                                                         parent.text    );\n\n  if (parent_index >= 0)\n    parent_item = g_bar_manager.bar_items[parent_index];\n  else {\n    respond(rsp, \"[!] Clone: Parent Item '%s' not found\\n\", parent.text);\n    return;\n  }\n\n  if (bar_manager_get_item_index_for_name(&g_bar_manager, name.text) >= 0) {\n    respond(rsp, \"[?] Clone: Item '%s' already exists\\n\", name.text);\n    return;\n  }\n  struct bar_item* bar_item = bar_manager_create_item(&g_bar_manager);\n  bar_item_inherit_from_item(bar_item, parent_item);\n  bar_item_set_name(bar_item, token_to_string(name));\n  if (token_equals(modifier, ARGUMENT_COMMON_VAL_BEFORE))\n    bar_manager_move_item(&g_bar_manager, bar_item, parent_item, true);\n  else if (token_equals(modifier, ARGUMENT_COMMON_VAL_AFTER))\n    bar_manager_move_item(&g_bar_manager, bar_item, parent_item, false);\n  bar_item_needs_update(bar_item);\n}\n\nstatic void handle_domain_add(FILE* rsp, struct token domain, char* message) {\n  struct token command  = get_token(&message);\n\n  if (token_equals(command, COMMAND_ADD_EVENT)) {\n    struct token event = get_token(&message);\n    if (strlen(message) > 0)\n      custom_events_append(&g_bar_manager.custom_events,\n                           token_to_string(event),\n                           token_to_string(get_token(&message)));\n    else custom_events_append(&g_bar_manager.custom_events,\n                              token_to_string(event),\n                              NULL                         );\n    return;\n  }\n\n  struct token name = get_token(&message);\n  struct token position = get_token(&message);\n\n  if (bar_manager_get_item_index_for_name(&g_bar_manager, name.text) >= 0) {\n    respond(rsp, \"[?] Add: Item '%s' already exists\\n\", name.text);\n    return;\n  }\n  struct bar_item* bar_item = bar_manager_create_item(&g_bar_manager);\n\n  if (!bar_item_set_type(bar_item, command.text)) {\n    respond(rsp, \"[?] Add %s: Invalid type '%s', assuming 'item'\\n\",\n                 name.text,\n                 command.text                                           );\n  }\n\n  if (bar_item->type != BAR_COMPONENT_GROUP &&\n      !bar_item_set_position(bar_item, position.text)) {\n    respond(rsp, \"[!] Add %s: Illegal position '%s'\\n\", name.text,\n                                                        position.text);\n    bar_manager_remove_item(&g_bar_manager, bar_item);\n    return;\n  }\n\n  if (!bar_item_set_name(bar_item, token_to_string(name))) {\n    respond(rsp, \"[!] Add: Illegal name '%s'\\n\", name.text);\n    bar_manager_remove_item(&g_bar_manager, bar_item);\n    return;\n  }\n\n  if (token_equals(command, COMMAND_ADD_ITEM)) {\n  } else if (command.length > 0) {\n    if (bar_item->type == BAR_COMPONENT_GRAPH) {\n      struct token width = get_token(&message);\n      graph_setup(&bar_item->graph, token_to_uint32t(width));\n    }\n    else if (bar_item->type == BAR_COMPONENT_SLIDER) {\n      struct token width = get_token(&message);\n      slider_setup(&bar_item->slider, token_to_uint32t(width));\n    }\n    else if (bar_item->type == BAR_COMPONENT_ALIAS) {\n      char* tmp_name = string_copy(name.text);\n      struct key_value_pair key_value_pair = get_key_value_pair(tmp_name, ',');\n      if (!key_value_pair.key || !key_value_pair.value)\n        alias_setup(&bar_item->alias, token_to_string(name), NULL);\n      else\n        alias_setup(&bar_item->alias,\n                    string_copy(key_value_pair.key),\n                    string_copy(key_value_pair.value));\n\n      free(tmp_name);\n    }\n    else if (bar_item->type == BAR_COMPONENT_SLIDER) {\n      char* tmp_name = string_copy(name.text);\n      struct key_value_pair key_value_pair = get_key_value_pair(tmp_name, ',');\n      if (!key_value_pair.key || !key_value_pair.value)\n        alias_setup(&bar_item->alias, token_to_string(name), NULL);\n      else\n        alias_setup(&bar_item->alias,\n                    string_copy(key_value_pair.key),\n                    string_copy(key_value_pair.value));\n\n      free(tmp_name);\n    }\n    else if (bar_item->type == BAR_COMPONENT_GROUP) {\n      struct token member = position;\n      bool first = true;\n      while (member.text && member.length > 0) {\n\n        uint32_t count = 0;\n        struct bar_item** bar_items = NULL;\n\n        if (member.length > 1 && member.text[0] == REGEX_DELIMITER\n            && member.text[member.length - 1] == REGEX_DELIMITER  ) {\n          bar_items = get_bar_items_for_regex(member, rsp, &count);\n        }\n        else {\n          int index = bar_manager_get_item_index_for_name(&g_bar_manager,\n                                                          member.text    );\n\n          if (index >= 0) {\n            bar_items = realloc(bar_items, sizeof(struct bar_item*));\n            bar_items[0] = g_bar_manager.bar_items[index];\n            count = 1;\n          }\n          else {\n            respond(rsp, \"[?] Add (Group) %s: Failed to add member '%s', item not found\\n\", bar_item->name, member.text);\n          }\n        }\n\n        if (bar_items && count > 0) {\n          for (int i = 0; i < count; i++) {\n            if (first) {\n              if (bar_items[i]->position == POSITION_POPUP) {\n                popup_add_item(&bar_items[i]->parent->popup, bar_item);\n                bar_item->position = POSITION_POPUP;\n              }\n              first = false;\n            }\n            group_add_member(bar_item->group, bar_items[i]);\n          }\n          free(bar_items);\n        } else if (first) {\n          bar_manager_remove_item(&g_bar_manager, bar_item);\n          break;\n        }\n        member = get_token(&message);\n      }\n    }\n  }\n\n  if (position.text[0] == POSITION_POPUP) {\n    char* pair = string_copy(position.text);\n    struct key_value_pair key_value_pair = get_key_value_pair(pair, '.');\n    if (key_value_pair.key && key_value_pair.value) {\n      int item_index_for_name = bar_manager_get_item_index_for_name(&g_bar_manager,\n                                                                    key_value_pair.value);\n      if (item_index_for_name < 0) {\n        respond(rsp,\n                \"[!] Add (Popup) %s: Item '%s' is not a valid popup host\\n\",\n                bar_item->name,\n                key_value_pair.value);\n\n        free(pair);\n        bar_manager_remove_item(&g_bar_manager, bar_item);\n        return;\n      }\n      struct bar_item* target_item = g_bar_manager.bar_items[item_index_for_name];\n      popup_add_item(&target_item->popup, bar_item);\n    }\n    free(pair);\n  }\n\n  bar_item_needs_update(bar_item);\n}\n\nstatic void handle_domain_default(FILE* rsp, struct token domain, char* message) {\n  bar_item_parse_set_message(&g_bar_manager.default_item, message, rsp);\n}\n\nstatic bool handle_domain_bar(FILE *rsp, struct token domain, char *message) {\n  struct token command  = get_token(&message);\n  bool needs_refresh = false;\n\n  if (token_equals(command, PROPERTY_MARGIN)) {\n    struct token token = get_token(&message);\n    ANIMATE(bar_manager_set_margin,\n            &g_bar_manager,\n            g_bar_manager.margin,\n            token_to_int(token)    );\n\n  } else if (token_equals(command, PROPERTY_YOFFSET)) {\n    struct token token = get_token(&message);\n    ANIMATE(bar_manager_set_y_offset,\n            &g_bar_manager,\n            g_bar_manager.background.y_offset,\n            token_to_int(token)      );\n\n  } else if (token_equals(command, PROPERTY_BLUR_RADIUS)) {\n    struct token token = get_token(&message);\n    ANIMATE(bar_manager_set_background_blur,\n            &g_bar_manager,\n            g_bar_manager.blur_radius,\n            token_to_int(token)             );\n\n  } else if (token_equals(command, PROPERTY_FONT_SMOOTHING)) {\n    struct token state = get_token(&message);\n    needs_refresh = bar_manager_set_font_smoothing(&g_bar_manager,\n                                                   evaluate_boolean_state(state,\n                                                                          g_bar_manager.font_smoothing));\n  } else if (token_equals(command, PROPERTY_SHADOW)) {\n    struct token state = get_token(&message);\n    needs_refresh = bar_manager_set_shadow(&g_bar_manager,\n                                           evaluate_boolean_state(state,\n                                                                  g_bar_manager.shadow));\n  } else if (token_equals(command, PROPERTY_NOTCH_WIDTH)) {\n    struct token token = get_token(&message);\n    ANIMATE(bar_manager_set_notch_width,\n            &g_bar_manager,\n            g_bar_manager.notch_width,\n            token_to_int(token)         );\n\n  } else if (token_equals(command, PROPERTY_NOTCH_OFFSET)) {\n    struct token token = get_token(&message);\n    ANIMATE(bar_manager_set_notch_offset,\n            &g_bar_manager,\n            g_bar_manager.notch_offset,\n            token_to_int(token)         );\n  } else if (token_equals(command, PROPERTY_NOTCH_DISPLAY_HEIGHT)) {\n    struct token token = get_token(&message);\n    ANIMATE(bar_manager_set_notch_display_height,\n            &g_bar_manager,\n            g_bar_manager.notch_display_height,\n            token_to_int(token)         );\n  } else if (token_equals(command, PROPERTY_HIDDEN)) {\n    struct token state = get_token(&message);\n    uint32_t adid = 0;\n    if (token_equals(state, \"current\")) {\n      adid = display_active_display_adid();\n\n      if (adid > 0 && adid <= g_bar_manager.bar_count)\n        needs_refresh = bar_manager_set_hidden(&g_bar_manager,\n                                               adid,\n                                               !g_bar_manager.bars[adid - 1]->hidden);\n      else\n        printf(\"No bar on display %u \\n\", adid);\n    } else needs_refresh = bar_manager_set_hidden(&g_bar_manager,\n                                                  adid,\n                                                  evaluate_boolean_state(state,\n                                                                         g_bar_manager.any_bar_hidden));\n  } else if (token_equals(command, PROPERTY_TOPMOST)) {\n    struct token token = get_token(&message);\n    if (token_equals(token, ARGUMENT_WINDOW)) {\n      needs_refresh = bar_manager_set_topmost(&g_bar_manager,\n                                              TOPMOST_LEVEL_WINDOW,\n                                              true                 );\n    } else {\n      needs_refresh = bar_manager_set_topmost(&g_bar_manager,\n                                              TOPMOST_LEVEL_ALL,\n                                              evaluate_boolean_state(token,\n                                                                     g_bar_manager.topmost));\n    }\n  } else if (token_equals(command, PROPERTY_STICKY)) {\n    struct token token = get_token(&message);\n    needs_refresh = bar_manager_set_sticky(&g_bar_manager,\n                                           evaluate_boolean_state(token,\n                                                                  g_bar_manager.sticky));\n  } else if (token_equals(command, PROPERTY_DISPLAY)) {\n    struct token display = get_token(&message);\n\n    uint32_t display_pattern = 0;\n    uint32_t count;\n    char** list = token_split(display, ',', &count);\n    if (list && count > 0) {\n      for (int i = 0; i < count; i++) {\n        if (strcmp(list[i], ARGUMENT_DISPLAY_ALL) == 0) {\n          display_pattern = DISPLAY_ALL_PATTERN;\n        } else if (strcmp(list[i], ARGUMENT_DISPLAY_MAIN) == 0) {\n          display_pattern = DISPLAY_MAIN_PATTERN;\n        }\n        else {\n          display_pattern |= 1 << (strtoul(list[i], NULL, 0) - 1);\n        }\n      }\n      free(list);\n    }\n    needs_refresh = bar_manager_set_displays(&g_bar_manager, display_pattern);\n  } else if (token_equals(command, PROPERTY_POSITION)) {\n    struct token position = get_token(&message);\n    if (position.length > 0)\n      needs_refresh = bar_manager_set_position(&g_bar_manager, position.text[0]);\n  } else if (token_equals(command, PROPERTY_CLIP)) {\n    respond(rsp, \"[!] Bar: Invalid property 'clip'\\n\");\n  } else if (token_equals(command, PROPERTY_HEIGHT)) {\n    struct token token = get_token(&message);\n    ANIMATE(bar_manager_set_bar_height,\n            &g_bar_manager,\n            g_bar_manager.background.bounds.size.height,\n            token_to_int(token)                         );\n  } else if (token_equals(command, PROPERTY_SHOW_IN_FULLSCREEN)) {\n      struct token token = get_token(&message);\n\n      needs_refresh = bar_manager_set_show_in_fullscreen(&g_bar_manager,\n          evaluate_boolean_state(token, g_bar_manager.show_in_fullscreen));\n  } else\n    needs_refresh = background_parse_sub_domain(&g_bar_manager.background, rsp, command, message);\n\n  return needs_refresh;\n}\n\nstatic char* reformat_batch_key_value_pair(struct token token) {\n  struct key_value_pair key_value_pair = get_key_value_pair(token.text, '=');\n  if (!key_value_pair.key) return NULL;\n  char* rbr_msg = malloc((strlen(key_value_pair.key) + (key_value_pair.value\n                                                        ? strlen(key_value_pair.value)\n                                                        : 0)\n                          + 3) * sizeof(char)                                         );\n\n  pack_key_value_pair(rbr_msg, &key_value_pair);\n  return rbr_msg;\n}\n\nstatic char* get_batch_line(char** message) {\n  char* cursor = *message;\n  bool end_of_batch = false;\n  while (true) {\n    if (*cursor == '\\0' && *(cursor + 1) == '\\0') {\n      end_of_batch = true;\n      break;\n    }\n\n    if (*cursor == '\\0' && *(cursor + 1) == '-')\n      break;\n\n    cursor++;\n  }\n  char* rbr_msg = malloc(sizeof(char) * (cursor - *message + 2));\n  memcpy(rbr_msg, *message, sizeof(char) * (cursor - *message + 1));\n  *(rbr_msg + (cursor - *message + 1)) = '\\0';\n  if (end_of_batch) *message = cursor;\n  else *message = cursor + 1;\n  return rbr_msg;\n}\n\nstatic void handle_domain_query(FILE* rsp, struct token domain, char* message) {\n  struct token token = get_token(&message);\n\n  if (token_equals(token, COMMAND_QUERY_DEFAULT_ITEMS)) {\n    print_all_menu_items(rsp);\n  } else if (token_equals(token, COMMAND_QUERY_ITEM)) {\n    struct token name  = get_token(&message);\n    int item_index_for_name = bar_manager_get_item_index_for_name(&g_bar_manager,\n                                                                  name.text      );\n    if (item_index_for_name < 0) {\n      respond(rsp, \"[!] Query: Item '%s' not found\\n\", name.text);\n      return;\n    }\n    bar_item_serialize(g_bar_manager.bar_items[item_index_for_name], rsp);\n  } else if (token_equals(token, COMMAND_QUERY_BAR)) {\n    bar_manager_serialize(&g_bar_manager, rsp);\n  } else if (token_equals(token, COMMAND_QUERY_DEFAULTS)) {\n    bar_item_serialize(&g_bar_manager.default_item, rsp);\n  } else if (token_equals(token, COMMAND_QUERY_EVENTS)) {\n    custom_events_serialize(&g_bar_manager.custom_events, rsp);\n  } else if (token_equals(token, COMMAND_QUERY_DISPLAYS)) {\n    display_serialize(rsp);\n  } else {\n    struct token name = token;\n    int item_index_for_name = bar_manager_get_item_index_for_name(&g_bar_manager,\n                                                                  name.text      );\n    if (item_index_for_name < 0) {\n      respond(rsp, \"[!] Query: Invalid query, or item '%s' not found \\n\", name.text);\n      return;\n    }\n    bar_item_serialize(g_bar_manager.bar_items[item_index_for_name], rsp);\n  }\n}\n\nstatic void handle_domain_remove(FILE* rsp, struct token domain, char* message) {\n  struct token name = get_token(&message);\n  uint32_t count = 0;\n  struct bar_item** bar_items = NULL;\n\n  if (name.length > 1 && name.text[0] == REGEX_DELIMITER\n      && name.text[name.length - 1] == REGEX_DELIMITER  ) {\n    bar_items = get_bar_items_for_regex(name, rsp, &count);\n  }\n  else {\n    int item_index_for_name = bar_manager_get_item_index_for_name(&g_bar_manager,\n                                                                  name.text      );\n    if (item_index_for_name < 0) {\n      respond(rsp, \"[!] Remove: Item '%s' not found\\n\", name.text);\n      return;\n    }\n    bar_items = realloc(bar_items, sizeof(struct bar_item*));\n    bar_items[0] = g_bar_manager.bar_items[item_index_for_name];\n    count = 1;\n  }\n  if (!bar_items || count == 0) return;\n\n  for (int i = 0; i < count; i++) {\n    bar_manager_remove_item(&g_bar_manager, bar_items[i]);\n  }\n\n  if (bar_items) free(bar_items);\n}\n\nstatic void handle_domain_move(FILE* rsp, struct token domain, char* message) {\n  struct token name = get_token(&message);\n  struct token direction = get_token(&message);\n  struct token reference = get_token(&message);\n\n  int item_index = bar_manager_get_item_index_for_name(&g_bar_manager, name.text);\n  int reference_item_index = bar_manager_get_item_index_for_name(&g_bar_manager, reference.text);\n  if (item_index < 0 || reference_item_index < 0) {\n      respond(rsp, \"[!] Move: Item '%s' or '%s' not found\\n\", name.text, reference.text);\n      return;\n  }\n\n  bar_manager_move_item(&g_bar_manager,\n                        g_bar_manager.bar_items[item_index],\n                        g_bar_manager.bar_items[reference_item_index],\n                        token_equals(direction, ARGUMENT_COMMON_VAL_BEFORE));\n\n  bar_item_needs_update(g_bar_manager.bar_items[item_index]);\n}\n\nstatic void handle_domain_order(FILE* rsp, struct token domain, char* message) {\n  struct bar_item* ordering[g_bar_manager.bar_item_count];\n  memset(ordering, 0, sizeof(struct bar_item*)*g_bar_manager.bar_item_count);\n\n  uint32_t count = 0;\n  struct token name = get_token(&message);\n  while (name.text && name.length > 0) {\n    int index = bar_manager_get_item_index_for_name(&g_bar_manager, name.text);\n    if (index < 0) {\n      respond(rsp, \"[!] Order: Item '%s' not found\\n\", name.text);\n      name = get_token(&message);\n      continue;\n    }\n    ordering[count] = g_bar_manager.bar_items[index];\n    count++;\n\n    name = get_token(&message);\n  }\n\n  bar_manager_sort(&g_bar_manager, ordering, count);\n  bar_manager_refresh(&g_bar_manager, false, false);\n}\n\nvoid handle_message_mach(struct mach_buffer* buffer) {\n  if (!buffer->message.descriptor.address) return;\n  char* message = buffer->message.descriptor.address;\n  char* response = NULL;\n  size_t length = 0;\n  FILE* rsp = open_memstream(&response, &length);\n  fprintf(rsp, \"\");\n\n  g_bar_manager.animator.interp_function = '\\0';\n  g_bar_manager.animator.duration = 0;\n  bar_manager_freeze(&g_bar_manager);\n  struct token command = get_token(&message);\n  bool bar_needs_refresh = false;\n\n  while (command.text && command.length > 0) {\n    if (token_equals(command, DOMAIN_SET)) {\n      struct token name = get_token(&message);\n      uint32_t count = 0;\n      struct bar_item** bar_items = NULL;\n\n      if (name.length > 1 && name.text[0] == REGEX_DELIMITER\n          && name.text[name.length - 1] == REGEX_DELIMITER  ) {\n        bar_items = get_bar_items_for_regex(name, rsp, &count);\n      }\n      else {\n        int item_index_for_name = bar_manager_get_item_index_for_name(&g_bar_manager, name.text);\n        if (item_index_for_name < 0) {\n          respond(rsp, \"[!] Set: Item not found '%s'\\n\", name.text);\n        } else {\n          bar_items = realloc(bar_items, sizeof(struct bar_item*));\n          bar_items[0] = g_bar_manager.bar_items[item_index_for_name];\n          count = 1;\n        }\n      }\n      if (!bar_items || count == 0) {\n        char* rest = get_batch_line(&message);\n        free(rest);\n      } else {\n        struct token token = get_token(&message);\n        while (token.text && token.length > 0) {\n          for (int i = 0; i < count; i++) {\n            struct token tmp = {string_copy(token.text), token.length};\n            char* rbr_msg = reformat_batch_key_value_pair(tmp);\n            free(tmp.text);\n            if (!rbr_msg) {\n              respond(rsp, \"[!] Set (%s): Expected <key>=<value> pair, but got: '%s'\\n\", bar_items[i]->name, token.text);\n              break;\n            }\n            bar_item_parse_set_message(bar_items[i], rbr_msg, rsp);\n            free(rbr_msg);\n          }\n          if (message && *message == '-') break;\n          token = get_token(&message);\n        }\n        free(bar_items);\n      }\n    } else if (token_equals(command, DOMAIN_DEFAULT)) {\n      struct token token = get_token(&message);\n      while (token.text && token.length > 0) {\n        char* rbr_msg = reformat_batch_key_value_pair(token);\n          if (!rbr_msg) {\n            respond(rsp, \"[!] Set (default): Expected <key>=<value> pair, but got: '%s'\\n\", token.text);\n            break;\n          }\n        handle_domain_default(rsp, command, rbr_msg);\n        free(rbr_msg);\n        if (message && *message == '-') break;\n        token = get_token(&message);\n      }\n    } else if (token_equals(command, DOMAIN_ANIMATE)) {\n      g_bar_manager.animator.interp_function = get_token(&message).text[0];\n      g_bar_manager.animator.duration = token_to_uint32t(get_token(&message));\n    } else if (token_equals(command, DOMAIN_BAR)) {\n      struct token token = get_token(&message);\n      while (token.text && token.length > 0) {\n        char* rbr_msg = reformat_batch_key_value_pair(token);\n        if (!rbr_msg) {\n          respond(rsp, \"[!] Bar: Expected <key>=<value> pair, but got: '%s'\\n\", token.text);\n          break;\n        }\n        bar_needs_refresh |= handle_domain_bar(rsp, command, rbr_msg);\n        free(rbr_msg);\n        if (message && *message == '-') break;\n        token = get_token(&message);\n      }\n    } else if (token_equals(command, DOMAIN_ADD)) {\n      char* rbr_msg = get_batch_line(&message);\n      handle_domain_add(rsp, command, rbr_msg);\n      free(rbr_msg);\n    } else if (token_equals(command, DOMAIN_CLONE)) {\n      char* rbr_msg = get_batch_line(&message);\n      handle_domain_clone(rsp, command, rbr_msg);\n      free(rbr_msg);\n    } else if (token_equals(command, DOMAIN_SUBSCRIBE)) {\n      char* rbr_msg = get_batch_line(&message);\n      handle_domain_subscribe(rsp, command, rbr_msg);\n      free(rbr_msg);\n    } else if (token_equals(command, DOMAIN_PUSH)) {\n      char* rbr_msg = get_batch_line(&message);\n      handle_domain_push(rsp, command, rbr_msg);\n      free(rbr_msg);\n    } else if (token_equals(command, DOMAIN_UPDATE)) {\n      bar_manager_update(&g_bar_manager, true);\n      bar_needs_refresh = true;\n    } else if (token_equals(command, DOMAIN_TRIGGER)) {\n      char* rbr_msg = get_batch_line(&message);\n      handle_domain_trigger(rsp, command, rbr_msg);\n      free(rbr_msg);\n    } else if (token_equals(command, DOMAIN_QUERY)) {\n      char* rbr_msg = get_batch_line(&message);\n      handle_domain_query(rsp, command, rbr_msg);\n      free(rbr_msg);\n    } else if (token_equals(command, DOMAIN_REORDER)) {\n      char* rbr_msg = get_batch_line(&message);\n      handle_domain_order(rsp, command, rbr_msg);\n      free(rbr_msg);\n    } else if (token_equals(command, DOMAIN_MOVE)) {\n      char* rbr_msg = get_batch_line(&message);\n      handle_domain_move(rsp, command, rbr_msg);\n      free(rbr_msg);\n    } else if (token_equals(command, DOMAIN_REMOVE)) {\n      char* rbr_msg = get_batch_line(&message);\n      handle_domain_remove(rsp, command, rbr_msg);\n      bar_needs_refresh = true;\n      free(rbr_msg);\n    } else if (token_equals(command, DOMAIN_RENAME)) {\n      char* rbr_msg = get_batch_line(&message);\n      handle_domain_rename(rsp, command, rbr_msg);\n      free(rbr_msg);\n    } else if (token_equals(command, DOMAIN_EXIT)) {\n      bar_manager_destroy(&g_bar_manager);\n      exit(0);\n    } else if (token_equals(command, DOMAIN_HOTLOAD)) {\n      struct token token = get_token(&message);\n      hotload_set_state(evaluate_boolean_state(token, hotload_get_state()));\n    } else if (token_equals(command, DOMAIN_ADD_FONT)) {\n      struct token token = get_token(&message);\n      font_register(token_to_string(token));\n    } else if (token_equals(command, DOMAIN_RELOAD)) {\n      char* rbr_msg = get_batch_line(&message);\n      char* cur = rbr_msg;\n      struct token token = get_token(&cur);\n\n      bool reload = false;\n      if (token.length > 0 && token.text) {\n        if (!set_config_file_path(token.text)) {\n          respond(rsp, \"[?] Reload: Invalid config path '%s'\\n\", token.text);\n        } else reload = true;\n      } else reload = true;\n      free(rbr_msg);\n\n      if (reload) {\n        struct event event = { NULL, HOTLOAD };\n        event_post(&event);\n      }\n    } else {\n      char* rbr_msg = get_batch_line(&message);\n      respond(rsp, \"[!] Unknown domain '%s'\\n\", command.text);\n      free(rbr_msg);\n    }\n    command = get_token(&message);\n  }\n\n  if (bar_needs_refresh) {\n    if (g_bar_manager.bar_needs_resize) bar_manager_resize(&g_bar_manager);\n\n    g_bar_manager.bar_needs_update = true;\n  }\n\n  animator_lock(&g_bar_manager.animator);\n  bar_manager_unfreeze(&g_bar_manager);\n  bar_manager_refresh(&g_bar_manager, false, false);\n\n  if (rsp) fclose(rsp);\n\n  response[length] = '\\0';\n  mach_send_message(buffer->message.header.msgh_remote_port, response,\n                                                             length + 1,\n                                                             false      );\n  if (response) free(response);\n}\n\nMACH_HANDLER(mach_message_handler) {\n  struct event event = { message, MACH_MESSAGE };\n  event_post(&event);\n}\n"
  },
  {
    "path": "src/message.h",
    "content": "#pragma once\n\n#include <regex.h>\n#include \"alias.h\"\n#include \"background.h\"\n#include \"bar_item.h\"\n#include \"bar_manager.h\"\n#include \"display.h\"\n#include \"group.h\"\n#include \"slider.h\"\n#include \"mach.h\"\n#include \"event.h\"\n#include \"misc/helpers.h\"\n#include \"misc/defines.h\"\n\n\nMACH_HANDLER(mach_message_handler);\nvoid handle_message_mach(struct mach_buffer* buffer);\n"
  },
  {
    "path": "src/misc/defines.h",
    "content": "#pragma once\n\n#define DOMAIN_ADD                             \"--add\"\n#define COMMAND_ADD_ITEM                       \"item\"\n#define COMMAND_ADD_COMPONENT                  \"component\"\n#define COMMAND_ADD_EVENT                      \"event\"\n\n#define DOMAIN_UPDATE                          \"--update\"\n\n#define DOMAIN_PUSH                            \"--push\"\n\n#define DOMAIN_TRIGGER                         \"--trigger\"\n\n#define DOMAIN_DEFAULT                         \"--default\"\n#define COMMAND_DEFAULT_RESET                  \"reset\"\n\n#define DOMAIN_CLONE                           \"--clone\"\n\n#define DOMAIN_RENAME                          \"--rename\"\n\n#define DOMAIN_REORDER                         \"--reorder\"\n\n#define DOMAIN_REMOVE                          \"--remove\"\n\n#define DOMAIN_MOVE                            \"--move\"\n\n#define DOMAIN_SET                             \"--set\"\n\n#define DOMAIN_ANIMATE                         \"--animate\"\n\n#define DOMAIN_EXIT                            \"--exit\"\n\n#define DOMAIN_HOTLOAD                         \"--hotload\"\n#define DOMAIN_RELOAD                          \"--reload\"\n#define DOMAIN_ADD_FONT                        \"--load-font\"\n\n#define SUB_DOMAIN_ICON                        \"icon\"\n#define SUB_DOMAIN_LABEL                       \"label\"\n#define SUB_DOMAIN_BACKGROUND                  \"background\"\n#define SUB_DOMAIN_GRAPH                       \"graph\"\n#define SUB_DOMAIN_ALIAS                       \"alias\"\n#define SUB_DOMAIN_POPUP                       \"popup\"\n#define SUB_DOMAIN_SHADOW                      \"shadow\"\n#define SUB_DOMAIN_IMAGE                       \"image\"\n#define SUB_DOMAIN_KNOB                        \"knob\"\n#define SUB_DOMAIN_SLIDER                      \"slider\"\n#define SUB_DOMAIN_FONT                        \"font\"\n#define SUB_DOMAIN_COLOR                       \"color\"\n#define SUB_DOMAIN_BORDER_COLOR                \"border_color\"\n#define SUB_DOMAIN_HIGHLIGHT_COLOR             \"highlight_color\"\n#define SUB_DOMAIN_FILL_COLOR                  \"fill_color\"\n\n#define PROPERTY_FONT                          \"font\"\n#define PROPERTY_COLOR                         \"color\"\n#define PROPERTY_HIGHLIGHT                     \"highlight\"\n#define PROPERTY_HIGHLIGHT_COLOR               \"highlight_color\"\n#define PROPERTY_PADDING_LEFT                  \"padding_left\"\n#define PROPERTY_PADDING_RIGHT                 \"padding_right\"\n#define PROPERTY_HEIGHT                        \"height\"\n#define PROPERTY_BORDER_COLOR                  \"border_color\"\n#define PROPERTY_BORDER_WIDTH                  \"border_width\"\n#define PROPERTY_CORNER_RADIUS                 \"corner_radius\"\n#define PROPERTY_FILL_COLOR                    \"fill_color\"\n#define PROPERTY_LINE_WIDTH                    \"line_width\"\n#define PROPERTY_BLUR_RADIUS                   \"blur_radius\"\n#define PROPERTY_DRAWING                       \"drawing\"\n#define PROPERTY_CLIP                          \"clip\"\n#define PROPERTY_DISTANCE                      \"distance\"\n#define PROPERTY_ANGLE                         \"angle\"\n#define PROPERTY_SCALE                         \"scale\"\n#define PROPERTY_STRING                        \"string\"\n#define PROPERTY_SCROLL_TEXTS                  \"scroll_texts\"\n#define PROPERTY_SCROLL_DURATION               \"scroll_duration\"\n\n#define PROPERTY_COLOR_HEX                     \"hex\"\n#define PROPERTY_COLOR_ALPHA                   \"alpha\"\n#define PROPERTY_COLOR_RED                     \"red\"\n#define PROPERTY_COLOR_GREEN                   \"green\"\n#define PROPERTY_COLOR_BLUE                    \"blue\"\n\n#define PROPERTY_FONT_FAMILY                   \"family\"\n#define PROPERTY_FONT_STYLE                    \"style\"\n#define PROPERTY_FONT_SIZE                     \"size\"\n#define PROPERTY_FONT_FEATURES                 \"features\"\n\n#define PROPERTY_UPDATES                       \"updates\"\n#define PROPERTY_POSITION                      \"position\"\n#define PROPERTY_ASSOCIATED_DISPLAY            \"associated_display\"\n#define PROPERTY_ASSOCIATED_SPACE              \"associated_space\"\n#define PROPERTY_UPDATE_FREQ                   \"update_freq\"\n#define PROPERTY_SCRIPT                        \"script\"\n#define PROPERTY_CLICK_SCRIPT                  \"click_script\"\n#define PROPERTY_ICON                          \"icon\"\n#define PROPERTY_XOFFSET                       \"x_offset\"\n#define PROPERTY_YOFFSET                       \"y_offset\"\n#define PROPERTY_WIDTH                         \"width\"\n#define PROPERTY_LABEL                         \"label\"\n#define PROPERTY_CACHE_SCRIPTS                 \"cache_scripts\"\n#define PROPERTY_LAZY                          \"lazy\"\n#define PROPERTY_IGNORE_ASSOCIATION            \"ignore_association\"\n#define PROPERTY_EVENT_PORT                    \"mach_helper\"\n#define PROPERTY_PERCENTAGE                    \"percentage\"\n#define PROPERTY_MAX_CHARS                     \"max_chars\"\n\n#define DOMAIN_BAR                             \"--bar\"\n#define PROPERTY_POSITION                      \"position\"\n#define PROPERTY_MARGIN                        \"margin\"\n#define PROPERTY_DISPLAY                       \"display\"\n#define PROPERTY_SPACE                         \"space\"\n#define PROPERTY_TOPMOST                       \"topmost\"\n#define PROPERTY_STICKY                        \"sticky\"\n#define PROPERTY_SHOW_IN_FULLSCREEN            \"show_in_fullscreen\"\n#define PROPERTY_HIDDEN                        \"hidden\"\n#define PROPERTY_FONT_SMOOTHING                \"font_smoothing\"\n#define PROPERTY_SHADOW                        \"shadow\"\n#define PROPERTY_ALIGN                         \"align\"\n#define PROPERTY_NOTCH_WIDTH                   \"notch_width\"\n#define PROPERTY_NOTCH_OFFSET                  \"notch_offset\"\n#define PROPERTY_NOTCH_DISPLAY_HEIGHT          \"notch_display_height\"\n#define PROPERTY_HORIZONTAL                    \"horizontal\"\n\n#define DOMAIN_SUBSCRIBE                       \"--subscribe\"\n#define COMMAND_SUBSCRIBE_FRONT_APP_SWITCHED   \"front_app_switched\"\n#define COMMAND_SUBSCRIBE_SPACE_CHANGE         \"space_change\"\n#define COMMAND_SUBSCRIBE_DISPLAY_CHANGE       \"display_change\"\n#define COMMAND_SUBSCRIBE_SYSTEM_WOKE          \"system_woke\"\n#define COMMAND_SUBSCRIBE_SYSTEM_WILL_SLEEP    \"system_will_sleep\"\n#define COMMAND_SUBSCRIBE_VOLUME_CHANGE        \"volume_change\"\n#define COMMAND_SUBSCRIBE_WIFI_CHANGE          \"wifi_change\"\n#define COMMAND_SUBSCRIBE_BRIGHTNESS_CHANGE    \"brightness_change\"\n#define COMMAND_SUBSCRIBE_POWER_SOURCE_CHANGE  \"power_source_change\"\n#define COMMAND_SUBSCRIBE_MEDIA_CHANGE         \"media_change\"\n#define COMMAND_SUBSCRIBE_MOUSE_ENTERED        \"mouse.entered\"\n#define COMMAND_SUBSCRIBE_MOUSE_EXITED         \"mouse.exited\"\n#define COMMAND_SUBSCRIBE_MOUSE_CLICKED        \"mouse.clicked\"\n#define COMMAND_SUBSCRIBE_MOUSE_SCROLLED       \"mouse.scrolled\"\n#define COMMAND_SUBSCRIBE_MOUSE_ENTERED_GLOBAL \"mouse.entered.global\"\n#define COMMAND_SUBSCRIBE_MOUSE_EXITED_GLOBAL  \"mouse.exited.global\"\n#define COMMAND_SUBSCRIBE_MOUSE_SCROLLED_GLOBAL \"mouse.scrolled.global\"\n#define COMMAND_SUBSCRIBE_SPACE_WINDOWS_CHANGE \"space_windows_change\"\n\n#define DOMAIN_QUERY                           \"--query\"\n#define COMMAND_QUERY_DEFAULT_ITEMS            \"default_menu_items\"\n#define COMMAND_QUERY_ITEM                     \"item\"\n#define COMMAND_QUERY_DEFAULTS                 \"defaults\"\n#define COMMAND_QUERY_BAR                      \"bar\"\n#define COMMAND_QUERY_EVENTS                   \"events\"\n#define COMMAND_QUERY_DISPLAYS                 \"displays\"\n\n#define ARGUMENT_COMMON_VAL_ON                 \"on\"\n#define ARGUMENT_COMMON_VAL_NOT_OFF            \"!off\"\n#define ARGUMENT_COMMON_VAL_TRUE               \"true\"\n#define ARGUMENT_COMMON_VAL_NOT_FALSE          \"!false\"\n#define ARGUMENT_COMMON_VAL_ONE                \"1\"\n#define ARGUMENT_COMMON_VAL_NOT_ZERO           \"!0\"\n#define ARGUMENT_COMMON_VAL_YES                \"yes\"\n#define ARGUMENT_COMMON_VAL_NOT_NO             \"!no\"\n#define ARGUMENT_COMMON_VAL_OFF                \"off\"\n#define ARGUMENT_COMMON_VAL_NOT_ON             \"!on\"\n#define ARGUMENT_COMMON_VAL_FALSE              \"false\"\n#define ARGUMENT_COMMON_VAL_NOT_TRUE           \"!true\"\n#define ARGUMENT_COMMON_VAL_ZERO               \"0\"\n#define ARGUMENT_COMMON_VAL_NOT_ONE            \"!1\"\n#define ARGUMENT_COMMON_VAL_NO                 \"no\"\n#define ARGUMENT_COMMON_VAL_NOT_YES            \"!yes\"\n#define ARGUMENT_COMMON_VAL_TOGGLE             \"toggle\"\n#define ARGUMENT_COMMON_VAL_BEFORE             \"before\"\n#define ARGUMENT_COMMON_VAL_AFTER              \"after\"\n\n#define ARGUMENT_DISPLAY_MAIN                  \"main\"\n#define ARGUMENT_DISPLAY_ALL                   \"all\"\n\n#define ARGUMENT_UPDATES_WHEN_SHOWN            \"when_shown\"\n#define ARGUMENT_DYNAMIC                       \"dynamic\"\n\n#define ARGUMENT_WINDOW                        \"window\"\n\n#define POSITION_TOP          't'\n#define POSITION_BOTTOM       'b'\n#define POSITION_LEFT         'l'\n#define POSITION_RIGHT        'r'\n#define POSITION_CENTER       'c'\n#define POSITION_POPUP        'p'\n#define POSITION_CENTER_LEFT  'q'\n#define POSITION_CENTER_RIGHT 'e'\n\n#define TYPE_GRAPH  \"graph\"\n#define TYPE_SPACE  \"space\"\n#define TYPE_ALIAS  \"alias\"\n#define TYPE_GROUP  \"bracket\"\n#define TYPE_SLIDER \"slider\"\n#define TYPE_ITEM   \"item\"\n\n#define REGEX_DELIMITER '/'\n"
  },
  {
    "path": "src/misc/env_vars.h",
    "content": "#pragma once\n#include <stdint.h>\n#include <string.h>\n#include <stdlib.h>\n\nstruct key_value_pair {\n  char* key;\n  char* value;\n};\n\nstruct env_vars {\n  uint32_t count;\n  struct key_value_pair** vars;\n};\n\nstatic inline void env_vars_init(struct env_vars* env_vars) {\n  env_vars->vars = NULL;\n  env_vars->count = 0;\n}\n\nstatic inline void env_vars_unset(struct env_vars* env_vars, char* key) {\n  struct key_value_pair* key_value_pair = NULL;\n  for (int i = 0; i < env_vars->count; i++) {\n    if (strcmp(env_vars->vars[i]->key, key) == 0)\n      key_value_pair = env_vars->vars[i];\n  }\n\n  if (key_value_pair == NULL) return;\n\n  if (env_vars->count == 1) {\n    free(env_vars->vars);\n    env_vars->vars = NULL;\n    env_vars->count = 0;\n  } else {\n    struct key_value_pair* tmp[env_vars->count - 1];\n    int count = 0;\n    for (int i = 0; i < env_vars->count; i++) {\n      if (env_vars->vars[i] == key_value_pair) continue;\n      tmp[count++] = env_vars->vars[i];\n    }\n    env_vars->count--;\n    env_vars->vars = realloc(env_vars->vars,\n                             sizeof(struct key_value_pair*)*env_vars->count);\n\n    memcpy(env_vars->vars,\n           tmp,\n           sizeof(struct key_value_pair*)*env_vars->count);\n  }\n\n  if (key_value_pair->key) free(key_value_pair->key);\n  if (key_value_pair->value) free(key_value_pair->value);\n  free(key_value_pair);\n}\n\nstatic inline void env_vars_set(struct env_vars* env_vars, char* key, char* value) {\n  env_vars_unset(env_vars, key);\n\n  env_vars->count++;\n  env_vars->vars = realloc(env_vars->vars,\n                           env_vars->count * sizeof(struct key_value_pair*));\n\n  env_vars->vars[env_vars->count - 1] = malloc(sizeof(struct key_value_pair));\n  env_vars->vars[env_vars->count - 1]->key = key;\n  env_vars->vars[env_vars->count - 1]->value = value;\n}\n\nstatic inline char* env_vars_get_value_for_key(struct env_vars* env_vars, char* key) {\n  for (int i = 0; i < env_vars->count; i++) {\n    if (strcmp(env_vars->vars[i]->key, key) == 0)\n      return env_vars->vars[i]->value;\n  }\n  return NULL;\n}\n\nstatic inline char* env_vars_copy_serialized_representation(struct env_vars* env_vars, uint32_t* len) {\n  uint32_t length = 0;\n  for (int i = 0; i < env_vars->count; i++) {\n    length += env_vars->vars[i]->key\n              ? strlen(env_vars->vars[i]->key) + 1\n              : 1;\n\n    length += env_vars->vars[i]->value\n              ? strlen(env_vars->vars[i]->value) + 1\n              : 1;\n  }\n\n  uint32_t caret = 0;\n  char* seri = (char*)malloc(++length);\n  for (int i = 0; i < env_vars->count; i++) {\n    if (env_vars->vars[i]->key) {\n      uint32_t len = strlen(env_vars->vars[i]->key) + 1;\n      memcpy(seri + caret,\n             env_vars->vars[i]->key,\n             len                    );\n\n      caret += len;\n    } else {\n      seri[caret++] = '\\0';\n    }\n\n    if (env_vars->vars[i]->value) {\n      uint32_t len = strlen(env_vars->vars[i]->value) + 1;\n      memcpy(seri + caret,\n             env_vars->vars[i]->value,\n             len                      );\n\n      caret += len;\n    } else {\n      seri[caret++] = '\\0';\n    }\n  }\n  seri[caret++] = '\\0';\n  assert(caret == length);\n  *len = length;\n  return seri;\n}\n\nstatic inline void env_vars_destroy(struct env_vars* env_vars) {\n  for (int i = 0; i < env_vars->count; i++) {\n    if (env_vars->vars[i]->key) free(env_vars->vars[i]->key);\n    if (env_vars->vars[i]->value) free(env_vars->vars[i]->value);\n    free(env_vars->vars[i]);\n  }\n  free(env_vars->vars);\n}\n"
  },
  {
    "path": "src/misc/extern.h",
    "content": "#include <CoreGraphics/CoreGraphics.h>\n\nextern CGError DisplayServicesRegisterForBrightnessChangeNotifications(uint32_t did, uint32_t passthrough, void* callback);\nextern CGError DisplayServicesRegisterForAmbientLightCompensationNotifications(uint32_t did, uint32_t passthrough, void* callback);\n\nextern CGError DisplayServicesUnregisterForBrightnessChangeNotifications(uint32_t did, uint32_t passthrough);\nextern CGError DisplayServicesUnregisterForAmbientLightCompensationNotifications(uint32_t did, uint32_t passthrough);\n\nextern CGError DisplayServicesGetBrightness(uint32_t did, float* brightness);\nextern CGError DisplayServicesCanChangeBrightness(uint32_t did);\nextern CGError DisplayServicesAmbientLightCompensationEnabled(uint32_t did, bool* out);\n\nextern CFArrayRef SLSCopyManagedDisplaySpaces(int cid);\nextern uint32_t SLSGetActiveSpace(int cid);\nextern CFStringRef SLSCopyManagedDisplayForSpace(int cid, uint64_t sid);\nextern CFArrayRef SLSHWCaptureSpace(int64_t cid, int64_t sid, int64_t flags);\n\nextern CGError SLSGetWindowOwner(int cid, uint32_t wid, int* out_cid);\nextern CGError SLSConnectionGetPID(int cid, pid_t *pid);\nextern CFArrayRef SLSCopyWindowsWithOptionsAndTags(int cid, uint32_t owner, CFArrayRef spaces, uint32_t options, uint64_t *set_tags, uint64_t *clear_tags);\nextern CFTypeRef SLSWindowQueryWindows(int cid, CFArrayRef windows, uint32_t options);\nextern CFTypeRef SLSWindowQueryResultCopyWindows(CFTypeRef window_query);\nextern int SLSWindowIteratorGetCount(CFTypeRef iterator);\nextern bool SLSWindowIteratorAdvance(CFTypeRef iterator);\nextern uint32_t SLSWindowIteratorGetParentID(CFTypeRef iterator);\nextern uint32_t SLSWindowIteratorGetWindowID(CFTypeRef iterator);\nextern uint64_t SLSWindowIteratorGetTags(CFTypeRef iterator);\nextern uint64_t SLSWindowIteratorGetAttributes(CFTypeRef iterator);\nextern CGError SLSRegisterNotifyProc(void* callback, uint32_t event, void* context);\nextern CGError SLSRequestNotificationsForWindows(int cid, uint32_t* wid_list, uint32_t list_count);\n\nextern CFUUIDRef CGDisplayCreateUUIDFromDisplayID(uint32_t did);\nextern CFArrayRef SLSCopyManagedDisplays(int cid);\nextern uint64_t SLSManagedDisplayGetCurrentSpace(int cid, CFStringRef uuid);\n\nextern CFStringRef SLSCopyBestManagedDisplayForRect(int cid, CGRect rect);\nextern CGError SLSGetCurrentCursorLocation(int cid, CGPoint *point);\nextern CFStringRef SLSCopyActiveMenuBarDisplayIdentifier(int cid);\nextern CGError SLSGetMenuBarAutohideEnabled(int cid, int *enabled);\nextern CGError SLSGetRevealedMenuBarBounds(CGRect *rect, int cid, uint64_t sid);\nextern CFStringRef SLSCopyBestManagedDisplayForPoint(int cid, CGPoint point);\nextern CGError SLSSetMenuBarVisibilityOverrideOnDisplay(int cid, int did, bool override);\nextern CGError SLSSetMenuBarAutohideEnabled(int cid, bool enabled);\nextern CGError SLSFlushWindowContentRegion(int cid, uint32_t wid, void* dirty);\nextern CFTypeRef SLSTransactionCreate(int cid);\nextern CGError SLSTransactionOrderWindow(CFTypeRef transaction, uint32_t wid, int mode, uint32_t relativeToWID);\nextern CGError SLSTransactionSetWindowLevel(CFTypeRef transaction, uint32_t wid, int level);\nextern CGError SLSTransactionSetWindowShape(CFTypeRef transaction, uint32_t wid, float x_offset, float y_offset, CFTypeRef shape);\nextern CGError SLSTransactionMoveWindowWithGroup(CFTypeRef transaction, uint32_t wid, CGPoint point);\nextern CGError SLSTransactionCommitUsingMethod(CFTypeRef transaction, uint32_t method);\nextern CGError SLSTransactionCommit(CFTypeRef transaction, uint32_t async);\n\nextern CFTypeRef CGRegionCreateEmptyRegion(void);\nextern CGError SLSDisableUpdate(int cid);\nextern CGError SLSReenableUpdate(int cid);\nextern CGError SLSNewWindowWithOpaqueShapeAndContext(int cid, int type, CFTypeRef region, CFTypeRef opaque_shape, int options, uint64_t *tags, float x, float y, int tag_size, uint32_t *wid, void *context);\nextern CGError SLSNewWindow(int cid, int type, float x, float y, CFTypeRef region, uint64_t *wid);\nextern CGError SLSReleaseWindow(int cid, uint32_t wid);\nextern CGError SLSSetWindowTags(int cid, uint32_t wid, uint64_t* tags, int tag_size);\nextern CGError SLSClearWindowTags(int cid, uint32_t wid, uint64_t* tags, int tag_size);\nextern CGError SLSSetWindowShape(int cid, uint32_t wid, float x_offset, float y_offset, CFTypeRef shape);\nextern CGError SLSSetWindowOpaqueShape(int cid, uint32_t wid, float x_offset, float y_offset, CFTypeRef region);\nextern CGError SLSSetWindowResolution(int cid, uint32_t wid, double res);\nextern CGError SLSSetWindowOpacity(int cid, uint32_t wid, bool isOpaque);\nextern CGError SLSSetWindowAlpha(int cid, uint32_t wid, float alpha);\nextern CGError SLSSetWindowBackgroundBlurRadius(int cid, uint32_t wid, uint32_t radius);\nextern CGError SLSOrderWindow(int cid, uint32_t wid, int mode, uint32_t relativeToWID);\nextern CGError SLSSetWindowLevel(int cid, uint32_t wid, int level);\nextern CGContextRef SLWindowContextCreate(int cid, uint32_t wid, CFDictionaryRef options);\nextern CGError CGSNewRegionWithRect(CGRect *rect, CFTypeRef *outRegion);\nextern CGError SLSAddActivationRegion(uint32_t cid, uint32_t wid, CFTypeRef region);\nextern CGError SLSAddTrackingRect(uint32_t cid, uint32_t wid, CGRect rect);\nextern CGError SLSClearActivationRegion(uint32_t cid, uint32_t wid);\nextern CGError SLSRemoveAllTrackingAreas(uint32_t cid, uint32_t wid);\nextern CGError SLSMoveWindow(int cid, uint32_t wid, CGPoint* point);\nextern CGError SLSWindowSetShadowProperties(uint32_t wid, CFDictionaryRef properties);\nextern CGError SLSAddWindowToWindowOrderingGroup(int cid, uint32_t parent_wid, uint32_t child_wid, int order);\nextern CGError SLSRemoveFromOrderingGroup(int cid, uint32_t wid);\nextern CGError SLSReassociateWindowsSpacesByGeometry(int cid, CFArrayRef wids);\nextern CGError SLSMoveWindowsToManagedSpace(int cid, CFArrayRef window_list, uint64_t sid);\nextern CGError SLSMoveWindowWithGroup(int cid, uint32_t wid, CGPoint* point);\n\nextern void SLSCaptureWindowsContentsToRectWithOptions(uint32_t cid, uint64_t* wid, bool meh, CGRect bounds, uint32_t flags, CGImageRef* image);\nextern int SLSGetScreenRectForWindow(uint32_t cid, uint32_t wid, CGRect* out);\n\nextern int SLSSpaceGetType(int cid, uint64_t sid);\n\nextern CGError SLSAddSurface(int cid, uint32_t wid, uint32_t* outSID);\nextern CGError SLSRemoveSurface(int cid, uint32_t wid, uint32_t sid);\nextern CGError SLSBindSurface(int cid, uint32_t wid, uint32_t sid, int param1, int param2, unsigned int context_id);\nextern CGError SLSSetSurfaceBounds(int cid, uint32_t wid, uint32_t sid, CGRect bounds);\nextern CGError SLSSetSurfaceOpacity(int cid, uint32_t wid, uint32_t sid, bool opaque);\nextern CGError SLSOrderSurface(int cid, uint32_t wid, uint32_t surface, int mode, uint32_t other_surface);\nextern CGError SLSSetSurfaceResolution(int cid, uint32_t wid, uint32_t sid, CGFloat scale);\nextern CGError SLSFlushSurface(int cid, uint32_t wid, uint32_t surface, int param);\nextern CGError SLSSetSurfaceColorSpace(int cid, uint32_t wid, uint32_t surface, CGColorSpaceRef color_space);\n\nextern int SLSSpaceCreate(int cid, int one, int zero);\nextern CGError SLSSpaceSetAbsoluteLevel(int cid, int sid, int level);\nextern CGError SLSShowSpaces(int cid, CFArrayRef space_list);\nextern CGError SLSHideSpaces(int cid, CFArrayRef space_list);\nextern CGError SLSSpaceAddWindowsAndRemoveFromSpaces(int cid, int sid, CFArrayRef array, int seven);\n\n\n"
  },
  {
    "path": "src/misc/help.h",
    "content": "static const char help_str[] = {\n  \"Usage: %s [options]\\n\\n\"\n  \"Startup: \\n\"\n  \"  -c, --config CONFIGFILE\\tRead CONFIGFILE as the configuration file\\n\"\n  \"                         \\tDefault CONFIGFILE is ~/.config/sketchybar/sketchybarrc\\n\\n\"\n  \"Set global bar properties, see https://felixkratz.github.io/SketchyBar/config/bar\\n\"\n  \"      --bar <setting>=<value> ... <setting>=<value>\\n\\n\"\n  \"Items and their properties, see https://felixkratz.github.io/SketchyBar/config/items\\n\"\n  \"      --add item <name> <position>\\tAdd item to bar\\n\"\n  \"      --set <name> <property>=<value> ... <property>=<value>\\n\"\n  \"                                  \\tChange item properties\\n\"\n  \"      --default <property>=<value> ... <property>=<value>\\n\"\n  \"                                  \\tChange default properties for new items\\n\"\n  \"      --set <name> popup.<popup_property>=<value>\\n\"\n  \"                                  \\tConfigure item popup menu\\n\"\n  \"                                  \\tSee https://felixkratz.github.io/SketchyBar/config/popups\\n\"\n  \"      --reorder <name> ... <name> \\tReorder items\\n\"\n  \"      --move <name> before <reference name>\\n\"\n  \"      --move <name> after <reference name>\\n\"\n  \"                                  \\tMove item relative to reference item\\n\"\n  \"      --clone <parent name> <name> [optional: before/after]\\n\"\n  \"                                  \\tClone parent to create new item\\n\"\n  \"      --rename <old name> <new name>\\tRename item\\n\"\n  \"      --remove <name>             \\tRemove item\\n\\n\"\n  \"Special components, see https://felixkratz.github.io/SketchyBar/config/components\\n\"\n  \"      --add graph <name> <position> <width in points>\\n\"\n  \"                                  \\tAdd graph component\\n\"\n  \"      --push <name> <data point> ... <data point>\\n\"\n  \"                                  \\tPush data points to a graph\\n\"\n  \"      --add space <name> <position>\\tAdd space component\\n\"\n  \"      --add bracket <name> <member name> ... <member name>\\n\"\n  \"                                  \\tAdd bracket component\\n\"\n  \"      --add alias <application_name> <position>\\n\"\n  \"                                  \\tAdd alias component\\n\"\n  \"      --add slider <name> <position> <width>\\n\"\n  \"                                  \\tAdd slider component\\n\\n\"\n  \"Events and Scripting, see https://felixkratz.github.io/SketchyBar/config/events\\n\"\n  \"      --subscribe <name> <event> ... <event>\\n\"\n  \"                                  \\tSubscribe to events\\n\"\n  \"      --add event <name> [optional: <NSDistributedNotificationName>]\\n\"\n  \"                                  \\tCreate custom event\\n\"\n  \"      --trigger <event> [optional: <envvar>=<value> ... <envvar>=<value>]\\n\"\n  \"                                  \\tTrigger custom event\\n\\n\"\n  \"Querying information, see https://felixkratz.github.io/SketchyBar/config/querying\\n\"\n  \"      --query bar               \\tQuery bar properties\\n\"\n  \"      --query <name>            \\tQuery item properties\\n\"\n  \"      --query defaults          \\tQuery default properties\\n\"\n  \"      --query events            \\tQuery events\\n\"\n  \"      --query default_menu_items\\tQuery names of available items for aliases\\n\\n\"\n  \"Animations, see https://felixkratz.github.io/SketchyBar/config/animations\\n\"\n  \"      --animate <linear|quadratic|tanh|sin|exp|circ> <duration> \\\\\\n\"\n  \"                --bar <property=value> ... <property=value>\\\\\\n\"\n  \"                --set <name> <property=value> ... <property=value>\\n\"\n  \"                         \\tAnimate from given source to target property values\\n\\n\"\n  \"Reloading the config\\n\"\n  \"      --hotload <boolean>        \\tEnable or disable the config hotloader\\n\"\n  \"      --reload [optional: <path>]\\tReload the current or the given config\\n\\n\"\n};\n"
  },
  {
    "path": "src/misc/helpers.h",
    "content": "#pragma once\n#include <CoreFoundation/CoreFoundation.h>\n#include <CoreGraphics/CoreGraphics.h>\n#include <ApplicationServices/ApplicationServices.h>\n#include <sys/mman.h>\n#include <sys/stat.h>\n#include <time.h>\n#include \"env_vars.h\"\n#include \"defines.h\"\n#include \"extern.h\"\n\n#define array_count(a) (sizeof((a)) / sizeof(*(a)))\n#define max(a, b) (a > b ? a : b)\n#define min(a, b) (a < b ? a : b)\n#define clamp(x, l, u) (min(max(x, l), u))\n\n#define MAXLEN 512\n#define FORK_TIMEOUT 60\n\nextern int g_connection;\n\nstruct signal_args {\n  struct env_vars env_vars;\n  void *entity;\n  void *param1;\n};\n\nstatic CGPoint g_nirvana = {-9999, -9999};\n\nstatic double deg_to_rad = 2.* M_PI / 360.;\n\nstruct token {\n  char *text;\n  unsigned int length;\n};\n\nstruct notification {\n  char* name;\n  char* info;\n};\n\nstatic inline struct notification* notification_create() {\n  struct notification* notification = malloc(sizeof(struct notification));\n  memset(notification, 0, sizeof(struct notification));\n  return notification;\n}\n\nstatic inline void notification_destroy(struct notification* notification) {\n  if (notification->name) free(notification->name);\n  if (notification->info) free(notification->info);\n  free(notification);\n}\n\nstatic inline double function_linear(double x) {\n  return x;\n}\n\nstatic inline double function_square(double x) {\n  return x*x;\n}\n\nstatic inline double function_tanh(double x) {\n  double a = 0.52;\n  return a * tanh(2. * atanh(1. / (2. * a)) * (x  - 0.5)) + 0.5;\n}\n\nstatic inline double function_sin(double x) {\n  return sin(M_PI / 2. * x);\n}\n\nstatic inline double function_exp(double x) {\n  return x*exp(x - 1.);\n}\n\nstatic inline double function_circ(double x) {\n    return sqrt(1.f - powf(x - 1.f, 2.f));\n}\n\nstatic inline char* format_bool(bool b) {\n  return b ? \"on\" : \"off\";\n}\n\nstatic inline char* escape_string(char* string) {\n  if (!string) return NULL;\n  int len = strlen(string);\n  char* buffer = malloc(2*len + 1);\n  int cursor = 0;\n  for (int i = 0; i < len; i++) {\n    if (string[i] == '\"') {\n      buffer[cursor++] = '\\\\';\n      buffer[cursor++] = '\"';\n    }\n    else if (string[i] == '\\n') {\n      buffer[cursor++] = '\\\\';\n      buffer[cursor++] = 'n';\n    }\n    else {\n      buffer[cursor++] = string[i];\n    }\n  }\n  buffer[cursor] = '\\0';\n  return buffer;\n}\n\nstatic inline void respond(FILE* rsp, char* response, ...) {\n  time_t t = time(NULL);\n  struct tm ltime = *localtime(&t);\n  printf(\"[%d-%02d-%02d %02d:%02d:%02d] \",\n         ltime.tm_year + 1900,\n         ltime.tm_mon + 1,\n         ltime.tm_mday,\n         ltime.tm_hour,\n         ltime.tm_min,\n         ltime.tm_sec                     );\n\n  va_list args_rsp;\n  va_list args_stdout;\n  va_start(args_rsp, response);\n  va_copy(args_stdout, args_rsp);\n  vfprintf(rsp, response, args_rsp);\n  vfprintf(stdout, response, args_stdout);\n  va_end(args_rsp);\n  va_end(args_stdout);\n}\n\nstatic inline struct key_value_pair get_key_value_pair(char *token, char split) {\n  struct key_value_pair key_value_pair;\n  key_value_pair.key = token;\n\n  while (*token) {\n    if (token[0] == split) break;\n    ++token;\n  }\n\n  if (*token != split) {\n    key_value_pair.key = NULL;\n    key_value_pair.value = NULL;\n  } else if (token[1]) {\n    *token = '\\0';\n    key_value_pair.value = token + 1;\n  } else {\n    *token = '\\0';\n    key_value_pair.value = NULL;\n  }\n\n  return key_value_pair;\n}\n\nstatic inline void pack_key_value_pair(char* cursor, struct key_value_pair* key_value_pair) {\n  uint32_t key_len = strlen(key_value_pair->key);\n  uint32_t val_len = key_value_pair->value ? strlen(key_value_pair->value) : 0;\n  memcpy(cursor, key_value_pair->key, key_len);\n  cursor += key_len;\n  *cursor++ = '\\0';\n  memcpy(cursor, key_value_pair->value, val_len);\n  cursor += val_len;\n  *cursor++ = '\\0';\n  *cursor++ = '\\0';\n}\n\nstatic inline bool is_root(void) {\n  return getuid() == 0 || geteuid() == 0;\n}\n\nstatic inline bool string_equals(const char *a, const char *b) {\n  return a && b && strcmp(a, b) == 0;\n}\n\nstatic inline char* get_type_description(uint32_t type) {\n  switch (type) {\n    case kCGEventLeftMouseUp:\n      return \"left\";\n    case kCGEventRightMouseUp:\n      return \"right\";\n    default:\n      return \"other\";\n  }\n}\n\nstatic inline char* get_modifier_description(uint32_t modifier) {\n    static char description[64];\n    description[0] = '\\0';\n\n    if (modifier & kCGEventFlagMaskShift)\n        strcat(description, \"shift,\");\n    if (modifier & kCGEventFlagMaskControl)\n        strcat(description, \"ctrl,\");\n    if (modifier & kCGEventFlagMaskAlternate)\n        strcat(description, \"alt,\");\n    if (modifier & kCGEventFlagMaskCommand)\n        strcat(description, \"cmd,\");\n    if (modifier & kCGEventFlagMaskSecondaryFn)\n        strcat(description, \"fn,\");\n\n    int len = strlen(description);\n    if (len > 0 && description[len - 1] == ',') {\n        description[len - 1] = '\\0';\n    }\n\n    return description[0] ? description : \"none\";\n}\n\nstatic inline char** token_split(struct token token, char split, uint32_t* count) {\n  if (!token.text || token.length == 0) return NULL;\n  char** list = NULL;\n  *count = 0;\n\n  int prev = -1;\n  for (int i = 0; i < token.length + 1; i++) {\n    if (token.text[i] == split || token.text[i] == '\\0') {\n      list = realloc(list, sizeof(char*) * ++*count);\n      token.text[i] = '\\0';\n      list[*count - 1] = &token.text[prev + 1];\n      prev = i;\n    }\n  }\n  return list;\n}\n\nstatic inline bool token_equals(struct token token, char *match) {\n  char *at = match;\n  for (int i = 0; i < token.length; ++i, ++at) {\n    if ((*at == 0) || (token.text[i] != *at)) {\n      return false;\n    }\n  }\n  return *at == 0;\n}\n\nstatic inline char *token_to_string(struct token token) {\n  char *result = malloc(token.length + 1);\n  if (!result) return NULL;\n\n  memcpy(result, token.text, token.length);\n  result[token.length] = '\\0';\n  return result;\n}\n\nstatic inline uint32_t token_to_uint32t(struct token token) {\n  char buffer[token.length + 1];\n  memcpy(buffer, token.text, token.length);\n  buffer[token.length] = '\\0';\n  return strtoul(buffer, NULL, 0);\n}\n\nstatic inline int token_to_int(struct token token) {\n  char buffer[token.length + 1];\n  memcpy(buffer, token.text, token.length);\n  buffer[token.length] = '\\0';\n  return (int) strtol(buffer, NULL, 0);\n}\n\nstatic inline float token_to_float(struct token token) {\n  char buffer[token.length + 1];\n  memcpy(buffer, token.text, token.length);\n  buffer[token.length] = '\\0';\n  return strtof(buffer, NULL);\n}\n\nstatic inline struct token get_token(char **message) {\n  struct token token;\n\n  token.text = *message;\n  while (**message) {\n    ++(*message);\n  }\n  token.length = *message - token.text;\n\n  if ((*message)[0] == '\\0' && (*message)[1] != '\\0') {\n    ++(*message);\n  } else {\n    // NOTE(koekeishiya): don't go past the null-terminator\n  }\n\n  return token;\n}\n\nstatic inline bool evaluate_boolean_state(struct token state, bool previous_state) {\n  if (token_equals(state, ARGUMENT_COMMON_VAL_ON)\n      || token_equals(state, ARGUMENT_COMMON_VAL_YES)\n      || token_equals(state, ARGUMENT_COMMON_VAL_TRUE)\n      || token_equals(state, ARGUMENT_COMMON_VAL_ONE)\n      || token_equals(state, ARGUMENT_COMMON_VAL_NOT_OFF)\n      || token_equals(state, ARGUMENT_COMMON_VAL_NOT_NO)\n      || token_equals(state, ARGUMENT_COMMON_VAL_NOT_FALSE)\n      || token_equals(state, ARGUMENT_COMMON_VAL_NOT_ZERO) )\n    return true;\n  else if (token_equals(state, ARGUMENT_COMMON_VAL_TOGGLE))\n    return !previous_state;\n  else return false;\n}\n\nstatic inline uint32_t get_set_bit_position(uint32_t mask) {\n  if (mask == 0) return UINT32_MAX;\n  uint32_t pos = 0;\n  while (!(mask & 1)) {\n    mask >>= 1;\n    pos++;\n  }\n  return pos;\n}\n\nstatic inline void clip_rect(CGContextRef context, CGRect region, float clip, uint32_t corner_radius) {\n  CGMutablePathRef path = CGPathCreateMutable();\n  if (corner_radius > region.size.height / 2.f || corner_radius > region.size.width / 2.f)\n    corner_radius = region.size.height > region.size.width ? region.size.width / 2.f : region.size.height / 2.f;\n  CGPathAddRoundedRect(path, NULL, region, corner_radius, corner_radius);\n  CGContextSetBlendMode(context, kCGBlendModeDestinationOut);\n  CGContextSetRGBFillColor(context, 0.f, 0.f, 0.f, clip);\n  CGContextAddPath(context, path);\n  CGContextDrawPath(context, kCGPathFillStroke);\n  CGContextSetBlendMode(context, kCGBlendModeNormal);\n  CFRelease(path);\n}\n\nstatic inline CGRect cgrect_mirror_y(CGRect rect, float y) {\n  CGRect mirrored_rect = rect;\n  mirrored_rect.origin.y = 2*y - rect.origin.y;\n  return mirrored_rect;\n}\n\nstatic inline bool cgrect_contains_point(CGRect* r, CGPoint* p) {\n  return p->x >= r->origin.x && p->x <= r->origin.x + r->size.width &&\n         p->y >= r->origin.y && p->y <= r->origin.y + r->size.height;\n}\n\nstatic inline char *string_escape_quote(char *s) {\n  if (!s) return NULL;\n\n  char *cursor = s;\n  int num_quotes = 0;\n\n  while (*cursor) {\n    if (*cursor == '\"') ++num_quotes;\n    ++cursor;\n  }\n\n  if (!num_quotes) return NULL;\n\n  int size_in_bytes = (int)(cursor - s) + num_quotes;\n  char *result = malloc(sizeof(char) * (size_in_bytes+1));\n  result[size_in_bytes] = '\\0';\n\n  for (char *dst = result, *cursor = s; *cursor; ++cursor) {\n    if (*cursor == '\"') *dst++ = '\\\\';\n    *dst++ = *cursor;\n  }\n\n  return result;\n}\n\nstatic inline CFArrayRef cfarray_of_cfnumbers(void *values, size_t size, int count, CFNumberType type) {\n  CFNumberRef temp[count];\n\n  for (int i = 0; i < count; ++i) {\n    temp[i] = CFNumberCreate(NULL, type, ((char *)values) + (size * i));\n  }\n\n  CFArrayRef result = CFArrayCreate(NULL, (const void **)temp, count, &kCFTypeArrayCallBacks);\n\n  for (int i = 0; i < count; ++i) {\n    CFRelease(temp[i]);\n  }\n\n  return result;\n}\n\nstatic inline char *cfstring_copy(CFStringRef string) {\n  CFIndex num_bytes = CFStringGetMaximumSizeForEncoding(CFStringGetLength(string), kCFStringEncodingUTF8);\n  char *result = malloc(num_bytes + 1);\n  if (!result) return NULL;\n\n  if (!CFStringGetCString(string, result, num_bytes + 1, kCFStringEncodingUTF8)) {\n    free(result);\n    result = NULL;\n  }\n\n  return result;\n}\n\nstatic inline char *string_copy(char *s) {\n  int length = strlen(s);\n  char *result = malloc(length + 1);\n  if (!result) return NULL;\n\n  memcpy(result, s, length);\n  result[length] = '\\0';\n  return result;\n}\n\nstatic inline char* read_file(char* path) {\n  int fd = open(path, O_RDONLY);\n  int len = lseek(fd, 0, SEEK_END);\n  char* file = mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0);\n  close(fd);\n  free(path);\n  return string_copy(file);\n}\n\nstatic inline char* resolve_path(char* path) {\n  if (!path) return NULL;\n\n  if (path[0] == '~') {\n    char* home = getenv(\"HOME\");\n    char buf[512];\n    snprintf(buf, sizeof(buf), \"%s%s\", home, &path[1]);\n    free(path);\n    return string_copy(buf);\n  }\n  return path;\n}\n\nstatic inline bool file_exists(char *filename) {\n  struct stat buffer;\n\n  if (stat(filename, &buffer) != 0) {\n    return false;\n  }\n\n  if (buffer.st_mode & S_IFDIR) {\n    return false;\n  }\n\n  return true;\n}\n\nstatic inline bool ensure_executable_permission(char *filename) {\n  struct stat buffer;\n\n  if (stat(filename, &buffer) != 0) {\n    return false;\n  }\n\n  bool is_executable = buffer.st_mode & S_IXUSR;\n  if (!is_executable && chmod(filename, S_IXUSR | buffer.st_mode) != 0) {\n    return false;\n  }\n  return true;\n}\n\nstatic inline bool sync_exec(char *command, struct env_vars *env_vars) {\n  if (env_vars) {\n    for (int i = 0; i < env_vars->count; i++) {\n      setenv(env_vars->vars[i]->key, env_vars->vars[i]->value, 1);\n    }\n  }\n\n  char *exec[] = { \"/usr/bin/env\", \"sh\", \"-c\", command, NULL};\n  return execvp(exec[0], exec);\n}\n\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\nstatic inline bool fork_exec(char *command, struct env_vars* env_vars) {\n  int pid = vfork();\n  if (pid == -1) return false;\n  if (pid !=  0) return true;\n\n  alarm(FORK_TIMEOUT);\n  exit(sync_exec(command, env_vars));\n}\n#pragma clang diagnostic pop\n\nstatic inline int mission_control_index(uint64_t sid) {\n  uint64_t result = 0;\n  int desktop_cnt = 1;\n\n  CFArrayRef display_spaces_ref = SLSCopyManagedDisplaySpaces(g_connection);\n  int display_spaces_count = CFArrayGetCount(display_spaces_ref);\n\n  for (int i = 0; i < display_spaces_count; ++i) {\n    CFDictionaryRef display_ref = CFArrayGetValueAtIndex(display_spaces_ref, i);\n    CFArrayRef spaces_ref = CFDictionaryGetValue(display_ref, CFSTR(\"Spaces\"));\n    int spaces_count = CFArrayGetCount(spaces_ref);\n\n    for (int j = 0; j < spaces_count; ++j) {\n      CFDictionaryRef space_ref = CFArrayGetValueAtIndex(spaces_ref, j);\n      CFNumberRef sid_ref = CFDictionaryGetValue(space_ref, CFSTR(\"id64\"));\n      CFNumberGetValue(sid_ref, CFNumberGetType(sid_ref), &result);\n      if (sid == result) goto out;\n\n      ++desktop_cnt;\n    }\n  }\n\n  desktop_cnt = 0;\nout:\n  CFRelease(display_spaces_ref);\n  return desktop_cnt;\n}\n\nstatic inline uint64_t dsid_from_sid(uint32_t sid) {\n  uint64_t result = 0;\n  int desktop_cnt = 1;\n\n  CFArrayRef display_spaces_ref = SLSCopyManagedDisplaySpaces(g_connection);\n  int display_spaces_count = CFArrayGetCount(display_spaces_ref);\n\n  for (int i = 0; i < display_spaces_count; ++i) {\n    CFDictionaryRef display_ref = CFArrayGetValueAtIndex(display_spaces_ref, i);\n    CFArrayRef spaces_ref = CFDictionaryGetValue(display_ref, CFSTR(\"Spaces\"));\n    int spaces_count = CFArrayGetCount(spaces_ref);\n\n    for (int j = 0; j < spaces_count; ++j) {\n      CFDictionaryRef space_ref = CFArrayGetValueAtIndex(spaces_ref, j);\n      CFNumberRef sid_ref = CFDictionaryGetValue(space_ref, CFSTR(\"id64\"));\n      CFNumberGetValue(sid_ref, CFNumberGetType(sid_ref), &result);\n      if (sid == desktop_cnt) goto out;\n\n      ++desktop_cnt;\n    }\n  }\n\n  result = 0;\nout:\n  CFRelease(display_spaces_ref);\n  return result;\n}\n\nstatic inline CGImageRef space_capture(uint32_t sid) {\n  uint64_t dsid = dsid_from_sid(sid);\n  CGImageRef image = NULL;\n  if (dsid) {\n    CFArrayRef result = SLSHWCaptureSpace(g_connection, dsid, 0);\n    uint32_t count = CFArrayGetCount(result);\n    if (count > 0) {\n      image = (CGImageRef)CFRetain(CFArrayGetValueAtIndex(result, 0));\n    }\n    CFRelease(result);\n  }\n  return image;\n}\n\nstatic inline uint32_t display_id_for_space(uint32_t sid) {\n  uint64_t dsid = dsid_from_sid(sid);\n  if (!dsid) return 0;\n  CFStringRef uuid_string = SLSCopyManagedDisplayForSpace(g_connection, dsid);\n  if (!uuid_string) return 0;\n\n  CFUUIDRef uuid = CFUUIDCreateFromString(NULL, uuid_string);\n  uint32_t id = CGDisplayGetDisplayIDFromUUID(uuid);\n\n  CFRelease(uuid);\n  CFRelease(uuid_string);\n\n  return id;\n}\n\nstatic inline void error(const char *format, ...) {\n    va_list args;\n    va_start(args, format);\n    vfprintf(stderr, format, args);\n    va_end(args);\n    exit(EXIT_FAILURE);\n}\n\nstatic inline int get_wid_from_cg_event(CGEventRef event) {\n  return CGEventGetIntegerValueField(event, 0x33);\n}\n"
  },
  {
    "path": "src/mouse.c",
    "content": "#include <Carbon/Carbon.h>\n#include \"mouse.h\"\n\nstatic const EventTypeSpec mouse_events [] = {\n    { kEventClassMouse, kEventMouseUp },\n    { kEventClassMouse, kEventMouseDragged },\n    { kEventClassMouse, kEventMouseEntered },\n    { kEventClassMouse, kEventMouseExited },\n    { kEventClassMouse, kEventMouseWheelMoved },\n    { kEventClassMouse, kEventMouseScroll }\n};\n\nstatic int carbon_event_translation[] = {\n  [kEventMouseUp] = MOUSE_UP,\n  [kEventMouseDragged] = MOUSE_DRAGGED,\n  [kEventMouseEntered] = MOUSE_ENTERED,\n  [kEventMouseExited]  = MOUSE_EXITED,\n  [kEventMouseWheelMoved] = MOUSE_SCROLLED,\n  [kEventMouseScroll] = MOUSE_SCROLLED\n};\n\nstatic pascal OSStatus mouse_handler(EventHandlerCallRef next, EventRef e, void *data) {\n  enum event_type event_type = carbon_event_translation[GetEventKind(e)];\n\n  CGEventRef cg_event = CopyEventCGEvent(e);\n  struct event event = { (void *) cg_event, event_type };\n  event_post(&event);\n  CFRelease(cg_event);\n\n  return CallNextEventHandler(next, e);\n}\n\nvoid mouse_begin(void) {\n  InstallEventHandler(GetEventDispatcherTarget(),\n                      NewEventHandlerUPP(mouse_handler),\n                      GetEventTypeCount(mouse_events),\n                      mouse_events, 0, 0);\n}\n"
  },
  {
    "path": "src/mouse.h",
    "content": "#pragma once\n#include \"event.h\"\n\nvoid mouse_begin(void);\n"
  },
  {
    "path": "src/popup.c",
    "content": "#include \"popup.h\"\n#include \"bar_item.h\"\n#include \"bar_manager.h\"\n#include \"bar.h\"\n#include \"animation.h\"\n\nvoid popup_init(struct popup* popup, struct bar_item* host) {\n  popup->drawing = false;\n  popup->horizontal = false;\n  popup->mouse_over = false;\n  popup->overrides_cell_size = false;\n  popup->needs_ordering = false;\n  popup->anchor = (CGPoint){0, 0};\n  popup->y_offset = 0;\n  popup->adid = 0;\n  popup->align = POSITION_LEFT;\n  popup->blur_radius = 0;\n  popup->topmost = true;\n  \n  popup->num_items = 0;\n  popup->cell_size = 30;\n  popup->items = NULL;\n  popup->host = host;\n  background_init(&popup->background);\n  window_init(&popup->window);\n  color_set_hex(&popup->background.border_color, 0xffff0000);\n  color_set_hex(&popup->background.color, 0x44000000);\n}\n\nstatic CGRect popup_get_frame(struct popup* popup) {\n  return (CGRect){{popup->anchor.x, popup->anchor.y},\n                  {popup->background.bounds.size.width,\n                   popup->background.bounds.size.height}};\n}\n\nstatic bool popup_set_blur_radius(struct popup* popup, uint32_t radius) {\n  if (popup->blur_radius == radius) return false;\n  popup->blur_radius = radius;\n  window_set_blur_radius(&popup->window, radius);\n  return false;\n}\n\nstatic void popup_order_windows(struct popup* popup) {\n  int level = popup->topmost\n              ? (kCGPopUpMenuWindowLevel)\n              : (kCGBackstopMenuLevel + 1);\n  window_set_level(&popup->window, level);\n  window_order(&popup->window, NULL, W_ABOVE);\n\n  struct window* previous_window = NULL;\n  struct window* first_window = NULL;\n  for (int i = 0; i < popup->num_items; i++) {\n    struct bar_item* bar_item = popup->items[i];\n\n    struct window* window = bar_item_get_window(bar_item, popup->adid);\n    window_set_level(window, level);\n    if (!first_window) first_window = window;\n\n    if (bar_item->type == BAR_COMPONENT_GROUP) {\n      if (first_window)\n        window_order(window, first_window, W_BELOW);\n      else\n        window_order(window, &popup->window, W_ABOVE);\n      continue;\n    }\n\n    if (previous_window) window_order(window, previous_window, W_ABOVE);\n    else window_order(window, &popup->window, W_ABOVE);\n\n    previous_window = window;\n  }\n}\n\nstatic void popup_calculate_popup_anchor_for_bar_item(struct popup* popup, struct bar_item* bar_item, struct bar* bar) {\n  if (popup->adid != g_bar_manager.active_adid) return;\n  struct window* window = bar_item_get_window(bar_item, popup->adid);\n\n  if (!bar_item->popup.overrides_cell_size)\n    bar_item->popup.cell_size = window->frame.size.height;\n\n  popup_calculate_bounds(&bar_item->popup, bar);\n\n  CGPoint anchor = window->origin;\n  if (bar_item->position != POSITION_POPUP || popup->horizontal) {\n    if (bar_item->popup.align == POSITION_CENTER) {\n      anchor.x += (window->frame.size.width\n                   - bar_item->popup.background.bounds.size.width) / 2;\n    } else if (bar_item->popup.align == POSITION_LEFT) {\n      anchor.x -= bar_item->background.padding_left;\n    } else {\n      anchor.x += window->frame.size.width\n                  - bar_item->popup.background.bounds.size.width;\n    }\n    anchor.y += (g_bar_manager.position == POSITION_BOTTOM\n                ? (- bar_item->popup.background.bounds.size.height)\n                : window->frame.size.height);\n  } else if (bar_item->parent) {\n    struct popup* host = &bar_item->parent->popup;\n    anchor.x = host->window.origin.x;\n    if (bar_item->popup.align == POSITION_LEFT) {\n      anchor.x -= bar_item->popup.background.bounds.size.width;\n    }\n    else {\n      anchor.x += host->window.frame.size.width;\n    }\n    anchor.y -= host->background.border_width;\n  }\n  popup_set_anchor(&bar_item->popup, anchor, popup->adid);\n}\n\nvoid popup_calculate_bounds(struct popup* popup, struct bar* bar) {\n  uint32_t y = popup->background.border_width;\n  uint32_t x = 0;\n  uint32_t total_item_width = 0;\n  uint32_t width = 0;\n  uint32_t height = 0;\n\n  if (popup->background.enabled\n      && popup->background.image.enabled) {\n    uint32_t image_width = image_get_size(&popup->background.image).width;\n    width = image_width + 2*popup->background.border_width;\n  }\n\n  if (popup->horizontal) {\n    for (int j = 0; j < popup->num_items; j++) {\n      struct bar_item* bar_item = popup->items[j];\n      if (!bar_item->drawing) continue;\n      if (bar_item->type == BAR_COMPONENT_GROUP) continue;\n      uint32_t cell_height = max(bar_item_get_height(bar_item),\n                                 popup->cell_size              );\n\n      total_item_width += bar_item->background.padding_right\n                          + bar_item->background.padding_left\n                          + bar_item_get_length(bar_item, false);\n\n      if (cell_height > height && popup->horizontal) height = cell_height;\n    }\n\n    if (popup->background.enabled\n        && popup->background.image.enabled) {\n      uint32_t image_height = image_get_size(&popup->background.image).height;\n      if (image_height > height) height = image_height;\n      \n      x = (width - total_item_width) / 2;\n    }\n  }\n\n  for (int j = 0; j < popup->num_items; j++) {\n    struct bar_item* bar_item = NULL;\n    bar_item = popup->items[j];\n    if (!bar_item->drawing) continue;\n    if (bar_item->type == BAR_COMPONENT_GROUP) continue;\n\n    uint32_t cell_height = max(bar_item_get_height(bar_item),\n                               popup->cell_size              );\n\n    uint32_t item_x = max((int)x + bar_item->background.padding_left, 0);\n    uint32_t item_height = popup->horizontal ? height : cell_height;\n    uint32_t item_y = item_height / 2;\n\n    uint32_t item_width = bar_item->background.padding_right\n                          + bar_item->background.padding_left\n                          + bar_item_calculate_bounds(bar_item,\n                                                      item_height,\n                                                      0,\n                                                      item_y      );\n\n    uint32_t bar_item_display_length = bar_item_get_length(bar_item, true);\n    if (popup->adid > 0) {\n      CGRect frame = {{popup->anchor.x + item_x,\n                       popup->anchor.y + y},\n                      {bar_item_display_length,\n                       item_height             }  };\n\n      window_set_frame(bar_item_get_window(bar_item, popup->adid), frame);\n    }\n\n    if (bar_item->popup.drawing)\n      popup_calculate_popup_anchor_for_bar_item(popup, bar_item, bar);\n\n    if (item_width > width && !popup->horizontal) width = item_width;\n    if (popup->horizontal) x += item_width;\n    else y += cell_height;\n  }\n\n  for (int j = 0; j < popup->num_items; j++) {\n    if (popup->adid <= 0) break;\n    struct bar_item* bar_item = NULL;\n    bar_item = popup->items[j];\n    if (!bar_item->drawing) continue;\n    if (bar_item->type != BAR_COMPONENT_GROUP) continue;\n\n    uint32_t cell_height = popup->cell_size;\n    if (bar_item->group->num_members > 2) {\n      cell_height = max(bar_item_get_height(bar_item->group->members[1]),\n                        popup->cell_size                                 );\n    }\n\n    uint32_t item_height = popup->horizontal ? height : cell_height;\n    uint32_t item_y = item_height / 2;\n\n    group_calculate_bounds(bar_item->group, bar, item_y);\n\n    window_set_frame(bar_item_get_window(bar_item->group->members[0],\n                                         popup->adid                 ),\n                     bar_item->group->bounds                           );\n  }\n\n\n  if (popup->horizontal) {\n    if (!popup->background.enabled || !popup->background.image.enabled) {\n      width = x + popup->background.border_width;\n    }\n    y += height;\n  }\n  else if (!popup->background.enabled || !popup->background.image.enabled) {\n    width += popup->background.border_width;\n  }\n  y += popup->background.border_width;\n\n  popup->background.bounds.size.width = width;\n  popup->background.bounds.size.height = y;\n\n  image_calculate_bounds(&popup->background.image,\n                         popup->background.border_width,\n                         popup->background.border_width\n                         + popup->background.image.bounds.size.height / 2);\n\n  if (popup->adid > 0)\n    window_set_frame(&popup->window, popup_get_frame(popup));\n}\n\nstatic void popup_create_window(struct popup* popup) {\n  popup->drawing = true;\n\n  if (popup == &g_bar_manager.default_item.popup) return;\n\n  window_create(&popup->window,(CGRect){{popup->anchor.x, popup->anchor.y},\n                                      {popup->background.bounds.size.width,\n                                       popup->background.bounds.size.height}});\n\n  if (!popup->background.shadow.enabled)\n    window_disable_shadow(&popup->window);\n\n  CGContextSetInterpolationQuality(popup->window.context,\n                                   kCGInterpolationNone);\n\n  context_set_font_smoothing(popup->window.context,\n                             g_bar_manager.font_smoothing);\n\n  window_set_blur_radius(&popup->window, popup->blur_radius);\n  popup->needs_ordering = true;\n}\n\nstatic void popup_close_window(struct popup* popup) {\n  if (popup == &g_bar_manager.default_item.popup) return;\n  window_close(&popup->window);\n}\n\nstatic bool popup_contains_item(struct popup* popup, struct bar_item* bar_item) {\n  for (int i = 0; i < popup->num_items; i++) {\n    if (popup->items[i] == bar_item) return true;\n  }\n  return false;\n}\n\nvoid popup_add_item(struct popup* popup, struct bar_item* bar_item) {\n  if (popup_contains_item(popup, bar_item)) return;\n  if (bar_item->parent) {\n    popup_remove_item(&bar_item->parent->popup, bar_item);\n  }\n  popup->num_items++;\n  popup->items = realloc(popup->items,\n                         sizeof(struct bar_item*)*popup->num_items);\n  popup->items[popup->num_items - 1] = bar_item;\n  bar_item->parent = popup->host;\n  popup->needs_ordering = true;\n  if (popup->num_items == 1){\n    popup_draw(popup);\n  }\n}\n\nvoid popup_remove_item(struct popup* popup, struct bar_item* bar_item) {\n  if (popup->num_items == 0 || !popup_contains_item(popup, bar_item))\n    return;\n  else if (popup->num_items == 1) {\n    free(popup->items);\n    popup->items = NULL;\n    popup->num_items = 0;\n    popup_close_window(popup);\n    return;\n  }\n\n  struct bar_item* tmp[popup->num_items - 1];\n  int count = 0;\n  for (int i = 0; i < popup->num_items; i++) {\n    if (popup->items[i] == bar_item) continue;\n    tmp[count++] = popup->items[i];\n  }\n  popup->num_items--;\n  popup->items = realloc(popup->items,\n                         sizeof(struct bar_item*)*popup->num_items);\n  memcpy(popup->items, tmp, sizeof(struct bar_item*)*popup->num_items);\n}\n\nvoid popup_set_anchor(struct popup* popup, CGPoint anchor, uint32_t adid) {\n  popup->anchor = anchor;\n  popup->anchor.y += popup->y_offset;\n\n  if ((popup->adid != adid)) {\n    popup->needs_ordering = true;\n    for (int i = 0; i < popup->num_items; i++) {\n      bar_item_needs_update(popup->items[i]);\n    }\n  }\n\n  popup->adid = adid;\n}\n\nvoid popup_clear_pointers(struct popup* popup) {\n  popup->items = NULL;\n  popup->num_items = 0;\n  popup->host = NULL;\n  window_clear(&popup->window);\n}\n\nbool popup_set_drawing(struct popup* popup, bool drawing) {\n  if (popup->drawing == drawing) return false;\n  if (!drawing) popup_close_window(popup);\n  popup->drawing = drawing;\n  popup->adid = 0;\n  return true;\n}\n\nvoid popup_draw(struct popup* popup) {\n  if (!popup->drawing || popup->adid < 1 || popup->num_items == 0) return;\n\n  windows_freeze();\n  if (!popup->window.id) popup_create_window(popup);\n\n  if (!window_apply_frame(&popup->window, false) && !popup->host->needs_update)\n    return;\n\n  CGContextClearRect(popup->window.context, popup->background.bounds);\n\n  window_assign_mouse_tracking_area(&popup->window, popup->window.frame);\n\n  bool shadow = popup->background.shadow.enabled;\n  popup->background.shadow.enabled = false;\n  background_draw(&popup->background, popup->window.context);\n  popup->background.shadow.enabled = shadow;\n\n  CGContextFlush(popup->window.context);\n  window_flush(&popup->window);\n\n  if (popup->needs_ordering) {\n    popup_order_windows(popup);\n    popup->needs_ordering = false;\n  }\n}\n\nvoid popup_change_space(struct popup* popup, uint64_t dsid, uint32_t adid) {\n  for (int i = 0; i < popup->num_items; i++) {\n    struct bar_item* bar_item = popup->items[i];\n    bar_item_change_space(bar_item, dsid, adid);\n  }\n\n  if (popup->drawing) {\n    window_send_to_space(&popup->window, dsid);\n  }\n}\n\nvoid popup_destroy(struct popup* popup) {\n  for (int i = 0; i < popup->num_items; i++) {\n    bar_manager_remove_item(&g_bar_manager, popup->items[i]);\n  }\n  if (popup->items) free(popup->items);\n  background_destroy(&popup->background);\n  popup_close_window(popup);\n}\n\nvoid popup_serialize(struct popup* popup, char* indent, FILE* rsp) {\n  char align[32] = { 0 };\n  switch (popup->align) {\n    case POSITION_LEFT:\n      snprintf(align, 32, \"left\");\n      break;\n    case POSITION_RIGHT:\n      snprintf(align, 32, \"right\");\n      break;\n    case POSITION_CENTER:\n      snprintf(align, 32, \"center\");\n      break;\n    case POSITION_BOTTOM:\n      snprintf(align, 32, \"bottom\");\n      break;\n    case POSITION_TOP:\n      snprintf(align, 32, \"top\");\n      break;\n    default:\n      snprintf(align, 32, \"invalid\");\n      break;\n  }\n\n  fprintf(rsp, \"%s\\\"drawing\\\": \\\"%s\\\",\\n\"\n               \"%s\\\"horizontal\\\": \\\"%s\\\",\\n\"\n               \"%s\\\"height\\\": %d,\\n\"\n               \"%s\\\"blur_radius\\\": %u,\\n\"\n               \"%s\\\"y_offset\\\": %d,\\n\"\n               \"%s\\\"align\\\": \\\"%s\\\",\\n\"\n               \"%s\\\"background\\\": {\\n\",\n               indent, format_bool(popup->drawing),\n               indent, format_bool(popup->horizontal),\n               indent, popup->overrides_cell_size ? popup->cell_size : -1,\n               indent, popup->blur_radius,\n               indent, popup->y_offset,\n               indent, align, indent                                      );\n\n  char deeper_indent[strlen(indent) + 2];\n  snprintf(deeper_indent, strlen(indent) + 2, \"%s\\t\", indent);\n  background_serialize(&popup->background, deeper_indent, rsp, true);\n\n  fprintf(rsp, \"\\n%s},\\n%s\\\"items\\\": [\\n\", indent, indent);\n  for (int i = 0; i < popup->num_items; i++) {\n    fprintf(rsp, \"%s\\t \\\"%s\\\"\", indent, popup->items[i]->name);\n    if (i < popup->num_items - 1) fprintf(rsp, \",\\n\");\n  }\n  fprintf(rsp, \"\\n%s]\", indent);\n}\n\nstatic bool popup_set_yoffset(struct popup* popup, int y_offset) {\n  if (popup->y_offset == y_offset) return false;\n  popup->y_offset = y_offset;\n  return true;\n}\n\nstatic bool popup_set_cell_size(struct popup* popup, int size) {\n  if (popup->cell_size == size && popup->overrides_cell_size) return false;\n  popup->overrides_cell_size = true;\n  popup->cell_size = size;\n  return true;\n}\n\nstatic bool popup_set_topmost(struct popup* popup, bool topmost) {\n  if (topmost == popup->topmost) return false;\n  popup->topmost = topmost;\n  popup->needs_ordering = true;\n  return true;\n}\n\nbool popup_parse_sub_domain(struct popup* popup, FILE* rsp, struct token property, char* message) {\n  bool needs_refresh = false;\n  if (token_equals(property, PROPERTY_YOFFSET)) {\n    ANIMATE(popup_set_yoffset, popup, popup->y_offset, token_to_int(get_token(&message)));\n  } else if (token_equals(property, PROPERTY_DRAWING)) {\n    return popup_set_drawing(popup,\n                             evaluate_boolean_state(get_token(&message),\n                             popup->drawing)                            );\n  } else if (token_equals(property, PROPERTY_HORIZONTAL)) {\n    popup->horizontal = evaluate_boolean_state(get_token(&message),\n                                               popup->horizontal   );\n    return true;\n  } else if (token_equals(property, PROPERTY_ALIGN)) {\n    popup->align = get_token(&message).text[0];\n    return true;\n  } else if (token_equals(property, PROPERTY_HEIGHT)) {\n    ANIMATE(popup_set_cell_size,\n            popup,\n            popup->cell_size,\n            token_to_int(get_token(&message)));\n  } else if (token_equals(property, PROPERTY_BLUR_RADIUS)) {\n    ANIMATE(popup_set_blur_radius,\n            popup,\n            popup->blur_radius,\n            token_to_int(get_token(&message)));\n    return false;\n  } else if (token_equals(property, PROPERTY_TOPMOST)) {\n    return popup_set_topmost(popup,\n                             evaluate_boolean_state(get_token(&message),\n                                                    popup->topmost      ));\n  }\n  else {\n    struct key_value_pair key_value_pair = get_key_value_pair(property.text,\n                                                              '.'           );\n    if (key_value_pair.key && key_value_pair.value) {\n      struct token subdom = { key_value_pair.key, strlen(key_value_pair.key) };\n      struct token entry = {key_value_pair.value,strlen(key_value_pair.value)};\n      if (token_equals(subdom, SUB_DOMAIN_BACKGROUND))\n        return background_parse_sub_domain(&popup->background,\n                                           rsp,\n                                           entry,\n                                           message            );\n      else {\n        respond(rsp, \"[!] Popup: Invalid subdomain '%s'\\n\", subdom.text);\n      }\n    }\n    else {\n      respond(rsp, \"[!] Popup: Invalid property '%s'\\n\", property.text);\n    }\n  }\n  return needs_refresh;\n}\n"
  },
  {
    "path": "src/popup.h",
    "content": "#pragma once\n#include \"background.h\"\n#include \"misc/helpers.h\"\n#include \"window.h\"\n\nstruct bar_item;\nstruct bar;\n\nstruct popup {\n  bool drawing;\n  bool horizontal;\n  bool overrides_cell_size;\n  bool mouse_over;\n  bool needs_ordering;\n  bool topmost;\n\n  char align;\n\n  uint32_t adid;\n  uint32_t cell_size;\n  uint32_t blur_radius;\n  int y_offset;\n\n  CGPoint anchor;\n  struct window window;\n\n  struct bar_item* host;\n  struct bar_item** items;\n  uint32_t num_items;\n\n  struct background background;\n};\n\nvoid popup_init(struct popup* popup, struct bar_item* host);\nvoid popup_set_anchor(struct popup* popup, CGPoint anchor, uint32_t adid);\nvoid popup_add_item(struct popup* popup, struct bar_item* item);\nbool popup_set_drawing(struct popup* popup, bool drawing);\nvoid popup_remove_item(struct popup* popup, struct bar_item* bar_item);\n\nvoid popup_clear_pointers(struct popup* popup);\n\nuint32_t popup_get_width(struct popup* popup);\nvoid popup_calculate_bounds(struct popup* popup, struct bar* bar);\nvoid popup_draw(struct popup* popup);\nvoid popup_destroy(struct popup* popup);\n\nvoid popup_change_space(struct popup* popup, uint64_t dsid, uint32_t adid);\nvoid popup_serialize(struct popup* popup, char* indent, FILE* rsp);\nbool popup_parse_sub_domain(struct popup* popup, FILE* rsp, struct token property, char* message);\n"
  },
  {
    "path": "src/power.c",
    "content": "#include \"power.h\"\n#include \"event.h\"\n\nuint32_t g_power_source = 0;\n\nvoid power_handler(void* context) {\n  CFTypeRef info = IOPSCopyPowerSourcesInfo();\n  CFStringRef type = IOPSGetProvidingPowerSourceType(info);\n\n  if (CFStringCompare(type, POWER_AC_KEY, 0) == 0) {\n    if (g_power_source != POWER_AC) {\n      g_power_source = POWER_AC;\n      char source[8];\n      snprintf(source, 8, \"AC\");\n      struct event event = { (void*) source, POWER_SOURCE_CHANGED };\n      event_post(&event);\n    }\n  } else if (CFStringCompare(type, POWER_BATTERY_KEY, 0) == 0) {\n    if (g_power_source != POWER_BATTERY) {\n      g_power_source = POWER_BATTERY;\n      char source[8];\n      snprintf(source, 8, \"BATTERY\");\n\n      struct event event = { (void*) source, POWER_SOURCE_CHANGED };\n      event_post(&event);\n    }\n  }\n  CFRelease(info);\n}\n\nvoid forced_power_event() {\n  g_power_source = 0;\n  power_handler(NULL);\n}\n\nvoid begin_receiving_power_events() {\n  CFRunLoopSourceRef source = IOPSNotificationCreateRunLoopSource(power_handler, NULL);\n  CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);\n}\n"
  },
  {
    "path": "src/power.h",
    "content": "#include <CoreFoundation/CoreFoundation.h>\n#include <IOKit/ps/IOPowerSources.h>\n\n#define POWER_AC_KEY      CFSTR(kIOPMACPowerKey)\n#define POWER_BATTERY_KEY CFSTR(kIOPMBatteryPowerKey)\n#define POWER_UPS_KEY     CFSTR(kIOPMUPSPowerKey)\n\n#define POWER_AC 1\n#define POWER_BATTERY 2\n\nvoid forced_power_event();\nvoid begin_receiving_power_events();\n"
  },
  {
    "path": "src/shadow.c",
    "content": "#include \"shadow.h\"\n#include \"bar_manager.h\"\n\nvoid shadow_init(struct shadow* shadow) {\n  shadow->enabled = false;\n  shadow->angle = 30;\n  shadow->distance = 5;\n  shadow->offset.x = ((float)shadow->distance)\n                      *cos(((double)shadow->angle)*deg_to_rad);\n  shadow->offset.y = -((float)shadow->distance)\n                       *sin(((double)shadow->angle)*deg_to_rad);\n\n  color_init(&shadow->color, 0xff000000);\n}\n\nstatic bool shadow_set_enabled(struct shadow* shadow, bool enabled) {\n  if (shadow->enabled == enabled) return false;\n  shadow->enabled = enabled;\n  return true;\n}\n\nstatic bool shadow_set_angle(struct shadow* shadow, uint32_t angle) {\n  if (shadow->angle == angle) return false;\n  shadow->angle = angle;\n  shadow->offset.x = ((float)shadow->distance)*cos(((double)shadow->angle)*deg_to_rad);\n  shadow->offset.y = -((float)shadow->distance)*sin(((double)shadow->angle)*deg_to_rad);\n  return true;\n}\n\nstatic bool shadow_set_distance(struct shadow* shadow, uint32_t distance) {\n  if (shadow->distance == distance) return false;\n  shadow->distance = distance;\n  shadow->offset.x = ((float)shadow->distance)\n                      *cos(((double)shadow->angle)*deg_to_rad);\n  shadow->offset.y = -((float)shadow->distance)\n                      *sin(((double)shadow->angle)*deg_to_rad);\n  return true;\n}\n\nstatic bool shadow_set_color(struct shadow* shadow, uint32_t color) {\n  bool changed = shadow_set_enabled(shadow, true);\n  return color_set_hex(&shadow->color, color) || changed;\n}\n\nCGRect shadow_get_bounds(struct shadow* shadow, CGRect reference_bounds) {\n  return (CGRect){{reference_bounds.origin.x + shadow->offset.x,\n                   reference_bounds.origin.y + shadow->offset.y },\n                   reference_bounds.size                          };\n}\n\nvoid shadow_serialize(struct shadow* shadow, char* indent, FILE* rsp) {\n  fprintf(rsp, \"%s\\\"drawing\\\": \\\"%s\\\",\\n\"\n               \"%s\\\"color\\\": \\\"0x%x\\\",\\n\"\n               \"%s\\\"angle\\\": %u,\\n\"\n               \"%s\\\"distance\\\": %u\",\n               indent, format_bool(shadow->enabled),\n               indent, shadow->color.hex,\n               indent, shadow->angle,\n               indent, shadow->distance                   );\n}\n\nbool shadow_parse_sub_domain(struct shadow* shadow, FILE* rsp, struct token property, char* message) {\n  bool needs_refresh = false;\n  if (token_equals(property, PROPERTY_DRAWING)) {\n    needs_refresh = shadow_set_enabled(shadow,\n                                       evaluate_boolean_state(get_token(&message),\n                                                              shadow->enabled     ));\n  }\n  else if (token_equals(property, PROPERTY_DISTANCE)) {\n    struct token token = get_token(&message);\n    ANIMATE(shadow_set_distance,\n            shadow,\n            shadow->distance,\n            token_to_int(token) );\n  }\n  else if (token_equals(property, PROPERTY_ANGLE)) {\n    struct token token = get_token(&message);\n    ANIMATE(shadow_set_angle,\n            shadow,\n            shadow->angle,\n            token_to_int(token));\n  }\n  else if (token_equals(property, PROPERTY_COLOR)) {\n    struct token token = get_token(&message);\n    ANIMATE_BYTES(shadow_set_color,\n                  shadow,\n                  shadow->color.hex,\n                  token_to_int(token)                );\n  }\n  else {\n    struct key_value_pair key_value_pair = get_key_value_pair(property.text,\n                                                              '.'           );\n    if (key_value_pair.key && key_value_pair.value) {\n      struct token subdom = {key_value_pair.key,strlen(key_value_pair.key)};\n      struct token entry = {key_value_pair.value,strlen(key_value_pair.value)};\n      if (token_equals(subdom, SUB_DOMAIN_COLOR)) {\n        return color_parse_sub_domain(&shadow->color, rsp, entry, message);\n      }\n      else {\n        respond(rsp, \"[!] Shadow: Invalid subdomain '%s'\\n\", subdom.text);\n      }\n    } else {\n      respond(rsp, \"[!] Shadow: Invalid property '%s'\\n\", property.text);\n    }\n  }\n\n  return needs_refresh;\n}\n\n"
  },
  {
    "path": "src/shadow.h",
    "content": "#pragma once\n#include \"misc/helpers.h\"\n#include \"color.h\"\n\nstruct shadow {\n  bool enabled;\n\n  uint32_t angle;\n  uint32_t distance;\n  CGPoint offset;   \n\n  struct color color;\n};\n\nvoid shadow_init(struct shadow* shadow);\nCGRect shadow_get_bounds(struct shadow* shadow, CGRect reference_bounds);\n\nvoid shadow_serialize(struct shadow* shadow, char* indent, FILE* rsp);\nbool shadow_parse_sub_domain(struct shadow* shadow, FILE* rsp, struct token property, char* message);\n"
  },
  {
    "path": "src/sketchybar.c",
    "content": "#include \"bar_manager.h\"\n#include \"event.h\"\n#include \"workspace.h\"\n#include \"mach.h\"\n#include \"mouse.h\"\n#include \"message.h\"\n#include \"power.h\"\n#include \"wifi.h\"\n#include \"misc/help.h\"\n#include \"media.h\"\n#include \"hotload.h\"\n#include <libgen.h>\n\n#define LCFILE_PATH_FMT  \"/tmp/%s_%s.lock\"\n\n#define CLIENT_OPT_LONG  \"--message\"\n#define CLIENT_OPT_SHRT  \"-m\"\n\n#define VERSION_OPT_LONG \"--version\"\n#define VERSION_OPT_SHRT \"-v\"\n\n#define CONFIG_OPT_LONG  \"--config\"\n#define CONFIG_OPT_SHRT  \"-c\"\n\n#define HELP_OPT_LONG    \"--help\"\n#define HELP_OPT_SHRT    \"-h\"\n\n#define MAJOR 2\n#define MINOR 23\n#define PATCH 0\n\n#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 140000\nextern CGError SLSWindowManagementBridgeSetDelegate(void* delegate);\n#endif\n\nextern CGError SLSRegisterNotifyProc(void* callback, uint32_t event, void* context);\nextern int SLSGetSpaceManagementMode(int cid);\nextern int SLSMainConnectionID(void);\nextern int RunApplicationEventLoop(void);\n\nint g_connection;\nCFTypeRef g_transaction;\nint g_space_management_mode;\n\nstruct bar_manager g_bar_manager;\nstruct mach_server g_mach_server;\nvoid *g_workspace_context;\n\nchar g_name[256];\nchar g_config_file[4096];\nchar g_lock_file[MAXLEN];\nbool g_volume_events;\nbool g_brightness_events;\nint64_t g_disable_capture = 0;\npid_t g_pid = 0;\n\nstatic int client_send_message(int argc, char **argv) {\n  if (argc <= 1) {\n    return EXIT_SUCCESS;\n  }\n\n  char *user = getenv(\"USER\");\n  if (!user) {\n    error(\"sketchybar-msg: 'env USER' not set! abort..\\n\");\n  }\n\n  int message_length = argc;\n  int argl[argc];\n\n  for (int i = 1; i < argc; ++i) {\n    argl[i] = strlen(argv[i]);\n    message_length += argl[i] + 1;\n  }\n\n  char* message = malloc((sizeof(char) * (message_length + 1)));\n  char* temp = message;\n\n  for (int i = 1; i < argc; ++i) {\n    memcpy(temp, argv[i], argl[i]);\n    temp += argl[i];\n    *temp++ = '\\0';\n  }\n  *temp++ = '\\0';\n\n  char bs_name[256];\n  snprintf(bs_name, 256, MACH_BS_NAME_FMT, g_name);\n\n  char* rsp = mach_send_message(mach_get_bs_port(bs_name),\n                                message,\n                                message_length,\n                                true                     );\n\n  free(message);\n  if (!rsp) return EXIT_SUCCESS;\n\n  if (strlen(rsp) > 2 && rsp[1] == '!') {\n    fprintf(stderr, \"%s\", rsp);\n    return EXIT_FAILURE;\n  } else {\n    fprintf(stdout, \"%s\", rsp);\n  }\n  free(rsp);\n\n  return EXIT_SUCCESS;\n}\n\nstatic void acquire_lockfile(void) {\n  int handle = open(g_lock_file, O_CREAT | O_WRONLY, 0600);\n  if (handle == -1) {\n    error(\"%s: could not create lock-file! abort..\\n\", g_name);\n  }\n\n  struct flock lockfd = {\n    .l_start  = 0,\n    .l_len    = 0,\n    .l_pid    = getpid(),\n    .l_type   = F_WRLCK,\n    .l_whence = SEEK_SET\n  };\n\n  if (fcntl(handle, F_SETLK, &lockfd) == -1) {\n    error(\"%s: could not acquire lock-file... already running?\\n\", g_name);\n  }\n}\n\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\nstatic inline void init_misc_settings(void) {\n  char *user = getenv(\"USER\");\n  if (!user) {\n    error(\"%s: 'env USER' not set! abort..\\n\", g_name);\n  }\n\n  snprintf(g_lock_file, sizeof(g_lock_file), LCFILE_PATH_FMT, g_name, user);\n\n  signal(SIGCHLD, SIG_IGN);\n  signal(SIGPIPE, SIG_IGN);\n  CGSetLocalEventsSuppressionInterval(0.0f);\n  CGEnableEventStateCombining(false);\n\n  g_connection = SLSMainConnectionID();\n  g_space_management_mode = SLSGetSpaceManagementMode(g_connection);\n\n  g_volume_events = false;\n  g_brightness_events = false;\n}\n#pragma clang diagnostic pop\n\nstatic void parse_arguments(int argc, char **argv) {\n  if ((string_equals(argv[1], VERSION_OPT_LONG))\n      || (string_equals(argv[1], VERSION_OPT_SHRT))) {\n    fprintf(stdout, \"sketchybar-v%d.%d.%d\\n\", MAJOR, MINOR, PATCH);\n    exit(EXIT_SUCCESS);\n  } else if ((string_equals(argv[1], HELP_OPT_LONG))\n      || (string_equals(argv[1], HELP_OPT_SHRT))) {\n    printf(help_str, argv[0]);\n    exit(EXIT_SUCCESS);\n  } else if ((string_equals(argv[1], CLIENT_OPT_LONG))\n             || (string_equals(argv[1], CLIENT_OPT_SHRT))) {\n    exit(client_send_message(argc-1, argv+1));\n  } else if ((string_equals(argv[1], CONFIG_OPT_LONG))\n             || (string_equals(argv[1], CONFIG_OPT_SHRT))) {\n    if (argc < 3) {\n      printf(\"[!] Error: Too few arguments for argument 'config'.\\n\");\n    } else {\n      if (set_config_file_path(argv[2])) return;\n      printf(\"[!] Error: Specified config file path invalid.\\n\");\n    }\n    exit(EXIT_FAILURE);\n  }\n\n  exit(client_send_message(argc, argv));\n}\n\nstatic void space_events(uint32_t event, void* data, size_t data_length, void* context) {\n  struct event ev = { NULL, SPACE_CHANGED };\n  event_post(&ev);\n}\n\nstatic void system_events(uint32_t event, void* data, size_t data_length, void* context) {\n  if (event == 1322) {\n    g_disable_capture = clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW_APPROX);\n  } else if (event == 905) {\n    g_disable_capture = -1;\n  } else {\n    g_disable_capture = 0;\n  }\n}\n\nint main(int argc, char **argv) {\n  snprintf(g_name, sizeof(g_name), \"%s\", basename(argv[0]));\n\n  if (is_root())\n    error(\"%s: running as root is not allowed! abort..\\n\", g_name);\n\n  setenv(\"BAR_NAME\", g_name, 1);\n\n  if (argc > 1) parse_arguments(argc, argv);\n\n  pid_for_task(mach_task_self(), &g_pid);\n  init_misc_settings();\n  acquire_lockfile();\n\n  SLSRegisterNotifyProc((void*)system_events, 904, NULL);\n  SLSRegisterNotifyProc((void*)system_events, 905, NULL);\n  SLSRegisterNotifyProc((void*)system_events, 1401, NULL);\n  SLSRegisterNotifyProc((void*)system_events, 1508, NULL);\n  SLSRegisterNotifyProc((void*)system_events, 1322, NULL);\n\n  if (__builtin_available(macOS 13.0, *)) {\n    SLSRegisterNotifyProc((void*)space_events, 1327, NULL);\n    SLSRegisterNotifyProc((void*)space_events, 1328, NULL);\n  }\n\n  struct event init = { NULL, INIT_MUTEX };\n  event_post(&init);\n\n  workspace_event_handler_init(&g_workspace_context);\n  bar_manager_init(&g_bar_manager);\n\n  mouse_begin();\n  display_begin();\n  workspace_event_handler_begin(&g_workspace_context);\n\n  windows_freeze();\n  bar_manager_begin(&g_bar_manager);\n  windows_unfreeze();\n\n  if (!mach_server_begin(&g_mach_server, mach_message_handler))\n    error(\"%s: could not initialize daemon! abort..\\n\", g_name);\n\n  begin_receiving_power_events();\n  begin_receiving_network_events();\n  initialize_media_events();\n\n  exec_config_file();\n  begin_receiving_config_change_events();\n\n  #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 140000\n  if (__builtin_available(macos 14.0, *)) {\n    SLSWindowManagementBridgeSetDelegate(NULL);\n  }\n  #endif\n\n  RunApplicationEventLoop();\n  return 0;\n}\n"
  },
  {
    "path": "src/slider.c",
    "content": "#include \"slider.h\"\n#include \"bar_manager.h\"\n#include \"animation.h\"\n\nstatic bool slider_set_width(struct slider* slider, uint32_t width) {\n  if (width == slider->background.bounds.size.width) return false;\n  slider->background.bounds.size.width = width;\n  return true;\n}\n\nstatic bool slider_set_foreground_color(struct slider* slider, uint32_t color) {\n  if (slider->foreground_color == color) return false;\n  slider->foreground_color = color;\n  return background_set_color(&slider->foreground, color);\n}\n\nstatic bool slider_set_percentage(struct slider* slider, uint32_t percentage) {\n  if (percentage == slider->percentage) return false;\n  slider->percentage = max(min(percentage, 100), 0);\n  return true;\n}\n\nuint32_t slider_get_percentage_for_point(struct slider* slider, CGPoint point) {\n  float delta = point.x - slider->background.bounds.origin.x;\n  if (delta < 0) delta = 0;\n  uint32_t percentage = delta / slider->background.bounds.size.width * 100.f\n                        + 0.5f;\n\n  return min(percentage, 100);\n}\n\nvoid slider_cancel_drag(struct slider* slider) {\n  slider->is_dragged = false;\n}\n\nbool slider_handle_drag(struct slider* slider, CGPoint point) {\n  uint32_t percentage = slider_get_percentage_for_point(slider, point);\n  slider->is_dragged = true;\n  return slider_set_percentage(slider, percentage);\n}\n\nvoid slider_init(struct slider* slider) {\n  slider->percentage = 0;\n  slider->background.bounds.size.width = 100;\n  slider->is_dragged = false;\n\n  slider->foreground_color = 0xff0000ff;\n  text_init(&slider->knob);\n  background_init(&slider->background);\n  background_init(&slider->foreground);\n  background_set_color(&slider->background, 0xff000000);\n  background_set_color(&slider->foreground, slider->foreground_color);\n}\n\nvoid slider_clear_pointers(struct slider* slider) {\n  background_clear_pointers(&slider->background);\n  background_clear_pointers(&slider->foreground);\n  text_clear_pointers(&slider->knob);\n}\n\nvoid slider_setup(struct slider* slider, uint32_t width) {\n  slider->background.bounds.size.width = width;\n  background_set_enabled(&slider->background, true);\n  background_set_enabled(&slider->foreground, true);\n}\n\nuint32_t slider_get_length(struct slider* slider) {\n  return slider->background.bounds.size.width;\n}\n\nvoid slider_calculate_bounds(struct slider* slider, uint32_t x, uint32_t y) {\n  background_calculate_bounds(&slider->background,\n                              x,\n                              y,\n                              slider->background.bounds.size.width,\n                              slider->background.bounds.size.height);\n\n  background_calculate_bounds(&slider->foreground,\n                              x,\n                              y,\n                              slider->background.bounds.size.width\n                              * ((float)slider->percentage)/100.f,\n                              slider->background.bounds.size.height);\n\n  int32_t raw_offset = ((float)slider->percentage)/100.f\n                        * slider->background.bounds.size.width\n                        - slider->knob.bounds.size.width / 2.;\n\n  uint32_t knob_offset = max(min(raw_offset,\n                                 slider->background.bounds.size.width\n                                 - (slider->knob.bounds.size.width + 1.f)), 0.f);\n\n  text_calculate_bounds(&slider->knob, x + knob_offset, y);\n}\n\nvoid slider_draw(struct slider* slider, CGContextRef context) {\n  background_draw(&slider->background, context);\n  background_draw(&slider->foreground, context);\n  text_draw(&slider->knob, context);\n}\n\nvoid slider_destroy(struct slider* slider) {\n  background_destroy(&slider->background);\n  background_destroy(&slider->foreground);\n  text_destroy(&slider->knob);\n  slider_clear_pointers(slider);\n}\n\nvoid slider_serialize(struct slider* slider, char* indent, FILE* rsp) {\n  fprintf(rsp, \"%s\\\"highlight_color\\\": \\\"0x%x\\\",\\n\"\n               \"%s\\\"percentage\\\": \\\"%d\\\",\\n\"\n               \"%s\\\"width\\\": \\\"%d\\\",\\n\",\n               indent, slider->foreground_color,\n               indent, slider->percentage,\n               indent, (int)slider->background.bounds.size.width);\n\n  char deeper_indent[strlen(indent) + 2];\n  snprintf(deeper_indent, strlen(indent) + 2, \"%s\\t\", indent);\n\n  fprintf(rsp, \"%s\\\"background\\\": {\\n\", indent);\n  background_serialize(&slider->background, deeper_indent, rsp, false);\n  fprintf(rsp, \"\\n%s},\\n\", indent);\n\n  fprintf(rsp, \"%s\\\"knob\\\": {\\n\", indent);\n  text_serialize(&slider->knob, deeper_indent, rsp);\n  fprintf(rsp, \"\\n%s}\", indent);\n}\n\nbool slider_parse_sub_domain(struct slider* slider, FILE* rsp, struct token property, char* message) {\n  bool needs_refresh = false;\n  if (token_equals(property, PROPERTY_PERCENTAGE)) {\n    struct token token = get_token(&message);\n    if (!slider->is_dragged) {\n      ANIMATE(slider_set_percentage,\n              slider,\n              slider->percentage,\n              token_to_uint32t(token));\n    }\n  }\n  else if (token_equals(property, PROPERTY_HIGHLIGHT_COLOR)) {\n    struct token token = get_token(&message);\n    ANIMATE_BYTES(slider_set_foreground_color,\n                  slider,\n                  slider->foreground_color,\n                  token_to_uint32t(token)     );\n  }\n  else if (token_equals(property, PROPERTY_WIDTH)) {\n    struct token token = get_token(&message);\n    if (!slider->is_dragged) {\n      ANIMATE(slider_set_width,\n              slider,\n              slider->background.bounds.size.width,\n              token_to_uint32t(token)              );\n    }\n  }\n  else if (token_equals(property, SUB_DOMAIN_KNOB)) {\n    struct token dummy = { PROPERTY_STRING, strlen(PROPERTY_STRING)};\n    needs_refresh = text_parse_sub_domain(&slider->knob,\n                                          rsp,\n                                          dummy,\n                                          message        );\n  }\n  else {\n    struct key_value_pair key_value_pair = get_key_value_pair(property.text, '.');\n    if (key_value_pair.key && key_value_pair.value) {\n      struct token subdom = { key_value_pair.key, strlen(key_value_pair.key) };\n      struct token entry = { key_value_pair.value, strlen(key_value_pair.value) };\n      if (token_equals(subdom, SUB_DOMAIN_BACKGROUND)) {\n        background_parse_sub_domain(&slider->foreground, rsp, entry, message);\n        background_set_color(&slider->foreground, slider->foreground_color);\n        return background_parse_sub_domain(&slider->background, rsp, entry, message);\n      }\n      else if (token_equals(subdom, SUB_DOMAIN_KNOB))\n        return text_parse_sub_domain(&slider->knob, rsp, entry, message);\n      else {\n        respond(rsp, \"[!] Slider: Invalid subdomain '%s' \\n\", subdom.text);\n      }\n    }\n    else {\n      respond(rsp, \"[!] Slider: Invalid property '%s'\\n\", property.text);\n    }\n  }\n\n  return needs_refresh;\n}\n"
  },
  {
    "path": "src/slider.h",
    "content": "#pragma once\n\n#include \"background.h\"\n#include \"text.h\"\n\nstruct slider {\n  bool is_dragged;\n  uint32_t percentage;\n  uint32_t foreground_color;\n\n  struct text knob;\n  struct background background;\n  struct background foreground;\n};\n\nvoid slider_init(struct slider* slider);\nvoid slider_clear_pointers(struct slider* slider);\nvoid slider_setup(struct slider* slider, uint32_t width);\nvoid slider_calculate_bounds(struct slider* slider, uint32_t x, uint32_t y);\nvoid slider_draw(struct slider* slider, CGContextRef context);\nbool slider_handle_drag(struct slider* slider, CGPoint point);\n\nuint32_t slider_get_percentage_for_point(struct slider* slider, CGPoint point);\nuint32_t slider_get_length(struct slider* slider);\n\nvoid slider_cancel_drag(struct slider* slider);\nvoid slider_destroy(struct slider* slider);\nvoid slider_serialize(struct slider* slider, char* indent, FILE* rsp);\nbool slider_parse_sub_domain(struct slider* graph, FILE* rsp, struct token property, char* message);\n"
  },
  {
    "path": "src/text.c",
    "content": "#include \"text.h\"\n#include \"bar_manager.h\"\n\nstatic void text_calculate_truncated_width(struct text* text, CFDictionaryRef attributes) {\n  if (text->max_chars > 0) {\n    uint32_t len = strlen(text->string) + 4;\n    char buffer[len];\n    memset(buffer, 0, len);\n\n    char* read = text->string;\n    char* write = buffer;\n    uint32_t counter = 0;\n    while (*read) {\n      if ((*read & 0xC0) != 0x80) counter++; \n      if (counter > text->max_chars) {\n        break;\n      }\n      *write++ = *read++;\n    }\n\n    CFStringRef string = CFStringCreateWithCString(NULL,\n                                                   buffer,\n                                                   kCFStringEncodingUTF8);\n\n    if (string) {\n      CFAttributedStringRef attr_string = CFAttributedStringCreate(NULL,\n                                                                   string,\n                                                                   attributes);\n\n      CTLineRef line = CTLineCreateWithAttributedString(attr_string);\n\n      CGRect bounds = CTLineGetBoundsWithOptions(line,\n                                              kCTLineBoundsUseGlyphPathBounds);\n      text->width = (uint32_t)(bounds.size.width + 1.5);\n      CFRelease(attr_string);\n      CFRelease(line);\n      CFRelease(string);\n    }\n  }\n}\n\nstatic void text_prepare_line(struct text* text) {\n  const void *keys[] = { kCTFontAttributeName,\n                         kCTForegroundColorFromContextAttributeName };\n\n  if (text->font.font_changed) {\n    font_create_ctfont(&text->font);\n    text->font.font_changed = false;\n  }\n  const void *values[] = { text->font.ct_font, kCFBooleanTrue };\n  CFDictionaryRef attributes = CFDictionaryCreate(NULL,\n                                                  keys,\n                                                  values,\n                                                  array_count(keys),\n                                                  &kCFTypeDictionaryKeyCallBacks,\n                                                  &kCFTypeDictionaryValueCallBacks);\n\n  CFStringRef string = CFStringCreateWithCString(NULL,\n                                                 text->string,\n                                                 kCFStringEncodingUTF8);\n\n  if (!string) string = CFStringCreateWithCString(NULL,\n                                          \"Warning: Malformed UTF-8 string\",\n                                          kCFStringEncodingUTF8             );\n\n  CFAttributedStringRef attr_string = CFAttributedStringCreate(NULL,\n                                                               string,\n                                                               attributes);\n\n  text->line.line = CTLineCreateWithAttributedString(attr_string);\n\n  CTLineGetTypographicBounds(text->line.line,\n                             &text->line.ascent,\n                             &text->line.descent,\n                             NULL                );\n\n  text->bounds = CTLineGetBoundsWithOptions(text->line.line,\n                                            kCTLineBoundsUseGlyphPathBounds);\n\n  text->bounds.size.width = (uint32_t) (text->bounds.size.width + 1.5);\n  text->bounds.size.height = (uint32_t) (text->bounds.size.height + 1.5);\n  text->bounds.origin.x = (int32_t) (text->bounds.origin.x + 0.5);\n  text->bounds.origin.y = (int32_t) (text->bounds.origin.y + 0.5);\n\n  text->width = text->bounds.size.width;\n\n  CFRelease(string);\n  CFRelease(attr_string);\n\n  text_calculate_truncated_width(text, attributes);\n  CFRelease(attributes);\n}\n\nstatic void text_destroy_line(struct text* text) {\n  if (text->line.line) CFRelease(text->line.line);\n  text->line.line = NULL;\n}\n\nbool text_set_max_chars(struct text* text, uint32_t max_chars) {\n  if (text->max_chars == max_chars) return false;\n  text->max_chars = max_chars;\n  if (strlen(text->string) > text->max_chars) {\n    text_set_string(text, text->string, true);\n  }\n  return strlen(text->string) > text->max_chars;\n}\n\nbool text_set_string(struct text* text, char* string, bool forced) {\n  if (!string) return false;\n  if (!forced && text->string && strcmp(text->string, string) == 0) { \n    if (!(string == text->string)) free(string);\n    return false; \n  }\n  if (text->line.line) text_destroy_line(text);\n  if (string != text->string && text->string) free(text->string);\n  text->string = string;\n  text_prepare_line(text);\n  return true;\n}\n\nvoid text_copy(struct text* text, struct text* source) {\n  font_set_family(&text->font, string_copy(source->font.family), true);\n  font_set_style(&text->font, string_copy(source->font.style), true);\n  font_set_size(&text->font, source->font.size);\n  text_set_string(text, string_copy(source->string), true);\n}\n\nbool text_set_font(struct text* text, char* font_string, bool forced) {\n  bool changed = font_set(&text->font, font_string, forced);\n  return changed;\n}\n\nvoid text_init(struct text* text) {\n  text->drawing = true;\n  text->highlight = false;\n  text->has_const_width = false;\n  text->custom_width = 0;\n  text->padding_left = 0;\n  text->padding_right = 0;\n  text->y_offset = 0;\n  text->max_chars = 0;\n  text->align = POSITION_LEFT;\n  text->scroll = 0.f;\n  text->scroll_duration = 100;\n\n  text->string = string_copy(\"\");\n  text_set_string(text, text->string, false);\n  shadow_init(&text->shadow);\n  background_init(&text->background);\n  font_init(&text->font);\n\n  color_init(&text->color, 0xffffffff);\n  color_init(&text->highlight_color, 0xff000000);\n}\n\nstatic bool text_set_color(struct text* text, uint32_t color) {\n  return color_set_hex(&text->color, color);\n}\n\nstatic bool text_set_highlight_color(struct text* text, uint32_t color) {\n  return color_set_hex(&text->highlight_color, color);\n}\n\nstatic bool text_set_padding_left(struct text* text, int padding) {\n  if (text->padding_left == padding) return false;\n  text->padding_left = padding;\n  return true;\n}\n\nstatic bool text_set_padding_right(struct text* text, int padding) {\n  if (text->padding_right == padding) return false;\n  text->padding_right = padding;\n  return true;\n}\n\nstatic bool text_set_yoffset(struct text* text, int offset) {\n  if (text->y_offset == offset) return false;\n  text->y_offset = offset;\n  return true;\n}\n\nstatic bool text_set_scroll_duration(struct text* text, int duration) {\n  if (duration < 0) return false;\n  text->scroll_duration = duration;\n  return false;\n}\n\nstatic bool text_set_width(struct text* text, int width) {\n  if (width < 0) {\n    bool prev = text->has_const_width;\n    text->has_const_width = false;\n    return prev != text->has_const_width;\n  }\n\n  if (text->custom_width == width && text->has_const_width) return false;\n  text->custom_width = width;\n  text->has_const_width = true;\n  return true;\n}\n\nvoid text_clear_pointers(struct text* text) {\n  text->string = NULL;\n  text->line.line = NULL;\n  background_clear_pointers(&text->background);\n  font_clear_pointers(&text->font);\n}\n\nuint32_t text_get_length(struct text* text, bool override) {\n  if (!text->drawing) return 0;\n\n  if (text->font.font_changed) {\n    text_set_string(text, text->string, true);\n  }\n\n  int len = text->width + text->padding_left + text->padding_right;\n  if ((!text->has_const_width || override)\n      && text->background.enabled\n      && text->background.image.enabled) {\n    CGSize image_size = image_get_size(&text->background.image);\n    if (image_size.width > len) {\n      return image_size.width;\n    }\n  }\n\n  if (text->has_const_width && !override) return text->custom_width;\n  return (len < 0 ? 0 : len);\n}\n\nuint32_t text_get_height(struct text* text) {\n  return text->drawing ? text->bounds.size.height : 0;\n}\n\nvoid text_destroy(struct text* text) {\n  background_destroy(&text->background);\n  font_destroy(&text->font);\n\n  if (text->string) free(text->string);\n  text_destroy_line(text);\n  text_clear_pointers(text);\n}\n\nvoid text_calculate_bounds(struct text* text, uint32_t x, uint32_t y) {\n  if (text->align == POSITION_CENTER && text->has_const_width)\n    text->bounds.origin.x = (int)x + ((int)text->custom_width\n                                 - (int)text_get_length(text, true)) / 2;\n  else if (text->align == POSITION_RIGHT && text->has_const_width)\n    text->bounds.origin.x = (int)x + (int)text->custom_width\n                            - (int)text_get_length(text, true);\n  else\n    text->bounds.origin.x = x;\n\n  text->bounds.origin.y =(uint32_t)(y - ((text->line.ascent\n                                          - text->line.descent) / 2));\n\n  if (text->background.enabled) {\n    uint32_t height = text->background.overrides_height\n                      ? text->background.bounds.size.height\n                      : text->bounds.size.height;\n\n    background_calculate_bounds(&text->background,\n                                x,\n                                y,\n                                text_get_length(text, false),\n                                height                       );\n  }\n}\n\nbool text_set_scroll(struct text* text, float scroll) {\n  if (text->scroll == scroll) return false;\n  text->scroll = scroll;\n  return true;\n}\n\nbool text_animate_scroll(struct text* text) {\n  if (text->max_chars == 0) return false;\n  if (text->scroll != 0) return false;\n  if (text->has_const_width && text->custom_width < text->width) return false;\n  if (text->width == 0 || text->width == text->bounds.size.width) return false;\n\n  g_bar_manager.animator.duration = text->scroll_duration\n                                    * (text->bounds.size.width / text->width);\n  g_bar_manager.animator.interp_function = INTERP_FUNCTION_LINEAR;\n\n  bool needs_refresh = false;\n  ANIMATE_FLOAT(text_set_scroll,\n                text,\n                text->scroll,\n                max(text->bounds.size.width, 0));\n\n  struct animation* animation = animation_create();\n  float initial_value = text->scroll;\n  float final_value = -max(text->width, 0);\n\n  animation_setup(animation,\n                  (void*)text,\n                  (bool (*)(void*, int))text_set_scroll,\n                  *(int*)&initial_value,\n                  *(int*)&final_value,\n                  0,\n                  INTERP_FUNCTION_LINEAR );\n  animation->as_float = true;\n  animator_add(&g_bar_manager.animator, animation);\n\n  g_bar_manager.animator.duration = text->scroll_duration;\n  ANIMATE_FLOAT(text_set_scroll, text, text->scroll, 0);\n\n  g_bar_manager.animator.duration = 0;\n  g_bar_manager.animator.interp_function = '\\0';\n\n  return needs_refresh;\n}\n\nvoid text_draw(struct text* text, CGContextRef context) {\n  if (!text->drawing) return;\n  if (text->background.enabled)\n    background_draw(&text->background, context);\n\n  CGContextSaveGState(context);\n  if (text->max_chars > 0) {\n    CGMutablePathRef path = CGPathCreateMutable();\n    CGRect bounds = text->bounds;\n    bounds.size.width = text->width;\n    bounds.origin.x += text->padding_left;\n    bounds.origin.y = -9999.f;\n    bounds.size.height = 2.f*9999.f;\n\n    CGPathAddRect(path, NULL, bounds);\n\n    CGContextAddPath(context, path);\n    CGContextClip(context);\n    CFRelease(path);\n  }\n\n  if (text->shadow.enabled) {\n    CGContextSetRGBFillColor(context,\n                             text->shadow.color.r,\n                             text->shadow.color.g,\n                             text->shadow.color.b,\n                             text->shadow.color.a );\n\n    CGRect bounds = shadow_get_bounds(&text->shadow, text->bounds);\n    CGContextSetTextPosition(context,\n                             bounds.origin.x + text->padding_left,\n                             bounds.origin.y + text->y_offset     );\n    CTLineDraw(text->line.line, context);\n  }\n\n  struct color color = text->highlight ? text->highlight_color : text->color;\n  CGContextSetRGBFillColor(context, color.r, color.g, color.b, color.a);\n\n  CGContextSetTextPosition(context,\n                           text->bounds.origin.x + text->padding_left\n                           - text->scroll,\n                           text->bounds.origin.y + text->y_offset    );\n  CTLineDraw(text->line.line, context);\n  CGContextRestoreGState(context);\n}\n\nvoid text_serialize(struct text* text, char* indent, FILE* rsp) {\n  char align[32] = { 0 };\n  switch (text->align) {\n    case POSITION_LEFT:\n      snprintf(align, 32, \"left\");\n      break;\n    case POSITION_RIGHT:\n      snprintf(align, 32, \"right\");\n      break;\n    case POSITION_CENTER:\n      snprintf(align, 32, \"center\");\n      break;\n    case POSITION_BOTTOM:\n      snprintf(align, 32, \"bottom\");\n      break;\n    case POSITION_TOP:\n      snprintf(align, 32, \"top\");\n      break;\n    default:\n      snprintf(align, 32, \"invalid\");\n      break;\n  }\n\n  fprintf(rsp, \"%s\\\"value\\\": \\\"%s\\\",\\n\"\n               \"%s\\\"drawing\\\": \\\"%s\\\",\\n\"\n               \"%s\\\"highlight\\\": \\\"%s\\\",\\n\"\n               \"%s\\\"color\\\": \\\"0x%x\\\",\\n\"\n               \"%s\\\"highlight_color\\\": \\\"0x%x\\\",\\n\"\n               \"%s\\\"padding_left\\\": %d,\\n\"\n               \"%s\\\"padding_right\\\": %d,\\n\"\n               \"%s\\\"y_offset\\\": %d,\\n\"\n               \"%s\\\"font\\\": \\\"%s:%s:%.2f\\\",\\n\"\n               \"%s\\\"width\\\": %d,\\n\"\n               \"%s\\\"scroll_duration\\\": %d,\\n\"\n               \"%s\\\"align\\\": \\\"%s\\\",\\n\"\n               \"%s\\\"background\\\": {\\n\",\n               indent, text->string,\n               indent, format_bool(text->drawing),\n               indent, format_bool(text->highlight),\n               indent, text->color.hex,\n               indent, text->highlight_color.hex,\n               indent, text->padding_left,\n               indent, text->padding_right,\n               indent, text->y_offset,\n               indent, text->font.family, text->font.style, text->font.size,\n               indent, text->custom_width,\n               indent, text->scroll_duration,\n               indent, align, indent                                        );\n\n  char deeper_indent[strlen(indent) + 2];\n  snprintf(deeper_indent, strlen(indent) + 2, \"%s\\t\", indent);\n  background_serialize(&text->background, deeper_indent, rsp, true);\n\n  fprintf(rsp, \"\\n%s},\\n%s\\\"shadow\\\": {\\n\", indent, indent);\n  shadow_serialize(&text->shadow, deeper_indent, rsp);\n  fprintf(rsp, \"\\n%s}\", indent);\n}\n\nbool text_parse_sub_domain(struct text* text, FILE* rsp, struct token property, char* message) {\n  bool needs_refresh = false;\n  if (token_equals(property, PROPERTY_COLOR)) {\n    struct token token = get_token(&message);\n    ANIMATE_BYTES(text_set_color,\n                  text,\n                  text->color.hex,\n                  token_to_int(token));\n  }\n  else if (token_equals(property, PROPERTY_HIGHLIGHT)) {\n    bool highlight = evaluate_boolean_state(get_token(&message),\n                                             text->highlight    );\n    if (g_bar_manager.animator.duration > 0) {\n      if (text->highlight && !highlight) {\n        animator_cancel(&g_bar_manager.animator,\n                        text,\n                        (animator_function*)text_set_color);\n\n        uint32_t target = text->color.hex;\n        text_set_color(text, text->highlight_color.hex);\n\n        ANIMATE_BYTES(text_set_color,\n                      text,\n                      text->color.hex,\n                      target          );\n      }\n      else if (!text->highlight && highlight) {\n        animator_cancel(&g_bar_manager.animator,\n                        text,\n                        (animator_function*)text_set_highlight_color);\n\n        uint32_t target = text->highlight_color.hex;\n        text_set_highlight_color(text, text->color.hex);\n\n        ANIMATE_BYTES(text_set_highlight_color,\n                      text,\n                      text->highlight_color.hex,\n                      target                    );\n      }\n    }\n\n    needs_refresh = text->highlight != highlight;\n    text->highlight = highlight;\n  } else if (token_equals(property, PROPERTY_FONT))\n    needs_refresh = text_set_font(text, string_copy(message), false);\n  else if (token_equals(property, PROPERTY_HIGHLIGHT_COLOR)) {\n    struct token token = get_token(&message);\n    ANIMATE_BYTES(text_set_highlight_color,\n                  text,\n                  text->highlight_color.hex,\n                  token_to_int(token)       );\n\n  } else if (token_equals(property, PROPERTY_PADDING_LEFT)) {\n    struct token token = get_token(&message);\n    ANIMATE(text_set_padding_left,\n            text,\n            text->padding_left,\n            token_to_int(token)  );\n\n  } else if (token_equals(property, PROPERTY_PADDING_RIGHT)) {\n    struct token token = get_token(&message);\n    ANIMATE(text_set_padding_right,\n            text,\n            text->padding_right,\n            token_to_int(token)    );\n\n  } else if (token_equals(property, PROPERTY_YOFFSET)) {\n    struct token token = get_token(&message);\n    ANIMATE(text_set_yoffset,\n            text,\n            text->y_offset,\n            token_to_int(token));\n\n  } else if (token_equals(property, PROPERTY_SCROLL_DURATION)) {\n    struct token token = get_token(&message);\n    text_set_scroll_duration(text, token_to_int(token));\n  } else if (token_equals(property, PROPERTY_WIDTH)) {\n    struct token token = get_token(&message);\n    if (token_equals(token, ARGUMENT_DYNAMIC)) {\n      ANIMATE(text_set_width,\n              text,\n              text->custom_width,\n              text_get_length(text, true));\n\n      struct animation* animation = animation_create();\n      animation_setup(animation,\n                      text,\n                      (bool (*)(void*, int))&text_set_width,\n                      text->custom_width,\n                      -1,\n                      0,\n                      INTERP_FUNCTION_LINEAR               );\n      animator_add(&g_bar_manager.animator, animation);\n    }\n    else {\n      ANIMATE(text_set_width,\n              text,\n              text_get_length(text, false),\n              token_to_int(token)          );\n    }\n  } else if (token_equals(property, PROPERTY_DRAWING)) {\n    bool prev = text->drawing;\n    text->drawing = evaluate_boolean_state(get_token(&message), text->drawing);\n    return prev != text->drawing;\n  } else if (token_equals(property, PROPERTY_ALIGN)) {\n    char prev = text->align;\n    text->align = get_token(&message).text[0];\n    return prev != text->align;\n  } else if (token_equals(property, PROPERTY_STRING)) {\n    uint32_t pre_width = text_get_length(text, false);\n    bool changed = text_set_string(text,\n                                   token_to_string(get_token(&message)),\n                                   false                                );\n\n    if (changed\n        && g_bar_manager.animator.duration > 0) {\n      uint32_t post_width = text_get_length(text, false);\n      if (post_width != pre_width) {\n        text_set_width(text, pre_width);\n        ANIMATE(text_set_width, text, pre_width, post_width);\n\n        struct animation* animation = animation_create();\n        animation_setup(animation,\n                        text,\n                        (bool (*)(void*, int))&text_set_width,\n                        text->custom_width,\n                        -1,\n                        0,\n                        INTERP_FUNCTION_LINEAR               );\n        animator_add(&g_bar_manager.animator, animation);\n      }\n    }\n\n    return changed;\n  } else if (token_equals(property, PROPERTY_MAX_CHARS)) {\n    return text_set_max_chars(text, token_to_int(get_token(&message)));\n  }\n  else {\n    struct key_value_pair key_value_pair = get_key_value_pair(property.text,\n                                                              '.'           );\n    if (key_value_pair.key && key_value_pair.value) {\n      struct token subdom = { key_value_pair.key, strlen(key_value_pair.key) };\n      struct token entry = { key_value_pair.value,\n                             strlen(key_value_pair.value) };\n      if (token_equals(subdom, SUB_DOMAIN_BACKGROUND))\n        return background_parse_sub_domain(&text->background,\n                                           rsp,\n                                           entry,\n                                           message           );\n      else if (token_equals(subdom, SUB_DOMAIN_SHADOW))\n        return shadow_parse_sub_domain(&text->shadow, rsp, entry, message);\n      else if (token_equals(subdom, SUB_DOMAIN_FONT))\n        return font_parse_sub_domain(&text->font, rsp, entry, message);\n      else if (token_equals(subdom, SUB_DOMAIN_COLOR))\n        return color_parse_sub_domain(&text->color, rsp, entry, message);\n      else if (token_equals(subdom, SUB_DOMAIN_HIGHLIGHT_COLOR))\n        return color_parse_sub_domain(&text->highlight_color,\n                                      rsp,\n                                      entry,\n                                      message);\n      else\n        respond(rsp, \"[!] Text: Invalid subdomain '%s' \\n\", subdom.text);\n    }\n    else {\n      respond(rsp, \"[!] Text: Invalid property '%s'\\n\", property.text);\n    }\n  }\n\n  return needs_refresh;\n}\n"
  },
  {
    "path": "src/text.h",
    "content": "#pragma once\n#include <CoreText/CoreText.h>\n#include \"background.h\"\n#include \"font.h\"\n\nstruct text_line {\n  CTLineRef line;\n  CGFloat ascent;\n  CGFloat descent;\n};\n\nstruct text {\n  bool highlight;\n  bool drawing;\n  bool has_const_width;\n\n  char align;\n  char* string;\n\n  int y_offset;\n  int padding_left;\n  int padding_right;\n  uint32_t custom_width;\n  uint32_t max_chars;\n  uint32_t scroll_duration;\n  float scroll;\n  float width;\n\n  CGRect bounds;\n\n  struct font font;\n  struct text_line line;\n  struct color color;\n  struct color highlight_color;\n  struct shadow shadow;\n\n  struct background background;\n};\n\nvoid text_init(struct text* text);\nvoid text_clear_pointers(struct text* text);\nuint32_t text_get_length(struct text* text, bool override);\nuint32_t text_get_height(struct text* text);\nbool text_set_string(struct text* text, char* string, bool forced);\nbool text_set_font(struct text* text, char* font_string, bool forced);\nvoid text_copy(struct text* text, struct text* source);\n\nbool text_animate_scroll(struct text* text);\nvoid text_calculate_bounds(struct text* text, uint32_t x, uint32_t y);\nvoid text_draw(struct text* text, CGContextRef context);\nvoid text_destroy(struct text* text);\n\nvoid text_serialize(struct text* text, char* indent, FILE* rsp);\nbool text_parse_sub_domain(struct text* text, FILE* rsp, struct token property, char* message);\n"
  },
  {
    "path": "src/volume.c",
    "content": "#include \"volume.h\"\n#include \"event.h\"\n\nextern bool g_volume_events;\n\n#if __MAC_OS_X_VERSION_MAX_ALLOWED < 120000\n#define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster\n#endif\n\nstatic AudioObjectPropertyAddress kHardwareDevicePropertyAddress = {\n                                   kAudioHardwarePropertyDefaultOutputDevice,\n                                   kAudioObjectPropertyScopeGlobal,\n                                   kAudioObjectPropertyElementMain           };\n\nstatic AudioObjectPropertyAddress kVolumeMainPropertyAddress = {\n                                            kAudioDevicePropertyVolumeScalar,\n                                            kAudioObjectPropertyScopeOutput,\n                                            kAudioObjectPropertyElementMain  };\n\nstatic AudioObjectPropertyAddress kVolumeLeftPropertyAddress = {\n                                            kAudioDevicePropertyVolumeScalar,\n                                            kAudioObjectPropertyScopeOutput,\n                                            1                                };\n\nstatic AudioObjectPropertyAddress kMuteMainPropertyAddress = {\n                                            kAudioDevicePropertyMute,\n                                            kAudioObjectPropertyScopeOutput,\n                                            kAudioObjectPropertyElementMain  };\n\nstatic AudioObjectPropertyAddress kMuteLeftPropertyAddress = {\n                                            kAudioDevicePropertyMute,\n                                            kAudioObjectPropertyScopeOutput,\n                                            1                                };\n\nstatic float g_last_volume = -1.f;\nstatic OSStatus handler(AudioObjectID id, uint32_t address_count, const AudioObjectPropertyAddress* addresses, void* context) {\n  float v = 0;\n  float* volume = &v;\n\n  uint32_t muted_main = 0;\n  uint32_t size = sizeof(uint32_t);\n\n  AudioObjectGetPropertyData(id,\n                             &kMuteMainPropertyAddress,\n                             0,\n                             NULL,\n                             &size,\n                             &muted_main               );\n\n  uint32_t muted_left = 0;\n  size = sizeof(uint32_t);\n  AudioObjectGetPropertyData(id,\n                             &kMuteLeftPropertyAddress,\n                             0,\n                             NULL,\n                             &size,\n                             &muted_left               );\n\n\n  size = sizeof(float);\n  float volume_main = 0.f;\n  AudioObjectGetPropertyData(id,\n                             &kVolumeMainPropertyAddress,\n                             0,\n                             NULL,\n                             &size,\n                             &volume_main                );\n\n  size = sizeof(float);\n  float volume_left = 0.f;\n  AudioObjectGetPropertyData(id,\n                             &kVolumeLeftPropertyAddress,\n                             0,\n                             NULL,\n                             &size,\n                             &volume_left                );\n\n  if (volume_left > 0.f) {\n    *volume = (muted_left || muted_main) ? 0.f : volume_left;\n  } else {\n    *volume = muted_main ? 0.f : volume_main;\n  }\n\n  if (*volume > g_last_volume + 1e-2 || *volume < g_last_volume - 1e-2) {\n    g_last_volume = *volume;\n    struct event event = { (void*) volume, VOLUME_CHANGED };\n    event_post(&event);\n  } \n  return KERN_SUCCESS;\n}\n\nstatic AudioObjectID g_audio_id = 0;\nOSStatus device_changed(AudioObjectID id, uint32_t address_count, const AudioObjectPropertyAddress* addresses, void* context) {\n  AudioObjectID new_id = 0;\n  uint32_t size = sizeof(AudioObjectID);\n  AudioObjectGetPropertyData(kAudioObjectSystemObject,\n                             &kHardwareDevicePropertyAddress,\n                             0,\n                             NULL,\n                             &size,\n                             &new_id                         );\n\n  if (g_audio_id) {\n    AudioObjectRemovePropertyListener(g_audio_id,\n                                      &kMuteMainPropertyAddress,\n                                      &handler,\n                                      NULL                      );\n\n    AudioObjectRemovePropertyListener(g_audio_id,\n                                      &kMuteLeftPropertyAddress,\n                                      &handler,\n                                      NULL                      );\n\n    AudioObjectRemovePropertyListener(g_audio_id,\n                                      &kVolumeMainPropertyAddress,\n                                      &handler,\n                                      NULL                        );\n\n    AudioObjectRemovePropertyListener(g_audio_id,\n                                      &kVolumeLeftPropertyAddress,\n                                      &handler,\n                                      NULL                        );\n  }\n\n  AudioObjectAddPropertyListener(new_id,\n                                 &kMuteMainPropertyAddress,\n                                 &handler,\n                                 NULL                      );\n\n  AudioObjectAddPropertyListener(new_id,\n                                 &kMuteLeftPropertyAddress,\n                                 &handler,\n                                 NULL                      );\n\n  AudioObjectAddPropertyListener(new_id,\n                                 &kVolumeMainPropertyAddress,\n                                 &handler,\n                                 NULL                        );\n\n  AudioObjectAddPropertyListener(new_id,\n                                 &kVolumeLeftPropertyAddress,\n                                 &handler,\n                                 NULL                        );\n  g_last_volume = -1.f;\n  g_audio_id = new_id;\n  handler(g_audio_id, address_count, addresses, context);\n  return KERN_SUCCESS;\n}\n\nvoid forced_volume_event() {\n  g_last_volume = -1.f;\n  handler(g_audio_id, 0, 0, 0);\n}\n\nvoid begin_receiving_volume_events() {\n  if (g_volume_events) return;\n  g_volume_events = true;\n\n  AudioObjectID id = 0;\n  uint32_t size = sizeof(AudioObjectID);\n  AudioObjectGetPropertyData(kAudioObjectSystemObject,\n                             &kHardwareDevicePropertyAddress,\n                             0,\n                             NULL,\n                             &size,\n                             &id                             );\n\n  g_audio_id = id;\n  AudioObjectAddPropertyListener(id,\n                                 &kMuteLeftPropertyAddress,\n                                 &handler,\n                                 NULL                      );\n\n  AudioObjectAddPropertyListener(id,\n                                 &kMuteMainPropertyAddress,\n                                 &handler,\n                                 NULL                      );\n\n  AudioObjectAddPropertyListener(id,\n                                 &kVolumeLeftPropertyAddress,\n                                 &handler,\n                                 NULL                        );\n\n  AudioObjectAddPropertyListener(id,\n                                 &kVolumeMainPropertyAddress,\n                                 &handler,\n                                 NULL                        );\n\n  AudioObjectAddPropertyListener(kAudioObjectSystemObject,\n                                 &kHardwareDevicePropertyAddress,\n                                 &device_changed,\n                                 NULL                            );\n}\n"
  },
  {
    "path": "src/volume.h",
    "content": "#include \"CoreAudio/CoreAudio.h\"\n#include <CoreAudio/AudioHardware.h>\n\nvoid forced_volume_event();\nvoid begin_receiving_volume_events();\n"
  },
  {
    "path": "src/wifi.h",
    "content": "void forced_network_event();\nvoid begin_receiving_network_events();\n"
  },
  {
    "path": "src/wifi.m",
    "content": "#include <CoreWLAN/CoreWLAN.h>\n#include <SystemConfiguration/SystemConfiguration.h>\n#include \"wifi.h\"\n#include \"event.h\"\n\nvoid update_ssid(SCDynamicStoreRef store, CFArrayRef keys, void* info) {\n  @autoreleasepool {\n    NSData* data = [[[CWWiFiClient sharedWiFiClient] interface] ssidData];\n    char ssid[[data length] + 1];\n    memcpy(ssid, [data bytes], [data length]);\n    ssid[[data length]] = '\\0'; \n\n    struct event event = { (void*) ssid, WIFI_CHANGED };\n    event_post(&event);\n  }\n}\n\nvoid forced_network_event() {\n  update_ssid(NULL, NULL, NULL);\n}\n\nvoid begin_receiving_network_events() {\n  SCDynamicStoreContext context = { 0, NULL, NULL, NULL, NULL };\n  SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR(\"network\"),\n                                                       update_ssid,\n                                                       &context         );\n\n  const void* values[] = { CFSTR(\".*/Network/Global/IPv4\") };\n  CFArrayRef keys = CFArrayCreate(NULL, values, 1, &kCFTypeArrayCallBacks);\n  SCDynamicStoreSetNotificationKeys(store, NULL, keys);\n  CFRunLoopSourceRef loop_source = SCDynamicStoreCreateRunLoopSource(NULL,\n                                                                     store,\n                                                                     0     );\n\n  CFRunLoopAddSource(CFRunLoopGetCurrent(),\n                     loop_source,\n                     kCFRunLoopDefaultMode );\n  CFRelease(keys);\n}\n"
  },
  {
    "path": "src/window.c",
    "content": "#include \"window.h\"\n#include \"bar_manager.h\"\n#include \"misc/helpers.h\"\n\nextern struct bar_manager g_bar_manager;\nextern int64_t g_disable_capture;\nint g_space = 0;\n\nvoid window_init(struct window* window) {\n  window->context = NULL;\n  window->parent = NULL;\n  window->frame = CGRectNull;\n  window->id = 0;\n  window->origin = CGPointZero;\n  window->surface_id = 0;\n  window->needs_move = false;\n  window->needs_resize = false;\n  window->order_mode = W_ABOVE;\n}\n\nstatic CFTypeRef window_create_region(struct window* window, CGRect frame) {\n  CFTypeRef frame_region;\n  CGSNewRegionWithRect(&frame, &frame_region);\n  return frame_region;\n}\n\nvoid window_create(struct window* window, CGRect frame) {\n  uint64_t set_tags = kCGSExposeFadeTagBit | kCGSPreventsActivationTagBit;\n  uint64_t clear_tags = 0;\n\n  window->origin = frame.origin;\n  window->frame.origin = CGPointZero;\n  window->frame.size = frame.size;\n  frame.origin = CGPointZero;\n\n  uint32_t id;\n  CFTypeRef frame_region = window_create_region(window, frame);\n  CFTypeRef empty_region = CGRegionCreateEmptyRegion();\n  SLSNewWindowWithOpaqueShapeAndContext(g_connection,\n                                        kCGBackingStoreBuffered,\n                                        frame_region,\n                                        empty_region,\n                                        13 | (1 << 18),\n                                        &set_tags,\n                                        window->origin.x,\n                                        window->origin.y,\n                                        64,\n                                        &id,\n                                        NULL                    );\n  CFRelease(empty_region);\n  CFRelease(frame_region);\n\n  window->id = id;\n\n  SLSSetWindowResolution(g_connection, window->id, 2.0f);\n  SLSSetWindowTags(g_connection, window->id, &set_tags, 64);\n  SLSClearWindowTags(g_connection, window->id, &clear_tags, 64);\n  SLSSetWindowOpacity(g_connection, window->id, 0);\n\n  window->context = SLWindowContextCreate(g_connection, window->id, NULL);\n\n  CGContextSetInterpolationQuality(window->context, kCGInterpolationNone);\n  window->needs_move = false;\n  window->needs_resize = false;\n\n\n  if (g_bar_manager.sticky) {\n    if (!g_space) {\n      g_space = SLSSpaceCreate(g_connection, 1, 0);\n      SLSSpaceSetAbsoluteLevel(g_connection, g_space, 0);\n\n      CFArrayRef space_list = cfarray_of_cfnumbers(&g_space,\n                                                   sizeof(uint32_t),\n                                                   1,\n                                                   kCFNumberSInt32Type);\n      SLSShowSpaces(g_connection, space_list);\n      CFRelease(space_list);\n    }\n\n    CFArrayRef window_list = cfarray_of_cfnumbers(&window->id,\n                                                  sizeof(uint32_t),\n                                                  1,\n                                                  kCFNumberSInt32Type);\n\n    SLSSpaceAddWindowsAndRemoveFromSpaces(g_connection,\n                                          g_space,\n                                          window_list,\n                                          0x7          );\n\n    CFRelease(window_list);\n  }\n}\n\nvoid window_clear(struct window* window) {\n  window->context = NULL;\n  window->parent = NULL;\n  window->id = 0;\n  window->origin = CGPointZero;\n  window->frame = CGRectNull;\n  window->needs_move = false;\n  window->needs_resize = false;\n}\n\nvoid window_flush(struct window* window) {\n  SLSFlushWindowContentRegion(g_connection, window->id, NULL);\n}\n\nvoid windows_freeze() {\n  if (g_transaction) return;\n\n  SLSDisableUpdate(g_connection);\n  g_transaction = SLSTransactionCreate(g_connection);\n}\n\nvoid windows_unfreeze() {\n  if (g_transaction) {\n    SLSTransactionCommit(g_transaction, 0);\n    CFRelease(g_transaction);\n    g_transaction = NULL;\n    SLSReenableUpdate(g_connection);\n  }\n}\n\nvoid window_set_frame(struct window* window, CGRect frame) {\n  if (window->needs_move\n      || !CGPointEqualToPoint(window->origin, frame.origin)) {\n    window->needs_move = true;\n    window->origin = frame.origin;\n  }\n\n  if (window->needs_resize\n      || !CGSizeEqualToSize(window->frame.size, frame.size)) {\n    window->needs_resize = true;\n    window->frame.size = frame.size;\n  }\n}\n\nvoid window_move(struct window* window, CGPoint point) {\n  window->origin = point;\n\n  if (__builtin_available(macOS 12.0, *)) {\n    // Monterey and later\n    windows_freeze();\n    SLSTransactionMoveWindowWithGroup(g_transaction, window->id, point);\n  } else {\n    // Big Sur and previous\n    SLSMoveWindow(g_connection, window->id, &point);\n    CFNumberRef number = CFNumberCreate(NULL,\n                                        kCFNumberSInt32Type,\n                                        &window->id         );\n\n    const void* values[1] = { number };\n    CFArrayRef array = CFArrayCreate(NULL, values , 1, &kCFTypeArrayCallBacks);\n    SLSReassociateWindowsSpacesByGeometry(g_connection, array);\n    CFRelease(array);\n    CFRelease(number);\n  }\n}\n\nbool window_apply_frame(struct window* window, bool forced) {\n  windows_freeze();\n  if (window->needs_resize || forced) {\n    CFTypeRef frame_region = window_create_region(window, window->frame);\n\n    if (__builtin_available(macOS 26.0, *)) {\n      SLSSetWindowShape(g_connection, window->id,\n                                      window->origin.x,\n                                      window->origin.y,\n                                      frame_region);\n    }\n    else if (__builtin_available(macOS 13.0, *)) {\n      // Ventura and later\n      SLSSetWindowShape(g_connection, window->id,\n                                      g_nirvana.x,\n                                      g_nirvana.y,\n                                      frame_region);\n      window_move(window, window->origin);\n    } else {\n      // Monterey and previous\n      if (window->parent) {\n        SLSOrderWindow(g_connection, window->id, 0, window->parent->id);\n      }\n\n      SLSSetWindowShape(g_connection, window->id, 0, 0, frame_region);\n\n      if (window->parent) {\n        CGContextClearRect(window->context, window->frame);\n        CGContextFlush(window->context);\n        window_order(window, window->parent, window->order_mode);\n      }\n      window_move(window, window->origin);\n    }\n\n    CFRelease(frame_region);\n\n    window->needs_move = false;\n    window->needs_resize = false;\n    return true;\n  } else if (window->needs_move) {\n    window_move(window, window->origin);\n    window->needs_move = false;\n    return false;\n  }\n  return false;\n}\n\nvoid window_send_to_space(struct window* window, uint64_t dsid) {\n  CFArrayRef window_list = cfarray_of_cfnumbers(&window->id,\n                                                sizeof(uint32_t),\n                                                1,\n                                                kCFNumberSInt32Type);\n\n  SLSMoveWindowsToManagedSpace(g_connection, window_list, dsid);\n  if (CGPointEqualToPoint(window->origin, g_nirvana)) {\n    SLSMoveWindow(g_connection, window->id, &g_nirvana);\n  }\n  CFRelease(window_list);\n}\n\nvoid window_close(struct window* window) {\n  if (!window->id) return;\n  windows_unfreeze();\n\n  SLSOrderWindow(g_connection, window->id, 0, 0);\n  CGContextRelease(window->context);\n  SLSReleaseWindow(g_connection, window->id);\n\n  window_clear(window);\n}\n\nvoid window_set_level(struct window* window, uint32_t level) {\n  windows_freeze();\n\n  if (__builtin_available(macOS 14.0, *)) {\n    // Sonoma and later\n    SLSTransactionSetWindowLevel(g_transaction, window->id, level);\n  } else {\n    // Ventura and previous\n    SLSSetWindowLevel(g_connection, window->id, level);\n  }\n}\n\nvoid window_order(struct window* window, struct window* parent, int mode) {\n  windows_freeze();\n  window->parent = parent;\n  if (mode != W_OUT) window->order_mode = mode;\n\n  if (__builtin_available(macOS 14.0, *)) {\n    // Sonoma and later\n    SLSTransactionOrderWindow(g_transaction,\n                              window->id,\n                              mode,\n                              parent ? parent->id : 0);\n  } else {\n    // Ventura and previous\n    SLSOrderWindow(g_connection, window->id, mode, parent ? parent->id : 0);\n  }\n}\n\nvoid window_assign_mouse_tracking_area(struct window* window, CGRect rect) {\n  SLSRemoveAllTrackingAreas(g_connection, window->id);\n  SLSAddTrackingRect(g_connection, window->id, rect);\n}\n\nvoid window_set_blur_radius(struct window* window, uint32_t blur_radius) {\n  SLSSetWindowBackgroundBlurRadius(g_connection, window->id, blur_radius);\n}\n\nvoid context_set_font_smoothing(CGContextRef context, bool smoothing) {\n  CGContextSetAllowsFontSmoothing(context, smoothing);\n}\n\nvoid window_disable_shadow(struct window* window) {\n  CFIndex shadow_density = 0;\n  CFNumberRef shadow_density_cf = CFNumberCreate(kCFAllocatorDefault,\n                                                 kCFNumberCFIndexType,\n                                                 &shadow_density      );\n\n  const void *keys[1] = { CFSTR(\"com.apple.WindowShadowDensity\") };\n  const void *values[1] = { shadow_density_cf };\n  CFDictionaryRef shadow_props_cf = CFDictionaryCreate(NULL,\n                                             keys,\n                                             values,\n                                             1,\n                                             &kCFTypeDictionaryKeyCallBacks,\n                                             &kCFTypeDictionaryValueCallBacks);\n\n  SLSWindowSetShadowProperties(window->id, shadow_props_cf);\n  CFRelease(shadow_density_cf);\n  CFRelease(shadow_props_cf);\n}\n\nCGImageRef window_capture(struct window* window, bool* disabled) {\n  if (g_disable_capture) {\n    int64_t time = clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW_APPROX);\n    if (g_disable_capture < 0) {\n      *disabled = true;\n      return NULL;\n    } else if (time - g_disable_capture > (1ULL << 30)) {\n      g_disable_capture = 0;\n    } else {\n      *disabled = true;\n      return NULL;\n    }\n  }\n\n  *disabled = false;\n  CGImageRef image_ref = NULL;\n\n  uint64_t wid = window->id;\n  SLSCaptureWindowsContentsToRectWithOptions(g_connection,\n                                             &wid,\n                                             true,\n                                             CGRectNull,\n                                             1 << 8,\n                                             &image_ref  );\n\n  CGRect bounds;\n  SLSGetScreenRectForWindow(g_connection, wid, &bounds);\n  bounds.size.width = (uint32_t) (bounds.size.width + 0.5);\n  window->frame.size = bounds.size;\n\n  return image_ref;\n}\n"
  },
  {
    "path": "src/window.h",
    "content": "#pragma once\n#include \"misc/helpers.h\"\n\n#define kCGSExposeFadeTagBit         (1ULL <<  1)\n#define kCGSPreventsActivationTagBit (1ULL <<  16)\n\n#define W_ABOVE  1\n#define W_OUT    0\n#define W_BELOW  -1\n\nextern CFTypeRef g_transaction;\n\nstruct window {\n  struct window* parent;\n  int order_mode;\n  bool needs_move;\n  bool needs_resize;\n\n  uint32_t id;\n  uint32_t surface_id;\n\n  CGRect frame;\n  CGPoint origin;\n  CGContextRef context;\n};\n\nvoid window_init(struct window* window);\nvoid window_create(struct window* window, CGRect frame);\nvoid window_close(struct window* window);\nvoid window_clear(struct window* window);\nvoid window_flush(struct window* window);\n\nvoid window_move(struct window* window, CGPoint point);\nvoid window_set_frame(struct window* window, CGRect frame);\nbool window_apply_frame(struct window* window, bool forced);\nvoid window_send_to_space(struct window* window, uint64_t dsid);\n\nvoid window_set_blur_radius(struct window* window, uint32_t blur_radius);\nvoid window_disable_shadow(struct window* window);\nvoid window_set_level(struct window* window, uint32_t level);\nvoid window_order(struct window* window, struct window* parent, int mode);\nvoid window_assign_mouse_tracking_area(struct window* window, CGRect rect);\n\nCGImageRef window_capture(struct window* window, bool* disabled);\n\nvoid context_set_font_smoothing(CGContextRef context, bool smoothing);\n\nvoid windows_freeze();\nvoid windows_unfreeze();\n"
  },
  {
    "path": "src/workspace.h",
    "content": "#pragma once\n#include \"event.h\"\n\nvoid workspace_create_custom_observer (void **context, char* notification);\nvoid workspace_event_handler_init(void **context);\nvoid workspace_event_handler_begin(void **context);\nvoid workspace_event_handler_end(void *context);\nint workspace_display_notch_height(uint32_t did);\nfloat workspace_get_scale();\n\nCGImageRef workspace_icon_for_app(char* app);\nchar* workspace_copy_app_name_for_pid(pid_t pid);\n"
  },
  {
    "path": "src/workspace.m",
    "content": "#include \"workspace.h\"\n#include \"misc/helpers.h\"\n\n#include <AppKit/AppKit.h>\n@interface workspace_context : NSObject {\n}\n- (id)init;\n- (void)addCustomObserver:(NSString *)name;\n@end\n\nfloat workspace_get_scale() {\n  @autoreleasepool {\n    float scale = 1.f;\n    NSArray* screens = [NSScreen screens];\n    for (int i = 0; i < [screens count]; i++) {\n      NSScreen* screen = screens[i];\n      float screen_scale = [screen backingScaleFactor]; \n      if (screen_scale > scale) scale = screen_scale;\n    }\n    return scale;\n  }\n}\n\nvoid workspace_event_handler_init(void **context) {\n    workspace_context *ws_context = [workspace_context alloc];\n    *context = ws_context;\n}\n\nvoid workspace_event_handler_begin(void **context) {\n    workspace_context *ws_context = *context;\n    [ws_context init];\n}\n\nvoid workspace_event_handler_end(void *context) {\n    workspace_context *ws_context = (workspace_context *) context;\n    [ws_context dealloc];\n}\n\nvoid workspace_create_custom_observer (void **context, char* notification) {\n    workspace_context *ws_context = *context;\n    [ws_context addCustomObserver:@(notification)];\n}\n\nint workspace_display_notch_height(uint32_t did)\n{\n  if (!CGDisplayIsBuiltin(did)) return 0;\n\n  int height = 0;\n  #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 120000\n  if (__builtin_available(macos 12.0, *)) {\n    @autoreleasepool {\n      for (NSScreen *screen in [NSScreen screens]) {\n        if ([[[screen deviceDescription] objectForKey:@\"NSScreenNumber\"] unsignedIntValue] == did) {\n            height = screen.safeAreaInsets.top;\n        }\n      }\n    }\n  }\n  #endif\n\n  return height;\n}\n\nvoid forced_front_app_event() {\n  @autoreleasepool {\n    NSString* name = [[[NSWorkspace sharedWorkspace] frontmostApplication] localizedName];\n    if (name) {\n      const char* front_app = [name cStringUsingEncoding:NSUTF8StringEncoding];\n\n      if (front_app) {\n        struct event event = { string_copy((char*)front_app),\n                               APPLICATION_FRONT_SWITCHED    };\n        event_post(&event);\n      }\n    }\n  }\n}\n\nchar* workspace_copy_app_name_for_pid(pid_t pid) {\n  @autoreleasepool {\n    NSRunningApplication* app = [NSRunningApplication runningApplicationWithProcessIdentifier:pid];\n    const char* result = [[app localizedName] UTF8String];\n    return result ? string_copy((char*)result) : NULL;\n  }\n}\n\nCGImageRef workspace_icon_for_app(char* app) {\n  @autoreleasepool {\n    NSString* ns_app = [NSString stringWithUTF8String:app];\n    NSURL* path = [[NSWorkspace sharedWorkspace] URLForApplicationWithBundleIdentifier:ns_app];\n    if (!path) {\n      bool recovered = false;\n      NSArray* running_apps = [[NSWorkspace sharedWorkspace] runningApplications];\n      for (NSRunningApplication* app in running_apps) {\n        if ([[app localizedName] isEqualToString:ns_app]) {\n          ns_app = [app bundleIdentifier];\n          if (ns_app) {\n            path = [[NSWorkspace sharedWorkspace] URLForApplicationWithBundleIdentifier:ns_app];\n            recovered = true;\n          }\n          break;\n        }\n      }\n      if (!recovered) return NULL;\n    }\n\n    NSImage* image = [[NSWorkspace sharedWorkspace] iconForFile:path.path];\n    if (!image) return NULL;\n\n    float scale = workspace_get_scale();\n    NSRect rect = NSMakeRect( 0, 0, 32 * scale, 32 * scale);\n    return (CGImageRef)CFRetain([image CGImageForProposedRect: &rect\n                                                      context: NULL\n                                                        hints: NULL]);\n  }\n}\n\n@implementation workspace_context\n- (id)init {\n    if ((self = [super init])) {\n        [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self\n                selector:@selector(activeDisplayDidChange:)\n                name:@\"NSWorkspaceActiveDisplayDidChangeNotification\"\n                object:nil];\n        [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self\n                selector:@selector(activeSpaceDidChange:)\n                name:NSWorkspaceActiveSpaceDidChangeNotification\n                object:nil];\n        [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self\n                selector:@selector(appSwitched:)\n                name:NSWorkspaceDidActivateApplicationNotification\n                object:nil];\n        [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self\n                selector:@selector(willSleep:)\n                name:NSWorkspaceWillSleepNotification\n                object:nil];\n        [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self\n                selector:@selector(didWake:)\n                name:NSWorkspaceDidWakeNotification\n                object:nil];\n        [[NSDistributedNotificationCenter defaultCenter] addObserver:self\n                selector:@selector(didChangeMenuBarHiding:)\n                name:@\"AppleInterfaceMenuBarHidingChangedNotification\"\n                object:nil];\n        [[NSDistributedNotificationCenter defaultCenter] addObserver:self\n                selector:@selector(didWake:)\n                name:@\"com.apple.screenIsUnlocked\"\n                object:nil];\n    }\n\n    return self;\n}\n\n- (void)addCustomObserver:(NSString *)name {\n  [[NSDistributedNotificationCenter defaultCenter] addObserver:self\n                                                  selector:@selector(allDistributedNotifications:)\n                                                  name:name\n                                                  object:nil];\n}\n\n- (void)dealloc {\n    [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];\n    [[NSNotificationCenter defaultCenter] removeObserver:self];\n    [[NSDistributedNotificationCenter defaultCenter] removeObserver:self];\n    [super dealloc];\n}\n\n- (void) allDistributedNotifications:(NSNotification *)note {\n  @autoreleasepool {\n    struct notification* notification = notification_create();\n    notification->name = string_copy((char*)[[note name] UTF8String]);\n    if (note.userInfo && [NSJSONSerialization isValidJSONObject:note.userInfo]) {\n      NSData* data = [NSJSONSerialization dataWithJSONObject:note.userInfo options:NSJSONWritingPrettyPrinted error:NULL];\n      if (data && [data length] > 0) {\n        char* info = malloc([data length] + 1);\n        memcpy(info, [data bytes], [data length]);\n        info[[data length]] = '\\0';\n        notification->info = info;\n      }\n    }\n\n    struct event event = { notification, DISTRIBUTED_NOTIFICATION };\n    event_post(&event);\n  }\n}\n\n- (void)willSleep:(NSNotification *)notification {\n    struct event event = { NULL, SYSTEM_WILL_SLEEP };\n    event_post(&event);\n}\n\n- (void)didWake:(NSNotification *)notification {\n    struct event event = { NULL, SYSTEM_WOKE };\n    event_post(&event);\n}\n\n- (void)appSwitched:(NSNotification *)notification {\n    @autoreleasepool {\n      char* name = NULL;\n      if (notification && notification.userInfo) {\n        NSRunningApplication* app = [notification.userInfo objectForKey:NSWorkspaceApplicationKey];\n        if (app) {\n          char* app_name_tmp = (char*)[[app localizedName] UTF8String];\n          if (app_name_tmp) name = string_copy(app_name_tmp);\n        }\n      }\n      struct event event = { name, APPLICATION_FRONT_SWITCHED };\n      event_post(&event);\n    }\n}\n\n- (void)didChangeMenuBarHiding:(NSNotification *)notification {\n    struct event event = { NULL, MENU_BAR_HIDDEN_CHANGED };\n    event_post(&event);\n}\n\n- (void)activeDisplayDidChange:(NSNotification *)notification {\n    struct event event = { NULL, DISPLAY_CHANGED };\n    event_post(&event);\n}\n\n- (void)activeSpaceDidChange:(NSNotification *)notification {\n    struct event event = { NULL, SPACE_CHANGED };\n    event_post(&event);\n}\n\n@end\n"
  }
]