Showing preview only (414K chars total). Download the full file or copy to clipboard to get everything.
Repository: kolbusa/stalonetray
Branch: master
Commit: 58d92e26d726
Files: 68
Total size: 393.5 KB
Directory structure:
gitextract_7_pxnf_6/
├── .clang-format
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ └── workflows/
│ ├── build.yml
│ ├── lint.yml
│ └── release.yml
├── .gitignore
├── AUTHORS
├── LICENSE
├── README.md
├── TODO
├── generate-manpage.sh
├── meson.build
├── meson.format
├── meson.options
├── src/
│ ├── common.h
│ ├── debug.c
│ ├── debug.h
│ ├── embed.c
│ ├── embed.h
│ ├── icons.c
│ ├── icons.h
│ ├── image.c
│ ├── image.h
│ ├── kde_tray.c
│ ├── kde_tray.h
│ ├── layout.c
│ ├── layout.h
│ ├── list.h
│ ├── main.c
│ ├── meson.build
│ ├── scrollbars.c
│ ├── scrollbars.h
│ ├── settings.c
│ ├── settings.h
│ ├── tray.c
│ ├── tray.h
│ ├── wmh.c
│ ├── wmh.h
│ ├── xembed.c
│ ├── xembed.h
│ ├── xinerama.c
│ ├── xinerama.h
│ ├── xutils.c
│ └── xutils.h
├── stalonetray.xml.in
├── stalonetrayrc.sample
└── utils/
├── get_props/
│ └── main.c
├── py-gtk-example/
│ ├── LICENSE
│ ├── README.md
│ ├── gtk-example.glade
│ ├── gtk-example.py
│ └── python3.xpm
├── tray-test-fdo/
│ ├── main.c
│ ├── run_icons
│ ├── seq1
│ ├── seq2
│ ├── xembed.c
│ └── xembed.h
├── tray-test-gtk/
│ └── traytest
└── tray-xembed-test/
├── common.h
├── debug.c
├── main.c
├── run
├── xembed.c
├── xembed.h
├── xutils.c
└── xutils.h
================================================
FILE CONTENTS
================================================
================================================
FILE: .clang-format
================================================
---
Language: Cpp
AccessModifierOffset: 0
AlignAfterOpenBracket: DontAlign
AlignConsecutiveMacros: false
AlignConsecutiveAssignments: false
AlignConsecutiveBitFields: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: DontAlign
AlignOperands: DontAlign
AlignTrailingComments: false
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortEnumsOnASingleLine: true
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterCaseLabel: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: true
AfterStruct: false
AfterUnion: false
BeforeElse: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Linux
BreakBeforeTernaryOperators: true
BreakStringLiterals: true
ColumnLimit: 79
CommentPragmas: '^ IWYU pragma:'
ContinuationIndentWidth: 4
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
ForEachMacros:
- foreach
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
- Regex: '.*'
Priority: 1
SortPriority: 0
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentCaseLabels: false
IndentCaseBlocks: false
IndentGotoLabels: false
IndentPPDirectives: None
IndentWidth: 4
IndentWrappedFunctionNames: true
InsertTrailingCommas: None
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
Standard: c++03
TabWidth: 4
UseCRLF: false
UseTab: Never
WhitespaceSensitiveMacros:
- STRINGIZE
- PP_STRINGIZE
- BOOST_PP_STRINGIZE
...
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the buggy behavior (even if intermittent):
1. Use WM FOO (describe configuration if relevant)
2. Launch app X
3. Launch app Y
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Log files**
Attach stalonetray output with `--log-level trace` for good and bad behavior.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/workflows/build.yml
================================================
name: Build
on:
push:
branches:
- master
pull_request: {}
jobs:
ubuntu:
name: Ubuntu
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
featureset: ["base", "xinerama", "xpm", "native_kde", "all", "debug"]
steps:
- name: Checkout source code
uses: actions/checkout@v5
- name: Disable man-db auto-update
run: |
echo "set man-db/auto-update false" | sudo debconf-communicate
sudo dpkg-reconfigure man-db
sudo rm -f /var/lib/man-db/auto-update
- name: Install and cache dependencies
uses: awalsh128/cache-apt-pkgs-action@v1
with:
packages: >
xsltproc docbook-xsl libxpm-dev libx11-dev libxinerama-dev meson
- name: Set up build directory
run: >
if [ "${{ matrix.featureset }}" = "base" ]; then
echo "== Basic build =="
meson setup --buildtype release build -D c_std=gnu2x
elif [ "${{ matrix.featureset }}" = "all" ]; then
echo "== Enabling all features =="
meson setup --buildtype release build -D c_std=gnu2x \
-D xinerama=enabled -D xpm=enabled -D native_kde=enabled
elif [ "${{ matrix.featureset }}" = "debug" ]; then
echo "== Debug build with all features enabled =="
meson setup --buildtype debug build -D c_std=gnu2x \
-D xinerama=enabled -D xpm=enabled -D native_kde=enabled \
-D trace_events=true -D delay_embedding_confirmation=true \
-D dump_window_information=true -D exit_gracefully=true
else
echo "== Enabling feature ${{ matrix.featureset }} =="
meson setup --buildtype release build -D c_std=gnu2x \
-D ${{ matrix.featureset }}=enabled
fi
- name: Build
run: meson compile -C build stalonetray
- name: Build manpage
run: meson compile -C build manpage
freebsd:
name: FreeBSD
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
version: ["14.2", "14.3", "15.0"]
steps:
- name: Checkout source code
uses: actions/checkout@v5
- name: Disable man-db auto-update
run: |
echo "set man-db/auto-update false" | sudo debconf-communicate
sudo dpkg-reconfigure man-db
sudo rm -f /var/lib/man-db/auto-update
- name: Install and cache vbox dependencies
uses: awalsh128/cache-apt-pkgs-action@v1
with:
execute_install_scripts: true
packages: >
zstd libvirt-daemon-system virt-manager qemu-kvm libosinfo-bin axel
expect screen sshpass
- name: Build on FreeBSD VM
uses: vmactions/freebsd-vm@v1
with:
arch: amd64
release: ${{ matrix.version }}
prepare: |
mkdir -p /usr/local/etc/pkg/repos
repoFile="/usr/local/etc/pkg/repos/FreeBSD.conf"
if [ ${{ matrix.version }} = "15.0" ]; then
repoURL="http://pkg.freebsd.org/\${ABI}/latest"
else
repoURL="pkg+http://pkg.freebsd.org/\${ABI}/latest"
fi
echo "FreeBSD: { url: ${repoURL} }" > "$repoFile"
IGNORE_OSVERSION=yes pkg update -f
pkg install -y pkgconf meson libX11 libXpm libXinerama docbook-xsl \
libxslt
run: |
echo "== Basic build =="
meson setup --buildtype release build
meson compile -C build stalonetray
meson compile -C build manpage
for feature in xinerama xpm native_kde; do
echo "== Building with $feature enabled =="
meson setup --wipe build -D $feature=enabled
meson compile -C build stalonetray
meson compile -C build manpage
done
echo "== Building with all features enabled =="
meson setup --wipe build \
-D xinerama=enabled -D xpm=enabled -D native_kde=enabled
meson compile -C build stalonetray
meson compile -C build manpage
================================================
FILE: .github/workflows/lint.yml
================================================
name: Lint
on:
push:
branches:
- master
pull_request: {}
jobs:
new-version:
name: Version Bump Check
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
outputs:
next-version: ${{ steps.next-version.outputs.version }}
has-next-version: ${{ steps.next-version.outputs.hasNextVersion }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- id: next-version
uses: thenativeweb/get-next-version@2.7.1
- name: Install meson
run: pip install 'meson>=1.5.0'
- name: Show the next version
run: |
mesonV=$(meson introspect meson.build --projectinfo | jq -r .version)
wantedV="${{ steps.next-version.outputs.version }}"
shouldBump="${{ steps.next-version.outputs.hasNextVersion }}"
echo "Should bump version: $shouldBump"
echo "Next version: $wantedV"
echo "Current version on meson.build: $mesonV"
if "$shouldBump" && [ "$mesonV" != "$wantedV" ]; then
echo "Version bump mismatch! Expected $wantedV but found $mesonV"
exit 1
elif "$shouldBump"; then
echo "Version bump detected and matches meson.build"
else
echo "No version change detected, nothing to do"
fi
meson-format:
name: Meson Format
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v5
- name: Install meson
run: pip install 'meson>=1.5.0'
- name: Run meson-format
run: meson format --recursive -q
================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
push:
branches:
- master
concurrency:
group: merge-to-master
cancel-in-progress: false
permissions:
contents: write
jobs:
new-version:
name: Get New Version
runs-on: ubuntu-latest
outputs:
next-version: ${{ steps.next-version.outputs.version }}
has-next-version: ${{ steps.next-version.outputs.hasNextVersion }}
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- id: next-version
uses: thenativeweb/get-next-version@2.7.1
- name: Show the next version
run: |
echo "Should bump version: ${{ steps.next-version.outputs.hasNextVersion }}"
echo "Next version: ${{ steps.next-version.outputs.version }}"
create-release:
name: Create
runs-on: ubuntu-latest
if: needs.new-version.outputs.has-next-version == 'true'
needs: [new-version]
steps:
- uses: actions/checkout@v5
- name: Build distribution tarball
run: |
echo "set man-db/auto-update false" | sudo debconf-communicate
sudo dpkg-reconfigure man-db
sudo rm -f /var/lib/man-db/auto-update
sudo apt-get update
sudo apt-get install -y \
xsltproc docbook-xsl libxpm-dev libx11-dev libxinerama-dev
pip install 'meson>=1.5.0'
meson setup --buildtype release _build -D c_std=gnu2x
meson dist -C _build
- uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.new-version.outputs.next-version }}
generate_release_notes: true
files: "_build/meson-dist/*"
================================================
FILE: .gitignore
================================================
# Generated by build system
/aclocal.m4
/autom4te.cache
Makefile.in
Makefile
/compile
/config.log
/config.status
/configure
/depcomp
/install-sh
/missing
.deps/
/src/config.h.in
/src/config.h
/src/stamp-h1
/build/
/.cache/
# Build artifacts
/src/*.o
/stalonetray.xml
# Output artifacts
/src/stalonetray
/stalonetray.1
/stalonetray.html
================================================
FILE: AUTHORS
================================================
Roman Dubtsov <dubtsov@gmail.com>
d3adb5 <me@d3adb5.net>
================================================
FILE: LICENSE
================================================
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
================================================
FILE: README.md
================================================
# STAnd aLONE TRAY [![Build][badge-build]][yaml-build] [![Lint][badge-lint]][yaml-lint]
[badge-build]: https://github.com/d3adb5/stalonetray/actions/workflows/build.yml/badge.svg
[yaml-build]: https://github.com/d3adb5/stalonetray/actions/workflows/build.yml
[badge-lint]: https://github.com/d3adb5/stalonetray/actions/workflows/lint.yml/badge.svg
[yaml-lint]: https://github.com/d3adb5/stalonetray/actions/workflows/lint.yml
Stalonetray is a STAnd-aLONE system TRAY (notification area) for Unix desktops
using the X11 windowing system. It has minimal default build and run-time
dependencies: the Xlib and libXinerama, though you could do away with the
latter by disabling a feature for even more minimalism. Stalonetray runs under
virtually any window manager.
To start using stalonetray, just copy `stalonetrayrc.sample` to
`~/.stalonetrayrc` or to `$XDG_CONFIG_HOME/stalonetrayrc`. It is well-commented
and should suffice for a quick start.
Note that some features are disabled by default and may not work out of the
box, depending on how stalonetray was built by the package maintainer. See the
"Building from source" section below if you want to compile it yourself with
the features you need.
## Maintenance status
This project was originally developed by [Roman Dubtsov (kolbusa)][gh-kolbusa]
and recently changed hands. Roman is still involved with, but no longer
actively maintains the project.
**To him goes all the credit for creating and maintaining this project for many
years. Thank you, Roman!**
[gh-kolbusa]: https://github.com/kolbusa
## Installation
Package managers are the most convenient way to install stalonetray. It is
packaged for several Linux distributions and BSD variants. On Debian and
Ubuntu, run:
```sh
sudo apt install stalonetray
```
On Fedora run:
```sh
sudo dnf install stalonetray
```
## Building from source
Stalonetray uses [Meson](https://mesonbuild.com/). Refer to the `meson.options`
file for available build options and their default values.
To build stalonetray using Meson, ensure necessary dependencies are installed
--- by default only Xlib and libXinerama development packages are required ---
and run the standard Meson build commands:
```sh
meson setup builddir
meson compile -C builddir stalonetray
```
This should build the `stalonetray` binary in the `builddir` directory.
To build stalonetray's documentation, you'll need `xsltproc` and DocBook
stylesheets installed first. Then build the `manpage` target:
```sh
meson compile -C builddir manpage
```
This creates the `stalonetray.1` file in the `builddir` directory.
Installation from source can be done with:
```sh
meson install -C builddir
```
================================================
FILE: TODO
================================================
+ Each icon must have a newly created parent window, which matches its
dimentions (for ease of move and for them to be centered ?).
+ Anvanced grow
+ Reorganize settings (and vars)
- Make use of resources (WONTDO)
+ rc file
+ Need to handle unusual sizes of "icons" (and resolve when to force
constraints)
+ Make satray survive even if window suddenly dissapears
(during some operation, when destroy\unmap was not handled)
+ KDE tray implementation (solves previous problem)
+ Handle tray icon move/resize
+ Simplify rc file syntax/re-reorganize settings
+ main.c cleanup
+ update transparent/parent background on icon move/resize
+ review DBG() calls in accordance with doc/DEBUG-LEVELS
+ reacquire tray selection after loss (if holder exits) (update sample rc(done))
+ find and fix typos
+ fix (almost all) warnings with -Wall
+ .spec file for building rpms
+ man page
+ tray grow with wnd deco
for 0.4
+ default values in the manpage, MWM/EWMH compliancy must be noted
+ ontop/onbottom/normal (wnd_layer param)
+ xorg7 build troubles
+ more flexible wnd deco specification
+ make use of ewmh spec (_NET_WM_STATE_SKIP_PAGER, _NET_WM_WINDOW_TYPE_DOCK and friends)
+ all configure.in needs review (refuse to build if something goes wrong)
+ update DBG so that it does not require C99
+ *wm tests (pwm(2), e17 have some issues):
+ WMaker (everything works)
+ Fvwm (for sure)
+ PWM(1) (no_{title,border} do not work)
+ E17 (the same =()
+ something has happened to vertical layout (all works)
+ tests on sf cf
+ now stalonetray can be build with Sun ONE 8 C compiler =) (because it's sources conform to C90 specs)
+ change status to beta =)
for 0.5(unreleased, renamed to 0.6)
+ code cleanup (again :() (REVIEW layout.c very thoroughly!!!)
+ fix geometry issues
+ GNUisms in debug.h
+ icons are not aligned properly inside their mid-parents
+ basename can be either in libgen.h or in string.h o_O : XPG ver from libgen.h was chosen
+ update manpages, etc (docbook for documentation ?)
+ fix default rc file name
+ backtrace in configure.in is not detected ?
+ make boolean cfg file opts to have _optional_ argument
+ make permanent behaviour default and remove it from configuration
+ default values in documentation
+ validate stalonetray.xml
+ collect icons on startup (test, KDE icons issue) needs XEMBED ?!!!
+ Full XEMBED implementation (?)
+ issue with composite extension on (seq1,2)
+ version info
+ pixmap bg
+ check rc keyword names for concistency and update stalonetrayrc.sample
+ test non-debug version
+ test with LARGE pixmap
+ PORTING
+ fix the website for IE (and colors)
for 0.7
- DOXYGEN documentation (WONTDO, 0.8?)
- one state instead of multiple flags for icons (WONTDO)
- split main.c into: signals.c events.c main.c (WONTDO)
- support for WINE icons (WONTDO: isn't it WINE problem?)
+ consistent naming scheme for funcs
+ shrink back mode
+ reconsider API
+ improve X11-using code
+ params for boolean cmd line parameters (reconsider cmd line params and rc file directives) ??? (--skip-taskbar=true)
+ indicate parsing errors (make them fatal?)
+ comments
+ bg tinting (with fade out)
+ wmaker mode (SORTOFWORKS)
+ fall back to legacy KDE icons handling scheme when list of icons is not available
+ withdrawn mode should be configurable
+ XEMBED test suite ?
+ rethink command line params names
+ merge changes from 0.6 bugfixes branch (x86_64 patches pending) (size hints handling?)
+ layout _needs_ fixin` (TEST!!!!, seq1) (HOORAY!!!!!)
+ automatic version string in _all_ documentation
+ update README/docs
+ sanitize DBG levels
for 0.7.6
+ ensure that x11_ok() called at all exit paths from x11-calling functions
+ startup issues (collect existing icons)
- focus issue in Wmaker
for 0.8
+ slot size vs. icon size
+ review ignore_icon_resize usage
+ all dims must be given in slots not in pixels
+ scrollable tray area? (see http://wnd.katei.fi/weblog/entry-7)
+ need repeater mode
+ support for mouse wheel (scroll vertically if mousewheeling over vert sb and
horizontally if over horz sb; default to orientation otherwise)
+ strut WM hints (requires Xinerama?)
+ fix geometry code
+ fix wmaker docking mode (--widthrawn=off/simple/wmaker)
- proper dockapp behaviour in WM
+ add --kludge command line param that turns kludges on/off
+ move respect icon hints under this category
+ geometry bugs (wrong placement) (CANTREPRODUCE?)
+ valgrind
+ color background
+ xpm background
+ xpm background + transparency
+ remote interface (via send event to tray selection manager)
+ profile the code
+ powertop; fix event loop to block if there are no events and no scrolling
+ proper handling of signals required!!!
+ SIGUSR1 handing is OK (its for debug only anyway)
+ termination signals wont be handled to avoid hacks...
+ fix complaint about icon placement in WM dockapp mode
+ fix complaint about window layer under WM (CANTREPRODUCE, Window Maker 0.92.0)
+ rework DBG/ERR/DIE
+ update sample RC
+ proper formatting of log lines (date + remove location)
+ fix bug with resize
for 0.8.1,2,../0.9
- systray 0.3 spec (VISUAL)
- proper dockapp behaviour in WM
- profile the code
- gprofile; use array in icons.c instead of list
- RENDER implementation of tinting/compositing, take implementation from rxvt
- sanitize x11 calls: check return values instead of relying on x11_ok() whenever possible
- track size changes of the icons that failed to fit into the tray
- rework root transparency code (handle cases when root pmap size is less than root window; take implementation from urxvt)
- restructure command line params and reduce their count
- rework command line interface
================================================
FILE: generate-manpage.sh
================================================
#!/bin/sh
#
# A simple script to build the manpage for stalonetray using xsltproc.
#
# The logic in this script would've been placed in the meson.build file, but as
# of the time of writing Meson doesn't provide a way to write intermediate
# files from the stdout of a command, instead relying on the command creating
# the file itself.
#
# Plenty of the logic was viable within Meson, but was moved here to simplify
# the meson.build file.
#
# Usage: ./generate-manpage.sh template output stalonetray-version
#
# Requires: sed, xsltproc
#
# TODO: Accept features as arguments to include/exclude parts of the manpage.
template="$1"
output="$2"
version_str="$3"
stylesheet=""
intermediate_xml="$(basename "$template" .in)"
for cmd in sed xsltproc; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo "$cmd is required to build the manpage." >&2
exit 1
fi
done
for root in \
/usr/share/sgml/docbook/stylesheet/xsl/nwalsh \
/usr/share/xml/docbook/stylesheet/docbook-xs \
/usr/share/xml/docbook/stylesheet/docbook-xsl-nons \
/usr/share/xml/docbook/xsl-stylesheets \
/usr/share/sgml/docbook/xsl-stylesheets \
/usr/share/xml/docbook/xsl-stylesheets-*-nons \
/usr/share/sgml/docbook/xsl-stylesheets \
/usr/share/xsl/docbook \
/usr/local/share/xsl/docbook \
; do
if [ -f "$root/manpages/docbook.xsl" ]; then
stylesheet="$root/manpages/docbook.xsl"
break
fi
done
if [ -z "$stylesheet" ]; then
echo "Could not find docbook manpage stylesheet." >&2
exit 2
fi
sed "s/@VERSION_STR@/$version_str/g" "$template" > "$intermediate_xml"
xsltproc -o "$output" "$stylesheet" "$intermediate_xml"
rm -f "$intermediate_xml"
# vim: tabstop=2 shiftwidth=2 expandtab
================================================
FILE: meson.build
================================================
project(
'stalonetray',
'c',
version: '1.0.3',
default_options: ['warning_level=3', 'c_std=gnu23'],
meson_version: '>=1.1.0',
)
build_args = [
'-O2',
'-Wno-missing-braces',
'-DPROGNAME="@0@"'.format(meson.project_name()),
'-DVERSION="@0@"'.format(meson.project_version()),
]
project_sources = []
# == Dependencies & Optional Features ==========================================
dependencies = {
'x11': dependency('x11', required: true),
'xinerama': dependency('xinerama', required: get_option('xinerama')),
'xpm': dependency('xpm', required: get_option('xpm')),
}
assert(dependencies['x11'].found(), 'Xlib is required to build stalonetray')
# == Sources & Headers =========================================================
subdir('src')
# == Build Options =============================================================
feature_list = []
if get_option('xinerama').enabled()
build_args += ['-D_ST_WITH_XINERAMA']
feature_list += ['xinerama']
endif
if get_option('xpm').enabled()
build_args += ['-D_ST_WITH_XPM']
feature_list += ['xpm']
endif
if get_option('native_kde').enabled()
build_args += ['-D_ST_WITH_NATIVE_KDE']
feature_list += ['native_kde']
endif
if get_option('trace_events')
build_args += ['-D_ST_TRACE_EVENTS']
feature_list += ['trace_events']
endif
if get_option('delay_embedding_confirmation')
build_args += ['-D_ST_DELAY_EMBEDDING_CONFIRMATION']
feature_list += ['delay_embedding_confirmation']
endif
if get_option('dump_window_information')
build_args += ['-D_ST_DUMP_WINDOW_INFORMATION']
feature_list += ['dump_window_information']
endif
if get_option('exit_gracefully')
build_args += ['-D_ST_EXIT_GRACEFULLY']
feature_list += ['exit_gracefully']
endif
build_args += ['-DFEATURE_LIST="@0@"'.format(' '.join(feature_list))]
# == Targets ===================================================================
# TODO: Replace this with dict.values() when meson 1.10 is released.
_dependencies = []
foreach _, dep : dependencies
_dependencies += [dep]
endforeach
executable(
'stalonetray',
project_sources,
c_args: build_args,
dependencies: _dependencies,
install: true,
)
# == Documentation =============================================================
gen_manpage = find_program('generate-manpage.sh')
custom_target(
'manpage',
input: 'stalonetray.xml.in',
output: 'stalonetray.1',
command: [gen_manpage, '@INPUT@', '@OUTPUT@', meson.project_version()],
install: true,
install_dir: join_paths(get_option('prefix'), get_option('mandir'), 'man1'),
)
# == Sample Configuration ======================================================
install_data(
'stalonetrayrc.sample',
install_dir: get_option('sysconfdir'),
rename: ['stalonetrayrc'],
)
================================================
FILE: meson.format
================================================
indent_by = ' '
max_line_length = 80
================================================
FILE: meson.options
================================================
option('xinerama', type: 'feature', value: 'enabled',
description: 'Enable Xinerama support for multiple monitors')
option('xpm', type: 'feature', value: 'auto',
description: 'Enable XPM background support')
option('native_kde', type: 'feature', value: 'auto',
description: 'Enable native KDE system tray support')
option('trace_events', type: 'boolean', value: false,
description: 'Enable X11 event tracing for debugging purposes')
option('delay_embedding_confirmation', type: 'boolean', value: false,
description: 'Delay sending XEMBED_EMBEDDED_NOTIFY message for debugging')
option('dump_window_information', type: 'boolean', value: false,
description: 'Use xprop/xwininfo to dump icon window information')
option('exit_gracefully', type: 'boolean', value: true,
description: 'Use non-portable hack to exit gracefully on signal')
================================================
FILE: src/common.h
================================================
/* -------------------------------
* vim:tabstop=4:shiftwidth=4
* common.h
* Mon, 01 May 2006 01:45:08 +0700
* -------------------------------
* Common declarations
* -------------------------------*/
#ifndef _COMMON_H_
#define _COMMON_H_
#include "debug.h"
/* Default icon size */
#define FALLBACK_ICON_SIZE 24
/* Minimal icon size */
#define MIN_ICON_SIZE 16
/* Default KDE icon size */
#define KDE_ICON_SIZE 22
/* Number of time icon is allowed to change its size after which
* stalonetray gives up */
#define ICON_SIZE_RESETS_THRESHOLD 2
/* Meaningful names for return values */
#define SUCCESS 1
#define FAILURE 0
/* Simple macro to return status and log it if necessary */
#define RETURN_STATUS(rc) \
do { \
LOG_TRACE( \
("status = %s\n", ((rc) == SUCCESS ? "SUCCESS" : "FAILURE"))); \
return rc; \
} while (0)
/* Meaningful names for return values of icon mass-operations */
#define MATCH 1
#define NO_MATCH 0
/* Simple macro to return mass-op status and log it if necessary */
#define RETURN_MATCH(rc) \
do { \
LOG_TRACE(("status = %s\n", rc == MATCH : "MATCH" : "NO_MATCH")); \
return rc; \
} while (0)
/* Meaningful names for return values of icon mass-operations */
#define MATCH 1
/* Portable macro for function name */
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#define __FUNC__ __func__
#elif defined(__GNUC__) && __GNUC__ >= 3
#define __FUNC__ __FUNCTION__
#else
#define __FUNC__ "unknown"
#endif
/* DIE */
#define DIEDIE() exit(-1)
/* Print a message and... DIE */
#define DIE(message) \
do { \
LOG_ERROR(message); \
DIEDIE(); \
} while (0)
/* Log OOM condition */
#define LOG_ERR_IE(message) \
do { \
LOG_ERROR( \
("Internal error, please report to maintaner (see AUTHORS)\n")); \
LOG_ERROR(message); \
} while (0);
/* DIE on internal error */
#define DIE_IE(message) \
do { \
LOG_ERR_IE(message); \
DIEDIE(); \
} while (0)
/* Log OOM condition */
#define LOG_ERR_OOM(message) \
do { \
LOG_ERROR(("Out of memory\n")); \
LOG_ERROR(message); \
} while (0);
/* DIE on OOM error */
#define DIE_OOM(message) \
do { \
LOG_ERR_OOM(message); \
DIEDIE(); \
} while (0)
/*** WARNING: feed following macros only with side-effects-free expressions
* ***/
/* Get a value within target interval */
#define cutoff(tgt, min, max) \
(tgt) < (min) ? (min) : ((tgt) > (max) ? max : tgt)
/* Update value to fit into target interval */
#define val_range(tgt, min, max) (tgt) = cutoff(tgt, min, max)
#endif
================================================
FILE: src/debug.c
================================================
/* -------------------------------
* vim:tabstop=4:shiftwidth=4
* debug.c
* Mon, 01 May 2006 01:44:42 +0700
* -------------------------------
* Debugging code/utilities
* -------------------------------*/
#include "debug.h"
#include "xutils.h"
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#ifdef DBG_PRINT_PID
#include <sys/types.h>
#include <unistd.h>
#endif
int debug_output_disabled = 0;
/* Disables all output from debugging macros */
void debug_disable_output()
{
debug_output_disabled = 1;
}
/* Print the message to STDERR (varadic macros is not used in the sake of
* portability) */
void print_message_to_stderr(const char *fmt, ...)
{
static char msg[PATH_MAX];
va_list va;
va_start(va, fmt);
vsnprintf(msg, PATH_MAX, fmt, va);
va_end(va);
fprintf(stderr, "%s", msg);
}
int trace_mode = False;
void print_trace_header(
const char *funcname, const char *fname, const int line)
{
static pid_t pid = 0;
#ifdef TRACE_TIMESTAMP
{
static char timestr[PATH_MAX + 1];
time_t curtime = time(NULL);
struct tm *loctime = localtime(&curtime);
strftime(timestr, PATH_MAX, "%b %e %H:%M:%S", loctime);
fprintf(stderr, "%s ", timestr);
}
#endif
#ifdef TRACE_WM_NAME
fprintf(stderr, "%s ", settings.wnd_name);
#endif
#ifdef TRACE_PID
if (!pid) pid = getpid();
fprintf(stderr, "(%d) ", pid);
#endif
#ifdef TRACE_DPY
if (tray_data.dpy != NULL)
fprintf(stderr, "(%s) ", DisplayString(tray_data.dpy));
else
fprintf(stderr, "(dpy) ");
#endif
#ifdef TRACE_VERBOSE_LOCATION
fprintf(stderr, "(%s:%4d) ", fname, line);
#endif
fprintf(stderr, "%s(): ", funcname);
(void) pid; /* unused */
(void) fname; /* unused */
(void) line; /* unused */
}
/* Print the summary of icon data */
int print_icon_data(struct TrayIcon *ti)
{
#ifdef DEBUG
XWindowAttributes xwa;
#endif
LOG_INFO(("wid = 0x%lx\n", ti->wid));
LOG_TRACE((" self = %p\n", (void *) ti));
LOG_TRACE((" prev = %p\n", (void *) ti->prev));
LOG_TRACE((" next = %p\n", (void *) ti->next));
LOG_TRACE((" invalid = %d\n", ti->is_invalid));
LOG_TRACE((" layed_out = %d\n", ti->is_layed_out));
LOG_TRACE((" update_pos = %d\n", ti->is_updated));
LOG_INFO((" name = %s\n",
x11_get_window_name(tray_data.dpy, ti->wid, "<unknown>")));
LOG_INFO((" visible = %d\n", ti->is_visible));
LOG_INFO((" position (grid) = %dx%d+%d+%d\n", ti->l.grd_rect.w,
ti->l.grd_rect.h, ti->l.grd_rect.x, ti->l.grd_rect.y));
LOG_INFO((" position (pixels) = %dx%d+%d+%d\n", ti->l.icn_rect.w,
ti->l.icn_rect.h, ti->l.icn_rect.x, ti->l.icn_rect.y));
LOG_INFO((" wnd_sz = %dx%d\n", ti->l.wnd_sz.x, ti->l.wnd_sz.y));
LOG_TRACE((" cmode = %d\n", ti->cmode));
LOG_INFO((" xembed = %d\n", ti->is_xembed_supported));
if (ti->is_xembed_supported) {
LOG_TRACE(
(" xembed_accepts_focus = %d\n", ti->is_xembed_accepts_focus));
LOG_TRACE(
(" xembed_last_timestamp = %ld\n", ti->xembed_last_timestamp));
LOG_TRACE((" xembed_last_msgid = %ld\n", ti->xembed_last_msgid));
}
LOG_INFO((" embedded = %d\n", ti->is_embedded));
#ifdef DEBUG
if (ti->is_embedded) {
LOG_TRACE((" mid-parent = 0x%lx\n", ti->mid_parent));
if (!XGetWindowAttributes(tray_data.dpy, ti->mid_parent, &xwa)) {
LOG_TRACE((
" ERR: XGetWindowAttributes() on mid-parent window faied\n"));
} else {
LOG_TRACE((" mid-parent wid = 0x%lx\n", ti->mid_parent));
LOG_TRACE((" mid-parent state = %s\n",
xwa.map_state == IsUnmapped
? "Unmapped"
: (xwa.map_state == IsUnviewable ? "Unviewable"
: "Viewable")));
LOG_TRACE((" mid-parent geometry = %dx%d+%d+%d\n", xwa.width,
xwa.height, xwa.x, xwa.y));
}
}
if (settings.log_level > LOG_LEVEL_INFO) {
Window real_parent, root, *children = NULL;
unsigned int nchildren = 0;
int rc;
rc = XQueryTree(tray_data.dpy, ti->wid, &root, &real_parent, &children,
&nchildren);
if (rc && children != NULL && nchildren > 0) XFree(children);
if (!rc) {
LOG_TRACE((" ERR: XQueryTree() failed\n"));
} else {
LOG_TRACE(
(" parent wid from XQueryTree() = 0x%lx\n", real_parent));
}
}
if (!XGetWindowAttributes(tray_data.dpy, ti->wid, &xwa)) {
LOG_TRACE((" ERR: XGetWindowAttributes() on icon window failed\n"));
} else {
LOG_TRACE((" geometry from XGetWindowAttributes() = %dx%d+%d+%d\n",
xwa.width, xwa.height, xwa.x, xwa.y));
LOG_TRACE((" mapstate from XGetWindowAttributes() = %s\n",
xwa.map_state == IsUnmapped
? "Unmapped"
: (xwa.map_state == IsUnviewable ? "Unviewable"
: "Viewable")));
}
#endif
/* This resets x11 error state (which might have left from X11 calls above)
*/
x11_ok();
return NO_MATCH;
}
void dump_icon_list()
{
icon_list_forall(&print_icon_data);
}
================================================
FILE: src/debug.h
================================================
/* -------------------------------
* vim:tabstop=4:shiftwidth=4
* debug.h
* Tue, 24 Aug 2004 12:05:38 +0700
* -------------------------------
* Debugging code/utilities
* -------------------------------*/
#ifndef _DEBUG_H_
#define _DEBUG_H_
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
#include "settings.h"
#include "tray.h"
#include <string.h>
#include <time.h>
#define LOG_LEVEL_ERR 0
#define LOG_LEVEL_INFO 1
#define LOG_LEVEL_TRACE 2
extern int debug_output_disabled;
/* Disables all output from debugging macros */
void debug_disable_output();
/* Print the message to STDERR (in the sake of portability, we do not use
* varadic macros) */
void print_message_to_stderr(const char *fmt, ...)
#ifdef __GNUC__
__attribute__((__format__(__printf__, 1, 2)))
#endif
;
/* The following defines control what is printed in each logged line */
#ifdef DEBUG
/* Print window name */
#define TRACE_WM_NAME
/* Print pid */
#undef TRACE_PID
/* Print name of display */
#undef TRACE_DPY
/* Print timestamp */
#define TRACE_TIMESTAMP
/* Print name of a function, file and line which outputs the message */
/* Othewise, only function name is printed */
#undef TRACE_VERBOSE_LOCATION
#endif
/* Print the debug header as specified by defines below */
void print_trace_header(
const char *funcname, const char *fname, const int line);
#define PRINT_TRACE_HEADER() \
do { \
print_trace_header(__FUNC__, __FILE__, __LINE__); \
} while (0)
/* Print the debug message of specified level */
#define LOG_TRACE(message) \
do { \
if (!debug_output_disabled \
&& settings.log_level >= LOG_LEVEL_TRACE) { \
PRINT_TRACE_HEADER(); \
print_message_to_stderr message; \
}; \
} while (0)
#define LOG_ERROR(message) \
do { \
if (!debug_output_disabled) { \
if (settings.log_level >= LOG_LEVEL_TRACE) PRINT_TRACE_HEADER(); \
if (settings.log_level >= LOG_LEVEL_ERR) \
print_message_to_stderr message; \
} \
} while (0)
#define LOG_INFO(message) \
do { \
if (!debug_output_disabled) { \
if (settings.log_level >= LOG_LEVEL_TRACE) PRINT_TRACE_HEADER(); \
if (settings.log_level >= LOG_LEVEL_INFO) \
print_message_to_stderr message; \
} \
} while (0)
/* Print the summary of icon data */
int print_icon_data(struct TrayIcon *ti);
/* Print icon list contents */
void dump_icon_list();
#endif
================================================
FILE: src/embed.c
================================================
/* -------------------------------
* vim:tabstop=4:shiftwidth=4
* embed.c
* Fri, 03 Sep 2004 20:38:55 +0700
* -------------------------------
* embedding cycle implementation
* -------------------------------*/
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <assert.h>
#include <unistd.h>
#ifdef DELAY_EMBEDDING_CONFIRMATION
#include <pthread.h>
#endif
#include "embed.h"
#include "common.h"
#include "debug.h"
#include "icons.h"
#include "settings.h"
#include "tray.h"
#include "xutils.h"
#define CALC_INNER_POS(x_, y_, ti_) \
do { \
x_ = (ti_->l.icn_rect.w - ti_->l.wnd_sz.x) / 2; \
y_ = (ti_->l.icn_rect.h - ti_->l.wnd_sz.y) / 2; \
} while (0);
#ifdef DELAY_EMBEDDING_CONFIRMATION
void *send_delayed_confirmation(void *dummy)
{
struct TrayIcon *ti = (struct TrayIcon *)dummy;
Display *dpy;
if ((dpy = XOpenDisplay(settings.display_str)) != NULL) {
LOG_TRACE(
("will now sleep for %d seconds\n", settings.confirmation_delay));
sleep(settings.confirmation_delay);
LOG_TRACE(("sending embedding confirmation\n"));
x11_send_client_msg32(dpy, tray_data.tray, tray_data.tray,
tray_data.xa_tray_opcode, 0, STALONE_TRAY_DOCK_CONFIRMED, ti->wid,
0, 0);
XSync(dpy, False);
XClose(dpy);
} else {
DIE_IE(("failed to initialize display\n"));
}
pthread_exit(NULL);
}
#endif
int embedder_embed(struct TrayIcon *ti)
{
int x, y, rc;
XSetWindowAttributes xswa;
/* If the icon is being embedded as hidden,
* we just start listening for property changes
* to track _XEMBED mapped state */
if (!ti->is_visible) {
XSelectInput(tray_data.dpy, ti->wid, PropertyChangeMask);
return x11_ok();
}
/* 0. Start listening for events on icon window */
XSelectInput(
tray_data.dpy, ti->wid, StructureNotifyMask | PropertyChangeMask);
if (!x11_ok()) RETURN_STATUS(FAILURE);
/* 1. Calculate position of mid-parent window */
CALC_INNER_POS(x, y, ti);
LOG_TRACE(
("position of icon 0x%lx inside the tray: (%d, %d)\n", ti->wid, x, y));
/* 2. Create mid-parent window */
ti->mid_parent = XCreateSimpleWindow(tray_data.dpy, tray_data.tray,
ti->l.icn_rect.x + x, ti->l.icn_rect.y + y, ti->l.wnd_sz.x,
ti->l.wnd_sz.y, 0, 0, 0);
/* 2.5. Setup mid-parent window properties */
xswa.win_gravity = settings.bit_gravity;
XChangeWindowAttributes(
tray_data.dpy, ti->mid_parent, CWWinGravity, &xswa);
#ifndef DEBUG_HIGHLIGHT_MIDPARENT
XSetWindowBackgroundPixmap(tray_data.dpy, ti->mid_parent, ParentRelative);
#else
XSetWindowBackgroundPixmap(tray_data.dpy, ti->mid_parent, 0);
#endif
if (!x11_ok() || ti->mid_parent == None) RETURN_STATUS(FAILURE);
LOG_TRACE(("created mid-parent window 0x%lx\n", ti->mid_parent));
/* 3. Embed window into mid-parent */
switch (ti->cmode) {
case CM_KDE:
case CM_FDO:
XReparentWindow(tray_data.dpy, ti->wid, ti->mid_parent, 0, 0);
XMapRaised(tray_data.dpy, ti->wid);
break;
default: break;
}
/* 4. Show mid-parent */
XMapWindow(tray_data.dpy, ti->mid_parent);
/* mid-parent must be lowered so that it does not osbcure
* scollbar windows */
XLowerWindow(tray_data.dpy, ti->mid_parent);
if (!x11_ok()) RETURN_STATUS(FAILURE);
#ifndef DELAY_EMBEDDING_CONFIRMATION
/* 5. Send message confirming embedding */
rc = x11_send_client_msg32(tray_data.dpy, tray_data.tray, tray_data.tray,
tray_data.xa_tray_opcode, 0, STALONE_TRAY_DOCK_CONFIRMED, ti->wid, 0,
0);
RETURN_STATUS(rc != 0);
#else
/* This is here for debugging purposes */
{
pthread_t delayed_thread;
pthread_create(
&delayed_thread, NULL, send_delayed_confirmation, (void *)ti);
LOG_TRACE(("sent delayed confirmation\n"));
RETURN_STATUS(SUCCESS);
}
#endif
}
int embedder_unembed(struct TrayIcon *ti)
{
if (!ti->is_embedded) return SUCCESS;
switch (ti->cmode) {
case CM_KDE:
case CM_FDO:
/* Unembed icon as described in system tray protocol */
if (ti->is_embedded) {
XSelectInput(tray_data.dpy, ti->wid, NoEventMask);
XUnmapWindow(tray_data.dpy, ti->wid);
XReparentWindow(tray_data.dpy, ti->wid,
DefaultRootWindow(tray_data.dpy), ti->l.icn_rect.x,
ti->l.icn_rect.y);
XMapRaised(tray_data.dpy, ti->wid);
if (!x11_ok())
LOG_ERROR(("failed to move icon 0x%lx out of the tray\n", ti->wid));
}
/* Destroy mid-parent */
if (ti->mid_parent != None) {
XDestroyWindow(tray_data.dpy, ti->mid_parent);
if (!x11_ok())
LOG_ERROR(("failed to destroy icon mid-parent 0x%lx\n",
ti->mid_parent));
}
break;
default:
LOG_ERR_IE(("Error: the compatibility mode %d is not supported "
"(should not happen)\n",
ti->cmode));
return FAILURE;
}
LOG_TRACE(("done unembedding 0x%lx\n", ti->wid));
RETURN_STATUS(x11_ok()
== 0); /* This resets error status for the generations to come (XXX) */
}
int embedder_hide(struct TrayIcon *ti)
{
XUnmapWindow(tray_data.dpy, ti->mid_parent);
/* We do not wany any StructureNotify events for icon window anymore */
XSelectInput(tray_data.dpy, ti->wid, PropertyChangeMask);
if (!x11_ok()) {
ti->is_invalid = True;
return FAILURE;
} else {
ti->is_size_set = False;
ti->num_size_resets = 0;
ti->is_visible = False;
return SUCCESS;
}
}
int embedder_show(struct TrayIcon *ti)
{
unsigned int x, y;
/* If the window has never been embedded,
* perform real embedding */
if (ti->mid_parent == None) {
ti->is_visible = True;
return embedder_embed(ti);
}
/* 0. calculate new position for mid-parent */
CALC_INNER_POS(x, y, ti);
/* 1. move mid-parent to new location */
XMoveResizeWindow(tray_data.dpy, ti->mid_parent, ti->l.icn_rect.x + x,
ti->l.icn_rect.y + y, ti->l.wnd_sz.x, ti->l.wnd_sz.y);
/* 2. adjust icon position inside mid-parent */
XMoveWindow(tray_data.dpy, ti->wid, 0, 0);
/* 3. map icon ? */
XMapRaised(tray_data.dpy, ti->wid);
/* 4. map mid-parent */
XMapWindow(tray_data.dpy, ti->mid_parent);
XSelectInput(
tray_data.dpy, ti->wid, StructureNotifyMask | PropertyChangeMask);
if (!x11_ok()) {
ti->is_invalid = True;
return FAILURE;
} else {
ti->is_visible = True;
return SUCCESS;
}
}
static int update_forced = False;
static int embedder_update_window_position(struct TrayIcon *ti)
{
int x, y;
/* Ignore hidden icons */
if (!ti->is_visible) return NO_MATCH;
/* Update only those icons that do want it (everyone if update was forced)
*/
if (!update_forced && !ti->is_updated && !ti->is_resized
&& ti->is_embedded)
return NO_MATCH;
LOG_TRACE(("Updating position of icon 0x%lx\n", ti->wid));
/* Recalculate icon position */
CALC_INNER_POS(x, y, ti);
/* Reset the flags */
ti->is_resized = False;
ti->is_updated = False;
/* Move mid-parent window */
XMoveResizeWindow(tray_data.dpy, ti->mid_parent, ti->l.icn_rect.x + x,
ti->l.icn_rect.y + y, ti->l.wnd_sz.x, ti->l.wnd_sz.y);
/* Sanitize icon position inside mid-parent */
XMoveWindow(tray_data.dpy, ti->wid, 0, 0);
/* Refresh the icon */
embedder_refresh(ti);
if (!x11_ok()) {
LOG_TRACE(("failed to update position of icon 0x%lx\n", ti->wid));
ti->is_invalid = True;
}
return NO_MATCH;
}
int embedder_update_positions(int forced)
{
/* I wish C had closures =( */
update_forced = forced;
icon_list_forall(&embedder_update_window_position);
return SUCCESS;
}
int embedder_refresh(struct TrayIcon *ti)
{
if (!ti->is_visible) return NO_MATCH;
XClearWindow(tray_data.dpy, ti->mid_parent);
x11_refresh_window(
tray_data.dpy, ti->wid, ti->l.wnd_sz.x, ti->l.wnd_sz.y, True);
/* Check if the icon has survived all these manipulations */
if (!x11_ok()) {
LOG_TRACE(("could not refresh 0x%lx\n", ti->wid));
ti->is_invalid = True;
}
return NO_MATCH;
}
/* This function defines initial icon size or
* is used to reset size of the icon window */
int embedder_reset_size(struct TrayIcon *ti)
{
struct Point icon_sz = {0, 0};
int rc = FAILURE;
/* Do not reset size for non-KDE icons with size set if icon_resizes
* are handled */
if (ti->is_size_set && ti->cmode != CM_KDE
&& !(settings.kludge_flags & KLUDGE_FORCE_ICONS_SIZE))
return SUCCESS;
/* Increase counter of size resets for given icon. If this number
* exeeds the threshold, do nothing. This should work around the icons
* that react badly to size changes */
if (ti->is_size_set) ti->num_size_resets++;
if (ti->num_size_resets > ICON_SIZE_RESETS_THRESHOLD) return SUCCESS;
if (ti->cmode == CM_KDE) {
icon_sz.x = settings.icon_size < KDE_ICON_SIZE ? settings.icon_size
: KDE_ICON_SIZE;
icon_sz.y = icon_sz.x;
} else {
/* If icon hints are to be respected, retrive the data */
if (settings.kludge_flags & KLUDGE_USE_ICONS_HINTS)
rc = x11_get_window_min_size(
tray_data.dpy, ti->wid, &icon_sz.x, &icon_sz.y);
/* If this has failed, or icon hinst are not respected, or minimal size
* hints are too small, fall back to default values */
if (!rc || !(settings.kludge_flags & KLUDGE_USE_ICONS_HINTS)
|| (settings.kludge_flags & KLUDGE_FORCE_ICONS_SIZE)
|| (icon_sz.x < settings.icon_size
&& icon_sz.y < settings.icon_size)) {
icon_sz.x = settings.icon_size;
icon_sz.y = settings.icon_size;
}
}
LOG_TRACE(("proposed icon size: %dx%d\n", icon_sz.x, icon_sz.y));
if (x11_set_window_size(tray_data.dpy, ti->wid, icon_sz.x, icon_sz.y)) {
ti->l.wnd_sz = icon_sz;
ti->is_size_set = True;
return SUCCESS;
} else {
ti->is_invalid = True;
return FAILURE;
}
}
================================================
FILE: src/embed.h
================================================
/* -------------------------------
* vim:tabstop=4:shiftwidth=4
* embed.h
* Fri, 03 Sep 2004 20:38:55 +0700
* -------------------------------
* embedding cycle implementation
* -------------------------------*/
#ifndef _EMBED_H_
#include "icons.h"
/* Constants for compatibility modes */
/* KDE */
#define CM_KDE 1
/* Generic, freedesktop.org */
#define CM_FDO 2
/* Embed an icon */
int embedder_embed(struct TrayIcon *ti);
/* Unembed an icon */
int embedder_unembed(struct TrayIcon *ti);
/* If (forced)
* recalculate and update positions of all icons;
* else
* recalculate and update positions of all icons that have requested an
* update; */
int embedder_update_positions(int force);
/* Show the icon */
int embedder_show(struct TrayIcon *ti);
/* Hide the icon */
int embedder_hide(struct TrayIcon *ti);
/* Refresh icon and its parent */
int embedder_refresh(struct TrayIcon *ti);
/* (Re)set icon size according to the policy */
int embedder_reset_size(struct TrayIcon *ti);
#endif
================================================
FILE: src/icons.c
================================================
/* -------------------------------
* vim:tabstop=4:shiftwidth=4
* icons.c
* Tue, 24 Aug 2004 12:05:38 +0700
* -------------------------------
* Manipulations with reparented
* windows --- tray icons
* -------------------------------*/
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "icons.h"
#include "common.h"
#include "debug.h"
#include "layout.h"
#include "list.h"
#include "tray.h"
#ifdef DEBUG
#include "xutils.h"
#endif
#include "assert.h"
struct TrayIcon *icons_head = NULL;
struct TrayIcon *icon_list_new(Window wid, int cmode)
{
struct TrayIcon *new_icon;
/* Do not allocate second structure for the same window */
if (icon_list_find(wid) != NULL) return NULL;
if ((new_icon = malloc(sizeof(struct TrayIcon))) == NULL) {
LOG_ERR_OOM(("Could not allocate memory for new icon\n"));
return NULL;
}
new_icon->wid = wid;
new_icon->l.wnd_sz.x = 0;
new_icon->l.wnd_sz.y = 0;
new_icon->mid_parent = None;
new_icon->cmode = cmode;
new_icon->is_embedded = False;
new_icon->is_layed_out = False;
new_icon->is_updated = False;
new_icon->is_resized = True;
new_icon->is_visible = False;
new_icon->is_invalid = False;
new_icon->is_xembed_supported = False;
new_icon->is_size_set = False;
new_icon->num_size_resets = 0;
LIST_ADD_ITEM(icons_head, new_icon);
return new_icon;
}
int icon_list_free(struct TrayIcon *ti)
{
if (ti != NULL) {
LIST_DEL_ITEM(icons_head, ti);
free(ti);
}
return SUCCESS;
}
struct TrayIcon *icon_list_next(struct TrayIcon *ti)
{
return (ti != NULL && ti->next != NULL) ? ti->next : icons_head;
}
struct TrayIcon *icon_list_prev(struct TrayIcon *ti)
{
struct TrayIcon *tmp;
if (ti != NULL && ti->prev != NULL)
return ti->prev;
else {
tmp = icons_head;
for (; tmp->next != NULL; tmp = tmp->next)
;
return tmp;
}
}
static struct TrayIcon *backup_head = NULL;
int icon_list_backup()
{
struct TrayIcon *tmp, *cur, *cur2;
/* Refuse to perform second backup in a row */
if (backup_head != NULL) {
DIE_IE(("Only one backup of icon list at a time is supported\n"));
}
/* For each icon in the list we allocate new temporary structure and add it
* to the end of temporary list backup_head */
for (cur = icons_head, cur2 = NULL; cur != NULL;
cur = cur->next, cur2 = tmp) {
tmp = (struct TrayIcon *)malloc(sizeof(struct TrayIcon));
if (tmp == NULL) {
LOG_ERR_OOM(("Could not allocate backup list"));
icon_list_backup_purge();
return FAILURE;
}
memcpy(tmp, cur, sizeof(struct TrayIcon));
LIST_INSERT_AFTER(backup_head, cur2, tmp);
cur2 = tmp;
}
return SUCCESS;
}
int icon_list_restore()
{
struct TrayIcon *cur_b, *cur_i, *prev_sv, *next_sv;
LOG_TRACE(("restoring the icon list from the backup\n"));
/* Restore the list by copying raw data from
* backup list. This assumes that sequences have the
* same length. */
for (cur_i = icons_head, cur_b = backup_head;
cur_i != NULL && cur_b != NULL;
cur_i = cur_i->next, cur_b = cur_b->next) {
prev_sv = cur_i->prev;
next_sv = cur_i->next;
memcpy(cur_i, cur_b, sizeof(struct TrayIcon));
cur_i->prev = prev_sv;
cur_i->next = next_sv;
}
/* Some consistency checking: ensures that
* both lists had the same length */
assert(cur_i == NULL && cur_b == NULL);
/* Clean backup list */
LIST_CLEAN(backup_head, cur_b);
backup_head = NULL;
return SUCCESS;
}
int icon_list_backup_purge()
{
struct TrayIcon *tmp;
LOG_TRACE(("purging the backed up icon list\n"));
/* Clean backup list */
LIST_CLEAN(backup_head, tmp);
backup_head = NULL;
return SUCCESS;
}
struct TrayIcon *icon_list_find(Window wid)
{
/* Traverse the whole list */
struct TrayIcon *tmp;
for (tmp = icons_head; tmp != NULL; tmp = tmp->next)
if (tmp->wid == wid) return tmp;
return NULL;
}
struct TrayIcon *icon_list_find_ex(Window wid)
{
/* Traverse the whole list */
struct TrayIcon *tmp;
for (tmp = icons_head; tmp != NULL; tmp = tmp->next)
if (tmp->wid == wid || tmp->mid_parent == wid) return tmp;
return NULL;
}
int icon_list_clean()
{
struct TrayIcon *tmp;
LIST_CLEAN(icons_head, tmp);
return SUCCESS;
}
int icon_list_clean_callback(IconCallbackFunc cbk)
{
struct TrayIcon *tmp;
LIST_CLEAN_CBK(icons_head, tmp, cbk);
return SUCCESS;
}
/* TODO: is it necessary always to sort the full list? */
void icon_list_sort(IconCmpFunc cmp)
{
struct TrayIcon *new_head = NULL, *cur, *tmp;
while (icons_head != NULL) {
/* Find the least element and move it to temporary list */
cur = icons_head;
for (tmp = icons_head; tmp != NULL; tmp = tmp->next)
if (cmp(tmp, cur) > 0) cur = tmp;
LIST_DEL_ITEM(icons_head, cur);
LIST_ADD_ITEM(new_head, cur);
}
icons_head = new_head;
}
struct TrayIcon *icon_list_forall(IconCallbackFunc cbk)
{
return icon_list_forall_from(icons_head, cbk);
}
struct TrayIcon *icon_list_forall_from(
struct TrayIcon *tgt, IconCallbackFunc cbk)
{
/* Traverse the list starting from tgt*/
struct TrayIcon *tmp;
for (tmp = (tgt != NULL ? tgt : icons_head); tmp != NULL; tmp = tmp->next)
if (cbk(tmp) == MATCH) { return tmp; }
return NULL;
}
================================================
FILE: src/icons.h
================================================
/* -------------------------------
* vim:tabstop=4:shiftwidth=4
* icons.h
* Tue, 24 Aug 2004 12:05:38 +0700
* -------------------------------
* Manipulations with the list of
* tray icons
* -------------------------------*/
#ifndef _ICONS_H_
#define _ICONS_H_
#include <X11/X.h>
#include <X11/Xmd.h>
/* Simple point & rect data structures */
struct Point {
int x, y;
};
struct Rect {
int x, y, w, h;
};
/* Tray icon layout data structure */
struct Layout {
struct Rect grd_rect; /* The rect in the grid */
struct Rect icn_rect; /* Real position inside the tray */
struct Point wnd_sz; /* Size of the window of the icon */
};
/* Tray icon data structure */
struct TrayIcon {
struct TrayIcon *next;
struct TrayIcon *prev;
Window wid; /* Window ID */
Window mid_parent; /* Mid-parent ID */
int cmode; /* Compatibility mode: CM_FDO/CM_KDE (see embed.h) */
int is_embedded; /* Flag: is the icon succesfully embedded ? */
int is_invalid; /* Flag: is the icon invalid ? */
int is_visible; /* Flag: is the icon hidden ? */
int is_resized; /* Flag: the icon has recently resized itself */
int is_layed_out; /* Flag: the icon is succesfully layed out */
int is_updated; /* Flag: the position of the icon needs to be updated */
int is_xembed_supported; /* Flag: does the icon support xembed */
unsigned long xembed_data[2]; /* XEMBED data */
int num_size_resets; /* How many times size was reset */
int is_size_set; /* Flag: has the size for the icon been set */
int is_xembed_accepts_focus; /* Flag: does the icon want focus */
long xembed_last_timestamp; /* The timestamp of last processed xembed
message */
long xembed_last_msgid; /* ID of the last processed xembed message */
struct Layout l; /* Layout info */
};
/* Typedef for comparison function */
typedef int (*IconCmpFunc)(struct TrayIcon *, struct TrayIcon *);
/* Typedef for callback function */
typedef int (*IconCallbackFunc)(struct TrayIcon *);
/* Add the new icon to the list */
struct TrayIcon *icon_list_new(Window w, int cmode);
/* Delete the icon from the list */
int icon_list_free(struct TrayIcon *ti);
/* Return the next/previous icon in the list after the icon specified by ti */
struct TrayIcon *icon_list_next(struct TrayIcon *ti);
struct TrayIcon *icon_list_prev(struct TrayIcon *ti);
/*************************************************
* BIG FAT WARNING: backup/restore routines will
* memleak/fail if the number of icons in the
* list has changed between backup/restore calls.
* (in return, it does not invalidate pointers :P)
*************************************************/
/* Back up the list */
int icon_list_backup();
/* Restore the list from the backup */
int icon_list_restore();
/* Free the back-up list */
int icon_list_backup_purge();
/* Apply a callback specified by cbk to all icons.
* List is traversed in a natural order. Function stops
* and returns current_icon if cbk(current_icon) == MATCH */
struct TrayIcon *icon_list_forall(IconCallbackFunc cbk);
/* For readability sake, we sometimes use this function */
#define icon_list_advanced_find icon_list_forall
/* Same as above, but start traversal from the icon specified by tgt */
struct TrayIcon *icon_list_forall_from(
struct TrayIcon *tgt, IconCallbackFunc cbk);
/* Clear the whole list */
int icon_list_clean();
/* Clear the whole list, calling cbk for each icon */
int icon_list_clean_callback(IconCallbackFunc cbk);
/* Sort the list using comparison function specified by cmp.
* Memo for writing comparison functions:
* if a < b => cmp(a,b) < 0
* if a = b => cmp(a,b) = 0
* if a > b => cmp(a,b) > 0 */
void icon_list_sort(IconCmpFunc cmp);
/* Find the icon with wid == w */
struct TrayIcon *icon_list_find(Window w);
/* Find the icon with wid == w or parent wid == w */
struct TrayIcon *icon_list_find_ex(Window w);
#endif
================================================
FILE: src/image.c
================================================
/* -------------------------------
* vim:tabstop=4:shiftwidth=4
* image.c
* Fri, 22 Jun 2007 23:32:27 +0700
* -------------------------------
* Simple XImage manipulation
* interface
* -------------------------------*/
#include "image.h"
#include "debug.h"
/***** Forward declarations *****/
/* Depth-specialized versions of tinting routine */
int image_tint_32(CARD8 *data, size_t len, CARD32 pixel, CARD8 alpha);
int image_tint_24(CARD8 *data, size_t len, CARD32 pixel, CARD8 alpha);
int image_tint_16(CARD16 *data, size_t len, CARD32 pixel, CARD8 alpha);
int image_tint_15(CARD16 *data, size_t len, CARD32 pixel, CARD8 alpha);
/* Depth-specialized versions of compose routine */
int image_compose_32(CARD8 *data, CARD8 *bg, CARD8 *mask, size_t len);
int image_compose_24(CARD8 *data, CARD8 *bg, CARD8 *mask, size_t len);
int image_compose_16(CARD16 *data, CARD16 *bg, CARD8 *mask, size_t len);
int image_compose_15(CARD16 *data, CARD16 *bg, CARD8 *mask, size_t len);
/***** Interface level *****/
int image_tint(XImage *image, XColor *color, CARD8 alpha)
{
switch (image->bits_per_pixel) {
case 32:
return image_tint_32((CARD8 *)image->data,
image->width * image->height, color->pixel, alpha);
case 24:
return image_tint_24((CARD8 *)image->data,
image->width * image->height, color->pixel, alpha);
case 16:
return image_tint_16((CARD16 *)image->data,
image->width * image->height, color->pixel, alpha);
case 15:
return image_tint_15((CARD16 *)image->data,
image->width * image->height, color->pixel, alpha);
default:
DIE_IE(("image_tint() called with unsupported depth %d\n",
image->bits_per_pixel));
return FAILURE;
}
}
int image_compose(XImage *image, XImage *bg, CARD8 *mask)
{
switch (image->bits_per_pixel) {
case 32:
return image_compose_32((CARD8 *)image->data, (CARD8 *)bg->data, mask,
(image->width * image->height));
case 24:
return image_compose_24((CARD8 *)image->data, (CARD8 *)bg->data, mask,
(image->width * image->height));
case 16:
return image_compose_16((CARD16 *)image->data, (CARD16 *)bg->data,
mask, (image->width * image->height));
case 15:
return image_compose_15((CARD16 *)image->data, (CARD16 *)bg->data,
mask, (image->width * image->height));
default:
DIE_IE(("image_compose() called with unsupported depth %d\n",
image->bits_per_pixel));
return FAILURE;
}
}
CARD8 *image_create_alpha_mask(int ord, int w, int h)
{
unsigned char *m, *ll, *ul;
int x, y, bord;
bord = (1 << ord) - 1;
m = malloc(w * h);
if (m == NULL) return NULL;
memset(m, 255, w * h);
/* Shade top and bootom of the rectangle */
ul = m; /* top */
ll = m + (w * (h - 1)); /* bottom */
for (y = 0; y < bord; y++) {
for (x = 0; x < w; x++) {
ul[x] = ((unsigned int)ul[x] * (y + 1)) >> ord;
ll[x] = ((unsigned int)ll[x] * (y + 1)) >> ord;
}
ul += w;
ll -= w;
}
/* Shade left and right of the rectangle */
for (x = 0; x < bord; x++) {
ul = m + x; /* left side */
ll = m + w - x - 1; /* right side */
for (y = 0; y < h; y++) {
*ul = ((unsigned int)*ul * (x + 1)) >> ord;
*ll = ((unsigned int)*ll * (x + 1)) >> ord;
ll += w;
ul += w;
}
}
return m;
}
/***** Implementation level *****/
#define sr16(p, a) (((CARD32)((p)&0xf800) * (a)))
#define sg16(p, a) (((CARD32)((p)&0x7e0) * (a)))
#define sb16(p, a) (((CARD32)((p)&0x1f) * (a)))
#define sr15(p, a) (((CARD32)((p)&0x7c00) * (a)))
#define sg15(p, a) (((CARD32)((p)&0x3e0) * (a)))
#define sb15(p, a) (((CARD32)((p)&0x1f) * (a)))
int image_tint_32(CARD8 *data, size_t len, CARD32 pixel, CARD8 alpha)
{
CARD8 *p, tr, tg, tb, ralpha;
ralpha = 255 - alpha;
#ifndef BIGENDIAN
tr = ((pixel & 0x00ff0000) * alpha) >> 24;
tg = ((pixel & 0x0000ff00) * alpha) >> 16;
tb = ((pixel & 0x000000ff) * alpha) >> 8;
#else
tr = ((pixel & 0x000000ff) * alpha) >> 8;
tg = ((pixel & 0x0000ff00) * alpha) >> 16;
tb = ((pixel & 0x00ff0000) * alpha) >> 24;
#endif
/* traverse data by 4 bytes starting from the end */
for (p = data + (len - 1) * 4; p >= data; p -= 4) {
#ifndef BIGENDIAN
p[0] = (((CARD16)p[0] * ralpha) >> 8) + tb;
p[1] = (((CARD16)p[1] * ralpha) >> 8) + tg;
p[2] = (((CARD16)p[2] * ralpha) >> 8) + tr;
#else
p[0] = (((CARD16)p[0] * ralpha) >> 8) + tr;
p[1] = (((CARD16)p[1] * ralpha) >> 8) + tg;
p[2] = (((CARD16)p[2] * ralpha) >> 8) + tb;
#endif
}
return SUCCESS;
}
int image_tint_24(CARD8 *data, size_t len, CARD32 pixel, CARD8 alpha)
{
CARD8 *p, tr, tg, tb, ralpha;
ralpha = 255 - alpha;
#ifndef BIGENDIAN
tr = ((pixel & 0x00ff0000) * alpha) >> 24;
tg = ((pixel & 0x0000ff00) * alpha) >> 16;
tb = ((pixel & 0x000000ff) * alpha) >> 8;
#else
tr = ((pixel & 0x000000ff) * alpha) >> 8;
tg = ((pixel & 0x0000ff00) * alpha) >> 16;
tb = ((pixel & 0x00ff0000) * alpha) >> 24;
#endif
/* traverse data by 3 bytes starting from the end */
for (p = data + (len - 1) * 3; p >= data; p -= 3) {
#ifndef BIGENDIAN
p[0] = (((CARD16)p[0] * ralpha) >> 8) + tb;
p[1] = (((CARD16)p[1] * ralpha) >> 8) + tg;
p[2] = (((CARD16)p[2] * ralpha) >> 8) + tr;
#else
p[0] = (((CARD16)p[0] * ralpha) >> 8) + tr;
p[1] = (((CARD16)p[1] * ralpha) >> 8) + tg;
p[2] = (((CARD16)p[2] * ralpha) >> 8) + tb;
#endif
}
return SUCCESS;
}
int image_tint_16(CARD16 *data, size_t len, CARD32 pixel, CARD8 alpha)
{
CARD32 tr, tg, tb;
CARD32 r, g, b;
CARD16 *p;
CARD8 ralpha;
ralpha = 255 - alpha;
tr = sr16(pixel, alpha);
tg = sg16(pixel, alpha);
tb = sb16(pixel, alpha);
/* traverse data by 2 bytes starting from the end */
for (p = data + len - 1; p >= data; p--) {
r = sr16(*p, ralpha) + tr;
g = sg16(*p, ralpha) + tg;
b = sb16(*p, ralpha) + tb;
*p = ((r >> 8) & 0xf800) | ((g >> 8) & 0x7e0) | ((b >> 8) & 0x1f);
}
return SUCCESS;
}
int image_tint_15(CARD16 *data, size_t len, CARD32 pixel, CARD8 alpha)
{
CARD32 tr, tg, tb;
CARD32 r, g, b;
CARD16 *p;
CARD8 ralpha;
ralpha = 255 - alpha;
tr = sr15(pixel, alpha);
tg = sg15(pixel, alpha);
tb = sb15(pixel, alpha);
/* traverse data by 2 bytes starting from the end */
for (p = data + len - 1; p >= data; p--) {
r = sr15(*p, ralpha) + tr;
g = sg15(*p, ralpha) + tg;
b = sb15(*p, ralpha) + tb;
*p = ((r >> 8) & 0x7c00) | ((g >> 8) & 0x3e0) | ((b >> 8) & 0x1f);
}
return SUCCESS;
}
int image_compose_32(CARD8 *data, CARD8 *bg, CARD8 *mask, size_t len)
{
CARD8 *p, *b, *m;
CARD16 a, ra;
/* traverse data, bg by 4 bytes and mask by 1 byte starting from the end */
for (p = data + (len - 1) * 4, b = bg + (len - 1) * 4, m = mask + len - 1;
p != data - 4; p -= 4, b -= 4, m--) {
a = *m;
ra = 255 - a;
p[0] = (p[0] * a + b[0] * ra) >> 8;
p[1] = (p[1] * a + b[1] * ra) >> 8;
p[2] = (p[2] * a + b[2] * ra) >> 8;
}
return SUCCESS;
}
int image_compose_24(CARD8 *data, CARD8 *bg, CARD8 *mask, size_t len)
{
CARD8 *p, *b, *m;
CARD16 a, ra;
/* traverse data, bg by 3 bytes and mask by 1 byte starting from the end */
for (p = data + (len - 1) * 3, b = bg + (len - 1) * 3, m = mask + len - 1;
p != data - 3; p -= 3, b -= 3, m--) {
a = *m;
ra = 255 - a;
p[0] = (p[0] * a + b[0] * ra) >> 8;
p[1] = (p[1] * a + b[1] * ra) >> 8;
p[2] = (p[2] * a + b[2] * ra) >> 8;
}
return SUCCESS;
}
int image_compose_16(CARD16 *data, CARD16 *bg, CARD8 *mask, size_t len)
{
CARD32 r, g, b;
CARD16 *p, *pb;
CARD8 a, ra, *m;
/* traverse data by 2 bytes starting from the end */
for (p = data + len - 1, pb = bg + len - 1, m = mask + len - 1; p != data;
p--, pb--, m--) {
a = *m;
ra = 255 - a;
r = sr16(*p, a) + sr16(*pb, ra);
g = sg16(*p, a) + sg16(*pb, ra);
b = sb16(*p, a) + sb16(*pb, ra);
*p = ((r >> 8) & 0xf800) | ((g >> 8) & 0x7e0) | ((b >> 8) & 0x1f);
}
return SUCCESS;
}
int image_compose_15(CARD16 *data, CARD16 *bg, CARD8 *mask, size_t len)
{
CARD32 r, g, b;
CARD16 *p, *pb;
CARD8 a, ra, *m;
/* traverse data by 2 bytes starting from the end */
for (p = data + len - 1, pb = bg + len - 1, m = mask + len - 1; p != data;
p--, pb--, m--) {
a = *mask;
ra = 255 - a;
r = sr15(*p, a) + sr15(*pb, ra);
g = sg15(*p, a) + sg15(*pb, ra);
b = sb15(*p, a) + sb15(*pb, ra);
*p = ((r >> 8) & 0x7c00) | ((g >> 8) & 0x3e0) | ((b >> 8) & 0x1f);
}
return SUCCESS;
}
================================================
FILE: src/image.h
================================================
/* -------------------------------
* vim:tabstop=4:shiftwidth=4
* image.h
* Fri, 22 Jun 2007 23:32:27 +0700
* -------------------------------
* Simple XImage manipulation
* interface
* -------------------------------*/
#ifndef _IMAGE_H_
#define _IMAGE_H_
#include <X11/Xlib.h>
#include <X11/Xmd.h>
/* outstanding TODO (for 0.8): use Xrender when available */
/* WARNING: works with ZPixmaps only */
/* Creates alpha channel mask with specified fade-out order */
CARD8 *image_create_alpha_mask(int ord, int w, int h);
/* Alpha-tint image using color. */
int image_tint(XImage *image, XColor *color, CARD8 alpha);
/* Compose image stored in tgt with image stored in bg.
* Alpha of each pixel is defined by mask, which should */
int image_compose(XImage *tgt, XImage *bg, CARD8 *mask);
#endif
================================================
FILE: src/kde_tray.c
================================================
/* -------------------------------
* vim:tabstop=4:shiftwidth=4
* kde_tray.c
* Sun, 19 Sep 2004 12:31:10 +0700
* -------------------------------
* kde tray related routines
* -------------------------------*/
#include <X11/X.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include "debug.h"
#include "kde_tray.h"
#include "xutils.h"
/* This list holds "old" KDE icons, e.g. the icons that are (likely) to be
* already embedded into some system tray and, therefore, are to be ignored.
* The list is empty initially */
Window *old_kde_icons = NULL;
unsigned long n_old_kde_icons = -1;
int kde_tray_update_fallback_mode(Display *dpy)
{
/* Get the contents of KDE_NET_SYSTEM_TRAY_WINDOWS root window property.
* All windows that are listed there are considered to be "old" KDE icons,
* i.e. icons that are to be ignored on the tray startup.
* If the property does not exist, fall back to old mode */
if (tray_data.xa_kde_net_system_tray_windows == None
|| !x11_get_root_winlist_prop(dpy,
tray_data.xa_kde_net_system_tray_windows,
(unsigned char **)&old_kde_icons, &n_old_kde_icons)) {
LOG_INFO(("WM does not support KDE_NET_SYSTEM_TRAY_WINDOWS, will use "
"legacy scheme\n"));
x11_extend_root_event_mask(tray_data.dpy, SubstructureNotifyMask);
tray_data.kde_tray_old_mode = 1;
} else {
tray_data.kde_tray_old_mode = 0;
}
return tray_data.kde_tray_old_mode;
}
void kde_tray_init(Display *dpy)
{
static int initialized = False;
unsigned long n_client_windows, i;
Window *client_windows;
Atom xa_net_client_list;
if (!kde_tray_update_fallback_mode(dpy)) return;
/* do nothing if this function was already called */
if (initialized) return;
/* 1. If theres no previous tray selection owner, try to embed all
* available KDE icons and, therefore, leave the list of old KDE icons
* empty */
if (tray_data.old_selection_owner == None) {
n_old_kde_icons = 0;
initialized = True;
return;
}
/* 2.Next, we are going to remove some entries from old_kde_icons list */
/* 2.a. First, we remove all icons that are listed in _NET_CLIENT_LIST
* property, since this means that they are not embedded in any kind of
* tray */
xa_net_client_list = XInternAtom(dpy, "_NET_CLIENT_LIST", True);
if (x11_get_root_winlist_prop(dpy, xa_net_client_list,
(unsigned char **)&client_windows, &n_client_windows)) {
for (i = 0; i < n_client_windows; i++)
kde_tray_old_icons_remove(client_windows[i]);
}
/* 2.b. Second, we remove all windows that have root window as their
* parent,
* since this also means that they are not embedded in any kind of tray */
for (i = 0; i < n_old_kde_icons; i++) {
Window root, parent, *children;
unsigned int nchildren;
int rc;
nchildren = 0;
children = NULL;
if ((rc = XQueryTree(dpy, old_kde_icons[i], &root, &parent, &children,
&nchildren))) {
if (root == parent) old_kde_icons[i] = None;
if (nchildren > 0) XFree(children);
}
if (!x11_ok() || !rc) old_kde_icons[i] = None;
}
#ifdef DEBUG
/* Some diagnostic output */
for (i = 0; i < n_old_kde_icons; i++)
if (old_kde_icons[i] != None)
LOG_TRACE(
("0x%lx is marked as an old KDE icon\n", old_kde_icons[i]));
#endif
initialized = True;
}
int kde_tray_update_old_icons(Display *dpy)
{
unsigned int i, rc;
XWindowAttributes xwa;
/* Remove dead entries from old kde icons list.
* We use XGetWindowAttributes to see if the
* window is still alive */
for (i = 0; i < n_old_kde_icons; i++) {
rc = XGetWindowAttributes(dpy, old_kde_icons[i], &xwa);
if (!x11_ok() || !rc) old_kde_icons[i] = None;
}
return SUCCESS;
}
int kde_tray_is_old_icon(Window w)
{
unsigned int i;
for (i = 0; i < n_old_kde_icons; i++)
if (old_kde_icons[i] == w) return True;
return False;
}
void kde_tray_old_icons_remove(Window w)
{
unsigned int i;
for (i = 0; i < n_old_kde_icons; i++)
if (old_kde_icons[i] == w) {
LOG_TRACE(("0x%lx unmarked as an old kde icon\n", w));
old_kde_icons[i] = None;
}
}
int kde_tray_check_for_icon(Display *dpy, Window w)
{
Atom actual_type;
int actual_format;
unsigned long nitems, bytes_after;
static Atom xa_kde_net_wm_system_tray_window_for = None;
unsigned char *data = NULL;
/* Check if the window has a property named _KDE_NET_WM_SYSTEM_TRAY_WINDOW
* FOR */
if (xa_kde_net_wm_system_tray_window_for == None)
xa_kde_net_wm_system_tray_window_for =
XInternAtom(dpy, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", True);
/* If this atom does not exist, we have nothing to check for */
if (xa_kde_net_wm_system_tray_window_for == None) return False;
/* TODO: use x11_ call */
XGetWindowProperty(dpy, w, xa_kde_net_wm_system_tray_window_for, 0L, 1L,
False, XA_WINDOW, &actual_type, &actual_format, &nitems, &bytes_after,
&data);
XFree(data);
if (x11_ok() && actual_type == XA_WINDOW && nitems == 1)
return SUCCESS;
else
return FAILURE;
}
Window kde_tray_find_icon(Display *dpy, Window w)
{
Window root, parent, *children = NULL;
unsigned int nchildren, i;
Window r = None;
if (kde_tray_check_for_icon(dpy, w)) return w;
XQueryTree(dpy, w, &root, &parent, &children, &nchildren);
if (!x11_ok()) goto bailout;
for (i = 0; i < nchildren; i++)
if ((r = kde_tray_find_icon(dpy, children[i])) != None) goto bailout;
bailout:
if (children != NULL && nchildren > 0) XFree(children);
return r;
}
================================================
FILE: src/kde_tray.h
================================================
/* -------------------------------
* vim:tabstop=4:shiftwidth=4
* kde_tray.h
* Sun, 19 Sep 2004 12:28:59 +0700
* -------------------------------
* KDE tray related routines
* -------------------------------*/
#ifndef _KDE_TRAY_H_
#define _KDE_TRAY_H_
/* Init support for KDE tray icons. */
void kde_tray_init(Display *dpy);
/* Check if WM supports KDE tray icons */
int kde_tray_update_fallback_mode(Display *dpy);
/* Update the list of "old" KDE icons. Icon is considered "old"
* if it was present before the tray was started. */
int kde_tray_update_old_icons(Display *dpy);
/* Check if the window w is an "old" KDE tray icon */
int kde_tray_is_old_icon(Window w);
/* Remove the window w from the list of "old" KDE tray icons */
void kde_tray_old_icons_remove(Window w);
/* Check if the window w is a KDE tray icon */
int kde_tray_check_for_icon(Display *dpy, Window w);
/* Find KDE tray icon in subwindows of w */
Window kde_tray_find_icon(Display *dpy, Window w);
#endif
================================================
FILE: src/layout.c
================================================
/* -------------------------------
* vim:tabstop=4:shiftwidth=4
* layout.c
* Tue, 24 Aug 2004 12:19:48 +0700
* -------------------------------
* Icon layout implementation.
* (Used to be) the dirtiest place around.
* -------------------------------*/
#include <limits.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "common.h"
#include "debug.h"
#include "icons.h"
#include "layout.h"
#include "list.h"
#include "tray.h"
/* not very nice (and calculates its arguments twice!), but allows doing things
* like swap(*a, *b) */
#define swap(a, b) \
do { \
int t; \
t = (a); \
(a) = (b); \
(b) = t; \
} while (0)
/*****************
* Grid interface *
*****************/
struct Point grid_sz = {0, 0};
int grid_add(struct TrayIcon *ti);
int grid_add_wrapper(struct TrayIcon *ti);
int grid_remove(struct TrayIcon *ti);
int grid_update(struct TrayIcon *ti, int sort);
int grid_translate_from_window(struct TrayIcon *ti);
int layout_translate_to_window(struct TrayIcon *ti);
int layout_unset_flag(struct TrayIcon *ti);
/************************
* Layout implementation *
************************/
int layout_add(struct TrayIcon *ti)
{
if (grid_add(ti)) {
grid_update(ti, True);
return SUCCESS;
} else
return FAILURE;
}
int layout_remove(struct TrayIcon *ti)
{
return ti->is_layed_out ? grid_remove(ti) : SUCCESS;
}
int layout_handle_icon_resize(struct TrayIcon *ti)
{
struct Rect old_grd_rect;
struct Point old_grid_sz;
int rc;
#ifdef DEBUG
if (settings.log_level >= LOG_LEVEL_TRACE) {
LOG_TRACE(("currently managed icons:\n"));
icon_list_forall(&print_icon_data);
}
#endif
if (!icon_list_backup()) return FAILURE;
old_grid_sz = grid_sz;
if (ti->is_layed_out) {
/* if the icon is already layed up and
* its grid rect did not change we do nothing,
* since its position inside grid cell will be
* updated by embedder_update_positions */
old_grd_rect = ti->l.grd_rect;
grid_translate_from_window(ti);
if (ti->l.grd_rect.w == old_grd_rect.w
&& ti->l.grd_rect.h == old_grd_rect.h) {
icon_list_backup_purge();
return SUCCESS;
}
}
/* Here's the place where icon sorting start playing its role.
* It is easy to see that resizing ti affects only those icons
* that are after ti in the icon list. */
/* 1. Unset layout flags of all icons after ti */
icon_list_forall_from(ti, &layout_unset_flag);
/* 2. If shrink mode is on, recalculate the size of the grid
* for the remaining icons. This ensures that final grid
* will be "minimal" (I did not prove that :) */
if (settings.shrink_back_mode) grid_update(ti, False);
#ifdef DEBUG
if (settings.log_level >= LOG_LEVEL_TRACE) {
LOG_TRACE(("list of icons which are to be added back to the grid\n"));
icon_list_forall_from(ti, &print_icon_data);
}
#endif
/* 3. Start adding icons after ti back to the grid one
* by one. If this fails for some icon, forall_icons_from()
* will return non-NULL value and this will be considered
* as a error condition */
if (icon_list_forall_from(ti, &grid_add_wrapper) == NULL) {
/* Everything is OK */
icon_list_backup_purge();
grid_update(ti, True);
rc = SUCCESS;
} else {
/* Error has occured */
icon_list_restore();
grid_sz = old_grid_sz;
rc = FAILURE;
}
#ifdef DEBUG
if (settings.log_level >= LOG_LEVEL_TRACE) {
LOG_TRACE(("currently managed icons:\n"));
icon_list_forall(&print_icon_data);
}
#endif
return rc;
}
void layout_get_size(int *width, int *height)
{
*width = grid_sz.x * settings.slot_size.x;
*height = grid_sz.y * settings.slot_size.y;
if (settings.vertical) swap((*width), (*height));
}
struct TrayIcon *layout_next(struct TrayIcon *current)
{
if ((settings.icon_gravity & GRAV_H) == GRAV_W)
return icon_list_next(current);
else
return icon_list_prev(current);
}
struct TrayIcon *layout_prev(struct TrayIcon *current)
{
if ((settings.icon_gravity & GRAV_H) == GRAV_W)
return icon_list_prev(current);
else
return icon_list_next(current);
}
/**********************
* Grid implementation *
**********************/
/* Structure to hold possible placement for an icon */
struct IconPlacement {
struct Point pos; /* Position */
struct Point sz_delta; /* Layout size delta */
int valid; /* Is the placement valid */
};
/* Find best placement for an icon */
struct IconPlacement *grid_find_placement(struct TrayIcon *ti);
/* Place tray icon as specified by ip */
int grid_place_icon(struct TrayIcon *ti, struct IconPlacement *ip);
/* Comparison function for icon_list_sort */
int trayicon_cmp_func(struct TrayIcon *ti1, struct TrayIcon *ti2);
/* Update grid dimentions */
int grid_recalc_size(struct TrayIcon *ti);
/* A. Coords translations */
int grid_translate_from_window(struct TrayIcon *ti)
{
ti->l.grd_rect.w = ti->l.wnd_sz.x / settings.slot_size.x
+ (ti->l.wnd_sz.x % settings.slot_size.x != 0);
ti->l.grd_rect.h = ti->l.wnd_sz.y / settings.slot_size.y
+ (ti->l.wnd_sz.y % settings.slot_size.y != 0);
if (settings.vertical) swap(ti->l.grd_rect.w, ti->l.grd_rect.h);
return NO_MATCH;
}
int layout_translate_to_window(struct TrayIcon *ti)
{
struct Rect old_icn_rect = ti->l.icn_rect;
if (!ti->is_layed_out) return NO_MATCH;
/* Swap vert & horz dimentions temporarily */
if (settings.vertical) {
swap(ti->l.grd_rect.w, ti->l.grd_rect.h);
swap(ti->l.grd_rect.x, ti->l.grd_rect.y);
}
/* Compute icon position and dimensions */
if (settings.icon_gravity & GRAV_W)
ti->l.icn_rect.x = ti->l.grd_rect.x * settings.slot_size.x;
else
ti->l.icn_rect.x = tray_data.xsh.width
- (ti->l.grd_rect.x + ti->l.grd_rect.w) * settings.slot_size.x;
if (settings.icon_gravity & GRAV_N)
ti->l.icn_rect.y = ti->l.grd_rect.y * settings.slot_size.y;
else
ti->l.icn_rect.y = tray_data.xsh.height
- (ti->l.grd_rect.y + ti->l.grd_rect.h) * settings.slot_size.y;
ti->l.icn_rect.w = ti->l.grd_rect.w * settings.slot_size.y;
ti->l.icn_rect.h = ti->l.grd_rect.h * settings.slot_size.y;
/* Swap vert & horz dimentions back */
if (settings.vertical) {
swap(ti->l.grd_rect.w, ti->l.grd_rect.h);
swap(ti->l.grd_rect.x, ti->l.grd_rect.y);
}
/* Shift icon position according to scrollbars positions */
/* TODO: must be in separate function, with a reverse */
ti->l.icn_rect.x += ((settings.icon_gravity & GRAV_W ? 1 : -1)
* (tray_data.scrollbars_data.scroll_base.x
- tray_data.scrollbars_data.scroll_pos.x));
ti->l.icn_rect.y += ((settings.icon_gravity & GRAV_N ? 1 : -1)
* (tray_data.scrollbars_data.scroll_base.y
- tray_data.scrollbars_data.scroll_pos.y));
LOG_TRACE(("grid %dx%d+%d+%d -> window %dx%d+%d+%d\n", ti->l.grd_rect.w,
ti->l.grd_rect.h, ti->l.grd_rect.x, ti->l.grd_rect.y, ti->l.icn_rect.w,
ti->l.icn_rect.h, ti->l.icn_rect.x, ti->l.icn_rect.y));
/* Set flag indicating if new position is different from old */
ti->is_updated = ti->is_updated
|| !(ti->l.icn_rect.x == old_icn_rect.x
&& ti->l.icn_rect.y == old_icn_rect.y
&& ti->l.icn_rect.w == old_icn_rect.w
&& ti->l.icn_rect.h == old_icn_rect.h);
return NO_MATCH;
}
/* B. Adding/removing icon from the grid */
int grid_add(struct TrayIcon *ti)
{
struct IconPlacement *pmt;
/* Invalidate grid position data */
ti->l.grd_rect.x = -1;
ti->l.grd_rect.y = -1;
ti->l.grd_rect.w = -1;
ti->l.grd_rect.h = -1;
/* Invalidate window position data */
ti->l.icn_rect.x = -1;
ti->l.icn_rect.y = -1;
ti->l.icn_rect.w = -1;
ti->l.icn_rect.h = -1;
/* Get window dimensions */
grid_translate_from_window(ti);
pmt = grid_find_placement(ti);
if (pmt == NULL) {
LOG_TRACE(("no candidates for placement found\n"));
ti->is_layed_out = False;
RETURN_STATUS(FAILURE);
}
ti->is_layed_out = True;
grid_place_icon(ti, pmt);
RETURN_STATUS(SUCCESS);
}
int grid_remove(struct TrayIcon *ti)
{
/* implementation is similar to that of layout_handle_icon_resize(),
* see detailed description there */
icon_list_forall_from(ti, &layout_unset_flag);
if (settings.shrink_back_mode || settings.scrollbars_mode != SB_MODE_NONE)
grid_update(ti, False);
/* Since NULL is a special case for icon_list_froall_from,
* avoid calling it for the last icon */
if (ti->next != NULL)
RETURN_STATUS(
icon_list_forall_from(ti->next, &grid_add_wrapper) == NULL);
else
RETURN_STATUS(SUCCESS);
}
/* C. Grid manipulations */
int grid_update(struct TrayIcon *ti, int sort)
{
icon_list_forall_from(ti, &layout_translate_to_window);
if (sort) icon_list_sort(&trayicon_cmp_func);
/* recalculate minimal size */
grid_sz.x = 0;
grid_sz.y = 0;
icon_list_forall(&grid_recalc_size);
LOG_TRACE(("final grid size: %dx%d\n", grid_sz.x, grid_sz.y));
return SUCCESS;
}
/* D. Placement functions */
int grid_place_icon(struct TrayIcon *ti, struct IconPlacement *ip)
{
struct Layout *l = &ti->l;
struct Point *p = &ip->pos;
/* Set the flag if icon position was really updated */
ti->is_updated =
ti->is_updated || (p->x != l->grd_rect.x || p->y != l->grd_rect.y);
if (ti->is_updated || !ti->is_layed_out) {
LOG_TRACE(("updating position (%d,%d) to (%d,%d)\n", ti->l.grd_rect.x,
ti->l.grd_rect.y, p->x, p->y));
l->grd_rect.x = p->x;
l->grd_rect.y = p->y;
}
/* Update grid dimensions */
grid_sz.x += ip->sz_delta.x;
grid_sz.y += ip->sz_delta.y;
/* Update icon position */
layout_translate_to_window(ti);
LOG_TRACE(("current grid size: %dx%d\n", grid_sz.x, grid_sz.y));
return ti->is_updated;
}
/* Oh, the wonders of languages without closures... */
static struct Rect chk_rect;
/* Check if grid rect of ti intersects with chk_rect */
int find_obstacle(struct TrayIcon *ti)
{
return ti->is_layed_out
&& (RECTS_ISECT(chk_rect, ti->l.grd_rect)
|| RECTS_ISECT(ti->l.grd_rect, chk_rect));
}
/* Check if grid rect of ti intersects with any other tray icon
* grid rect and return its width or height, depending on tray orientation */
int grid_check_rect_free(int x, int y, int w, int h)
{
struct TrayIcon *obst;
chk_rect.x = x;
chk_rect.y = y;
chk_rect.w = w;
chk_rect.h = h;
obst = icon_list_advanced_find(&find_obstacle);
if (obst == NULL) {
return 0;
} else {
if (settings.vertical)
return obst->l.grd_rect.h;
else
return obst->l.grd_rect.w;
}
}
/* Simple function to fill in fields of IconPlacement struct
* x, y: position of the icon
* rect: provides the size of the icon
* return value: returns true if the placement is valid; returns false
* otherwise.
*/
int icon_placement_create(
struct IconPlacement *ip, int x, int y, struct Rect *rect)
{
/* scrollbar & tray orientation-aware shortcuts for maximal layout dimensions.
* if scrollbar is enabled in some direction, layout size in this dimension is
* not limited */
#define max_layout_width \
(settings.scrollbars_mode & SB_MODE_HORZ \
? INT_MAX \
: ((settings.vertical ? settings.max_tray_dims.y \
: settings.max_tray_dims.x) \
/ settings.slot_size.x))
#define max_layout_height \
(settings.scrollbars_mode & SB_MODE_VERT \
? INT_MAX \
: ((settings.vertical ? settings.max_tray_dims.x \
: settings.max_tray_dims.y) \
/ settings.slot_size.y))
ip->valid = 0;
ip->pos.x = x;
ip->pos.y = y;
ip->sz_delta.x = x + rect->w - grid_sz.x;
ip->sz_delta.y = y + rect->h - grid_sz.y;
ip->sz_delta.x = ip->sz_delta.x > 0 ? ip->sz_delta.x : 0;
ip->sz_delta.y = ip->sz_delta.y > 0 ? ip->sz_delta.y : 0;
ip->valid = (ip->pos.x + rect->w <= max_layout_width)
&& (ip->pos.y + rect->h <= max_layout_height);
#ifdef DEBUG
LOG_TRACE(("placement (%d, %d, %d, %d) is %s\n", x, y, ip->sz_delta.x,
ip->sz_delta.y, ip->valid ? "valid" : "invalid"));
#endif
return ip->valid;
}
/* Compare points in lexographical order */
#define compare_points(a, b) \
(((a).y < (b).y) || ((a).y == (b).y && (a).x < (b).x))
/* Choose best placement --- placement policy is defined here
* Placement A is considered to be better than placement B
* if either
* - min_space_policy is on and (window) size delta for A is
* strictly less then size delta for B;
* - position for A is less (in lexographical order)
* than position for B.
* */
void icon_placement_choose_best(
struct IconPlacement *old, struct IconPlacement *new)
{
int old_lout_delta_norm, new_lout_delta_norm;
int lout_norm_cmp;
struct Point new_wnd_sz_delta, old_wnd_sz_delta;
int old_wnd_delta_norm = 0, new_wnd_delta_norm = 0;
int wnd_norm_cmp = 0;
/* I whish there were nested functions in C standard */
#define calc_wnd_sz_delta(delta, pmt) \
do { \
delta.x = cutoff((grid_sz.x + pmt->sz_delta.x) * settings.slot_size.x \
- tray_data.xsh.width, \
0, pmt->sz_delta.x * settings.slot_size.x); \
delta.y = cutoff((grid_sz.y + pmt->sz_delta.y) * settings.slot_size.y \
- tray_data.xsh.height, \
0, pmt->sz_delta.y * settings.slot_size.y); \
} while (0)
/* If tray is vertically oriented, swap width <-> height
* (just for readability sake) */
if (settings.vertical) {
swap(tray_data.xsh.width, tray_data.xsh.height);
swap(settings.orig_tray_dims.x, settings.orig_tray_dims.y);
}
calc_wnd_sz_delta(old_wnd_sz_delta, old);
calc_wnd_sz_delta(new_wnd_sz_delta, new);
/* Some black magic. This is probably buggy and you are not supposed to
* understand this (I definetly don't). The basic idea is that sometimes
* layout resize means window resize. Sometimes it does not. */
if (settings.shrink_back_mode) {
if (grid_sz.x >= settings.orig_tray_dims.x / settings.slot_size.x) {
old_wnd_sz_delta.x = old->sz_delta.x * settings.slot_size.x;
new_wnd_sz_delta.x = new->sz_delta.x *settings.slot_size.x;
}
if (grid_sz.y >= settings.orig_tray_dims.y / settings.slot_size.y) {
old_wnd_sz_delta.y = old->sz_delta.y * settings.slot_size.y;
new_wnd_sz_delta.y = new->sz_delta.y *settings.slot_size.y;
}
}
/* Restore values */
if (settings.vertical) {
swap(tray_data.xsh.width, tray_data.xsh.height);
swap(settings.orig_tray_dims.x, settings.orig_tray_dims.y);
}
LOG_TRACE(("old placement = (%d, %d, %d, %d, %d, %d)\n", old->pos.x,
old->pos.y, old->sz_delta.x, old->sz_delta.y, old_wnd_sz_delta.x,
old_wnd_sz_delta.y));
LOG_TRACE(("new placement = (%d, %d, %d, %d, %d, %d)\n", new->pos.x,
new->pos.y, new->sz_delta.x, new->sz_delta.y, new_wnd_sz_delta.x,
new_wnd_sz_delta.y));
/* Compute norms for window deltas */
old_wnd_delta_norm = old_wnd_sz_delta.x + old_wnd_sz_delta.y;
new_wnd_delta_norm = new_wnd_sz_delta.x + new_wnd_sz_delta.y;
wnd_norm_cmp = new_wnd_delta_norm < old_wnd_delta_norm;
/* Compute norms for layout deltas */
old_lout_delta_norm = old->sz_delta.x + old->sz_delta.y;
new_lout_delta_norm = new->sz_delta.x + new->sz_delta.y;
lout_norm_cmp = new_lout_delta_norm < old_lout_delta_norm;
LOG_TRACE(("old wnd delta norm = %d, lout delta norm = %d\n",
old_wnd_delta_norm, old_lout_delta_norm));
LOG_TRACE(("new wnd delta norm = %d, lout delta norm = %d\n",
new_wnd_delta_norm, new_lout_delta_norm));
LOG_TRACE(("lout norm cmp = %d, wnd norm cmp = %d\n", lout_norm_cmp,
wnd_norm_cmp));
LOG_TRACE(("compare points (new, old) = %d\n",
compare_points(new->pos, old->pos)));
/* CORE of placement logic. In short,
* - valid placement is always better than invalid
* - with minimal space policy on (off by default), placement with smaller
* deltas is better
* - placement which does not cause tray window to grow is always better
* than one that does
* - otherwise, placement that is "closer" to initial point (that depends
* on gravity) is better */
if (!old->valid && new->valid) goto replace;
if (settings.min_space_policy && (lout_norm_cmp || wnd_norm_cmp))
goto replace;
if (old_lout_delta_norm && !new_lout_delta_norm) goto replace;
if (old_wnd_delta_norm && !new_wnd_delta_norm) goto replace;
if ((!old_lout_delta_norm == !new_lout_delta_norm)
&& compare_points(new->pos, old->pos))
goto replace;
LOG_TRACE(("old placement is better\n"));
return;
replace:
LOG_TRACE(("new placement is better\n"));
*old = *new;
}
/* WARNING: returns ptr to STATIC buffer */
struct IconPlacement *grid_find_placement(struct TrayIcon *ti)
{
static struct IconPlacement cur_pmt;
struct IconPlacement tmp_pmt;
int x = 0, y = 0, skip, orig_layed_out;
orig_layed_out = ti->is_layed_out;
ti->is_layed_out = 0; /* to avoid self-intersections */
/* General idea is to look through all possible placements
* while keeping one which is considered to be the "best" and
* comparing current placement with "best" one and updating
* it accordingly */
if (grid_sz.x == 0 || grid_sz.y == 0) { /* Grid is empty */
/* This is the only possible placement */
icon_placement_create(&cur_pmt, 0, 0, &ti->l.grd_rect);
} else { /* Seek and place */
/* Create two obvious placements */
/* Rightmost-top position */
icon_placement_create(&cur_pmt, grid_sz.x, 0, &ti->l.grd_rect);
/* Bottommost-left position */
icon_placement_create(&tmp_pmt, 0, grid_sz.y, &ti->l.grd_rect);
/* Choose the best one */
icon_placement_choose_best(&cur_pmt, &tmp_pmt);
/* Start looking through possible placements.
* Current placement is determined by x and y variables. */
do {
/* Check if the current rect is free */
skip =
grid_check_rect_free(x, y, ti->l.grd_rect.w, ti->l.grd_rect.h);
LOG_TRACE(("x=%d, y=%d, skip=%d\n", x, y, skip));
/* If so, create new placement */
if (skip == 0) {
if (icon_placement_create(&tmp_pmt, x, y, &ti->l.grd_rect)) {
icon_placement_choose_best(&cur_pmt, &tmp_pmt);
/* If not settings.full_pmt_search, take the first valid
* non-resizing placement and run */
if (!settings.full_pmt_search && !cur_pmt.sz_delta.x
&& !cur_pmt.sz_delta.y)
break;
}
skip = ti->l.grd_rect.w;
}
/* Advance to the next possible placement */
x += skip;
if (x >= grid_sz.x) {
x = 0;
y++;
}
} while (x < grid_sz.x && y < grid_sz.y);
}
/* Restore layed out state */
ti->is_layed_out = orig_layed_out;
if (cur_pmt.valid) {
LOG_TRACE(("using placement (%d, %d, %d, %d)\n", cur_pmt.pos.x,
cur_pmt.pos.y, cur_pmt.sz_delta.x, cur_pmt.sz_delta.y));
return &cur_pmt;
} else {
return NULL;
}
}
/* E. Mass-ops helpers */
int trayicon_cmp_func(struct TrayIcon *ti1, struct TrayIcon *ti2)
{
return (!ti2->is_layed_out && ti1->is_layed_out)
|| compare_points(ti2->l.grd_rect, ti1->l.grd_rect);
}
int grid_add_wrapper(struct TrayIcon *ti)
{
/* Ignore hidden icons */
return !ti->is_visible || grid_add(ti) ? NO_MATCH : MATCH;
}
int grid_recalc_size(struct TrayIcon *ti)
{
int x, y;
/* Ignore icons that are not layed out */
if (!ti->is_layed_out) return NO_MATCH;
/* Calculate coordinates for bottom-right corner of ti */
x = ti->l.grd_rect.x + ti->l.grd_rect.w;
y = ti->l.grd_rect.y + ti->l.grd_rect.h;
/* Update grid dimensions */
grid_sz.x = x > grid_sz.x ? x : grid_sz.x;
grid_sz.y = y > grid_sz.y ? y : grid_sz.y;
LOG_TRACE(("new grid size: %dx%d\n", grid_sz.x, grid_sz.y));
return NO_MATCH;
}
int layout_unset_flag(struct TrayIcon *ti)
{
ti->is_layed_out = 0;
return NO_MATCH;
}
================================================
FILE: src/layout.h
================================================
/* -------------------------------
* vim:tabstop=4:shiftwidth=4
* layout.h
* Tue, 24 Aug 2004 12:19:27 +0700
* -------------------------------
* Icon layout implementation
* -------------------------------*/
#ifndef _LAYOUT_H_
#define _LAYOUT_H_
#include "icons.h"
/* Gravity constants */
/* East gravity */
#define GRAV_E (1L << 0)
/* West gravity */
#define GRAV_W (1L << 1)
/* South gravity */
#define GRAV_S (1L << 2)
/* North gravity */
#define GRAV_N (1L << 3)
/* Shortcut for horisontal gravity */
#define GRAV_H (GRAV_W | GRAV_E)
/* Shortcut for vertical gravity */
#define GRAV_V (GRAV_S | GRAV_N)
/* Macros to test rect intersection */
/* Helpers */
#define RX1(r) (r.x)
#define RY1(r) (r.y)
#define RX2(r) (r.x + r.w - 1)
#define RY2(r) (r.y + r.h - 1)
#define RECTS_ISECT_(r1, r2) \
(((RX1(r1) <= RX1(r2) && RX1(r2) <= RX2(r1)) \
|| (RX1(r1) <= RX2(r2) && RX2(r2) <= RX2(r1))) \
&& ((RY1(r1) <= RY1(r2) && RY1(r2) <= RY2(r1)) \
|| (RY1(r1) <= RY2(r2) && RY2(r2) <= RY2(r1))))
/* Macro itself */
#define RECTS_ISECT(r1, r2) (RECTS_ISECT_(r1, r2) || RECTS_ISECT_(r2, r1))
/* Add icon to layout */
int layout_add(struct TrayIcon *ti);
/* Remove icon from layout */
int layout_remove(struct TrayIcon *ti);
/* Relayout the icon which has changed its size */
int layout_handle_icon_resize(struct TrayIcon *ti);
/* Get current layout dimensions */
void layout_get_size(int *width, int *height);
/* Translate grid coordinates into window coordinates */
int layout_translate_to_window(struct TrayIcon *ti);
/* Return next icon in tab chain */
struct TrayIcon *layout_next(struct TrayIcon *current);
/* Return previous icon in tab chain */
struct TrayIcon *layout_prev(struct TrayIcon *current);
#endif
================================================
FILE: src/list.h
================================================
/* -------------------------------
* vim:tabstop=4:shiftwidth=4
* list.h
* Fri, 10 Sep 2004 23:34:48 +0700
* -------------------------------
* Simple double linked list
* -------------------------------*/
#ifndef _LIST_H_
#define _LIST_H_
/* Implement basic functions for doubly-linked list.
* Each structure to hold an element of the list must have two
* fields pointer fields: prev and next. */
/* Add item i_ to the head of the list h_ (pointer to the head) */
#define LIST_ADD_ITEM(h_, i_) \
do { \
(i_)->prev = NULL; \
if ((h_) != NULL) { \
(i_)->next = (h_); \
(h_)->prev = (i_); \
} else { \
(i_)->next = NULL; \
} \
(h_) = (i_); \
} while (0)
/* Insert item i_ after item t_ to the list h_ (pointer to the head) */
#define LIST_INSERT_AFTER(h_, t_, i_) \
do { \
(i_)->prev = (t_); \
if ((t_) != NULL) { \
(i_)->next = (t_)->next; \
(t_)->next = (i_); \
} else { \
if ((h_) != NULL) { \
(i_)->next = (h_); \
(h_)->prev = (i_); \
} else { \
(i_)->next = NULL; \
} \
(h_) = (i_); \
} \
} while (0)
/* Delete item i_ from the list h_ (pointer to the head) */
#define LIST_DEL_ITEM(h_, i_) \
do { \
if (i_->prev != NULL) i_->prev->next = i_->next; \
if (i_->next != NULL) i_->next->prev = i_->prev; \
if (i_ == h_) h_ = i_->next; \
} while (0)
/* Clean the list h_ (pointer to the head);
* tmp_ is a temporary variable */
#define LIST_CLEAN(h_, tmp_) \
do { \
for (tmp_ = h_, h_ = (h_ != NULL) ? h_->next : NULL; tmp_ != NULL; \
tmp_ = h_, h_ = h_ != NULL ? h_->next : NULL) { \
free(tmp_); \
} \
h_ = NULL; \
} while (0)
/* Clean the list h_ (pointer to the head) calling cbk_ for every
* element of the list; tmp_ is a temporary variable */
#define LIST_CLEAN_CBK(h_, tmp_, cbk_) \
do { \
for (tmp_ = h_, h_ = (h_ != NULL) ? h_->next : NULL; tmp_ != NULL; \
tmp_ = h_, h_ = h_ != NULL ? h_->next : NULL) { \
cbk_(tmp_); \
free(tmp_); \
} \
h_ = NULL; \
} while (0)
#endif
================================================
FILE: src/main.c
================================================
/* -------------------------------
* vim:tabstop=4:shiftwidth=4
* main.c
* Tue, 24 Aug 2004 12:19:48 +0700
* -------------------------------
* main is main
* -------------------------------*/
#include <X11/X.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <X11/Xmd.h>
#include <X11/Xutil.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include "common.h"
#include "debug.h"
#include "embed.h"
#include "icons.h"
#include "layout.h"
#include "wmh.h"
#include "xembed.h"
#include "xutils.h"
#ifdef _ST_WITH_NATIVE_KDE
#include "kde_tray.h"
#endif
#include "scrollbars.h"
#include "settings.h"
#include "tray.h"
#include "xinerama.h"
struct TrayData tray_data;
static int tray_status_requested = 0;
#ifdef _ST_EXIT_GRACEFULLY
static Display *async_dpy;
#endif
void my_usleep(useconds_t usec)
{
struct timeval timeout;
fd_set rfds;
FD_ZERO(&rfds);
timeout.tv_sec = 0;
timeout.tv_usec = usec;
select(1, &rfds, NULL, NULL, &timeout);
}
/****************************
* Signal handlers, cleanup
***************************/
void request_tray_status_on_signal(int)
{
tray_status_requested = 1;
}
#ifdef _ST_EXIT_GRACEFULLY
void exit_on_signal(int sig)
{
if (sig == SIGPIPE) {
debug_disable_output();
} else {
psignal(sig, "");
/* This is UGLY and is, probably, to be submitted to
* Daily WTF, but it is the only way I found not to
* use usleep in main event loop. */
LOG_TRACE(("Sending fake WM_DELETE_WINDOW message\n"));
}
x11_send_client_msg32(async_dpy, tray_data.tray, tray_data.tray,
tray_data.xa_wm_protocols, tray_data.xa_wm_delete_window, 0, 0, 0, 0);
XSync(async_dpy, False);
}
#endif
void cleanup()
{
static int clean = 0;
static int cleanup_in_progress = 0;
if (!clean && cleanup_in_progress) {
LOG_ERROR(("forced to die\n"));
abort();
}
if (clean) return;
cleanup_in_progress = 1;
if (tray_data.dpy != NULL && x11_connection_status()) {
LOG_TRACE(("being nice to the icons\n"));
/* Clean the list unembedding icons one by one */
icon_list_clean_callback(&embedder_unembed);
/* Give away the selection */
if (tray_data.is_active)
XSetSelectionOwner(
tray_data.dpy, tray_data.xa_tray_selection, None, CurrentTime);
/* Sync in order to wait until all icons finish their reparenting
* process */
XSync(tray_data.dpy, False);
XCloseDisplay(tray_data.dpy);
tray_data.dpy = NULL;
}
cleanup_in_progress = 0;
clean = 1;
}
/**************************************
* Helper functions
**************************************/
/* Print tray status */
void dump_tray_status()
{
int grid_w, grid_h;
tray_status_requested = 0;
layout_get_size(&grid_w, &grid_h);
LOG_INFO(("----------- tray status -----------\n"));
LOG_INFO(("active: %s\n", tray_data.is_active ? "yes" : "no"));
LOG_INFO(("geometry: %dx%d+%d+%d\n", tray_data.xsh.width,
tray_data.xsh.height, tray_data.xsh.x, tray_data.xsh.y));
if (tray_data.xembed_data.current)
LOG_INFO(("XEMBED focus: 0x%lx\n", tray_data.xembed_data.current->wid));
else
LOG_INFO(("XEMBED focus: none\n"));
LOG_INFO(("currently managed icons:\n"));
icon_list_forall(&print_icon_data);
LOG_INFO(("-----------------------------------\n"));
}
/* Checks whether a given window class should be ignored */
int is_ignored_class(const char *name) {
struct WindowClass *haystack = NULL;
for (haystack = settings.ignored_classes; haystack; haystack = haystack->next) {
if (!strcmp(name, haystack->name))
return 1;
}
return 0;
}
/**************************************
* (Un)embedding cycle implementation
**************************************/
/* Add icon to the tray */
void add_icon(Window w, int cmode)
{
struct TrayIcon *ti;
const char *classname = NULL;
/* Aviod adding duplicates */
if ((ti = icon_list_find(w)) != NULL) {
LOG_TRACE(("ignoring second request to embed 0x%lx"
" (requested cmode=%d, current cmode=%d)\n",
w, cmode, ti->cmode));
return;
}
/* Dear Edsger W. Dijkstra, I see you behind my back =( */
if ((ti = icon_list_new(w, cmode)) == NULL) goto icon_allocation_failed;
LOG_TRACE(("starting embedding for icon 0x%lx, cmode=%d\n", w, cmode));
x11_dump_win_info(tray_data.dpy, w);
classname = x11_get_window_class(tray_data.dpy, w);
if (classname == NULL) {
LOG_TRACE(("Ignoring icon, x11_get_window_class() failed: 0x%lx"
" (requested cmode=%d)\n",
w, cmode));
goto embedding_failed;
}
if (is_ignored_class(classname)) {
LOG_INFO(("Ignoring icon because its class is ignored: %s\n", classname));
goto done;
}
/* Start embedding cycle */
if (!xembed_check_support(ti)) goto embedding_failed;
if (ti->is_xembed_supported)
ti->is_visible = xembed_get_mapped_state(ti);
else
ti->is_visible = True;
if (ti->is_visible) {
if (!embedder_reset_size(ti)) goto embedding_failed;
if (!layout_add(ti)) goto embedding_failed;
}
if (!xembed_embed(ti)) goto embedding_failed_after_layout;
if (!embedder_embed(ti)) goto embedding_failed_after_layout;
embedder_update_positions(False);
tray_update_window_props();
/* Report success */
LOG_INFO(("added icon %s (wid 0x%lx) as %s\n",
x11_get_window_name(tray_data.dpy, ti->wid, "<unknown>"), ti->wid,
ti->is_visible ? "visible" : "hidden"));
goto done;
embedding_failed_after_layout:
layout_remove(ti);
embedding_failed:
icon_list_free(ti);
icon_allocation_failed:
LOG_INFO(("failed to add icon %s (wid 0x%lx)\n",
x11_get_window_name(tray_data.dpy, ti->wid, "<unknown>"), ti->wid));
done:
if (classname != NULL)
free((void *) classname);
if (settings.log_level >= LOG_LEVEL_TRACE) dump_tray_status();
return;
}
/* Remove icon from the tray */
void remove_icon(Window w)
{
struct TrayIcon *ti;
/* Ignore false alarms */
if ((ti = icon_list_find(w)) == NULL) return;
dump_tray_status();
embedder_unembed(ti);
xembed_unembed(ti);
layout_remove(ti);
icon_list_free(ti);
LOG_INFO(("removed icon %s (wid 0x%lx)\n",
x11_get_window_name(tray_data.dpy, ti->wid, "<unknown>"), w));
/* no need to call embedde_update_positions(), as
* scrollbars_click(SB_WND_MAX) will call it */
/* XXX: maybe we need a different name for this
* routine instad of passing cryptinc constant? */
scrollbars_click(SB_WND_MAX);
tray_update_window_props();
dump_tray_status();
}
/* Track icon visibility state changes */
void icon_track_visibility_changes(Window w)
{
struct TrayIcon *ti;
int mapped;
/* Ignore false alarms */
if ((ti = icon_list_find(w)) == NULL || !ti->is_xembed_supported) return;
mapped = xembed_get_mapped_state(ti);
LOG_TRACE(("xembed_is_mapped(0x%lx) = %u\n", w, mapped));
LOG_TRACE(("is_visible = %u\n", ti->is_visible));
#ifdef DEBUG
x11_dump_win_info(tray_data.dpy, ti->wid);
#endif
/* Nothing has changed */
if (mapped == ti->is_visible) return;
ti->is_visible = mapped;
LOG_INFO(("%s icon 0x%lx\n", mapped ? "showing" : "hiding", w));
if (mapped) { /* Icon has become mapped and is listed as hidden. Show this
icon. */
embedder_reset_size(ti);
if (!layout_add(ti)) {
xembed_set_mapped_state(ti, False);
return;
}
embedder_show(ti);
} else { /* Icon has become unmapped and is listed as visible. Hide this
icon. */
layout_remove(ti);
embedder_hide(ti);
}
embedder_update_positions(False);
tray_update_window_props();
}
/* helper to identify invalid icons */
int find_invalid_icons(struct TrayIcon *ti)
{
return ti->is_invalid;
}
#ifdef _ST_WITH_NATIVE_KDE
/* Find newly available KDE icons and add them as necessary */
/* TODO: move to kde_tray.c */
void kde_icons_update()
{
unsigned long list_len, i;
Window *kde_tray_icons;
if (tray_data.kde_tray_old_mode
|| !x11_get_root_winlist_prop(tray_data.dpy,
tray_data.xa_kde_net_system_tray_windows,
(unsigned char **)&kde_tray_icons, &list_len)) {
return;
}
for (i = 0; i < list_len; i++)
/* If the icon is not None and is non old, try to add it
* (if the icon is already there, nothing is gonna happen). */
if (kde_tray_icons[i] != None
&& !kde_tray_is_old_icon(kde_tray_icons[i])) {
LOG_TRACE(("found (possibly unembedded) KDE icon %s (wid 0x%lx)\n",
x11_get_window_name(
tray_data.dpy, kde_tray_icons[i], "<unknown>"),
kde_tray_icons[i]));
add_icon(kde_tray_icons[i], CM_KDE);
}
XFree(kde_tray_icons);
}
#endif
void find_unmanaged_chromium_icons()
{
unsigned int n;
Window *topwins, dummy;
XQueryTree(tray_data.dpy, DefaultRootWindow(tray_data.dpy), &dummy, &dummy,
&topwins, &n);
if (topwins == NULL) return;
// Find toplevel windows (unmanaged) that have:
//_NET_WM_WINDOW_TYPE(ATOM) == _NET_WM_WINDOW_TYPE_NOTIFICATION
// CHROMIUM_COMPOSITE_WINDOW(CARDINAL) == 1
Atom win_type = XInternAtom(tray_data.dpy, "_NET_WM_WINDOW_TYPE", False);
Atom win_type_notif =
XInternAtom(tray_data.dpy, "_NET_WM_WINDOW_TYPE_NOTIFICATION", False);
Atom chrom_composite =
XInternAtom(tray_data.dpy, "CHROMIUM_COMPOSITE_WINDOW", False);
for (unsigned int i = 0; i < n; i++) {
Atom *aitem;
unsigned int *citem;
unsigned long nitems;
int rc = False;
Bool ok = True;
LOG_TRACE(("Chromium hack - checking window 0x%lx\n", topwins[i]));
rc = x11_get_window_prop32(tray_data.dpy, topwins[i], win_type,
XA_ATOM, (unsigned char **)&aitem, &nitems);
LOG_TRACE(("Chromium hack, ret: %x %x=%x %lx\n", x11_ok(), rc, Success,
nitems));
if (!(x11_ok() && rc == SUCCESS && nitems == 1)) continue;
LOG_TRACE(("Chromium hack, comp: %lx=%lx\n", aitem[0], win_type_notif));
ok = aitem[0] == win_type_notif;
XFree(aitem);
if (!ok) continue;
LOG_TRACE(("Found toplevel notification window 0x%lx\n", topwins[i]));
rc = x11_get_window_prop32(tray_data.dpy, topwins[i], chrom_composite,
XA_CARDINAL, (unsigned char **)&citem, &nitems);
LOG_TRACE(("Chromium hack, ret: %x %x=%x %lx\n", x11_ok(), rc, Success,
nitems));
if (!(x11_ok() && rc == SUCCESS && nitems == 1)) continue;
LOG_TRACE(("Chromium hack, comp: %lx=%lx\n", aitem[0], win_type_notif));
ok = citem[0] == 1;
XFree(citem);
if (!ok) continue;
LOG_TRACE(("Found chromium composite window 0x%ld\n", topwins[i]));
add_icon(topwins[i], CM_FDO);
}
XFree(topwins);
}
#define PT_MASK_SB (1L << 0)
#define PT_MASK_ALL PT_MASK_SB
/* Perform several periodic tasks */
void perform_periodic_tasks(int mask)
{
struct TrayIcon *ti;
/* 1. Remove all invalid icons */
while ((ti = icon_list_forall(&find_invalid_icons)) != NULL) {
LOG_TRACE(("icon 0x%lx is invalid; removing\n", ti->wid));
remove_icon(ti->wid);
}
/* 2. Print tray status if asked to */
if (tray_status_requested) dump_tray_status();
/* 3. KLUDGE to fix window size on (buggy?) WMs */
if (settings.kludge_flags & KLUDGE_FIX_WND_SIZE) {
/* KLUDGE TODO: resolve */
XWindowAttributes xwa;
XGetWindowAttributes(tray_data.dpy, tray_data.tray, &xwa);
if (!tray_data.is_reparented
&& (xwa.width != tray_data.xsh.width
|| xwa.height != tray_data.xsh.height)) {
LOG_TRACE(("KLUDGE: fixing tray window size (current: %dx%d, "
"required: %dx%d)\n",
xwa.width, xwa.height, tray_data.xsh.width,
tray_data.xsh.height));
tray_update_window_props();
}
}
/* 4. run scrollbars periodic tasks */
if (mask & PT_MASK_SB) scrollbars_periodic_tasks();
}
/**********************
* Event handlers
**********************/
void expose(XExposeEvent ev)
{
if (ev.window == tray_data.tray && settings.parent_bg && ev.count == 0)
tray_refresh_window(False);
}
void visibility_notify(XVisibilityEvent) {}
void property_notify(XPropertyEvent ev)
{
#define TRACE_PROPS
#if defined(DEBUG) && defined(TRACE_PROPS)
char *atom_name;
atom_name = XGetAtomName(tray_data.dpy, ev.atom);
LOG_TRACE(("atom = %s\n", atom_name));
XFree(atom_name);
#endif
/* React on wallpaper change */
if (ev.atom == tray_data.xa_xrootpmap_id
|| ev.atom == tray_data.xa_xsetroot_id) {
if (settings.transparent) tray_update_bg(True);
if (settings.parent_bg || settings.transparent || settings.fuzzy_edges)
tray_refresh_window(True);
}
#ifdef _ST_WITH_NATIVE_KDE
/* React on change of list of KDE icons */
if (ev.atom == tray_data.xa_kde_net_system_tray_windows) {
if (tray_data.is_active)
kde_icons_update();
else
LOG_TRACE(("not updating KDE icons list: tray is not active\n"));
kde_tray_update_old_icons(tray_data.dpy);
}
#endif
/* React on WM (re)starts */
if (ev.atom
== XInternAtom(tray_data.dpy, _NET_SUPPORTING_WM_CHECK, False)) {
#ifdef DEBUG
ewmh_list_supported_atoms(tray_data.dpy);
#endif
tray_set_wm_hints();
#ifdef _ST_WITH_NATIVE_KDE
kde_tray_update_fallback_mode(tray_data.dpy);
#endif
}
/* React on _XEMBED_INFO changes of embedded icons
* (currently used to track icon visibility status) */
if (ev.atom == tray_data.xembed_data.xa_xembed_info) {
icon_track_visibility_changes(ev.window);
}
if (ev.atom == tray_data.xa_net_client_list) {
Window *windows;
unsigned long nwindows, rc, i;
rc = x11_get_root_winlist_prop(tray_data.dpy,
tray_data.xa_net_client_list, (unsigned char **)&windows,
&nwindows);
if (x11_ok() && rc) {
tray_data.is_reparented = True;
for (i = 0; i < nwindows; i++)
if (windows[i] == tray_data.tray) {
tray_data.is_reparented = False;
break;
}
}
if (nwindows) XFree(windows);
LOG_TRACE((
"tray was %sreparented\n", tray_data.is_reparented ? "" : "not "));
}
}
void reparent_notify(XReparentEvent ev)
{
struct TrayIcon *ti;
ti = icon_list_find(ev.window);
if (ti == NULL) return;
/* Reparenting out of the tray is one of non-destructive
* ways to end XEMBED protocol (see spec) */
if (ti->is_embedded && ti->mid_parent != ev.parent) {
LOG_TRACE(("will now unembed 0x%lx\n", ti->wid));
#ifdef DEBUG
print_icon_data(ti);
x11_dump_win_info(tray_data.dpy, ev.parent);
#endif
remove_icon(ev.window);
}
}
void client_message(XClientMessageEvent ev)
{
int cmode = CM_FDO;
struct TrayIcon *ti;
#ifdef DEBUG
/* Print neat message(s) about this event to aid debugging */
char *msg_type_name;
msg_type_name = XGetAtomName(tray_data.dpy, ev.message_type);
if (msg_type_name != NULL) {
LOG_TRACE(("message \"%s\"\n", msg_type_name));
XFree(msg_type_name);
}
if (ev.message_type == tray_data.xa_wm_protocols) {
msg_type_name = XGetAtomName(tray_data.dpy, ev.data.l[0]);
if (msg_type_name != NULL) {
LOG_TRACE(("WM_PROTOCOLS message type: %s\n", msg_type_name));
XFree(msg_type_name);
}
}
#endif
/* Graceful exit */
if (ev.message_type == tray_data.xa_wm_protocols
&& (unsigned long) ev.data.l[0] == tray_data.xa_wm_delete_window
&& ev.window == tray_data.tray) {
LOG_TRACE(("got WM_DELETE message, will now exit\n"));
exit(0); // atexit will call cleanup()
}
/* Handle _NET_WM_PING */
if (ev.message_type == tray_data.xa_wm_protocols
&& (unsigned long) ev.data.l[0] == tray_data.xa_net_wm_ping
&& ev.window == tray_data.tray) {
LOG_TRACE(("got WM_PING message, sending it back\n"));
XEvent reply;
reply.xclient = ev;
reply.xclient.window = DefaultRootWindow(tray_data.dpy);
XSendEvent(tray_data.dpy, DefaultRootWindow(tray_data.dpy), False,
(SubstructureNotifyMask | SubstructureRedirectMask), &reply);
}
/* Handle _NET_SYSTEM_TRAY_* messages */
if (ev.message_type == tray_data.xa_tray_opcode && tray_data.is_active) {
LOG_TRACE(("this is the _NET_SYSTEM_TRAY_OPCODE(%lu) message\n",
ev.data.l[1]));
switch (ev.data.l[1]) {
/* This is the starting point of NET SYSTEM TRAY protocol */
case SYSTEM_TRAY_REQUEST_DOCK:
LOG_TRACE(
("dockin' requested by window 0x%lx, serving in a moment\n",
ev.data.l[2]));
#ifdef _ST_WITH_NATIVE_KDE
if (kde_tray_check_for_icon(tray_data.dpy, ev.data.l[2]))
cmode = CM_KDE;
if (kde_tray_is_old_icon(ev.data.l[2]))
kde_tray_old_icons_remove(ev.data.l[2]);
#endif
add_icon(ev.data.l[2], cmode);
break;
/* We ignore these messages, since we do not show
* any baloons anyways */
case SYSTEM_TRAY_BEGIN_MESSAGE:
case SYSTEM_TRAY_CANCEL_MESSAGE: break;
/* Below are special cases added by this implementation */
/* STALONETRAY_TRAY_DOCK_CONFIRMED is sent by stalonetray
* to itself. (see embed.c) */
case STALONE_TRAY_DOCK_CONFIRMED:
ti = icon_list_find(ev.data.l[2]);
if (ti != NULL && !ti->is_embedded) {
ti->is_embedded = True;
LOG_TRACE(("embedding confirmed for icon 0x%lx\n", ti->wid));
#ifdef DEBUG
dump_tray_status();
#endif
}
tray_update_window_props();
break;
/* Dump tray status on request */
case STALONE_TRAY_STATUS_REQUESTED: dump_tray_status(); break;
/* Find icon and scroll to it if necessary */
case STALONE_TRAY_REMOTE_CONTROL:
ti = icon_list_find(ev.window);
if (ti == NULL) break;
scrollbars_scroll_to(ti);
#if 0
/* Quick hack */
{
Window icon = ev.window;
int rc;
int x = ev.data.l[3], y = ev.data.l[4], depth = 0, idummy, i;
int btn = ev.data.l[2];
Window win, root;
unsigned int udummy, w, h;
XGetGeometry(tray_data.dpy, icon, &root,
&idummy, &idummy,
&w, &h, &udummy, &udummy);
LOG_TRACE(("wid=0x%x w=%d h=%d\n", icon, w, h));
x = (x == REMOTE_CLICK_POS_DEFAULT) ? w / 2 : x;
y = (y == REMOTE_CLICK_POS_DEFAULT) ? h / 2 : y;
/* 3.2. Find subwindow to execute click on */
win = x11_find_subwindow_at(tray_data.dpy, icon, &x, &y, depth);
/* 3.3. Send mouse click(s) to target */
LOG_TRACE(("wid=0x%x btn=%d x=%d y=%d\n",
win, btn, x, y));
#define SEND_BTN_EVENT(press, time) \
do { \
x11_send_button(tray_data.dpy, /* dispslay */ \
press, /* event type */ \
win, /* target window */ \
root, /* root window */ \
time, /* time */ \
btn, /* button */ \
Button1Mask << (btn - 1), /* state mask */ \
x, /* x coord (relative) */ \
y); /* y coord (relative) */ \
} while (0)
for (i = 0; i < ev.data.l[0]; i++) {
SEND_BTN_EVENT(1, x11_get_server_timestamp(tray_data.dpy, tray_data.tray));
my_usleep(250);
SEND_BTN_EVENT(0, x11_get_server_timestamp(tray_data.dpy, tray_data.tray));
}
#undef SEND_BTN_EVENT
}
#endif
break;
default: break;
}
}
#ifdef DEBUG
if (ev.message_type == tray_data.xa_tray_opcode && !tray_data.is_active)
LOG_TRACE(("ignoring _NET_SYSTEM_TRAY_OPCODE(%d) message because "
"tray is not active\n",
tray_data.is_active));
#endif
}
void destroy_notify(XDestroyWindowEvent ev)
{
if (!tray_data.is_active && ev.window == tray_data.old_selection_owner) {
/* Old tray selection owner was destroyed. Take over selection
* ownership. */
tray_acquire_selection();
} else if (ev.window != tray_data.tray) {
/* Try to remove icon from the tray */
remove_icon(ev.window);
#ifdef _ST_WITH_NATIVE_KDE
} else if (kde_tray_is_old_icon(ev.window)) {
/* Since X Server may reuse window ids, remove ev.window
* from the list of old KDE icons */
kde_tray_old_icons_remove(ev.window);
#endif
}
}
void configure_notify(XConfigureEvent ev)
{
struct TrayIcon *ti;
struct Point sz;
XWindowAttributes xwa;
if (ev.window == tray_data.tray) {
/* Tray window was resized */
/* TODO: distinguish between synthetic and real configure notifies */
/* TODO: catch rejected configure requests */
/* XXX: Geometry stuff is a mess. Geometry
* is specified in slots, but almost always is
* stored in pixels... */
LOG_TRACE(("tray window geometry from event: %ux%u+%d+%d\n", ev.width,
ev.height, ev.x, ev.y));
/* Sometimes, configure notifies come too late, so we fetch real
* geometry ourselves */
XGetWindowAttributes(tray_data.dpy, tray_data.tray, &xwa);
x11_get_window_abs_coords(
tray_data.dpy, tray_data.tray, &tray_data.xsh.x, &tray_data.xsh.y);
LOG_TRACE(("tray window geometry from X11 calls: %dx%d+%d+%d\n",
xwa.width, xwa.height, tray_data.xsh.x, tray_data.xsh.y));
tray_data.xsh.width = xwa.width;
tray_data.xsh.height = xwa.height;
/* Update icons positions */
/* XXX: internal API is bad. example below */
icon_list_forall(&layout_translate_to_window);
embedder_update_positions(True);
/* Adjust window background if necessary */
tray_update_bg(False);
tray_refresh_window(True);
tray_update_window_strut();
scrollbars_update();
} else if ((ti = icon_list_find(ev.window))
!= NULL) { /* Some icon has resized its window */
/* KDE icons are not allowed to change their size. Reset icon size. */
if (ti->cmode == CM_KDE
|| settings.kludge_flags & KLUDGE_FORCE_ICONS_SIZE) {
embedder_reset_size(ti);
return;
}
if (settings.kludge_flags & KLUDGE_FORCE_ICONS_SIZE) return;
/* Get new window size */
if (!x11_get_window_size(tray_data.dpy, ti->wid, &sz.x, &sz.y)) {
embedder_unembed(ti);
return;
}
LOG_TRACE(("icon 0x%lx was resized, new size: %ux%u, old size: %ux%u\n",
ev.window, sz.x, sz.y, ti->l.wnd_sz.x, ti->l.wnd_sz.y));
/* Check if the size has really changed */
if (sz.x == ti->l.wnd_sz.x && sz.y == ti->l.wnd_sz.y) return;
ti->l.wnd_sz = sz;
ti->is_resized = True;
/* Do the job */
layout_handle_icon_resize(ti);
embedder_refresh(ti);
#ifdef DEBUG
print_icon_data(ti);
#endif
embedder_update_positions(False);
tray_update_window_props();
#ifdef DEBUG
dump_tray_status();
#endif
}
}
void selection_clear(XSelectionClearEvent ev)
{
/* Is it _NET_SYSTEM_TRAY selection? */
if (ev.selection == tray_data.xa_tray_selection) {
/* Is it us who has lost the selection */
if (ev.window == tray_data.tray) {
LOG_INFO(("another tray detected; deactivating\n"));
tray_data.is_active = False;
tray_data.old_selection_owner =
XGetSelectionOwner(tray_data.dpy, tray_data.xa_tray_selection);
if (!x11_ok()) {
LOG_INFO(("could not find proper new tray; reactivating\n"));
tray_acquire_selection();
};
LOG_TRACE(("new selection owner is 0x%lx\n",
tray_data.old_selection_owner));
XSelectInput(tray_data.dpy, tray_data.old_selection_owner,
StructureNotifyMask);
return;
} else if (!tray_data.is_active) {
/* Someone else has lost selection and tray is not active --- take
* over the selection */
LOG_INFO(("another tray exited; reactivating\n"));
tray_acquire_selection();
} else {
/* Just in case */
LOG_TRACE(("WEIRD: tray is active and someone else has lost tray "
"selection\n"));
}
}
}
void map_notify(XMapEvent ev)
{
#ifdef _ST_WITH_NATIVE_KDE
/* Legacy scheme to handle KDE icons */
if (tray_data.kde_tray_old_mode) {
struct TrayIcon *ti = icon_list_find_ex(ev.window);
if (ti == NULL) {
Window w = kde_tray_find_icon(tray_data.dpy, ev.window);
if (w != None) {
LOG_TRACE(("Legacy scheme for KDE icons: detected KDE icon "
"0x%lx. Adding.\n",
w));
add_icon(w, CM_KDE);
/* TODO: remove some properties to trick ion3 so that it no
* longer thinks that w is a toplevel. Candidates for removal:
* - WM_STATE */
}
}
}
#else
(void) ev; /* unused */
#endif
}
void unmap_notify(XUnmapEvent ev)
{
struct TrayIcon *ti;
ti = icon_list_find(ev.window);
if (ti != NULL && !ti->is_invalid) {
/* KLUDGE! sometimes icons occasionally
* unmap their windows, but do _not_ destroy
* them. We map those windows back */
/* XXX: not root caused */
LOG_TRACE(("Unmap icons KLUDGE executed for 0x%lx\n", ti->wid));
XMapRaised(tray_data.dpy, ti->wid);
if (!x11_ok()) ti->is_invalid = True;
}
}
/*********************************************************/
/* main() for usual operation */
int tray_main(int argc, char **argv)
{
XEvent ev;
/* Interpret those settings that need an open display */
interpret_settings();
#ifdef DEBUG
ewmh_list_supported_atoms(tray_data.dpy);
#endif
xinerama_init(tray_data.dpy);
xinerama_update_geometry();
/* Create and show tray window */
tray_create_window(argc, argv);
tray_acquire_selection();
tray_show_window();
#ifdef _ST_WITH_NATIVE_KDE
kde_tray_init(tray_data.dpy);
#endif
xembed_init();
#ifdef _ST_WITH_NATIVE_KDE
kde_icons_update();
#endif
find_unmanaged_chromium_icons();
/* Main event loop */
while ("my guitar gently wheeps") {
/* This is ugly and extra dependency. But who cares?
* Rationale: we want to block unless absolutely needed.
* This way we ensure that stalonetray does not show up
* in powertop (i.e. does not eat unnecessary power and
* CPU cycles)
* Drawback: handling of signals is very limited. XNextEvent()
* does not if signal occurs. This means that graceful
* exit on e.g. Ctrl-C cannot be implemented without hacks. */
while (XPending(tray_data.dpy)
|| tray_data.scrollbars_data.scrollbar_down == -1) {
XNextEvent(tray_data.dpy, &ev);
xembed_handle_event(ev);
scrollbars_handle_event(ev);
switch (ev.type) {
case VisibilityNotify:
LOG_TRACE(("VisibilityNotify (0x%lx, state=%d)\n",
ev.xvisibility.window, ev.xvisibility.state));
visibility_notify(ev.xvisibility);
break;
case Expose:
LOG_TRACE(("Expose (0x%lx)\n", ev.xexpose.window));
expose(ev.xexpose);
break;
case PropertyNotify:
LOG_TRACE(("PropertyNotify(0x%lx)\n", ev.xproperty.window));
property_notify(ev.xproperty);
break;
case DestroyNotify:
LOG_TRACE(("DestroyNotify(0x%lx)\n", ev.xdestroywindow.window));
destroy_notify(ev.xdestroywindow);
break;
case ClientMessage:
LOG_TRACE(("ClientMessage(from 0x%lx?)\n", ev.xclient.window));
client_message(ev.xclient);
break;
case ConfigureNotify:
LOG_TRACE(("ConfigureNotify(0x%lx)\n", ev.xconfigure.window));
configure_notify(ev.xconfigure);
break;
case MapNotify:
LOG_TRACE(("MapNotify(0x%lx)\n", ev.xmap.window));
map_notify(ev.xmap);
break;
case ReparentNotify:
LOG_TRACE(("ReparentNotify(0x%lx to 0x%lx)\n",
ev.xreparent.window, ev.xreparent.parent));
reparent_notify(ev.xreparent);
break;
case SelectionClear:
LOG_TRACE(("SelectionClear (0x%lx has lost selection)\n",
ev.xselectionclear.window));
selection_clear(ev.xselectionclear);
break;
case SelectionNotify: LOG_TRACE(("SelectionNotify\n")); break;
case SelectionRequest:
LOG_TRACE(("SelectionRequest (from 0x%lx to 0x%lx)\n",
ev.xselectionrequest.requestor,
ev.xselectionrequest.owner));
break;
case UnmapNotify:
LOG_TRACE(("UnmapNotify(0x%lx)\n", ev.xunmap.window));
unmap_notify(ev.xunmap);
break;
default:
#if defined(DEBUG) && defined(_ST_TRACE_EVENTS)
LOG_TRACE(("Unhandled event: %s, serial: %ld, window: 0x%lx\n",
x11_event_names[ev.type], ev.xany.serial, ev.xany.window));
#endif
break;
}
if (tray_data.terminated) goto bailout;
/* Perform all periodic tasks but for scrollbars */
perform_periodic_tasks(PT_MASK_ALL & (~PT_MASK_SB));
}
perform_periodic_tasks(PT_MASK_ALL);
my_usleep(500000L);
}
bailout:
LOG_TRACE(("Clean exit\n"));
return 0;
}
/* main() for controlling stalonetray remotely */
int remote_main(int, char **)
{
Window tray, icon = None;
int rc;
int x, y, depth = 0, idummy, i;
Window win, root;
unsigned int udummy, w, h;
tray_init_selection_atoms();
tray_create_phony_window();
LOG_TRACE(
("name=\"%s\" btn=%d cnt=%d x=%d y=%d\n", settings.remote_click_name,
settings.remote_click_btn, settings.remote_click_cnt,
settings.remote_click_pos.x, settings.remote_click_pos.y));
tray = XGetSelectionOwner(tray_data.dpy, tray_data.xa_tray_selection);
if (tray == None) return 255;
/* 1. find window matching requested name */
icon = x11_find_subwindow_by_name(
tray_data.dpy, tray, settings.remote_click_name);
if (icon == None) return 127;
/* 2. form a message to tray requesting it to show the icon */
rc = x11_send_client_msg32(tray_data.dpy, /* display */
tray, /* destination */
icon, /* window */
tray_data.xa_tray_opcode, /* atom */
settings.remote_click_cnt, /* data0 */
STALONE_TRAY_REMOTE_CONTROL, /* data1 */
settings.remote_click_btn, /* data2 */
settings.remote_click_pos.x, /* data3 */
settings.remote_click_pos.y /* data4 */
);
if (!rc) return 63;
/* 3. Execute the click */
/* 3.1. Sort out click position */
XGetGeometry(tray_data.dpy, icon, &root, &idummy, &idummy, &w, &h, &udummy,
&udummy);
LOG_TRACE(("wid=0x%lx w=%d h=%d\n", icon, w, h));
x = (settings.remote_click_pos.x == REMOTE_CLICK_POS_DEFAULT)
? w / 2
: (unsigned int) settings.remote_click_pos.x;
y = (settings.remote_click_pos.y == REMOTE_CLICK_POS_DEFAULT)
? h / 2
: (unsigned int) settings.remote_click_pos.y;
/* 3.2. Find subwindow to execute click on */
win = x11_find_subwindow_at(tray_data.dpy, icon, &x, &y, depth);
/* 3.3. Send mouse click(s) to target */
LOG_TRACE(
("wid=0x%lx btn=%d x=%d y=%d\n", win, settings.remote_click_btn, x, y));
#define SEND_BTN_EVENT(press, time) \
do { \
x11_send_button(tray_data.dpy, /* dispslay */ \
press, /* event type */ \
win, /* target window */ \
root, /* root window */ \
time, /* time */ \
settings.remote_click_btn, /* button */ \
Button1Mask << (settings.remote_click_btn - 1), /* state mask */ \
x, /* x coord (relative) */ \
y); /* y coord (relative) */ \
} while (0)
for (i = 0; i < settings.remote_click_cnt; i++) {
SEND_BTN_EVENT(
1, x11_get_server_timestamp(tray_data.dpy, tray_data.tray));
my_usleep(250);
SEND_BTN_EVENT(
0, x11_get_server_timestamp(tray_data.dpy, tray_data.tray));
}
#undef SEND_BTN_EVENT
return 0;
}
/* main() */
int main(int argc, char **argv)
{
/* Read settings */
tray_init();
read_settings(argc, argv);
/* Register cleanup and signal handlers */
atexit(cleanup);
signal(SIGUSR1, &request_tray_status_on_signal);
#ifdef _ST_EXIT_GRACEFULLY
signal(SIGINT, &exit_on_signal);
signal(SIGTERM, &exit_on_signal);
signal(SIGPIPE, &exit_on_signal);
#endif
/* Open display */
if ((tray_data.dpy = XOpenDisplay(settings.display_str)) == NULL)
DIE(("could not open display\n"));
else
LOG_TRACE(("Opened dpy %p\n", (void *) tray_data.dpy));
#ifdef _ST_EXIT_GRACEFULLY
if ((async_dpy = XOpenDisplay(settings.display_str)) == NULL)
DIE(("could not open display\n"));
else
LOG_TRACE(("Opened async dpy %p\n", async_dpy));
#endif
if (settings.xsync) XSynchronize(tray_data.dpy, True);
x11_trap_errors();
/* Execute proper main() function */
if (settings.remote_click_name != NULL)
return remote_main(argc, argv);
else
return tray_main(argc, argv);
}
================================================
FILE: src/meson.build
================================================
project_sources += [
'src/debug.c',
'src/embed.c',
'src/icons.c',
'src/image.c',
'src/layout.c',
'src/main.c',
'src/scrollbars.c',
'src/settings.c',
'src/tray.c',
'src/wmh.c',
'src/xembed.c',
'src/xinerama.c',
'src/xutils.c',
]
if get_option('native_kde').enabled()
project_sources += ['src/kde_tray.c']
endif
================================================
FILE: src/scrollbars.c
================================================
/* ************************************
* vim:tabstop=4:shiftwidth=4:cindent:fen:fdm=syntax
* tray.c
* Mon, 09 Mar 2009 12:38:30 -0400
* ************************************
* scrollbars functions
* ************************************/
#include <limits.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <X11/Xmd.h>
#include <X11/Xutil.h>
#include "embed.h"
#include "tray.h"
#include "xutils.h"
void scrollbars_init()
{
tray_data.scrollbars_data.scroll_pos.x = 0;
tray_data.scrollbars_data.scroll_pos.y = 0;
tray_data.scrollbars_data.scroll_base.x = 0;
tray_data.scrollbars_data.scroll_base.y = 0;
tray_data.scrollbars_data.scrollbar_down = -1;
}
void scrollbars_create()
{
int i;
/*#define DEBUG_SCROLLBAR_POSITIONS*/
if (settings.scrollbars_mode & SB_MODE_VERT) {
tray_data.scrollbars_data.scrollbar[SB_WND_TOP] =
XCreateSimpleWindow(tray_data.dpy, tray_data.tray, 0, 0, 1, 1, 0,
#ifdef DEBUG_SCROLLBAR_POSITIONS
0xff00ff, 0xff00ff);
#else
settings.bg_color.pixel, settings.bg_color.pixel);
#endif
tray_data.scrollbars_data.scrollbar[SB_WND_BOT] =
XCreateSimpleWindow(tray_data.dpy, tray_data.tray, 0, 0, 1, 1, 0,
#ifdef DEBUG_SCROLLBAR_POSITIONS
0xffff00, 0xffff00);
#else
settings.bg_color.pixel, settings.bg_color.pixel);
#endif
} else {
tray_data.scrollbars_data.scrollbar[SB_WND_TOP] = None;
tray_data.scrollbars_data.scrollbar[SB_WND_BOT] = None;
}
if (settings.scrollbars_mode & SB_MODE_HORZ) {
tray_data.scrollbars_data.scrollbar[SB_WND_LFT] =
XCreateSimpleWindow(tray_data.dpy, tray_data.tray, 0, 0, 1, 1, 0,
#ifdef DEBUG_SCROLLBAR_POSITIONS
0x00ff00, 0x00ff00);
#else
settings.bg_color.pixel, settings.bg_color.pixel);
#endif
tray_data.scrollbars_data.scrollbar[SB_WND_RHT] =
XCreateSimpleWindow(tray_data.dpy, tray_data.tray, 0, 0, 1, 1, 0,
#ifdef DEBUG_SCROLLBAR_POSITIONS
0x00ffff, 0x00ffff);
#else
settings.bg_color.pixel, settings.bg_color.pixel);
#endif
} else {
tray_data.scrollbars_data.scrollbar[SB_WND_LFT] = None;
tray_data.scrollbars_data.scrollbar[SB_WND_RHT] = None;
}
scrollbars_update();
if (settings.scroll_everywhere && settings.scrollbars_mode != SB_MODE_NONE) {
XGrabButton(tray_data.dpy, Button5, AnyModifier, tray_data.tray, False,
ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None);
XGrabButton(tray_data.dpy, Button4, AnyModifier, tray_data.tray, False,
ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None);
}
for (i = 0; i < SB_WND_MAX; i++)
if (tray_data.scrollbars_data.scrollbar[i] != None) {
#ifndef DEBUG_SCROLLBAR_POSITIONS
XSetWindowBackgroundPixmap(tray_data.dpy,
tray_data.scrollbars_data.scrollbar[i], ParentRelative);
#endif
XMapRaised(tray_data.dpy, tray_data.scrollbars_data.scrollbar[i]);
XSelectInput(tray_data.dpy, tray_data.scrollbars_data.scrollbar[i],
ButtonPressMask | ButtonReleaseMask | ButtonMotionMask
| EnterWindowMask | LeaveWindowMask);
}
}
int scrollbars_refresh(int exposures)
{
int i;
for (i = 0; i < SB_WND_MAX; i++)
if (tray_data.scrollbars_data.scrollbar[i] != None)
x11_refresh_window(tray_data.dpy,
tray_data.scrollbars_data.scrollbar[i],
tray_data.scrollbars_data.scrollbar_xsh[i].width,
tray_data.scrollbars_data.scrollbar_xsh[i].height, exposures);
return SUCCESS;
}
int scrollbars_update()
{
int offset, i;
static int initialized = 0;
XSizeHints scrollbar_xsh_local[SB_WND_MAX];
if (settings.scrollbars_mode & SB_MODE_HORZ) {
offset =
(settings.vertical && (settings.scrollbars_mode & SB_MODE_VERT))
? settings.scrollbars_size
: 0;
scrollbar_xsh_local[SB_WND_LFT].x = 0;
scrollbar_xsh_local[SB_WND_LFT].y = offset;
scrollbar_xsh_local[SB_WND_LFT].width = settings.scrollbars_size;
scrollbar_xsh_local[SB_WND_LFT].height =
tray_data.xsh.height - offset * 2;
scrollbar_xsh_local[SB_WND_RHT] = scrollbar_xsh_local[SB_WND_LFT];
scrollbar_xsh_local[SB_WND_RHT].x =
tray_data.xsh.width - settings.scrollbars_size;
}
if (settings.scrollbars_mode & SB_MODE_VERT) {
offset =
(settings.vertical && (settings.scrollbars_mode & SB_MODE_HORZ))
? settings.scrollbars_size
: 0;
scrollbar_xsh_local[SB_WND_TOP].x = offset;
scrollbar_xsh_local[SB_WND_TOP].y = 0;
scrollbar_xsh_local[SB_WND_TOP].width =
tray_data.xsh.width - offset * 2;
scrollbar_xsh_local[SB_WND_TOP].height = settings.scrollbars_size;
scrollbar_xsh_local[SB_WND_BOT] = scrollbar_xsh_local[SB_WND_TOP];
scrollbar_xsh_local[SB_WND_BOT].y =
tray_data.xsh.height - settings.scrollbars_size;
}
for (i = 0; i < SB_WND_MAX; i++)
if (tray_data.scrollbars_data.scrollbar[i] != None
&& (!initialized
|| (scrollbar_xsh_local[i].x
!= tray_data.scrollbars_data.scrollbar_xsh[i].x
|| scrollbar_xsh_local[i].y
!= tray_data.scrollbars_data.scrollbar_xsh[i].y
|| scrollbar_xsh_local[i].width
!= tray_data.scrollbars_data.scrollbar_xsh[i].width
|| scrollbar_xsh_local[i].height
!= tray_data.scrollbars_data.scrollbar_xsh[i]
.height))) {
XMoveResizeWindow(tray_data.dpy,
tray_data.scrollbars_data.scrollbar[i],
scrollbar_xsh_local[i].x, scrollbar_xsh_local[i].y,
scrollbar_xsh_local[i].width, scrollbar_xsh_local[i].height);
tray_data.scrollbars_data.scrollbar_xsh[i] =
scrollbar_xsh_local[i];
}
initialized = 1;
return x11_ok();
}
int scrollbars_get_id(Window wid, int x, int y)
{
int i;
for (i = 0; i < SB_WND_MAX; i++)
if (wid == tray_data.scrollbars_data.scrollbar[i] && 0 <= x && 0 <= y
&& x < tray_data.scrollbars_data.scrollbar_xsh[i].width
&& y < tray_data.scrollbars_data.scrollbar_xsh[i].height) {
return i;
}
return -1;
}
void scrollbars_validate_scroll_pos()
{
int layout_width, layout_height;
int base_width, base_height;
struct Point max_scroll_pos;
layout_get_size(&layout_width, &layout_height);
tray_calc_tray_area_size(
tray_data.xsh.width, tray_data.xsh.height, &base_width, &base_height);
max_scroll_pos.x = layout_width - base_width;
max_scroll_pos.y = layout_height - base_height;
val_range(max_scroll_pos.x, 0, INT_MAX);
val_range(max_scroll_pos.y, 0, INT_MAX);
LOG_TRACE(("computed max scroll position: %dx%d\n", max_scroll_pos.x,
max_scroll_pos.y));
val_range(tray_data.scrollbars_data.scroll_pos.x, 0, max_scroll_pos.x);
val_range(tray_data.scrollbars_data.scroll_pos.y, 0, max_scroll_pos.y);
}
int scrollbars_click(int id)
{
/* TODO: implement scroll gravity (i.e. scroll weel must scroll in
* direction that agrees with current tray orientation) */
/* TODO: scrollbars_inc must be settable via cmdline */
/* last entry is for action that just sanitizes current scroll
* position after icon removal */
static struct Point scrollbars_deltas[SB_WND_MAX + 1] = {
{0, -1}, {0, 1}, {-1, 0}, {1, 0}, {0, 0}};
tray_data.scrollbars_data.scroll_pos.x +=
(settings.icon_gravity & GRAV_W ? 1 : -1) * scrollbars_deltas[id].x
* settings.scrollbars_inc;
tray_data.scrollbars_data.scroll_pos.y +=
(settings.icon_gravity & GRAV_N ? 1 : -1) * scrollbars_deltas[id].y
* settings.scrollbars_inc;
scrollbars_validate_scroll_pos();
LOG_TRACE(("computed new scroll position: %dx%d\n",
tray_data.scrollbars_data.scroll_pos.x,
tray_data.scrollbars_data.scroll_pos.y));
icon_list_forall(&layout_translate_to_window);
embedder_update_positions(id != SB_WND_MAX);
return SUCCESS;
}
void scrollbars_handle_event(XEvent ev)
{
int id;
switch (ev.type) {
case EnterNotify:
case LeaveNotify:
LOG_TRACE(("%s, wid=0x%lx x=%d y=%d\n",
ev.type == EnterNotify ? "EnterNotify" : "LeaveNotify",
ev.xcrossing.window, ev.xcrossing.x, ev.xcrossing.y));
if (settings.scrollbars_highlight_color_str != NULL
&& (id = scrollbars_get_id(ev.xcrossing.window, 0, 0)) != -1) {
if (ev.type == EnterNotify)
scrollbars_highlight_on(id);
else
scrollbars_highlight_off(id);
}
break;
case ButtonPress:
LOG_TRACE(("ButtonPress, state=0x%x\n", ev.xbutton.state));
if (ev.xbutton.button == Button1
&& (id = scrollbars_get_id(
ev.xbutton.window, ev.xbutton.x, ev.xbutton.y))
!= -1) {
tray_data.scrollbars_data.scrollbar_down = id;
tray_data.scrollbars_data.scrollbar_repeat_active = 1;
tray_data.scrollbars_data.scrollbar_repeat_counter =
SCROLLBAR_REPEAT_COUNTDOWN_MAX_1ST;
tray_data.scrollbars_data.scrollbar_repeats_done = 0;
}
break;
case MotionNotify:
LOG_TRACE(("MotionNotify, state=0x%x\n", ev.xbutton.state));
tray_data.scrollbars_data.scrollbar_repeat_active =
(tray_data.scrollbars_data.scrollbar_down != -1
&& scrollbars_get_id(
ev.xmotion.window, ev.xmotion.x, ev.xmotion.y)
== tray_data.scrollbars_data.scrollbar_down);
break;
case ButtonRelease:
LOG_TRACE(("ButtonRelease, state=0x%x\n", ev.xbutton.state));
switch (ev.xbutton.button) {
case Button1:
if (tray_data.scrollbars_data.scrollbar_down != -1) {
#if 0
/* If no repeats were done, advance scroll position */
if ((scrollbars_get_id(ev.xbutton.window, ev.xbutton.x, ev.xbutton.y) != -1) &&
(tray_data.scrollbars_data.scrollbar_repeats_done == 0))
{
scrollbars_click(tray_data.scrollbars_data.scrollbar_down);
}
#endif
tray_data.scrollbars_data.scrollbar_down = -1;
tray_data.scrollbars_data.scrollbar_repeat_active = 0;
}
break;
case Button4:
if (settings.vertical && settings.scrollbars_mode & SB_MODE_VERT)
scrollbars_click(SB_WND_TOP);
else if (settings.scrollbars_mode & SB_MODE_HORZ)
scrollbars_click(SB_WND_LFT);
break;
case Button5:
if (settings.vertical && settings.scrollbars_mode & SB_MODE_VERT)
scrollbars_click(SB_WND_BOT);
else if (settings.scrollbars_mode & SB_MODE_HORZ)
scrollbars_click(SB_WND_RHT);
break;
default: break;
}
break;
}
}
void scrollbars_periodic_tasks()
{
if (tray_data.scrollbars_data.scrollbar_down != -1
&& tray_data.scrollbars_data.scrollbar_repeat_active) {
scrollbars_click(tray_data.scrollbars_data.scrollbar_down);
}
}
int scrollbars_scroll_to(struct TrayIcon *ti)
{
struct Rect tray_viewport_rect;
tray_viewport_rect.x = tray_data.scrollbars_data.scroll_base.x,
tray_viewport_rect.y = tray_data.scrollbars_data.scroll_base.y,
tray_calc_tray_area_size(tray_data.xsh.width, tray_data.xsh.height,
&tray_viewport_rect.w, &tray_viewport_rect.h);
/* Check if icon is already visible. If so, nothing needs to be done */
if (RECTS_ISECT(tray_viewport_rect, ti->l.icn_rect)) return SUCCESS;
/* Update scroll pos so that the icon is visible */
/* TODO: must be in separate function, with a reverse */
tray_data.scrollbars_data.scroll_pos.x =
(settings.icon_gravity & GRAV_W ? 1 : -1)
* (ti->l.icn_rect.x - tray_data.scrollbars_data.scroll_base.x);
tray_data.scrollbars_data.scroll_pos.y =
(settings.icon_gravity & GRAV_W ? 1 : -1)
* (ti->l.icn_rect.y - tray_data.scrollbars_data.scroll_base.y);
scrollbars_validate_scroll_pos();
LOG_TRACE(("computed required scroll position: %dx%d\n",
tray_data.scrollbars_data.scroll_pos.x,
tray_data.scrollbars_data.scroll_pos.y));
icon_list_forall(&layout_translate_to_window);
embedder_update_positions(True);
return SUCCESS;
}
int scrollbars_highlight_on(int id)
{
Window sb_wid;
sb_wid =
(0 <= id && id < 4) ? tray_data.scrollbars_data.scrollbar[id] : None;
if (sb_wid != None)
XSetWindowBackground(
tray_data.dpy, sb_wid, settings.scrollbars_highlight_color.pixel);
scrollbars_refresh(1);
return SUCCESS;
}
int scrollbars_highlight_off(int id)
{
Window sb_wid;
sb_wid =
(0 <= id && id < 4) ? tray_data.scrollbars_data.scrollbar[id] : None;
if (sb_wid != None)
XSetWindowBackgroundPixmap(tray_data.dpy, sb_wid, ParentRelative);
scrollbars_refresh(1);
return SUCCESS;
}
================================================
FILE: src/scrollbars.h
================================================
/* -------------------------------
* vim:tabstop=4:shiftwidth=4
* tray.h
* Mon, 09 Mar 2009 12:41:32 -0400
* -------------------------------
* Scrollbars data & interface
* -------------------------------*/
#ifndef _SCROLLBAR_H_
#define _SCROLLBAR_H_
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#define SB_WND_TOP 0
#define SB_WND_BOT 1
#define SB_WND_LFT 2
#define SB_WND_RHT 3
#define SB_WND_MAX 4
#define SB_MODE_NONE 0
#define SB_MODE_VERT 1
#define SB_MODE_HORZ 2
#define SCROLLBAR_REPEAT_COUNTDOWN_MAX_1ST 10
#define SCROLLBAR_REPEAT_COUNTDOWN_MAX_SUCC 5
/* Scrollbar data */
struct ScrollbarsData {
struct Point scroll_base; /* Base scroll position */
struct Point scroll_pos; /* Current scroll position */
Window scrollbar[SB_WND_MAX]; /* Window IDs of scrollbars */
XSizeHints
scrollbar_xsh[SB_WND_MAX]; /* Cached window sizes for scrollbars */
int scrollbar_down; /* Click state */
int scrollbar_highlighted; /* Highlight state */
int scrollbar_repeat_active; /* If repeat is active */
int scrollbar_repeat_counter; /* Countown for repeat action */
int scrollbar_repeats_done; /* Numberf of executed repeat actions */
};
/* Initialize data structures */
void scrollbars_init();
/* Create scrollbars windows */
void scrollbars_create();
/* Update positions of scrollbars */
int scrollbars_update();
/* Refresh scrollbars */
int scrollbars_refresh(int exposures);
/* Get scrollbar under given coords */
int scrollbars_get_id(Window wid, int x, int y);
/* Update tray wrt scrollbar click;
* - id == SB_WND_MAX is a special case used to
* update scroll positions without chaning it
* (i.e. after icon removal)
*/
int scrollbars_click(int id);
/* Event handler for scrollbars */
void scrollbars_handle_event(XEvent ev);
/* Perform periodic tasks for scrollbars */
void scrollbars_periodic_tasks();
/* Scroll to icon */
int scrollbars_scroll_to(struct TrayIcon *ti);
/* Highlight scrollbar */
int scrollbars_highlight_on(int id);
/* Switch hightlight off */
int scrollbars_highlight_off(int id);
#endif
================================================
FILE: src/settings.c
================================================
/* -------------------------------
* vim:tabstop=4:shiftwidth=4
* settings.c
* Sun, 12 Sep 2004 18:55:53 +0700
* -------------------------------
* settings parser\container
* -------------------------------*/
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <libgen.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "common.h"
#include "debug.h"
#include "layout.h"
#include "list.h"
#include "settings.h"
#include "tray.h"
#include "wmh.h"
#include "xutils.h"
/* Here we keep our filthy settings */
struct Settings settings;
/* Initialize data */
void init_default_settings(void)
{
settings.bg_color_str = "gray";
settings.tint_color_str = "white";
settings.scrollbars_highlight_color_str = "white";
settings.display_str = NULL;
#ifdef DEBUG
settings.log_level = LOG_LEVEL_ERR;
#endif
settings.geometry_str = NULL;
settings.max_geometry_str = "0x0";
settings.icon_size = FALLBACK_ICON_SIZE;
settings.slot_size.x = -1;
settings.slot_size.y = -1;
settings.deco_flags = DECO_NONE;
settings.max_tray_dims.x = 0;
settings.max_tray_dims.y = 0;
settings.parent_bg = 0;
settings.shrink_back_mode = 1;
settings.sticky = 1;
settings.skip_taskbar = 1;
settings.transparent = 0;
settings.vertical = 0;
settings.grow_gravity = GRAV_N | GRAV_W;
settings.icon_gravity = GRAV_N | GRAV_W;
settings.wnd_type = _NET_WM_WINDOW_TYPE_DOCK;
settings.wnd_layer = NULL;
settings.wnd_name = PROGNAME;
settings.xsync = 0;
settings.need_help = 0;
settings.config_fname = NULL;
settings.full_pmt_search = 1;
settings.min_space_policy = 0;
settings.pixmap_bg = 0;
settings.bg_pmap_path = NULL;
settings.tint_level = 0;
settings.fuzzy_edges = 0;
settings.dockapp_mode = DOCKAPP_NONE;
settings.scrollbars_size = -1;
settings.scrollbars_mode = SB_MODE_NONE;
settings.scrollbars_inc = -1;
settings.wm_strut_mode = WM_STRUT_AUTO;
settings.kludge_flags = 0;
settings.remote_click_name = NULL;
settings.remote_click_btn = REMOTE_CLICK_BTN_DEFAULT;
settings.remote_click_cnt = REMOTE_CLICK_CNT_DEFAULT;
settings.remote_click_pos.x = REMOTE_CLICK_POS_DEFAULT;
settings.remote_click_pos.y = REMOTE_CLICK_POS_DEFAULT;
settings.ignored_classes = NULL;
settings.scroll_everywhere = 0;
#ifdef DELAY_EMBEDDING_CONFIRMATION
settings.confirmation_delay = 3;
#endif
#ifdef _ST_WITH_XINERAMA
settings.monitor = 0;
#endif
}
/* ******* general parsing utils ********* */
#define PARSING_ERROR(msg, str) \
if (!silent) LOG_ERROR(("Parsing error: " msg ", \"%s\" found\n", str));
/* Parse highlight color */
int parse_scrollbars_highlight_color(int, const char **argv, void **references, int)
{
char **highlight_color = (char **) references[0];
if (!strcasecmp(argv[0], "disable"))
*highlight_color = NULL;
else if ((*highlight_color = strdup(argv[0])) == NULL)
DIE_OOM(("Could not copy value from parameter\n"));
return SUCCESS;
}
/* Parse log level */
int parse_log_level(int, const char **argv, void **references, int silent)
{
int *log_level = (int *) references[0];
if (!strcmp(argv[0], "err"))
*log_level = LOG_LEVEL_ERR;
else if (!strcmp(argv[0], "info"))
*log_level = LOG_LEVEL_INFO;
else if (!strcmp(argv[0], "trace"))
*log_level = LOG_LEVEL_TRACE;
else {
PARSING_ERROR("err, info, or trace expected", argv[0]);
return FAILURE;
}
return SUCCESS;
}
/* Parse list of ignored window classes */
int parse_ignored_classes(int argc, const char **argv, void **references, int)
{
struct WindowClass **classes = (struct WindowClass **) references[0];
struct WindowClass *newclass = NULL;
int i;
for (i = 0; i < argc; i++) {
newclass = malloc(sizeof(struct WindowClass));
newclass->name = strdup(argv[i]);
LIST_ADD_ITEM(*classes, newclass);
}
return SUCCESS;
}
/* Parse dockapp mode */
int parse_dockapp_mode(int, const char **argv, void **references, int silent)
{
int *dockapp_mode = (int *) references[0];
if (!strcmp(argv[0], "none"))
*dockapp_mode = DOCKAPP_NONE;
else if (!strcmp(argv[0], "simple"))
*dockapp_mode = DOCKAPP_SIMPLE;
else if (!strcmp(argv[0], "wmaker"))
*dockapp_mode = DOCKAPP_WMAKER;
else {
PARSING_ERROR("none, simple, or wmaker expected", argv[0]);
return FAILURE;
}
return SUCCESS;
}
/* Parse gravity string ORing resulting value
* with current value of tgt */
int parse_gravity(int, const char **argv, void **references, int silent)
{
int *gravity = (int *) references[0];
const char *gravity_s = argv[0];
int parsed = 0;
if (strlen(gravity_s) > 2)
goto fail;
for (; *gravity_s; gravity_s++) {
switch (tolower(*gravity_s)) {
case 'n': parsed |= GRAV_N; break;
case 's': parsed |= GRAV_S; break;
case 'w': parsed |= GRAV_W; break;
case 'e': parsed |= GRAV_E; break;
default:
goto fail;
}
}
if ((parsed & GRAV_N && parsed & GRAV_S) || (parsed & GRAV_E && parsed & GRAV_W))
goto fail;
*gravity = parsed;
return SUCCESS;
fail:
PARSING_ERROR("gravity expected", gravity_s);
return FAILURE;
}
/* Parse integer string storing resulting value in tgt */
int parse_int(int, const char **argv, void **references, int silent)
{
int *parsed = (int *) references[0];
char *invalid;
*parsed = strtol(argv[0], &invalid, 0);
if (*invalid != '\0') {
PARSING_ERROR("integer expected", argv[0]);
return FAILURE;
}
return SUCCESS;
}
/* Parse kludges mode */
int parse_kludges(int, const char **argv, void **references, int silent)
{
const char *token = strtok((char *) argv[0], ",");
int *kludges = (int *) references[0];
for (; token != NULL; token = strtok(NULL, ",")) {
if (!strcasecmp(token, "fix_window_pos"))
*kludges |= KLUDGE_FIX_WND_POS;
else if (!strcasecmp(token, "force_icons_size"))
*kludges |= KLUDGE_FORCE_ICONS_SIZE;
else if (!strcasecmp(token, "use_icons_hints"))
*kludges |= KLUDGE_USE_ICONS_HINTS;
else {
PARSING_ERROR("kludge flag expected", token);
return FAILURE;
}
}
return SUCCESS;
}
/* Parse strut mode */
int parse_strut_mode(int, const char **argv, void **references, int silent)
{
int *strut_mode = (int *) references[0];
if (!strcasecmp(argv[0], "auto"))
*strut_mode = WM_STRUT_AUTO;
else if (!strcasecmp(argv[0], "top"))
*strut_mode = WM_STRUT_TOP;
else if (!strcasecmp(argv[0], "bottom"))
*strut_mode = WM_STRUT_BOT;
else if (!strcasecmp(argv[0], "left"))
*strut_mode = WM_STRUT_LFT;
else if (!strcasecmp(argv[0], "right"))
*strut_mode = WM_STRUT_RHT;
else if (!strcasecmp(argv[0], "none"))
*strut_mode = WM_STRUT_NONE;
else {
PARSING_ERROR(
"one of top, bottom, left, right, or auto expected", argv[0]);
return FAILURE;
}
return SUCCESS;
}
/* Parse boolean string storing result in tgt*/
int parse_bool(int, const char **argv, void **references, int silent)
{
const char *true_str[] = {"yes", "on", "true", "1", NULL};
const char *false_str[] = {"no", "off", "false", "0", NULL};
int *boolean = (int *) references[0];
for (const char **s = true_str; *s; s++) {
if (!strcasecmp(argv[0], *s)) {
*boolean = True;
return SUCCESS;
}
}
for (const char **s = false_str; *s; s++) {
if (!strcasecmp(argv[0], *s)) {
*boolean = False;
return SUCCESS;
}
}
PARSING_ERROR("boolean expected", argv[0]);
return FAILURE;
}
/* Backwards version of the boolean parser */
int parse_bool_rev(int argc, const char **argv, void **references, int silent)
{
int *boolean = (int *) references[0];
if (parse_bool(argc, argv, references, silent)) {
*boolean = ! *boolean;
return SUCCESS;
}
return FAILURE;
}
/* Parse window layer string storing result in tgt */
int parse_wnd_layer(int, const char **argv, void **references, int silent)
{
char **window_layer = (char **) references[0];
if (!strcasecmp(argv[0], "top"))
*window_layer = _NET_WM_STATE_ABOVE;
else if (!strcasecmp(argv[0], "bottom"))
*window_layer = _NET_WM_STATE_BELOW;
else if (!strcasecmp(argv[0], "normal"))
*window_layer = NULL;
else {
PARSING_ERROR("window layer expected", argv[0]);
return FAILURE;
}
return SUCCESS;
}
/* Parse window type string storing result in tgt */
int parse_wnd_type(int, const char **argv, void **references, int silent)
{
const char **window_type = (const char **) references[0];
if (!strcasecmp(argv[0], "dock"))
*window_type = _NET_WM_WINDOW_TYPE_DOCK;
else if (!strcasecmp(argv[0], "toolbar"))
*window_type = _NET_WM_WINDOW_TYPE_TOOLBAR;
else if (!strcasecmp(argv[0], "utility"))
*window_type = _NET_WM_WINDOW_TYPE_UTILITY;
else if (!strcasecmp(argv[0], "normal"))
*window_type = _NET_WM_WINDOW_TYPE_NORMAL;
else if (!strcasecmp(argv[0], "desktop"))
*window_type = _NET_WM_WINDOW_TYPE_DESKTOP;
else {
PARSING_ERROR("window type expected", argv[0]);
return FAILURE;
}
return SUCCESS;
}
/* Just copy string from arg to *tgt */
int parse_copystr(int, const char **argv, void **references, int)
{
const char **stringref = (const char **) references[0];
/* Valgrind note: this memory will never be freed before stalonetray's exit. */
if ((*stringref = strdup(argv[0])) == NULL)
DIE_OOM(("Could not copy value from parameter\n"));
return SUCCESS;
}
/* Parses window decoration specification */
int parse_deco(int, const char **argv, void **references, int silent)
{
int *decorations = (int *) references[0];
const char *arg = argv[0];
if (!strcasecmp(arg, "none"))
*decorations = DECO_NONE;
else if (!strcasecmp(arg, "all"))
*decorations = DECO_ALL;
else if (!strcasecmp(arg, "border"))
*decorations = DECO_BORDER;
else if (!strcasecmp(arg, "title"))
*decorations = DECO_TITLE;
else {
PARSING_ERROR("decoration specification expected", arg);
return FAILURE;
}
return SUCCESS;
}
/* Parses window decoration specification */
int parse_sb_mode(int, const char **argv, void **references, int silent)
{
int *sb_mode = (int *) references[0];
if (!strcasecmp(argv[0], "none"))
*sb_mode = 0;
else if (!strcasecmp(argv[0], "vertical"))
*sb_mode = SB_MODE_VERT;
else if (!strcasecmp(argv[0], "horizontal"))
*sb_mode = SB_MODE_HORZ;
else if (!strcasecmp(argv[0], "all"))
*sb_mode = SB_MODE_HORZ | SB_MODE_VERT;
else {
PARSING_ERROR("scrollbars specification expected", argv[0]);
return FAILURE;
}
return SUCCESS;
}
#if 0
/* Parses remote op specification */
int parse_remote(char *str, void **tgt, int silent)
{
#define NEXT_TOK(str, rest) \
do { \
(str) = (rest); \
if ((str) != NULL) { \
(rest) = strchr((str), ','); \
if ((rest) != NULL) *((rest)++) = 0; \
} \
} while (0)
#define PARSE_INT(tgt, str, tail, def, msg) \
do { \
if (str == NULL || *(str) == '\0') { \
(tgt) = def; \
} else { \
(tgt) = strtol((str), &(tail), 0); \
if (*(tail) != '\0') { \
PARSING_ERROR(msg, (str)); \
return FAILURE; \
} \
} \
} while (0)
/* Handy names for parameters */
int *flag = (int *) tgt[0];
char **name = (char **) tgt[1];
int *btn = (int *) tgt[2];
struct Point *pos = (struct Point *) tgt[3];
/* Local variables */
char *rest = str, *tail;
if (str == NULL || strlen(str) == 0) return FAILURE;
*flag = 1;
NEXT_TOK(str, rest);
*name = strdup(str);
NEXT_TOK(str, rest);
PARSE_INT(*btn, str, tail, INT_MIN, "remote click: button number expected");
NEXT_TOK(str, rest);
PARSE_INT(pos->x, str, tail, INT_MIN, "remote click: x coordinate expected");
NEXT_TOK(str, rest);
PARSE_INT(pos->y, str, tail, INT_MIN, "remote click: y coordinate expected");
return SUCCESS;
#undef NEXT_TOK
#undef PARSE_INT
}
#endif
int parse_remote_click_type(int, const char **argv, void **references, int silent)
{
int *remote_click_type = (int *) references[0];
if (!strcasecmp(argv[0], "single"))
*remote_click_type = 1;
else if (!strcasecmp(argv[0], "double"))
*remote_click_type = 2;
else {
PARSING_ERROR("click type can be single or double", argv[0]);
return FAILURE;
}
return SUCCESS;
}
int parse_pos(int, const char **argv, void **references, int)
{
struct Point *pos = (struct Point *) references[0];
unsigned int dummy;
XParseGeometry(argv[0], &pos->x, &pos->y, &dummy, &dummy);
return SUCCESS;
}
int parse_size(int, const char **argv, void **references, int)
{
struct Point *size = (struct Point *) references[0];
unsigned int width, height;
int bitmask, dummy;
bitmask = XParseGeometry(argv[0], &dummy, &dummy, &width, &height);
if (bitmask == 0 || bitmask & ~(WidthValue | HeightValue))
return FAILURE;
if ((bitmask & HeightValue) == 0)
height = width;
size->x = (width > (unsigned int) INT_MAX) ? INT_MAX : (int) width;
size->y = (height > (unsigned int) INT_MAX) ? INT_MAX : (int) height;
return SUCCESS;
}
/************ CLI **************/
#define MAX_TARGETS 10
#define MAX_DEFAULT_ARGS 10
/* parameter parser function */
typedef int (*param_parser_t)(int argc, const char **argv, void **references, int silent);
struct Param {
const char *short_name; /* short command line parameter name */
const char *long_name; /* long command line parameter name */
const char *rc_name; /* parameter name for config file */
void *references[MAX_TARGETS]; /* array of references necessary when parsing */
const int pass; /* 0th pass parameters are parsed before rc file, */
/* 1st pass parameters are parsed after it */
const int min_argc; /* minimum number of expected arguments */
const int max_argc; /* maximum number of expected arguments, 0 for unlimited */
const int default_argc; /* number of default arguments, if present */
const char *default_argv[MAX_DEFAULT_ARGS]; /* default arguments if none are given */
param_parser_t parser; /* pointer to parsing function */
};
struct Param params[] = {
{
.short_name = "-display",
.long_name = NULL,
.rc_name = "display",
.references = { (void *) &settings.display_str },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_copystr
},
{
.short_name = NULL,
.long_name = "--log-level",
.rc_name = "log_level",
.references = { (void *) &settings.log_level },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_log_level
},
{
.short_name = "-bg",
.long_name = "--background",
.rc_name = "background",
.references = { (void *) &settings.bg_color_str },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_copystr
},
{
.short_name = "-c",
.long_name = "--config",
.rc_name = NULL,
.references = { (void *) &settings.config_fname },
.pass = 0,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_copystr
},
{
.short_name = "-d",
.long_name = "--decorations",
.rc_name = "decorations",
.references = { (void *) &settings.deco_flags },
.pass = 1,
.min_argc = 0,
.max_argc = 1,
.default_argc = 1,
.default_argv = { "all" },
.parser = (param_parser_t) &parse_deco
},
{
.short_name = NULL,
.long_name = "--dockapp-mode",
.rc_name = "dockapp_mode",
.references = { (void *) &settings.dockapp_mode },
.pass = 1,
.min_argc = 0,
.max_argc = 1,
.default_argc = 1,
.default_argv = { "simple" },
.parser = (param_parser_t) &parse_dockapp_mode
},
{
.short_name = "-f",
.long_name = "--fuzzy-edges",
.rc_name = "fuzzy_edges",
.references = { (void *) &settings.fuzzy_edges },
.pass = 1,
.min_argc = 0,
.max_argc = 1,
.default_argc = 1,
.default_argv = { "2" },
.parser = (param_parser_t) &parse_int
},
{
.short_name = "-geometry",
.long_name = "--geometry",
.rc_name = "geometry",
.references = { (void *) &settings.geometry_str },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_copystr
},
{
.short_name = NULL,
.long_name = "--grow-gravity",
.rc_name = "grow_gravity",
.references = { (void *) &settings.grow_gravity },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_gravity
},
{
.short_name = NULL,
.long_name = "--icon-gravity",
.rc_name = "icon_gravity",
.references = { (void *) &settings.icon_gravity },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_gravity
},
{
.short_name = "-i",
.long_name = "--icon-size",
.rc_name = "icon_size",
.references = { (void *) &settings.icon_size },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_int
},
{
.short_name = "-h",
.long_name = "--help",
.rc_name = NULL,
.references = { (void *) &settings.need_help },
.pass = 0,
.min_argc = 0,
.max_argc = 0,
.default_argc = 1,
.default_argv = {"true"},
.parser = (param_parser_t) &parse_bool
},
{
.short_name = NULL,
.long_name = "--kludges",
.rc_name = "kludges",
.references = { (void *) &settings.kludge_flags },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_kludges
},
{
.short_name = NULL,
.long_name = "--max-geometry",
.rc_name = "max_geometry",
.references = { (void *) &settings.max_geometry_str },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_copystr
},
#ifdef _ST_WITH_XINERAMA
{
.short_name = "-m",
.long_name = "--monitor",
.rc_name = "monitor",
.references = { (void *) &settings.monitor },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_int
},
#endif
{
.short_name = NULL,
.long_name = "--no-shrink",
.rc_name = "no_shrink",
.references = { (void *) &settings.shrink_back_mode },
.pass = 1,
.min_argc = 0,
.max_argc = 1,
.default_argc = 1,
.default_argv = {"true"},
.parser = (param_parser_t) &parse_bool_rev
},
{
.short_name = "-p",
.long_name = "--parent-bg",
.rc_name = "parent_bg",
.references = { (void *) &settings.parent_bg },
.pass = 1,
.min_argc = 0,
.max_argc = 1,
.default_argc = 1,
.default_argv = {"true"},
.parser = (param_parser_t) &parse_bool
},
{
.short_name = "-r",
.long_name = "--remote-click-icon",
.rc_name = NULL,
.references = { (void *) &settings.remote_click_name },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_copystr
},
{
.short_name = NULL,
.long_name = "--remote-click-button",
.rc_name = NULL,
.references = { (void *) &settings.remote_click_btn },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_int
},
{
.short_name = NULL,
.long_name = "--remote-click-position",
.rc_name = NULL,
.references = { (void *) &settings.remote_click_pos },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_pos
},
{
.short_name = NULL,
.long_name = "--remote-click-type",
.rc_name = NULL,
.references = { (void *) &settings.remote_click_cnt },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_remote_click_type
},
{
.short_name = NULL,
.long_name = "--scrollbars",
.rc_name = "scrollbars",
.references = { (void *) &settings.scrollbars_mode },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_sb_mode
},
{
.short_name = NULL,
.long_name = "--scrollbars-highlight",
.rc_name = "scrollbars_highlight",
.references = { (void *) &settings.scrollbars_highlight_color_str },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_scrollbars_highlight_color
},
{
.short_name = NULL,
.long_name = "--scrollbars-step",
.rc_name = "scrollbars_step",
.references = { (void *) &settings.scrollbars_inc },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_int
},
{
.short_name = NULL,
.long_name = "--scrollbars-size",
.rc_name = "scrollbars_size",
.references = { (void *) &settings.scrollbars_size },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_int
},
{
.short_name = NULL,
.long_name = "--scroll-everywhere",
.rc_name = "scroll_everywhere",
.references = { (void *) &settings.scroll_everywhere },
.pass = 1,
.min_argc = 0,
.max_argc = 1,
.default_argc = 1,
.default_argv = {"true"},
.parser = (param_parser_t) &parse_bool
},
{
.short_name = NULL,
.long_name = "--skip-taskbar",
.rc_name = "skip_taskbar",
.references = { (void *) &settings.skip_taskbar },
.pass = 1,
.min_argc = 0,
.max_argc = 1,
.default_argc = 1,
.default_argv = {"true"},
.parser = (param_parser_t) &parse_bool
},
{
.short_name = "-s",
.long_name = "--slot-size",
.rc_name = "slot_size",
.references = { (void *) &settings.slot_size },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_size
},
{
.short_name = NULL,
.long_name = "--sticky",
.rc_name = "sticky",
.references = { (void *) &settings.sticky },
.pass = 1,
.min_argc = 0,
.max_argc = 1,
.default_argc = 1,
.default_argv = {"true"},
.parser = (param_parser_t) &parse_bool
},
{
.short_name = NULL,
.long_name = "--tint-color",
.rc_name = "tint_color",
.references = { (void *) &settings.tint_color_str },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_copystr
},
{
.short_name = NULL,
.long_name = "--tint-level",
.rc_name = "tint_level",
.references = { (void *) &settings.tint_level },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_int
},
{
.short_name = "-t",
.long_name = "--transparent",
.rc_name = "transparent",
.references = { (void *) &settings.transparent },
.pass = 1,
.min_argc = 0,
.max_argc = 1,
.default_argc = 1,
.default_argv = {"true"},
.parser = (param_parser_t) &parse_bool
},
{
.short_name = "-v",
.long_name = "--vertical",
.rc_name = "vertical",
.references = { (void *) &settings.vertical },
.pass = 1,
.min_argc = 0,
.max_argc = 1,
.default_argc = 1,
.default_argv = {"true"},
.parser = (param_parser_t) &parse_bool
},
{
.short_name = NULL,
.long_name = "--window-layer",
.rc_name = "window_layer",
.references = { (void *) &settings.wnd_layer },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_wnd_layer
},
{
.short_name = NULL,
.long_name = "--window-name",
.rc_name = "window_name",
.references = { (void *) &settings.wnd_name },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_copystr
},
{
.short_name = NULL,
.long_name = "--window-strut",
.rc_name = "window_strut",
.references = { (void *) &settings.wm_strut_mode },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_strut_mode
},
{
.short_name = NULL,
.long_name = "--window-type",
.rc_name = "window_type",
.references = { (void *) &settings.wnd_type },
.pass = 1,
.min_argc = 1,
.max_argc = 1,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_wnd_type
},
{
.short_name = NULL,
.long_name = "--xsync",
.rc_name = "xsync",
.references = { (void *) &settings.xsync },
.pass = 1,
.min_argc = 0,
.max_argc = 1,
.default_argc = 1,
.default_argv = {"true"},
.parser = (param_parser_t) &parse_bool
},
{
.short_name = NULL,
.long_name = NULL,
.rc_name = "ignore_classes",
.references = { (void *) &settings.ignored_classes },
.pass = 1,
.min_argc = 1,
.max_argc = 0,
.default_argc = 0,
.default_argv = NULL,
.parser = (param_parser_t) &parse_ignored_classes
},
#ifdef DELAY_EMBEDDING_CONFIRMATION
{
.short_name = NULL,
gitextract_7_pxnf_6/
├── .clang-format
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ └── workflows/
│ ├── build.yml
│ ├── lint.yml
│ └── release.yml
├── .gitignore
├── AUTHORS
├── LICENSE
├── README.md
├── TODO
├── generate-manpage.sh
├── meson.build
├── meson.format
├── meson.options
├── src/
│ ├── common.h
│ ├── debug.c
│ ├── debug.h
│ ├── embed.c
│ ├── embed.h
│ ├── icons.c
│ ├── icons.h
│ ├── image.c
│ ├── image.h
│ ├── kde_tray.c
│ ├── kde_tray.h
│ ├── layout.c
│ ├── layout.h
│ ├── list.h
│ ├── main.c
│ ├── meson.build
│ ├── scrollbars.c
│ ├── scrollbars.h
│ ├── settings.c
│ ├── settings.h
│ ├── tray.c
│ ├── tray.h
│ ├── wmh.c
│ ├── wmh.h
│ ├── xembed.c
│ ├── xembed.h
│ ├── xinerama.c
│ ├── xinerama.h
│ ├── xutils.c
│ └── xutils.h
├── stalonetray.xml.in
├── stalonetrayrc.sample
└── utils/
├── get_props/
│ └── main.c
├── py-gtk-example/
│ ├── LICENSE
│ ├── README.md
│ ├── gtk-example.glade
│ ├── gtk-example.py
│ └── python3.xpm
├── tray-test-fdo/
│ ├── main.c
│ ├── run_icons
│ ├── seq1
│ ├── seq2
│ ├── xembed.c
│ └── xembed.h
├── tray-test-gtk/
│ └── traytest
└── tray-xembed-test/
├── common.h
├── debug.c
├── main.c
├── run
├── xembed.c
├── xembed.h
├── xutils.c
└── xutils.h
SYMBOL INDEX (352 symbols across 30 files)
FILE: src/debug.c
function debug_disable_output (line 25) | void debug_disable_output()
function print_message_to_stderr (line 32) | void print_message_to_stderr(const char *fmt, ...)
function print_trace_header (line 44) | void print_trace_header(
function print_icon_data (line 81) | int print_icon_data(struct TrayIcon *ti)
function dump_icon_list (line 160) | void dump_icon_list()
FILE: src/debug.h
type TrayIcon (line 90) | struct TrayIcon
FILE: src/embed.c
type TrayIcon (line 38) | struct TrayIcon
type TrayIcon (line 38) | struct TrayIcon
function embedder_embed (line 58) | int embedder_embed(struct TrayIcon *ti)
function embedder_unembed (line 125) | int embedder_unembed(struct TrayIcon *ti)
function embedder_hide (line 161) | int embedder_hide(struct TrayIcon *ti)
function embedder_show (line 177) | int embedder_show(struct TrayIcon *ti)
function embedder_update_window_position (line 210) | static int embedder_update_window_position(struct TrayIcon *ti)
function embedder_update_positions (line 240) | int embedder_update_positions(int forced)
function embedder_refresh (line 248) | int embedder_refresh(struct TrayIcon *ti)
function embedder_reset_size (line 264) | int embedder_reset_size(struct TrayIcon *ti)
FILE: src/embed.h
type TrayIcon (line 20) | struct TrayIcon
type TrayIcon (line 23) | struct TrayIcon
type TrayIcon (line 33) | struct TrayIcon
type TrayIcon (line 36) | struct TrayIcon
type TrayIcon (line 39) | struct TrayIcon
type TrayIcon (line 42) | struct TrayIcon
FILE: src/icons.c
type TrayIcon (line 27) | struct TrayIcon
type TrayIcon (line 29) | struct TrayIcon
type TrayIcon (line 31) | struct TrayIcon
type TrayIcon (line 34) | struct TrayIcon
function icon_list_free (line 56) | int icon_list_free(struct TrayIcon *ti)
type TrayIcon (line 65) | struct TrayIcon
type TrayIcon (line 65) | struct TrayIcon
type TrayIcon (line 70) | struct TrayIcon
type TrayIcon (line 70) | struct TrayIcon
type TrayIcon (line 72) | struct TrayIcon
type TrayIcon (line 83) | struct TrayIcon
function icon_list_backup (line 85) | int icon_list_backup()
function icon_list_restore (line 109) | int icon_list_restore()
function icon_list_backup_purge (line 134) | int icon_list_backup_purge()
type TrayIcon (line 144) | struct TrayIcon
type TrayIcon (line 147) | struct TrayIcon
type TrayIcon (line 153) | struct TrayIcon
type TrayIcon (line 156) | struct TrayIcon
function icon_list_clean (line 162) | int icon_list_clean()
function icon_list_clean_callback (line 169) | int icon_list_clean_callback(IconCallbackFunc cbk)
function icon_list_sort (line 177) | void icon_list_sort(IconCmpFunc cmp)
type TrayIcon (line 191) | struct TrayIcon
type TrayIcon (line 196) | struct TrayIcon
type TrayIcon (line 197) | struct TrayIcon
type TrayIcon (line 200) | struct TrayIcon
FILE: src/icons.h
type Point (line 17) | struct Point {
type Rect (line 20) | struct Rect {
type Layout (line 25) | struct Layout {
type TrayIcon (line 32) | struct TrayIcon {
type TrayIcon (line 56) | struct TrayIcon
type TrayIcon (line 56) | struct TrayIcon
type TrayIcon (line 59) | struct TrayIcon
type TrayIcon (line 62) | struct TrayIcon
type TrayIcon (line 65) | struct TrayIcon
type TrayIcon (line 68) | struct TrayIcon
type TrayIcon (line 68) | struct TrayIcon
type TrayIcon (line 69) | struct TrayIcon
type TrayIcon (line 69) | struct TrayIcon
type TrayIcon (line 90) | struct TrayIcon
type TrayIcon (line 95) | struct TrayIcon
type TrayIcon (line 96) | struct TrayIcon
type TrayIcon (line 112) | struct TrayIcon
type TrayIcon (line 115) | struct TrayIcon
FILE: src/image.c
function image_tint (line 29) | int image_tint(XImage *image, XColor *color, CARD8 alpha)
function image_compose (line 51) | int image_compose(XImage *image, XImage *bg, CARD8 *mask)
function CARD8 (line 73) | CARD8 *image_create_alpha_mask(int ord, int w, int h)
function image_tint_32 (line 121) | int image_tint_32(CARD8 *data, size_t len, CARD32 pixel, CARD8 alpha)
function image_tint_24 (line 153) | int image_tint_24(CARD8 *data, size_t len, CARD32 pixel, CARD8 alpha)
function image_tint_16 (line 185) | int image_tint_16(CARD16 *data, size_t len, CARD32 pixel, CARD8 alpha)
function image_tint_15 (line 208) | int image_tint_15(CARD16 *data, size_t len, CARD32 pixel, CARD8 alpha)
function image_compose_32 (line 231) | int image_compose_32(CARD8 *data, CARD8 *bg, CARD8 *mask, size_t len)
function image_compose_24 (line 249) | int image_compose_24(CARD8 *data, CARD8 *bg, CARD8 *mask, size_t len)
function image_compose_16 (line 267) | int image_compose_16(CARD16 *data, CARD16 *bg, CARD8 *mask, size_t len)
function image_compose_15 (line 287) | int image_compose_15(CARD16 *data, CARD16 *bg, CARD8 *mask, size_t len)
FILE: src/kde_tray.c
function kde_tray_update_fallback_mode (line 23) | int kde_tray_update_fallback_mode(Display *dpy)
function kde_tray_init (line 43) | void kde_tray_init(Display *dpy)
function kde_tray_update_old_icons (line 101) | int kde_tray_update_old_icons(Display *dpy)
function kde_tray_is_old_icon (line 115) | int kde_tray_is_old_icon(Window w)
function kde_tray_old_icons_remove (line 123) | void kde_tray_old_icons_remove(Window w)
function kde_tray_check_for_icon (line 133) | int kde_tray_check_for_icon(Display *dpy, Window w)
function Window (line 158) | Window kde_tray_find_icon(Display *dpy, Window w)
FILE: src/layout.c
type Point (line 37) | struct Point
type TrayIcon (line 39) | struct TrayIcon
type TrayIcon (line 40) | struct TrayIcon
type TrayIcon (line 41) | struct TrayIcon
type TrayIcon (line 42) | struct TrayIcon
type TrayIcon (line 43) | struct TrayIcon
type TrayIcon (line 45) | struct TrayIcon
type TrayIcon (line 46) | struct TrayIcon
function layout_add (line 52) | int layout_add(struct TrayIcon *ti)
function layout_remove (line 61) | int layout_remove(struct TrayIcon *ti)
function layout_handle_icon_resize (line 66) | int layout_handle_icon_resize(struct TrayIcon *ti)
function layout_get_size (line 131) | void layout_get_size(int *width, int *height)
type TrayIcon (line 138) | struct TrayIcon
type TrayIcon (line 138) | struct TrayIcon
type TrayIcon (line 146) | struct TrayIcon
type TrayIcon (line 146) | struct TrayIcon
type IconPlacement (line 159) | struct IconPlacement {
type IconPlacement (line 166) | struct IconPlacement
type TrayIcon (line 166) | struct TrayIcon
type TrayIcon (line 168) | struct TrayIcon
type IconPlacement (line 168) | struct IconPlacement
type TrayIcon (line 170) | struct TrayIcon
type TrayIcon (line 170) | struct TrayIcon
type TrayIcon (line 172) | struct TrayIcon
function grid_translate_from_window (line 175) | int grid_translate_from_window(struct TrayIcon *ti)
function layout_translate_to_window (line 185) | int layout_translate_to_window(struct TrayIcon *ti)
function grid_add (line 233) | int grid_add(struct TrayIcon *ti)
function grid_remove (line 259) | int grid_remove(struct TrayIcon *ti)
function grid_update (line 276) | int grid_update(struct TrayIcon *ti, int sort)
function grid_place_icon (line 289) | int grid_place_icon(struct TrayIcon *ti, struct IconPlacement *ip)
type Rect (line 312) | struct Rect
function find_obstacle (line 315) | int find_obstacle(struct TrayIcon *ti)
function grid_check_rect_free (line 324) | int grid_check_rect_free(int x, int y, int w, int h)
function icon_placement_create (line 348) | int icon_placement_create(
function icon_placement_choose_best (line 394) | void icon_placement_choose_best(
type IconPlacement (line 484) | struct IconPlacement
type TrayIcon (line 484) | struct TrayIcon
type IconPlacement (line 486) | struct IconPlacement
type IconPlacement (line 487) | struct IconPlacement
function trayicon_cmp_func (line 546) | int trayicon_cmp_func(struct TrayIcon *ti1, struct TrayIcon *ti2)
function grid_add_wrapper (line 552) | int grid_add_wrapper(struct TrayIcon *ti)
function grid_recalc_size (line 558) | int grid_recalc_size(struct TrayIcon *ti)
function layout_unset_flag (line 573) | int layout_unset_flag(struct TrayIcon *ti)
FILE: src/layout.h
type TrayIcon (line 43) | struct TrayIcon
type TrayIcon (line 46) | struct TrayIcon
type TrayIcon (line 49) | struct TrayIcon
type TrayIcon (line 55) | struct TrayIcon
type TrayIcon (line 58) | struct TrayIcon
type TrayIcon (line 58) | struct TrayIcon
type TrayIcon (line 61) | struct TrayIcon
type TrayIcon (line 61) | struct TrayIcon
FILE: src/main.c
type TrayData (line 41) | struct TrayData
function my_usleep (line 47) | void my_usleep(useconds_t usec)
function request_tray_status_on_signal (line 60) | void request_tray_status_on_signal(int)
function exit_on_signal (line 66) | void exit_on_signal(int sig)
function cleanup (line 83) | void cleanup()
function dump_tray_status (line 116) | void dump_tray_status()
function is_ignored_class (line 135) | int is_ignored_class(const char *name) {
function add_icon (line 151) | void add_icon(Window w, int cmode)
function remove_icon (line 219) | void remove_icon(Window w)
function icon_track_visibility_changes (line 241) | void icon_track_visibility_changes(Window w)
function find_invalid_icons (line 275) | int find_invalid_icons(struct TrayIcon *ti)
function kde_icons_update (line 283) | void kde_icons_update()
function find_unmanaged_chromium_icons (line 308) | void find_unmanaged_chromium_icons()
function perform_periodic_tasks (line 364) | void perform_periodic_tasks(int mask)
function expose (line 397) | void expose(XExposeEvent ev)
function visibility_notify (line 403) | void visibility_notify(XVisibilityEvent) {}
function property_notify (line 405) | void property_notify(XPropertyEvent ev)
function reparent_notify (line 467) | void reparent_notify(XReparentEvent ev)
function client_message (line 484) | void client_message(XClientMessageEvent ev)
function destroy_notify (line 617) | void destroy_notify(XDestroyWindowEvent ev)
function configure_notify (line 635) | void configure_notify(XConfigureEvent ev)
function selection_clear (line 701) | void selection_clear(XSelectionClearEvent ev)
function map_notify (line 733) | void map_notify(XMapEvent ev)
function unmap_notify (line 757) | void unmap_notify(XUnmapEvent ev)
function tray_main (line 774) | int tray_main(int argc, char **argv)
function remote_main (line 881) | int remote_main(int, char **)
function main (line 952) | int main(int argc, char **argv)
FILE: src/scrollbars.c
function scrollbars_init (line 20) | void scrollbars_init()
function scrollbars_create (line 29) | void scrollbars_create()
function scrollbars_refresh (line 94) | int scrollbars_refresh(int exposures)
function scrollbars_update (line 106) | int scrollbars_update()
function scrollbars_get_id (line 170) | int scrollbars_get_id(Window wid, int x, int y)
function scrollbars_validate_scroll_pos (line 182) | void scrollbars_validate_scroll_pos()
function scrollbars_click (line 204) | int scrollbars_click(int id)
function scrollbars_handle_event (line 232) | void scrollbars_handle_event(XEvent ev)
function scrollbars_periodic_tasks (line 305) | void scrollbars_periodic_tasks()
function scrollbars_scroll_to (line 313) | int scrollbars_scroll_to(struct TrayIcon *ti)
function scrollbars_highlight_on (line 339) | int scrollbars_highlight_on(int id)
function scrollbars_highlight_off (line 351) | int scrollbars_highlight_off(int id)
FILE: src/scrollbars.h
type ScrollbarsData (line 30) | struct ScrollbarsData {
type TrayIcon (line 64) | struct TrayIcon
FILE: src/settings.c
type Settings (line 36) | struct Settings
function init_default_settings (line 38) | void init_default_settings(void)
function parse_scrollbars_highlight_color (line 103) | int parse_scrollbars_highlight_color(int, const char **argv, void **refe...
function parse_log_level (line 116) | int parse_log_level(int, const char **argv, void **references, int silent)
function parse_ignored_classes (line 134) | int parse_ignored_classes(int argc, const char **argv, void **references...
function parse_dockapp_mode (line 150) | int parse_dockapp_mode(int, const char **argv, void **references, int si...
function parse_gravity (line 169) | int parse_gravity(int, const char **argv, void **references, int silent)
function parse_int (line 202) | int parse_int(int, const char **argv, void **references, int silent)
function parse_kludges (line 218) | int parse_kludges(int, const char **argv, void **references, int silent)
function parse_strut_mode (line 240) | int parse_strut_mode(int, const char **argv, void **references, int silent)
function parse_bool (line 266) | int parse_bool(int, const char **argv, void **references, int silent)
function parse_bool_rev (line 291) | int parse_bool_rev(int argc, const char **argv, void **references, int s...
function parse_wnd_layer (line 304) | int parse_wnd_layer(int, const char **argv, void **references, int silent)
function parse_wnd_type (line 323) | int parse_wnd_type(int, const char **argv, void **references, int silent)
function parse_copystr (line 346) | int parse_copystr(int, const char **argv, void **references, int)
function parse_deco (line 358) | int parse_deco(int, const char **argv, void **references, int silent)
function parse_sb_mode (line 379) | int parse_sb_mode(int, const char **argv, void **references, int silent)
function parse_remote (line 401) | int parse_remote(char *str, void **tgt, int silent)
function parse_remote_click_type (line 446) | int parse_remote_click_type(int, const char **argv, void **references, i...
function parse_pos (line 462) | int parse_pos(int, const char **argv, void **references, int)
function parse_size (line 470) | int parse_size(int, const char **argv, void **references, int)
type Param (line 497) | struct Param {
type Param (line 515) | struct Param
function usage (line 1182) | void usage(char *progname)
function parse_cmdline (line 1318) | int parse_cmdline(int argc, char **argv, int pass)
function get_args (line 1430) | int get_args(char *line, int *argc, char ***argv)
type stat (line 1500) | struct stat
function parse_rc (line 1522) | void parse_rc(void)
function interpret_settings (line 1628) | void interpret_settings(void)
function read_settings (line 1772) | int read_settings(int argc, char **argv)
FILE: src/settings.h
type WindowClass (line 20) | struct WindowClass {
type Settings (line 26) | struct Settings {
type Settings (line 102) | struct Settings
FILE: src/tray.c
function tray_init (line 34) | void tray_init()
function tray_init_pixmap_bg (line 55) | int tray_init_pixmap_bg()
function Pixmap (line 79) | Pixmap tray_get_root_pixmap(Atom prop)
function tray_update_root_bg_pmap (line 99) | int tray_update_root_bg_pmap(Pixmap *pmap, int *width, int *height)
function tray_update_bg (line 132) | int tray_update_bg(int update_pixmap)
function tray_refresh_window (line 287) | void tray_refresh_window(int exposures)
function tray_calc_window_size (line 296) | int tray_calc_window_size(
function tray_calc_tray_area_size (line 308) | int tray_calc_tray_area_size(
function tray_update_window_strut (line 320) | int tray_update_window_strut()
function tray_update_window_props (line 404) | int tray_update_window_props()
function tray_create_window (line 508) | void tray_create_window(int argc, char **argv)
function tray_create_phony_window (line 624) | void tray_create_phony_window()
function tray_set_wm_hints (line 633) | int tray_set_wm_hints()
function tray_init_selection_atoms (line 662) | void tray_init_selection_atoms()
function tray_acquire_selection (line 683) | void tray_acquire_selection()
function tray_show_window (line 710) | void tray_show_window()
FILE: src/tray.h
type TrayData (line 80) | struct TrayData {
type TrayData (line 126) | struct TrayData
FILE: src/wmh.c
type PropMotifWmHints (line 17) | typedef struct {
function ewmh_wm_present (line 33) | int ewmh_wm_present(Display *dpy)
function ewmh_add_window_state (line 59) | int ewmh_add_window_state(Display *dpy, Window wnd, char *state)
function ewmh_add_window_type (line 87) | int ewmh_add_window_type(Display *dpy, Window wnd, char *type)
function ewmh_set_window_strut (line 101) | int ewmh_set_window_strut(Display *dpy, Window wnd, wm_strut_t wm_strut)
function ewmh_set_window_atom32 (line 115) | int ewmh_set_window_atom32(
function mwm_set_hints (line 141) | int mwm_set_hints(Display *dpy, Window wnd, unsigned long decorations,
function ewmh_list_supported_atoms (line 182) | int ewmh_list_supported_atoms(Display *dpy)
function ewmh_dump_window_states (line 211) | int ewmh_dump_window_states(Display *dpy, Window wnd)
FILE: src/xembed.c
type XEMBEDAccel (line 71) | struct XEMBEDAccel {
type TrayIcon (line 104) | struct TrayIcon
type TrayIcon (line 106) | struct TrayIcon
type TrayIcon (line 108) | struct TrayIcon
type TrayIcon (line 110) | struct TrayIcon
type XEMBEDAccel (line 118) | struct XEMBEDAccel
type TrayIcon (line 120) | struct TrayIcon
function xembed_init (line 128) | void xembed_init()
function xembed_handle_event (line 151) | void xembed_handle_event(XEvent ev)
function xembed_check_support (line 205) | int xembed_check_support(struct TrayIcon *ti)
function xembed_get_mapped_state (line 212) | int xembed_get_mapped_state(struct TrayIcon *ti)
function xembed_set_mapped_state (line 228) | int xembed_set_mapped_state(struct TrayIcon *ti, int state)
function xembed_embed (line 238) | int xembed_embed(struct TrayIcon *ti)
function xembed_unembed (line 264) | int xembed_unembed(struct TrayIcon *ti)
function xembed_switch_focus_to (line 283) | void xembed_switch_focus_to(struct TrayIcon *tgt, long focus)
function broadcast_activate_msg (line 308) | int broadcast_activate_msg(struct TrayIcon *ti)
function xembed_track_focus_change (line 319) | void xembed_track_focus_change(int in)
function xembed_message (line 328) | void xembed_message(XClientMessageEvent ev)
function xembed_add_accel (line 396) | void xembed_add_accel(long id, long symb, long mods)
function xembed_del_accel (line 421) | void xembed_del_accel(long id)
type XEMBEDAccel (line 442) | struct XEMBEDAccel
function xembed_act_accel_helper (line 444) | int xembed_act_accel_helper(struct TrayIcon *ti)
function xembed_act_accel (line 452) | void xembed_act_accel(struct XEMBEDAccel *accel)
function xembed_process_kbd_event (line 461) | int xembed_process_kbd_event(XKeyEvent xkey)
type TrayIcon (line 479) | struct TrayIcon
type TrayIcon (line 481) | struct TrayIcon
type TrayIcon (line 492) | struct TrayIcon
type TrayIcon (line 494) | struct TrayIcon
function xembed_retrive_data (line 505) | int xembed_retrive_data(struct TrayIcon *ti)
function xembed_post_data (line 529) | int xembed_post_data(struct TrayIcon *ti)
function xembed_request_focus_from_wm (line 539) | void xembed_request_focus_from_wm()
FILE: src/xembed.h
type XEMBEDData (line 17) | struct XEMBEDData {
type TrayIcon (line 35) | struct TrayIcon
type TrayIcon (line 38) | struct TrayIcon
type TrayIcon (line 41) | struct TrayIcon
type TrayIcon (line 44) | struct TrayIcon
type TrayIcon (line 47) | struct TrayIcon
FILE: src/xinerama.c
function xinerama_init (line 13) | void xinerama_init(Display *dpy)
function xinerama_update_geometry (line 32) | void xinerama_update_geometry(void)
FILE: src/xutils.c
function x11_io_error_handler (line 28) | int x11_io_error_handler(Display *dpy)
function x11_connection_status (line 35) | int x11_connection_status()
function x11_error_handler (line 40) | int x11_error_handler(Display *dpy, XErrorEvent *err)
function x11_ok_helper (line 53) | int x11_ok_helper(const char *file, const int line, const char *func)
function x11_current_error (line 64) | int x11_current_error()
function x11_trap_errors (line 69) | void x11_trap_errors()
function x11_untrap_errors (line 76) | int x11_untrap_errors()
function Bool (line 85) | Bool x11_wait_for_timestamp(Display *, XEvent *xevent, XPointer data)
function Time (line 94) | Time x11_get_server_timestamp(Display *dpy, Window wnd)
function x11_get_window_prop32 (line 116) | int x11_get_window_prop32(Display *dpy, Window dst, Atom atom, Atom type,
function x11_send_client_msg32 (line 150) | int x11_send_client_msg32(Display *dpy, Window dst, Window wnd, Atom type,
function x11_send_visibility (line 173) | int x11_send_visibility(Display *, Window dst, long state)
function x11_send_button (line 184) | int x11_send_button(Display *dpy, int press, Window dst, Window root,
function x11_send_expose (line 217) | int x11_send_expose(
function x11_refresh_window (line 233) | int x11_refresh_window(
function x11_set_window_size (line 242) | int x11_set_window_size(Display *dpy, Window w, int x, int y)
function x11_get_window_size (line 254) | int x11_get_window_size(Display *dpy, Window w, int *x, int *y)
function x11_get_window_min_size (line 264) | int x11_get_window_min_size(Display *dpy, Window w, int *x, int *y)
function x11_get_window_abs_coords (line 280) | int x11_get_window_abs_coords(Display *dpy, Window dst, int *x, int *y)
function Window (line 300) | Window x11_find_subwindow_by_name(Display *dpy, Window tgt, char *name)
function Window (line 324) | Window x11_find_subwindow_at(
function x11_extend_root_event_mask (line 365) | void x11_extend_root_event_mask(Display *dpy, long mask)
function x11_parse_color (line 372) | int x11_parse_color(Display *dpy, char *str, XColor *color)
function x11_dump_win_info (line 410) | void x11_dump_win_info(Display *dpy, Window wid)
FILE: utils/get_props/main.c
function error_handler (line 21) | static int error_handler(Display *display, XErrorEvent *error)
function trap_errors (line 27) | void trap_errors()
function untrap_errors (line 33) | int untrap_errors()
function die (line 39) | void die(char *msg)
function main (line 45) | int main(int argc, char **argv)
FILE: utils/py-gtk-example/gtk-example.py
class TrayIcon (line 24) | class TrayIcon:
method __init__ (line 26) | def __init__(self, appid, icon, menu):
method onPopupMenu (line 47) | def onPopupMenu(self, icon, button, time):
class Handler (line 52) | class Handler:
method __init__ (line 54) | def __init__(self):
method onShowButtonClicked (line 57) | def onShowButtonClicked(self, button):
method onNotify (line 64) | def onNotify(self, *args):
method onShowOrHide (line 67) | def onShowOrHide(self, *args):
method onQuit (line 75) | def onQuit(self, *args):
FILE: utils/tray-test-fdo/main.c
function die (line 54) | void die(char *msg)
function main (line 60) | int main(int argc, char **argv)
FILE: utils/tray-test-fdo/xembed.c
function error_handler (line 15) | static int error_handler(Display *display, XErrorEvent *error)
function trap_errors (line 21) | void trap_errors()
function untrap_errors (line 27) | int untrap_errors()
function xclient_msg32 (line 33) | int xclient_msg32(Display *dpy, Window dst, Atom type, long data0, long ...
function xembed_msg (line 50) | int xembed_msg(
function xembed_get_info (line 63) | unsigned long xembed_get_info(Display *dpy, Window src, long *version)
function xembed_embeded_notify (line 95) | int xembed_embeded_notify(Display *dpy, Window src, Window dst)
function xembed_window_activate (line 100) | int xembed_window_activate(Display *dpy, Window dst)
function xembed_window_deactivate (line 105) | int xembed_window_deactivate(Display *dpy, Window dst)
function xembed_focus_in (line 110) | int xembed_focus_in(Display *dpy, Window dst, int focus)
function xembed_focus_out (line 115) | int xembed_focus_out(Display *dpy, Window dst)
FILE: utils/tray-xembed-test/debug.c
function print_message_to_stderr (line 15) | void print_message_to_stderr(const char *fmt,...)
FILE: utils/tray-xembed-test/main.c
function create_window (line 76) | void create_window(int argc, char **argv)
function redraw_buttons (line 146) | void redraw_buttons()
function focus_in (line 164) | void focus_in(int how)
function focus_out (line 182) | void focus_out()
function focus_next (line 189) | void focus_next()
function focus_prev (line 203) | void focus_prev()
function client_event (line 219) | void client_event(XClientMessageEvent ev)
function key_release (line 260) | void key_release(XKeyReleasedEvent ev)
function main (line 281) | int main(int argc, char** argv)
FILE: utils/tray-xembed-test/xembed.c
function xembed_retrive_data (line 21) | int xembed_retrive_data(Display *dpy, Window w, CARD32 *data)
function xembed_post_data (line 51) | int xembed_post_data(Display *dpy, Window w, CARD32 *data)
FILE: utils/tray-xembed-test/xutils.c
function x11_error_handler (line 24) | int x11_error_handler(Display *dpy, XErrorEvent *err)
function x11_ok_helper (line 33) | int x11_ok_helper(const char *file, const int line, const char *func)
function x11_current_error (line 47) | int x11_current_error()
function x11_trap_errors (line 52) | void x11_trap_errors()
function x11_untrap_errors (line 58) | int x11_untrap_errors()
function Bool (line 67) | Bool x11_wait_for_timestamp(Display *dpy, XEvent *xevent, XPointer data)
function Time (line 74) | Time x11_get_server_timestamp(Display *dpy, Window wnd)
function x11_get_win_prop32 (line 93) | int x11_get_win_prop32(Display *dpy, Window dst, Atom atom, Atom type, u...
function x11_send_client_msg32 (line 125) | int x11_send_client_msg32(Display *dpy, Window dst, Window wnd, Atom typ...
function x11_set_window_size (line 143) | int x11_set_window_size(Display *dpy, Window w, int x, int y)
function x11_get_window_size (line 162) | int x11_get_window_size(Display *dpy, Window w, int *x, int *y)
function x11_get_window_min_size (line 178) | int x11_get_window_min_size(Display *dpy, Window w, int *x, int *y)
function x11_get_window_abs_coords (line 196) | int x11_get_window_abs_coords(Display *dpy, Window dst, int *x, int *y)
function x11_extend_root_event_mask (line 224) | void x11_extend_root_event_mask(Display *dpy, long mask)
function x11_dump_win_info (line 270) | void x11_dump_win_info(Display *dpy, Window wid)
Condensed preview — 68 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (424K chars).
[
{
"path": ".clang-format",
"chars": 2933,
"preview": "---\nLanguage: Cpp\nAccessModifierOffset: 0\nAlignAfterOpenBracket: DontAlign\nAlignConsecutiveMacros: false\nAlignCon"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 533,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the b"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 595,
"preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your fea"
},
{
"path": ".github/workflows/build.yml",
"chars": 4179,
"preview": "name: Build\n\non:\n push:\n branches:\n - master\n pull_request: {}\n\njobs:\n ubuntu:\n name: Ubuntu\n runs-on: "
},
{
"path": ".github/workflows/lint.yml",
"chars": 1629,
"preview": "name: Lint\n\non:\n push:\n branches:\n - master\n pull_request: {}\n\njobs:\n new-version:\n name: Version Bump Che"
},
{
"path": ".github/workflows/release.yml",
"chars": 1640,
"preview": "name: Release\n\non:\n push:\n branches:\n - master\n\nconcurrency:\n group: merge-to-master\n cancel-in-progress: fal"
},
{
"path": ".gitignore",
"chars": 339,
"preview": "# Generated by build system\n/aclocal.m4\n/autom4te.cache\nMakefile.in\nMakefile\n/compile\n/config.log\n/config.status\n/config"
},
{
"path": "AUTHORS",
"chars": 57,
"preview": "Roman Dubtsov <dubtsov@gmail.com>\nd3adb5 <me@d3adb5.net>\n"
},
{
"path": "LICENSE",
"chars": 18092,
"preview": " GNU GENERAL PUBLIC LICENSE\n Version 2, June 1991\n\n Copyright (C) 1989, 1991 Fr"
},
{
"path": "README.md",
"chars": 2674,
"preview": "# STAnd aLONE TRAY [![Build][badge-build]][yaml-build] [![Lint][badge-lint]][yaml-lint]\n\n[badge-build]: https://github.c"
},
{
"path": "TODO",
"chars": 5633,
"preview": "+ Each icon must have a newly created parent window, which matches its\n dimentions (for ease of move and for them to be"
},
{
"path": "generate-manpage.sh",
"chars": 1716,
"preview": "#!/bin/sh\n#\n# A simple script to build the manpage for stalonetray using xsltproc.\n#\n# The logic in this script would've"
},
{
"path": "meson.build",
"chars": 2751,
"preview": "project(\n 'stalonetray',\n 'c',\n version: '1.0.3',\n default_options: ['warning_level=3', 'c_std=gnu23'],\n meson_vers"
},
{
"path": "meson.format",
"chars": 38,
"preview": "indent_by = ' '\nmax_line_length = 80\n"
},
{
"path": "meson.options",
"chars": 848,
"preview": "option('xinerama', type: 'feature', value: 'enabled',\n description: 'Enable Xinerama support for multiple monitors')\nop"
},
{
"path": "src/common.h",
"chars": 2635,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * common.h\n * Mon, 01 May 2006 01:45:08 +0700\n * -----"
},
{
"path": "src/debug.c",
"chars": 5320,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * debug.c\n * Mon, 01 May 2006 01:44:42 +0700\n * ------"
},
{
"path": "src/debug.h",
"chars": 2506,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * debug.h\n * Tue, 24 Aug 2004 12:05:38 +0700\n * ------"
},
{
"path": "src/embed.c",
"chars": 10413,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * embed.c\n * Fri, 03 Sep 2004 20:38:55 +0700\n * ------"
},
{
"path": "src/embed.h",
"chars": 1009,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * embed.h\n * Fri, 03 Sep 2004 20:38:55 +0700\n * ------"
},
{
"path": "src/icons.c",
"chars": 5528,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * icons.c\n * Tue, 24 Aug 2004 12:05:38 +0700\n * ------"
},
{
"path": "src/icons.h",
"chars": 3948,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * icons.h\n * Tue, 24 Aug 2004 12:05:38 +0700\n * ------"
},
{
"path": "src/image.c",
"chars": 9052,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * image.c\n * Fri, 22 Jun 2007 23:32:27 +0700\n * ------"
},
{
"path": "src/image.h",
"chars": 806,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * image.h\n * Fri, 22 Jun 2007 23:32:27 +0700\n * ------"
},
{
"path": "src/kde_tray.c",
"chars": 5851,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * kde_tray.c\n * Sun, 19 Sep 2004 12:31:10 +0700\n * ---"
},
{
"path": "src/kde_tray.h",
"chars": 991,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * kde_tray.h\n * Sun, 19 Sep 2004 12:28:59 +0700\n * ---"
},
{
"path": "src/layout.c",
"chars": 20864,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * layout.c\n * Tue, 24 Aug 2004 12:19:48 +0700\n * -----"
},
{
"path": "src/layout.h",
"chars": 1756,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * layout.h\n * Tue, 24 Aug 2004 12:19:27 +0700\n * -----"
},
{
"path": "src/list.h",
"chars": 2297,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * list.h\n * Fri, 10 Sep 2004 23:34:48 +0700\n * -------"
},
{
"path": "src/main.c",
"chars": 34657,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * main.c\n * Tue, 24 Aug 2004 12:19:48 +0700\n * -------"
},
{
"path": "src/meson.build",
"chars": 339,
"preview": "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"
},
{
"path": "src/scrollbars.c",
"chars": 13437,
"preview": "/* ************************************\n * vim:tabstop=4:shiftwidth=4:cindent:fen:fdm=syntax\n * tray.c\n * Mon, 09 Mar 20"
},
{
"path": "src/scrollbars.h",
"chars": 2095,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * tray.h\n * Mon, 09 Mar 2009 12:41:32 -0400\n * -------"
},
{
"path": "src/settings.c",
"chars": 55621,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * settings.c\n * Sun, 12 Sep 2004 18:55:53 +0700\n * ---"
},
{
"path": "src/settings.h",
"chars": 4266,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * settings.h\n * Sun, 12 Sep 2004 18:06:08 +0700\n * ---"
},
{
"path": "src/tray.c",
"chars": 29477,
"preview": "/* ************************************\n * vim:tabstop=4:shiftwidth=4:cindent:fen:fdm=syntax\n * tray.c\n * Tue, 07 Mar 20"
},
{
"path": "src/tray.h",
"chars": 5257,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * tray.h\n * Wed, 29 Sep 2004 23:10:02 +0700\n * -------"
},
{
"path": "src/wmh.c",
"chars": 8316,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * ewmh.c\n * Thu, 30 Mar 2006 23:15:37 +0700\n * -------"
},
{
"path": "src/wmh.h",
"chars": 3973,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * ewmh.h\n * Thu, 30 Mar 2006 23:12:43 +0700\n * -------"
},
{
"path": "src/xembed.c",
"chars": 20331,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * xembed.c\n * Tue, 24 Aug 2004 12:05:38 +0700\n * -----"
},
{
"path": "src/xembed.h",
"chars": 1513,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * icons.h\n * Tue, 24 Aug 2004 12:05:38 +0700\n * ------"
},
{
"path": "src/xinerama.c",
"chars": 1640,
"preview": "#include <X11/Xlib.h>\n\n#include \"debug.h\"\n#include \"xinerama.h\"\n\n#ifdef _ST_WITH_XINERAMA\n#include <X11/extensions/Xiner"
},
{
"path": "src/xinerama.h",
"chars": 169,
"preview": "#ifndef _STALONETRAY_XINERAMA_H_\n#define _STALONETRAY_XINERAMA_H_\n\n#include <X11/Xlib.h>\n\nvoid xinerama_init(Display *dp"
},
{
"path": "src/xutils.c",
"chars": 12802,
"preview": "/* ************************************\n * vim:tabstop=4:shiftwidth=4\n * xutils.c\n * Sun, 05 Mar 2006 17:56:56 +0600\n * "
},
{
"path": "src/xutils.h",
"chars": 4093,
"preview": "/* ************************************\n * vim:tabstop=4:shiftwidth=4\n * xutils.h\n * Sun, 05 Mar 2006 17:16:44 +0600\n * "
},
{
"path": "stalonetray.xml.in",
"chars": 22717,
"preview": "<?xml version=\"1.0\" encoding=\"latin1\" standalone=\"no\"?>\n<!--\n vim: et:tw=80\n-->\n\n<!DOCTYPE refentry PUBLIC\n \"-//OASI"
},
{
"path": "stalonetrayrc.sample",
"chars": 6024,
"preview": "# vim: filetype=config textwidth=80 expandtab\n#\n# This is sample ~/.stalonetrayrc, resembling default configuration.\n# R"
},
{
"path": "utils/get_props/main.c",
"chars": 3706,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * main.c\n * Tue, 24 Aug 2004 12:00:24 +0700\n * -------"
},
{
"path": "utils/py-gtk-example/LICENSE",
"chars": 1086,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2016 Aleksander Alekseev\n\nPermission is hereby granted, free of charge, to any pers"
},
{
"path": "utils/py-gtk-example/README.md",
"chars": 16,
"preview": "# py-gtk-example"
},
{
"path": "utils/py-gtk-example/gtk-example.glade",
"chars": 6778,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- Generated with glade 3.16.1 -->\n<interface>\n <requires lib=\"gtk+\" version=\""
},
{
"path": "utils/py-gtk-example/gtk-example.py",
"chars": 2653,
"preview": "#!/usr/bin/env python3\n\n# gtk-example.py\n# (c) Aleksander Alekseev 2016\n# http://eax.me/\n\nimport signal\nimport os\nimport"
},
{
"path": "utils/py-gtk-example/python3.xpm",
"chars": 7286,
"preview": "/* XPM */\nstatic char * pylogo_xpm[] = {\n\"32 32 316 2\",\n\" \tc None\",\n\". \tc #8DB0CE\",\n\"+ \tc #6396BF\",\n\"@ \tc #4985B7\",\n\"# "
},
{
"path": "utils/tray-test-fdo/main.c",
"chars": 7507,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * main.c\n * Tue, 24 Aug 2004 12:00:24 +0700\n * -------"
},
{
"path": "utils/tray-test-fdo/run_icons",
"chars": 136,
"preview": "#!/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 &\n"
},
{
"path": "utils/tray-test-fdo/seq1",
"chars": 245,
"preview": "#!/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 "
},
{
"path": "utils/tray-test-fdo/seq2",
"chars": 185,
"preview": "#!/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 "
},
{
"path": "utils/tray-test-fdo/xembed.c",
"chars": 3023,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * icons.h\n * Tue, 24 Aug 2004 12:05:38 +0700\n * ------"
},
{
"path": "utils/tray-test-fdo/xembed.h",
"chars": 2173,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * icons.h\n * Tue, 24 Aug 2004 12:05:38 +0700\n * ------"
},
{
"path": "utils/tray-test-gtk/traytest",
"chars": 1315,
"preview": "#!/usr/bin/env python\n# vim:et\nimport gtk, pygtk, gobject\nimport os\n\ndef send_signal(sig = 1):\n pass\n# os.system('"
},
{
"path": "utils/tray-xembed-test/common.h",
"chars": 1281,
"preview": "/* -------------------------------\n* vim:tabstop=4:shiftwidth=4\n* common.h\n* Mon, 01 May 2006 01:45:08 +0700\n* ---------"
},
{
"path": "utils/tray-xembed-test/debug.c",
"chars": 564,
"preview": "/* -------------------------------\n* vim:tabstop=4:shiftwidth=4\n* debug.c\n* Mon, 01 May 2006 01:44:42 +0700\n* ----------"
},
{
"path": "utils/tray-xembed-test/main.c",
"chars": 8022,
"preview": "/* -------------------------------\n * vim:tabstop=4:shiftwidth=4\n * main.c\n * Tue, 24 Aug 2004 12:00:24 +0700\n * -------"
},
{
"path": "utils/tray-xembed-test/run",
"chars": 101,
"preview": "#!/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",
"chars": 1378,
"preview": "/* -------------------------------\n* vim:tabstop=4:shiftwidth=4\n* xembed.c\n* Tue, 24 Aug 2004 12:05:38 +0700\n* ---------"
},
{
"path": "utils/tray-xembed-test/xembed.h",
"chars": 3288,
"preview": "/* -------------------------------\n* vim:tabstop=4:shiftwidth=4\n* icons.h\n* Tue, 24 Aug 2004 12:05:38 +0700\n* ----------"
},
{
"path": "utils/tray-xembed-test/xutils.c",
"chars": 6423,
"preview": "/* ************************************\n * vim:tabstop=4:shiftwidth=4\n * xutils.c\n * Sun, 05 Mar 2006 17:56:56 +0600\n * "
},
{
"path": "utils/tray-xembed-test/xutils.h",
"chars": 2464,
"preview": "/* ************************************\n * vim:tabstop=4:shiftwidth=4\n * xutils.h\n * Sun, 05 Mar 2006 17:16:44 +0600\n * "
}
]
About this extraction
This page contains the full source code of the kolbusa/stalonetray GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 68 files (393.5 KB), approximately 111.2k tokens, and a symbol index with 352 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.