Compare commits
285 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ee460335d | ||
|
|
988b8d2747 | ||
|
|
9f4f110c53 | ||
|
|
e898fa589a | ||
|
|
702abc7a03 | ||
|
|
b77f76f02e | ||
|
|
cf091bea37 | ||
|
|
1040febfb9 | ||
|
|
652cb7efb4 | ||
|
|
7d91f978eb | ||
|
|
0994c57b3e | ||
|
|
cb6f2ca3b3 | ||
|
|
bd6abfcc5d | ||
|
|
1d3822ee8b | ||
|
|
ea531ca9ae | ||
|
|
e69adcefea | ||
|
|
f2017eb3d6 | ||
|
|
d9282b7f86 | ||
|
|
efac6e70b0 | ||
|
|
0dfc4ed738 | ||
|
|
b49925c86d | ||
|
|
8e80871c50 | ||
|
|
d5ee1febca | ||
|
|
941c527af9 | ||
|
|
b66f8f362d | ||
|
|
85ff05062c | ||
|
|
db5e6ce8f4 | ||
|
|
aad4dbaf3d | ||
|
|
98a61f0896 | ||
|
|
7c6620c92d | ||
|
|
75af42c83a | ||
|
|
c4e428e9d5 | ||
|
|
5c0d7ea662 | ||
|
|
176aad4f3c | ||
|
|
af49b76586 | ||
|
|
6c61f3e5e2 | ||
|
|
bec5e9d25a | ||
|
|
790342b913 | ||
|
|
4234813417 | ||
|
|
d65f69b4db | ||
|
|
b75d35adb4 | ||
|
|
3acffdb194 | ||
|
|
7e22272ebb | ||
|
|
7292bfcd89 | ||
|
|
8413ade9d7 | ||
|
|
54b665898f | ||
|
|
f8a2ff48b3 | ||
|
|
a8b2058fcf | ||
|
|
ceca7cfcc7 | ||
|
|
598ec5797e | ||
|
|
f615866248 | ||
|
|
5214a61e9f | ||
|
|
0cec75e45e | ||
|
|
ace514ffc7 | ||
|
|
f976c2d44c | ||
|
|
399489567c | ||
|
|
a94767e492 | ||
|
|
2ba788d9c6 | ||
|
|
6e972d30ca | ||
|
|
1f4278a5e2 | ||
|
|
ec044eced0 | ||
|
|
0f588998fe | ||
|
|
500b00b344 | ||
|
|
36186d37ea | ||
|
|
d7f93a3a69 | ||
|
|
814e620247 | ||
|
|
bc3de38aa7 | ||
|
|
52134ab8ce | ||
|
|
0d5cbfcfce | ||
|
|
293d71264e | ||
|
|
59f994ee94 | ||
|
|
e90f605a52 | ||
|
|
63103f991d | ||
|
|
d0dfaf0488 | ||
|
|
22d108f53e | ||
|
|
5d1b58559c | ||
|
|
c6638c30d5 | ||
|
|
92bc6705ca | ||
|
|
be8e32098f | ||
|
|
9615431593 | ||
|
|
7e9650ad95 | ||
|
|
ebcd20d8d0 | ||
|
|
77bfbc4f7f | ||
|
|
3822f09185 | ||
|
|
3ba614b59b | ||
|
|
6a6cd6697f | ||
|
|
bb12727bc0 | ||
|
|
5a20d463b5 | ||
|
|
af10f6f8cd | ||
|
|
3441f4c68d | ||
|
|
756f822a6b | ||
|
|
ae38efedbb | ||
|
|
7b26b4bd94 | ||
|
|
20eebb80e1 | ||
|
|
c40e797e6b | ||
|
|
b094074ff2 | ||
|
|
25e1d0c442 | ||
|
|
836d044df8 | ||
|
|
ac6093a577 | ||
|
|
deabe2bacf | ||
|
|
859c18a2e9 | ||
|
|
74c5d97181 | ||
|
|
e5fec3326e | ||
|
|
705e575d6d | ||
|
|
32119a1df7 | ||
|
|
ed560eab4e | ||
|
|
c1caf4bbc7 | ||
|
|
72ceedd4db | ||
|
|
4a9c7693f4 | ||
|
|
167d7dd7c3 | ||
|
|
1827f0d974 | ||
|
|
0e6997b6fc | ||
|
|
4d74c7b46e | ||
|
|
5b81b43aa6 | ||
|
|
137361a95d | ||
|
|
313731f0f2 | ||
|
|
a89287cb3e | ||
|
|
8f9e85f122 | ||
|
|
3d9717a8a3 | ||
|
|
c73009160e | ||
|
|
732227eff5 | ||
|
|
f866f8e4e7 | ||
|
|
8df6bc9554 | ||
|
|
c37a923def | ||
|
|
0bcc135ffd | ||
|
|
58d215ddfe | ||
|
|
756e6662e1 | ||
|
|
b758e4925b | ||
|
|
3d3ba4b9c5 | ||
|
|
4f10496123 | ||
|
|
8e3eade7ba | ||
|
|
62ce907ecd | ||
|
|
bfcf655b1e | ||
|
|
5caac20268 | ||
|
|
3001f79fc3 | ||
|
|
55d700f746 | ||
|
|
9009033d59 | ||
|
|
8440ac3d23 | ||
|
|
93b52df269 | ||
|
|
72907cc744 | ||
|
|
0467e46474 | ||
|
|
bc6ab69390 | ||
|
|
8831260be3 | ||
|
|
f00625692b | ||
|
|
6734fa3135 | ||
|
|
e59d203256 | ||
|
|
ca6ce6868e | ||
|
|
6f5f003e2b | ||
|
|
37feb7d6a3 | ||
|
|
28c5074fab | ||
|
|
ca1bf262ce | ||
|
|
8c07ffb173 | ||
|
|
52d67616f1 | ||
|
|
3a77b879e0 | ||
|
|
bc86ce78a7 | ||
|
|
c01f81b692 | ||
|
|
9db8b76473 | ||
|
|
1b8f56eb45 | ||
|
|
eb253a4547 | ||
|
|
842a5242f7 | ||
|
|
7bd55a53bf | ||
|
|
7796f7f848 | ||
|
|
0a08cefc68 | ||
|
|
762d37758d | ||
|
|
b040fca067 | ||
|
|
3c33f4559f | ||
|
|
4d66a60a4f | ||
|
|
0de8610b67 | ||
|
|
8afb7fcd1a | ||
|
|
1ca271084a | ||
|
|
00851dd4d3 | ||
|
|
ed3db5048c | ||
|
|
7735c9a4f6 | ||
|
|
d885e93b73 | ||
|
|
12bea04941 | ||
|
|
6413a4d6f8 | ||
|
|
d110ba93e6 | ||
|
|
98debc663a | ||
|
|
d45888a785 | ||
|
|
6b28d57730 | ||
|
|
1e36a6b4ac | ||
|
|
7226ef2c15 | ||
|
|
2e2b3e549f | ||
|
|
9833fbba1f | ||
|
|
8d8e6882e8 | ||
|
|
813417915c | ||
|
|
2de1603cd2 | ||
|
|
10c82ed7fe | ||
|
|
73b7176e0b | ||
|
|
85ea8b8aa8 | ||
|
|
791f514078 | ||
|
|
2bf4462154 | ||
|
|
6b6b63da17 | ||
|
|
296cea499c | ||
|
|
0e35c6acb0 | ||
|
|
5fd090e618 | ||
|
|
a9d32c4d78 | ||
|
|
289b4f50eb | ||
|
|
dcf060effa | ||
|
|
d964455d36 | ||
|
|
d3f6c05590 | ||
|
|
7a094bc702 | ||
|
|
c09f0f029f | ||
|
|
dbbaecfd1d | ||
|
|
16e38a9a84 | ||
|
|
4c4dc9aa95 | ||
|
|
fb2ffd425e | ||
|
|
cfa8625699 | ||
|
|
f868f51fcd | ||
|
|
0dfcc1420d | ||
|
|
2e301b1d39 | ||
|
|
b8aa8c15e8 | ||
|
|
c45a9eac73 | ||
|
|
337ff1edb5 | ||
|
|
50a55f2ff1 | ||
|
|
6ba430a818 | ||
|
|
5aa1863762 | ||
|
|
94aa427a1b | ||
|
|
23fa204e69 | ||
|
|
a91adf9c80 | ||
|
|
36eed785d2 | ||
|
|
ec421f1ea1 | ||
|
|
9049bf15b2 | ||
|
|
ee11dfa8fe | ||
|
|
936dbb039c | ||
|
|
b1f64b266b | ||
|
|
e14160bda6 | ||
|
|
6a8401d85a | ||
|
|
d45d26e257 | ||
|
|
7e92619967 | ||
|
|
6b60c52ee1 | ||
|
|
e8fc45da63 | ||
|
|
a3342d0ced | ||
|
|
e80e8e9eb5 | ||
|
|
72d68fcec4 | ||
|
|
305ab6c4e6 | ||
|
|
55f4971a92 | ||
|
|
523d5e199e | ||
|
|
b4633a0061 | ||
|
|
92a21487b2 | ||
|
|
8bcc3070fb | ||
|
|
a4a1a1ac9e | ||
|
|
506b4f2cfa | ||
|
|
94be674cf8 | ||
|
|
0d038021a6 | ||
|
|
8fafc456cb | ||
|
|
c6f1f169a7 | ||
|
|
520c36ad7a | ||
|
|
12873ccd78 | ||
|
|
697db7198d | ||
|
|
7e2b148262 | ||
|
|
9e5a647092 | ||
|
|
c4fe74df37 | ||
|
|
db76fff1ca | ||
|
|
29e02c2a29 | ||
|
|
ef88e39de6 | ||
|
|
b3e640a8ef | ||
|
|
69e319c729 | ||
|
|
43c614559c | ||
|
|
fbdef7b58a | ||
|
|
9591af02a8 | ||
|
|
dfd6e76de5 | ||
|
|
4c09883ef6 | ||
|
|
e665eea97e | ||
|
|
f9b6f6a066 | ||
|
|
89d7a81b9c | ||
|
|
3f3082efb3 | ||
|
|
ad5d20bd6a | ||
|
|
03253e82f7 | ||
|
|
667503ef7d | ||
|
|
87491192cb | ||
|
|
9264b0f994 | ||
|
|
445d305bf8 | ||
|
|
e077b949a6 | ||
|
|
39c97e28f6 | ||
|
|
364bce1ed0 | ||
|
|
ab3b5c2805 | ||
|
|
9e29406bb5 | ||
|
|
68daebb713 | ||
|
|
eddcd2bfdf | ||
|
|
53e7f3d16a | ||
|
|
f37a5d1c64 | ||
|
|
13307d2b17 | ||
|
|
52a8489043 | ||
|
|
6c4eeda434 |
@ -1,71 +0,0 @@
|
||||
version: 2.1
|
||||
jobs:
|
||||
misc-doxygen:
|
||||
docker:
|
||||
- image: dunst/ci:misc-doxygen
|
||||
environment:
|
||||
SYSTEMD: 0
|
||||
SERVICEDIR_DBUS: /tmp/none
|
||||
SERVICEDIR_SYSTEMD: /tmp/none
|
||||
PKG_CONFIG: echo
|
||||
steps:
|
||||
- checkout
|
||||
- run: make -j doc-doxygen
|
||||
|
||||
compileandtest:
|
||||
environment:
|
||||
CFLAGS: -Werror
|
||||
parameters:
|
||||
distro:
|
||||
type: string
|
||||
docker:
|
||||
- image: dunst/ci:<<parameters.distro>>
|
||||
steps:
|
||||
- checkout
|
||||
- run: make CC=clang -j all dunstify test/test
|
||||
- run: make CC=clang -j test-valgrind
|
||||
- run: ./test/test-install.sh
|
||||
- run: make clean
|
||||
- run: make CC=gcc -j all dunstify test/test
|
||||
- run: make CC=gcc -j test-valgrind
|
||||
- run: make clean
|
||||
- run: make -j test-coverage
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build-in-docker:
|
||||
jobs:
|
||||
- misc-doxygen
|
||||
- compileandtest:
|
||||
name: Alpine
|
||||
distro: alpine
|
||||
- compileandtest:
|
||||
name: Debian Stretch
|
||||
distro: debian-stretch
|
||||
requires:
|
||||
- misc-doxygen
|
||||
- Alpine
|
||||
- compileandtest:
|
||||
name: Arch Linux
|
||||
distro: archlinux
|
||||
requires:
|
||||
- misc-doxygen
|
||||
- Alpine
|
||||
- compileandtest:
|
||||
name: Fedora 30
|
||||
distro: fedora30
|
||||
requires:
|
||||
- misc-doxygen
|
||||
- Alpine
|
||||
- compileandtest:
|
||||
name: Ubuntu 16.04
|
||||
distro: ubuntu-xenial
|
||||
requires:
|
||||
- misc-doxygen
|
||||
- Alpine
|
||||
- compileandtest:
|
||||
name: Ubuntu 18.04
|
||||
distro: ubuntu-bionic
|
||||
requires:
|
||||
- misc-doxygen
|
||||
- Alpine
|
||||
82
.github/workflows/main.yml
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
name: main
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
CC:
|
||||
- clang
|
||||
- gcc
|
||||
distro:
|
||||
- alpine
|
||||
- archlinux
|
||||
- debian-stretch
|
||||
- debian-buster
|
||||
- fedora
|
||||
- ubuntu-xenial
|
||||
- ubuntu-bionic
|
||||
- ubuntu-focal
|
||||
|
||||
env:
|
||||
CC: ${{ matrix.CC }}
|
||||
EXTRA_CFLAGS: "-Werror"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
# Clone the whole branch, we have to fetch tags later
|
||||
fetch-depth: 0
|
||||
|
||||
# Fetch tags to determine proper version number inside git
|
||||
- name: fetch tags
|
||||
run: git fetch --tags
|
||||
# We cannot pull tags with old distros, since there is no `.git`. See below.
|
||||
if: "! (matrix.distro == 'ubuntu-bionic' || matrix.distro == 'ubuntu-xenial' || matrix.distro == 'debian-stretch')"
|
||||
|
||||
# The Github checkout Action doesn't support distros with git older than 2.18
|
||||
# With git<2.18 it downloads the code via API and does not clone it via git :facepalm:
|
||||
# To succeed the tests, we have to manually replace the VERSION macro
|
||||
- name: fix version number for old distros
|
||||
run: 'sed -i "s/-non-git/-ci-oldgit-$GITHUB_SHA/" Makefile'
|
||||
if: " (matrix.distro == 'ubuntu-bionic' || matrix.distro == 'ubuntu-xenial' || matrix.distro == 'debian-stretch')"
|
||||
|
||||
- name: build
|
||||
run: make -j all dunstify test/test
|
||||
|
||||
- name: test
|
||||
run: make -j test
|
||||
|
||||
- name: installation
|
||||
run: ./test/test-install.sh
|
||||
|
||||
- name: valgrind memleaks
|
||||
run: |
|
||||
make clean
|
||||
make -j test-valgrind
|
||||
|
||||
- name: coverage
|
||||
run: |
|
||||
make clean
|
||||
make -j test-coverage
|
||||
|
||||
- name: Generate coverage report
|
||||
run: lcov -c -d . -o lcov.info
|
||||
if: "matrix.CC == 'gcc'"
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
flags: unittests
|
||||
name: ${{ matrix.distro }}-${{ matrix.CC }}
|
||||
fail_ci_if_error: true
|
||||
if: "matrix.CC == 'gcc'"
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: dunst/ci:${{ matrix.distro }}
|
||||
4
.gitignore
vendored
@ -3,11 +3,13 @@
|
||||
*.gcda
|
||||
*.gcno
|
||||
*.gcov
|
||||
/lcov.info
|
||||
|
||||
core
|
||||
vgcore.*
|
||||
|
||||
/docs/dunst.1
|
||||
/docs/*.1
|
||||
/docs/*.5
|
||||
/docs/internal/coverage
|
||||
/docs/internal/html
|
||||
/dunst
|
||||
|
||||
38
.travis.yml
@ -1,38 +0,0 @@
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- doxygen
|
||||
- graphviz
|
||||
- libdbus-1-dev
|
||||
- libx11-dev
|
||||
- libxrandr-dev
|
||||
- libxinerama-dev
|
||||
- libxss-dev
|
||||
- libglib2.0-dev
|
||||
- libpango1.0-dev
|
||||
- libcairo2-dev
|
||||
- libnotify-dev
|
||||
- libgtk-3-dev
|
||||
- valgrind
|
||||
dist: xenial
|
||||
sudo: false
|
||||
language: c
|
||||
|
||||
git:
|
||||
depth: false
|
||||
|
||||
before_install:
|
||||
- pip install --user cpp-coveralls
|
||||
|
||||
script:
|
||||
- CFLAGS="-Werror" make all dunstify test-valgrind doc-doxygen
|
||||
- ./test/test-install.sh
|
||||
- CFLAGS="-Werror" make clean
|
||||
- CFLAGS="-Werror" make test-coverage
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- compiler: gcc
|
||||
after_success:
|
||||
- coveralls
|
||||
- compiler: clang
|
||||
@ -20,14 +20,48 @@
|
||||
fun:g_error_new_valist
|
||||
fun:g_set_error
|
||||
obj:*/librsvg-2.so*
|
||||
fun:rsvg_handle_close
|
||||
obj:*/loaders/libpixbufloader-svg.so
|
||||
fun:gdk_pixbuf_loader_close
|
||||
fun:gdk_pixbuf_get_file_info
|
||||
fun:get_pixbuf_from_file
|
||||
...
|
||||
}
|
||||
|
||||
# same as above, but as occurs in CI environment
|
||||
{
|
||||
rsvg_error_handle_close2
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: definite
|
||||
fun:malloc
|
||||
fun:g_malloc
|
||||
fun:g_slice_alloc
|
||||
fun:g_error_new_valist
|
||||
fun:g_set_error
|
||||
obj:*/librsvg-2.so*
|
||||
obj:*/librsvg-2.so*
|
||||
obj:*/loaders/libpixbufloader-svg.so
|
||||
obj:*/libgdk_pixbuf-2.0.so*
|
||||
fun:gdk_pixbuf_new_from_file
|
||||
fun:gdk_pixbuf_loader_close
|
||||
fun:gdk_pixbuf_get_file_info
|
||||
fun:get_pixbuf_from_file
|
||||
...
|
||||
}
|
||||
|
||||
# Some new in ArchLinux
|
||||
{
|
||||
rsvg_rust_handle_close
|
||||
Memcheck:Leak
|
||||
match-leak-kinds: definite
|
||||
fun:malloc
|
||||
...
|
||||
fun:rsvg_rust_handle_close
|
||||
obj:*/loaders/libpixbufloader-svg.so
|
||||
...
|
||||
fun:gdk_pixbuf_new_from_file
|
||||
...
|
||||
}
|
||||
|
||||
# rsvg_error_writehandler got fixed in
|
||||
# - GNOME/librsvg@7b4cc9b
|
||||
# (2018-11-12, first tags: v2.45.0, v2.44.9)
|
||||
@ -45,7 +79,8 @@
|
||||
fun:rsvg_handle_write
|
||||
obj:*/loaders/libpixbufloader-svg.so
|
||||
obj:*/libgdk_pixbuf-2.0.so*
|
||||
fun:gdk_pixbuf_new_from_file
|
||||
fun:gdk_pixbuf_loader_close
|
||||
fun:gdk_pixbuf_get_file_info
|
||||
fun:get_pixbuf_from_file
|
||||
...
|
||||
}
|
||||
|
||||
61
CHANGELOG.md
@ -1,5 +1,66 @@
|
||||
# Dunst changelog
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
### Changed
|
||||
### Fixed
|
||||
|
||||
## 1.6.1 - 2021-02-21:
|
||||
|
||||
### Fixed
|
||||
- Incorrect version in Makefile
|
||||
|
||||
## 1.6.0 - 2021-02-21:
|
||||
|
||||
### Added
|
||||
- Wayland support. Dunst now runs natively on wayland. This fixes several bugs
|
||||
with dunst on wayland and allows idle detection. (#781)
|
||||
- A progress bar, useful for showing volume or brightness in notifications (#775)
|
||||
- A script in contrib for using the progress bar (#791)
|
||||
- `dunstctl count` for showing the number of notifications (#793)
|
||||
- Expose environment variables info about the notification to scripts (#802)
|
||||
- `text_icon_padding` for adding padding between the notification icon and text
|
||||
(#810)
|
||||
|
||||
### Changed
|
||||
- Dunst now installs a system-wide config in `/etc/dunst/dunstrc` (#798)
|
||||
- Move part of the man page to dunst(5) (#799)
|
||||
|
||||
### Fixed
|
||||
- `history_ignore` flag broken when using multiple rules (#747)
|
||||
- Divide by zero in radius calculation (#750)
|
||||
- Monitor setting overriding `follow_mode` (#755)
|
||||
- Incorrect monitor usage when using multiple X11 screens (#762)
|
||||
- Emit signal when `paused` property changes (#766)
|
||||
- `dunstify` can pass empty appname to libnotify (#768)
|
||||
- Incorrect handling of 'do_action, close' mouse action (#778)
|
||||
|
||||
# Removed
|
||||
|
||||
- `DUNST_COMMAND_{PAUSE,RESUME,TOGGLE}` (#830)
|
||||
|
||||
## 1.5.0 - 2020-07-23
|
||||
|
||||
### Added
|
||||
- `min_icon_size` option to automatically scale up icons to a desired value (#674)
|
||||
- `vertical_alignment` option to control the text/icon alignment within the notification (#684)
|
||||
- Ability to configure multiple actions for each mouse event (#705)
|
||||
- `dunstctl` command line control client (#651)
|
||||
- RGBA support for all color strings (#717)
|
||||
- Ability to run multiple scripts for each notification
|
||||
- `ignore_dbusclose` setting (#732)
|
||||
|
||||
### Changed
|
||||
- `dunstify` notification client is now installed by default (#701)
|
||||
- Keyboard follow mode falls back to the monitor with the mouse if no window has keyboard focus (#708)
|
||||
|
||||
### Fixed
|
||||
- Overflow when setting a >=40 minute timeout (#646)
|
||||
- Unset configuration options not falling back to default values (#649)
|
||||
- Crash when `$HOME` environment variable is unset (#693)
|
||||
- Lack of antialiasing with round corners enabled (#713)
|
||||
|
||||
## 1.4.1 - 2019-07-03
|
||||
|
||||
### Fixed
|
||||
|
||||
@ -15,6 +15,10 @@
|
||||
- Add the comments to the prototype. Doxygen will merge the protoype and implementation documentation anyways.
|
||||
Except for **static** methods, add the documentation header to the implementation and *not to the prototype*.
|
||||
- Member documentation should happen with `/**<` and should span to the right side of the member
|
||||
- Test files that have the same name as a file in src/\* can include the
|
||||
associated .c file. This is because they are being compiled INSTEAD of the src
|
||||
file.
|
||||
|
||||
|
||||
## Log messages
|
||||
|
||||
|
||||
86
Makefile
@ -3,7 +3,7 @@
|
||||
|
||||
include config.mk
|
||||
|
||||
VERSION := "1.4.1 (2019-07-03)"
|
||||
VERSION := "1.6.1-non-git"
|
||||
ifneq ($(wildcard ./.git/),)
|
||||
VERSION := $(shell ${GIT} describe --tags)
|
||||
endif
|
||||
@ -33,6 +33,14 @@ $(error "Failed to query $(PKG_CONFIG) for package 'systemd'!")
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq (0,${WAYLAND})
|
||||
DATA_DIR_WAYLAND_PROTOCOLS ?= $(shell $(PKG_CONFIG) wayland-protocols --variable=pkgdatadir)
|
||||
DATA_DIR_WAYLAND_PROTOCOLS := ${DATA_DIR_WAYLAND_PROTOCOLS}
|
||||
ifeq (,${DATA_DIR_WAYLAND_PROTOCOLS})
|
||||
$(warning "Failed to query $(PKG_CONFIG) for package 'wayland-protocols'!")
|
||||
endif
|
||||
endif
|
||||
|
||||
LIBS := $(shell $(PKG_CONFIG) --libs ${pkg_config_packs})
|
||||
INCS := $(shell $(PKG_CONFIG) --cflags ${pkg_config_packs})
|
||||
|
||||
@ -45,7 +53,14 @@ endif
|
||||
CFLAGS := ${DEFAULT_CPPFLAGS} ${CPPFLAGS} ${DEFAULT_CFLAGS} ${CFLAGS} ${INCS} -MMD -MP
|
||||
LDFLAGS := ${DEFAULT_LDFLAGS} ${LDFLAGS} ${LIBS}
|
||||
|
||||
|
||||
ifeq (0,${WAYLAND})
|
||||
# without wayland support
|
||||
SRC := $(sort $(shell ${FIND} src/ -not \( -path src/wayland -prune \) -name '*.c'))
|
||||
else
|
||||
# with Wayland support
|
||||
SRC := $(sort $(shell ${FIND} src/ -name '*.c'))
|
||||
endif
|
||||
OBJ := ${SRC:.c=.o}
|
||||
TEST_SRC := $(sort $(shell ${FIND} test/ -name '*.c'))
|
||||
TEST_OBJ := $(TEST_SRC:.c=.o)
|
||||
@ -53,7 +68,7 @@ DEPS := ${SRC:.c=.d} ${TEST_SRC:.c=.d}
|
||||
|
||||
|
||||
.PHONY: all debug
|
||||
all: doc dunst service
|
||||
all: doc dunst dunstify service
|
||||
|
||||
debug: CFLAGS += ${CPPFLAGS_DEBUG} ${CFLAGS_DEBUG}
|
||||
debug: LDFLAGS += ${LDFLAGS_DEBUG}
|
||||
@ -75,7 +90,9 @@ dunstify: dunstify.o
|
||||
|
||||
.PHONY: test test-valgrind test-coverage
|
||||
test: test/test clean-coverage-run
|
||||
./test/test -v
|
||||
# Make sure an error code is returned when the test fails
|
||||
/usr/bin/env bash -c 'set -euo pipefail;\
|
||||
./test/test -v | ./test/greenest.awk '
|
||||
|
||||
test-valgrind: test/test
|
||||
${VALGRIND} \
|
||||
@ -106,13 +123,21 @@ test/test: ${OBJ} ${TEST_OBJ}
|
||||
${CC} -o ${@} ${TEST_OBJ} $(filter-out ${TEST_OBJ:test/%=src/%},${OBJ}) ${CFLAGS} ${LDFLAGS}
|
||||
|
||||
.PHONY: doc doc-doxygen
|
||||
doc: docs/dunst.1
|
||||
docs/dunst.1: docs/dunst.pod
|
||||
doc: docs/dunst.1 docs/dunst.5 docs/dunstctl.1
|
||||
|
||||
# Can't dedup this as we need to explicitly provide the name and title text to
|
||||
# pod2man :(
|
||||
docs/dunst.1: docs/dunst.1.pod
|
||||
${POD2MAN} --name=dunst -c "Dunst Reference" --section=1 --release=${VERSION} $< > $@
|
||||
docs/dunst.5: docs/dunst.5.pod
|
||||
${POD2MAN} --name=dunst -c "Dunst Reference" --section=5 --release=${VERSION} $< > $@
|
||||
docs/dunstctl.1: docs/dunstctl.pod
|
||||
${POD2MAN} --name=dunstctl -c "dunstctl reference" --section=1 --release=${VERSION} $< > $@
|
||||
|
||||
doc-doxygen:
|
||||
${DOXYGEN} docs/internal/Doxyfile
|
||||
|
||||
.PHONY: service service-dbus service-systemd
|
||||
.PHONY: service service-dbus service-systemd wayland-protocols
|
||||
service: service-dbus
|
||||
service-dbus:
|
||||
@${SED} "s|##PREFIX##|$(PREFIX)|" org.knopwob.dunst.service.in > org.knopwob.dunst.service
|
||||
@ -122,7 +147,23 @@ service-systemd:
|
||||
@${SED} "s|##PREFIX##|$(PREFIX)|" dunst.systemd.service.in > dunst.systemd.service
|
||||
endif
|
||||
|
||||
.PHONY: clean clean-dunst clean-dunstify clean-doc clean-tests clean-coverage clean-coverage-run
|
||||
ifneq (0,${WAYLAND})
|
||||
wayland-protocols: src/wayland/protocols/wlr-layer-shell-unstable-v1.xml src/wayland/protocols/wlr-foreign-toplevel-management-unstable-v1.xml
|
||||
# TODO: write this shorter
|
||||
mkdir -p src/wayland/protocols
|
||||
wayland-scanner private-code ${DATA_DIR_WAYLAND_PROTOCOLS}/stable/xdg-shell/xdg-shell.xml src/wayland/protocols/xdg-shell.h
|
||||
wayland-scanner client-header ${DATA_DIR_WAYLAND_PROTOCOLS}/stable/xdg-shell/xdg-shell.xml src/wayland/protocols/xdg-shell-client-header.h
|
||||
wayland-scanner client-header ${DATA_DIR_WAYLAND_PROTOCOLS}/unstable/xdg-output/xdg-output-unstable-v1.xml src/wayland/protocols/xdg-output-unstable-v1-client-header.h
|
||||
wayland-scanner private-code ${DATA_DIR_WAYLAND_PROTOCOLS}/unstable/xdg-output/xdg-output-unstable-v1.xml src/wayland/protocols/xdg-output-unstable-v1.h
|
||||
wayland-scanner client-header src/wayland/protocols/wlr-layer-shell-unstable-v1.xml src/wayland/protocols/wlr-layer-shell-unstable-v1-client-header.h
|
||||
wayland-scanner private-code src/wayland/protocols/wlr-layer-shell-unstable-v1.xml src/wayland/protocols/wlr-layer-shell-unstable-v1.h
|
||||
wayland-scanner client-header src/wayland/protocols/idle.xml src/wayland/protocols/idle-client-header.h
|
||||
wayland-scanner private-code src/wayland/protocols/idle.xml src/wayland/protocols/idle.h
|
||||
wayland-scanner client-header src/wayland/protocols/wlr-foreign-toplevel-management-unstable-v1.xml src/wayland/protocols/wlr-foreign-toplevel-management-unstable-v1-client-header.h
|
||||
wayland-scanner private-code src/wayland/protocols/wlr-foreign-toplevel-management-unstable-v1.xml src/wayland/protocols/wlr-foreign-toplevel-management-unstable-v1.h
|
||||
endif
|
||||
|
||||
.PHONY: clean clean-dunst clean-dunstify clean-doc clean-tests clean-coverage clean-coverage-run clean-wayland-protocols
|
||||
clean: clean-dunst clean-dunstify clean-doc clean-tests clean-coverage clean-coverage-run
|
||||
|
||||
clean-dunst:
|
||||
@ -137,6 +178,8 @@ clean-dunstify:
|
||||
|
||||
clean-doc:
|
||||
rm -f docs/dunst.1
|
||||
rm -f docs/dunst.5
|
||||
rm -f docs/dunstctl.1
|
||||
rm -fr docs/internal/html
|
||||
rm -fr docs/internal/coverage
|
||||
|
||||
@ -151,18 +194,26 @@ clean-coverage-run:
|
||||
${FIND} . -type f -name '*.gcov' -delete
|
||||
${FIND} . -type f -name '*.gcda' -delete
|
||||
|
||||
.PHONY: install install-dunst install-doc \
|
||||
clean-wayland-protocols:
|
||||
rm -f src/wayland/protocols/*.h
|
||||
|
||||
.PHONY: install install-dunst install-dunstctl install-doc \
|
||||
install-service install-service-dbus install-service-systemd \
|
||||
uninstall \
|
||||
uninstall uninstall-dunstctl \
|
||||
uninstall-service uninstall-service-dbus uninstall-service-systemd
|
||||
install: install-dunst install-doc install-service
|
||||
install: install-dunst install-dunstctl install-doc install-service install-dunstify
|
||||
|
||||
install-dunst: dunst doc
|
||||
install -Dm755 dunst ${DESTDIR}${BINDIR}/dunst
|
||||
install -Dm644 docs/dunst.1 ${DESTDIR}${MANPREFIX}/man1/dunst.1
|
||||
install -Dm644 docs/dunst.5 ${DESTDIR}${MANPREFIX}/man5/dunst.5
|
||||
install -Dm644 docs/dunstctl.1 ${DESTDIR}${MANPREFIX}/man1/dunstctl.1
|
||||
|
||||
install-dunstctl: dunstctl
|
||||
install -Dm755 dunstctl ${DESTDIR}${BINDIR}/dunstctl
|
||||
|
||||
install-doc:
|
||||
install -Dm644 dunstrc ${DESTDIR}${DATADIR}/dunst/dunstrc
|
||||
install -Dm644 dunstrc ${DESTDIR}${SYSCONFDIR}/dunst/dunstrc
|
||||
|
||||
install-service: install-service-dbus
|
||||
install-service-dbus: service-dbus
|
||||
@ -173,10 +224,19 @@ install-service-systemd: service-systemd
|
||||
install -Dm644 dunst.systemd.service ${DESTDIR}${SERVICEDIR_SYSTEMD}/dunst.service
|
||||
endif
|
||||
|
||||
uninstall: uninstall-service
|
||||
install-dunstify: dunstify
|
||||
install -Dm755 dunstify ${DESTDIR}${BINDIR}/dunstify
|
||||
|
||||
uninstall: uninstall-service uninstall-dunstctl
|
||||
rm -f ${DESTDIR}${BINDIR}/dunst
|
||||
rm -f ${DESTDIR}${BINDIR}/dunstify
|
||||
rm -f ${DESTDIR}${MANPREFIX}/man1/dunst.1
|
||||
rm -rf ${DESTDIR}${DATADIR}/dunst
|
||||
rm -f ${DESTDIR}${MANPREFIX}/man5/dunst.5
|
||||
rm -f ${DESTDIR}${MANPREFIX}/man1/dunstctl.1
|
||||
rm -rf ${DESTDIR}${SYSCONFDIR}/dunst
|
||||
|
||||
uninstall-dunstctl:
|
||||
rm -f ${DESTDIR}${BINDIR}/dunstctl
|
||||
|
||||
uninstall-service: uninstall-service-dbus
|
||||
uninstall-service-dbus:
|
||||
|
||||
113
README.md
@ -1,30 +1,94 @@
|
||||
[](https://circleci.com/gh/dunst-project/dunst/tree/master) [](https://travis-ci.org/dunst-project/dunst) [](https://coveralls.io/github/dunst-project/dunst?branch=master)
|
||||
[](https://github.com/dunst-project/dunst/actions?query=workflow%3Amain) [](https://codecov.io/gh/dunst-project/dunst)
|
||||
|
||||
## Dunst
|
||||
# Dunst
|
||||
|
||||
* [Wiki][wiki]
|
||||
* [Description](#description)
|
||||
* [Compiling](#compiling)
|
||||
<i>A highly configurable and lightweight notification daemon.</i>
|
||||
|
||||

|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Features](#features)
|
||||
* [Building](#building)
|
||||
* [Documentation](#documentation)
|
||||
* [Copyright](#copyright)
|
||||
|
||||
## Description
|
||||
# Features
|
||||
|
||||
Dunst is a highly configurable and lightweight notification daemon.
|
||||
## ⚙️ Highly customizable
|
||||
|
||||
Customize fonts, icons, timeouts, and more. Are you unhappy with the default
|
||||
shortcuts and colors? No worries, you can change these all with a simple
|
||||
configuration file tweak.
|
||||
|
||||
_click the images to see the dunstrc_
|
||||
|
||||
<a href="https://gist.github.com/NNBnh/5f6e601a6a82a6ed43b1959698758141">
|
||||
<img alt="screenshot1" src="contrib/screenshots/screenshot1_cut.png">
|
||||
</a>
|
||||
|
||||
<a href="https://gist.github.com/fwSmit/9127d988b07bcec9d869f2c927d0f616">
|
||||
<img alt="screenshot2" src="contrib/screenshots/screenshot2_cut.png">
|
||||
</a>
|
||||
|
||||
## 📜 Scripting
|
||||
|
||||
<a href="https://gitlab.manjaro.org/profiles-and-settings/manjaro-theme-settings/-/blob/master/skel/.config/dunst/dunstrc">
|
||||
<img alt="screenshot_urgency" src="contrib/screenshots/screenshot_urgency.png">
|
||||
</a>
|
||||
|
||||
Run custom scripts on notifications matching a specified pattern. Have espeak
|
||||
read out your notifications, or play a song when your significant other signs on
|
||||
in pidgin!
|
||||
|
||||
## 📋 Rules
|
||||
|
||||
Change the look or behavior of notifications matching a specified pattern. You
|
||||
could use this to change the color of message notifications from your favorite
|
||||
jabber buddies, or to prevent important work email notifications from
|
||||
disappearing until you manually dismiss them.
|
||||
|
||||
## ⏸️ Pause
|
||||
|
||||
If you want to take a break and not receive any notifications for a while, just
|
||||
pause dunst. All notifications will be saved for you to catch up
|
||||
later.
|
||||
|
||||
## 🕘 History
|
||||
|
||||
Catch an unread notification disappearing from the corner of your eye? Just tap
|
||||
a keyboard shortcut to replay the last notification, or continue tapping to see
|
||||
your notification history.
|
||||
|
||||
# Documentation
|
||||
|
||||
Most documentation can be found in dunst's man pages. In
|
||||
[**dunst(1)**](docs/dunst.1.pod) contains some general instructions on how
|
||||
to run dunst and in
|
||||
[**dunst(5)**](docs/dunst.5.pod) all of dunst's configuration options are
|
||||
explained.
|
||||
|
||||
On the dunst [wiki][wiki] you can find guides and installation instructions and
|
||||
on the dunst [website][website] there is a [FAQ][FAQ] with common issues.
|
||||
|
||||
## Installation
|
||||
|
||||
Dunst is available in many package repositories. If it's not available in your
|
||||
distro's repositories, don't worry, it's not hard to build it yourself.
|
||||
|
||||
### Dependencies
|
||||
|
||||
Dunst has a number of build dependencies that must be present before attempting configuration. The names are different depending on [distribution](https://github.com/dunst-project/dunst/wiki/Dependencies):
|
||||
|
||||
- dbus
|
||||
- dbus (runtime)
|
||||
- libxinerama
|
||||
- libxrandr
|
||||
- libxss
|
||||
- glib
|
||||
- pango/cairo
|
||||
- libgtk-3-dev
|
||||
- libnotify (optional, for dunstify)
|
||||
- wayland-client (can build without, see [make parameters](#make-parameters))
|
||||
- wayland-protocols (optional, for recompiling protocols)
|
||||
|
||||
The names will be different depending on your [distribution](https://github.com/dunst-project/dunst/wiki/Dependencies).
|
||||
|
||||
### Building
|
||||
|
||||
@ -37,21 +101,32 @@ sudo make install
|
||||
|
||||
### Make parameters
|
||||
|
||||
- `DESTDIR=<PATH>`: Set the destination directory of the installation. (Default: `/`)
|
||||
- `PREFIX=<PATH>`: Set the prefix of the installation. (Default: `/usr/local`)
|
||||
- `BINDIR=<PATH>`: Set the `dunst` executable's path (Default: `${PREFIX}/bin`)
|
||||
- `DATADIR=<PATH>`: Set the path for shared files. (Default: `${PREFIX}/share`)
|
||||
- `MANDIR=<PATH>`: Set the prefix of the manpage. (Default: `${DATADIR}/man`)
|
||||
- `SYSTEMD=(0|1)`: Enable/Disable the systemd unit. (Default: detected via `pkg-config`)
|
||||
- `SYSTEMD=(0|1)`: Disable/Enable the systemd unit. (Default: detected via `pkg-config`)
|
||||
- `WAYLAND=(0|1)`: Disable/Enable wayland support. (Default: 1 (enabled))
|
||||
- `SERVICEDIR_SYSTEMD=<PATH>`: The path to put the systemd user service file. Unused, if `SYSTEMD=0`. (Default: detected via `pkg-config`)
|
||||
- `SERVICEDIR_DBUS=<PATH>`: The path to put the dbus service file. (Default: detected via `pkg-config`)
|
||||
- `EXTRA_CFLAGS=<FLAGS>`: Additional flags for the compiler.
|
||||
|
||||
**Make sure to run all make calls with the same parameter set. So when building with `make PREFIX=/usr`, you have to install it with `make PREFIX=/usr install`, too.**
|
||||
|
||||
Checkout the [wiki][wiki] for more information.
|
||||
|
||||
## Bug reports
|
||||
|
||||
Please use the [issue tracker][issue-tracker] provided by GitHub to send us bug reports or feature requests. You can also join us on the IRC channel `#dunst` on Freenode.
|
||||
Please use the [issue tracker][issue-tracker] provided by GitHub to send us bug reports or feature requests.
|
||||
|
||||
## Screenshots
|
||||
|
||||
<a href="https://gist.github.com/MCotocel/2b34486ae59ccda4319fcb93454d212c">
|
||||
<img alt="screenshot3" src="contrib/screenshots/screenshot3_cut.png">
|
||||
</a>
|
||||
|
||||
<a href="https://gitlab.manjaro.org/profiles-and-settings/manjaro-theme-settings/-/blob/master/skel/.config/dunst/dunstrc">
|
||||
<img alt="progress" src="https://user-images.githubusercontent.com/23078054/102542111-98b01e00-40b1-11eb-967e-bc952430bd06.png">
|
||||
</a>
|
||||
|
||||
## Maintainers
|
||||
|
||||
@ -60,13 +135,13 @@ Please use the [issue tracker][issue-tracker] provided by GitHub to send us bug
|
||||
|
||||
## Author
|
||||
|
||||
written by Sascha Kruse <dunst@knopwob.de>
|
||||
Written by Sascha Kruse <dunst@knopwob.de>
|
||||
|
||||
## Copyright
|
||||
|
||||
copyright 2013 Sascha Kruse and contributors (see [`LICENSE`](./LICENSE) for licensing information)
|
||||
|
||||
If you feel that copyrights are violated, please send me an email.
|
||||
Copyright 2013 Sascha Kruse and contributors (see [`LICENSE`](./LICENSE) for licensing information)
|
||||
|
||||
[issue-tracker]: https://github.com/dunst-project/dunst/issues
|
||||
[wiki]: https://github.com/dunst-project/dunst/wiki
|
||||
[website]: https://dunst-project.org
|
||||
[FAQ]: https://dunst-project.org/faq
|
||||
|
||||
@ -1,3 +1,50 @@
|
||||
===================================================================================
|
||||
Release Notes For v1.6.0
|
||||
===================================================================================
|
||||
|
||||
For users:
|
||||
|
||||
At long last, dunst has native wayland support. On startup dunst will now
|
||||
autodetect the display environment it's run on and use the appropriate backend
|
||||
(X11 or wayland).
|
||||
Additionally, support for progress bars has been added when the 'value' hint is
|
||||
used. Try it out with `notify-send -h int:value:70 'Progress bars!'`
|
||||
|
||||
Last but most importantly, support for the
|
||||
`DUNST_COMMAND_{PAUSE,RESUME,TOGGLE}` has been removed as they could
|
||||
potentially be used to DoS dunst. `dunstctl` has been available as a direct
|
||||
replacement for the use-case they served since last release. See
|
||||
https://github.com/dunst-project/dunst/pull/830 for details
|
||||
|
||||
For maintainers:
|
||||
|
||||
Dunst now depends on the wayland libraries and (optionally) on the
|
||||
wayland-protocols package. A global configuration file is now installed by
|
||||
default in `/etc/dunst/dunstrc`
|
||||
|
||||
|
||||
===================================================================================
|
||||
Release Notes For v1.5.0
|
||||
===================================================================================
|
||||
|
||||
For users:
|
||||
|
||||
The most important update from the previous version is the addition of the
|
||||
dunstctl command and dunstify utility, a drop-in notify-send replacement (which
|
||||
existed for a while, but wasn't installed by default).
|
||||
The internal keyboard shortcut support in dunst is now considered deprecated
|
||||
and should be replaced by dunstctl calls. You can use the configuration of your
|
||||
WM or DE to bind these to shortcuts of your choice.
|
||||
|
||||
Additionally, another long requested feature implemented is RGBA/transparency
|
||||
support. Given an active compositor you can now add an optional transparency
|
||||
component to all colors in #RRGGBBAA format.
|
||||
|
||||
For maintainers:
|
||||
|
||||
As mentioned above, two new binaries are now installed by default, dunstctl and dunstify.
|
||||
libnotify has been added as a dependency as it's used internally by dunstify.
|
||||
|
||||
===================================================================================
|
||||
Release Notes For v1.4.0
|
||||
===================================================================================
|
||||
|
||||
29
config.h
@ -6,10 +6,13 @@ struct settings defaults = {
|
||||
.markup = MARKUP_NO,
|
||||
.colors_norm.bg = "#1793D1",
|
||||
.colors_norm.fg = "#DDDDDD",
|
||||
.colors_norm.highlight = "#1745d1",
|
||||
.colors_crit.bg = "#ffaaaa",
|
||||
.colors_crit.fg = "#000000",
|
||||
.colors_crit.highlight = "#ff6666",
|
||||
.colors_low.bg = "#aaaaff",
|
||||
.colors_low.fg = "#000000",
|
||||
.colors_low.highlight = "#7f7fff",
|
||||
.format = "%s %b", /* default format */
|
||||
|
||||
.timeouts = { S2US(10), S2US(10), S2US(0) }, /* low, normal, critical */
|
||||
@ -33,19 +36,25 @@ struct settings defaults = {
|
||||
.idle_threshold = 0, /* don't timeout notifications when idle for x seconds */
|
||||
.show_age_threshold = -1, /* show age of notification, when notification is older than x seconds */
|
||||
.align = ALIGN_LEFT, /* text alignment ALIGN_[LEFT|CENTER|RIGHT] */
|
||||
.vertical_alignment = VERTICAL_CENTER, /* vertical content alignment VERTICAL_[TOP|CENTER|BOTTOM] */
|
||||
.sticky_history = true,
|
||||
.history_length = 20, /* max amount of notifications kept in history */
|
||||
.show_indicators = true,
|
||||
.word_wrap = false,
|
||||
.ignore_dbusclose = false,
|
||||
.ellipsize = ELLIPSE_MIDDLE,
|
||||
.ignore_newline = false,
|
||||
.line_height = 0, /* if line height < font height, it will be raised to font height */
|
||||
.notification_height = 0, /* if notification height < font height and padding, it will be raised */
|
||||
.corner_radius = 0,
|
||||
|
||||
.force_xinerama = false,
|
||||
.force_xwayland = false,
|
||||
|
||||
.separator_height = 2, /* height of the separator line between two notifications */
|
||||
.padding = 0,
|
||||
.h_padding = 0, /* horizontal padding */
|
||||
.text_icon_padding = 0, /* padding between icon and text*/
|
||||
.sep_color = {SEP_AUTO}, /* SEP_AUTO, SEP_FOREGROUND, SEP_FRAME, SEP_CUSTOM */
|
||||
|
||||
.frame_width = 0,
|
||||
@ -65,6 +74,7 @@ struct settings defaults = {
|
||||
|
||||
.browser = "/usr/bin/firefox",
|
||||
|
||||
.min_icon_size = 0,
|
||||
.max_icon_size = 0,
|
||||
|
||||
/* paths to default icons */
|
||||
@ -101,12 +111,23 @@ struct settings defaults = {
|
||||
.code = 0,.sym = NoSymbol,.is_valid = false
|
||||
}, /* ignore this */
|
||||
|
||||
.mouse_left_click = MOUSE_CLOSE_CURRENT,
|
||||
.mouse_left_click = (enum mouse_action []){MOUSE_CLOSE_CURRENT, -1},
|
||||
|
||||
.mouse_middle_click = MOUSE_DO_ACTION,
|
||||
.mouse_middle_click = (enum mouse_action []){MOUSE_DO_ACTION, -1},
|
||||
|
||||
.mouse_right_click = MOUSE_CLOSE_ALL,
|
||||
.mouse_right_click = (enum mouse_action []){MOUSE_CLOSE_ALL, -1},
|
||||
|
||||
.progress_bar_height = 10,
|
||||
|
||||
.progress_bar_min_width = 150,
|
||||
|
||||
.progress_bar_max_width = 300,
|
||||
|
||||
.progress_bar_frame_width = 1,
|
||||
|
||||
.progress_bar = true,
|
||||
|
||||
.layer = ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
|
||||
};
|
||||
|
||||
struct rule default_rules[] = {
|
||||
@ -138,4 +159,4 @@ struct rule default_rules[] = {
|
||||
}
|
||||
};
|
||||
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
30
config.mk
@ -1,10 +1,12 @@
|
||||
# paths
|
||||
PREFIX ?= /usr/local
|
||||
BINDIR ?= ${PREFIX}/bin
|
||||
SYSCONFDIR ?= /etc
|
||||
DATADIR ?= ${PREFIX}/share
|
||||
# around for backwards compatibility
|
||||
MANPREFIX ?= ${DATADIR}/man
|
||||
MANDIR ?= ${MANPREFIX}
|
||||
EXTRA_CFLAGS ?=
|
||||
|
||||
DOXYGEN ?= doxygen
|
||||
FIND ?= find
|
||||
@ -20,14 +22,23 @@ VALGRIND ?= valgrind
|
||||
# if you don't want to use systemd albeit installed
|
||||
#SYSTEMD ?= 0
|
||||
|
||||
# Disable dependency on wayland. This will force dunst to use
|
||||
# xwayland on wayland compositors
|
||||
# You can also use "make WAYLAND=0" to build without wayland
|
||||
# WAYLAND ?= 0
|
||||
|
||||
ifneq (0, ${WAYLAND})
|
||||
ENABLE_WAYLAND= -DENABLE_WAYLAND
|
||||
endif
|
||||
|
||||
# uncomment to disable parsing of dunstrc
|
||||
# or use "CFLAGS=-DSTATIC_CONFIG make" to build
|
||||
#STATIC= -DSTATIC_CONFIG # Warning: This is deprecated behavior
|
||||
|
||||
# flags
|
||||
DEFAULT_CPPFLAGS = -D_DEFAULT_SOURCE -DVERSION=\"${VERSION}\"
|
||||
DEFAULT_CFLAGS = -g --std=gnu99 -pedantic -Wall -Wno-overlength-strings -Os ${STATIC}
|
||||
DEFAULT_LDFLAGS = -lm
|
||||
DEFAULT_CFLAGS = -g --std=gnu99 -pedantic -Wall -Wno-overlength-strings -Os ${STATIC} ${ENABLE_WAYLAND} ${EXTRA_CFLAGS}
|
||||
DEFAULT_LDFLAGS = -lm -lrt
|
||||
|
||||
CPPFLAGS_DEBUG := -DDEBUG_BUILD
|
||||
CFLAGS_DEBUG := -O0
|
||||
@ -41,13 +52,16 @@ pkg_config_packs := gio-2.0 \
|
||||
xinerama \
|
||||
xext \
|
||||
"xrandr >= 1.5" \
|
||||
xscrnsaver
|
||||
xscrnsaver \
|
||||
|
||||
|
||||
# dunstify also needs libnotify
|
||||
pkg_config_packs += libnotify
|
||||
|
||||
ifneq (0,${WAYLAND})
|
||||
pkg_config_packs += wayland-client
|
||||
endif
|
||||
|
||||
ifneq (,$(findstring STATIC_CONFIG,$(CFLAGS)))
|
||||
$(warning STATIC_CONFIG is deprecated behavior. It will get removed in future releases)
|
||||
endif
|
||||
|
||||
# dunstify also needs libnotify
|
||||
ifneq (,$(findstring dunstify,${MAKECMDGOALS}))
|
||||
pkg_config_packs += libnotify
|
||||
endif
|
||||
|
||||
75
contrib/progress-notify.sh
Executable file
@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# progress-notify - Send audio and brightness notifications for dunst
|
||||
|
||||
# dependencies: dunstify, ponymix, Papirus (icons)
|
||||
|
||||
### How to use: ###
|
||||
# Pass the values via stdin and provide the notification type
|
||||
# as an argument. Options are audio, brightness and muted
|
||||
|
||||
### Audio notifications ###
|
||||
# ponymix increase 5 | notify audio
|
||||
# ponymix decrease 5 | notify audio
|
||||
# pulsemixer --toggle-mute --get-mute | notify muted
|
||||
### Brightness notifications ###
|
||||
# xbacklight -inc 5 && xbacklight -get | notify brightness
|
||||
# xbacklight -dec 5 && xbacklight -get | notify brightness
|
||||
|
||||
notifyMuted() {
|
||||
volume="$1"
|
||||
dunstify -h string:x-canonical-private-synchronous:audio "Muted" -h int:value:"$volume" -t 1500 --icon audio-volume-muted
|
||||
}
|
||||
|
||||
notifyAudio() {
|
||||
volume="$1"
|
||||
ponymix is-muted && notifyMuted "$volume" && return
|
||||
|
||||
if [ $volume -eq 0 ]; then
|
||||
notifyMuted "$volume"
|
||||
elif [ $volume -le 30 ]; then
|
||||
dunstify -h string:x-canonical-private-synchronous:audio "Volume: " -h int:value:"$volume" -t 1500 --icon audio-volume-low
|
||||
elif [ $volume -le 70 ]; then
|
||||
dunstify -h string:x-canonical-private-synchronous:audio "Volume: " -h int:value:"$volume" -t 1500 --icon audio-volume-medium
|
||||
else
|
||||
dunstify -h string:x-canonical-private-synchronous:audio "Volume: " -h int:value:"$volume" -t 1500 --icon audio-volume-high
|
||||
fi
|
||||
}
|
||||
|
||||
notifyBrightness() {
|
||||
brightness="$1"
|
||||
if [ $brightness -eq 0 ]; then
|
||||
dunstify -h string:x-canonical-private-synchronous:brightness "Brightness: " -h int:value:"$brightness" -t 1500 --icon display-brightness-off-symbolic
|
||||
elif [ $brightness -le 30 ]; then
|
||||
dunstify -h string:x-canonical-private-synchronous:brightness "Brightness: " -h int:value:"$brightness" -t 1500 --icon display-brightness-low-symbolic
|
||||
elif [ $brightness -le 70 ]; then
|
||||
dunstify -h string:x-canonical-private-synchronous:brightness "Brightness: " -h int:value:"$brightness" -t 1500 --icon display-brightness-medium-symbolic
|
||||
else
|
||||
dunstify -h string:x-canonical-private-synchronous:brightness "Brightness: " -h int:value:"$brightness" -t 1500 --icon display-brightness-high-symbolic
|
||||
fi
|
||||
}
|
||||
|
||||
input=`cat /dev/stdin`
|
||||
|
||||
case "$1" in
|
||||
muted)
|
||||
volume=`ponymix get-volume`
|
||||
if [ "$input" -eq 0 ]
|
||||
then
|
||||
notifyAudio "$volume"
|
||||
else
|
||||
notifyMuted "$volume"
|
||||
fi
|
||||
;;
|
||||
audio)
|
||||
notifyAudio "$input"
|
||||
;;
|
||||
brightness)
|
||||
notifyBrightness "$input"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Not the right arguments"
|
||||
echo "$1"
|
||||
exit 2
|
||||
esac
|
||||
BIN
contrib/screenshots/default_config.png
Normal file
|
After Width: | Height: | Size: 388 KiB |
BIN
contrib/screenshots/music.png
Normal file
|
After Width: | Height: | Size: 406 KiB |
BIN
contrib/screenshots/screenshot1.png
Normal file
|
After Width: | Height: | Size: 672 KiB |
BIN
contrib/screenshots/screenshot1_cut.png
Normal file
|
After Width: | Height: | Size: 214 KiB |
BIN
contrib/screenshots/screenshot2.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
contrib/screenshots/screenshot2_cut.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
contrib/screenshots/screenshot3.png
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
BIN
contrib/screenshots/screenshot3_cut.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
contrib/screenshots/screenshot_urgency.png
Normal file
|
After Width: | Height: | Size: 393 KiB |
172
docs/dunst.1.pod
Normal file
@ -0,0 +1,172 @@
|
||||
=head1 NAME
|
||||
|
||||
dunst - A customizable and lightweight notification-daemon
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
dunst [-conf file] [-font font] [-geometry geom] [-format fmt] [-follow mode] [-monitor n] [-history_length n] ...
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Dunst is a highly configurable and lightweight notification daemon.
|
||||
|
||||
=head2 Autostarting dunst
|
||||
|
||||
On most installations dunst should be able to automatically be started by D-Bus
|
||||
when a notification is sent. This is not recommended when multiple notification
|
||||
deamons are installed, because D-Bus will not know which one to start.
|
||||
Other ways of autostarting dunst include starting dunst with your desktop
|
||||
environment or window manager's autostart functionality or via the provided
|
||||
systemd service.
|
||||
|
||||
=head1 COMMAND LINE OPTIONS
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<-h/--help>
|
||||
|
||||
List all command line flags
|
||||
|
||||
=item B<-conf/-config file>
|
||||
|
||||
Use alternative config file.
|
||||
|
||||
=item B<-v/--version>
|
||||
|
||||
Print version information.
|
||||
|
||||
=item B<-print>
|
||||
|
||||
Print notifications to stdout. This might be useful for logging, setting up
|
||||
rules or using the output in other scripts.
|
||||
|
||||
=item B<->F<SETTING> B<[value]>
|
||||
|
||||
Where F<SETTING> can be any setting that's available in the global section of
|
||||
the configuration file. See dunst(5) for possible settings.
|
||||
|
||||
Each configuration option in the global section can be overridden from the
|
||||
command line by adding a single dash in front of it's name.
|
||||
For example the font option can be overridden by running
|
||||
|
||||
$ dunst -font "LiberationSans Mono 4"
|
||||
|
||||
Configuration options that take boolean values can only currently be set to
|
||||
"true" through the command line via the same method. e.g.
|
||||
|
||||
$ dunst -shrink
|
||||
|
||||
This is a known limitation of the way command line parameters are parsed and
|
||||
will be changed in the future.
|
||||
|
||||
=back
|
||||
|
||||
=head1 CONFIGURATION
|
||||
|
||||
An example configuration file is included (usually /etc/dunst/dunstrc). Note:
|
||||
this was previously /usr/share/dunst/dunstrc.
|
||||
Before using dunst, copy this file to ~/.config/dunst/dunstrc and edit
|
||||
it accordingly. See dunst(5) for all possible settings.
|
||||
|
||||
=head2 NOTIFY-SEND
|
||||
|
||||
dunst is able to get different colors for a message via notify-send.
|
||||
In order to do that you have to add a hint via the -h option.
|
||||
The progress value can be set with a hint, too.
|
||||
|
||||
=over 4
|
||||
|
||||
=item notify-send -h string:fgcolor:#ff4444
|
||||
|
||||
=item notify-send -h string:bgcolor:#4444ff -h string:fgcolor:#ff4444 -h string:frcolor:#44ff44
|
||||
|
||||
=item notify-send -h int:value:42 "Working ..."
|
||||
|
||||
=back
|
||||
|
||||
=head1 ACTIONS
|
||||
|
||||
Dunst allows notifiers (i.e.: programs that send the notifications) to specify
|
||||
actions. Dunst has support for both displaying indicators for these, and
|
||||
interacting with these actions.
|
||||
|
||||
If "show_indicators" is true and a notification has an action, an "(A)" will be
|
||||
prepended to the notification format. Likewise, an "(U)" is prepended to
|
||||
notifications with URLs. It is possible to interact with notifications that
|
||||
have actions regardless of this setting, though it may not be obvious which
|
||||
notifications HAVE actions.
|
||||
|
||||
The "context" keybinding is used to interact with these actions, by showing a
|
||||
menu of possible actions. This feature requires "dmenu" or a dmenu drop-in
|
||||
replacement present. It is preferred to set this keybinding with your window
|
||||
manager or desktop envirorment and let it execute C<dunsctl context>. Another
|
||||
option is to set this keybinding in your dunstrc, but this is soon to be deprecated
|
||||
(and doesn't work on Wayland).
|
||||
|
||||
Alternatively, you can invoke an action with a middle click on the notification.
|
||||
If there is exactly one associated action, or one is marked as default, that one
|
||||
is invoked. If there are multiple, the context menu is shown. The same applies
|
||||
to URLs when there are no actions. You can change the mouse button to right click
|
||||
by setting C<mouse_right_click = close_all> in your dunstrc.
|
||||
|
||||
=head1 MISCELLANEOUS
|
||||
|
||||
Dunst can be paused via the `dunstctl set-paused true` command. To unpause dunst use
|
||||
`dunstctl set-paused false`.
|
||||
Alternatively you can send SIGUSR1 and SIGUSR2 to pause and unpause
|
||||
respectively. For Example:
|
||||
|
||||
=over 4
|
||||
|
||||
=item killall -SIGUSR1 dunst # pause
|
||||
|
||||
=item killall -SIGUSR2 dunst # resume
|
||||
|
||||
=back
|
||||
|
||||
When paused dunst will not display any notifications but keep all notifications
|
||||
in a queue. This can for example be wrapped around a screen locker (i3lock,
|
||||
slock) to prevent flickering of notifications through the lock and to read all
|
||||
missed notifications after returning to the computer.
|
||||
|
||||
=head1 FILES
|
||||
|
||||
These are the places where dunst will look for a configuration file. They are
|
||||
listed here in order and if dunst finds one of them, it will stop looking for
|
||||
more.
|
||||
|
||||
$XDG_CONFIG_HOME/dunst/dunstrc
|
||||
|
||||
$HOME/.config/dunst/dunstrc
|
||||
|
||||
-or-
|
||||
|
||||
$XDG_CONFIG_HOME/dunst/dunstrc
|
||||
|
||||
/etc/xdg/dunst/dunstrc
|
||||
|
||||
=over 4
|
||||
|
||||
=item /etc/dunst/dunstrc
|
||||
|
||||
This is where the default config file is located
|
||||
|
||||
=back
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Written by Sascha Kruse <knopwob@googlemail.com>
|
||||
|
||||
=head1 REPORTING BUGS
|
||||
|
||||
Bugs and suggestions should be reported on GitHub at https://github.com/dunst-project/dunst/issues
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information)
|
||||
|
||||
If you feel that copyrights are violated, please send me an email.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
dunst(5), dunstctl(1), dmenu(1), notify-send(1)
|
||||
@ -1,48 +1,20 @@
|
||||
=head1 NAME
|
||||
|
||||
dunst - A customizable and lightweight notification-daemon
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
dunst [-conf file] [-font font] [-geometry geom] [-format fmt] [-follow mode] [-monitor n] [-history_length n] ...
|
||||
dunst - configuration file
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Dunst is a highly configurable and lightweight notification daemon.
|
||||
|
||||
=head1 COMMAND LINE OPTIONS
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<-h/--help>
|
||||
|
||||
List all command line flags
|
||||
|
||||
=item B<-conf/-config file>
|
||||
|
||||
Use alternative config file.
|
||||
|
||||
=item B<-v/--version>
|
||||
|
||||
Print version information.
|
||||
|
||||
=item B<-print>
|
||||
|
||||
Print notifications to stdout. This might be useful for logging, setting up
|
||||
rules or using the output in other scripts.
|
||||
|
||||
=back
|
||||
|
||||
=head1 CONFIGURATION
|
||||
|
||||
An example configuration file is included (usually /usr/share/dunst/dunstrc).
|
||||
An example configuration file is included (usually /etc/dunst/dunstrc). Note:
|
||||
this was previously /usr/share/dunst/dunstrc.
|
||||
To change the configuration, copy this file to ~/.config/dunst/dunstrc and edit
|
||||
it accordingly.
|
||||
|
||||
The configuration is divided into sections in an ini-like format. The 'global'
|
||||
section contains most general settings while the 'shortcuts' sections contains
|
||||
all keyboard configuration and the 'experimental' section all the features that
|
||||
have not yet been tested thoroughly.
|
||||
section contains most general settings while the setions 'urgency_low',
|
||||
'urgency_normal' and 'urgency_critical' are for low, normal and critical urgency
|
||||
notifications respectively. The 'shortcuts' section (deprecated) contains all
|
||||
keyboard configuration and the 'experimental' section all the features that have
|
||||
not yet been tested thoroughly.
|
||||
|
||||
Any section that is not one of the above is assumed to be a rule, see RULES for
|
||||
more details.
|
||||
@ -50,24 +22,6 @@ more details.
|
||||
For backwards compatibility reasons the section name 'frame' is considered bound
|
||||
and can't be used as a rule.
|
||||
|
||||
=head2 Command line
|
||||
|
||||
Each configuration option in the global section can be overridden from the
|
||||
command line by adding a single dash in front of it's name.
|
||||
For example the font option can be overridden by running
|
||||
|
||||
$ dunst -font "LiberationSans Mono 4"
|
||||
|
||||
Configuration options that take boolean values can only currently be set to
|
||||
"true" through the command line via the same method. e.g.
|
||||
|
||||
$ dunst -shrink
|
||||
|
||||
This is a known limitation of the way command line parameters are parsed and
|
||||
will be changed in the future.
|
||||
|
||||
Available settings per section:
|
||||
|
||||
=head2 Global section
|
||||
|
||||
=over 4
|
||||
@ -82,6 +36,10 @@ starts at 0. See the B<follow> setting.
|
||||
Defines where the notifications should be placed in a multi-monitor setup. All
|
||||
values except I<none> override the B<monitor> setting.
|
||||
|
||||
On Wayland there is no difference between mouse and keyboard focus. When either
|
||||
of the is used, the compositor will choose an output. This will generally be
|
||||
the output last interacted with.
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<none>
|
||||
@ -142,6 +100,32 @@ the notification on the left border of the screen while a horizontal offset of
|
||||
|
||||
=back
|
||||
|
||||
=item B<progress_bar> (values: [true/false], default: true)
|
||||
|
||||
When an integer value is passed to dunst as a hint (see B<NOTIFY-SEND>), a
|
||||
progress bar will be drawn at the bottom of the notification. This
|
||||
behavior can be turned off by setting this setting to false.
|
||||
|
||||
=item B<progress_bar_height> (default: 10)
|
||||
|
||||
The height of the progress bar in pixel. This includes the frame. Make sure
|
||||
this value is bigger than twice the frame width.
|
||||
|
||||
=item B<progress_bar_min_width> (default: 150)
|
||||
|
||||
The minimum width of the progress bar in pixels. The notification is rescaled
|
||||
to fit the bar.
|
||||
|
||||
=item B<progress_bar_max_width> (default: 300)
|
||||
|
||||
The maximum width of the progress bar in pixels. The notification is resized
|
||||
to fit the progress bar.
|
||||
|
||||
=item B<progress_bar_frame_width> (default: 1)
|
||||
|
||||
The frame width of the progress bar in pixels. This value should be smaller
|
||||
than half of the progress bar height.
|
||||
|
||||
=item B<indicate_hidden> (values: [true/false], default: true)
|
||||
|
||||
If this is set to true, a notification indicating how many notifications are
|
||||
@ -188,6 +172,23 @@ in the vertical axis
|
||||
The distance in pixels from the content to the border of the window
|
||||
in the horizontal axis
|
||||
|
||||
=item B<text_icon_padding> (default: 0)
|
||||
|
||||
The distance in pixels from the text to the icon (or vice versa)
|
||||
in the horizontal axis.
|
||||
|
||||
Setting this to a non-zero value overwrites any padding that horizontal_padding was adding between the notification text and icon.
|
||||
|
||||
So for example setting
|
||||
|
||||
text_icon_padding=10
|
||||
horizontal_padding=10
|
||||
|
||||
is equivalent to
|
||||
|
||||
text_icon_padding=0
|
||||
horizontal_padding=10
|
||||
|
||||
=item B<frame_width> (default: 0)
|
||||
|
||||
Defines width in pixels of frame around the notification window. Set to 0 to
|
||||
@ -238,6 +239,25 @@ Set to 0 to disable.
|
||||
A client can mark a notification as transient to bypass this setting and timeout
|
||||
anyway. Use a rule with 'set_transient = no' to disable this behavior.
|
||||
|
||||
Note: this doesn't work on xwayland.
|
||||
|
||||
=item B<layer> (Wayland only)
|
||||
|
||||
One of bottom, top or overlay.
|
||||
|
||||
Place dunst notifications on the selected layer. Using overlay
|
||||
will cause notifications to be displayed above fullscreen windows, though
|
||||
this may also occur at top depending on your compositor.
|
||||
|
||||
The bottom layer is below all windows and above the background.
|
||||
|
||||
Default: overlay
|
||||
|
||||
=item B<force_xwayland> (values: [true/false], default: false) (Wayland only)
|
||||
|
||||
Force the use of X11 output, even on a wayland compositor. This setting
|
||||
has no effect when not using a Wayland compositor.
|
||||
|
||||
=item B<font> (default: "Monospace 8")
|
||||
|
||||
Defines the font or font set used. Optionally set the size as a decimal number
|
||||
@ -272,7 +292,7 @@ Allow a small subset of html markup in notifications
|
||||
<u>underline</u>
|
||||
|
||||
For a complete reference see
|
||||
<http://developer.gnome.org/pango/stable/PangoMarkupFormat.html>
|
||||
<https://developer.gnome.org/pango/stable/pango-Markup.html>
|
||||
|
||||
=item B<strip>
|
||||
|
||||
@ -337,6 +357,11 @@ removed from the format.
|
||||
|
||||
Defines how the text should be aligned within the notification.
|
||||
|
||||
=item B<vertical_alignment> (values: [top/center/bottom], default: center)
|
||||
|
||||
Defines how the text and icon should be aligned vertically within the
|
||||
notification. If icons are disabled, this option has no effect.
|
||||
|
||||
=item B<show_age_threshold> (default: -1)
|
||||
|
||||
Show age of message if message is older than this time.
|
||||
@ -370,7 +395,7 @@ being displayed separately.
|
||||
Two notifications are considered duplicate if the name of the program that sent
|
||||
it, summary, body, icon and urgency are all identical.
|
||||
|
||||
=item B<hide_duplicates_count> (values: [true/false], default: false)
|
||||
=item B<hide_duplicate_count> (values: [true/false], default: false)
|
||||
|
||||
Hide the count of stacked duplicate notifications.
|
||||
|
||||
@ -384,14 +409,28 @@ ACTIONS below for further details.
|
||||
Defines the position of the icon in the notification window. Setting it to off
|
||||
disables icons.
|
||||
|
||||
=item B<min_icon_size> (default: 0)
|
||||
|
||||
Defines the minimum size in pixels for the icons.
|
||||
If the icon is larger than or equal to the specified value it won't be affected.
|
||||
If it's smaller then it will be scaled up so that the smaller axis is equivalent
|
||||
to the specified size.
|
||||
|
||||
Set to 0 to disable icon upscaling. (default)
|
||||
|
||||
If B<icon_position> is set to off, this setting is ignored.
|
||||
|
||||
=item B<max_icon_size> (default: 0)
|
||||
|
||||
Defines the maximum size in pixels for the icons.
|
||||
If the icon is smaller than the specified value it won't be affected.
|
||||
If the icon is smaller than or equal to the specified value it won't be affected.
|
||||
If it's larger then it will be scaled down so that the larger axis is equivalent
|
||||
to the specified size.
|
||||
|
||||
Set to 0 to disable icon scaling. (default)
|
||||
Set to 0 to disable icon downscaling. (default)
|
||||
|
||||
If both B<min_icon_size> and B<max_icon_size> are enabled, the latter
|
||||
gets the last say.
|
||||
|
||||
If B<icon_position> is set to off, this setting is ignored.
|
||||
|
||||
@ -450,7 +489,7 @@ Do not display log messages, which have lower precedence than specified
|
||||
verbosity. This won't affect printing notifications on the terminal. Use
|
||||
the '-print' option for this.
|
||||
|
||||
=item B<force_xinerama> (values: [true/false], default: false)
|
||||
=item B<force_xinerama> (values: [true/false], default: false) (X11 only)
|
||||
|
||||
Use the Xinerama extension instead of RandR for multi-monitor support. This
|
||||
setting is provided for compatibility with older nVidia drivers that do not
|
||||
@ -472,9 +511,10 @@ single notification.
|
||||
To avoid the corners clipping the icon or text the corner radius will be
|
||||
automatically lowered to half of the notification height if it exceeds it.
|
||||
|
||||
=item B<mouse_left/middle/right_click> (values: [none/do_action/close_current/close_all])
|
||||
=item B<mouse_left/middle/right_click> (values: [none/do_action/close_current/close_all/context/context_all])
|
||||
|
||||
Defines action of mouse click.
|
||||
Defines action of mouse click. A touch input in Wayland acts as a mouse left
|
||||
click.
|
||||
|
||||
=over 4
|
||||
|
||||
@ -484,7 +524,13 @@ Don't do anything.
|
||||
|
||||
=item B<do_action> (default for mouse_middle_click)
|
||||
|
||||
If the notification has exactly one action, or one is marked as default, invoke it. If there are multiple and no default, open the context menu.
|
||||
Invoke the action determined by the action_name rule. If there is no such
|
||||
action, open the context menu.
|
||||
|
||||
=item B<open_url>
|
||||
|
||||
If the notification has exactly one url, open it. If there are multiple
|
||||
ones, open the context menu.
|
||||
|
||||
=item B<close_current> (default for mouse_left_click)
|
||||
|
||||
@ -494,11 +540,26 @@ Close current notification.
|
||||
|
||||
Close all notifications.
|
||||
|
||||
=back
|
||||
=item B<context>
|
||||
|
||||
Open context menu for the notification.
|
||||
|
||||
=item B<context_all>
|
||||
|
||||
Open context menu for all notifications.
|
||||
|
||||
=back
|
||||
|
||||
=head2 Shortcut section
|
||||
|
||||
=item B<ignore_dbusclose> (default: false)
|
||||
|
||||
Ignore the dbus closeNotification message. This is useful to enforce the timeout
|
||||
set by dunst configuration. Without this parameter, an application may close
|
||||
the notification sent before the user defined timeout.
|
||||
|
||||
=back
|
||||
|
||||
=head2 Shortcut section B<DEPRECATED SEE DUNSTCTL> (X11 only)
|
||||
|
||||
Keyboard shortcuts are defined in the following format: "Modifier+key" where the
|
||||
modifier is one of ctrl,mod1,mod2,mod3,mod4 and key is any keyboard key.
|
||||
@ -535,8 +596,8 @@ Specifies the keyboard shortcut that opens the context menu.
|
||||
|
||||
The urgency sections work in a similar way to rules and can be used to specify
|
||||
attributes for the different urgency levels of notifications (low, normal,
|
||||
critical). Currently only the background, foreground, timeout, frame_color and
|
||||
icon attributes can be modified.
|
||||
critical). Currently only the background, foreground, hightlight, timeout,
|
||||
frame_color and icon attributes can be modified.
|
||||
|
||||
The urgency sections are urgency_low, urgency_normal, urgency_critical for low,
|
||||
normal and critical urgency respectively.
|
||||
@ -569,6 +630,12 @@ Defines the background color for low, normal and critical notifications respecti
|
||||
|
||||
See COLORS for the value format.
|
||||
|
||||
=item B<-lh/nh/ch color>
|
||||
|
||||
Defines the highlight color for low, normal and critical notifications respectively.
|
||||
|
||||
See COLORS for the value format.
|
||||
|
||||
=item B<-lfr/nfr/cfr color>
|
||||
|
||||
Defines the frame color for low, normal and critical notifications respectively.
|
||||
@ -583,18 +650,53 @@ See TIME FORMAT for valid times.
|
||||
|
||||
=back
|
||||
|
||||
=head1 DUNSTCTL
|
||||
|
||||
Dunst now contains a command line control command that can be used to interact
|
||||
with it. It supports all functions previously done only via keyboard shortcuts
|
||||
but also has a lot of extra functionality. So see more see dunstctl(1).
|
||||
|
||||
=head1 HISTORY
|
||||
|
||||
Dunst saves a number of notifications (specified by B<history_length>) in memory.
|
||||
These notifications can be recalled (i.e. redisplayed) by pressing the
|
||||
B<history_key> (see the shortcuts section), whether these notifications will
|
||||
time out like if they have been just send depends on the value of the
|
||||
B<sticky_history> setting.
|
||||
These notifications can be recalled (i.e. redisplayed) by calling
|
||||
B<dunstctl history> (see dunstctl(1)). Whether these notifications will time out
|
||||
like if they have been just send depends on the value of the B<sticky_history>
|
||||
setting.
|
||||
|
||||
Past notifications are redisplayed in a first-in-last-out order, meaning that
|
||||
pressing the history key once will bring up the most recent notification that
|
||||
had been closed/timed out.
|
||||
|
||||
=head1 WAYLAND
|
||||
|
||||
Dunst has Wayland support since version 1.6.0. Because the Wayland protocol
|
||||
is more focused on security, some things that are possible in X11 are not
|
||||
possible in Wayland. Those differences are reflected in the configuration.
|
||||
The main things that change are that dunst on Wayland cannot use global
|
||||
hotkeys (they are deprecated anyways, use dunstctl).
|
||||
|
||||
Some dunst features on wayland might need your compositor to support a certain
|
||||
protocol. Dunst will warn you if an optional feature isn't supported and will
|
||||
disable the corresponding functionality.
|
||||
|
||||
Fullscreen detection works on wayland with some limitations (see B<fullscreen>).
|
||||
If you want notifications to appear over fullscreen windows, set
|
||||
B<layer = overlay> in the global options.
|
||||
|
||||
Note that the same limitations exist when using xwayland.
|
||||
If something doesn't quite work in Wayland, please file a bug report. In the
|
||||
mean time, you can try if the X11 output does work on wayland. Use
|
||||
B<force_xwayland = true> for that.
|
||||
|
||||
If you have your dunst notifications on the same side of your display as your
|
||||
status bar, you might notice that your notifications appear a bit higher or
|
||||
lower than on X11. This is because the notification cannot be placed on top of
|
||||
your status bar. The notifications are placed relative to your status bar,
|
||||
making them appear higher or lower by the height of your status bar. We cannot
|
||||
do anything about that behavior, so you will need to change your B<geometry>
|
||||
variable accordingly.
|
||||
|
||||
=head1 RULES
|
||||
|
||||
Rules allow the conditional modification of notifications. They are defined by
|
||||
@ -692,7 +794,12 @@ The background color of the notification. See COLORS for possible values.
|
||||
|
||||
=item C<foreground>
|
||||
|
||||
The background color of the notification. See COLORS for possible values.
|
||||
The foreground color of the notification. See COLORS for possible values.
|
||||
|
||||
=item C<highlight>
|
||||
|
||||
The highlight color of the notification. This color is used for coloring the
|
||||
progress bar. See COLORS for possible values.
|
||||
|
||||
=item C<format>
|
||||
|
||||
@ -706,7 +813,7 @@ The frame color color of the notification. See COLORS for possible values.
|
||||
|
||||
One of show, delay, or pushback.
|
||||
|
||||
This attribute speicifies how notifications are handled if a fullscreen window
|
||||
This attribute specifies how notifications are handled if a fullscreen window
|
||||
is focused. By default it's set to show so notifications are being shown.
|
||||
|
||||
Other possible values are delay: Already shown notifications are continued to be
|
||||
@ -717,6 +824,16 @@ Or pushback which is equivalent to delay with the difference that already
|
||||
existing notifications are paused and hidden until the focus to the fullscreen
|
||||
window is lost.
|
||||
|
||||
On wayland, if B<follow> is set to mouse or keyboard, the output where the
|
||||
notification is located cannot be determined. So dunst will delay or pushback if
|
||||
any of the outputs is fullscreen. Since the fullscreen protocol is fairly new,
|
||||
you will need a recent version of a compositor that supports it. At the time of
|
||||
writing, you will need the git version of sway.
|
||||
See also B<layer> to change if notifications appear above fullscreen windows in
|
||||
Wayland.
|
||||
|
||||
Default: show
|
||||
|
||||
=item C<new_icon>
|
||||
|
||||
Updates the icon of the notification, it should be a path to a valid image.
|
||||
@ -725,7 +842,7 @@ Updates the icon of the notification, it should be a path to a valid image.
|
||||
|
||||
Sets the stack tag for the notification, notifications with the same (non-empty)
|
||||
stack tag will replace each-other so only the newest one is visible. This can be
|
||||
useful for example in volume or brightness notifications where only want one of
|
||||
useful for example in volume or brightness notifications where you only want one of
|
||||
the same type visible.
|
||||
|
||||
The stack tag can be set by the client with the 'synchronous',
|
||||
@ -757,6 +874,13 @@ later. Use C<msg_urgency> to match it.
|
||||
Setting this to true will prevent the notification from being displayed
|
||||
initially but will be saved in history for later viewing.
|
||||
|
||||
=item C<action_name>
|
||||
|
||||
Sets the name of the action to be invoked on do_action. If not specified, the
|
||||
action set as default action or the only available action will be invoked.
|
||||
|
||||
Default: "default"
|
||||
|
||||
=back
|
||||
|
||||
As with the filtering attributes, each one corresponds to
|
||||
@ -776,11 +900,22 @@ Within rules you can specify a script to be run every time the rule is matched
|
||||
by assigning the 'script' option to the name of the script to be run.
|
||||
|
||||
When the script is called details of the notification that triggered it will be
|
||||
passed via command line parameters in the following order: appname, summary,
|
||||
body, icon, urgency.
|
||||
passed via environment variables. The following variables are available:
|
||||
B<DUNST_APP_NAME>, B<DUNST_SUMMARY>, B<DUNST_BODY>, B<DUNST_ICON_PATH>,
|
||||
B<DUNST_URGENCY>, B<DUNST_ID>, B<DUNST_PROGRESS>, B<DUNST_CATEGORY>,
|
||||
B<DUNST_STACK_TAG>, B<DUNST_URLS>, B<DUNST_TIMEOUT>, B<DUNST_TIMESTAMP>
|
||||
and B<DUNST_STACK_TAG>.
|
||||
|
||||
Where icon is the absolute path to the icon file if there is one and urgency is
|
||||
one of "LOW", "NORMAL" or "CRITICAL".
|
||||
Another, less recommended way to get notifcations details from a script is via
|
||||
command line parameters. These are passed to the script in the following order:
|
||||
B<appname>, B<summary>, B<body>, B<icon_path>, B<urgency>.
|
||||
|
||||
Where B<DUNST_ICON_PATH> or B<icon_path> is the absolute path to the icon file
|
||||
if there is one. B<DUNST_URGENCY> or B<urgency> is one of "LOW", "NORMAL" or
|
||||
"CRITICAL". B<DUNST_URLS> is a newline-separated list of urls associated with
|
||||
the notification.
|
||||
|
||||
Note that some variables may be empty.
|
||||
|
||||
If the notification is suppressed, the script will not be run unless
|
||||
B<always_run_scripts> is set to true.
|
||||
@ -795,6 +930,8 @@ Colors are interpreted as X11 color values. This includes both verbatim
|
||||
color names such as "Yellow", "Blue", "White", etc as well as #RGB and #RRGGBB
|
||||
values.
|
||||
|
||||
You may also specify a transparency component in #RGBA or #RRGGBBAA format.
|
||||
|
||||
B<NOTE>: '#' is interpreted as a comment, to use it the entire value needs to
|
||||
be in quotes like so: separator_color="#123456"
|
||||
|
||||
@ -804,6 +941,46 @@ dunst is able to get different colors for a message via notify-send.
|
||||
In order to do that you have to add a hint via the -h option.
|
||||
The progress value can be set with a hint, too.
|
||||
|
||||
B<All hints>
|
||||
|
||||
See RULES for more detailed explanations for some options.
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<fgcolor>:
|
||||
Foreground cololor
|
||||
|
||||
=item B<bgcolor>:
|
||||
Background color
|
||||
|
||||
=item B<frcolor>:
|
||||
Frame color
|
||||
|
||||
=item B<hlcolor>:
|
||||
Highlight color
|
||||
|
||||
=item B<value>:
|
||||
Progress value.
|
||||
|
||||
=item B<image-path>:
|
||||
Icon name. This may be a path or just the icon name.
|
||||
|
||||
=item B<image-data>:
|
||||
A stream of raw image data.
|
||||
|
||||
=item B<category>:
|
||||
The category.
|
||||
|
||||
=item B<desktop_entry>:
|
||||
The desktop entry.
|
||||
|
||||
=item B<transient>:
|
||||
The transient value.
|
||||
|
||||
=back
|
||||
|
||||
B<Examples>
|
||||
|
||||
=over 4
|
||||
|
||||
=item notify-send -h string:fgcolor:#ff4444
|
||||
@ -846,9 +1023,8 @@ Example time: "1000ms" "10m"
|
||||
|
||||
=head1 MISCELLANEOUS
|
||||
|
||||
Dunst can be paused by sending a notification with a summary of
|
||||
"DUNST_COMMAND_PAUSE", resumed with a summary of "DUNST_COMMAND_RESUME" and
|
||||
toggled with a summary of "DUNST_COMMAND_TOGGLE".
|
||||
Dunst can be paused via the `dunstctl set-paused true` command. To unpause dunst use
|
||||
`dunstctl set-paused false`.
|
||||
Alternatively you can send SIGUSR1 and SIGUSR2 to pause and unpause
|
||||
respectively. For Example:
|
||||
|
||||
@ -867,11 +1043,27 @@ missed notifications after returning to the computer.
|
||||
|
||||
=head1 FILES
|
||||
|
||||
These are the places where dunst will look for a configuration file. They are
|
||||
listed here in order and if dunst finds one of them, it will stop looking for
|
||||
more.
|
||||
|
||||
$XDG_CONFIG_HOME/dunst/dunstrc
|
||||
|
||||
$HOME/.config/dunst/dunstrc
|
||||
|
||||
-or-
|
||||
|
||||
$HOME/.config/dunst/dunstrc
|
||||
$XDG_CONFIG_HOME/dunst/dunstrc
|
||||
|
||||
/etc/xdg/dunst/dunstrc
|
||||
|
||||
=over 4
|
||||
|
||||
=item /etc/dunst/dunstrc
|
||||
|
||||
This is where the default config file is located
|
||||
|
||||
=back
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
@ -889,4 +1081,4 @@ If you feel that copyrights are violated, please send me an email.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
dwm(1), dmenu(1), twmn(1), notify-send(1)
|
||||
dunst(1), dunstctl(1), dmenu(1), notify-send(1)
|
||||
64
docs/dunstctl.pod
Normal file
@ -0,0 +1,64 @@
|
||||
=head1 NAME
|
||||
|
||||
dunstctl - Command line control utility for dunst, a customizable and lightweight notification-daemon
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
dunstctl COMMAND [PARAMETER]
|
||||
|
||||
=head1 COMMANDS
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<action> notification_position
|
||||
|
||||
Performs the default action or, if not available, opens the context menu of the
|
||||
notification at the given position (starting count at the top, first
|
||||
notification being 0).
|
||||
|
||||
=item B<close>
|
||||
|
||||
Close the topmost notification currently being displayed.
|
||||
|
||||
=item B<close-all>
|
||||
|
||||
Close all notifications currently being displayed
|
||||
|
||||
=item B<context>
|
||||
|
||||
Open the context menu, presenting all available actions and urls for the
|
||||
currently open notifications.
|
||||
|
||||
=item B<count> [displayed/history/waiting]
|
||||
|
||||
Returns the number of displayed, shown and waiting notifications. If no argument
|
||||
is provided, everything will be printed.
|
||||
|
||||
=item B<history-pop>
|
||||
|
||||
Redisplay the notification that was most recently closed. This can be called
|
||||
multiple times to show older notifications, up to the history limit configured
|
||||
in dunst.
|
||||
|
||||
=item B<is-paused>
|
||||
|
||||
Check if dunst is currently running or paused. If dunst is paused notifications
|
||||
will be kept but not shown until it is unpaused.
|
||||
|
||||
=item B<set-paused> true/false/toggle
|
||||
|
||||
Set the paused status of dunst. If false, dunst is running normally, if true,
|
||||
dunst is paused. See the is-paused command and the dunst man page for more
|
||||
information.
|
||||
|
||||
=item B<debug>
|
||||
|
||||
Tries to contact dunst and checks for common faults between dunstctl and dunst.
|
||||
Useful if something isn't working
|
||||
|
||||
=item B<help>
|
||||
|
||||
Show all available commands with a brief description
|
||||
|
||||
=back
|
||||
|
||||
33
docs/internal/release-checklist.md
Normal file
@ -0,0 +1,33 @@
|
||||
# Main repo
|
||||
- [ ] Verify that the changelog is up to date
|
||||
- [ ] Write release notes (Only if non-patch release)
|
||||
|
||||
- [ ] Verify that the working directory is clean and on the master branch
|
||||
- [ ] Change the version in the Makefile to "x.x.x (iso-date)"
|
||||
- [ ] Update changelog `Unreleased` entry to the new version
|
||||
- [ ] Commit changes (Commit title: `Dunst vX.X.X`)
|
||||
- [ ] Tag commit, make sure it's an annotated tag (git tag -a) (Tag title: Dunst vX.X.X)
|
||||
- [ ] Push commits
|
||||
- [ ] Push tags
|
||||
|
||||
# Dunst-project.org
|
||||
- [ ] Update release number in the download page
|
||||
- [ ] Update release date in the download page
|
||||
- [ ] Update version number in the download link
|
||||
- [ ] Copy release notes to the download page (Only if non-patch release)
|
||||
- [ ] Copy changelog to the changelog page
|
||||
- [ ] Copy documentation to the documentation page
|
||||
- [ ] Verify that they look fine when rendered
|
||||
- [ ] Commit changes
|
||||
- [ ] Run deploy script
|
||||
- [ ] Push to main website repo
|
||||
- [ ] Push to gh-pages
|
||||
|
||||
# Main repo
|
||||
- [ ] Copy release notes to githubs release feature
|
||||
- [ ] Publish release on github
|
||||
- [ ] Update maint branch to point to master
|
||||
|
||||
- [ ] Create new Unreleased section for the changelog
|
||||
- [ ] Update Makefile version to -non-git
|
||||
- [ ] Commit & push
|
||||
@ -7,7 +7,3 @@ PartOf=graphical-session.target
|
||||
Type=dbus
|
||||
BusName=org.freedesktop.Notifications
|
||||
ExecStart=##PREFIX##/bin/dunst
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
|
||||
|
||||
122
dunstctl
Executable file
@ -0,0 +1,122 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
DBUS_NAME="org.freedesktop.Notifications"
|
||||
DBUS_PATH="/org/freedesktop/Notifications"
|
||||
DBUS_IFAC_DUNST="org.dunstproject.cmd0"
|
||||
DBUS_IFAC_PROP="org.freedesktop.DBus.Properties"
|
||||
DBUS_IFAC_FDN="org.freedesktop.Notifications"
|
||||
|
||||
die(){ printf "%s\n" "${1}" >&2; exit 1; }
|
||||
|
||||
show_help() {
|
||||
cat <<-EOH
|
||||
Usage: dunstctl <command> [parameters]"
|
||||
Commands:
|
||||
action Perform the default action, or open the
|
||||
context menu of the notification at the
|
||||
given position
|
||||
close Close the last notification
|
||||
close-all Close the all notifications
|
||||
context Open context menu
|
||||
count [displayed|history|waiting] Show the number of notifications
|
||||
history-pop Pop one notification from history
|
||||
is-paused Check if dunst is running or paused
|
||||
set-paused [true|false|toggle] Set the pause status
|
||||
debug Print debugging information
|
||||
help Show this help
|
||||
EOH
|
||||
}
|
||||
dbus_send_checked() {
|
||||
dbus-send "$@" \
|
||||
|| die "Failed to communicate with dunst, is it running? Or maybe the version is outdated. You can try 'dunstctl debug' as a next debugging step."
|
||||
}
|
||||
|
||||
method_call() {
|
||||
dbus_send_checked --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "$@"
|
||||
}
|
||||
|
||||
property_get() {
|
||||
dbus_send_checked --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_PROP}.Get" "string:${DBUS_IFAC_DUNST}" "string:${1}"
|
||||
}
|
||||
|
||||
property_set() {
|
||||
dbus_send_checked --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_PROP}.Set" "string:${DBUS_IFAC_DUNST}" "string:${1}" "${2}"
|
||||
}
|
||||
|
||||
command -v dbus-send >/dev/null 2>/dev/null || \
|
||||
die "Command dbus-send not found"
|
||||
|
||||
|
||||
case "${1:-}" in
|
||||
"action")
|
||||
method_call "${DBUS_IFAC_DUNST}.NotificationAction" "int32:${2:-0}" >/dev/null
|
||||
;;
|
||||
"close")
|
||||
method_call "${DBUS_IFAC_DUNST}.NotificationCloseLast" >/dev/null
|
||||
;;
|
||||
"close-all")
|
||||
method_call "${DBUS_IFAC_DUNST}.NotificationCloseAll" >/dev/null
|
||||
;;
|
||||
"context")
|
||||
method_call "${DBUS_IFAC_DUNST}.ContextMenuCall" >/dev/null
|
||||
;;
|
||||
"count")
|
||||
[ $# -eq 1 ] || [ "${2}" = "displayed" ] || [ "${2}" = "history" ] || [ "${2}" = "waiting" ] \
|
||||
|| die "Please give either 'displayed', 'history', 'waiting' or none as count parameter."
|
||||
if [ $# -eq 1 ]; then
|
||||
property_get waitingLength | ( read -r _ _ waiting; printf " Waiting: %s\n" "${waiting}" )
|
||||
property_get displayedLength | ( read -r _ _ displayed; printf " Currently displayed: %s\n" "${displayed}" )
|
||||
property_get historyLength | ( read -r _ _ history; printf " History: %s\n" "${history}")
|
||||
else
|
||||
property_get ${2}Length | ( read -r _ _ notifications; printf "%s\n" "${notifications}"; )
|
||||
fi
|
||||
;;
|
||||
"history-pop")
|
||||
method_call "${DBUS_IFAC_DUNST}.NotificationShow" >/dev/null
|
||||
;;
|
||||
"is-paused")
|
||||
property_get paused | ( read -r _ _ paused; printf "%s\n" "${paused}"; )
|
||||
;;
|
||||
"set-paused")
|
||||
[ "${2:-}" ] \
|
||||
|| die "No status parameter specified. Please give either 'true', 'false' or 'toggle' as paused parameter."
|
||||
[ "${2}" = "true" ] || [ "${2}" = "false" ] || [ "${2}" = "toggle" ] \
|
||||
|| die "Please give either 'true', 'false' or 'toggle' as paused parameter."
|
||||
if [ "${2}" = "toggle" ]; then
|
||||
paused=$(property_get paused | ( read -r _ _ paused; printf "%s\n" "${paused}"; ))
|
||||
if [ "${paused}" = "true" ]; then
|
||||
property_set paused variant:boolean:false
|
||||
else
|
||||
property_set paused variant:boolean:true
|
||||
fi
|
||||
else
|
||||
property_set paused variant:boolean:"$2"
|
||||
fi
|
||||
;;
|
||||
"help"|"--help"|"-h")
|
||||
show_help
|
||||
;;
|
||||
"debug")
|
||||
dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_FDN}.GetServerInformation" >/dev/null 2>/dev/null \
|
||||
|| die "Dunst is not running."
|
||||
|
||||
dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_FDN}.GetServerInformation" \
|
||||
| (
|
||||
read -r name _ version _
|
||||
[ "${name}" = "dunst" ]
|
||||
printf "dunst version: %s\n" "${version}"
|
||||
) \
|
||||
|| die "Another notification manager is running. It's not dunst"
|
||||
|
||||
dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_DUNST}.Ping" >/dev/null 2>/dev/null \
|
||||
|| die "Dunst controlling interface not available. Is the version too old?"
|
||||
;;
|
||||
"")
|
||||
die "dunstctl: No command specified. Please consult the usage."
|
||||
;;
|
||||
*)
|
||||
die "dunstctl: unrecognized command '${1:-}'. Please consult the usage."
|
||||
;;
|
||||
esac
|
||||
@ -127,6 +127,11 @@ void parse_commandline(int argc, char *argv[])
|
||||
die(0);
|
||||
}
|
||||
|
||||
if (*appname == '\0') {
|
||||
g_printerr("Provided appname was empty\n");
|
||||
die(1);
|
||||
}
|
||||
|
||||
int n_args = count_args(argv, argc);
|
||||
if (n_args < 2 && close_id < 1) {
|
||||
g_printerr("I need at least a summary\n");
|
||||
@ -234,7 +239,7 @@ void add_action(NotifyNotification *n, char *str)
|
||||
char *label = strchr(str, ',');
|
||||
|
||||
if (!label || *(label+1) == '\0') {
|
||||
g_printerr("Malformed action. Excpected \"action,label\", got \"%s\"", str);
|
||||
g_printerr("Malformed action. Expected \"action,label\", got \"%s\"", str);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -359,4 +364,4 @@ int main(int argc, char *argv[])
|
||||
die(0);
|
||||
}
|
||||
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
91
dunstrc
@ -31,6 +31,23 @@
|
||||
# screen width minus the width defined in within the geometry option.
|
||||
geometry = "300x5-30+20"
|
||||
|
||||
# Turn on the progess bar
|
||||
progress_bar = true
|
||||
|
||||
# Set the progress bar height. This includes the frame, so make sure
|
||||
# it's at least twice as big as the frame width.
|
||||
progress_bar_height = 10
|
||||
|
||||
# Set the frame width of the progress bar
|
||||
progress_bar_frame_width = 1
|
||||
|
||||
# Set the minimum width for the progress bar
|
||||
progress_bar_min_width = 150
|
||||
|
||||
# Set the maximum width for the progress bar
|
||||
progress_bar_max_width = 300
|
||||
|
||||
|
||||
# Show how many messages are currently hidden (because of geometry).
|
||||
indicate_hidden = yes
|
||||
|
||||
@ -59,6 +76,9 @@
|
||||
# Horizontal padding.
|
||||
horizontal_padding = 8
|
||||
|
||||
# Padding between text and icon.
|
||||
text_icon_padding = 0
|
||||
|
||||
# Defines width in pixels of frame around the notification window.
|
||||
# Set to 0 to disable.
|
||||
frame_width = 3
|
||||
@ -100,7 +120,7 @@
|
||||
# <u>underline</u>
|
||||
#
|
||||
# For a complete reference see
|
||||
# <http://developer.gnome.org/pango/stable/PangoMarkupFormat.html>.
|
||||
# <https://developer.gnome.org/pango/stable/pango-Markup.html>.
|
||||
#
|
||||
# strip: This setting is provided for compatibility with some broken
|
||||
# clients that send markup even though it's not enabled on the
|
||||
@ -132,6 +152,10 @@
|
||||
# Possible values are "left", "center" and "right".
|
||||
alignment = left
|
||||
|
||||
# Vertical alignment of message text and icon.
|
||||
# Possible values are "top", "center" and "bottom".
|
||||
vertical_alignment = center
|
||||
|
||||
# Show age of message if message is older than show_age_threshold
|
||||
# seconds.
|
||||
# Set to -1 to disable.
|
||||
@ -160,7 +184,12 @@
|
||||
### Icons ###
|
||||
|
||||
# Align icons left/right/off
|
||||
icon_position = off
|
||||
icon_position = left
|
||||
|
||||
# Scale small icons up to this size, set to 0 to disable. Helpful
|
||||
# for e.g. small files or high-dpi screens. In case of conflict,
|
||||
# max_icon_size takes precedence over this.
|
||||
min_icon_size = 0
|
||||
|
||||
# Scale larger icons down to this size, set to 0 to disable
|
||||
max_icon_size = 32
|
||||
@ -215,6 +244,22 @@
|
||||
# notification height to avoid clipping text and/or icons.
|
||||
corner_radius = 0
|
||||
|
||||
# Ignore the dbus closeNotification message.
|
||||
# Useful to enforce the timeout set by dunst configuration. Without this
|
||||
# parameter, an application may close the notification sent before the
|
||||
# user defined timeout.
|
||||
ignore_dbusclose = false
|
||||
|
||||
### Wayland ###
|
||||
# These settings are Wayland-specific. They have no effect when using X11
|
||||
|
||||
# Uncomment this if you want to let notications appear under fullscreen
|
||||
# applications (default: overlay)
|
||||
# layer = top
|
||||
|
||||
# Set this to true to use X11 output on Wayland.
|
||||
force_xwayland = false
|
||||
|
||||
### Legacy
|
||||
|
||||
# Use the Xinerama extension instead of RandR for multi-monitor support.
|
||||
@ -229,15 +274,21 @@
|
||||
|
||||
### mouse
|
||||
|
||||
# Defines action of mouse event
|
||||
# Defines list of actions for each mouse event
|
||||
# Possible values are:
|
||||
# * none: Don't do anything.
|
||||
# * do_action: If the notification has exactly one action, or one is marked as default,
|
||||
# invoke it. If there are multiple and no default, open the context menu.
|
||||
# * do_action: Invoke the action determined by the action_name rule. If there is no
|
||||
# such action, open the context menu.
|
||||
# * open_url: If the notification has exactly one url, open it. If there are multiple
|
||||
# ones, open the context menu.
|
||||
# * close_current: Close current notification.
|
||||
# * close_all: Close all notifications.
|
||||
# * context: Open context menu for the notification.
|
||||
# * context_all: Open context menu for all notifications.
|
||||
# These values can be strung together for each mouse event, and
|
||||
# will be executed in sequence.
|
||||
mouse_left_click = close_current
|
||||
mouse_middle_click = do_action
|
||||
mouse_middle_click = do_action, close_current
|
||||
mouse_right_click = close_all
|
||||
|
||||
# Experimental features that may or may not work correctly. Do not expect them
|
||||
@ -250,6 +301,10 @@
|
||||
# where there are multiple screens with very different dpi values.
|
||||
per_monitor_dpi = false
|
||||
|
||||
# The internal keyboard shortcut support in dunst is now considered deprecated
|
||||
# and should be replaced by dunstctl calls. You can use the configuration of your
|
||||
# WM or DE to bind these to shortcuts of your choice.
|
||||
# Check the dunstctl manual page for more info.
|
||||
[shortcuts]
|
||||
|
||||
# Shortcuts are specified as [modifier+][modifier+]...key
|
||||
@ -257,20 +312,21 @@
|
||||
# "mod3" and "mod4" (windows-key).
|
||||
# Xev might be helpful to find names for keys.
|
||||
|
||||
# Close notification.
|
||||
close = ctrl+space
|
||||
# Close notification. Equivalent dunstctl command:
|
||||
# dunstctl close
|
||||
# close = ctrl+space
|
||||
|
||||
# Close all notifications.
|
||||
close_all = ctrl+shift+space
|
||||
# Close all notifications. Equivalent dunstctl command:
|
||||
# dunstctl close-all
|
||||
# close_all = ctrl+shift+space
|
||||
|
||||
# Redisplay last message(s).
|
||||
# On the US keyboard layout "grave" is normally above TAB and left
|
||||
# of "1". Make sure this key actually exists on your keyboard layout,
|
||||
# e.g. check output of 'xmodmap -pke'
|
||||
history = ctrl+grave
|
||||
# Redisplay last message(s). Equivalent dunstctl command:
|
||||
# dunstctl history-pop
|
||||
# history = ctrl+grave
|
||||
|
||||
# Context menu.
|
||||
context = ctrl+shift+period
|
||||
# Context menu. Equivalent dunstctl command:
|
||||
# dunstctl context
|
||||
# context = ctrl+shift+period
|
||||
|
||||
[urgency_low]
|
||||
# IMPORTANT: colors have to be defined in quotation marks.
|
||||
@ -321,6 +377,7 @@
|
||||
# set_transient
|
||||
# timeout
|
||||
# urgency
|
||||
# action_name
|
||||
#
|
||||
# Shell-like globbing will get expanded.
|
||||
#
|
||||
|
||||
2
main.c
@ -4,4 +4,4 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
return dunst_main(argc, argv);
|
||||
}
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
344
src/dbus.c
@ -8,6 +8,7 @@
|
||||
|
||||
#include "dunst.h"
|
||||
#include "log.h"
|
||||
#include "menu.h"
|
||||
#include "notification.h"
|
||||
#include "queues.h"
|
||||
#include "settings.h"
|
||||
@ -17,6 +18,12 @@
|
||||
#define FDN_IFAC "org.freedesktop.Notifications"
|
||||
#define FDN_NAME "org.freedesktop.Notifications"
|
||||
|
||||
#define DUNST_PATH "/org/freedesktop/Notifications"
|
||||
#define DUNST_IFAC "org.dunstproject.cmd0"
|
||||
#define DUNST_NAME "org.freedesktop.Notifications"
|
||||
|
||||
#define PROPERTIES_IFAC "org.freedesktop.DBus.Properties"
|
||||
|
||||
GDBusConnection *dbus_conn;
|
||||
|
||||
static GDBusNodeInfo *introspection_data = NULL;
|
||||
@ -62,7 +69,27 @@ static const char *introspection_xml =
|
||||
" <arg name=\"id\" type=\"u\"/>"
|
||||
" <arg name=\"action_key\" type=\"s\"/>"
|
||||
" </signal>"
|
||||
" </interface>"
|
||||
" </interface>"
|
||||
" <interface name=\""DUNST_IFAC"\">"
|
||||
|
||||
" <method name=\"ContextMenuCall\" />"
|
||||
" <method name=\"NotificationAction\">"
|
||||
" <arg name=\"number\" type=\"i\"/>"
|
||||
" </method>"
|
||||
" <method name=\"NotificationCloseLast\" />"
|
||||
" <method name=\"NotificationCloseAll\" />"
|
||||
" <method name=\"NotificationShow\" />"
|
||||
" <method name=\"Ping\" />"
|
||||
|
||||
" <property name=\"paused\" type=\"b\" access=\"readwrite\">"
|
||||
" <annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"true\"/>"
|
||||
" </property>"
|
||||
|
||||
" <property name=\"displayedLength\" type=\"u\" access=\"read\" />"
|
||||
" <property name=\"historyLength\" type=\"u\" access=\"read\" />"
|
||||
" <property name=\"waitingLength\" type=\"u\" access=\"read\" />"
|
||||
|
||||
" </interface>"
|
||||
"</node>";
|
||||
|
||||
static const char *stack_tag_hints[] = {
|
||||
@ -98,7 +125,6 @@ DBUS_METHOD(Notify);
|
||||
DBUS_METHOD(CloseNotification);
|
||||
DBUS_METHOD(GetCapabilities);
|
||||
DBUS_METHOD(GetServerInformation);
|
||||
|
||||
static struct dbus_method methods_fdn[] = {
|
||||
{"CloseNotification", dbus_cb_CloseNotification},
|
||||
{"GetCapabilities", dbus_cb_GetCapabilities},
|
||||
@ -106,7 +132,7 @@ static struct dbus_method methods_fdn[] = {
|
||||
{"Notify", dbus_cb_Notify},
|
||||
};
|
||||
|
||||
void handle_method_call(GDBusConnection *connection,
|
||||
void dbus_cb_fdn_methods(GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
@ -115,12 +141,12 @@ void handle_method_call(GDBusConnection *connection,
|
||||
GDBusMethodInvocation *invocation,
|
||||
gpointer user_data)
|
||||
{
|
||||
struct dbus_method *m = bsearch(
|
||||
method_name,
|
||||
&methods_fdn,
|
||||
G_N_ELEMENTS(methods_fdn),
|
||||
sizeof(struct dbus_method),
|
||||
cmp_methods);
|
||||
|
||||
struct dbus_method *m = bsearch(method_name,
|
||||
methods_fdn,
|
||||
G_N_ELEMENTS(methods_fdn),
|
||||
sizeof(struct dbus_method),
|
||||
cmp_methods);
|
||||
|
||||
if (m) {
|
||||
m->method(connection, sender, parameters, invocation);
|
||||
@ -131,6 +157,143 @@ void handle_method_call(GDBusConnection *connection,
|
||||
}
|
||||
}
|
||||
|
||||
DBUS_METHOD(dunst_ContextMenuCall);
|
||||
DBUS_METHOD(dunst_NotificationAction);
|
||||
DBUS_METHOD(dunst_NotificationCloseAll);
|
||||
DBUS_METHOD(dunst_NotificationCloseLast);
|
||||
DBUS_METHOD(dunst_NotificationShow);
|
||||
DBUS_METHOD(dunst_Ping);
|
||||
static struct dbus_method methods_dunst[] = {
|
||||
{"ContextMenuCall", dbus_cb_dunst_ContextMenuCall},
|
||||
{"NotificationAction", dbus_cb_dunst_NotificationAction},
|
||||
{"NotificationCloseAll", dbus_cb_dunst_NotificationCloseAll},
|
||||
{"NotificationCloseLast", dbus_cb_dunst_NotificationCloseLast},
|
||||
{"NotificationShow", dbus_cb_dunst_NotificationShow},
|
||||
{"Ping", dbus_cb_dunst_Ping},
|
||||
};
|
||||
|
||||
void dbus_cb_dunst_methods(GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
const gchar *method_name,
|
||||
GVariant *parameters,
|
||||
GDBusMethodInvocation *invocation,
|
||||
gpointer user_data)
|
||||
{
|
||||
|
||||
struct dbus_method *m = bsearch(method_name,
|
||||
methods_dunst,
|
||||
G_N_ELEMENTS(methods_dunst),
|
||||
sizeof(struct dbus_method),
|
||||
cmp_methods);
|
||||
|
||||
if (m) {
|
||||
m->method(connection, sender, parameters, invocation);
|
||||
} else {
|
||||
LOG_M("Unknown method name: '%s' (sender: '%s').",
|
||||
method_name,
|
||||
sender);
|
||||
}
|
||||
}
|
||||
|
||||
static void dbus_cb_dunst_ContextMenuCall(GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
GVariant *parameters,
|
||||
GDBusMethodInvocation *invocation)
|
||||
{
|
||||
LOG_D("CMD: Calling context menu");
|
||||
context_menu();
|
||||
|
||||
g_dbus_method_invocation_return_value(invocation, NULL);
|
||||
g_dbus_connection_flush(connection, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static void dbus_cb_dunst_NotificationAction(GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
GVariant *parameters,
|
||||
GDBusMethodInvocation *invocation)
|
||||
{
|
||||
int notification_nr = 0;
|
||||
g_variant_get(parameters, "(i)", ¬ification_nr);
|
||||
|
||||
LOG_D("CMD: Calling action for notification %d", notification_nr);
|
||||
|
||||
if (notification_nr < 0 || queues_length_waiting() < notification_nr) {
|
||||
g_dbus_method_invocation_return_error(invocation,
|
||||
G_DBUS_ERROR,
|
||||
G_DBUS_ERROR_INVALID_ARGS,
|
||||
"Couldn't activate action for notification in position %d, %d notifications currently open",
|
||||
notification_nr, queues_length_waiting());
|
||||
return;
|
||||
}
|
||||
|
||||
struct notification *n = g_list_nth_data(queues_get_displayed(), notification_nr);
|
||||
|
||||
if (n) {
|
||||
LOG_D("CMD: Calling action for notification %s", n->summary);
|
||||
notification_do_action(n);
|
||||
}
|
||||
|
||||
g_dbus_method_invocation_return_value(invocation, NULL);
|
||||
g_dbus_connection_flush(connection, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static void dbus_cb_dunst_NotificationCloseAll(GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
GVariant *parameters,
|
||||
GDBusMethodInvocation *invocation)
|
||||
{
|
||||
LOG_D("CMD: Pushing all to history");
|
||||
queues_history_push_all();
|
||||
wake_up();
|
||||
|
||||
g_dbus_method_invocation_return_value(invocation, NULL);
|
||||
g_dbus_connection_flush(connection, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static void dbus_cb_dunst_NotificationCloseLast(GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
GVariant *parameters,
|
||||
GDBusMethodInvocation *invocation)
|
||||
{
|
||||
LOG_D("CMD: Closing last notification");
|
||||
const GList *list = queues_get_displayed();
|
||||
if (list && list->data) {
|
||||
struct notification *n = list->data;
|
||||
queues_notification_close_id(n->id, REASON_USER);
|
||||
wake_up();
|
||||
}
|
||||
|
||||
g_dbus_method_invocation_return_value(invocation, NULL);
|
||||
g_dbus_connection_flush(connection, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static void dbus_cb_dunst_NotificationShow(GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
GVariant *parameters,
|
||||
GDBusMethodInvocation *invocation)
|
||||
{
|
||||
LOG_D("CMD: Showing last notification from history");
|
||||
queues_history_pop();
|
||||
wake_up();
|
||||
|
||||
g_dbus_method_invocation_return_value(invocation, NULL);
|
||||
g_dbus_connection_flush(connection, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
/* Just a simple Ping command to give the ability to dunstctl to test for the existence of this interface
|
||||
* Any other way requires parsing the XML of the Introspection or other foo. Just calling the Ping on an old dunst version will fail. */
|
||||
static void dbus_cb_dunst_Ping(GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
GVariant *parameters,
|
||||
GDBusMethodInvocation *invocation)
|
||||
{
|
||||
g_dbus_method_invocation_return_value(invocation, NULL);
|
||||
g_dbus_connection_flush(connection, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
static void dbus_cb_GetCapabilities(
|
||||
GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
@ -144,6 +307,7 @@ static void dbus_cb_GetCapabilities(
|
||||
g_variant_builder_add(builder, "s", "actions");
|
||||
g_variant_builder_add(builder, "s", "body");
|
||||
g_variant_builder_add(builder, "s", "body-hyperlinks");
|
||||
g_variant_builder_add(builder, "s", "icon-static");
|
||||
|
||||
for (int i = 0; i < sizeof(stack_tag_hints)/sizeof(*stack_tag_hints); ++i)
|
||||
g_variant_builder_add(builder, "s", stack_tag_hints[i]);
|
||||
@ -222,6 +386,11 @@ static struct notification *dbus_message_to_notification(const gchar *sender, GV
|
||||
g_variant_unref(dict_value);
|
||||
}
|
||||
|
||||
if ((dict_value = g_variant_lookup_value(hints, "hlcolor", G_VARIANT_TYPE_STRING))) {
|
||||
n->colors.highlight = g_variant_dup_string(dict_value, NULL);
|
||||
g_variant_unref(dict_value);
|
||||
}
|
||||
|
||||
if ((dict_value = g_variant_lookup_value(hints, "category", G_VARIANT_TYPE_STRING))) {
|
||||
n->category = g_variant_dup_string(dict_value, NULL);
|
||||
g_variant_unref(dict_value);
|
||||
@ -286,7 +455,7 @@ static struct notification *dbus_message_to_notification(const gchar *sender, GV
|
||||
}
|
||||
|
||||
if (timeout >= 0)
|
||||
n->timeout = timeout * 1000;
|
||||
n->timeout = ((gint64)timeout) * 1000;
|
||||
|
||||
g_variant_unref(hints);
|
||||
g_variant_type_free(required_type);
|
||||
@ -335,7 +504,17 @@ static void dbus_cb_CloseNotification(
|
||||
{
|
||||
guint32 id;
|
||||
g_variant_get(parameters, "(u)", &id);
|
||||
queues_notification_close_id(id, REASON_SIG);
|
||||
if (settings.ignore_dbusclose) {
|
||||
LOG_D("Ignoring CloseNotification message");
|
||||
// Stay commpliant by lying to the sender, telling him we closed the notification
|
||||
if (id > 0) {
|
||||
struct notification *n = queues_get_by_id(id);
|
||||
if (n)
|
||||
signal_notification_closed(n, REASON_SIG);
|
||||
}
|
||||
} else {
|
||||
queues_notification_close_id(id, REASON_SIG);
|
||||
}
|
||||
wake_up();
|
||||
g_dbus_method_invocation_return_value(invocation, NULL);
|
||||
g_dbus_connection_flush(connection, NULL, NULL, NULL);
|
||||
@ -347,19 +526,15 @@ static void dbus_cb_GetServerInformation(
|
||||
GVariant *parameters,
|
||||
GDBusMethodInvocation *invocation)
|
||||
{
|
||||
GVariant *value;
|
||||
|
||||
value = g_variant_new("(ssss)", "dunst", "knopwob", VERSION, "1.2");
|
||||
g_dbus_method_invocation_return_value(invocation, value);
|
||||
GVariant *answer = g_variant_new("(ssss)", "dunst", "knopwob", VERSION, "1.2");
|
||||
|
||||
g_dbus_method_invocation_return_value(invocation, answer);
|
||||
g_dbus_connection_flush(connection, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void signal_notification_closed(struct notification *n, enum reason reason)
|
||||
{
|
||||
if (!n->dbus_valid) {
|
||||
LOG_W("Closing notification '%s' not supported. "
|
||||
"Notification already closed.", n->summary);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -384,11 +559,34 @@ void signal_notification_closed(struct notification *n, enum reason reason)
|
||||
body,
|
||||
&err);
|
||||
|
||||
notification_invalidate_actions(n);
|
||||
|
||||
n->dbus_valid = false;
|
||||
|
||||
if (err) {
|
||||
LOG_W("Unable to close notification: %s", err->message);
|
||||
g_error_free(err);
|
||||
} else {
|
||||
char* reason_string;
|
||||
switch (reason) {
|
||||
case REASON_TIME:
|
||||
reason_string="time";
|
||||
break;
|
||||
case REASON_USER:
|
||||
reason_string="user";
|
||||
break;
|
||||
case REASON_SIG:
|
||||
reason_string="signal";
|
||||
break;
|
||||
case REASON_UNDEF:
|
||||
reason_string="undfined";
|
||||
break;
|
||||
default:
|
||||
reason_string="unknown";
|
||||
}
|
||||
|
||||
LOG_D("Queues: Closing notification for reason: %s", reason_string);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -418,28 +616,106 @@ void signal_action_invoked(const struct notification *n, const char *identifier)
|
||||
}
|
||||
}
|
||||
|
||||
static const GDBusInterfaceVTable interface_vtable = {
|
||||
handle_method_call
|
||||
GVariant *dbus_cb_dunst_Properties_Get(GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
const gchar *property_name,
|
||||
GError **error,
|
||||
gpointer user_data)
|
||||
{
|
||||
struct dunst_status status = dunst_status_get();
|
||||
|
||||
if (STR_EQ(property_name, "paused")) {
|
||||
return g_variant_new_boolean(!status.running);
|
||||
} else if (STR_EQ(property_name, "displayedLength")) {
|
||||
unsigned int displayed = queues_length_displayed();
|
||||
return g_variant_new_uint32(displayed);
|
||||
} else if (STR_EQ(property_name, "historyLength")) {
|
||||
unsigned int history = queues_length_history();
|
||||
return g_variant_new_uint32(history);
|
||||
} else if (STR_EQ(property_name, "waitingLength")) {
|
||||
unsigned int waiting = queues_length_waiting();
|
||||
return g_variant_new_uint32(waiting);
|
||||
} else {
|
||||
LOG_W("Unknown property!\n");
|
||||
*error = g_error_new(G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean dbus_cb_dunst_Properties_Set(GDBusConnection *connection,
|
||||
const gchar *sender,
|
||||
const gchar *object_path,
|
||||
const gchar *interface_name,
|
||||
const gchar *property_name,
|
||||
GVariant *value,
|
||||
GError **error,
|
||||
gpointer user_data)
|
||||
{
|
||||
if (STR_EQ(property_name, "paused")) {
|
||||
dunst_status(S_RUNNING, !g_variant_get_boolean(value));
|
||||
wake_up();
|
||||
|
||||
GVariantBuilder *builder = g_variant_builder_new(G_VARIANT_TYPE_ARRAY);
|
||||
GVariantBuilder *invalidated_builder = g_variant_builder_new(G_VARIANT_TYPE("as"));
|
||||
g_variant_builder_add(builder,
|
||||
"{sv}",
|
||||
"paused", g_variant_new_boolean(g_variant_get_boolean(value)));
|
||||
g_dbus_connection_emit_signal(connection,
|
||||
NULL,
|
||||
object_path,
|
||||
"org.freedesktop.DBus.Properties",
|
||||
"PropertiesChanged",
|
||||
g_variant_new("(sa{sv}as)",
|
||||
interface_name,
|
||||
builder,
|
||||
invalidated_builder),
|
||||
NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
*error = g_error_new(G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_PROPERTY, "Unknown property");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static const GDBusInterfaceVTable interface_vtable_fdn = {
|
||||
dbus_cb_fdn_methods
|
||||
};
|
||||
|
||||
static const GDBusInterfaceVTable interface_vtable_dunst = {
|
||||
dbus_cb_dunst_methods,
|
||||
dbus_cb_dunst_Properties_Get,
|
||||
dbus_cb_dunst_Properties_Set,
|
||||
};
|
||||
|
||||
static void dbus_cb_bus_acquired(GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
gpointer user_data)
|
||||
{
|
||||
guint registration_id;
|
||||
|
||||
GError *err = NULL;
|
||||
if(!g_dbus_connection_register_object(
|
||||
connection,
|
||||
FDN_PATH,
|
||||
introspection_data->interfaces[0],
|
||||
&interface_vtable_fdn,
|
||||
NULL,
|
||||
NULL,
|
||||
&err)) {
|
||||
DIE("Unable to register dbus connection interface '%s': %s", introspection_data->interfaces[0]->name, err->message);
|
||||
}
|
||||
|
||||
registration_id = g_dbus_connection_register_object(connection,
|
||||
FDN_PATH,
|
||||
introspection_data->interfaces[0],
|
||||
&interface_vtable,
|
||||
NULL,
|
||||
NULL,
|
||||
&err);
|
||||
|
||||
if (registration_id == 0) {
|
||||
DIE("Unable to register dbus connection: %s", err->message);
|
||||
if(!g_dbus_connection_register_object(
|
||||
connection,
|
||||
FDN_PATH,
|
||||
introspection_data->interfaces[1],
|
||||
&interface_vtable_dunst,
|
||||
NULL,
|
||||
NULL,
|
||||
&err)) {
|
||||
DIE("Unable to register dbus connection interface '%s': %s", introspection_data->interfaces[1]->name, err->message);
|
||||
}
|
||||
}
|
||||
|
||||
@ -447,7 +723,9 @@ static void dbus_cb_name_acquired(GDBusConnection *connection,
|
||||
const gchar *name,
|
||||
gpointer user_data)
|
||||
{
|
||||
dbus_conn = connection;
|
||||
// If we're not able to get org.fd.N bus, we've still got a problem
|
||||
if (STR_EQ(name, FDN_NAME))
|
||||
dbus_conn = connection;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -609,4 +887,4 @@ void dbus_teardown(int owner_id)
|
||||
g_bus_unown_name(owner_id);
|
||||
}
|
||||
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#ifndef DUNST_DBUS_H
|
||||
#define DUNST_DBUS_H
|
||||
|
||||
#include "dunst.h"
|
||||
#include "notification.h"
|
||||
|
||||
/// The reasons according to the notification spec
|
||||
@ -21,4 +22,4 @@ void signal_notification_closed(struct notification *n, enum reason reason);
|
||||
void signal_action_invoked(const struct notification *n, const char *identifier);
|
||||
|
||||
#endif
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
354
src/draw.c
@ -1,7 +1,6 @@
|
||||
#include "draw.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <cairo.h>
|
||||
#include <math.h>
|
||||
#include <pango/pango-attributes.h>
|
||||
#include <pango/pangocairo.h>
|
||||
@ -9,6 +8,8 @@
|
||||
#include <pango/pango-layout.h>
|
||||
#include <pango/pango-types.h>
|
||||
#include <stdlib.h>
|
||||
#include <inttypes.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "dunst.h"
|
||||
#include "icon.h"
|
||||
@ -16,12 +17,21 @@
|
||||
#include "markup.h"
|
||||
#include "notification.h"
|
||||
#include "queues.h"
|
||||
#include "x11/x.h"
|
||||
#include "output.h"
|
||||
#include "settings.h"
|
||||
|
||||
struct color {
|
||||
double r;
|
||||
double g;
|
||||
double b;
|
||||
double a;
|
||||
};
|
||||
|
||||
struct colored_layout {
|
||||
PangoLayout *l;
|
||||
struct color fg;
|
||||
struct color bg;
|
||||
struct color highlight;
|
||||
struct color frame;
|
||||
char *text;
|
||||
PangoAttrList *attr;
|
||||
@ -29,24 +39,33 @@ struct colored_layout {
|
||||
const struct notification *n;
|
||||
};
|
||||
|
||||
struct window_x11 *win;
|
||||
const struct output *output;
|
||||
window win;
|
||||
|
||||
PangoFontDescription *pango_fdesc;
|
||||
|
||||
#define UINT_MAX_N(bits) ((1 << bits) - 1)
|
||||
|
||||
void draw_setup(void)
|
||||
{
|
||||
x_setup();
|
||||
const struct output *out = output_create(settings.force_xwayland);
|
||||
output = out;
|
||||
|
||||
win = out->win_create();
|
||||
|
||||
win = x_win_create();
|
||||
pango_fdesc = pango_font_description_from_string(settings.font);
|
||||
}
|
||||
|
||||
static struct color hex_to_color(int hexValue)
|
||||
static struct color hex_to_color(uint32_t hexValue, int dpc)
|
||||
{
|
||||
const int bpc = 4 * dpc;
|
||||
const unsigned single_max = UINT_MAX_N(bpc);
|
||||
|
||||
struct color ret;
|
||||
ret.r = ((hexValue >> 16) & 0xFF) / 255.0;
|
||||
ret.g = ((hexValue >> 8) & 0xFF) / 255.0;
|
||||
ret.b = ((hexValue) & 0xFF) / 255.0;
|
||||
ret.r = ((hexValue >> 3 * bpc) & single_max) / (double)single_max;
|
||||
ret.g = ((hexValue >> 2 * bpc) & single_max) / (double)single_max;
|
||||
ret.b = ((hexValue >> 1 * bpc) & single_max) / (double)single_max;
|
||||
ret.a = ((hexValue) & single_max) / (double)single_max;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -54,15 +73,24 @@ static struct color hex_to_color(int hexValue)
|
||||
static struct color string_to_color(const char *str)
|
||||
{
|
||||
char *end;
|
||||
long int val = strtol(str+1, &end, 16);
|
||||
if (*end != '\0' && *(end+1) != '\0') {
|
||||
uint_fast32_t val = strtoul(str+1, &end, 16);
|
||||
if (end[0] != '\0' && end[1] != '\0') {
|
||||
LOG_W("Invalid color string: '%s'", str);
|
||||
}
|
||||
|
||||
return hex_to_color(val);
|
||||
switch (end - (str+1)) {
|
||||
case 3: return hex_to_color((val << 4) | 0xF, 1);
|
||||
case 6: return hex_to_color((val << 8) | 0xFF, 2);
|
||||
case 4: return hex_to_color(val, 1);
|
||||
case 8: return hex_to_color(val, 2);
|
||||
}
|
||||
|
||||
/* return black on error */
|
||||
LOG_W("Invalid color string: '%s'", str);
|
||||
return hex_to_color(0xF, 1);
|
||||
}
|
||||
|
||||
static double color_apply_delta(double base, double delta)
|
||||
static inline double color_apply_delta(double base, double delta)
|
||||
{
|
||||
base += delta;
|
||||
if (base > 1)
|
||||
@ -113,10 +141,11 @@ static struct color layout_get_sepcolor(struct colored_layout *cl,
|
||||
|
||||
static void layout_setup_pango(PangoLayout *layout, int width)
|
||||
{
|
||||
int scale = output->get_scale();
|
||||
pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
|
||||
pango_layout_set_width(layout, width * PANGO_SCALE);
|
||||
pango_layout_set_width(layout, width * scale * PANGO_SCALE);
|
||||
pango_layout_set_font_description(layout, pango_fdesc);
|
||||
pango_layout_set_spacing(layout, settings.line_height * PANGO_SCALE);
|
||||
pango_layout_set_spacing(layout, settings.line_height * scale * PANGO_SCALE);
|
||||
|
||||
PangoAlignment align;
|
||||
switch (settings.align) {
|
||||
@ -150,11 +179,36 @@ static bool have_dynamic_width(void)
|
||||
return (settings.geometry.width_set && settings.geometry.w == 0);
|
||||
}
|
||||
|
||||
static int get_text_icon_padding()
|
||||
{
|
||||
if (settings.text_icon_padding) {
|
||||
return settings.text_icon_padding;
|
||||
} else {
|
||||
return settings.h_padding;
|
||||
}
|
||||
}
|
||||
|
||||
static bool have_progress_bar(const struct notification *n)
|
||||
{
|
||||
return (n->progress >= 0 && settings.progress_bar == true);
|
||||
}
|
||||
|
||||
static void get_text_size(PangoLayout *l, int *w, int *h, int scale) {
|
||||
pango_layout_get_pixel_size(l, w, h);
|
||||
// scale the size down, because it may be rendered at higher DPI
|
||||
|
||||
if (w)
|
||||
*w /= scale;
|
||||
if (h)
|
||||
*h /= scale;
|
||||
}
|
||||
|
||||
static struct dimensions calculate_dimensions(GSList *layouts)
|
||||
{
|
||||
struct dimensions dim = { 0 };
|
||||
int scale = output->get_scale();
|
||||
|
||||
struct screen_info *scr = get_active_screen();
|
||||
const struct screen_info *scr = output->get_active_screen();
|
||||
if (have_dynamic_width()) {
|
||||
/* dynamic width */
|
||||
dim.w = 0;
|
||||
@ -179,10 +233,10 @@ static struct dimensions calculate_dimensions(GSList *layouts)
|
||||
for (GSList *iter = layouts; iter; iter = iter->next) {
|
||||
struct colored_layout *cl = iter->data;
|
||||
int w=0,h=0;
|
||||
pango_layout_get_pixel_size(cl->l, &w, &h);
|
||||
get_text_size(cl->l, &w, &h, scale);
|
||||
if (cl->icon) {
|
||||
h = MAX(cairo_image_surface_get_height(cl->icon), h);
|
||||
w += cairo_image_surface_get_width(cl->icon) + settings.h_padding;
|
||||
h = MAX(get_icon_height(cl->icon, scale), h);
|
||||
w += get_icon_width(cl->icon, scale) + settings.h_padding;
|
||||
}
|
||||
h = MAX(settings.notification_height, h + settings.padding * 2);
|
||||
dim.h += h;
|
||||
@ -207,20 +261,27 @@ static struct dimensions calculate_dimensions(GSList *layouts)
|
||||
w = dim.w;
|
||||
w -= 2 * settings.h_padding;
|
||||
w -= 2 * settings.frame_width;
|
||||
if (cl->icon) w -= cairo_image_surface_get_width(cl->icon) + settings.h_padding;
|
||||
if (cl->icon) {
|
||||
w -= get_icon_width(cl->icon, scale) + get_text_icon_padding();
|
||||
}
|
||||
layout_setup_pango(cl->l, w);
|
||||
|
||||
/* re-read information */
|
||||
pango_layout_get_pixel_size(cl->l, &w, &h);
|
||||
get_text_size(cl->l, &w, &h, scale);
|
||||
if (cl->icon) {
|
||||
h = MAX(cairo_image_surface_get_height(cl->icon), h);
|
||||
w += cairo_image_surface_get_width(cl->icon) + settings.h_padding;
|
||||
h = MAX(get_icon_height(cl->icon, scale), h);
|
||||
w += get_icon_width(cl->icon, scale) + settings.h_padding;
|
||||
}
|
||||
h = MAX(settings.notification_height, h + settings.padding * 2);
|
||||
dim.h += h;
|
||||
text_width = MAX(w, text_width);
|
||||
}
|
||||
|
||||
if (have_progress_bar(cl->n)){
|
||||
dim.h += settings.progress_bar_height + settings.padding;
|
||||
dim.w = MAX(dim.w, settings.progress_bar_min_width);
|
||||
}
|
||||
|
||||
dim.corner_radius = MIN(dim.corner_radius, h/2);
|
||||
}
|
||||
|
||||
@ -234,10 +295,10 @@ static struct dimensions calculate_dimensions(GSList *layouts)
|
||||
|
||||
static PangoLayout *layout_create(cairo_t *c)
|
||||
{
|
||||
struct screen_info *screen = get_active_screen();
|
||||
const struct screen_info *screen = output->get_active_screen();
|
||||
|
||||
PangoContext *context = pango_cairo_create_context(c);
|
||||
pango_cairo_context_set_resolution(context, screen_dpi_get(screen));
|
||||
pango_cairo_context_set_resolution(context, screen->dpi);
|
||||
|
||||
PangoLayout *layout = pango_layout_new(context);
|
||||
|
||||
@ -250,6 +311,7 @@ static struct colored_layout *layout_init_shared(cairo_t *c, const struct notifi
|
||||
{
|
||||
struct colored_layout *cl = g_malloc(sizeof(struct colored_layout));
|
||||
cl->l = layout_create(c);
|
||||
int scale = output->get_scale();
|
||||
|
||||
if (!settings.word_wrap) {
|
||||
PangoEllipsizeMode ellipsize;
|
||||
@ -283,6 +345,7 @@ static struct colored_layout *layout_init_shared(cairo_t *c, const struct notifi
|
||||
|
||||
cl->fg = string_to_color(n->colors.fg);
|
||||
cl->bg = string_to_color(n->colors.bg);
|
||||
cl->highlight = string_to_color(n->colors.highlight);
|
||||
cl->frame = string_to_color(n->colors.frame);
|
||||
|
||||
cl->n = n;
|
||||
@ -295,7 +358,9 @@ static struct colored_layout *layout_init_shared(cairo_t *c, const struct notifi
|
||||
} else {
|
||||
width -= 2 * settings.h_padding;
|
||||
width -= 2 * settings.frame_width;
|
||||
if (cl->icon) width -= cairo_image_surface_get_width(cl->icon) + settings.h_padding;
|
||||
if (cl->icon) {
|
||||
width -= get_icon_width(cl->icon, scale) + get_text_icon_padding();
|
||||
}
|
||||
layout_setup_pango(cl->l, width);
|
||||
}
|
||||
|
||||
@ -315,6 +380,7 @@ static struct colored_layout *layout_from_notification(cairo_t *c, struct notifi
|
||||
{
|
||||
|
||||
struct colored_layout *cl = layout_init_shared(c, n);
|
||||
int scale = output->get_scale();
|
||||
|
||||
/* markup */
|
||||
GError *err = NULL;
|
||||
@ -336,9 +402,15 @@ static struct colored_layout *layout_from_notification(cairo_t *c, struct notifi
|
||||
}
|
||||
|
||||
|
||||
pango_layout_get_pixel_size(cl->l, NULL, &(n->displayed_height));
|
||||
if (cl->icon) n->displayed_height = MAX(cairo_image_surface_get_height(cl->icon), n->displayed_height);
|
||||
n->displayed_height = MAX(settings.notification_height, n->displayed_height + settings.padding * 2);
|
||||
get_text_size(cl->l, NULL, &(n->displayed_height), scale);
|
||||
if (cl->icon) n->displayed_height = MAX(get_icon_height(cl->icon, scale), n->displayed_height);
|
||||
|
||||
n->displayed_height = n->displayed_height + settings.padding * 2;
|
||||
|
||||
// progress bar
|
||||
if (have_progress_bar(n)) n->displayed_height += settings.progress_bar_height + settings.padding;
|
||||
|
||||
n->displayed_height = MAX(settings.notification_height, n->displayed_height);
|
||||
|
||||
n->first_render = false;
|
||||
return cl;
|
||||
@ -377,15 +449,46 @@ static GSList *create_layouts(cairo_t *c)
|
||||
}
|
||||
|
||||
|
||||
static int layout_get_height(struct colored_layout *cl)
|
||||
static int layout_get_height(struct colored_layout *cl, int scale)
|
||||
{
|
||||
int h;
|
||||
int h_icon = 0;
|
||||
pango_layout_get_pixel_size(cl->l, NULL, &h);
|
||||
int h_progress_bar = 0;
|
||||
get_text_size(cl->l, NULL, &h, scale);
|
||||
if (cl->icon)
|
||||
h_icon = cairo_image_surface_get_height(cl->icon);
|
||||
h_icon = get_icon_height(cl->icon, scale);
|
||||
if (have_progress_bar(cl->n)){
|
||||
h_progress_bar = settings.progress_bar_height + settings.padding;
|
||||
}
|
||||
|
||||
return MAX(h, h_icon);
|
||||
int res = MAX(h, h_icon) + h_progress_bar;
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Attempt to make internal radius more organic.
|
||||
* Simple r-w is not enough for too small r/w ratio.
|
||||
* simplifications: r/2 == r - w + w*w / (r * 2) with (w == r)
|
||||
* r, w - corner radius & frame width,
|
||||
* h - box height
|
||||
*/
|
||||
static int frame_internal_radius (int r, int w, int h)
|
||||
{
|
||||
if (r == 0 || h + (w - r) * 2 == 0)
|
||||
return 0;
|
||||
|
||||
// Integer precision scaler, using 1/4 of int size
|
||||
const int s = 2 << (8 * sizeof(int) / 4);
|
||||
|
||||
int r1, r2, ret;
|
||||
h *= s;
|
||||
r *= s;
|
||||
w *= s;
|
||||
r1 = r - w + w * w / (r * 2); // w < r
|
||||
r2 = r * h / (h + (w - r) * 2); // w >= r
|
||||
|
||||
ret = (r > w) ? r1 : (r / 2 < r2) ? r / 2 : r2;
|
||||
|
||||
return ret / s;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -393,8 +496,14 @@ static int layout_get_height(struct colored_layout *cl)
|
||||
* The top corners will get rounded by `corner_radius`, if `first` is set.
|
||||
* Respectably the same for `last` with the bottom corners.
|
||||
*/
|
||||
static void draw_rounded_rect(cairo_t *c, int x, int y, int width, int height, int corner_radius, bool first, bool last)
|
||||
void draw_rounded_rect(cairo_t *c, int x, int y, int width, int height, int corner_radius, int scale, bool first, bool last)
|
||||
{
|
||||
width *= scale;
|
||||
height *= scale;
|
||||
x *= scale;
|
||||
y *= scale;
|
||||
corner_radius *= scale;
|
||||
|
||||
const float degrees = M_PI / 180.0;
|
||||
|
||||
cairo_new_sub_path(c);
|
||||
@ -442,6 +551,13 @@ static void draw_rounded_rect(cairo_t *c, int x, int y, int width, int height, i
|
||||
cairo_close_path(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* A small wrapper around cairo_rectange for drawing a scaled rectangle.
|
||||
*/
|
||||
void draw_rect(cairo_t *c, int x, int y, int width, int height, int scale) {
|
||||
cairo_rectangle(c, x * scale, y * scale, width * scale, height * scale);
|
||||
}
|
||||
|
||||
static cairo_surface_t *render_background(cairo_surface_t *srf,
|
||||
struct colored_layout *cl,
|
||||
struct colored_layout *cl_next,
|
||||
@ -451,12 +567,20 @@ static cairo_surface_t *render_background(cairo_surface_t *srf,
|
||||
int corner_radius,
|
||||
bool first,
|
||||
bool last,
|
||||
int *ret_width)
|
||||
int *ret_width,
|
||||
int scale)
|
||||
{
|
||||
int x = 0;
|
||||
int radius_int = corner_radius;
|
||||
|
||||
cairo_t *c = cairo_create(srf);
|
||||
|
||||
/* stroke area doesn't intersect with main area */
|
||||
cairo_set_fill_rule(c, CAIRO_FILL_RULE_EVEN_ODD);
|
||||
|
||||
/* for correct combination of adjacent areas */
|
||||
cairo_set_operator(c, CAIRO_OPERATOR_ADD);
|
||||
|
||||
if (first)
|
||||
height += settings.frame_width;
|
||||
if (last)
|
||||
@ -464,9 +588,7 @@ static cairo_surface_t *render_background(cairo_surface_t *srf,
|
||||
else
|
||||
height += settings.separator_height;
|
||||
|
||||
cairo_set_source_rgb(c, cl->frame.r, cl->frame.g, cl->frame.b);
|
||||
draw_rounded_rect(c, x, y, width, height, corner_radius, first, last);
|
||||
cairo_fill(c);
|
||||
draw_rounded_rect(c, x, y, width, height, corner_radius, scale, first, last);
|
||||
|
||||
/* adding frame */
|
||||
x += settings.frame_width;
|
||||
@ -482,17 +604,25 @@ static cairo_surface_t *render_background(cairo_surface_t *srf,
|
||||
else
|
||||
height -= settings.separator_height;
|
||||
|
||||
cairo_set_source_rgb(c, cl->bg.r, cl->bg.g, cl->bg.b);
|
||||
draw_rounded_rect(c, x, y, width, height, corner_radius, first, last);
|
||||
radius_int = frame_internal_radius(corner_radius, settings.frame_width, height);
|
||||
|
||||
draw_rounded_rect(c, x, y, width, height, radius_int, scale, first, last);
|
||||
cairo_set_source_rgba(c, cl->frame.r, cl->frame.g, cl->frame.b, cl->frame.a);
|
||||
cairo_fill(c);
|
||||
|
||||
draw_rounded_rect(c, x, y, width, height, radius_int, scale, first, last);
|
||||
cairo_set_source_rgba(c, cl->bg.r, cl->bg.g, cl->bg.b, cl->bg.a);
|
||||
cairo_fill(c);
|
||||
|
||||
cairo_set_operator(c, CAIRO_OPERATOR_SOURCE);
|
||||
|
||||
if ( settings.sep_color.type != SEP_FRAME
|
||||
&& settings.separator_height > 0
|
||||
&& !last) {
|
||||
struct color sep_color = layout_get_sepcolor(cl, cl_next);
|
||||
cairo_set_source_rgb(c, sep_color.r, sep_color.g, sep_color.b);
|
||||
cairo_set_source_rgba(c, sep_color.r, sep_color.g, sep_color.b, sep_color.a);
|
||||
|
||||
cairo_rectangle(c, settings.frame_width, y + height, width, settings.separator_height);
|
||||
draw_rect(c, settings.frame_width, y + height, width, settings.separator_height, scale);
|
||||
|
||||
cairo_fill(c);
|
||||
}
|
||||
@ -502,48 +632,104 @@ static cairo_surface_t *render_background(cairo_surface_t *srf,
|
||||
if (ret_width)
|
||||
*ret_width = width;
|
||||
|
||||
return cairo_surface_create_for_rectangle(srf, x, y, width, height);
|
||||
return cairo_surface_create_for_rectangle(srf, x * scale, y * scale, width * scale, height * scale);
|
||||
}
|
||||
|
||||
static void render_content(cairo_t *c, struct colored_layout *cl, int width)
|
||||
static void render_content(cairo_t *c, struct colored_layout *cl, int width, int scale)
|
||||
{
|
||||
const int h = layout_get_height(cl);
|
||||
int h_text;
|
||||
pango_layout_get_pixel_size(cl->l, NULL, &h_text);
|
||||
|
||||
if (cl->icon && settings.icon_position == ICON_LEFT) {
|
||||
cairo_move_to(c, cairo_image_surface_get_width(cl->icon) + 2 * settings.h_padding,
|
||||
settings.padding + h/2 - h_text/2);
|
||||
} else if (cl->icon && settings.icon_position == ICON_RIGHT) {
|
||||
cairo_move_to(c, settings.h_padding, settings.padding + h/2 - h_text/2);
|
||||
} else {
|
||||
cairo_move_to(c, settings.h_padding, settings.padding);
|
||||
const int h = layout_get_height(cl, scale);
|
||||
int h_without_progress_bar = h;
|
||||
if (have_progress_bar(cl->n)){
|
||||
h_without_progress_bar -= settings.progress_bar_height + settings.padding;
|
||||
}
|
||||
int h_text;
|
||||
get_text_size(cl->l, NULL, &h_text, scale);
|
||||
|
||||
cairo_set_source_rgb(c, cl->fg.r, cl->fg.g, cl->fg.b);
|
||||
int text_x = settings.h_padding,
|
||||
text_y = settings.padding + h_without_progress_bar / 2 - h_text / 2;
|
||||
|
||||
// text positioning
|
||||
if (cl->icon) {
|
||||
// vertical alignment
|
||||
if (settings.vertical_alignment == VERTICAL_TOP) {
|
||||
text_y = settings.padding;
|
||||
} else if (settings.vertical_alignment == VERTICAL_BOTTOM) {
|
||||
text_y = h_without_progress_bar + settings.padding - h_text;
|
||||
if (text_y < 0)
|
||||
text_y = settings.padding;
|
||||
} // else VERTICAL_CENTER
|
||||
|
||||
// icon position
|
||||
if (settings.icon_position == ICON_LEFT) {
|
||||
text_x = get_icon_width(cl->icon, scale) + settings.h_padding + get_text_icon_padding();
|
||||
} // else ICON_RIGHT
|
||||
}
|
||||
cairo_move_to(c, text_x * scale, text_y * scale);
|
||||
|
||||
cairo_set_source_rgba(c, cl->fg.r, cl->fg.g, cl->fg.b, cl->fg.a);
|
||||
pango_cairo_update_layout(c, cl->l);
|
||||
pango_cairo_show_layout(c, cl->l);
|
||||
|
||||
|
||||
// icon positioning
|
||||
if (cl->icon) {
|
||||
unsigned int image_width = cairo_image_surface_get_width(cl->icon),
|
||||
image_height = cairo_image_surface_get_height(cl->icon),
|
||||
image_x,
|
||||
image_y = settings.padding + h/2 - image_height/2;
|
||||
unsigned int image_width = get_icon_width(cl->icon, scale),
|
||||
image_height = get_icon_height(cl->icon, scale),
|
||||
image_x = width - settings.h_padding - image_width,
|
||||
image_y = settings.padding + h_without_progress_bar/2 - image_height/2;
|
||||
|
||||
// vertical alignment
|
||||
if (settings.vertical_alignment == VERTICAL_TOP) {
|
||||
image_y = settings.padding;
|
||||
} else if (settings.vertical_alignment == VERTICAL_BOTTOM) {
|
||||
image_y = h_without_progress_bar + settings.padding - image_height;
|
||||
if (image_y < settings.padding || image_y > h_without_progress_bar)
|
||||
image_y = settings.padding;
|
||||
} // else VERTICAL_CENTER
|
||||
|
||||
// icon position
|
||||
if (settings.icon_position == ICON_LEFT) {
|
||||
image_x = settings.h_padding;
|
||||
} else if (settings.icon_position == ICON_RIGHT){
|
||||
image_x = width - settings.h_padding - image_width;
|
||||
} else {
|
||||
LOG_E("Tried to draw icon but icon position is not valid. %s:%d", __FILE__, __LINE__);
|
||||
}
|
||||
} // else ICON_RIGHT
|
||||
|
||||
cairo_set_source_surface(c, cl->icon, image_x, image_y);
|
||||
cairo_rectangle(c, image_x, image_y, image_width, image_height);
|
||||
cairo_set_source_surface(c, cl->icon, image_x * scale, image_y * scale);
|
||||
draw_rect(c, image_x, image_y, image_width, image_height, scale);
|
||||
cairo_fill(c);
|
||||
}
|
||||
|
||||
// progress bar positioning
|
||||
if (have_progress_bar(cl->n)){
|
||||
int progress = MIN(cl->n->progress, 100);
|
||||
unsigned int frame_width = settings.progress_bar_frame_width,
|
||||
progress_width = MIN(width - 2 * settings.h_padding, settings.progress_bar_max_width),
|
||||
progress_height = settings.progress_bar_height - frame_width,
|
||||
frame_x = settings.h_padding,
|
||||
frame_y = settings.padding + h - settings.progress_bar_height,
|
||||
progress_width_without_frame = progress_width - 2 * frame_width,
|
||||
progress_width_1 = progress_width_without_frame * progress / 100,
|
||||
progress_width_2 = progress_width_without_frame - progress_width_1,
|
||||
x_bar_1 = frame_x + frame_width,
|
||||
x_bar_2 = x_bar_1 + progress_width_1;
|
||||
|
||||
double half_frame_width = frame_width / 2.0;
|
||||
|
||||
// draw progress bar
|
||||
// Note: the bar could be drawn a bit smaller, because the frame is drawn on top
|
||||
// left side
|
||||
cairo_set_source_rgba(c, cl->highlight.r, cl->highlight.g, cl->highlight.b, cl->highlight.a);
|
||||
draw_rect(c, x_bar_1, frame_y, progress_width_1, progress_height, scale);
|
||||
cairo_fill(c);
|
||||
// right side
|
||||
cairo_set_source_rgba(c, cl->bg.r, cl->bg.g, cl->bg.b, cl->bg.a);
|
||||
draw_rect(c, x_bar_2, frame_y, progress_width_2, progress_height, scale);
|
||||
cairo_fill(c);
|
||||
// border
|
||||
cairo_set_source_rgba(c, cl->frame.r, cl->frame.g, cl->frame.b, cl->frame.a);
|
||||
// TODO draw_rect instead of cairo_rectangle resulted in blurry lines. Why?
|
||||
cairo_rectangle(c, (frame_x + half_frame_width) * scale, (frame_y + half_frame_width) * scale, (progress_width - frame_width) * scale, progress_height * scale);
|
||||
cairo_set_line_width(c, frame_width * scale);
|
||||
cairo_stroke(c);
|
||||
}
|
||||
}
|
||||
|
||||
static struct dimensions layout_render(cairo_surface_t *srf,
|
||||
@ -553,18 +739,19 @@ static struct dimensions layout_render(cairo_surface_t *srf,
|
||||
bool first,
|
||||
bool last)
|
||||
{
|
||||
const int cl_h = layout_get_height(cl);
|
||||
int scale = output->get_scale();
|
||||
const int cl_h = layout_get_height(cl, scale);
|
||||
|
||||
int h_text = 0;
|
||||
pango_layout_get_pixel_size(cl->l, NULL, &h_text);
|
||||
get_text_size(cl->l, NULL, &h_text, scale);
|
||||
|
||||
int bg_width = 0;
|
||||
int bg_height = MAX(settings.notification_height, (2 * settings.padding) + cl_h);
|
||||
|
||||
cairo_surface_t *content = render_background(srf, cl, cl_next, dim.y, dim.w, bg_height, dim.corner_radius, first, last, &bg_width);
|
||||
cairo_surface_t *content = render_background(srf, cl, cl_next, dim.y, dim.w, bg_height, dim.corner_radius, first, last, &bg_width, scale);
|
||||
cairo_t *c = cairo_create(content);
|
||||
|
||||
render_content(c, cl, bg_width);
|
||||
render_content(c, cl, bg_width, scale);
|
||||
|
||||
/* adding frame */
|
||||
if (first)
|
||||
@ -590,7 +777,7 @@ static struct dimensions layout_render(cairo_surface_t *srf,
|
||||
*/
|
||||
static void calc_window_pos(int width, int height, int *ret_x, int *ret_y)
|
||||
{
|
||||
struct screen_info *scr = get_active_screen();
|
||||
const struct screen_info *scr = output->get_active_screen();
|
||||
|
||||
if (ret_x) {
|
||||
if (settings.geometry.negative_x) {
|
||||
@ -613,11 +800,12 @@ void draw(void)
|
||||
{
|
||||
assert(queues_length_displayed() > 0);
|
||||
|
||||
GSList *layouts = create_layouts(x_win_get_context(win));
|
||||
GSList *layouts = create_layouts(output->win_get_context(win));
|
||||
|
||||
struct dimensions dim = calculate_dimensions(layouts);
|
||||
int scale = output->get_scale();
|
||||
|
||||
cairo_surface_t *image_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, dim.w, dim.h);
|
||||
cairo_surface_t *image_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, dim.w * scale, dim.h * scale);
|
||||
|
||||
bool first = true;
|
||||
for (GSList *iter = layouts; iter; iter = iter->next) {
|
||||
@ -631,7 +819,7 @@ void draw(void)
|
||||
}
|
||||
|
||||
calc_window_pos(dim.w, dim.h, &dim.x, &dim.y);
|
||||
x_display_surface(image_surface, win, &dim);
|
||||
output->display_surface(image_surface, win, &dim);
|
||||
|
||||
cairo_surface_destroy(image_surface);
|
||||
g_slist_free_full(layouts, free_colored_layout);
|
||||
@ -639,7 +827,17 @@ void draw(void)
|
||||
|
||||
void draw_deinit(void)
|
||||
{
|
||||
x_win_destroy(win);
|
||||
x_free();
|
||||
output->win_destroy(win);
|
||||
output->deinit();
|
||||
}
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
int draw_get_scale(void)
|
||||
{
|
||||
if (output) {
|
||||
return output->get_scale();
|
||||
} else {
|
||||
LOG_W("Called draw_get_scale before output init");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
16
src/draw.h
@ -1,14 +1,24 @@
|
||||
#ifndef DUNST_DRAW_H
|
||||
#define DUNST_DRAW_H
|
||||
|
||||
#include "x11/x.h"
|
||||
extern struct window_x11 *win; // Temporary
|
||||
#include <stdbool.h>
|
||||
#include <cairo.h>
|
||||
#include "output.h"
|
||||
|
||||
extern window win; // Temporary
|
||||
extern const struct output *output;
|
||||
|
||||
void draw_setup(void);
|
||||
|
||||
void draw(void);
|
||||
|
||||
void draw_rounded_rect(cairo_t *c, int x, int y, int width, int height, int corner_radius, int scale, bool first, bool last);
|
||||
|
||||
// TODO get rid of this function by passing scale to everything that needs it.
|
||||
int draw_get_scale(void);
|
||||
|
||||
void draw_deinit(void);
|
||||
|
||||
|
||||
#endif
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
27
src/dunst.c
@ -9,7 +9,6 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
#include "dbus.h"
|
||||
#include "draw.h"
|
||||
@ -20,12 +19,12 @@
|
||||
#include "queues.h"
|
||||
#include "settings.h"
|
||||
#include "utils.h"
|
||||
#include "x11/screen.h"
|
||||
#include "x11/x.h"
|
||||
#include "output.h"
|
||||
|
||||
GMainLoop *mainloop = NULL;
|
||||
|
||||
static struct dunst_status status;
|
||||
static bool setup_done = false;
|
||||
|
||||
/* see dunst.h */
|
||||
void dunst_status(const enum dunst_status_field field,
|
||||
@ -58,6 +57,15 @@ static gboolean run(void *data);
|
||||
|
||||
void wake_up(void)
|
||||
{
|
||||
// If wake_up is being called before the output has been setup we should
|
||||
// return.
|
||||
if (!setup_done)
|
||||
{
|
||||
LOG_D("Ignoring wake up");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_D("Waking up");
|
||||
run(NULL);
|
||||
}
|
||||
|
||||
@ -67,8 +75,8 @@ static gboolean run(void *data)
|
||||
|
||||
LOG_D("RUN");
|
||||
|
||||
dunst_status(S_FULLSCREEN, have_fullscreen_window());
|
||||
dunst_status(S_IDLE, x_is_idle());
|
||||
dunst_status(S_FULLSCREEN, output->have_fullscreen_window());
|
||||
dunst_status(S_IDLE, output->is_idle());
|
||||
|
||||
queues_update(status);
|
||||
|
||||
@ -77,9 +85,9 @@ static gboolean run(void *data)
|
||||
if (active) {
|
||||
// Call draw before showing the window to avoid flickering
|
||||
draw();
|
||||
x_win_show(win);
|
||||
output->win_show(win);
|
||||
} else {
|
||||
x_win_hide(win);
|
||||
output->win_hide(win);
|
||||
}
|
||||
|
||||
if (active) {
|
||||
@ -87,6 +95,8 @@ static gboolean run(void *data)
|
||||
gint64 sleep = queues_get_next_datachange(now);
|
||||
gint64 timeout_at = now + sleep;
|
||||
|
||||
LOG_D("Sleeping for %li ms", sleep/1000);
|
||||
|
||||
if (sleep >= 0) {
|
||||
if (next_timeout < now || timeout_at < next_timeout) {
|
||||
g_timeout_add(sleep/1000, run, NULL);
|
||||
@ -196,6 +206,7 @@ int dunst_main(int argc, char *argv[])
|
||||
// we do not call wakeup now, wake_up does not work here yet
|
||||
}
|
||||
|
||||
setup_done = true;
|
||||
run(NULL);
|
||||
g_main_loop_run(mainloop);
|
||||
g_clear_pointer(&mainloop, g_main_loop_unref);
|
||||
@ -229,4 +240,4 @@ void print_version(void)
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
@ -41,4 +41,4 @@ void usage(int exit_status);
|
||||
void print_version(void);
|
||||
|
||||
#endif
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
138
src/icon.c
@ -86,6 +86,14 @@ static void pixbuf_data_to_cairo_data(
|
||||
}
|
||||
}
|
||||
|
||||
int get_icon_width(cairo_surface_t *icon, int scale) {
|
||||
return cairo_image_surface_get_width(icon) / scale;
|
||||
}
|
||||
|
||||
int get_icon_height(cairo_surface_t *icon, int scale) {
|
||||
return cairo_image_surface_get_height(icon) / scale;
|
||||
}
|
||||
|
||||
cairo_surface_t *gdk_pixbuf_to_cairo_surface(GdkPixbuf *pixbuf)
|
||||
{
|
||||
assert(pixbuf);
|
||||
@ -110,25 +118,60 @@ cairo_surface_t *gdk_pixbuf_to_cairo_surface(GdkPixbuf *pixbuf)
|
||||
return icon_surface;
|
||||
}
|
||||
|
||||
GdkPixbuf *icon_pixbuf_scale(GdkPixbuf *pixbuf)
|
||||
/**
|
||||
* Scales the given image dimensions if necessary according to the settings.
|
||||
*
|
||||
* @param w a pointer to the image width, to be modified in-place
|
||||
* @param h a pointer to the image height, to be modified in-place
|
||||
* @return TRUE if the dimensions were updated, FALSE if they were left unchanged
|
||||
*/
|
||||
static bool icon_size_clamp(int *w, int *h) {
|
||||
int _w = *w, _h = *h;
|
||||
int landscape = _w > _h;
|
||||
int orig_larger = landscape ? _w : _h;
|
||||
double larger = orig_larger;
|
||||
double smaller = landscape ? _h : _w;
|
||||
if (settings.min_icon_size && smaller < settings.min_icon_size) {
|
||||
larger = larger / smaller * settings.min_icon_size;
|
||||
smaller = settings.min_icon_size;
|
||||
}
|
||||
if (settings.max_icon_size && larger > settings.max_icon_size) {
|
||||
smaller = smaller / larger * settings.max_icon_size;
|
||||
larger = settings.max_icon_size;
|
||||
}
|
||||
if ((int) larger != orig_larger) {
|
||||
*w = (int) (landscape ? larger : smaller);
|
||||
*h = (int) (landscape ? smaller : larger);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scales the given GdkPixbuf if necessary according to the settings.
|
||||
*
|
||||
* @param pixbuf (nullable) The pixbuf, which may be too big.
|
||||
* Takes ownership of the reference.
|
||||
* @param dpi_scale An integer for the dpi scaling. That doesn't mean the icon
|
||||
* is always scaled by dpi_scale.
|
||||
* @return the scaled version of the pixbuf. If scaling wasn't
|
||||
* necessary, it returns the same pixbuf. Transfers full
|
||||
* ownership of the reference.
|
||||
*/
|
||||
static GdkPixbuf *icon_pixbuf_scale(GdkPixbuf *pixbuf, int dpi_scale)
|
||||
{
|
||||
ASSERT_OR_RET(pixbuf, NULL);
|
||||
|
||||
int w = gdk_pixbuf_get_width(pixbuf);
|
||||
int h = gdk_pixbuf_get_height(pixbuf);
|
||||
int larger = w > h ? w : h;
|
||||
if (settings.max_icon_size && larger > settings.max_icon_size) {
|
||||
int scaled_w = settings.max_icon_size;
|
||||
int scaled_h = settings.max_icon_size;
|
||||
if (w >= h)
|
||||
scaled_h = (settings.max_icon_size * h) / w;
|
||||
else
|
||||
scaled_w = (settings.max_icon_size * w) / h;
|
||||
|
||||
|
||||
// TODO immediately rescale icon upon scale changes
|
||||
if (icon_size_clamp(&w, &h)) {
|
||||
GdkPixbuf *scaled = gdk_pixbuf_scale_simple(
|
||||
pixbuf,
|
||||
scaled_w,
|
||||
scaled_h,
|
||||
w * dpi_scale,
|
||||
h * dpi_scale,
|
||||
GDK_INTERP_BILINEAR);
|
||||
g_object_unref(pixbuf);
|
||||
pixbuf = scaled;
|
||||
@ -137,12 +180,24 @@ GdkPixbuf *icon_pixbuf_scale(GdkPixbuf *pixbuf)
|
||||
return pixbuf;
|
||||
}
|
||||
|
||||
GdkPixbuf *get_pixbuf_from_file(const char *filename)
|
||||
GdkPixbuf *get_pixbuf_from_file(const char *filename, int scale)
|
||||
{
|
||||
char *path = string_to_path(g_strdup(filename));
|
||||
GError *error = NULL;
|
||||
gint w, h;
|
||||
|
||||
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &error);
|
||||
if (!gdk_pixbuf_get_file_info (path, &w, &h)) {
|
||||
LOG_W("Failed to load image info for %s", filename);
|
||||
g_free(path);
|
||||
return NULL;
|
||||
}
|
||||
// TODO immediately rescale icon upon scale changes
|
||||
icon_size_clamp(&w, &h);
|
||||
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_scale(path,
|
||||
w * scale,
|
||||
h * scale,
|
||||
TRUE,
|
||||
&error);
|
||||
|
||||
if (error) {
|
||||
LOG_W("%s", error->message);
|
||||
@ -153,14 +208,14 @@ GdkPixbuf *get_pixbuf_from_file(const char *filename)
|
||||
return pixbuf;
|
||||
}
|
||||
|
||||
GdkPixbuf *get_pixbuf_from_icon(const char *iconname)
|
||||
char *get_path_from_icon_name(const char *iconname)
|
||||
{
|
||||
if (STR_EMPTY(iconname))
|
||||
return NULL;
|
||||
|
||||
const char *suffixes[] = { ".svg", ".png", ".xpm", NULL };
|
||||
GdkPixbuf *pixbuf = NULL;
|
||||
const char *suffixes[] = { ".svg", ".svgz", ".png", ".xpm", NULL };
|
||||
gchar *uri_path = NULL;
|
||||
char *new_name = NULL;
|
||||
|
||||
if (g_str_has_prefix(iconname, "file://")) {
|
||||
uri_path = g_filename_from_uri(iconname, NULL, NULL);
|
||||
@ -170,7 +225,7 @@ GdkPixbuf *get_pixbuf_from_icon(const char *iconname)
|
||||
|
||||
/* absolute path? */
|
||||
if (iconname[0] == '/' || iconname[0] == '~') {
|
||||
pixbuf = get_pixbuf_from_file(iconname);
|
||||
new_name = g_strdup(iconname);
|
||||
} else {
|
||||
/* search in icon_path */
|
||||
char *start = settings.icon_path,
|
||||
@ -182,41 +237,62 @@ GdkPixbuf *get_pixbuf_from_icon(const char *iconname)
|
||||
current_folder = g_strndup(start, end - start);
|
||||
|
||||
for (const char **suf = suffixes; *suf; suf++) {
|
||||
maybe_icon_path = g_strconcat(current_folder, "/", iconname, *suf, NULL);
|
||||
if (is_readable_file(maybe_icon_path))
|
||||
pixbuf = get_pixbuf_from_file(maybe_icon_path);
|
||||
gchar *name_with_extension = g_strconcat(iconname, *suf, NULL);
|
||||
maybe_icon_path = g_build_filename(current_folder, name_with_extension, NULL);
|
||||
if (is_readable_file(maybe_icon_path)) {
|
||||
new_name = g_strdup(maybe_icon_path);
|
||||
}
|
||||
g_free(name_with_extension);
|
||||
g_free(maybe_icon_path);
|
||||
|
||||
if (pixbuf)
|
||||
if (new_name)
|
||||
break;
|
||||
}
|
||||
|
||||
g_free(current_folder);
|
||||
if (pixbuf)
|
||||
if (new_name)
|
||||
break;
|
||||
|
||||
start = end + 1;
|
||||
} while (STR_FULL(end));
|
||||
if (!pixbuf)
|
||||
if (!new_name)
|
||||
LOG_W("No icon found in path: '%s'", iconname);
|
||||
}
|
||||
|
||||
g_free(uri_path);
|
||||
return new_name;
|
||||
}
|
||||
|
||||
GdkPixbuf *get_pixbuf_from_icon(const char *iconname, int scale)
|
||||
{
|
||||
char *path = get_path_from_icon_name(iconname);
|
||||
if (!path) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GdkPixbuf *pixbuf = NULL;
|
||||
|
||||
pixbuf = get_pixbuf_from_file(path, scale);
|
||||
g_free(path);
|
||||
|
||||
if (!pixbuf)
|
||||
LOG_W("No icon found in path: '%s'", iconname);
|
||||
|
||||
return pixbuf;
|
||||
}
|
||||
|
||||
GdkPixbuf *icon_get_for_name(const char *name, char **id)
|
||||
GdkPixbuf *icon_get_for_name(const char *name, char **id, int scale)
|
||||
{
|
||||
ASSERT_OR_RET(name, NULL);
|
||||
ASSERT_OR_RET(id, NULL);
|
||||
|
||||
GdkPixbuf *pb = get_pixbuf_from_icon(name);
|
||||
GdkPixbuf *pb = get_pixbuf_from_icon(name, scale);
|
||||
if (pb)
|
||||
*id = g_strdup(name);
|
||||
return pb;
|
||||
}
|
||||
|
||||
GdkPixbuf *icon_get_for_data(GVariant *data, char **id)
|
||||
GdkPixbuf *icon_get_for_data(GVariant *data, char **id, int dpi_scale)
|
||||
{
|
||||
ASSERT_OR_RET(data, NULL);
|
||||
ASSERT_OR_RET(id, NULL);
|
||||
@ -284,7 +360,13 @@ GdkPixbuf *icon_get_for_data(GVariant *data, char **id)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// g_memdup is deprecated in glib 2.67.4 and higher.
|
||||
// g_memdup2 is a safer alternative
|
||||
#if GLIB_CHECK_VERSION(2,67,3)
|
||||
data_pb = (guchar *) g_memdup2(g_variant_get_data(data_variant), len_actual);
|
||||
#else
|
||||
data_pb = (guchar *) g_memdup(g_variant_get_data(data_variant), len_actual);
|
||||
#endif
|
||||
|
||||
pixbuf = gdk_pixbuf_new_from_data(data_pb,
|
||||
GDK_COLORSPACE_RGB,
|
||||
@ -320,7 +402,9 @@ GdkPixbuf *icon_get_for_data(GVariant *data, char **id)
|
||||
g_free(data_chk);
|
||||
g_variant_unref(data_variant);
|
||||
|
||||
pixbuf = icon_pixbuf_scale(pixbuf, dpi_scale);
|
||||
|
||||
return pixbuf;
|
||||
}
|
||||
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
58
src/icon.h
@ -8,38 +8,54 @@
|
||||
|
||||
cairo_surface_t *gdk_pixbuf_to_cairo_surface(GdkPixbuf *pixbuf);
|
||||
|
||||
/**
|
||||
* Scales the given GdkPixbuf if necessary according to the settings.
|
||||
*
|
||||
* @param pixbuf (nullable) The pixbuf, which may be too big.
|
||||
* Takes ownership of the reference.
|
||||
* @return the scaled version of the pixbuf. If scaling wasn't
|
||||
* necessary, it returns the same pixbuf. Transfers full
|
||||
* ownership of the reference.
|
||||
*/
|
||||
GdkPixbuf *icon_pixbuf_scale(GdkPixbuf *pixbuf);
|
||||
|
||||
/** Retrieve an icon by its full filepath.
|
||||
/** Retrieve an icon by its full filepath, scaled according to settings.
|
||||
*
|
||||
* @param filename A string representing a readable file path
|
||||
* @param scale An integer representing the output dpi scaling.
|
||||
*
|
||||
* @return an instance of `GdkPixbuf`
|
||||
* @retval NULL: file does not exist, not readable, etc..
|
||||
*/
|
||||
GdkPixbuf *get_pixbuf_from_file(const char *filename);
|
||||
GdkPixbuf *get_pixbuf_from_file(const char *filename, int scale);
|
||||
|
||||
/** Retrieve an icon by its name sent via the notification bus
|
||||
|
||||
/**
|
||||
* Get the unscaled icon width.
|
||||
*
|
||||
* If scale is 2 for example, the icon will render in twice the size, but
|
||||
* get_icon_width still returns the same size as when scale is 1.
|
||||
*/
|
||||
int get_icon_width(cairo_surface_t *icon, int scale);
|
||||
|
||||
/**
|
||||
* Get the unscaled icon height, see get_icon_width.
|
||||
*/
|
||||
int get_icon_height(cairo_surface_t *icon, int scale);
|
||||
|
||||
/** Retrieve a path from an icon name.
|
||||
*
|
||||
* @param iconname A string describing a `file://` URL, an arbitary filename
|
||||
* or an icon name, which then gets searched for in the
|
||||
* settings.icon_path
|
||||
*
|
||||
* @return a newly allocated string with the icon path
|
||||
* @retval NULL: file does not exist, not readable, etc..
|
||||
*/
|
||||
char *get_path_from_icon_name(const char *iconname);
|
||||
|
||||
/** Retrieve an icon by its name sent via the notification bus, scaled according to settings
|
||||
*
|
||||
* @param iconname A string describing a `file://` URL, an arbitary filename
|
||||
* or an icon name, which then gets searched for in the
|
||||
* settings.icon_path
|
||||
* @param scale An integer representing the output dpi scaling.
|
||||
*
|
||||
* @return an instance of `GdkPixbuf`
|
||||
* @retval NULL: file does not exist, not readable, etc..
|
||||
*/
|
||||
GdkPixbuf *get_pixbuf_from_icon(const char *iconname);
|
||||
GdkPixbuf *get_pixbuf_from_icon(const char *iconname, int scale);
|
||||
|
||||
/** Read an icon from disk and convert it to a GdkPixbuf.
|
||||
/** Read an icon from disk and convert it to a GdkPixbuf, scaled according to settings
|
||||
*
|
||||
* The returned id will be a unique identifier. To check if two given
|
||||
* GdkPixbufs are equal, it's sufficient to just compare the id strings.
|
||||
@ -49,12 +65,13 @@ GdkPixbuf *get_pixbuf_from_icon(const char *iconname);
|
||||
* get searched in the folders of the icon_path setting.
|
||||
* @param id (necessary) A unique identifier of the returned pixbuf. Only filled,
|
||||
* if the return value is non-NULL.
|
||||
* @param dpi_scale An integer representing the output dpi scaling.
|
||||
* @return an instance of `GdkPixbuf`, representing the name's image
|
||||
* @retval NULL: Invalid path given
|
||||
*/
|
||||
GdkPixbuf *icon_get_for_name(const char *name, char **id);
|
||||
GdkPixbuf *icon_get_for_name(const char *name, char **id, int dpi_scale);
|
||||
|
||||
/** Convert a GVariant like described in GdkPixbuf
|
||||
/** Convert a GVariant like described in GdkPixbuf, scaled according to settings
|
||||
*
|
||||
* The returned id will be a unique identifier. To check if two given
|
||||
* GdkPixbufs are equal, it's sufficient to just compare the id strings.
|
||||
@ -63,10 +80,11 @@ GdkPixbuf *icon_get_for_name(const char *name, char **id);
|
||||
* like described in the notification spec.
|
||||
* @param id (necessary) A unique identifier of the returned pixbuf.
|
||||
* Only filled, if the return value is non-NULL.
|
||||
* @param scale An integer representing the output dpi scaling.
|
||||
* @return an instance of `GdkPixbuf` derived from the GVariant
|
||||
* @retval NULL: GVariant parameter nulled, invalid or in wrong format
|
||||
*/
|
||||
GdkPixbuf *icon_get_for_data(GVariant *data, char **id);
|
||||
GdkPixbuf *icon_get_for_data(GVariant *data, char **id, int scale);
|
||||
|
||||
#endif
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
81
src/input.c
Normal file
@ -0,0 +1,81 @@
|
||||
#include "input.h"
|
||||
#include "log.h"
|
||||
#include "menu.h"
|
||||
#include "settings.h"
|
||||
#include "queues.h"
|
||||
#include <stddef.h>
|
||||
#include <linux/input-event-codes.h>
|
||||
|
||||
void input_handle_click(unsigned int button, bool button_down, int mouse_x, int mouse_y){
|
||||
LOG_I("Pointer handle button %i: %i", button, button_down);
|
||||
|
||||
if (button_down) {
|
||||
// make sure it only reacts on button release
|
||||
return;
|
||||
}
|
||||
|
||||
enum mouse_action *acts;
|
||||
|
||||
switch (button) {
|
||||
case BTN_LEFT:
|
||||
acts = settings.mouse_left_click;
|
||||
break;
|
||||
case BTN_MIDDLE:
|
||||
acts = settings.mouse_middle_click;
|
||||
break;
|
||||
case BTN_RIGHT:
|
||||
acts = settings.mouse_right_click;
|
||||
break;
|
||||
case BTN_TOUCH:
|
||||
// TODO Add separate action for touch
|
||||
acts = settings.mouse_left_click;
|
||||
break;
|
||||
default:
|
||||
LOG_W("Unsupported mouse button: '%d'", button);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; acts[i]; i++) {
|
||||
enum mouse_action act = acts[i];
|
||||
if (act == MOUSE_CLOSE_ALL) {
|
||||
queues_history_push_all();
|
||||
break;
|
||||
}
|
||||
|
||||
if (act == MOUSE_CONTEXT_ALL) {
|
||||
context_menu();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (act == MOUSE_DO_ACTION || act == MOUSE_CLOSE_CURRENT || act == MOUSE_CONTEXT || act == MOUSE_OPEN_URL) {
|
||||
int y = settings.separator_height;
|
||||
struct notification *n = NULL;
|
||||
int first = true;
|
||||
for (const GList *iter = queues_get_displayed(); iter;
|
||||
iter = iter->next) {
|
||||
n = iter->data;
|
||||
if (mouse_y > y && mouse_y < y + n->displayed_height)
|
||||
break;
|
||||
|
||||
y += n->displayed_height + settings.separator_height;
|
||||
if (first)
|
||||
y += settings.frame_width;
|
||||
}
|
||||
|
||||
if (n) {
|
||||
if (act == MOUSE_CLOSE_CURRENT) {
|
||||
n->marked_for_closure = REASON_USER;
|
||||
} else if (act == MOUSE_DO_ACTION) {
|
||||
notification_do_action(n);
|
||||
} else if (act == MOUSE_OPEN_URL) {
|
||||
notification_open_url(n);
|
||||
} else {
|
||||
notification_open_context_menu(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wake_up();
|
||||
}
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
18
src/input.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef DUNST_INPUT_H
|
||||
#define DUNST_INPUT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* Handle incoming mouse click events
|
||||
*
|
||||
* @param button code, A linux input event code
|
||||
* @param button_down State of the button
|
||||
* @param mouse_x X-position of the mouse, relative to the window
|
||||
* @param mouse_y Y-position of the mouse, relative to the window
|
||||
*
|
||||
*/
|
||||
void input_handle_click(unsigned int button, bool button_down, int mouse_x, int mouse_y);
|
||||
|
||||
#endif
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
12
src/log.c
@ -74,19 +74,21 @@ static void dunst_log_handler(
|
||||
gpointer testing)
|
||||
{
|
||||
if (testing)
|
||||
return;
|
||||
log_level = G_LOG_LEVEL_ERROR;
|
||||
|
||||
GLogLevelFlags message_level_masked = message_level & G_LOG_LEVEL_MASK;
|
||||
|
||||
/* if you want to have a debug build, you want to log anything,
|
||||
* unconditionally, without specifying debug log level again */
|
||||
#ifndef DEBUG_BUILD
|
||||
if (log_level < message_level)
|
||||
if (log_level < message_level_masked)
|
||||
return;
|
||||
#endif
|
||||
const char *log_level_str =
|
||||
log_level_to_string(message_level & G_LOG_LEVEL_MASK);
|
||||
log_level_to_string(message_level_masked);
|
||||
|
||||
/* Use stderr for warnings and higher */
|
||||
if (message_level <= G_LOG_LEVEL_WARNING)
|
||||
if (message_level_masked <= G_LOG_LEVEL_WARNING)
|
||||
g_printerr("%s: %s\n", log_level_str, message);
|
||||
else
|
||||
g_print("%s: %s\n", log_level_str, message);
|
||||
@ -98,4 +100,4 @@ void dunst_log_init(bool testing)
|
||||
g_log_set_default_handler(dunst_log_handler, (void*)testing);
|
||||
}
|
||||
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
@ -45,4 +45,4 @@ void log_set_level_from_string(const char* level);
|
||||
void dunst_log_init(bool testing);
|
||||
|
||||
#endif
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
@ -338,4 +338,4 @@ char *markup_transform(char *str, enum markup_mode markup_mode)
|
||||
return str;
|
||||
}
|
||||
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
@ -48,4 +48,4 @@ void markup_strip_img(char **str, char **urls);
|
||||
char *markup_transform(char *str, enum markup_mode markup_mode);
|
||||
|
||||
#endif
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
109
src/menu.c
@ -23,12 +23,12 @@
|
||||
static bool is_initialized = false;
|
||||
static regex_t url_regex;
|
||||
|
||||
struct notification_lock {
|
||||
struct notification *n;
|
||||
gint64 timeout;
|
||||
};
|
||||
static gpointer context_menu_thread(gpointer data);
|
||||
|
||||
struct {
|
||||
GList *locked_notifications;
|
||||
} menu_ctx;
|
||||
|
||||
/**
|
||||
* Initializes regexes needed for matching.
|
||||
*
|
||||
@ -290,9 +290,43 @@ char *invoke_dmenu(const char *dmenu_input)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock and get all notifications with an action or URL.
|
||||
**/
|
||||
static GList *get_actionable_notifications(void)
|
||||
{
|
||||
GList *locked_notifications = NULL;
|
||||
|
||||
for (const GList *iter = queues_get_displayed(); iter;
|
||||
iter = iter->next) {
|
||||
struct notification *n = iter->data;
|
||||
|
||||
if (n->urls || g_hash_table_size(n->actions)) {
|
||||
notification_lock(n);
|
||||
locked_notifications = g_list_prepend(locked_notifications, n);
|
||||
}
|
||||
}
|
||||
|
||||
return locked_notifications;
|
||||
}
|
||||
|
||||
/* see menu.h */
|
||||
void context_menu(void)
|
||||
{
|
||||
GList *notifications = get_actionable_notifications();
|
||||
context_menu_for(notifications);
|
||||
}
|
||||
|
||||
/* see menu.h */
|
||||
void context_menu_for(GList *notifications)
|
||||
{
|
||||
if (menu_ctx.locked_notifications) {
|
||||
LOG_W("Context menu already running, refusing to rerun");
|
||||
return;
|
||||
}
|
||||
|
||||
menu_ctx.locked_notifications = notifications;
|
||||
|
||||
GError *err = NULL;
|
||||
g_thread_unref(g_thread_try_new("dmenu",
|
||||
context_menu_thread,
|
||||
@ -305,32 +339,41 @@ void context_menu(void)
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean context_menu_result_dispatch(gpointer user_data)
|
||||
{
|
||||
char *dmenu_output = (char*)user_data;
|
||||
|
||||
dispatch_menu_result(dmenu_output);
|
||||
|
||||
for (GList *iter = menu_ctx.locked_notifications; iter; iter = iter->next) {
|
||||
struct notification *n = iter->data;
|
||||
notification_unlock(n);
|
||||
if (n->marked_for_closure) {
|
||||
// Don't close notification if context was aborted
|
||||
if (dmenu_output != NULL)
|
||||
queues_notification_close(n, n->marked_for_closure);
|
||||
n->marked_for_closure = 0;
|
||||
}
|
||||
}
|
||||
|
||||
menu_ctx.locked_notifications = NULL;
|
||||
|
||||
g_list_free(menu_ctx.locked_notifications);
|
||||
g_free(dmenu_output);
|
||||
|
||||
wake_up();
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static gpointer context_menu_thread(gpointer data)
|
||||
{
|
||||
char *dmenu_input = NULL;
|
||||
char *dmenu_output;
|
||||
|
||||
GList *locked_notifications = NULL;
|
||||
|
||||
for (const GList *iter = queues_get_displayed(); iter;
|
||||
iter = iter->next) {
|
||||
for (GList *iter = menu_ctx.locked_notifications; iter; iter = iter->next) {
|
||||
struct notification *n = iter->data;
|
||||
|
||||
|
||||
// Reference and lock the notification if we need it
|
||||
if (n->urls || g_hash_table_size(n->actions)) {
|
||||
notification_ref(n);
|
||||
|
||||
struct notification_lock *nl =
|
||||
g_malloc(sizeof(struct notification_lock));
|
||||
|
||||
nl->n = n;
|
||||
nl->timeout = n->timeout;
|
||||
n->timeout = 0;
|
||||
|
||||
locked_notifications = g_list_prepend(locked_notifications, nl);
|
||||
}
|
||||
|
||||
char *dmenu_str = notification_dmenu_string(n);
|
||||
dmenu_input = string_append(dmenu_input, dmenu_str, "\n");
|
||||
g_free(dmenu_str);
|
||||
@ -340,26 +383,10 @@ static gpointer context_menu_thread(gpointer data)
|
||||
}
|
||||
|
||||
dmenu_output = invoke_dmenu(dmenu_input);
|
||||
dispatch_menu_result(dmenu_output);
|
||||
g_timeout_add(50, context_menu_result_dispatch, dmenu_output);
|
||||
|
||||
g_free(dmenu_input);
|
||||
g_free(dmenu_output);
|
||||
|
||||
// unref all notifications
|
||||
for (GList *iter = locked_notifications;
|
||||
iter;
|
||||
iter = iter->next) {
|
||||
|
||||
struct notification_lock *nl = iter->data;
|
||||
struct notification *n = nl->n;
|
||||
|
||||
n->timeout = nl->timeout;
|
||||
|
||||
g_free(nl);
|
||||
notification_unref(n);
|
||||
}
|
||||
g_list_free(locked_notifications);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
12
src/menu.h
@ -2,6 +2,8 @@
|
||||
#ifndef DUNST_MENU_H
|
||||
#define DUNST_MENU_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
/**
|
||||
* Extract all urls from the given string.
|
||||
*
|
||||
@ -16,9 +18,15 @@ void invoke_action(const char *action);
|
||||
void regex_teardown(void);
|
||||
|
||||
/**
|
||||
* Open the context menu that lets the user select urls/actions/etc.
|
||||
* Open the context menu that lets the user select urls/actions/etc for all displayed notifications.
|
||||
*/
|
||||
void context_menu(void);
|
||||
|
||||
/**
|
||||
* Open the context menu that lets the user select urls/actions/etc for the specified notifications.
|
||||
* @param notifications (nullable) List of notifications for which the context menu should be opened
|
||||
*/
|
||||
void context_menu_for(GList *notifications);
|
||||
|
||||
#endif
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
#include "rules.h"
|
||||
#include "settings.h"
|
||||
#include "utils.h"
|
||||
#include "draw.h"
|
||||
|
||||
static void notification_extract_urls(struct notification *n);
|
||||
static void notification_format_message(struct notification *n);
|
||||
@ -64,6 +65,7 @@ void notification_print(const struct notification *n)
|
||||
printf("\tformatted: '%s'\n", n->msg);
|
||||
printf("\tfg: %s\n", n->colors.fg);
|
||||
printf("\tbg: %s\n", n->colors.bg);
|
||||
printf("\thighlight: %s\n", n->colors.highlight);
|
||||
printf("\tframe: %s\n", n->colors.frame);
|
||||
printf("\tfullscreen: %s\n", enum_to_string_fullscreen(n->fullscreen));
|
||||
printf("\tprogress: %d\n", n->progress);
|
||||
@ -88,16 +90,21 @@ void notification_print(const struct notification *n)
|
||||
printf("\t\t\"%s\": \"%s\"\n", (char*)p_key, (char*)p_value);
|
||||
printf("\t}\n");
|
||||
}
|
||||
printf("\tscript: %s\n", n->script);
|
||||
printf("\tscript_count: %d\n", n->script_count);
|
||||
if (n->script_count > 0) {
|
||||
printf("\tscripts: ");
|
||||
for (int i = 0; i < n->script_count; i++) {
|
||||
printf("'%s' ",n->scripts[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
printf("}\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
/* see notification.h */
|
||||
void notification_run_script(struct notification *n)
|
||||
{
|
||||
if (STR_EMPTY(n->script))
|
||||
return;
|
||||
|
||||
if (n->script_run && !settings.always_run_script)
|
||||
return;
|
||||
|
||||
@ -110,26 +117,54 @@ void notification_run_script(struct notification *n)
|
||||
|
||||
const char *urgency = notification_urgency_to_string(n->urgency);
|
||||
|
||||
int pid1 = fork();
|
||||
for(int i = 0; i < n->script_count; i++) {
|
||||
|
||||
if (pid1) {
|
||||
int status;
|
||||
waitpid(pid1, &status, 0);
|
||||
} else {
|
||||
int pid2 = fork();
|
||||
if (pid2) {
|
||||
exit(0);
|
||||
const char *script = n->scripts[i];
|
||||
|
||||
if (STR_EMPTY(script))
|
||||
continue;
|
||||
|
||||
int pid1 = fork();
|
||||
|
||||
if (pid1) {
|
||||
int status;
|
||||
waitpid(pid1, &status, 0);
|
||||
} else {
|
||||
int ret = execlp(n->script,
|
||||
n->script,
|
||||
appname,
|
||||
summary,
|
||||
body,
|
||||
icon,
|
||||
urgency,
|
||||
(char *)NULL);
|
||||
if (ret != 0) {
|
||||
LOG_W("Unable to run script: %s", strerror(errno));
|
||||
// second fork to prevent zombie processes
|
||||
int pid2 = fork();
|
||||
if (pid2) {
|
||||
exit(0);
|
||||
} else {
|
||||
// Set environment variables
|
||||
gchar *n_id_str = g_strdup_printf("%i", n->id);
|
||||
gchar *n_progress_str = g_strdup_printf("%i", n->progress);
|
||||
gchar *n_timeout_str = g_strdup_printf("%li", n->timeout/1000);
|
||||
gchar *n_timestamp_str = g_strdup_printf("%li", n->timestamp / 1000);
|
||||
char* icon_path = get_path_from_icon_name(icon);
|
||||
safe_setenv("DUNST_APP_NAME", appname);
|
||||
safe_setenv("DUNST_SUMMARY", summary);
|
||||
safe_setenv("DUNST_BODY", body);
|
||||
safe_setenv("DUNST_ICON_PATH", icon_path);
|
||||
safe_setenv("DUNST_URGENCY", urgency);
|
||||
safe_setenv("DUNST_ID", n_id_str);
|
||||
safe_setenv("DUNST_PROGRESS", n_progress_str);
|
||||
safe_setenv("DUNST_CATEGORY", n->category);
|
||||
safe_setenv("DUNST_STACK_TAG", n->stack_tag);
|
||||
safe_setenv("DUNST_URLS", n->urls);
|
||||
safe_setenv("DUNST_TIMEOUT", n_timeout_str);
|
||||
safe_setenv("DUNST_TIMESTAMP", n_timestamp_str);
|
||||
safe_setenv("DUNST_STACK_TAG", n->stack_tag);
|
||||
|
||||
execlp(script,
|
||||
script,
|
||||
appname,
|
||||
summary,
|
||||
body,
|
||||
icon,
|
||||
urgency,
|
||||
(char *)NULL);
|
||||
|
||||
LOG_W("Unable to run script %s: %s", n->scripts[i], strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
@ -158,7 +193,7 @@ const char *notification_urgency_to_string(const enum urgency urgency)
|
||||
/* see notification.h */
|
||||
int notification_cmp(const struct notification *a, const struct notification *b)
|
||||
{
|
||||
if (a->urgency != b->urgency) {
|
||||
if (settings.sort && a->urgency != b->urgency) {
|
||||
return b->urgency - a->urgency;
|
||||
} else {
|
||||
return a->id - b->id;
|
||||
@ -171,8 +206,6 @@ int notification_cmp_data(const void *va, const void *vb, void *data)
|
||||
struct notification *a = (struct notification *) va;
|
||||
struct notification *b = (struct notification *) vb;
|
||||
|
||||
ASSERT_OR_RET(settings.sort, 1);
|
||||
|
||||
return notification_cmp(a, b);
|
||||
}
|
||||
|
||||
@ -185,6 +218,30 @@ bool notification_is_duplicate(const struct notification *a, const struct notifi
|
||||
&& a->urgency == b->urgency;
|
||||
}
|
||||
|
||||
bool notification_is_locked(struct notification *n) {
|
||||
assert(n);
|
||||
|
||||
return g_atomic_int_get(&n->locked) != 0;
|
||||
}
|
||||
|
||||
struct notification* notification_lock(struct notification *n) {
|
||||
assert(n);
|
||||
|
||||
g_atomic_int_set(&n->locked, 1);
|
||||
notification_ref(n);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
struct notification* notification_unlock(struct notification *n) {
|
||||
assert(n);
|
||||
|
||||
g_atomic_int_set(&n->locked, 0);
|
||||
notification_unref(n);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static void notification_private_free(NotificationPrivate *p)
|
||||
{
|
||||
g_free(p);
|
||||
@ -224,11 +281,13 @@ void notification_unref(struct notification *n)
|
||||
g_free(n->urls);
|
||||
g_free(n->colors.fg);
|
||||
g_free(n->colors.bg);
|
||||
g_free(n->colors.highlight);
|
||||
g_free(n->colors.frame);
|
||||
g_free(n->stack_tag);
|
||||
g_free(n->desktop_entry);
|
||||
|
||||
g_hash_table_unref(n->actions);
|
||||
g_free(n->default_action_name);
|
||||
|
||||
if (n->icon)
|
||||
g_object_unref(n->icon);
|
||||
@ -236,6 +295,10 @@ void notification_unref(struct notification *n)
|
||||
|
||||
notification_private_free(n->priv);
|
||||
|
||||
if (n->script_count > 0){
|
||||
g_free(n->scripts);
|
||||
}
|
||||
|
||||
g_free(n);
|
||||
}
|
||||
|
||||
@ -251,8 +314,7 @@ void notification_icon_replace_path(struct notification *n, const char *new_icon
|
||||
g_clear_object(&n->icon);
|
||||
g_clear_pointer(&n->icon_id, g_free);
|
||||
|
||||
n->icon = icon_get_for_name(new_icon, &n->icon_id);
|
||||
n->icon = icon_pixbuf_scale(n->icon);
|
||||
n->icon = icon_get_for_name(new_icon, &n->icon_id, draw_get_scale());
|
||||
}
|
||||
|
||||
void notification_icon_replace_data(struct notification *n, GVariant *new_icon)
|
||||
@ -263,8 +325,7 @@ void notification_icon_replace_data(struct notification *n, GVariant *new_icon)
|
||||
g_clear_object(&n->icon);
|
||||
g_clear_pointer(&n->icon_id, g_free);
|
||||
|
||||
n->icon = icon_get_for_data(new_icon, &n->icon_id);
|
||||
n->icon = icon_pixbuf_scale(n->icon);
|
||||
n->icon = icon_get_for_data(new_icon, &n->icon_id, draw_get_scale());
|
||||
}
|
||||
|
||||
/* see notification.h */
|
||||
@ -325,7 +386,9 @@ struct notification *notification_create(void)
|
||||
n->fullscreen = FS_SHOW;
|
||||
|
||||
n->actions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
|
||||
n->default_action_name = g_strdup("default");
|
||||
|
||||
n->script_count = 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
@ -378,6 +441,8 @@ void notification_init(struct notification *n)
|
||||
n->colors.fg = g_strdup(defcolors.fg);
|
||||
if (!n->colors.bg)
|
||||
n->colors.bg = g_strdup(defcolors.bg);
|
||||
if (!n->colors.highlight)
|
||||
n->colors.highlight = g_strdup(defcolors.highlight);
|
||||
if (!n->colors.frame)
|
||||
n->colors.frame = g_strdup(defcolors.frame);
|
||||
|
||||
@ -388,6 +453,12 @@ void notification_init(struct notification *n)
|
||||
/* Process rules */
|
||||
rule_apply_all(n);
|
||||
|
||||
if (g_str_has_prefix(n->summary, "DUNST_COMMAND_")) {
|
||||
char *msg = "DUNST_COMMAND_* has been removed, please switch to dunstctl. See #830 for more details. https://github.com/dunst-project/dunst/pull/830";
|
||||
LOG_W("%s", msg);
|
||||
n->body = string_append(n->body, msg, "\n");
|
||||
}
|
||||
|
||||
/* UPDATE derived fields */
|
||||
notification_extract_urls(n);
|
||||
notification_format_message(n);
|
||||
@ -577,27 +648,47 @@ void notification_update_text_to_render(struct notification *n)
|
||||
}
|
||||
|
||||
/* see notification.h */
|
||||
void notification_do_action(const struct notification *n)
|
||||
void notification_do_action(struct notification *n)
|
||||
{
|
||||
assert(n->default_action_name);
|
||||
|
||||
if (g_hash_table_size(n->actions)) {
|
||||
if (g_hash_table_contains(n->actions, "default")) {
|
||||
signal_action_invoked(n, "default");
|
||||
if (g_hash_table_contains(n->actions, n->default_action_name)) {
|
||||
signal_action_invoked(n, n->default_action_name);
|
||||
return;
|
||||
}
|
||||
if (g_hash_table_size(n->actions) == 1) {
|
||||
if (strcmp(n->default_action_name, "default") == 0 && g_hash_table_size(n->actions) == 1) {
|
||||
GList *keys = g_hash_table_get_keys(n->actions);
|
||||
signal_action_invoked(n, keys->data);
|
||||
g_list_free(keys);
|
||||
return;
|
||||
}
|
||||
context_menu();
|
||||
notification_open_context_menu(n);
|
||||
|
||||
} else if (n->urls) {
|
||||
if (strstr(n->urls, "\n"))
|
||||
context_menu();
|
||||
else
|
||||
open_browser(n->urls);
|
||||
}
|
||||
}
|
||||
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* see notification.h */
|
||||
void notification_open_url(struct notification *n)
|
||||
{
|
||||
if (strstr(n->urls, "\n"))
|
||||
notification_open_context_menu(n);
|
||||
else
|
||||
open_browser(n->urls);
|
||||
}
|
||||
|
||||
/* see notification.h */
|
||||
void notification_open_context_menu(struct notification *n)
|
||||
{
|
||||
GList *notifications = NULL;
|
||||
notifications = g_list_append(notifications, n);
|
||||
notification_lock(n);
|
||||
|
||||
context_menu_for(notifications);
|
||||
}
|
||||
|
||||
void notification_invalidate_actions(struct notification *n) {
|
||||
g_hash_table_remove_all(n->actions);
|
||||
}
|
||||
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
@ -33,6 +33,7 @@ struct notification_colors {
|
||||
char *frame;
|
||||
char *bg;
|
||||
char *fg;
|
||||
char *highlight;
|
||||
};
|
||||
|
||||
struct notification {
|
||||
@ -54,15 +55,18 @@ struct notification {
|
||||
char *iconname; /**< plain icon information (may be a path or just a name)
|
||||
Use this to compare the icon name with rules.*/
|
||||
|
||||
gint64 start; /**< begin of current display */
|
||||
gint64 timestamp; /**< arrival time */
|
||||
gint64 timeout; /**< time to display */
|
||||
gint64 start; /**< begin of current display (in milliseconds) */
|
||||
gint64 timestamp; /**< arrival time (in milliseconds) */
|
||||
gint64 timeout; /**< time to display (in milliseconds) */
|
||||
int locked; /**< If non-zero the notification is locked **/
|
||||
|
||||
GHashTable *actions;
|
||||
char *default_action_name; /**< The name of the action to be invoked on do_action */
|
||||
|
||||
enum markup_mode markup;
|
||||
const char *format;
|
||||
const char *script;
|
||||
const char **scripts;
|
||||
int script_count;
|
||||
struct notification_colors colors;
|
||||
|
||||
char *stack_tag; /**< stack notifications by tag */
|
||||
@ -80,6 +84,7 @@ struct notification {
|
||||
int displayed_height;
|
||||
enum behavior_fullscreen fullscreen; //!< The instruction what to do with it, when desktop enters fullscreen
|
||||
bool script_run; /**< Has the script been executed already? */
|
||||
guint8 marked_for_closure;
|
||||
|
||||
/* derived fields */
|
||||
char *msg; /**< formatted message */
|
||||
@ -137,6 +142,12 @@ int notification_cmp_data(const void *va, const void *vb, void *data);
|
||||
|
||||
bool notification_is_duplicate(const struct notification *a, const struct notification *b);
|
||||
|
||||
bool notification_is_locked(struct notification *n);
|
||||
|
||||
struct notification *notification_lock(struct notification *n);
|
||||
|
||||
struct notification *notification_unlock(struct notification *n);
|
||||
|
||||
/**Replace the current notification's icon with the icon specified by path.
|
||||
*
|
||||
* Removes the reference for the previous icon automatically and will also free the
|
||||
@ -185,11 +196,33 @@ void notification_replace_single_field(char **haystack,
|
||||
void notification_update_text_to_render(struct notification *n);
|
||||
|
||||
/**
|
||||
* If the notification has exactly one action, or one is marked as default,
|
||||
* invoke it. If there are multiple and no default, open the context menu. If
|
||||
* there are no actions, proceed similarly with urls.
|
||||
* If the notification has an action named n->default_action_name or there is only one
|
||||
* action and n->default_action_name is set to "default", invoke it. If there is no
|
||||
* such action, open the context menu if threre are other actions. Otherwise, do nothing.
|
||||
*/
|
||||
void notification_do_action(const struct notification *n);
|
||||
void notification_do_action(struct notification *n);
|
||||
|
||||
/**
|
||||
* If the notification has exactly one url, invoke it. If there are multiple,
|
||||
* open the context menu. If there are no urls, do nothing.
|
||||
*/
|
||||
void notification_open_url(struct notification *n);
|
||||
|
||||
/**
|
||||
* Open the context menu for the notification.
|
||||
*
|
||||
* Convenience function that creates the GList and passes it to context_menu_for().
|
||||
*/
|
||||
void notification_open_context_menu(struct notification *n);
|
||||
|
||||
/**
|
||||
* Remove all client action data from the notification.
|
||||
*
|
||||
* This should be called after a notification is closed to avoid showing
|
||||
* actions that will not work anymore since the client has stopped listening
|
||||
* for them.
|
||||
*/
|
||||
void notification_invalidate_actions(struct notification *n);
|
||||
|
||||
const char *notification_urgency_to_string(const enum urgency urgency);
|
||||
|
||||
@ -202,4 +235,4 @@ const char *notification_urgency_to_string(const enum urgency urgency);
|
||||
const char *enum_to_string_fullscreen(enum behavior_fullscreen in);
|
||||
|
||||
#endif
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
@ -44,7 +44,7 @@ static int cmdline_find_option(const char *key);
|
||||
|
||||
bool string_parse_alignment(const char *s, enum alignment *ret)
|
||||
{
|
||||
ASSERT_OR_RET(s, false);
|
||||
ASSERT_OR_RET(STR_FULL(s), false);
|
||||
ASSERT_OR_RET(ret, false);
|
||||
|
||||
STRING_PARSE_RET("left", ALIGN_LEFT);
|
||||
@ -56,7 +56,7 @@ bool string_parse_alignment(const char *s, enum alignment *ret)
|
||||
|
||||
bool string_parse_ellipsize(const char *s, enum ellipsize *ret)
|
||||
{
|
||||
ASSERT_OR_RET(s, false);
|
||||
ASSERT_OR_RET(STR_FULL(s), false);
|
||||
ASSERT_OR_RET(ret, false);
|
||||
|
||||
STRING_PARSE_RET("start", ELLIPSE_START);
|
||||
@ -68,7 +68,7 @@ bool string_parse_ellipsize(const char *s, enum ellipsize *ret)
|
||||
|
||||
bool string_parse_follow_mode(const char *s, enum follow_mode *ret)
|
||||
{
|
||||
ASSERT_OR_RET(s, false);
|
||||
ASSERT_OR_RET(STR_FULL(s), false);
|
||||
ASSERT_OR_RET(ret, false);
|
||||
|
||||
STRING_PARSE_RET("mouse", FOLLOW_MOUSE);
|
||||
@ -81,7 +81,7 @@ bool string_parse_follow_mode(const char *s, enum follow_mode *ret)
|
||||
|
||||
bool string_parse_fullscreen(const char *s, enum behavior_fullscreen *ret)
|
||||
{
|
||||
ASSERT_OR_RET(s, false);
|
||||
ASSERT_OR_RET(STR_FULL(s), false);
|
||||
ASSERT_OR_RET(ret, false);
|
||||
|
||||
STRING_PARSE_RET("show", FS_SHOW);
|
||||
@ -93,7 +93,7 @@ bool string_parse_fullscreen(const char *s, enum behavior_fullscreen *ret)
|
||||
|
||||
bool string_parse_icon_position(const char *s, enum icon_position *ret)
|
||||
{
|
||||
ASSERT_OR_RET(s, false);
|
||||
ASSERT_OR_RET(STR_FULL(s), false);
|
||||
ASSERT_OR_RET(ret, false);
|
||||
|
||||
STRING_PARSE_RET("left", ICON_LEFT);
|
||||
@ -103,9 +103,21 @@ bool string_parse_icon_position(const char *s, enum icon_position *ret)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool string_parse_vertical_alignment(const char *s, enum vertical_alignment *ret)
|
||||
{
|
||||
ASSERT_OR_RET(STR_FULL(s), false);
|
||||
ASSERT_OR_RET(ret, false);
|
||||
|
||||
STRING_PARSE_RET("top", VERTICAL_TOP);
|
||||
STRING_PARSE_RET("center", VERTICAL_CENTER);
|
||||
STRING_PARSE_RET("bottom", VERTICAL_BOTTOM);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool string_parse_markup_mode(const char *s, enum markup_mode *ret)
|
||||
{
|
||||
ASSERT_OR_RET(s, false);
|
||||
ASSERT_OR_RET(STR_FULL(s), false);
|
||||
ASSERT_OR_RET(ret, false);
|
||||
|
||||
STRING_PARSE_RET("strip", MARKUP_STRIP);
|
||||
@ -118,22 +130,46 @@ bool string_parse_markup_mode(const char *s, enum markup_mode *ret)
|
||||
|
||||
bool string_parse_mouse_action(const char *s, enum mouse_action *ret)
|
||||
{
|
||||
ASSERT_OR_RET(s, false);
|
||||
ASSERT_OR_RET(STR_FULL(s), false);
|
||||
ASSERT_OR_RET(ret, false);
|
||||
|
||||
STRING_PARSE_RET("none", MOUSE_NONE);
|
||||
STRING_PARSE_RET("do_action", MOUSE_DO_ACTION);
|
||||
STRING_PARSE_RET("close_current", MOUSE_CLOSE_CURRENT);
|
||||
STRING_PARSE_RET("close_all", MOUSE_CLOSE_ALL);
|
||||
STRING_PARSE_RET("context", MOUSE_CONTEXT);
|
||||
STRING_PARSE_RET("context_all", MOUSE_CONTEXT_ALL);
|
||||
STRING_PARSE_RET("open_url", MOUSE_OPEN_URL);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool string_parse_sepcolor(const char *s, struct separator_color_data *ret)
|
||||
bool string_parse_mouse_action_list(char **s, enum mouse_action **ret)
|
||||
{
|
||||
ASSERT_OR_RET(s, false);
|
||||
ASSERT_OR_RET(ret, false);
|
||||
|
||||
int len = 0;
|
||||
while (s[len])
|
||||
len++;
|
||||
|
||||
*ret = g_malloc_n((len + 1), sizeof(enum mouse_action));
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (!string_parse_mouse_action(s[i], *ret + i)) {
|
||||
LOG_W("Unknown mouse action value: '%s'", s[i]);
|
||||
g_free(*ret);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
(*ret)[len] = -1; // sentinel end value
|
||||
return true;
|
||||
}
|
||||
|
||||
bool string_parse_sepcolor(const char *s, struct separator_color_data *ret)
|
||||
{
|
||||
ASSERT_OR_RET(STR_FULL(s), false);
|
||||
ASSERT_OR_RET(ret, false);
|
||||
|
||||
STRING_PARSE_RET("auto", (struct separator_color_data){.type = SEP_AUTO});
|
||||
STRING_PARSE_RET("foreground", (struct separator_color_data){.type = SEP_FOREGROUND});
|
||||
STRING_PARSE_RET("frame", (struct separator_color_data){.type = SEP_FRAME});
|
||||
@ -146,7 +182,7 @@ bool string_parse_sepcolor(const char *s, struct separator_color_data *ret)
|
||||
|
||||
bool string_parse_urgency(const char *s, enum urgency *ret)
|
||||
{
|
||||
ASSERT_OR_RET(s, false);
|
||||
ASSERT_OR_RET(STR_FULL(s), false);
|
||||
ASSERT_OR_RET(ret, false);
|
||||
|
||||
STRING_PARSE_RET("low", URG_LOW);
|
||||
@ -156,6 +192,18 @@ bool string_parse_urgency(const char *s, enum urgency *ret)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool string_parse_layer(const char *s, enum zwlr_layer_shell_v1_layer *ret)
|
||||
{
|
||||
ASSERT_OR_RET(STR_FULL(s), false);
|
||||
ASSERT_OR_RET(ret, false);
|
||||
|
||||
STRING_PARSE_RET("bottom", ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM);
|
||||
STRING_PARSE_RET("top", ZWLR_LAYER_SHELL_V1_LAYER_TOP);
|
||||
STRING_PARSE_RET("overlay", ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct section *new_section(const char *name)
|
||||
{
|
||||
for (int i = 0; i < section_count; i++) {
|
||||
@ -248,6 +296,15 @@ gint64 ini_get_time(const char *section, const char *key, gint64 def)
|
||||
return val;
|
||||
}
|
||||
|
||||
char **ini_get_list(const char *section, const char *key, const char *def)
|
||||
{
|
||||
const char *value = get_value(section, key);
|
||||
if (value)
|
||||
return string_to_array(value);
|
||||
else
|
||||
return string_to_array(def);
|
||||
}
|
||||
|
||||
int ini_get_int(const char *section, const char *key, int def)
|
||||
{
|
||||
const char *value = get_value(section, key);
|
||||
@ -463,6 +520,17 @@ char *cmdline_get_path(const char *key, const char *def, const char *description
|
||||
return string_to_path(g_strdup(def));
|
||||
}
|
||||
|
||||
char **cmdline_get_list(const char *key, const char *def, const char *description)
|
||||
{
|
||||
cmdline_usage_append(key, "list", description);
|
||||
const char *str = cmdline_get_value(key);
|
||||
|
||||
if (str)
|
||||
return string_to_array(str);
|
||||
else
|
||||
return string_to_array(def);
|
||||
}
|
||||
|
||||
gint64 cmdline_get_time(const char *key, gint64 def, const char *description)
|
||||
{
|
||||
cmdline_usage_append(key, "time", description);
|
||||
@ -562,6 +630,23 @@ gint64 option_get_time(const char *ini_section,
|
||||
return cmdline_get_time(cmdline_key, ini_val, description);
|
||||
}
|
||||
|
||||
|
||||
char **option_get_list(const char *ini_section,
|
||||
const char *ini_key,
|
||||
const char *cmdline_key,
|
||||
const char *def,
|
||||
const char *description)
|
||||
{
|
||||
char **val = NULL;
|
||||
if (cmdline_key)
|
||||
val = cmdline_get_list(cmdline_key, NULL, description);
|
||||
|
||||
if (val)
|
||||
return val;
|
||||
else
|
||||
return ini_get_list(ini_section, ini_key, def);
|
||||
}
|
||||
|
||||
int option_get_int(const char *ini_section,
|
||||
const char *ini_key,
|
||||
const char *cmdline_key,
|
||||
@ -646,4 +731,4 @@ const char *cmdline_create_usage(void)
|
||||
return usage_str;
|
||||
}
|
||||
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
@ -14,15 +14,19 @@ bool string_parse_ellipsize(const char *s, enum ellipsize *ret);
|
||||
bool string_parse_follow_mode(const char *s, enum follow_mode *ret);
|
||||
bool string_parse_fullscreen(const char *s, enum behavior_fullscreen *ret);
|
||||
bool string_parse_icon_position(const char *s, enum icon_position *ret);
|
||||
bool string_parse_vertical_alignment(const char *s, enum vertical_alignment *ret);
|
||||
bool string_parse_markup_mode(const char *s, enum markup_mode *ret);
|
||||
bool string_parse_mouse_action(const char *s, enum mouse_action *ret);
|
||||
bool string_parse_mouse_action_list(char **s, enum mouse_action **ret);
|
||||
bool string_parse_sepcolor(const char *s, struct separator_color_data *ret);
|
||||
bool string_parse_urgency(const char *s, enum urgency *ret);
|
||||
bool string_parse_layer(const char *s, enum zwlr_layer_shell_v1_layer *ret);
|
||||
|
||||
int load_ini_file(FILE *);
|
||||
char *ini_get_path(const char *section, const char *key, const char *def);
|
||||
char *ini_get_string(const char *section, const char *key, const char *def);
|
||||
gint64 ini_get_time(const char *section, const char *key, gint64 def);
|
||||
char **ini_get_list(const char *section, const char *key, const char *def);
|
||||
int ini_get_int(const char *section, const char *key, int def);
|
||||
double ini_get_double(const char *section, const char *key, double def);
|
||||
int ini_get_bool(const char *section, const char *key, int def);
|
||||
@ -33,6 +37,7 @@ void cmdline_load(int argc, char *argv[]);
|
||||
/* for all cmdline_get_* key can be either "-key" or "-key/-longkey" */
|
||||
char *cmdline_get_string(const char *key, const char *def, const char *description);
|
||||
char *cmdline_get_path(const char *key, const char *def, const char *description);
|
||||
char **cmdline_get_list(const char *key, const char *def, const char *description);
|
||||
int cmdline_get_int(const char *key, int def, const char *description);
|
||||
double cmdline_get_double(const char *key, double def, const char *description);
|
||||
int cmdline_get_bool(const char *key, int def, const char *description);
|
||||
@ -54,6 +59,11 @@ gint64 option_get_time(const char *ini_section,
|
||||
const char *cmdline_key,
|
||||
gint64 def,
|
||||
const char *description);
|
||||
char **option_get_list(const char *ini_section,
|
||||
const char *ini_key,
|
||||
const char *cmdline_key,
|
||||
const char *def,
|
||||
const char *description);
|
||||
int option_get_int(const char *ini_section,
|
||||
const char *ini_key,
|
||||
const char *cmdline_key,
|
||||
@ -77,4 +87,4 @@ int option_get_bool(const char *ini_section,
|
||||
const char *next_section(const char *section);
|
||||
|
||||
#endif
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
96
src/output.c
Normal file
@ -0,0 +1,96 @@
|
||||
#include "output.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "x11/x.h"
|
||||
#include "x11/screen.h"
|
||||
|
||||
#ifdef ENABLE_WAYLAND
|
||||
#include "wayland/wl.h"
|
||||
#endif
|
||||
|
||||
bool is_running_wayland(void) {
|
||||
char* wayland_display = getenv("WAYLAND_DISPLAY");
|
||||
return !(wayland_display == NULL);
|
||||
}
|
||||
|
||||
const struct output output_x11 = {
|
||||
x_setup,
|
||||
x_free,
|
||||
|
||||
x_win_create,
|
||||
x_win_destroy,
|
||||
|
||||
x_win_show,
|
||||
x_win_hide,
|
||||
|
||||
x_display_surface,
|
||||
x_win_get_context,
|
||||
|
||||
get_active_screen,
|
||||
|
||||
x_is_idle,
|
||||
have_fullscreen_window,
|
||||
|
||||
x_get_scale,
|
||||
};
|
||||
|
||||
#ifdef ENABLE_WAYLAND
|
||||
const struct output output_wl = {
|
||||
wl_init,
|
||||
wl_deinit,
|
||||
|
||||
wl_win_create,
|
||||
wl_win_destroy,
|
||||
|
||||
wl_win_show,
|
||||
wl_win_hide,
|
||||
|
||||
wl_display_surface,
|
||||
wl_win_get_context,
|
||||
|
||||
wl_get_active_screen,
|
||||
|
||||
wl_is_idle,
|
||||
wl_have_fullscreen_window,
|
||||
|
||||
wl_get_scale,
|
||||
};
|
||||
#endif
|
||||
|
||||
const struct output* get_x11_output() {
|
||||
const struct output* output = &output_x11;
|
||||
if (output->init()) {
|
||||
return output;
|
||||
} else {
|
||||
LOG_E("Couldn't initialize X11 output. Aborting...");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_WAYLAND
|
||||
const struct output* get_wl_output() {
|
||||
const struct output* output = &output_wl;
|
||||
if (output->init()) {
|
||||
return output;
|
||||
} else {
|
||||
LOG_W("Couldn't initialize wayland output. Falling back to X11 output.");
|
||||
output->deinit();
|
||||
return get_x11_output();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const struct output* output_create(bool force_xwayland)
|
||||
{
|
||||
#ifdef ENABLE_WAYLAND
|
||||
if (!force_xwayland && is_running_wayland()) {
|
||||
LOG_I("Using Wayland output");
|
||||
return get_wl_output();
|
||||
} else {
|
||||
LOG_I("Using X11 output");
|
||||
return get_x11_output();
|
||||
}
|
||||
#else
|
||||
return get_x11_output();
|
||||
#endif
|
||||
}
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
61
src/output.h
Normal file
@ -0,0 +1,61 @@
|
||||
#ifndef DUNST_OUTPUT_H
|
||||
#define DUNST_OUTPUT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <glib.h>
|
||||
#include <cairo.h>
|
||||
|
||||
typedef gpointer window;
|
||||
|
||||
struct dimensions {
|
||||
int x;
|
||||
int y;
|
||||
int w;
|
||||
int h;
|
||||
|
||||
int corner_radius;
|
||||
};
|
||||
|
||||
struct screen_info {
|
||||
int id;
|
||||
int x;
|
||||
int y;
|
||||
unsigned int h;
|
||||
unsigned int mmh;
|
||||
unsigned int w;
|
||||
int dpi;
|
||||
};
|
||||
|
||||
struct output {
|
||||
bool (*init)(void);
|
||||
void (*deinit)(void);
|
||||
|
||||
window (*win_create)(void);
|
||||
void (*win_destroy)(window);
|
||||
|
||||
void (*win_show)(window);
|
||||
void (*win_hide)(window);
|
||||
|
||||
void (*display_surface)(cairo_surface_t *srf, window win, const struct dimensions*);
|
||||
|
||||
cairo_t* (*win_get_context)(window);
|
||||
|
||||
const struct screen_info* (*get_active_screen)(void);
|
||||
|
||||
bool (*is_idle)(void);
|
||||
bool (*have_fullscreen_window)(void);
|
||||
|
||||
int (*get_scale)(void);
|
||||
};
|
||||
|
||||
/**
|
||||
* return an initialized output, selecting the correct output type from either
|
||||
* wayland or X11 according to the settings and environment.
|
||||
* When the wayland output fails to initilize, it falls back to X11 output.
|
||||
*/
|
||||
const struct output* output_create(bool force_xwayland);
|
||||
|
||||
bool is_running_wayland(void);
|
||||
|
||||
#endif
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
54
src/queues.c
@ -25,6 +25,7 @@
|
||||
#include "notification.h"
|
||||
#include "settings.h"
|
||||
#include "utils.h"
|
||||
#include "output.h" // For checking if wayland is active.
|
||||
|
||||
/* notification lists */
|
||||
static GQueue *waiting = NULL; /**< all new notifications get into here */
|
||||
@ -45,7 +46,7 @@ void queues_init(void)
|
||||
}
|
||||
|
||||
/* see queues.h */
|
||||
const GList *queues_get_displayed(void)
|
||||
GList *queues_get_displayed(void)
|
||||
{
|
||||
return g_queue_peek_head_link(displayed);
|
||||
}
|
||||
@ -167,19 +168,6 @@ int queues_notification_insert(struct notification *n)
|
||||
LOG_M("Skipping notification: '%s' '%s'", n->body, n->summary);
|
||||
return 0;
|
||||
}
|
||||
/* Do not insert the message if it's a command */
|
||||
if (STR_EQ("DUNST_COMMAND_PAUSE", n->summary)) {
|
||||
dunst_status(S_RUNNING, false);
|
||||
return 0;
|
||||
}
|
||||
if (STR_EQ("DUNST_COMMAND_RESUME", n->summary)) {
|
||||
dunst_status(S_RUNNING, true);
|
||||
return 0;
|
||||
}
|
||||
if (STR_EQ("DUNST_COMMAND_TOGGLE", n->summary)) {
|
||||
dunst_status(S_RUNNING, !dunst_status_get().running);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool inserted = false;
|
||||
if (n->id != 0) {
|
||||
@ -389,6 +377,19 @@ void queues_update(struct dunst_status status)
|
||||
struct notification *n = iter->data;
|
||||
nextiter = iter->next;
|
||||
|
||||
if (notification_is_locked(n)) {
|
||||
iter = nextiter;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (n->marked_for_closure) {
|
||||
queues_notification_close(n, n->marked_for_closure);
|
||||
n->marked_for_closure = 0;
|
||||
iter = nextiter;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (queues_notification_is_finished(n, status)){
|
||||
queues_notification_close(n, REASON_TIME);
|
||||
iter = nextiter;
|
||||
@ -506,6 +507,27 @@ gint64 queues_get_next_datachange(gint64 time)
|
||||
return sleep != G_MAXINT64 ? sleep : -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* see queues.h */
|
||||
struct notification* queues_get_by_id(int id)
|
||||
{
|
||||
assert(id > 0);
|
||||
|
||||
GQueue *recqueues[] = { displayed, waiting, history };
|
||||
for (int i = 0; i < sizeof(recqueues)/sizeof(GQueue*); i++) {
|
||||
for (GList *iter = g_queue_peek_head_link(recqueues[i]); iter;
|
||||
iter = iter->next) {
|
||||
struct notification *cur = iter->data;
|
||||
if (cur->id == id)
|
||||
return cur;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for queues_teardown() to free a single notification
|
||||
*
|
||||
@ -527,4 +549,6 @@ void queues_teardown(void)
|
||||
g_queue_free_full(waiting, teardown_notification);
|
||||
waiting = NULL;
|
||||
}
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
15
src/queues.h
@ -24,7 +24,7 @@ void queues_init(void);
|
||||
*
|
||||
* @return read only list of notifications
|
||||
*/
|
||||
const GList *queues_get_displayed(void);
|
||||
GList *queues_get_displayed(void);
|
||||
|
||||
/**
|
||||
* Get the highest notification in line
|
||||
@ -145,6 +145,17 @@ void queues_update(struct dunst_status status);
|
||||
*/
|
||||
gint64 queues_get_next_datachange(gint64 time);
|
||||
|
||||
/**
|
||||
* Get the notification which has the given id in the displayed and waiting queue or
|
||||
* NULL if not found
|
||||
*
|
||||
* @param id the id searched for.
|
||||
*
|
||||
* @return the `id` notification or NULL
|
||||
*/
|
||||
struct notification* queues_get_by_id(int id);
|
||||
|
||||
|
||||
/**
|
||||
* Remove all notifications from all list and free the notifications
|
||||
*
|
||||
@ -153,4 +164,4 @@ gint64 queues_get_next_datachange(gint64 time);
|
||||
void queues_teardown(void);
|
||||
|
||||
#endif
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
20
src/rules.c
@ -26,6 +26,10 @@ void rule_apply(struct rule *r, struct notification *n)
|
||||
n->transient = r->set_transient;
|
||||
if (r->skip_display != -1)
|
||||
n->skip_display = r->skip_display;
|
||||
if (r->action_name) {
|
||||
g_free(n->default_action_name);
|
||||
n->default_action_name = g_strdup(r->action_name);
|
||||
}
|
||||
if (r->markup != MARKUP_NULL)
|
||||
n->markup = r->markup;
|
||||
if (r->new_icon)
|
||||
@ -38,14 +42,22 @@ void rule_apply(struct rule *r, struct notification *n)
|
||||
g_free(n->colors.bg);
|
||||
n->colors.bg = g_strdup(r->bg);
|
||||
}
|
||||
if (r->highlight) {
|
||||
g_free(n->colors.highlight);
|
||||
n->colors.highlight = g_strdup(r->highlight);
|
||||
}
|
||||
if (r->fc) {
|
||||
g_free(n->colors.frame);
|
||||
n->colors.frame = g_strdup(r->fc);
|
||||
}
|
||||
if (r->format)
|
||||
n->format = r->format;
|
||||
if (r->script)
|
||||
n->script = r->script;
|
||||
if (r->script){
|
||||
n->scripts = g_renew(const char*,n->scripts,n->script_count + 1);
|
||||
n->scripts[n->script_count] = r->script;
|
||||
|
||||
n->script_count++;
|
||||
}
|
||||
if (r->set_stack_tag) {
|
||||
g_free(n->stack_tag);
|
||||
n->stack_tag = g_strdup(r->set_stack_tag);
|
||||
@ -74,7 +86,7 @@ struct rule *rule_new(void)
|
||||
r->urgency = URG_NONE;
|
||||
r->fullscreen = FS_NULL;
|
||||
r->markup = MARKUP_NULL;
|
||||
r->history_ignore = false;
|
||||
r->history_ignore = -1;
|
||||
r->match_transient = -1;
|
||||
r->set_transient = -1;
|
||||
r->skip_display = -1;
|
||||
@ -102,4 +114,4 @@ bool rule_matches_notification(struct rule *r, struct notification *n)
|
||||
&& rule_field_matches_string(n->category, r->category)
|
||||
&& rule_field_matches_string(n->stack_tag, r->stack_tag);
|
||||
}
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
@ -23,6 +23,7 @@ struct rule {
|
||||
/* actions */
|
||||
gint64 timeout;
|
||||
enum urgency urgency;
|
||||
char *action_name;
|
||||
enum markup_mode markup;
|
||||
int history_ignore;
|
||||
int match_transient;
|
||||
@ -31,6 +32,7 @@ struct rule {
|
||||
char *new_icon;
|
||||
char *fg;
|
||||
char *bg;
|
||||
char *highlight;
|
||||
char *fc;
|
||||
const char *format;
|
||||
const char *script;
|
||||
@ -52,4 +54,4 @@ void rule_apply_all(struct notification *n);
|
||||
bool rule_matches_notification(struct rule *r, struct notification *n);
|
||||
|
||||
#endif
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
168
src/settings.c
@ -13,6 +13,7 @@
|
||||
#include "rules.h"
|
||||
#include "utils.h"
|
||||
#include "x11/x.h"
|
||||
#include "output.h"
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
@ -53,6 +54,10 @@ static FILE *xdg_config(const char *filename)
|
||||
g_free(path);
|
||||
}
|
||||
|
||||
if (!f) {
|
||||
f = fopen("/etc/dunst/dunstrc", "r");
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
@ -118,6 +123,12 @@ void load_settings(char *cmdline_config_path)
|
||||
"Force the use of the Xinerama extension"
|
||||
);
|
||||
|
||||
settings.force_xwayland = option_get_bool(
|
||||
"global",
|
||||
"force_xwayland", "-force_xwayland", false,
|
||||
"Force the use of the xwayland output"
|
||||
);
|
||||
|
||||
settings.font = option_get_string(
|
||||
"global",
|
||||
"font", "-font/-fn", defaults.font,
|
||||
@ -176,6 +187,11 @@ void load_settings(char *cmdline_config_path)
|
||||
"word_wrap", "-word_wrap", defaults.word_wrap,
|
||||
"Truncating long lines or do word wrap"
|
||||
);
|
||||
settings.ignore_dbusclose = option_get_bool(
|
||||
"global",
|
||||
"ignore_dbusclose", "-ignore_dbusclose", defaults.ignore_dbusclose,
|
||||
"Ignore dbus CloseNotification events"
|
||||
);
|
||||
|
||||
{
|
||||
char *c = option_get_string(
|
||||
@ -204,6 +220,22 @@ void load_settings(char *cmdline_config_path)
|
||||
"Don't timeout notifications if user is longer idle than threshold"
|
||||
);
|
||||
|
||||
#ifndef ENABLE_WAYLAND
|
||||
if (is_running_wayland()){
|
||||
/* We are using xwayland now. Setting force_xwayland to make sure
|
||||
* the idle workaround below is activated */
|
||||
settings.force_xwayland = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (settings.force_xwayland && is_running_wayland()) {
|
||||
if (settings.idle_threshold > 0)
|
||||
LOG_W("Using xwayland. Disabling idle.");
|
||||
/* There is no way to detect if the user is idle
|
||||
* on xwayland, so turn this feature off */
|
||||
settings.idle_threshold = 0;
|
||||
}
|
||||
|
||||
settings.monitor = option_get_int(
|
||||
"global",
|
||||
"monitor", "-mon/-monitor", defaults.monitor,
|
||||
@ -338,6 +370,12 @@ void load_settings(char *cmdline_config_path)
|
||||
"horizontal padding"
|
||||
);
|
||||
|
||||
settings.text_icon_padding = option_get_int(
|
||||
"global",
|
||||
"text_icon_padding", "-text_icon_padding", defaults.text_icon_padding,
|
||||
"Padding between text and icon"
|
||||
);
|
||||
|
||||
settings.transparency = option_get_int(
|
||||
"global",
|
||||
"transparency", "-transparency", defaults.transparency,
|
||||
@ -350,6 +388,49 @@ void load_settings(char *cmdline_config_path)
|
||||
"Window corner radius"
|
||||
);
|
||||
|
||||
settings.progress_bar_height = option_get_int(
|
||||
"global",
|
||||
"progress_bar_height", "-progress_bar_height", defaults.progress_bar_height,
|
||||
"Height of the progress bar"
|
||||
);
|
||||
|
||||
settings.progress_bar_min_width = option_get_int(
|
||||
"global",
|
||||
"progress_bar_min_width", "-progress_bar_min_width", defaults.progress_bar_min_width,
|
||||
"Minimum width of the progress bar"
|
||||
);
|
||||
|
||||
settings.progress_bar_max_width = option_get_int(
|
||||
"global",
|
||||
"progress_bar_max_width", "-progress_bar_max_width", defaults.progress_bar_max_width,
|
||||
"Maximum width of the progress bar"
|
||||
);
|
||||
|
||||
settings.progress_bar_frame_width = option_get_int(
|
||||
"global",
|
||||
"progress_bar_frame_width", "-progress_bar_frame_width", defaults.progress_bar_frame_width,
|
||||
"Frame width of the progress bar"
|
||||
);
|
||||
|
||||
settings.progress_bar = option_get_bool(
|
||||
"global",
|
||||
"progress_bar", "-progress_bar", true,
|
||||
"Show the progress bar"
|
||||
);
|
||||
|
||||
// check sanity of the progress bar options
|
||||
{
|
||||
if (settings.progress_bar_height < (2 * settings.progress_bar_frame_width)){
|
||||
LOG_E("setting progress_bar_frame_width is bigger than half of progress_bar_height");
|
||||
}
|
||||
if (settings.progress_bar_max_width < (2 * settings.progress_bar_frame_width)){
|
||||
LOG_E("setting progress_bar_frame_width is bigger than half of progress_bar_max_width");
|
||||
}
|
||||
if (settings.progress_bar_max_width < settings.progress_bar_min_width){
|
||||
LOG_E("setting progress_bar_max_width is smaller than progress_bar_min_width");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
char *c = option_get_string(
|
||||
"global",
|
||||
@ -411,7 +492,7 @@ void load_settings(char *cmdline_config_path)
|
||||
{
|
||||
char *c = option_get_string(
|
||||
"global",
|
||||
"icon_position", "-icon_position", "off",
|
||||
"icon_position", "-icon_position", "left",
|
||||
"Align icons left/right/off"
|
||||
);
|
||||
|
||||
@ -423,6 +504,43 @@ void load_settings(char *cmdline_config_path)
|
||||
g_free(c);
|
||||
}
|
||||
|
||||
{
|
||||
char *c = option_get_string(
|
||||
"global",
|
||||
"vertical_alignment", "-vertical_alignment", "center",
|
||||
"Align icon and text top/center/bottom"
|
||||
);
|
||||
if (!string_parse_vertical_alignment(c, &settings.vertical_alignment)) {
|
||||
if (c)
|
||||
LOG_W("Unknown vertical alignment: '%s'", c);
|
||||
settings.vertical_alignment = defaults.vertical_alignment;
|
||||
}
|
||||
g_free(c);
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
char *c = option_get_string(
|
||||
"global",
|
||||
"layer", "-layer", "overlay",
|
||||
"Select the layer where notifications should be placed"
|
||||
);
|
||||
|
||||
if (!string_parse_layer(c, &settings.layer)) {
|
||||
if (c)
|
||||
LOG_W("Unknown layer: '%s'", c);
|
||||
settings.layer = defaults.layer;
|
||||
}
|
||||
g_free(c);
|
||||
|
||||
}
|
||||
|
||||
settings.min_icon_size = option_get_int(
|
||||
"global",
|
||||
"min_icon_size", "-min_icon_size", defaults.min_icon_size,
|
||||
"Scale smaller icons up to this size, set to 0 to disable. If max_icon_size also specified, that has the final say."
|
||||
);
|
||||
|
||||
settings.max_icon_size = option_get_int(
|
||||
"global",
|
||||
"max_icon_size", "-max_icon_size", defaults.max_icon_size,
|
||||
@ -508,48 +626,42 @@ void load_settings(char *cmdline_config_path)
|
||||
}
|
||||
|
||||
{
|
||||
char *c = option_get_string(
|
||||
char **c = option_get_list(
|
||||
"global",
|
||||
"mouse_left_click", "-left_click", NULL,
|
||||
"mouse_left_click", "-mouse_left_click", NULL,
|
||||
"Action of Left click event"
|
||||
);
|
||||
|
||||
if (!string_parse_mouse_action(c, &settings.mouse_left_click)) {
|
||||
if (c)
|
||||
LOG_W("Unknown mouse action value: '%s'", c);
|
||||
if (!string_parse_mouse_action_list(c, &settings.mouse_left_click)) {
|
||||
settings.mouse_left_click = defaults.mouse_left_click;
|
||||
}
|
||||
g_free(c);
|
||||
free_string_array(c);
|
||||
}
|
||||
|
||||
{
|
||||
char *c = option_get_string(
|
||||
char **c = option_get_list(
|
||||
"global",
|
||||
"mouse_middle_click", "-mouse_middle_click", NULL,
|
||||
"Action of middle click event"
|
||||
);
|
||||
|
||||
if (!string_parse_mouse_action(c, &settings.mouse_middle_click)) {
|
||||
if (c)
|
||||
LOG_W("Unknown mouse action value: '%s'", c);
|
||||
if (!string_parse_mouse_action_list(c, &settings.mouse_middle_click)) {
|
||||
settings.mouse_middle_click = defaults.mouse_middle_click;
|
||||
}
|
||||
g_free(c);
|
||||
free_string_array(c);
|
||||
}
|
||||
|
||||
{
|
||||
char *c = option_get_string(
|
||||
char **c = option_get_list(
|
||||
"global",
|
||||
"mouse_right_click", "-mouse_right_click", NULL,
|
||||
"Action of right click event"
|
||||
);
|
||||
|
||||
if (!string_parse_mouse_action(c, &settings.mouse_right_click)) {
|
||||
if (c)
|
||||
LOG_W("Unknown mouse action value: '%s'", c);
|
||||
if (!string_parse_mouse_action_list(c, &settings.mouse_right_click)) {
|
||||
settings.mouse_right_click = defaults.mouse_right_click;
|
||||
}
|
||||
g_free(c);
|
||||
free_string_array(c);
|
||||
}
|
||||
|
||||
settings.colors_low.bg = option_get_string(
|
||||
@ -564,6 +676,12 @@ void load_settings(char *cmdline_config_path)
|
||||
"Foreground color for notifications with low urgency"
|
||||
);
|
||||
|
||||
settings.colors_low.highlight = option_get_string(
|
||||
"urgency_low",
|
||||
"highlight", "-lh", defaults.colors_low.highlight,
|
||||
"Highlight color for notifications with low urgency"
|
||||
);
|
||||
|
||||
settings.colors_low.frame = option_get_string(
|
||||
"urgency_low",
|
||||
"frame_color", "-lfr", settings.frame_color ? settings.frame_color : defaults.colors_low.frame,
|
||||
@ -594,6 +712,12 @@ void load_settings(char *cmdline_config_path)
|
||||
"Foreground color for notifications with normal urgency"
|
||||
);
|
||||
|
||||
settings.colors_norm.highlight = option_get_string(
|
||||
"urgency_normal",
|
||||
"highlight", "-nh", defaults.colors_norm.highlight,
|
||||
"Highlight color for notifications with normal urgency"
|
||||
);
|
||||
|
||||
settings.colors_norm.frame = option_get_string(
|
||||
"urgency_normal",
|
||||
"frame_color", "-nfr", settings.frame_color ? settings.frame_color : defaults.colors_norm.frame,
|
||||
@ -624,6 +748,12 @@ void load_settings(char *cmdline_config_path)
|
||||
"Foreground color for notifications with ciritical urgency"
|
||||
);
|
||||
|
||||
settings.colors_crit.highlight = option_get_string(
|
||||
"urgency_critical",
|
||||
"highlight", "-ch", defaults.colors_crit.highlight,
|
||||
"Highlight color for notifications with ciritical urgency"
|
||||
);
|
||||
|
||||
settings.colors_crit.frame = option_get_string(
|
||||
"urgency_critical",
|
||||
"frame_color", "-cfr", settings.frame_color ? settings.frame_color : defaults.colors_crit.frame,
|
||||
@ -732,10 +862,12 @@ void load_settings(char *cmdline_config_path)
|
||||
g_free(c);
|
||||
}
|
||||
|
||||
r->action_name = ini_get_string(cur_section, "action_name", NULL);
|
||||
r->urgency = ini_get_urgency(cur_section, "urgency", r->urgency);
|
||||
r->msg_urgency = ini_get_urgency(cur_section, "msg_urgency", r->msg_urgency);
|
||||
r->fg = ini_get_string(cur_section, "foreground", r->fg);
|
||||
r->bg = ini_get_string(cur_section, "background", r->bg);
|
||||
r->highlight = ini_get_string(cur_section, "highlight", r->highlight);
|
||||
r->fc = ini_get_string(cur_section, "frame_color", r->fc);
|
||||
r->format = ini_get_string(cur_section, "format", r->format);
|
||||
r->new_icon = ini_get_string(cur_section, "new_icon", r->new_icon);
|
||||
@ -767,4 +899,4 @@ void load_settings(char *cmdline_config_path)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
@ -4,6 +4,10 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef ENABLE_WAYLAND
|
||||
#include "wayland/protocols/wlr-layer-shell-unstable-v1-client-header.h"
|
||||
#endif
|
||||
|
||||
#include "markup.h"
|
||||
#include "notification.h"
|
||||
#include "x11/x.h"
|
||||
@ -11,9 +15,20 @@
|
||||
enum alignment { ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT };
|
||||
enum ellipsize { ELLIPSE_START, ELLIPSE_MIDDLE, ELLIPSE_END };
|
||||
enum icon_position { ICON_LEFT, ICON_RIGHT, ICON_OFF };
|
||||
enum vertical_alignment { VERTICAL_TOP, VERTICAL_CENTER, VERTICAL_BOTTOM };
|
||||
enum separator_color { SEP_FOREGROUND, SEP_AUTO, SEP_FRAME, SEP_CUSTOM };
|
||||
enum follow_mode { FOLLOW_NONE, FOLLOW_MOUSE, FOLLOW_KEYBOARD };
|
||||
enum mouse_action { MOUSE_NONE, MOUSE_DO_ACTION, MOUSE_CLOSE_CURRENT, MOUSE_CLOSE_ALL };
|
||||
enum mouse_action { MOUSE_NONE, MOUSE_DO_ACTION, MOUSE_CLOSE_CURRENT, MOUSE_CLOSE_ALL, MOUSE_CONTEXT, MOUSE_CONTEXT_ALL, MOUSE_OPEN_URL };
|
||||
#ifndef ZWLR_LAYER_SHELL_V1_LAYER_ENUM
|
||||
#define ZWLR_LAYER_SHELL_V1_LAYER_ENUM
|
||||
// Needed for compiling without wayland dependency
|
||||
enum zwlr_layer_shell_v1_layer {
|
||||
ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND = 0,
|
||||
ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM = 1,
|
||||
ZWLR_LAYER_SHELL_V1_LAYER_TOP = 2,
|
||||
ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY = 3,
|
||||
};
|
||||
#endif /* ZWLR_LAYER_SHELL_V1_LAYER_ENUM */
|
||||
|
||||
struct separator_color_data {
|
||||
enum separator_color type;
|
||||
@ -58,6 +73,7 @@ struct settings {
|
||||
int history_length;
|
||||
int show_indicators;
|
||||
int word_wrap;
|
||||
int ignore_dbusclose;
|
||||
enum ellipsize ellipsize;
|
||||
int ignore_newline;
|
||||
int line_height;
|
||||
@ -65,6 +81,7 @@ struct settings {
|
||||
int separator_height;
|
||||
int padding;
|
||||
int h_padding;
|
||||
int text_icon_padding;
|
||||
struct separator_color_data sep_color;
|
||||
int frame_width;
|
||||
char *frame_color;
|
||||
@ -75,6 +92,8 @@ struct settings {
|
||||
char *browser;
|
||||
char **browser_cmd;
|
||||
enum icon_position icon_position;
|
||||
enum vertical_alignment vertical_alignment;
|
||||
int min_icon_size;
|
||||
int max_icon_size;
|
||||
char *icon_path;
|
||||
enum follow_mode f_mode;
|
||||
@ -84,10 +103,17 @@ struct settings {
|
||||
struct keyboard_shortcut history_ks;
|
||||
struct keyboard_shortcut context_ks;
|
||||
bool force_xinerama;
|
||||
bool force_xwayland;
|
||||
int corner_radius;
|
||||
enum mouse_action mouse_left_click;
|
||||
enum mouse_action mouse_middle_click;
|
||||
enum mouse_action mouse_right_click;
|
||||
enum mouse_action *mouse_left_click;
|
||||
enum mouse_action *mouse_middle_click;
|
||||
enum mouse_action *mouse_right_click;
|
||||
int progress_bar_height;
|
||||
int progress_bar_min_width;
|
||||
int progress_bar_max_width;
|
||||
int progress_bar_frame_width;
|
||||
bool progress_bar;
|
||||
enum zwlr_layer_shell_v1_layer layer;
|
||||
};
|
||||
|
||||
extern struct settings settings;
|
||||
@ -95,4 +121,4 @@ extern struct settings settings;
|
||||
void load_settings(char *cmdline_config_path);
|
||||
|
||||
#endif
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
59
src/utils.c
@ -5,12 +5,25 @@
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <glib.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
/* see utils.h */
|
||||
void free_string_array(char **arr)
|
||||
{
|
||||
if (arr){
|
||||
for (int i = 0; arr[i]; i++){
|
||||
g_free(arr[i]);
|
||||
}
|
||||
}
|
||||
g_free(arr);
|
||||
}
|
||||
|
||||
/* see utils.h */
|
||||
char *string_replace_char(char needle, char replacement, char *haystack)
|
||||
{
|
||||
@ -133,12 +146,25 @@ void string_strip_delimited(char *str, char a, char b)
|
||||
str[iwrite] = 0;
|
||||
}
|
||||
|
||||
/* see utils.h */
|
||||
char **string_to_array(const char *string)
|
||||
{
|
||||
char **arr = NULL;
|
||||
if (string) {
|
||||
arr = g_strsplit(string, ",", 0);
|
||||
for (int i = 0; arr[i]; i++){
|
||||
g_strstrip(arr[i]);
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
/* see utils.h */
|
||||
char *string_to_path(char *string)
|
||||
{
|
||||
|
||||
if (string && STRN_EQ(string, "~/", 2)) {
|
||||
char *home = g_strconcat(getenv("HOME"), "/", NULL);
|
||||
char *home = g_strconcat(user_get_home(), "/", NULL);
|
||||
|
||||
string = string_replace_at(string, 0, 2, home);
|
||||
|
||||
@ -204,4 +230,33 @@ gint64 time_monotonic_now(void)
|
||||
#endif
|
||||
return S2US(tv_now.tv_sec) + tv_now.tv_nsec / 1000;
|
||||
}
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
/* see utils.h */
|
||||
const char *user_get_home(void)
|
||||
{
|
||||
static const char *home_directory = NULL;
|
||||
ASSERT_OR_RET(!home_directory, home_directory);
|
||||
|
||||
// Check the HOME variable for the user's home
|
||||
home_directory = getenv("HOME");
|
||||
ASSERT_OR_RET(!home_directory, home_directory);
|
||||
|
||||
// Check the /etc/passwd entry for the user's home
|
||||
home_directory = getpwuid(getuid())->pw_dir;
|
||||
|
||||
return home_directory;
|
||||
}
|
||||
|
||||
bool safe_setenv(const char* key, const char* value){
|
||||
if (!key)
|
||||
return false;
|
||||
|
||||
if (!value)
|
||||
setenv(key, "", 1);
|
||||
else
|
||||
setenv(key, value, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
41
src/utils.h
@ -4,6 +4,7 @@
|
||||
|
||||
#include <glib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
//! Test if a string is NULL or empty
|
||||
#define STR_EMPTY(s) (!s || (*s == '\0'))
|
||||
@ -22,6 +23,15 @@
|
||||
//! Convert a second into the internal time representation
|
||||
#define S2US(s) (((gint64)(s)) * 1000 * 1000)
|
||||
|
||||
/**
|
||||
* Frees an array of strings whose last element is a NULL pointer.
|
||||
*
|
||||
* Assumes the last element is a NULL pointer, otherwise will result in a buffer overflow.
|
||||
|
||||
* @param arr The array of strings to free
|
||||
*/
|
||||
void free_string_array(char **arr);
|
||||
|
||||
/**
|
||||
* Replaces all occurrences of the char \p needle with the char \p replacement in \p haystack.
|
||||
*
|
||||
@ -82,6 +92,17 @@ char *string_strip_quotes(const char *value);
|
||||
*/
|
||||
void string_strip_delimited(char *str, char a, char b);
|
||||
|
||||
/**
|
||||
* Parse a comma-delimited string into a dynamic array of tokens
|
||||
*
|
||||
* The string is split on commas and strips spaces from tokens. The last element
|
||||
* of the array is NULL in order to avoid passing around a length variable.
|
||||
*
|
||||
* @param string The string to convert to an array
|
||||
* @returns The array of tokens.
|
||||
*/
|
||||
char **string_to_array(const char *string);
|
||||
|
||||
/**
|
||||
* Replace tilde and path-specific values with its equivalents
|
||||
*
|
||||
@ -110,5 +131,23 @@ gint64 string_to_time(const char *string);
|
||||
*/
|
||||
gint64 time_monotonic_now(void);
|
||||
|
||||
/**
|
||||
* Retrieve the HOME directory of the user running dunst
|
||||
*
|
||||
* @returns: A string of the current home directory
|
||||
*/
|
||||
const char *user_get_home(void);
|
||||
|
||||
/**
|
||||
* Try to set an environment variable safely. If an environment variable with
|
||||
* name `key` exists, it will be overwritten.
|
||||
* If `value` is null, `key` will be set to an empty string.
|
||||
*
|
||||
* @param key (nullable) The environment variable to change
|
||||
* @param value (nullable) The value to change it to.
|
||||
* @returns: A bool that is true when it succeeds
|
||||
*/
|
||||
bool safe_setenv(const char* key, const char* value);
|
||||
|
||||
#endif
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
192
src/wayland/libgwater-wayland.c
Normal file
@ -0,0 +1,192 @@
|
||||
/*
|
||||
* libgwater-wayland - Wayland GSource
|
||||
*
|
||||
* Copyright © 2014-2017 Quentin "Sardem FF7" Glidic
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#ifdef G_LOG_DOMAIN
|
||||
#undef G_LOG_DOMAIN
|
||||
#endif /* G_LOG_DOMAIN */
|
||||
#define G_LOG_DOMAIN "GWaterWayland"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "libgwater-wayland.h"
|
||||
|
||||
struct _GWaterWaylandSource {
|
||||
GSource source;
|
||||
gboolean display_owned;
|
||||
struct wl_display *display;
|
||||
gpointer fd;
|
||||
int error;
|
||||
};
|
||||
|
||||
static gboolean
|
||||
_g_water_wayland_source_prepare(GSource *source, gint *timeout)
|
||||
{
|
||||
GWaterWaylandSource *self = (GWaterWaylandSource *)source;
|
||||
|
||||
*timeout = 0;
|
||||
if ( wl_display_prepare_read(self->display) != 0 )
|
||||
return TRUE;
|
||||
else if ( wl_display_flush(self->display) < 0 )
|
||||
{
|
||||
self->error = errno;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
*timeout = -1;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_g_water_wayland_source_check(GSource *source)
|
||||
{
|
||||
GWaterWaylandSource *self = (GWaterWaylandSource *)source;
|
||||
|
||||
if ( self->error > 0 )
|
||||
return TRUE;
|
||||
|
||||
GIOCondition revents;
|
||||
revents = g_source_query_unix_fd(source, self->fd);
|
||||
|
||||
if ( revents & G_IO_IN )
|
||||
{
|
||||
if ( wl_display_read_events(self->display) < 0 )
|
||||
self->error = errno;
|
||||
}
|
||||
else
|
||||
wl_display_cancel_read(self->display);
|
||||
|
||||
return ( revents > 0 );
|
||||
}
|
||||
|
||||
static gboolean
|
||||
_g_water_wayland_source_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
|
||||
{
|
||||
GWaterWaylandSource *self = (GWaterWaylandSource *)source;
|
||||
GIOCondition revents;
|
||||
|
||||
revents = g_source_query_unix_fd(source, self->fd);
|
||||
if ( ( self->error > 0 ) || ( revents & (G_IO_ERR | G_IO_HUP) ) )
|
||||
{
|
||||
errno = self->error;
|
||||
self->error = 0;
|
||||
if ( callback != NULL )
|
||||
return callback(user_data);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
if ( wl_display_dispatch_pending(self->display) < 0 )
|
||||
{
|
||||
if ( callback != NULL )
|
||||
return callback(user_data);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static void
|
||||
_g_water_wayland_source_finalize(GSource *source)
|
||||
{
|
||||
GWaterWaylandSource *self = (GWaterWaylandSource *)source;
|
||||
|
||||
if ( self->display_owned )
|
||||
wl_display_disconnect(self->display);
|
||||
}
|
||||
|
||||
static GSourceFuncs _g_water_wayland_source_funcs = {
|
||||
.prepare = _g_water_wayland_source_prepare,
|
||||
.check = _g_water_wayland_source_check,
|
||||
.dispatch = _g_water_wayland_source_dispatch,
|
||||
.finalize = _g_water_wayland_source_finalize,
|
||||
};
|
||||
|
||||
GWaterWaylandSource *
|
||||
g_water_wayland_source_new(GMainContext *context, const gchar *name)
|
||||
{
|
||||
struct wl_display *display;
|
||||
GWaterWaylandSource *self;
|
||||
|
||||
display = wl_display_connect(name);
|
||||
if ( display == NULL )
|
||||
return NULL;
|
||||
|
||||
self = g_water_wayland_source_new_for_display(context, display);
|
||||
self->display_owned = TRUE;
|
||||
return self;
|
||||
}
|
||||
|
||||
GWaterWaylandSource *
|
||||
g_water_wayland_source_new_for_display(GMainContext *context, struct wl_display *display)
|
||||
{
|
||||
g_return_val_if_fail(display != NULL, NULL);
|
||||
|
||||
GSource *source;
|
||||
GWaterWaylandSource *self;
|
||||
|
||||
source = g_source_new(&_g_water_wayland_source_funcs, sizeof(GWaterWaylandSource));
|
||||
self = (GWaterWaylandSource *)source;
|
||||
self->display = display;
|
||||
|
||||
self->fd = g_source_add_unix_fd(source, wl_display_get_fd(self->display), G_IO_IN | G_IO_ERR | G_IO_HUP);
|
||||
|
||||
g_source_attach(source, context);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
g_water_wayland_source_free(GWaterWaylandSource *self)
|
||||
{
|
||||
GSource * source = (GSource *)self;
|
||||
g_return_if_fail(self != NULL);
|
||||
|
||||
g_source_destroy(source);
|
||||
|
||||
g_source_unref(source);
|
||||
}
|
||||
|
||||
void
|
||||
g_water_wayland_source_set_error_callback(GWaterWaylandSource *self, GSourceFunc callback, gpointer user_data, GDestroyNotify destroy_notify)
|
||||
{
|
||||
g_return_if_fail(self != NULL);
|
||||
|
||||
g_source_set_callback((GSource *)self, callback, user_data, destroy_notify);
|
||||
}
|
||||
|
||||
struct wl_display *
|
||||
g_water_wayland_source_get_display(GWaterWaylandSource *self)
|
||||
{
|
||||
g_return_val_if_fail(self != NULL, NULL);
|
||||
|
||||
return self->display;
|
||||
}
|
||||
42
src/wayland/libgwater-wayland.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* libgwater-wayland - Wayland GSource
|
||||
*
|
||||
* Copyright © 2014-2017 Quentin "Sardem FF7" Glidic
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __G_WATER_WAYLAND_H__
|
||||
#define __G_WATER_WAYLAND_H__
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GWaterWaylandSource GWaterWaylandSource;
|
||||
|
||||
GWaterWaylandSource *g_water_wayland_source_new(GMainContext *context, const gchar *name);
|
||||
GWaterWaylandSource *g_water_wayland_source_new_for_display(GMainContext *context, struct wl_display *display);
|
||||
void g_water_wayland_source_free(GWaterWaylandSource *self);
|
||||
|
||||
void g_water_wayland_source_set_error_callback(GWaterWaylandSource *self, GSourceFunc callback, gpointer user_data, GDestroyNotify destroy_notify);
|
||||
struct wl_display *g_water_wayland_source_get_display(GWaterWaylandSource *source);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_WATER_WAYLAND_H__ */
|
||||
150
src/wayland/pool-buffer.c
Normal file
@ -0,0 +1,150 @@
|
||||
#define _POSIX_C_SOURCE 200112L
|
||||
#include <cairo/cairo.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <wayland-client.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pool-buffer.h"
|
||||
|
||||
static void randname(char *buf) {
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
long r = ts.tv_nsec;
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
buf[i] = 'A'+(r&15)+(r&16)*2;
|
||||
r >>= 5;
|
||||
}
|
||||
}
|
||||
|
||||
static int anonymous_shm_open(void) {
|
||||
char name[] = "/dunst-XXXXXX";
|
||||
int retries = 100;
|
||||
|
||||
do {
|
||||
randname(name + strlen(name) - 6);
|
||||
|
||||
--retries;
|
||||
// shm_open guarantees that O_CLOEXEC is set
|
||||
int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
|
||||
if (fd >= 0) {
|
||||
shm_unlink(name);
|
||||
return fd;
|
||||
}
|
||||
} while (retries > 0 && errno == EEXIST);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int create_shm_file(off_t size) {
|
||||
int fd = anonymous_shm_open();
|
||||
if (fd < 0) {
|
||||
return fd;
|
||||
}
|
||||
|
||||
if (ftruncate(fd, size) < 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void buffer_handle_release(void *data, struct wl_buffer *wl_buffer) {
|
||||
struct pool_buffer *buffer = data;
|
||||
buffer->busy = false;
|
||||
}
|
||||
|
||||
static const struct wl_buffer_listener buffer_listener = {
|
||||
.release = buffer_handle_release,
|
||||
};
|
||||
|
||||
static struct pool_buffer *create_buffer(struct wl_shm *shm,
|
||||
struct pool_buffer *buf, int32_t width, int32_t height) {
|
||||
const enum wl_shm_format wl_fmt = WL_SHM_FORMAT_ARGB8888;
|
||||
const cairo_format_t cairo_fmt = CAIRO_FORMAT_ARGB32;
|
||||
|
||||
uint32_t stride = cairo_format_stride_for_width(cairo_fmt, width);
|
||||
size_t size = stride * height;
|
||||
|
||||
void *data = NULL;
|
||||
if (size > 0) {
|
||||
int fd = create_shm_file(size);
|
||||
if (fd == -1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (data == MAP_FAILED) {
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
|
||||
buf->buffer =
|
||||
wl_shm_pool_create_buffer(pool, 0, width, height, stride, wl_fmt);
|
||||
wl_buffer_add_listener(buf->buffer, &buffer_listener, buf);
|
||||
wl_shm_pool_destroy(pool);
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
buf->data = data;
|
||||
buf->size = size;
|
||||
buf->width = width;
|
||||
buf->height = height;
|
||||
buf->surface = cairo_image_surface_create_for_data(data, cairo_fmt, width,
|
||||
height, stride);
|
||||
buf->cairo = cairo_create(buf->surface);
|
||||
buf->pango = pango_cairo_create_context(buf->cairo);
|
||||
return buf;
|
||||
}
|
||||
|
||||
void finish_buffer(struct pool_buffer *buffer) {
|
||||
if (buffer->buffer) {
|
||||
wl_buffer_destroy(buffer->buffer);
|
||||
}
|
||||
if (buffer->cairo) {
|
||||
cairo_destroy(buffer->cairo);
|
||||
}
|
||||
if (buffer->surface) {
|
||||
cairo_surface_destroy(buffer->surface);
|
||||
}
|
||||
if (buffer->pango) {
|
||||
g_object_unref(buffer->pango);
|
||||
}
|
||||
if (buffer->data) {
|
||||
munmap(buffer->data, buffer->size);
|
||||
}
|
||||
memset(buffer, 0, sizeof(struct pool_buffer));
|
||||
}
|
||||
|
||||
struct pool_buffer *get_next_buffer(struct wl_shm *shm,
|
||||
struct pool_buffer pool[static 2], uint32_t width, uint32_t height) {
|
||||
struct pool_buffer *buffer = NULL;
|
||||
for (size_t i = 0; i < 2; ++i) {
|
||||
if (pool[i].busy) {
|
||||
continue;
|
||||
}
|
||||
buffer = &pool[i];
|
||||
}
|
||||
if (!buffer) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (buffer->width != width || buffer->height != height) {
|
||||
finish_buffer(buffer);
|
||||
}
|
||||
|
||||
if (!buffer->buffer) {
|
||||
if (!create_buffer(shm, buffer, width, height)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
26
src/wayland/pool-buffer.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef DUNST_POOL_BUFFER_H
|
||||
#define DUNST_POOL_BUFFER_H
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
#include <pango/pangocairo.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
struct pool_buffer {
|
||||
struct wl_buffer *buffer;
|
||||
cairo_surface_t *surface;
|
||||
cairo_t *cairo;
|
||||
PangoContext *pango;
|
||||
uint32_t width, height;
|
||||
void *data;
|
||||
size_t size;
|
||||
bool busy;
|
||||
};
|
||||
|
||||
struct pool_buffer *get_next_buffer(struct wl_shm *shm,
|
||||
struct pool_buffer pool[static 2], uint32_t width, uint32_t height);
|
||||
void finish_buffer(struct pool_buffer *buffer);
|
||||
|
||||
#endif
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
233
src/wayland/protocols/idle-client-header.h
Normal file
@ -0,0 +1,233 @@
|
||||
/* Generated by wayland-scanner 1.18.0 */
|
||||
|
||||
#ifndef IDLE_CLIENT_PROTOCOL_H
|
||||
#define IDLE_CLIENT_PROTOCOL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "wayland-client.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @page page_idle The idle protocol
|
||||
* @section page_ifaces_idle Interfaces
|
||||
* - @subpage page_iface_org_kde_kwin_idle - User idle time manager
|
||||
* - @subpage page_iface_org_kde_kwin_idle_timeout -
|
||||
* @section page_copyright_idle Copyright
|
||||
* <pre>
|
||||
*
|
||||
* Copyright (C) 2015 Martin Gräßlin
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* </pre>
|
||||
*/
|
||||
struct org_kde_kwin_idle;
|
||||
struct org_kde_kwin_idle_timeout;
|
||||
struct wl_seat;
|
||||
|
||||
/**
|
||||
* @page page_iface_org_kde_kwin_idle org_kde_kwin_idle
|
||||
* @section page_iface_org_kde_kwin_idle_desc Description
|
||||
*
|
||||
* This interface allows to monitor user idle time on a given seat. The interface
|
||||
* allows to register timers which trigger after no user activity was registered
|
||||
* on the seat for a given interval. It notifies when user activity resumes.
|
||||
*
|
||||
* This is useful for applications wanting to perform actions when the user is not
|
||||
* interacting with the system, e.g. chat applications setting the user as away, power
|
||||
* management features to dim screen, etc..
|
||||
* @section page_iface_org_kde_kwin_idle_api API
|
||||
* See @ref iface_org_kde_kwin_idle.
|
||||
*/
|
||||
/**
|
||||
* @defgroup iface_org_kde_kwin_idle The org_kde_kwin_idle interface
|
||||
*
|
||||
* This interface allows to monitor user idle time on a given seat. The interface
|
||||
* allows to register timers which trigger after no user activity was registered
|
||||
* on the seat for a given interval. It notifies when user activity resumes.
|
||||
*
|
||||
* This is useful for applications wanting to perform actions when the user is not
|
||||
* interacting with the system, e.g. chat applications setting the user as away, power
|
||||
* management features to dim screen, etc..
|
||||
*/
|
||||
extern const struct wl_interface org_kde_kwin_idle_interface;
|
||||
/**
|
||||
* @page page_iface_org_kde_kwin_idle_timeout org_kde_kwin_idle_timeout
|
||||
* @section page_iface_org_kde_kwin_idle_timeout_api API
|
||||
* See @ref iface_org_kde_kwin_idle_timeout.
|
||||
*/
|
||||
/**
|
||||
* @defgroup iface_org_kde_kwin_idle_timeout The org_kde_kwin_idle_timeout interface
|
||||
*/
|
||||
extern const struct wl_interface org_kde_kwin_idle_timeout_interface;
|
||||
|
||||
#define ORG_KDE_KWIN_IDLE_GET_IDLE_TIMEOUT 0
|
||||
|
||||
|
||||
/**
|
||||
* @ingroup iface_org_kde_kwin_idle
|
||||
*/
|
||||
#define ORG_KDE_KWIN_IDLE_GET_IDLE_TIMEOUT_SINCE_VERSION 1
|
||||
|
||||
/** @ingroup iface_org_kde_kwin_idle */
|
||||
static inline void
|
||||
org_kde_kwin_idle_set_user_data(struct org_kde_kwin_idle *org_kde_kwin_idle, void *user_data)
|
||||
{
|
||||
wl_proxy_set_user_data((struct wl_proxy *) org_kde_kwin_idle, user_data);
|
||||
}
|
||||
|
||||
/** @ingroup iface_org_kde_kwin_idle */
|
||||
static inline void *
|
||||
org_kde_kwin_idle_get_user_data(struct org_kde_kwin_idle *org_kde_kwin_idle)
|
||||
{
|
||||
return wl_proxy_get_user_data((struct wl_proxy *) org_kde_kwin_idle);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
org_kde_kwin_idle_get_version(struct org_kde_kwin_idle *org_kde_kwin_idle)
|
||||
{
|
||||
return wl_proxy_get_version((struct wl_proxy *) org_kde_kwin_idle);
|
||||
}
|
||||
|
||||
/** @ingroup iface_org_kde_kwin_idle */
|
||||
static inline void
|
||||
org_kde_kwin_idle_destroy(struct org_kde_kwin_idle *org_kde_kwin_idle)
|
||||
{
|
||||
wl_proxy_destroy((struct wl_proxy *) org_kde_kwin_idle);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_org_kde_kwin_idle
|
||||
*/
|
||||
static inline struct org_kde_kwin_idle_timeout *
|
||||
org_kde_kwin_idle_get_idle_timeout(struct org_kde_kwin_idle *org_kde_kwin_idle, struct wl_seat *seat, uint32_t timeout)
|
||||
{
|
||||
struct wl_proxy *id;
|
||||
|
||||
id = wl_proxy_marshal_constructor((struct wl_proxy *) org_kde_kwin_idle,
|
||||
ORG_KDE_KWIN_IDLE_GET_IDLE_TIMEOUT, &org_kde_kwin_idle_timeout_interface, NULL, seat, timeout);
|
||||
|
||||
return (struct org_kde_kwin_idle_timeout *) id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_org_kde_kwin_idle_timeout
|
||||
* @struct org_kde_kwin_idle_timeout_listener
|
||||
*/
|
||||
struct org_kde_kwin_idle_timeout_listener {
|
||||
/**
|
||||
* Triggered when there has not been any user activity in the requested idle time interval
|
||||
*
|
||||
*
|
||||
*/
|
||||
void (*idle)(void *data,
|
||||
struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout);
|
||||
/**
|
||||
* Triggered on the first user activity after an idle event
|
||||
*
|
||||
*
|
||||
*/
|
||||
void (*resumed)(void *data,
|
||||
struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup iface_org_kde_kwin_idle_timeout
|
||||
*/
|
||||
static inline int
|
||||
org_kde_kwin_idle_timeout_add_listener(struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout,
|
||||
const struct org_kde_kwin_idle_timeout_listener *listener, void *data)
|
||||
{
|
||||
return wl_proxy_add_listener((struct wl_proxy *) org_kde_kwin_idle_timeout,
|
||||
(void (**)(void)) listener, data);
|
||||
}
|
||||
|
||||
#define ORG_KDE_KWIN_IDLE_TIMEOUT_RELEASE 0
|
||||
#define ORG_KDE_KWIN_IDLE_TIMEOUT_SIMULATE_USER_ACTIVITY 1
|
||||
|
||||
/**
|
||||
* @ingroup iface_org_kde_kwin_idle_timeout
|
||||
*/
|
||||
#define ORG_KDE_KWIN_IDLE_TIMEOUT_IDLE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_org_kde_kwin_idle_timeout
|
||||
*/
|
||||
#define ORG_KDE_KWIN_IDLE_TIMEOUT_RESUMED_SINCE_VERSION 1
|
||||
|
||||
/**
|
||||
* @ingroup iface_org_kde_kwin_idle_timeout
|
||||
*/
|
||||
#define ORG_KDE_KWIN_IDLE_TIMEOUT_RELEASE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_org_kde_kwin_idle_timeout
|
||||
*/
|
||||
#define ORG_KDE_KWIN_IDLE_TIMEOUT_SIMULATE_USER_ACTIVITY_SINCE_VERSION 1
|
||||
|
||||
/** @ingroup iface_org_kde_kwin_idle_timeout */
|
||||
static inline void
|
||||
org_kde_kwin_idle_timeout_set_user_data(struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout, void *user_data)
|
||||
{
|
||||
wl_proxy_set_user_data((struct wl_proxy *) org_kde_kwin_idle_timeout, user_data);
|
||||
}
|
||||
|
||||
/** @ingroup iface_org_kde_kwin_idle_timeout */
|
||||
static inline void *
|
||||
org_kde_kwin_idle_timeout_get_user_data(struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout)
|
||||
{
|
||||
return wl_proxy_get_user_data((struct wl_proxy *) org_kde_kwin_idle_timeout);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
org_kde_kwin_idle_timeout_get_version(struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout)
|
||||
{
|
||||
return wl_proxy_get_version((struct wl_proxy *) org_kde_kwin_idle_timeout);
|
||||
}
|
||||
|
||||
/** @ingroup iface_org_kde_kwin_idle_timeout */
|
||||
static inline void
|
||||
org_kde_kwin_idle_timeout_destroy(struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout)
|
||||
{
|
||||
wl_proxy_destroy((struct wl_proxy *) org_kde_kwin_idle_timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_org_kde_kwin_idle_timeout
|
||||
*/
|
||||
static inline void
|
||||
org_kde_kwin_idle_timeout_release(struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) org_kde_kwin_idle_timeout,
|
||||
ORG_KDE_KWIN_IDLE_TIMEOUT_RELEASE);
|
||||
|
||||
wl_proxy_destroy((struct wl_proxy *) org_kde_kwin_idle_timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_org_kde_kwin_idle_timeout
|
||||
*/
|
||||
static inline void
|
||||
org_kde_kwin_idle_timeout_simulate_user_activity(struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) org_kde_kwin_idle_timeout,
|
||||
ORG_KDE_KWIN_IDLE_TIMEOUT_SIMULATE_USER_ACTIVITY);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
68
src/wayland/protocols/idle.h
Normal file
@ -0,0 +1,68 @@
|
||||
/* Generated by wayland-scanner 1.18.0 */
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Martin Gräßlin
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "wayland-util.h"
|
||||
|
||||
#ifndef __has_attribute
|
||||
# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
|
||||
#endif
|
||||
|
||||
#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
|
||||
#define WL_PRIVATE __attribute__ ((visibility("hidden")))
|
||||
#else
|
||||
#define WL_PRIVATE
|
||||
#endif
|
||||
|
||||
extern const struct wl_interface org_kde_kwin_idle_timeout_interface;
|
||||
extern const struct wl_interface wl_seat_interface;
|
||||
|
||||
static const struct wl_interface *idle_types[] = {
|
||||
&org_kde_kwin_idle_timeout_interface,
|
||||
&wl_seat_interface,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct wl_message org_kde_kwin_idle_requests[] = {
|
||||
{ "get_idle_timeout", "nou", idle_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface org_kde_kwin_idle_interface = {
|
||||
"org_kde_kwin_idle", 1,
|
||||
1, org_kde_kwin_idle_requests,
|
||||
0, NULL,
|
||||
};
|
||||
|
||||
static const struct wl_message org_kde_kwin_idle_timeout_requests[] = {
|
||||
{ "release", "", idle_types + 0 },
|
||||
{ "simulate_user_activity", "", idle_types + 0 },
|
||||
};
|
||||
|
||||
static const struct wl_message org_kde_kwin_idle_timeout_events[] = {
|
||||
{ "idle", "", idle_types + 0 },
|
||||
{ "resumed", "", idle_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface org_kde_kwin_idle_timeout_interface = {
|
||||
"org_kde_kwin_idle_timeout", 1,
|
||||
2, org_kde_kwin_idle_timeout_requests,
|
||||
2, org_kde_kwin_idle_timeout_events,
|
||||
};
|
||||
|
||||
49
src/wayland/protocols/idle.xml
Normal file
@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="idle">
|
||||
<copyright><![CDATA[
|
||||
Copyright (C) 2015 Martin Gräßlin
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2.1 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 Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
]]></copyright>
|
||||
<interface name="org_kde_kwin_idle" version="1">
|
||||
<description summary="User idle time manager">
|
||||
This interface allows to monitor user idle time on a given seat. The interface
|
||||
allows to register timers which trigger after no user activity was registered
|
||||
on the seat for a given interval. It notifies when user activity resumes.
|
||||
|
||||
This is useful for applications wanting to perform actions when the user is not
|
||||
interacting with the system, e.g. chat applications setting the user as away, power
|
||||
management features to dim screen, etc..
|
||||
</description>
|
||||
<request name="get_idle_timeout">
|
||||
<arg name="id" type="new_id" interface="org_kde_kwin_idle_timeout"/>
|
||||
<arg name="seat" type="object" interface="wl_seat"/>
|
||||
<arg name="timeout" type="uint" summary="The idle timeout in msec"/>
|
||||
</request>
|
||||
</interface>
|
||||
<interface name="org_kde_kwin_idle_timeout" version="1">
|
||||
<request name="release" type="destructor">
|
||||
<description summary="release the timeout object"/>
|
||||
</request>
|
||||
<request name="simulate_user_activity">
|
||||
<description summary="Simulates user activity for this timeout, behaves just like real user activity on the seat"/>
|
||||
</request>
|
||||
<event name="idle">
|
||||
<description summary="Triggered when there has not been any user activity in the requested idle time interval"/>
|
||||
</event>
|
||||
<event name="resumed">
|
||||
<description summary="Triggered on the first user activity after an idle event"/>
|
||||
</event>
|
||||
</interface>
|
||||
</protocol>
|
||||
@ -0,0 +1,611 @@
|
||||
/* Generated by wayland-scanner 1.18.0 */
|
||||
|
||||
#ifndef WLR_FOREIGN_TOPLEVEL_MANAGEMENT_UNSTABLE_V1_CLIENT_PROTOCOL_H
|
||||
#define WLR_FOREIGN_TOPLEVEL_MANAGEMENT_UNSTABLE_V1_CLIENT_PROTOCOL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "wayland-client.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @page page_wlr_foreign_toplevel_management_unstable_v1 The wlr_foreign_toplevel_management_unstable_v1 protocol
|
||||
* @section page_ifaces_wlr_foreign_toplevel_management_unstable_v1 Interfaces
|
||||
* - @subpage page_iface_zwlr_foreign_toplevel_manager_v1 - list and control opened apps
|
||||
* - @subpage page_iface_zwlr_foreign_toplevel_handle_v1 - an opened toplevel
|
||||
* @section page_copyright_wlr_foreign_toplevel_management_unstable_v1 Copyright
|
||||
* <pre>
|
||||
*
|
||||
* Copyright © 2018 Ilia Bozhinov
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this
|
||||
* software and its documentation for any purpose is hereby granted
|
||||
* without fee, provided that the above copyright notice appear in
|
||||
* all copies and that both that copyright notice and this permission
|
||||
* notice appear in supporting documentation, and that the name of
|
||||
* the copyright holders not be used in advertising or publicity
|
||||
* pertaining to distribution of the software without specific,
|
||||
* written prior permission. The copyright holders make no
|
||||
* representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied
|
||||
* warranty.
|
||||
*
|
||||
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
* THIS SOFTWARE.
|
||||
* </pre>
|
||||
*/
|
||||
struct wl_output;
|
||||
struct wl_seat;
|
||||
struct wl_surface;
|
||||
struct zwlr_foreign_toplevel_handle_v1;
|
||||
struct zwlr_foreign_toplevel_manager_v1;
|
||||
|
||||
/**
|
||||
* @page page_iface_zwlr_foreign_toplevel_manager_v1 zwlr_foreign_toplevel_manager_v1
|
||||
* @section page_iface_zwlr_foreign_toplevel_manager_v1_desc Description
|
||||
*
|
||||
* The purpose of this protocol is to enable the creation of taskbars
|
||||
* and docks by providing them with a list of opened applications and
|
||||
* letting them request certain actions on them, like maximizing, etc.
|
||||
*
|
||||
* After a client binds the zwlr_foreign_toplevel_manager_v1, each opened
|
||||
* toplevel window will be sent via the toplevel event
|
||||
* @section page_iface_zwlr_foreign_toplevel_manager_v1_api API
|
||||
* See @ref iface_zwlr_foreign_toplevel_manager_v1.
|
||||
*/
|
||||
/**
|
||||
* @defgroup iface_zwlr_foreign_toplevel_manager_v1 The zwlr_foreign_toplevel_manager_v1 interface
|
||||
*
|
||||
* The purpose of this protocol is to enable the creation of taskbars
|
||||
* and docks by providing them with a list of opened applications and
|
||||
* letting them request certain actions on them, like maximizing, etc.
|
||||
*
|
||||
* After a client binds the zwlr_foreign_toplevel_manager_v1, each opened
|
||||
* toplevel window will be sent via the toplevel event
|
||||
*/
|
||||
extern const struct wl_interface zwlr_foreign_toplevel_manager_v1_interface;
|
||||
/**
|
||||
* @page page_iface_zwlr_foreign_toplevel_handle_v1 zwlr_foreign_toplevel_handle_v1
|
||||
* @section page_iface_zwlr_foreign_toplevel_handle_v1_desc Description
|
||||
*
|
||||
* A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel
|
||||
* window. Each app may have multiple opened toplevels.
|
||||
*
|
||||
* Each toplevel has a list of outputs it is visible on, conveyed to the
|
||||
* client with the output_enter and output_leave events.
|
||||
* @section page_iface_zwlr_foreign_toplevel_handle_v1_api API
|
||||
* See @ref iface_zwlr_foreign_toplevel_handle_v1.
|
||||
*/
|
||||
/**
|
||||
* @defgroup iface_zwlr_foreign_toplevel_handle_v1 The zwlr_foreign_toplevel_handle_v1 interface
|
||||
*
|
||||
* A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel
|
||||
* window. Each app may have multiple opened toplevels.
|
||||
*
|
||||
* Each toplevel has a list of outputs it is visible on, conveyed to the
|
||||
* client with the output_enter and output_leave events.
|
||||
*/
|
||||
extern const struct wl_interface zwlr_foreign_toplevel_handle_v1_interface;
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_manager_v1
|
||||
* @struct zwlr_foreign_toplevel_manager_v1_listener
|
||||
*/
|
||||
struct zwlr_foreign_toplevel_manager_v1_listener {
|
||||
/**
|
||||
* a toplevel has been created
|
||||
*
|
||||
* This event is emitted whenever a new toplevel window is
|
||||
* created. It is emitted for all toplevels, regardless of the app
|
||||
* that has created them.
|
||||
*
|
||||
* All initial details of the toplevel(title, app_id, states, etc.)
|
||||
* will be sent immediately after this event via the corresponding
|
||||
* events in zwlr_foreign_toplevel_handle_v1.
|
||||
*/
|
||||
void (*toplevel)(void *data,
|
||||
struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *toplevel);
|
||||
/**
|
||||
* the compositor has finished with the toplevel manager
|
||||
*
|
||||
* This event indicates that the compositor is done sending
|
||||
* events to the zwlr_foreign_toplevel_manager_v1. The server will
|
||||
* destroy the object immediately after sending this request, so it
|
||||
* will become invalid and the client should free any resources
|
||||
* associated with it.
|
||||
*/
|
||||
void (*finished)(void *data,
|
||||
struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_manager_v1
|
||||
*/
|
||||
static inline int
|
||||
zwlr_foreign_toplevel_manager_v1_add_listener(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1,
|
||||
const struct zwlr_foreign_toplevel_manager_v1_listener *listener, void *data)
|
||||
{
|
||||
return wl_proxy_add_listener((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1,
|
||||
(void (**)(void)) listener, data);
|
||||
}
|
||||
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_STOP 0
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_manager_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_TOPLEVEL_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_manager_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_FINISHED_SINCE_VERSION 1
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_manager_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_STOP_SINCE_VERSION 1
|
||||
|
||||
/** @ingroup iface_zwlr_foreign_toplevel_manager_v1 */
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_manager_v1_set_user_data(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1, void *user_data)
|
||||
{
|
||||
wl_proxy_set_user_data((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1, user_data);
|
||||
}
|
||||
|
||||
/** @ingroup iface_zwlr_foreign_toplevel_manager_v1 */
|
||||
static inline void *
|
||||
zwlr_foreign_toplevel_manager_v1_get_user_data(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1)
|
||||
{
|
||||
return wl_proxy_get_user_data((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
zwlr_foreign_toplevel_manager_v1_get_version(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1)
|
||||
{
|
||||
return wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1);
|
||||
}
|
||||
|
||||
/** @ingroup iface_zwlr_foreign_toplevel_manager_v1 */
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_manager_v1_destroy(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1)
|
||||
{
|
||||
wl_proxy_destroy((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_manager_v1
|
||||
*
|
||||
* Indicates the client no longer wishes to receive events for new toplevels.
|
||||
* However the compositor may emit further toplevel_created events, until
|
||||
* the finished event is emitted.
|
||||
*
|
||||
* The client must not send any more requests after this one.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_manager_v1_stop(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1,
|
||||
ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_STOP);
|
||||
}
|
||||
|
||||
#ifndef ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ENUM
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ENUM
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
* types of states on the toplevel
|
||||
*
|
||||
* The different states that a toplevel can have. These have the same meaning
|
||||
* as the states with the same names defined in xdg-toplevel
|
||||
*/
|
||||
enum zwlr_foreign_toplevel_handle_v1_state {
|
||||
/**
|
||||
* the toplevel is maximized
|
||||
*/
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED = 0,
|
||||
/**
|
||||
* the toplevel is minimized
|
||||
*/
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED = 1,
|
||||
/**
|
||||
* the toplevel is active
|
||||
*/
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED = 2,
|
||||
/**
|
||||
* the toplevel is fullscreen
|
||||
* @since 2
|
||||
*/
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN = 3,
|
||||
};
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN_SINCE_VERSION 2
|
||||
#endif /* ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ENUM */
|
||||
|
||||
#ifndef ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_ENUM
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_ENUM
|
||||
enum zwlr_foreign_toplevel_handle_v1_error {
|
||||
/**
|
||||
* the provided rectangle is invalid
|
||||
*/
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_INVALID_RECTANGLE = 0,
|
||||
};
|
||||
#endif /* ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_ENUM */
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
* @struct zwlr_foreign_toplevel_handle_v1_listener
|
||||
*/
|
||||
struct zwlr_foreign_toplevel_handle_v1_listener {
|
||||
/**
|
||||
* title change
|
||||
*
|
||||
* This event is emitted whenever the title of the toplevel
|
||||
* changes.
|
||||
*/
|
||||
void (*title)(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
|
||||
const char *title);
|
||||
/**
|
||||
* app-id change
|
||||
*
|
||||
* This event is emitted whenever the app-id of the toplevel
|
||||
* changes.
|
||||
*/
|
||||
void (*app_id)(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
|
||||
const char *app_id);
|
||||
/**
|
||||
* toplevel entered an output
|
||||
*
|
||||
* This event is emitted whenever the toplevel becomes visible on
|
||||
* the given output. A toplevel may be visible on multiple outputs.
|
||||
*/
|
||||
void (*output_enter)(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
|
||||
struct wl_output *output);
|
||||
/**
|
||||
* toplevel left an output
|
||||
*
|
||||
* This event is emitted whenever the toplevel stops being
|
||||
* visible on the given output. It is guaranteed that an
|
||||
* entered-output event with the same output has been emitted
|
||||
* before this event.
|
||||
*/
|
||||
void (*output_leave)(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
|
||||
struct wl_output *output);
|
||||
/**
|
||||
* the toplevel state changed
|
||||
*
|
||||
* This event is emitted immediately after the
|
||||
* zlw_foreign_toplevel_handle_v1 is created and each time the
|
||||
* toplevel state changes, either because of a compositor action or
|
||||
* because of a request in this protocol.
|
||||
*/
|
||||
void (*state)(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
|
||||
struct wl_array *state);
|
||||
/**
|
||||
* all information about the toplevel has been sent
|
||||
*
|
||||
* This event is sent after all changes in the toplevel state
|
||||
* have been sent.
|
||||
*
|
||||
* This allows changes to the zwlr_foreign_toplevel_handle_v1
|
||||
* properties to be seen as atomic, even if they happen via
|
||||
* multiple events.
|
||||
*/
|
||||
void (*done)(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1);
|
||||
/**
|
||||
* this toplevel has been destroyed
|
||||
*
|
||||
* This event means the toplevel has been destroyed. It is
|
||||
* guaranteed there won't be any more events for this
|
||||
* zwlr_foreign_toplevel_handle_v1. The toplevel itself becomes
|
||||
* inert so any requests will be ignored except the destroy
|
||||
* request.
|
||||
*/
|
||||
void (*closed)(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1);
|
||||
/**
|
||||
* parent change
|
||||
*
|
||||
* This event is emitted whenever the parent of the toplevel
|
||||
* changes.
|
||||
*
|
||||
* No event is emitted when the parent handle is destroyed by the
|
||||
* client.
|
||||
* @since 3
|
||||
*/
|
||||
void (*parent)(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *parent);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
static inline int
|
||||
zwlr_foreign_toplevel_handle_v1_add_listener(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
|
||||
const struct zwlr_foreign_toplevel_handle_v1_listener *listener, void *data)
|
||||
{
|
||||
return wl_proxy_add_listener((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
|
||||
(void (**)(void)) listener, data);
|
||||
}
|
||||
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MAXIMIZED 0
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MAXIMIZED 1
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MINIMIZED 2
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MINIMIZED 3
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ACTIVATE 4
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSE 5
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_RECTANGLE 6
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_DESTROY 7
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_FULLSCREEN 8
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_FULLSCREEN 9
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_TITLE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_APP_ID_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_OUTPUT_ENTER_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_OUTPUT_LEAVE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_DONE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSED_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_PARENT_SINCE_VERSION 3
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MAXIMIZED_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MAXIMIZED_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MINIMIZED_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MINIMIZED_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ACTIVATE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_RECTANGLE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_DESTROY_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_FULLSCREEN_SINCE_VERSION 2
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_FULLSCREEN_SINCE_VERSION 2
|
||||
|
||||
/** @ingroup iface_zwlr_foreign_toplevel_handle_v1 */
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_handle_v1_set_user_data(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, void *user_data)
|
||||
{
|
||||
wl_proxy_set_user_data((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, user_data);
|
||||
}
|
||||
|
||||
/** @ingroup iface_zwlr_foreign_toplevel_handle_v1 */
|
||||
static inline void *
|
||||
zwlr_foreign_toplevel_handle_v1_get_user_data(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
|
||||
{
|
||||
return wl_proxy_get_user_data((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
zwlr_foreign_toplevel_handle_v1_get_version(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
|
||||
{
|
||||
return wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*
|
||||
* Requests that the toplevel be maximized. If the maximized state actually
|
||||
* changes, this will be indicated by the state event.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_handle_v1_set_maximized(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MAXIMIZED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*
|
||||
* Requests that the toplevel be unmaximized. If the maximized state actually
|
||||
* changes, this will be indicated by the state event.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_handle_v1_unset_maximized(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MAXIMIZED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*
|
||||
* Requests that the toplevel be minimized. If the minimized state actually
|
||||
* changes, this will be indicated by the state event.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_handle_v1_set_minimized(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MINIMIZED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*
|
||||
* Requests that the toplevel be unminimized. If the minimized state actually
|
||||
* changes, this will be indicated by the state event.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_handle_v1_unset_minimized(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MINIMIZED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*
|
||||
* Request that this toplevel be activated on the given seat.
|
||||
* There is no guarantee the toplevel will be actually activated.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_handle_v1_activate(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, struct wl_seat *seat)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ACTIVATE, seat);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*
|
||||
* Send a request to the toplevel to close itself. The compositor would
|
||||
* typically use a shell-specific method to carry out this request, for
|
||||
* example by sending the xdg_toplevel.close event. However, this gives
|
||||
* no guarantees the toplevel will actually be destroyed. If and when
|
||||
* this happens, the zwlr_foreign_toplevel_handle_v1.closed event will
|
||||
* be emitted.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_handle_v1_close(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*
|
||||
* The rectangle of the surface specified in this request corresponds to
|
||||
* the place where the app using this protocol represents the given toplevel.
|
||||
* It can be used by the compositor as a hint for some operations, e.g
|
||||
* minimizing. The client is however not required to set this, in which
|
||||
* case the compositor is free to decide some default value.
|
||||
*
|
||||
* If the client specifies more than one rectangle, only the last one is
|
||||
* considered.
|
||||
*
|
||||
* The dimensions are given in surface-local coordinates.
|
||||
* Setting width=height=0 removes the already-set rectangle.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_handle_v1_set_rectangle(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, struct wl_surface *surface, int32_t x, int32_t y, int32_t width, int32_t height)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_RECTANGLE, surface, x, y, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*
|
||||
* Destroys the zwlr_foreign_toplevel_handle_v1 object.
|
||||
*
|
||||
* This request should be called either when the client does not want to
|
||||
* use the toplevel anymore or after the closed event to finalize the
|
||||
* destruction of the object.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_handle_v1_destroy(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_DESTROY);
|
||||
|
||||
wl_proxy_destroy((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*
|
||||
* Requests that the toplevel be fullscreened on the given output. If the
|
||||
* fullscreen state and/or the outputs the toplevel is visible on actually
|
||||
* change, this will be indicated by the state and output_enter/leave
|
||||
* events.
|
||||
*
|
||||
* The output parameter is only a hint to the compositor. Also, if output
|
||||
* is NULL, the compositor should decide which output the toplevel will be
|
||||
* fullscreened on, if at all.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_handle_v1_set_fullscreen(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, struct wl_output *output)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_FULLSCREEN, output);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*
|
||||
* Requests that the toplevel be unfullscreened. If the fullscreen state
|
||||
* actually changes, this will be indicated by the state event.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_handle_v1_unset_fullscreen(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_FULLSCREEN);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,106 @@
|
||||
/* Generated by wayland-scanner 1.18.0 */
|
||||
|
||||
/*
|
||||
* Copyright © 2018 Ilia Bozhinov
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this
|
||||
* software and its documentation for any purpose is hereby granted
|
||||
* without fee, provided that the above copyright notice appear in
|
||||
* all copies and that both that copyright notice and this permission
|
||||
* notice appear in supporting documentation, and that the name of
|
||||
* the copyright holders not be used in advertising or publicity
|
||||
* pertaining to distribution of the software without specific,
|
||||
* written prior permission. The copyright holders make no
|
||||
* representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied
|
||||
* warranty.
|
||||
*
|
||||
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
* THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "wayland-util.h"
|
||||
|
||||
#ifndef __has_attribute
|
||||
# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
|
||||
#endif
|
||||
|
||||
#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
|
||||
#define WL_PRIVATE __attribute__ ((visibility("hidden")))
|
||||
#else
|
||||
#define WL_PRIVATE
|
||||
#endif
|
||||
|
||||
extern const struct wl_interface wl_output_interface;
|
||||
extern const struct wl_interface wl_seat_interface;
|
||||
extern const struct wl_interface wl_surface_interface;
|
||||
extern const struct wl_interface zwlr_foreign_toplevel_handle_v1_interface;
|
||||
|
||||
static const struct wl_interface *wlr_foreign_toplevel_management_unstable_v1_types[] = {
|
||||
NULL,
|
||||
&zwlr_foreign_toplevel_handle_v1_interface,
|
||||
&wl_seat_interface,
|
||||
&wl_surface_interface,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&wl_output_interface,
|
||||
&wl_output_interface,
|
||||
&wl_output_interface,
|
||||
&zwlr_foreign_toplevel_handle_v1_interface,
|
||||
};
|
||||
|
||||
static const struct wl_message zwlr_foreign_toplevel_manager_v1_requests[] = {
|
||||
{ "stop", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
};
|
||||
|
||||
static const struct wl_message zwlr_foreign_toplevel_manager_v1_events[] = {
|
||||
{ "toplevel", "n", wlr_foreign_toplevel_management_unstable_v1_types + 1 },
|
||||
{ "finished", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface zwlr_foreign_toplevel_manager_v1_interface = {
|
||||
"zwlr_foreign_toplevel_manager_v1", 3,
|
||||
1, zwlr_foreign_toplevel_manager_v1_requests,
|
||||
2, zwlr_foreign_toplevel_manager_v1_events,
|
||||
};
|
||||
|
||||
static const struct wl_message zwlr_foreign_toplevel_handle_v1_requests[] = {
|
||||
{ "set_maximized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
{ "unset_maximized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
{ "set_minimized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
{ "unset_minimized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
{ "activate", "o", wlr_foreign_toplevel_management_unstable_v1_types + 2 },
|
||||
{ "close", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
{ "set_rectangle", "oiiii", wlr_foreign_toplevel_management_unstable_v1_types + 3 },
|
||||
{ "destroy", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
{ "set_fullscreen", "2?o", wlr_foreign_toplevel_management_unstable_v1_types + 8 },
|
||||
{ "unset_fullscreen", "2", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
};
|
||||
|
||||
static const struct wl_message zwlr_foreign_toplevel_handle_v1_events[] = {
|
||||
{ "title", "s", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
{ "app_id", "s", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
{ "output_enter", "o", wlr_foreign_toplevel_management_unstable_v1_types + 9 },
|
||||
{ "output_leave", "o", wlr_foreign_toplevel_management_unstable_v1_types + 10 },
|
||||
{ "state", "a", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
{ "done", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
{ "closed", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
{ "parent", "3?o", wlr_foreign_toplevel_management_unstable_v1_types + 11 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface zwlr_foreign_toplevel_handle_v1_interface = {
|
||||
"zwlr_foreign_toplevel_handle_v1", 3,
|
||||
10, zwlr_foreign_toplevel_handle_v1_requests,
|
||||
8, zwlr_foreign_toplevel_handle_v1_events,
|
||||
};
|
||||
|
||||
@ -0,0 +1,270 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="wlr_foreign_toplevel_management_unstable_v1">
|
||||
<copyright>
|
||||
Copyright © 2018 Ilia Bozhinov
|
||||
|
||||
Permission to use, copy, modify, distribute, and sell this
|
||||
software and its documentation for any purpose is hereby granted
|
||||
without fee, provided that the above copyright notice appear in
|
||||
all copies and that both that copyright notice and this permission
|
||||
notice appear in supporting documentation, and that the name of
|
||||
the copyright holders not be used in advertising or publicity
|
||||
pertaining to distribution of the software without specific,
|
||||
written prior permission. The copyright holders make no
|
||||
representations about the suitability of this software for any
|
||||
purpose. It is provided "as is" without express or implied
|
||||
warranty.
|
||||
|
||||
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
THIS SOFTWARE.
|
||||
</copyright>
|
||||
|
||||
<interface name="zwlr_foreign_toplevel_manager_v1" version="3">
|
||||
<description summary="list and control opened apps">
|
||||
The purpose of this protocol is to enable the creation of taskbars
|
||||
and docks by providing them with a list of opened applications and
|
||||
letting them request certain actions on them, like maximizing, etc.
|
||||
|
||||
After a client binds the zwlr_foreign_toplevel_manager_v1, each opened
|
||||
toplevel window will be sent via the toplevel event
|
||||
</description>
|
||||
|
||||
<event name="toplevel">
|
||||
<description summary="a toplevel has been created">
|
||||
This event is emitted whenever a new toplevel window is created. It
|
||||
is emitted for all toplevels, regardless of the app that has created
|
||||
them.
|
||||
|
||||
All initial details of the toplevel(title, app_id, states, etc.) will
|
||||
be sent immediately after this event via the corresponding events in
|
||||
zwlr_foreign_toplevel_handle_v1.
|
||||
</description>
|
||||
<arg name="toplevel" type="new_id" interface="zwlr_foreign_toplevel_handle_v1"/>
|
||||
</event>
|
||||
|
||||
<request name="stop">
|
||||
<description summary="stop sending events">
|
||||
Indicates the client no longer wishes to receive events for new toplevels.
|
||||
However the compositor may emit further toplevel_created events, until
|
||||
the finished event is emitted.
|
||||
|
||||
The client must not send any more requests after this one.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<event name="finished">
|
||||
<description summary="the compositor has finished with the toplevel manager">
|
||||
This event indicates that the compositor is done sending events to the
|
||||
zwlr_foreign_toplevel_manager_v1. The server will destroy the object
|
||||
immediately after sending this request, so it will become invalid and
|
||||
the client should free any resources associated with it.
|
||||
</description>
|
||||
</event>
|
||||
</interface>
|
||||
|
||||
<interface name="zwlr_foreign_toplevel_handle_v1" version="3">
|
||||
<description summary="an opened toplevel">
|
||||
A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel
|
||||
window. Each app may have multiple opened toplevels.
|
||||
|
||||
Each toplevel has a list of outputs it is visible on, conveyed to the
|
||||
client with the output_enter and output_leave events.
|
||||
</description>
|
||||
|
||||
<event name="title">
|
||||
<description summary="title change">
|
||||
This event is emitted whenever the title of the toplevel changes.
|
||||
</description>
|
||||
<arg name="title" type="string"/>
|
||||
</event>
|
||||
|
||||
<event name="app_id">
|
||||
<description summary="app-id change">
|
||||
This event is emitted whenever the app-id of the toplevel changes.
|
||||
</description>
|
||||
<arg name="app_id" type="string"/>
|
||||
</event>
|
||||
|
||||
<event name="output_enter">
|
||||
<description summary="toplevel entered an output">
|
||||
This event is emitted whenever the toplevel becomes visible on
|
||||
the given output. A toplevel may be visible on multiple outputs.
|
||||
</description>
|
||||
<arg name="output" type="object" interface="wl_output"/>
|
||||
</event>
|
||||
|
||||
<event name="output_leave">
|
||||
<description summary="toplevel left an output">
|
||||
This event is emitted whenever the toplevel stops being visible on
|
||||
the given output. It is guaranteed that an entered-output event
|
||||
with the same output has been emitted before this event.
|
||||
</description>
|
||||
<arg name="output" type="object" interface="wl_output"/>
|
||||
</event>
|
||||
|
||||
<request name="set_maximized">
|
||||
<description summary="requests that the toplevel be maximized">
|
||||
Requests that the toplevel be maximized. If the maximized state actually
|
||||
changes, this will be indicated by the state event.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="unset_maximized">
|
||||
<description summary="requests that the toplevel be unmaximized">
|
||||
Requests that the toplevel be unmaximized. If the maximized state actually
|
||||
changes, this will be indicated by the state event.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="set_minimized">
|
||||
<description summary="requests that the toplevel be minimized">
|
||||
Requests that the toplevel be minimized. If the minimized state actually
|
||||
changes, this will be indicated by the state event.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="unset_minimized">
|
||||
<description summary="requests that the toplevel be unminimized">
|
||||
Requests that the toplevel be unminimized. If the minimized state actually
|
||||
changes, this will be indicated by the state event.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="activate">
|
||||
<description summary="activate the toplevel">
|
||||
Request that this toplevel be activated on the given seat.
|
||||
There is no guarantee the toplevel will be actually activated.
|
||||
</description>
|
||||
<arg name="seat" type="object" interface="wl_seat"/>
|
||||
</request>
|
||||
|
||||
<enum name="state">
|
||||
<description summary="types of states on the toplevel">
|
||||
The different states that a toplevel can have. These have the same meaning
|
||||
as the states with the same names defined in xdg-toplevel
|
||||
</description>
|
||||
|
||||
<entry name="maximized" value="0" summary="the toplevel is maximized"/>
|
||||
<entry name="minimized" value="1" summary="the toplevel is minimized"/>
|
||||
<entry name="activated" value="2" summary="the toplevel is active"/>
|
||||
<entry name="fullscreen" value="3" summary="the toplevel is fullscreen" since="2"/>
|
||||
</enum>
|
||||
|
||||
<event name="state">
|
||||
<description summary="the toplevel state changed">
|
||||
This event is emitted immediately after the zlw_foreign_toplevel_handle_v1
|
||||
is created and each time the toplevel state changes, either because of a
|
||||
compositor action or because of a request in this protocol.
|
||||
</description>
|
||||
|
||||
<arg name="state" type="array"/>
|
||||
</event>
|
||||
|
||||
<event name="done">
|
||||
<description summary="all information about the toplevel has been sent">
|
||||
This event is sent after all changes in the toplevel state have been
|
||||
sent.
|
||||
|
||||
This allows changes to the zwlr_foreign_toplevel_handle_v1 properties
|
||||
to be seen as atomic, even if they happen via multiple events.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<request name="close">
|
||||
<description summary="request that the toplevel be closed">
|
||||
Send a request to the toplevel to close itself. The compositor would
|
||||
typically use a shell-specific method to carry out this request, for
|
||||
example by sending the xdg_toplevel.close event. However, this gives
|
||||
no guarantees the toplevel will actually be destroyed. If and when
|
||||
this happens, the zwlr_foreign_toplevel_handle_v1.closed event will
|
||||
be emitted.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="set_rectangle">
|
||||
<description summary="the rectangle which represents the toplevel">
|
||||
The rectangle of the surface specified in this request corresponds to
|
||||
the place where the app using this protocol represents the given toplevel.
|
||||
It can be used by the compositor as a hint for some operations, e.g
|
||||
minimizing. The client is however not required to set this, in which
|
||||
case the compositor is free to decide some default value.
|
||||
|
||||
If the client specifies more than one rectangle, only the last one is
|
||||
considered.
|
||||
|
||||
The dimensions are given in surface-local coordinates.
|
||||
Setting width=height=0 removes the already-set rectangle.
|
||||
</description>
|
||||
|
||||
<arg name="surface" type="object" interface="wl_surface"/>
|
||||
<arg name="x" type="int"/>
|
||||
<arg name="y" type="int"/>
|
||||
<arg name="width" type="int"/>
|
||||
<arg name="height" type="int"/>
|
||||
</request>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="invalid_rectangle" value="0"
|
||||
summary="the provided rectangle is invalid"/>
|
||||
</enum>
|
||||
|
||||
<event name="closed">
|
||||
<description summary="this toplevel has been destroyed">
|
||||
This event means the toplevel has been destroyed. It is guaranteed there
|
||||
won't be any more events for this zwlr_foreign_toplevel_handle_v1. The
|
||||
toplevel itself becomes inert so any requests will be ignored except the
|
||||
destroy request.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy the zwlr_foreign_toplevel_handle_v1 object">
|
||||
Destroys the zwlr_foreign_toplevel_handle_v1 object.
|
||||
|
||||
This request should be called either when the client does not want to
|
||||
use the toplevel anymore or after the closed event to finalize the
|
||||
destruction of the object.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<!-- Version 2 additions -->
|
||||
|
||||
<request name="set_fullscreen" since="2">
|
||||
<description summary="request that the toplevel be fullscreened">
|
||||
Requests that the toplevel be fullscreened on the given output. If the
|
||||
fullscreen state and/or the outputs the toplevel is visible on actually
|
||||
change, this will be indicated by the state and output_enter/leave
|
||||
events.
|
||||
|
||||
The output parameter is only a hint to the compositor. Also, if output
|
||||
is NULL, the compositor should decide which output the toplevel will be
|
||||
fullscreened on, if at all.
|
||||
</description>
|
||||
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
|
||||
</request>
|
||||
|
||||
<request name="unset_fullscreen" since="2">
|
||||
<description summary="request that the toplevel be unfullscreened">
|
||||
Requests that the toplevel be unfullscreened. If the fullscreen state
|
||||
actually changes, this will be indicated by the state event.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<!-- Version 3 additions -->
|
||||
|
||||
<event name="parent" since="3">
|
||||
<description summary="parent change">
|
||||
This event is emitted whenever the parent of the toplevel changes.
|
||||
|
||||
No event is emitted when the parent handle is destroyed by the client.
|
||||
</description>
|
||||
<arg name="parent" type="object" interface="zwlr_foreign_toplevel_handle_v1" allow-null="true"/>
|
||||
</event>
|
||||
</interface>
|
||||
</protocol>
|
||||
@ -0,0 +1,559 @@
|
||||
/* Generated by wayland-scanner 1.18.0 */
|
||||
|
||||
#ifndef WLR_LAYER_SHELL_UNSTABLE_V1_CLIENT_PROTOCOL_H
|
||||
#define WLR_LAYER_SHELL_UNSTABLE_V1_CLIENT_PROTOCOL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "wayland-client.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @page page_wlr_layer_shell_unstable_v1 The wlr_layer_shell_unstable_v1 protocol
|
||||
* @section page_ifaces_wlr_layer_shell_unstable_v1 Interfaces
|
||||
* - @subpage page_iface_zwlr_layer_shell_v1 - create surfaces that are layers of the desktop
|
||||
* - @subpage page_iface_zwlr_layer_surface_v1 - layer metadata interface
|
||||
* @section page_copyright_wlr_layer_shell_unstable_v1 Copyright
|
||||
* <pre>
|
||||
*
|
||||
* Copyright © 2017 Drew DeVault
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this
|
||||
* software and its documentation for any purpose is hereby granted
|
||||
* without fee, provided that the above copyright notice appear in
|
||||
* all copies and that both that copyright notice and this permission
|
||||
* notice appear in supporting documentation, and that the name of
|
||||
* the copyright holders not be used in advertising or publicity
|
||||
* pertaining to distribution of the software without specific,
|
||||
* written prior permission. The copyright holders make no
|
||||
* representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied
|
||||
* warranty.
|
||||
*
|
||||
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
* THIS SOFTWARE.
|
||||
* </pre>
|
||||
*/
|
||||
struct wl_output;
|
||||
struct wl_surface;
|
||||
struct xdg_popup;
|
||||
struct zwlr_layer_shell_v1;
|
||||
struct zwlr_layer_surface_v1;
|
||||
|
||||
/**
|
||||
* @page page_iface_zwlr_layer_shell_v1 zwlr_layer_shell_v1
|
||||
* @section page_iface_zwlr_layer_shell_v1_desc Description
|
||||
*
|
||||
* Clients can use this interface to assign the surface_layer role to
|
||||
* wl_surfaces. Such surfaces are assigned to a "layer" of the output and
|
||||
* rendered with a defined z-depth respective to each other. They may also be
|
||||
* anchored to the edges and corners of a screen and specify input handling
|
||||
* semantics. This interface should be suitable for the implementation of
|
||||
* many desktop shell components, and a broad number of other applications
|
||||
* that interact with the desktop.
|
||||
* @section page_iface_zwlr_layer_shell_v1_api API
|
||||
* See @ref iface_zwlr_layer_shell_v1.
|
||||
*/
|
||||
/**
|
||||
* @defgroup iface_zwlr_layer_shell_v1 The zwlr_layer_shell_v1 interface
|
||||
*
|
||||
* Clients can use this interface to assign the surface_layer role to
|
||||
* wl_surfaces. Such surfaces are assigned to a "layer" of the output and
|
||||
* rendered with a defined z-depth respective to each other. They may also be
|
||||
* anchored to the edges and corners of a screen and specify input handling
|
||||
* semantics. This interface should be suitable for the implementation of
|
||||
* many desktop shell components, and a broad number of other applications
|
||||
* that interact with the desktop.
|
||||
*/
|
||||
extern const struct wl_interface zwlr_layer_shell_v1_interface;
|
||||
/**
|
||||
* @page page_iface_zwlr_layer_surface_v1 zwlr_layer_surface_v1
|
||||
* @section page_iface_zwlr_layer_surface_v1_desc Description
|
||||
*
|
||||
* An interface that may be implemented by a wl_surface, for surfaces that
|
||||
* are designed to be rendered as a layer of a stacked desktop-like
|
||||
* environment.
|
||||
*
|
||||
* Layer surface state (size, anchor, exclusive zone, margin, interactivity)
|
||||
* is double-buffered, and will be applied at the time wl_surface.commit of
|
||||
* the corresponding wl_surface is called.
|
||||
* @section page_iface_zwlr_layer_surface_v1_api API
|
||||
* See @ref iface_zwlr_layer_surface_v1.
|
||||
*/
|
||||
/**
|
||||
* @defgroup iface_zwlr_layer_surface_v1 The zwlr_layer_surface_v1 interface
|
||||
*
|
||||
* An interface that may be implemented by a wl_surface, for surfaces that
|
||||
* are designed to be rendered as a layer of a stacked desktop-like
|
||||
* environment.
|
||||
*
|
||||
* Layer surface state (size, anchor, exclusive zone, margin, interactivity)
|
||||
* is double-buffered, and will be applied at the time wl_surface.commit of
|
||||
* the corresponding wl_surface is called.
|
||||
*/
|
||||
extern const struct wl_interface zwlr_layer_surface_v1_interface;
|
||||
|
||||
#ifndef ZWLR_LAYER_SHELL_V1_ERROR_ENUM
|
||||
#define ZWLR_LAYER_SHELL_V1_ERROR_ENUM
|
||||
enum zwlr_layer_shell_v1_error {
|
||||
/**
|
||||
* wl_surface has another role
|
||||
*/
|
||||
ZWLR_LAYER_SHELL_V1_ERROR_ROLE = 0,
|
||||
/**
|
||||
* layer value is invalid
|
||||
*/
|
||||
ZWLR_LAYER_SHELL_V1_ERROR_INVALID_LAYER = 1,
|
||||
/**
|
||||
* wl_surface has a buffer attached or committed
|
||||
*/
|
||||
ZWLR_LAYER_SHELL_V1_ERROR_ALREADY_CONSTRUCTED = 2,
|
||||
};
|
||||
#endif /* ZWLR_LAYER_SHELL_V1_ERROR_ENUM */
|
||||
|
||||
#ifndef ZWLR_LAYER_SHELL_V1_LAYER_ENUM
|
||||
#define ZWLR_LAYER_SHELL_V1_LAYER_ENUM
|
||||
/**
|
||||
* @ingroup iface_zwlr_layer_shell_v1
|
||||
* available layers for surfaces
|
||||
*
|
||||
* These values indicate which layers a surface can be rendered in. They
|
||||
* are ordered by z depth, bottom-most first. Traditional shell surfaces
|
||||
* will typically be rendered between the bottom and top layers.
|
||||
* Fullscreen shell surfaces are typically rendered at the top layer.
|
||||
* Multiple surfaces can share a single layer, and ordering within a
|
||||
* single layer is undefined.
|
||||
*/
|
||||
enum zwlr_layer_shell_v1_layer {
|
||||
ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND = 0,
|
||||
ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM = 1,
|
||||
ZWLR_LAYER_SHELL_V1_LAYER_TOP = 2,
|
||||
ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY = 3,
|
||||
};
|
||||
#endif /* ZWLR_LAYER_SHELL_V1_LAYER_ENUM */
|
||||
|
||||
#define ZWLR_LAYER_SHELL_V1_GET_LAYER_SURFACE 0
|
||||
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_layer_shell_v1
|
||||
*/
|
||||
#define ZWLR_LAYER_SHELL_V1_GET_LAYER_SURFACE_SINCE_VERSION 1
|
||||
|
||||
/** @ingroup iface_zwlr_layer_shell_v1 */
|
||||
static inline void
|
||||
zwlr_layer_shell_v1_set_user_data(struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1, void *user_data)
|
||||
{
|
||||
wl_proxy_set_user_data((struct wl_proxy *) zwlr_layer_shell_v1, user_data);
|
||||
}
|
||||
|
||||
/** @ingroup iface_zwlr_layer_shell_v1 */
|
||||
static inline void *
|
||||
zwlr_layer_shell_v1_get_user_data(struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1)
|
||||
{
|
||||
return wl_proxy_get_user_data((struct wl_proxy *) zwlr_layer_shell_v1);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
zwlr_layer_shell_v1_get_version(struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1)
|
||||
{
|
||||
return wl_proxy_get_version((struct wl_proxy *) zwlr_layer_shell_v1);
|
||||
}
|
||||
|
||||
/** @ingroup iface_zwlr_layer_shell_v1 */
|
||||
static inline void
|
||||
zwlr_layer_shell_v1_destroy(struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1)
|
||||
{
|
||||
wl_proxy_destroy((struct wl_proxy *) zwlr_layer_shell_v1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_layer_shell_v1
|
||||
*
|
||||
* Create a layer surface for an existing surface. This assigns the role of
|
||||
* layer_surface, or raises a protocol error if another role is already
|
||||
* assigned.
|
||||
*
|
||||
* Creating a layer surface from a wl_surface which has a buffer attached
|
||||
* or committed is a client error, and any attempts by a client to attach
|
||||
* or manipulate a buffer prior to the first layer_surface.configure call
|
||||
* must also be treated as errors.
|
||||
*
|
||||
* You may pass NULL for output to allow the compositor to decide which
|
||||
* output to use. Generally this will be the one that the user most
|
||||
* recently interacted with.
|
||||
*
|
||||
* Clients can specify a namespace that defines the purpose of the layer
|
||||
* surface.
|
||||
*/
|
||||
static inline struct zwlr_layer_surface_v1 *
|
||||
zwlr_layer_shell_v1_get_layer_surface(struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1, struct wl_surface *surface, struct wl_output *output, uint32_t layer, const char *namespace)
|
||||
{
|
||||
struct wl_proxy *id;
|
||||
|
||||
id = wl_proxy_marshal_constructor((struct wl_proxy *) zwlr_layer_shell_v1,
|
||||
ZWLR_LAYER_SHELL_V1_GET_LAYER_SURFACE, &zwlr_layer_surface_v1_interface, NULL, surface, output, layer, namespace);
|
||||
|
||||
return (struct zwlr_layer_surface_v1 *) id;
|
||||
}
|
||||
|
||||
#ifndef ZWLR_LAYER_SURFACE_V1_ERROR_ENUM
|
||||
#define ZWLR_LAYER_SURFACE_V1_ERROR_ENUM
|
||||
enum zwlr_layer_surface_v1_error {
|
||||
/**
|
||||
* provided surface state is invalid
|
||||
*/
|
||||
ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SURFACE_STATE = 0,
|
||||
/**
|
||||
* size is invalid
|
||||
*/
|
||||
ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SIZE = 1,
|
||||
/**
|
||||
* anchor bitfield is invalid
|
||||
*/
|
||||
ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_ANCHOR = 2,
|
||||
};
|
||||
#endif /* ZWLR_LAYER_SURFACE_V1_ERROR_ENUM */
|
||||
|
||||
#ifndef ZWLR_LAYER_SURFACE_V1_ANCHOR_ENUM
|
||||
#define ZWLR_LAYER_SURFACE_V1_ANCHOR_ENUM
|
||||
enum zwlr_layer_surface_v1_anchor {
|
||||
/**
|
||||
* the top edge of the anchor rectangle
|
||||
*/
|
||||
ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP = 1,
|
||||
/**
|
||||
* the bottom edge of the anchor rectangle
|
||||
*/
|
||||
ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM = 2,
|
||||
/**
|
||||
* the left edge of the anchor rectangle
|
||||
*/
|
||||
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT = 4,
|
||||
/**
|
||||
* the right edge of the anchor rectangle
|
||||
*/
|
||||
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT = 8,
|
||||
};
|
||||
#endif /* ZWLR_LAYER_SURFACE_V1_ANCHOR_ENUM */
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_layer_surface_v1
|
||||
* @struct zwlr_layer_surface_v1_listener
|
||||
*/
|
||||
struct zwlr_layer_surface_v1_listener {
|
||||
/**
|
||||
* suggest a surface change
|
||||
*
|
||||
* The configure event asks the client to resize its surface.
|
||||
*
|
||||
* Clients should arrange their surface for the new states, and
|
||||
* then send an ack_configure request with the serial sent in this
|
||||
* configure event at some point before committing the new surface.
|
||||
*
|
||||
* The client is free to dismiss all but the last configure event
|
||||
* it received.
|
||||
*
|
||||
* The width and height arguments specify the size of the window in
|
||||
* surface-local coordinates.
|
||||
*
|
||||
* The size is a hint, in the sense that the client is free to
|
||||
* ignore it if it doesn't resize, pick a smaller size (to satisfy
|
||||
* aspect ratio or resize in steps of NxM pixels). If the client
|
||||
* picks a smaller size and is anchored to two opposite anchors
|
||||
* (e.g. 'top' and 'bottom'), the surface will be centered on this
|
||||
* axis.
|
||||
*
|
||||
* If the width or height arguments are zero, it means the client
|
||||
* should decide its own window dimension.
|
||||
*/
|
||||
void (*configure)(void *data,
|
||||
struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1,
|
||||
uint32_t serial,
|
||||
uint32_t width,
|
||||
uint32_t height);
|
||||
/**
|
||||
* surface should be closed
|
||||
*
|
||||
* The closed event is sent by the compositor when the surface
|
||||
* will no longer be shown. The output may have been destroyed or
|
||||
* the user may have asked for it to be removed. Further changes to
|
||||
* the surface will be ignored. The client should destroy the
|
||||
* resource after receiving this event, and create a new surface if
|
||||
* they so choose.
|
||||
*/
|
||||
void (*closed)(void *data,
|
||||
struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_layer_surface_v1
|
||||
*/
|
||||
static inline int
|
||||
zwlr_layer_surface_v1_add_listener(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1,
|
||||
const struct zwlr_layer_surface_v1_listener *listener, void *data)
|
||||
{
|
||||
return wl_proxy_add_listener((struct wl_proxy *) zwlr_layer_surface_v1,
|
||||
(void (**)(void)) listener, data);
|
||||
}
|
||||
|
||||
#define ZWLR_LAYER_SURFACE_V1_SET_SIZE 0
|
||||
#define ZWLR_LAYER_SURFACE_V1_SET_ANCHOR 1
|
||||
#define ZWLR_LAYER_SURFACE_V1_SET_EXCLUSIVE_ZONE 2
|
||||
#define ZWLR_LAYER_SURFACE_V1_SET_MARGIN 3
|
||||
#define ZWLR_LAYER_SURFACE_V1_SET_KEYBOARD_INTERACTIVITY 4
|
||||
#define ZWLR_LAYER_SURFACE_V1_GET_POPUP 5
|
||||
#define ZWLR_LAYER_SURFACE_V1_ACK_CONFIGURE 6
|
||||
#define ZWLR_LAYER_SURFACE_V1_DESTROY 7
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_layer_surface_v1
|
||||
*/
|
||||
#define ZWLR_LAYER_SURFACE_V1_CONFIGURE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_layer_surface_v1
|
||||
*/
|
||||
#define ZWLR_LAYER_SURFACE_V1_CLOSED_SINCE_VERSION 1
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_layer_surface_v1
|
||||
*/
|
||||
#define ZWLR_LAYER_SURFACE_V1_SET_SIZE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_layer_surface_v1
|
||||
*/
|
||||
#define ZWLR_LAYER_SURFACE_V1_SET_ANCHOR_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_layer_surface_v1
|
||||
*/
|
||||
#define ZWLR_LAYER_SURFACE_V1_SET_EXCLUSIVE_ZONE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_layer_surface_v1
|
||||
*/
|
||||
#define ZWLR_LAYER_SURFACE_V1_SET_MARGIN_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_layer_surface_v1
|
||||
*/
|
||||
#define ZWLR_LAYER_SURFACE_V1_SET_KEYBOARD_INTERACTIVITY_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_layer_surface_v1
|
||||
*/
|
||||
#define ZWLR_LAYER_SURFACE_V1_GET_POPUP_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_layer_surface_v1
|
||||
*/
|
||||
#define ZWLR_LAYER_SURFACE_V1_ACK_CONFIGURE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_layer_surface_v1
|
||||
*/
|
||||
#define ZWLR_LAYER_SURFACE_V1_DESTROY_SINCE_VERSION 1
|
||||
|
||||
/** @ingroup iface_zwlr_layer_surface_v1 */
|
||||
static inline void
|
||||
zwlr_layer_surface_v1_set_user_data(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, void *user_data)
|
||||
{
|
||||
wl_proxy_set_user_data((struct wl_proxy *) zwlr_layer_surface_v1, user_data);
|
||||
}
|
||||
|
||||
/** @ingroup iface_zwlr_layer_surface_v1 */
|
||||
static inline void *
|
||||
zwlr_layer_surface_v1_get_user_data(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1)
|
||||
{
|
||||
return wl_proxy_get_user_data((struct wl_proxy *) zwlr_layer_surface_v1);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
zwlr_layer_surface_v1_get_version(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1)
|
||||
{
|
||||
return wl_proxy_get_version((struct wl_proxy *) zwlr_layer_surface_v1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_layer_surface_v1
|
||||
*
|
||||
* Sets the size of the surface in surface-local coordinates. The
|
||||
* compositor will display the surface centered with respect to its
|
||||
* anchors.
|
||||
*
|
||||
* If you pass 0 for either value, the compositor will assign it and
|
||||
* inform you of the assignment in the configure event. You must set your
|
||||
* anchor to opposite edges in the dimensions you omit; not doing so is a
|
||||
* protocol error. Both values are 0 by default.
|
||||
*
|
||||
* Size is double-buffered, see wl_surface.commit.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_layer_surface_v1_set_size(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, uint32_t width, uint32_t height)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_layer_surface_v1,
|
||||
ZWLR_LAYER_SURFACE_V1_SET_SIZE, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_layer_surface_v1
|
||||
*
|
||||
* Requests that the compositor anchor the surface to the specified edges
|
||||
* and corners. If two orthoginal edges are specified (e.g. 'top' and
|
||||
* 'left'), then the anchor point will be the intersection of the edges
|
||||
* (e.g. the top left corner of the output); otherwise the anchor point
|
||||
* will be centered on that edge, or in the center if none is specified.
|
||||
*
|
||||
* Anchor is double-buffered, see wl_surface.commit.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_layer_surface_v1_set_anchor(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, uint32_t anchor)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_layer_surface_v1,
|
||||
ZWLR_LAYER_SURFACE_V1_SET_ANCHOR, anchor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_layer_surface_v1
|
||||
*
|
||||
* Requests that the compositor avoids occluding an area of the surface
|
||||
* with other surfaces. The compositor's use of this information is
|
||||
* implementation-dependent - do not assume that this region will not
|
||||
* actually be occluded.
|
||||
*
|
||||
* A positive value is only meaningful if the surface is anchored to an
|
||||
* edge, rather than a corner. The zone is the number of surface-local
|
||||
* coordinates from the edge that are considered exclusive.
|
||||
*
|
||||
* Surfaces that do not wish to have an exclusive zone may instead specify
|
||||
* how they should interact with surfaces that do. If set to zero, the
|
||||
* surface indicates that it would like to be moved to avoid occluding
|
||||
* surfaces with a positive excluzive zone. If set to -1, the surface
|
||||
* indicates that it would not like to be moved to accomodate for other
|
||||
* surfaces, and the compositor should extend it all the way to the edges
|
||||
* it is anchored to.
|
||||
*
|
||||
* For example, a panel might set its exclusive zone to 10, so that
|
||||
* maximized shell surfaces are not shown on top of it. A notification
|
||||
* might set its exclusive zone to 0, so that it is moved to avoid
|
||||
* occluding the panel, but shell surfaces are shown underneath it. A
|
||||
* wallpaper or lock screen might set their exclusive zone to -1, so that
|
||||
* they stretch below or over the panel.
|
||||
*
|
||||
* The default value is 0.
|
||||
*
|
||||
* Exclusive zone is double-buffered, see wl_surface.commit.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_layer_surface_v1_set_exclusive_zone(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, int32_t zone)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_layer_surface_v1,
|
||||
ZWLR_LAYER_SURFACE_V1_SET_EXCLUSIVE_ZONE, zone);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_layer_surface_v1
|
||||
*
|
||||
* Requests that the surface be placed some distance away from the anchor
|
||||
* point on the output, in surface-local coordinates. Setting this value
|
||||
* for edges you are not anchored to has no effect.
|
||||
*
|
||||
* The exclusive zone includes the margin.
|
||||
*
|
||||
* Margin is double-buffered, see wl_surface.commit.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_layer_surface_v1_set_margin(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, int32_t top, int32_t right, int32_t bottom, int32_t left)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_layer_surface_v1,
|
||||
ZWLR_LAYER_SURFACE_V1_SET_MARGIN, top, right, bottom, left);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_layer_surface_v1
|
||||
*
|
||||
* Set to 1 to request that the seat send keyboard events to this layer
|
||||
* surface. For layers below the shell surface layer, the seat will use
|
||||
* normal focus semantics. For layers above the shell surface layers, the
|
||||
* seat will always give exclusive keyboard focus to the top-most layer
|
||||
* which has keyboard interactivity set to true.
|
||||
*
|
||||
* Layer surfaces receive pointer, touch, and tablet events normally. If
|
||||
* you do not want to receive them, set the input region on your surface
|
||||
* to an empty region.
|
||||
*
|
||||
* Events is double-buffered, see wl_surface.commit.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_layer_surface_v1_set_keyboard_interactivity(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, uint32_t keyboard_interactivity)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_layer_surface_v1,
|
||||
ZWLR_LAYER_SURFACE_V1_SET_KEYBOARD_INTERACTIVITY, keyboard_interactivity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_layer_surface_v1
|
||||
*
|
||||
* This assigns an xdg_popup's parent to this layer_surface. This popup
|
||||
* should have been created via xdg_surface::get_popup with the parent set
|
||||
* to NULL, and this request must be invoked before committing the popup's
|
||||
* initial state.
|
||||
*
|
||||
* See the documentation of xdg_popup for more details about what an
|
||||
* xdg_popup is and how it is used.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_layer_surface_v1_get_popup(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, struct xdg_popup *popup)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_layer_surface_v1,
|
||||
ZWLR_LAYER_SURFACE_V1_GET_POPUP, popup);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_layer_surface_v1
|
||||
*
|
||||
* When a configure event is received, if a client commits the
|
||||
* surface in response to the configure event, then the client
|
||||
* must make an ack_configure request sometime before the commit
|
||||
* request, passing along the serial of the configure event.
|
||||
*
|
||||
* If the client receives multiple configure events before it
|
||||
* can respond to one, it only has to ack the last configure event.
|
||||
*
|
||||
* A client is not required to commit immediately after sending
|
||||
* an ack_configure request - it may even ack_configure several times
|
||||
* before its next surface commit.
|
||||
*
|
||||
* A client may send multiple ack_configure requests before committing, but
|
||||
* only the last request sent before a commit indicates which configure
|
||||
* event the client really is responding to.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_layer_surface_v1_ack_configure(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, uint32_t serial)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_layer_surface_v1,
|
||||
ZWLR_LAYER_SURFACE_V1_ACK_CONFIGURE, serial);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_layer_surface_v1
|
||||
*
|
||||
* This request destroys the layer surface.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_layer_surface_v1_destroy(struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_layer_surface_v1,
|
||||
ZWLR_LAYER_SURFACE_V1_DESTROY);
|
||||
|
||||
wl_proxy_destroy((struct wl_proxy *) zwlr_layer_surface_v1);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
91
src/wayland/protocols/wlr-layer-shell-unstable-v1.h
Normal file
@ -0,0 +1,91 @@
|
||||
/* Generated by wayland-scanner 1.18.0 */
|
||||
|
||||
/*
|
||||
* Copyright © 2017 Drew DeVault
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this
|
||||
* software and its documentation for any purpose is hereby granted
|
||||
* without fee, provided that the above copyright notice appear in
|
||||
* all copies and that both that copyright notice and this permission
|
||||
* notice appear in supporting documentation, and that the name of
|
||||
* the copyright holders not be used in advertising or publicity
|
||||
* pertaining to distribution of the software without specific,
|
||||
* written prior permission. The copyright holders make no
|
||||
* representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied
|
||||
* warranty.
|
||||
*
|
||||
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
* THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "wayland-util.h"
|
||||
|
||||
#ifndef __has_attribute
|
||||
# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
|
||||
#endif
|
||||
|
||||
#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
|
||||
#define WL_PRIVATE __attribute__ ((visibility("hidden")))
|
||||
#else
|
||||
#define WL_PRIVATE
|
||||
#endif
|
||||
|
||||
extern const struct wl_interface wl_output_interface;
|
||||
extern const struct wl_interface wl_surface_interface;
|
||||
extern const struct wl_interface xdg_popup_interface;
|
||||
extern const struct wl_interface zwlr_layer_surface_v1_interface;
|
||||
|
||||
static const struct wl_interface *wlr_layer_shell_unstable_v1_types[] = {
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&zwlr_layer_surface_v1_interface,
|
||||
&wl_surface_interface,
|
||||
&wl_output_interface,
|
||||
NULL,
|
||||
NULL,
|
||||
&xdg_popup_interface,
|
||||
};
|
||||
|
||||
static const struct wl_message zwlr_layer_shell_v1_requests[] = {
|
||||
{ "get_layer_surface", "no?ous", wlr_layer_shell_unstable_v1_types + 4 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface zwlr_layer_shell_v1_interface = {
|
||||
"zwlr_layer_shell_v1", 1,
|
||||
1, zwlr_layer_shell_v1_requests,
|
||||
0, NULL,
|
||||
};
|
||||
|
||||
static const struct wl_message zwlr_layer_surface_v1_requests[] = {
|
||||
{ "set_size", "uu", wlr_layer_shell_unstable_v1_types + 0 },
|
||||
{ "set_anchor", "u", wlr_layer_shell_unstable_v1_types + 0 },
|
||||
{ "set_exclusive_zone", "i", wlr_layer_shell_unstable_v1_types + 0 },
|
||||
{ "set_margin", "iiii", wlr_layer_shell_unstable_v1_types + 0 },
|
||||
{ "set_keyboard_interactivity", "u", wlr_layer_shell_unstable_v1_types + 0 },
|
||||
{ "get_popup", "o", wlr_layer_shell_unstable_v1_types + 9 },
|
||||
{ "ack_configure", "u", wlr_layer_shell_unstable_v1_types + 0 },
|
||||
{ "destroy", "", wlr_layer_shell_unstable_v1_types + 0 },
|
||||
};
|
||||
|
||||
static const struct wl_message zwlr_layer_surface_v1_events[] = {
|
||||
{ "configure", "uuu", wlr_layer_shell_unstable_v1_types + 0 },
|
||||
{ "closed", "", wlr_layer_shell_unstable_v1_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface zwlr_layer_surface_v1_interface = {
|
||||
"zwlr_layer_surface_v1", 1,
|
||||
8, zwlr_layer_surface_v1_requests,
|
||||
2, zwlr_layer_surface_v1_events,
|
||||
};
|
||||
|
||||
285
src/wayland/protocols/wlr-layer-shell-unstable-v1.xml
Normal file
@ -0,0 +1,285 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="wlr_layer_shell_unstable_v1">
|
||||
<copyright>
|
||||
Copyright © 2017 Drew DeVault
|
||||
|
||||
Permission to use, copy, modify, distribute, and sell this
|
||||
software and its documentation for any purpose is hereby granted
|
||||
without fee, provided that the above copyright notice appear in
|
||||
all copies and that both that copyright notice and this permission
|
||||
notice appear in supporting documentation, and that the name of
|
||||
the copyright holders not be used in advertising or publicity
|
||||
pertaining to distribution of the software without specific,
|
||||
written prior permission. The copyright holders make no
|
||||
representations about the suitability of this software for any
|
||||
purpose. It is provided "as is" without express or implied
|
||||
warranty.
|
||||
|
||||
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
THIS SOFTWARE.
|
||||
</copyright>
|
||||
|
||||
<interface name="zwlr_layer_shell_v1" version="1">
|
||||
<description summary="create surfaces that are layers of the desktop">
|
||||
Clients can use this interface to assign the surface_layer role to
|
||||
wl_surfaces. Such surfaces are assigned to a "layer" of the output and
|
||||
rendered with a defined z-depth respective to each other. They may also be
|
||||
anchored to the edges and corners of a screen and specify input handling
|
||||
semantics. This interface should be suitable for the implementation of
|
||||
many desktop shell components, and a broad number of other applications
|
||||
that interact with the desktop.
|
||||
</description>
|
||||
|
||||
<request name="get_layer_surface">
|
||||
<description summary="create a layer_surface from a surface">
|
||||
Create a layer surface for an existing surface. This assigns the role of
|
||||
layer_surface, or raises a protocol error if another role is already
|
||||
assigned.
|
||||
|
||||
Creating a layer surface from a wl_surface which has a buffer attached
|
||||
or committed is a client error, and any attempts by a client to attach
|
||||
or manipulate a buffer prior to the first layer_surface.configure call
|
||||
must also be treated as errors.
|
||||
|
||||
You may pass NULL for output to allow the compositor to decide which
|
||||
output to use. Generally this will be the one that the user most
|
||||
recently interacted with.
|
||||
|
||||
Clients can specify a namespace that defines the purpose of the layer
|
||||
surface.
|
||||
</description>
|
||||
<arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/>
|
||||
<arg name="surface" type="object" interface="wl_surface"/>
|
||||
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
|
||||
<arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/>
|
||||
<arg name="namespace" type="string" summary="namespace for the layer surface"/>
|
||||
</request>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="role" value="0" summary="wl_surface has another role"/>
|
||||
<entry name="invalid_layer" value="1" summary="layer value is invalid"/>
|
||||
<entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/>
|
||||
</enum>
|
||||
|
||||
<enum name="layer">
|
||||
<description summary="available layers for surfaces">
|
||||
These values indicate which layers a surface can be rendered in. They
|
||||
are ordered by z depth, bottom-most first. Traditional shell surfaces
|
||||
will typically be rendered between the bottom and top layers.
|
||||
Fullscreen shell surfaces are typically rendered at the top layer.
|
||||
Multiple surfaces can share a single layer, and ordering within a
|
||||
single layer is undefined.
|
||||
</description>
|
||||
|
||||
<entry name="background" value="0"/>
|
||||
<entry name="bottom" value="1"/>
|
||||
<entry name="top" value="2"/>
|
||||
<entry name="overlay" value="3"/>
|
||||
</enum>
|
||||
</interface>
|
||||
|
||||
<interface name="zwlr_layer_surface_v1" version="1">
|
||||
<description summary="layer metadata interface">
|
||||
An interface that may be implemented by a wl_surface, for surfaces that
|
||||
are designed to be rendered as a layer of a stacked desktop-like
|
||||
environment.
|
||||
|
||||
Layer surface state (size, anchor, exclusive zone, margin, interactivity)
|
||||
is double-buffered, and will be applied at the time wl_surface.commit of
|
||||
the corresponding wl_surface is called.
|
||||
</description>
|
||||
|
||||
<request name="set_size">
|
||||
<description summary="sets the size of the surface">
|
||||
Sets the size of the surface in surface-local coordinates. The
|
||||
compositor will display the surface centered with respect to its
|
||||
anchors.
|
||||
|
||||
If you pass 0 for either value, the compositor will assign it and
|
||||
inform you of the assignment in the configure event. You must set your
|
||||
anchor to opposite edges in the dimensions you omit; not doing so is a
|
||||
protocol error. Both values are 0 by default.
|
||||
|
||||
Size is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="width" type="uint"/>
|
||||
<arg name="height" type="uint"/>
|
||||
</request>
|
||||
|
||||
<request name="set_anchor">
|
||||
<description summary="configures the anchor point of the surface">
|
||||
Requests that the compositor anchor the surface to the specified edges
|
||||
and corners. If two orthoginal edges are specified (e.g. 'top' and
|
||||
'left'), then the anchor point will be the intersection of the edges
|
||||
(e.g. the top left corner of the output); otherwise the anchor point
|
||||
will be centered on that edge, or in the center if none is specified.
|
||||
|
||||
Anchor is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="anchor" type="uint" enum="anchor"/>
|
||||
</request>
|
||||
|
||||
<request name="set_exclusive_zone">
|
||||
<description summary="configures the exclusive geometry of this surface">
|
||||
Requests that the compositor avoids occluding an area of the surface
|
||||
with other surfaces. The compositor's use of this information is
|
||||
implementation-dependent - do not assume that this region will not
|
||||
actually be occluded.
|
||||
|
||||
A positive value is only meaningful if the surface is anchored to an
|
||||
edge, rather than a corner. The zone is the number of surface-local
|
||||
coordinates from the edge that are considered exclusive.
|
||||
|
||||
Surfaces that do not wish to have an exclusive zone may instead specify
|
||||
how they should interact with surfaces that do. If set to zero, the
|
||||
surface indicates that it would like to be moved to avoid occluding
|
||||
surfaces with a positive excluzive zone. If set to -1, the surface
|
||||
indicates that it would not like to be moved to accomodate for other
|
||||
surfaces, and the compositor should extend it all the way to the edges
|
||||
it is anchored to.
|
||||
|
||||
For example, a panel might set its exclusive zone to 10, so that
|
||||
maximized shell surfaces are not shown on top of it. A notification
|
||||
might set its exclusive zone to 0, so that it is moved to avoid
|
||||
occluding the panel, but shell surfaces are shown underneath it. A
|
||||
wallpaper or lock screen might set their exclusive zone to -1, so that
|
||||
they stretch below or over the panel.
|
||||
|
||||
The default value is 0.
|
||||
|
||||
Exclusive zone is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="zone" type="int"/>
|
||||
</request>
|
||||
|
||||
<request name="set_margin">
|
||||
<description summary="sets a margin from the anchor point">
|
||||
Requests that the surface be placed some distance away from the anchor
|
||||
point on the output, in surface-local coordinates. Setting this value
|
||||
for edges you are not anchored to has no effect.
|
||||
|
||||
The exclusive zone includes the margin.
|
||||
|
||||
Margin is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="top" type="int"/>
|
||||
<arg name="right" type="int"/>
|
||||
<arg name="bottom" type="int"/>
|
||||
<arg name="left" type="int"/>
|
||||
</request>
|
||||
|
||||
<request name="set_keyboard_interactivity">
|
||||
<description summary="requests keyboard events">
|
||||
Set to 1 to request that the seat send keyboard events to this layer
|
||||
surface. For layers below the shell surface layer, the seat will use
|
||||
normal focus semantics. For layers above the shell surface layers, the
|
||||
seat will always give exclusive keyboard focus to the top-most layer
|
||||
which has keyboard interactivity set to true.
|
||||
|
||||
Layer surfaces receive pointer, touch, and tablet events normally. If
|
||||
you do not want to receive them, set the input region on your surface
|
||||
to an empty region.
|
||||
|
||||
Events is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="keyboard_interactivity" type="uint"/>
|
||||
</request>
|
||||
|
||||
<request name="get_popup">
|
||||
<description summary="assign this layer_surface as an xdg_popup parent">
|
||||
This assigns an xdg_popup's parent to this layer_surface. This popup
|
||||
should have been created via xdg_surface::get_popup with the parent set
|
||||
to NULL, and this request must be invoked before committing the popup's
|
||||
initial state.
|
||||
|
||||
See the documentation of xdg_popup for more details about what an
|
||||
xdg_popup is and how it is used.
|
||||
</description>
|
||||
<arg name="popup" type="object" interface="xdg_popup"/>
|
||||
</request>
|
||||
|
||||
<request name="ack_configure">
|
||||
<description summary="ack a configure event">
|
||||
When a configure event is received, if a client commits the
|
||||
surface in response to the configure event, then the client
|
||||
must make an ack_configure request sometime before the commit
|
||||
request, passing along the serial of the configure event.
|
||||
|
||||
If the client receives multiple configure events before it
|
||||
can respond to one, it only has to ack the last configure event.
|
||||
|
||||
A client is not required to commit immediately after sending
|
||||
an ack_configure request - it may even ack_configure several times
|
||||
before its next surface commit.
|
||||
|
||||
A client may send multiple ack_configure requests before committing, but
|
||||
only the last request sent before a commit indicates which configure
|
||||
event the client really is responding to.
|
||||
</description>
|
||||
<arg name="serial" type="uint" summary="the serial from the configure event"/>
|
||||
</request>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy the layer_surface">
|
||||
This request destroys the layer surface.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<event name="configure">
|
||||
<description summary="suggest a surface change">
|
||||
The configure event asks the client to resize its surface.
|
||||
|
||||
Clients should arrange their surface for the new states, and then send
|
||||
an ack_configure request with the serial sent in this configure event at
|
||||
some point before committing the new surface.
|
||||
|
||||
The client is free to dismiss all but the last configure event it
|
||||
received.
|
||||
|
||||
The width and height arguments specify the size of the window in
|
||||
surface-local coordinates.
|
||||
|
||||
The size is a hint, in the sense that the client is free to ignore it if
|
||||
it doesn't resize, pick a smaller size (to satisfy aspect ratio or
|
||||
resize in steps of NxM pixels). If the client picks a smaller size and
|
||||
is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
|
||||
surface will be centered on this axis.
|
||||
|
||||
If the width or height arguments are zero, it means the client should
|
||||
decide its own window dimension.
|
||||
</description>
|
||||
<arg name="serial" type="uint"/>
|
||||
<arg name="width" type="uint"/>
|
||||
<arg name="height" type="uint"/>
|
||||
</event>
|
||||
|
||||
<event name="closed">
|
||||
<description summary="surface should be closed">
|
||||
The closed event is sent by the compositor when the surface will no
|
||||
longer be shown. The output may have been destroyed or the user may
|
||||
have asked for it to be removed. Further changes to the surface will be
|
||||
ignored. The client should destroy the resource after receiving this
|
||||
event, and create a new surface if they so choose.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
|
||||
<entry name="invalid_size" value="1" summary="size is invalid"/>
|
||||
<entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
|
||||
</enum>
|
||||
|
||||
<enum name="anchor" bitfield="true">
|
||||
<entry name="top" value="1" summary="the top edge of the anchor rectangle"/>
|
||||
<entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/>
|
||||
<entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
|
||||
<entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
|
||||
</enum>
|
||||
</interface>
|
||||
</protocol>
|
||||
409
src/wayland/protocols/xdg-output-unstable-v1-client-header.h
Normal file
@ -0,0 +1,409 @@
|
||||
/* Generated by wayland-scanner 1.18.0 */
|
||||
|
||||
#ifndef XDG_OUTPUT_UNSTABLE_V1_CLIENT_PROTOCOL_H
|
||||
#define XDG_OUTPUT_UNSTABLE_V1_CLIENT_PROTOCOL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "wayland-client.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @page page_xdg_output_unstable_v1 The xdg_output_unstable_v1 protocol
|
||||
* Protocol to describe output regions
|
||||
*
|
||||
* @section page_desc_xdg_output_unstable_v1 Description
|
||||
*
|
||||
* This protocol aims at describing outputs in a way which is more in line
|
||||
* with the concept of an output on desktop oriented systems.
|
||||
*
|
||||
* Some information are more specific to the concept of an output for
|
||||
* a desktop oriented system and may not make sense in other applications,
|
||||
* such as IVI systems for example.
|
||||
*
|
||||
* Typically, the global compositor space on a desktop system is made of
|
||||
* a contiguous or overlapping set of rectangular regions.
|
||||
*
|
||||
* Some of the information provided in this protocol might be identical
|
||||
* to their counterparts already available from wl_output, in which case
|
||||
* the information provided by this protocol should be preferred to their
|
||||
* equivalent in wl_output. The goal is to move the desktop specific
|
||||
* concepts (such as output location within the global compositor space,
|
||||
* the connector name and types, etc.) out of the core wl_output protocol.
|
||||
*
|
||||
* Warning! The protocol described in this file is experimental and
|
||||
* backward incompatible changes may be made. Backward compatible
|
||||
* changes may be added together with the corresponding interface
|
||||
* version bump.
|
||||
* Backward incompatible changes are done by bumping the version
|
||||
* number in the protocol and interface names and resetting the
|
||||
* interface version. Once the protocol is to be declared stable,
|
||||
* the 'z' prefix and the version number in the protocol and
|
||||
* interface names are removed and the interface version number is
|
||||
* reset.
|
||||
*
|
||||
* @section page_ifaces_xdg_output_unstable_v1 Interfaces
|
||||
* - @subpage page_iface_zxdg_output_manager_v1 - manage xdg_output objects
|
||||
* - @subpage page_iface_zxdg_output_v1 - compositor logical output region
|
||||
* @section page_copyright_xdg_output_unstable_v1 Copyright
|
||||
* <pre>
|
||||
*
|
||||
* Copyright © 2017 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
* </pre>
|
||||
*/
|
||||
struct wl_output;
|
||||
struct zxdg_output_manager_v1;
|
||||
struct zxdg_output_v1;
|
||||
|
||||
/**
|
||||
* @page page_iface_zxdg_output_manager_v1 zxdg_output_manager_v1
|
||||
* @section page_iface_zxdg_output_manager_v1_desc Description
|
||||
*
|
||||
* A global factory interface for xdg_output objects.
|
||||
* @section page_iface_zxdg_output_manager_v1_api API
|
||||
* See @ref iface_zxdg_output_manager_v1.
|
||||
*/
|
||||
/**
|
||||
* @defgroup iface_zxdg_output_manager_v1 The zxdg_output_manager_v1 interface
|
||||
*
|
||||
* A global factory interface for xdg_output objects.
|
||||
*/
|
||||
extern const struct wl_interface zxdg_output_manager_v1_interface;
|
||||
/**
|
||||
* @page page_iface_zxdg_output_v1 zxdg_output_v1
|
||||
* @section page_iface_zxdg_output_v1_desc Description
|
||||
*
|
||||
* An xdg_output describes part of the compositor geometry.
|
||||
*
|
||||
* This typically corresponds to a monitor that displays part of the
|
||||
* compositor space.
|
||||
*
|
||||
* For objects version 3 onwards, after all xdg_output properties have been
|
||||
* sent (when the object is created and when properties are updated), a
|
||||
* wl_output.done event is sent. This allows changes to the output
|
||||
* properties to be seen as atomic, even if they happen via multiple events.
|
||||
* @section page_iface_zxdg_output_v1_api API
|
||||
* See @ref iface_zxdg_output_v1.
|
||||
*/
|
||||
/**
|
||||
* @defgroup iface_zxdg_output_v1 The zxdg_output_v1 interface
|
||||
*
|
||||
* An xdg_output describes part of the compositor geometry.
|
||||
*
|
||||
* This typically corresponds to a monitor that displays part of the
|
||||
* compositor space.
|
||||
*
|
||||
* For objects version 3 onwards, after all xdg_output properties have been
|
||||
* sent (when the object is created and when properties are updated), a
|
||||
* wl_output.done event is sent. This allows changes to the output
|
||||
* properties to be seen as atomic, even if they happen via multiple events.
|
||||
*/
|
||||
extern const struct wl_interface zxdg_output_v1_interface;
|
||||
|
||||
#define ZXDG_OUTPUT_MANAGER_V1_DESTROY 0
|
||||
#define ZXDG_OUTPUT_MANAGER_V1_GET_XDG_OUTPUT 1
|
||||
|
||||
|
||||
/**
|
||||
* @ingroup iface_zxdg_output_manager_v1
|
||||
*/
|
||||
#define ZXDG_OUTPUT_MANAGER_V1_DESTROY_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zxdg_output_manager_v1
|
||||
*/
|
||||
#define ZXDG_OUTPUT_MANAGER_V1_GET_XDG_OUTPUT_SINCE_VERSION 1
|
||||
|
||||
/** @ingroup iface_zxdg_output_manager_v1 */
|
||||
static inline void
|
||||
zxdg_output_manager_v1_set_user_data(struct zxdg_output_manager_v1 *zxdg_output_manager_v1, void *user_data)
|
||||
{
|
||||
wl_proxy_set_user_data((struct wl_proxy *) zxdg_output_manager_v1, user_data);
|
||||
}
|
||||
|
||||
/** @ingroup iface_zxdg_output_manager_v1 */
|
||||
static inline void *
|
||||
zxdg_output_manager_v1_get_user_data(struct zxdg_output_manager_v1 *zxdg_output_manager_v1)
|
||||
{
|
||||
return wl_proxy_get_user_data((struct wl_proxy *) zxdg_output_manager_v1);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
zxdg_output_manager_v1_get_version(struct zxdg_output_manager_v1 *zxdg_output_manager_v1)
|
||||
{
|
||||
return wl_proxy_get_version((struct wl_proxy *) zxdg_output_manager_v1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zxdg_output_manager_v1
|
||||
*
|
||||
* Using this request a client can tell the server that it is not
|
||||
* going to use the xdg_output_manager object anymore.
|
||||
*
|
||||
* Any objects already created through this instance are not affected.
|
||||
*/
|
||||
static inline void
|
||||
zxdg_output_manager_v1_destroy(struct zxdg_output_manager_v1 *zxdg_output_manager_v1)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zxdg_output_manager_v1,
|
||||
ZXDG_OUTPUT_MANAGER_V1_DESTROY);
|
||||
|
||||
wl_proxy_destroy((struct wl_proxy *) zxdg_output_manager_v1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zxdg_output_manager_v1
|
||||
*
|
||||
* This creates a new xdg_output object for the given wl_output.
|
||||
*/
|
||||
static inline struct zxdg_output_v1 *
|
||||
zxdg_output_manager_v1_get_xdg_output(struct zxdg_output_manager_v1 *zxdg_output_manager_v1, struct wl_output *output)
|
||||
{
|
||||
struct wl_proxy *id;
|
||||
|
||||
id = wl_proxy_marshal_constructor((struct wl_proxy *) zxdg_output_manager_v1,
|
||||
ZXDG_OUTPUT_MANAGER_V1_GET_XDG_OUTPUT, &zxdg_output_v1_interface, NULL, output);
|
||||
|
||||
return (struct zxdg_output_v1 *) id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zxdg_output_v1
|
||||
* @struct zxdg_output_v1_listener
|
||||
*/
|
||||
struct zxdg_output_v1_listener {
|
||||
/**
|
||||
* position of the output within the global compositor space
|
||||
*
|
||||
* The position event describes the location of the wl_output
|
||||
* within the global compositor space.
|
||||
*
|
||||
* The logical_position event is sent after creating an xdg_output
|
||||
* (see xdg_output_manager.get_xdg_output) and whenever the
|
||||
* location of the output changes within the global compositor
|
||||
* space.
|
||||
* @param x x position within the global compositor space
|
||||
* @param y y position within the global compositor space
|
||||
*/
|
||||
void (*logical_position)(void *data,
|
||||
struct zxdg_output_v1 *zxdg_output_v1,
|
||||
int32_t x,
|
||||
int32_t y);
|
||||
/**
|
||||
* size of the output in the global compositor space
|
||||
*
|
||||
* The logical_size event describes the size of the output in the
|
||||
* global compositor space.
|
||||
*
|
||||
* For example, a surface without any buffer scale, transformation
|
||||
* nor rotation set, with the size matching the logical_size will
|
||||
* have the same size as the corresponding output when displayed.
|
||||
*
|
||||
* Most regular Wayland clients should not pay attention to the
|
||||
* logical size and would rather rely on xdg_shell interfaces.
|
||||
*
|
||||
* Some clients such as Xwayland, however, need this to configure
|
||||
* their surfaces in the global compositor space as the compositor
|
||||
* may apply a different scale from what is advertised by the
|
||||
* output scaling property (to achieve fractional scaling, for
|
||||
* example).
|
||||
*
|
||||
* For example, for a wl_output mode 3840×2160 and a scale factor
|
||||
* 2:
|
||||
*
|
||||
* - A compositor not scaling the surface buffers will advertise a
|
||||
* logical size of 3840×2160,
|
||||
*
|
||||
* - A compositor automatically scaling the surface buffers will
|
||||
* advertise a logical size of 1920×1080,
|
||||
*
|
||||
* - A compositor using a fractional scale of 1.5 will advertise a
|
||||
* logical size to 2560×1620.
|
||||
*
|
||||
* For example, for a wl_output mode 1920×1080 and a 90 degree
|
||||
* rotation, the compositor will advertise a logical size of
|
||||
* 1080x1920.
|
||||
*
|
||||
* The logical_size event is sent after creating an xdg_output (see
|
||||
* xdg_output_manager.get_xdg_output) and whenever the logical size
|
||||
* of the output changes, either as a result of a change in the
|
||||
* applied scale or because of a change in the corresponding output
|
||||
* mode(see wl_output.mode) or transform (see wl_output.transform).
|
||||
* @param width width in global compositor space
|
||||
* @param height height in global compositor space
|
||||
*/
|
||||
void (*logical_size)(void *data,
|
||||
struct zxdg_output_v1 *zxdg_output_v1,
|
||||
int32_t width,
|
||||
int32_t height);
|
||||
/**
|
||||
* all information about the output have been sent
|
||||
*
|
||||
* This event is sent after all other properties of an xdg_output
|
||||
* have been sent.
|
||||
*
|
||||
* This allows changes to the xdg_output properties to be seen as
|
||||
* atomic, even if they happen via multiple events.
|
||||
*
|
||||
* For objects version 3 onwards, this event is deprecated.
|
||||
* Compositors are not required to send it anymore and must send
|
||||
* wl_output.done instead.
|
||||
*/
|
||||
void (*done)(void *data,
|
||||
struct zxdg_output_v1 *zxdg_output_v1);
|
||||
/**
|
||||
* name of this output
|
||||
*
|
||||
* Many compositors will assign names to their outputs, show them
|
||||
* to the user, allow them to be configured by name, etc. The
|
||||
* client may wish to know this name as well to offer the user
|
||||
* similar behaviors.
|
||||
*
|
||||
* The naming convention is compositor defined, but limited to
|
||||
* alphanumeric characters and dashes (-). Each name is unique
|
||||
* among all wl_output globals, but if a wl_output global is
|
||||
* destroyed the same name may be reused later. The names will also
|
||||
* remain consistent across sessions with the same hardware and
|
||||
* software configuration.
|
||||
*
|
||||
* Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc.
|
||||
* However, do not assume that the name is a reflection of an
|
||||
* underlying DRM connector, X11 connection, etc.
|
||||
*
|
||||
* The name event is sent after creating an xdg_output (see
|
||||
* xdg_output_manager.get_xdg_output). This event is only sent once
|
||||
* per xdg_output, and the name does not change over the lifetime
|
||||
* of the wl_output global.
|
||||
* @param name output name
|
||||
* @since 2
|
||||
*/
|
||||
void (*name)(void *data,
|
||||
struct zxdg_output_v1 *zxdg_output_v1,
|
||||
const char *name);
|
||||
/**
|
||||
* human-readable description of this output
|
||||
*
|
||||
* Many compositors can produce human-readable descriptions of
|
||||
* their outputs. The client may wish to know this description as
|
||||
* well, to communicate the user for various purposes.
|
||||
*
|
||||
* The description is a UTF-8 string with no convention defined for
|
||||
* its contents. Examples might include 'Foocorp 11" Display' or
|
||||
* 'Virtual X11 output via :1'.
|
||||
*
|
||||
* The description event is sent after creating an xdg_output (see
|
||||
* xdg_output_manager.get_xdg_output) and whenever the description
|
||||
* changes. The description is optional, and may not be sent at
|
||||
* all.
|
||||
*
|
||||
* For objects of version 2 and lower, this event is only sent once
|
||||
* per xdg_output, and the description does not change over the
|
||||
* lifetime of the wl_output global.
|
||||
* @param description output description
|
||||
* @since 2
|
||||
*/
|
||||
void (*description)(void *data,
|
||||
struct zxdg_output_v1 *zxdg_output_v1,
|
||||
const char *description);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup iface_zxdg_output_v1
|
||||
*/
|
||||
static inline int
|
||||
zxdg_output_v1_add_listener(struct zxdg_output_v1 *zxdg_output_v1,
|
||||
const struct zxdg_output_v1_listener *listener, void *data)
|
||||
{
|
||||
return wl_proxy_add_listener((struct wl_proxy *) zxdg_output_v1,
|
||||
(void (**)(void)) listener, data);
|
||||
}
|
||||
|
||||
#define ZXDG_OUTPUT_V1_DESTROY 0
|
||||
|
||||
/**
|
||||
* @ingroup iface_zxdg_output_v1
|
||||
*/
|
||||
#define ZXDG_OUTPUT_V1_LOGICAL_POSITION_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zxdg_output_v1
|
||||
*/
|
||||
#define ZXDG_OUTPUT_V1_LOGICAL_SIZE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zxdg_output_v1
|
||||
*/
|
||||
#define ZXDG_OUTPUT_V1_DONE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zxdg_output_v1
|
||||
*/
|
||||
#define ZXDG_OUTPUT_V1_NAME_SINCE_VERSION 2
|
||||
/**
|
||||
* @ingroup iface_zxdg_output_v1
|
||||
*/
|
||||
#define ZXDG_OUTPUT_V1_DESCRIPTION_SINCE_VERSION 2
|
||||
|
||||
/**
|
||||
* @ingroup iface_zxdg_output_v1
|
||||
*/
|
||||
#define ZXDG_OUTPUT_V1_DESTROY_SINCE_VERSION 1
|
||||
|
||||
/** @ingroup iface_zxdg_output_v1 */
|
||||
static inline void
|
||||
zxdg_output_v1_set_user_data(struct zxdg_output_v1 *zxdg_output_v1, void *user_data)
|
||||
{
|
||||
wl_proxy_set_user_data((struct wl_proxy *) zxdg_output_v1, user_data);
|
||||
}
|
||||
|
||||
/** @ingroup iface_zxdg_output_v1 */
|
||||
static inline void *
|
||||
zxdg_output_v1_get_user_data(struct zxdg_output_v1 *zxdg_output_v1)
|
||||
{
|
||||
return wl_proxy_get_user_data((struct wl_proxy *) zxdg_output_v1);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
zxdg_output_v1_get_version(struct zxdg_output_v1 *zxdg_output_v1)
|
||||
{
|
||||
return wl_proxy_get_version((struct wl_proxy *) zxdg_output_v1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zxdg_output_v1
|
||||
*
|
||||
* Using this request a client can tell the server that it is not
|
||||
* going to use the xdg_output object anymore.
|
||||
*/
|
||||
static inline void
|
||||
zxdg_output_v1_destroy(struct zxdg_output_v1 *zxdg_output_v1)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zxdg_output_v1,
|
||||
ZXDG_OUTPUT_V1_DESTROY);
|
||||
|
||||
wl_proxy_destroy((struct wl_proxy *) zxdg_output_v1);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
78
src/wayland/protocols/xdg-output-unstable-v1.h
Normal file
@ -0,0 +1,78 @@
|
||||
/* Generated by wayland-scanner 1.18.0 */
|
||||
|
||||
/*
|
||||
* Copyright © 2017 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "wayland-util.h"
|
||||
|
||||
#ifndef __has_attribute
|
||||
# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
|
||||
#endif
|
||||
|
||||
#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
|
||||
#define WL_PRIVATE __attribute__ ((visibility("hidden")))
|
||||
#else
|
||||
#define WL_PRIVATE
|
||||
#endif
|
||||
|
||||
extern const struct wl_interface wl_output_interface;
|
||||
extern const struct wl_interface zxdg_output_v1_interface;
|
||||
|
||||
static const struct wl_interface *xdg_output_unstable_v1_types[] = {
|
||||
NULL,
|
||||
NULL,
|
||||
&zxdg_output_v1_interface,
|
||||
&wl_output_interface,
|
||||
};
|
||||
|
||||
static const struct wl_message zxdg_output_manager_v1_requests[] = {
|
||||
{ "destroy", "", xdg_output_unstable_v1_types + 0 },
|
||||
{ "get_xdg_output", "no", xdg_output_unstable_v1_types + 2 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface zxdg_output_manager_v1_interface = {
|
||||
"zxdg_output_manager_v1", 3,
|
||||
2, zxdg_output_manager_v1_requests,
|
||||
0, NULL,
|
||||
};
|
||||
|
||||
static const struct wl_message zxdg_output_v1_requests[] = {
|
||||
{ "destroy", "", xdg_output_unstable_v1_types + 0 },
|
||||
};
|
||||
|
||||
static const struct wl_message zxdg_output_v1_events[] = {
|
||||
{ "logical_position", "ii", xdg_output_unstable_v1_types + 0 },
|
||||
{ "logical_size", "ii", xdg_output_unstable_v1_types + 0 },
|
||||
{ "done", "", xdg_output_unstable_v1_types + 0 },
|
||||
{ "name", "2s", xdg_output_unstable_v1_types + 0 },
|
||||
{ "description", "2s", xdg_output_unstable_v1_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface zxdg_output_v1_interface = {
|
||||
"zxdg_output_v1", 3,
|
||||
1, zxdg_output_v1_requests,
|
||||
5, zxdg_output_v1_events,
|
||||
};
|
||||
|
||||
1970
src/wayland/protocols/xdg-shell-client-header.h
Normal file
181
src/wayland/protocols/xdg-shell.h
Normal file
@ -0,0 +1,181 @@
|
||||
/* Generated by wayland-scanner 1.18.0 */
|
||||
|
||||
/*
|
||||
* Copyright © 2008-2013 Kristian Høgsberg
|
||||
* Copyright © 2013 Rafael Antognolli
|
||||
* Copyright © 2013 Jasper St. Pierre
|
||||
* Copyright © 2010-2013 Intel Corporation
|
||||
* Copyright © 2015-2017 Samsung Electronics Co., Ltd
|
||||
* Copyright © 2015-2017 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "wayland-util.h"
|
||||
|
||||
#ifndef __has_attribute
|
||||
# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
|
||||
#endif
|
||||
|
||||
#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
|
||||
#define WL_PRIVATE __attribute__ ((visibility("hidden")))
|
||||
#else
|
||||
#define WL_PRIVATE
|
||||
#endif
|
||||
|
||||
extern const struct wl_interface wl_output_interface;
|
||||
extern const struct wl_interface wl_seat_interface;
|
||||
extern const struct wl_interface wl_surface_interface;
|
||||
extern const struct wl_interface xdg_popup_interface;
|
||||
extern const struct wl_interface xdg_positioner_interface;
|
||||
extern const struct wl_interface xdg_surface_interface;
|
||||
extern const struct wl_interface xdg_toplevel_interface;
|
||||
|
||||
static const struct wl_interface *xdg_shell_types[] = {
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&xdg_positioner_interface,
|
||||
&xdg_surface_interface,
|
||||
&wl_surface_interface,
|
||||
&xdg_toplevel_interface,
|
||||
&xdg_popup_interface,
|
||||
&xdg_surface_interface,
|
||||
&xdg_positioner_interface,
|
||||
&xdg_toplevel_interface,
|
||||
&wl_seat_interface,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&wl_seat_interface,
|
||||
NULL,
|
||||
&wl_seat_interface,
|
||||
NULL,
|
||||
NULL,
|
||||
&wl_output_interface,
|
||||
&wl_seat_interface,
|
||||
NULL,
|
||||
&xdg_positioner_interface,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_wm_base_requests[] = {
|
||||
{ "destroy", "", xdg_shell_types + 0 },
|
||||
{ "create_positioner", "n", xdg_shell_types + 4 },
|
||||
{ "get_xdg_surface", "no", xdg_shell_types + 5 },
|
||||
{ "pong", "u", xdg_shell_types + 0 },
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_wm_base_events[] = {
|
||||
{ "ping", "u", xdg_shell_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface xdg_wm_base_interface = {
|
||||
"xdg_wm_base", 3,
|
||||
4, xdg_wm_base_requests,
|
||||
1, xdg_wm_base_events,
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_positioner_requests[] = {
|
||||
{ "destroy", "", xdg_shell_types + 0 },
|
||||
{ "set_size", "ii", xdg_shell_types + 0 },
|
||||
{ "set_anchor_rect", "iiii", xdg_shell_types + 0 },
|
||||
{ "set_anchor", "u", xdg_shell_types + 0 },
|
||||
{ "set_gravity", "u", xdg_shell_types + 0 },
|
||||
{ "set_constraint_adjustment", "u", xdg_shell_types + 0 },
|
||||
{ "set_offset", "ii", xdg_shell_types + 0 },
|
||||
{ "set_reactive", "3", xdg_shell_types + 0 },
|
||||
{ "set_parent_size", "3ii", xdg_shell_types + 0 },
|
||||
{ "set_parent_configure", "3u", xdg_shell_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface xdg_positioner_interface = {
|
||||
"xdg_positioner", 3,
|
||||
10, xdg_positioner_requests,
|
||||
0, NULL,
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_surface_requests[] = {
|
||||
{ "destroy", "", xdg_shell_types + 0 },
|
||||
{ "get_toplevel", "n", xdg_shell_types + 7 },
|
||||
{ "get_popup", "n?oo", xdg_shell_types + 8 },
|
||||
{ "set_window_geometry", "iiii", xdg_shell_types + 0 },
|
||||
{ "ack_configure", "u", xdg_shell_types + 0 },
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_surface_events[] = {
|
||||
{ "configure", "u", xdg_shell_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface xdg_surface_interface = {
|
||||
"xdg_surface", 3,
|
||||
5, xdg_surface_requests,
|
||||
1, xdg_surface_events,
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_toplevel_requests[] = {
|
||||
{ "destroy", "", xdg_shell_types + 0 },
|
||||
{ "set_parent", "?o", xdg_shell_types + 11 },
|
||||
{ "set_title", "s", xdg_shell_types + 0 },
|
||||
{ "set_app_id", "s", xdg_shell_types + 0 },
|
||||
{ "show_window_menu", "ouii", xdg_shell_types + 12 },
|
||||
{ "move", "ou", xdg_shell_types + 16 },
|
||||
{ "resize", "ouu", xdg_shell_types + 18 },
|
||||
{ "set_max_size", "ii", xdg_shell_types + 0 },
|
||||
{ "set_min_size", "ii", xdg_shell_types + 0 },
|
||||
{ "set_maximized", "", xdg_shell_types + 0 },
|
||||
{ "unset_maximized", "", xdg_shell_types + 0 },
|
||||
{ "set_fullscreen", "?o", xdg_shell_types + 21 },
|
||||
{ "unset_fullscreen", "", xdg_shell_types + 0 },
|
||||
{ "set_minimized", "", xdg_shell_types + 0 },
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_toplevel_events[] = {
|
||||
{ "configure", "iia", xdg_shell_types + 0 },
|
||||
{ "close", "", xdg_shell_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface xdg_toplevel_interface = {
|
||||
"xdg_toplevel", 3,
|
||||
14, xdg_toplevel_requests,
|
||||
2, xdg_toplevel_events,
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_popup_requests[] = {
|
||||
{ "destroy", "", xdg_shell_types + 0 },
|
||||
{ "grab", "ou", xdg_shell_types + 22 },
|
||||
{ "reposition", "3ou", xdg_shell_types + 24 },
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_popup_events[] = {
|
||||
{ "configure", "iiii", xdg_shell_types + 0 },
|
||||
{ "popup_done", "", xdg_shell_types + 0 },
|
||||
{ "repositioned", "3u", xdg_shell_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface xdg_popup_interface = {
|
||||
"xdg_popup", 3,
|
||||
3, xdg_popup_requests,
|
||||
3, xdg_popup_events,
|
||||
};
|
||||
|
||||
917
src/wayland/wl.c
Normal file
@ -0,0 +1,917 @@
|
||||
#define _POSIX_C_SOURCE 200112L
|
||||
#include "wl.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-client-protocol.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/input-event-codes.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "protocols/xdg-shell-client-header.h"
|
||||
#include "protocols/xdg-shell.h"
|
||||
#include "protocols/wlr-layer-shell-unstable-v1-client-header.h"
|
||||
#include "protocols/wlr-layer-shell-unstable-v1.h"
|
||||
#include "protocols/wlr-foreign-toplevel-management-unstable-v1-client-header.h"
|
||||
#include "protocols/wlr-foreign-toplevel-management-unstable-v1.h"
|
||||
#include "protocols/idle-client-header.h"
|
||||
#include "protocols/idle.h"
|
||||
#include "pool-buffer.h"
|
||||
|
||||
|
||||
#include "../log.h"
|
||||
#include "../settings.h"
|
||||
#include "../queues.h"
|
||||
#include "../input.h"
|
||||
#include "libgwater-wayland.h"
|
||||
|
||||
#define MAX_TOUCHPOINTS 10
|
||||
|
||||
struct window_wl {
|
||||
cairo_surface_t *c_surface;
|
||||
cairo_t * c_ctx;
|
||||
|
||||
GWaterWaylandSource *esrc;
|
||||
};
|
||||
|
||||
struct wl_ctx {
|
||||
struct wl_display *display;
|
||||
struct wl_registry *registry;
|
||||
struct wl_compositor *compositor;
|
||||
struct wl_shm *shm;
|
||||
struct zwlr_layer_shell_v1 *layer_shell;
|
||||
struct wl_seat *seat;
|
||||
|
||||
struct wl_list outputs;
|
||||
|
||||
struct wl_surface *surface;
|
||||
struct dunst_output *surface_output;
|
||||
struct zwlr_layer_surface_v1 *layer_surface;
|
||||
struct dunst_output *layer_surface_output;
|
||||
struct wl_callback *frame_callback;
|
||||
struct org_kde_kwin_idle *idle_handler;
|
||||
struct org_kde_kwin_idle_timeout *idle_timeout;
|
||||
struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager;
|
||||
bool configured;
|
||||
bool dirty;
|
||||
bool is_idle;
|
||||
bool has_idle_monitor;
|
||||
|
||||
struct {
|
||||
struct wl_pointer *wl_pointer;
|
||||
int32_t x, y;
|
||||
} pointer;
|
||||
|
||||
struct {
|
||||
struct wl_touch *wl_touch;
|
||||
struct {
|
||||
int32_t x, y;
|
||||
} pts[MAX_TOUCHPOINTS];
|
||||
} touch;
|
||||
|
||||
struct dimensions cur_dim;
|
||||
|
||||
int32_t width, height;
|
||||
struct pool_buffer buffers[2];
|
||||
struct pool_buffer *current_buffer;
|
||||
};
|
||||
|
||||
struct dunst_output {
|
||||
uint32_t global_name;
|
||||
char *name;
|
||||
struct wl_output *wl_output;
|
||||
struct wl_list link;
|
||||
|
||||
uint32_t scale;
|
||||
uint32_t subpixel; // TODO do something with it
|
||||
bool fullscreen;
|
||||
struct zwlr_foreign_toplevel_handle_v1 *fullscreen_toplevel; // the toplevel that is fullscreened on this output
|
||||
};
|
||||
|
||||
struct wl_ctx ctx;
|
||||
|
||||
static void noop() {
|
||||
// This space intentionally left blank
|
||||
}
|
||||
|
||||
void set_dirty();
|
||||
|
||||
static void output_handle_geometry(void *data, struct wl_output *wl_output,
|
||||
int32_t x, int32_t y, int32_t phy_width, int32_t phy_height,
|
||||
int32_t subpixel, const char *make, const char *model,
|
||||
int32_t transform) {
|
||||
//TODO do something with the subpixel data
|
||||
struct dunst_output *output = data;
|
||||
output->subpixel = subpixel;
|
||||
}
|
||||
|
||||
static void output_handle_scale(void *data, struct wl_output *wl_output,
|
||||
int32_t factor) {
|
||||
struct dunst_output *output = data;
|
||||
output->scale = factor;
|
||||
|
||||
wake_up();
|
||||
}
|
||||
|
||||
static const struct wl_output_listener output_listener = {
|
||||
.geometry = output_handle_geometry,
|
||||
.mode = noop,
|
||||
.done = noop,
|
||||
.scale = output_handle_scale,
|
||||
};
|
||||
|
||||
static void create_output( struct wl_output *wl_output, uint32_t global_name) {
|
||||
struct dunst_output *output = g_malloc0(sizeof(struct dunst_output));
|
||||
if (output == NULL) {
|
||||
LOG_E("allocation failed");
|
||||
return;
|
||||
}
|
||||
static int number = 0;
|
||||
LOG_I("New output found - id %i", number);
|
||||
output->global_name = global_name;
|
||||
output->wl_output = wl_output;
|
||||
output->scale = 1;
|
||||
output->fullscreen = false;
|
||||
wl_list_insert(&ctx.outputs, &output->link);
|
||||
|
||||
wl_output_set_user_data(wl_output, output);
|
||||
wl_output_add_listener(wl_output, &output_listener, output);
|
||||
number++;
|
||||
}
|
||||
|
||||
static void destroy_output(struct dunst_output *output) {
|
||||
if (ctx.surface_output == output) {
|
||||
ctx.surface_output = NULL;
|
||||
}
|
||||
if (ctx.layer_surface_output == output) {
|
||||
ctx.layer_surface_output = NULL;
|
||||
}
|
||||
wl_list_remove(&output->link);
|
||||
wl_output_destroy(output->wl_output);
|
||||
free(output->name);
|
||||
free(output);
|
||||
}
|
||||
|
||||
static void touch_handle_motion(void *data, struct wl_touch *wl_touch,
|
||||
uint32_t time, int32_t id,
|
||||
wl_fixed_t surface_x, wl_fixed_t surface_y) {
|
||||
if (id >= MAX_TOUCHPOINTS) {
|
||||
return;
|
||||
}
|
||||
ctx.touch.pts[id].x = wl_fixed_to_int(surface_x);
|
||||
ctx.touch.pts[id].y = wl_fixed_to_int(surface_y);
|
||||
}
|
||||
|
||||
static void touch_handle_down(void *data, struct wl_touch *wl_touch,
|
||||
uint32_t serial, uint32_t time, struct wl_surface *sfc, int32_t id,
|
||||
wl_fixed_t surface_x, wl_fixed_t surface_y) {
|
||||
if (id >= MAX_TOUCHPOINTS) {
|
||||
return;
|
||||
}
|
||||
ctx.touch.pts[id].x = wl_fixed_to_int(surface_x);
|
||||
ctx.touch.pts[id].y = wl_fixed_to_int(surface_y);
|
||||
}
|
||||
|
||||
static void touch_handle_up(void *data, struct wl_touch *wl_touch,
|
||||
uint32_t serial, uint32_t time, int32_t id) {
|
||||
if (id >= MAX_TOUCHPOINTS) {
|
||||
return;
|
||||
}
|
||||
input_handle_click(BTN_TOUCH, false,
|
||||
ctx.touch.pts[id].x, ctx.touch.pts[id].y);
|
||||
|
||||
}
|
||||
|
||||
static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
|
||||
ctx.pointer.x = wl_fixed_to_int(surface_x);
|
||||
ctx.pointer.y = wl_fixed_to_int(surface_y);
|
||||
}
|
||||
|
||||
static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
|
||||
uint32_t serial, uint32_t time, uint32_t button,
|
||||
uint32_t button_state) {
|
||||
input_handle_click(button, button_state, ctx.pointer.x, ctx.pointer.y);
|
||||
}
|
||||
|
||||
static const struct wl_pointer_listener pointer_listener = {
|
||||
.enter = noop,
|
||||
.leave = noop,
|
||||
.motion = pointer_handle_motion,
|
||||
.button = pointer_handle_button,
|
||||
.axis = noop,
|
||||
};
|
||||
|
||||
static const struct wl_touch_listener touch_listener = {
|
||||
.down = touch_handle_down,
|
||||
.up = touch_handle_up,
|
||||
.motion = touch_handle_motion,
|
||||
.frame = noop,
|
||||
.cancel = noop,
|
||||
};
|
||||
|
||||
static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
|
||||
uint32_t capabilities) {
|
||||
|
||||
if (ctx.pointer.wl_pointer != NULL) {
|
||||
wl_pointer_release(ctx.pointer.wl_pointer);
|
||||
ctx.pointer.wl_pointer = NULL;
|
||||
}
|
||||
if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
|
||||
ctx.pointer.wl_pointer = wl_seat_get_pointer(wl_seat);
|
||||
wl_pointer_add_listener(ctx.pointer.wl_pointer,
|
||||
&pointer_listener, ctx.seat);
|
||||
}
|
||||
if (ctx.touch.wl_touch != NULL) {
|
||||
wl_touch_release(ctx.touch.wl_touch);
|
||||
ctx.touch.wl_touch = NULL;
|
||||
}
|
||||
if (capabilities & WL_SEAT_CAPABILITY_TOUCH) {
|
||||
ctx.touch.wl_touch = wl_seat_get_touch(wl_seat);
|
||||
wl_touch_add_listener(ctx.touch.wl_touch,
|
||||
&touch_listener, ctx.seat);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct wl_seat_listener seat_listener = {
|
||||
.capabilities = seat_handle_capabilities,
|
||||
.name = noop,
|
||||
};
|
||||
|
||||
|
||||
static void surface_handle_enter(void *data, struct wl_surface *surface,
|
||||
struct wl_output *wl_output) {
|
||||
// Don't bother keeping a list of outputs, a layer surface can only be on
|
||||
// one output a a time
|
||||
ctx.surface_output = wl_output_get_user_data(wl_output);
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
static void surface_handle_leave(void *data, struct wl_surface *surface,
|
||||
struct wl_output *wl_output) {
|
||||
ctx.surface_output = NULL;
|
||||
}
|
||||
|
||||
static const struct wl_surface_listener surface_listener = {
|
||||
.enter = surface_handle_enter,
|
||||
.leave = surface_handle_leave,
|
||||
};
|
||||
|
||||
|
||||
static void schedule_frame_and_commit();
|
||||
static void send_frame();
|
||||
|
||||
static void layer_surface_handle_configure(void *data,
|
||||
struct zwlr_layer_surface_v1 *surface,
|
||||
uint32_t serial, uint32_t width, uint32_t height) {
|
||||
ctx.configured = true;
|
||||
ctx.width = width;
|
||||
ctx.height = height;
|
||||
|
||||
// not needed as it is set somewhere else
|
||||
/* zwlr_layer_surface_v1_set_size(surface, width, height); */
|
||||
zwlr_layer_surface_v1_ack_configure(surface, serial);
|
||||
send_frame();
|
||||
}
|
||||
|
||||
static void layer_surface_handle_closed(void *data,
|
||||
struct zwlr_layer_surface_v1 *surface) {
|
||||
LOG_I("Destroying layer");
|
||||
if (ctx.layer_surface)
|
||||
zwlr_layer_surface_v1_destroy(ctx.layer_surface);
|
||||
ctx.layer_surface = NULL;
|
||||
|
||||
if (ctx.surface)
|
||||
wl_surface_destroy(ctx.surface);
|
||||
ctx.surface = NULL;
|
||||
|
||||
if (ctx.frame_callback) {
|
||||
wl_callback_destroy(ctx.frame_callback);
|
||||
ctx.frame_callback = NULL;
|
||||
ctx.dirty = true;
|
||||
}
|
||||
|
||||
if (ctx.configured) {
|
||||
ctx.configured = false;
|
||||
ctx.width = ctx.height = 0;
|
||||
ctx.dirty = true;
|
||||
}
|
||||
|
||||
if (ctx.dirty) {
|
||||
schedule_frame_and_commit();
|
||||
}
|
||||
}
|
||||
|
||||
static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
|
||||
.configure = layer_surface_handle_configure,
|
||||
.closed = layer_surface_handle_closed,
|
||||
};
|
||||
|
||||
|
||||
static void idle_start (void *data, struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout) {
|
||||
ctx.is_idle = true;
|
||||
LOG_D("User went idle");
|
||||
}
|
||||
static void idle_stop (void *data, struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout) {
|
||||
ctx.is_idle = false;
|
||||
LOG_D("User isn't idle anymore");
|
||||
}
|
||||
|
||||
static const struct org_kde_kwin_idle_timeout_listener idle_timeout_listener = {
|
||||
.idle = idle_start,
|
||||
.resumed = idle_stop,
|
||||
};
|
||||
|
||||
static void add_seat_to_idle_handler(struct wl_seat *seat) {
|
||||
if (!ctx.idle_handler){
|
||||
return;
|
||||
}
|
||||
if (settings.idle_threshold > 0) {
|
||||
uint32_t timeout_ms = settings.idle_threshold/1000;
|
||||
ctx.idle_timeout = org_kde_kwin_idle_get_idle_timeout(ctx.idle_handler, seat, timeout_ms);
|
||||
org_kde_kwin_idle_timeout_add_listener(ctx.idle_timeout, &idle_timeout_listener, 0);
|
||||
ctx.has_idle_monitor = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Warning, can return NULL
|
||||
static struct dunst_output *get_configured_output() {
|
||||
int n = 0;
|
||||
int target_monitor = settings.monitor;
|
||||
|
||||
struct dunst_output *first_output = NULL, *configured_output = NULL,
|
||||
*tmp_output = NULL;
|
||||
wl_list_for_each(tmp_output, &ctx.outputs, link) {
|
||||
if (n == 0)
|
||||
first_output = tmp_output;
|
||||
if (n == target_monitor)
|
||||
configured_output = tmp_output;
|
||||
n++;
|
||||
}
|
||||
|
||||
// There's only 1 output, so return that
|
||||
if (n == 1)
|
||||
return first_output;
|
||||
|
||||
switch (settings.f_mode){
|
||||
case FOLLOW_NONE: ; // this semicolon is neccesary
|
||||
if (!configured_output) {
|
||||
LOG_W("Monitor %i doesn't exist, using focused monitor", settings.monitor);
|
||||
}
|
||||
return configured_output;
|
||||
case FOLLOW_MOUSE:
|
||||
// fallthrough
|
||||
case FOLLOW_KEYBOARD:
|
||||
// fallthrough
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// does not do null checking
|
||||
static void dunst_output_set_fullscreen(struct dunst_output *output,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *toplevel,
|
||||
bool fullscreen) {
|
||||
output->fullscreen = fullscreen;
|
||||
if (fullscreen)
|
||||
output->fullscreen_toplevel = toplevel;
|
||||
else
|
||||
output->fullscreen_toplevel = NULL;
|
||||
|
||||
LOG_D("Set output %i fullscreen state %i", output->global_name, fullscreen);
|
||||
wake_up();
|
||||
}
|
||||
|
||||
static void toplevel_output_leave(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *toplevel,
|
||||
struct wl_output *output) {
|
||||
zwlr_foreign_toplevel_handle_v1_set_user_data(toplevel, NULL);
|
||||
}
|
||||
|
||||
static void toplevel_output_enter(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *toplevel,
|
||||
struct wl_output *output) {
|
||||
// FIXME toplevel can be on multiple outputs, so a list of outputs should be kept
|
||||
zwlr_foreign_toplevel_handle_v1_set_user_data(toplevel, output);
|
||||
}
|
||||
|
||||
static void toplevel_closed(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *toplevel) {
|
||||
struct wl_output *output_wl = (struct wl_output*) data;
|
||||
|
||||
if (output_wl == NULL) {
|
||||
return;
|
||||
}
|
||||
struct dunst_output *output = (struct dunst_output*) wl_output_get_user_data(output_wl);
|
||||
|
||||
if (output == NULL) {
|
||||
return;
|
||||
}
|
||||
dunst_output_set_fullscreen(output, toplevel, false);
|
||||
}
|
||||
|
||||
static void toplevel_state(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *toplevel,
|
||||
struct wl_array *state) {
|
||||
struct wl_output *output_wl = (struct wl_output*) data;
|
||||
if (output_wl == NULL) {
|
||||
return;
|
||||
}
|
||||
struct dunst_output *output = (struct dunst_output*) wl_output_get_user_data(output_wl);
|
||||
if (output == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool fullscreen = false;
|
||||
bool activated = false;
|
||||
enum zwlr_foreign_toplevel_handle_v1_state* element;
|
||||
wl_array_for_each(element, state){
|
||||
if (*element == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN) {
|
||||
fullscreen = true;
|
||||
}
|
||||
if (*element == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED) {
|
||||
activated = true;
|
||||
}
|
||||
}
|
||||
if (fullscreen && activated) {
|
||||
dunst_output_set_fullscreen(output, toplevel, true);
|
||||
|
||||
} else {
|
||||
if (output->fullscreen_toplevel == toplevel) {
|
||||
// this toplevel was fullscreen, but isn't anymore
|
||||
dunst_output_set_fullscreen(output, toplevel, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const struct zwlr_foreign_toplevel_handle_v1_listener foreign_toplevel_handle_listener = {
|
||||
.title = noop,
|
||||
.app_id = noop,
|
||||
.output_enter = toplevel_output_enter,
|
||||
.output_leave = toplevel_output_leave,
|
||||
.state = toplevel_state,
|
||||
.done = noop,
|
||||
.closed = toplevel_closed,
|
||||
};
|
||||
|
||||
static void toplevel_created(void *data,
|
||||
struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *toplevel){
|
||||
zwlr_foreign_toplevel_handle_v1_add_listener(toplevel, &foreign_toplevel_handle_listener, NULL);
|
||||
}
|
||||
|
||||
static void toplevel_finished(void *data,
|
||||
struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1){
|
||||
}
|
||||
|
||||
static const struct zwlr_foreign_toplevel_manager_v1_listener foreign_toplevel_manager_listener = {
|
||||
.toplevel = toplevel_created,
|
||||
.finished = toplevel_finished,
|
||||
};
|
||||
|
||||
static void handle_global(void *data, struct wl_registry *registry,
|
||||
uint32_t name, const char *interface, uint32_t version) {
|
||||
int *count = data;
|
||||
if (*count == 0)
|
||||
{
|
||||
if (strcmp(interface, wl_compositor_interface.name) == 0) {
|
||||
ctx.compositor = wl_registry_bind(registry, name,
|
||||
&wl_compositor_interface, 4);
|
||||
} else if (strcmp(interface, wl_shm_interface.name) == 0) {
|
||||
ctx.shm = wl_registry_bind(registry, name,
|
||||
&wl_shm_interface, 1);
|
||||
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
|
||||
ctx.layer_shell = wl_registry_bind(registry, name,
|
||||
&zwlr_layer_shell_v1_interface, 1);
|
||||
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
||||
ctx.seat = wl_registry_bind(registry, name, &wl_seat_interface, 3);
|
||||
wl_seat_add_listener(ctx.seat, &seat_listener, ctx.seat);
|
||||
add_seat_to_idle_handler(ctx.seat);
|
||||
} else if (strcmp(interface, wl_output_interface.name) == 0) {
|
||||
struct wl_output *output =
|
||||
wl_registry_bind(registry, name, &wl_output_interface, 3);
|
||||
create_output(output, name);
|
||||
} else if (strcmp(interface, org_kde_kwin_idle_interface.name) == 0 &&
|
||||
version >= ORG_KDE_KWIN_IDLE_TIMEOUT_IDLE_SINCE_VERSION) {
|
||||
ctx.idle_handler = wl_registry_bind(registry, name, &org_kde_kwin_idle_interface, 1);
|
||||
}
|
||||
} else {
|
||||
if (strcmp(interface, zwlr_foreign_toplevel_manager_v1_interface.name) == 0 &&
|
||||
version >= ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN_SINCE_VERSION) {
|
||||
// Only bind after the second pass to bind after binding to all the outputs.
|
||||
// This is because otherwise toplevel_enter evens won't be sent.
|
||||
ctx.toplevel_manager = wl_registry_bind(registry, name, &zwlr_foreign_toplevel_manager_v1_interface, 2);
|
||||
zwlr_foreign_toplevel_manager_v1_add_listener(ctx.toplevel_manager, &foreign_toplevel_manager_listener, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_global_remove(void *data, struct wl_registry *registry,
|
||||
uint32_t name) {
|
||||
struct dunst_output *output, *tmp;
|
||||
wl_list_for_each_safe(output, tmp, &ctx.outputs, link) {
|
||||
if (output->global_name == name) {
|
||||
destroy_output(output);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const struct wl_registry_listener registry_listener = {
|
||||
.global = handle_global,
|
||||
.global_remove = handle_global_remove,
|
||||
};
|
||||
|
||||
bool wl_init() {
|
||||
wl_list_init(&ctx.outputs);
|
||||
//wl_list_init(&ctx.seats); // TODO multi-seat support
|
||||
|
||||
ctx.display = wl_display_connect(NULL);
|
||||
|
||||
if (ctx.display == NULL) {
|
||||
LOG_W("failed to create display");
|
||||
return false;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
ctx.registry = wl_display_get_registry(ctx.display);
|
||||
wl_registry_add_listener(ctx.registry, ®istry_listener, &count);
|
||||
wl_display_roundtrip(ctx.display);
|
||||
|
||||
count = 1;
|
||||
// we need a second pass to let for foreign_toplevel (look there for more info)
|
||||
ctx.registry = wl_display_get_registry(ctx.display);
|
||||
wl_registry_add_listener(ctx.registry, ®istry_listener, &count);
|
||||
wl_display_roundtrip(ctx.display);
|
||||
|
||||
if (ctx.compositor == NULL) {
|
||||
LOG_W("compositor doesn't support wl_compositor");
|
||||
return false;
|
||||
}
|
||||
if (ctx.shm == NULL) {
|
||||
LOG_W("compositor doesn't support wl_shm");
|
||||
return false;
|
||||
}
|
||||
if (ctx.layer_shell == NULL) {
|
||||
LOG_W("compositor doesn't support zwlr_layer_shell_v1");
|
||||
return false;
|
||||
}
|
||||
if (ctx.seat == NULL) {
|
||||
LOG_W("no seat was found, so dunst cannot see input");
|
||||
} else {
|
||||
if (ctx.idle_handler == NULL) {
|
||||
LOG_I("compositor doesn't support org_kde_kwin_idle_interface");
|
||||
}
|
||||
else if (ctx.idle_timeout == NULL) {
|
||||
// something went wrong in setting the timeout
|
||||
LOG_W("couldn't set idle timeout");
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.toplevel_manager == NULL) {
|
||||
LOG_W("compositor doesn't support zwlr_foreign_toplevel_v1. Fullscreen detection won't work");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void wl_deinit() {
|
||||
// We need to check if any of these are NULL, since the initialization
|
||||
// could have been aborted half way through, or the compositor doesn't
|
||||
// support some of these features.
|
||||
if (ctx.layer_surface != NULL) {
|
||||
zwlr_layer_surface_v1_destroy(ctx.layer_surface);
|
||||
}
|
||||
if (ctx.surface != NULL) {
|
||||
wl_surface_destroy(ctx.surface);
|
||||
}
|
||||
finish_buffer(&ctx.buffers[0]);
|
||||
finish_buffer(&ctx.buffers[1]);
|
||||
|
||||
// The output list is initialized at the start of init, so no need to
|
||||
// check for NULL
|
||||
struct dunst_output *output, *output_tmp;
|
||||
wl_list_for_each_safe(output, output_tmp, &ctx.outputs, link) {
|
||||
destroy_output(output);
|
||||
}
|
||||
|
||||
if (ctx.seat) {
|
||||
if (ctx.pointer.wl_pointer)
|
||||
wl_pointer_release(ctx.pointer.wl_pointer);
|
||||
wl_seat_release(ctx.seat);
|
||||
ctx.seat = NULL;
|
||||
}
|
||||
|
||||
if (ctx.idle_handler)
|
||||
org_kde_kwin_idle_destroy(ctx.idle_handler);
|
||||
|
||||
if (ctx.idle_timeout)
|
||||
org_kde_kwin_idle_timeout_release(ctx.idle_timeout);
|
||||
|
||||
if (ctx.layer_shell)
|
||||
zwlr_layer_shell_v1_destroy(ctx.layer_shell);
|
||||
|
||||
if (ctx.compositor)
|
||||
wl_compositor_destroy(ctx.compositor);
|
||||
|
||||
if (ctx.shm)
|
||||
wl_shm_destroy(ctx.shm);
|
||||
|
||||
if (ctx.registry)
|
||||
wl_registry_destroy(ctx.registry);
|
||||
|
||||
if (ctx.display)
|
||||
wl_display_disconnect(ctx.display);
|
||||
}
|
||||
|
||||
static void schedule_frame_and_commit();
|
||||
|
||||
// Draw and commit a new frame.
|
||||
static void send_frame() {
|
||||
int scale = wl_get_scale();
|
||||
|
||||
struct dunst_output *output = get_configured_output();
|
||||
int height = ctx.cur_dim.h;
|
||||
int width = ctx.cur_dim.w;
|
||||
|
||||
// There are two cases where we want to tear down the surface: zero
|
||||
// notifications (height = 0) or moving between outputs.
|
||||
if (height == 0 || ctx.layer_surface_output != output) {
|
||||
if (ctx.layer_surface != NULL) {
|
||||
zwlr_layer_surface_v1_destroy(ctx.layer_surface);
|
||||
ctx.layer_surface = NULL;
|
||||
}
|
||||
if (ctx.surface != NULL) {
|
||||
wl_surface_destroy(ctx.surface);
|
||||
ctx.surface = NULL;
|
||||
}
|
||||
ctx.width = ctx.height = 0;
|
||||
ctx.surface_output = NULL;
|
||||
ctx.configured = false;
|
||||
}
|
||||
|
||||
// If there are no notifications, there's no point in recreating the
|
||||
// surface right now.
|
||||
if (height == 0) {
|
||||
ctx.dirty = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// If we've made it here, there is something to draw. If the surface
|
||||
// doesn't exist (this is the first notification, or we moved to a
|
||||
// different output), we need to create it.
|
||||
if (ctx.layer_surface == NULL) {
|
||||
struct wl_output *wl_output = NULL;
|
||||
if (output != NULL) {
|
||||
wl_output = output->wl_output;
|
||||
}
|
||||
ctx.layer_surface_output = output;
|
||||
ctx.surface = wl_compositor_create_surface(ctx.compositor);
|
||||
wl_surface_add_listener(ctx.surface, &surface_listener, NULL);
|
||||
|
||||
ctx.layer_surface = zwlr_layer_shell_v1_get_layer_surface(
|
||||
ctx.layer_shell, ctx.surface, wl_output,
|
||||
settings.layer, "notifications");
|
||||
zwlr_layer_surface_v1_add_listener(ctx.layer_surface,
|
||||
&layer_surface_listener, NULL);
|
||||
|
||||
// Because we're creating a new surface, we aren't going to draw
|
||||
// anything into it during this call. We don't know what size the
|
||||
// surface will be until we've asked the compositor for what we want
|
||||
// and it has responded with what it actually gave us. We also know
|
||||
// that the height we would _like_ to draw (greater than zero, or we
|
||||
// would have bailed already) is different from our ctx.height
|
||||
// (which has to be zero here), so we can fall through to the next
|
||||
// block to let it set the size for us.
|
||||
}
|
||||
|
||||
assert(ctx.layer_surface);
|
||||
|
||||
// We now want to resize the surface if it isn't the right size. If the
|
||||
// surface is brand new, it doesn't even have a size yet. If it already
|
||||
// exists, we might need to resize if the list of notifications has changed
|
||||
// since the last time we drew.
|
||||
if (ctx.height != height || ctx.width != width) {
|
||||
struct dimensions dim = ctx.cur_dim;
|
||||
// Set window size
|
||||
zwlr_layer_surface_v1_set_size(ctx.layer_surface,
|
||||
dim.w, dim.h);
|
||||
|
||||
// TODO Do this only once
|
||||
uint32_t anchor = 0;
|
||||
if (settings.geometry.negative_x) {
|
||||
anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
|
||||
} else {
|
||||
anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT;
|
||||
}
|
||||
|
||||
if (settings.geometry.negative_y) {
|
||||
anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
|
||||
} else {
|
||||
anchor |= ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP;
|
||||
}
|
||||
|
||||
// Put the window at the right position
|
||||
zwlr_layer_surface_v1_set_anchor(ctx.layer_surface,
|
||||
anchor);
|
||||
zwlr_layer_surface_v1_set_margin(ctx.layer_surface,
|
||||
abs(settings.geometry.y), // top
|
||||
abs(settings.geometry.x), // right
|
||||
abs(settings.geometry.y), // bottom
|
||||
abs(settings.geometry.x));// left
|
||||
|
||||
wl_surface_commit(ctx.surface);
|
||||
|
||||
// Now we're going to bail without drawing anything. This gives the
|
||||
// compositor a chance to create the surface and tell us what size we
|
||||
// were actually granted, which may be smaller than what we asked for
|
||||
// depending on the screen size and layout of other layer surfaces.
|
||||
// This information is provided in layer_surface_handle_configure,
|
||||
// which will then call send_frame again. When that call happens, the
|
||||
// layer surface will exist and the height will hopefully match what
|
||||
// we asked for. That means we won't return here, and will actually
|
||||
// draw into the surface down below.
|
||||
// TODO: If the compositor doesn't send a configure with the size we
|
||||
// requested, we'll enter an infinite loop. We need to keep track of
|
||||
// the fact that a request was sent separately from what height we are.
|
||||
wl_display_roundtrip(ctx.display);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(ctx.configured);
|
||||
|
||||
// Yay we can finally draw something!
|
||||
wl_surface_set_buffer_scale(ctx.surface, scale);
|
||||
wl_surface_damage_buffer(ctx.surface, 0, 0, INT32_MAX, INT32_MAX);
|
||||
wl_surface_attach(ctx.surface, ctx.current_buffer->buffer, 0, 0);
|
||||
ctx.current_buffer->busy = true;
|
||||
|
||||
// Schedule a frame in case the state becomes dirty again
|
||||
schedule_frame_and_commit();
|
||||
|
||||
ctx.dirty = false;
|
||||
}
|
||||
|
||||
static void frame_handle_done(void *data, struct wl_callback *callback,
|
||||
uint32_t time) {
|
||||
wl_callback_destroy(ctx.frame_callback);
|
||||
ctx.frame_callback = NULL;
|
||||
|
||||
// Only draw again if we need to
|
||||
if (ctx.dirty) {
|
||||
send_frame();
|
||||
}
|
||||
}
|
||||
|
||||
static const struct wl_callback_listener frame_listener = {
|
||||
.done = frame_handle_done,
|
||||
};
|
||||
|
||||
static void schedule_frame_and_commit() {
|
||||
if (ctx.frame_callback) {
|
||||
return;
|
||||
}
|
||||
if (ctx.surface == NULL) {
|
||||
// We don't yet have a surface, create it immediately
|
||||
send_frame();
|
||||
return;
|
||||
}
|
||||
ctx.frame_callback = wl_surface_frame(ctx.surface);
|
||||
wl_callback_add_listener(ctx.frame_callback, &frame_listener, NULL);
|
||||
wl_surface_commit(ctx.surface);
|
||||
}
|
||||
|
||||
void set_dirty() {
|
||||
if (ctx.dirty) {
|
||||
return;
|
||||
}
|
||||
ctx.dirty = true;
|
||||
schedule_frame_and_commit();
|
||||
}
|
||||
|
||||
window wl_win_create(void) {
|
||||
struct window_wl *win = g_malloc0(sizeof(struct window_wl));
|
||||
|
||||
win->esrc = g_water_wayland_source_new_for_display(NULL, ctx.display);
|
||||
return win;
|
||||
}
|
||||
|
||||
void wl_win_destroy(window winptr) {
|
||||
struct window_wl *win = (struct window_wl*)winptr;
|
||||
|
||||
g_water_wayland_source_free(win->esrc);
|
||||
// FIXME: Dealloc everything
|
||||
g_free(win);
|
||||
}
|
||||
|
||||
void wl_win_show(window win) {
|
||||
// This is here for compatibilty with the X11 output. The window is
|
||||
// already shown in wl_display_surface.
|
||||
}
|
||||
|
||||
void wl_win_hide(window win) {
|
||||
LOG_I("Wayland: Hiding window");
|
||||
ctx.cur_dim.h = 0;
|
||||
set_dirty();
|
||||
wl_display_roundtrip(ctx.display);
|
||||
}
|
||||
|
||||
void wl_display_surface(cairo_surface_t *srf, window winptr, const struct dimensions* dim) {
|
||||
/* struct window_wl *win = (struct window_wl*)winptr; */
|
||||
int scale = wl_get_scale();
|
||||
LOG_D("Buffer size (scaled) %ix%i", dim->w * scale, dim->h * scale);
|
||||
ctx.current_buffer = get_next_buffer(ctx.shm, ctx.buffers,
|
||||
dim->w * scale, dim->h * scale);
|
||||
|
||||
cairo_t *c = ctx.current_buffer->cairo;
|
||||
cairo_save(c);
|
||||
cairo_set_source_surface(c, srf, 0, 0);
|
||||
cairo_rectangle(c, 0, 0, dim->w * scale, dim->h * scale);
|
||||
cairo_fill(c);
|
||||
cairo_restore(c);
|
||||
|
||||
ctx.cur_dim = *dim;
|
||||
|
||||
set_dirty();
|
||||
wl_display_roundtrip(ctx.display);
|
||||
}
|
||||
|
||||
cairo_t* wl_win_get_context(window winptr) {
|
||||
struct window_wl *win = (struct window_wl*)winptr;
|
||||
ctx.current_buffer = get_next_buffer(ctx.shm, ctx.buffers, 500, 500);
|
||||
win->c_surface = ctx.current_buffer->surface;
|
||||
win->c_ctx = ctx.current_buffer->cairo;
|
||||
return win->c_ctx;
|
||||
}
|
||||
|
||||
const struct screen_info* wl_get_active_screen(void) {
|
||||
// TODO Screen size detection
|
||||
static struct screen_info scr = {
|
||||
.w = 1920,
|
||||
.h = 1080,
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.id = 0,
|
||||
.mmh = 500
|
||||
};
|
||||
scr.dpi = wl_get_scale() * 96;
|
||||
return &scr;
|
||||
}
|
||||
|
||||
bool wl_is_idle(void) {
|
||||
LOG_I("Idle status queried: %i", ctx.is_idle);
|
||||
// When the user doesn't have a seat, or their compositor doesn't support the idle
|
||||
// protocol, we'll assume that they are not idle.
|
||||
if (settings.idle_threshold == 0 || ctx.has_idle_monitor == false) {
|
||||
return false;
|
||||
} else {
|
||||
return ctx.is_idle;
|
||||
}
|
||||
}
|
||||
|
||||
bool wl_have_fullscreen_window(void) {
|
||||
bool have_fullscreen = false;
|
||||
|
||||
struct dunst_output *current_output = get_configured_output();
|
||||
|
||||
if (!current_output) {
|
||||
// Cannot detect focused output, so return true if any of the
|
||||
// outputs is fullscreen. This will work even when unfocused
|
||||
// outputs have fullscreen toplevels, since a toplevel has to
|
||||
// be fullscreen and activate to consider an output fullscreen.
|
||||
struct dunst_output *output;
|
||||
wl_list_for_each(output, &ctx.outputs, link) {
|
||||
have_fullscreen |= output->fullscreen;
|
||||
}
|
||||
} else {
|
||||
have_fullscreen = current_output->fullscreen;
|
||||
}
|
||||
|
||||
LOG_D("Fullscreen queried: %i", have_fullscreen);
|
||||
return have_fullscreen;
|
||||
}
|
||||
|
||||
int wl_get_scale(void) {
|
||||
int scale = 0;
|
||||
struct dunst_output *output = get_configured_output();
|
||||
if (output) {
|
||||
scale = output->scale;
|
||||
} else {
|
||||
// return the largest scale
|
||||
struct dunst_output *output;
|
||||
wl_list_for_each(output, &ctx.outputs, link) {
|
||||
scale = MAX(output->scale, scale);
|
||||
}
|
||||
}
|
||||
if (scale <= 0)
|
||||
scale = 1;
|
||||
return scale;
|
||||
}
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
32
src/wayland/wl.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef DUNST_WL_H
|
||||
#define DUNST_WL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <cairo.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "../output.h"
|
||||
|
||||
bool wl_init(void);
|
||||
void wl_deinit(void);
|
||||
|
||||
window wl_win_create(void);
|
||||
void wl_win_destroy(window);
|
||||
|
||||
void wl_win_show(window);
|
||||
void wl_win_hide(window);
|
||||
|
||||
void wl_display_surface(cairo_surface_t *srf, window win, const struct dimensions*);
|
||||
cairo_t* wl_win_get_context(window);
|
||||
|
||||
const struct screen_info* wl_get_active_screen(void);
|
||||
|
||||
bool wl_is_idle(void);
|
||||
bool wl_have_fullscreen_window(void);
|
||||
|
||||
// Return the dpi scaling of the current output. Everything that's rendered
|
||||
// should be multiplied by this value, but don't use it to multiply other
|
||||
// values. All sizes should be in unscaled units.
|
||||
int wl_get_scale(void);
|
||||
#endif
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
@ -69,12 +69,12 @@ static double screen_dpi_get_from_xft(void)
|
||||
return screen_dpi_xft_cache;
|
||||
}
|
||||
|
||||
static double screen_dpi_get_from_monitor(struct screen_info *scr)
|
||||
static double screen_dpi_get_from_monitor(const struct screen_info *scr)
|
||||
{
|
||||
return (double)scr->h * 25.4 / (double)scr->mmh;
|
||||
}
|
||||
|
||||
double screen_dpi_get(struct screen_info *scr)
|
||||
double screen_dpi_get(const struct screen_info *scr)
|
||||
{
|
||||
if ( ! settings.force_xinerama
|
||||
&& settings.per_monitor_dpi)
|
||||
@ -156,6 +156,7 @@ void randr_update(void)
|
||||
screens[i].w = m[i].width;
|
||||
screens[i].h = m[i].height;
|
||||
screens[i].mmh = m[i].mheight;
|
||||
screens[i].dpi = screen_dpi_get(&screens[i]);
|
||||
}
|
||||
|
||||
XRRFreeMonitors(m);
|
||||
@ -300,28 +301,52 @@ bool window_is_fullscreen(Window window)
|
||||
* Select the screen on which the Window
|
||||
* should be displayed.
|
||||
*/
|
||||
struct screen_info *get_active_screen(void)
|
||||
const struct screen_info *get_active_screen(void)
|
||||
{
|
||||
int ret = 0;
|
||||
if (settings.monitor > 0 && settings.monitor < screens_len) {
|
||||
ret = settings.monitor;
|
||||
goto sc_cleanup;
|
||||
}
|
||||
|
||||
x_follow_setup_error_handler();
|
||||
bool force_follow_mouse = false;
|
||||
|
||||
if (settings.f_mode == FOLLOW_NONE) {
|
||||
ret = XDefaultScreen(xctx.dpy);
|
||||
if (settings.monitor >= 0 && settings.monitor < screens_len) {
|
||||
ret = settings.monitor;
|
||||
}
|
||||
goto sc_cleanup;
|
||||
|
||||
} else {
|
||||
int x, y;
|
||||
assert(settings.f_mode == FOLLOW_MOUSE
|
||||
|| settings.f_mode == FOLLOW_KEYBOARD);
|
||||
|
||||
x_follow_setup_error_handler();
|
||||
|
||||
Window root =
|
||||
RootWindow(xctx.dpy, DefaultScreen(xctx.dpy));
|
||||
|
||||
if (settings.f_mode == FOLLOW_MOUSE) {
|
||||
if (settings.f_mode == FOLLOW_KEYBOARD) {
|
||||
Window focused = get_focused_window();
|
||||
|
||||
if (!focused) {
|
||||
/*
|
||||
* If no window is focused, or the focus is set
|
||||
* to dynamically change to the root window of
|
||||
* the screen the pointer is on, force following
|
||||
* the mouse.
|
||||
*/
|
||||
force_follow_mouse = true;
|
||||
} else {
|
||||
Window child_return;
|
||||
/*
|
||||
* The window with input focus might be on a
|
||||
* different X screen. Use the mouse location
|
||||
* in that case.
|
||||
*/
|
||||
force_follow_mouse = !XTranslateCoordinates(
|
||||
xctx.dpy, focused,root,
|
||||
0, 0, &x, &y,
|
||||
&child_return);
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.f_mode == FOLLOW_MOUSE || force_follow_mouse) {
|
||||
int dummy;
|
||||
unsigned int dummy_ui;
|
||||
Window dummy_win;
|
||||
@ -337,21 +362,6 @@ struct screen_info *get_active_screen(void)
|
||||
&dummy_ui);
|
||||
}
|
||||
|
||||
if (settings.f_mode == FOLLOW_KEYBOARD) {
|
||||
|
||||
Window focused = get_focused_window();
|
||||
|
||||
if (focused == 0) {
|
||||
/* something went wrong. Fall back to default */
|
||||
ret = XDefaultScreen(xctx.dpy);
|
||||
goto sc_cleanup;
|
||||
}
|
||||
|
||||
Window child_return;
|
||||
XTranslateCoordinates(xctx.dpy, focused, root,
|
||||
0, 0, &x, &y, &child_return);
|
||||
}
|
||||
|
||||
for (int i = 0; i < screens_len; i++) {
|
||||
if (INRECT(x, y, screens[i].x, screens[i].y,
|
||||
screens[i].w, screens[i].h)) {
|
||||
@ -363,7 +373,7 @@ struct screen_info *get_active_screen(void)
|
||||
goto sc_cleanup;
|
||||
|
||||
/* something seems to be wrong. Fall back to default */
|
||||
ret = XDefaultScreen(xctx.dpy);
|
||||
ret = 0;
|
||||
goto sc_cleanup;
|
||||
}
|
||||
sc_cleanup:
|
||||
@ -379,32 +389,13 @@ sc_cleanup:
|
||||
*/
|
||||
static Window get_focused_window(void)
|
||||
{
|
||||
Window focused = 0;
|
||||
Atom type;
|
||||
int format;
|
||||
unsigned long nitems, bytes_after;
|
||||
unsigned char *prop_return = NULL;
|
||||
Window root = RootWindow(xctx.dpy, DefaultScreen(xctx.dpy));
|
||||
Atom netactivewindow =
|
||||
XInternAtom(xctx.dpy, "_NET_ACTIVE_WINDOW", false);
|
||||
Window focused;
|
||||
int ignored;
|
||||
|
||||
XGetWindowProperty(xctx.dpy,
|
||||
root,
|
||||
netactivewindow,
|
||||
0L,
|
||||
sizeof(Window),
|
||||
false,
|
||||
XA_WINDOW,
|
||||
&type,
|
||||
&format,
|
||||
&nitems,
|
||||
&bytes_after,
|
||||
&prop_return);
|
||||
if (prop_return) {
|
||||
focused = *(Window *)prop_return;
|
||||
XFree(prop_return);
|
||||
}
|
||||
XGetInputFocus(xctx.dpy, &focused, &ignored);
|
||||
|
||||
if (focused == None || focused == PointerRoot)
|
||||
focused = 0;
|
||||
return focused;
|
||||
}
|
||||
|
||||
@ -433,4 +424,4 @@ static int FollowXErrorHandler(Display *display, XErrorEvent *e)
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
@ -7,21 +7,12 @@
|
||||
|
||||
#define INRECT(x,y,rx,ry,rw,rh) ((x) >= (rx) && (x) < (rx)+(rw) && (y) >= (ry) && (y) < (ry)+(rh))
|
||||
|
||||
struct screen_info {
|
||||
int id;
|
||||
int x;
|
||||
int y;
|
||||
unsigned int h;
|
||||
unsigned int mmh;
|
||||
unsigned int w;
|
||||
};
|
||||
|
||||
void init_screens(void);
|
||||
void screen_dpi_xft_cache_purge(void);
|
||||
bool screen_check_event(XEvent *ev);
|
||||
|
||||
struct screen_info *get_active_screen(void);
|
||||
double screen_dpi_get(struct screen_info *scr);
|
||||
const struct screen_info *get_active_screen(void);
|
||||
double screen_dpi_get(const struct screen_info *scr);
|
||||
|
||||
/**
|
||||
* Find the currently focused window and check if it's in
|
||||
@ -45,4 +36,4 @@ bool have_fullscreen_window(void);
|
||||
bool window_is_fullscreen(Window window);
|
||||
|
||||
#endif
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
264
src/x11/x.c
@ -19,6 +19,7 @@
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xresource.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <linux/input-event-codes.h>
|
||||
|
||||
#include "../dbus.h"
|
||||
#include "../draw.h"
|
||||
@ -30,6 +31,7 @@
|
||||
#include "../queues.h"
|
||||
#include "../settings.h"
|
||||
#include "../utils.h"
|
||||
#include "../input.h"
|
||||
|
||||
#include "screen.h"
|
||||
|
||||
@ -67,8 +69,10 @@ static int x_shortcut_tear_down_error_handler(void);
|
||||
static void setopacity(Window win, unsigned long opacity);
|
||||
static void x_handle_click(XEvent ev);
|
||||
|
||||
static void x_win_move(struct window_x11 *win, int x, int y, int width, int height)
|
||||
static void x_win_move(window winptr, int x, int y, int width, int height)
|
||||
{
|
||||
struct window_x11 *win = (struct window_x11*)winptr;
|
||||
|
||||
/* move and resize */
|
||||
if (x != win->dim.x || y != win->dim.y) {
|
||||
XMoveWindow(xctx.dpy, win->xwin, x, y);
|
||||
@ -85,97 +89,99 @@ static void x_win_move(struct window_x11 *win, int x, int y, int width, int heig
|
||||
}
|
||||
}
|
||||
|
||||
static void x_win_round_corners(struct window_x11 *win, const int rad)
|
||||
static void x_win_corners_shape(struct window_x11 *win, const int rad)
|
||||
{
|
||||
const int width = win->dim.w;
|
||||
const int height = win->dim.h;
|
||||
const int dia = 2 * rad;
|
||||
const int degrees = 64; // the factor to convert degrees to XFillArc's angle param
|
||||
|
||||
Pixmap mask = XCreatePixmap(xctx.dpy, win->xwin, width, height, 1);
|
||||
XGCValues xgcv;
|
||||
Pixmap mask;
|
||||
cairo_surface_t * cxbm;
|
||||
cairo_t * cr;
|
||||
Screen * scr;
|
||||
|
||||
GC shape_gc = XCreateGC(xctx.dpy, mask, 0, &xgcv);
|
||||
mask = XCreatePixmap(xctx.dpy, win->xwin, width, height, 1);
|
||||
scr = ScreenOfDisplay(xctx.dpy, win->cur_screen);
|
||||
cxbm = cairo_xlib_surface_create_for_bitmap(xctx.dpy, mask, scr, width, height);
|
||||
cr = cairo_create(cxbm);
|
||||
|
||||
XSetForeground(xctx.dpy, shape_gc, 0);
|
||||
XFillRectangle(xctx.dpy,
|
||||
mask,
|
||||
shape_gc,
|
||||
0,
|
||||
0,
|
||||
width,
|
||||
height);
|
||||
cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
|
||||
XSetForeground(xctx.dpy, shape_gc, 1);
|
||||
cairo_set_source_rgba(cr, 0, 0, 0, 0);
|
||||
cairo_paint(cr);
|
||||
cairo_set_source_rgba(cr, 1, 1, 1, 1);
|
||||
|
||||
/* To mark all pixels, which should get exposed, we
|
||||
* use a circle for every corner and two overlapping rectangles */
|
||||
unsigned const int centercoords[] = {
|
||||
0, 0,
|
||||
width - dia - 1, 0,
|
||||
0, height - dia - 1,
|
||||
width - dia - 1, height - dia - 1,
|
||||
};
|
||||
draw_rounded_rect(cr, 0, 0,
|
||||
width, height,
|
||||
rad, 1,
|
||||
true, true);
|
||||
cairo_fill(cr);
|
||||
|
||||
for (int i = 0; i < sizeof(centercoords)/sizeof(unsigned int); i = i+2) {
|
||||
XFillArc(xctx.dpy,
|
||||
mask,
|
||||
shape_gc,
|
||||
centercoords[i],
|
||||
centercoords[i+1],
|
||||
dia,
|
||||
dia,
|
||||
degrees * 0,
|
||||
degrees * 360);
|
||||
}
|
||||
XFillRectangle(xctx.dpy,
|
||||
mask,
|
||||
shape_gc,
|
||||
rad,
|
||||
0,
|
||||
width-dia,
|
||||
height);
|
||||
XFillRectangle(xctx.dpy,
|
||||
mask,
|
||||
shape_gc,
|
||||
0,
|
||||
rad,
|
||||
width,
|
||||
height-dia);
|
||||
cairo_show_page(cr);
|
||||
cairo_destroy(cr);
|
||||
cairo_surface_flush(cxbm);
|
||||
cairo_surface_destroy(cxbm);
|
||||
|
||||
XShapeCombineMask(xctx.dpy, win->xwin, ShapeBounding, 0, 0, mask, ShapeSet);
|
||||
|
||||
XFreeGC(xctx.dpy, shape_gc);
|
||||
XFreePixmap(xctx.dpy, mask);
|
||||
|
||||
XShapeSelectInput(xctx.dpy,
|
||||
win->xwin, ShapeNotifyMask);
|
||||
}
|
||||
|
||||
void x_display_surface(cairo_surface_t *srf, struct window_x11 *win, const struct dimensions *dim)
|
||||
static void x_win_corners_unshape(window winptr)
|
||||
{
|
||||
struct window_x11 *win = (struct window_x11*)winptr;
|
||||
XRectangle rect = {
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = win->dim.w,
|
||||
.height = win->dim.h };
|
||||
XShapeCombineRectangles(xctx.dpy, win->xwin, ShapeBounding, 0, 0, &rect, 1, ShapeSet, 1);
|
||||
XShapeSelectInput(xctx.dpy,
|
||||
win->xwin, ShapeNotifyMask);
|
||||
}
|
||||
|
||||
static bool x_win_composited(struct window_x11 *win)
|
||||
{
|
||||
char astr[sizeof("_NET_WM_CM_S") / sizeof(char) + 8];
|
||||
Atom cm_sel;
|
||||
|
||||
sprintf(astr, "_NET_WM_CM_S%i", win->cur_screen);
|
||||
cm_sel = XInternAtom(xctx.dpy, astr, true);
|
||||
|
||||
if (cm_sel == None) {
|
||||
return false;
|
||||
} else {
|
||||
return XGetSelectionOwner(xctx.dpy, cm_sel) != None;
|
||||
}
|
||||
}
|
||||
|
||||
void x_display_surface(cairo_surface_t *srf, window winptr, const struct dimensions *dim)
|
||||
{
|
||||
struct window_x11 *win = (struct window_x11*)winptr;
|
||||
x_win_move(win, dim->x, dim->y, dim->w, dim->h);
|
||||
cairo_xlib_surface_set_size(win->root_surface, dim->w, dim->h);
|
||||
|
||||
XClearWindow(xctx.dpy, win->xwin);
|
||||
|
||||
cairo_set_source_surface(win->c_ctx, srf, 0, 0);
|
||||
cairo_paint(win->c_ctx);
|
||||
cairo_show_page(win->c_ctx);
|
||||
|
||||
if (settings.corner_radius != 0)
|
||||
x_win_round_corners(win, dim->corner_radius);
|
||||
if (settings.corner_radius != 0 && ! x_win_composited(win))
|
||||
x_win_corners_shape(win, dim->corner_radius);
|
||||
else
|
||||
x_win_corners_unshape(win);
|
||||
|
||||
XFlush(xctx.dpy);
|
||||
|
||||
}
|
||||
|
||||
bool x_win_visible(struct window_x11 *win)
|
||||
cairo_t* x_win_get_context(window winptr)
|
||||
{
|
||||
return win->visible;
|
||||
}
|
||||
|
||||
cairo_t* x_win_get_context(struct window_x11 *win)
|
||||
{
|
||||
return win->c_ctx;
|
||||
return ((struct window_x11*)win)->c_ctx;
|
||||
}
|
||||
|
||||
static void setopacity(Window win, unsigned long opacity)
|
||||
@ -273,7 +279,7 @@ gboolean x_mainloop_fd_dispatch(GSource *source, GSourceFunc callback, gpointer
|
||||
struct window_x11 *win = ((struct x11_source*) source)->win;
|
||||
|
||||
bool fullscreen_now;
|
||||
struct screen_info *scr;
|
||||
const struct screen_info *scr;
|
||||
XEvent ev;
|
||||
unsigned int state;
|
||||
while (XPending(xctx.dpy) > 0) {
|
||||
@ -394,57 +400,47 @@ bool x_is_idle(void)
|
||||
return xctx.screensaver_info->idle > settings.idle_threshold / 1000;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert x button code to linux event code
|
||||
* Returns 0 if button is not recognized.
|
||||
*/
|
||||
static unsigned int x_mouse_button_to_linux_event_code(unsigned int x_button)
|
||||
{
|
||||
switch (x_button) {
|
||||
case Button1:
|
||||
return BTN_LEFT;
|
||||
case Button2:
|
||||
return BTN_MIDDLE;
|
||||
case Button3:
|
||||
return BTN_RIGHT;
|
||||
default:
|
||||
LOG_W("Unsupported mouse button: '%d'", x_button);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO move to x_mainloop_* */
|
||||
/*
|
||||
* Handle incoming mouse click events
|
||||
*/
|
||||
static void x_handle_click(XEvent ev)
|
||||
{
|
||||
enum mouse_action act;
|
||||
|
||||
switch (ev.xbutton.button) {
|
||||
case Button1:
|
||||
act = settings.mouse_left_click;
|
||||
break;
|
||||
case Button2:
|
||||
act = settings.mouse_middle_click;
|
||||
break;
|
||||
case Button3:
|
||||
act = settings.mouse_right_click;
|
||||
break;
|
||||
default:
|
||||
LOG_W("Unsupported mouse button: '%d'", ev.xbutton.button);
|
||||
return;
|
||||
}
|
||||
|
||||
if (act == MOUSE_CLOSE_ALL) {
|
||||
queues_history_push_all();
|
||||
unsigned int linux_code = x_mouse_button_to_linux_event_code(ev.xbutton.button);
|
||||
|
||||
if (linux_code == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (act == MOUSE_DO_ACTION || act == MOUSE_CLOSE_CURRENT) {
|
||||
int y = settings.separator_height;
|
||||
struct notification *n = NULL;
|
||||
int first = true;
|
||||
for (const GList *iter = queues_get_displayed(); iter;
|
||||
iter = iter->next) {
|
||||
n = iter->data;
|
||||
if (ev.xbutton.y > y && ev.xbutton.y < y + n->displayed_height)
|
||||
break;
|
||||
|
||||
y += n->displayed_height + settings.separator_height;
|
||||
if (first)
|
||||
y += settings.frame_width;
|
||||
}
|
||||
|
||||
if (n) {
|
||||
if (act == MOUSE_CLOSE_CURRENT)
|
||||
queues_notification_close(n, REASON_USER);
|
||||
else
|
||||
notification_do_action(n);
|
||||
}
|
||||
bool button_state;
|
||||
if(ev.type == ButtonRelease) {
|
||||
button_state = false; // button is up
|
||||
} else {
|
||||
// this shouldn't happen, because this function
|
||||
// is only called when it'a a ButtonRelease event
|
||||
button_state = true; // button is down
|
||||
}
|
||||
|
||||
input_handle_click(linux_code, button_state, ev.xbutton.x, ev.xbutton.y);
|
||||
}
|
||||
|
||||
void x_free(void)
|
||||
@ -500,14 +496,16 @@ static void XRM_update_db(void)
|
||||
/*
|
||||
* Setup X11 stuff
|
||||
*/
|
||||
void x_setup(void)
|
||||
bool x_setup(void)
|
||||
{
|
||||
|
||||
/* initialize xctx.dc, font, keyboard, colors */
|
||||
if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
|
||||
LOG_W("No locale support");
|
||||
|
||||
if (!(xctx.dpy = XOpenDisplay(NULL))) {
|
||||
DIE("Cannot open X11 display.");
|
||||
LOG_W("Cannot open X11 display.");
|
||||
return false;
|
||||
}
|
||||
|
||||
x_shortcut_init(&settings.close_ks);
|
||||
@ -526,10 +524,12 @@ void x_setup(void)
|
||||
|
||||
xctx.screensaver_info = XScreenSaverAllocInfo();
|
||||
|
||||
XrmInitialize();
|
||||
XRM_update_db();
|
||||
|
||||
init_screens();
|
||||
x_shortcut_grab(&settings.history_ks);
|
||||
|
||||
XrmInitialize();
|
||||
return true;
|
||||
}
|
||||
|
||||
struct geometry x_parse_geometry(const char *geom_str)
|
||||
@ -634,22 +634,37 @@ GSource* x_win_reg_source(struct window_x11 *win)
|
||||
/*
|
||||
* Setup the window
|
||||
*/
|
||||
struct window_x11 *x_win_create(void)
|
||||
window x_win_create(void)
|
||||
{
|
||||
struct window_x11 *win = g_malloc0(sizeof(struct window_x11));
|
||||
|
||||
Window root;
|
||||
int scr_n;
|
||||
int depth;
|
||||
Visual * vis;
|
||||
XVisualInfo vi;
|
||||
XSetWindowAttributes wa;
|
||||
|
||||
root = RootWindow(xctx.dpy, DefaultScreen(xctx.dpy));
|
||||
scr_n = DefaultScreen(xctx.dpy);
|
||||
root = RootWindow(xctx.dpy, scr_n);
|
||||
if (XMatchVisualInfo(xctx.dpy, scr_n, 32, TrueColor, &vi)) {
|
||||
vis = vi.visual;
|
||||
depth = vi.depth;
|
||||
} else {
|
||||
vis = DefaultVisual(xctx.dpy, scr_n);
|
||||
depth = DefaultDepth(xctx.dpy, scr_n);
|
||||
}
|
||||
|
||||
wa.override_redirect = true;
|
||||
wa.background_pixmap = ParentRelative;
|
||||
wa.background_pixmap = None;
|
||||
wa.background_pixel = 0;
|
||||
wa.border_pixel = 0;
|
||||
wa.colormap = XCreateColormap(xctx.dpy, root, vis, AllocNone);
|
||||
wa.event_mask =
|
||||
ExposureMask | KeyPressMask | VisibilityChangeMask |
|
||||
ButtonReleaseMask | FocusChangeMask| StructureNotifyMask;
|
||||
|
||||
struct screen_info *scr = get_active_screen();
|
||||
const struct screen_info *scr = get_active_screen();
|
||||
win->xwin = XCreateWindow(xctx.dpy,
|
||||
root,
|
||||
scr->x,
|
||||
@ -657,10 +672,10 @@ struct window_x11 *x_win_create(void)
|
||||
scr->w,
|
||||
1,
|
||||
0,
|
||||
DefaultDepth(xctx.dpy, DefaultScreen(xctx.dpy)),
|
||||
depth,
|
||||
CopyFromParent,
|
||||
DefaultVisual(xctx.dpy, DefaultScreen(xctx.dpy)),
|
||||
CWOverrideRedirect | CWBackPixmap | CWEventMask,
|
||||
vis,
|
||||
CWOverrideRedirect | CWBackPixmap | CWBackPixel | CWBorderPixel | CWColormap | CWEventMask,
|
||||
&wa);
|
||||
|
||||
x_set_wm(win->xwin);
|
||||
@ -671,7 +686,7 @@ struct window_x11 *x_win_create(void)
|
||||
(0xffffffff / 100)));
|
||||
|
||||
win->root_surface = cairo_xlib_surface_create(xctx.dpy, win->xwin,
|
||||
DefaultVisual(xctx.dpy, 0),
|
||||
vis,
|
||||
WIDTH, HEIGHT);
|
||||
win->c_ctx = cairo_create(win->root_surface);
|
||||
|
||||
@ -691,11 +706,13 @@ struct window_x11 *x_win_create(void)
|
||||
}
|
||||
XSelectInput(xctx.dpy, root, root_event_mask);
|
||||
|
||||
return win;
|
||||
return (window)win;
|
||||
}
|
||||
|
||||
void x_win_destroy(struct window_x11 *win)
|
||||
void x_win_destroy(window winptr)
|
||||
{
|
||||
struct window_x11 *win = (struct window_x11*)winptr;
|
||||
|
||||
g_source_destroy(win->esrc);
|
||||
g_source_unref(win->esrc);
|
||||
|
||||
@ -709,12 +726,15 @@ void x_win_destroy(struct window_x11 *win)
|
||||
/*
|
||||
* Show the window and grab shortcuts.
|
||||
*/
|
||||
void x_win_show(struct window_x11 *win)
|
||||
void x_win_show(window winptr)
|
||||
{
|
||||
struct window_x11 *win = (struct window_x11*)winptr;
|
||||
|
||||
/* window is already mapped or there's nothing to show */
|
||||
if (win->visible)
|
||||
return;
|
||||
|
||||
|
||||
x_shortcut_grab(&settings.close_ks);
|
||||
x_shortcut_grab(&settings.close_all_ks);
|
||||
x_shortcut_grab(&settings.context_ks);
|
||||
@ -736,13 +756,17 @@ void x_win_show(struct window_x11 *win)
|
||||
|
||||
XMapRaised(xctx.dpy, win->xwin);
|
||||
win->visible = true;
|
||||
|
||||
x_display_surface(win->root_surface, win, &win->dim);
|
||||
}
|
||||
|
||||
/*
|
||||
* Hide the window and ungrab unused keyboard_shortcuts
|
||||
*/
|
||||
void x_win_hide(struct window_x11 *win)
|
||||
void x_win_hide(window winptr)
|
||||
{
|
||||
LOG_I("X11: Hiding window");
|
||||
struct window_x11 *win = (struct window_x11*)winptr;
|
||||
ASSERT_OR_RET(win->visible,);
|
||||
|
||||
x_shortcut_ungrab(&settings.close_ks);
|
||||
@ -918,4 +942,8 @@ static void x_shortcut_init(struct keyboard_shortcut *ks)
|
||||
g_free(str_begin);
|
||||
}
|
||||
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
int x_get_scale(void) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
37
src/x11/x.h
@ -11,6 +11,8 @@
|
||||
#include <X11/X.h>
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
#include "../output.h"
|
||||
|
||||
#include "screen.h"
|
||||
|
||||
struct keyboard_shortcut {
|
||||
@ -24,48 +26,31 @@ struct keyboard_shortcut {
|
||||
// Cyclical dependency
|
||||
#include "../settings.h"
|
||||
|
||||
struct window_x11;
|
||||
|
||||
struct dimensions {
|
||||
int x;
|
||||
int y;
|
||||
int w;
|
||||
int h;
|
||||
|
||||
int corner_radius;
|
||||
};
|
||||
|
||||
struct x_context {
|
||||
Display *dpy;
|
||||
XScreenSaverInfo *screensaver_info;
|
||||
};
|
||||
|
||||
struct color {
|
||||
double r;
|
||||
double g;
|
||||
double b;
|
||||
};
|
||||
|
||||
extern struct x_context xctx;
|
||||
|
||||
/* window */
|
||||
struct window_x11 *x_win_create(void);
|
||||
void x_win_destroy(struct window_x11 *win);
|
||||
window x_win_create(void);
|
||||
void x_win_destroy(window);
|
||||
|
||||
void x_win_show(struct window_x11 *win);
|
||||
void x_win_hide(struct window_x11 *win);
|
||||
void x_win_show(window);
|
||||
void x_win_hide(window);
|
||||
|
||||
void x_display_surface(cairo_surface_t *srf, struct window_x11 *win, const struct dimensions *dim);
|
||||
void x_display_surface(cairo_surface_t *srf, window, const struct dimensions *dim);
|
||||
|
||||
bool x_win_visible(struct window_x11 *win);
|
||||
cairo_t* x_win_get_context(struct window_x11 *win);
|
||||
cairo_t* x_win_get_context(window);
|
||||
|
||||
/* X misc */
|
||||
bool x_is_idle(void);
|
||||
void x_setup(void);
|
||||
bool x_setup(void);
|
||||
void x_free(void);
|
||||
|
||||
struct geometry x_parse_geometry(const char *geom_str);
|
||||
|
||||
int x_get_scale(void);
|
||||
#endif
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
@ -27,6 +27,16 @@
|
||||
unquoted_comment = String with a # comment
|
||||
color_comment = "#ffffff" # comment
|
||||
|
||||
[list]
|
||||
simple = A,simple,list
|
||||
spaces = A, list, with, spaces
|
||||
multiword = A list, with, multiword entries
|
||||
quoted = "A, quoted, list"
|
||||
quoted_with_quotes = "A, list, "with quotes""
|
||||
unquoted_with_quotes = A, list, "with quotes"
|
||||
quoted_comment = "List, with, a" # comment
|
||||
unquoted_comment = List, with, a # comment
|
||||
|
||||
[path]
|
||||
expand_tilde = ~/.path/to/tilde
|
||||
|
||||
|
||||
65
test/dbus.c
@ -6,6 +6,7 @@
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "helpers.h"
|
||||
#include "queues.h"
|
||||
|
||||
extern const char *base;
|
||||
@ -252,33 +253,13 @@ bool dbus_notification_fire(struct dbus_notification *n, uint *id)
|
||||
|
||||
void dbus_notification_set_raw_image(struct dbus_notification *n_dbus, const char *path)
|
||||
{
|
||||
GdkPixbuf *pb = gdk_pixbuf_new_from_file(path, NULL);
|
||||
|
||||
if (!pb)
|
||||
GVariant *hint = notification_setup_raw_image(path);
|
||||
if (!hint)
|
||||
return;
|
||||
|
||||
GVariant *hint_data = g_variant_new_from_data(
|
||||
G_VARIANT_TYPE("ay"),
|
||||
gdk_pixbuf_read_pixels(pb),
|
||||
gdk_pixbuf_get_byte_length(pb),
|
||||
TRUE,
|
||||
(GDestroyNotify) g_object_unref,
|
||||
g_object_ref(pb));
|
||||
|
||||
GVariant *hint = g_variant_new(
|
||||
"(iiibii@ay)",
|
||||
gdk_pixbuf_get_width(pb),
|
||||
gdk_pixbuf_get_height(pb),
|
||||
gdk_pixbuf_get_rowstride(pb),
|
||||
gdk_pixbuf_get_has_alpha(pb),
|
||||
gdk_pixbuf_get_bits_per_sample(pb),
|
||||
gdk_pixbuf_get_n_channels(pb),
|
||||
hint_data);
|
||||
|
||||
g_hash_table_insert(n_dbus->hints,
|
||||
g_strdup("image-data"),
|
||||
g_variant_ref_sink(hint));
|
||||
g_object_unref(pb);
|
||||
}
|
||||
|
||||
/////// TESTS
|
||||
@ -660,6 +641,34 @@ TEST test_hint_raw_image(void)
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* We didn't process the timeout parameter via DBus correctly
|
||||
* and it got limited to an int instead of a long int
|
||||
* See: Issue #646 (The timeout value in dunst wraps around) */
|
||||
TEST test_timeout_overflow(void)
|
||||
{
|
||||
struct notification *n;
|
||||
struct dbus_notification *n_dbus;
|
||||
|
||||
n_dbus = dbus_notification_new();
|
||||
n_dbus->app_name = "dunstteststack";
|
||||
n_dbus->app_icon = "NONE";
|
||||
n_dbus->summary = "test_hint_urgency";
|
||||
n_dbus->body = "Summary of it";
|
||||
n_dbus->expire_timeout = 2147484;
|
||||
gint64 expected_timeout = G_GINT64_CONSTANT(2147484000);
|
||||
|
||||
guint id;
|
||||
ASSERT(dbus_notification_fire(n_dbus, &id));
|
||||
ASSERT(id != 0);
|
||||
|
||||
n = queues_debug_find_notification_by_id(id);
|
||||
ASSERT_EQ_FMT(expected_timeout, n->timeout, "%" G_GINT64_FORMAT);
|
||||
|
||||
dbus_notification_free(n_dbus);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_server_caps(enum markup_mode markup)
|
||||
{
|
||||
GVariant *reply;
|
||||
@ -677,6 +686,7 @@ TEST test_server_caps(enum markup_mode markup)
|
||||
ASSERT(g_strv_contains(capsarray, "actions"));
|
||||
ASSERT(g_strv_contains(capsarray, "body"));
|
||||
ASSERT(g_strv_contains(capsarray, "body-hyperlinks"));
|
||||
ASSERT(g_strv_contains(capsarray, "icon-static"));
|
||||
ASSERT(g_strv_contains(capsarray, "x-dunst-stack-tag"));
|
||||
|
||||
if (settings.markup != MARKUP_NO)
|
||||
@ -790,6 +800,12 @@ TEST assert_methodlists_sorted(void)
|
||||
methods_fdn[i+1].method_name));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i+1 < G_N_ELEMENTS(methods_dunst); i++) {
|
||||
ASSERT(0 > strcmp(
|
||||
methods_dunst[i].method_name,
|
||||
methods_dunst[i+1].method_name));
|
||||
}
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
||||
@ -821,6 +837,7 @@ gpointer run_threaded_tests(gpointer data)
|
||||
RUN_TESTp(test_server_caps, MARKUP_NO);
|
||||
RUN_TEST(test_close_and_signal);
|
||||
RUN_TEST(test_signal_actioninvoked);
|
||||
RUN_TEST(test_timeout_overflow);
|
||||
|
||||
RUN_TEST(assert_methodlists_sorted);
|
||||
|
||||
@ -840,6 +857,10 @@ SUITE(suite_dbus)
|
||||
loop = g_main_loop_new(NULL, false);
|
||||
|
||||
dbus_bus = g_test_dbus_new(G_TEST_DBUS_NONE);
|
||||
|
||||
// workaround bug in glib where stdout output is duplicated
|
||||
// See https://gitlab.gnome.org/GNOME/glib/-/issues/2322
|
||||
fflush(stdout);
|
||||
g_test_dbus_up(dbus_bus);
|
||||
|
||||
thread_tests = g_thread_new("testexecutor", run_threaded_tests, loop);
|
||||
|
||||
@ -1,6 +1,15 @@
|
||||
#define dbus_signal_status_changed(status) signal_sent_stub(status)
|
||||
#include "../src/dunst.c"
|
||||
#include "greatest.h"
|
||||
|
||||
static bool signal_sent = false;
|
||||
|
||||
void signal_sent_stub(struct dunst_status status)
|
||||
{
|
||||
signal_sent = true;
|
||||
return;
|
||||
}
|
||||
|
||||
TEST test_dunst_status(void)
|
||||
{
|
||||
status = (struct dunst_status) {false, false, false};
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
[global]
|
||||
font = Monospace 8
|
||||
allow_markup = no
|
||||
format = "<b>%s</b>\n<i>%b</i>"
|
||||
format = "%s\n%b"
|
||||
sort = yes
|
||||
indicate_hidden = yes
|
||||
alignment = left
|
||||
|
||||
10
test/functional-tests/dunstrc.progress_bar
Normal file
@ -0,0 +1,10 @@
|
||||
[global]
|
||||
font = Monospace 8
|
||||
allow_markup = yes
|
||||
format = "<b>%s</b>\n%b"
|
||||
geometry = "0x5-30+20"
|
||||
icon_position = left
|
||||
progress_bar_min_width = 100
|
||||
progress_bar_max_width = 200
|
||||
progress_bar_frame_width = 5
|
||||
progress_bar_height = 30
|
||||
@ -80,15 +80,17 @@ function markup {
|
||||
killall dunst
|
||||
../../dunst -config dunstrc.markup "200x0+10+10" &
|
||||
../../dunstify -a "dunst tester" "Markup Tests" -u "c"
|
||||
../../dunstify -a "dunst tester" "<b>bold</b> <i>italic</i>"
|
||||
../../dunstify -a "dunst tester" "<b>broken markup</i>"
|
||||
../../dunstify -a "dunst tester" "There should be no markup in the title" -u "c"
|
||||
../../dunstify -a "dunst tester" "Title" "<b>bold</b> <i>italic</i>"
|
||||
../../dunstify -a "dunst tester" "Title" "<a href="github.com"> Github link </a>"
|
||||
../../dunstify -a "dunst tester" "Title" "<b>broken markup</i>"
|
||||
keypress
|
||||
|
||||
killall dunst
|
||||
../../dunst -config dunstrc.nomarkup "200x0+10+10" &
|
||||
../../dunstify -a "dunst tester" -u c "NO Markup Tests"
|
||||
../../dunstify -a "dunst tester" "<b>bold</b><i>italic</i>"
|
||||
../../dunstify -a "dunst tester" "<b>broken markup</i>"
|
||||
../../dunstify -a "dunst tester" -u c "No markup Tests"
|
||||
../../dunstify -a "dunst tester" "<b>Title</b>" "<b>bold</b><i>italic</i>"
|
||||
../../dunstify -a "dunst tester" "<b>Title</b>" "<b>broken markup</i>"
|
||||
keypress
|
||||
|
||||
}
|
||||
@ -160,13 +162,13 @@ function geometry {
|
||||
|
||||
killall dunst
|
||||
../../dunst -config dunstrc.default -geom "-300x1" &
|
||||
../../dunstify -a "dunst tester" -u c "-300x1"
|
||||
../../dunstify -a "dunst tester" -u c -- "-300x1"
|
||||
basic_notifications
|
||||
keypress
|
||||
|
||||
killall dunst
|
||||
../../dunst -config dunstrc.default -geom "-300x1-20-20" &
|
||||
../../dunstify -a "dunst tester" -u c "-300x1-20-20"
|
||||
../../dunstify -a "dunst tester" -u c -- "-300x1-20-20"
|
||||
basic_notifications
|
||||
keypress
|
||||
|
||||
@ -177,12 +179,35 @@ function geometry {
|
||||
keypress
|
||||
}
|
||||
|
||||
function progress_bar {
|
||||
killall dunst
|
||||
../../dunst -config dunstrc.default &
|
||||
../../dunstify -h int:value:0 -a "dunst tester" -u c "Progress bar 0%: "
|
||||
../../dunstify -h int:value:33 -a "dunst tester" -u c "Progress bar 33%: "
|
||||
../../dunstify -h int:value:66 -a "dunst tester" -u c "Progress bar 66%: "
|
||||
../../dunstify -h int:value:100 -a "dunst tester" -u c "Progress bar 100%: "
|
||||
keypress
|
||||
killall dunst
|
||||
../../dunst -config dunstrc.default &
|
||||
../../dunstify -h int:value:33 -a "dunst tester" -u l "Low priority: "
|
||||
../../dunstify -h int:value:33 -a "dunst tester" -u n "Normal priority: "
|
||||
../../dunstify -h int:value:33 -a "dunst tester" -u c "Critical priority: "
|
||||
keypress
|
||||
killall dunst
|
||||
../../dunst -config dunstrc.progress_bar &
|
||||
../../dunstify -h int:value:33 -a "dunst tester" -u n "The progress bar should not be the entire width"
|
||||
../../dunstify -h int:value:33 -a "dunst tester" -u n "You might also notice height and frame size are changed"
|
||||
../../dunstify -h int:value:33 -a "dunst tester" -u c "Short"
|
||||
keypress
|
||||
}
|
||||
|
||||
if [ -n "$1" ]; then
|
||||
while [ -n "$1" ]; do
|
||||
$1
|
||||
shift
|
||||
done
|
||||
else
|
||||
progress_bar
|
||||
geometry
|
||||
corners
|
||||
show_age
|
||||
|
||||
33
test/greenest.awk
Executable file
@ -0,0 +1,33 @@
|
||||
#!/usr/bin/awk -f
|
||||
# Copyright (c) 2016 Scott Vokes <vokes.s@gmail.com>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
BEGIN {
|
||||
GREEN = "\033[32m"
|
||||
RED = "\033[31m"
|
||||
YELLOW = "\033[33m"
|
||||
RESET = "\033[m"
|
||||
}
|
||||
|
||||
/^PASS/ { sub("PASS", GREEN "PASS" RESET) }
|
||||
/^SKIP/ { sub("SKIP", YELLOW "SKIP" RESET) }
|
||||
/^FAIL/ { sub("FAIL", RED "FAIL" RESET) }
|
||||
|
||||
# highlight hexdump difference markers
|
||||
/^[0-9a-f]/ {
|
||||
sub("X", GREEN "X" RESET, $2)
|
||||
gsub("<", GREEN "<" RESET, $0)
|
||||
}
|
||||
|
||||
{ print($0) }
|
||||
35
test/helpers.c
Normal file
@ -0,0 +1,35 @@
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
|
||||
#include "helpers.h"
|
||||
|
||||
GVariant *notification_setup_raw_image(const char *path)
|
||||
{
|
||||
GdkPixbuf *pb = gdk_pixbuf_new_from_file(path, NULL);
|
||||
|
||||
if (!pb)
|
||||
return NULL;
|
||||
|
||||
GVariant *hint_data = g_variant_new_from_data(
|
||||
G_VARIANT_TYPE("ay"),
|
||||
gdk_pixbuf_read_pixels(pb),
|
||||
gdk_pixbuf_get_byte_length(pb),
|
||||
TRUE,
|
||||
(GDestroyNotify) g_object_unref,
|
||||
g_object_ref(pb));
|
||||
|
||||
GVariant *hint = g_variant_new(
|
||||
"(iiibii@ay)",
|
||||
gdk_pixbuf_get_width(pb),
|
||||
gdk_pixbuf_get_height(pb),
|
||||
gdk_pixbuf_get_rowstride(pb),
|
||||
gdk_pixbuf_get_has_alpha(pb),
|
||||
gdk_pixbuf_get_bits_per_sample(pb),
|
||||
gdk_pixbuf_get_n_channels(pb),
|
||||
hint_data);
|
||||
|
||||
g_object_unref(pb);
|
||||
|
||||
return hint;
|
||||
}
|
||||
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
9
test/helpers.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef DUNST_TEST_HELPERS_H
|
||||
#define DUNST_TEST_HELPERS_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
GVariant *notification_setup_raw_image(const char *path);
|
||||
|
||||
#endif
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
174
test/icon.c
@ -12,6 +12,69 @@
|
||||
|
||||
extern const char *base;
|
||||
|
||||
int scale = 1;
|
||||
|
||||
TEST test_get_path_from_icon_null(void){
|
||||
char *result = get_path_from_icon_name(NULL);
|
||||
ASSERT_EQ(result, NULL);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_get_path_from_icon_sorting(void)
|
||||
{
|
||||
const char *iconpath = ICONPREFIX;
|
||||
|
||||
const char* icon_names[] = { "onlypng", "onlysvg", "icon1" };
|
||||
const char* icon_paths[] = { "valid/onlypng.png", "valid/onlysvg.svg", "invalid/icon1.svg" };
|
||||
ASSERT_EQm("Test is incorrect", G_N_ELEMENTS(icon_names), G_N_ELEMENTS(icon_paths));
|
||||
|
||||
for (int i = 0; i < G_N_ELEMENTS(icon_names); i++){
|
||||
gchar *path = g_build_filename(base, iconpath, icon_paths[i], NULL);
|
||||
char *result = get_path_from_icon_name(icon_names[i]);
|
||||
ASSERT(result);
|
||||
ASSERT_EQ(strcmp(result, path), 0);
|
||||
g_free(path);
|
||||
g_free(result);
|
||||
}
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_get_path_from_icon_name(void)
|
||||
{
|
||||
const char *iconpath = ICONPREFIX;
|
||||
|
||||
const char* icon_name = "onlypng";
|
||||
const char* expected_suffix = ".png";
|
||||
char* full_name = g_strconcat(icon_name, expected_suffix, NULL);
|
||||
|
||||
gchar *path = g_build_filename(base, iconpath, "valid", full_name, NULL);
|
||||
char *result = get_path_from_icon_name(icon_name);
|
||||
|
||||
ASSERT(result);
|
||||
ASSERT_EQ(strcmp(result, path), 0);
|
||||
|
||||
g_free(full_name);
|
||||
g_free(path);
|
||||
g_free(result);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_get_path_from_icon_name_full(void)
|
||||
{
|
||||
const char *iconpath = ICONPREFIX;
|
||||
|
||||
gchar *path = g_build_filename(base, iconpath, "valid", "icon1.svg", NULL);
|
||||
|
||||
char *result = get_path_from_icon_name(path);
|
||||
ASSERT(result);
|
||||
ASSERT_EQ(strcmp(result, path), 0);
|
||||
|
||||
g_free(path);
|
||||
g_free(result);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_get_pixbuf_from_file_tilde(void)
|
||||
{
|
||||
const char *home = g_get_home_dir();
|
||||
@ -25,7 +88,7 @@ TEST test_get_pixbuf_from_file_tilde(void)
|
||||
gchar *path = g_build_filename(base, iconpath, "valid", "icon1.svg", NULL);
|
||||
path = string_replace_at(path, 0, strlen(home), "~");
|
||||
|
||||
GdkPixbuf *pixbuf = get_pixbuf_from_file(path);
|
||||
GdkPixbuf *pixbuf = get_pixbuf_from_file(path, scale);
|
||||
g_clear_pointer(&path, g_free);
|
||||
|
||||
ASSERT(pixbuf);
|
||||
@ -40,7 +103,7 @@ TEST test_get_pixbuf_from_file_absolute(void)
|
||||
|
||||
gchar *path = g_build_filename(base, iconpath, "valid", "icon1.svg", NULL);
|
||||
|
||||
GdkPixbuf *pixbuf = get_pixbuf_from_file(path);
|
||||
GdkPixbuf *pixbuf = get_pixbuf_from_file(path, scale);
|
||||
g_clear_pointer(&path, g_free);
|
||||
|
||||
ASSERT(pixbuf);
|
||||
@ -52,7 +115,7 @@ TEST test_get_pixbuf_from_file_absolute(void)
|
||||
|
||||
TEST test_get_pixbuf_from_icon_invalid(void)
|
||||
{
|
||||
GdkPixbuf *pixbuf = get_pixbuf_from_icon("invalid");
|
||||
GdkPixbuf *pixbuf = get_pixbuf_from_icon("invalid", scale);
|
||||
ASSERT(pixbuf == NULL);
|
||||
g_clear_pointer(&pixbuf, g_object_unref);
|
||||
|
||||
@ -61,9 +124,9 @@ TEST test_get_pixbuf_from_icon_invalid(void)
|
||||
|
||||
TEST test_get_pixbuf_from_icon_both(void)
|
||||
{
|
||||
GdkPixbuf *pixbuf = get_pixbuf_from_icon("icon1");
|
||||
ASSERT(pixbuf);
|
||||
ASSERTm("SVG pixbuf hasn't precedence", IS_ICON_SVG(pixbuf));
|
||||
GdkPixbuf *pixbuf = get_pixbuf_from_icon("icon1", scale);
|
||||
// the first icon found is invalid, so the pixbuf is empty
|
||||
ASSERT(!pixbuf);
|
||||
g_clear_pointer(&pixbuf, g_object_unref);
|
||||
|
||||
PASS();
|
||||
@ -71,7 +134,7 @@ TEST test_get_pixbuf_from_icon_both(void)
|
||||
|
||||
TEST test_get_pixbuf_from_icon_onlysvg(void)
|
||||
{
|
||||
GdkPixbuf *pixbuf = get_pixbuf_from_icon("onlysvg");
|
||||
GdkPixbuf *pixbuf = get_pixbuf_from_icon("onlysvg", scale);
|
||||
ASSERT(pixbuf);
|
||||
ASSERTm("SVG pixbuf isn't loaded", IS_ICON_SVG(pixbuf));
|
||||
g_clear_pointer(&pixbuf, g_object_unref);
|
||||
@ -81,7 +144,7 @@ TEST test_get_pixbuf_from_icon_onlysvg(void)
|
||||
|
||||
TEST test_get_pixbuf_from_icon_onlypng(void)
|
||||
{
|
||||
GdkPixbuf *pixbuf = get_pixbuf_from_icon("onlypng");
|
||||
GdkPixbuf *pixbuf = get_pixbuf_from_icon("onlypng", scale);
|
||||
ASSERT(pixbuf);
|
||||
ASSERTm("PNG pixbuf isn't loaded", IS_ICON_PNG(pixbuf));
|
||||
g_clear_pointer(&pixbuf, g_object_unref);
|
||||
@ -92,7 +155,7 @@ TEST test_get_pixbuf_from_icon_onlypng(void)
|
||||
TEST test_get_pixbuf_from_icon_filename(void)
|
||||
{
|
||||
char *icon = g_strconcat(base, "/data/icons/valid.png", NULL);
|
||||
GdkPixbuf *pixbuf = get_pixbuf_from_icon(icon);
|
||||
GdkPixbuf *pixbuf = get_pixbuf_from_icon(icon, scale);
|
||||
ASSERT(pixbuf);
|
||||
ASSERTm("PNG pixbuf isn't loaded", IS_ICON_PNG(pixbuf));
|
||||
g_clear_pointer(&pixbuf, g_object_unref);
|
||||
@ -104,7 +167,7 @@ TEST test_get_pixbuf_from_icon_filename(void)
|
||||
TEST test_get_pixbuf_from_icon_fileuri(void)
|
||||
{
|
||||
char *icon = g_strconcat("file://", base, "/data/icons/valid.svg", NULL);
|
||||
GdkPixbuf *pixbuf = get_pixbuf_from_icon(icon);
|
||||
GdkPixbuf *pixbuf = get_pixbuf_from_icon(icon, scale);
|
||||
ASSERT(pixbuf);
|
||||
ASSERTm("SVG pixbuf isn't loaded", IS_ICON_SVG(pixbuf));
|
||||
g_clear_pointer(&pixbuf, g_object_unref);
|
||||
@ -113,14 +176,80 @@ TEST test_get_pixbuf_from_icon_fileuri(void)
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_icon_size_clamp_too_small(void)
|
||||
{
|
||||
int w = 12, h = 24;
|
||||
bool resized = icon_size_clamp(&w, &h);
|
||||
ASSERT(resized);
|
||||
ASSERT_EQ(w, 16);
|
||||
ASSERT_EQ(h, 32);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_icon_size_clamp_not_necessary(void)
|
||||
{
|
||||
int w = 20, h = 30;
|
||||
bool resized = icon_size_clamp(&w, &h);
|
||||
ASSERT(!resized);
|
||||
ASSERT_EQ(w, 20);
|
||||
ASSERT_EQ(h, 30);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_icon_size_clamp_too_big(void)
|
||||
{
|
||||
int w = 75, h = 150;
|
||||
bool resized = icon_size_clamp(&w, &h);
|
||||
ASSERT(resized);
|
||||
ASSERT_EQ(w, 50);
|
||||
ASSERT_EQ(h, 100);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_icon_size_clamp_too_small_then_too_big(void)
|
||||
{
|
||||
int w = 8, h = 80;
|
||||
bool resized = icon_size_clamp(&w, &h);
|
||||
ASSERT(resized);
|
||||
ASSERT_EQ(w, 10);
|
||||
ASSERT_EQ(h, 100);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_get_pixbuf_from_icon_both_is_scaled(void)
|
||||
{
|
||||
GdkPixbuf *pixbuf = get_pixbuf_from_icon("onlypng", scale);
|
||||
ASSERT(pixbuf);
|
||||
ASSERT_EQ(gdk_pixbuf_get_width(pixbuf), 16);
|
||||
ASSERT_EQ(gdk_pixbuf_get_height(pixbuf), 16);
|
||||
g_clear_pointer(&pixbuf, g_object_unref);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
||||
SUITE(suite_icon)
|
||||
{
|
||||
// set only valid icons in the path
|
||||
settings.icon_path = g_strconcat(
|
||||
base, ICONPREFIX "/valid"
|
||||
":", base, ICONPREFIX "/both",
|
||||
NULL);
|
||||
RUN_TEST(test_get_path_from_icon_name);
|
||||
|
||||
g_clear_pointer(&settings.icon_path, g_free);
|
||||
settings.icon_path = g_strconcat(
|
||||
base, ICONPREFIX "/invalid"
|
||||
":", base, ICONPREFIX "/valid"
|
||||
":", base, ICONPREFIX "/both",
|
||||
NULL);
|
||||
|
||||
RUN_TEST(test_get_path_from_icon_null);
|
||||
RUN_TEST(test_get_path_from_icon_sorting);
|
||||
RUN_TEST(test_get_path_from_icon_name_full);
|
||||
RUN_TEST(test_get_pixbuf_from_file_tilde);
|
||||
RUN_TEST(test_get_pixbuf_from_file_absolute);
|
||||
RUN_TEST(test_get_pixbuf_from_icon_invalid);
|
||||
@ -129,6 +258,31 @@ SUITE(suite_icon)
|
||||
RUN_TEST(test_get_pixbuf_from_icon_onlypng);
|
||||
RUN_TEST(test_get_pixbuf_from_icon_filename);
|
||||
RUN_TEST(test_get_pixbuf_from_icon_fileuri);
|
||||
RUN_TEST(test_icon_size_clamp_not_necessary);
|
||||
|
||||
settings.min_icon_size = 16;
|
||||
settings.max_icon_size = 100;
|
||||
|
||||
RUN_TEST(test_get_pixbuf_from_icon_both_is_scaled);
|
||||
RUN_TEST(test_icon_size_clamp_too_small);
|
||||
RUN_TEST(test_icon_size_clamp_not_necessary);
|
||||
RUN_TEST(test_icon_size_clamp_too_big);
|
||||
RUN_TEST(test_icon_size_clamp_too_small_then_too_big);
|
||||
|
||||
settings.min_icon_size = 16;
|
||||
settings.max_icon_size = 0;
|
||||
|
||||
RUN_TEST(test_icon_size_clamp_too_small);
|
||||
RUN_TEST(test_icon_size_clamp_not_necessary);
|
||||
|
||||
settings.min_icon_size = 0;
|
||||
settings.max_icon_size = 100;
|
||||
|
||||
RUN_TEST(test_icon_size_clamp_not_necessary);
|
||||
RUN_TEST(test_icon_size_clamp_too_big);
|
||||
|
||||
settings.min_icon_size = 0;
|
||||
settings.max_icon_size = 0;
|
||||
|
||||
g_clear_pointer(&settings.icon_path, g_free);
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#include "../src/notification.c"
|
||||
#include "greatest.h"
|
||||
#include "helpers.h"
|
||||
|
||||
#include "../src/option_parser.h"
|
||||
#include "../src/settings.h"
|
||||
@ -124,6 +125,76 @@ TEST test_notification_referencing(void)
|
||||
PASS();
|
||||
}
|
||||
|
||||
|
||||
static struct notification *notification_load_icon_with_scaling(int min_icon_size, int max_icon_size)
|
||||
{
|
||||
struct notification *n = notification_create();
|
||||
|
||||
char *path = g_strconcat(base, "/data/icons/valid.svg", NULL); // 16x16
|
||||
|
||||
GVariant *rawIcon = notification_setup_raw_image(path);
|
||||
|
||||
settings.min_icon_size = min_icon_size;
|
||||
settings.max_icon_size = max_icon_size;
|
||||
notification_icon_replace_data(n, rawIcon);
|
||||
settings.min_icon_size = 0;
|
||||
settings.max_icon_size = 0;
|
||||
|
||||
g_variant_unref(rawIcon);
|
||||
g_free(path);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
TEST test_notification_icon_scaling_toosmall(void)
|
||||
{
|
||||
struct notification *n = notification_load_icon_with_scaling(20, 100);
|
||||
|
||||
ASSERT_EQ(gdk_pixbuf_get_width(n->icon), 20);
|
||||
ASSERT_EQ(gdk_pixbuf_get_height(n->icon), 20);
|
||||
|
||||
notification_unref(n);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
||||
|
||||
TEST test_notification_icon_scaling_toolarge(void)
|
||||
{
|
||||
struct notification *n = notification_load_icon_with_scaling(5, 10);
|
||||
|
||||
ASSERT_EQ(gdk_pixbuf_get_width(n->icon), 10);
|
||||
ASSERT_EQ(gdk_pixbuf_get_height(n->icon), 10);
|
||||
|
||||
notification_unref(n);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_notification_icon_scaling_notconfigured(void)
|
||||
{
|
||||
struct notification *n = notification_load_icon_with_scaling(0, 0);
|
||||
|
||||
ASSERT_EQ(gdk_pixbuf_get_width(n->icon), 16);
|
||||
ASSERT_EQ(gdk_pixbuf_get_height(n->icon), 16);
|
||||
|
||||
notification_unref(n);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_notification_icon_scaling_notneeded(void)
|
||||
{
|
||||
struct notification *n = notification_load_icon_with_scaling(10, 20);
|
||||
|
||||
ASSERT_EQ(gdk_pixbuf_get_width(n->icon), 16);
|
||||
ASSERT_EQ(gdk_pixbuf_get_height(n->icon), 16);
|
||||
|
||||
notification_unref(n);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_notification_format_message(struct notification *n, const char *format, const char *exp)
|
||||
{
|
||||
n->format = format;
|
||||
@ -167,6 +238,10 @@ SUITE(suite_notification)
|
||||
RUN_TEST(test_notification_is_duplicate);
|
||||
RUN_TEST(test_notification_replace_single_field);
|
||||
RUN_TEST(test_notification_referencing);
|
||||
RUN_TEST(test_notification_icon_scaling_toosmall);
|
||||
RUN_TEST(test_notification_icon_scaling_toolarge);
|
||||
RUN_TEST(test_notification_icon_scaling_notconfigured);
|
||||
RUN_TEST(test_notification_icon_scaling_notneeded);
|
||||
|
||||
// TEST notification_format_message
|
||||
struct notification *a = notification_create();
|
||||
|
||||
@ -8,6 +8,7 @@ TEST test_next_section(void)
|
||||
const char *section = NULL;
|
||||
ASSERT_STR_EQ("bool", (section = next_section(section)));
|
||||
ASSERT_STR_EQ("string", (section = next_section(section)));
|
||||
ASSERT_STR_EQ("list", (section = next_section(section)));
|
||||
ASSERT_STR_EQ("path", (section = next_section(section)));
|
||||
ASSERT_STR_EQ("int", (section = next_section(section)));
|
||||
ASSERT_STR_EQ("double", (section = next_section(section)));
|
||||
@ -68,6 +69,55 @@ TEST test_ini_get_string(void)
|
||||
PASS();
|
||||
}
|
||||
|
||||
enum greatest_test_res ARRAY_EQ(char **a, char **b){
|
||||
ASSERT(a);
|
||||
ASSERT(b);
|
||||
int i = 0;
|
||||
while (a[i] && b[i]){
|
||||
ASSERT_STR_EQ(a[i], b[i]);
|
||||
i++;
|
||||
}
|
||||
ASSERT_FALSE(a[i]);
|
||||
ASSERT_FALSE(b[i]);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_ini_get_list(void)
|
||||
{
|
||||
char *list_section = "list";
|
||||
|
||||
char *cmp1[] = {"A", "simple", "list", NULL};
|
||||
char *cmp2[] = {"A", "list", "with", "spaces", NULL};
|
||||
char *cmp3[] = {"A list", "with", "multiword entries", NULL};
|
||||
char *cmp4[] = {"A", "quoted", "list", NULL};
|
||||
char *cmp5[] = {"A", "list", "\"with quotes\"", NULL};
|
||||
char *cmp6[] = {"List", "with", "a", NULL};
|
||||
|
||||
char **ptr;
|
||||
CHECK_CALL(ARRAY_EQ(cmp1, (ptr = ini_get_list(list_section, "simple", ""))));
|
||||
free_string_array(ptr);
|
||||
|
||||
CHECK_CALL(ARRAY_EQ(cmp2, (ptr = ini_get_list(list_section, "spaces", ""))));
|
||||
free_string_array(ptr);
|
||||
|
||||
CHECK_CALL(ARRAY_EQ(cmp3, (ptr = ini_get_list(list_section, "multiword", ""))));
|
||||
free_string_array(ptr);
|
||||
CHECK_CALL(ARRAY_EQ(cmp4, (ptr = ini_get_list(list_section, "quoted", ""))));
|
||||
free_string_array(ptr);
|
||||
|
||||
CHECK_CALL(ARRAY_EQ(cmp5, (ptr = ini_get_list(list_section, "quoted_with_quotes", ""))));
|
||||
free_string_array(ptr);
|
||||
CHECK_CALL(ARRAY_EQ(cmp5, (ptr = ini_get_list(list_section, "unquoted_with_quotes", ""))));
|
||||
free_string_array(ptr);
|
||||
|
||||
CHECK_CALL(ARRAY_EQ(cmp6, (ptr = ini_get_list(list_section, "quoted_comment", ""))));
|
||||
free_string_array(ptr);
|
||||
CHECK_CALL(ARRAY_EQ(cmp6, (ptr = ini_get_list(list_section, "unquoted_comment", ""))));
|
||||
free_string_array(ptr);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_ini_get_path(void)
|
||||
{
|
||||
char *section = "path";
|
||||
@ -151,6 +201,22 @@ TEST test_cmdline_get_string(void)
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_cmdline_get_list(void)
|
||||
{
|
||||
char **ptr;
|
||||
char *cmp1[] = {"A", "simple", "list", "from", "the", "cmdline", NULL};
|
||||
char *cmp2[] = {"A", "list", "with", "spaces", NULL};
|
||||
char *cmp3[] = {"A", "default", "list", NULL};
|
||||
|
||||
CHECK_CALL(ARRAY_EQ(cmp1, (ptr = cmdline_get_list("-list", "", ""))));
|
||||
free_string_array(ptr);
|
||||
CHECK_CALL(ARRAY_EQ(cmp2, (ptr = cmdline_get_list("-list2", "", ""))));
|
||||
free_string_array(ptr);
|
||||
CHECK_CALL(ARRAY_EQ(cmp3, (ptr = cmdline_get_list("-nonexistent", "A, default, list", ""))));
|
||||
free_string_array(ptr);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_cmdline_get_int(void)
|
||||
{
|
||||
ASSERT_EQ(3, cmdline_get_int("-int", 0, ""));
|
||||
@ -221,6 +287,29 @@ TEST test_option_get_string(void)
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_option_get_list(void)
|
||||
{
|
||||
char *list_section = "list";
|
||||
char **ptr;
|
||||
|
||||
char *cmp1[] = {"A", "simple", "list", NULL};
|
||||
char *cmp2[] = {"A", "list", "with", "spaces", NULL};
|
||||
char *cmp3[] = {"A", "simple", "list", "from", "the", "cmdline", NULL};
|
||||
char *cmp4[] = {"A", "default", "list", NULL};
|
||||
|
||||
CHECK_CALL(ARRAY_EQ(cmp1, (ptr = option_get_list(list_section, "simple", "-nonexistent", "", ""))));
|
||||
free_string_array(ptr);
|
||||
CHECK_CALL(ARRAY_EQ(cmp2, (ptr = option_get_list(list_section, "quoted", "-list2", "", ""))));
|
||||
free_string_array(ptr);
|
||||
CHECK_CALL(ARRAY_EQ(cmp3, (ptr = option_get_list(list_section, "simple", "-list", "", ""))));
|
||||
free_string_array(ptr);
|
||||
CHECK_CALL(ARRAY_EQ(cmp3, (ptr = option_get_list(list_section, "simple", "-list/-l", "", ""))));
|
||||
free_string_array(ptr);
|
||||
CHECK_CALL(ARRAY_EQ(cmp4, (ptr = option_get_list(list_section, "nonexistent", "-nonexistent", "A, default, list", ""))));
|
||||
free_string_array(ptr);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_option_get_path(void)
|
||||
{
|
||||
char *section = "path";
|
||||
@ -308,11 +397,13 @@ SUITE(suite_option_parser)
|
||||
RUN_TEST(test_next_section);
|
||||
RUN_TEST(test_ini_get_bool);
|
||||
RUN_TEST(test_ini_get_string);
|
||||
RUN_TEST(test_ini_get_list);
|
||||
RUN_TEST(test_ini_get_path);
|
||||
RUN_TEST(test_ini_get_int);
|
||||
RUN_TEST(test_ini_get_double);
|
||||
char cmdline[] = "dunst -bool -b "
|
||||
"-string \"A simple string from the cmdline\" -s Single_word_string "
|
||||
"-list A,simple,list,from,the,cmdline -list2 \"A, list, with, spaces\" "
|
||||
"-int 3 -i 2 -negative -7 -zeroes 04 -intdecim 2.5 "
|
||||
"-path ~/path/from/cmdline "
|
||||
"-simple_double 2 -double 5.2"
|
||||
@ -322,6 +413,7 @@ SUITE(suite_option_parser)
|
||||
g_shell_parse_argv(&cmdline[0], &argc, &argv, NULL);
|
||||
cmdline_load(argc, argv);
|
||||
RUN_TEST(test_cmdline_get_string);
|
||||
RUN_TEST(test_cmdline_get_list);
|
||||
RUN_TEST(test_cmdline_get_path);
|
||||
RUN_TEST(test_cmdline_get_int);
|
||||
RUN_TEST(test_cmdline_get_double);
|
||||
@ -329,6 +421,7 @@ SUITE(suite_option_parser)
|
||||
RUN_TEST(test_cmdline_create_usage);
|
||||
|
||||
RUN_TEST(test_option_get_string);
|
||||
RUN_TEST(test_option_get_list);
|
||||
RUN_TEST(test_option_get_path);
|
||||
RUN_TEST(test_option_get_int);
|
||||
RUN_TEST(test_option_get_double);
|
||||
|
||||
@ -740,6 +740,91 @@ TEST test_queues_timeout_before_paused(void)
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_queue_find_by_id(void)
|
||||
{
|
||||
struct notification *n;
|
||||
int id;
|
||||
queues_init();
|
||||
|
||||
n = test_notification("n", 0);
|
||||
queues_notification_insert(n);
|
||||
n = test_notification("n1", 0);
|
||||
queues_notification_insert(n);
|
||||
id = n->id;
|
||||
n = test_notification("n2", 0);
|
||||
queues_notification_insert(n);
|
||||
|
||||
n = queues_get_by_id(id);
|
||||
|
||||
ASSERT(n->id == id);
|
||||
ASSERT(!strncmp(n->summary, "n1", 2));
|
||||
|
||||
queues_teardown();
|
||||
PASS();
|
||||
}
|
||||
|
||||
|
||||
void print_queues() {
|
||||
printf("\nQueues:\n");
|
||||
for (GList *iter = g_queue_peek_head_link(QUEUE_WAIT); iter;
|
||||
iter = iter->next) {
|
||||
struct notification *notif = iter->data;
|
||||
printf("waiting %s\n", notif->summary);
|
||||
}
|
||||
}
|
||||
|
||||
// Test if notifications are correctly sorted, even if dunst is paused in
|
||||
// between. See #838 for the issue.
|
||||
TEST test_queue_no_sort_and_pause(void)
|
||||
{
|
||||
// Setting sort to false, this means that notifications will only be
|
||||
// sorted based on time
|
||||
settings.sort = false;
|
||||
settings.geometry.h = 0;
|
||||
struct notification *n;
|
||||
queues_init();
|
||||
|
||||
n = test_notification("n0", 0);
|
||||
queues_notification_insert(n);
|
||||
queues_update(STATUS_NORMAL);
|
||||
|
||||
n = test_notification("n1", 0);
|
||||
queues_notification_insert(n);
|
||||
queues_update(STATUS_NORMAL);
|
||||
|
||||
n = test_notification("n2", 0);
|
||||
queues_notification_insert(n);
|
||||
queues_update(STATUS_PAUSE);
|
||||
|
||||
n = test_notification("n3", 0);
|
||||
queues_notification_insert(n);
|
||||
queues_update(STATUS_PAUSE);
|
||||
/* queues_update(STATUS_NORMAL); */
|
||||
|
||||
n = test_notification("n4", 0);
|
||||
queues_notification_insert(n);
|
||||
queues_update(STATUS_NORMAL);
|
||||
|
||||
QUEUE_LEN_ALL(0, 5, 0);
|
||||
|
||||
const char* order[] = {
|
||||
"n0",
|
||||
"n1",
|
||||
"n2",
|
||||
"n3",
|
||||
"n4",
|
||||
};
|
||||
|
||||
for (int i = 0; i < g_queue_get_length(QUEUE_DISP); i++) {
|
||||
struct notification *notif = g_queue_peek_nth(QUEUE_DISP, i);
|
||||
ASSERTm("Notifications are not in the right order",
|
||||
STR_EQ(notif->summary, order[i]));
|
||||
}
|
||||
|
||||
queues_teardown();
|
||||
PASS();
|
||||
}
|
||||
|
||||
SUITE(suite_queues)
|
||||
{
|
||||
settings.icon_path = "";
|
||||
@ -770,6 +855,8 @@ SUITE(suite_queues)
|
||||
RUN_TEST(test_queues_update_seeping);
|
||||
RUN_TEST(test_queues_update_xmore);
|
||||
RUN_TEST(test_queues_timeout_before_paused);
|
||||
RUN_TEST(test_queue_find_by_id);
|
||||
RUN_TEST(test_queue_no_sort_and_pause);
|
||||
|
||||
settings.icon_path = NULL;
|
||||
}
|
||||
|
||||
@ -1,24 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Throw error any time a command fails
|
||||
set -euo pipefail
|
||||
|
||||
BASE="$(dirname "$(dirname "$(readlink -f "$0")")")"
|
||||
PREFIX="${BASE}/install"
|
||||
DESTDIR="${BASE}/install"
|
||||
PREFIX="/testprefix"
|
||||
SYSCONFDIR="/sysconfdir"
|
||||
|
||||
make -C "${BASE}" SYSTEMD=1 SERVICEDIR_SYSTEMD="${PREFIX}/systemd" SERVICEDIR_DBUS="${PREFIX}/dbus" PREFIX="${PREFIX}" install
|
||||
make -C "${BASE}" SYSTEMD=1 DESTDIR="${DESTDIR}" PREFIX="${PREFIX}" SYSCONFDIR="${SYSCONFDIR}" SERVICEDIR_SYSTEMD="/systemd" SERVICEDIR_DBUS="/dbus" install
|
||||
|
||||
diff -u <(find "${PREFIX}" -type f -printf "%P\n" | sort) - <<EOF
|
||||
bin/dunst
|
||||
diff -u <(find "${DESTDIR}" -type f -printf "%P\n" | sort) - <<EOF
|
||||
dbus/org.knopwob.dunst.service
|
||||
share/dunst/dunstrc
|
||||
share/man/man1/dunst.1
|
||||
sysconfdir/dunst/dunstrc
|
||||
systemd/dunst.service
|
||||
testprefix/bin/dunst
|
||||
testprefix/bin/dunstctl
|
||||
testprefix/bin/dunstify
|
||||
testprefix/share/man/man1/dunst.1
|
||||
testprefix/share/man/man1/dunstctl.1
|
||||
testprefix/share/man/man5/dunst.5
|
||||
EOF
|
||||
# make sure to manually sort the above values
|
||||
|
||||
make -C "${BASE}" SYSTEMD=1 SERVICEDIR_SYSTEMD="${PREFIX}/systemd" SERVICEDIR_DBUS="${PREFIX}/dbus" PREFIX="${PREFIX}" uninstall
|
||||
make -C "${BASE}" SYSTEMD=1 DESTDIR="${DESTDIR}" PREFIX="${PREFIX}" SYSCONFDIR="${SYSCONFDIR}" SERVICEDIR_SYSTEMD="/systemd" SERVICEDIR_DBUS="/dbus" uninstall
|
||||
|
||||
if ! [ -z "$(find "${PREFIX}" -type f)" ]; then
|
||||
if ! [ -z "$(find "${DESTDIR}" -type f)" ]; then
|
||||
echo "Uninstall failed, following files weren't removed"
|
||||
find "${PREFIX}" -type f
|
||||
find "${DESTDIR}" -type f
|
||||
exit 1
|
||||
fi
|
||||
|
||||