[
  {
    "path": ".clang-format",
    "content": "---\nLanguage:        Cpp\nAccessModifierOffset: 0\nAlignAfterOpenBracket: DontAlign\nAlignConsecutiveMacros: false\nAlignConsecutiveAssignments: false\nAlignConsecutiveBitFields: false\nAlignConsecutiveDeclarations: false\nAlignEscapedNewlines: DontAlign\nAlignOperands:   DontAlign\nAlignTrailingComments: false\nAllowAllArgumentsOnNextLine: true\nAllowAllParametersOfDeclarationOnNextLine: true\nAllowShortEnumsOnASingleLine: true\nAllowShortBlocksOnASingleLine: Empty\nAllowShortCaseLabelsOnASingleLine: true\nAllowShortFunctionsOnASingleLine: Inline\nAllowShortIfStatementsOnASingleLine: WithoutElse\nAllowShortLoopsOnASingleLine: true\nAlwaysBreakAfterDefinitionReturnType: None\nAlwaysBreakAfterReturnType: None\nAlwaysBreakBeforeMultilineStrings: false\nBinPackArguments: true\nBinPackParameters: true\nBraceWrapping:\n  AfterCaseLabel:  false\n  AfterControlStatement: Never\n  AfterEnum:       false\n  AfterFunction:   true\n  AfterStruct:     false\n  AfterUnion:      false\n  BeforeElse:      false\n  BeforeWhile:     false\n  IndentBraces:    false\n  SplitEmptyFunction: true\n  SplitEmptyRecord: true\n  SplitEmptyNamespace: true\nBreakBeforeBinaryOperators: NonAssignment\nBreakBeforeBraces: Linux\nBreakBeforeTernaryOperators: true\nBreakStringLiterals: true\nColumnLimit:     79\nCommentPragmas:  '^ IWYU pragma:'\nContinuationIndentWidth: 4\nDeriveLineEnding: true\nDerivePointerAlignment: false\nDisableFormat:   false\nForEachMacros:\n  - foreach\nIncludeBlocks:   Preserve\nIncludeCategories:\n  - Regex:           '^(<|\"(gtest|gmock|isl|json)/)'\n    Priority:        3\n    SortPriority:    0\n  - Regex:           '.*'\n    Priority:        1\n    SortPriority:    0\nIncludeIsMainRegex: '(Test)?$'\nIncludeIsMainSourceRegex: ''\nIndentCaseLabels: false\nIndentCaseBlocks: false\nIndentGotoLabels: false\nIndentPPDirectives: None\nIndentWidth:     4\nIndentWrappedFunctionNames: true\nInsertTrailingCommas: None\nKeepEmptyLinesAtTheStartOfBlocks: true\nMacroBlockBegin: ''\nMacroBlockEnd:   ''\nMaxEmptyLinesToKeep: 1\nPenaltyBreakAssignment: 2\nPenaltyBreakBeforeFirstCallParameter: 19\nPenaltyBreakComment: 300\nPenaltyBreakFirstLessLess: 120\nPenaltyBreakString: 1000\nPenaltyBreakTemplateDeclaration: 10\nPenaltyExcessCharacter: 1000000\nPenaltyReturnTypeOnItsOwnLine: 60\nPointerAlignment: Right\nReflowComments:  true\nSortIncludes:    true\nSortUsingDeclarations: true\nSpaceAfterCStyleCast: false\nSpaceAfterLogicalNot: false\nSpaceBeforeAssignmentOperators: true\nSpaceBeforeParens: ControlStatements\nSpaceInEmptyBlock: false\nSpaceInEmptyParentheses: false\nSpacesBeforeTrailingComments: 1\nSpacesInAngles:  false\nSpacesInConditionalStatement: false\nSpacesInContainerLiterals: true\nSpacesInCStyleCastParentheses: false\nSpacesInParentheses: false\nSpacesInSquareBrackets: false\nSpaceBeforeSquareBrackets: false\nStandard:        c++03\nTabWidth:        4\nUseCRLF:         false\nUseTab:          Never\nWhitespaceSensitiveMacros:\n  - STRINGIZE\n  - PP_STRINGIZE\n  - BOOST_PP_STRINGIZE\n...\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the buggy behavior (even if intermittent):\n1. Use WM FOO (describe configuration if relevant)\n2. Launch app X\n3. Launch app Y\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Log files**\nAttach stalonetray output with `--log-level trace` for good and bad behavior.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build\n\non:\n  push:\n    branches:\n      - master\n  pull_request: {}\n\njobs:\n  ubuntu:\n    name: Ubuntu\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix:\n        featureset: [\"base\", \"xinerama\", \"xpm\", \"native_kde\", \"all\", \"debug\"]\n\n    steps:\n      - name: Checkout source code\n        uses: actions/checkout@v5\n\n      - name: Disable man-db auto-update\n        run: |\n          echo \"set man-db/auto-update false\" | sudo debconf-communicate\n          sudo dpkg-reconfigure man-db\n          sudo rm -f /var/lib/man-db/auto-update\n\n      - name: Install and cache dependencies\n        uses: awalsh128/cache-apt-pkgs-action@v1\n        with:\n          packages: >\n            xsltproc docbook-xsl libxpm-dev libx11-dev libxinerama-dev meson\n\n      - name: Set up build directory\n        run: >\n          if [ \"${{ matrix.featureset }}\" = \"base\" ]; then\n            echo \"== Basic build ==\"\n            meson setup --buildtype release build -D c_std=gnu2x\n          elif [ \"${{ matrix.featureset }}\" = \"all\" ]; then\n            echo \"== Enabling all features ==\"\n            meson setup --buildtype release build -D c_std=gnu2x \\\n              -D xinerama=enabled -D xpm=enabled -D native_kde=enabled\n          elif [ \"${{ matrix.featureset }}\" = \"debug\" ]; then\n            echo \"== Debug build with all features enabled ==\"\n            meson setup --buildtype debug build -D c_std=gnu2x \\\n              -D xinerama=enabled -D xpm=enabled -D native_kde=enabled \\\n              -D trace_events=true -D delay_embedding_confirmation=true \\\n              -D dump_window_information=true -D exit_gracefully=true\n          else\n            echo \"== Enabling feature ${{ matrix.featureset }} ==\"\n            meson setup --buildtype release build -D c_std=gnu2x \\\n              -D ${{ matrix.featureset }}=enabled\n          fi\n\n      - name: Build\n        run: meson compile -C build stalonetray\n\n      - name: Build manpage\n        run: meson compile -C build manpage\n\n  freebsd:\n    name: FreeBSD\n    runs-on: ubuntu-latest\n\n    strategy:\n      fail-fast: false\n      matrix:\n        version: [\"14.2\", \"14.3\", \"15.0\"]\n\n    steps:\n      - name: Checkout source code\n        uses: actions/checkout@v5\n\n      - name: Disable man-db auto-update\n        run: |\n          echo \"set man-db/auto-update false\" | sudo debconf-communicate\n          sudo dpkg-reconfigure man-db\n          sudo rm -f /var/lib/man-db/auto-update\n\n      - name: Install and cache vbox dependencies\n        uses: awalsh128/cache-apt-pkgs-action@v1\n        with:\n          execute_install_scripts: true\n          packages: >\n            zstd libvirt-daemon-system virt-manager qemu-kvm libosinfo-bin axel\n            expect screen sshpass\n\n      - name: Build on FreeBSD VM\n        uses: vmactions/freebsd-vm@v1\n        with:\n          arch: amd64\n          release: ${{ matrix.version }}\n          prepare: |\n            mkdir -p /usr/local/etc/pkg/repos\n            repoFile=\"/usr/local/etc/pkg/repos/FreeBSD.conf\"\n\n            if [ ${{ matrix.version }} = \"15.0\" ]; then\n              repoURL=\"http://pkg.freebsd.org/\\${ABI}/latest\"\n            else\n              repoURL=\"pkg+http://pkg.freebsd.org/\\${ABI}/latest\"\n            fi\n\n            echo \"FreeBSD: { url: ${repoURL} }\" > \"$repoFile\"\n            IGNORE_OSVERSION=yes pkg update -f\n\n            pkg install -y pkgconf meson libX11 libXpm libXinerama docbook-xsl \\\n              libxslt\n          run: |\n            echo \"== Basic build ==\"\n            meson setup --buildtype release build\n            meson compile -C build stalonetray\n            meson compile -C build manpage\n\n            for feature in xinerama xpm native_kde; do\n              echo \"== Building with $feature enabled ==\"\n              meson setup --wipe build -D $feature=enabled\n              meson compile -C build stalonetray\n              meson compile -C build manpage\n            done\n\n            echo \"== Building with all features enabled ==\"\n            meson setup --wipe build \\\n              -D xinerama=enabled -D xpm=enabled -D native_kde=enabled\n            meson compile -C build stalonetray\n            meson compile -C build manpage\n"
  },
  {
    "path": ".github/workflows/lint.yml",
    "content": "name: Lint\n\non:\n  push:\n    branches:\n      - master\n  pull_request: {}\n\njobs:\n  new-version:\n    name: Version Bump Check\n    runs-on: ubuntu-latest\n\n    if: github.event_name == 'pull_request'\n\n    outputs:\n      next-version: ${{ steps.next-version.outputs.version }}\n      has-next-version: ${{ steps.next-version.outputs.hasNextVersion }}\n\n    steps:\n      - uses: actions/checkout@v5\n        with:\n          fetch-depth: 0\n\n      - id: next-version\n        uses: thenativeweb/get-next-version@2.7.1\n\n      - name: Install meson\n        run: pip install 'meson>=1.5.0'\n\n      - name: Show the next version\n        run: |\n          mesonV=$(meson introspect meson.build --projectinfo | jq -r .version)\n          wantedV=\"${{ steps.next-version.outputs.version }}\"\n          shouldBump=\"${{ steps.next-version.outputs.hasNextVersion }}\"\n\n          echo \"Should bump version: $shouldBump\"\n          echo \"Next version: $wantedV\"\n          echo \"Current version on meson.build: $mesonV\"\n\n          if \"$shouldBump\" && [ \"$mesonV\" != \"$wantedV\" ]; then\n            echo \"Version bump mismatch! Expected $wantedV but found $mesonV\"\n            exit 1\n          elif \"$shouldBump\"; then\n            echo \"Version bump detected and matches meson.build\"\n          else\n            echo \"No version change detected, nothing to do\"\n          fi\n\n  meson-format:\n    name: Meson Format\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checkout source code\n        uses: actions/checkout@v5\n\n      - name: Install meson\n        run: pip install 'meson>=1.5.0'\n\n      - name: Run meson-format\n        run: meson format --recursive -q\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\n\non:\n  push:\n    branches:\n      - master\n\nconcurrency:\n  group: merge-to-master\n  cancel-in-progress: false\n\npermissions:\n  contents: write\n\njobs:\n  new-version:\n    name: Get New Version\n    runs-on: ubuntu-latest\n\n    outputs:\n      next-version: ${{ steps.next-version.outputs.version }}\n      has-next-version: ${{ steps.next-version.outputs.hasNextVersion }}\n\n    steps:\n      - uses: actions/checkout@v5\n        with:\n          fetch-depth: 0\n\n      - id: next-version\n        uses: thenativeweb/get-next-version@2.7.1\n\n      - name: Show the next version\n        run: |\n          echo \"Should bump version: ${{ steps.next-version.outputs.hasNextVersion }}\"\n          echo \"Next version: ${{ steps.next-version.outputs.version }}\"\n\n  create-release:\n    name: Create\n    runs-on: ubuntu-latest\n\n    if: needs.new-version.outputs.has-next-version == 'true'\n    needs: [new-version]\n\n    steps:\n      - uses: actions/checkout@v5\n\n      - name: Build distribution tarball\n        run: |\n          echo \"set man-db/auto-update false\" | sudo debconf-communicate\n          sudo dpkg-reconfigure man-db\n          sudo rm -f /var/lib/man-db/auto-update\n          sudo apt-get update\n          sudo apt-get install -y \\\n            xsltproc docbook-xsl libxpm-dev libx11-dev libxinerama-dev\n\n          pip install 'meson>=1.5.0'\n          meson setup --buildtype release _build -D c_std=gnu2x\n          meson dist -C _build\n\n      - uses: softprops/action-gh-release@v2\n        with:\n          tag_name: ${{ needs.new-version.outputs.next-version }}\n          generate_release_notes: true\n          files: \"_build/meson-dist/*\"\n"
  },
  {
    "path": ".gitignore",
    "content": "# Generated by build system\n/aclocal.m4\n/autom4te.cache\nMakefile.in\nMakefile\n/compile\n/config.log\n/config.status\n/configure\n/depcomp\n/install-sh\n/missing\n.deps/\n/src/config.h.in\n/src/config.h\n/src/stamp-h1\n\n/build/\n/.cache/\n\n# Build artifacts\n/src/*.o\n/stalonetray.xml\n\n# Output artifacts\n/src/stalonetray\n/stalonetray.1\n/stalonetray.html\n"
  },
  {
    "path": "AUTHORS",
    "content": "Roman Dubtsov <dubtsov@gmail.com>\nd3adb5 <me@d3adb5.net>\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\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 licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  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\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions 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\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the 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\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n                            NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\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\nconvey 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 2 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 along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision 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, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.\n"
  },
  {
    "path": "README.md",
    "content": "# STAnd aLONE TRAY [![Build][badge-build]][yaml-build] [![Lint][badge-lint]][yaml-lint]\n\n[badge-build]: https://github.com/d3adb5/stalonetray/actions/workflows/build.yml/badge.svg\n[yaml-build]: https://github.com/d3adb5/stalonetray/actions/workflows/build.yml\n[badge-lint]: https://github.com/d3adb5/stalonetray/actions/workflows/lint.yml/badge.svg\n[yaml-lint]: https://github.com/d3adb5/stalonetray/actions/workflows/lint.yml\n\nStalonetray is a STAnd-aLONE system TRAY (notification area) for Unix desktops\nusing the X11 windowing system. It has minimal default build and run-time\ndependencies: the Xlib and libXinerama, though you could do away with the\nlatter by disabling a feature for even more minimalism. Stalonetray runs under\nvirtually any window manager.\n\nTo start using stalonetray, just copy `stalonetrayrc.sample` to\n`~/.stalonetrayrc` or to `$XDG_CONFIG_HOME/stalonetrayrc`. It is well-commented\nand should suffice for a quick start.\n\nNote that some features are disabled by default and may not work out of the\nbox, depending on how stalonetray was built by the package maintainer. See the\n\"Building from source\" section below if you want to compile it yourself with\nthe features you need.\n\n## Maintenance status\n\nThis project was originally developed by [Roman Dubtsov (kolbusa)][gh-kolbusa]\nand recently changed hands. Roman is still involved with, but no longer\nactively maintains the project.\n\n**To him goes all the credit for creating and maintaining this project for many\nyears. Thank you, Roman!**\n\n[gh-kolbusa]: https://github.com/kolbusa\n\n## Installation\n\nPackage managers are the most convenient way to install stalonetray. It is\npackaged for several Linux distributions and BSD variants. On Debian and\nUbuntu, run:\n\n```sh\nsudo apt install stalonetray\n```\n\nOn Fedora run:\n\n```sh\nsudo dnf install stalonetray\n```\n\n## Building from source\n\nStalonetray uses [Meson](https://mesonbuild.com/). Refer to the `meson.options`\nfile for available build options and their default values.\n\nTo build stalonetray using Meson, ensure necessary dependencies are installed\n--- by default only Xlib and libXinerama development packages are required ---\nand run the standard Meson build commands:\n\n```sh\nmeson setup builddir\nmeson compile -C builddir stalonetray\n```\n\nThis should build the `stalonetray` binary in the `builddir` directory.\n\nTo build stalonetray's documentation, you'll need `xsltproc` and DocBook\nstylesheets installed first. Then build the `manpage` target:\n\n```sh\nmeson compile -C builddir manpage\n```\n\nThis creates the `stalonetray.1` file in the `builddir` directory.\n\nInstallation from source can be done with:\n\n```sh\nmeson install -C builddir\n```\n"
  },
  {
    "path": "TODO",
    "content": "+ Each icon must have a newly created parent window, which matches its\n  dimentions (for ease of move and for them to be centered ?).\n+ Anvanced grow\n+ Reorganize settings (and vars)\n- Make use of resources (WONTDO)\n+ rc file\n+ Need to handle unusual sizes of \"icons\" (and resolve when to force\n  constraints)\n+ Make satray survive even if window suddenly dissapears\n  (during some operation, when destroy\\unmap was not handled)\n+ KDE tray implementation (solves previous problem)\n+ Handle tray icon move/resize\n+ Simplify rc file syntax/re-reorganize settings\n+ main.c cleanup\n+ update transparent/parent background on icon move/resize\n+ review DBG() calls in accordance with doc/DEBUG-LEVELS\n+ reacquire tray selection after loss (if holder exits) (update sample rc(done))\n+ find and fix typos\n+ fix (almost all) warnings with -Wall\n+ .spec file for building rpms\n+ man page\n+ tray grow with wnd deco\n\nfor 0.4\n+ default values in the manpage, MWM/EWMH compliancy must be noted\n+ ontop/onbottom/normal (wnd_layer param)\n+ xorg7 build troubles\n+ more flexible wnd deco specification\n+ make use of ewmh spec (_NET_WM_STATE_SKIP_PAGER, _NET_WM_WINDOW_TYPE_DOCK and friends)\n+ all configure.in needs review (refuse to build if something goes wrong)\n+ update DBG so that it does not require C99\n+ *wm tests (pwm(2), e17 have some issues):\n\t+ WMaker (everything works)\n\t+ Fvwm (for sure)\n\t+ PWM(1) (no_{title,border} do not work)\n\t+ E17 (the same =()\n+ something has happened to vertical layout (all works)\n+ tests on sf cf\n\t+ now stalonetray can be build with Sun ONE 8 C compiler =) (because it's sources conform to C90 specs)\n+ change status to beta =)\n\n\nfor 0.5(unreleased, renamed to 0.6)\n+ code cleanup (again :() (REVIEW layout.c very thoroughly!!!)\n+ fix geometry issues\n+ GNUisms in debug.h\n+ icons are not aligned properly inside their mid-parents\n+ basename can be either in libgen.h or in string.h o_O : XPG ver from libgen.h was chosen\n+ update manpages, etc (docbook for documentation ?)\n+ fix default rc file name\n+ backtrace in configure.in is not detected ?\n+ make boolean cfg file opts to have _optional_ argument\n+ make permanent behaviour default and remove it from configuration\n+ default values in documentation\n+ validate stalonetray.xml\n+ collect icons on startup (test, KDE icons issue) needs XEMBED ?!!!\n+ Full XEMBED implementation (?)\n+ issue with composite extension on (seq1,2)\n+ version info\n+ pixmap bg\n+ check rc keyword names for concistency and update stalonetrayrc.sample\n+ test non-debug version\n+ test with LARGE pixmap\n+ PORTING\n+ fix the website for IE (and colors)\n\nfor 0.7\n- DOXYGEN documentation (WONTDO, 0.8?)\n- one state instead of multiple flags for icons (WONTDO)\n- split main.c into: signals.c events.c main.c (WONTDO)\n- support for WINE icons (WONTDO: isn't it WINE problem?)\n+ consistent naming scheme for funcs\n+ shrink back mode\n+ reconsider API\n+ improve X11-using code\n+ params for boolean cmd line parameters (reconsider cmd line params and rc file directives) ??? (--skip-taskbar=true)\n+ indicate parsing errors (make them fatal?)\n+ comments\n+ bg tinting (with fade out)\n+ wmaker mode (SORTOFWORKS)\n+ fall back to legacy KDE icons handling scheme when list of icons is not available\n+ withdrawn mode should be configurable\n+ XEMBED test suite ?\n+ rethink command line params names\n+ merge changes from 0.6 bugfixes branch (x86_64 patches pending) (size hints handling?)\n+ layout _needs_ fixin` (TEST!!!!, seq1) (HOORAY!!!!!)\n+ automatic version string in _all_ documentation\n+ update README/docs\n+ sanitize DBG levels\n\nfor 0.7.6\n+ ensure that x11_ok() called at all exit paths from x11-calling functions\n+ startup issues (collect existing icons)\n- focus issue in Wmaker\n\nfor 0.8\n+ slot size vs. icon size\n+ review ignore_icon_resize usage\n+ all dims must be given in slots not in pixels\n+ scrollable tray area? (see http://wnd.katei.fi/weblog/entry-7)\n\t+ need repeater mode\n\t+ support for mouse wheel (scroll vertically if mousewheeling over vert sb and\n\t  horizontally if over horz sb; default to orientation otherwise)\n+ strut WM hints (requires Xinerama?)\n+ fix geometry code\n+ fix wmaker docking mode (--widthrawn=off/simple/wmaker)\n\t- proper dockapp behaviour in WM\n+ add --kludge command line param that turns kludges on/off\n\t+ move respect icon hints under this category\n+ geometry bugs (wrong placement) (CANTREPRODUCE?)\n+ valgrind\n\t+ color background\n\t+ xpm background\n\t+ xpm background + transparency\n+ remote interface (via send event to tray selection manager)\n+ profile the code\n\t+ powertop; fix event loop to block if there are no events and no scrolling\n\t\t+ proper handling of signals required!!!\n\t\t\t+ SIGUSR1 handing is OK (its for debug only anyway)\n\t\t\t+ termination signals wont be handled to avoid hacks...\n+ fix complaint about icon placement in WM dockapp mode\n+ fix complaint about window layer under WM (CANTREPRODUCE, Window Maker 0.92.0)\n+ rework DBG/ERR/DIE\n+ update sample RC\n+ proper formatting of log lines (date + remove location)\n+ fix bug with resize\n\nfor 0.8.1,2,../0.9\n- systray 0.3 spec (VISUAL)\n- proper dockapp behaviour in WM\n- profile the code\n\t- gprofile; use array in icons.c instead of list\n- RENDER implementation of tinting/compositing, take implementation from rxvt\n- sanitize x11 calls: check return values instead of relying on x11_ok() whenever possible\n- track size changes of the icons that failed to fit into the tray\n- rework root transparency code (handle cases when root pmap size is less than root window; take implementation from urxvt)\n- restructure command line params and reduce their count\n- rework command line interface\n"
  },
  {
    "path": "generate-manpage.sh",
    "content": "#!/bin/sh\n#\n# A simple script to build the manpage for stalonetray using xsltproc.\n#\n# The logic in this script would've been placed in the meson.build file, but as\n# of the time of writing Meson doesn't provide a way to write intermediate\n# files from the stdout of a command, instead relying on the command creating\n# the file itself.\n#\n# Plenty of the logic was viable within Meson, but was moved here to simplify\n# the meson.build file.\n#\n# Usage: ./generate-manpage.sh template output stalonetray-version\n#\n# Requires: sed, xsltproc\n#\n# TODO: Accept features as arguments to include/exclude parts of the manpage.\n\ntemplate=\"$1\"\noutput=\"$2\"\nversion_str=\"$3\"\nstylesheet=\"\"\n\nintermediate_xml=\"$(basename \"$template\" .in)\"\n\nfor cmd in sed xsltproc; do\n  if ! command -v \"$cmd\" >/dev/null 2>&1; then\n    echo \"$cmd is required to build the manpage.\" >&2\n    exit 1\n  fi\ndone\n\nfor root in \\\n    /usr/share/sgml/docbook/stylesheet/xsl/nwalsh \\\n    /usr/share/xml/docbook/stylesheet/docbook-xs \\\n    /usr/share/xml/docbook/stylesheet/docbook-xsl-nons \\\n    /usr/share/xml/docbook/xsl-stylesheets \\\n    /usr/share/sgml/docbook/xsl-stylesheets \\\n    /usr/share/xml/docbook/xsl-stylesheets-*-nons \\\n    /usr/share/sgml/docbook/xsl-stylesheets \\\n    /usr/share/xsl/docbook \\\n    /usr/local/share/xsl/docbook \\\n  ; do\n  if [ -f \"$root/manpages/docbook.xsl\" ]; then\n    stylesheet=\"$root/manpages/docbook.xsl\"\n    break\n  fi\ndone\n\nif [ -z \"$stylesheet\" ]; then\n  echo \"Could not find docbook manpage stylesheet.\" >&2\n  exit 2\nfi\n\nsed \"s/@VERSION_STR@/$version_str/g\" \"$template\" > \"$intermediate_xml\"\nxsltproc -o \"$output\" \"$stylesheet\" \"$intermediate_xml\"\n\nrm -f \"$intermediate_xml\"\n\n# vim: tabstop=2 shiftwidth=2 expandtab\n"
  },
  {
    "path": "meson.build",
    "content": "project(\n  'stalonetray',\n  'c',\n  version: '1.0.3',\n  default_options: ['warning_level=3', 'c_std=gnu23'],\n  meson_version: '>=1.1.0',\n)\n\nbuild_args = [\n  '-O2',\n  '-Wno-missing-braces',\n  '-DPROGNAME=\"@0@\"'.format(meson.project_name()),\n  '-DVERSION=\"@0@\"'.format(meson.project_version()),\n]\n\nproject_sources = []\n\n# == Dependencies & Optional Features ==========================================\n\ndependencies = {\n  'x11': dependency('x11', required: true),\n  'xinerama': dependency('xinerama', required: get_option('xinerama')),\n  'xpm': dependency('xpm', required: get_option('xpm')),\n}\n\nassert(dependencies['x11'].found(), 'Xlib is required to build stalonetray')\n\n# == Sources & Headers =========================================================\n\nsubdir('src')\n\n# == Build Options =============================================================\n\nfeature_list = []\n\nif get_option('xinerama').enabled()\n  build_args += ['-D_ST_WITH_XINERAMA']\n  feature_list += ['xinerama']\nendif\n\nif get_option('xpm').enabled()\n  build_args += ['-D_ST_WITH_XPM']\n  feature_list += ['xpm']\nendif\n\nif get_option('native_kde').enabled()\n  build_args += ['-D_ST_WITH_NATIVE_KDE']\n  feature_list += ['native_kde']\nendif\n\nif get_option('trace_events')\n  build_args += ['-D_ST_TRACE_EVENTS']\n  feature_list += ['trace_events']\nendif\n\nif get_option('delay_embedding_confirmation')\n  build_args += ['-D_ST_DELAY_EMBEDDING_CONFIRMATION']\n  feature_list += ['delay_embedding_confirmation']\nendif\n\nif get_option('dump_window_information')\n  build_args += ['-D_ST_DUMP_WINDOW_INFORMATION']\n  feature_list += ['dump_window_information']\nendif\n\nif get_option('exit_gracefully')\n  build_args += ['-D_ST_EXIT_GRACEFULLY']\n  feature_list += ['exit_gracefully']\nendif\n\nbuild_args += ['-DFEATURE_LIST=\"@0@\"'.format(' '.join(feature_list))]\n\n# == Targets ===================================================================\n\n# TODO: Replace this with dict.values() when meson 1.10 is released.\n_dependencies = []\n\nforeach _, dep : dependencies\n  _dependencies += [dep]\nendforeach\n\nexecutable(\n  'stalonetray',\n  project_sources,\n  c_args: build_args,\n  dependencies: _dependencies,\n  install: true,\n)\n\n# == Documentation =============================================================\n\ngen_manpage = find_program('generate-manpage.sh')\n\ncustom_target(\n  'manpage',\n  input: 'stalonetray.xml.in',\n  output: 'stalonetray.1',\n  command: [gen_manpage, '@INPUT@', '@OUTPUT@', meson.project_version()],\n  install: true,\n  install_dir: join_paths(get_option('prefix'), get_option('mandir'), 'man1'),\n)\n\n# == Sample Configuration ======================================================\n\ninstall_data(\n  'stalonetrayrc.sample',\n  install_dir: get_option('sysconfdir'),\n  rename: ['stalonetrayrc'],\n)\n"
  },
  {
    "path": "meson.format",
    "content": "indent_by = '  '\nmax_line_length = 80\n"
  },
  {
    "path": "meson.options",
    "content": "option('xinerama', type: 'feature', value: 'enabled',\n  description: 'Enable Xinerama support for multiple monitors')\noption('xpm', type: 'feature', value: 'auto',\n  description: 'Enable XPM background support')\noption('native_kde', type: 'feature', value: 'auto',\n  description: 'Enable native KDE system tray support')\n\noption('trace_events', type: 'boolean', value: false,\n  description: 'Enable X11 event tracing for debugging purposes')\noption('delay_embedding_confirmation', type: 'boolean', value: false,\n  description: 'Delay sending XEMBED_EMBEDDED_NOTIFY message for debugging')\noption('dump_window_information', type: 'boolean', value: false,\n  description: 'Use xprop/xwininfo to dump icon window information')\noption('exit_gracefully', type: 'boolean', value: true,\n  description: 'Use non-portable hack to exit gracefully on signal')\n"
  },
  {
    "path": "src/common.h",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * common.h\n * Mon, 01 May 2006 01:45:08 +0700\n * -------------------------------\n * Common declarations\n * -------------------------------*/\n\n#ifndef _COMMON_H_\n#define _COMMON_H_\n\n#include \"debug.h\"\n\n/* Default icon size */\n#define FALLBACK_ICON_SIZE 24\n/* Minimal icon size */\n#define MIN_ICON_SIZE 16\n/* Default KDE icon size */\n#define KDE_ICON_SIZE 22\n/* Number of time icon is allowed to change its size after which\n * stalonetray gives up */\n#define ICON_SIZE_RESETS_THRESHOLD 2\n\n/* Meaningful names for return values */\n#define SUCCESS 1\n#define FAILURE 0\n\n/* Simple macro to return status and log it if necessary */\n#define RETURN_STATUS(rc) \\\n    do { \\\n        LOG_TRACE( \\\n            (\"status = %s\\n\", ((rc) == SUCCESS ? \"SUCCESS\" : \"FAILURE\"))); \\\n        return rc; \\\n    } while (0)\n\n/* Meaningful names for return values of icon mass-operations */\n#define MATCH 1\n#define NO_MATCH 0\n\n/* Simple macro to return mass-op status and log it if necessary */\n#define RETURN_MATCH(rc) \\\n    do { \\\n        LOG_TRACE((\"status = %s\\n\", rc == MATCH : \"MATCH\" : \"NO_MATCH\")); \\\n        return rc; \\\n    } while (0)\n\n/* Meaningful names for return values of icon mass-operations */\n#define MATCH 1\n/* Portable macro for function name */\n#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L\n#define __FUNC__ __func__\n#elif defined(__GNUC__) && __GNUC__ >= 3\n#define __FUNC__ __FUNCTION__\n#else\n#define __FUNC__ \"unknown\"\n#endif\n\n/* DIE */\n#define DIEDIE() exit(-1)\n/* Print a message and... DIE */\n#define DIE(message) \\\n    do { \\\n        LOG_ERROR(message); \\\n        DIEDIE(); \\\n    } while (0)\n/* Log OOM condition */\n#define LOG_ERR_IE(message) \\\n    do { \\\n        LOG_ERROR( \\\n            (\"Internal error, please report to maintaner (see AUTHORS)\\n\")); \\\n        LOG_ERROR(message); \\\n    } while (0);\n/* DIE on internal error */\n#define DIE_IE(message) \\\n    do { \\\n        LOG_ERR_IE(message); \\\n        DIEDIE(); \\\n    } while (0)\n/* Log OOM condition */\n#define LOG_ERR_OOM(message) \\\n    do { \\\n        LOG_ERROR((\"Out of memory\\n\")); \\\n        LOG_ERROR(message); \\\n    } while (0);\n/* DIE on OOM error */\n#define DIE_OOM(message) \\\n    do { \\\n        LOG_ERR_OOM(message); \\\n        DIEDIE(); \\\n    } while (0)\n\n/*** WARNING: feed following macros only with side-effects-free expressions\n * ***/\n/* Get a value within target interval */\n#define cutoff(tgt, min, max) \\\n    (tgt) < (min) ? (min) : ((tgt) > (max) ? max : tgt)\n/* Update value to fit into target interval */\n#define val_range(tgt, min, max) (tgt) = cutoff(tgt, min, max)\n\n#endif\n"
  },
  {
    "path": "src/debug.c",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * debug.c\n * Mon, 01 May 2006 01:44:42 +0700\n * -------------------------------\n * Debugging code/utilities\n * -------------------------------*/\n\n#include \"debug.h\"\n#include \"xutils.h\"\n\n#include <limits.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <string.h>\n\n#ifdef DBG_PRINT_PID\n#include <sys/types.h>\n#include <unistd.h>\n#endif\n\nint debug_output_disabled = 0;\n\n/* Disables all output from debugging macros */\nvoid debug_disable_output()\n{\n    debug_output_disabled = 1;\n}\n\n/* Print the message to STDERR (varadic macros is not used in the sake of\n * portability) */\nvoid print_message_to_stderr(const char *fmt, ...)\n{\n    static char msg[PATH_MAX];\n    va_list va;\n    va_start(va, fmt);\n    vsnprintf(msg, PATH_MAX, fmt, va);\n    va_end(va);\n    fprintf(stderr, \"%s\", msg);\n}\n\nint trace_mode = False;\n\nvoid print_trace_header(\n    const char *funcname, const char *fname, const int line)\n{\n    static pid_t pid = 0;\n#ifdef TRACE_TIMESTAMP\n    {\n        static char timestr[PATH_MAX + 1];\n        time_t curtime = time(NULL);\n        struct tm *loctime = localtime(&curtime);\n        strftime(timestr, PATH_MAX, \"%b %e %H:%M:%S\", loctime);\n        fprintf(stderr, \"%s \", timestr);\n    }\n#endif\n#ifdef TRACE_WM_NAME\n    fprintf(stderr, \"%s \", settings.wnd_name);\n#endif\n#ifdef TRACE_PID\n    if (!pid) pid = getpid();\n    fprintf(stderr, \"(%d) \", pid);\n#endif\n#ifdef TRACE_DPY\n    if (tray_data.dpy != NULL)\n        fprintf(stderr, \"(%s) \", DisplayString(tray_data.dpy));\n    else\n        fprintf(stderr, \"(dpy) \");\n#endif\n#ifdef TRACE_VERBOSE_LOCATION\n    fprintf(stderr, \"(%s:%4d) \", fname, line);\n#endif\n    fprintf(stderr, \"%s(): \", funcname);\n\n    (void) pid; /* unused */\n    (void) fname; /* unused */\n    (void) line; /* unused */\n}\n\n/* Print the summary of icon data */\nint print_icon_data(struct TrayIcon *ti)\n{\n#ifdef DEBUG\n    XWindowAttributes xwa;\n#endif\n    LOG_INFO((\"wid = 0x%lx\\n\", ti->wid));\n    LOG_TRACE((\"  self = %p\\n\", (void *) ti));\n    LOG_TRACE((\"  prev = %p\\n\", (void *) ti->prev));\n    LOG_TRACE((\"  next = %p\\n\", (void *) ti->next));\n    LOG_TRACE((\"  invalid = %d\\n\", ti->is_invalid));\n    LOG_TRACE((\"  layed_out = %d\\n\", ti->is_layed_out));\n    LOG_TRACE((\"  update_pos = %d\\n\", ti->is_updated));\n    LOG_INFO((\"  name = %s\\n\",\n        x11_get_window_name(tray_data.dpy, ti->wid, \"<unknown>\")));\n    LOG_INFO((\"  visible = %d\\n\", ti->is_visible));\n    LOG_INFO((\"  position (grid) = %dx%d+%d+%d\\n\", ti->l.grd_rect.w,\n        ti->l.grd_rect.h, ti->l.grd_rect.x, ti->l.grd_rect.y));\n    LOG_INFO((\"  position (pixels) = %dx%d+%d+%d\\n\", ti->l.icn_rect.w,\n        ti->l.icn_rect.h, ti->l.icn_rect.x, ti->l.icn_rect.y));\n    LOG_INFO((\"  wnd_sz = %dx%d\\n\", ti->l.wnd_sz.x, ti->l.wnd_sz.y));\n    LOG_TRACE((\"  cmode = %d\\n\", ti->cmode));\n    LOG_INFO((\"  xembed = %d\\n\", ti->is_xembed_supported));\n    if (ti->is_xembed_supported) {\n        LOG_TRACE(\n            (\"  xembed_accepts_focus = %d\\n\", ti->is_xembed_accepts_focus));\n        LOG_TRACE(\n            (\"  xembed_last_timestamp = %ld\\n\", ti->xembed_last_timestamp));\n        LOG_TRACE((\"  xembed_last_msgid = %ld\\n\", ti->xembed_last_msgid));\n    }\n    LOG_INFO((\"  embedded = %d\\n\", ti->is_embedded));\n#ifdef DEBUG\n    if (ti->is_embedded) {\n        LOG_TRACE((\"  mid-parent = 0x%lx\\n\", ti->mid_parent));\n        if (!XGetWindowAttributes(tray_data.dpy, ti->mid_parent, &xwa)) {\n            LOG_TRACE((\n                \"  ERR: XGetWindowAttributes() on mid-parent window faied\\n\"));\n        } else {\n            LOG_TRACE((\"  mid-parent wid = 0x%lx\\n\", ti->mid_parent));\n            LOG_TRACE((\"  mid-parent state = %s\\n\",\n                xwa.map_state == IsUnmapped\n                    ? \"Unmapped\"\n                    : (xwa.map_state == IsUnviewable ? \"Unviewable\"\n                                                     : \"Viewable\")));\n            LOG_TRACE((\"  mid-parent geometry = %dx%d+%d+%d\\n\", xwa.width,\n                xwa.height, xwa.x, xwa.y));\n        }\n    }\n    if (settings.log_level > LOG_LEVEL_INFO) {\n        Window real_parent, root, *children = NULL;\n        unsigned int nchildren = 0;\n        int rc;\n        rc = XQueryTree(tray_data.dpy, ti->wid, &root, &real_parent, &children,\n            &nchildren);\n        if (rc && children != NULL && nchildren > 0) XFree(children);\n        if (!rc) {\n            LOG_TRACE((\"  ERR: XQueryTree() failed\\n\"));\n        } else {\n            LOG_TRACE(\n                (\"  parent wid from XQueryTree() = 0x%lx\\n\", real_parent));\n        }\n    }\n    if (!XGetWindowAttributes(tray_data.dpy, ti->wid, &xwa)) {\n        LOG_TRACE((\"  ERR: XGetWindowAttributes() on icon window failed\\n\"));\n    } else {\n        LOG_TRACE((\"  geometry from XGetWindowAttributes() = %dx%d+%d+%d\\n\",\n            xwa.width, xwa.height, xwa.x, xwa.y));\n        LOG_TRACE((\"  mapstate from XGetWindowAttributes() = %s\\n\",\n            xwa.map_state == IsUnmapped\n                ? \"Unmapped\"\n                : (xwa.map_state == IsUnviewable ? \"Unviewable\"\n                                                 : \"Viewable\")));\n    }\n#endif\n    /* This resets x11 error state (which might have left from X11 calls above)\n     */\n    x11_ok();\n    return NO_MATCH;\n}\n\nvoid dump_icon_list()\n{\n    icon_list_forall(&print_icon_data);\n}\n"
  },
  {
    "path": "src/debug.h",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * debug.h\n * Tue, 24 Aug 2004 12:05:38 +0700\n * -------------------------------\n * Debugging code/utilities\n * -------------------------------*/\n\n#ifndef _DEBUG_H_\n#define _DEBUG_H_\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"common.h\"\n#include \"settings.h\"\n#include \"tray.h\"\n\n#include <string.h>\n#include <time.h>\n\n#define LOG_LEVEL_ERR 0\n#define LOG_LEVEL_INFO 1\n#define LOG_LEVEL_TRACE 2\n\nextern int debug_output_disabled;\n\n/* Disables all output from debugging macros */\nvoid debug_disable_output();\n\n/* Print the message to STDERR (in the sake of portability, we do not use\n * varadic macros) */\nvoid print_message_to_stderr(const char *fmt, ...)\n#ifdef __GNUC__\n    __attribute__((__format__(__printf__, 1, 2)))\n#endif\n    ;\n\n/* The following defines control what is printed in each logged line */\n#ifdef DEBUG\n/* Print window name */\n#define TRACE_WM_NAME\n/* Print pid */\n#undef TRACE_PID\n/* Print name of display */\n#undef TRACE_DPY\n/* Print timestamp */\n#define TRACE_TIMESTAMP\n/* Print name of a function, file and line which outputs the message */\n/* Othewise, only function name is printed */\n#undef TRACE_VERBOSE_LOCATION\n#endif\n\n/* Print the debug header as specified by defines below */\nvoid print_trace_header(\n    const char *funcname, const char *fname, const int line);\n#define PRINT_TRACE_HEADER() \\\n    do { \\\n        print_trace_header(__FUNC__, __FILE__, __LINE__); \\\n    } while (0)\n/* Print the debug message of specified level */\n#define LOG_TRACE(message) \\\n    do { \\\n        if (!debug_output_disabled \\\n            && settings.log_level >= LOG_LEVEL_TRACE) { \\\n            PRINT_TRACE_HEADER(); \\\n            print_message_to_stderr message; \\\n        }; \\\n    } while (0)\n\n#define LOG_ERROR(message) \\\n    do { \\\n        if (!debug_output_disabled) { \\\n            if (settings.log_level >= LOG_LEVEL_TRACE) PRINT_TRACE_HEADER(); \\\n            if (settings.log_level >= LOG_LEVEL_ERR) \\\n                print_message_to_stderr message; \\\n        } \\\n    } while (0)\n\n#define LOG_INFO(message) \\\n    do { \\\n        if (!debug_output_disabled) { \\\n            if (settings.log_level >= LOG_LEVEL_TRACE) PRINT_TRACE_HEADER(); \\\n            if (settings.log_level >= LOG_LEVEL_INFO) \\\n                print_message_to_stderr message; \\\n        } \\\n    } while (0)\n\n/* Print the summary of icon data */\nint print_icon_data(struct TrayIcon *ti);\n/* Print icon list contents */\nvoid dump_icon_list();\n\n#endif\n"
  },
  {
    "path": "src/embed.c",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * embed.c\n * Fri, 03 Sep 2004 20:38:55 +0700\n * -------------------------------\n * embedding cycle implementation\n * -------------------------------*/\n\n#include <X11/Xlib.h>\n#include <X11/Xutil.h>\n\n#include <assert.h>\n#include <unistd.h>\n\n#ifdef DELAY_EMBEDDING_CONFIRMATION\n#include <pthread.h>\n#endif\n\n#include \"embed.h\"\n\n#include \"common.h\"\n#include \"debug.h\"\n#include \"icons.h\"\n#include \"settings.h\"\n#include \"tray.h\"\n\n#include \"xutils.h\"\n\n#define CALC_INNER_POS(x_, y_, ti_) \\\n    do { \\\n        x_ = (ti_->l.icn_rect.w - ti_->l.wnd_sz.x) / 2; \\\n        y_ = (ti_->l.icn_rect.h - ti_->l.wnd_sz.y) / 2; \\\n    } while (0);\n\n#ifdef DELAY_EMBEDDING_CONFIRMATION\nvoid *send_delayed_confirmation(void *dummy)\n{\n    struct TrayIcon *ti = (struct TrayIcon *)dummy;\n    Display *dpy;\n\n    if ((dpy = XOpenDisplay(settings.display_str)) != NULL) {\n        LOG_TRACE(\n            (\"will now sleep for %d seconds\\n\", settings.confirmation_delay));\n        sleep(settings.confirmation_delay);\n        LOG_TRACE((\"sending embedding confirmation\\n\"));\n        x11_send_client_msg32(dpy, tray_data.tray, tray_data.tray,\n            tray_data.xa_tray_opcode, 0, STALONE_TRAY_DOCK_CONFIRMED, ti->wid,\n            0, 0);\n        XSync(dpy, False);\n        XClose(dpy);\n    } else {\n        DIE_IE((\"failed to initialize display\\n\"));\n    }\n    pthread_exit(NULL);\n}\n#endif\n\nint embedder_embed(struct TrayIcon *ti)\n{\n    int x, y, rc;\n    XSetWindowAttributes xswa;\n    /* If the icon is being embedded as hidden,\n     * we just start listening for property changes\n     * to track _XEMBED mapped state */\n    if (!ti->is_visible) {\n        XSelectInput(tray_data.dpy, ti->wid, PropertyChangeMask);\n        return x11_ok();\n    }\n    /* 0. Start listening for events on icon window */\n    XSelectInput(\n        tray_data.dpy, ti->wid, StructureNotifyMask | PropertyChangeMask);\n    if (!x11_ok()) RETURN_STATUS(FAILURE);\n    /* 1. Calculate position of mid-parent window */\n    CALC_INNER_POS(x, y, ti);\n    LOG_TRACE(\n        (\"position of icon 0x%lx inside the tray: (%d, %d)\\n\", ti->wid, x, y));\n    /* 2. Create mid-parent window */\n    ti->mid_parent = XCreateSimpleWindow(tray_data.dpy, tray_data.tray,\n        ti->l.icn_rect.x + x, ti->l.icn_rect.y + y, ti->l.wnd_sz.x,\n        ti->l.wnd_sz.y, 0, 0, 0);\n    /* 2.5. Setup mid-parent window properties */\n    xswa.win_gravity = settings.bit_gravity;\n    XChangeWindowAttributes(\n        tray_data.dpy, ti->mid_parent, CWWinGravity, &xswa);\n#ifndef DEBUG_HIGHLIGHT_MIDPARENT\n    XSetWindowBackgroundPixmap(tray_data.dpy, ti->mid_parent, ParentRelative);\n#else\n    XSetWindowBackgroundPixmap(tray_data.dpy, ti->mid_parent, 0);\n#endif\n    if (!x11_ok() || ti->mid_parent == None) RETURN_STATUS(FAILURE);\n    LOG_TRACE((\"created mid-parent window 0x%lx\\n\", ti->mid_parent));\n    /* 3. Embed window into mid-parent */\n    switch (ti->cmode) {\n    case CM_KDE:\n    case CM_FDO:\n        XReparentWindow(tray_data.dpy, ti->wid, ti->mid_parent, 0, 0);\n        XMapRaised(tray_data.dpy, ti->wid);\n        break;\n    default: break;\n    }\n    /* 4. Show mid-parent */\n    XMapWindow(tray_data.dpy, ti->mid_parent);\n    /* mid-parent must be lowered so that it does not osbcure\n     * scollbar windows */\n    XLowerWindow(tray_data.dpy, ti->mid_parent);\n    if (!x11_ok()) RETURN_STATUS(FAILURE);\n#ifndef DELAY_EMBEDDING_CONFIRMATION\n    /* 5. Send message confirming embedding */\n    rc = x11_send_client_msg32(tray_data.dpy, tray_data.tray, tray_data.tray,\n        tray_data.xa_tray_opcode, 0, STALONE_TRAY_DOCK_CONFIRMED, ti->wid, 0,\n        0);\n    RETURN_STATUS(rc != 0);\n#else\n    /* This is here for debugging purposes */\n    {\n        pthread_t delayed_thread;\n        pthread_create(\n            &delayed_thread, NULL, send_delayed_confirmation, (void *)ti);\n        LOG_TRACE((\"sent delayed confirmation\\n\"));\n        RETURN_STATUS(SUCCESS);\n    }\n#endif\n}\n\nint embedder_unembed(struct TrayIcon *ti)\n{\n    if (!ti->is_embedded) return SUCCESS;\n    switch (ti->cmode) {\n    case CM_KDE:\n    case CM_FDO:\n        /* Unembed icon as described in system tray protocol */\n        if (ti->is_embedded) {\n            XSelectInput(tray_data.dpy, ti->wid, NoEventMask);\n            XUnmapWindow(tray_data.dpy, ti->wid);\n            XReparentWindow(tray_data.dpy, ti->wid,\n                DefaultRootWindow(tray_data.dpy), ti->l.icn_rect.x,\n                ti->l.icn_rect.y);\n            XMapRaised(tray_data.dpy, ti->wid);\n            if (!x11_ok())\n                LOG_ERROR((\"failed to move icon 0x%lx out of the tray\\n\", ti->wid));\n        }\n        /* Destroy mid-parent */\n        if (ti->mid_parent != None) {\n            XDestroyWindow(tray_data.dpy, ti->mid_parent);\n            if (!x11_ok())\n                LOG_ERROR((\"failed to destroy icon mid-parent 0x%lx\\n\",\n                    ti->mid_parent));\n        }\n        break;\n    default:\n        LOG_ERR_IE((\"Error: the compatibility mode %d is not supported \"\n                    \"(should not happen)\\n\",\n            ti->cmode));\n        return FAILURE;\n    }\n    LOG_TRACE((\"done unembedding 0x%lx\\n\", ti->wid));\n    RETURN_STATUS(x11_ok()\n        == 0); /* This resets error status for the generations to come (XXX) */\n}\n\nint embedder_hide(struct TrayIcon *ti)\n{\n    XUnmapWindow(tray_data.dpy, ti->mid_parent);\n    /* We do not wany any StructureNotify events for icon window anymore */\n    XSelectInput(tray_data.dpy, ti->wid, PropertyChangeMask);\n    if (!x11_ok()) {\n        ti->is_invalid = True;\n        return FAILURE;\n    } else {\n        ti->is_size_set = False;\n        ti->num_size_resets = 0;\n        ti->is_visible = False;\n        return SUCCESS;\n    }\n}\n\nint embedder_show(struct TrayIcon *ti)\n{\n    unsigned int x, y;\n    /* If the window has never been embedded,\n     * perform real embedding */\n    if (ti->mid_parent == None) {\n        ti->is_visible = True;\n        return embedder_embed(ti);\n    }\n    /* 0. calculate new position for mid-parent */\n    CALC_INNER_POS(x, y, ti);\n    /* 1. move mid-parent to new location */\n    XMoveResizeWindow(tray_data.dpy, ti->mid_parent, ti->l.icn_rect.x + x,\n        ti->l.icn_rect.y + y, ti->l.wnd_sz.x, ti->l.wnd_sz.y);\n    /* 2. adjust icon position inside mid-parent */\n    XMoveWindow(tray_data.dpy, ti->wid, 0, 0);\n    /* 3. map icon ? */\n    XMapRaised(tray_data.dpy, ti->wid);\n    /* 4. map mid-parent */\n    XMapWindow(tray_data.dpy, ti->mid_parent);\n    XSelectInput(\n        tray_data.dpy, ti->wid, StructureNotifyMask | PropertyChangeMask);\n    if (!x11_ok()) {\n        ti->is_invalid = True;\n        return FAILURE;\n    } else {\n        ti->is_visible = True;\n        return SUCCESS;\n    }\n}\n\nstatic int update_forced = False;\n\nstatic int embedder_update_window_position(struct TrayIcon *ti)\n{\n    int x, y;\n    /* Ignore hidden icons */\n    if (!ti->is_visible) return NO_MATCH;\n    /* Update only those icons that do want it (everyone if update was forced)\n     */\n    if (!update_forced && !ti->is_updated && !ti->is_resized\n        && ti->is_embedded)\n        return NO_MATCH;\n    LOG_TRACE((\"Updating position of icon 0x%lx\\n\", ti->wid));\n    /* Recalculate icon position */\n    CALC_INNER_POS(x, y, ti);\n    /* Reset the flags */\n    ti->is_resized = False;\n    ti->is_updated = False;\n    /* Move mid-parent window */\n    XMoveResizeWindow(tray_data.dpy, ti->mid_parent, ti->l.icn_rect.x + x,\n        ti->l.icn_rect.y + y, ti->l.wnd_sz.x, ti->l.wnd_sz.y);\n    /* Sanitize icon position inside mid-parent */\n    XMoveWindow(tray_data.dpy, ti->wid, 0, 0);\n    /* Refresh the icon */\n    embedder_refresh(ti);\n    if (!x11_ok()) {\n        LOG_TRACE((\"failed to update position of icon 0x%lx\\n\", ti->wid));\n        ti->is_invalid = True;\n    }\n    return NO_MATCH;\n}\n\nint embedder_update_positions(int forced)\n{\n    /* I wish C had closures =( */\n    update_forced = forced;\n    icon_list_forall(&embedder_update_window_position);\n    return SUCCESS;\n}\n\nint embedder_refresh(struct TrayIcon *ti)\n{\n    if (!ti->is_visible) return NO_MATCH;\n    XClearWindow(tray_data.dpy, ti->mid_parent);\n    x11_refresh_window(\n        tray_data.dpy, ti->wid, ti->l.wnd_sz.x, ti->l.wnd_sz.y, True);\n    /* Check if the icon has survived all these manipulations */\n    if (!x11_ok()) {\n        LOG_TRACE((\"could not refresh 0x%lx\\n\", ti->wid));\n        ti->is_invalid = True;\n    }\n    return NO_MATCH;\n}\n\n/* This function defines initial icon size or\n * is used to reset size of the icon window */\nint embedder_reset_size(struct TrayIcon *ti)\n{\n    struct Point icon_sz = {0, 0};\n    int rc = FAILURE;\n    /* Do not reset size for non-KDE icons with size set if icon_resizes\n     * are handled */\n    if (ti->is_size_set && ti->cmode != CM_KDE\n        && !(settings.kludge_flags & KLUDGE_FORCE_ICONS_SIZE))\n        return SUCCESS;\n    /* Increase counter of size resets for given icon. If this number\n     * exeeds the threshold, do nothing. This should work around the icons\n     * that react badly to size changes */\n    if (ti->is_size_set) ti->num_size_resets++;\n    if (ti->num_size_resets > ICON_SIZE_RESETS_THRESHOLD) return SUCCESS;\n    if (ti->cmode == CM_KDE) {\n        icon_sz.x = settings.icon_size < KDE_ICON_SIZE ? settings.icon_size\n                                                       : KDE_ICON_SIZE;\n        icon_sz.y = icon_sz.x;\n    } else {\n        /* If icon hints are to be respected, retrive the data */\n        if (settings.kludge_flags & KLUDGE_USE_ICONS_HINTS)\n            rc = x11_get_window_min_size(\n                tray_data.dpy, ti->wid, &icon_sz.x, &icon_sz.y);\n        /* If this has failed, or icon hinst are not respected, or minimal size\n         * hints are too small, fall back to default values */\n        if (!rc || !(settings.kludge_flags & KLUDGE_USE_ICONS_HINTS)\n            || (settings.kludge_flags & KLUDGE_FORCE_ICONS_SIZE)\n            || (icon_sz.x < settings.icon_size\n                && icon_sz.y < settings.icon_size)) {\n            icon_sz.x = settings.icon_size;\n            icon_sz.y = settings.icon_size;\n        }\n    }\n    LOG_TRACE((\"proposed icon size: %dx%d\\n\", icon_sz.x, icon_sz.y));\n    if (x11_set_window_size(tray_data.dpy, ti->wid, icon_sz.x, icon_sz.y)) {\n        ti->l.wnd_sz = icon_sz;\n        ti->is_size_set = True;\n        return SUCCESS;\n    } else {\n        ti->is_invalid = True;\n        return FAILURE;\n    }\n}\n"
  },
  {
    "path": "src/embed.h",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * embed.h\n * Fri, 03 Sep 2004 20:38:55 +0700\n * -------------------------------\n * embedding cycle implementation\n * -------------------------------*/\n\n#ifndef _EMBED_H_\n\n#include \"icons.h\"\n\n/* Constants for compatibility modes */\n/* KDE */\n#define CM_KDE 1\n/* Generic, freedesktop.org */\n#define CM_FDO 2\n\n/* Embed an icon */\nint embedder_embed(struct TrayIcon *ti);\n\n/* Unembed an icon */\nint embedder_unembed(struct TrayIcon *ti);\n\n/* If (forced)\n * \t\trecalculate and update positions of all icons;\n * else\n * \t\trecalculate and update positions of all icons that have requested an\n * update; */\nint embedder_update_positions(int force);\n\n/* Show the icon */\nint embedder_show(struct TrayIcon *ti);\n\n/* Hide the icon */\nint embedder_hide(struct TrayIcon *ti);\n\n/* Refresh icon and its parent */\nint embedder_refresh(struct TrayIcon *ti);\n\n/* (Re)set icon size according to the policy */\nint embedder_reset_size(struct TrayIcon *ti);\n\n#endif\n"
  },
  {
    "path": "src/icons.c",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * icons.c\n * Tue, 24 Aug 2004 12:05:38 +0700\n * -------------------------------\n * Manipulations with reparented\n * windows --- tray icons\n * -------------------------------*/\n\n#include <X11/Xlib.h>\n#include <X11/Xutil.h>\n\n#include \"icons.h\"\n\n#include \"common.h\"\n#include \"debug.h\"\n#include \"layout.h\"\n#include \"list.h\"\n#include \"tray.h\"\n\n#ifdef DEBUG\n#include \"xutils.h\"\n#endif\n\n#include \"assert.h\"\n\nstruct TrayIcon *icons_head = NULL;\n\nstruct TrayIcon *icon_list_new(Window wid, int cmode)\n{\n    struct TrayIcon *new_icon;\n    /* Do not allocate second structure for the same window */\n    if (icon_list_find(wid) != NULL) return NULL;\n    if ((new_icon = malloc(sizeof(struct TrayIcon))) == NULL) {\n        LOG_ERR_OOM((\"Could not allocate memory for new icon\\n\"));\n        return NULL;\n    }\n    new_icon->wid = wid;\n    new_icon->l.wnd_sz.x = 0;\n    new_icon->l.wnd_sz.y = 0;\n    new_icon->mid_parent = None;\n    new_icon->cmode = cmode;\n    new_icon->is_embedded = False;\n    new_icon->is_layed_out = False;\n    new_icon->is_updated = False;\n    new_icon->is_resized = True;\n    new_icon->is_visible = False;\n    new_icon->is_invalid = False;\n    new_icon->is_xembed_supported = False;\n    new_icon->is_size_set = False;\n    new_icon->num_size_resets = 0;\n    LIST_ADD_ITEM(icons_head, new_icon);\n    return new_icon;\n}\n\nint icon_list_free(struct TrayIcon *ti)\n{\n    if (ti != NULL) {\n        LIST_DEL_ITEM(icons_head, ti);\n        free(ti);\n    }\n    return SUCCESS;\n}\n\nstruct TrayIcon *icon_list_next(struct TrayIcon *ti)\n{\n    return (ti != NULL && ti->next != NULL) ? ti->next : icons_head;\n}\n\nstruct TrayIcon *icon_list_prev(struct TrayIcon *ti)\n{\n    struct TrayIcon *tmp;\n    if (ti != NULL && ti->prev != NULL)\n        return ti->prev;\n    else {\n        tmp = icons_head;\n        for (; tmp->next != NULL; tmp = tmp->next)\n            ;\n        return tmp;\n    }\n}\n\nstatic struct TrayIcon *backup_head = NULL;\n\nint icon_list_backup()\n{\n    struct TrayIcon *tmp, *cur, *cur2;\n    /* Refuse to perform second backup in a row */\n    if (backup_head != NULL) {\n        DIE_IE((\"Only one backup of icon list at a time is supported\\n\"));\n    }\n    /* For each icon in the list we allocate new temporary structure and add it\n     * to the end of temporary list backup_head */\n    for (cur = icons_head, cur2 = NULL; cur != NULL;\n         cur = cur->next, cur2 = tmp) {\n        tmp = (struct TrayIcon *)malloc(sizeof(struct TrayIcon));\n        if (tmp == NULL) {\n            LOG_ERR_OOM((\"Could not allocate backup list\"));\n            icon_list_backup_purge();\n            return FAILURE;\n        }\n        memcpy(tmp, cur, sizeof(struct TrayIcon));\n        LIST_INSERT_AFTER(backup_head, cur2, tmp);\n        cur2 = tmp;\n    }\n    return SUCCESS;\n}\n\nint icon_list_restore()\n{\n    struct TrayIcon *cur_b, *cur_i, *prev_sv, *next_sv;\n    LOG_TRACE((\"restoring the icon list from the backup\\n\"));\n    /* Restore the list by copying raw data from\n     * backup list. This assumes that sequences have the\n     * same length. */\n    for (cur_i = icons_head, cur_b = backup_head;\n         cur_i != NULL && cur_b != NULL;\n         cur_i = cur_i->next, cur_b = cur_b->next) {\n        prev_sv = cur_i->prev;\n        next_sv = cur_i->next;\n        memcpy(cur_i, cur_b, sizeof(struct TrayIcon));\n        cur_i->prev = prev_sv;\n        cur_i->next = next_sv;\n    }\n    /* Some consistency checking: ensures that\n     * both lists had the same length */\n    assert(cur_i == NULL && cur_b == NULL);\n    /* Clean backup list */\n    LIST_CLEAN(backup_head, cur_b);\n    backup_head = NULL;\n    return SUCCESS;\n}\n\nint icon_list_backup_purge()\n{\n    struct TrayIcon *tmp;\n    LOG_TRACE((\"purging the backed up icon list\\n\"));\n    /* Clean backup list */\n    LIST_CLEAN(backup_head, tmp);\n    backup_head = NULL;\n    return SUCCESS;\n}\n\nstruct TrayIcon *icon_list_find(Window wid)\n{\n    /* Traverse the whole list */\n    struct TrayIcon *tmp;\n    for (tmp = icons_head; tmp != NULL; tmp = tmp->next)\n        if (tmp->wid == wid) return tmp;\n    return NULL;\n}\n\nstruct TrayIcon *icon_list_find_ex(Window wid)\n{\n    /* Traverse the whole list */\n    struct TrayIcon *tmp;\n    for (tmp = icons_head; tmp != NULL; tmp = tmp->next)\n        if (tmp->wid == wid || tmp->mid_parent == wid) return tmp;\n    return NULL;\n}\n\nint icon_list_clean()\n{\n    struct TrayIcon *tmp;\n    LIST_CLEAN(icons_head, tmp);\n    return SUCCESS;\n}\n\nint icon_list_clean_callback(IconCallbackFunc cbk)\n{\n    struct TrayIcon *tmp;\n    LIST_CLEAN_CBK(icons_head, tmp, cbk);\n    return SUCCESS;\n}\n\n/* TODO: is it necessary always to sort the full list? */\nvoid icon_list_sort(IconCmpFunc cmp)\n{\n    struct TrayIcon *new_head = NULL, *cur, *tmp;\n    while (icons_head != NULL) {\n        /* Find the least element and move it to temporary list */\n        cur = icons_head;\n        for (tmp = icons_head; tmp != NULL; tmp = tmp->next)\n            if (cmp(tmp, cur) > 0) cur = tmp;\n        LIST_DEL_ITEM(icons_head, cur);\n        LIST_ADD_ITEM(new_head, cur);\n    }\n    icons_head = new_head;\n}\n\nstruct TrayIcon *icon_list_forall(IconCallbackFunc cbk)\n{\n    return icon_list_forall_from(icons_head, cbk);\n}\n\nstruct TrayIcon *icon_list_forall_from(\n    struct TrayIcon *tgt, IconCallbackFunc cbk)\n{\n    /* Traverse the list starting from tgt*/\n    struct TrayIcon *tmp;\n    for (tmp = (tgt != NULL ? tgt : icons_head); tmp != NULL; tmp = tmp->next)\n        if (cbk(tmp) == MATCH) { return tmp; }\n    return NULL;\n}\n"
  },
  {
    "path": "src/icons.h",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * icons.h\n * Tue, 24 Aug 2004 12:05:38 +0700\n * -------------------------------\n * Manipulations with the list of\n * tray icons\n * -------------------------------*/\n\n#ifndef _ICONS_H_\n#define _ICONS_H_\n\n#include <X11/X.h>\n#include <X11/Xmd.h>\n\n/* Simple point & rect data structures */\nstruct Point {\n    int x, y;\n};\nstruct Rect {\n    int x, y, w, h;\n};\n\n/* Tray icon layout data structure */\nstruct Layout {\n    struct Rect grd_rect; /* The rect in the grid */\n    struct Rect icn_rect; /* Real position inside the tray */\n    struct Point wnd_sz; /* Size of the window of the icon */\n};\n\n/* Tray icon data structure */\nstruct TrayIcon {\n    struct TrayIcon *next;\n    struct TrayIcon *prev;\n    Window wid; /* Window ID */\n    Window mid_parent; /* Mid-parent ID */\n    int cmode; /* Compatibility mode: CM_FDO/CM_KDE (see embed.h) */\n    int is_embedded; /* Flag: is the icon succesfully embedded ? */\n    int is_invalid; /* Flag: is the icon invalid ? */\n    int is_visible; /* Flag: is the icon hidden ? */\n    int is_resized; /* Flag: the icon has recently resized itself */\n    int is_layed_out; /* Flag: the icon is succesfully layed out */\n    int is_updated; /* Flag: the position of the icon needs to be updated */\n    int is_xembed_supported; /* Flag: does the icon support xembed */\n    unsigned long xembed_data[2]; /* XEMBED data */\n    int num_size_resets; /* How many times size was reset */\n    int is_size_set; /* Flag: has the size for the icon been set */\n    int is_xembed_accepts_focus; /* Flag: does the icon want focus */\n    long xembed_last_timestamp; /* The timestamp of last processed xembed\n                                   message */\n    long xembed_last_msgid; /* ID of the last processed xembed message */\n    struct Layout l; /* Layout info */\n};\n\n/* Typedef for comparison function */\ntypedef int (*IconCmpFunc)(struct TrayIcon *, struct TrayIcon *);\n\n/* Typedef for callback function */\ntypedef int (*IconCallbackFunc)(struct TrayIcon *);\n\n/* Add the new icon to the list */\nstruct TrayIcon *icon_list_new(Window w, int cmode);\n\n/* Delete the icon from the list */\nint icon_list_free(struct TrayIcon *ti);\n\n/* Return the next/previous icon in the list after the icon specified by ti */\nstruct TrayIcon *icon_list_next(struct TrayIcon *ti);\nstruct TrayIcon *icon_list_prev(struct TrayIcon *ti);\n\n/*************************************************\n * BIG FAT  WARNING: backup/restore routines  will\n * memleak/fail  if  the  number  of icons in  the\n * list has changed between  backup/restore calls.\n * (in return, it does not invalidate pointers :P)\n *************************************************/\n\n/* Back up the list */\nint icon_list_backup();\n\n/* Restore the list from the backup */\nint icon_list_restore();\n\n/* Free the back-up list */\nint icon_list_backup_purge();\n\n/* Apply a callback specified by cbk to all icons.\n * List is traversed in a natural order. Function stops\n * and returns current_icon if cbk(current_icon) == MATCH */\nstruct TrayIcon *icon_list_forall(IconCallbackFunc cbk);\n/* For readability sake, we sometimes use this function */\n#define icon_list_advanced_find icon_list_forall\n\n/* Same as above, but start traversal from the icon specified by tgt */\nstruct TrayIcon *icon_list_forall_from(\n    struct TrayIcon *tgt, IconCallbackFunc cbk);\n\n/* Clear the whole list */\nint icon_list_clean();\n\n/* Clear the whole list, calling cbk for each icon */\nint icon_list_clean_callback(IconCallbackFunc cbk);\n\n/* Sort the list using comparison function specified by cmp.\n * Memo for writing comparison functions:\n * if a < b => cmp(a,b) < 0\n * if a = b => cmp(a,b) = 0\n * if a > b => cmp(a,b) > 0 */\nvoid icon_list_sort(IconCmpFunc cmp);\n\n/* Find the icon with wid == w */\nstruct TrayIcon *icon_list_find(Window w);\n\n/* Find the icon with wid == w or parent wid == w */\nstruct TrayIcon *icon_list_find_ex(Window w);\n\n#endif\n"
  },
  {
    "path": "src/image.c",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * image.c\n * Fri, 22 Jun 2007 23:32:27 +0700\n * -------------------------------\n * Simple XImage manipulation\n * interface\n * -------------------------------*/\n\n#include \"image.h\"\n#include \"debug.h\"\n\n/***** Forward declarations  *****/\n\n/* Depth-specialized versions of tinting routine */\nint image_tint_32(CARD8 *data, size_t len, CARD32 pixel, CARD8 alpha);\nint image_tint_24(CARD8 *data, size_t len, CARD32 pixel, CARD8 alpha);\nint image_tint_16(CARD16 *data, size_t len, CARD32 pixel, CARD8 alpha);\nint image_tint_15(CARD16 *data, size_t len, CARD32 pixel, CARD8 alpha);\n\n/* Depth-specialized versions of compose routine */\nint image_compose_32(CARD8 *data, CARD8 *bg, CARD8 *mask, size_t len);\nint image_compose_24(CARD8 *data, CARD8 *bg, CARD8 *mask, size_t len);\nint image_compose_16(CARD16 *data, CARD16 *bg, CARD8 *mask, size_t len);\nint image_compose_15(CARD16 *data, CARD16 *bg, CARD8 *mask, size_t len);\n\n/***** Interface level *****/\n\nint image_tint(XImage *image, XColor *color, CARD8 alpha)\n{\n    switch (image->bits_per_pixel) {\n    case 32:\n        return image_tint_32((CARD8 *)image->data,\n            image->width * image->height, color->pixel, alpha);\n    case 24:\n        return image_tint_24((CARD8 *)image->data,\n            image->width * image->height, color->pixel, alpha);\n    case 16:\n        return image_tint_16((CARD16 *)image->data,\n            image->width * image->height, color->pixel, alpha);\n    case 15:\n        return image_tint_15((CARD16 *)image->data,\n            image->width * image->height, color->pixel, alpha);\n    default:\n        DIE_IE((\"image_tint() called with unsupported depth %d\\n\",\n            image->bits_per_pixel));\n        return FAILURE;\n    }\n}\n\nint image_compose(XImage *image, XImage *bg, CARD8 *mask)\n{\n    switch (image->bits_per_pixel) {\n    case 32:\n        return image_compose_32((CARD8 *)image->data, (CARD8 *)bg->data, mask,\n            (image->width * image->height));\n    case 24:\n        return image_compose_24((CARD8 *)image->data, (CARD8 *)bg->data, mask,\n            (image->width * image->height));\n    case 16:\n        return image_compose_16((CARD16 *)image->data, (CARD16 *)bg->data,\n            mask, (image->width * image->height));\n    case 15:\n        return image_compose_15((CARD16 *)image->data, (CARD16 *)bg->data,\n            mask, (image->width * image->height));\n    default:\n        DIE_IE((\"image_compose() called with unsupported depth %d\\n\",\n            image->bits_per_pixel));\n        return FAILURE;\n    }\n}\n\nCARD8 *image_create_alpha_mask(int ord, int w, int h)\n{\n    unsigned char *m, *ll, *ul;\n    int x, y, bord;\n\n    bord = (1 << ord) - 1;\n\n    m = malloc(w * h);\n    if (m == NULL) return NULL;\n    memset(m, 255, w * h);\n\n    /* Shade top and bootom of the rectangle */\n    ul = m; /* top */\n    ll = m + (w * (h - 1)); /* bottom */\n    for (y = 0; y < bord; y++) {\n        for (x = 0; x < w; x++) {\n            ul[x] = ((unsigned int)ul[x] * (y + 1)) >> ord;\n            ll[x] = ((unsigned int)ll[x] * (y + 1)) >> ord;\n        }\n        ul += w;\n        ll -= w;\n    }\n\n    /* Shade left and right of the rectangle */\n    for (x = 0; x < bord; x++) {\n        ul = m + x; /* left side */\n        ll = m + w - x - 1; /* right side */\n        for (y = 0; y < h; y++) {\n            *ul = ((unsigned int)*ul * (x + 1)) >> ord;\n            *ll = ((unsigned int)*ll * (x + 1)) >> ord;\n            ll += w;\n            ul += w;\n        }\n    }\n\n    return m;\n}\n\n/***** Implementation level *****/\n\n#define sr16(p, a) (((CARD32)((p)&0xf800) * (a)))\n#define sg16(p, a) (((CARD32)((p)&0x7e0) * (a)))\n#define sb16(p, a) (((CARD32)((p)&0x1f) * (a)))\n\n#define sr15(p, a) (((CARD32)((p)&0x7c00) * (a)))\n#define sg15(p, a) (((CARD32)((p)&0x3e0) * (a)))\n#define sb15(p, a) (((CARD32)((p)&0x1f) * (a)))\n\nint image_tint_32(CARD8 *data, size_t len, CARD32 pixel, CARD8 alpha)\n{\n    CARD8 *p, tr, tg, tb, ralpha;\n\n    ralpha = 255 - alpha;\n\n#ifndef BIGENDIAN\n    tr = ((pixel & 0x00ff0000) * alpha) >> 24;\n    tg = ((pixel & 0x0000ff00) * alpha) >> 16;\n    tb = ((pixel & 0x000000ff) * alpha) >> 8;\n#else\n    tr = ((pixel & 0x000000ff) * alpha) >> 8;\n    tg = ((pixel & 0x0000ff00) * alpha) >> 16;\n    tb = ((pixel & 0x00ff0000) * alpha) >> 24;\n#endif\n\n    /* traverse data by 4 bytes starting from the end */\n    for (p = data + (len - 1) * 4; p >= data; p -= 4) {\n#ifndef BIGENDIAN\n        p[0] = (((CARD16)p[0] * ralpha) >> 8) + tb;\n        p[1] = (((CARD16)p[1] * ralpha) >> 8) + tg;\n        p[2] = (((CARD16)p[2] * ralpha) >> 8) + tr;\n#else\n        p[0] = (((CARD16)p[0] * ralpha) >> 8) + tr;\n        p[1] = (((CARD16)p[1] * ralpha) >> 8) + tg;\n        p[2] = (((CARD16)p[2] * ralpha) >> 8) + tb;\n#endif\n    }\n\n    return SUCCESS;\n}\n\nint image_tint_24(CARD8 *data, size_t len, CARD32 pixel, CARD8 alpha)\n{\n    CARD8 *p, tr, tg, tb, ralpha;\n\n    ralpha = 255 - alpha;\n\n#ifndef BIGENDIAN\n    tr = ((pixel & 0x00ff0000) * alpha) >> 24;\n    tg = ((pixel & 0x0000ff00) * alpha) >> 16;\n    tb = ((pixel & 0x000000ff) * alpha) >> 8;\n#else\n    tr = ((pixel & 0x000000ff) * alpha) >> 8;\n    tg = ((pixel & 0x0000ff00) * alpha) >> 16;\n    tb = ((pixel & 0x00ff0000) * alpha) >> 24;\n#endif\n\n    /* traverse data by 3 bytes starting from the end */\n    for (p = data + (len - 1) * 3; p >= data; p -= 3) {\n#ifndef BIGENDIAN\n        p[0] = (((CARD16)p[0] * ralpha) >> 8) + tb;\n        p[1] = (((CARD16)p[1] * ralpha) >> 8) + tg;\n        p[2] = (((CARD16)p[2] * ralpha) >> 8) + tr;\n#else\n        p[0] = (((CARD16)p[0] * ralpha) >> 8) + tr;\n        p[1] = (((CARD16)p[1] * ralpha) >> 8) + tg;\n        p[2] = (((CARD16)p[2] * ralpha) >> 8) + tb;\n#endif\n    }\n\n    return SUCCESS;\n}\n\nint image_tint_16(CARD16 *data, size_t len, CARD32 pixel, CARD8 alpha)\n{\n    CARD32 tr, tg, tb;\n    CARD32 r, g, b;\n    CARD16 *p;\n    CARD8 ralpha;\n\n    ralpha = 255 - alpha;\n    tr = sr16(pixel, alpha);\n    tg = sg16(pixel, alpha);\n    tb = sb16(pixel, alpha);\n\n    /* traverse data by 2 bytes starting from the end */\n    for (p = data + len - 1; p >= data; p--) {\n        r = sr16(*p, ralpha) + tr;\n        g = sg16(*p, ralpha) + tg;\n        b = sb16(*p, ralpha) + tb;\n        *p = ((r >> 8) & 0xf800) | ((g >> 8) & 0x7e0) | ((b >> 8) & 0x1f);\n    }\n\n    return SUCCESS;\n}\n\nint image_tint_15(CARD16 *data, size_t len, CARD32 pixel, CARD8 alpha)\n{\n    CARD32 tr, tg, tb;\n    CARD32 r, g, b;\n    CARD16 *p;\n    CARD8 ralpha;\n\n    ralpha = 255 - alpha;\n    tr = sr15(pixel, alpha);\n    tg = sg15(pixel, alpha);\n    tb = sb15(pixel, alpha);\n\n    /* traverse data by 2 bytes starting from the end */\n    for (p = data + len - 1; p >= data; p--) {\n        r = sr15(*p, ralpha) + tr;\n        g = sg15(*p, ralpha) + tg;\n        b = sb15(*p, ralpha) + tb;\n        *p = ((r >> 8) & 0x7c00) | ((g >> 8) & 0x3e0) | ((b >> 8) & 0x1f);\n    }\n\n    return SUCCESS;\n}\n\nint image_compose_32(CARD8 *data, CARD8 *bg, CARD8 *mask, size_t len)\n{\n    CARD8 *p, *b, *m;\n    CARD16 a, ra;\n\n    /* traverse data, bg by 4 bytes and mask by 1 byte starting from the end */\n    for (p = data + (len - 1) * 4, b = bg + (len - 1) * 4, m = mask + len - 1;\n         p != data - 4; p -= 4, b -= 4, m--) {\n        a = *m;\n        ra = 255 - a;\n        p[0] = (p[0] * a + b[0] * ra) >> 8;\n        p[1] = (p[1] * a + b[1] * ra) >> 8;\n        p[2] = (p[2] * a + b[2] * ra) >> 8;\n    }\n\n    return SUCCESS;\n}\n\nint image_compose_24(CARD8 *data, CARD8 *bg, CARD8 *mask, size_t len)\n{\n    CARD8 *p, *b, *m;\n    CARD16 a, ra;\n\n    /* traverse data, bg by 3 bytes and mask by 1 byte starting from the end */\n    for (p = data + (len - 1) * 3, b = bg + (len - 1) * 3, m = mask + len - 1;\n         p != data - 3; p -= 3, b -= 3, m--) {\n        a = *m;\n        ra = 255 - a;\n        p[0] = (p[0] * a + b[0] * ra) >> 8;\n        p[1] = (p[1] * a + b[1] * ra) >> 8;\n        p[2] = (p[2] * a + b[2] * ra) >> 8;\n    }\n\n    return SUCCESS;\n}\n\nint image_compose_16(CARD16 *data, CARD16 *bg, CARD8 *mask, size_t len)\n{\n    CARD32 r, g, b;\n    CARD16 *p, *pb;\n    CARD8 a, ra, *m;\n\n    /* traverse data by 2 bytes starting from the end */\n    for (p = data + len - 1, pb = bg + len - 1, m = mask + len - 1; p != data;\n         p--, pb--, m--) {\n        a = *m;\n        ra = 255 - a;\n        r = sr16(*p, a) + sr16(*pb, ra);\n        g = sg16(*p, a) + sg16(*pb, ra);\n        b = sb16(*p, a) + sb16(*pb, ra);\n        *p = ((r >> 8) & 0xf800) | ((g >> 8) & 0x7e0) | ((b >> 8) & 0x1f);\n    }\n\n    return SUCCESS;\n}\n\nint image_compose_15(CARD16 *data, CARD16 *bg, CARD8 *mask, size_t len)\n{\n    CARD32 r, g, b;\n    CARD16 *p, *pb;\n    CARD8 a, ra, *m;\n\n    /* traverse data by 2 bytes starting from the end */\n    for (p = data + len - 1, pb = bg + len - 1, m = mask + len - 1; p != data;\n         p--, pb--, m--) {\n        a = *mask;\n        ra = 255 - a;\n        r = sr15(*p, a) + sr15(*pb, ra);\n        g = sg15(*p, a) + sg15(*pb, ra);\n        b = sb15(*p, a) + sb15(*pb, ra);\n        *p = ((r >> 8) & 0x7c00) | ((g >> 8) & 0x3e0) | ((b >> 8) & 0x1f);\n    }\n\n    return SUCCESS;\n}\n"
  },
  {
    "path": "src/image.h",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * image.h\n * Fri, 22 Jun 2007 23:32:27 +0700\n * -------------------------------\n * Simple XImage manipulation\n * interface\n * -------------------------------*/\n\n#ifndef _IMAGE_H_\n#define _IMAGE_H_\n\n#include <X11/Xlib.h>\n#include <X11/Xmd.h>\n\n/* outstanding TODO (for 0.8): use Xrender when available */\n\n/* WARNING: works with ZPixmaps only */\n\n/* Creates alpha channel mask with specified fade-out order */\nCARD8 *image_create_alpha_mask(int ord, int w, int h);\n\n/* Alpha-tint image using color. */\nint image_tint(XImage *image, XColor *color, CARD8 alpha);\n\n/* Compose image stored in tgt with image stored in bg.\n * Alpha of each pixel is defined by mask, which should */\nint image_compose(XImage *tgt, XImage *bg, CARD8 *mask);\n\n#endif\n"
  },
  {
    "path": "src/kde_tray.c",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * kde_tray.c\n * Sun, 19 Sep 2004 12:31:10 +0700\n * -------------------------------\n * kde tray related routines\n * -------------------------------*/\n\n#include <X11/X.h>\n#include <X11/Xatom.h>\n#include <X11/Xlib.h>\n\n#include \"debug.h\"\n#include \"kde_tray.h\"\n#include \"xutils.h\"\n\n/* This list holds \"old\" KDE icons, e.g. the icons that are (likely) to be\n * already embedded into some system tray and, therefore, are to be ignored.\n * The list is empty initially */\nWindow *old_kde_icons = NULL;\nunsigned long n_old_kde_icons = -1;\n\nint kde_tray_update_fallback_mode(Display *dpy)\n{\n    /* Get the contents of KDE_NET_SYSTEM_TRAY_WINDOWS root window property.\n     * All windows that are listed there are considered to be \"old\" KDE icons,\n     * i.e. icons that are to be ignored on the tray startup.\n     * If the property does not exist, fall back to old mode */\n    if (tray_data.xa_kde_net_system_tray_windows == None\n        || !x11_get_root_winlist_prop(dpy,\n            tray_data.xa_kde_net_system_tray_windows,\n            (unsigned char **)&old_kde_icons, &n_old_kde_icons)) {\n        LOG_INFO((\"WM does not support KDE_NET_SYSTEM_TRAY_WINDOWS, will use \"\n                  \"legacy scheme\\n\"));\n        x11_extend_root_event_mask(tray_data.dpy, SubstructureNotifyMask);\n        tray_data.kde_tray_old_mode = 1;\n    } else {\n        tray_data.kde_tray_old_mode = 0;\n    }\n    return tray_data.kde_tray_old_mode;\n}\n\nvoid kde_tray_init(Display *dpy)\n{\n    static int initialized = False;\n\n    unsigned long n_client_windows, i;\n    Window *client_windows;\n    Atom xa_net_client_list;\n\n    if (!kde_tray_update_fallback_mode(dpy)) return;\n\n    /* do nothing if this function was already called */\n    if (initialized) return;\n\n    /* 1. If theres no previous tray selection owner, try to embed all\n     * available KDE icons and, therefore, leave the list of old KDE icons\n     * empty */\n    if (tray_data.old_selection_owner == None) {\n        n_old_kde_icons = 0;\n        initialized = True;\n        return;\n    }\n    /* 2.Next, we are going to remove some entries from old_kde_icons list */\n    /* 2.a. First, we remove all icons that are listed in _NET_CLIENT_LIST\n     * property, since this means that they are not embedded in any kind of\n     * tray */\n    xa_net_client_list = XInternAtom(dpy, \"_NET_CLIENT_LIST\", True);\n    if (x11_get_root_winlist_prop(dpy, xa_net_client_list,\n            (unsigned char **)&client_windows, &n_client_windows)) {\n        for (i = 0; i < n_client_windows; i++)\n            kde_tray_old_icons_remove(client_windows[i]);\n    }\n    /* 2.b. Second, we remove all windows that have root window as their\n     * parent,\n     * since this also means that they are not embedded in any kind of tray */\n    for (i = 0; i < n_old_kde_icons; i++) {\n        Window root, parent, *children;\n        unsigned int nchildren;\n        int rc;\n        nchildren = 0;\n        children = NULL;\n        if ((rc = XQueryTree(dpy, old_kde_icons[i], &root, &parent, &children,\n                 &nchildren))) {\n            if (root == parent) old_kde_icons[i] = None;\n            if (nchildren > 0) XFree(children);\n        }\n        if (!x11_ok() || !rc) old_kde_icons[i] = None;\n    }\n#ifdef DEBUG\n    /* Some diagnostic output */\n    for (i = 0; i < n_old_kde_icons; i++)\n        if (old_kde_icons[i] != None)\n            LOG_TRACE(\n                (\"0x%lx is marked as an old KDE icon\\n\", old_kde_icons[i]));\n#endif\n\n    initialized = True;\n}\n\nint kde_tray_update_old_icons(Display *dpy)\n{\n    unsigned int i, rc;\n    XWindowAttributes xwa;\n    /* Remove dead entries from old kde icons list.\n     * We use XGetWindowAttributes to see if the\n     * window is still alive */\n    for (i = 0; i < n_old_kde_icons; i++) {\n        rc = XGetWindowAttributes(dpy, old_kde_icons[i], &xwa);\n        if (!x11_ok() || !rc) old_kde_icons[i] = None;\n    }\n    return SUCCESS;\n}\n\nint kde_tray_is_old_icon(Window w)\n{\n    unsigned int i;\n    for (i = 0; i < n_old_kde_icons; i++)\n        if (old_kde_icons[i] == w) return True;\n    return False;\n}\n\nvoid kde_tray_old_icons_remove(Window w)\n{\n    unsigned int i;\n    for (i = 0; i < n_old_kde_icons; i++)\n        if (old_kde_icons[i] == w) {\n            LOG_TRACE((\"0x%lx unmarked as an old kde icon\\n\", w));\n            old_kde_icons[i] = None;\n        }\n}\n\nint kde_tray_check_for_icon(Display *dpy, Window w)\n{\n    Atom actual_type;\n    int actual_format;\n    unsigned long nitems, bytes_after;\n    static Atom xa_kde_net_wm_system_tray_window_for = None;\n    unsigned char *data = NULL;\n    /* Check if the window has a property named _KDE_NET_WM_SYSTEM_TRAY_WINDOW\n     * FOR */\n    if (xa_kde_net_wm_system_tray_window_for == None)\n        xa_kde_net_wm_system_tray_window_for =\n            XInternAtom(dpy, \"_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR\", True);\n    /* If this atom does not exist, we have nothing to check for */\n    if (xa_kde_net_wm_system_tray_window_for == None) return False;\n    /* TODO: use x11_ call */\n    XGetWindowProperty(dpy, w, xa_kde_net_wm_system_tray_window_for, 0L, 1L,\n        False, XA_WINDOW, &actual_type, &actual_format, &nitems, &bytes_after,\n        &data);\n    XFree(data);\n    if (x11_ok() && actual_type == XA_WINDOW && nitems == 1)\n        return SUCCESS;\n    else\n        return FAILURE;\n}\n\nWindow kde_tray_find_icon(Display *dpy, Window w)\n{\n    Window root, parent, *children = NULL;\n    unsigned int nchildren, i;\n    Window r = None;\n    if (kde_tray_check_for_icon(dpy, w)) return w;\n    XQueryTree(dpy, w, &root, &parent, &children, &nchildren);\n    if (!x11_ok()) goto bailout;\n    for (i = 0; i < nchildren; i++)\n        if ((r = kde_tray_find_icon(dpy, children[i])) != None) goto bailout;\nbailout:\n    if (children != NULL && nchildren > 0) XFree(children);\n    return r;\n}\n"
  },
  {
    "path": "src/kde_tray.h",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * kde_tray.h\n * Sun, 19 Sep 2004 12:28:59 +0700\n * -------------------------------\n * KDE tray related routines\n * -------------------------------*/\n\n#ifndef _KDE_TRAY_H_\n#define _KDE_TRAY_H_\n\n/* Init support for KDE tray icons. */\nvoid kde_tray_init(Display *dpy);\n\n/* Check if WM supports KDE tray icons */\nint kde_tray_update_fallback_mode(Display *dpy);\n\n/* Update the list of \"old\" KDE icons. Icon is considered \"old\"\n * if it was present before the tray was started. */\nint kde_tray_update_old_icons(Display *dpy);\n\n/* Check if the window  w is an \"old\" KDE tray icon */\nint kde_tray_is_old_icon(Window w);\n\n/* Remove the window w from the list of \"old\" KDE tray icons */\nvoid kde_tray_old_icons_remove(Window w);\n\n/* Check if the window w is a KDE tray icon */\nint kde_tray_check_for_icon(Display *dpy, Window w);\n\n/* Find KDE tray icon in subwindows of w */\nWindow kde_tray_find_icon(Display *dpy, Window w);\n\n#endif\n"
  },
  {
    "path": "src/layout.c",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * layout.c\n * Tue, 24 Aug 2004 12:19:48 +0700\n * -------------------------------\n * Icon layout implementation.\n * (Used to be) the dirtiest place around.\n * -------------------------------*/\n\n#include <limits.h>\n\n#include <X11/Xlib.h>\n#include <X11/Xutil.h>\n\n#include \"common.h\"\n\n#include \"debug.h\"\n#include \"icons.h\"\n#include \"layout.h\"\n#include \"list.h\"\n#include \"tray.h\"\n\n/* not very nice (and calculates its arguments twice!), but allows doing things\n * like swap(*a, *b) */\n#define swap(a, b) \\\n    do { \\\n        int t; \\\n        t = (a); \\\n        (a) = (b); \\\n        (b) = t; \\\n    } while (0)\n\n/*****************\n * Grid interface *\n *****************/\n\nstruct Point grid_sz = {0, 0};\n\nint grid_add(struct TrayIcon *ti);\nint grid_add_wrapper(struct TrayIcon *ti);\nint grid_remove(struct TrayIcon *ti);\nint grid_update(struct TrayIcon *ti, int sort);\nint grid_translate_from_window(struct TrayIcon *ti);\n\nint layout_translate_to_window(struct TrayIcon *ti);\nint layout_unset_flag(struct TrayIcon *ti);\n\n/************************\n * Layout implementation *\n ************************/\n\nint layout_add(struct TrayIcon *ti)\n{\n    if (grid_add(ti)) {\n        grid_update(ti, True);\n        return SUCCESS;\n    } else\n        return FAILURE;\n}\n\nint layout_remove(struct TrayIcon *ti)\n{\n    return ti->is_layed_out ? grid_remove(ti) : SUCCESS;\n}\n\nint layout_handle_icon_resize(struct TrayIcon *ti)\n{\n    struct Rect old_grd_rect;\n    struct Point old_grid_sz;\n    int rc;\n#ifdef DEBUG\n    if (settings.log_level >= LOG_LEVEL_TRACE) {\n        LOG_TRACE((\"currently managed icons:\\n\"));\n        icon_list_forall(&print_icon_data);\n    }\n#endif\n    if (!icon_list_backup()) return FAILURE;\n    old_grid_sz = grid_sz;\n    if (ti->is_layed_out) {\n        /* if the icon is already layed up and\n         * its grid rect did not change we do nothing,\n         * since its position inside grid cell will be\n         * updated by embedder_update_positions */\n        old_grd_rect = ti->l.grd_rect;\n        grid_translate_from_window(ti);\n        if (ti->l.grd_rect.w == old_grd_rect.w\n            && ti->l.grd_rect.h == old_grd_rect.h) {\n            icon_list_backup_purge();\n            return SUCCESS;\n        }\n    }\n    /* Here's the place where icon sorting start playing its role.\n     * It is easy to see that resizing ti affects only those icons\n     * that are after ti in the icon list. */\n    /* 1. Unset layout flags of all icons after ti */\n    icon_list_forall_from(ti, &layout_unset_flag);\n    /* 2. If shrink mode is on, recalculate the size of the grid\n     *    for the remaining icons. This ensures that final grid\n     *    will be \"minimal\" (I did not prove that :) */\n    if (settings.shrink_back_mode) grid_update(ti, False);\n#ifdef DEBUG\n    if (settings.log_level >= LOG_LEVEL_TRACE) {\n        LOG_TRACE((\"list of icons which are to be added back to the grid\\n\"));\n        icon_list_forall_from(ti, &print_icon_data);\n    }\n#endif\n    /* 3. Start adding icons after ti back to the grid one\n     *    by one. If this fails for some icon, forall_icons_from()\n     *    will return non-NULL value and this will be considered\n     *    as a error condition */\n    if (icon_list_forall_from(ti, &grid_add_wrapper) == NULL) {\n        /* Everything is OK */\n        icon_list_backup_purge();\n        grid_update(ti, True);\n        rc = SUCCESS;\n    } else {\n        /* Error has occured */\n        icon_list_restore();\n        grid_sz = old_grid_sz;\n        rc = FAILURE;\n    }\n#ifdef DEBUG\n    if (settings.log_level >= LOG_LEVEL_TRACE) {\n        LOG_TRACE((\"currently managed icons:\\n\"));\n        icon_list_forall(&print_icon_data);\n    }\n#endif\n    return rc;\n}\n\nvoid layout_get_size(int *width, int *height)\n{\n    *width = grid_sz.x * settings.slot_size.x;\n    *height = grid_sz.y * settings.slot_size.y;\n    if (settings.vertical) swap((*width), (*height));\n}\n\nstruct TrayIcon *layout_next(struct TrayIcon *current)\n{\n    if ((settings.icon_gravity & GRAV_H) == GRAV_W)\n        return icon_list_next(current);\n    else\n        return icon_list_prev(current);\n}\n\nstruct TrayIcon *layout_prev(struct TrayIcon *current)\n{\n    if ((settings.icon_gravity & GRAV_H) == GRAV_W)\n        return icon_list_prev(current);\n    else\n        return icon_list_next(current);\n}\n\n/**********************\n * Grid implementation *\n **********************/\n\n/* Structure to hold possible placement for an icon */\nstruct IconPlacement {\n    struct Point pos; /* Position */\n    struct Point sz_delta; /* Layout size delta */\n    int valid; /* Is the placement valid */\n};\n\n/* Find best placement for an icon */\nstruct IconPlacement *grid_find_placement(struct TrayIcon *ti);\n/* Place tray icon as specified by ip */\nint grid_place_icon(struct TrayIcon *ti, struct IconPlacement *ip);\n/* Comparison function for icon_list_sort */\nint trayicon_cmp_func(struct TrayIcon *ti1, struct TrayIcon *ti2);\n/* Update grid dimentions */\nint grid_recalc_size(struct TrayIcon *ti);\n\n/* A. Coords translations */\nint grid_translate_from_window(struct TrayIcon *ti)\n{\n    ti->l.grd_rect.w = ti->l.wnd_sz.x / settings.slot_size.x\n        + (ti->l.wnd_sz.x % settings.slot_size.x != 0);\n    ti->l.grd_rect.h = ti->l.wnd_sz.y / settings.slot_size.y\n        + (ti->l.wnd_sz.y % settings.slot_size.y != 0);\n    if (settings.vertical) swap(ti->l.grd_rect.w, ti->l.grd_rect.h);\n    return NO_MATCH;\n}\n\nint layout_translate_to_window(struct TrayIcon *ti)\n{\n    struct Rect old_icn_rect = ti->l.icn_rect;\n    if (!ti->is_layed_out) return NO_MATCH;\n    /* Swap vert & horz dimentions temporarily */\n    if (settings.vertical) {\n        swap(ti->l.grd_rect.w, ti->l.grd_rect.h);\n        swap(ti->l.grd_rect.x, ti->l.grd_rect.y);\n    }\n    /* Compute icon position and dimensions */\n    if (settings.icon_gravity & GRAV_W)\n        ti->l.icn_rect.x = ti->l.grd_rect.x * settings.slot_size.x;\n    else\n        ti->l.icn_rect.x = tray_data.xsh.width\n            - (ti->l.grd_rect.x + ti->l.grd_rect.w) * settings.slot_size.x;\n    if (settings.icon_gravity & GRAV_N)\n        ti->l.icn_rect.y = ti->l.grd_rect.y * settings.slot_size.y;\n    else\n        ti->l.icn_rect.y = tray_data.xsh.height\n            - (ti->l.grd_rect.y + ti->l.grd_rect.h) * settings.slot_size.y;\n    ti->l.icn_rect.w = ti->l.grd_rect.w * settings.slot_size.y;\n    ti->l.icn_rect.h = ti->l.grd_rect.h * settings.slot_size.y;\n    /* Swap vert & horz dimentions back */\n    if (settings.vertical) {\n        swap(ti->l.grd_rect.w, ti->l.grd_rect.h);\n        swap(ti->l.grd_rect.x, ti->l.grd_rect.y);\n    }\n    /* Shift icon position according to scrollbars positions */\n    /* TODO: must be in separate function, with a reverse */\n    ti->l.icn_rect.x += ((settings.icon_gravity & GRAV_W ? 1 : -1)\n        * (tray_data.scrollbars_data.scroll_base.x\n            - tray_data.scrollbars_data.scroll_pos.x));\n    ti->l.icn_rect.y += ((settings.icon_gravity & GRAV_N ? 1 : -1)\n        * (tray_data.scrollbars_data.scroll_base.y\n            - tray_data.scrollbars_data.scroll_pos.y));\n    LOG_TRACE((\"grid %dx%d+%d+%d -> window  %dx%d+%d+%d\\n\", ti->l.grd_rect.w,\n        ti->l.grd_rect.h, ti->l.grd_rect.x, ti->l.grd_rect.y, ti->l.icn_rect.w,\n        ti->l.icn_rect.h, ti->l.icn_rect.x, ti->l.icn_rect.y));\n    /* Set flag indicating if new position is different from old */\n    ti->is_updated = ti->is_updated\n        || !(ti->l.icn_rect.x == old_icn_rect.x\n            && ti->l.icn_rect.y == old_icn_rect.y\n            && ti->l.icn_rect.w == old_icn_rect.w\n            && ti->l.icn_rect.h == old_icn_rect.h);\n    return NO_MATCH;\n}\n\n/* B. Adding/removing icon from the grid */\nint grid_add(struct TrayIcon *ti)\n{\n    struct IconPlacement *pmt;\n    /* Invalidate grid position data */\n    ti->l.grd_rect.x = -1;\n    ti->l.grd_rect.y = -1;\n    ti->l.grd_rect.w = -1;\n    ti->l.grd_rect.h = -1;\n    /* Invalidate window position data */\n    ti->l.icn_rect.x = -1;\n    ti->l.icn_rect.y = -1;\n    ti->l.icn_rect.w = -1;\n    ti->l.icn_rect.h = -1;\n    /* Get window dimensions */\n    grid_translate_from_window(ti);\n    pmt = grid_find_placement(ti);\n    if (pmt == NULL) {\n        LOG_TRACE((\"no candidates for placement found\\n\"));\n        ti->is_layed_out = False;\n        RETURN_STATUS(FAILURE);\n    }\n    ti->is_layed_out = True;\n    grid_place_icon(ti, pmt);\n    RETURN_STATUS(SUCCESS);\n}\n\nint grid_remove(struct TrayIcon *ti)\n{\n    /* implementation is similar to that of layout_handle_icon_resize(),\n     * see detailed description there */\n    icon_list_forall_from(ti, &layout_unset_flag);\n    if (settings.shrink_back_mode || settings.scrollbars_mode != SB_MODE_NONE)\n        grid_update(ti, False);\n    /* Since NULL is a special case for icon_list_froall_from,\n     * avoid calling it for the last icon */\n    if (ti->next != NULL)\n        RETURN_STATUS(\n            icon_list_forall_from(ti->next, &grid_add_wrapper) == NULL);\n    else\n        RETURN_STATUS(SUCCESS);\n}\n\n/* C. Grid manipulations */\nint grid_update(struct TrayIcon *ti, int sort)\n{\n    icon_list_forall_from(ti, &layout_translate_to_window);\n    if (sort) icon_list_sort(&trayicon_cmp_func);\n    /* recalculate minimal size */\n    grid_sz.x = 0;\n    grid_sz.y = 0;\n    icon_list_forall(&grid_recalc_size);\n    LOG_TRACE((\"final grid size: %dx%d\\n\", grid_sz.x, grid_sz.y));\n    return SUCCESS;\n}\n\n/* D. Placement functions */\nint grid_place_icon(struct TrayIcon *ti, struct IconPlacement *ip)\n{\n    struct Layout *l = &ti->l;\n    struct Point *p = &ip->pos;\n    /* Set the flag if icon position was really updated */\n    ti->is_updated =\n        ti->is_updated || (p->x != l->grd_rect.x || p->y != l->grd_rect.y);\n    if (ti->is_updated || !ti->is_layed_out) {\n        LOG_TRACE((\"updating position (%d,%d) to (%d,%d)\\n\", ti->l.grd_rect.x,\n            ti->l.grd_rect.y, p->x, p->y));\n        l->grd_rect.x = p->x;\n        l->grd_rect.y = p->y;\n    }\n    /* Update grid dimensions */\n    grid_sz.x += ip->sz_delta.x;\n    grid_sz.y += ip->sz_delta.y;\n    /* Update icon position */\n    layout_translate_to_window(ti);\n    LOG_TRACE((\"current grid size: %dx%d\\n\", grid_sz.x, grid_sz.y));\n    return ti->is_updated;\n}\n\n/* Oh, the wonders of languages without closures... */\nstatic struct Rect chk_rect;\n\n/* Check if grid rect of ti intersects with chk_rect */\nint find_obstacle(struct TrayIcon *ti)\n{\n    return ti->is_layed_out\n        && (RECTS_ISECT(chk_rect, ti->l.grd_rect)\n            || RECTS_ISECT(ti->l.grd_rect, chk_rect));\n}\n\n/* Check if grid rect of ti intersects with any other tray icon\n * grid rect and return its width or height, depending on tray orientation */\nint grid_check_rect_free(int x, int y, int w, int h)\n{\n    struct TrayIcon *obst;\n    chk_rect.x = x;\n    chk_rect.y = y;\n    chk_rect.w = w;\n    chk_rect.h = h;\n    obst = icon_list_advanced_find(&find_obstacle);\n    if (obst == NULL) {\n        return 0;\n    } else {\n        if (settings.vertical)\n            return obst->l.grd_rect.h;\n        else\n            return obst->l.grd_rect.w;\n    }\n}\n\n/* Simple function to fill in fields of IconPlacement struct\n * \tx, y: position of the icon\n * \trect: provides the size of the icon\n * \treturn value: returns true if the placement is valid; returns false\n * otherwise.\n */\nint icon_placement_create(\n    struct IconPlacement *ip, int x, int y, struct Rect *rect)\n{\n/* scrollbar & tray orientation-aware shortcuts for maximal layout dimensions.\n * if scrollbar is enabled in some direction, layout size in this dimension is\n * not limited */\n#define max_layout_width \\\n    (settings.scrollbars_mode & SB_MODE_HORZ \\\n            ? INT_MAX \\\n            : ((settings.vertical ? settings.max_tray_dims.y \\\n                                  : settings.max_tray_dims.x) \\\n                / settings.slot_size.x))\n#define max_layout_height \\\n    (settings.scrollbars_mode & SB_MODE_VERT \\\n            ? INT_MAX \\\n            : ((settings.vertical ? settings.max_tray_dims.x \\\n                                  : settings.max_tray_dims.y) \\\n                / settings.slot_size.y))\n    ip->valid = 0;\n    ip->pos.x = x;\n    ip->pos.y = y;\n    ip->sz_delta.x = x + rect->w - grid_sz.x;\n    ip->sz_delta.y = y + rect->h - grid_sz.y;\n    ip->sz_delta.x = ip->sz_delta.x > 0 ? ip->sz_delta.x : 0;\n    ip->sz_delta.y = ip->sz_delta.y > 0 ? ip->sz_delta.y : 0;\n    ip->valid = (ip->pos.x + rect->w <= max_layout_width)\n        && (ip->pos.y + rect->h <= max_layout_height);\n#ifdef DEBUG\n    LOG_TRACE((\"placement (%d, %d, %d, %d) is %s\\n\", x, y, ip->sz_delta.x,\n        ip->sz_delta.y, ip->valid ? \"valid\" : \"invalid\"));\n#endif\n    return ip->valid;\n}\n\n/* Compare points in lexographical order */\n#define compare_points(a, b) \\\n    (((a).y < (b).y) || ((a).y == (b).y && (a).x < (b).x))\n\n/* Choose best placement --- placement policy is defined here\n * Placement A is considered to be better than placement B\n * if either\n * \t- min_space_policy is on and (window) size delta for A is\n * \t  strictly less then size delta for B;\n * \t- position for A is less (in lexographical order)\n * \t  than position for B.\n * \t  */\nvoid icon_placement_choose_best(\n    struct IconPlacement *old, struct IconPlacement *new)\n{\n    int old_lout_delta_norm, new_lout_delta_norm;\n    int lout_norm_cmp;\n    struct Point new_wnd_sz_delta, old_wnd_sz_delta;\n    int old_wnd_delta_norm = 0, new_wnd_delta_norm = 0;\n    int wnd_norm_cmp = 0;\n/* I whish there were nested functions in C standard */\n#define calc_wnd_sz_delta(delta, pmt) \\\n    do { \\\n        delta.x = cutoff((grid_sz.x + pmt->sz_delta.x) * settings.slot_size.x \\\n                - tray_data.xsh.width, \\\n            0, pmt->sz_delta.x * settings.slot_size.x); \\\n        delta.y = cutoff((grid_sz.y + pmt->sz_delta.y) * settings.slot_size.y \\\n                - tray_data.xsh.height, \\\n            0, pmt->sz_delta.y * settings.slot_size.y); \\\n    } while (0)\n    /* If tray is vertically oriented, swap width <-> height\n     * (just for readability sake) */\n    if (settings.vertical) {\n        swap(tray_data.xsh.width, tray_data.xsh.height);\n        swap(settings.orig_tray_dims.x, settings.orig_tray_dims.y);\n    }\n    calc_wnd_sz_delta(old_wnd_sz_delta, old);\n    calc_wnd_sz_delta(new_wnd_sz_delta, new);\n    /* Some black magic. This is probably buggy and you are not supposed to\n     * understand this (I definetly don't). The basic idea is that sometimes\n     * layout resize means window resize. Sometimes it does not. */\n    if (settings.shrink_back_mode) {\n        if (grid_sz.x >= settings.orig_tray_dims.x / settings.slot_size.x) {\n            old_wnd_sz_delta.x = old->sz_delta.x * settings.slot_size.x;\n            new_wnd_sz_delta.x = new->sz_delta.x *settings.slot_size.x;\n        }\n        if (grid_sz.y >= settings.orig_tray_dims.y / settings.slot_size.y) {\n            old_wnd_sz_delta.y = old->sz_delta.y * settings.slot_size.y;\n            new_wnd_sz_delta.y = new->sz_delta.y *settings.slot_size.y;\n        }\n    }\n    /* Restore values */\n    if (settings.vertical) {\n        swap(tray_data.xsh.width, tray_data.xsh.height);\n        swap(settings.orig_tray_dims.x, settings.orig_tray_dims.y);\n    }\n    LOG_TRACE((\"old placement = (%d, %d, %d, %d, %d, %d)\\n\", old->pos.x,\n        old->pos.y, old->sz_delta.x, old->sz_delta.y, old_wnd_sz_delta.x,\n        old_wnd_sz_delta.y));\n    LOG_TRACE((\"new placement = (%d, %d, %d, %d, %d, %d)\\n\", new->pos.x,\n        new->pos.y, new->sz_delta.x, new->sz_delta.y, new_wnd_sz_delta.x,\n        new_wnd_sz_delta.y));\n    /* Compute norms for window deltas */\n    old_wnd_delta_norm = old_wnd_sz_delta.x + old_wnd_sz_delta.y;\n    new_wnd_delta_norm = new_wnd_sz_delta.x + new_wnd_sz_delta.y;\n    wnd_norm_cmp = new_wnd_delta_norm < old_wnd_delta_norm;\n    /* Compute norms for layout deltas */\n    old_lout_delta_norm = old->sz_delta.x + old->sz_delta.y;\n    new_lout_delta_norm = new->sz_delta.x + new->sz_delta.y;\n    lout_norm_cmp = new_lout_delta_norm < old_lout_delta_norm;\n    LOG_TRACE((\"old wnd delta norm = %d, lout delta norm = %d\\n\",\n        old_wnd_delta_norm, old_lout_delta_norm));\n    LOG_TRACE((\"new wnd delta norm = %d, lout delta norm = %d\\n\",\n        new_wnd_delta_norm, new_lout_delta_norm));\n    LOG_TRACE((\"lout norm cmp = %d, wnd norm cmp = %d\\n\", lout_norm_cmp,\n        wnd_norm_cmp));\n    LOG_TRACE((\"compare points (new, old) = %d\\n\",\n        compare_points(new->pos, old->pos)));\n    /* CORE of placement logic. In short,\n     * - valid placement is always better than invalid\n     * - with minimal space policy on (off by default), placement with smaller\n     * deltas is better\n     * - placement which does not cause tray window to grow is always better\n     * than one that does\n     * - otherwise, placement that is \"closer\" to initial point (that depends\n     * on gravity) is better */\n    if (!old->valid && new->valid) goto replace;\n    if (settings.min_space_policy && (lout_norm_cmp || wnd_norm_cmp))\n        goto replace;\n    if (old_lout_delta_norm && !new_lout_delta_norm) goto replace;\n    if (old_wnd_delta_norm && !new_wnd_delta_norm) goto replace;\n    if ((!old_lout_delta_norm == !new_lout_delta_norm)\n        && compare_points(new->pos, old->pos))\n        goto replace;\n    LOG_TRACE((\"old placement is better\\n\"));\n    return;\nreplace:\n    LOG_TRACE((\"new placement is better\\n\"));\n    *old = *new;\n}\n\n/* WARNING: returns ptr to STATIC buffer */\nstruct IconPlacement *grid_find_placement(struct TrayIcon *ti)\n{\n    static struct IconPlacement cur_pmt;\n    struct IconPlacement tmp_pmt;\n    int x = 0, y = 0, skip, orig_layed_out;\n    orig_layed_out = ti->is_layed_out;\n    ti->is_layed_out = 0; /* to avoid self-intersections */\n    /* General idea is to look through all possible placements\n     * while keeping one which is considered to be the \"best\" and\n     * comparing current placement with \"best\" one and updating\n     * it accordingly */\n    if (grid_sz.x == 0 || grid_sz.y == 0) { /* Grid is empty */\n        /* This is the only possible placement */\n        icon_placement_create(&cur_pmt, 0, 0, &ti->l.grd_rect);\n    } else { /* Seek and place */\n        /* Create two obvious placements */\n        /* Rightmost-top position */\n        icon_placement_create(&cur_pmt, grid_sz.x, 0, &ti->l.grd_rect);\n        /* Bottommost-left position */\n        icon_placement_create(&tmp_pmt, 0, grid_sz.y, &ti->l.grd_rect);\n        /* Choose the best one */\n        icon_placement_choose_best(&cur_pmt, &tmp_pmt);\n        /* Start looking through possible placements.\n         * Current placement is determined by x and y variables. */\n        do {\n            /* Check if the current rect is free */\n            skip =\n                grid_check_rect_free(x, y, ti->l.grd_rect.w, ti->l.grd_rect.h);\n            LOG_TRACE((\"x=%d, y=%d, skip=%d\\n\", x, y, skip));\n            /* If so, create new placement */\n            if (skip == 0) {\n                if (icon_placement_create(&tmp_pmt, x, y, &ti->l.grd_rect)) {\n                    icon_placement_choose_best(&cur_pmt, &tmp_pmt);\n                    /* If not settings.full_pmt_search, take the first valid\n                     * non-resizing placement and run */\n                    if (!settings.full_pmt_search && !cur_pmt.sz_delta.x\n                        && !cur_pmt.sz_delta.y)\n                        break;\n                }\n                skip = ti->l.grd_rect.w;\n            }\n            /* Advance to the next possible placement */\n            x += skip;\n            if (x >= grid_sz.x) {\n                x = 0;\n                y++;\n            }\n        } while (x < grid_sz.x && y < grid_sz.y);\n    }\n    /* Restore layed out state */\n    ti->is_layed_out = orig_layed_out;\n    if (cur_pmt.valid) {\n        LOG_TRACE((\"using placement (%d, %d, %d, %d)\\n\", cur_pmt.pos.x,\n            cur_pmt.pos.y, cur_pmt.sz_delta.x, cur_pmt.sz_delta.y));\n        return &cur_pmt;\n    } else {\n        return NULL;\n    }\n}\n\n/* E. Mass-ops helpers */\n\nint trayicon_cmp_func(struct TrayIcon *ti1, struct TrayIcon *ti2)\n{\n    return (!ti2->is_layed_out && ti1->is_layed_out)\n        || compare_points(ti2->l.grd_rect, ti1->l.grd_rect);\n}\n\nint grid_add_wrapper(struct TrayIcon *ti)\n{\n    /* Ignore hidden icons */\n    return !ti->is_visible || grid_add(ti) ? NO_MATCH : MATCH;\n}\n\nint grid_recalc_size(struct TrayIcon *ti)\n{\n    int x, y;\n    /* Ignore icons that are not layed out */\n    if (!ti->is_layed_out) return NO_MATCH;\n    /* Calculate coordinates for bottom-right corner of ti */\n    x = ti->l.grd_rect.x + ti->l.grd_rect.w;\n    y = ti->l.grd_rect.y + ti->l.grd_rect.h;\n    /* Update grid dimensions */\n    grid_sz.x = x > grid_sz.x ? x : grid_sz.x;\n    grid_sz.y = y > grid_sz.y ? y : grid_sz.y;\n    LOG_TRACE((\"new grid size: %dx%d\\n\", grid_sz.x, grid_sz.y));\n    return NO_MATCH;\n}\n\nint layout_unset_flag(struct TrayIcon *ti)\n{\n    ti->is_layed_out = 0;\n    return NO_MATCH;\n}\n"
  },
  {
    "path": "src/layout.h",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * layout.h\n * Tue, 24 Aug 2004 12:19:27 +0700\n * -------------------------------\n * Icon layout implementation\n * -------------------------------*/\n\n#ifndef _LAYOUT_H_\n#define _LAYOUT_H_\n\n#include \"icons.h\"\n\n/* Gravity constants */\n/* East gravity */\n#define GRAV_E (1L << 0)\n/* West gravity */\n#define GRAV_W (1L << 1)\n/* South gravity */\n#define GRAV_S (1L << 2)\n/* North gravity */\n#define GRAV_N (1L << 3)\n/* Shortcut for horisontal gravity */\n#define GRAV_H (GRAV_W | GRAV_E)\n/* Shortcut for vertical gravity */\n#define GRAV_V (GRAV_S | GRAV_N)\n\n/* Macros to test rect intersection */\n/* Helpers */\n#define RX1(r) (r.x)\n#define RY1(r) (r.y)\n#define RX2(r) (r.x + r.w - 1)\n#define RY2(r) (r.y + r.h - 1)\n#define RECTS_ISECT_(r1, r2) \\\n    (((RX1(r1) <= RX1(r2) && RX1(r2) <= RX2(r1)) \\\n         || (RX1(r1) <= RX2(r2) && RX2(r2) <= RX2(r1))) \\\n        && ((RY1(r1) <= RY1(r2) && RY1(r2) <= RY2(r1)) \\\n            || (RY1(r1) <= RY2(r2) && RY2(r2) <= RY2(r1))))\n/* Macro itself */\n#define RECTS_ISECT(r1, r2) (RECTS_ISECT_(r1, r2) || RECTS_ISECT_(r2, r1))\n\n/* Add icon to layout */\nint layout_add(struct TrayIcon *ti);\n\n/* Remove icon from layout */\nint layout_remove(struct TrayIcon *ti);\n\n/* Relayout the icon which has changed its size */\nint layout_handle_icon_resize(struct TrayIcon *ti);\n\n/* Get current layout dimensions */\nvoid layout_get_size(int *width, int *height);\n\n/* Translate grid coordinates into window coordinates */\nint layout_translate_to_window(struct TrayIcon *ti);\n\n/* Return next icon in tab chain */\nstruct TrayIcon *layout_next(struct TrayIcon *current);\n\n/* Return previous icon in tab chain */\nstruct TrayIcon *layout_prev(struct TrayIcon *current);\n\n#endif\n"
  },
  {
    "path": "src/list.h",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * list.h\n * Fri, 10 Sep 2004 23:34:48 +0700\n * -------------------------------\n * Simple double linked list\n * -------------------------------*/\n\n#ifndef _LIST_H_\n#define _LIST_H_\n\n/* Implement basic functions for doubly-linked list.\n * Each structure to hold an element of the list must have two\n * fields pointer fields: prev and next. */\n\n/* Add item i_ to the head of the list h_ (pointer to the head) */\n#define LIST_ADD_ITEM(h_, i_) \\\n    do { \\\n        (i_)->prev = NULL; \\\n        if ((h_) != NULL) { \\\n            (i_)->next = (h_); \\\n            (h_)->prev = (i_); \\\n        } else { \\\n            (i_)->next = NULL; \\\n        } \\\n        (h_) = (i_); \\\n    } while (0)\n\n/* Insert item i_ after item t_ to the list h_ (pointer to the head) */\n#define LIST_INSERT_AFTER(h_, t_, i_) \\\n    do { \\\n        (i_)->prev = (t_); \\\n        if ((t_) != NULL) { \\\n            (i_)->next = (t_)->next; \\\n            (t_)->next = (i_); \\\n        } else { \\\n            if ((h_) != NULL) { \\\n                (i_)->next = (h_); \\\n                (h_)->prev = (i_); \\\n            } else { \\\n                (i_)->next = NULL; \\\n            } \\\n            (h_) = (i_); \\\n        } \\\n    } while (0)\n\n/* Delete item i_ from the list h_ (pointer to the head) */\n#define LIST_DEL_ITEM(h_, i_) \\\n    do { \\\n        if (i_->prev != NULL) i_->prev->next = i_->next; \\\n        if (i_->next != NULL) i_->next->prev = i_->prev; \\\n        if (i_ == h_) h_ = i_->next; \\\n    } while (0)\n\n/* Clean the list h_ (pointer to the head);\n * tmp_ is a temporary variable */\n#define LIST_CLEAN(h_, tmp_) \\\n    do { \\\n        for (tmp_ = h_, h_ = (h_ != NULL) ? h_->next : NULL; tmp_ != NULL; \\\n             tmp_ = h_, h_ = h_ != NULL ? h_->next : NULL) { \\\n            free(tmp_); \\\n        } \\\n        h_ = NULL; \\\n    } while (0)\n\n/* Clean the list h_ (pointer to the head) calling cbk_ for every\n * element of the list; tmp_ is a temporary variable */\n#define LIST_CLEAN_CBK(h_, tmp_, cbk_) \\\n    do { \\\n        for (tmp_ = h_, h_ = (h_ != NULL) ? h_->next : NULL; tmp_ != NULL; \\\n             tmp_ = h_, h_ = h_ != NULL ? h_->next : NULL) { \\\n            cbk_(tmp_); \\\n            free(tmp_); \\\n        } \\\n        h_ = NULL; \\\n    } while (0)\n\n#endif\n"
  },
  {
    "path": "src/main.c",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * main.c\n * Tue, 24 Aug 2004 12:19:48 +0700\n * -------------------------------\n * main is main\n * -------------------------------*/\n\n#include <X11/X.h>\n#include <X11/Xatom.h>\n#include <X11/Xlib.h>\n#include <X11/Xmd.h>\n#include <X11/Xutil.h>\n\n#include <signal.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/select.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include \"common.h\"\n#include \"debug.h\"\n#include \"embed.h\"\n#include \"icons.h\"\n#include \"layout.h\"\n#include \"wmh.h\"\n#include \"xembed.h\"\n#include \"xutils.h\"\n\n#ifdef _ST_WITH_NATIVE_KDE\n#include \"kde_tray.h\"\n#endif\n\n#include \"scrollbars.h\"\n#include \"settings.h\"\n#include \"tray.h\"\n#include \"xinerama.h\"\n\nstruct TrayData tray_data;\nstatic int tray_status_requested = 0;\n#ifdef _ST_EXIT_GRACEFULLY\nstatic Display *async_dpy;\n#endif\n\nvoid my_usleep(useconds_t usec)\n{\n    struct timeval timeout;\n    fd_set rfds;\n    FD_ZERO(&rfds);\n    timeout.tv_sec = 0;\n    timeout.tv_usec = usec;\n    select(1, &rfds, NULL, NULL, &timeout);\n}\n\n/****************************\n * Signal handlers, cleanup\n ***************************/\nvoid request_tray_status_on_signal(int)\n{\n    tray_status_requested = 1;\n}\n\n#ifdef _ST_EXIT_GRACEFULLY\nvoid exit_on_signal(int sig)\n{\n    if (sig == SIGPIPE) {\n        debug_disable_output();\n    } else {\n        psignal(sig, \"\");\n        /* This is UGLY and is, probably, to be submitted to\n         * Daily WTF, but it is the only way I found not to\n         * use usleep in main event loop. */\n        LOG_TRACE((\"Sending fake WM_DELETE_WINDOW message\\n\"));\n    }\n    x11_send_client_msg32(async_dpy, tray_data.tray, tray_data.tray,\n        tray_data.xa_wm_protocols, tray_data.xa_wm_delete_window, 0, 0, 0, 0);\n    XSync(async_dpy, False);\n}\n#endif\n\nvoid cleanup()\n{\n    static int clean = 0;\n    static int cleanup_in_progress = 0;\n    if (!clean && cleanup_in_progress) {\n        LOG_ERROR((\"forced to die\\n\"));\n        abort();\n    }\n    if (clean) return;\n    cleanup_in_progress = 1;\n    if (tray_data.dpy != NULL && x11_connection_status()) {\n        LOG_TRACE((\"being nice to the icons\\n\"));\n        /* Clean the list unembedding icons one by one */\n        icon_list_clean_callback(&embedder_unembed);\n        /* Give away the selection */\n        if (tray_data.is_active)\n            XSetSelectionOwner(\n                tray_data.dpy, tray_data.xa_tray_selection, None, CurrentTime);\n        /* Sync in order to wait until all icons finish their reparenting\n         * process */\n        XSync(tray_data.dpy, False);\n        XCloseDisplay(tray_data.dpy);\n        tray_data.dpy = NULL;\n    }\n    cleanup_in_progress = 0;\n    clean = 1;\n}\n\n/**************************************\n * Helper functions\n **************************************/\n\n/* Print tray status */\nvoid dump_tray_status()\n{\n    int grid_w, grid_h;\n    tray_status_requested = 0;\n    layout_get_size(&grid_w, &grid_h);\n    LOG_INFO((\"----------- tray status -----------\\n\"));\n    LOG_INFO((\"active: %s\\n\", tray_data.is_active ? \"yes\" : \"no\"));\n    LOG_INFO((\"geometry: %dx%d+%d+%d\\n\", tray_data.xsh.width,\n        tray_data.xsh.height, tray_data.xsh.x, tray_data.xsh.y));\n    if (tray_data.xembed_data.current)\n        LOG_INFO((\"XEMBED focus: 0x%lx\\n\", tray_data.xembed_data.current->wid));\n    else\n        LOG_INFO((\"XEMBED focus: none\\n\"));\n    LOG_INFO((\"currently managed icons:\\n\"));\n    icon_list_forall(&print_icon_data);\n    LOG_INFO((\"-----------------------------------\\n\"));\n}\n\n/* Checks whether a given window class should be ignored */\nint is_ignored_class(const char *name) {\n    struct WindowClass *haystack = NULL;\n\n    for (haystack = settings.ignored_classes; haystack; haystack = haystack->next) {\n        if (!strcmp(name, haystack->name))\n            return 1;\n    }\n\n    return 0;\n}\n\n/**************************************\n * (Un)embedding cycle implementation\n **************************************/\n\n/* Add icon to the tray */\nvoid add_icon(Window w, int cmode)\n{\n    struct TrayIcon *ti;\n    const char *classname = NULL;\n    /* Aviod adding duplicates */\n    if ((ti = icon_list_find(w)) != NULL) {\n        LOG_TRACE((\"ignoring second request to embed 0x%lx\"\n                   \" (requested cmode=%d, current cmode=%d)\\n\",\n            w, cmode, ti->cmode));\n        return;\n    }\n    /* Dear Edsger W. Dijkstra, I see you behind my back =( */\n    if ((ti = icon_list_new(w, cmode)) == NULL) goto icon_allocation_failed;\n    LOG_TRACE((\"starting embedding for icon 0x%lx, cmode=%d\\n\", w, cmode));\n    x11_dump_win_info(tray_data.dpy, w);\n\n    classname = x11_get_window_class(tray_data.dpy, w);\n    if (classname == NULL) {\n        LOG_TRACE((\"Ignoring icon, x11_get_window_class() failed: 0x%lx\"\n                   \" (requested cmode=%d)\\n\",\n            w, cmode));\n        goto embedding_failed;\n    }\n\n    if (is_ignored_class(classname)) {\n        LOG_INFO((\"Ignoring icon because its class is ignored: %s\\n\", classname));\n        goto done;\n    }\n\n    /* Start embedding cycle */\n    if (!xembed_check_support(ti)) goto embedding_failed;\n    if (ti->is_xembed_supported)\n        ti->is_visible = xembed_get_mapped_state(ti);\n    else\n        ti->is_visible = True;\n    if (ti->is_visible) {\n        if (!embedder_reset_size(ti)) goto embedding_failed;\n        if (!layout_add(ti)) goto embedding_failed;\n    }\n    if (!xembed_embed(ti)) goto embedding_failed_after_layout;\n    if (!embedder_embed(ti)) goto embedding_failed_after_layout;\n    embedder_update_positions(False);\n    tray_update_window_props();\n    /* Report success */\n    LOG_INFO((\"added icon %s (wid 0x%lx) as %s\\n\",\n        x11_get_window_name(tray_data.dpy, ti->wid, \"<unknown>\"), ti->wid,\n        ti->is_visible ? \"visible\" : \"hidden\"));\n    goto done;\n\nembedding_failed_after_layout:\n    layout_remove(ti);\n\nembedding_failed:\n    icon_list_free(ti);\n\nicon_allocation_failed:\n    LOG_INFO((\"failed to add icon %s (wid 0x%lx)\\n\",\n        x11_get_window_name(tray_data.dpy, ti->wid, \"<unknown>\"), ti->wid));\n\ndone:\n    if (classname != NULL)\n        free((void *) classname);\n\n    if (settings.log_level >= LOG_LEVEL_TRACE) dump_tray_status();\n    return;\n}\n\n/* Remove icon from the tray */\nvoid remove_icon(Window w)\n{\n    struct TrayIcon *ti;\n    /* Ignore false alarms */\n    if ((ti = icon_list_find(w)) == NULL) return;\n    dump_tray_status();\n    embedder_unembed(ti);\n    xembed_unembed(ti);\n    layout_remove(ti);\n    icon_list_free(ti);\n    LOG_INFO((\"removed icon %s (wid 0x%lx)\\n\",\n        x11_get_window_name(tray_data.dpy, ti->wid, \"<unknown>\"), w));\n    /* no need to call embedde_update_positions(), as\n     * scrollbars_click(SB_WND_MAX) will call it */\n    /* XXX: maybe we need a different name for this\n     * routine instad of passing cryptinc constant? */\n    scrollbars_click(SB_WND_MAX);\n    tray_update_window_props();\n    dump_tray_status();\n}\n\n/* Track icon visibility state changes */\nvoid icon_track_visibility_changes(Window w)\n{\n    struct TrayIcon *ti;\n    int mapped;\n    /* Ignore false alarms */\n    if ((ti = icon_list_find(w)) == NULL || !ti->is_xembed_supported) return;\n    mapped = xembed_get_mapped_state(ti);\n    LOG_TRACE((\"xembed_is_mapped(0x%lx) = %u\\n\", w, mapped));\n    LOG_TRACE((\"is_visible = %u\\n\", ti->is_visible));\n#ifdef DEBUG\n    x11_dump_win_info(tray_data.dpy, ti->wid);\n#endif\n    /* Nothing has changed */\n    if (mapped == ti->is_visible) return;\n    ti->is_visible = mapped;\n    LOG_INFO((\"%s icon 0x%lx\\n\", mapped ? \"showing\" : \"hiding\", w));\n    if (mapped) { /* Icon has become mapped and is listed as hidden. Show this\n                     icon. */\n        embedder_reset_size(ti);\n        if (!layout_add(ti)) {\n            xembed_set_mapped_state(ti, False);\n            return;\n        }\n        embedder_show(ti);\n    } else { /* Icon has become unmapped and is listed as visible. Hide this\n                icon. */\n        layout_remove(ti);\n        embedder_hide(ti);\n    }\n    embedder_update_positions(False);\n    tray_update_window_props();\n}\n\n/* helper to identify invalid icons */\nint find_invalid_icons(struct TrayIcon *ti)\n{\n    return ti->is_invalid;\n}\n\n#ifdef _ST_WITH_NATIVE_KDE\n/* Find newly available KDE icons and add them as necessary */\n/* TODO: move to kde_tray.c */\nvoid kde_icons_update()\n{\n    unsigned long list_len, i;\n    Window *kde_tray_icons;\n    if (tray_data.kde_tray_old_mode\n        || !x11_get_root_winlist_prop(tray_data.dpy,\n            tray_data.xa_kde_net_system_tray_windows,\n            (unsigned char **)&kde_tray_icons, &list_len)) {\n        return;\n    }\n    for (i = 0; i < list_len; i++)\n        /* If the icon is not None and is non old, try to add it\n         * (if the icon is already there, nothing is gonna happen). */\n        if (kde_tray_icons[i] != None\n            && !kde_tray_is_old_icon(kde_tray_icons[i])) {\n            LOG_TRACE((\"found (possibly unembedded) KDE icon %s (wid 0x%lx)\\n\",\n                x11_get_window_name(\n                    tray_data.dpy, kde_tray_icons[i], \"<unknown>\"),\n                kde_tray_icons[i]));\n            add_icon(kde_tray_icons[i], CM_KDE);\n        }\n    XFree(kde_tray_icons);\n}\n#endif\n\nvoid find_unmanaged_chromium_icons()\n{\n    unsigned int n;\n    Window *topwins, dummy;\n    XQueryTree(tray_data.dpy, DefaultRootWindow(tray_data.dpy), &dummy, &dummy,\n        &topwins, &n);\n    if (topwins == NULL) return;\n\n    // Find toplevel windows (unmanaged) that have:\n    //_NET_WM_WINDOW_TYPE(ATOM) == _NET_WM_WINDOW_TYPE_NOTIFICATION\n    // CHROMIUM_COMPOSITE_WINDOW(CARDINAL) == 1\n    Atom win_type = XInternAtom(tray_data.dpy, \"_NET_WM_WINDOW_TYPE\", False);\n    Atom win_type_notif =\n        XInternAtom(tray_data.dpy, \"_NET_WM_WINDOW_TYPE_NOTIFICATION\", False);\n    Atom chrom_composite =\n        XInternAtom(tray_data.dpy, \"CHROMIUM_COMPOSITE_WINDOW\", False);\n    for (unsigned int i = 0; i < n; i++) {\n        Atom *aitem;\n        unsigned int *citem;\n        unsigned long nitems;\n        int rc = False;\n        Bool ok = True;\n        LOG_TRACE((\"Chromium hack - checking window 0x%lx\\n\", topwins[i]));\n\n        rc = x11_get_window_prop32(tray_data.dpy, topwins[i], win_type,\n            XA_ATOM, (unsigned char **)&aitem, &nitems);\n        LOG_TRACE((\"Chromium hack, ret: %x %x=%x %lx\\n\", x11_ok(), rc, Success,\n            nitems));\n        if (!(x11_ok() && rc == SUCCESS && nitems == 1)) continue;\n        LOG_TRACE((\"Chromium hack, comp: %lx=%lx\\n\", aitem[0], win_type_notif));\n        ok = aitem[0] == win_type_notif;\n        XFree(aitem);\n        if (!ok) continue;\n        LOG_TRACE((\"Found toplevel notification window 0x%lx\\n\", topwins[i]));\n\n        rc = x11_get_window_prop32(tray_data.dpy, topwins[i], chrom_composite,\n            XA_CARDINAL, (unsigned char **)&citem, &nitems);\n        LOG_TRACE((\"Chromium hack, ret: %x %x=%x %lx\\n\", x11_ok(), rc, Success,\n            nitems));\n        if (!(x11_ok() && rc == SUCCESS && nitems == 1)) continue;\n        LOG_TRACE((\"Chromium hack, comp: %lx=%lx\\n\", aitem[0], win_type_notif));\n        ok = citem[0] == 1;\n        XFree(citem);\n        if (!ok) continue;\n        LOG_TRACE((\"Found chromium composite window 0x%ld\\n\", topwins[i]));\n\n        add_icon(topwins[i], CM_FDO);\n    }\n\n    XFree(topwins);\n}\n\n#define PT_MASK_SB (1L << 0)\n#define PT_MASK_ALL PT_MASK_SB\n\n/* Perform several periodic tasks */\nvoid perform_periodic_tasks(int mask)\n{\n    struct TrayIcon *ti;\n    /* 1. Remove all invalid icons */\n    while ((ti = icon_list_forall(&find_invalid_icons)) != NULL) {\n        LOG_TRACE((\"icon 0x%lx is invalid; removing\\n\", ti->wid));\n        remove_icon(ti->wid);\n    }\n    /* 2. Print tray status if asked to */\n    if (tray_status_requested) dump_tray_status();\n    /* 3. KLUDGE to fix window size on (buggy?) WMs */\n    if (settings.kludge_flags & KLUDGE_FIX_WND_SIZE) {\n        /* KLUDGE TODO: resolve */\n        XWindowAttributes xwa;\n        XGetWindowAttributes(tray_data.dpy, tray_data.tray, &xwa);\n        if (!tray_data.is_reparented\n            && (xwa.width != tray_data.xsh.width\n                || xwa.height != tray_data.xsh.height)) {\n            LOG_TRACE((\"KLUDGE: fixing tray window size (current: %dx%d, \"\n                       \"required: %dx%d)\\n\",\n                xwa.width, xwa.height, tray_data.xsh.width,\n                tray_data.xsh.height));\n            tray_update_window_props();\n        }\n    }\n    /* 4. run scrollbars periodic tasks */\n    if (mask & PT_MASK_SB) scrollbars_periodic_tasks();\n}\n\n/**********************\n * Event handlers\n **********************/\n\nvoid expose(XExposeEvent ev)\n{\n    if (ev.window == tray_data.tray && settings.parent_bg && ev.count == 0)\n        tray_refresh_window(False);\n}\n\nvoid visibility_notify(XVisibilityEvent) {}\n\nvoid property_notify(XPropertyEvent ev)\n{\n#define TRACE_PROPS\n#if defined(DEBUG) && defined(TRACE_PROPS)\n    char *atom_name;\n    atom_name = XGetAtomName(tray_data.dpy, ev.atom);\n    LOG_TRACE((\"atom = %s\\n\", atom_name));\n    XFree(atom_name);\n#endif\n    /* React on wallpaper change */\n    if (ev.atom == tray_data.xa_xrootpmap_id\n        || ev.atom == tray_data.xa_xsetroot_id) {\n        if (settings.transparent) tray_update_bg(True);\n        if (settings.parent_bg || settings.transparent || settings.fuzzy_edges)\n            tray_refresh_window(True);\n    }\n#ifdef _ST_WITH_NATIVE_KDE\n    /* React on change of list of KDE icons */\n    if (ev.atom == tray_data.xa_kde_net_system_tray_windows) {\n        if (tray_data.is_active)\n            kde_icons_update();\n        else\n            LOG_TRACE((\"not updating KDE icons list: tray is not active\\n\"));\n        kde_tray_update_old_icons(tray_data.dpy);\n    }\n#endif\n    /* React on WM (re)starts */\n    if (ev.atom\n        == XInternAtom(tray_data.dpy, _NET_SUPPORTING_WM_CHECK, False)) {\n#ifdef DEBUG\n        ewmh_list_supported_atoms(tray_data.dpy);\n#endif\n        tray_set_wm_hints();\n#ifdef _ST_WITH_NATIVE_KDE\n        kde_tray_update_fallback_mode(tray_data.dpy);\n#endif\n    }\n    /* React on _XEMBED_INFO changes of embedded icons\n     * (currently used to track icon visibility status) */\n    if (ev.atom == tray_data.xembed_data.xa_xembed_info) {\n        icon_track_visibility_changes(ev.window);\n    }\n    if (ev.atom == tray_data.xa_net_client_list) {\n        Window *windows;\n        unsigned long nwindows, rc, i;\n        rc = x11_get_root_winlist_prop(tray_data.dpy,\n            tray_data.xa_net_client_list, (unsigned char **)&windows,\n            &nwindows);\n        if (x11_ok() && rc) {\n            tray_data.is_reparented = True;\n            for (i = 0; i < nwindows; i++)\n                if (windows[i] == tray_data.tray) {\n                    tray_data.is_reparented = False;\n                    break;\n                }\n        }\n        if (nwindows) XFree(windows);\n        LOG_TRACE((\n            \"tray was %sreparented\\n\", tray_data.is_reparented ? \"\" : \"not \"));\n    }\n}\n\nvoid reparent_notify(XReparentEvent ev)\n{\n    struct TrayIcon *ti;\n    ti = icon_list_find(ev.window);\n    if (ti == NULL) return;\n    /* Reparenting out of the tray is one of non-destructive\n     * ways to end XEMBED protocol (see spec) */\n    if (ti->is_embedded && ti->mid_parent != ev.parent) {\n        LOG_TRACE((\"will now unembed 0x%lx\\n\", ti->wid));\n#ifdef DEBUG\n        print_icon_data(ti);\n        x11_dump_win_info(tray_data.dpy, ev.parent);\n#endif\n        remove_icon(ev.window);\n    }\n}\n\nvoid client_message(XClientMessageEvent ev)\n{\n    int cmode = CM_FDO;\n    struct TrayIcon *ti;\n#ifdef DEBUG\n    /* Print neat message(s) about this event to aid debugging */\n    char *msg_type_name;\n    msg_type_name = XGetAtomName(tray_data.dpy, ev.message_type);\n    if (msg_type_name != NULL) {\n        LOG_TRACE((\"message \\\"%s\\\"\\n\", msg_type_name));\n        XFree(msg_type_name);\n    }\n    if (ev.message_type == tray_data.xa_wm_protocols) {\n        msg_type_name = XGetAtomName(tray_data.dpy, ev.data.l[0]);\n        if (msg_type_name != NULL) {\n            LOG_TRACE((\"WM_PROTOCOLS message type: %s\\n\", msg_type_name));\n            XFree(msg_type_name);\n        }\n    }\n#endif\n    /* Graceful exit */\n    if (ev.message_type == tray_data.xa_wm_protocols\n        && (unsigned long) ev.data.l[0] == tray_data.xa_wm_delete_window\n        && ev.window == tray_data.tray) {\n        LOG_TRACE((\"got WM_DELETE message, will now exit\\n\"));\n        exit(0); // atexit will call cleanup()\n    }\n    /* Handle _NET_WM_PING */\n    if (ev.message_type == tray_data.xa_wm_protocols\n        && (unsigned long) ev.data.l[0] == tray_data.xa_net_wm_ping\n        && ev.window == tray_data.tray) {\n        LOG_TRACE((\"got WM_PING message, sending it back\\n\"));\n        XEvent reply;\n        reply.xclient = ev;\n        reply.xclient.window = DefaultRootWindow(tray_data.dpy);\n        XSendEvent(tray_data.dpy, DefaultRootWindow(tray_data.dpy), False,\n            (SubstructureNotifyMask | SubstructureRedirectMask), &reply);\n    }\n    /* Handle _NET_SYSTEM_TRAY_* messages */\n    if (ev.message_type == tray_data.xa_tray_opcode && tray_data.is_active) {\n        LOG_TRACE((\"this is the _NET_SYSTEM_TRAY_OPCODE(%lu) message\\n\",\n            ev.data.l[1]));\n        switch (ev.data.l[1]) {\n        /* This is the starting point of NET SYSTEM TRAY protocol */\n        case SYSTEM_TRAY_REQUEST_DOCK:\n            LOG_TRACE(\n                (\"dockin' requested by window 0x%lx, serving in a moment\\n\",\n                    ev.data.l[2]));\n#ifdef _ST_WITH_NATIVE_KDE\n            if (kde_tray_check_for_icon(tray_data.dpy, ev.data.l[2]))\n                cmode = CM_KDE;\n            if (kde_tray_is_old_icon(ev.data.l[2]))\n                kde_tray_old_icons_remove(ev.data.l[2]);\n#endif\n            add_icon(ev.data.l[2], cmode);\n            break;\n        /* We ignore these messages, since we do not show\n         * any baloons anyways */\n        case SYSTEM_TRAY_BEGIN_MESSAGE:\n        case SYSTEM_TRAY_CANCEL_MESSAGE: break;\n        /* Below are special cases added by this implementation */\n        /* STALONETRAY_TRAY_DOCK_CONFIRMED is sent by stalonetray\n         * to itself. (see embed.c) */\n        case STALONE_TRAY_DOCK_CONFIRMED:\n            ti = icon_list_find(ev.data.l[2]);\n            if (ti != NULL && !ti->is_embedded) {\n                ti->is_embedded = True;\n                LOG_TRACE((\"embedding confirmed for icon 0x%lx\\n\", ti->wid));\n#ifdef DEBUG\n                dump_tray_status();\n#endif\n            }\n            tray_update_window_props();\n            break;\n        /* Dump tray status on request */\n        case STALONE_TRAY_STATUS_REQUESTED: dump_tray_status(); break;\n        /* Find icon and scroll to it if necessary */\n        case STALONE_TRAY_REMOTE_CONTROL:\n            ti = icon_list_find(ev.window);\n            if (ti == NULL) break;\n            scrollbars_scroll_to(ti);\n#if 0\n\t\t\t\t/* Quick hack */\n\t\t\t\t{\n\t\t\t\t\tWindow icon = ev.window;\n\t\t\t\t\tint rc;\n\t\t\t\t\tint x = ev.data.l[3], y = ev.data.l[4], depth = 0, idummy, i;\n\t\t\t\t\tint btn = ev.data.l[2];\n\t\t\t\t\tWindow win, root;\n\t\t\t\t\tunsigned int udummy, w, h;\n\t\t\t\t\tXGetGeometry(tray_data.dpy, icon, &root,\n\t\t\t\t\t\t\t&idummy, &idummy,\n\t\t\t\t\t\t\t&w, &h, &udummy, &udummy);\n\t\t\t\t\tLOG_TRACE((\"wid=0x%x w=%d h=%d\\n\", icon, w, h));\n\t\t\t\t\tx = (x == REMOTE_CLICK_POS_DEFAULT) ? w / 2 : x;\n\t\t\t\t\ty = (y == REMOTE_CLICK_POS_DEFAULT) ? h / 2 : y;\n\t\t\t\t\t/* 3.2. Find subwindow to execute click on */\n\t\t\t\t\twin = x11_find_subwindow_at(tray_data.dpy, icon, &x, &y, depth);\n\t\t\t\t\t/* 3.3. Send mouse click(s) to target */\n\t\t\t\t\tLOG_TRACE((\"wid=0x%x btn=%d x=%d y=%d\\n\",\n\t\t\t\t\t\t\t\twin, btn, x, y));\n#define SEND_BTN_EVENT(press, time) \\\n    do { \\\n        x11_send_button(tray_data.dpy, /* dispslay */ \\\n            press, /* event type */ \\\n            win, /* target window */ \\\n            root, /* root window */ \\\n            time, /* time */ \\\n            btn, /* button */ \\\n            Button1Mask << (btn - 1), /* state mask */ \\\n            x, /* x coord (relative) */ \\\n            y); /* y coord (relative) */ \\\n    } while (0)\n\t\t\t\t\tfor (i = 0; i < ev.data.l[0]; i++) {\n\t\t\t\t\t\tSEND_BTN_EVENT(1, x11_get_server_timestamp(tray_data.dpy, tray_data.tray));\n\t\t\t\t\t\tmy_usleep(250);\n\t\t\t\t\t\tSEND_BTN_EVENT(0, x11_get_server_timestamp(tray_data.dpy, tray_data.tray));\n\t\t\t\t\t}\n#undef SEND_BTN_EVENT\n\t\t\t\t}\n#endif\n            break;\n        default: break;\n        }\n    }\n#ifdef DEBUG\n    if (ev.message_type == tray_data.xa_tray_opcode && !tray_data.is_active)\n        LOG_TRACE((\"ignoring _NET_SYSTEM_TRAY_OPCODE(%d) message because \"\n                   \"tray is not active\\n\",\n            tray_data.is_active));\n#endif\n}\n\nvoid destroy_notify(XDestroyWindowEvent ev)\n{\n    if (!tray_data.is_active && ev.window == tray_data.old_selection_owner) {\n        /* Old tray selection owner was destroyed. Take over selection\n         * ownership. */\n        tray_acquire_selection();\n    } else if (ev.window != tray_data.tray) {\n        /* Try to remove icon from the tray */\n        remove_icon(ev.window);\n#ifdef _ST_WITH_NATIVE_KDE\n    } else if (kde_tray_is_old_icon(ev.window)) {\n        /* Since X Server may reuse window ids, remove ev.window\n         * from the list of old KDE icons */\n        kde_tray_old_icons_remove(ev.window);\n#endif\n    }\n}\n\nvoid configure_notify(XConfigureEvent ev)\n{\n    struct TrayIcon *ti;\n    struct Point sz;\n    XWindowAttributes xwa;\n    if (ev.window == tray_data.tray) {\n        /* Tray window was resized */\n        /* TODO: distinguish between synthetic and real configure notifies */\n        /* TODO: catch rejected configure requests */\n        /* XXX: Geometry stuff is a mess. Geometry\n         * is specified in slots, but almost always is\n         * stored in pixels... */\n        LOG_TRACE((\"tray window geometry from event: %ux%u+%d+%d\\n\", ev.width,\n            ev.height, ev.x, ev.y));\n        /* Sometimes, configure notifies come too late, so we fetch real\n         * geometry ourselves */\n        XGetWindowAttributes(tray_data.dpy, tray_data.tray, &xwa);\n        x11_get_window_abs_coords(\n            tray_data.dpy, tray_data.tray, &tray_data.xsh.x, &tray_data.xsh.y);\n        LOG_TRACE((\"tray window geometry from X11 calls: %dx%d+%d+%d\\n\",\n            xwa.width, xwa.height, tray_data.xsh.x, tray_data.xsh.y));\n        tray_data.xsh.width = xwa.width;\n        tray_data.xsh.height = xwa.height;\n        /* Update icons positions */\n        /* XXX: internal API is bad. example below */\n        icon_list_forall(&layout_translate_to_window);\n        embedder_update_positions(True);\n        /* Adjust window background if necessary */\n        tray_update_bg(False);\n        tray_refresh_window(True);\n        tray_update_window_strut();\n        scrollbars_update();\n    } else if ((ti = icon_list_find(ev.window))\n        != NULL) { /* Some icon has resized its window */\n        /* KDE icons are not allowed to change their size. Reset icon size. */\n        if (ti->cmode == CM_KDE\n            || settings.kludge_flags & KLUDGE_FORCE_ICONS_SIZE) {\n            embedder_reset_size(ti);\n            return;\n        }\n        if (settings.kludge_flags & KLUDGE_FORCE_ICONS_SIZE) return;\n        /* Get new window size */\n        if (!x11_get_window_size(tray_data.dpy, ti->wid, &sz.x, &sz.y)) {\n            embedder_unembed(ti);\n            return;\n        }\n        LOG_TRACE((\"icon 0x%lx was resized, new size: %ux%u, old size: %ux%u\\n\",\n            ev.window, sz.x, sz.y, ti->l.wnd_sz.x, ti->l.wnd_sz.y));\n        /* Check if the size has really changed */\n        if (sz.x == ti->l.wnd_sz.x && sz.y == ti->l.wnd_sz.y) return;\n        ti->l.wnd_sz = sz;\n        ti->is_resized = True;\n        /* Do the job */\n        layout_handle_icon_resize(ti);\n        embedder_refresh(ti);\n#ifdef DEBUG\n        print_icon_data(ti);\n#endif\n        embedder_update_positions(False);\n        tray_update_window_props();\n#ifdef DEBUG\n        dump_tray_status();\n#endif\n    }\n}\n\nvoid selection_clear(XSelectionClearEvent ev)\n{\n    /* Is it _NET_SYSTEM_TRAY selection? */\n    if (ev.selection == tray_data.xa_tray_selection) {\n        /* Is it us who has lost the selection */\n        if (ev.window == tray_data.tray) {\n            LOG_INFO((\"another tray detected; deactivating\\n\"));\n            tray_data.is_active = False;\n            tray_data.old_selection_owner =\n                XGetSelectionOwner(tray_data.dpy, tray_data.xa_tray_selection);\n            if (!x11_ok()) {\n                LOG_INFO((\"could not find proper new tray; reactivating\\n\"));\n                tray_acquire_selection();\n            };\n            LOG_TRACE((\"new selection owner is 0x%lx\\n\",\n                tray_data.old_selection_owner));\n            XSelectInput(tray_data.dpy, tray_data.old_selection_owner,\n                StructureNotifyMask);\n            return;\n        } else if (!tray_data.is_active) {\n            /* Someone else has lost selection and tray is not active --- take\n             * over the selection */\n            LOG_INFO((\"another tray exited; reactivating\\n\"));\n            tray_acquire_selection();\n        } else {\n            /* Just in case */\n            LOG_TRACE((\"WEIRD: tray is active and someone else has lost tray \"\n                       \"selection\\n\"));\n        }\n    }\n}\n\nvoid map_notify(XMapEvent ev)\n{\n#ifdef _ST_WITH_NATIVE_KDE\n    /* Legacy scheme to handle KDE icons */\n    if (tray_data.kde_tray_old_mode) {\n        struct TrayIcon *ti = icon_list_find_ex(ev.window);\n        if (ti == NULL) {\n            Window w = kde_tray_find_icon(tray_data.dpy, ev.window);\n            if (w != None) {\n                LOG_TRACE((\"Legacy scheme for KDE icons: detected KDE icon \"\n                           \"0x%lx. Adding.\\n\",\n                    w));\n                add_icon(w, CM_KDE);\n                /* TODO: remove some properties to trick ion3 so that it no\n                 * longer thinks that w is a toplevel. Candidates for removal:\n                 * \t- WM_STATE */\n            }\n        }\n    }\n#else\n    (void) ev; /* unused */\n#endif\n}\n\nvoid unmap_notify(XUnmapEvent ev)\n{\n    struct TrayIcon *ti;\n    ti = icon_list_find(ev.window);\n    if (ti != NULL && !ti->is_invalid) {\n        /* KLUDGE! sometimes icons occasionally\n         * unmap their windows, but do _not_ destroy\n         * them. We map those windows back */\n        /* XXX: not root caused */\n        LOG_TRACE((\"Unmap icons KLUDGE executed for 0x%lx\\n\", ti->wid));\n        XMapRaised(tray_data.dpy, ti->wid);\n        if (!x11_ok()) ti->is_invalid = True;\n    }\n}\n\n/*********************************************************/\n/* main() for usual operation */\nint tray_main(int argc, char **argv)\n{\n    XEvent ev;\n    /* Interpret those settings that need an open display */\n    interpret_settings();\n#ifdef DEBUG\n    ewmh_list_supported_atoms(tray_data.dpy);\n#endif\n    xinerama_init(tray_data.dpy);\n    xinerama_update_geometry();\n    /* Create and show tray window */\n    tray_create_window(argc, argv);\n    tray_acquire_selection();\n    tray_show_window();\n#ifdef _ST_WITH_NATIVE_KDE\n    kde_tray_init(tray_data.dpy);\n#endif\n    xembed_init();\n#ifdef _ST_WITH_NATIVE_KDE\n    kde_icons_update();\n#endif\n    find_unmanaged_chromium_icons();\n    /* Main event loop */\n    while (\"my guitar gently wheeps\") {\n        /* This is ugly and extra dependency. But who cares?\n         * Rationale: we want to block unless absolutely needed.\n         * This way we ensure that stalonetray does not show up\n         * in powertop (i.e. does not eat unnecessary power and\n         * CPU cycles)\n         * Drawback: handling of signals is very limited. XNextEvent()\n         * does not if signal occurs. This means that graceful\n         * exit on e.g. Ctrl-C cannot be implemented without hacks. */\n        while (XPending(tray_data.dpy)\n            || tray_data.scrollbars_data.scrollbar_down == -1) {\n            XNextEvent(tray_data.dpy, &ev);\n            xembed_handle_event(ev);\n            scrollbars_handle_event(ev);\n            switch (ev.type) {\n            case VisibilityNotify:\n                LOG_TRACE((\"VisibilityNotify (0x%lx, state=%d)\\n\",\n                    ev.xvisibility.window, ev.xvisibility.state));\n                visibility_notify(ev.xvisibility);\n                break;\n            case Expose:\n                LOG_TRACE((\"Expose (0x%lx)\\n\", ev.xexpose.window));\n                expose(ev.xexpose);\n                break;\n            case PropertyNotify:\n                LOG_TRACE((\"PropertyNotify(0x%lx)\\n\", ev.xproperty.window));\n                property_notify(ev.xproperty);\n                break;\n            case DestroyNotify:\n                LOG_TRACE((\"DestroyNotify(0x%lx)\\n\", ev.xdestroywindow.window));\n                destroy_notify(ev.xdestroywindow);\n                break;\n            case ClientMessage:\n                LOG_TRACE((\"ClientMessage(from 0x%lx?)\\n\", ev.xclient.window));\n                client_message(ev.xclient);\n                break;\n            case ConfigureNotify:\n                LOG_TRACE((\"ConfigureNotify(0x%lx)\\n\", ev.xconfigure.window));\n                configure_notify(ev.xconfigure);\n                break;\n            case MapNotify:\n                LOG_TRACE((\"MapNotify(0x%lx)\\n\", ev.xmap.window));\n                map_notify(ev.xmap);\n                break;\n            case ReparentNotify:\n                LOG_TRACE((\"ReparentNotify(0x%lx to 0x%lx)\\n\",\n                    ev.xreparent.window, ev.xreparent.parent));\n                reparent_notify(ev.xreparent);\n                break;\n            case SelectionClear:\n                LOG_TRACE((\"SelectionClear (0x%lx has lost selection)\\n\",\n                    ev.xselectionclear.window));\n                selection_clear(ev.xselectionclear);\n                break;\n            case SelectionNotify: LOG_TRACE((\"SelectionNotify\\n\")); break;\n            case SelectionRequest:\n                LOG_TRACE((\"SelectionRequest (from 0x%lx to 0x%lx)\\n\",\n                    ev.xselectionrequest.requestor,\n                    ev.xselectionrequest.owner));\n                break;\n            case UnmapNotify:\n                LOG_TRACE((\"UnmapNotify(0x%lx)\\n\", ev.xunmap.window));\n                unmap_notify(ev.xunmap);\n                break;\n            default:\n#if defined(DEBUG) && defined(_ST_TRACE_EVENTS)\n                LOG_TRACE((\"Unhandled event: %s, serial: %ld, window: 0x%lx\\n\",\n                    x11_event_names[ev.type], ev.xany.serial, ev.xany.window));\n#endif\n                break;\n            }\n            if (tray_data.terminated) goto bailout;\n            /* Perform all periodic tasks but for scrollbars */\n            perform_periodic_tasks(PT_MASK_ALL & (~PT_MASK_SB));\n        }\n        perform_periodic_tasks(PT_MASK_ALL);\n        my_usleep(500000L);\n    }\nbailout:\n    LOG_TRACE((\"Clean exit\\n\"));\n    return 0;\n}\n\n/* main() for controlling stalonetray remotely */\nint remote_main(int, char **)\n{\n    Window tray, icon = None;\n    int rc;\n    int x, y, depth = 0, idummy, i;\n    Window win, root;\n    unsigned int udummy, w, h;\n    tray_init_selection_atoms();\n    tray_create_phony_window();\n    LOG_TRACE(\n        (\"name=\\\"%s\\\" btn=%d cnt=%d x=%d y=%d\\n\", settings.remote_click_name,\n            settings.remote_click_btn, settings.remote_click_cnt,\n            settings.remote_click_pos.x, settings.remote_click_pos.y));\n    tray = XGetSelectionOwner(tray_data.dpy, tray_data.xa_tray_selection);\n    if (tray == None) return 255;\n    /* 1. find window matching requested name */\n    icon = x11_find_subwindow_by_name(\n        tray_data.dpy, tray, settings.remote_click_name);\n    if (icon == None) return 127;\n    /* 2. form a message to tray requesting it to show the icon */\n    rc = x11_send_client_msg32(tray_data.dpy, /* display */\n        tray, /* destination */\n        icon, /* window */\n        tray_data.xa_tray_opcode, /* atom */\n        settings.remote_click_cnt, /* data0 */\n        STALONE_TRAY_REMOTE_CONTROL, /* data1 */\n        settings.remote_click_btn, /* data2 */\n        settings.remote_click_pos.x, /* data3 */\n        settings.remote_click_pos.y /* data4 */\n    );\n    if (!rc) return 63;\n    /* 3. Execute the click */\n    /* 3.1. Sort out click position */\n    XGetGeometry(tray_data.dpy, icon, &root, &idummy, &idummy, &w, &h, &udummy,\n        &udummy);\n    LOG_TRACE((\"wid=0x%lx w=%d h=%d\\n\", icon, w, h));\n    x = (settings.remote_click_pos.x == REMOTE_CLICK_POS_DEFAULT)\n        ? w / 2\n        : (unsigned int) settings.remote_click_pos.x;\n    y = (settings.remote_click_pos.y == REMOTE_CLICK_POS_DEFAULT)\n        ? h / 2\n        : (unsigned int) settings.remote_click_pos.y;\n    /* 3.2. Find subwindow to execute click on */\n    win = x11_find_subwindow_at(tray_data.dpy, icon, &x, &y, depth);\n    /* 3.3. Send mouse click(s) to target */\n    LOG_TRACE(\n        (\"wid=0x%lx btn=%d x=%d y=%d\\n\", win, settings.remote_click_btn, x, y));\n#define SEND_BTN_EVENT(press, time) \\\n    do { \\\n        x11_send_button(tray_data.dpy, /* dispslay */ \\\n            press, /* event type */ \\\n            win, /* target window */ \\\n            root, /* root window */ \\\n            time, /* time */ \\\n            settings.remote_click_btn, /* button */ \\\n            Button1Mask << (settings.remote_click_btn - 1), /* state mask */ \\\n            x, /* x coord (relative) */ \\\n            y); /* y coord (relative) */ \\\n    } while (0)\n    for (i = 0; i < settings.remote_click_cnt; i++) {\n        SEND_BTN_EVENT(\n            1, x11_get_server_timestamp(tray_data.dpy, tray_data.tray));\n        my_usleep(250);\n        SEND_BTN_EVENT(\n            0, x11_get_server_timestamp(tray_data.dpy, tray_data.tray));\n    }\n#undef SEND_BTN_EVENT\n    return 0;\n}\n\n/* main() */\nint main(int argc, char **argv)\n{\n    /* Read settings */\n    tray_init();\n    read_settings(argc, argv);\n    /* Register cleanup and signal handlers */\n    atexit(cleanup);\n    signal(SIGUSR1, &request_tray_status_on_signal);\n#ifdef _ST_EXIT_GRACEFULLY\n    signal(SIGINT, &exit_on_signal);\n    signal(SIGTERM, &exit_on_signal);\n    signal(SIGPIPE, &exit_on_signal);\n#endif\n    /* Open display */\n    if ((tray_data.dpy = XOpenDisplay(settings.display_str)) == NULL)\n        DIE((\"could not open display\\n\"));\n    else\n        LOG_TRACE((\"Opened dpy %p\\n\", (void *) tray_data.dpy));\n#ifdef _ST_EXIT_GRACEFULLY\n    if ((async_dpy = XOpenDisplay(settings.display_str)) == NULL)\n        DIE((\"could not open display\\n\"));\n    else\n        LOG_TRACE((\"Opened async dpy %p\\n\", async_dpy));\n#endif\n    if (settings.xsync) XSynchronize(tray_data.dpy, True);\n    x11_trap_errors();\n    /* Execute proper main() function */\n    if (settings.remote_click_name != NULL)\n        return remote_main(argc, argv);\n    else\n        return tray_main(argc, argv);\n}\n"
  },
  {
    "path": "src/meson.build",
    "content": "project_sources += [\n  'src/debug.c',\n  'src/embed.c',\n  'src/icons.c',\n  'src/image.c',\n  'src/layout.c',\n  'src/main.c',\n  'src/scrollbars.c',\n  'src/settings.c',\n  'src/tray.c',\n  'src/wmh.c',\n  'src/xembed.c',\n  'src/xinerama.c',\n  'src/xutils.c',\n]\n\nif get_option('native_kde').enabled()\n  project_sources += ['src/kde_tray.c']\nendif\n"
  },
  {
    "path": "src/scrollbars.c",
    "content": "/* ************************************\n * vim:tabstop=4:shiftwidth=4:cindent:fen:fdm=syntax\n * tray.c\n * Mon, 09 Mar 2009 12:38:30 -0400\n * ************************************\n * scrollbars functions\n * ************************************/\n\n#include <limits.h>\n\n#include <X11/Xatom.h>\n#include <X11/Xlib.h>\n#include <X11/Xmd.h>\n#include <X11/Xutil.h>\n\n#include \"embed.h\"\n#include \"tray.h\"\n#include \"xutils.h\"\n\nvoid scrollbars_init()\n{\n    tray_data.scrollbars_data.scroll_pos.x = 0;\n    tray_data.scrollbars_data.scroll_pos.y = 0;\n    tray_data.scrollbars_data.scroll_base.x = 0;\n    tray_data.scrollbars_data.scroll_base.y = 0;\n    tray_data.scrollbars_data.scrollbar_down = -1;\n}\n\nvoid scrollbars_create()\n{\n    int i;\n    /*#define DEBUG_SCROLLBAR_POSITIONS*/\n\n    if (settings.scrollbars_mode & SB_MODE_VERT) {\n        tray_data.scrollbars_data.scrollbar[SB_WND_TOP] =\n            XCreateSimpleWindow(tray_data.dpy, tray_data.tray, 0, 0, 1, 1, 0,\n#ifdef DEBUG_SCROLLBAR_POSITIONS\n                0xff00ff, 0xff00ff);\n#else\n                settings.bg_color.pixel, settings.bg_color.pixel);\n#endif\n        tray_data.scrollbars_data.scrollbar[SB_WND_BOT] =\n            XCreateSimpleWindow(tray_data.dpy, tray_data.tray, 0, 0, 1, 1, 0,\n#ifdef DEBUG_SCROLLBAR_POSITIONS\n                0xffff00, 0xffff00);\n#else\n                settings.bg_color.pixel, settings.bg_color.pixel);\n#endif\n    } else {\n        tray_data.scrollbars_data.scrollbar[SB_WND_TOP] = None;\n        tray_data.scrollbars_data.scrollbar[SB_WND_BOT] = None;\n    }\n    if (settings.scrollbars_mode & SB_MODE_HORZ) {\n        tray_data.scrollbars_data.scrollbar[SB_WND_LFT] =\n            XCreateSimpleWindow(tray_data.dpy, tray_data.tray, 0, 0, 1, 1, 0,\n#ifdef DEBUG_SCROLLBAR_POSITIONS\n                0x00ff00, 0x00ff00);\n#else\n                settings.bg_color.pixel, settings.bg_color.pixel);\n#endif\n        tray_data.scrollbars_data.scrollbar[SB_WND_RHT] =\n            XCreateSimpleWindow(tray_data.dpy, tray_data.tray, 0, 0, 1, 1, 0,\n#ifdef DEBUG_SCROLLBAR_POSITIONS\n                0x00ffff, 0x00ffff);\n#else\n                settings.bg_color.pixel, settings.bg_color.pixel);\n#endif\n    } else {\n        tray_data.scrollbars_data.scrollbar[SB_WND_LFT] = None;\n        tray_data.scrollbars_data.scrollbar[SB_WND_RHT] = None;\n    }\n    scrollbars_update();\n    \n    if (settings.scroll_everywhere && settings.scrollbars_mode != SB_MODE_NONE) {\n        XGrabButton(tray_data.dpy, Button5, AnyModifier, tray_data.tray, False,\n                ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None);\n        XGrabButton(tray_data.dpy, Button4, AnyModifier, tray_data.tray, False,\n                ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None);\n    }\n\n    for (i = 0; i < SB_WND_MAX; i++)\n        if (tray_data.scrollbars_data.scrollbar[i] != None) {\n#ifndef DEBUG_SCROLLBAR_POSITIONS\n            XSetWindowBackgroundPixmap(tray_data.dpy,\n                tray_data.scrollbars_data.scrollbar[i], ParentRelative);\n#endif\n            XMapRaised(tray_data.dpy, tray_data.scrollbars_data.scrollbar[i]);\n            XSelectInput(tray_data.dpy, tray_data.scrollbars_data.scrollbar[i],\n                ButtonPressMask | ButtonReleaseMask | ButtonMotionMask\n                    | EnterWindowMask | LeaveWindowMask);\n        }\n}\n\nint scrollbars_refresh(int exposures)\n{\n    int i;\n    for (i = 0; i < SB_WND_MAX; i++)\n        if (tray_data.scrollbars_data.scrollbar[i] != None)\n            x11_refresh_window(tray_data.dpy,\n                tray_data.scrollbars_data.scrollbar[i],\n                tray_data.scrollbars_data.scrollbar_xsh[i].width,\n                tray_data.scrollbars_data.scrollbar_xsh[i].height, exposures);\n    return SUCCESS;\n}\n\nint scrollbars_update()\n{\n    int offset, i;\n    static int initialized = 0;\n\n    XSizeHints scrollbar_xsh_local[SB_WND_MAX];\n\n    if (settings.scrollbars_mode & SB_MODE_HORZ) {\n        offset =\n            (settings.vertical && (settings.scrollbars_mode & SB_MODE_VERT))\n            ? settings.scrollbars_size\n            : 0;\n\n        scrollbar_xsh_local[SB_WND_LFT].x = 0;\n        scrollbar_xsh_local[SB_WND_LFT].y = offset;\n        scrollbar_xsh_local[SB_WND_LFT].width = settings.scrollbars_size;\n        scrollbar_xsh_local[SB_WND_LFT].height =\n            tray_data.xsh.height - offset * 2;\n\n        scrollbar_xsh_local[SB_WND_RHT] = scrollbar_xsh_local[SB_WND_LFT];\n        scrollbar_xsh_local[SB_WND_RHT].x =\n            tray_data.xsh.width - settings.scrollbars_size;\n    }\n    if (settings.scrollbars_mode & SB_MODE_VERT) {\n        offset =\n            (settings.vertical && (settings.scrollbars_mode & SB_MODE_HORZ))\n            ? settings.scrollbars_size\n            : 0;\n\n        scrollbar_xsh_local[SB_WND_TOP].x = offset;\n        scrollbar_xsh_local[SB_WND_TOP].y = 0;\n        scrollbar_xsh_local[SB_WND_TOP].width =\n            tray_data.xsh.width - offset * 2;\n        scrollbar_xsh_local[SB_WND_TOP].height = settings.scrollbars_size;\n\n        scrollbar_xsh_local[SB_WND_BOT] = scrollbar_xsh_local[SB_WND_TOP];\n        scrollbar_xsh_local[SB_WND_BOT].y =\n            tray_data.xsh.height - settings.scrollbars_size;\n    }\n\n    for (i = 0; i < SB_WND_MAX; i++)\n        if (tray_data.scrollbars_data.scrollbar[i] != None\n            && (!initialized\n                || (scrollbar_xsh_local[i].x\n                        != tray_data.scrollbars_data.scrollbar_xsh[i].x\n                    || scrollbar_xsh_local[i].y\n                        != tray_data.scrollbars_data.scrollbar_xsh[i].y\n                    || scrollbar_xsh_local[i].width\n                        != tray_data.scrollbars_data.scrollbar_xsh[i].width\n                    || scrollbar_xsh_local[i].height\n                        != tray_data.scrollbars_data.scrollbar_xsh[i]\n                               .height))) {\n            XMoveResizeWindow(tray_data.dpy,\n                tray_data.scrollbars_data.scrollbar[i],\n                scrollbar_xsh_local[i].x, scrollbar_xsh_local[i].y,\n                scrollbar_xsh_local[i].width, scrollbar_xsh_local[i].height);\n            tray_data.scrollbars_data.scrollbar_xsh[i] =\n                scrollbar_xsh_local[i];\n        }\n\n    initialized = 1;\n    return x11_ok();\n}\n\nint scrollbars_get_id(Window wid, int x, int y)\n{\n    int i;\n    for (i = 0; i < SB_WND_MAX; i++)\n        if (wid == tray_data.scrollbars_data.scrollbar[i] && 0 <= x && 0 <= y\n            && x < tray_data.scrollbars_data.scrollbar_xsh[i].width\n            && y < tray_data.scrollbars_data.scrollbar_xsh[i].height) {\n            return i;\n        }\n    return -1;\n}\n\nvoid scrollbars_validate_scroll_pos()\n{\n    int layout_width, layout_height;\n    int base_width, base_height;\n    struct Point max_scroll_pos;\n\n    layout_get_size(&layout_width, &layout_height);\n    tray_calc_tray_area_size(\n        tray_data.xsh.width, tray_data.xsh.height, &base_width, &base_height);\n\n    max_scroll_pos.x = layout_width - base_width;\n    max_scroll_pos.y = layout_height - base_height;\n\n    val_range(max_scroll_pos.x, 0, INT_MAX);\n    val_range(max_scroll_pos.y, 0, INT_MAX);\n    LOG_TRACE((\"computed max scroll position: %dx%d\\n\", max_scroll_pos.x,\n        max_scroll_pos.y));\n\n    val_range(tray_data.scrollbars_data.scroll_pos.x, 0, max_scroll_pos.x);\n    val_range(tray_data.scrollbars_data.scroll_pos.y, 0, max_scroll_pos.y);\n}\n\nint scrollbars_click(int id)\n{\n    /* TODO: implement scroll gravity (i.e. scroll weel must scroll in\n     * direction that agrees with current tray orientation) */\n    /* TODO: scrollbars_inc must be settable via cmdline */\n\n    /* last entry is for action that just sanitizes current scroll\n     * position after icon removal */\n    static struct Point scrollbars_deltas[SB_WND_MAX + 1] = {\n        {0, -1}, {0, 1}, {-1, 0}, {1, 0}, {0, 0}};\n\n    tray_data.scrollbars_data.scroll_pos.x +=\n        (settings.icon_gravity & GRAV_W ? 1 : -1) * scrollbars_deltas[id].x\n        * settings.scrollbars_inc;\n    tray_data.scrollbars_data.scroll_pos.y +=\n        (settings.icon_gravity & GRAV_N ? 1 : -1) * scrollbars_deltas[id].y\n        * settings.scrollbars_inc;\n\n    scrollbars_validate_scroll_pos();\n    LOG_TRACE((\"computed new scroll position: %dx%d\\n\",\n        tray_data.scrollbars_data.scroll_pos.x,\n        tray_data.scrollbars_data.scroll_pos.y));\n\n    icon_list_forall(&layout_translate_to_window);\n    embedder_update_positions(id != SB_WND_MAX);\n    return SUCCESS;\n}\n\nvoid scrollbars_handle_event(XEvent ev)\n{\n    int id;\n    switch (ev.type) {\n    case EnterNotify:\n    case LeaveNotify:\n        LOG_TRACE((\"%s, wid=0x%lx x=%d y=%d\\n\",\n            ev.type == EnterNotify ? \"EnterNotify\" : \"LeaveNotify\",\n            ev.xcrossing.window, ev.xcrossing.x, ev.xcrossing.y));\n        if (settings.scrollbars_highlight_color_str != NULL\n            && (id = scrollbars_get_id(ev.xcrossing.window, 0, 0)) != -1) {\n            if (ev.type == EnterNotify)\n                scrollbars_highlight_on(id);\n            else\n                scrollbars_highlight_off(id);\n        }\n        break;\n    case ButtonPress:\n        LOG_TRACE((\"ButtonPress, state=0x%x\\n\", ev.xbutton.state));\n        if (ev.xbutton.button == Button1\n            && (id = scrollbars_get_id(\n                    ev.xbutton.window, ev.xbutton.x, ev.xbutton.y))\n                != -1) {\n            tray_data.scrollbars_data.scrollbar_down = id;\n            tray_data.scrollbars_data.scrollbar_repeat_active = 1;\n            tray_data.scrollbars_data.scrollbar_repeat_counter =\n                SCROLLBAR_REPEAT_COUNTDOWN_MAX_1ST;\n            tray_data.scrollbars_data.scrollbar_repeats_done = 0;\n        }\n        break;\n    case MotionNotify:\n        LOG_TRACE((\"MotionNotify, state=0x%x\\n\", ev.xbutton.state));\n        tray_data.scrollbars_data.scrollbar_repeat_active =\n            (tray_data.scrollbars_data.scrollbar_down != -1\n                && scrollbars_get_id(\n                       ev.xmotion.window, ev.xmotion.x, ev.xmotion.y)\n                    == tray_data.scrollbars_data.scrollbar_down);\n        break;\n    case ButtonRelease:\n        LOG_TRACE((\"ButtonRelease, state=0x%x\\n\", ev.xbutton.state));\n        switch (ev.xbutton.button) {\n        case Button1:\n            if (tray_data.scrollbars_data.scrollbar_down != -1) {\n#if 0\n\t\t\t\t/* If no repeats were done, advance scroll position */\n\t\t\t\tif ((scrollbars_get_id(ev.xbutton.window, ev.xbutton.x, ev.xbutton.y) != -1) &&\n\t\t\t\t\t(tray_data.scrollbars_data.scrollbar_repeats_done == 0))\n\t\t\t\t{\n\t\t\t\t\tscrollbars_click(tray_data.scrollbars_data.scrollbar_down);\n\t\t\t\t}\n#endif\n                tray_data.scrollbars_data.scrollbar_down = -1;\n                tray_data.scrollbars_data.scrollbar_repeat_active = 0;\n            }\n            break;\n        case Button4:\n            if (settings.vertical && settings.scrollbars_mode & SB_MODE_VERT)\n                scrollbars_click(SB_WND_TOP);\n            else if (settings.scrollbars_mode & SB_MODE_HORZ)\n                scrollbars_click(SB_WND_LFT);\n            break;\n        case Button5:\n            if (settings.vertical && settings.scrollbars_mode & SB_MODE_VERT)\n                scrollbars_click(SB_WND_BOT);\n            else if (settings.scrollbars_mode & SB_MODE_HORZ)\n                scrollbars_click(SB_WND_RHT);\n            break;\n        default: break;\n        }\n        break;\n    }\n}\n\nvoid scrollbars_periodic_tasks()\n{\n    if (tray_data.scrollbars_data.scrollbar_down != -1\n        && tray_data.scrollbars_data.scrollbar_repeat_active) {\n        scrollbars_click(tray_data.scrollbars_data.scrollbar_down);\n    }\n}\n\nint scrollbars_scroll_to(struct TrayIcon *ti)\n{\n    struct Rect tray_viewport_rect;\n    tray_viewport_rect.x = tray_data.scrollbars_data.scroll_base.x,\n    tray_viewport_rect.y = tray_data.scrollbars_data.scroll_base.y,\n    tray_calc_tray_area_size(tray_data.xsh.width, tray_data.xsh.height,\n        &tray_viewport_rect.w, &tray_viewport_rect.h);\n    /* Check if icon is already visible. If so, nothing needs to be done */\n    if (RECTS_ISECT(tray_viewport_rect, ti->l.icn_rect)) return SUCCESS;\n    /* Update scroll pos so that the icon is visible */\n    /* TODO: must be in separate function, with a reverse */\n    tray_data.scrollbars_data.scroll_pos.x =\n        (settings.icon_gravity & GRAV_W ? 1 : -1)\n        * (ti->l.icn_rect.x - tray_data.scrollbars_data.scroll_base.x);\n    tray_data.scrollbars_data.scroll_pos.y =\n        (settings.icon_gravity & GRAV_W ? 1 : -1)\n        * (ti->l.icn_rect.y - tray_data.scrollbars_data.scroll_base.y);\n    scrollbars_validate_scroll_pos();\n    LOG_TRACE((\"computed required scroll position: %dx%d\\n\",\n        tray_data.scrollbars_data.scroll_pos.x,\n        tray_data.scrollbars_data.scroll_pos.y));\n    icon_list_forall(&layout_translate_to_window);\n    embedder_update_positions(True);\n    return SUCCESS;\n}\n\nint scrollbars_highlight_on(int id)\n{\n    Window sb_wid;\n    sb_wid =\n        (0 <= id && id < 4) ? tray_data.scrollbars_data.scrollbar[id] : None;\n    if (sb_wid != None)\n        XSetWindowBackground(\n            tray_data.dpy, sb_wid, settings.scrollbars_highlight_color.pixel);\n    scrollbars_refresh(1);\n    return SUCCESS;\n}\n\nint scrollbars_highlight_off(int id)\n{\n    Window sb_wid;\n    sb_wid =\n        (0 <= id && id < 4) ? tray_data.scrollbars_data.scrollbar[id] : None;\n    if (sb_wid != None)\n        XSetWindowBackgroundPixmap(tray_data.dpy, sb_wid, ParentRelative);\n    scrollbars_refresh(1);\n    return SUCCESS;\n}\n"
  },
  {
    "path": "src/scrollbars.h",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * tray.h\n * Mon, 09 Mar 2009 12:41:32 -0400\n * -------------------------------\n * Scrollbars data & interface\n * -------------------------------*/\n\n#ifndef _SCROLLBAR_H_\n#define _SCROLLBAR_H_\n\n#include <X11/X.h>\n#include <X11/Xlib.h>\n#include <X11/Xutil.h>\n\n#define SB_WND_TOP 0\n#define SB_WND_BOT 1\n#define SB_WND_LFT 2\n#define SB_WND_RHT 3\n#define SB_WND_MAX 4\n\n#define SB_MODE_NONE 0\n#define SB_MODE_VERT 1\n#define SB_MODE_HORZ 2\n\n#define SCROLLBAR_REPEAT_COUNTDOWN_MAX_1ST 10\n#define SCROLLBAR_REPEAT_COUNTDOWN_MAX_SUCC 5\n\n/* Scrollbar data */\nstruct ScrollbarsData {\n    struct Point scroll_base; /* Base scroll position */\n    struct Point scroll_pos; /* Current scroll position */\n    Window scrollbar[SB_WND_MAX]; /* Window IDs of scrollbars */\n    XSizeHints\n        scrollbar_xsh[SB_WND_MAX]; /* Cached window sizes for scrollbars */\n    int scrollbar_down; /* Click state */\n    int scrollbar_highlighted; /* Highlight state */\n    int scrollbar_repeat_active; /* If repeat is active */\n    int scrollbar_repeat_counter; /* Countown for repeat action */\n    int scrollbar_repeats_done; /* Numberf of executed repeat actions */\n};\n\n/* Initialize data structures */\nvoid scrollbars_init();\n/* Create scrollbars windows */\nvoid scrollbars_create();\n/* Update positions of scrollbars */\nint scrollbars_update();\n/* Refresh scrollbars */\nint scrollbars_refresh(int exposures);\n/* Get scrollbar under given coords */\nint scrollbars_get_id(Window wid, int x, int y);\n/* Update tray wrt scrollbar click;\n *  - id == SB_WND_MAX is a special case used to\n *    update scroll positions without chaning it\n *    (i.e. after icon removal)\n */\nint scrollbars_click(int id);\n/* Event handler for scrollbars */\nvoid scrollbars_handle_event(XEvent ev);\n/* Perform periodic tasks for scrollbars */\nvoid scrollbars_periodic_tasks();\n/* Scroll to icon */\nint scrollbars_scroll_to(struct TrayIcon *ti);\n/* Highlight scrollbar */\nint scrollbars_highlight_on(int id);\n/* Switch hightlight off */\nint scrollbars_highlight_off(int id);\n#endif\n"
  },
  {
    "path": "src/settings.c",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * settings.c\n * Sun, 12 Sep 2004 18:55:53 +0700\n * -------------------------------\n * settings parser\\container\n * -------------------------------*/\n\n#include <X11/X.h>\n#include <X11/Xlib.h>\n#include <X11/Xutil.h>\n\n#include <assert.h>\n#include <ctype.h>\n#include <errno.h>\n#include <libgen.h>\n#include <limits.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include \"common.h\"\n#include \"debug.h\"\n#include \"layout.h\"\n#include \"list.h\"\n#include \"settings.h\"\n#include \"tray.h\"\n\n#include \"wmh.h\"\n#include \"xutils.h\"\n\n/* Here we keep our filthy settings */\nstruct Settings settings;\n/* Initialize data */\nvoid init_default_settings(void)\n{\n    settings.bg_color_str = \"gray\";\n    settings.tint_color_str = \"white\";\n    settings.scrollbars_highlight_color_str = \"white\";\n    settings.display_str = NULL;\n#ifdef DEBUG\n    settings.log_level = LOG_LEVEL_ERR;\n#endif\n    settings.geometry_str = NULL;\n    settings.max_geometry_str = \"0x0\";\n    settings.icon_size = FALLBACK_ICON_SIZE;\n    settings.slot_size.x = -1;\n    settings.slot_size.y = -1;\n    settings.deco_flags = DECO_NONE;\n    settings.max_tray_dims.x = 0;\n    settings.max_tray_dims.y = 0;\n    settings.parent_bg = 0;\n    settings.shrink_back_mode = 1;\n    settings.sticky = 1;\n    settings.skip_taskbar = 1;\n    settings.transparent = 0;\n    settings.vertical = 0;\n    settings.grow_gravity = GRAV_N | GRAV_W;\n    settings.icon_gravity = GRAV_N | GRAV_W;\n    settings.wnd_type = _NET_WM_WINDOW_TYPE_DOCK;\n    settings.wnd_layer = NULL;\n    settings.wnd_name = PROGNAME;\n    settings.xsync = 0;\n    settings.need_help = 0;\n    settings.config_fname = NULL;\n    settings.full_pmt_search = 1;\n    settings.min_space_policy = 0;\n    settings.pixmap_bg = 0;\n    settings.bg_pmap_path = NULL;\n    settings.tint_level = 0;\n    settings.fuzzy_edges = 0;\n    settings.dockapp_mode = DOCKAPP_NONE;\n    settings.scrollbars_size = -1;\n    settings.scrollbars_mode = SB_MODE_NONE;\n    settings.scrollbars_inc = -1;\n    settings.wm_strut_mode = WM_STRUT_AUTO;\n    settings.kludge_flags = 0;\n    settings.remote_click_name = NULL;\n    settings.remote_click_btn = REMOTE_CLICK_BTN_DEFAULT;\n    settings.remote_click_cnt = REMOTE_CLICK_CNT_DEFAULT;\n    settings.remote_click_pos.x = REMOTE_CLICK_POS_DEFAULT;\n    settings.remote_click_pos.y = REMOTE_CLICK_POS_DEFAULT;\n    settings.ignored_classes = NULL;\n    settings.scroll_everywhere = 0;\n#ifdef DELAY_EMBEDDING_CONFIRMATION\n    settings.confirmation_delay = 3;\n#endif\n\n#ifdef _ST_WITH_XINERAMA\n    settings.monitor = 0;\n#endif\n}\n\n/* ******* general parsing utils ********* */\n\n#define PARSING_ERROR(msg, str) \\\n    if (!silent) LOG_ERROR((\"Parsing error: \" msg \", \\\"%s\\\" found\\n\", str));\n\n/* Parse highlight color */\nint parse_scrollbars_highlight_color(int, const char **argv, void **references, int)\n{\n    char **highlight_color = (char **) references[0];\n\n    if (!strcasecmp(argv[0], \"disable\"))\n        *highlight_color = NULL;\n    else if ((*highlight_color = strdup(argv[0])) == NULL)\n        DIE_OOM((\"Could not copy value from parameter\\n\"));\n\n    return SUCCESS;\n}\n\n/* Parse log level */\nint parse_log_level(int, const char **argv, void **references, int silent)\n{\n    int *log_level = (int *) references[0];\n\n    if (!strcmp(argv[0], \"err\"))\n        *log_level = LOG_LEVEL_ERR;\n    else if (!strcmp(argv[0], \"info\"))\n        *log_level = LOG_LEVEL_INFO;\n    else if (!strcmp(argv[0], \"trace\"))\n        *log_level = LOG_LEVEL_TRACE;\n    else {\n        PARSING_ERROR(\"err, info, or trace expected\", argv[0]);\n        return FAILURE;\n    }\n    return SUCCESS;\n}\n\n/* Parse list of ignored window classes */\nint parse_ignored_classes(int argc, const char **argv, void **references, int)\n{\n    struct WindowClass **classes = (struct WindowClass **) references[0];\n    struct WindowClass *newclass = NULL;\n    int i;\n\n    for (i = 0; i < argc; i++) {\n        newclass = malloc(sizeof(struct WindowClass));\n        newclass->name = strdup(argv[i]);\n        LIST_ADD_ITEM(*classes, newclass);\n    }\n\n    return SUCCESS;\n}\n\n/* Parse dockapp mode */\nint parse_dockapp_mode(int, const char **argv, void **references, int silent)\n{\n    int *dockapp_mode = (int *) references[0];\n\n    if (!strcmp(argv[0], \"none\"))\n        *dockapp_mode = DOCKAPP_NONE;\n    else if (!strcmp(argv[0], \"simple\"))\n        *dockapp_mode = DOCKAPP_SIMPLE;\n    else if (!strcmp(argv[0], \"wmaker\"))\n        *dockapp_mode = DOCKAPP_WMAKER;\n    else {\n        PARSING_ERROR(\"none, simple, or wmaker expected\", argv[0]);\n        return FAILURE;\n    }\n    return SUCCESS;\n}\n\n/* Parse gravity string ORing resulting value\n * with current value of tgt */\nint parse_gravity(int, const char **argv, void **references, int silent)\n{\n    int *gravity = (int *) references[0];\n    const char *gravity_s = argv[0];\n    int parsed = 0;\n\n    if (strlen(gravity_s) > 2)\n        goto fail;\n\n    for (; *gravity_s; gravity_s++) {\n        switch (tolower(*gravity_s)) {\n            case 'n': parsed |= GRAV_N; break;\n            case 's': parsed |= GRAV_S; break;\n            case 'w': parsed |= GRAV_W; break;\n            case 'e': parsed |= GRAV_E; break;\n            default:\n                goto fail;\n        }\n    }\n\n    if ((parsed & GRAV_N && parsed & GRAV_S) || (parsed & GRAV_E && parsed & GRAV_W))\n        goto fail;\n\n    *gravity = parsed;\n\n    return SUCCESS;\n\nfail:\n    PARSING_ERROR(\"gravity expected\", gravity_s);\n    return FAILURE;\n}\n\n/* Parse integer string storing resulting value in tgt */\nint parse_int(int, const char **argv, void **references, int silent)\n{\n    int *parsed = (int *) references[0];\n    char *invalid;\n\n    *parsed = strtol(argv[0], &invalid, 0);\n\n    if (*invalid != '\\0') {\n        PARSING_ERROR(\"integer expected\", argv[0]);\n        return FAILURE;\n    }\n\n    return SUCCESS;\n}\n\n/* Parse kludges mode */\nint parse_kludges(int, const char **argv, void **references, int silent)\n{\n    const char *token = strtok((char *) argv[0], \",\");\n    int *kludges = (int *) references[0];\n\n    for (; token != NULL; token = strtok(NULL, \",\")) {\n        if (!strcasecmp(token, \"fix_window_pos\"))\n            *kludges |= KLUDGE_FIX_WND_POS;\n        else if (!strcasecmp(token, \"force_icons_size\"))\n            *kludges |= KLUDGE_FORCE_ICONS_SIZE;\n        else if (!strcasecmp(token, \"use_icons_hints\"))\n            *kludges |= KLUDGE_USE_ICONS_HINTS;\n        else {\n            PARSING_ERROR(\"kludge flag expected\", token);\n            return FAILURE;\n        }\n    }\n\n    return SUCCESS;\n}\n\n/* Parse strut mode */\nint parse_strut_mode(int, const char **argv, void **references, int silent)\n{\n    int *strut_mode = (int *) references[0];\n\n    if (!strcasecmp(argv[0], \"auto\"))\n        *strut_mode = WM_STRUT_AUTO;\n    else if (!strcasecmp(argv[0], \"top\"))\n        *strut_mode = WM_STRUT_TOP;\n    else if (!strcasecmp(argv[0], \"bottom\"))\n        *strut_mode = WM_STRUT_BOT;\n    else if (!strcasecmp(argv[0], \"left\"))\n        *strut_mode = WM_STRUT_LFT;\n    else if (!strcasecmp(argv[0], \"right\"))\n        *strut_mode = WM_STRUT_RHT;\n    else if (!strcasecmp(argv[0], \"none\"))\n        *strut_mode = WM_STRUT_NONE;\n    else {\n        PARSING_ERROR(\n            \"one of top, bottom, left, right, or auto expected\", argv[0]);\n        return FAILURE;\n    }\n\n    return SUCCESS;\n}\n\n/* Parse boolean string storing result in tgt*/\nint parse_bool(int, const char **argv, void **references, int silent)\n{\n    const char *true_str[] = {\"yes\", \"on\", \"true\", \"1\", NULL};\n    const char *false_str[] = {\"no\", \"off\", \"false\", \"0\", NULL};\n    int *boolean = (int *) references[0];\n\n    for (const char **s = true_str; *s; s++) {\n        if (!strcasecmp(argv[0], *s)) {\n            *boolean = True;\n            return SUCCESS;\n        }\n    }\n\n    for (const char **s = false_str; *s; s++) {\n        if (!strcasecmp(argv[0], *s)) {\n            *boolean = False;\n            return SUCCESS;\n        }\n    }\n\n    PARSING_ERROR(\"boolean expected\", argv[0]);\n    return FAILURE;\n}\n\n/* Backwards version of the boolean parser */\nint parse_bool_rev(int argc, const char **argv, void **references, int silent)\n{\n    int *boolean = (int *) references[0];\n\n    if (parse_bool(argc, argv, references, silent)) {\n        *boolean = ! *boolean;\n        return SUCCESS;\n    }\n\n    return FAILURE;\n}\n\n/* Parse window layer string storing result in tgt */\nint parse_wnd_layer(int, const char **argv, void **references, int silent)\n{\n    char **window_layer = (char **) references[0];\n\n    if (!strcasecmp(argv[0], \"top\"))\n        *window_layer = _NET_WM_STATE_ABOVE;\n    else if (!strcasecmp(argv[0], \"bottom\"))\n        *window_layer = _NET_WM_STATE_BELOW;\n    else if (!strcasecmp(argv[0], \"normal\"))\n        *window_layer = NULL;\n    else {\n        PARSING_ERROR(\"window layer expected\", argv[0]);\n        return FAILURE;\n    }\n\n    return SUCCESS;\n}\n\n/* Parse window type string storing result in tgt */\nint parse_wnd_type(int, const char **argv, void **references, int silent)\n{\n    const char **window_type = (const char **) references[0];\n\n    if (!strcasecmp(argv[0], \"dock\"))\n        *window_type = _NET_WM_WINDOW_TYPE_DOCK;\n    else if (!strcasecmp(argv[0], \"toolbar\"))\n        *window_type = _NET_WM_WINDOW_TYPE_TOOLBAR;\n    else if (!strcasecmp(argv[0], \"utility\"))\n        *window_type = _NET_WM_WINDOW_TYPE_UTILITY;\n    else if (!strcasecmp(argv[0], \"normal\"))\n        *window_type = _NET_WM_WINDOW_TYPE_NORMAL;\n    else if (!strcasecmp(argv[0], \"desktop\"))\n        *window_type = _NET_WM_WINDOW_TYPE_DESKTOP;\n    else {\n        PARSING_ERROR(\"window type expected\", argv[0]);\n        return FAILURE;\n    }\n\n    return SUCCESS;\n}\n\n/* Just copy string from arg to *tgt */\nint parse_copystr(int, const char **argv, void **references, int)\n{\n    const char **stringref = (const char **) references[0];\n\n    /* Valgrind note: this memory will never be freed before stalonetray's exit. */\n    if ((*stringref = strdup(argv[0])) == NULL)\n        DIE_OOM((\"Could not copy value from parameter\\n\"));\n\n    return SUCCESS;\n}\n\n/* Parses window decoration specification */\nint parse_deco(int, const char **argv, void **references, int silent)\n{\n    int *decorations = (int *) references[0];\n    const char *arg = argv[0];\n\n    if (!strcasecmp(arg, \"none\"))\n        *decorations = DECO_NONE;\n    else if (!strcasecmp(arg, \"all\"))\n        *decorations = DECO_ALL;\n    else if (!strcasecmp(arg, \"border\"))\n        *decorations = DECO_BORDER;\n    else if (!strcasecmp(arg, \"title\"))\n        *decorations = DECO_TITLE;\n    else {\n        PARSING_ERROR(\"decoration specification expected\", arg);\n        return FAILURE;\n    }\n    return SUCCESS;\n}\n\n/* Parses window decoration specification */\nint parse_sb_mode(int, const char **argv, void **references, int silent)\n{\n    int *sb_mode = (int *) references[0];\n\n    if (!strcasecmp(argv[0], \"none\"))\n        *sb_mode = 0;\n    else if (!strcasecmp(argv[0], \"vertical\"))\n        *sb_mode = SB_MODE_VERT;\n    else if (!strcasecmp(argv[0], \"horizontal\"))\n        *sb_mode = SB_MODE_HORZ;\n    else if (!strcasecmp(argv[0], \"all\"))\n        *sb_mode = SB_MODE_HORZ | SB_MODE_VERT;\n    else {\n        PARSING_ERROR(\"scrollbars specification expected\", argv[0]);\n        return FAILURE;\n    }\n\n    return SUCCESS;\n}\n\n#if 0\n/* Parses remote op specification */\nint parse_remote(char *str, void **tgt, int silent)\n{\n#define NEXT_TOK(str, rest) \\\n    do { \\\n        (str) = (rest); \\\n        if ((str) != NULL) { \\\n            (rest) = strchr((str), ','); \\\n            if ((rest) != NULL) *((rest)++) = 0; \\\n        } \\\n    } while (0)\n#define PARSE_INT(tgt, str, tail, def, msg) \\\n    do { \\\n        if (str == NULL || *(str) == '\\0') { \\\n            (tgt) = def; \\\n        } else { \\\n            (tgt) = strtol((str), &(tail), 0); \\\n            if (*(tail) != '\\0') { \\\n                PARSING_ERROR(msg, (str)); \\\n                return FAILURE; \\\n            } \\\n        } \\\n    } while (0)\n\t/* Handy names for parameters */\n\tint *flag = (int *) tgt[0];\n\tchar **name = (char **) tgt[1];\n\tint *btn = (int *) tgt[2];\n\tstruct Point *pos = (struct Point *) tgt[3];\n\t/* Local variables */\n\tchar *rest = str, *tail;\n\tif (str == NULL || strlen(str) == 0) return FAILURE;\n\t*flag = 1;\n\tNEXT_TOK(str, rest);\n\t*name = strdup(str);\n\tNEXT_TOK(str, rest);\n\tPARSE_INT(*btn, str, tail, INT_MIN, \"remote click: button number expected\");\n\tNEXT_TOK(str, rest);\n\tPARSE_INT(pos->x, str, tail, INT_MIN, \"remote click: x coordinate expected\");\n\tNEXT_TOK(str, rest);\n\tPARSE_INT(pos->y, str, tail, INT_MIN, \"remote click: y coordinate expected\");\n\treturn SUCCESS;\n#undef NEXT_TOK\n#undef PARSE_INT\n}\n#endif\n\nint parse_remote_click_type(int, const char **argv, void **references, int silent)\n{\n    int *remote_click_type = (int *) references[0];\n\n    if (!strcasecmp(argv[0], \"single\"))\n        *remote_click_type = 1;\n    else if (!strcasecmp(argv[0], \"double\"))\n        *remote_click_type = 2;\n    else {\n        PARSING_ERROR(\"click type can be single or double\", argv[0]);\n        return FAILURE;\n    }\n\n    return SUCCESS;\n}\n\nint parse_pos(int, const char **argv, void **references, int)\n{\n    struct Point *pos = (struct Point *) references[0];\n    unsigned int dummy;\n    XParseGeometry(argv[0], &pos->x, &pos->y, &dummy, &dummy);\n    return SUCCESS;\n}\n\nint parse_size(int, const char **argv, void **references, int)\n{\n    struct Point *size = (struct Point *) references[0];\n    unsigned int width, height;\n    int bitmask, dummy;\n\n    bitmask = XParseGeometry(argv[0], &dummy, &dummy, &width, &height);\n\n    if (bitmask == 0 || bitmask & ~(WidthValue | HeightValue))\n        return FAILURE;\n    if ((bitmask & HeightValue) == 0)\n        height = width;\n\n    size->x = (width > (unsigned int) INT_MAX) ? INT_MAX : (int) width;\n    size->y = (height > (unsigned int) INT_MAX) ? INT_MAX : (int) height;\n\n    return SUCCESS;\n}\n\n/************ CLI **************/\n\n#define MAX_TARGETS 10\n#define MAX_DEFAULT_ARGS 10\n\n/* parameter parser function */\ntypedef int (*param_parser_t)(int argc, const char **argv, void **references, int silent);\n\nstruct Param {\n    const char *short_name;        /* short command line parameter name */\n    const char *long_name;         /* long command line parameter name */\n    const char *rc_name;           /* parameter name for config file */\n    void *references[MAX_TARGETS]; /* array of references necessary when parsing */\n\n    const int pass; /* 0th pass parameters are parsed before rc file, */\n                    /* 1st pass parameters are parsed after it */\n\n    const int min_argc; /* minimum number of expected arguments */\n    const int max_argc; /* maximum number of expected arguments, 0 for unlimited */\n\n    const int default_argc;                     /* number of default arguments, if present */\n    const char *default_argv[MAX_DEFAULT_ARGS]; /* default arguments if none are given */\n\n    param_parser_t parser;  /* pointer to parsing function */\n};\n\nstruct Param params[] = {\n    {\n        .short_name = \"-display\",\n        .long_name  = NULL,\n        .rc_name    = \"display\",\n        .references = { (void *) &settings.display_str },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_copystr\n    },\n    {\n        .short_name = NULL,\n        .long_name = \"--log-level\",\n        .rc_name = \"log_level\",\n        .references = { (void *) &settings.log_level },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_log_level\n    },\n    {\n        .short_name = \"-bg\",\n        .long_name = \"--background\",\n        .rc_name = \"background\",\n        .references = { (void *) &settings.bg_color_str },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_copystr\n    },\n    {\n        .short_name = \"-c\",\n        .long_name = \"--config\",\n        .rc_name = NULL,\n        .references = { (void *) &settings.config_fname },\n\n        .pass = 0,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_copystr\n    },\n    {\n        .short_name = \"-d\",\n        .long_name = \"--decorations\",\n        .rc_name = \"decorations\",\n        .references = { (void *) &settings.deco_flags },\n\n        .pass = 1,\n\n        .min_argc = 0,\n        .max_argc = 1,\n\n        .default_argc = 1,\n        .default_argv = { \"all\" },\n\n        .parser = (param_parser_t) &parse_deco\n    },\n    {\n        .short_name = NULL,\n        .long_name = \"--dockapp-mode\",\n        .rc_name = \"dockapp_mode\",\n        .references = { (void *) &settings.dockapp_mode },\n\n        .pass = 1,\n\n        .min_argc = 0,\n        .max_argc = 1,\n\n        .default_argc = 1,\n        .default_argv = { \"simple\" },\n\n        .parser = (param_parser_t) &parse_dockapp_mode\n    },\n    {\n        .short_name = \"-f\",\n        .long_name = \"--fuzzy-edges\",\n        .rc_name = \"fuzzy_edges\",\n        .references = { (void *) &settings.fuzzy_edges },\n\n        .pass = 1,\n\n        .min_argc = 0,\n        .max_argc = 1,\n\n        .default_argc = 1,\n        .default_argv = { \"2\" },\n\n        .parser = (param_parser_t) &parse_int\n    },\n    {\n        .short_name = \"-geometry\",\n        .long_name = \"--geometry\",\n        .rc_name = \"geometry\",\n        .references = { (void *) &settings.geometry_str },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_copystr\n    },\n    {\n        .short_name = NULL,\n        .long_name = \"--grow-gravity\",\n        .rc_name = \"grow_gravity\",\n        .references = { (void *) &settings.grow_gravity },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_gravity\n    },\n    {\n        .short_name = NULL,\n        .long_name = \"--icon-gravity\",\n        .rc_name = \"icon_gravity\",\n        .references = { (void *) &settings.icon_gravity },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_gravity\n    },\n    {\n        .short_name = \"-i\",\n        .long_name = \"--icon-size\",\n        .rc_name = \"icon_size\",\n        .references = { (void *) &settings.icon_size },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_int\n    },\n    {\n        .short_name = \"-h\",\n        .long_name = \"--help\",\n        .rc_name = NULL,\n        .references = { (void *) &settings.need_help },\n\n        .pass = 0,\n\n        .min_argc = 0,\n        .max_argc = 0,\n\n        .default_argc = 1,\n        .default_argv = {\"true\"},\n\n        .parser = (param_parser_t) &parse_bool\n    },\n    {\n        .short_name = NULL,\n        .long_name = \"--kludges\",\n        .rc_name = \"kludges\",\n        .references = { (void *) &settings.kludge_flags },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_kludges\n    },\n    {\n        .short_name = NULL,\n        .long_name = \"--max-geometry\",\n        .rc_name = \"max_geometry\",\n        .references = { (void *) &settings.max_geometry_str },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_copystr\n    },\n#ifdef _ST_WITH_XINERAMA\n    {\n        .short_name = \"-m\",\n        .long_name = \"--monitor\",\n        .rc_name = \"monitor\",\n        .references = { (void *) &settings.monitor },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_int\n    },\n#endif\n    {\n        .short_name = NULL,\n        .long_name = \"--no-shrink\",\n        .rc_name = \"no_shrink\",\n        .references = { (void *) &settings.shrink_back_mode },\n\n        .pass = 1,\n\n        .min_argc = 0,\n        .max_argc = 1,\n\n        .default_argc = 1,\n        .default_argv = {\"true\"},\n\n        .parser = (param_parser_t) &parse_bool_rev\n    },\n    {\n        .short_name = \"-p\",\n        .long_name = \"--parent-bg\",\n        .rc_name = \"parent_bg\",\n        .references = { (void *) &settings.parent_bg },\n\n        .pass = 1,\n\n        .min_argc = 0,\n        .max_argc = 1,\n\n        .default_argc = 1,\n        .default_argv = {\"true\"},\n\n        .parser = (param_parser_t) &parse_bool\n    },\n    {\n        .short_name = \"-r\",\n        .long_name = \"--remote-click-icon\",\n        .rc_name = NULL,\n        .references = { (void *) &settings.remote_click_name },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_copystr\n    },\n    {\n        .short_name = NULL,\n        .long_name = \"--remote-click-button\",\n        .rc_name = NULL,\n        .references = { (void *) &settings.remote_click_btn },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_int\n    },\n    {\n        .short_name = NULL,\n        .long_name = \"--remote-click-position\",\n        .rc_name = NULL,\n        .references = { (void *) &settings.remote_click_pos },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_pos\n    },\n    {\n        .short_name = NULL,\n        .long_name = \"--remote-click-type\",\n        .rc_name = NULL,\n        .references = { (void *) &settings.remote_click_cnt },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_remote_click_type\n    },\n    {\n        .short_name = NULL,\n        .long_name = \"--scrollbars\",\n        .rc_name = \"scrollbars\",\n        .references = { (void *) &settings.scrollbars_mode },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_sb_mode\n    },\n    {\n        .short_name = NULL,\n        .long_name = \"--scrollbars-highlight\",\n        .rc_name = \"scrollbars_highlight\",\n        .references = { (void *) &settings.scrollbars_highlight_color_str },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_scrollbars_highlight_color\n    },\n    {\n        .short_name = NULL,\n        .long_name = \"--scrollbars-step\",\n        .rc_name = \"scrollbars_step\",\n        .references = { (void *) &settings.scrollbars_inc },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_int\n    },\n    {\n        .short_name = NULL,\n        .long_name = \"--scrollbars-size\",\n        .rc_name = \"scrollbars_size\",\n        .references = { (void *) &settings.scrollbars_size },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_int\n    },\n    {\n        .short_name = NULL,\n        .long_name = \"--scroll-everywhere\",\n        .rc_name = \"scroll_everywhere\",\n        .references = { (void *) &settings.scroll_everywhere },\n\n        .pass = 1,\n\n        .min_argc = 0,\n        .max_argc = 1,\n\n        .default_argc = 1,\n        .default_argv = {\"true\"},\n\n        .parser = (param_parser_t) &parse_bool\n    },\n    {\n        .short_name = NULL,\n        .long_name = \"--skip-taskbar\",\n        .rc_name = \"skip_taskbar\",\n        .references = { (void *) &settings.skip_taskbar },\n\n        .pass = 1,\n\n        .min_argc = 0,\n        .max_argc = 1,\n\n        .default_argc = 1,\n        .default_argv = {\"true\"},\n\n        .parser = (param_parser_t) &parse_bool\n    },\n    {\n        .short_name = \"-s\",\n        .long_name = \"--slot-size\",\n        .rc_name = \"slot_size\",\n        .references = { (void *) &settings.slot_size },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_size\n    },\n    {\n        .short_name = NULL,\n        .long_name = \"--sticky\",\n        .rc_name = \"sticky\",\n        .references = { (void *) &settings.sticky },\n\n        .pass = 1,\n\n        .min_argc = 0,\n        .max_argc = 1,\n\n        .default_argc = 1,\n        .default_argv = {\"true\"},\n\n        .parser = (param_parser_t) &parse_bool\n    },\n    {\n        .short_name = NULL,\n        .long_name = \"--tint-color\",\n        .rc_name = \"tint_color\",\n        .references = { (void *) &settings.tint_color_str },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_copystr\n    },\n    {\n        .short_name = NULL,\n        .long_name = \"--tint-level\",\n        .rc_name = \"tint_level\",\n        .references = { (void *) &settings.tint_level },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_int\n    },\n    {\n        .short_name = \"-t\",\n        .long_name = \"--transparent\",\n        .rc_name = \"transparent\",\n        .references = { (void *) &settings.transparent },\n\n        .pass = 1,\n\n        .min_argc = 0,\n        .max_argc = 1,\n\n        .default_argc = 1,\n        .default_argv = {\"true\"},\n\n        .parser = (param_parser_t) &parse_bool\n    },\n    {\n        .short_name = \"-v\",\n        .long_name = \"--vertical\",\n        .rc_name = \"vertical\",\n        .references = { (void *) &settings.vertical },\n\n        .pass = 1,\n\n        .min_argc = 0,\n        .max_argc = 1,\n\n        .default_argc = 1,\n        .default_argv = {\"true\"},\n\n        .parser = (param_parser_t) &parse_bool\n    },\n    {\n        .short_name = NULL,\n        .long_name = \"--window-layer\",\n        .rc_name = \"window_layer\",\n        .references = { (void *) &settings.wnd_layer },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_wnd_layer\n    },\n    {\n        .short_name = NULL,\n        .long_name = \"--window-name\",\n        .rc_name = \"window_name\",\n        .references = { (void *) &settings.wnd_name },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_copystr\n    },\n    {\n        .short_name = NULL,\n        .long_name = \"--window-strut\",\n        .rc_name = \"window_strut\",\n        .references = { (void *) &settings.wm_strut_mode },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_strut_mode\n    },\n    {\n        .short_name = NULL,\n        .long_name = \"--window-type\",\n        .rc_name = \"window_type\",\n        .references = { (void *) &settings.wnd_type },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_wnd_type\n    },\n    {\n        .short_name = NULL,\n        .long_name = \"--xsync\",\n        .rc_name = \"xsync\",\n        .references = { (void *) &settings.xsync },\n\n        .pass = 1,\n\n        .min_argc = 0,\n        .max_argc = 1,\n\n        .default_argc = 1,\n        .default_argv = {\"true\"},\n\n        .parser = (param_parser_t) &parse_bool\n    },\n    {\n        .short_name = NULL,\n        .long_name = NULL,\n        .rc_name = \"ignore_classes\",\n        .references = { (void *) &settings.ignored_classes },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 0,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_ignored_classes\n    },\n\n#ifdef DELAY_EMBEDDING_CONFIRMATION\n    {\n        .short_name = NULL,\n        .long_name = \"--confirmation-delay\",\n        .rc_name = \"confirmation_delay\",\n        .references = { (void *) &settings.confirmation_delay },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_int\n    },\n#endif\n\n#ifdef _ST_WITH_XPM\n    {\n        .short_name = NULL,\n        .long_name = \"--pixmap-bg\",\n        .rc_name = \"pixmap_bg\",\n        .references = { (void *) &settings.bg_pmap_path },\n\n        .pass = 1,\n\n        .min_argc = 1,\n        .max_argc = 1,\n\n        .default_argc = 0,\n        .default_argv = NULL,\n\n        .parser = (param_parser_t) &parse_copystr\n    },\n#endif\n};\n\nvoid usage(char *progname)\n{\n    printf(\"\\nstalonetray \" VERSION \" [ \" FEATURE_LIST \" ]\\n\");\n    printf(\n        \"\\nUsage: %s [options...]\\n\"\n        \"\\n\"\n        \"For short options argument can be specified as -o value or -ovalue.\\n\"\n        \"For long options argument can be specified as --option value or\\n\"\n        \"--option=value. All flag-options have expilicit optional boolean \\n\"\n        \"argument, which can be true (1, yes) or false (0, no).\\n\"\n        \"\\n\", progname);\n    printf(\n        \"Possible options are:\\n\"\n        \"    -display <display>          use X display <display>\\n\"\n        \"    -bg, --background <color>   select background color (default: \"\n        \"#777777)\\n\"\n        \"    -c, --config <filename>     read configuration from <file>\\n\"\n        \"                                (instead of default \"\n        \"$HOME/.stalonetrayrc)\\n\"\n        \"    -d, --decorations <deco>    set what part of window decorations \"\n        \"are\\n\"\n        \"                                visible; deco can be: none \"\n        \"(default),\\n\"\n        \"                                title, border, all\\n\"\n        \"    --dockapp-mode [<mode>]     enable dockapp mode; mode can be \"\n        \"none (default),\\n\"\n        \"                                simple (default if no mode \"\n        \"specified), or wmaker\\n\"\n        \"    -f, --fuzzy-edges [<level>] set edges fuzziness level from\\n\"\n        \"                                0 (disabled) to 3 (maximum); works \"\n        \"with\\n\"\n        \"                                tinting and/or pixmap background;\\n\"\n        \"                                if not specified, level defaults to \"\n        \"2\\n\"\n        \"    [-]-geometry <geometry>     set initial tray`s geometry (width \"\n        \"and height\\n\"\n        \"                                are defined in icon slots; offsets \"\n        \"are defined\\n\"\n        \"                                in pixels)\\n\"\n        \"    --grow-gravity <gravity>    set tray`s grow gravity,\\n\"\n        \"                                either to N, S, W, E, NW, NE, SW, or \"\n        \"SE\\n\"\n        \"    --icon-gravity <gravity>    icon positioning gravity (NW, NE, \"\n        \"SW, SE)\\n\"\n        \"    -i, --icon-size <n>         set basic icon size to <n>; default \"\n        \"is 24\\n\"\n        \"   --ignore-classes <classes>   ignore tray icons in xembed if the \"\n        \"tray window has one of the given classes, separated by commas\\n\"\n        \"    -h, --help                  show this message\\n\"\n        );\n    printf(\n#ifdef DEBUG\n        \"    --log-level <level>         set the level of output to either \"\n        \"err\\n\"\n        \"                                (default), info, or trace\\n\"\n#else\n        \"    --log-level <level>         set the level of output to either \"\n        \"err\\n\"\n        \"                                (default), or info\\n\"\n#endif\n        \"    --kludges <list>            enable specific kludges to work \"\n        \"around\\n\"\n        \"                                non-conforming WMs and/or \"\n        \"stalonetray bugs;\\n\"\n        \"                                argument is a comma-separated list \"\n        \"of:\\n\"\n        \"                                 - fix_window_pos (fix window \"\n        \"position),\\n\"\n        \"                                 - force_icons_size (ignore icon \"\n        \"resizes),\\n\"\n        \"                                 - use_icons_hints (use icon size \"\n        \"hints)\\n\"\n        \"    --max-geometry <geometry>   set tray maximal width and height; 0 \"\n        \"indicates\\n\"\n        \"                                no limit in respective direction\\n\"\n        \"    -m, --monitor <monitor>     What monitor to display in\\n\"\n        \"    --no-shrink                 do not shrink window back after icon \"\n        \"removal\\n\"\n        \"    -p, --parent-bg             use parent for background\\n\"\n        \"    --pixmap-bg <pixmap>        use pixmap for tray`s window \"\n        \"background\\n\"\n        \"    -r, --remote-click-icon <name> remote control (assumes an \"\n        \"instance of\\n\"\n        \"                                stalonetray is already an active \"\n        \"tray on this\\n\"\n        \"                                screen); sends click to icon which \"\n        \"window's \\n\"\n        \"                                name is <name>\\n\"\n        \"    --remote-click-button <n>   defines mouse button for \"\n        \"--remote-click-icon\\n\"\n        \"    --remote-click-position <x>x<y> defines position for \"\n        \"--remote-click-icon\\n\"\n        \"    --remote-click-type <type>  defines click type for \"\n        \"--remote-click-icon;\\n\"\n        \"                                type can be either single, or \"\n        \"double\\n\"\n        \"    --scrollbars <mode>         set scrollbar mode either to all, \"\n        \"horizontal,\\n\"\n        \"                                vertical, or none\\n\"\n        \"    --scrollbars-highlight <mode> set scrollbar highlighting mode \"\n        \"which can\\n\"\n        \"                                be either color spec (default color \"\n        \"is red)\\n\"\n        \"                                or disable\\n\"\n        \"    --scrollbars-step <n>       set scrollbar step to n pixels\\n\"\n        \"    --scrollbars-size <n>       set scrollbar size to n pixels\\n\"\n        \"    --scroll-everywhere         enable scrolling outside of the scrollbars too\\n\"\n        \"    --slot-size <w>[x<h>]       set icon slot size in pixels\\n\"\n        \"                                if omitted, hight is set equal to width\\n\"\n        \"    --skip-taskbar              hide tray`s window from the taskbar\\n\"\n        \"    --sticky                    make tray`s window sticky across \"\n        \"multiple\\n\"\n        \"                                desktops/pages\\n\"\n        \"    -t, --transparent           enable root transparency\\n\"\n        \"    --tint-color <color>        tint tray background with color (not \"\n        \"used\\n\"\n        \"                                with plain color background)\\n\"\n        \"    --tint-level <level>        set tinting level from 0 to 255\\n\"\n        \"    -v, --vertical              use vertical layout of icons \"\n        \"(horizontal\\n\"\n        \"                                layout is used by default)\\n\"\n        \"    --window-layer <layer>      set tray`s window EWMH layer\\n\"\n        \"                                either to bottom, normal, or top\\n\"\n        \"    --window-strut <mode>       set window strut mode to either \"\n        \"auto,\\n\"\n        \"                                left, right, top, or bottom\\n\"\n        \"    --window-type <type>        set tray`s window EWMH type to \"\n        \"either\\n\"\n        \"                                normal, dock, toolbar, utility, \"\n        \"desktop\\n\"\n        \"    --xsync                     operate on X server synchronously \"\n        \"(SLOW)\\n\"\n        \"\\n\");\n}\n\n/* Parse command line parameters */\nint parse_cmdline(int argc, char **argv, int pass)\n{\n    struct Param *p, *match;\n    char *progname = argv[0];\n    const char **p_argv = NULL, *argbuf[MAX_DEFAULT_ARGS];\n    int p_argc;\n\n    while (--argc > 0) {\n        argv++;\n        match = NULL;\n        for (p = params; p->parser != NULL; p++) {\n            if (p->max_argc) {\n                if (p->short_name != NULL && strstr(*argv, p->short_name) == *argv) {\n                    if ((*argv)[strlen(p->short_name)] != '\\0') { /* accept arguments in the form -a5 */\n                        argbuf[0] = *argv + strlen(p->short_name);\n                        p_argc = 1;\n                        p_argv = argbuf;\n                    } else if (argc > 1 && argv[1][0] != '-') { /* accept arguments in the form -a 5, do not accept values starting with '-' */\n                        argbuf[0] = *(++argv);\n                        p_argc = 1;\n                        p_argv = argbuf;\n                        argc--;\n                    } else if (p->min_argc > 0) { /* argument is missing */\n                        LOG_ERROR((\"%s expects an argument\\n\", p->short_name));\n                        break;\n                    } else { /* argument is optional, use default value */\n                        p_argc = p->default_argc;\n                        p_argv = p->default_argv;\n                    }\n                } else if (p->long_name != NULL && strstr(*argv, p->long_name) == *argv) {\n                    if ((*argv)[strlen(p->long_name)] == '=') {/* accept arguments in the form --abcd=5 */\n                        argbuf[0] = *argv + strlen(p->long_name) + 1;\n                        p_argc = 1;\n                        p_argv = argbuf;\n                    } else if ((*argv)[strlen(p->long_name)] == '\\0') { /* accept arguments in the from --abcd 5 */\n                        if (argc > 1 && argv[1][0] != '-') { /* arguments cannot start with the dash */\n                            argbuf[0] = *(++argv);\n                            p_argc = 1;\n                            p_argv = argbuf;\n                            argc--;\n                        } else if (p->min_argc > 0) { /* argument is missing */\n                            LOG_ERROR((\"%s expects an argument\\n\", p->long_name));\n                            break;\n                        } else { /* argument is optional, use default value */\n                            p_argc = p->default_argc;\n                            p_argv = p->default_argv;\n                        }\n                    } else\n                        continue; /* just in case when there can be both --abc and --abcd */\n                } else\n                    continue;\n                match = p;\n                break;\n            } else if (strcmp(*argv, p->short_name) == 0 || strcmp(*argv, p->long_name) == 0) {\n                match = p;\n                p_argc = p->default_argc;\n                p_argv = p->default_argv;\n                break;\n            }\n        }\n\n#define USAGE_AND_DIE() \\\n    do { \\\n        usage(progname); \\\n        DIE((\"Could not parse command line\\n\")); \\\n    } while (0)\n\n        if (match == NULL) USAGE_AND_DIE();\n        if (match->pass != pass) continue;\n        if (p_argv == NULL) DIE_IE((\"Argument cannot be NULL!\\n\"));\n\n        LOG_TRACE((\"cmdline: pass %d, param \\\"%s\\\", args: [\", pass, match->long_name != NULL ? match->long_name : match->short_name));\n\n        for (int i = 0; i < p_argc - 1; i++)\n            LOG_TRACE((\"\\\"%s\\\", \", p_argv[i]));\n\n        LOG_TRACE((\"\\\"%s\\\"]\\n\", p_argv[p_argc - 1]));\n\n        if (!match->parser(p_argc, p_argv, match->references, match->min_argc == 0)) {\n            if (match->min_argc == 0) {\n                assert(p_argv != match->default_argv);\n                match->parser(match->default_argc, match->default_argv, match->references, False);\n                argc++;\n                argv--;\n            } else\n                USAGE_AND_DIE();\n        }\n    }\n\n    if (settings.need_help) {\n        usage(progname);\n        exit(0);\n    }\n\n    return SUCCESS;\n}\n\n/************ .stalonetrayrc ************/\n\n/**************************************************************************************\n * <line> ::= [<whitespaces>] [(<arg> <whitespaces>)* <arg>] [<whitespaces>]\n *<comment> <arg> ::= \"<arbitrary-text>\"|<text-without-spaces-and-#> <comment>\n *::= # <arbitrary-text>\n **************************************************************************************/\n\n/* Does exactly what its name says */\n#define SKIP_SPACES(p) \\\n    { \\\n        for (; *p != 0 && isspace((int)*p); p++) \\\n            ; \\\n    }\n/* Break the line in argc, argv pair */\nint get_args(char *line, int *argc, char ***argv)\n{\n    int q_flag = 0;\n    char *arg_start, *q_pos;\n    *argc = 0;\n    *argv = NULL;\n    /* 1. Strip leading spaces */\n    SKIP_SPACES(line);\n    if (0 == *line) { /* meaningless line */\n        return SUCCESS;\n    }\n    arg_start = line;\n    /* 2. Strip comments */\n    for (; 0 != *line; line++) {\n        q_flag = ('\"' == *line) ? !q_flag : q_flag;\n        if ('#' == *line && !q_flag) {\n            *line = 0;\n            break;\n        }\n    }\n    if (q_flag) { /* disbalance of quotes */\n        LOG_ERROR((\"Disbalance of quotes\\n\"));\n        return FAILURE;\n    }\n    if (arg_start == line) { /* meaningless line */\n        return SUCCESS;\n    }\n    line--;\n    /* 3. Strip trailing spaces */\n    for (; line != arg_start && isspace((int)*line); line--)\n        ;\n    if (arg_start == line) { /* meaningless line */\n        return FAILURE;\n    }\n    *(line + 1) = 0; /* this _is_ really ok since isspace(0) != 0 */\n    line = arg_start;\n    /* 4. Extract arguments */\n    do {\n        (*argc)++;\n        /* Add space to store one more argument */\n        if (NULL == (*argv = realloc(*argv, *argc * sizeof(char *))))\n            DIE_OOM((\"Could not allocate memory to parse parameters\\n\"));\n        if (*arg_start\n            == '\"') { /* 4.1. argument is quoted: find matching quote */\n            arg_start++;\n            (*argv)[*argc - 1] = arg_start;\n            if (NULL == (q_pos = strchr(arg_start, '\"'))) {\n                free(*argv);\n                DIE_IE((\"Quotes balance calculation failed\\n\"));\n                return FAILURE;\n            }\n            arg_start = q_pos;\n        } else { /* 4.2. whitespace-separated argument: find fist whitespace */\n            (*argv)[*argc - 1] = arg_start;\n            for (; 0 != *arg_start && !isspace((int)*arg_start); arg_start++)\n                ;\n        }\n        if (*arg_start != 0) {\n            *arg_start = 0;\n            arg_start++;\n            SKIP_SPACES(arg_start);\n        }\n    } while (*arg_start != 0);\n    return SUCCESS;\n}\n\nchar *find_rc(const char *path_part1, const char *path_part2, const char *rc)\n{\n    static char full_path[PATH_MAX];\n    int len;\n    struct stat statbuf;\n\n    if (path_part1 == NULL) return NULL;\n    if (path_part2 == NULL) {\n        LOG_TRACE((\"looking for config file in '%s/%s'\\n\", path_part1, rc));\n        len = snprintf(full_path, sizeof(full_path), \"%s/%s\", path_part1, rc);\n    } else {\n        LOG_TRACE((\"looking for config file in '%s/%s/%s'\\n\", path_part1,\n            path_part2, rc));\n        len = snprintf(full_path, sizeof(full_path), \"%s/%s/%s\", path_part1,\n            path_part2, rc);\n    }\n\n    if (len < 0 || (size_t) len >= sizeof(full_path)) return NULL;\n    if (stat(full_path, &statbuf) != 0) return NULL;\n\n    return full_path;\n}\n\n#define READ_BUF_SZ 512\n/* Parses rc file (path is either taken from settings.config_fname\n * or ~/.stalonetrayrc is used) */\nvoid parse_rc(void)\n{\n    FILE *cfg;\n\n    char buf[READ_BUF_SZ + 1];\n    int lnum = 0;\n\n    int argc, p_argc;\n    char **argv;\n    const char **p_argv;\n\n    struct Param *p, *match;\n\n    /* 1. Setup file name */\n    if (settings.config_fname == NULL)\n        settings.config_fname =\n            find_rc(getenv(\"HOME\"), NULL, \".\" STALONETRAY_RC);\n    if (settings.config_fname == NULL)\n        settings.config_fname =\n            find_rc(getenv(\"XDG_CONFIG_HOME\"), NULL, STALONETRAY_RC);\n    if (settings.config_fname == NULL)\n        settings.config_fname =\n            find_rc(getenv(\"HOME\"), \".config\", STALONETRAY_RC);\n    if (settings.config_fname == NULL) {\n        LOG_INFO((\"could not find any config files.\\n\"));\n        return;\n    }\n\n    LOG_INFO((\"using config file \\\"%s\\\"\\n\", settings.config_fname));\n\n    /* 2. Open file */\n    cfg = fopen(settings.config_fname, \"r\");\n    if (cfg == NULL) {\n        LOG_ERROR((\"could not open %s (%s)\\n\", settings.config_fname,\n            strerror(errno)));\n        return;\n    }\n\n    /* 3. Read the file line by line */\n    buf[READ_BUF_SZ] = 0;\n    while (!feof(cfg)) {\n        lnum++;\n        if (fgets(buf, READ_BUF_SZ, cfg) == NULL) {\n            if (ferror(cfg)) LOG_ERROR((\"read error (%s)\\n\", strerror(errno)));\n            break;\n        }\n\n        if (!get_args(buf, &argc, &argv)) {\n            DIE((\"Configuration file parse error at %s:%d: could not parse \"\n                 \"line\\n\",\n                settings.config_fname, lnum));\n        }\n        if (!argc) continue; /* This is empty/comment-only line */\n\n        match = NULL;\n        p_argc = argc - 1;\n        p_argv = (const char **) argv + 1;\n\n        for (p = params; p->parser != NULL; p++) {\n            if (p->rc_name != NULL && strcmp(argv[0], p->rc_name) == 0) {\n                if (argc - 1 < p->min_argc || (p->max_argc && argc - 1 > p->max_argc)) {\n                    DIE((\"Configuration file parse error at %s:%d: \"\n                         \"invalid number of args for \\\"%s\\\" (%d-%d required)\\n\",\n                        settings.config_fname, lnum,\n                        p->rc_name, p->min_argc, p->max_argc));\n                }\n\n                match = p;\n\n                if (p_argc == 0) {\n                    p_argc = match->default_argc;\n                    p_argv = match->default_argv;\n                }\n\n                break;\n            }\n        }\n\n        if (!match) {\n            DIE((\"Configuration file parse error at %s:%d: unrecognized rc \"\n                 \"file keyword \\\"%s\\\".\\n\",\n                settings.config_fname, lnum, argv[0]));\n        }\n\n        assert(p_argv != NULL);\n\n        LOG_TRACE((\"rc: param \\\"%s\\\", args [\\\"\", match->rc_name));\n\n        for (int i = 0; i < p_argc - 1; i++)\n            LOG_TRACE((\"\\\"%s\\\", \", p_argv[i]));\n\n        LOG_TRACE((\"\\\"%s\\\"]\\n\", p_argv[p_argc - 1]));\n\n        if (!match->parser(p_argc, p_argv, match->references, False)) {\n            DIE((\"Configuration file parse error at %s:%d: could not parse \"\n                 \"argument for \\\"%s\\\".\\n\",\n                settings.config_fname, lnum, argv[0]));\n        }\n\n        free(argv);\n    }\n\n    fclose(cfg);\n}\n\n/* Interpret all settings that need an open display or other settings */\nvoid interpret_settings(void)\n{\n    static int gravity_matrix[11] = {ForgetGravity, EastGravity, WestGravity,\n        ForgetGravity, SouthGravity, SouthEastGravity, SouthWestGravity,\n        ForgetGravity, NorthGravity, NorthEastGravity, NorthWestGravity};\n    int geom_flags;\n    int dummy;\n    XWindowAttributes root_wa;\n    /* Sanitize icon size */\n    val_range(settings.icon_size, MIN_ICON_SIZE, INT_MAX);\n    if (settings.slot_size.x < settings.icon_size)\n        settings.slot_size.x = settings.icon_size;\n    if (settings.slot_size.y < settings.icon_size)\n        settings.slot_size.y = settings.icon_size;\n    /* Sanitize scrollbar settings */\n    if (settings.scrollbars_mode != SB_MODE_NONE) {\n        val_range(settings.scrollbars_inc, settings.slot_size.x / 2, INT_MAX);\n        if (settings.scrollbars_size < 0)\n            settings.scrollbars_size = settings.slot_size.x / 4;\n    }\n    /* Sanitize all gravity strings */\n    settings.icon_gravity |= ((settings.icon_gravity & GRAV_V) ? 0 : GRAV_N);\n    settings.icon_gravity |= ((settings.icon_gravity & GRAV_H) ? 0 : GRAV_W);\n    settings.win_gravity = gravity_matrix[settings.grow_gravity];\n    settings.bit_gravity = gravity_matrix[settings.icon_gravity];\n    /* Parse all background-related settings */\n#ifdef _ST_WITH_XPM\n    settings.pixmap_bg = (settings.bg_pmap_path != NULL);\n#endif\n    if (settings.pixmap_bg) {\n        settings.parent_bg = False;\n        settings.transparent = False;\n    }\n    if (settings.transparent) settings.parent_bg = False;\n    /* Parse color-related settings */\n    if (!x11_parse_color(\n            tray_data.dpy, settings.bg_color_str, &settings.bg_color))\n        DIE((\"Could not parse background color \\\"%s\\\"\\n\",\n            settings.bg_color_str));\n    if (settings.scrollbars_highlight_color_str != NULL) {\n        if (!x11_parse_color(tray_data.dpy,\n                settings.scrollbars_highlight_color_str,\n                &settings.scrollbars_highlight_color)) {\n            DIE((\"Could not parse scrollbars highlight color \\\"%s\\\"\\n\",\n                settings.bg_color_str));\n        }\n    }\n    /* Sanitize tint level value */\n    val_range(settings.tint_level, 0, 255);\n    if (settings.tint_level) {\n        /* Parse tint color */\n        if (!x11_parse_color(\n                tray_data.dpy, settings.tint_color_str, &settings.tint_color))\n            DIE((\"Could not parse tint color \\\"%s\\\"\\n\",\n                settings.tint_color_str));\n    }\n    /* Sanitize edges fuzziness */\n    val_range(settings.fuzzy_edges, 0, 3);\n    /* Get dimensions of root window */\n    if (!XGetWindowAttributes(\n            tray_data.dpy, DefaultRootWindow(tray_data.dpy), &root_wa))\n        DIE((\"Could not get root window dimensions.\\n\"));\n    tray_data.root_wnd.x = 0;\n    tray_data.root_wnd.y = 0;\n    tray_data.root_wnd.width = root_wa.width;\n    tray_data.root_wnd.height = root_wa.height;\n    /* Parse geometry */\n#define DEFAULT_GEOMETRY \"1x1+0+0\"\n    tray_data.xsh.flags = PResizeInc | PBaseSize;\n    tray_data.xsh.x = 0;\n    tray_data.xsh.y = 0;\n    tray_data.xsh.width_inc = settings.slot_size.x;\n    tray_data.xsh.height_inc = settings.slot_size.y;\n    tray_data.xsh.base_width = 0;\n    tray_data.xsh.base_height = 0;\n    tray_calc_window_size(\n        0, 0, &tray_data.xsh.base_width, &tray_data.xsh.base_height);\n    geom_flags = XWMGeometry(tray_data.dpy, DefaultScreen(tray_data.dpy),\n        settings.geometry_str, DEFAULT_GEOMETRY, 0, &tray_data.xsh,\n        &tray_data.xsh.x, &tray_data.xsh.y, &tray_data.xsh.width,\n        &tray_data.xsh.height, &tray_data.xsh.win_gravity);\n    tray_data.xsh.win_gravity = settings.win_gravity;\n    tray_data.xsh.min_width = tray_data.xsh.width;\n    tray_data.xsh.min_height = tray_data.xsh.height;\n    tray_data.xsh.max_width = tray_data.xsh.width;\n    tray_data.xsh.min_height = tray_data.xsh.height;\n    tray_data.xsh.flags =\n        PResizeInc | PBaseSize | PMinSize | PMaxSize | PWinGravity;\n    tray_calc_tray_area_size(tray_data.xsh.width, tray_data.xsh.height,\n        &settings.orig_tray_dims.x, &settings.orig_tray_dims.y);\n    /* Dockapp mode */\n    if (settings.dockapp_mode == DOCKAPP_WMAKER)\n        tray_data.xsh.flags |= USPosition;\n    else {\n        if (geom_flags & (XValue | YValue))\n            tray_data.xsh.flags |= USPosition;\n        else\n            tray_data.xsh.flags |= PPosition;\n        if (geom_flags & (WidthValue | HeightValue))\n            tray_data.xsh.flags |= USSize;\n        else\n            tray_data.xsh.flags |= PSize;\n    }\n    LOG_TRACE((\"final geometry: %dx%d at (%d,%d)\\n\", tray_data.xsh.width,\n        tray_data.xsh.height, tray_data.xsh.x, tray_data.xsh.y));\n    if ((geom_flags & XNegative) && (geom_flags & YNegative))\n        settings.geom_gravity = SouthEastGravity;\n    else if (geom_flags & YNegative)\n        settings.geom_gravity = SouthWestGravity;\n    else if (geom_flags & XNegative)\n        settings.geom_gravity = NorthEastGravity;\n    else\n        settings.geom_gravity = NorthWestGravity;\n    /* Set tray maximal width/height */\n    geom_flags = XParseGeometry(settings.max_geometry_str, &dummy, &dummy,\n        (unsigned int *)&settings.max_tray_dims.x,\n        (unsigned int *)&settings.max_tray_dims.y);\n    LOG_TRACE((\"max geometry from max_geometry_str: %dx%d\\n\",\n        settings.max_tray_dims.x, settings.max_tray_dims.y));\n    if (!settings.max_tray_dims.x)\n        settings.max_tray_dims.x = root_wa.width;\n    else {\n        settings.max_tray_dims.x *= settings.slot_size.x;\n        val_range(\n            settings.max_tray_dims.x, settings.orig_tray_dims.x, INT_MAX);\n    }\n    if (!settings.max_tray_dims.y)\n        settings.max_tray_dims.y = root_wa.height;\n    else {\n        settings.max_tray_dims.y *= settings.slot_size.y;\n        val_range(\n            settings.max_tray_dims.y, settings.orig_tray_dims.y, INT_MAX);\n    }\n    LOG_TRACE((\"max geometry after normalization: %dx%d\\n\",\n        settings.max_tray_dims.x, settings.max_tray_dims.y));\n    /* XXX: this assumes certain degree of symmetry and in some point\n     * in the future this may not be the case... */\n    tray_calc_window_size(0, 0, &tray_data.scrollbars_data.scroll_base.x,\n        &tray_data.scrollbars_data.scroll_base.y);\n    tray_data.scrollbars_data.scroll_base.x /= 2;\n    tray_data.scrollbars_data.scroll_base.y /= 2;\n}\n\n/************** \"main\" ***********/\nint read_settings(int argc, char **argv)\n{\n    init_default_settings();\n    /* Parse 0th pass command line args */\n    parse_cmdline(argc, argv, 0);\n    /* Parse configuration files */\n    parse_rc();\n    /* Parse 1st pass command line args */\n    parse_cmdline(argc, argv, 1);\n    /* Display some settings */\n    LOG_TRACE((\"stalonetray \" VERSION \" [ \" FEATURE_LIST \" ]\\n\"));\n    LOG_TRACE((\"bg_color_str = \\\"%s\\\"\\n\", settings.bg_color_str));\n    LOG_TRACE((\"config_fname = \\\"%s\\\"\\n\", settings.config_fname));\n    LOG_TRACE((\"deco_flags = 0x%x\\n\", settings.deco_flags));\n    LOG_TRACE((\"display_str = \\\"%s\\\"\\n\", settings.display_str));\n    LOG_TRACE((\"dockapp_mode = %d\\n\", settings.dockapp_mode));\n    LOG_TRACE((\"full_pmt_search = %d\\n\", settings.full_pmt_search));\n    LOG_TRACE((\"geometry_str = \\\"%s\\\"\\n\", settings.geometry_str));\n    LOG_TRACE((\"grow_gravity = 0x%x\\n\", settings.grow_gravity));\n    LOG_TRACE((\"icon_gravity = 0x%x\\n\", settings.icon_gravity));\n    LOG_TRACE((\"icon_size = %d\\n\", settings.icon_size));\n    LOG_TRACE((\"log_level = %d\\n\", settings.log_level));\n    LOG_TRACE((\"max_tray_dims.x = %d\\n\", settings.max_tray_dims.x));\n    LOG_TRACE((\"max_tray_dims.y = %d\\n\", settings.max_tray_dims.y));\n    LOG_TRACE((\"min_space_policy = %d\\n\", settings.min_space_policy));\n    LOG_TRACE((\"need_help = %d\\n\", settings.need_help));\n    LOG_TRACE((\"parent_bg = %d\\n\", settings.parent_bg));\n    LOG_TRACE((\"scrollbars_highlight_color_str = \\\"%s\\\"\\n\",\n        settings.scrollbars_highlight_color_str));\n    LOG_TRACE((\"scrollbars_highlight_color.pixel = %ld\\n\",\n        settings.scrollbars_highlight_color.pixel));\n    LOG_TRACE((\"scrollbars_inc = %d\\n\", settings.scrollbars_inc));\n    LOG_TRACE((\"scrollbars_mode = %d\\n\", settings.scrollbars_mode));\n    LOG_TRACE((\"scrollbars_size = %d\\n\", settings.scrollbars_size));\n    LOG_TRACE((\"shrink_back_mode = %d\\n\", settings.shrink_back_mode));\n    LOG_TRACE((\"slot_size.x = %d\\n\", settings.slot_size.x));\n    LOG_TRACE((\"slot_size.y = %d\\n\", settings.slot_size.y));\n    LOG_TRACE((\"vertical = %d\\n\", settings.vertical));\n    LOG_TRACE((\"xsync = %d\\n\", settings.xsync));\n    return SUCCESS;\n}\n"
  },
  {
    "path": "src/settings.h",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * settings.h\n * Sun, 12 Sep 2004 18:06:08 +0700\n * -------------------------------\n * settings parser\\container\n * -------------------------------*/\n\n#ifndef _SETTINGS_H_\n#define _SETTINGS_H_\n\n#include <X11/X.h>\n#include <X11/Xlib.h>\n\n#include \"layout.h\"\n\n/* Default name of configuration file */\n#define STALONETRAY_RC \"stalonetrayrc\"\n\nstruct WindowClass {\n    char *name;\n    struct WindowClass *next;\n    struct WindowClass *prev;\n};\n\nstruct Settings {\n    /* Flags */\n    int parent_bg; /* Enable pseudo-transparency using parents' background*/\n    int deco_flags; /* Decoration flags */\n    int transparent; /* Enable root-transparency */\n    int skip_taskbar; /* Remove tray from wm`s taskbar */\n    int sticky; /* Make tray sticky across desktops/pages */\n    int xsync; /* Operate on X server syncronously */\n    int pixmap_bg; /* Is pixmap used for background */\n    int min_space_policy; /* Use placement that cause minimal grow */\n    int full_pmt_search; /* Use non-first-match search algorithm */\n    int vertical; /* Use vertical icon layout */\n    int shrink_back_mode; /* Keep tray's window size minimal */\n    int dockapp_mode; /* Activate dockapp mode */\n    int kludge_flags; /* What kludges to activate */\n    int scroll_everywhere; /* Whether scrolling is limited to the scrollbars only */\n\n    int need_help; /* Print usage and exit */\n\n    /* Lists of strings */\n    struct WindowClass *ignored_classes; /* List of window classes to ignore */\n\n    /* Strings */\n    char *display_str; /* Name of the display */\n    char *bg_color_str; /* Background color name */\n    char *scrollbars_highlight_color_str; /* Name of color to highlight\n                                             scrollbars with. NULL means\n                                             highlighting is disabled */\n    char *geometry_str; /* Geometry spec */\n    char *max_geometry_str; /* Geometry spec */\n    char *config_fname; /* Path to the configuration file */\n    char *wnd_type; /* Window type */\n    char *wnd_layer; /* Window layer */\n    char *wnd_name; /* Window name (WM_NAME) */\n    char *bg_pmap_path; /* Background pixmap path */\n    char *tint_color_str; /* Color used for tinting */\n    char *remote_click_name; /* Icon name to execute remote click on */\n\n    /* Values */\n    int icon_size; /* Icon size */\n    struct Point slot_size; /* Grid slot size */\n    int grow_gravity; /* Icon gravity (interpretation of icon_gravity_str) */\n    int icon_gravity; /* Grow gravity (interpretation of grow_gravity_str) */\n    int win_gravity; /* Tray window gravity (computed using grow gravity) */\n    int bit_gravity; /* Tray window bit gravity (computed using icon_gravity)\n                      */\n    int geom_gravity; /* Tray window gravity when mapping the window (computed\n                         using geometry_str) */\n    int fuzzy_edges; /* Level of edges fuzziness (0 = disabled) */\n    int tint_level; /* Tinting level (0 = disabled) */\n    int scrollbars_mode; /* SB_MODE_NONE | SB_MODE_VERT | SB_MODE_HORZ */\n    int scrollbars_size; /* Size of scrollbar windows in pixels */\n    int scrollbars_inc; /* Step of scrollbar */\n    int wm_strut_mode; /* WM strut mode */\n    struct Point max_tray_dims; /* Maximal tray dimensions */\n    struct Point max_layout_dims; /* Maximal layout dimensions */\n    struct Point orig_tray_dims; /* Original tray dimensions */\n    struct Point remote_click_pos; /* Remote click position */\n    int remote_click_btn; /* Remote click button */\n    int remote_click_cnt; /* Remote click count */\n\n    XColor tint_color; /* Color used for tinting */\n\n#ifdef DELAY_EMBEDDING_CONFIRMATION\n    int confirmation_delay;\n#endif\n\n    XColor bg_color; /* Tray background color */\n    XColor scrollbars_highlight_color; /* Color to highlight scrollbars with */\n    int log_level; /* Debug level */\n\n#ifdef _ST_WITH_XINERAMA\n    int monitor; /* Which monitor to display tray in */\n#endif\n};\n\nextern struct Settings settings;\n\n/* Read settings from cmd line and configuration file */\nint read_settings(int argc, char **argv);\n/* Interpret all settings that either need an\n * open display or are interpreted from other\n * settings */\nvoid interpret_settings();\n\n#endif\n"
  },
  {
    "path": "src/tray.c",
    "content": "/* ************************************\n * vim:tabstop=4:shiftwidth=4:cindent:fen:fdm=syntax\n * tray.c\n * Tue, 07 Mar 2006 10:36:10 +0600\n * ************************************\n * tray functions\n * ************************************/\n\n#include <X11/Xatom.h>\n#include <X11/Xlib.h>\n#include <X11/Xmd.h>\n#include <X11/Xutil.h>\n\n#ifdef _ST_WITH_XPM\n#include <X11/xpm.h>\n#endif\n\n#include <unistd.h>\n\n#include \"common.h\"\n#include \"embed.h\"\n#include \"icons.h\"\n#include \"image.h\"\n#include \"settings.h\"\n#include \"tray.h\"\n#include \"wmh.h\"\n#include \"xutils.h\"\n\n#ifdef _ST_WITH_NATIVE_KDE\n#endif\n\n#include \"debug.h\"\n\nvoid tray_init()\n{\n    tray_data.tray = None;\n    tray_data.hint_win = None;\n    tray_data.dpy = NULL;\n    tray_data.terminated = False;\n    tray_data.bg_pmap = None;\n    tray_data.xa_xrootpmap_id = None;\n    tray_data.xa_xsetroot_id = None;\n    tray_data.kde_tray_old_mode = 0;\n\n#ifdef _ST_WITH_XINERAMA\n    tray_data.xinerama_active = False;\n    tray_data.n_monitors = 0;\n    tray_data.monitors = NULL;\n#endif\n\n    scrollbars_init();\n}\n\n#ifdef _ST_WITH_XPM\nint tray_init_pixmap_bg()\n{\n    XpmAttributes xpma;\n    Pixmap mask = None;\n    int rc;\n    xpma.valuemask = 0;\n    rc = XpmReadFileToPixmap(tray_data.dpy, tray_data.tray,\n        settings.bg_pmap_path, &tray_data.bg_pmap, &mask, &xpma);\n    if (rc != XpmSuccess) {\n        DIE((\"Could not read background pixmap from %s.\\n\",\n            settings.bg_pmap_path));\n        return FAILURE;\n    }\n    /* Ignore the mask */\n    if (mask != None) XFreePixmap(tray_data.dpy, mask);\n    tray_data.bg_pmap_dims.x = xpma.height;\n    tray_data.bg_pmap_dims.y = xpma.width;\n    LOG_TRACE(\n        (\"created background pixmap (%dx%d)\\n\", xpma.width, xpma.height));\n    return SUCCESS;\n}\n#endif\n\n/* Mostly from FVWM:fvwm/colorset.c:172 */\nPixmap tray_get_root_pixmap(Atom prop)\n{\n    Atom type;\n    int format;\n    unsigned long length, after;\n    unsigned char *reteval = NULL;\n    int ret;\n    Pixmap pix = None;\n    Window root_window =\n        XRootWindow(tray_data.dpy, DefaultScreen(tray_data.dpy));\n    ret = XGetWindowProperty(tray_data.dpy, root_window, prop, 0L, 1L, False,\n        XA_PIXMAP, &type, &format, &length, &after, &reteval);\n    if (x11_ok() && ret == Success && type == XA_PIXMAP && format == 32\n        && length == 1 && after == 0)\n        pix = (Pixmap)(*(long *)reteval);\n    if (reteval) XFree(reteval);\n    return pix;\n}\n\n/* Originally from FVWM:fvwm/colorset.c:195 */\nint tray_update_root_bg_pmap(Pixmap *pmap, int *width, int *height)\n{\n    unsigned int w = 0, h = 0;\n    int rc = 0;\n    XID dummy;\n    Pixmap pix = None;\n    /* Retrive root image pixmap */\n    /* Try _XROOTPMAP_ID first */\n    if (tray_data.xa_xrootpmap_id != None)\n        pix = tray_get_root_pixmap(tray_data.xa_xrootpmap_id);\n    /* Else try _XSETROOT_ID */\n    if (!pix && tray_data.xa_xsetroot_id != None)\n        pix = tray_get_root_pixmap(tray_data.xa_xsetroot_id);\n    /* Get root pixmap size */\n    if (pix)\n        rc = XGetGeometry(tray_data.dpy, pix, &dummy, (int *)&dummy,\n            (int *)&dummy, &w, &h, (unsigned int *)&dummy,\n            (unsigned int *)&dummy);\n    if (!x11_ok() || !pix || !rc) {\n        LOG_TRACE((\"could not update root background pixmap\\n\"));\n        return FAILURE;\n    }\n    *pmap = pix;\n    if (width != NULL) *width = w;\n    if (height != NULL) *height = h;\n    LOG_TRACE((\"got new root pixmap: 0x%lx (%ix%i)\\n\", pix, w, h));\n    return SUCCESS;\n}\n\n/* XXX: most of Pixmaps are not really needed. Use XImages instead.\n * GCs, XImages and Pixmaps stay allocated during cleanup, valgrind\n * complains. Who cares?\n * TODO: move pixmaps/GCs into tray_data and free them during the exit. */\nint tray_update_bg(int update_pixmap)\n{\n    static Pixmap root_pmap = None;\n    static Pixmap bg_pmap = None;\n    static Pixmap final_pmap = None;\n    static GC root_gc = None;\n    static GC bg_gc = None;\n    static GC final_gc = None;\n    static int old_width = -1, old_height = -1;\n    struct Rect clr; /* clipping rectangle */\n    struct Rect clr_loc; /* clipping rectangle in local coords */\n    XImage *bg_img;\n    int bg_pmap_updated = False;\n#define make_gc(pmap, gc) \\\n    do { \\\n        XGCValues gcv; \\\n        if (gc != None) XFreeGC(tray_data.dpy, gc); \\\n        gcv.graphics_exposures = False; \\\n        gc = XCreateGC(tray_data.dpy, pmap, GCGraphicsExposures, &gcv); \\\n    } while (0)\n#define make_pmap(pmap) \\\n    do { \\\n        if (pmap != None) XFreePixmap(tray_data.dpy, pmap); \\\n        pmap = XCreatePixmap(tray_data.dpy, tray_data.tray, \\\n            tray_data.xsh.width, tray_data.xsh.height, \\\n            DefaultDepth(tray_data.dpy, DefaultScreen(tray_data.dpy))); \\\n    } while (0)\n#define recreate_pixmap(pmap, gc) \\\n    do { \\\n        make_pmap(pmap); \\\n        make_gc(pmap, gc); \\\n    } while (0)\n    /* No need to update background if it is a color one */\n    if (!settings.transparent && !settings.pixmap_bg) return SUCCESS;\n    /* Calculate clipping rect */\n    clr.x = cutoff(tray_data.xsh.x, 0, tray_data.root_wnd.width);\n    clr.y = cutoff(tray_data.xsh.y, 0, tray_data.root_wnd.height);\n    clr.w = cutoff(tray_data.xsh.x + tray_data.xsh.width, 0,\n                tray_data.root_wnd.width)\n        - clr.x;\n    clr.h = cutoff(tray_data.xsh.y + tray_data.xsh.height, 0,\n                tray_data.root_wnd.height)\n        - clr.y;\n    /* There's no need to update background if tray is not visible */\n    /* TODO: visibility is better to be tracked using some events */\n    if (clr.w == 0 || clr.h == 0) return SUCCESS;\n    /* Calculate local clipping rect */\n    clr_loc.x = clr.x - tray_data.xsh.x;\n    clr_loc.y = clr.y - tray_data.xsh.y;\n    clr_loc.w = clr.w;\n    clr_loc.h = clr.h;\n    if (old_width != tray_data.xsh.width || old_height != tray_data.xsh.height\n        || final_pmap == None)\n        recreate_pixmap(final_pmap, final_gc);\n    /* Update root pixmap if asked and necessary */\n    if ((root_pmap == None || update_pixmap)\n        && (settings.transparent || settings.fuzzy_edges)) {\n        if (!tray_update_root_bg_pmap(&root_pmap, NULL, NULL)) {\n            /* More gracefull solution */\n            LOG_TRACE((\"still waiting for root pixmap\\n\"));\n            return SUCCESS;\n        } else\n            make_gc(root_pmap, root_gc);\n    }\n    /* We don't have to update background if only window position has\n     * changed unless we depend on root pixmap */\n    if (tray_data.xsh.width == old_width && tray_data.xsh.width == old_height\n        && !settings.transparent && !settings.fuzzy_edges) {\n        return SUCCESS;\n    }\n    /* If pixmap bg is used, bg_pmap holds tinted and tiled background pixmap,\n     * so there's no need to update it unless window size has changed */\n    if (settings.pixmap_bg\n        && (bg_pmap == None || old_width != tray_data.xsh.width\n            || old_height != tray_data.xsh.height)) {\n        int i, j;\n        recreate_pixmap(bg_pmap, bg_gc);\n        for (i = 0; i < tray_data.xsh.width / tray_data.bg_pmap_dims.x + 1;\n             i++)\n            for (j = 0;\n                 j < tray_data.xsh.height / tray_data.bg_pmap_dims.y + 1;\n                 j++) {\n                XCopyArea(tray_data.dpy, tray_data.bg_pmap, bg_pmap, bg_gc, 0,\n                    0, tray_data.bg_pmap_dims.x, tray_data.bg_pmap_dims.y,\n                    i * tray_data.bg_pmap_dims.x,\n                    j * tray_data.bg_pmap_dims.y);\n            }\n        bg_pmap_updated = True;\n    } else\n        /* If root transparency is enabled, it is neccessary to copy portion of\n         * root pixmap under the window (root_pmap) to bg_pmap */\n        /* XXX: must correctly work around situations when bg pixmap is smaller\n           than root window (but how?) */\n        if (settings.transparent) {\n        recreate_pixmap(bg_pmap, bg_gc);\n        XCopyArea(tray_data.dpy, root_pmap, bg_pmap, bg_gc, tray_data.xsh.x,\n            tray_data.xsh.y, tray_data.xsh.width, tray_data.xsh.height, 0, 0);\n    }\n    /* Create an XImage from bg_pmap */\n    bg_img = XGetImage(tray_data.dpy, bg_pmap, 0, 0, tray_data.xsh.width,\n        tray_data.xsh.height, XAllPlanes(), ZPixmap);\n    if (bg_img == NULL) return FAILURE;\n    /* Tint the image if necessary. If bg_pmap was not updated, tinting\n     * is not needed, since it has been already done */\n    if (settings.tint_level\n        && ((settings.pixmap_bg && bg_pmap_updated) || settings.transparent)) {\n        image_tint(bg_img, &settings.tint_color, settings.tint_level);\n        XPutImage(tray_data.dpy, bg_pmap, bg_gc, bg_img, 0, 0, 0, 0,\n            tray_data.xsh.width, tray_data.xsh.height);\n        bg_pmap_updated = False;\n    }\n    /* Apply fuzzy edges */\n    /* XXX: THIS IS UGLY */\n    if (settings.fuzzy_edges) {\n        static CARD8 *alpha_mask = NULL;\n        /* Portion of root pixmap under the tray */\n        XImage *root_img = XGetImage(tray_data.dpy, root_pmap, clr.x, clr.y,\n            clr.w, clr.h, XAllPlanes(), ZPixmap);\n        /* Proxy structure to work on */\n        XImage *tmp_img = NULL;\n        static Pixmap tmp_pmap = None;\n        static GC tmp_gc = None;\n        if (root_img == NULL) {\n            LOG_ERROR(\n                (\"Failed to get image of root pixmap under tray window\\n\"));\n            LOG_TRACE((\"Clipping rectangle: %dx%d+%d+%d\\n\", clr.w, clr.h,\n                clr.x, clr.y));\n            return x11_ok();\n        }\n        /* Alpha mask needs to be updated only on size changes */\n        if (old_width != tray_data.xsh.width\n            || old_height != tray_data.xsh.height) {\n            recreate_pixmap(tmp_pmap, tmp_gc);\n            if (alpha_mask != NULL) free(alpha_mask);\n            alpha_mask = image_create_alpha_mask(settings.fuzzy_edges,\n                tray_data.xsh.width, tray_data.xsh.height);\n        }\n        XPutImage(tray_data.dpy, tmp_pmap, tmp_gc, root_img, clr_loc.x,\n            clr_loc.y, 0, 0, clr_loc.w, clr_loc.h);\n        tmp_img = XGetImage(tray_data.dpy, tmp_pmap, 0, 0, tray_data.xsh.width,\n            tray_data.xsh.height, XAllPlanes(), ZPixmap);\n        if (alpha_mask != NULL && tmp_img != NULL)\n            image_compose(bg_img, tmp_img, alpha_mask);\n        XDestroyImage(root_img);\n        if (tmp_img != NULL) XDestroyImage(tmp_img);\n    }\n    XPutImage(tray_data.dpy, final_pmap, final_gc, bg_img, 0, 0, 0, 0,\n        tray_data.xsh.width, tray_data.xsh.height);\n    XSetWindowBackgroundPixmap(tray_data.dpy, tray_data.tray, final_pmap);\n    XDestroyImage(bg_img);\n    old_width = tray_data.xsh.width;\n    old_height = tray_data.xsh.height;\n    RETURN_STATUS(x11_ok());\n}\n\nvoid tray_refresh_window(int exposures)\n{\n    LOG_TRACE((\"refreshing tray window\\n\"));\n    icon_list_forall(&embedder_refresh);\n    x11_refresh_window(tray_data.dpy, tray_data.tray, tray_data.xsh.width,\n        tray_data.xsh.height, exposures);\n    scrollbars_refresh(exposures);\n}\n\nint tray_calc_window_size(\n    int base_width, int base_height, int *wnd_width, int *wnd_height)\n{\n    *wnd_width = base_width;\n    *wnd_height = base_height;\n    if (settings.scrollbars_mode & SB_MODE_HORZ)\n        *wnd_width += settings.scrollbars_size * 2;\n    if (settings.scrollbars_mode & SB_MODE_VERT)\n        *wnd_height += settings.scrollbars_size * 2;\n    return SUCCESS;\n}\n\nint tray_calc_tray_area_size(\n    int wnd_width, int wnd_height, int *base_width, int *base_height)\n{\n    *base_width = wnd_width;\n    *base_height = wnd_height;\n    if (settings.scrollbars_mode & SB_MODE_HORZ)\n        *base_width -= settings.scrollbars_size * 2;\n    if (settings.scrollbars_mode & SB_MODE_VERT)\n        *base_height -= settings.scrollbars_size * 2;\n    return SUCCESS;\n}\n\nint tray_update_window_strut()\n{\n    /* Set window strut */\n    if (settings.wm_strut_mode != WM_STRUT_NONE) {\n        int strut_mode;\n        if (settings.wm_strut_mode == WM_STRUT_AUTO) {\n            /* Do autodetection: if some edge of tray is adjacent to one\n             * of screen edges, we could set window strut to that */\n            int h_strut_mode, v_strut_mode;\n            int p_strut_mode, s_strut_mode;\n            h_strut_mode =\n                (tray_data.xsh.x == 0 ? WM_STRUT_LFT\n                                      : (tray_data.xsh.x + tray_data.xsh.width\n                                                  == tray_data.root_wnd.width\n                                              ? WM_STRUT_RHT\n                                              : WM_STRUT_NONE));\n            v_strut_mode =\n                (tray_data.xsh.y == 0 ? WM_STRUT_TOP\n                                      : (tray_data.xsh.x + tray_data.xsh.height\n                                                  == tray_data.root_wnd.height\n                                              ? WM_STRUT_BOT\n                                              : WM_STRUT_NONE));\n            /* If tray is vertical, horizontal strut mode has higher priority,\n             * else vertical strut mode has higher priority */\n            if (settings.vertical) {\n                p_strut_mode = h_strut_mode;\n                s_strut_mode = v_strut_mode;\n            } else {\n                p_strut_mode = v_strut_mode;\n                s_strut_mode = h_strut_mode;\n            }\n            if (p_strut_mode != WM_STRUT_NONE)\n                strut_mode = p_strut_mode;\n            else\n                strut_mode = s_strut_mode;\n        } else\n            strut_mode = settings.wm_strut_mode;\n        LOG_TRACE((\"computed final strut mode: %d\\n\", strut_mode));\n        /* Update respective window hint */\n        if (strut_mode != WM_STRUT_NONE) {\n            wm_strut_t wm_strut;\n            int base_idx;\n            memset(wm_strut, 0, sizeof(wm_strut));\n            LOG_TRACE(\n                (\"current tray geometry: %dx%d+%d+%d\\n\", tray_data.xsh.width,\n                    tray_data.xsh.height, tray_data.xsh.x, tray_data.xsh.y));\n            if (strut_mode == WM_STRUT_TOP || strut_mode == WM_STRUT_BOT) {\n                if (strut_mode == WM_STRUT_TOP) {\n                    base_idx = WM_STRUT_IDX_TOP;\n                    wm_strut[WM_STRUT_IDX_TOP] =\n                        tray_data.xsh.y + tray_data.xsh.height;\n                } else {\n                    base_idx = WM_STRUT_IDX_BOT;\n                    wm_strut[WM_STRUT_IDX_BOT] =\n                        tray_data.root_wnd.height - tray_data.xsh.y;\n                }\n                wm_strut[WM_STRUT_IDX_START(base_idx)] = tray_data.xsh.x;\n                wm_strut[WM_STRUT_IDX_END(base_idx)] =\n                    tray_data.xsh.x + tray_data.xsh.width - 1;\n            } else {\n                if (strut_mode == WM_STRUT_LFT) {\n                    base_idx = WM_STRUT_IDX_LFT;\n                    wm_strut[WM_STRUT_IDX_LFT] =\n                        tray_data.xsh.x + tray_data.xsh.width;\n                } else {\n                    base_idx = WM_STRUT_IDX_RHT;\n                    wm_strut[WM_STRUT_IDX_RHT] =\n                        tray_data.root_wnd.width - tray_data.xsh.x;\n                }\n                wm_strut[WM_STRUT_IDX_START(base_idx)] = tray_data.xsh.y;\n                wm_strut[WM_STRUT_IDX_END(base_idx)] =\n                    tray_data.xsh.y + tray_data.xsh.height - 1;\n            }\n            {\n                int i;\n                for (i = 0; i < _NET_WM_STRUT_PARTIAL_SZ; i++)\n                    LOG_TRACE((\"computed hints [%d] = %lu\\n\", i, wm_strut[i]));\n            }\n            ewmh_set_window_strut(tray_data.dpy, tray_data.tray, wm_strut);\n        }\n    }\n    return SUCCESS;\n}\n\nint tray_update_window_props()\n{\n    XSizeHints xsh;\n    int cur_base_width, cur_base_height;\n    int new_width, new_height;\n    int layout_width, layout_height;\n    /* Phase 1: calculate new tray window dimensions.\n     * Algorithm summary:\n     * new_dims =\n     *      if (layout_dims > max_dims) max_dims;\n     * else if ((shrink_back && layout_dims > orig_dims) ||\n     *          (layout_dims > current_dims)) layout_dims;\n     * else if (shrink_back) orig_dims;\n     * else                  current_dims;\n     */\n    x11_get_window_size(tray_data.dpy, tray_data.tray, &tray_data.xsh.width,\n        &tray_data.xsh.height);\n    layout_get_size(&layout_width, &layout_height);\n    LOG_TRACE((\"layout geometry: %dx%d\\n\", layout_width, layout_height));\n    tray_calc_tray_area_size(tray_data.xsh.width, tray_data.xsh.height,\n        &cur_base_width, &cur_base_height);\n    LOG_TRACE(\n        (\"base tray geometry: %dx%d\\n\", cur_base_width, cur_base_height));\n    LOG_TRACE((\"orig tray geometry: %dx%d\\n\", settings.orig_tray_dims.x,\n        settings.orig_tray_dims.y));\n    LOG_TRACE((\"max tray geometry: %dx%d\\n\", settings.max_tray_dims.x,\n        settings.max_tray_dims.y));\n#define CALC_DIM(tgt, cur, layout, max, orig) \\\n    if (layout > max) \\\n        tgt = max; \\\n    else if ((settings.shrink_back_mode && layout > orig) || layout > cur) \\\n        tgt = layout; \\\n    else if (settings.shrink_back_mode) \\\n        tgt = orig; \\\n    else \\\n        tgt = cur;\n    CALC_DIM(new_width, cur_base_width, layout_width, settings.max_tray_dims.x,\n        settings.orig_tray_dims.x);\n    CALC_DIM(new_height, cur_base_height, layout_height,\n        settings.max_tray_dims.y, settings.orig_tray_dims.y);\n    LOG_TRACE((\"new base tray geometry: %dx%d\\n\", new_width, new_height));\n    tray_calc_window_size(new_width, new_height, &new_width, &new_height);\n    LOG_TRACE((\"new tray geometry: %dx%d\\n\", new_width, new_height));\n#if 1\n    /* Not sure if this is really necessary */\n    xsh.x = tray_data.xsh.x;\n    xsh.y = tray_data.xsh.y;\n    xsh.width = new_width;\n    xsh.height = new_height;\n#endif\n    xsh.min_width = new_width;\n    xsh.min_height = new_height;\n    xsh.max_width = new_width;\n    xsh.max_height = new_height;\n    xsh.width_inc = settings.slot_size.x;\n    xsh.height_inc = settings.slot_size.y;\n    tray_calc_window_size(0, 0, &xsh.base_width, &xsh.base_height);\n    xsh.win_gravity = NorthWestGravity;\n    xsh.flags = tray_data.xsh.flags;\n    XSetWMNormalHints(tray_data.dpy, tray_data.tray, &xsh);\n    /* Phase 2: set new window size\n     * This check helps to avod extra (erroneous) moves of the window when\n     * geometry changes are not updated yet, but tray_update_window_props() was\n     * called once again */\n    if (new_width != tray_data.xsh.width\n        || new_height != tray_data.xsh.height) {\n        /* Apparently, not every WM (hello, WindowMaker!) handles gravity the\n         * way I have expected (i.e. using it to calculate reference point as\n         * described in ICCM/WM specs). Perhaps, I was dreaming.  So, prior to\n         * resizing trays window, it is necessary to recalculate window\n         * absolute position and shift it according to grow gravity settings */\n        x11_get_window_abs_coords(\n            tray_data.dpy, tray_data.tray, &tray_data.xsh.x, &tray_data.xsh.y);\n        LOG_TRACE((\"old tray window geometry: %dx%d+%d+%d\\n\", new_width,\n            new_height, tray_data.xsh.x, tray_data.xsh.y));\n        if (settings.grow_gravity & GRAV_E)\n            tray_data.xsh.x -= new_width - tray_data.xsh.width;\n        else if (!(settings.grow_gravity & GRAV_H))\n            tray_data.xsh.x -= (new_width - tray_data.xsh.width) / 2;\n        if (settings.grow_gravity & GRAV_S)\n            tray_data.xsh.y -= new_height - tray_data.xsh.height;\n        else if (!(settings.grow_gravity & GRAV_V))\n            tray_data.xsh.y -= (new_height - tray_data.xsh.height) / 2;\n        tray_data.xsh.width = new_width;\n        tray_data.xsh.height = new_height;\n        LOG_TRACE((\"new tray window geometry: %dx%d+%d+%d\\n\", new_width,\n            new_height, tray_data.xsh.x, tray_data.xsh.y));\n        XResizeWindow(tray_data.dpy, tray_data.tray, new_width, new_height);\n        XMoveWindow(\n            tray_data.dpy, tray_data.tray, tray_data.xsh.x, tray_data.xsh.y);\n        if (!x11_ok()) {\n            LOG_TRACE((\"could not update tray window geometry\\n\"));\n            return FAILURE;\n        }\n    } else {\n        /* XXX: Why do we need this again? */\n        XResizeWindow(tray_data.dpy, tray_data.tray, tray_data.xsh.width,\n            tray_data.xsh.height);\n    }\n    tray_update_window_strut();\n    scrollbars_update();\n    return SUCCESS;\n}\n\nvoid tray_create_window(int argc, char **argv)\n{\n    XTextProperty wm_name;\n    XSetWindowAttributes xswa;\n    XClassHint xch;\n    XWMHints xwmh;\n    Atom net_system_tray_orientation;\n    Atom orient;\n    Atom protocols_atoms[3];\n    /* Create some atoms */\n    tray_data.xa_wm_delete_window =\n        XInternAtom(tray_data.dpy, \"WM_DELETE_WINDOW\", False);\n    tray_data.xa_net_wm_ping =\n        XInternAtom(tray_data.dpy, \"_NET_WM_PING\", False);\n    tray_data.xa_wm_take_focus =\n        XInternAtom(tray_data.dpy, \"WM_TAKE_FOCUS\", False);\n    tray_data.xa_wm_protocols =\n        XInternAtom(tray_data.dpy, \"WM_PROTOCOLS\", False);\n    tray_data.xa_kde_net_system_tray_windows =\n        XInternAtom(tray_data.dpy, \"_KDE_NET_SYSTEM_TRAY_WINDOWS\", False);\n    tray_data.xa_net_client_list =\n        XInternAtom(tray_data.dpy, \"_NET_CLIENT_LIST\", False);\n    /* Create tray window */\n    tray_data.tray = XCreateSimpleWindow(tray_data.dpy,\n        DefaultRootWindow(tray_data.dpy), tray_data.xsh.x, tray_data.xsh.y,\n        tray_data.xsh.width, tray_data.xsh.height, 0, settings.bg_color.pixel,\n        settings.bg_color.pixel);\n    LOG_TRACE((\"created tray window: 0x%lx\\n\", tray_data.tray));\n    if (settings.dockapp_mode == DOCKAPP_WMAKER) {\n        tray_data.hint_win = XCreateSimpleWindow(tray_data.dpy,\n            DefaultRootWindow(tray_data.dpy), 0, 0, 1, 1, 0,\n            settings.bg_color.pixel, settings.bg_color.pixel);\n        LOG_TRACE((\"created hint_win window: 0x%lx\\n\", tray_data.hint_win));\n    }\n    /* Set tray window properties */\n    xswa.bit_gravity = settings.bit_gravity;\n    xswa.win_gravity = settings.win_gravity;\n    xswa.backing_store = settings.parent_bg ? NotUseful : WhenMapped;\n    XChangeWindowAttributes(tray_data.dpy, tray_data.tray,\n        CWBitGravity | CWWinGravity | CWBackingStore, &xswa);\n    {\n        /* XXX: use XStoreName ?*/\n        int numtries = 0;\n        /* First, try user-supplied value */\n        while (1) {\n            if (XmbTextListToTextProperty(\n                    tray_data.dpy, &settings.wnd_name, 1, XTextStyle, &wm_name)\n                != Success)\n                /* Retry with default value */\n                settings.wnd_name = PROGNAME;\n            else\n                break;\n            if (numtries++ > 1)\n                DIE((\"Invalid window name \\\"%s\\\"\\n\", settings.wnd_name));\n        }\n    }\n    XSetWMName(tray_data.dpy, tray_data.tray, &wm_name);\n    XFree(wm_name.value);\n    /* Setup class hints */\n    xch.res_class = PROGNAME;\n    xch.res_name = PROGNAME;\n    /* Setup window manager hints */\n    xwmh.flags = StateHint | InputHint;\n    xwmh.input = False;\n    xwmh.initial_state =\n        settings.dockapp_mode != DOCKAPP_NONE ? WithdrawnState : NormalState;\n    /* Apply hints */\n    XSetWMHints(tray_data.dpy, tray_data.tray, &xwmh);\n    XSetClassHint(tray_data.dpy, tray_data.tray, &xch);\n    XSetWMNormalHints(tray_data.dpy, tray_data.tray, &tray_data.xsh);\n    XSetCommand(tray_data.dpy, tray_data.tray, argv, argc);\n    /* Set properties of hint window if WindowMaker dockapp mode enabled */\n    if (settings.dockapp_mode == DOCKAPP_WMAKER) {\n        xwmh.flags |= IconWindowHint | IconPositionHint | WindowGroupHint;\n        xwmh.initial_state = WithdrawnState;\n        xwmh.icon_x = tray_data.xsh.x;\n        xwmh.icon_y = tray_data.xsh.y;\n        xwmh.icon_window = tray_data.tray;\n        xwmh.window_group = tray_data.hint_win;\n        XSetClassHint(tray_data.dpy, tray_data.hint_win, &xch);\n        XSetWMHints(tray_data.dpy, tray_data.hint_win, &xwmh);\n    }\n    /* v0.2 tray protocol support */\n    orient = settings.vertical ? _NET_SYSTEM_TRAY_ORIENTATION_HORZ\n                               : _NET_SYSTEM_TRAY_ORIENTATION_VERT;\n    net_system_tray_orientation =\n        XInternAtom(tray_data.dpy, TRAY_ORIENTATION_ATOM, False);\n    XChangeProperty(tray_data.dpy, tray_data.tray, net_system_tray_orientation,\n        net_system_tray_orientation, 32, PropModeReplace,\n        (unsigned char *)&orient, 1);\n    /* Ask X server / WM to report certain events */\n    protocols_atoms[0] = tray_data.xa_wm_delete_window;\n    protocols_atoms[1] = tray_data.xa_wm_take_focus;\n    protocols_atoms[2] = tray_data.xa_net_wm_ping;\n    XSetWMProtocols(tray_data.dpy, tray_data.tray, protocols_atoms, 3);\n    XSelectInput(tray_data.dpy, tray_data.tray,\n        StructureNotifyMask | FocusChangeMask | PropertyChangeMask\n            | ExposureMask);\n    x11_extend_root_event_mask(tray_data.dpy, PropertyChangeMask);\n    scrollbars_create();\n    /* Set tray window background if necessary */\n#ifdef _ST_WITH_XPM\n    if (settings.pixmap_bg) tray_init_pixmap_bg();\n#endif\n    if (settings.parent_bg)\n        XSetWindowBackgroundPixmap(\n            tray_data.dpy, tray_data.tray, ParentRelative);\n    else if (settings.transparent || settings.fuzzy_edges) {\n        tray_data.xa_xrootpmap_id =\n            XInternAtom(tray_data.dpy, \"_XROOTPMAP_ID\", False);\n        tray_data.xa_xsetroot_id =\n            XInternAtom(tray_data.dpy, \"_XSETROOT_ID\", False);\n    }\n    tray_update_bg(True);\n}\n\nvoid tray_create_phony_window()\n{\n    /* Create fake tray window */\n    tray_data.tray = XCreateSimpleWindow(\n        tray_data.dpy, DefaultRootWindow(tray_data.dpy), 0, 0, 1, 1, 0, 0, 0);\n    /* Select for PropertyNotify so that x11_get_server_timestamp() works */\n    XSelectInput(tray_data.dpy, tray_data.tray, PropertyChangeMask);\n}\n\nint tray_set_wm_hints()\n{\n    int mwm_decor = 0;\n    if (settings.deco_flags & DECO_TITLE)\n        mwm_decor |= MWM_DECOR_TITLE | MWM_DECOR_MENU;\n    if (settings.deco_flags & DECO_BORDER)\n        mwm_decor |= MWM_DECOR_RESIZEH | MWM_DECOR_BORDER;\n    mwm_set_hints(tray_data.dpy, tray_data.tray, mwm_decor, MWM_FUNC_ALL);\n    if (settings.sticky) {\n        ewmh_add_window_state(\n            tray_data.dpy, tray_data.tray, _NET_WM_STATE_STICKY);\n        ewmh_set_window_atom32(\n            tray_data.dpy, tray_data.tray, _NET_WM_DESKTOP, 0xFFFFFFFF);\n    }\n    if (settings.skip_taskbar)\n        ewmh_add_window_state(\n            tray_data.dpy, tray_data.tray, _NET_WM_STATE_SKIP_TASKBAR);\n    if (settings.wnd_layer != NULL)\n        ewmh_add_window_state(\n            tray_data.dpy, tray_data.tray, settings.wnd_layer);\n    if (strcmp(settings.wnd_type, _NET_WM_WINDOW_TYPE_NORMAL) != 0)\n        ewmh_add_window_type(tray_data.dpy, tray_data.tray, settings.wnd_type);\n    /* Alwas add NORMAL window type for WM that do not support (some) special\n     * types */\n    ewmh_add_window_type(\n        tray_data.dpy, tray_data.tray, _NET_WM_WINDOW_TYPE_NORMAL);\n    return SUCCESS;\n}\n\nvoid tray_init_selection_atoms()\n{\n    static char *tray_sel_atom_name = NULL;\n    /* Obtain selection atom name basing on current screen number */\n    if (tray_sel_atom_name == NULL) {\n        tray_sel_atom_name = (char *)malloc(strlen(TRAY_SEL_ATOM) + 10);\n        if (tray_sel_atom_name == NULL)\n            DIE_OOM((\"could not allocate memory for selection atom name\\n\"));\n        snprintf(tray_sel_atom_name, strlen(TRAY_SEL_ATOM) + 10, \"%s%u\",\n            TRAY_SEL_ATOM, DefaultScreen(tray_data.dpy));\n    }\n    LOG_TRACE((\"tray_sel_atom_name=%s\\n\", tray_sel_atom_name));\n    /* Initialize atom values */\n    tray_data.xa_tray_selection =\n        XInternAtom(tray_data.dpy, tray_sel_atom_name, False);\n    tray_data.xa_tray_opcode =\n        XInternAtom(tray_data.dpy, \"_NET_SYSTEM_TRAY_OPCODE\", False);\n    tray_data.xa_tray_data =\n        XInternAtom(tray_data.dpy, \"_NET_SYSTEM_TRAY_MESSAGE_DATA\", False);\n}\n\nvoid tray_acquire_selection()\n{\n    Time timestamp = x11_get_server_timestamp(tray_data.dpy, tray_data.tray);\n    tray_init_selection_atoms();\n    /* Save old selection owner */\n    tray_data.old_selection_owner =\n        XGetSelectionOwner(tray_data.dpy, tray_data.xa_tray_selection);\n    LOG_TRACE((\"old selection owner: 0x%lx\\n\", tray_data.old_selection_owner));\n    /* Acquire selection */\n    XSetSelectionOwner(\n        tray_data.dpy, tray_data.xa_tray_selection, tray_data.tray, timestamp);\n    /* Check if we have really got the selection */\n    if (XGetSelectionOwner(tray_data.dpy, tray_data.xa_tray_selection)\n        != tray_data.tray) {\n        DIE((\"could not set selection owner.\\nMay be another (greedy) tray \"\n             \"running?\\n\"));\n    } else {\n        tray_data.is_active = True;\n        LOG_TRACE((\"ok, got _NET_SYSTEM_TRAY selection\\n\"));\n    }\n    /* Send the message notifying about new MANAGER */\n    x11_send_client_msg32(tray_data.dpy, DefaultRootWindow(tray_data.dpy),\n        DefaultRootWindow(tray_data.dpy),\n        XInternAtom(tray_data.dpy, \"MANAGER\", False), timestamp,\n        tray_data.xa_tray_selection, tray_data.tray, 0, 0);\n}\n\nvoid tray_show_window()\n{\n    tray_set_wm_hints();\n    tray_update_window_props();\n    XMapRaised(tray_data.dpy, tray_data.tray);\n    if (settings.dockapp_mode == DOCKAPP_NONE)\n        XMoveWindow(\n            tray_data.dpy, tray_data.tray, tray_data.xsh.x, tray_data.xsh.y);\n    if (settings.dockapp_mode == DOCKAPP_WMAKER)\n        XMapWindow(tray_data.dpy, tray_data.hint_win);\n    /* XXX: I do not why, but for some WM it is\n     * required to set hints / window properties\n     * after and before window creation */\n    /* TODO: check if this is really necessary */\n    tray_set_wm_hints();\n    tray_update_window_props();\n}\n"
  },
  {
    "path": "src/tray.h",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * tray.h\n * Wed, 29 Sep 2004 23:10:02 +0700\n * -------------------------------\n * Common tray routines\n * -------------------------------*/\n\n#ifndef _TRAY_H_\n#define _TRAY_H_\n\n#include <X11/X.h>\n#include <X11/Xlib.h>\n#include <X11/Xutil.h>\n\n#include <limits.h>\n\n#include \"common.h\"\n#include \"icons.h\"\n#include \"scrollbars.h\"\n#include \"xembed.h\"\n\n#ifdef _ST_WITH_XINERAMA\n#include <X11/extensions/Xinerama.h>\n#endif\n\n/* Tray opcode messages from System Tray Protocol Specification\n * http:freedesktop.org/Standards/systemtray-spec/systemtray-spec-0.2.html */\n#define SYSTEM_TRAY_REQUEST_DOCK 0\n/* These two are unused */\n#define SYSTEM_TRAY_BEGIN_MESSAGE 1\n#define SYSTEM_TRAY_CANCEL_MESSAGE 2\n/* Custom message: remote control */\n#define STALONE_TRAY_REMOTE_CONTROL 0xFFFD\n/* Custom message: request for status */\n#define STALONE_TRAY_STATUS_REQUESTED 0xFFFE\n/* Custom message: confirmation of embedding */\n#define STALONE_TRAY_DOCK_CONFIRMED 0xFFFF\n/* Name of tray selection atom */\n#define TRAY_SEL_ATOM \"_NET_SYSTEM_TRAY_S\"\n/* Name of tray orientation atom*/\n#define TRAY_ORIENTATION_ATOM \"_NET_SYSTEM_TRAY_ORIENTATION\"\n/* Name of tray orientation atom*/\n#define STALONETRAY_REMOTE_ATOM \"STALONETRAY_REMOTE\"\n/* Values of tray orientation property */\n#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0\n#define _NET_SYSTEM_TRAY_ORIENTATION_VERT 1\n\n/* Window decoration flags */\n#define DECO_BORDER (1 << 0)\n#define DECO_TITLE (1 << 1)\n#define DECO_NONE 0\n#define DECO_ALL (DECO_BORDER | DECO_TITLE)\n\n/* WM struts flags */\n#define WM_STRUT_NONE 0\n#define WM_STRUT_LFT 1\n#define WM_STRUT_RHT 2\n#define WM_STRUT_TOP 3\n#define WM_STRUT_BOT 4\n#define WM_STRUT_AUTO 5\n\n/* Dockapp modes */\n#define DOCKAPP_NONE 0\n#define DOCKAPP_SIMPLE 1\n#define DOCKAPP_WMAKER 2\n\n/* Kludge flags */\n#define KLUDGE_FIX_WND_SIZE (1L << 1)\n#define KLUDGE_FIX_WND_POS (1L << 2)\n#define KLUDGE_USE_ICONS_HINTS (1L << 3)\n#define KLUDGE_FORCE_ICONS_SIZE (1L << 4)\n\n/* Remote click constants */\n#define REMOTE_CLICK_BTN_DEFAULT Button1\n#define REMOTE_CLICK_POS_DEFAULT INT_MAX\n#define REMOTE_CLICK_CNT_DEFAULT 1\n\n/* Structure to hold all tray data */\nstruct TrayData {\n    /* General */\n    Window tray; /* ID of tray window */\n    Window hint_win; /* ID of icon window */\n    Display *dpy; /* Display pointer */\n    XSizeHints xsh; /* Size & position of the tray window */\n    XSizeHints root_wnd; /* Size & position :) of the root window */\n    Window old_selection_owner; /* Old owner of tray selection */\n    int terminated; /* Exit flag */\n    int is_active; /* Is the tray active? */\n    int is_reparented; /* Was the tray reparented in smth like FvwmButtons ? */\n    int kde_tray_old_mode; /* Use legacy scheme to handle KDE icons via\n                              MapNotify */\n\n    /* Atoms */\n    Atom xa_tray_selection; /* Atom: _NET_SYSTEM_TRAY_SELECTION_S<creen number>\n                             */\n    Atom xa_tray_opcode; /* Atom: _NET_SYSTEM_TRAY_MESSAGE_OPCODE */\n    Atom xa_tray_data; /* Atom: _NET_SYSTEM_TRAY_MESSAGE_DATA */\n    Atom xa_wm_protocols; /* Atom: WM_PROTOCOLS */\n    Atom xa_wm_delete_window; /* Atom: WM_DELETE_WINDOW */\n    Atom xa_net_wm_ping; /* Atom: WM_PING */\n    Atom xa_wm_take_focus; /* Atom: WM_TAKE_FOCUS */\n    Atom xa_kde_net_system_tray_windows; /* Atom: _KDE_NET_SYSTEM_TRAY_WINDOWS\n                                          */\n    Atom xa_net_client_list; /* Atom: _NET_CLIENT_LIST */\n\n    /* Background pixmap */\n    Atom xa_xrootpmap_id; /* Atom: _XROOTPMAP_ID */\n    Atom xa_xsetroot_id; /* Atom: _XSETROOT_ID */\n    Pixmap bg_pmap; /* Pixmap for tray background */\n    struct Point bg_pmap_dims; /* Background pixmap dimensions */\n\n#ifdef _ST_WITH_XINERAMA\n    /* Xinerama data */\n    int xinerama_active; /* Is Xinerama active? */\n    int n_monitors; /* Number of Xinerama screens */\n    XineramaScreenInfo *monitors; /* Xinerama screens info */\n#endif\n\n    /* XEMBED data */\n    struct XEMBEDData xembed_data; /* XEMBED data */\n\n    /* Scrollbar data */\n    struct ScrollbarsData scrollbars_data;\n};\nextern struct TrayData tray_data;\n\n/* Initialize all tray data structures */\nvoid tray_init();\n/* Create tray window */\nvoid tray_create_window(int argc, char **argv);\n/* Create phony tray window so that certain x11_ calls work */\nvoid tray_create_phony_window();\n/* Initialize tray selection atoms */\nvoid tray_init_selection_atoms();\n/* Acquire tray selection */\nvoid tray_acquire_selection();\n/* Show tray window */\nvoid tray_show_window();\n/* Refresh tray window */\nvoid tray_refresh_window(int exposures);\n/* Update tray background (and pixmap, if update_pixmap is true) */\nint tray_update_bg(int update_pixmap);\n/* Calculate tray window size given the size of icon area in pixels. */\nint tray_calc_window_size(\n    int base_width, int base_height, int *new_width, int *new_height);\n/* Calculate size of icon area given the tray window size in pixels. */\nint tray_calc_tray_area_size(\n    int wnd_width, int wnd_height, int *base_width, int *base_height);\n/* Update window struts (if enabled) */\nint tray_update_window_strut();\n/* Update tray window size and hints */\nint tray_update_window_props();\n/* Set tray window WM hints */\nint tray_set_wm_hints();\n\n#endif\n"
  },
  {
    "path": "src/wmh.c",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * ewmh.c\n * Thu, 30 Mar 2006 23:15:37 +0700\n * -------------------------------\n * EWMH/MWM support\n * ------------------------------- */\n#include <X11/Xatom.h>\n#include <X11/Xmd.h>\n\n#include \"common.h\"\n#include \"debug.h\"\n#include \"wmh.h\"\n#include \"xutils.h\"\n\n/* Structure for Motif WM hints */\ntypedef struct {\n    unsigned long flags;\n    unsigned long functions;\n    unsigned long decorations;\n    long inputMode;\n    unsigned long status;\n} PropMotifWmHints;\n/* Bit flags for fields of MWM hints data structure */\n#define MWM_HINTS_FUNCTIONS (1L << 0)\n#define MWM_HINTS_DECORATIONS (1L << 1)\n#define MWM_HINTS_INPUT_MODE (1L << 2)\n#define MWM_HINTS_STATUS (1L << 3)\n/* Number of CARD32 entries in MWM hints data structure */\n#define PROP_MOTIF_WM_HINTS_ELEMENTS 5\n\n/* Check if WM that supports EWMH hints is present on given display */\nint ewmh_wm_present(Display *dpy)\n{\n    Window *check_win, *check_win_self_ref;\n    unsigned long cw_len = 0, cwsr_len = 0;\n    int rc = False;\n    /* see _NET_SUPPORTING_WM_CHECK in the WM spec. */\n    rc = x11_get_root_winlist_prop(dpy,\n        XInternAtom(dpy, _NET_SUPPORTING_WM_CHECK, False),\n        (unsigned char **)&check_win, &cw_len);\n    if (x11_ok() && rc && cw_len == 1) {\n        LOG_TRACE((\"_NET_SUPPORTING_WM_CHECK (root) = 0x%lx\\n\", check_win[0]));\n        x11_get_window_prop32(dpy, check_win[0],\n            XInternAtom(dpy, _NET_SUPPORTING_WM_CHECK, False), XA_WINDOW,\n            (unsigned char **)&check_win_self_ref, &cwsr_len);\n        rc = (x11_ok() && rc && cwsr_len == 1\n            && check_win[0] == check_win_self_ref[0]);\n        LOG_TRACE((\"_NET_SUPPORTING_WM_CHECK (self reference) = 0x%lx\\n\",\n            check_win[0]));\n    }\n    if (cw_len != 0) XFree(check_win);\n    if (cwsr_len != 0) XFree(check_win_self_ref);\n    LOG_TRACE((\"EWMH WM %sdetected\\n\", rc ? \"\" : \"not \"));\n    return rc;\n}\n\n/* Add EWMH window state for the given window */\nint ewmh_add_window_state(Display *dpy, Window wnd, char *state)\n{\n    Atom prop;\n    Atom atom;\n    XWindowAttributes xwa;\n    int rc;\n    prop = XInternAtom(dpy, \"_NET_WM_STATE\", False);\n    atom = XInternAtom(dpy, state, False);\n    LOG_TRACE((\"adding state %s to window 0x%lx\\n\", state, atom));\n    /* Ping the window and get its state */\n    rc = XGetWindowAttributes(dpy, wnd, &xwa);\n    if (!x11_ok() || !rc) return FAILURE;\n\n    if (xwa.map_state != IsUnmapped && ewmh_wm_present(dpy)) {\n        /* For mapped windows, ask WM (if it is here) to add the window state\n         */\n        rc = x11_send_client_msg32(\n            dpy, xwa.root, wnd, prop, _NET_WM_STATE_ADD, atom, 0, 0, 0);\n    } else {\n        /* Else, alter the window state atom value ourselves */\n        rc = XChangeProperty(dpy, wnd, prop, XA_ATOM, 32, PropModeAppend,\n            (unsigned char *)&atom, 1);\n        rc = x11_ok() && rc;\n    }\n    return rc;\n}\n\n/* Add EWMH window type for the given window */\nint ewmh_add_window_type(Display *dpy, Window wnd, char *type)\n{\n    Atom prop;\n    Atom atom;\n    prop = XInternAtom(dpy, \"_NET_WM_WINDOW_TYPE\", False);\n    atom = XInternAtom(dpy, type, False);\n    LOG_TRACE((\"adding type %s to window 0x%lx\\n\", type, atom));\n    /* Update property value (append) */\n    XChangeProperty(dpy, wnd, prop, XA_ATOM, 32, PropModeAppend,\n        (unsigned char *)&atom, 1);\n    return x11_ok();\n}\n\n/* Set data for _NET_WM_STRUT{,_PARTIAL} hints */\nint ewmh_set_window_strut(Display *dpy, Window wnd, wm_strut_t wm_strut)\n{\n    Atom prop_strut;\n    Atom prop_strut_partial;\n    prop_strut = XInternAtom(dpy, _NET_WM_STRUT, False);\n    prop_strut_partial = XInternAtom(dpy, _NET_WM_STRUT_PARTIAL, False);\n    XChangeProperty(dpy, wnd, prop_strut, XA_CARDINAL, 32, PropModeReplace,\n        (unsigned char *)wm_strut, _NET_WM_STRUT_SZ);\n    XChangeProperty(dpy, wnd, prop_strut_partial, XA_CARDINAL, 32,\n        PropModeReplace, (unsigned char *)wm_strut, _NET_WM_STRUT_PARTIAL_SZ);\n    return x11_ok();\n}\n\n/* Set CARD32 value of EWMH atom for a given window */\nint ewmh_set_window_atom32(\n    Display *dpy, Window wnd, char *prop_name, CARD32 value)\n{\n    Atom prop;\n    XWindowAttributes xwa;\n    int rc;\n    prop = XInternAtom(dpy, prop_name, False);\n    LOG_TRACE((\"0x%lx: setting atom %s to 0x%x\\n\", wnd, prop_name, value));\n    /* Ping the window and get its state */\n    rc = XGetWindowAttributes(dpy, wnd, &xwa);\n    if (!x11_ok() || !rc) return FAILURE;\n    if (xwa.map_state != IsUnmapped && ewmh_wm_present(dpy)) {\n        /* For mapped windows, ask WM (if it is here) to add the window state\n         */\n        return x11_send_client_msg32(dpy, DefaultRootWindow(dpy), wnd, prop,\n            value, 2 /* source indication */, 0, 0, 0);\n    } else {\n        Atom atom = value;\n        /* Else, alter the window state atom value ourselves */\n        XChangeProperty(dpy, wnd, prop, XA_ATOM, 32, PropModeAppend,\n            (unsigned char *)&atom, 1);\n        return x11_ok();\n    }\n}\n\n/* Set MWM hints */\nint mwm_set_hints(Display *dpy, Window wnd, unsigned long decorations,\n    unsigned long functions)\n{\n    PropMotifWmHints *prop = NULL, new_prop;\n    int act_fmt;\n    unsigned long nitems, bytes_after;\n    static Atom atom = None, act_type;\n    /* Check if WM supports Motif WM hints */\n    if (atom == None) atom = XInternAtom(dpy, \"_MOTIF_WM_HINTS\", False);\n    if (atom == None) return FAILURE;\n    /* Get current hints */\n    XGetWindowProperty(dpy, wnd, atom, 0, 5, False, atom, &act_type,\n        &act_fmt, &nitems, &bytes_after, (unsigned char **)&prop);\n    if ((act_type == None && act_fmt == 0 && bytes_after == 0)\n        || nitems == 0) {\n        /* Hints are either not set or have some other type.\n         * Reset all values. */\n        memset(&new_prop, 0, sizeof(PropMotifWmHints));\n        if (prop != NULL) XFree(prop);\n        prop = &new_prop;\n    } else if (prop != NULL) {\n        /* Copy value */\n        new_prop = *prop;\n        XFree(prop);\n        prop = &new_prop;\n    } else {\n        /* Something is broken */\n        x11_ok(); /* Reset x11 error status */\n        return FAILURE;\n    }\n    /* Update value */\n    prop->flags |= MWM_HINTS_DECORATIONS | MWM_HINTS_FUNCTIONS;\n    prop->decorations = decorations;\n    prop->functions = functions;\n    XChangeProperty(dpy, wnd, atom, atom, 32, PropModeReplace,\n        (unsigned char *)prop, PROP_MOTIF_WM_HINTS_ELEMENTS);\n    return x11_ok();\n}\n\n#ifdef DEBUG\n/* Dumps EWMH atoms supported by WM */\nint ewmh_list_supported_atoms(Display *dpy)\n{\n    Atom *atom_list;\n    unsigned long atom_list_len, i;\n    char *atom_name;\n    if (ewmh_wm_present(dpy)) {\n        if (x11_get_window_prop32(dpy, DefaultRootWindow(dpy),\n                XInternAtom(dpy, _NET_SUPPORTED, False), XA_ATOM,\n                (unsigned char **)&atom_list, &atom_list_len))\n            if (atom_list_len) {\n                for (i = 0; i < atom_list_len; i++) {\n                    atom_name = XGetAtomName(dpy, atom_list[i]);\n                    if (atom_name != NULL) {\n                        LOG_TRACE((\"_NET_SUPPORTED[%ld]: %s (0x%lx)\\n\", i,\n                            atom_name, atom_list[i]));\n                    } else\n                        LOG_TRACE((\"_NET_SUPPORTED[%ld]: bogus value (0x%lx)\\n\",\n                            i, atom_list[i]));\n                    XFree(atom_name);\n                    x11_ok();\n                }\n                free(atom_list);\n                return SUCCESS;\n            }\n    }\n    return FAILURE;\n}\n\n/* List EWMH states that are set for the given window */\nint ewmh_dump_window_states(Display *dpy, Window wnd)\n{\n    Atom prop, *data;\n    unsigned long prop_len;\n    int j;\n    char *tmp;\n    /* Check if WM supports _NET_WM_STATE */\n    prop = XInternAtom(tray_data.dpy, \"_NET_WM_STATE\", True);\n    if (prop == None) return FAILURE;\n    /* Retrive the list of states */\n    if (x11_get_window_prop32(\n            dpy, wnd, prop, XA_ATOM, (unsigned char **)&data, &prop_len)) {\n        for (j = 0; j < prop_len; j++) {\n            tmp = XGetAtomName(tray_data.dpy, data[j]);\n            if (x11_ok() && tmp != NULL) {\n                LOG_TRACE((\"0x%lx:_NET_WM_STATE[%d] = %s\\n\", wnd, j, tmp));\n                XFree(tmp);\n            }\n        }\n        return SUCCESS;\n    }\n    return FAILURE;\n}\n#endif\n"
  },
  {
    "path": "src/wmh.h",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * ewmh.h\n * Thu, 30 Mar 2006 23:12:43 +0700\n * -------------------------------\n * EWMH/MWM support\n * ------------------------------- */\n\n#ifndef _EWMH_H_\n#define _EWMH_H_\n\n#include <X11/X.h>\n#include <X11/Xlib.h>\n\n/* Defines for Motif WM functions */\n#define MWM_FUNC_ALL (1L << 0)\n#define MWM_FUNC_RESIZE (1L << 1)\n#define MWM_FUNC_MOVE (1L << 2)\n#define MWM_FUNC_MINIMIZE (1L << 3)\n#define MWM_FUNC_MAXIMIZE (1L << 4)\n#define MWM_FUNC_CLOSE (1L << 5)\n/* Defines for Motif WM decorations */\n#define MWM_DECOR_ALL (1L << 0)\n#define MWM_DECOR_BORDER (1L << 1)\n#define MWM_DECOR_RESIZEH (1L << 2)\n#define MWM_DECOR_TITLE (1L << 3)\n#define MWM_DECOR_MENU (1L << 4)\n#define MWM_DECOR_MINIMIZE (1L << 5)\n#define MWM_DECOR_MAXIMIZE (1L << 6)\n\n/* EWMH atoms */\n#define _NET_SUPPORTED \"_NET_SUPPORTED\"\n#define _NET_SUPPORTING_WM_CHECK \"_NET_SUPPORTING_WM_CHECK\"\n#define _NET_WM_DESKTOP \"_NET_WM_DESKTOP\"\n#define _NET_ACTIVE_WINDOW \"_NET_ACTIVE_WINDOW\"\n#define _NET_CLIENT_LIST \"_NET_CLIENT_LIST\"\n#define _NET_WM_PING \"_NET_WM_PING\"\n#define _NET_WM_STRUT_PARTIAL \"_NET_WM_STRUT_PARTIAL\"\n#define _NET_WM_STRUT \"_NET_WM_STRUT\"\n\n/* Defines for EWMH window types */\n#define _NET_WM_WINDOW_TYPE_DESKTOP \"_NET_WM_WINDOW_TYPE_DESKTOP\"\n#define _NET_WM_WINDOW_TYPE_DOCK \"_NET_WM_WINDOW_TYPE_DOCK\"\n#define _NET_WM_WINDOW_TYPE_TOOLBAR \"_NET_WM_WINDOW_TYPE_TOOLBAR\"\n#define _NET_WM_WINDOW_TYPE_MENU \"_NET_WM_WINDOW_TYPE_MENU\"\n#define _NET_WM_WINDOW_TYPE_UTILITY \"_NET_WM_WINDOW_TYPE_UTILITY\"\n#define _NET_WM_WINDOW_TYPE_SPLASH \"_NET_WM_WINDOW_TYPE_SPLASH\"\n#define _NET_WM_WINDOW_TYPE_DIALOG \"_NET_WM_WINDOW_TYPE_DIALOG\"\n#define _NET_WM_WINDOW_TYPE_NORMAL \"_NET_WM_WINDOW_TYPE_NORMAL\"\n\n/* Defined for EWMH window states */\n#define _NET_WM_STATE_MODAL \"_NET_WM_STATE_MODAL\"\n#define _NET_WM_STATE_STICKY \"_NET_WM_STATE_STICKY\"\n#define _NET_WM_STATE_MAXIMIZED_VERT \"_NET_WM_STATE_MAXIMIZED_VERT\"\n#define _NET_WM_STATE_MAXIMIZED_HORZ \"_NET_WM_STATE_MAXIMIZED_HORZ\"\n#define _NET_WM_STATE_SHADED \"_NET_WM_STATE_SHADED\"\n#define _NET_WM_STATE_SKIP_TASKBAR \"_NET_WM_STATE_SKIP_TASKBAR\"\n#define _NET_WM_STATE_SKIP_PAGER \"_NET_WM_STATE_SKIP_PAGER\"\n#define _NET_WM_STATE_HIDDEN \"_NET_WM_STATE_HIDDEN\"\n#define _NET_WM_STATE_FULLSCREEN \"_NET_WM_STATE_FULLSCREEN\"\n#define _NET_WM_STATE_ABOVE \"_NET_WM_STATE_ABOVE\"\n#define _NET_WM_STATE_BELOW \"_NET_WM_STATE_BELOW\"\n#define _NET_WM_STATE_DEMANDS_ATTENTION \"_NET_WM_STATE_DEMANDS_ATTENTION\"\n\n/* Flags for window state manipulations */\n#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */\n#define _NET_WM_STATE_ADD 1 /* add/set property */\n#define _NET_WM_STATE_TOGGLE 2 /* toggle property  */\n\n#define _NET_WM_STRUT_PARTIAL_SZ 12\n#define _NET_WM_STRUT_SZ 4\ntypedef unsigned long wm_strut_t[_NET_WM_STRUT_PARTIAL_SZ];\n#define WM_STRUT_IDX_LFT 0\n#define WM_STRUT_IDX_RHT 1\n#define WM_STRUT_IDX_TOP 2\n#define WM_STRUT_IDX_BOT 3\n#define WM_STRUT_IDX_START(B) B * 2 + 4\n#define WM_STRUT_IDX_END(B) B * 2 + 5\n\n/* Check if WM that supports EWMH hints is present on given display */\nint ewmh_wm_present(Display *dpy);\n/* Add window type for the window wnd */\nint ewmh_add_window_state(Display *dpy, Window wnd, char *state);\n/* Add window type for the window wnd */\nint ewmh_add_window_type(Display *dpy, Window wnd, char *type);\n/* Set data for _NET_WM_STRUT_PARTIAL hint */\nint ewmh_set_window_strut(Display *dpy, Window wnd, wm_strut_t wm_strut);\n/* Set CARD32 value of EWMH atom for a given window */\nint ewmh_set_window_atom32(\n    Display *dpy, Window wnd, char *prop_name, CARD32 value);\n/* Set Motif WM hints for window wnd; read MWM spec for more info */\nint mwm_set_hints(Display *dpy, Window wnd, unsigned long decorations,\n    unsigned long functions);\n\n#ifdef DEBUG\n/* Dumps EWMH atoms supported by WM */\nint ewmh_list_supported_atoms(Display *dpy);\n/* Dump all EWMH states that have been set for the window wnd */\nint ewmh_dump_window_states(Display *dpy, Window wnd);\n#endif\n\n#endif\n"
  },
  {
    "path": "src/xembed.c",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * xembed.c\n * Tue, 24 Aug 2004 12:05:38 +0700\n * -------------------------------\n * XEMBED protocol implementation\n * -------------------------------*/\n\n/* Currently broken:\n * - XEMBED_{ACTIVATE,REGISTER,UNREGISTER}_ACCELERATOR\n * - XEMBED_REQUEST_FOCUS */\n\n#include <X11/X.h>\n#include <X11/Xlib.h>\n#include <X11/Xmd.h>\n#include <X11/Xutil.h>\n\n#include \"common.h\"\n#include \"debug.h\"\n#include \"wmh.h\"\n#include \"xembed.h\"\n#include \"xutils.h\"\n\n#include \"list.h\"\n\n/* Internal return codes */\n#define XEMBED_RESULT_OK 0\n#define XEMBED_RESULT_UNSUPPORTED 1\n#define XEMBED_RESULT_X11ERROR 2\n\n/* XEMBED messages */\n#define XEMBED_EMBEDDED_NOTIFY 0\n#define XEMBED_WINDOW_ACTIVATE 1\n#define XEMBED_WINDOW_DEACTIVATE 2\n#define XEMBED_REQUEST_FOCUS 3\n#define XEMBED_FOCUS_IN 4\n#define XEMBED_FOCUS_OUT 5\n#define XEMBED_FOCUS_NEXT 6\n#define XEMBED_FOCUS_PREV 7\n/* 8-9 were used for XEMBED_GRAB_KEY/XEMBED_UNGRAB_KEY */\n#define XEMBED_MODALITY_ON 10\n#define XEMBED_MODALITY_OFF 11\n#define XEMBED_REGISTER_ACCELERATOR 12\n#define XEMBED_UNREGISTER_ACCELERATOR 13\n#define XEMBED_ACTIVATE_ACCELERATOR 14\n\n/* Details for  XEMBED_FOCUS_IN */\n#define XEMBED_FOCUS_CURRENT 0\n#define XEMBED_FOCUS_FIRST 1\n#define XEMBED_FOCUS_LAST 2\n\n/* Modifiers field for XEMBED_REGISTER_ACCELERATOR */\n#define XEMBED_MODIFIER_SHIFT (1 << 0)\n#define XEMBED_MODIFIER_CONTROL (1 << 1)\n#define XEMBED_MODIFIER_ALT (1 << 2)\n#define XEMBED_MODIFIER_SUPER (1 << 3)\n#define XEMBED_MODIFIER_HYPER (1 << 4)\n\n/* Flags for XEMBED_ACTIVATE_ACCELERATOR */\n#define XEMBED_ACCELERATOR_OVERLOADED (1 << 0)\n\n/* Directions for focusing */\n#define XEMBED_DIRECTION_DEFAULT 0\n#define XEMBED_DIRECTION_UP_DOWN 1\n#define XEMBED_DIRECTION_LEFT_RIGHT 2\n\n/* Flags for _XEMBED_INFO */\n#define XEMBED_MAPPED (1 << 0)\n\n/* Structure to hold XEMBED accelerator data */\nstruct XEMBEDAccel {\n    struct XEMBEDAccel *next, *prev;\n    int overloaded; /* Is this accelerator overloaded? */\n    long id; /* Accelerator Id */\n    long symb; /* Symbol */\n    long mods; /* Modifiers */\n};\n\n/* Shortcuts for sending XEMBED messages */\n#define xembed_send_msg(dpy, dst, timestamp, msg, detail, data1, data2) \\\n    x11_send_client_msg32(dpy, dst, dst, tray_data.xembed_data.xa_xembed, \\\n        timestamp, msg, detail, data1, data2)\n\n#define xembed_send_embedded_notify(dpy, src, dst, timestamp) \\\n    xembed_send_msg(dpy, dst, timestamp, XEMBED_EMBEDDED_NOTIFY, 0, src, 0)\n\n#define xembed_send_window_activate(dpy, dst, timestamp) \\\n    xembed_send_msg(dpy, dst, timestamp, XEMBED_WINDOW_ACTIVATE, 0, 0, 0)\n\n#define xembed_send_window_deactivate(dpy, dst, timestamp) \\\n    xembed_send_msg(dpy, dst, timestamp, XEMBED_WINDOW_DEACTIVATE, 0, 0, 0)\n\n#define xembed_send_focus_in(dpy, dst, focus, timestamp) \\\n    xembed_send_msg(dpy, dst, timestamp, XEMBED_FOCUS_IN, focus, 0, 0)\n\n#define xembed_send_focus_out(dpy, dst, timestamp) \\\n    xembed_send_msg(dpy, dst, timestamp, XEMBED_FOCUS_OUT, 0, 0, 0)\n\n#define xembed_send_activate_accelerator(dpy, dst, timestamp, id, overloaded) \\\n    xembed_send_msg( \\\n        dpy, dst, timestamp, XEMBED_ACTIVATE_ACCELERATOR, id, overloaded, 0)\n\n/* Retrive XEMBED data for the given icon */\nint xembed_retrive_data(struct TrayIcon *ti);\n/* Post icon XEMBED data to window property */\nint xembed_post_data(struct TrayIcon *ti);\n/* Returns the next icon in tab chain */\nstruct TrayIcon *xembed_next();\n/* Returns the previous icon in tab chain */\nstruct TrayIcon *xembed_prev();\n/* XEMBED event handler */\nint xembed_process_kbd_event(XKeyEvent xkey);\n/* Register new XEMBED accelerator */\nvoid xembed_add_accel(long id, long symb, long mods);\n/* Delete previously registered XEMBED accelerator */\nvoid xembed_del_accel(long id);\n/* Activate previously registered XEMBED accelerator */\nvoid xembed_act_accel(struct XEMBEDAccel *accel);\n/* Switch XEMBED focus to the specified icon */\nvoid xembed_switch_focus_to(struct TrayIcon *tgt, long focus);\n/* Broadcast the focus change to all icons */\nvoid xembed_track_focus_change(int activate);\n/* Process XEMBED message */\nvoid xembed_message(XClientMessageEvent ev);\n/* Tries to request focus from WM */\nvoid xembed_request_focus_from_wm();\n\nvoid xembed_init()\n{\n    /* 1. Initialize data structures */\n    tray_data.xembed_data.window_has_focus = False;\n    tray_data.xembed_data.focus_requested = False;\n    tray_data.xembed_data.current = NULL;\n    tray_data.xembed_data.accels = NULL;\n    tray_data.xembed_data.timestamp = CurrentTime;\n    tray_data.xembed_data.xa_xembed =\n        XInternAtom(tray_data.dpy, \"_XEMBED\", False);\n    tray_data.xembed_data.xa_xembed_info =\n        XInternAtom(tray_data.dpy, \"_XEMBED_INFO\", False);\n    /* 2. Create focus proxy (see XEMBED spec) */\n    tray_data.xembed_data.focus_proxy = XCreateSimpleWindow(\n        tray_data.dpy, tray_data.tray, -1, -1, 1, 1, 0, 0, 0);\n    XSelectInput(tray_data.dpy, tray_data.xembed_data.focus_proxy,\n        FocusChangeMask | KeyPressMask | KeyReleaseMask);\n    XMapRaised(tray_data.dpy, tray_data.xembed_data.focus_proxy);\n    if (!x11_ok()) DIE((\"could not create focus proxy\\n\"));\n    LOG_TRACE((\n        \"created focus proxy, wid=0x%lx\\n\", tray_data.xembed_data.focus_proxy));\n}\n\nvoid xembed_handle_event(XEvent ev)\n{\n    switch (ev.type) {\n    case FocusOut:\n        /* Broadcast that the focus has left tray window */\n        LOG_TRACE((\"FocusOut 0x%lx\\n\", ev.xfocus.window));\n        if (ev.xfocus.window == tray_data.xembed_data.focus_proxy)\n            xembed_track_focus_change(False);\n        break;\n    case ClientMessage:\n        /* Handle XEMBED-related messages */\n        if (ev.xclient.message_type == tray_data.xembed_data.xa_xembed) {\n            xembed_message(ev.xclient);\n        } else if (ev.xclient.message_type == tray_data.xa_tray_opcode) {\n            /* we peek at _NET_SYSTEM_TRAY_OPCODE messages\n             * to obtain proper timestamp for embedding */\n            tray_data.xembed_data.timestamp = ev.xclient.data.l[0];\n            if (tray_data.xembed_data.timestamp == CurrentTime)\n                tray_data.xembed_data.timestamp =\n                    x11_get_server_timestamp(tray_data.dpy, tray_data.tray);\n        } else if (ev.xclient.message_type == tray_data.xa_wm_protocols\n            && (unsigned long) ev.xclient.data.l[0] == tray_data.xa_wm_take_focus\n            && tray_data.xembed_data.focus_requested) {\n            XSetInputFocus(tray_data.dpy, tray_data.xembed_data.focus_proxy,\n                RevertToParent, ev.xclient.data.l[1]);\n            if (!x11_ok())\n                DIE_IE((\"Could not set focus to XEMBED focus proxy\\n\"));\n            LOG_TRACE((\"focus set to focus proxy\\n\"));\n            xembed_track_focus_change(True);\n            tray_data.xembed_data.focus_requested = False;\n        }\n        break;\n    case KeyRelease:\n    case KeyPress:\n        /* Propagate key events to currently focused icon */\n        tray_data.xembed_data.timestamp = ev.xkey.time;\n        if (ev.type == KeyRelease && xembed_process_kbd_event(ev.xkey)) break;\n        if (tray_data.xembed_data.current != NULL) {\n            int rc;\n            LOG_TRACE((\"current icon accepts_focus: %d\\n\",\n                tray_data.xembed_data.current->is_xembed_accepts_focus));\n            rc = XSendEvent(tray_data.dpy, tray_data.xembed_data.current->wid,\n                False, NoEventMask, &ev);\n            if (!x11_ok() || rc == 0) {\n                tray_data.xembed_data.current->is_invalid = True;\n                return;\n            }\n            LOG_TRACE((\"sent key event to 0x%lx\\n\",\n                tray_data.xembed_data.current->wid));\n        }\n        break;\n    }\n}\n\nint xembed_check_support(struct TrayIcon *ti)\n{\n    int rc = xembed_retrive_data(ti);\n    ti->is_xembed_supported = (rc == XEMBED_RESULT_OK);\n    return rc != XEMBED_RESULT_X11ERROR;\n}\n\nint xembed_get_mapped_state(struct TrayIcon *ti)\n{\n    /* It is OK to retrive data each time this function\n     * is called, since there is some overhead only during\n     * initialization, when xembed_retrive_data is called 2\n     * times in a row(). */\n    int rc = xembed_retrive_data(ti);\n    if (ti->is_xembed_supported && rc == XEMBED_RESULT_OK)\n        return ((ti->xembed_data[1] & XEMBED_MAPPED) != 0);\n    else {\n        ti->is_xembed_supported = False;\n        ti->is_invalid = (rc == XEMBED_RESULT_X11ERROR);\n        return False;\n    }\n}\n\nint xembed_set_mapped_state(struct TrayIcon *ti, int state)\n{\n    if (!ti->is_xembed_supported) return FAILURE;\n    if (state)\n        ti->xembed_data[1] |= XEMBED_MAPPED;\n    else\n        ti->xembed_data[1] &= ~XEMBED_MAPPED;\n    return xembed_post_data(ti);\n}\n\nint xembed_embed(struct TrayIcon *ti)\n{\n    /* if XEMBED is not supported, do nothing */\n    if (!ti->is_xembed_supported) return SUCCESS;\n    /* By default, consider that all icons accept focus */\n    ti->is_xembed_accepts_focus = True;\n    /* Send notification */\n    if (!xembed_send_embedded_notify(tray_data.dpy, tray_data.tray, ti->wid,\n            tray_data.xembed_data.timestamp))\n        return FAILURE;\n    ti->xembed_last_timestamp = tray_data.xembed_data.timestamp;\n    ti->xembed_last_msgid = XEMBED_EMBEDDED_NOTIFY;\n    if (tray_data.xembed_data.current == NULL) {\n        /* No icon has focus. Set focus to this one */\n        if (!xembed_send_focus_in(tray_data.dpy, ti->wid, XEMBED_FOCUS_FIRST,\n                tray_data.xembed_data.timestamp))\n            return FAILURE;\n        tray_data.xembed_data.current = ti;\n    }\n    /* Send activation message if tray window has focus */\n    if (tray_data.xembed_data.window_has_focus)\n        return xembed_send_window_activate(\n            tray_data.dpy, ti->wid, tray_data.xembed_data.timestamp);\n    return SUCCESS;\n}\n\nint xembed_unembed(struct TrayIcon *ti)\n{\n    struct TrayIcon *tmp;\n    tray_data.xembed_data.timestamp =\n        x11_get_server_timestamp(tray_data.dpy, tray_data.tray);\n    if (ti == tray_data.xembed_data.current) {\n        /* Currently focused icon is being unembedded,\n         * move focus to the next icon. */\n        tmp = xembed_next();\n        if (tmp == ti || tmp->is_xembed_accepts_focus == False) {\n            xembed_switch_focus_to(NULL, 0);\n        } else {\n            xembed_switch_focus_to(tmp, XEMBED_FOCUS_FIRST);\n        }\n    }\n    return SUCCESS;\n}\n\n/*********** implementation level ***************/\nvoid xembed_switch_focus_to(struct TrayIcon *tgt, long focus)\n{\n    /* 1. Send \"focus out\" message to the currently focused icon */\n    if (tray_data.xembed_data.current != NULL) {\n        LOG_TRACE((\"XEMBED focus was removed from icon 0x%lx (pointer %p)\\n\",\n            tray_data.xembed_data.current->wid,\n            (void *) tray_data.xembed_data.current));\n        xembed_send_focus_out(tray_data.dpy,\n            tray_data.xembed_data.current->wid,\n            tray_data.xembed_data.timestamp);\n    }\n    /* 2. Send \"focus in\" message to the icon to be focused */\n    if (tgt != NULL) {\n        xembed_send_focus_in(\n            tray_data.dpy, tgt->wid, focus, tray_data.xembed_data.timestamp);\n        LOG_TRACE((\"XEMBED focus was set to icon 0x%lx (pointer %p)\\n\",\n            tgt->wid, (void *) tgt));\n    } else {\n        LOG_TRACE((\"XEMBED focus was unset\\n\"));\n    }\n    tray_data.xembed_data.current = tgt;\n}\n\nstatic int activate = 0;\n\nint broadcast_activate_msg(struct TrayIcon *ti)\n{\n    if (activate)\n        xembed_send_window_activate(\n            tray_data.dpy, ti->wid, tray_data.xembed_data.timestamp);\n    else\n        xembed_send_window_deactivate(\n            tray_data.dpy, ti->wid, tray_data.xembed_data.timestamp);\n    return NO_MATCH;\n}\n\nvoid xembed_track_focus_change(int in)\n{\n    if (tray_data.xembed_data.window_has_focus == in) return;\n    tray_data.xembed_data.window_has_focus = in;\n    activate = in;\n    icon_list_forall(&broadcast_activate_msg);\n    LOG_TRACE((\"XEMBED focus is %s\\n\", in ? \"ON\" : \"OFF\"));\n}\n\nvoid xembed_message(XClientMessageEvent ev)\n{\n    long msgid;\n    LOG_TRACE((\"this is an _XEMBED message, window: 0x%lx, timestamp: %lu, \"\n               \"opcode: %lu, \\ndetail: 0x%lx, data1 = 0x%lx, data2 = 0x%lx\\n\",\n        ev.window, ev.data.l[0], ev.data.l[1], ev.data.l[2], ev.data.l[3],\n        ev.data.l[4]));\n#if DEBUG\n    if (tray_data.xembed_data.current != NULL)\n        LOG_TRACE((\"XEMBED focus is in window 0x%lx (pointer %p)\\n\",\n            tray_data.xembed_data.current->wid,\n            tray_data.xembed_data.current));\n    else\n        LOG_TRACE((\"XEMBED focus is unset\\n\"));\n#endif\n    if (ev.window != tray_data.tray) {\n        LOG_TRACE((\"inoring _XEMBED message to some other window\\n\"));\n        return;\n    }\n    /* Update timestamp if necessary */\n    if (ev.data.l[0] == CurrentTime)\n        ev.data.l[0] = x11_get_server_timestamp(tray_data.dpy, tray_data.tray);\n    tray_data.xembed_data.timestamp = ev.data.l[0];\n    msgid = ev.data.l[1];\n    LOG_TRACE((\"_XEMBED message %lu\\n\", msgid));\n    switch (msgid) {\n    case XEMBED_REQUEST_FOCUS: xembed_request_focus_from_wm(); break;\n    case XEMBED_FOCUS_NEXT:\n    case XEMBED_FOCUS_PREV:\n        if (tray_data.xembed_data.current != NULL) {\n            struct TrayIcon *old_focus, *new_focus;\n            old_focus = tray_data.xembed_data.current;\n            new_focus =\n                (msgid == XEMBED_FOCUS_NEXT) ? xembed_next() : xembed_prev();\n            if (new_focus->is_xembed_accepts_focus) {\n                /* If the last message for the new focus target was\n                 * focus_{next,prev} and it has the same timestamp as the\n                 * current message, it is likely that the corresponding icon\n                 * does not want to be focused at all. So mark it as not\n                 * accepting focus. */\n                if (new_focus->xembed_last_timestamp\n                        == tray_data.xembed_data.timestamp\n                    && (new_focus->xembed_last_msgid == XEMBED_FOCUS_NEXT\n                        || new_focus->xembed_last_msgid\n                            == XEMBED_FOCUS_PREV)) {\n                    new_focus->is_xembed_accepts_focus = False;\n                    new_focus = False;\n                }\n                old_focus->xembed_last_timestamp =\n                    tray_data.xembed_data.timestamp;\n                old_focus->xembed_last_msgid = msgid;\n            } else\n                new_focus = NULL;\n            xembed_switch_focus_to(new_focus,\n                (msgid == XEMBED_FOCUS_NEXT) ? XEMBED_FOCUS_FIRST\n                                             : XEMBED_FOCUS_LAST);\n        }\n        break;\n    case XEMBED_REGISTER_ACCELERATOR:\n        xembed_add_accel(ev.data.l[2], ev.data.l[3], ev.data.l[4]);\n        break;\n    case XEMBED_UNREGISTER_ACCELERATOR: xembed_del_accel(ev.data.l[2]); break;\n    default:\n        LOG_TRACE((\"Unhandled _XEMBED message, id = %ld\\n\", ev.data.l[1]));\n        break;\n    }\n}\n\nvoid xembed_add_accel(long id, long symb, long mods)\n{\n    struct XEMBEDAccel *xaccel, *tmp;\n    xaccel = (struct XEMBEDAccel *)malloc(sizeof(struct XEMBEDAccel));\n    if (xaccel == NULL) {\n        LOG_ERR_OOM((\"Could not register new XEMBED accelerator\\n\"));\n        return;\n    }\n    xaccel->id = id;\n    xaccel->symb = symb;\n    xaccel->mods = mods;\n    xaccel->overloaded = 0;\n    /* Check if there are already registered accelerators that are overloaded\n     * by this one */\n    for (tmp = tray_data.xembed_data.accels; tmp != NULL; tmp = tmp->next)\n        if (tmp->symb == symb && tmp->mods == mods) {\n            xaccel->overloaded++;\n            tmp->overloaded++;\n        }\n    LIST_ADD_ITEM(tray_data.xembed_data.accels, xaccel);\n    LOG_TRACE((\"added new XEMBED accelerator: id=0x%lx, sym=0x%lx, mods=0x%lx, \"\n               \"overloaded=%d\\n\",\n        id, symb, mods, xaccel->overloaded));\n}\n\nvoid xembed_del_accel(long id)\n{\n    struct XEMBEDAccel *tmp, *tgt = NULL;\n    for (tmp = tray_data.xembed_data.accels; tmp != NULL; tmp = tmp->next)\n        if (tmp->id == id) {\n            tgt = tmp;\n            return;\n        }\n    if (tgt == NULL) {\n        LOG_TRACE((\"refusing to remove unregistered XEMBED accelerator\\n\"));\n        return;\n    }\n    /* Update overloaded status of the remaining accelerators */\n    for (tmp = tray_data.xembed_data.accels; tmp != NULL; tmp = tmp->next)\n        if (tmp->symb == tgt->symb && tmp->mods == tgt->mods)\n            tmp->overloaded--;\n    LIST_DEL_ITEM(tray_data.xembed_data.accels, tgt);\n    LOG_TRACE((\"removed XEMBED accelator id=0x%lx\", tgt->id));\n    free(tgt);\n}\n\nstatic struct XEMBEDAccel *cur_accel;\n\nint xembed_act_accel_helper(struct TrayIcon *ti)\n{\n    xembed_send_activate_accelerator(tray_data.dpy, ti->wid,\n        tray_data.xembed_data.timestamp, cur_accel->id,\n        cur_accel->overloaded ? 1 : 0);\n    return NO_MATCH;\n}\n\nvoid xembed_act_accel(struct XEMBEDAccel *accel)\n{\n    LOG_TRACE(\n        (\"activating XEMBED accelerator: id=0x%lx (symb=0x%lx, mods=0x%lx)\\n\",\n            accel->id, accel->symb, accel->mods));\n    cur_accel = accel;\n    icon_list_forall(&xembed_act_accel_helper);\n}\n\nint xembed_process_kbd_event(XKeyEvent xkey)\n{\n    struct XEMBEDAccel *tmp;\n    int hits = 0;\n    KeySym keysym;\n    static char buf[20];\n    XLookupString(&xkey, buf, 20, &keysym, NULL);\n    LOG_TRACE(\n        (\"Key event (type=%d) with keycode=0x%x, symb=0x%lx, state=0x%x\\n\",\n            xkey.type, xkey.keycode, keysym, xkey.state));\n    for (tmp = tray_data.xembed_data.accels; tmp != NULL; tmp = tmp->next)\n        if ((unsigned long) tmp->symb == keysym && tmp->mods == xkey.state) {\n            xembed_act_accel(tmp);\n            hits = 1;\n        }\n    return hits;\n}\n\nstruct TrayIcon *xembed_next()\n{\n    struct TrayIcon *tmp, *blocker;\n    tmp = tray_data.xembed_data.current != NULL ? tray_data.xembed_data.current\n                                                : NULL;\n    blocker = tmp != NULL ? tmp : icon_list_next(NULL);\n    do {\n        tmp = layout_next(tmp);\n    } while ((!tmp->is_xembed_supported || !tmp->is_xembed_accepts_focus)\n        && tmp != blocker);\n    return tmp;\n}\n\nstruct TrayIcon *xembed_prev()\n{\n    struct TrayIcon *tmp, *blocker;\n    tmp = tray_data.xembed_data.current != NULL ? tray_data.xembed_data.current\n                                                : NULL;\n    blocker = tmp != NULL ? tmp : icon_list_prev(tmp);\n    do {\n        tmp = layout_prev(tmp);\n    } while ((!tmp->is_xembed_supported || !tmp->is_xembed_accepts_focus)\n        && tmp != blocker);\n    return tmp;\n}\n\nint xembed_retrive_data(struct TrayIcon *ti)\n{\n    Atom act_type;\n    int act_fmt;\n    unsigned long nitems, bytesafter, *data;\n    unsigned char *tmpdata;\n    int rc;\n    /* NOTE: x11_get_win_prop32 is not used since we need to distinguish\n     * between X11 errors and absence of the property */\n    rc = XGetWindowProperty(tray_data.dpy, ti->wid,\n        tray_data.xembed_data.xa_xembed_info, 0, 2, False,\n        tray_data.xembed_data.xa_xembed_info, &act_type, &act_fmt, &nitems,\n        &bytesafter, &tmpdata);\n    if (!x11_ok() || rc != Success) return XEMBED_RESULT_X11ERROR;\n    rc = (act_type == tray_data.xembed_data.xa_xembed_info && nitems == 2);\n    if (rc) {\n        data = (unsigned long *)tmpdata;\n        ti->xembed_data[0] = data[0];\n        ti->xembed_data[1] = data[1];\n    }\n    if (nitems && tmpdata != NULL) XFree(tmpdata);\n    return rc ? XEMBED_RESULT_OK : XEMBED_RESULT_UNSUPPORTED;\n}\n\nint xembed_post_data(struct TrayIcon *ti)\n{\n    if (!ti->is_xembed_supported) return XEMBED_RESULT_UNSUPPORTED;\n    XChangeProperty(tray_data.dpy, ti->wid,\n        tray_data.xembed_data.xa_xembed_info,\n        tray_data.xembed_data.xa_xembed_info, 32, PropModeReplace,\n        (unsigned char *)ti->xembed_data, 2);\n    return x11_ok() ? XEMBED_RESULT_OK : XEMBED_RESULT_X11ERROR;\n}\n\nvoid xembed_request_focus_from_wm()\n{\n    if (!tray_data.is_reparented) {\n        x11_send_client_msg32(tray_data.dpy, DefaultRootWindow(tray_data.dpy),\n            tray_data.tray,\n            XInternAtom(tray_data.dpy, \"_NET_ACTIVE_WINDOW\", True),\n            1, /* Request is from application */\n            x11_get_server_timestamp(\n                tray_data.dpy, tray_data.tray), /* Timestamp */\n            0, /* None window is focused current (?) */\n            0, /* Unused */\n            0); /* Unused */\n        tray_data.xembed_data.focus_requested = True;\n    }\n}\n"
  },
  {
    "path": "src/xembed.h",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * icons.h\n * Tue, 24 Aug 2004 12:05:38 +0700\n * -------------------------------\n * XEMBED protocol implementation\n * -------------------------------*/\n\n#ifndef _XEMBED_H_\n#define _XEMBED_H_\n\n#include <X11/X.h>\n\n#include \"icons.h\"\n\n/* Data structure for all XEMBED-related data for the tray */\nstruct XEMBEDData {\n    struct TrayIcon *current; /* Pointer to the currently focused icon */\n    struct XEMBEDAccel *accels; /* List of registered XEMBED accelerators */\n    int window_has_focus; /* Flag: does tray's window have focus */\n    int focus_requested; /* Flag: if there is not completed focus request */\n    Window focus_proxy; /* Window ID of XEMBED focus proxy */\n    long timestamp; /* Timestamp of current processed message */\n    Atom xa_xembed_info; /* Atom: XEMBED_INFO */\n    Atom xa_xembed; /* Atom: XEMBED */\n};\n\n/* Initialize XEMBED data structures */\nvoid xembed_init();\n\n/* Event handling routine for XEMBED support */\nvoid xembed_handle_event(XEvent ev);\n\n/* Check if icon ti supports XEMBED */\nint xembed_check_support(struct TrayIcon *ti);\n\n/* Send XEMBED embedding acknowledgement to icon ti */\nint xembed_embed(struct TrayIcon *ti);\n\n/* Same as above for unembedding */\nint xembed_unembed(struct TrayIcon *ti);\n\n/* Get XEMBED mapped state from XEMBED info */\nint xembed_get_mapped_state(struct TrayIcon *ti);\n\n/* Set XEMBED mapped state in XEMBED info */\nint xembed_set_mapped_state(struct TrayIcon *ti, int state);\n\n#endif\n"
  },
  {
    "path": "src/xinerama.c",
    "content": "#include <X11/Xlib.h>\n\n#include \"debug.h\"\n#include \"xinerama.h\"\n\n#ifdef _ST_WITH_XINERAMA\n#include <X11/extensions/Xinerama.h>\n\n#include \"tray.h\"\n#include \"settings.h\"\n#endif\n\nvoid xinerama_init(Display *dpy)\n{\n#ifdef _ST_WITH_XINERAMA\n    if (!XineramaIsActive(dpy)) {\n        LOG_TRACE((\"Xinerama is not active, returning\\n\"));\n        return;\n    }\n\n    LOG_TRACE((\"Xinerama is active\\n\"));\n\n    tray_data.xinerama_active = True;\n    tray_data.monitors = XineramaQueryScreens(dpy, &tray_data.n_monitors);\n\n    LOG_TRACE((\"Xinerama reports %d monitors\\n\", tray_data.n_monitors));\n#else\n    (void) dpy; /* unused */\n#endif\n}\n\nvoid xinerama_update_geometry(void)\n{\n#ifdef _ST_WITH_XINERAMA\n    XineramaScreenInfo chosen_monitor;\n    unsigned int dummy;\n    int x = 0, y = 0, flags;\n\n    if (!tray_data.xinerama_active)\n        return;\n\n    LOG_TRACE((\"Updating geometry based on chosen Xinerama monitor\\n\"));\n\n    flags = XParseGeometry(settings.geometry_str, &x, &y, &dummy, &dummy);\n    chosen_monitor = tray_data.monitors[settings.monitor];\n\n    LOG_TRACE((\"Chosen monitor %d: %dx%d+%d+%d\\n\", settings.monitor,\n        chosen_monitor.width, chosen_monitor.height, chosen_monitor.x_org,\n        chosen_monitor.y_org));\n\n    if (flags & XValue && flags & XNegative)\n        x += chosen_monitor.width - tray_data.xsh.width;\n    if (flags & YValue && flags & YNegative)\n        y += chosen_monitor.height - tray_data.xsh.height;\n\n    tray_data.xsh.x = chosen_monitor.x_org + x;\n    tray_data.xsh.y = chosen_monitor.y_org + y;\n\n    LOG_TRACE((\"New tray position (x,y): %d,%d\\n\", tray_data.xsh.x, tray_data.xsh.y));\n#else\n    return;\n#endif\n}\n"
  },
  {
    "path": "src/xinerama.h",
    "content": "#ifndef _STALONETRAY_XINERAMA_H_\n#define _STALONETRAY_XINERAMA_H_\n\n#include <X11/Xlib.h>\n\nvoid xinerama_init(Display *dpy);\nvoid xinerama_update_geometry(void);\n\n#endif\n"
  },
  {
    "path": "src/xutils.c",
    "content": "/* ************************************\n * vim:tabstop=4:shiftwidth=4\n * xutils.c\n * Sun, 05 Mar 2006 17:56:56 +0600\n * ************************************\n * misc X11 utilities\n * ************************************/\n\n#include <X11/Xatom.h>\n#include <X11/Xlib.h>\n#include <X11/Xmd.h>\n#include <X11/Xutil.h>\n\n#include <limits.h>\n#include <unistd.h>\n#include <string.h>\n\n#include \"xutils.h\"\n\n#include \"common.h\"\n#include \"debug.h\"\n\nstatic int trapped_x11_error_code = 0;\nstatic int (*old_x11_error_handler)(Display *, XErrorEvent *) = NULL;\nstatic int current_x11_connection_status = 1;\nstatic int (*old_x11_io_error_handler)(Display *) = NULL;\n\nint x11_io_error_handler(Display *dpy)\n{\n    current_x11_connection_status = 0;\n    old_x11_io_error_handler(dpy);\n    DIE((\"Connection to X11 server lost. Dying.\\n\"));\n}\n\nint x11_connection_status()\n{\n    return current_x11_connection_status;\n}\n\nint x11_error_handler(Display *dpy, XErrorEvent *err)\n{\n    static char msg[PATH_MAX], req_num_str[32], req_str[PATH_MAX];\n    trapped_x11_error_code = err->error_code;\n    XGetErrorText(dpy, trapped_x11_error_code, msg, sizeof(msg) - 1);\n    snprintf(req_num_str, 32, \"%d\", err->request_code);\n    XGetErrorDatabaseText(\n        dpy, \"XRequest\", req_num_str, \"Unknown\", req_str, PATH_MAX);\n    LOG_ERROR((\"X11 error: %s (request: %s, resource 0x%x)\\n\", msg, req_str,\n        err->request_code));\n    return 0;\n}\n\nint x11_ok_helper(const char *file, const int line, const char *func)\n{\n    if (trapped_x11_error_code) {\n        LOG_ERROR((\"X11 error %d detected at %s:%d:%s\\n\",\n            trapped_x11_error_code, file, line, func));\n        trapped_x11_error_code = 0;\n        return FAILURE;\n    } else\n        return SUCCESS;\n}\n\nint x11_current_error()\n{\n    return trapped_x11_error_code;\n}\n\nvoid x11_trap_errors()\n{\n    old_x11_io_error_handler = XSetIOErrorHandler(x11_io_error_handler);\n    old_x11_error_handler = XSetErrorHandler(x11_error_handler);\n    trapped_x11_error_code = 0;\n}\n\nint x11_untrap_errors()\n{\n    XSetErrorHandler(old_x11_error_handler);\n    return trapped_x11_error_code;\n}\n\nstatic Window timestamp_wnd;\nstatic Atom timestamp_atom = None;\n\nBool x11_wait_for_timestamp(Display *, XEvent *xevent, XPointer data)\n{\n    return ((xevent->type == PropertyNotify\n                && xevent->xproperty.window == *((Window *)data)\n                && xevent->xproperty.atom == timestamp_atom)\n        || (xevent->type == DestroyNotify\n            && xevent->xdestroywindow.window == *((Window *)data)));\n}\n\nTime x11_get_server_timestamp(Display *dpy, Window wnd)\n{\n    unsigned char c = 's';\n    XEvent xevent;\n\n    if (timestamp_atom == None)\n        timestamp_atom = XInternAtom(dpy, \"STALONETRAY_TIMESTAMP\", False);\n\n    x11_ok(); /* Just reset the status (XXX) */\n    /* Trigger PropertyNotify event which has a timestamp field */\n    XChangeProperty(\n        dpy, wnd, timestamp_atom, timestamp_atom, 8, PropModeReplace, &c, 1);\n    if (!x11_ok()) return CurrentTime;\n\n    /* Wait for the event\n     * XXX: this may block... */\n    timestamp_wnd = wnd;\n    XIfEvent(dpy, &xevent, x11_wait_for_timestamp, (XPointer)&timestamp_wnd);\n\n    return x11_ok() ? xevent.xproperty.time : CurrentTime;\n}\n\nint x11_get_window_prop32(Display *dpy, Window dst, Atom atom, Atom type,\n    unsigned char **data, unsigned long *len)\n{\n    Atom act_type;\n    int act_fmt, rc;\n    unsigned long bytes_after, prop_len, buf_len;\n    unsigned char *buf = NULL;\n\n    *data = NULL;\n    *len = 0;\n    /* Get the property size */\n    rc = XGetWindowProperty(dpy, dst, atom, 0L, 1L, False, type, &act_type,\n        &act_fmt, &buf_len, &bytes_after, &buf);\n\n    /* The requested property does not exist */\n    if (!x11_ok() || rc != Success || act_type != type || act_fmt != 32)\n        return FAILURE;\n\n    if (buf != NULL) XFree(buf);\n\n    /* Now go get the property */\n    prop_len = bytes_after / 4 + 1;\n    XGetWindowProperty(dpy, dst, atom, 0L, prop_len, False, type, &act_type,\n        &act_fmt, &buf_len, &bytes_after, &buf);\n\n    if (x11_ok()) {\n        *len = buf_len;\n        *data = buf;\n        return SUCCESS;\n    } else {\n        return FAILURE;\n    }\n}\n\nint x11_send_client_msg32(Display *dpy, Window dst, Window wnd, Atom type,\n    long data0, long data1, long data2, long data3, long data4)\n{\n    XEvent ev;\n    Status rc;\n    ev.xclient.type = ClientMessage;\n    ev.xclient.serial = 0;\n    ev.xclient.send_event = True;\n    ev.xclient.message_type = type;\n    ev.xclient.window = wnd;\n    ev.xclient.format = 32;\n    ev.xclient.data.l[0] = data0;\n    ev.xclient.data.l[1] = data1;\n    ev.xclient.data.l[2] = data2;\n    ev.xclient.data.l[3] = data3;\n    ev.xclient.data.l[4] = data4;\n    /* XXX: Replace 0xFFFFFF for better portability? */\n    /* XXX: This should actually read NoEventMask...\n     * seems like extra parameter is necessary */\n    rc = XSendEvent(dpy, dst, False, 0xFFFFFF, &ev);\n    return x11_ok() && rc != 0;\n}\n\nint x11_send_visibility(Display *, Window dst, long state)\n{\n    XEvent xe;\n    int rc;\n    xe.type = VisibilityNotify;\n    xe.xvisibility.window = dst;\n    xe.xvisibility.state = state;\n    rc = XSendEvent(tray_data.dpy, dst, True, NoEventMask, &xe);\n    return x11_ok() && rc != 0;\n}\n\nint x11_send_button(Display *dpy, int press, Window dst, Window root,\n    Time time, unsigned int button, unsigned int state, int x, int y)\n{\n    XEvent ev;\n    int rx, ry, rc, idummy;\n    unsigned int udummy;\n    Window dst_root;\n\n    if (!x11_get_window_abs_coords(dpy, dst, &rx, &ry)) return FAILURE;\n    XGetGeometry(dpy, dst, &dst_root, &idummy, &idummy, &udummy, &udummy,\n        &udummy, &udummy);\n    if (!x11_ok()) return FAILURE;\n\n    ev.type = press ? ButtonPress : ButtonRelease;\n    ev.xbutton.display = dpy;\n    ev.xbutton.window = dst;\n    ev.xbutton.subwindow = None;\n    ev.xbutton.root = root;\n    ev.xbutton.time = time;\n    ev.xbutton.x = x;\n    ev.xbutton.y = y;\n    ev.xbutton.x_root = rx + x;\n    ev.xbutton.y_root = ry + y;\n    ev.xbutton.button = button;\n    ev.xbutton.state = state;\n    ev.xbutton.same_screen = (root == dst_root);\n\n    rc = XSendEvent(dpy, dst, True,\n        SubstructureNotifyMask | (press ? ButtonPressMask : ButtonReleaseMask),\n        &ev);\n    return rc && x11_ok();\n}\n\nint x11_send_expose(\n    Display *, Window dst, int x, int y, int width, int height)\n{\n    XEvent xe;\n    int rc;\n    xe.type = Expose;\n    xe.xexpose.window = dst;\n    xe.xexpose.x = x;\n    xe.xexpose.y = y;\n    xe.xexpose.width = width;\n    xe.xexpose.height = height;\n    xe.xexpose.count = 0;\n    rc = XSendEvent(tray_data.dpy, dst, True, NoEventMask, &xe);\n    return x11_ok() && rc != 0;\n}\n\nint x11_refresh_window(\n    Display *, Window dst, int width, int height, int exposures)\n{\n    x11_send_visibility(tray_data.dpy, dst, VisibilityFullyObscured);\n    x11_send_visibility(tray_data.dpy, dst, VisibilityUnobscured);\n    XClearArea(tray_data.dpy, dst, 0, 0, width, height, exposures);\n    return x11_ok();\n}\n\nint x11_set_window_size(Display *dpy, Window w, int x, int y)\n{\n    XSizeHints xsh;\n    xsh.flags = PSize;\n    xsh.width = x;\n    xsh.height = y;\n    XSetWMNormalHints(dpy, w, &xsh);\n    XResizeWindow(dpy, w, x, y);\n    if (!x11_ok()) return FAILURE;\n    return SUCCESS;\n}\n\nint x11_get_window_size(Display *dpy, Window w, int *x, int *y)\n{\n    XWindowAttributes xwa;\n    XGetWindowAttributes(dpy, w, &xwa);\n    if (!x11_ok()) return FAILURE;\n    *x = xwa.width;\n    *y = xwa.height;\n    return SUCCESS;\n}\n\nint x11_get_window_min_size(Display *dpy, Window w, int *x, int *y)\n{\n    XSizeHints xsh;\n    long flags = 0;\n    int rc = FAILURE;\n    if (XGetWMNormalHints(dpy, w, &xsh, &flags)) {\n        flags = xsh.flags & flags;\n        if (flags & PMinSize) {\n            *x = xsh.min_width;\n            *y = xsh.min_height;\n            rc = SUCCESS;\n        }\n    }\n    return x11_ok() && rc;\n}\n\nint x11_get_window_abs_coords(Display *dpy, Window dst, int *x, int *y)\n{\n    XWindowAttributes xwa;\n    Window child;\n#if 0\n\tx11_ok(); /* XXX: this should go away, shouldn't it? */\n#endif\n    XGetWindowAttributes(dpy, dst, &xwa);\n    XTranslateCoordinates(dpy, dst, xwa.root, 0, 0, x, y, &child);\n    return x11_ok();\n}\n\nchar *x11_get_window_name(Display *dpy, Window dst, char *def)\n{\n    static char *name;\n    if (name != NULL) XFree(name);\n    if (!XFetchName(dpy, dst, &name)) name = NULL;\n    return (name != NULL ? name : def);\n}\n\nWindow x11_find_subwindow_by_name(Display *dpy, Window tgt, char *name)\n{\n    char *tgt_name = NULL;\n    Window ret = None, *children, dummy;\n    unsigned int i, nchildren;\n    if (XFetchName(dpy, tgt, &tgt_name)) {\n        LOG_TRACE((\"tgt_name=\\\"%s\\\", name=\\\"%s\\\"\\n\", tgt_name, name));\n        if (!strcmp(tgt_name, name)) ret = tgt;\n    }\n    if (!x11_ok()) return None;\n    if (tgt_name != NULL) XFree(tgt_name);\n    if (ret != None) return ret;\n    XQueryTree(dpy, tgt, &dummy, &dummy, &children, &nchildren);\n    if (!x11_ok()) return None;\n    for (i = 0; i < nchildren; i++) {\n        ret = x11_find_subwindow_by_name(dpy, children[i], name);\n        if (ret != None) break;\n    }\n    if (children != NULL) XFree(children);\n    return ret;\n}\n\n/* x and y are relative to top at input, and relative to found window on return\n */\nWindow x11_find_subwindow_at(\n    Display *dpy, Window top, int *x, int *y, int depth)\n{\n    int d, c, bx = 0, by = 0;\n    Window dummy, *children;\n    unsigned int nchildren;\n    Window cur = top, old = None;\n    for (d = 1; d != depth && cur != old && old != None; d++) {\n        old = cur;\n        /* Query children of current window and traverse them starting\n         * from the top in stacking order (i.e. from the end of the list\n         * returned by XQueryTree) */\n        XQueryTree(dpy, cur, &dummy, &dummy, &children, &nchildren);\n        LOG_TRACE((\"cur=0x%lx nchildren=%d\\n\", cur, nchildren));\n        if (!x11_ok()) goto fail;\n        /* Exit the loop if window has no children */\n        if (nchildren == 0) break;\n        /* Loop over children starting from topmost */\n        for (c = nchildren - 1; c >= 0; c--) {\n            XWindowAttributes xwa;\n            XGetWindowAttributes(dpy, children[c], &xwa);\n            if (!x11_ok()) goto fail;\n            /* Check if this child contains the (x,y) point */\n            if (xwa.x + bx <= *x && *x < xwa.x + xwa.width + bx\n                && xwa.y + by <= *y && *y < xwa.y + xwa.height + by) {\n                cur = children[c];\n                bx += xwa.x;\n                by += xwa.y;\n                break;\n            }\n        }\n        if (children != NULL) XFree(children);\n    }\n    *x -= bx;\n    *y -= by;\n    return cur;\nfail:\n    if (children != NULL) XFree(children);\n    return None;\n}\n\nvoid x11_extend_root_event_mask(Display *dpy, long mask)\n{\n    static long old_mask = 0;\n    old_mask |= mask;\n    XSelectInput(dpy, DefaultRootWindow(dpy), old_mask);\n}\n\nint x11_parse_color(Display *dpy, char *str, XColor *color)\n{\n    int rc;\n    rc = XParseColor(\n        dpy, XDefaultColormap(dpy, DefaultScreen(dpy)), str, color);\n    if (rc) XAllocColor(dpy, XDefaultColormap(dpy, DefaultScreen(dpy)), color);\n    return x11_ok() && rc;\n}\n\nchar *x11_get_window_class(Display *dpy, Window w) {\n    XClassHint hint;\n    Status res = 0;\n    char *classname = NULL;\n\n    res = XGetClassHint(dpy, w, &hint);\n    if (res == 0) {\n        LOG_TRACE((\"XGetClassHint(dpy, 0x%lx, &hint) failed: %x\", w, res));\n        return NULL;\n    }\n\n    classname = strdup(hint.res_class);\n    XFree(hint.res_name);\n    XFree(hint.res_class);\n\n    return classname;\n}\n\n#ifdef DEBUG\nconst char *x11_event_names[LASTEvent] = {\"unknown0\", \"unknown1\", \"KeyPress\",\n    \"KeyRelease\", \"ButtonPress\", \"ButtonRelease\", \"MotionNotify\",\n    \"EnterNotify\", \"LeaveNotify\", \"FocusIn\", \"FocusOut\", \"KeymapNotify\",\n    \"Expose\", \"GraphicsExpose\", \"NoExpose\", \"VisibilityNotify\", \"CreateNotify\",\n    \"DestroyNotify\", \"UnmapNotify\", \"MapNotify\", \"MapRequest\",\n    \"ReparentNotify\", \"ConfigureNotify\", \"ConfigureRequest\", \"GravityNotify\",\n    \"ResizeRequest\", \"CirculateNotify\", \"CirculateRequest\", \"PropertyNotify\",\n    \"SelectionClear\", \"SelectionRequest\", \"SelectionNotify\", \"ColormapNotify\",\n    \"ClientMessage\", \"MappingNotify\"};\n\nvoid x11_dump_win_info(Display *dpy, Window wid)\n{\n#if defined(DEBUG) && defined(_ST_WITH_DUMP_WIN_INFO)\n    if (settings.log_level >= LOG_LEVEL_TRACE) {\n        char cmd[PATH_MAX];\n        int rc;\n        snprintf(cmd, PATH_MAX,\n            \"xwininfo -tree -size -bits -stats -id 0x%x 1>&2\\n\",\n            (unsigned int)wid);\n        rc = system(cmd);\n        if (rc != 0) LOG_ERROR((\"command failed: '%s'\\n\", cmd));\n        snprintf(cmd, PATH_MAX, \"xprop -id 0x%x 1>&2\\n\", (unsigned int)wid);\n        rc = system(cmd);\n        if (rc != 0) LOG_ERROR((\"command failed: '%s'\\n\", cmd));\n    }\n#endif\n}\n#endif\n"
  },
  {
    "path": "src/xutils.h",
    "content": "/* ************************************\n * vim:tabstop=4:shiftwidth=4\n * xutils.h\n * Sun, 05 Mar 2006 17:16:44 +0600\n * ************************************\n * misc X11 utilities\n * ************************************/\n\n#ifndef _XUTILS_H_\n#define _XUTILS_H_\n\n#include <X11/X.h>\n#include <X11/Xatom.h>\n\n#include \"common.h\"\n#include \"icons.h\"\n\n/* Returns 1 if connection is active, 0 otherwise */\nint x11_connection_status();\n\n/* Return current server timestamp */\nTime x11_get_server_timestamp(Display *dpy, Window wnd);\n\n/* Convinient way to send a client message event */\nint x11_send_client_msg32(Display *dpy, Window dst, Window wnd, Atom type,\n    long data0, long data1, long data2, long data3, long data4);\n\n/* Same for visibility event */\nint x11_send_visibility(Display *dpy, Window dst, long state);\n\n/* Same for expose event */\nint x11_send_expose(\n    Display *dpy, Window dst, int x, int y, int width, int height);\n\n/* Same for button event */\nint x11_send_button(Display *dpy, int press, Window dst, Window root,\n    Time time, unsigned int button, unsigned int state, int x, int y);\n\n/* Refresh window */\nint x11_refresh_window(\n    Display *dpy, Window dst, int width, int height, int exposures);\n\n/* Set window size updating its size hints */\nint x11_set_window_size(Display *dpy, Window w, int x, int y);\n\n/* Get window size (uses XGetWindowAttributes) */\nint x11_get_window_size(Display *dpy, Window w, int *x, int *y);\n\n/* Get window minimal size hints if they are available */\nint x11_get_window_min_size(Display *dpy, Window w, int *x, int *y);\n\n/* Retrive 32-bit property from the target window */\nint x11_get_window_prop32(Display *dpy, Window dst, Atom atom, Atom type,\n    unsigned char **data, unsigned long *len);\n\n/* Retrive window-list property from the specified window */\n#define x11_get_winlist_prop(dpy, dst, atom, data, len) \\\n    x11_get_window_prop32(dpy, dst, atom, XA_WINDOW, data, len)\n\n/* Shortcut for the root window case */\n#define x11_get_root_winlist_prop(dpy, atom, data, len) \\\n    x11_get_winlist_prop(dpy, DefaultRootWindow(dpy), atom, data, len)\n\n/* Returns window absolute position (relative to the root window) */\nint x11_get_window_abs_coords(Display *dpy, Window dst, int *x, int *y);\n\n/* Get window name. NOT THREAD SAFE. Returns pointer to static buffer */\nchar *x11_get_window_name(Display *dpy, Window dst, char *def);\n\n/* Get window class name. Returns pointer to string created with strdup() */\nchar *x11_get_window_class(Display *dpy, Window w);\n\n/* Find subwindow by name */\nWindow x11_find_subwindow_by_name(Display *dpy, Window tgt, char *name);\n\n/* Find subwindow by at coords */\nWindow x11_find_subwindow_at(\n    Display *dpy, Window top, int *x, int *y, int depth);\n\n/* Extends event mask of the root window */\nvoid x11_extend_root_event_mask(Display *dpy, long mask);\n\n/* Parse text representation of a color */\nint x11_parse_color(Display *dpy, char *str, XColor *color);\n\n/* Checks if any X11 errors have occured so far. */\n/* ACHTUNG!!! after any sequence X operations any of which\n * that might have failed, you _must_ call x11_ok(), since\n * it resets the internal error flag. If you dont, it will show\n * up as a error elsewhere. JFYI, always check for x11_ok() first,\n * since\n * \t\tif (!rc || x11_ok()) { fail; }\n * is likely to leave error condition on: x11_ok() wont be called\n * if rc != 0. */\n#define x11_ok() x11_ok_helper(__FILE__, __LINE__, __FUNC__)\nint x11_ok_helper(const char *file, const int line, const char *func);\n\n/* WARNING: following functions do not support nested calls */\n\n/* Installs custom X11 error handler */\nvoid x11_trap_errors();\n\n/* Removes custom X11 error handler */\nint x11_untrap_errors();\n\n#ifdef DEBUG\n\n/* Array that maps event_number -> event_name */\nconst extern char *x11_event_names[LASTEvent];\n\n/* Dumps window info. Does nothing unless _ST_WITH_DUMP_WIN_INFO is defined,\n * launches xwininfo and xwinprop otherwise */\nvoid x11_dump_win_info(Display *dpy, Window w);\n\n#else\n\n/* Dummy delcaration */\n#define x11_dump_win_info(dpy, w) \\\n    do { \\\n    } while (0);\n#endif\n\n#endif\n"
  },
  {
    "path": "stalonetray.xml.in",
    "content": "<?xml version=\"1.0\" encoding=\"latin1\" standalone=\"no\"?>\n<!--\n    vim: et:tw=80\n-->\n\n<!DOCTYPE refentry PUBLIC\n  \"-//OASIS//DTD DocBook V4.4//EN\"\n  \"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd\"\n>\n\n<refentry id=\"stalonetray\">\n\n<info>\n    <author>\n        <personname>\n            <firstname>Roman</firstname>\n            <surname>Dubtsov</surname>\n        </personname>\n        <personblurb></personblurb>\n    </author>\n</info>\n\n<refmeta>\n  <refentrytitle>stalonetray</refentrytitle>\n  <manvolnum>1</manvolnum>\n  <refmiscinfo class=\"software\">stalonetray</refmiscinfo>\n  <refmiscinfo class=\"source\">stalonetray</refmiscinfo>\n  <refmiscinfo class=\"version\">@VERSION_STR@</refmiscinfo>\n  <refmiscinfo class=\"manual\">User Commands</refmiscinfo>\n</refmeta>\n\n<refnamediv>\n  <refname>stalonetray</refname>\n  <refpurpose><emphasis>sta</emphasis>nd-<emphasis>alone</emphasis>\n  system tray (notification area) implementation. This document covers\n  @VERSION_STR@ version of stalonetray.</refpurpose>\n</refnamediv>\n\n<refsynopsisdiv>\n  <cmdsynopsis>\n  <command>stalonetray</command>\n  <arg rep=\"repeat\"><replaceable>option</replaceable></arg>\n  </cmdsynopsis>\n</refsynopsisdiv>\n\n<refsect1><title>DESCRIPTION</title>\n\n  <para>Stalonetray is a stand-alone system tray (notification area)\n  for X Window System/X11 (e.g. XOrg or XFree86). It has minimal build\n  and run-time dependencies: an X11 lib only. Complete\n  <abbrev>XEMBED</abbrev> support is under development. Stalonetray\n  works with virtually any <abbrev>EWMH</abbrev>-compliant window\n  manager.</para>\n\n  <para>The behaviour and the look of stalonetray can be configured either via\n  command line options or via configuration file. As usual, command-line options\n  have precedence over options that are specified in the configuration\n  file.</para>\n\n  <para>Names of command line parameter may have two variants: short\n  (<option>-o</option>) and long (<option>--option</option>).\n  Write <option>-o</option><replaceable>value</replaceable> or\n  <option>-o</option> <replaceable>value</replaceable> to pass a value\n  using the short name of a parameter; to pass a value using a long name, write\n  <option>--option</option> <replaceable>value</replaceable> or\n  <option>--option</option>=<replaceable>value</replaceable>. All\n  flag-like parameters have optional boolean value that when ommited is assumed\n  to be \"true\". Write \"true\", \"yes\", \"1\", for positive boolean values, and\n  \"false\", \"no\", \"0\" for negative ones.</para>\n\n  <para>Default configuration file is <filename>$HOME/.stalonetrayrc</filename>.\n  If it is not found, then <filename>$XDG_CONFIG_HOME/stalonetrayrc</filename>\n  is checked for. A configuration file contains case-insensetive\n  keyword-argument pairs, one per line. Lines starting with '#' and empty\n  lines are ignored. Alternatively, confiuration file can specified via\n  <option>-c</option> or <option>--config</option> command-line options.</para>\n\n  <para>Stalonetray can be configured to write diagnostic log messages to the\n  standard error stream.</para>\n\n  <para>Below is the list of possible command line/configuration file options.\n  Options starting with hyphens are command-line parameters others are\n  configuration file keywords.  Options that are new in @VERSION_STR@ version\n  are marked with \"NEW in @VERSION_STR@\". </para>\n</refsect1>\n\n<refsect1 id=\"opt\">\n  <title>OPTIONS</title>\n\n  <variablelist>\n\n  <varlistentry>\n  <term><option>-bg</option> <replaceable>color</replaceable></term>\n  <term><option>--background</option> <replaceable>color</replaceable></term>\n  <term><option>background</option> <replaceable>color</replaceable></term>\n  <listitem><para>Use <replaceable>color</replaceable> for tray`s background.\n  <replaceable>color</replaceable> can be specified as an <abbrev>HTML</abbrev> hex triplet or\n  as a name from rgb.txt (note that '#' must be quoted). Default value: <userinput>#777777</userinput>.\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>-c</option> <replaceable>filename</replaceable></term>\n  <term><option>--config</option> <replaceable>filename</replaceable></term>\n  <listitem><para>Read configuration from\n  <replaceable>filename</replaceable> instead of default\n  <filename>$HOME/.stalonetrayrc</filename>.</para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>-d</option> <replaceable>decspec</replaceable></term>\n  <term><option>--decorations</option> <replaceable>decspec</replaceable></term>\n  <term><option>decorations</option> <replaceable>decspec</replaceable></term>\n  <listitem><para>Specify visiblie tray window decorations. Possible values for\n  <replaceable>decspec</replaceable> are: <userinput>all</userinput>,\n  <userinput>title</userinput>, <userinput>border</userinput>,\n  <userinput>none</userinput> (default).\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>-display</option> <replaceable>display</replaceable></term>\n  <term><option>display</option> <replaceable>display</replaceable>\n  </term>\n  <listitem><para>Use X display <replaceable>display</replaceable>.</para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--dockapp-mode</option>\n  <optional><replaceable>mode</replaceable></optional></term>\n  <term><option>dockapp_mode</option>\n  <optional><replaceable>mode</replaceable></optional></term>\n  <listitem><para>Set dockapp mode, which can be either\n  <userinput>simple</userinput> for e.g. OpenBox, <userinput>wmaker</userinput>\n  for WindowMaker, or <userinput>none</userinput> (default).\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>-f</option> <optional><replaceable>level</replaceable></optional></term>\n  <term><option>--fuzzy-edges</option> <optional><replaceable>level</replaceable></optional></term>\n  <term><option>fuzzy_edges</option> <optional><replaceable>level</replaceable></optional></term>\n  <listitem><para>Enable fuzzy edges of tray window and set fuzziness level\n  which can range from <userinput>0</userinput> (disabled, default) to <userinput>3</userinput>.\n  When ommited, the value of <replaceable>level</replaceable> defaults to 2.\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--geometry</option> <replaceable>geometry_spec</replaceable></term>\n  <term><option>geometry</option> <replaceable>geometry_spec</replaceable></term>\n  <listitem><para>Set tray`s initial geometry to <replaceable>geometry_spec</replaceable>,\n  specified in standard X notation:\n  <replaceable>width</replaceable>x<replaceable>height</replaceable>[+<replaceable>x</replaceable>[+<replaceable>y</replaceable>]],\n  where width and height are specified in icon slot multiples. Default value: <userinput>1x1+0-0</userinput>.\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--grow-gravity</option>\n  <replaceable>gravity</replaceable></term> <term><option>grow_gravity</option>\n  <replaceable>gravity</replaceable></term> <listitem><para>Specify icon\n  positioning gravity (eigher <userinput>N</userinput>,\n  <userinput>S</userinput>, <userinput>W</userinput>, <userinput>E</userinput>,\n  <userinput>NW</userinput>, <userinput>NE</userinput>,\n  <userinput>SW</userinput>, <userinput>SE</userinput>). Grow gravity specifies\n  directions in which the tray's window may grow. For instance, if you specify\n  <userinput>NW</userinput> the tray's window will grow down vertically and to\n  the right horizontally (these are sides that are opposite to upper-left or\n  North-West corner of the window); with <userinput>W</userinput> the tray's\n  window will grow horizontally to the left side only, and it will vertically\n  grow both upwards and downwards maintaining position of its center. Please\n  note that the latter behaviour is new in 0.8. Default value:\n  <userinput>NW</userinput>. </para></listitem> </varlistentry>\n\n  <varlistentry>\n  <term><option>--icon-gravity</option> <replaceable>gravity</replaceable></term>\n  <term><option>icon_gravity</option> <replaceable>gravity</replaceable></term>\n  <listitem><para>Specify icon positioning gravity (either\n  <userinput>NW</userinput>, <userinput>NE</userinput>,\n  <userinput>SW</userinput>, <userinput>SE</userinput>). If you specify, e.g\n  <userinput>SW</userinput>, then icons will appear starting from the lower-left\n  corner of the tray's window. Default value: <userinput>NW</userinput>.\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>-i</option> <replaceable>n</replaceable></term>\n  <term><option>--icon-size</option> <replaceable>n</replaceable></term>\n  <term><option>icon_size</option> <replaceable>n</replaceable></term>\n  <listitem><para>Set default icon size to <replaceable>n</replaceable>. Default\n  value: <userinput>24</userinput>. Minimum: <userinput>16</userinput>.\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>-h</option></term>\n  <term><option>--help</option></term>\n  <listitem><para>Show help message.</para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>ignore_classes</option> <replaceable>classes</replaceable></term>\n  <listitem><para>Set X11 window class names to be ignored by stalonetray.\n  Class names are separate arguments: <userinput>class1 class2 ...</userinput>.\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--log-level</option> <replaceable>level</replaceable></term>\n  <term><option>log_level</option> <replaceable>level</replaceable></term>\n  <listitem><para>Set the amount of info to be output by stalonetray to the\n  standard output. Possible values for <replaceable>level</replaceable>:\n  <userinput>err</userinput> (default), <userinput>info</userinput>,\n  and <userinput>trace</userinput>. For the <userinput>trace</userinput>\n  option to be available, stalonetray must be configured with\n  <option>--enable-debug</option> at build-time.</para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--kludges</option>\n  <replaceable>kludge</replaceable><arg rep=\"repeat\">,<replaceable>kludge</replaceable></arg></term>\n  <listitem><para>\n  Enable specific kludges to work around non-conforming WMs and/or stalonetray\n  bugs. Argument is a comma-separated list of:</para>\n  <itemizedlist>\n  <listitem><para><userinput>fix_window_pos</userinput> &mdash; fix tray window\n  position on erroneous moves by WM</para></listitem>\n  <listitem><para><userinput>force_icons_size</userinput> &mdash; ignore resize\n  events on all icons; force their size to be equal to\n  <option>icon_size</option></para></listitem>\n  <listitem><para><userinput>use_icons_hints</userinput> &mdash; use icon window\n  hints to determine icon size</para></listitem>\n  </itemizedlist>\n  </listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--max-geometry</option>\n  <replaceable>geometry_spec</replaceable></term>\n  <term><option>max_geometry</option> <replaceable>geometry_spec</replaceable></term>\n  <listitem><para>Set tray`s maximal geometry to <replaceable>geometry_spec</replaceable>\n  Default value: <userinput>0x0</userinput>, no limit.\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>-m</option> <replaceable>n</replaceable></term>\n  <term><option>--monitor</option> <replaceable>n</replaceable></term>\n  <term><option>monitor</option> <replaceable>n</replaceable></term>\n  <listitem><para>Set target monitor to <replaceable>n</replaceable>. Default\n  value: <userinput>0</userinput>.\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--no-shrink</option></term>\n  <term><option>no_shrink</option> <optional><replaceable>bool</replaceable></optional></term>\n  <listitem><para>Do not shrink tray window back after icon removal. Useful when\n  tray is swallowed by another window like FvwmButtons. Default value: <userinput>false</userinput>.\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>-p</option></term>\n  <term><option>--parent-bg</option></term>\n  <term><option>parent_bg</option> <optional><replaceable>bool</replaceable></optional></term>\n  <listitem><para>Use the parent's window as a background of the tray's window.\n  Default value: <userinput>false</userinput>.\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--pixmap-bg <replaceable>path_to_xpm</replaceable></option></term>\n  <term><option>pixmap_bg</option> <replaceable>path_to_xpm</replaceable></term>\n  <listitem><para>Use the pixmap from an XPM file specified by\n  <replaceable>path_to_xpm</replaceable> for the tray`s window background (pixmap will be\n  tiled to fill the entire window).\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>-r</option> <replaceable>name</replaceable></term>\n  <term><option>--remote-click-icon</option> <replaceable>name</replaceable></term>\n  <listitem><para>\n  Remote control/click. When this option is specified, stalonetray sends a fake\n  click to the icon with a window named <replaceable>name</replaceable> and\n  exits. The icon is searched for in the currently active tray for the current\n  screen. By default, stalonetray sends a single click with the 1st mouse button\n  to the center of the icon. See the options below for additional information on\n  how to alter the defaults.\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--remote-click-button</option> <replaceable>n</replaceable></term>\n  <listitem><para>\n  Sets the remote click's button number to <replaceable>n</replaceable> (in the\n  X11 numbering order).\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--remote-click-position</option> <replaceable>x</replaceable>x<replaceable>y</replaceable></term>\n  <listitem><para>\n  Sets the remote click's position.\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--remote-click-type</option> <replaceable>type</replaceable></term>\n  <listitem><para>\n  Sets the remote click's type. Possible values: <userinput>single</userinput>\n  and <userinput>double</userinput>.\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>-s</option> <replaceable>n</replaceable></term>\n  <term><option>--slot-size</option> <replaceable>w</replaceable><optional>x<replaceable>w</replaceable></optional></term>\n  <term><option>slot_size</option> <replaceable>w</replaceable><optional>x<replaceable>w</replaceable></optional></term>\n  <listitem><para>Set grid slot width to <replaceable>w</replaceable> and height\n  to <replaceable>b</replaceable>, both of which cannot be less then\n  <option>icon_size</option>. By default, the slot size is the same as the icon\n  size. If omitted, height is set to be same as width.\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--scrollbars</option> <replaceable>mode</replaceable></term>\n  <term><option>scrollbars</option> <replaceable>mode</replaceable></term>\n  <listitem><para>Set scrollbar mode. Possible values:\n  <userinput>vertical</userinput>, <userinput>horizontal</userinput>,\n  <userinput>all</userinput>, or <userinput>none</userinput> (default).\n  Scrollbars appear as additional space at tray borders that can be clicked to\n  scroll icon area. Mouse wheel also\n  works.\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--scrollbars-highlight</option> <replaceable>mode</replaceable></term>\n  <term><option>scrollbars_highlight</option> <replaceable>mode</replaceable></term>\n  <listitem><para>Set scrollbars highlight mode. Possible values: a color spec,\n  or <userinput>disable</userinput>.\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--scrollbars-size</option> <replaceable>n</replaceable></term>\n  <term><option>scrollbars_size</option> <replaceable>n</replaceable></term>\n  <listitem><para>Set scrollbar size to <userinput>n</userinput> pixels.\n  By default, the size is 1/4 of <option>slot_size</option>.\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--scrollbars-step</option> <replaceable>n</replaceable></term>\n  <term><option>scrollbars_step</option> <replaceable>n</replaceable></term>\n  <listitem><para>Set scrollbar step to <userinput>n</userinput> pixels.\n  Default is 1/2 of <option>slot_size</option>.\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--scroll-everywhere</option></term>\n  <term><option>scroll_everywhere</option> <optional><replaceable>bool</replaceable></optional></term>\n  <listitem><para>Enable scrolling outside of the scrollbars too. Default value:\n  <userinput>false</userinput>.\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--skip-taskbar</option></term>\n  <term><option>skip_taskbar</option> <optional><replaceable>bool</replaceable></optional></term>\n  <listitem><para>Hide tray`s window from the taskbar. Default value:\n  <userinput>false</userinput>.\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--sticky</option></term>\n  <term><option>sticky</option> <optional><replaceable>bool</replaceable></optional></term>\n  <listitem><para>Make tray`s window sticky across multiple desktops/pages.\n  Default value: <userinput>false</userinput>.\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--tint-color</option></term>\n  <term><option>tint_color</option> <optional><replaceable>bool</replaceable></optional></term>\n  <listitem><para>Set tinting color. Default value:\n  <userinput>white</userinput>.\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--tint-level</option></term>\n  <term><option>tint_level</option> <optional><replaceable>level</replaceable></optional></term>\n  <listitem><para>Set tinting level. Default value: <userinput>0</userinput>\n  (tinting disabled).\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>-t</option></term>\n  <term><option>--transparent</option></term>\n  <term><option>transparent</option> <optional><replaceable>bool</replaceable></optional></term>\n  <listitem><para>Enable root transparency. Default value:\n  <userinput>false</userinput>.\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>-v</option></term>\n  <term><option>--vertical</option></term>\n  <term><option>vertical</option> <optional><replaceable>bool</replaceable></optional></term>\n  <listitem><para>Use vertical layout of icons (horizontal is used by default).</para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--window-layer</option> <replaceable>layer</replaceable></term>\n  <term><option>window_layer</option> <replaceable>layer</replaceable></term>\n  <listitem><para>Sets the <abbrev>EWMH</abbrev>-compliant layer of tray`s window. Possible values for <replaceable>layer</replaceable>:\n  <userinput>bottom</userinput>, <userinput>normal</userinput>, <userinput>top</userinput>. Default value: <userinput>normal</userinput>.\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--window-strut</option> <replaceable>mode</replaceable></term>\n  <term><option>window_strut</option> <replaceable>mode</replaceable></term>\n  <listitem><para>Enable window struts for tray window (to avoid covering of tray window by\n  maximized windows). Mode defines to which screen border tray window will be\n  attached. It can be either <userinput>top</userinput>,\n  <userinput>bottom</userinput>, <userinput>left</userinput>,\n  <userinput>right</userinput>, <userinput>none</userinput>, or\n  <userinput>auto</userinput> (default).\n  </para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--window-type</option> <replaceable>type</replaceable></term>\n  <term><option>window_type</option> <replaceable>type</replaceable></term>\n  <listitem><para>Sets the <abbrev>EWMH</abbrev>-compliant type of tray`s window. Possible values for <replaceable>type</replaceable>:\n  <userinput>desktop</userinput>, <userinput>dock</userinput>, <userinput>normal</userinput>, <userinput>toolbar</userinput>,\n  <userinput>utility</userinput>. Default value: <userinput>dock</userinput>.</para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--xsync</option></term>\n  <term><option>xsync</option> <optional><replaceable>bool</replaceable></optional></term>\n  <listitem><para>Operate on X server synchronously (<emphasis>SLOW</emphasis>, turned off by default).\n  </para></listitem>\n  </varlistentry>\n<!--\n  <varlistentry>\n  <term><option></option> <replaceable></replaceable></term>\n  <listitem><para><replaceable></replaceable>\n  </para></listitem>\n  </varlistentry>\n-->\n  </variablelist>\n</refsect1>\n\n<refsect1>\n  <title>DEPRECATIONS</title>\n  <para>As of stalonetray 0.8, the following command line and configuration file\n  parameters are deprecated:\n  </para>\n\n  <variablelist>\n\n  <varlistentry>\n  <term><option>--dbg-level</option></term>\n  <term><option>dbg_level</option></term>\n  <listitem><para>Please use <option>--log-level</option>\n  instead.</para></listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--max-tray-width</option></term>\n  <term><option>--max-tray-height</option></term>\n  <term><option>max_tray_width</option></term>\n  <term><option>max_tray_height</option></term>\n  <listitem><para>Please use <option>--max-geometry</option> instead.</para> </listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>-w</option></term>\n  <term><option>--withdrawn</option></term>\n  <term><option>withdrawn</option></term>\n  <listitem><para>Please use <option>--dockapp-mode</option> instead.</para> </listitem>\n  </varlistentry>\n\n  <varlistentry>\n  <term><option>--respect-icon-hints</option></term>\n  <term><option>respect_icon_hints</option></term>\n  <listitem><para>Please use <option>--kludges</option> with\n  <userinput>use_icon_hints</userinput> parameter instead.</para></listitem>\n  </varlistentry>\n\n  </variablelist>\n\n</refsect1>\n\n<refsect1>\n<title>FILES</title>\n<para><filename>$HOME/.stalonetrayrc</filename> &mdash; default configuration file.</para>\n</refsect1>\n\n<refsect1>\n<title>BUGS</title>\n<para>There are some, definetly.</para>\n\n<para>If you need support, the best way to get it is open an issue at the\nstalonetray <ulink url=\"https://github.com/kolbusa/stalonetray\">github\npage</ulink> or to e-mail me directly at\n<email>busa_ru@users.sourceforge.net</email>.</para>\n\n<para>If you have found a bug, please try to reproduce it with the log level set\nto <userinput>trace</userinput> and redirect standard error stream to a file.\nThen attach the file to a github issue or to an e-mail. If the issue is\nintermittent, attach two log files -- one with the good and one with the bad\nbehavior. If you have installed stalonetray from a package repository, you\ncan also file a bug in the respective bug tracking system.</para>\n\n</refsect1>\n</refentry>\n"
  },
  {
    "path": "stalonetrayrc.sample",
    "content": "# vim: filetype=config textwidth=80 expandtab\n#\n# This is sample ~/.stalonetrayrc, resembling default configuration.\n# Remember: command line parameters take precedence.\n#\n####################################################################\n#\n# stalonetray understands following directives\n#\n####################################################################\n\n# background <color>         # color can be specified as an HTML hex triplet or\n                             # as a name from rgb.txt, note that '#' must be\n                             # quoted\nbackground \"#777777\"\n\n# decorations <decspec>      # set trays window decorations; possible values for\n                             # decspec are: all, title, border, none\ndecorations none\n\n# display <display name>     # as usual\n\n# dockapp_mode <mode>        # set dockapp mode, which can be either simple (for\n                             # e.g. OpenBox, wmaker for WindowMaker, or none\n                             # (default).\ndockapp_mode none\n\n# fuzzy_edges [<level>]      # enable fuzzy edges and set fuzziness level. level\n                             # can be from 0 (disabled) to 3; this setting works\n                             # with tinting and/or transparent and/or pixmap\n                             # backgrounds\nfuzzy_edges 0\n\n# geometry <geometry>        # tray's geometry in standard X notation; width and\n                             # height are specified in slot_size multiples\ngeometry 1x1+0+0\n\n# grow_gravity <gravity>     # one of N, S, E, W, NW, NE, SW, SE; tray will grow\n                             # in the direction opposite to one specified by\n                             # grow_gravity; if horizontal or vertical\n                             # direction is not specified, tray will not grow in\n                             # that direction\ngrow_gravity NW\n\n# icon_gravity <gravity>     # icon placement gravity, one of NW, NE, SW, SE\nicon_gravity NW\n\n# icon_size <int>            # specifies dimensions of an icon\nicon_size 24\n\n# slot_size <w>[x<h>]        # specifies width and hight of an icon slot that\n                             # contains icon; must be larger than icon size;\n                             # height is set to width if it is omitted;\n                             # defaults to icon size\nslot_size 32x64\n\n# log_level <level>          # controls the amount of logging output, level can\n                             # be err (default), info, or trace (enabled only\n                             # when stalonetray configured with --enable-debug)\nlog_level err\n\n# kludges kludge[,kludge]    # enable specific kludges to work around\n                             # non-conforming WMs and/or stalonetray bugs.\n                             # Argument is a comma-separated list of\n                             # * fix_window_pos - fix tray window position on\n                             #     erroneous moves by WM\n                             # * force_icons_size - ignore resize events on all\n                             #     icons; force their size to be equal to\n                             #     icon_size\n                             # * use_icon_hints - use icon window hints to\n                             #     dtermine icon size\n\n# max_geometry <geometry>    # maximal tray dimensions; 0 in width/height means\n                             # no limit\nmax_geometry 0x0\n\n# monitor <int>              # specifies which monitor to draw the tray in\n# monitor 0\n\n# no_shrink [<bool>]         # disables shrink-back mode\nno_shrink false\n\n# parent_bg [<bool>]         # whether to use pseudo-transparency\n                             # (looks better when reparented into smth like\n                             #   FvwmButtons)\nparent_bg false\n\n# pixmap_bg <path_to_xpm>    # use pixmap from specified xpm file for (tiled)\n                             # background\n# pixmap_bg /home/user/.stalonetraybg.xpm\n\n# scrollbars <mode>          # enable/disable scrollbars; mode is either\n                             # vertical, horizontal, all or none (default)\nscrollbars none\n\n# scrollbars-size <size>     # scrollbars step in pixels; default is slot_size/4\n# scrollbars-step 8\n\n# scrollbars-step <step>     # scrollbars step in pixels; default is slot_size/2\n# scrollbars-step 32\n\n# slot_size <int>            # specifies size of icon slot, defaults to\n                             # icon_size.\n\n# skip_taskbar [<bool>]      # hide tray`s window from the taskbar\nskip_taskbar true\n\n# sticky [<bool>]            # make a tray`s window sticky across the\n                             # desktops/pages\nsticky true\n\n# tint_color <color>         # set tinting color\ntint_color white\n\n# tint_level <level>         # set tinting level; level ranges from 0 (disabled)\n                             # to 255\ntint_level 0\n\n# transparent [<bool>]       # whether to use root-transparency (background\n                             # image must be set with Esetroot or compatible\n                             # utility)\ntransparent false\n\n# vertical [<bool>]          # whether to use vertical layout (horisontal layout\n                             # is used by default)\nvertical false\n\n# window_layer <layer>       # set the EWMH-compatible window layer; one of:\n                             # bottom, normal, top\nwindow_layer normal\n\n# window_strut <mode>        # enable/disable window struts for tray window (to\n                             # avoid converting of tray window by maximized\n                             # windows); mode defines to which screen border\n                             # tray will be attached; it can be either top,\n                             # bottom, left, right, none or auto (default)\nwindow_strut auto\n\n# window_type <type>         # set the EWMH-compatible window type; one of:\n                             # desktop, dock, normal, toolbar, utility\nwindow_type dock\n\n# xsync [<bool>]             # whether to operate on X server synchronously\n                             # (SLOOOOW)\nxsync false\n"
  },
  {
    "path": "utils/get_props/main.c",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * main.c\n * Tue, 24 Aug 2004 12:00:24 +0700\n * -------------------------------\n * main is main\n * ------------------------------- */\n\n/* TODO: XEMBED Implementation wanted */\n\n#include <X11/Xatom.h>\n#include <X11/Xlib.h>\n#include <X11/Xutil.h>\n\n#include <stdio.h>\n#include <stdlib.h>\n\nint trapped_error_code = 0;\nstatic int (*old_error_handler)(Display *, XErrorEvent *);\n\nstatic int error_handler(Display *display, XErrorEvent *error)\n{\n    trapped_error_code = error->error_code;\n    return 0;\n}\n\nvoid trap_errors()\n{\n    trapped_error_code = 0;\n    old_error_handler = XSetErrorHandler(error_handler);\n}\n\nint untrap_errors()\n{\n    XSetErrorHandler(old_error_handler);\n    return trapped_error_code;\n}\n\nvoid die(char *msg)\n{\n    fprintf(stderr, \"%s\", msg);\n    exit(1);\n}\n\nint main(int argc, char **argv)\n{\n    Display *dpy;\n    Window wid;\n    XClassHint xch;\n\n    unsigned long flags;\n\n    char *tmp;\n\n    Atom *atoms;\n    int nprops, i;\n\n    wid = strtol(argv[1], &tmp, 0);\n\n    if ((dpy = XOpenDisplay(NULL)) == NULL) {\n        die(\"Error: could not open display\\n\");\n    }\n\n    XSynchronize(dpy, True);\n\n    atoms = XListProperties(dpy, wid, &nprops);\n\n    fprintf(stdout, \"ATOMS ----------------------------\\n\");\n\n    for (i = 0; i < nprops; i++) {\n        Atom act_type, *atom_list;\n        int act_fmt, j;\n        short *sints;\n        long *lints;\n        unsigned long prop_len, bytes_after;\n        unsigned char *prop, *aname;\n\n        tmp = XGetAtomName(dpy, atoms[i]);\n        fprintf(stdout, \"Atom #%d\\n  name: '%s'\\n\", i, tmp);\n\n        XGetWindowProperty(dpy, wid, atoms[i], 0L, 0L, False, AnyPropertyType,\n            &act_type, &act_fmt, &prop_len, &bytes_after, &prop);\n\n        prop_len = bytes_after / (act_fmt == 8 ? 1 : (act_fmt == 16 ? 2 : 4));\n\n        XGetWindowProperty(dpy, wid, atoms[i], 0L, prop_len, False, act_type,\n            &act_type, &act_fmt, &prop_len, &bytes_after, &prop);\n\n        fprintf(stdout, \"  size: %lu\\n\", prop_len);\n\n        switch (act_fmt) {\n        case 8:\n            fprintf(stdout, \"  possible content type is string: %s\\n\", prop);\n            break;\n        case 16:\n            fprintf(\n                stdout, \"  possible content type is list of short ints:\\n\");\n            sints = (short *)prop;\n            for (j = 0; j < prop_len; j++)\n                fprintf(stdout, \"    %s[%i] = %d\\n\", tmp, j, sints[j]);\n            break;\n        case 32:\n            fprintf(stdout, \"  possible content type is list of long ints:\\n\");\n            lints = (long *)prop;\n            for (j = 0; j < prop_len; j++)\n                fprintf(stdout, \"    %s[%i] = %ld\\n\", tmp, j, lints[j]);\n\n            fprintf(stdout, \"  possible content type is list atoms:\\n\");\n            atom_list = (Atom *)prop;\n            for (j = 0; j < prop_len; j++) {\n                trap_errors();\n                aname = (unsigned char *)XGetAtomName(dpy, atom_list[j]);\n                if (!untrap_errors() && aname != NULL) {\n                    fprintf(stdout, \"    %s[%i] = %s\\n\", tmp, j, aname);\n                    XFree(aname);\n                }\n            }\n\n            break;\n        default:\n            fprintf(stdout, \"  WOW: unknown format: %u\\n\", act_fmt);\n            break;\n        }\n\n        XFree(prop);\n        XFree(tmp);\n    }\n\n    fprintf(stdout, \"NAMES ----------------------------\\n\");\n\n    XGetClassHint(dpy, wid, &xch);\n\n    fprintf(\n        stdout, \"res_class: %s\\nres_name: %s\\n\", xch.res_class, xch.res_name);\n\n    fprintf(stdout, \"HINTS ----------------------------\\n\");\n    {\n        XSizeHints xsh;\n        XGetWMNormalHints(dpy, wid, &xsh, &flags);\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "utils/py-gtk-example/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016 Aleksander Alekseev\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "utils/py-gtk-example/README.md",
    "content": "# py-gtk-example"
  },
  {
    "path": "utils/py-gtk-example/gtk-example.glade",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- Generated with glade 3.16.1 -->\n<interface>\n  <requires lib=\"gtk+\" version=\"3.10\"/>\n  <object class=\"GtkMenu\" id=\"menu1\">\n    <property name=\"visible\">True</property>\n    <property name=\"can_focus\">False</property>\n    <child>\n      <object class=\"GtkMenuItem\" id=\"menuitem1\">\n        <property name=\"visible\">True</property>\n        <property name=\"can_focus\">False</property>\n        <property name=\"label\" translatable=\"yes\">Show pop-up</property>\n        <property name=\"use_underline\">True</property>\n        <signal name=\"activate\" handler=\"onNotify\" swapped=\"no\"/>\n      </object>\n    </child>\n    <child>\n      <object class=\"GtkMenuItem\" id=\"menuitem2\">\n        <property name=\"visible\">True</property>\n        <property name=\"can_focus\">False</property>\n        <property name=\"label\" translatable=\"yes\">Show/hide window</property>\n        <property name=\"use_underline\">True</property>\n        <signal name=\"activate\" handler=\"onShowOrHide\" swapped=\"no\"/>\n      </object>\n    </child>\n    <child>\n      <object class=\"GtkMenuItem\" id=\"menuitem3\">\n        <property name=\"visible\">True</property>\n        <property name=\"can_focus\">False</property>\n        <property name=\"label\" translatable=\"yes\">Quit</property>\n        <property name=\"use_underline\">True</property>\n        <signal name=\"activate\" handler=\"onQuit\" swapped=\"no\"/>\n      </object>\n    </child>\n  </object>\n  <object class=\"GtkWindow\" id=\"window1\">\n    <property name=\"can_focus\">False</property>\n    <property name=\"title\" translatable=\"yes\">GTK Test</property>\n    <property name=\"resizable\">False</property>\n    <signal name=\"delete-event\" handler=\"onQuit\" swapped=\"no\"/>\n    <child>\n      <object class=\"GtkNotebook\" id=\"notebook1\">\n        <property name=\"visible\">True</property>\n        <property name=\"can_focus\">True</property>\n        <child>\n          <object class=\"GtkBox\" id=\"box1\">\n            <property name=\"visible\">True</property>\n            <property name=\"can_focus\">False</property>\n            <property name=\"orientation\">vertical</property>\n            <child>\n              <object class=\"GtkBox\" id=\"box2\">\n                <property name=\"visible\">True</property>\n                <property name=\"can_focus\">False</property>\n                <child>\n                  <object class=\"GtkLabel\" id=\"label1\">\n                    <property name=\"visible\">True</property>\n                    <property name=\"can_focus\">False</property>\n                    <property name=\"label\" translatable=\"yes\">Text: </property>\n                  </object>\n                  <packing>\n                    <property name=\"expand\">False</property>\n                    <property name=\"fill\">True</property>\n                    <property name=\"position\">0</property>\n                  </packing>\n                </child>\n                <child>\n                  <object class=\"GtkEntry\" id=\"entry1\">\n                    <property name=\"visible\">True</property>\n                    <property name=\"can_focus\">True</property>\n                  </object>\n                  <packing>\n                    <property name=\"expand\">True</property>\n                    <property name=\"fill\">True</property>\n                    <property name=\"position\">1</property>\n                  </packing>\n                </child>\n                <child>\n                  <object class=\"GtkButton\" id=\"button1\">\n                    <property name=\"label\" translatable=\"yes\">Show</property>\n                    <property name=\"visible\">True</property>\n                    <property name=\"can_focus\">True</property>\n                    <property name=\"receives_default\">True</property>\n                    <property name=\"relief\">half</property>\n                    <property name=\"xalign\">0.50999999046325684</property>\n                    <property name=\"yalign\">0.49000000953674316</property>\n                    <signal name=\"clicked\" handler=\"onShowButtonClicked\" swapped=\"no\"/>\n                  </object>\n                  <packing>\n                    <property name=\"expand\">False</property>\n                    <property name=\"fill\">True</property>\n                    <property name=\"position\">2</property>\n                  </packing>\n                </child>\n              </object>\n              <packing>\n                <property name=\"expand\">False</property>\n                <property name=\"fill\">False</property>\n                <property name=\"position\">0</property>\n              </packing>\n            </child>\n            <child>\n              <placeholder/>\n            </child>\n          </object>\n          <packing>\n            <property name=\"tab_fill\">False</property>\n          </packing>\n        </child>\n        <child type=\"tab\">\n          <object class=\"GtkLabel\" id=\"tab1\">\n            <property name=\"visible\">True</property>\n            <property name=\"can_focus\">False</property>\n            <property name=\"label\" translatable=\"yes\">Tab1</property>\n          </object>\n          <packing>\n            <property name=\"tab_fill\">False</property>\n          </packing>\n        </child>\n        <child>\n          <object class=\"GtkBox\" id=\"box3\">\n            <property name=\"visible\">True</property>\n            <property name=\"can_focus\">False</property>\n            <property name=\"orientation\">vertical</property>\n            <child>\n              <object class=\"GtkLabel\" id=\"label2\">\n                <property name=\"visible\">True</property>\n                <property name=\"can_focus\">False</property>\n                <property name=\"label\" translatable=\"yes\">Nothing here yet</property>\n              </object>\n              <packing>\n                <property name=\"expand\">False</property>\n                <property name=\"fill\">True</property>\n                <property name=\"position\">0</property>\n              </packing>\n            </child>\n            <child>\n              <placeholder/>\n            </child>\n            <child>\n              <placeholder/>\n            </child>\n          </object>\n          <packing>\n            <property name=\"position\">1</property>\n          </packing>\n        </child>\n        <child type=\"tab\">\n          <object class=\"GtkLabel\" id=\"tab2\">\n            <property name=\"visible\">True</property>\n            <property name=\"can_focus\">False</property>\n            <property name=\"label\" translatable=\"yes\">Tab2</property>\n          </object>\n          <packing>\n            <property name=\"position\">1</property>\n            <property name=\"tab_fill\">False</property>\n          </packing>\n        </child>\n        <child>\n          <placeholder/>\n        </child>\n        <child type=\"tab\">\n          <placeholder/>\n        </child>\n      </object>\n    </child>\n  </object>\n</interface>\n"
  },
  {
    "path": "utils/py-gtk-example/gtk-example.py",
    "content": "#!/usr/bin/env python3\n\n# gtk-example.py\n# (c) Aleksander Alekseev 2016\n# http://eax.me/\n\nimport signal\nimport os\nimport gi\ngi.require_version('Gtk', '3.0')\ngi.require_version('Notify', '0.7')\nfrom gi.repository import Gtk\nfrom gi.repository import Notify\n\nAPPID = \"GTK Test\"\nCURRDIR = os.path.dirname(os.path.abspath(__file__))\n# could be PNG or SVG as well\nICON = os.path.join(CURRDIR, 'python3.xpm')\n\n# Cross-platform tray icon implementation\n# See:\n# * http://ubuntuforums.org/showthread.php?t=1923373#post11902222\n# * https://github.com/syncthing/syncthing-gtk/blob/master/syncthing_gtk/statusicon.py\nclass TrayIcon:\n\n    def __init__(self, appid, icon, menu):\n        self.menu = menu\n\n        APPIND_SUPPORT = 1\n        try:\n            from gi.repository import AyatanaAppIndicator3 as AppIndicator3\n        except:\n            APPIND_SUPPORT = 0\n\n        if APPIND_SUPPORT == 1:\n            print(\"Using app indicator\")\n            self.ind = AppIndicator3.Indicator.new(\n                appid, icon,\n                AppIndicator3.IndicatorCategory.APPLICATION_STATUS)\n            self.ind.set_status(AppIndicator3.IndicatorStatus.ACTIVE)\n            self.ind.set_menu(self.menu)\n        else:\n            self.ind = Gtk.StatusIcon()\n            self.ind.set_from_file(icon)\n            self.ind.connect('popup-menu', self.onPopupMenu)\n\n    def onPopupMenu(self, icon, button, time):\n        self.menu.popup(None, None, Gtk.StatusIcon.position_menu, icon,\n                        button, time)\n\n\nclass Handler:\n\n    def __init__(self):\n        self.window_is_hidden = False\n\n    def onShowButtonClicked(self, button):\n        msg = \"Clicked: \" + entry.get_text()\n        dialog = Gtk.MessageDialog(window, 0, Gtk.MessageType.INFO,\n                                   Gtk.ButtonsType.OK, msg)\n        dialog.run()\n        dialog.destroy()\n\n    def onNotify(self, *args):\n        Notify.Notification.new(\"Notification\", \"Hello!\", ICON).show()\n\n    def onShowOrHide(self, *args):\n        if self.window_is_hidden:\n            window.show()\n        else:\n            window.hide()\n\n        self.window_is_hidden = not self.window_is_hidden\n\n    def onQuit(self, *args):\n        Notify.uninit()\n        Gtk.main_quit()\n\n# Handle pressing Ctr+C properly, ignored by default\nsignal.signal(signal.SIGINT, signal.SIG_DFL)\n\nbuilder = Gtk.Builder()\nbuilder.add_from_file('gtk-example.glade')\nbuilder.connect_signals(Handler())\n\nwindow = builder.get_object('window1')\nwindow.set_icon_from_file(ICON)\nwindow.show_all()\n\nentry = builder.get_object('entry1')\nmenu = builder.get_object('menu1')\nicon = TrayIcon(APPID, ICON, menu)\nNotify.init(APPID)\n\nGtk.main()\n"
  },
  {
    "path": "utils/py-gtk-example/python3.xpm",
    "content": "/* XPM */\nstatic char * pylogo_xpm[] = {\n\"32 32 316 2\",\n\"  \tc None\",\n\". \tc #8DB0CE\",\n\"+ \tc #6396BF\",\n\"@ \tc #4985B7\",\n\"# \tc #4181B5\",\n\"$ \tc #417EB2\",\n\"% \tc #417EB1\",\n\"& \tc #4D83B0\",\n\"* \tc #6290B6\",\n\"= \tc #94B2CA\",\n\"- \tc #70A1C8\",\n\"; \tc #3D83BC\",\n\"> \tc #3881BD\",\n\", \tc #387DB6\",\n\"' \tc #387CB5\",\n\") \tc #387BB3\",\n\"! \tc #3779B0\",\n\"~ \tc #3778AE\",\n\"{ \tc #3776AB\",\n\"] \tc #3776AA\",\n\"^ \tc #3775A9\",\n\"/ \tc #4A7FAC\",\n\"( \tc #709FC5\",\n\"_ \tc #3A83BE\",\n\": \tc #5795C7\",\n\"< \tc #94B9DB\",\n\"[ \tc #73A4CE\",\n\"} \tc #3D80B7\",\n\"| \tc #387CB4\",\n\"1 \tc #377AB2\",\n\"2 \tc #377AB0\",\n\"3 \tc #3777AC\",\n\"4 \tc #3774A7\",\n\"5 \tc #3773A5\",\n\"6 \tc #3C73A5\",\n\"7 \tc #4586BB\",\n\"8 \tc #4489C1\",\n\"9 \tc #A7C7E1\",\n\"0 \tc #F7F9FD\",\n\"a \tc #E1E9F1\",\n\"b \tc #4C89BC\",\n\"c \tc #3779AF\",\n\"d \tc #3778AD\",\n\"e \tc #3873A5\",\n\"f \tc #4B7CA4\",\n\"g \tc #3982BE\",\n\"h \tc #4389C1\",\n\"i \tc #A6C6E1\",\n\"j \tc #F6F9FC\",\n\"k \tc #D6E4F0\",\n\"l \tc #4A88BB\",\n\"m \tc #3773A6\",\n\"n \tc #366F9F\",\n\"o \tc #366E9D\",\n\"p \tc #376E9C\",\n\"q \tc #4A8BC0\",\n\"r \tc #79A7CD\",\n\"s \tc #548EBD\",\n\"t \tc #387AB0\",\n\"u \tc #3773A4\",\n\"v \tc #366D9C\",\n\"w \tc #387FBA\",\n\"x \tc #387DB7\",\n\"y \tc #387BB4\",\n\"z \tc #3775A8\",\n\"A \tc #366FA0\",\n\"B \tc #4981AF\",\n\"C \tc #427BAA\",\n\"D \tc #3772A4\",\n\"E \tc #376B97\",\n\"F \tc #77A3C8\",\n\"G \tc #4586BC\",\n\"H \tc #3882BE\",\n\"I \tc #3B76A7\",\n\"J \tc #3B76A6\",\n\"K \tc #366E9E\",\n\"L \tc #376B98\",\n\"M \tc #376B96\",\n\"N \tc #5681A3\",\n\"O \tc #F5EEB8\",\n\"P \tc #FFED60\",\n\"Q \tc #FFE85B\",\n\"R \tc #FFE659\",\n\"S \tc #FDE55F\",\n\"T \tc #5592C4\",\n\"U \tc #3A83BF\",\n\"V \tc #3882BD\",\n\"W \tc #387FB9\",\n\"X \tc #3779AE\",\n\"Y \tc #366F9E\",\n\"Z \tc #366C98\",\n\"` \tc #376A94\",\n\" .\tc #5D85A7\",\n\"..\tc #F5EDB7\",\n\"+.\tc #FFEA5D\",\n\"@.\tc #FFE75A\",\n\"#.\tc #FFE354\",\n\"$.\tc #FDDD56\",\n\"%.\tc #669DC8\",\n\"&.\tc #3885C3\",\n\"*.\tc #3884C2\",\n\"=.\tc #387EB8\",\n\"-.\tc #387CB6\",\n\";.\tc #377AB1\",\n\">.\tc #3772A3\",\n\",.\tc #366D9B\",\n\"'.\tc #F5EBB5\",\n\").\tc #FFE557\",\n\"!.\tc #FFE455\",\n\"~.\tc #FFDF50\",\n\"{.\tc #FFDB4C\",\n\"].\tc #FAD862\",\n\"^.\tc #8EB4D2\",\n\"/.\tc #3C86C1\",\n\"(.\tc #3883C0\",\n\"_.\tc #3882BF\",\n\":.\tc #3881BC\",\n\"<.\tc #3880BB\",\n\"[.\tc #3775AA\",\n\"}.\tc #F5EAB3\",\n\"|.\tc #FFE051\",\n\"1.\tc #FFDE4F\",\n\"2.\tc #FFDA4A\",\n\"3.\tc #FED446\",\n\"4.\tc #F5DF9D\",\n\"5.\tc #77A5CA\",\n\"6.\tc #3885C2\",\n\"7.\tc #387BB2\",\n\"8.\tc #6B8EA8\",\n\"9.\tc #F8E7A1\",\n\"0.\tc #FFE153\",\n\"a.\tc #FFDD4E\",\n\"b.\tc #FFDB4B\",\n\"c.\tc #FFD746\",\n\"d.\tc #FFD645\",\n\"e.\tc #FFD342\",\n\"f.\tc #F6DB8D\",\n\"g.\tc #508DBE\",\n\"h.\tc #3771A3\",\n\"i.\tc #376A95\",\n\"j.\tc #3D6F97\",\n\"k.\tc #C3CBC2\",\n\"l.\tc #FBD964\",\n\"m.\tc #FFDC4D\",\n\"n.\tc #FFD544\",\n\"o.\tc #FFD040\",\n\"p.\tc #F9CF58\",\n\"q.\tc #3F83BB\",\n\"r.\tc #376B95\",\n\"s.\tc #3A6C95\",\n\"t.\tc #4E7BA0\",\n\"u.\tc #91AABC\",\n\"v.\tc #F6E4A3\",\n\"w.\tc #FFDA4B\",\n\"x.\tc #FFD646\",\n\"y.\tc #FFD443\",\n\"z.\tc #FFD241\",\n\"A.\tc #FFCE3D\",\n\"B.\tc #FFCC3B\",\n\"C.\tc #FCC83E\",\n\"D.\tc #3880BC\",\n\"E.\tc #3C79AC\",\n\"F.\tc #5F8DB4\",\n\"G.\tc #7AA0C0\",\n\"H.\tc #82A6C3\",\n\"I.\tc #82A3BF\",\n\"J.\tc #82A2BE\",\n\"K.\tc #82A1BB\",\n\"L.\tc #82A1B9\",\n\"M.\tc #8BA4B5\",\n\"N.\tc #C1C5AE\",\n\"O.\tc #F2E19F\",\n\"P.\tc #FDD74C\",\n\"Q.\tc #FFD94A\",\n\"R.\tc #FFD343\",\n\"S.\tc #FFCE3E\",\n\"T.\tc #FFCB39\",\n\"U.\tc #FFC937\",\n\"V.\tc #FEC636\",\n\"W.\tc #3D79AB\",\n\"X.\tc #9DB6C6\",\n\"Y.\tc #D0CFA2\",\n\"Z.\tc #EFE598\",\n\"`.\tc #F8EE9B\",\n\" +\tc #F8EB97\",\n\".+\tc #F8E996\",\n\"++\tc #F8E894\",\n\"@+\tc #FAE489\",\n\"#+\tc #FCDB64\",\n\"$+\tc #FFDA4D\",\n\"%+\tc #FFCF3E\",\n\"&+\tc #FFCB3A\",\n\"*+\tc #FFC734\",\n\"=+\tc #FFC532\",\n\"-+\tc #3F82B7\",\n\";+\tc #387EB9\",\n\">+\tc #9EB9D0\",\n\",+\tc #F2E287\",\n\"'+\tc #FDEB69\",\n\")+\tc #FEEC60\",\n\"!+\tc #FFEB5E\",\n\"~+\tc #FFE254\",\n\"{+\tc #FFE152\",\n\"]+\tc #FFD747\",\n\"^+\tc #FFC633\",\n\"/+\tc #FCC235\",\n\"(+\tc #578FBE\",\n\"_+\tc #6996BC\",\n\":+\tc #DED9A8\",\n\"<+\tc #FEEC62\",\n\"[+\tc #FFE658\",\n\"}+\tc #FFDF51\",\n\"|+\tc #FFDE50\",\n\"1+\tc #FFD03F\",\n\"2+\tc #FFCD3C\",\n\"3+\tc #FFC431\",\n\"4+\tc #FFBF2C\",\n\"5+\tc #FAC244\",\n\"6+\tc #85AACA\",\n\"7+\tc #A1BBD2\",\n\"8+\tc #F7E47C\",\n\"9+\tc #FFE456\",\n\"0+\tc #FFC735\",\n\"a+\tc #FFBC29\",\n\"b+\tc #F7D280\",\n\"c+\tc #9DBAD2\",\n\"d+\tc #3B7CB2\",\n\"e+\tc #ABC2D6\",\n\"f+\tc #FDEB7B\",\n\"g+\tc #FFC12E\",\n\"h+\tc #FDBD30\",\n\"i+\tc #F4DEA8\",\n\"j+\tc #5F91BA\",\n\"k+\tc #ABC1D4\",\n\"l+\tc #FDEE7E\",\n\"m+\tc #FFE253\",\n\"n+\tc #FFCC3C\",\n\"o+\tc #FFBA27\",\n\"p+\tc #FAC75B\",\n\"q+\tc #4A82B0\",\n\"r+\tc #3877AB\",\n\"s+\tc #3774A6\",\n\"t+\tc #AAC0D4\",\n\"u+\tc #FDEE7D\",\n\"v+\tc #FFEC5F\",\n\"w+\tc #FFE255\",\n\"x+\tc #FFD848\",\n\"y+\tc #FFD444\",\n\"z+\tc #FFCF3F\",\n\"A+\tc #FFBC2A\",\n\"B+\tc #FFBB28\",\n\"C+\tc #FDBA32\",\n\"D+\tc #447AA8\",\n\"E+\tc #4379A7\",\n\"F+\tc #FFE95C\",\n\"G+\tc #FFE558\",\n\"H+\tc #FFE355\",\n\"I+\tc #FED84B\",\n\"J+\tc #FCD149\",\n\"K+\tc #FBCE47\",\n\"L+\tc #FBCD46\",\n\"M+\tc #FBC840\",\n\"N+\tc #FBC63E\",\n\"O+\tc #FBC037\",\n\"P+\tc #FAC448\",\n\"Q+\tc #FDD44C\",\n\"R+\tc #FCD14E\",\n\"S+\tc #FFC836\",\n\"T+\tc #FFC22F\",\n\"U+\tc #FFC02D\",\n\"V+\tc #FFE052\",\n\"W+\tc #FFC636\",\n\"X+\tc #FFCF5C\",\n\"Y+\tc #FFD573\",\n\"Z+\tc #FFC33E\",\n\"`+\tc #FEBD2D\",\n\" @\tc #FFDB4D\",\n\".@\tc #FFD949\",\n\"+@\tc #FFD545\",\n\"@@\tc #FFD140\",\n\"#@\tc #FFCB48\",\n\"$@\tc #FFF7E4\",\n\"%@\tc #FFFCF6\",\n\"&@\tc #FFE09D\",\n\"*@\tc #FFBA2E\",\n\"=@\tc #FDBE2F\",\n\"-@\tc #FFD748\",\n\";@\tc #FFCA38\",\n\">@\tc #FFC844\",\n\",@\tc #FFF2D7\",\n\"'@\tc #FFF9EC\",\n\")@\tc #FFDB94\",\n\"!@\tc #FFB92D\",\n\"~@\tc #FAC54D\",\n\"{@\tc #FDD54E\",\n\"]@\tc #FFBD2D\",\n\"^@\tc #FFC858\",\n\"/@\tc #FFD174\",\n\"(@\tc #FFBF3E\",\n\"_@\tc #FCBD3C\",\n\":@\tc #FAD66A\",\n\"<@\tc #FECD3F\",\n\"[@\tc #FFC330\",\n\"}@\tc #FFBD2A\",\n\"|@\tc #FFB724\",\n\"1@\tc #FFB521\",\n\"2@\tc #FFB526\",\n\"3@\tc #FBC457\",\n\"4@\tc #F7E09E\",\n\"5@\tc #F8D781\",\n\"6@\tc #FAC349\",\n\"7@\tc #FCC134\",\n\"8@\tc #FEBE2C\",\n\"9@\tc #FBBE3F\",\n\"0@\tc #F7CF79\",\n\"a@\tc #F5D795\",\n\"                      . + @ # $ % % & * =                       \",\n\"                  - ; > > , ' ) ! ~ { ] ^ /                     \",\n\"                ( _ : < [ } | 1 2 ~ 3 4 5 5 6                   \",\n\"                7 8 9 0 a b 2 c d 3 { 5 5 5 e f                 \",\n\"                g h i j k l c ~ { { m 5 5 n o p                 \",\n\"                > > q r s t c c d 4 5 u n v v v                 \",\n\"                w x ' y 2 c d d z 5 u A v v v v                 \",\n\"                              B C 5 D v v v v E                 \",\n\"      F G H H H x ' ) c c c d I J 5 K v v L M N O P Q R S       \",\n\"    T U H V V W ' ) c c X ~ 5 5 5 Y v v Z ` `  ...+.@.#.#.$.    \",\n\"  %.&.*.> w W =.-.;.c 3 { ^ 5 5 >.o v ,.E ` `  .'.).!.#.~.{.].  \",\n\"^./.(._.:.<., ' ) ;.X d [.5 5 >.K v ,.E ` ` `  .}.#.|.1.{.2.3.4.\",\n\"5.6.(.H H x ' 7.c c 3 3 4 5 D K v v ,.` ` ` ` 8.9.0.a.b.c.d.e.f.\",\n\"g._.> <.w ' ' | 2 3 { z 5 5 h.v v v i.` ` ` j.k.l.m.{.d.n.e.o.p.\",\n\"q.> > :.-.' 1 c c c ] 5 5 >.v v ,.r.` ` s.t.u.v.{.w.x.y.z.A.B.C.\",\n\"D.D.w -.' 1 c c c E.F.G.H.I.J.J.K.L.L.L.M.N.O.P.Q.c.R.S.B.T.U.V.\",\n\"D.D.=.' ' 1 c c W.X.Y.Z.`.`.`.`.`. +.+++@+#+$+Q.d.R.%+B.&+*+=+=+\",\n\"-+;+-.' ;.2 c c >+,+'+)+P P P !+Q R ~+{+1.{.]+d.y.%+B.&+^+=+=+/+\",\n\"(+' ' ;.c X X _+:+<+P P P P !+R [+~+}+|+{.]+n.R.1+2+&+^+=+3+4+5+\",\n\"6+' ) ! ~ { { 7+8+P P P P !+R 9+#.{+{.w.]+y.z.S.&+0+=+=+3+4+a+b+\",\n\"c+d+7.! d 3 z e+f+P P P !+R 9+#.{+m.{.]+y.1+B.&+0+=+=+g+4+a+h+i+\",\n\"  j+c d 3 { 4 k+l+P P !+@.9+m+1.m.{.]+y.1+n+B.*+=+=+g+a+a+o+p+  \",\n\"    q+r+{ s+m t+u+v+@.R w+{+}+{.x+d.y+z+n+B.0+=+=+g+A+a+B+C+    \",\n\"      * D+E+E+  +.F+G+H+}+}+{.I+J+K+L+M+M+M+M+N+O+O+O+O+P+      \",\n\"                ).).#.{+a.{.x+Q+R+                              \",\n\"                #.m+1.a.{.x+y.o.2+B.S+=+=+T+U+O+                \",\n\"                0.V+{.{.x+n.o.2+B.B.W+X+Y+Z+a+`+                \",\n\"                 @{..@+@n.@@B.B.S+^+#@$@%@&@*@=@                \",\n\"                ].-@x.y.o.%+;@S+=+=+>@,@'@)@!@~@                \",\n\"                  {@z.z+2+U.=+=+=+T+]@^@/@(@_@                  \",\n\"                    :@<@U.=+=+[@4+}@|@1@2@3@                    \",\n\"                      4@5@6@7@8@a+a+9@0@a@                      \"};\n"
  },
  {
    "path": "utils/tray-test-fdo/main.c",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * main.c\n * Tue, 24 Aug 2004 12:00:24 +0700\n * -------------------------------\n * main is main\n * ------------------------------- */\n\n/* TODO: XEMBED Implementation wanted */\n\n#include <X11/Xatom.h>\n#include <X11/Xlib.h>\n#include <X11/Xutil.h>\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n/* from System Tray Protocol Specification\n * http://freedesktop.org/Standards/systemtray-spec/systemtray-spec-0.1.html */\n#define SYSTEM_TRAY_REQUEST_DOCK 0\n#define SYSTEM_TRAY_BEGIN_MESSAGE 1\n#define SYSTEM_TRAY_CANCEL_MESSAGE 2\n\n#define TRAY_SEL_ATOM \"_NET_SYSTEM_TRAY_S\"\n\n#include \"xembed.h\"\n\n#define GROW_PERIOD 50\n\n/* just globals */\nDisplay *dpy;\nWindow wnd;\nWindow wnd1;\nWindow tray;\nint reparent_out = 0;\nint grow = 0;\nint grow_cd = GROW_PERIOD;\nint maintain_size = 1;\n\nXSizeHints xsh =\n    {flags : (PSize | PPosition), x : 100, y : 100, width : 20, height : 20};\n\nAtom xa_xembed_info;\nAtom xa_tray_selection;\nAtom xa_tray_opcode;\nAtom xa_tray_data;\nAtom xa_wm_delete_window;\nAtom xa_wm_protocols;\nAtom xa_wm_take_focus;\nAtom xa_xembed;\n\nvoid die(char *msg)\n{\n    fprintf(stderr, \"%s\", msg);\n    exit(1);\n}\n\nint main(int argc, char **argv)\n{\n    XClassHint xch;\n    XWMHints xwmh = {\n        flags : (InputHint | StateHint),\n        input : True,\n        initial_state : NormalState,\n        icon_pixmap : None,\n        icon_window : None,\n        icon_x : 0,\n        icon_y : 0,\n        icon_mask : None,\n        window_group : 0\n    };\n\n    char *wnd_name = \"test_tray_icon\";\n    XTextProperty wm_name;\n\n    XWindowAttributes xwa;\n    XEvent ev;\n    XColor xc_black =\n        {flags : 0, pad : 0, pixel : 0x000000, red : 0, green : 0, blue : 0};\n    XColor xc_bg =\n        {flags : 0, pad : 0, pixel : 0x777777, red : 0, green : 0, blue : 0};\n\n    char *tray_sel_atom_name;\n    char *tmp;\n\n    long mwm_hints[5] = {2, 0, 0, 0, 0};\n    Atom mwm_wm_hints;\n\n    char *dpy_name = NULL;\n    char *bg_color_name = NULL;\n\n    while (--argc > 0) {\n        ++argv;\n\n        if (!strcmp(argv[0], \"-w\")) {\n            argv++;\n            argc--;\n            xsh.width = strtol(argv[0], &tmp, 0);\n        }\n\n        if (!strcmp(argv[0], \"-h\")) {\n            argv++;\n            argc--;\n            xsh.height = strtol(argv[0], &tmp, 0);\n        }\n\n        if (!strcmp(argv[0], \"-r\")) {\n            argv++;\n            argc--;\n            reparent_out = 1;\n        }\n\n        if (!strcmp(argv[0], \"-c\")) {\n            argv++;\n            argc--;\n            bg_color_name = argv[0];\n        }\n\n        if (!strcmp(argv[0], \"-g\")) {\n            grow = 1;\n        }\n\n        if (!strcmp(argv[0], \"-gg\")) {\n            grow = 2;\n        }\n\n        if (!strcmp(argv[0], \"-n\")) {\n            maintain_size = 0;\n        }\n    }\n\n    if ((dpy = XOpenDisplay(dpy_name)) == NULL) {\n        die(\"Error: could not open display\\n\");\n    }\n\n    if (bg_color_name != NULL) {\n        XParseColor(dpy, XDefaultColormap(dpy, DefaultScreen(dpy)),\n            bg_color_name, &xc_bg);\n        XAllocColor(dpy, XDefaultColormap(dpy, DefaultScreen(dpy)), &xc_bg);\n    }\n\n    XSynchronize(dpy, True);\n\n    if ((tray_sel_atom_name = (char *)malloc(strlen(TRAY_SEL_ATOM) + 2))\n        == NULL) {\n        die(\"Error: low mem\\n\");\n    }\n\n    snprintf(tray_sel_atom_name, strlen(TRAY_SEL_ATOM) + 2, \"%s%u\",\n        TRAY_SEL_ATOM, DefaultScreen(dpy));\n\n    xa_tray_selection = XInternAtom(dpy, tray_sel_atom_name, False);\n\n    free(tray_sel_atom_name);\n\n    xa_tray_opcode = XInternAtom(dpy, \"_NET_SYSTEM_TRAY_OPCODE\", False);\n    xa_tray_data = XInternAtom(dpy, \"_NET_SYSTEM_TRAY_MESSAGE_DATA\", False);\n    xa_xembed_info = XInternAtom(dpy, \"_XEMBED_INFO\", False);\n    printf(\"_XEMBED_INFO = 0x%lx\\n\", xa_xembed_info);\n\n    xa_xembed = XInternAtom(dpy, \"_XEMBED\", False);\n\n    xch.res_class = wnd_name;\n    xch.res_name = wnd_name;\n\n    trap_errors();\n\n    wnd = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), xsh.x, xsh.y,\n        xsh.width, xsh.height, 0, 0, xc_black.pixel);\n    wnd1 = XCreateSimpleWindow(\n        dpy, wnd, 1, 1, xsh.width - 2, xsh.height - 2, 0, 0, xc_bg.pixel);\n\n    if (untrap_errors()) {\n        die(\"Error: could not create simple window\\n\");\n    } else {\n        printf(\"wid=0x%lx\\n\", wnd);\n    }\n\n    XmbTextListToTextProperty(dpy, &wnd_name, 1, XTextStyle, &wm_name);\n\n    XSetWMProperties(dpy, wnd, &wm_name, NULL, argv, argc, &xsh, &xwmh, &xch);\n\n    if ((tray = XGetSelectionOwner(dpy, xa_tray_selection)) == None) {\n        printf(\"Error: no tray found\\n\");\n    } else {\n        xclient_msg32(dpy, tray, xa_tray_opcode, CurrentTime,\n            SYSTEM_TRAY_REQUEST_DOCK, wnd, 0, 0);\n    }\n\n    XSelectInput(dpy, wnd,\n        SubstructureNotifyMask | StructureNotifyMask | PropertyChangeMask\n            | ButtonPressMask | ButtonReleaseMask);\n\n    XMapRaised(dpy, wnd);\n    XMapRaised(dpy, wnd1);\n\n    XFlush(dpy);\n\n    printf(\"::::: wid: 0x%lx, color: %s\\n\", wnd,\n        bg_color_name != NULL ? bg_color_name : \"gray\");\n\n    if (reparent_out) {\n        usleep(5000000L);\n        fflush(stdout);\n        printf(\"reparenting out\\n\");\n        fflush(stdout);\n        XReparentWindow(dpy, wnd, DefaultRootWindow(dpy), 100, 100);\n    }\n\n    for (;;) {\n        while (XPending(dpy)) {\n            XNextEvent(dpy, &ev);\n            switch (ev.type) {\n            case DestroyNotify: return 0;\n            case ConfigureNotify:\n                if (maintain_size\n                    && (ev.xconfigure.width != xsh.width\n                        || ev.xconfigure.height != xsh.height)) {\n                    printf(\"0x%lx: configured: %dx%d\\n\", wnd,\n                        ev.xconfigure.width, ev.xconfigure.height);\n                    printf(\"0x%lx: maintaining size\\n\", wnd);\n                    XResizeWindow(dpy, wnd, xsh.width, xsh.height);\n                }\n                break;\n            case ButtonPress:\n            case ButtonRelease:\n                printf(\"%s: btn=%d, state=0x%x, x=%d, y=%d, send_event=%d\\n\",\n                    ev.type == ButtonPress ? \"ButtonPress\" : \"ButtonRelease\",\n                    ev.xbutton.button, ev.xbutton.state, ev.xbutton.x,\n                    ev.xbutton.y, ev.xbutton.send_event);\n                break;\n            default: break;\n            }\n        }\n\n        if (grow == 1) {\n            grow_cd--;\n            if (!grow_cd) {\n                printf(\"0x%lx: size increased 2x\\n\", wnd);\n                XResizeWindow(dpy, wnd, xsh.width * 2, xsh.height * 2);\n                XResizeWindow(\n                    dpy, wnd1, xsh.width * 2 - 2, xsh.height * 2 - 2);\n                xsh.width *= 2;\n                xsh.height *= 2;\n                grow = 0;\n            }\n        } else if (grow == 2) {\n            grow_cd--;\n            if (grow_cd == 0) {\n                printf(\"0x%lx: size increased 2x\\n\", wnd);\n                XResizeWindow(dpy, wnd, xsh.width * 2, xsh.height * 2);\n                XResizeWindow(\n                    dpy, wnd1, xsh.width * 2 - 2, xsh.height * 2 - 2);\n                xsh.width *= 2;\n                xsh.height *= 2;\n            } else if (grow_cd == -GROW_PERIOD) {\n                printf(\"0x%lx: size shrinked 2x\\n\", wnd);\n                xsh.width /= 2;\n                xsh.height /= 2;\n                XResizeWindow(dpy, wnd, xsh.width, xsh.height);\n                XResizeWindow(dpy, wnd1, xsh.width - 2, xsh.height - 2);\n                grow = 0;\n            }\n        }\n\n        usleep(50000L);\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "utils/tray-test-fdo/run_icons",
    "content": "#!/bin/bash\n\n[[ $# -gt 0 ]] && num=$1\n[[ $# -eq 0 ]] && num=2\n\nfor ((ts=0; $ts < $num; ts=ts+1)); do\n\t./tray-test-fdo &\ndone\n\necho\necho\n"
  },
  {
    "path": "utils/tray-test-fdo/seq1",
    "content": "#!/bin/sh\n./tray-test-fdo -w 22 -h 22 -c blue 2>&1 &\n./tray-test-fdo -w 22 -h 22 -c green  2>&1 &\n./tray-test-fdo -w 22 -h 22 -c magenta -gg 2>&1 &\nsleep 1\n./tray-test-fdo -w 22 -h 22 -c yellow 2>&1 &\n./tray-test-fdo -w 22 -h 22 -c black 2>&1 &\n"
  },
  {
    "path": "utils/tray-test-fdo/seq2",
    "content": "#!/bin/sh\n./tray-test-fdo -w 22 -h 22 -c blue 2>&1 &\n./tray-test-fdo -w 22 -h 22 -c green  2>&1 &\n./tray-test-fdo -w 22 -h 22 -c red 2>&1 &\n./tray-test-fdo -w 22 -h 22 -c yellow 2>&1 &\n"
  },
  {
    "path": "utils/tray-test-fdo/xembed.c",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * icons.h\n * Tue, 24 Aug 2004 12:05:38 +0700\n * -------------------------------\n * XEMBED protocol implementation\n * -------------------------------*/\n\n#include \"xembed.h\"\n\n/* error handling code taken from xembed spec */\nint trapped_error_code = 0;\nstatic int (*old_error_handler)(Display *, XErrorEvent *);\n\nstatic int error_handler(Display *display, XErrorEvent *error)\n{\n    trapped_error_code = error->error_code;\n    return 0;\n}\n\nvoid trap_errors()\n{\n    trapped_error_code = 0;\n    old_error_handler = XSetErrorHandler(error_handler);\n}\n\nint untrap_errors()\n{\n    XSetErrorHandler(old_error_handler);\n    return trapped_error_code;\n}\n\nint xclient_msg32(Display *dpy, Window dst, Atom type, long data0, long data1,\n    long data2, long data3, long data4)\n{\n    XEvent ev;\n    ev.type = ClientMessage;\n    ev.xclient.window = dst;\n    ev.xclient.message_type = type;\n    ev.xclient.format = 32;\n    ev.xclient.data.l[0] = data0;\n    ev.xclient.data.l[1] = data1;\n    ev.xclient.data.l[2] = data2;\n    ev.xclient.data.l[3] = data3;\n    ev.xclient.data.l[4] = data4;\n\n    return XSendEvent(dpy, dst, False, NoEventMask, &ev);\n}\n\nint xembed_msg(\n    Display *dpy, Window dst, long msg, long detail, long data1, long data2)\n{\n    int status;\n    static Atom xa_xembed = None;\n    trap_errors();\n    if (xa_xembed == None) xa_xembed = XInternAtom(dpy, \"_XEMBED\", False);\n    status = xclient_msg32(\n        dpy, dst, xa_xembed, CurrentTime, msg, detail, data1, data2);\n    if (untrap_errors()) return trapped_error_code;\n    return status;\n}\n\nunsigned long xembed_get_info(Display *dpy, Window src, long *version)\n{\n    Atom xa_xembed_info, act_type;\n    int act_fmt;\n    unsigned long nitems, bytesafter;\n    unsigned char *data;\n\n    xa_xembed_info = XInternAtom(dpy, \"_XEMBED_INFO\", False);\n\n    trap_errors();\n\n    XGetWindowProperty(dpy, src, xa_xembed_info, 0, 2, False, xa_xembed_info,\n        &act_type, &act_fmt, &nitems, &bytesafter, &data);\n\n    if (untrap_errors()) /* FIXME: must somehow indicate a error */\n        return 0;\n\n    if (act_type != xa_xembed_info) {\n        if (version != NULL) *version = 0;\n        return 0;\n    }\n\n    if (nitems < 2) /* FIXME: the same */\n        return 0;\n\n    if (version != NULL) *version = ((unsigned long *)data)[0];\n\n    XFree(data);\n\n    return ((unsigned long *)data)[1];\n}\n\nint xembed_embeded_notify(Display *dpy, Window src, Window dst)\n{\n    return xembed_msg(dpy, dst, XEMBED_EMBEDDED_NOTIFY, 0, src, 0);\n}\n\nint xembed_window_activate(Display *dpy, Window dst)\n{\n    return xembed_msg(dpy, dst, XEMBED_WINDOW_ACTIVATE, 0, 0, 0);\n}\n\nint xembed_window_deactivate(Display *dpy, Window dst)\n{\n    return xembed_msg(dpy, dst, XEMBED_WINDOW_DEACTIVATE, 0, 0, 0);\n}\n\nint xembed_focus_in(Display *dpy, Window dst, int focus)\n{\n    return xembed_msg(dpy, dst, XEMBED_FOCUS_IN, focus, 0, 0);\n}\n\nint xembed_focus_out(Display *dpy, Window dst)\n{\n    return xembed_msg(dpy, dst, XEMBED_FOCUS_OUT, 0, 0, 0);\n}\n"
  },
  {
    "path": "utils/tray-test-fdo/xembed.h",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * icons.h\n * Tue, 24 Aug 2004 12:05:38 +0700\n * -------------------------------\n * XEMBED protocol implementation\n * -------------------------------*/\n\n#ifndef _XEMBED_H_\n#define _XEMBED_H_\n\n#include <X11/Xlib.h>\n#include <X11/Xutil.h>\n\n/* XEMBED messages */\n#define XEMBED_EMBEDDED_NOTIFY 0\n#define XEMBED_WINDOW_ACTIVATE 1\n#define XEMBED_WINDOW_DEACTIVATE 2\n#define XEMBED_REQUEST_FOCUS 3\n#define XEMBED_FOCUS_IN 4\n#define XEMBED_FOCUS_OUT 5\n#define XEMBED_FOCUS_NEXT 6\n#define XEMBED_FOCUS_PREV 7\n/* 8-9 were used for XEMBED_GRAB_KEY/XEMBED_UNGRAB_KEY */\n#define XEMBED_MODALITY_ON 10\n#define XEMBED_MODALITY_OFF 11\n#define XEMBED_REGISTER_ACCELERATOR 12\n#define XEMBED_UNREGISTER_ACCELERATOR 13\n#define XEMBED_ACTIVATE_ACCELERATOR 14\n\n/* Details for  XEMBED_FOCUS_IN: */\n#define XEMBED_FOCUS_CURRENT 0\n#define XEMBED_FOCUS_FIRST 1\n#define XEMBED_FOCUS_LAST 2\n\n/* Modifiers field for XEMBED_REGISTER_ACCELERATOR */\n#define XEMBED_MODIFIER_SHIFT (1 << 0)\n#define XEMBED_MODIFIER_CONTROL (1 << 1)\n#define XEMBED_MODIFIER_ALT (1 << 2)\n#define XEMBED_MODIFIER_SUPER (1 << 3)\n#define XEMBED_MODIFIER_HYPER (1 << 4)\n\n/* Flags for XEMBED_ACTIVATE_ACCELERATOR */\n#define XEMBED_ACCELERATOR_OVERLOADED (1 << 0)\n\n/* Directions for focusing */\n#define XEMBED_DIRECTION_DEFAULT 0\n#define XEMBED_DIRECTION_UP_DOWN 1\n#define XEMBED_DIRECTION_LEFT_RIGHT 2\n\n/* Flags for _XEMBED_INFO */\n#define XEMBED_MAPPED (1 << 0)\n\n/* error handling */\nextern int trapped_error_code;\nvoid trap_errors();\nint untrap_errors();\n\n/* utils */\nint xclient_msg32(Display *dpy, Window dst, Atom type, long data0, long data1,\n    long data2, long data3, long data4);\nint xembed_msg(\n    Display *dpy, Window dst, long msg, long detail, long data1, long data2);\n\n/* xembed funcs */\nunsigned long xembed_get_info(Display *dpy, Window dst, long *version);\nint xembed_embeded_notify(Display *dpy, Window src, Window dst);\nint xembed_window_activate(Display *dpy, Window dst);\nint xembed_window_deactivate(Display *dpy, Window dst);\nint xembed_focus_in(Display *dpy, Window dst, int focus);\nint xembed_focus_out(Display *dpy, Window dst);\n\n#endif\n"
  },
  {
    "path": "utils/tray-test-gtk/traytest",
    "content": "#!/usr/bin/env python\n# vim:et\nimport gtk, pygtk, gobject\nimport os\n\ndef send_signal(sig = 1):\n    pass\n#    os.system('pkill -USR%s stalonetray' % sig)\n\ndef show_menu(status_icon, button, activate_time, menu):\n    menu.popup(None, None, gtk.status_icon_position_menu, button, activate_time, status_icon)\n\ndef hide_icon(widget):\n    print \"hide\"\n    send_signal(2)\n    tray_icon.set_visible(False)\n    gobject.timeout_add(500, show_icon, None)\n\ndef show_icon(widget):\n    print \"show\"\n    send_signal(2)\n    tray_icon.set_visible(True)\n#   gobject.timeout_add(500, hide_icon, None)\n    return False\n\ndef notify_about_resize(icon, size, **args):\n    print \"notify_about_resize:\", size\n    return False\n\nsend_signal(2)\ntray_menu = gtk.Menu()\nmenu_hide = gtk.ImageMenuItem(gtk.STOCK_REMOVE)\nmenu_hide.connect(\"activate\", hide_icon)\ntray_menu.append(menu_hide)\nmenu_quit = gtk.ImageMenuItem(gtk.STOCK_CLOSE)\nmenu_quit.connect(\"activate\", lambda w: gtk.main_quit())\ntray_menu.append(menu_quit)\ntray_menu.show_all()\n\nsend_signal(2)\n\ntray_icon = gtk.status_icon_new_from_stock(gtk.STOCK_DIALOG_ERROR)\ntray_icon.connect('popup-menu', show_menu, tray_menu)\ntray_icon.connect('size-changed', notify_about_resize);\ntray_icon.set_tooltip(\"Right click me!\")\n#gobject.timeout_add(500, hide_icon, None)\n\nsend_signal()\n\ngtk.main()\n"
  },
  {
    "path": "utils/tray-xembed-test/common.h",
    "content": "/* -------------------------------\n* vim:tabstop=4:shiftwidth=4\n* common.h\n* Mon, 01 May 2006 01:45:08 +0700\n* -------------------------------\n* Common declarations\n* -------------------------------*/\n\n#ifndef _COMMON_H_\n#define _COMMON_H_\n\n#define DBG(level, message) print_message_to_stderr message\n\n/* Meaningful names for return values */\n#define SUCCESS 1\n#define FAILURE 0\n\n/* Meaningful names for return values of icon mass-operations */\n#define MATCH 1\n#define NO_MATCH 0\n\n/* Portable macro for function name */\n#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L\n\t#define __FUNC__ __func__\n#elif defined(__GNUC__) && __GNUC__ >= 3\n\t#define __FUNC__ __FUNCTION__\n#else\n\t#define __FUNC__ \"unknown\"\n#endif\n\n/* Print a error message */\n#ifdef DEBUG\n\t#define ERR(message)\tDBG(0, message)\n#else\n\t#define ERR(message)\tprint_message_to_stderr message\n#endif\n\n/* Print a message and... DIE */\n#define DIE(message)\t\tdo { ERR(message); exit(-1); } while(0)\n\n/* WARNING: feed following macro only with\n * side-effects-free expressions */\n\n/* Get a value within target interval */\n#define cutoff(tgt,min,max) (tgt) < (min) ? (min) : ((tgt) > (max) ? max : tgt)\n\n/* Update value to fit into target interval */\n#define val_range(tgt,min,max) (tgt) = cutoff(tgt,min,max)\n\n#endif\n"
  },
  {
    "path": "utils/tray-xembed-test/debug.c",
    "content": "/* -------------------------------\n* vim:tabstop=4:shiftwidth=4\n* debug.c\n* Mon, 01 May 2006 01:44:42 +0700\n* -------------------------------\n* Debugging code/utilities\n* -------------------------------*/\n\n#include <stdio.h>\n#include <string.h>\n#include <limits.h>\n#include <stdarg.h>\n\n/* Print the message to STDERR (varadic macros is not used in the sake of portability) */\nvoid print_message_to_stderr(const char *fmt,...)\n{\n\tstatic char msg[PATH_MAX];\n\tva_list va;\n\tva_start(va, fmt);\n\tvsnprintf(msg, PATH_MAX, fmt, va);\n\tva_end(va);\n\tfprintf(stderr, msg);\n}\n\n"
  },
  {
    "path": "utils/tray-xembed-test/main.c",
    "content": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * main.c\n * Tue, 24 Aug 2004 12:00:24 +0700\n * -------------------------------\n * main is main\n * ------------------------------- */\n\n#include <X11/Xlib.h>\n#include <X11/Xutil.h>\n#include <X11/Xatom.h>\n#include <X11/Xmd.h>\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n\n/* from System Tray Protocol Specification\n * http://freedesktop.org/Standards/systemtray-spec/systemtray-spec-0.1.html */\n#define SYSTEM_TRAY_REQUEST_DOCK    0\n#define SYSTEM_TRAY_BEGIN_MESSAGE   1\n#define SYSTEM_TRAY_CANCEL_MESSAGE  2\n\n#define\tTRAY_SEL_ATOM \"_NET_SYSTEM_TRAY_S\"\n\n#include \"common.h\"\n#include \"xutils.h\"\n#include \"xembed.h\"\n\n/* just globals */\nDisplay\t\t*dpy;\n\nWindow\t\twnd;\nWindow\t\tbuttons[4];\n\nCARD32\t\txembed_data[2];\n\nint \t\thas_focus = 0;\nint\t\t\tsel_idx = 0;\nint\t\t\tembedded = 0;\nint\t\t\tdo_not_accept_focus = 0;\nint\t\t\trequest_focus = 0;\nint\t\t\tregister_accel = 0;\n\nCARD32\t\taccel_id = 0x11ff11ff;\nCARD32\t\taccel_sym = XK_Page_Down;\nCARD32\t\taccel_mods = 0x14; /* Ctrl */\n\nWindow \t\ttray = None;\n\nCARD32\t\tcurrent_xembed_timestamp;\n\nXColor\t\twnd_bg_embedded = {.pixel = 0x000000};\nXColor\t\twnd_bg_unembedded = {.pixel = 0x770000};\nXColor\t\tbuttons_focused = {.pixel = 0x999999};\nXColor\t\tbuttons_unfocused = {.pixel = 0x666666};\nXColor\t\tbuttons_selected = {.pixel = 0x0000ff};\n\nXSizeHints\txsh = {\n\tflags:\t(PSize | PPosition),\n\tx:\t\t100,\n\ty:\t\t100,\n\twidth:\t24,\n\theight:\t24\n};\n\nAtom \t\txa_xembed_info;\nAtom \t\txa_tray_selection;\nAtom\t\txa_tray_opcode;\nAtom\t\txa_tray_data;\nAtom \t\txa_wm_delete_window;\nAtom\t\txa_wm_protocols;\nAtom\t\txa_wm_take_focus;\nAtom \t\txa_xembed;\n\nvoid create_window(int argc, char **argv)\n{\n#if 0\n\tXWMHints\t\t\txwmh = {\n\t\tflags:\t(InputHint | StateHint ),\n\t\tinput:\tTrue,\n\t\tinitial_state: NormalState,\n\t\ticon_pixmap: None,\n\t\ticon_window: None,\n\t\ticon_x: 0,\n\t\ticon_y: 0,\n\t\ticon_mask: None,\n\t\twindow_group: 0\t\n\t};\t\n#else\n\tXWMHints\t\t\t*xwmh;\n#endif\n\tchar\t\t\t\t*wnd_name = \"test_tray_icon\";\n\tXTextProperty\t\twm_name;\n\tXClassHint\t\t\t*xch;\n\tXWindowAttributes \txwa;\n\tint \t\t\t\tx, y;\n\n\txch = XAllocClassHint();\n\txch->res_class = wnd_name;\n\txch->res_name = wnd_name;\n\n\twnd = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy),\n\t\t\t\t\txsh.x, xsh.y, xsh.width, xsh.height, 0, 0, wnd_bg_unembedded.pixel);\n\n\tprintf(\"Created window: 0x%x\\n\", wnd);\n\n\tfor (x = 0; x < 2; x++)\n\t\tfor (y = 0; y < 2; y++) {\n\t\t\tbuttons[x + y * 2] = XCreateSimpleWindow(dpy, wnd, 2 + x * 12, 2 + y * 12, 8, 8, 0, 0, buttons_unfocused.pixel);\n\t\t\tXMapRaised(dpy, buttons[x + y * 2]);\n\t\t}\n\n\tif (!x11_ok()) {\n\t\tDIE((\"Error: could not create simple window\\n\"));\n\t}\n\n\txembed_data[0] = 0;\n\txembed_data[1] = 1;\n\n\txembed_post_data(dpy, wnd, xembed_data);\n\n\tXmbTextListToTextProperty(dpy, &wnd_name, 1, XTextStyle, &wm_name);\n\n\txwmh = XAllocWMHints();\n\txwmh->flags = InputHint | StateHint | WindowGroupHint | IconWindowHint;\n\txwmh->flags = True;\n\txwmh->initial_state = NormalState;\n\txwmh->window_group = 0;\n\txwmh->icon_window = None;\n\n\tXSetWMProperties(dpy, wnd, &wm_name, NULL, argv, argc, &xsh, xwmh, xch);\n\n\tif ((tray = XGetSelectionOwner(dpy, xa_tray_selection)) == None) {\n\t\tprintf(\"Error: no tray found\\n\");\n\t} else {\n\t\tx11_send_client_msg32(dpy, tray, wnd, xa_tray_opcode, CurrentTime, SYSTEM_TRAY_REQUEST_DOCK, wnd, 0, 0);\n\t}\n\n\tXSelectInput(dpy, wnd, SubstructureNotifyMask | StructureNotifyMask | PropertyChangeMask | KeyReleaseMask | ButtonReleaseMask );\n\t\n\tXMapRaised(dpy, wnd);\t\n\n}\n\nvoid redraw_buttons()\n{\n\tint i;\n\tunsigned long pixel = 0;\n\tfor (i = 0; i < 4; i++) {\n\t\tif (has_focus) {\n\t\t\tif (i == sel_idx)\n\t\t\t\tpixel = buttons_selected.pixel;\n\t\t\telse\n\t\t\t\tpixel = buttons_focused.pixel;\n\t\t} else\n\t\t\tpixel = buttons_unfocused.pixel;\n\n\t\tXSetWindowBackground(dpy, buttons[i], pixel);\n\t\tXClearWindow(dpy, buttons[i]);\n\t}\n}\n\nvoid focus_in(int how)\n{\n\tDBG(0, (\"got focus\\n\"));\n\tif (do_not_accept_focus) {\n\t\tif (how == XEMBED_FOCUS_FIRST)\n\t\t\txembed_send_focus_next(dpy, tray, current_xembed_timestamp);\n\t\telse\n\t\t\txembed_send_focus_prev(dpy, tray, current_xembed_timestamp);\n\t} else {\n\t\thas_focus = 1;\n\t\tif (how == XEMBED_FOCUS_FIRST)\n\t\t\tsel_idx = 0;\n\t\telse if (how == XEMBED_FOCUS_LAST)\n\t\t\tsel_idx = 3;\n\t\tredraw_buttons();\n\t}\n}\n\nvoid focus_out()\n{\n\tDBG(0, (\"lost focus\\n\"));\n\thas_focus = 0;\n\tredraw_buttons();\n}\n\nvoid focus_next()\n{\n\tif (!embedded) {\n\t\tsel_idx = (sel_idx + 1) % 4;\n\t\tredraw_buttons();\n\t} else {\n\t\tif (sel_idx < 3) {\n\t\t\tsel_idx++;\n\t\t\tredraw_buttons();\n\t\t} else\n\t\t\txembed_send_focus_next(dpy, tray, x11_get_server_timestamp(dpy, wnd));\n\t}\n}\n\nvoid focus_prev()\n{\n\tDBG(0, (\"focus_prev\\n\"));\n\tif (!embedded) {\n\t\tsel_idx = (4 + (sel_idx - 1)) % 4;\n\t\tredraw_buttons();\n\t} else {\n\t\tif (sel_idx > 0) {\n\t\t\tsel_idx--;\n\t\t\tredraw_buttons();\n\t\t} else\n\t\t\txembed_send_focus_prev(dpy, tray, x11_get_server_timestamp(dpy, wnd));\n\t};\n\tDBG(0, (\"sel_idx=%d\\n\", sel_idx));\n}\n\nvoid client_event(XClientMessageEvent ev)\n{\n    char *msg_type_name;\n\n\tmsg_type_name = XGetAtomName(dpy, ev.message_type);\n\n\tif (msg_type_name != NULL) {\n        DBG(3, (\"message name: \\\"%s\\\"\\n\", msg_type_name));\n        XFree(msg_type_name);\n    }\n\n\tif (ev.message_type == xa_xembed) {\n\t\tDBG(3, (\"XEMBED message opcode: %d\\n\", ev.data.l[1]));\n\t\t\n\t\tcurrent_xembed_timestamp = ev.data.l[0];\n\t\tif (current_xembed_timestamp == CurrentTime)\n\t\t\tcurrent_xembed_timestamp = x11_get_server_timestamp(dpy, wnd);\n\n\t\tswitch (ev.data.l[1]) {\n\t\tcase XEMBED_EMBEDDED_NOTIFY:\n\t\t\tembedded = 1;\n\t\t\tXSetWindowBackground(dpy, wnd, wnd_bg_embedded.pixel);\n\t\t\tXClearWindow(dpy, wnd);\n\t\t\tredraw_buttons();\n\t\t\tif (request_focus)\n\t\t\t\txembed_send_request_focus(dpy, tray, x11_get_server_timestamp(dpy, wnd));\n\t\t\tbreak;\n\t\tcase XEMBED_FOCUS_OUT:\n\t\t\tfocus_out();\n\t\t\tbreak;\n\t\tcase XEMBED_FOCUS_IN:\n\t\t\tfocus_in(ev.data.l[2]);\n\t\t\tbreak;\n\t\tcase XEMBED_ACTIVATE_ACCELERATOR:\n\t\t\tDBG(3, (\"Got ACTIVATE_ACCELERATOR message, id=0x%x, overloaded=0x%x\", ev.data.l[2], ev.data.l[3]));\n\t\t\tbreak;\n\t\t}\n\t\t\t\n\t}\n}\n\nvoid key_release(XKeyReleasedEvent ev)\n{\n\tchar buf[20];\n\tKeySym keysym;\n\n\tXLookupString(&ev, buf, 20, &keysym, NULL);\n\n\tDBG(0, (\"key_release: code=%d, mask=0x%x, sym=0x%x\\n\", ev.keycode, ev.state, keysym));\n\n\tswitch (keysym) {\n\tcase XK_Tab:\n\t\tfocus_next();\n\t\tbreak;\n\tcase XK_ISO_Left_Tab:\n\t\tfocus_prev();\n\t\tbreak;\n\tdefault:\n\t\tbreak;\n\t}\n}\n\nint main(int argc, char** argv)\n{\n\tXEvent\t\tev;\n\t\n\tchar\t\t*tray_sel_atom_name;\n\tchar\t\t*dpy_name = NULL;\n\n\tint\t\t\ti;\n\n\tfor (i = 1; i < argc; i++)\n\t\tif (!strcmp(argv[i], \"-n\"))\n\t\t\tdo_not_accept_focus = 1;\n\t\telse if (!strcmp(argv[i], \"-r\"))\n\t\t\trequest_focus = 1;\n\t\telse if (!strcmp(argv[i], \"-a\"))\n\t\t\tregister_accel = 1;\n\n\tif ((dpy = XOpenDisplay(dpy_name)) == NULL) {\n\t\tDIE((\"Error: could not open display\\n\"));\n\t}\n\n\tx11_trap_errors();\t\n\t\n\tif ((tray_sel_atom_name = (char *)malloc(strlen(TRAY_SEL_ATOM) + 2)) == NULL) {\n\t\tDIE((\"OOM\\n\"));\n\t}\n\n\tsnprintf(tray_sel_atom_name, strlen(TRAY_SEL_ATOM) + 2,\n\t\t\"%s%u\", TRAY_SEL_ATOM, DefaultScreen(dpy));\n\n\txa_tray_selection = XInternAtom(dpy, tray_sel_atom_name, False);\n\n\tfree(tray_sel_atom_name);\n\t\n\txa_tray_opcode = XInternAtom(dpy, \"_NET_SYSTEM_TRAY_OPCODE\", False);\n\txa_tray_data = XInternAtom(dpy, \"_NET_SYSTEM_TRAY_MESSAGE_DATA\", False);\n\txa_xembed_info = XInternAtom(dpy, \"_XEMBED_INFO\", False);\n\txa_xembed = XInternAtom(dpy, \"_XEMBED\", False);\n\n\tcreate_window(argc, argv);\n\n\tif (register_accel)\n\t\txembed_send_register_accelerator(dpy, tray, x11_get_server_timestamp(dpy, wnd), accel_id, accel_sym, accel_mods);\n\n\tXFlush(dpy);\n\n\tredraw_buttons();\n\n\tfor(;;) {\n\t\tXNextEvent(dpy, &ev);\n\t\tswitch(ev.type) {\n\t\tcase ClientMessage:\n\t\t\tclient_event(ev.xclient);\n\t\t\tbreak;\n/*        case DestroyNotify:*/\n/*            return 0;*/\n\t\tcase ConfigureNotify:\n\t\t\t/* Maintain size */\n\t\t\tif (ev.xconfigure.width != xsh.width || ev.xconfigure.height != xsh.height)\n\t\t\t\tXResizeWindow(dpy, wnd, xsh.width, xsh.height);\n\t\t\tbreak;\n\t\tcase KeyRelease:\n\t\t\tkey_release(ev.xkey);\n\t\t\tbreak;\n\t\tcase ButtonRelease:\n\t\t\txembed_send_request_focus(dpy, tray, x11_get_server_timestamp(dpy, wnd));\n\t\t\tbreak;\n\t\tcase UnmapNotify:\n\t\t\tif (ev.xunmap.window == wnd) XMapRaised(dpy, wnd);\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\t}\n\t\n\treturn 0;\n}\n"
  },
  {
    "path": "utils/tray-xembed-test/run",
    "content": "#!/bin/sh\n\n./tray-xembed-test &\n./tray-xembed-test -n &\n./tray-xembed-test &\n./tray-xembed-test -n &\n"
  },
  {
    "path": "utils/tray-xembed-test/xembed.c",
    "content": "/* -------------------------------\n* vim:tabstop=4:shiftwidth=4\n* xembed.c\n* Tue, 24 Aug 2004 12:05:38 +0700\n* -------------------------------\n* XEMBED protocol implementation\n* -------------------------------*/\n\n#include <X11/Xmd.h>\n#include <X11/X.h>\n#include <X11/Xlib.h>\n#include <X11/Xutil.h>\n\n#include \"common.h\"\n#include \"xembed.h\"\n#include \"xutils.h\"\n\nextern Atom xa_xembed;\nextern Atom xa_xembed_info;\n\nint xembed_retrive_data(Display *dpy, Window w, CARD32 *data)\n{\n\tAtom act_type;\n\tint act_fmt;\n\tunsigned long nitems, bytesafter;\n\tunsigned char *tmpdata;\n\tint rc;\n\tXGetWindowProperty(dpy,\n\t\t\t\t\t   w,\n\t\t\t\t\t   xa_xembed_info,\n\t\t\t\t\t   0,\n\t\t\t\t\t   2,\n\t\t\t\t\t   False,\n\t\t\t\t\t   xa_xembed_info,\n\t\t\t\t\t   &act_type,\n\t\t\t\t\t   &act_fmt,\n\t\t\t\t\t   &nitems,\n\t\t\t\t\t   &bytesafter,\n\t\t\t\t\t   &tmpdata);\n\n\tif (!x11_ok()) return XEMBED_RESULT_X11ERROR;\n\trc = (x11_ok() && act_type == xa_xembed_info && nitems == 2);\n\tif (rc) {\n\t\tdata[0] = ((CARD32 *) tmpdata)[0];\n\t\tdata[1] = ((CARD32 *) tmpdata)[1];\n\t}\n\tif (nitems && tmpdata != NULL) XFree(tmpdata);\n\treturn rc ? XEMBED_RESULT_OK : XEMBED_RESULT_UNSUPPORTED;\n}\n\nint xembed_post_data(Display *dpy, Window w, CARD32 *data)\n{\n\tXChangeProperty(dpy,\n\t\t\t\t\tw,\n\t\t\t\t\txa_xembed_info,\n\t\t\t\t\txa_xembed_info,\n\t\t\t\t\t32,\n\t\t\t\t\tPropModeReplace,\n\t\t\t\t\t(unsigned char *) data,\n\t                2);\n\treturn x11_ok() ? XEMBED_RESULT_OK : XEMBED_RESULT_X11ERROR;\n}\n\n"
  },
  {
    "path": "utils/tray-xembed-test/xembed.h",
    "content": "/* -------------------------------\n* vim:tabstop=4:shiftwidth=4\n* icons.h\n* Tue, 24 Aug 2004 12:05:38 +0700\n* -------------------------------\n* XEMBED protocol implementation\n* -------------------------------*/\n\n#ifndef _XEMBED_H_\n#define _XEMBED_H_\n\n#include <X11/X.h>\n\n/* Internal return codes */\n#define XEMBED_RESULT_OK\t\t\t\t0\n#define XEMBED_RESULT_UNSUPPORTED\t\t1\n#define XEMBED_RESULT_X11ERROR\t\t\t2\n\n/* XEMBED messages */\n#define XEMBED_EMBEDDED_NOTIFY\t\t\t0\n#define XEMBED_WINDOW_ACTIVATE  \t\t1\n#define XEMBED_WINDOW_DEACTIVATE  \t\t2\n#define XEMBED_REQUEST_FOCUS\t\t \t3\n#define XEMBED_FOCUS_IN \t \t\t\t4\n#define XEMBED_FOCUS_OUT  \t\t\t\t5\n#define XEMBED_FOCUS_NEXT \t\t\t\t6\t\n#define XEMBED_FOCUS_PREV \t\t\t\t7\n/* 8-9 were used for XEMBED_GRAB_KEY/XEMBED_UNGRAB_KEY */\n#define XEMBED_MODALITY_ON \t\t\t\t10\n#define XEMBED_MODALITY_OFF \t\t\t11\n#define XEMBED_REGISTER_ACCELERATOR     12\n#define XEMBED_UNREGISTER_ACCELERATOR   13\n#define XEMBED_ACTIVATE_ACCELERATOR     14\n\n/* Details for  XEMBED_FOCUS_IN */\n#define XEMBED_FOCUS_CURRENT\t\t\t0\n#define XEMBED_FOCUS_FIRST \t\t\t\t1\n#define XEMBED_FOCUS_LAST\t\t\t\t2\n\n/* Modifiers field for XEMBED_REGISTER_ACCELERATOR */\n#define XEMBED_MODIFIER_SHIFT\t\t\t(1 << 0)\n#define XEMBED_MODIFIER_CONTROL\t\t\t(1 << 1)\n#define XEMBED_MODIFIER_ALT\t\t\t\t(1 << 2)\n#define XEMBED_MODIFIER_SUPER\t\t\t(1 << 3)\n#define XEMBED_MODIFIER_HYPER\t\t\t(1 << 4)\n\n/* Flags for XEMBED_ACTIVATE_ACCELERATOR */\n#define XEMBED_ACCELERATOR_OVERLOADED\t(1 << 0)\n\n/* Directions for focusing */\n#define XEMBED_DIRECTION_DEFAULT\t\t0\n#define XEMBED_DIRECTION_UP_DOWN\t\t1\n#define XEMBED_DIRECTION_LEFT_RIGHT\t\t2\n\n/* Flags for _XEMBED_INFO */\n#define XEMBED_MAPPED\t\t\t\t\t(1 << 0)\n\n#define xembed_send_msg(dpy, dst, timestamp, msg, detail, data1, data2) \\\n\tx11_send_client_msg32(dpy, dst, dst, xa_xembed, timestamp, msg, detail, data1, data2)\n\n#define xembed_send_embedded_notify(dpy, src, dst, timestamp) \\\n\txembed_send_msg(dpy, dst, timestamp, XEMBED_EMBEDDED_NOTIFY, 0, src, 0)\n\n#define xembed_send_window_activate(dpy, dst, timestamp) \\\n\txembed_send_msg(dpy, dst, timestamp, XEMBED_WINDOW_ACTIVATE, 0, 0, 0)\n\n#define xembed_send_window_deactivate(dpy, dst, timestamp) \\\n\txembed_send_msg(dpy, dst, timestamp, XEMBED_WINDOW_DEACTIVATE, 0, 0, 0)\n\n#define xembed_send_request_focus(dpy, dst, timestamp) \\\n\txembed_send_msg(dpy, dst, timestamp, XEMBED_REQUEST_FOCUS, 0, 0, 0)\n\n#define xembed_send_focus_in(dpy, dst, focus, timestamp) \\\n\txembed_send_msg(dpy, dst, timestamp, XEMBED_FOCUS_IN, focus, 0, 0)\n\n#define xembed_send_focus_out(dpy, dst, timestamp) \\\n\txembed_send_msg(dpy, dst, timestamp, XEMBED_FOCUS_OUT, 0, 0, 0)\n\n#define xembed_send_focus_next(dpy, dst, timestamp) \\\n\txembed_send_msg(dpy, dst, timestamp, XEMBED_FOCUS_NEXT, 0, 0, 0)\n\n#define xembed_send_focus_prev(dpy, dst, timestamp) \\\n\txembed_send_msg(dpy, dst, timestamp, XEMBED_FOCUS_PREV, 0, 0, 0)\n\n#define xembed_send_register_accelerator(dpy, dst, timestamp, id, sym, mods) \\\n\txembed_send_msg(dpy, dst, timestamp, XEMBED_REGISTER_ACCELERATOR, id, sym, mods)\n\n#define xembed_send_activate_accelerator(dpy, dst, timestamp, id, overloaded) \\\n\txembed_send_msg(dpy, dst, timestamp, XEMBED_ACTIVATE_ACCELERATOR, id, overloaded, 0)\n\n\nint xembed_retrive_data(Display *dpy, Window w, CARD32 *data);\nint xembed_post_data(Display *dpy, Window w, CARD32 *data);\n\n\n#endif\n"
  },
  {
    "path": "utils/tray-xembed-test/xutils.c",
    "content": "/* ************************************\n * vim:tabstop=4:shiftwidth=4\n * xutils.c\n * Sun, 05 Mar 2006 17:56:56 +0600\n * ************************************\n * misc X11 utilities\n * ************************************/\n\n#include <X11/Xmd.h>\n#include <X11/Xlib.h>\n#include <X11/Xutil.h>\n#include <X11/Xatom.h>\n\n#include <limits.h>\n#include <unistd.h>\n\n#include \"common.h\"\n\n#include \"xutils.h\"\n\nstatic int trapped_x11_error_code = 0;\nstatic int (*old_x11_error_handler) (Display *, XErrorEvent *);\n\nint x11_error_handler(Display *dpy, XErrorEvent *err)\n{\n\tstatic char msg[PATH_MAX];\n\ttrapped_x11_error_code = err->error_code;\n\tXGetErrorText(dpy, trapped_x11_error_code, msg, sizeof(msg)-1);\n\tDBG(0, (\"X11 error: %s (request: %u/%u, resource 0x%x)\\n\", msg, err->request_code, err->minor_code, err->resourceid));\n\treturn 0;\n}\n\nint x11_ok_helper(const char *file, const int line, const char *func)\n{\n\tif (trapped_x11_error_code) {\n\t\tDBG(0, (\"X11 error %d detected at %s:%d:%s\\n\",\n\t\t\t\t\ttrapped_x11_error_code,\n\t\t\t\t\tfile,\n\t\t\t\t\tline,\n\t\t\t\t\tfunc));\n\t\ttrapped_x11_error_code = 0;\n\t\treturn FAILURE;\n\t} else\n\t\treturn SUCCESS;\n}\n\nint x11_current_error()\n{\n\treturn trapped_x11_error_code;\n}\n\nvoid x11_trap_errors()\n{\n\told_x11_error_handler = XSetErrorHandler(x11_error_handler);\n\ttrapped_x11_error_code = 0;\n}\n\nint x11_untrap_errors()\n{\n\tXSetErrorHandler(old_x11_error_handler);\n\treturn trapped_x11_error_code;\n}\n\nstatic Window timestamp_wnd;\nstatic Atom timestamp_atom = None;\n\nBool x11_wait_for_timestamp(Display *dpy, XEvent *xevent, XPointer data)\n{\n\treturn (xevent->type == PropertyNotify &&\n\t    xevent->xproperty.window == *((Window *)data) &&\n\t\txevent->xproperty.atom == timestamp_atom);\n}\n\nTime x11_get_server_timestamp(Display *dpy, Window wnd)\n{\n\tunsigned char c = 's';\n\tXEvent xevent;\n\n\tif (timestamp_atom == None)\n\t\ttimestamp_atom = XInternAtom(dpy, \"STALONETRAY_TIMESTAMP\", False);\n\n\t/* Trigger PropertyNotify event which has a timestamp field */\n\tXChangeProperty(dpy, wnd, timestamp_atom, timestamp_atom, 8, PropModeReplace, &c, 1);\n\tif (!x11_ok()) return CurrentTime;\n\n\t/* Wait for the event */\n\ttimestamp_wnd = wnd;\n\tXIfEvent(dpy, &xevent, x11_wait_for_timestamp, (XPointer)&timestamp_wnd);\n\n\treturn x11_ok() ? xevent.xproperty.time : CurrentTime;\n}\n\nint x11_get_win_prop32(Display *dpy, Window dst, Atom atom, Atom type, unsigned char **data, unsigned long *len)\n{\n\tAtom act_type;\n\tint act_fmt, rc;\n\tunsigned long bytes_after, prop_len, buf_len;\n\tunsigned char *buf = NULL;\t\n\n\t*data = NULL; *len = 0;\n\t/* Get the property size */\n\trc = XGetWindowProperty(dpy, dst, atom,\n\t\t    0L, 0L, False, type, &act_type, &act_fmt,\n\t\t\t&buf_len, &bytes_after, &buf);\n\n\t/* The requested property does not exist */\n\tif (rc != Success || act_type != type || act_fmt != 32) return FAILURE;\n\n\tXFree(buf);\n\n\t/* Now go get the property */\n\tprop_len = bytes_after / 4;\n\tXGetWindowProperty(dpy, dst, atom,\n\t\t\t0L, prop_len, False, type, &act_type, &act_fmt,\n\t\t\t&buf_len, &bytes_after, &buf);\n\n\tif (x11_ok()) {\n\t\t*len = buf_len;\n\t\t*data = buf;\n\t\treturn SUCCESS;\n\t} else\n\t\treturn FAILURE;\n}\n\nint x11_send_client_msg32(Display *dpy, Window dst, Window wnd, Atom type, long data0, long data1, long data2, long data3, long data4)\n{\n\tXEvent ev;\n\tev.xclient.type = ClientMessage;\n\tev.xclient.serial = 0;\n\tev.xclient.send_event = True;\n\tev.xclient.message_type = type;\n\tev.xclient.window = wnd;\n\tev.xclient.format = 32;\n\tev.xclient.data.l[0] = data0;\n\tev.xclient.data.l[1] = data1;\n\tev.xclient.data.l[2] = data2;\n\tev.xclient.data.l[3] = data3;\n\tev.xclient.data.l[4] = data4;\n\t/* XXX: Replace 0xFFFFFF for better portability? */\n\treturn XSendEvent(dpy, dst, False, 0xFFFFFF, &ev);\n}\n\nint x11_set_window_size(Display *dpy, Window w, int x, int y)\n{\n\tXSizeHints xsh;\n\t\n\txsh.flags = PSize;\n\txsh.width = x;\n\txsh.height = y;\n\n\tXSetWMNormalHints(dpy, w, &xsh);\n\tXResizeWindow(dpy, w, x, y);\n\n\tif (!x11_ok()) {\n\t\tDBG(0, (\"failed to force 0x%x size to %dx%d\\n\", w, x, y));\n\t\treturn FAILURE;\n\t}\n\n\treturn SUCCESS;\n}\n\nint x11_get_window_size(Display *dpy, Window w, int *x, int *y)\n{\n\tXWindowAttributes xwa;\n\n\tXGetWindowAttributes(dpy, w, &xwa);\n\t\n\tif (!x11_ok()) {\n\t\tDBG(0, (\"failed to get 0x%x attributes\\n\", w));\n\t\treturn FAILURE;\n\t}\n\n\t*x = xwa.width;\n\t*y = xwa.height;\n\treturn SUCCESS;\n}\n\nint x11_get_window_min_size(Display *dpy, Window w, int *x, int *y)\n{\n\tXSizeHints xsh;\n\tlong flags = 0;\n\tint rc = FAILURE;\n\tif (XGetWMNormalHints(dpy, w, &xsh, &flags)) {\n\t\tflags = xsh.flags & flags;\n\t\tDBG(4, (\"flags = 0x%x\\n\", flags));\n\t\tif (flags & PMinSize) {\n\t\t\tDBG(4, (\"min_width = %d, min_height = %d\\n\", xsh.min_width, xsh.min_height));\n\t\t\t*x = xsh.min_width;\n\t\t\t*y = xsh.min_height;\n\t\t\trc = SUCCESS;\n\t\t}\n\t}\n\treturn rc;\n}\n\nint x11_get_window_abs_coords(Display *dpy, Window dst, int *x, int *y)\n{\n\tWindow root, parent, *wjunk = NULL;\n\tint x11_, y_, x11__, y__;\n\tunsigned int junk;\n\n\tXGetGeometry(dpy, dst, &root, &x11_, &y_, &junk, &junk, &junk, &junk);\n\tXQueryTree(dpy, dst, &root, &parent, &wjunk, &junk);\n\n\tif (junk != 0) XFree(wjunk);\n\n\tif (!x11_ok())\n\t\treturn FAILURE;\n\n\tif (parent == root) {\n\t\t*x = x11_;\n\t\t*y = y_;\n\t} else {\n\t\tif (x11_get_window_abs_coords(dpy, parent, &x11__, &y__)) {\n\t\t\t*x = x11_ + x11__;\n\t\t\t*y = y_ + y__;\n\t\t} else\n\t\t\treturn FAILURE;\n\t}\n\t\n\treturn SUCCESS;\n}\n\nvoid x11_extend_root_event_mask(Display *dpy, long mask)\n{\n\tstatic long old_mask = 0;\n\told_mask |= mask;\n\tXSelectInput(dpy, RootWindow(dpy, DefaultScreen(dpy)), old_mask);\n}\n\n#ifdef DEBUG\nconst char *x11_event_names[LASTEvent] = {\n\t\"unknown0\",\n\t\"unknown1\",\n\t\"KeyPress\",\n\t\"KeyRelease\",\n\t\"ButtonPress\",\n\t\"ButtonRelease\",\n\t\"MotionNotify\",\n\t\"EnterNotify\",\n\t\"LeaveNotify\",\n\t\"FocusIn\",\n\t\"FocusOut\",\n\t\"KeymapNotify\",\n\t\"Expose\",\n\t\"GraphicsExpose\",\n\t\"NoExpose\",\n\t\"VisibilityNotify\",\n\t\"CreateNotify\",\n\t\"DestroyNotify\",\n\t\"UnmapNotify\",\n\t\"MapNotify\",\n\t\"MapRequest\",\n\t\"ReparentNotify\",\n\t\"ConfigureNotify\",\n\t\"ConfigureRequest\",\n\t\"GravityNotify\",\n\t\"ResizeRequest\",\n\t\"CirculateNotify\",\n\t\"CirculateRequest\",\n\t\"PropertyNotify\",\n\t\"SelectionClear\",\n\t\"SelectionRequest\",\n\t\"SelectionNotify\",\n\t\"ColormapNotify\",\n\t\"ClientMessage\",\n\t\"MappingNotify\"\n};\n\nvoid x11_dump_win_info(Display *dpy, Window wid)\n{\t\n#ifdef _ST_WITH_DUMP_WIN_INFO\n\tchar cmd[PATH_MAX];\n\n\tDBG(4, (\"Dumping info for 0x%x\\n\", wid));\n\n\tsnprintf(cmd, PATH_MAX, \"xwininfo -size -bits -stats -id 0x%x\\n\", (unsigned int) wid);\n\tsystem(cmd);\n\n\tsnprintf(cmd, PATH_MAX, \"xprop -id 0x%x\\n\", (unsigned int) wid);\n\tsystem(cmd);\n#endif\n}\t\n#endif\n"
  },
  {
    "path": "utils/tray-xembed-test/xutils.h",
    "content": "/* ************************************\n * vim:tabstop=4:shiftwidth=4\n * xutils.h\n * Sun, 05 Mar 2006 17:16:44 +0600\n * ************************************\n * misc X11 utilities\n * ************************************/\n\n#ifndef _XUTILS_H_\n#define _XUTILS_H_\n\n#include <X11/X.h>\n\n/* Return current server timestamp */\nTime x11_get_server_timestamp(Display *dpy, Window wnd);\n\n/* Convinient way to send a message */\nint x11_send_client_msg32(Display *dpy, Window dst, Window wnd,\n                      Atom type, long data0, long data1,\n                      long data2, long data3, long data4);\n\n/* Set window size updating its size hints */\nint x11_set_window_size(Display *dpy, Window w, int x, int y);\n\n/* Get window size (uses XGetWindowAttributes) */\nint x11_get_window_size(Display *dpy, Window w, int *x, int *y);\n\n/* Get window minimal size hints if they are available */\nint x11_get_window_min_size(Display *dpy, Window w, int *x, int *y);\n\n/* Retrive 32-bit property from the target window */\nint x11_get_win_prop32(Display *dpy, Window dst, Atom atom, Atom type, unsigned char **data, unsigned long *len);\n\n/* Retrive window-list property from the specified window */\n#define x11_get_winlist_prop(dpy, dst, atom, data, len) x11_get_win_prop32(dpy, dst, atom, XA_WINDOW, data, len)\n\n/* Shortcut for the root window case */\n#define x11_get_root_winlist_prop(dpy, atom, data, len) x11_get_winlist_prop(dpy, DefaultRootWindow(dpy), atom, data, len)\n\n/* Returns window absolute position (relative to the root window) */\nint x11_get_window_abs_coords(Display *dpy, Window dst, int *x, int *y);\n\n/* Extends event mask of the root window */\nvoid x11_extend_root_event_mask(Display *dpy, long mask);\n\n/* Checks if any X11 errors have occured so far. */\n#define x11_ok() x11_ok_helper(__FILE__, __LINE__, __FUNC__)\nint x11_ok_helper(const char * file, const int line, const char *func);\n\n/* WARNING: following functions do not support nested calls */\n\n/* Installs custom X11 error handler */\nvoid x11_trap_errors();\n\n/* Removes custom X11 error handler */\nint x11_untrap_errors();\n\n#ifdef DEBUG\n\n/* Array that maps event_number -> event_name */\nconst extern char *x11_event_names[LASTEvent];\n\n/* Dumps window info. Does nothing unless _ST_WITH_DUMP_WIN_INFO is defined,\n * launches xwininfo and xwinprop otherwise */\nvoid x11_dump_win_info(Display *dpy, Window w);\n\n#else\n\n/* Dummy delcaration */\n#define x11_dump_win_info(dpy,w) do {} while (0);\n#endif\n\n#endif\n\n"
  }
]