Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a62fed33d |
2
.github/ISSUE_TEMPLATE.md
vendored
@ -7,7 +7,7 @@ These program calls might help:
|
|||||||
While the notification gets sent:
|
While the notification gets sent:
|
||||||
`dbus-monitor path=/org/freedesktop/Notifications`
|
`dbus-monitor path=/org/freedesktop/Notifications`
|
||||||
|
|
||||||
If dunst segfaults (please install the debug symbols or install dunst manually again):
|
If dunst segfaults (please install the debug symbols or install dunst manually again):
|
||||||
`gdb -ex run dunst -ex bt`
|
`gdb -ex run dunst -ex bt`
|
||||||
|
|
||||||
* ISSUE DESCRIPTION GOES BELOW THIS LINE * -->
|
* ISSUE DESCRIPTION GOES BELOW THIS LINE * -->
|
||||||
|
|||||||
82
.github/workflows/main.yml
vendored
@ -1,82 +0,0 @@
|
|||||||
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 }}
|
|
||||||
22
.gitignore
vendored
@ -1,19 +1,9 @@
|
|||||||
|
dunst
|
||||||
*.o
|
*.o
|
||||||
*.d
|
|
||||||
*.gcda
|
|
||||||
*.gcno
|
|
||||||
*.gcov
|
|
||||||
/lcov.info
|
|
||||||
|
|
||||||
core
|
core
|
||||||
vgcore.*
|
vgcore.*
|
||||||
|
dunst.1
|
||||||
/docs/*.1
|
org.knopwob.dunst.service
|
||||||
/docs/*.5
|
dunst.systemd.service
|
||||||
/docs/internal/coverage
|
dunstify
|
||||||
/docs/internal/html
|
test/test
|
||||||
/dunst
|
|
||||||
/dunstify
|
|
||||||
/dunst.systemd.service
|
|
||||||
/org.knopwob.dunst.service
|
|
||||||
/test/test
|
|
||||||
|
|||||||
27
.travis.yml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- libdbus-1-dev
|
||||||
|
- libx11-dev
|
||||||
|
- libxrandr-dev
|
||||||
|
- libxinerama-dev
|
||||||
|
- libxss-dev
|
||||||
|
- libxdg-basedir-dev
|
||||||
|
- libglib2.0-dev
|
||||||
|
- libpango1.0-dev
|
||||||
|
- libcairo2-dev
|
||||||
|
- libnotify-dev
|
||||||
|
- libgtk-3-dev
|
||||||
|
dist: trusty
|
||||||
|
sudo: false
|
||||||
|
language: c
|
||||||
|
script: CFLAGS=-Werror make all dunstify test
|
||||||
|
compiler:
|
||||||
|
- gcc
|
||||||
|
- clang
|
||||||
|
notifications:
|
||||||
|
irc:
|
||||||
|
channels:
|
||||||
|
- "chat.freenode.net#dunst"
|
||||||
|
on_success: change
|
||||||
|
on_failure: always
|
||||||
@ -1,86 +0,0 @@
|
|||||||
# Ignore musls' weird error
|
|
||||||
{
|
|
||||||
musl_alpine_libc
|
|
||||||
Memcheck:Free
|
|
||||||
fun:free
|
|
||||||
obj:/lib/ld-musl-x86_64.so.1
|
|
||||||
}
|
|
||||||
|
|
||||||
# rsvg_error_handle_close got fixed in
|
|
||||||
# - GNOME/librsvg@7bf1014
|
|
||||||
# (2018-11-12, first tags: v2.45.0, v2.44.9)
|
|
||||||
# but the release has to seep into the distros
|
|
||||||
{
|
|
||||||
rsvg_error_handle_close
|
|
||||||
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*
|
|
||||||
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_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)
|
|
||||||
# but the release has to seep into the distros
|
|
||||||
{
|
|
||||||
rsvg_error_writehandler
|
|
||||||
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*
|
|
||||||
fun:rsvg_handle_write
|
|
||||||
obj:*/loaders/libpixbufloader-svg.so
|
|
||||||
obj:*/libgdk_pixbuf-2.0.so*
|
|
||||||
fun:gdk_pixbuf_loader_close
|
|
||||||
fun:gdk_pixbuf_get_file_info
|
|
||||||
fun:get_pixbuf_from_file
|
|
||||||
...
|
|
||||||
}
|
|
||||||
158
CHANGELOG.md
@ -3,162 +3,18 @@
|
|||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
### Changed
|
- `ellipsize` option to control how long lines should be ellipsized when `word_wrap` is set to `false`
|
||||||
### Fixed
|
|
||||||
|
|
||||||
## 1.6.1 - 2021-02-21:
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Incorrect version in Makefile
|
- `new_icon` rule being ignored on notifications that had a raw icon
|
||||||
|
- Do not replace format strings, which are in notification content
|
||||||
|
- DBus related memory leaks closed:
|
||||||
|
On the DBus connection, all hints never got freed. For raw icons, dunst saved them three times.
|
||||||
|
|
||||||
## 1.6.0 - 2021-02-21:
|
## Changed
|
||||||
|
- transient hints are now handled
|
||||||
### 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
|
|
||||||
|
|
||||||
- `max_icon_size` not working with dynamic width (#614)
|
|
||||||
- Failure to parse color strings with trailing comments in the config (#626)
|
|
||||||
- Negative width in geometry being ignored (#628)
|
|
||||||
- Incorrect handling of the argument terminator `--` in dunstify
|
|
||||||
- Crash when changing DPI while no notifications are displayed (#630)
|
|
||||||
- Fullscreen status change not being detected in some cases (#613)
|
|
||||||
|
|
||||||
## 1.4.0 - 2019-03-30
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Add support to override `frame_color` via rules (#498)
|
|
||||||
- Support for round corners (#420)
|
|
||||||
- Ability to reference `$HOME` in icon paths with `~/` (#520)
|
|
||||||
- Support to customize the mouse bindings (#530)
|
|
||||||
- Command to toggle pause status (#535)
|
|
||||||
- Ability to automatically replace similar notifications (like volume changes)
|
|
||||||
via `stack_tag` (#552)
|
|
||||||
- Comparison of raw icons for duplicate notifications (#571)
|
|
||||||
- Introduce new desktop-entry filter (#470)
|
|
||||||
- `fullscreen` rule to hide notifications when a fullscreen window is active (#472)
|
|
||||||
- Added `skip_display` rule option to skip initial notification display, and
|
|
||||||
include the notification in the history. (#590)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Notification age not counting the time while the computer was suspended (#492)
|
|
||||||
- Dunst losing always-on-top status on a window manager restart (#160)
|
|
||||||
- Xpm icons not being recognized
|
|
||||||
- When new notifications arrive, but display is full, important notifications don't
|
|
||||||
have to wait for a timeout in a displayed notification (#541)
|
|
||||||
- Dunst hanging while the context menu is open (#456)
|
|
||||||
- Having & inside a notification breaking markup (#546)
|
|
||||||
- `<I> more` notifications don't occupy space anymore, if there is only a single
|
|
||||||
notification waiting to get displayed. The notification gets displayed directly (#467)
|
|
||||||
- Segfault when comparing icon name with a notification with a raw icon (#536)
|
|
||||||
- Icon size can no longer be larger than the notification when a fixed width is specified (#540)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Transient notifications no longer skip history by default (#508)
|
|
||||||
- The notification summary no longer accepts markup (#497)
|
|
||||||
|
|
||||||
### Removed
|
|
||||||
|
|
||||||
- Dependency on libxdg-basedir (#550)
|
|
||||||
|
|
||||||
## 1.3.2 - 2018-05-06
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Crash when trying to load an invalid or corrupt icon (#512)
|
|
||||||
|
|
||||||
## 1.3.1 - 2018-01-30
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Race condition resulting in the service files being empty (#488)
|
|
||||||
|
|
||||||
## 1.3.0 - 2018-01-05
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- `ellipsize` option to control how long lines should be ellipsized when `word_wrap` is set to `false` (#374)
|
|
||||||
- A beginning tilde of a path is now expanded to the home of the current user (#351)
|
|
||||||
- The image-path hint is now respected, as GApplications send their icon only via this link (#447)
|
|
||||||
- The (legacy) image\_data hint is now respected (#353)
|
|
||||||
- If dunst can't acquire the DBus name, dunst prints the PID of the process holding the name (#458 #460)
|
|
||||||
- Increased accuracy of timeouts by using microseconds internally (#379 #291)
|
|
||||||
- Support for specifying timeout values in milliseconds, minutes, hours, or days. (#379)
|
|
||||||
- Support for HTML img tags (via context menu) (#428)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- `new_icon` rule being ignored on notifications that had a raw icon (#423)
|
|
||||||
- Format strings being replaced recursively in some cases (#322 #365)
|
|
||||||
- DBus related memory leaks (#397)
|
|
||||||
- Crash on X11 servers with RandR support less than 1.5. (#413 #364)
|
|
||||||
- Silently reading the default config file, if `-conf` did not specify a valid file (#452)
|
|
||||||
- Notification window flickering when a notification is replaced (#320 #415)
|
|
||||||
- Inaccurate timeout in some cases (#291 #379)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Transient hints are now handled (#343 #310)
|
|
||||||
An additional rule option (`match_transient` and `set_transient`) is added
|
An additional rule option (`match_transient` and `set_transient`) is added
|
||||||
to optionally reset the transient setting
|
to optionally reset the transient setting
|
||||||
- HTML links are now referred to by their text in the context menu rather than numbers (#428)
|
|
||||||
- `icon_folders` setting renamed to `icon_path` (#170)
|
|
||||||
- `config.def.h` and `config.h` got merged (#371)
|
|
||||||
- The dependency on GTK3+ has been removed. Instead of GTK3+, dunst now
|
|
||||||
requires gdk-pixbuf which had been a transient dependency before. (#334
|
|
||||||
#376)
|
|
||||||
- The `_GNU_SOURCE` macros had been removed to make dunst portable to nonGNU systems (#403)
|
|
||||||
- Internal refactorings of the notification queue handling. (#411)
|
|
||||||
- Dunst does now install the systemd and dbus service files into their proper location given
|
|
||||||
by pkg-config. Use `SERVICEDIR_(DBUS|SYSTEMD)` params to overwrite them. (#463)
|
|
||||||
|
|
||||||
## 1.2.0 - 2017-07-12
|
## 1.2.0 - 2017-07-12
|
||||||
|
|
||||||
|
|||||||
50
HACKING.md
@ -1,50 +0,0 @@
|
|||||||
# Important notes on the code
|
|
||||||
|
|
||||||
**You can generate an internal overview with doxygen. For this, use `make doc-doxygen` and you'll find an internal overview of all functions and symbols in `docs/internal/html`.**
|
|
||||||
|
|
||||||
# Comments
|
|
||||||
|
|
||||||
- Comment system is held similar to JavaDoc
|
|
||||||
- Use `@param` to describe all input parameters
|
|
||||||
- Use `@return` to describe the output value
|
|
||||||
- Use `@retval` to describe special return values (like `NULL`)
|
|
||||||
- Documentation comments should start with a double star (`/**`)
|
|
||||||
- Append `()` to function names and prepend variables with `#` to properly reference them in the docs
|
|
||||||
- Add comments to all functions and methods
|
|
||||||
- Markdown inside the comments is allowed and also desired
|
|
||||||
- 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
|
|
||||||
|
|
||||||
### Messages
|
|
||||||
|
|
||||||
- Keep your message in common format: `<problem>: <problematic value/description>`
|
|
||||||
- If you have to write text, single quote values in your sentence.
|
|
||||||
|
|
||||||
### Levels
|
|
||||||
|
|
||||||
For logging, there are printf-like macros `LOG_(E|C|W|M|I|D)`.
|
|
||||||
|
|
||||||
- `LOG_E` (ERROR):
|
|
||||||
- All messages, which lead to immediate abort and are caused by a programming error. The program needs patching and the error is not user recoverable.
|
|
||||||
- e.g.: Switching over an enum, `LOG_E` would go into the default case.
|
|
||||||
- `LOG_C` (CRITICAL):
|
|
||||||
- The program cannot continue to work. It is used in the wrong manner or some outer conditions are not met.
|
|
||||||
- e.g.: `-config` parameter value is unreadable file
|
|
||||||
- `LOG_W` (WARNING):
|
|
||||||
- Something is not in shape, but it's recoverable.
|
|
||||||
- e.g.: A value is not parsable in the config file, which will default.
|
|
||||||
- `LOG_M` (MESSAGE):
|
|
||||||
- Important info, which informs about the state.
|
|
||||||
- e.g.: An empty notification does get removed immediately.
|
|
||||||
- `LOG_I` (INFO):
|
|
||||||
- Mostly unneccessary info, but important to debug (as the user) some use cases.
|
|
||||||
- e.g.: print the notification contents after arriving
|
|
||||||
- `LOG_D` (DEBUG):
|
|
||||||
- Only important during development or tracing some bugs (as the developer).
|
|
||||||
244
Makefile
@ -3,247 +3,103 @@
|
|||||||
|
|
||||||
include config.mk
|
include config.mk
|
||||||
|
|
||||||
VERSION := "1.6.1-non-git"
|
VERSION := "1.2.0-non-git"
|
||||||
ifneq ($(wildcard ./.git/),)
|
ifneq ($(wildcard ./.git/.),)
|
||||||
VERSION := $(shell ${GIT} describe --tags)
|
VERSION := $(shell git describe --tags)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq (,${SYSTEMD})
|
LIBS := $(shell pkg-config --libs ${pkg_config_packs})
|
||||||
# Check for systemctl to avoid discrepancies on systems, where
|
INCS := $(shell pkg-config --cflags ${pkg_config_packs})
|
||||||
# systemd is installed, but systemd.pc is in another package
|
|
||||||
systemctl := $(shell command -v ${SYSTEMCTL} >/dev/null && echo systemctl)
|
|
||||||
ifeq (systemctl,${systemctl})
|
|
||||||
SYSTEMD := 1
|
|
||||||
else
|
|
||||||
SYSTEMD := 0
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
SERVICEDIR_DBUS ?= $(shell $(PKG_CONFIG) dbus-1 --variable=session_bus_services_dir)
|
|
||||||
SERVICEDIR_DBUS := ${SERVICEDIR_DBUS}
|
|
||||||
ifeq (,${SERVICEDIR_DBUS})
|
|
||||||
$(error "Failed to query $(PKG_CONFIG) for package 'dbus-1'!")
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifneq (0,${SYSTEMD})
|
|
||||||
SERVICEDIR_SYSTEMD ?= $(shell $(PKG_CONFIG) systemd --variable=systemduserunitdir)
|
|
||||||
SERVICEDIR_SYSTEMD := ${SERVICEDIR_SYSTEMD}
|
|
||||||
ifeq (,${SERVICEDIR_SYSTEMD})
|
|
||||||
$(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})
|
|
||||||
|
|
||||||
ifneq (clean, $(MAKECMDGOALS))
|
ifneq (clean, $(MAKECMDGOALS))
|
||||||
ifeq ($(and $(INCS),$(LIBS)),)
|
ifeq ($(and $(INCS),$(LIBS)),)
|
||||||
$(error "$(PKG_CONFIG) failed!")
|
$(error "pkg-config failed!")
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
CFLAGS := ${DEFAULT_CPPFLAGS} ${CPPFLAGS} ${DEFAULT_CFLAGS} ${CFLAGS} ${INCS} -MMD -MP
|
CFLAGS += -I. ${INCS}
|
||||||
LDFLAGS := ${DEFAULT_LDFLAGS} ${LDFLAGS} ${LIBS}
|
LDFLAGS+= -L. ${LIBS}
|
||||||
|
|
||||||
|
SRC := $(sort $(shell find src/ -name '*.c'))
|
||||||
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}
|
OBJ := ${SRC:.c=.o}
|
||||||
TEST_SRC := $(sort $(shell ${FIND} test/ -name '*.c'))
|
TEST_SRC := $(sort $(shell find test/ -name '*.c'))
|
||||||
TEST_OBJ := $(TEST_SRC:.c=.o)
|
TEST_OBJ := $(TEST_SRC:.c=.o)
|
||||||
DEPS := ${SRC:.c=.d} ${TEST_SRC:.c=.d}
|
|
||||||
|
|
||||||
|
|
||||||
.PHONY: all debug
|
.PHONY: all debug
|
||||||
all: doc dunst dunstify service
|
all: doc dunst service
|
||||||
|
|
||||||
debug: CFLAGS += ${CPPFLAGS_DEBUG} ${CFLAGS_DEBUG}
|
debug: CFLAGS += ${CFLAGS_DEBUG}
|
||||||
debug: LDFLAGS += ${LDFLAGS_DEBUG}
|
debug: LDFLAGS += ${LDFLAGS_DEBUG}
|
||||||
debug: CPPFLAGS += ${CPPFLAGS_DEBUG}
|
debug: CPPFLAGS += ${CPPFLAGS_DEBUG}
|
||||||
debug: all
|
debug: all
|
||||||
|
|
||||||
-include $(DEPS)
|
.c.o:
|
||||||
|
|
||||||
${OBJ} ${TEST_OBJ}: Makefile config.mk
|
|
||||||
|
|
||||||
%.o: %.c
|
|
||||||
${CC} -o $@ -c $< ${CFLAGS}
|
${CC} -o $@ -c $< ${CFLAGS}
|
||||||
|
|
||||||
|
${OBJ}: config.mk
|
||||||
|
|
||||||
dunst: ${OBJ} main.o
|
dunst: ${OBJ} main.o
|
||||||
${CC} -o ${@} ${OBJ} main.o ${CFLAGS} ${LDFLAGS}
|
${CC} ${CFLAGS} -o $@ ${OBJ} main.o ${LDFLAGS}
|
||||||
|
|
||||||
dunstify: dunstify.o
|
dunstify: dunstify.o
|
||||||
${CC} -o ${@} dunstify.o ${CFLAGS} ${LDFLAGS}
|
${CC} ${CFLAGS} -o $@ dunstify.o ${LDFLAGS}
|
||||||
|
|
||||||
.PHONY: test test-valgrind test-coverage
|
.PHONY: test
|
||||||
test: test/test clean-coverage-run
|
test: test/test
|
||||||
# Make sure an error code is returned when the test fails
|
cd test && ./test
|
||||||
/usr/bin/env bash -c 'set -euo pipefail;\
|
|
||||||
./test/test -v | ./test/greenest.awk '
|
|
||||||
|
|
||||||
test-valgrind: test/test
|
|
||||||
${VALGRIND} \
|
|
||||||
--suppressions=.valgrind.suppressions \
|
|
||||||
--leak-check=full \
|
|
||||||
--show-leak-kinds=definite \
|
|
||||||
--errors-for-leak-kinds=definite \
|
|
||||||
--num-callers=40 \
|
|
||||||
--error-exitcode=123 \
|
|
||||||
./test/test -v
|
|
||||||
|
|
||||||
test-coverage: CFLAGS += -fprofile-arcs -ftest-coverage -O0
|
|
||||||
test-coverage: test
|
|
||||||
|
|
||||||
test-coverage-report: test-coverage
|
|
||||||
mkdir -p docs/internal/coverage
|
|
||||||
${GCOVR} \
|
|
||||||
-r . \
|
|
||||||
--exclude=test \
|
|
||||||
--html \
|
|
||||||
--html-details \
|
|
||||||
-o docs/internal/coverage/index.html
|
|
||||||
|
|
||||||
test/%.o: test/%.c src/%.c
|
|
||||||
${CC} -o $@ -c $< ${CFLAGS}
|
|
||||||
|
|
||||||
test/test: ${OBJ} ${TEST_OBJ}
|
test/test: ${OBJ} ${TEST_OBJ}
|
||||||
${CC} -o ${@} ${TEST_OBJ} $(filter-out ${TEST_OBJ:test/%=src/%},${OBJ}) ${CFLAGS} ${LDFLAGS}
|
${CC} ${CFLAGS} -o $@ ${TEST_OBJ} ${OBJ} ${LDFLAGS}
|
||||||
|
|
||||||
.PHONY: doc doc-doxygen
|
.PHONY: doc
|
||||||
doc: docs/dunst.1 docs/dunst.5 docs/dunstctl.1
|
doc: docs/dunst.1
|
||||||
|
docs/dunst.1: docs/dunst.pod
|
||||||
|
pod2man --name=dunst -c "Dunst Reference" --section=1 --release=${VERSION} $< > $@
|
||||||
|
|
||||||
# Can't dedup this as we need to explicitly provide the name and title text to
|
.PHONY: service
|
||||||
# pod2man :(
|
service:
|
||||||
docs/dunst.1: docs/dunst.1.pod
|
@sed "s|##PREFIX##|$(PREFIX)|" org.knopwob.dunst.service.in > org.knopwob.dunst.service
|
||||||
${POD2MAN} --name=dunst -c "Dunst Reference" --section=1 --release=${VERSION} $< > $@
|
@sed "s|##PREFIX##|$(PREFIX)|" dunst.systemd.service.in > dunst.systemd.service
|
||||||
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:
|
.PHONY: clean clean-dunst clean-dunstify clean-doc clean-tests
|
||||||
${DOXYGEN} docs/internal/Doxyfile
|
clean: clean-dunst clean-dunstify clean-doc clean-tests
|
||||||
|
|
||||||
.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
|
|
||||||
ifneq (0,${SYSTEMD})
|
|
||||||
service: service-systemd
|
|
||||||
service-systemd:
|
|
||||||
@${SED} "s|##PREFIX##|$(PREFIX)|" dunst.systemd.service.in > dunst.systemd.service
|
|
||||||
endif
|
|
||||||
|
|
||||||
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:
|
clean-dunst:
|
||||||
rm -f dunst ${OBJ} main.o main.d ${DEPS}
|
rm -f dunst ${OBJ} main.o
|
||||||
rm -f org.knopwob.dunst.service
|
rm -f org.knopwob.dunst.service
|
||||||
rm -f dunst.systemd.service
|
rm -f dunst.systemd.service
|
||||||
|
|
||||||
clean-dunstify:
|
clean-dunstify:
|
||||||
rm -f dunstify.o
|
rm -f dunstify.o
|
||||||
rm -f dunstify.d
|
|
||||||
rm -f dunstify
|
rm -f dunstify
|
||||||
|
|
||||||
clean-doc:
|
clean-doc:
|
||||||
rm -f docs/dunst.1
|
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
|
|
||||||
|
|
||||||
clean-tests:
|
clean-tests:
|
||||||
rm -f test/test test/*.o test/*.d
|
rm -f test/test test/*.o
|
||||||
|
|
||||||
clean-coverage: clean-coverage-run
|
.PHONY: install install-dunst install-doc install-service uninstall
|
||||||
${FIND} . -type f -name '*.gcno' -delete
|
install: install-dunst install-doc install-service
|
||||||
${FIND} . -type f -name '*.gcna' -delete
|
|
||||||
# Cleans the coverage data before every run to not double count any lines
|
|
||||||
clean-coverage-run:
|
|
||||||
${FIND} . -type f -name '*.gcov' -delete
|
|
||||||
${FIND} . -type f -name '*.gcda' -delete
|
|
||||||
|
|
||||||
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-dunstctl \
|
|
||||||
uninstall-service uninstall-service-dbus uninstall-service-systemd
|
|
||||||
install: install-dunst install-dunstctl install-doc install-service install-dunstify
|
|
||||||
|
|
||||||
install-dunst: dunst doc
|
install-dunst: dunst doc
|
||||||
install -Dm755 dunst ${DESTDIR}${BINDIR}/dunst
|
mkdir -p ${DESTDIR}${PREFIX}/bin
|
||||||
install -Dm644 docs/dunst.1 ${DESTDIR}${MANPREFIX}/man1/dunst.1
|
install -m755 dunst ${DESTDIR}${PREFIX}/bin
|
||||||
install -Dm644 docs/dunst.5 ${DESTDIR}${MANPREFIX}/man5/dunst.5
|
mkdir -p ${DESTDIR}${MANPREFIX}/man1
|
||||||
install -Dm644 docs/dunstctl.1 ${DESTDIR}${MANPREFIX}/man1/dunstctl.1
|
install -m644 docs/dunst.1 ${DESTDIR}${MANPREFIX}/man1
|
||||||
|
|
||||||
install-dunstctl: dunstctl
|
|
||||||
install -Dm755 dunstctl ${DESTDIR}${BINDIR}/dunstctl
|
|
||||||
|
|
||||||
install-doc:
|
install-doc:
|
||||||
install -Dm644 dunstrc ${DESTDIR}${SYSCONFDIR}/dunst/dunstrc
|
mkdir -p ${DESTDIR}${PREFIX}/share/dunst
|
||||||
|
install -m644 dunstrc ${DESTDIR}${PREFIX}/share/dunst
|
||||||
|
|
||||||
install-service: install-service-dbus
|
install-service: service
|
||||||
install-service-dbus: service-dbus
|
mkdir -p ${DESTDIR}${PREFIX}/share/dbus-1/services/
|
||||||
install -Dm644 org.knopwob.dunst.service ${DESTDIR}${SERVICEDIR_DBUS}/org.knopwob.dunst.service
|
install -m644 org.knopwob.dunst.service ${DESTDIR}${PREFIX}/share/dbus-1/services
|
||||||
ifneq (0,${SYSTEMD})
|
install -Dm644 dunst.systemd.service ${DESTDIR}${PREFIX}/lib/systemd/user/dunst.service
|
||||||
install-service: install-service-systemd
|
|
||||||
install-service-systemd: service-systemd
|
|
||||||
install -Dm644 dunst.systemd.service ${DESTDIR}${SERVICEDIR_SYSTEMD}/dunst.service
|
|
||||||
endif
|
|
||||||
|
|
||||||
install-dunstify: dunstify
|
uninstall:
|
||||||
install -Dm755 dunstify ${DESTDIR}${BINDIR}/dunstify
|
rm -f ${DESTDIR}${PREFIX}/bin/dunst
|
||||||
|
|
||||||
uninstall: uninstall-service uninstall-dunstctl
|
|
||||||
rm -f ${DESTDIR}${BINDIR}/dunst
|
|
||||||
rm -f ${DESTDIR}${BINDIR}/dunstify
|
|
||||||
rm -f ${DESTDIR}${MANPREFIX}/man1/dunst.1
|
rm -f ${DESTDIR}${MANPREFIX}/man1/dunst.1
|
||||||
rm -f ${DESTDIR}${MANPREFIX}/man5/dunst.5
|
rm -f ${DESTDIR}${PREFIX}/share/dbus-1/services/org.knopwob.dunst.service
|
||||||
rm -f ${DESTDIR}${MANPREFIX}/man1/dunstctl.1
|
rm -f ${DESTDIR}${PREFIX}/lib/systemd/user/dunst.service
|
||||||
rm -rf ${DESTDIR}${SYSCONFDIR}/dunst
|
rm -rf ${DESTDIR}${PREFIX}/share/dunst
|
||||||
|
|
||||||
uninstall-dunstctl:
|
|
||||||
rm -f ${DESTDIR}${BINDIR}/dunstctl
|
|
||||||
|
|
||||||
uninstall-service: uninstall-service-dbus
|
|
||||||
uninstall-service-dbus:
|
|
||||||
rm -f ${DESTDIR}${SERVICEDIR_DBUS}/org.knopwob.dunst.service
|
|
||||||
|
|
||||||
ifneq (0,${SYSTEMD})
|
|
||||||
uninstall-service: uninstall-service-systemd
|
|
||||||
uninstall-service-systemd:
|
|
||||||
rm -f ${DESTDIR}${SERVICEDIR_SYSTEMD}/dunst.service
|
|
||||||
endif
|
|
||||||
|
|||||||
138
README.md
@ -1,147 +1,51 @@
|
|||||||
[](https://github.com/dunst-project/dunst/actions?query=workflow%3Amain) [](https://codecov.io/gh/dunst-project/dunst)
|
[](https://travis-ci.org/dunst-project/dunst)
|
||||||
|
|
||||||
# Dunst
|
## Dunst
|
||||||
|
|
||||||
<i>A highly configurable and lightweight notification daemon.</i>
|
* [Wiki][wiki]
|
||||||
|
* [Description](#description)
|
||||||

|
* [Compiling](#compiling)
|
||||||
|
|
||||||
## Table of Contents
|
|
||||||
|
|
||||||
* [Features](#features)
|
|
||||||
* [Building](#building)
|
|
||||||
* [Documentation](#documentation)
|
|
||||||
* [Copyright](#copyright)
|
* [Copyright](#copyright)
|
||||||
|
|
||||||
# Features
|
## Description
|
||||||
|
|
||||||
## ⚙️ Highly customizable
|
Dunst is a highly configurable and lightweight notification daemon.
|
||||||
|
|
||||||
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_
|
## Compiling
|
||||||
|
|
||||||
<a href="https://gist.github.com/NNBnh/5f6e601a6a82a6ed43b1959698758141">
|
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):
|
||||||
<img alt="screenshot1" src="contrib/screenshots/screenshot1_cut.png">
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="https://gist.github.com/fwSmit/9127d988b07bcec9d869f2c927d0f616">
|
- dbus
|
||||||
<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
|
|
||||||
|
|
||||||
- dbus (runtime)
|
|
||||||
- libxinerama
|
- libxinerama
|
||||||
- libxrandr
|
- libxrandr
|
||||||
- libxss
|
- libxss
|
||||||
|
- libxdg-basedir
|
||||||
- glib
|
- glib
|
||||||
- pango/cairo
|
- pango/cairo
|
||||||
- libnotify (optional, for dunstify)
|
- libgtk-3-dev
|
||||||
- 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).
|
Checkout the [wiki][wiki] for more information.
|
||||||
|
|
||||||
### Building
|
|
||||||
|
|
||||||
```
|
|
||||||
git clone https://github.com/dunst-project/dunst.git
|
|
||||||
cd dunst
|
|
||||||
make
|
|
||||||
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)`: 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.**
|
|
||||||
|
|
||||||
## Bug reports
|
## Bug reports
|
||||||
|
|
||||||
Please use the [issue tracker][issue-tracker] provided by GitHub to send us bug reports or feature requests.
|
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.
|
||||||
|
|
||||||
## Screenshots
|
## Mantainers
|
||||||
|
|
||||||
<a href="https://gist.github.com/MCotocel/2b34486ae59ccda4319fcb93454d212c">
|
Nikos Tsipinakis <nikos@tsipinakis.com>
|
||||||
<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">
|
Jonathan Lusso <jonilusso@gmail.com>
|
||||||
<img alt="progress" src="https://user-images.githubusercontent.com/23078054/102542111-98b01e00-40b1-11eb-967e-bc952430bd06.png">
|
|
||||||
</a>
|
|
||||||
|
|
||||||
## Maintainers
|
|
||||||
|
|
||||||
- [Nikos Tsipinakis](https://github.com/tsipinakis) <nikos@tsipinakis.com>
|
|
||||||
- [Benedikt Heine](https://github.com/bebehei) <bebe@bebehei.de>
|
|
||||||
|
|
||||||
## Author
|
## Author
|
||||||
|
|
||||||
Written by Sascha Kruse <dunst@knopwob.de>
|
written by Sascha Kruse <dunst@knopwob.de>
|
||||||
|
|
||||||
## Copyright
|
## Copyright
|
||||||
|
|
||||||
Copyright 2013 Sascha Kruse and contributors (see [`LICENSE`](./LICENSE) for licensing information)
|
copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information)
|
||||||
|
|
||||||
|
If you feel that copyrights are violated, please send me an email.
|
||||||
|
|
||||||
[issue-tracker]: https://github.com/dunst-project/dunst/issues
|
[issue-tracker]: https://github.com/dunst-project/dunst/issues
|
||||||
[wiki]: https://github.com/dunst-project/dunst/wiki
|
[wiki]: https://github.com/dunst-project/dunst/wiki
|
||||||
[website]: https://dunst-project.org
|
|
||||||
[FAQ]: https://dunst-project.org/faq
|
|
||||||
|
|||||||
136
RELEASE_NOTES
@ -1,139 +1,3 @@
|
|||||||
===================================================================================
|
|
||||||
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
|
|
||||||
===================================================================================
|
|
||||||
|
|
||||||
There has been significant internal refactoring since the last release which
|
|
||||||
might have introduced some new bugs. Be sure to report anything you find.
|
|
||||||
However, as usual, there has been a lot of bug-fixing and a lot of new features
|
|
||||||
have been added as well. Look at the full changelog for a breakdown.
|
|
||||||
Some important points to note:
|
|
||||||
|
|
||||||
For users:
|
|
||||||
|
|
||||||
* Behavioural changes
|
|
||||||
|
|
||||||
In the previous release we introduced support for clients to mark
|
|
||||||
notifications as 'transient'. Transient notifications used to 1) bypass
|
|
||||||
idle_threshold and 2) not be saved in history.
|
|
||||||
The latter behaviour has been disabled by default and can be re-created using
|
|
||||||
rules if necessary. Transient notifications will now only bypass
|
|
||||||
idle_threshold.
|
|
||||||
|
|
||||||
Additionally, to be compliant with the notification spec, the notification
|
|
||||||
summary no longer accepts markup.
|
|
||||||
|
|
||||||
For maintainers:
|
|
||||||
|
|
||||||
* Dependency on libxdg-basedir has been removed
|
|
||||||
|
|
||||||
===================================================================================
|
|
||||||
Release Notes For v1.3.0
|
|
||||||
===================================================================================
|
|
||||||
|
|
||||||
Version 1.3 is supposed to be fully backwards compatible with 1.2.
|
|
||||||
|
|
||||||
For users:
|
|
||||||
|
|
||||||
* Behavioural changes
|
|
||||||
|
|
||||||
Dunst respects the timeout with millisecond accuracy now. Notifications with
|
|
||||||
a one second timeout are not shown up to three seconds.
|
|
||||||
Additionally you can specify timeout values in milliseconds, seconds, minutes,
|
|
||||||
hours or days using the ms, s, h, or d suffix in the config value
|
|
||||||
respectively.
|
|
||||||
|
|
||||||
Transient notifications time out ignoring the `idle_threshold` setting and are not
|
|
||||||
saved in history. This can be overridden with a rule containing `set_transient = no`.
|
|
||||||
In the same vein there is the `match_transient` condition to match transient
|
|
||||||
notifications via rules.
|
|
||||||
|
|
||||||
A prefixed tilde (`~/`) in path settings (browser, dmenu, script) is interpreted as the
|
|
||||||
home folder of the user.
|
|
||||||
|
|
||||||
* Configuration Options
|
|
||||||
|
|
||||||
`icon_folders` got deprecated and renamed to `icon_path`. `icon_folders` is still
|
|
||||||
supported, but will get removed in future.
|
|
||||||
|
|
||||||
The option `ellipsize` got introduced. It controls where to ellipsize the text of
|
|
||||||
an overlong notification if `word_wrap = no`.
|
|
||||||
|
|
||||||
For maintainers:
|
|
||||||
|
|
||||||
* Dependencies
|
|
||||||
|
|
||||||
The GTK3+ dependency got removed. Instead of this gdk-pixbuf is required
|
|
||||||
explicitly. This had been a transient dependency before.
|
|
||||||
|
|
||||||
In the Makefile, libxrandr is now specified to require version 1.5 or newer.
|
|
||||||
The dependency on libxrandr >= 1.5 is not new, Dunst 1.2.0 required it too
|
|
||||||
but there was no active check for it.
|
|
||||||
|
|
||||||
* Installation process
|
|
||||||
|
|
||||||
The internals of dunst's make installation process have slightly changed. The
|
|
||||||
install routine won't install the service files for DBus and systemd in a hardcoded
|
|
||||||
subdirectory of $PREFIX. It'll now query the `dbus-1` and `systemd` pkg-config
|
|
||||||
packages for those paths and will put it there.
|
|
||||||
|
|
||||||
To overwrite the pkg-config values, you can manually specify another path.
|
|
||||||
Use `SERVICEDIR_(DBUS|SYSTEMD)` vars as parameters to your make calls.
|
|
||||||
|
|
||||||
For all introduced variables, see [the README.md].
|
|
||||||
|
|
||||||
* Portability
|
|
||||||
|
|
||||||
GNU-specific functions have been disabled to make dunst portable to nongnu libc's.
|
|
||||||
|
|
||||||
For a full list of changes see [CHANGELOG.md].
|
|
||||||
|
|
||||||
===================================================================================
|
===================================================================================
|
||||||
Release Notes For v1.2.0
|
Release Notes For v1.2.0
|
||||||
===================================================================================
|
===================================================================================
|
||||||
|
|||||||
94
config.h
@ -1,33 +1,22 @@
|
|||||||
/* see example dunstrc for additional explanations about these options */
|
/* see example dunstrc for additional explanations about these options */
|
||||||
|
|
||||||
struct settings defaults = {
|
settings_t defaults = {
|
||||||
|
|
||||||
.font = "-*-terminus-medium-r-*-*-16-*-*-*-*-*-*-*",
|
.font = "-*-terminus-medium-r-*-*-16-*-*-*-*-*-*-*",
|
||||||
.markup = MARKUP_NO,
|
.markup = MARKUP_NO,
|
||||||
.colors_norm.bg = "#1793D1",
|
.normbgcolor = "#1793D1",
|
||||||
.colors_norm.fg = "#DDDDDD",
|
.normfgcolor = "#DDDDDD",
|
||||||
.colors_norm.highlight = "#1745d1",
|
.critbgcolor = "#ffaaaa",
|
||||||
.colors_crit.bg = "#ffaaaa",
|
.critfgcolor = "#000000",
|
||||||
.colors_crit.fg = "#000000",
|
.lowbgcolor = "#aaaaff",
|
||||||
.colors_crit.highlight = "#ff6666",
|
.lowfgcolor = "#000000",
|
||||||
.colors_low.bg = "#aaaaff",
|
|
||||||
.colors_low.fg = "#000000",
|
|
||||||
.colors_low.highlight = "#7f7fff",
|
|
||||||
.format = "%s %b", /* default format */
|
.format = "%s %b", /* default format */
|
||||||
|
|
||||||
.timeouts = { S2US(10), S2US(10), S2US(0) }, /* low, normal, critical */
|
.timeouts = { 10*G_USEC_PER_SEC, 10*G_USEC_PER_SEC, 0 }, /* low, normal, critical */
|
||||||
.icons = { "dialog-information", "dialog-information", "dialog-warning" }, /* low, normal, critical */
|
.icons = { "dialog-information", "dialog-information", "dialog-warning" }, /* low, normal, critical */
|
||||||
|
|
||||||
.transparency = 0, /* transparency */
|
.transparency = 0, /* transparency */
|
||||||
.geometry = { .x = 0, /* geometry */
|
.geom = "0x0", /* geometry */
|
||||||
.y = 0,
|
|
||||||
.w = 0,
|
|
||||||
.h = 0,
|
|
||||||
.negative_x = 0,
|
|
||||||
.negative_y = 0,
|
|
||||||
.negative_width = 0,
|
|
||||||
.width_set = 0
|
|
||||||
},
|
|
||||||
.title = "Dunst", /* the title of dunst notification windows */
|
.title = "Dunst", /* the title of dunst notification windows */
|
||||||
.class = "Dunst", /* the class of dunst notification windows */
|
.class = "Dunst", /* the class of dunst notification windows */
|
||||||
.shrink = false, /* shrinking */
|
.shrink = false, /* shrinking */
|
||||||
@ -35,27 +24,21 @@ struct settings defaults = {
|
|||||||
.indicate_hidden = true, /* show count of hidden messages */
|
.indicate_hidden = true, /* show count of hidden messages */
|
||||||
.idle_threshold = 0, /* don't timeout notifications when idle for x seconds */
|
.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 */
|
.show_age_threshold = -1, /* show age of notification, when notification is older than x seconds */
|
||||||
.align = ALIGN_LEFT, /* text alignment ALIGN_[LEFT|CENTER|RIGHT] */
|
.align = left, /* text alignment [left/center/right] */
|
||||||
.vertical_alignment = VERTICAL_CENTER, /* vertical content alignment VERTICAL_[TOP|CENTER|BOTTOM] */
|
|
||||||
.sticky_history = true,
|
.sticky_history = true,
|
||||||
.history_length = 20, /* max amount of notifications kept in history */
|
.history_length = 20, /* max amount of notifications kept in history */
|
||||||
.show_indicators = true,
|
.show_indicators = true,
|
||||||
.word_wrap = false,
|
.word_wrap = false,
|
||||||
.ignore_dbusclose = false,
|
.ellipsize = middle,
|
||||||
.ellipsize = ELLIPSE_MIDDLE,
|
|
||||||
.ignore_newline = false,
|
.ignore_newline = false,
|
||||||
.line_height = 0, /* if line height < font height, it will be raised to font height */
|
.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 */
|
.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 */
|
.separator_height = 2, /* height of the separator line between two notifications */
|
||||||
.padding = 0,
|
.padding = 0,
|
||||||
.h_padding = 0, /* horizontal padding */
|
.h_padding = 0, /* horizontal padding */
|
||||||
.text_icon_padding = 0, /* padding between icon and text*/
|
.sep_color = AUTO, /* AUTO, FOREGROUND, FRAME, CUSTOM */
|
||||||
.sep_color = {SEP_AUTO}, /* SEP_AUTO, SEP_FOREGROUND, SEP_FRAME, SEP_CUSTOM */
|
.sep_custom_color_str = NULL,/* custom color if sep_color is set to CUSTOM */
|
||||||
|
|
||||||
.frame_width = 0,
|
.frame_width = 0,
|
||||||
.frame_color = "#888888",
|
.frame_color = "#888888",
|
||||||
@ -74,7 +57,6 @@ struct settings defaults = {
|
|||||||
|
|
||||||
.browser = "/usr/bin/firefox",
|
.browser = "/usr/bin/firefox",
|
||||||
|
|
||||||
.min_icon_size = 0,
|
|
||||||
.max_icon_size = 0,
|
.max_icon_size = 0,
|
||||||
|
|
||||||
/* paths to default icons */
|
/* paths to default icons */
|
||||||
@ -111,26 +93,9 @@ struct settings defaults = {
|
|||||||
.code = 0,.sym = NoSymbol,.is_valid = false
|
.code = 0,.sym = NoSymbol,.is_valid = false
|
||||||
}, /* ignore this */
|
}, /* ignore this */
|
||||||
|
|
||||||
.mouse_left_click = (enum mouse_action []){MOUSE_CLOSE_CURRENT, -1},
|
|
||||||
|
|
||||||
.mouse_middle_click = (enum mouse_action []){MOUSE_DO_ACTION, -1},
|
|
||||||
|
|
||||||
.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[] = {
|
rule_t default_rules[] = {
|
||||||
/* name can be any unique string. It is used to identify
|
/* name can be any unique string. It is used to identify
|
||||||
* the rule in dunstrc to override it there
|
* the rule in dunstrc to override it there
|
||||||
*/
|
*/
|
||||||
@ -147,16 +112,37 @@ struct rule default_rules[] = {
|
|||||||
.timeout = -1,
|
.timeout = -1,
|
||||||
.urgency = -1,
|
.urgency = -1,
|
||||||
.markup = MARKUP_NULL,
|
.markup = MARKUP_NULL,
|
||||||
.history_ignore = -1,
|
.history_ignore = 1,
|
||||||
.match_transient = -1,
|
.match_transient = 1,
|
||||||
.set_transient = -1,
|
.set_transient = -1,
|
||||||
.skip_display = -1,
|
|
||||||
.new_icon = NULL,
|
.new_icon = NULL,
|
||||||
.fg = NULL,
|
.fg = NULL,
|
||||||
.bg = NULL,
|
.bg = NULL,
|
||||||
.format = NULL,
|
.format = NULL,
|
||||||
.script = NULL,
|
.script = NULL,
|
||||||
}
|
},
|
||||||
|
|
||||||
|
/* ignore transient hints in history by default */
|
||||||
|
{
|
||||||
|
.name = "ignore_transient_in_history",
|
||||||
|
.appname = NULL,
|
||||||
|
.summary = NULL,
|
||||||
|
.body = NULL,
|
||||||
|
.icon = NULL,
|
||||||
|
.category = NULL,
|
||||||
|
.msg_urgency = -1,
|
||||||
|
.timeout = -1,
|
||||||
|
.urgency = -1,
|
||||||
|
.markup = MARKUP_NULL,
|
||||||
|
.history_ignore = 1,
|
||||||
|
.match_transient = 1,
|
||||||
|
.set_transient = -1,
|
||||||
|
.new_icon = NULL,
|
||||||
|
.fg = NULL,
|
||||||
|
.bg = NULL,
|
||||||
|
.format = NULL,
|
||||||
|
.script = NULL,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
|||||||
65
config.mk
@ -1,67 +1,38 @@
|
|||||||
# paths
|
# paths
|
||||||
PREFIX ?= /usr/local
|
PREFIX ?= /usr/local
|
||||||
BINDIR ?= ${PREFIX}/bin
|
MANPREFIX = ${PREFIX}/share/man
|
||||||
SYSCONFDIR ?= /etc
|
|
||||||
DATADIR ?= ${PREFIX}/share
|
|
||||||
# around for backwards compatibility
|
|
||||||
MANPREFIX ?= ${DATADIR}/man
|
|
||||||
MANDIR ?= ${MANPREFIX}
|
|
||||||
EXTRA_CFLAGS ?=
|
|
||||||
|
|
||||||
DOXYGEN ?= doxygen
|
|
||||||
FIND ?= find
|
|
||||||
GCOVR ?= gcovr
|
|
||||||
GIT ?= git
|
|
||||||
PKG_CONFIG ?= pkg-config
|
|
||||||
POD2MAN ?= pod2man
|
|
||||||
SED ?= sed
|
|
||||||
SYSTEMCTL ?= systemctl
|
|
||||||
VALGRIND ?= valgrind
|
|
||||||
|
|
||||||
# Disable systemd service file installation,
|
|
||||||
# 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
|
# uncomment to disable parsing of dunstrc
|
||||||
# or use "CFLAGS=-DSTATIC_CONFIG make" to build
|
# or use "CFLAGS=-DSTATIC_CONFIG make" to build
|
||||||
#STATIC= -DSTATIC_CONFIG # Warning: This is deprecated behavior
|
#STATIC= -DSTATIC_CONFIG # Warning: This is deprecated behavior
|
||||||
|
|
||||||
# flags
|
# flags
|
||||||
DEFAULT_CPPFLAGS = -D_DEFAULT_SOURCE -DVERSION=\"${VERSION}\"
|
CPPFLAGS += -D_DEFAULT_SOURCE -DVERSION=\"${VERSION}\"
|
||||||
DEFAULT_CFLAGS = -g --std=gnu99 -pedantic -Wall -Wno-overlength-strings -Os ${STATIC} ${ENABLE_WAYLAND} ${EXTRA_CFLAGS}
|
CFLAGS += -g --std=gnu99 -pedantic -Wall -Wno-overlength-strings -Os ${STATIC} ${CPPFLAGS}
|
||||||
DEFAULT_LDFLAGS = -lm -lrt
|
LDFLAGS += -lm -L${X11LIB}
|
||||||
|
|
||||||
CPPFLAGS_DEBUG := -DDEBUG_BUILD
|
CPPFLAGS_DEBUG := -DDEBUG_BUILD
|
||||||
CFLAGS_DEBUG := -O0
|
CFLAGS_DEBUG := -O0
|
||||||
LDFLAGS_DEBUG :=
|
LDFLAGS_DEBUG :=
|
||||||
|
|
||||||
pkg_config_packs := gio-2.0 \
|
pkg_config_packs := dbus-1 \
|
||||||
gdk-pixbuf-2.0 \
|
gio-2.0 \
|
||||||
"glib-2.0 >= 2.44" \
|
gdk-3.0 \
|
||||||
|
"glib-2.0 >= 2.36" \
|
||||||
pangocairo \
|
pangocairo \
|
||||||
x11 \
|
x11 \
|
||||||
xinerama \
|
xinerama \
|
||||||
xext \
|
|
||||||
"xrandr >= 1.5" \
|
"xrandr >= 1.5" \
|
||||||
xscrnsaver \
|
xscrnsaver
|
||||||
|
|
||||||
|
# check if we need libxdg-basedir
|
||||||
# dunstify also needs libnotify
|
ifeq (,$(findstring STATIC_CONFIG,$(CFLAGS)))
|
||||||
pkg_config_packs += libnotify
|
pkg_config_packs += libxdg-basedir
|
||||||
|
else
|
||||||
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)
|
$(warning STATIC_CONFIG is deprecated behavior. It will get removed in future releases)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# dunstify also needs libnotify
|
||||||
|
ifneq (,$(findstring dunstify,${MAKECMDGOALS}))
|
||||||
|
pkg_config_packs += libnotify
|
||||||
|
endif
|
||||||
|
|||||||
@ -1,255 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
###############################################################################
|
|
||||||
##
|
|
||||||
## Usage
|
|
||||||
##
|
|
||||||
## ./<script_name> [<OPTIONS>]
|
|
||||||
##
|
|
||||||
## If it does not run, give execute permissions to the script with
|
|
||||||
## chmod +x <script_name>. Then run ./<script_name>.
|
|
||||||
##
|
|
||||||
## Options
|
|
||||||
##
|
|
||||||
## -h|--help Optional. Show help message.
|
|
||||||
##
|
|
||||||
## Description
|
|
||||||
##
|
|
||||||
## This script creates a dunst themed user config in $HOME/.config/dunst/
|
|
||||||
## folder, changing dunst basic theming options (like fonts, colors, etc.)
|
|
||||||
## according to your current X resources color palette.
|
|
||||||
##
|
|
||||||
## To make this possible, it reads your current user config
|
|
||||||
## ($HOME/.conf/dunst/dunstrc, which is copied from the default config if
|
|
||||||
## it does not exist) and changes the attributes values with new ones (see
|
|
||||||
## Theming section for more info). Then it dumps the new configuration to
|
|
||||||
## $user_xr_color_conf file.
|
|
||||||
##
|
|
||||||
## Theming
|
|
||||||
##
|
|
||||||
## To change colors and fonts:
|
|
||||||
##
|
|
||||||
## * Firstly you have to ensure that those dunst attributes are defined in
|
|
||||||
## the corresponding sections. For example, to change the frame_color
|
|
||||||
## value from the global section, in $HOME/.config/dunst/dunstrc should
|
|
||||||
## be:
|
|
||||||
##
|
|
||||||
## ...
|
|
||||||
## [global]
|
|
||||||
## ...
|
|
||||||
## frame_color = <value>
|
|
||||||
## ...
|
|
||||||
##
|
|
||||||
## * Then, you can change attribute values as you wish with the
|
|
||||||
## following format in theme_attr_dict:
|
|
||||||
##
|
|
||||||
## ["<sec>-<attr>"]="<val>|$(xrdb_get '<X_res>' '<dev_val>')"
|
|
||||||
##
|
|
||||||
## Each <variable> means the following:
|
|
||||||
##
|
|
||||||
## * sec - current section name e.g.: global.
|
|
||||||
## * attr - current attribute name e.g.: frame-color.
|
|
||||||
## * val - a simple value e.g.: "#fffeee", Monospace, 11...
|
|
||||||
## * X_res - X resource name e.g.: color8.
|
|
||||||
## * dev_val - default value to set if X_res is not found e.g.: #65737e.
|
|
||||||
##
|
|
||||||
## Theming example (you can check theme_attr_dict for other values):
|
|
||||||
##
|
|
||||||
## ["global-frame_color"]="\"$(xrdb_get 'color8' '#65737e')
|
|
||||||
##
|
|
||||||
## The function xrdb_get, searches the first parameter in the X resources
|
|
||||||
## database with appres (command installed from xorg-appres in archlinux
|
|
||||||
## and x11-utils in ubuntu). If the first parameter does not exist, the
|
|
||||||
## function returns the second parameter. For hex colors, is important to
|
|
||||||
## scape " characters for proper functioning of dunst config reader (for
|
|
||||||
## example "#ffeegg").
|
|
||||||
##
|
|
||||||
## You can define X_res variables in $HOME/.Xresources file. For in depth
|
|
||||||
## syntax go to https://wiki.archlinux.org/index.php/X_resources.
|
|
||||||
##
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Check if appres is installed
|
|
||||||
if [ ! "$(command -v appres)" ]; then
|
|
||||||
printf 'Install xorg-appres in archlinux and x11-utils in debian/ubuntu.\n'
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
readonly script_name="$(basename "$0")"
|
|
||||||
readonly base_dir="$(realpath "$(dirname "$0")")"
|
|
||||||
|
|
||||||
# Show ussage
|
|
||||||
usage() {
|
|
||||||
grep -e '^##[^#]*$' "$base_dir/$script_name" | \
|
|
||||||
sed -e "s/^##[[:space:]]\{0,1\}//g" \
|
|
||||||
-e "s/<script_name>/${script_name}/g"
|
|
||||||
exit 2
|
|
||||||
} 2>/dev/null
|
|
||||||
|
|
||||||
# Show help
|
|
||||||
if [ "$#" -gt 0 ]; then
|
|
||||||
if ! [[ "$1" == "--help" || "$1" == "-h" ]]; then
|
|
||||||
printf '\nUnknown option.\n'
|
|
||||||
fi
|
|
||||||
usage
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Function to get resource values
|
|
||||||
xrdb_get () {
|
|
||||||
output="$(appres Dunst | grep "$1:" | head -n1 | cut -f2)"
|
|
||||||
default="$2"
|
|
||||||
printf '%s' "${output:-$default}"
|
|
||||||
}
|
|
||||||
|
|
||||||
#
|
|
||||||
# Attributes dictionary. Add or remove attributes (see header for more info)
|
|
||||||
#
|
|
||||||
declare -A theme_attr_dict=(
|
|
||||||
["global-font"]="$(xrdb_get 'font' 'Monospace') $(xrdb_get '*.font_size:' '11')"
|
|
||||||
["global-frame_width"]="$(xrdb_get 'border_width' '1')"
|
|
||||||
["global-frame_color"]="\"$(xrdb_get 'color8' '#65737e')\""
|
|
||||||
|
|
||||||
["urgency_low-background"]="\"$(xrdb_get 'color0' '#2b303b')\""
|
|
||||||
["urgency_low-foreground"]="\"$(xrdb_get 'color4' '#65737e')\""
|
|
||||||
["urgency_low-frame_color"]="\"$(xrdb_get 'color4' '#65737e')\""
|
|
||||||
|
|
||||||
["urgency_normal-background"]="\"$(xrdb_get 'color0' '#2b303b')\""
|
|
||||||
["urgency_normal-foreground"]="\"$(xrdb_get 'color2' '#a3be8c')\""
|
|
||||||
["urgency_normal-frame_color"]="\"$(xrdb_get 'color2' '#a3be8c')\""
|
|
||||||
|
|
||||||
["urgency_critical-background"]="\"$(xrdb_get 'color0' '#2b303b')\""
|
|
||||||
["urgency_critical-foreground"]="\"$(xrdb_get 'color1' '#bf616a')\""
|
|
||||||
["urgency_critical-frame_color"]="\"$(xrdb_get 'color1' '#bf616a')\""
|
|
||||||
)
|
|
||||||
|
|
||||||
# Attributes dictionary keys.
|
|
||||||
readonly valid_keys="${!theme_attr_dict[@]}"
|
|
||||||
|
|
||||||
#
|
|
||||||
# File paths
|
|
||||||
#
|
|
||||||
# User config dir and file
|
|
||||||
readonly user_conf_dir="${XDG_CONFIG_HOME:-$HOME/.config}/dunst"
|
|
||||||
readonly user_conf="$user_conf_dir/dunstrc"
|
|
||||||
|
|
||||||
# Default config dir and example file
|
|
||||||
example_conf_dir="/usr/share/dunst"
|
|
||||||
example_conf="$example_conf_dir/dunstrc"
|
|
||||||
|
|
||||||
# User xresources color config file
|
|
||||||
readonly user_xr_color_conf="$user_conf_dir/dunstrc_xr_colors"
|
|
||||||
|
|
||||||
|
|
||||||
# Check if the user config directory exists
|
|
||||||
if ! [ -d "$user_conf_dir" ]; then
|
|
||||||
printf 'Creating folder "%s".\n' "$user_conf_dir"
|
|
||||||
mkdir -p "$user_conf_dir"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if the user config file exists
|
|
||||||
if ! [ -f "$user_conf" ]; then
|
|
||||||
printf '"%s" file does not exist.\nChecking for config file example.' \
|
|
||||||
"$user_conf"
|
|
||||||
|
|
||||||
if [ -d "/usr/share/dunst" ]; then
|
|
||||||
# Archlinux default dir and example file
|
|
||||||
example_conf_dir="/usr/share/dunst"
|
|
||||||
|
|
||||||
if [ -f "$example_conf_dir/dunstrc" ]; then
|
|
||||||
example_conf="$example_conf_dir/dunstrc"
|
|
||||||
else
|
|
||||||
printf 'Could not find the example config file in "%s".
|
|
||||||
\nPlease, change $example_conf variable in the script.' \
|
|
||||||
"$example_conf_dir"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
elif [ -d "/usr/share/doc/dunst" ]; then
|
|
||||||
# Debian/Ubuntu default dir
|
|
||||||
example_conf_dir="/usr/share/doc/dunst"
|
|
||||||
|
|
||||||
if [ -f "$example_conf_dir/dunstrc.example.gz" ]; then
|
|
||||||
# Ubuntu <= 17.10 and Debian <= 1.2.0-2 example file:
|
|
||||||
example_conf="$example_conf_dir/dunstrc.example.gz"
|
|
||||||
elif [ -f "$example_conf_dir/dunstrc.gz" ]; then
|
|
||||||
# Ubuntu >= 18.04 and Debian >= 1.3.0-2 example file:
|
|
||||||
example_conf="$example_conf_dir/dunstrc.gz"
|
|
||||||
else
|
|
||||||
printf 'Could not find the example config file in "%s".
|
|
||||||
\nPlease, change $example_conf variable in the script.' \
|
|
||||||
"$example_conf_dir"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
else
|
|
||||||
printf 'Could not find the example config directory.
|
|
||||||
\nPlease, change $example_conf_dir variable in the script.'
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
printf 'Copying example config to "%s".\n' "$user_conf_dir"
|
|
||||||
|
|
||||||
# Get the extension to check if the file is compressed
|
|
||||||
if [[ "${example_conf##*\.}" == "gz" ]]; then
|
|
||||||
# Extract example file to user config file
|
|
||||||
gunzip -c "$example_conf" > "$user_conf"
|
|
||||||
else
|
|
||||||
cp "$example_conf" "$user_conf_dir"
|
|
||||||
fi
|
|
||||||
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Regular expressions
|
|
||||||
readonly re_section_line='^\[(.*)\]$'
|
|
||||||
readonly re_empty_comment_line='(^$)|(^[[:space:]]*(\#)|(;))'
|
|
||||||
readonly re_attribute_line='^([[:space:]]*)([_[:alnum:]]+)'
|
|
||||||
|
|
||||||
# Create an array with the file lines
|
|
||||||
mapfile -t conf < "$user_conf"
|
|
||||||
|
|
||||||
# Iterate over the file lines
|
|
||||||
for idx in "${!conf[@]}"; do
|
|
||||||
# Current line
|
|
||||||
curr_line="${conf[$idx]}"
|
|
||||||
# If we are in a new section:
|
|
||||||
if [[ "$curr_line" =~ $re_section_line ]]; then
|
|
||||||
curr_section="${BASH_REMATCH[1]}"
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
# Skip the line if it is empty or has a comment
|
|
||||||
if [[ "$curr_line" =~ $re_empty_comment_line ]]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
# Get the attribute in the current line
|
|
||||||
[[ "$curr_line" =~ $re_attribute_line ]]
|
|
||||||
curr_attr_name="${BASH_REMATCH[2]}"
|
|
||||||
curr_sett_name="${curr_section}-${curr_attr_name}"
|
|
||||||
# If the current attribute is not in our dictionary, continue
|
|
||||||
case "$valid_keys" in
|
|
||||||
*"$curr_sett_name"*)
|
|
||||||
printf -v conf[$idx] ' %s = %s' \
|
|
||||||
"${curr_attr_name}" \
|
|
||||||
"${theme_attr_dict[$curr_sett_name]}"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
# Create a header for the xr_color config file
|
|
||||||
user_xr_color_conf_content="\
|
|
||||||
###################################
|
|
||||||
#
|
|
||||||
# Config file created with
|
|
||||||
# $script_name wrapper
|
|
||||||
#
|
|
||||||
###################################
|
|
||||||
|
|
||||||
"
|
|
||||||
|
|
||||||
# After everything is completed, write the new config to a file
|
|
||||||
user_xr_color_conf_content+="$(printf '%s\n' "${conf[@]}")"
|
|
||||||
|
|
||||||
printf '%s\n' "$user_xr_color_conf_content" > "$user_xr_color_conf"
|
|
||||||
|
|
||||||
printf '"%s" updated successfully.\n' "$user_xr_color_conf"
|
|
||||||
|
|
||||||
@ -1,75 +0,0 @@
|
|||||||
#!/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
|
|
||||||
|
Before Width: | Height: | Size: 388 KiB |
|
Before Width: | Height: | Size: 406 KiB |
|
Before Width: | Height: | Size: 672 KiB |
|
Before Width: | Height: | Size: 214 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 393 KiB |
172
docs/dunst.1.pod
@ -1,172 +0,0 @@
|
|||||||
=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,20 +1,48 @@
|
|||||||
=head1 NAME
|
=head1 NAME
|
||||||
|
|
||||||
dunst - configuration file
|
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
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
An example configuration file is included (usually /etc/dunst/dunstrc). Note:
|
Dunst is a highly configurable and lightweight notification daemon.
|
||||||
this was previously /usr/share/dunst/dunstrc.
|
|
||||||
|
=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).
|
||||||
To change the configuration, copy this file to ~/.config/dunst/dunstrc and edit
|
To change the configuration, copy this file to ~/.config/dunst/dunstrc and edit
|
||||||
it accordingly.
|
it accordingly.
|
||||||
|
|
||||||
The configuration is divided into sections in an ini-like format. The 'global'
|
The configuration is divided into sections in an ini-like format. The 'global'
|
||||||
section contains most general settings while the setions 'urgency_low',
|
section contains most general settings while the 'shortcuts' sections contains
|
||||||
'urgency_normal' and 'urgency_critical' are for low, normal and critical urgency
|
all keyboard configuration and the 'experimental' section all the features that
|
||||||
notifications respectively. The 'shortcuts' section (deprecated) contains all
|
have not yet been tested thoroughly.
|
||||||
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
|
Any section that is not one of the above is assumed to be a rule, see RULES for
|
||||||
more details.
|
more details.
|
||||||
@ -22,6 +50,24 @@ more details.
|
|||||||
For backwards compatibility reasons the section name 'frame' is considered bound
|
For backwards compatibility reasons the section name 'frame' is considered bound
|
||||||
and can't be used as a rule.
|
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
|
=head2 Global section
|
||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
@ -36,10 +82,6 @@ starts at 0. See the B<follow> setting.
|
|||||||
Defines where the notifications should be placed in a multi-monitor setup. All
|
Defines where the notifications should be placed in a multi-monitor setup. All
|
||||||
values except I<none> override the B<monitor> setting.
|
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
|
=over 4
|
||||||
|
|
||||||
=item B<none>
|
=item B<none>
|
||||||
@ -100,32 +142,6 @@ the notification on the left border of the screen while a horizontal offset of
|
|||||||
|
|
||||||
=back
|
=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)
|
=item B<indicate_hidden> (values: [true/false], default: true)
|
||||||
|
|
||||||
If this is set to true, a notification indicating how many notifications are
|
If this is set to true, a notification indicating how many notifications are
|
||||||
@ -172,23 +188,6 @@ in the vertical axis
|
|||||||
The distance in pixels from the content to the border of the window
|
The distance in pixels from the content to the border of the window
|
||||||
in the horizontal axis
|
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)
|
=item B<frame_width> (default: 0)
|
||||||
|
|
||||||
Defines width in pixels of frame around the notification window. Set to 0 to
|
Defines width in pixels of frame around the notification window. Set to 0 to
|
||||||
@ -236,27 +235,8 @@ See TIME FORMAT for valid times.
|
|||||||
|
|
||||||
Set to 0 to disable.
|
Set to 0 to disable.
|
||||||
|
|
||||||
A client can mark a notification as transient to bypass this setting and timeout
|
Transient notifications will ignore this setting and timeout anyway.
|
||||||
anyway. Use a rule with 'set_transient = no' to disable this behavior.
|
Use a rule overwriting 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")
|
=item B<font> (default: "Monospace 8")
|
||||||
|
|
||||||
@ -292,7 +272,7 @@ Allow a small subset of html markup in notifications
|
|||||||
<u>underline</u>
|
<u>underline</u>
|
||||||
|
|
||||||
For a complete reference see
|
For a complete reference see
|
||||||
<https://developer.gnome.org/pango/stable/pango-Markup.html>
|
<http://developer.gnome.org/pango/stable/PangoMarkupFormat.html>
|
||||||
|
|
||||||
=item B<strip>
|
=item B<strip>
|
||||||
|
|
||||||
@ -357,11 +337,6 @@ removed from the format.
|
|||||||
|
|
||||||
Defines how the text should be aligned within the notification.
|
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)
|
=item B<show_age_threshold> (default: -1)
|
||||||
|
|
||||||
Show age of message if message is older than this time.
|
Show age of message if message is older than this time.
|
||||||
@ -373,7 +348,7 @@ Set to -1 to disable.
|
|||||||
|
|
||||||
Specifies how very long lines should be handled
|
Specifies how very long lines should be handled
|
||||||
|
|
||||||
If it's set to false, long lines will be truncated and ellipsized.
|
If it's set to false, long lines will be truncated an ellipsised.
|
||||||
|
|
||||||
If it's set to true, long lines will be broken into multiple lines expanding
|
If it's set to true, long lines will be broken into multiple lines expanding
|
||||||
the notification window height as necessary for them to fit.
|
the notification window height as necessary for them to fit.
|
||||||
@ -395,7 +370,7 @@ being displayed separately.
|
|||||||
Two notifications are considered duplicate if the name of the program that sent
|
Two notifications are considered duplicate if the name of the program that sent
|
||||||
it, summary, body, icon and urgency are all identical.
|
it, summary, body, icon and urgency are all identical.
|
||||||
|
|
||||||
=item B<hide_duplicate_count> (values: [true/false], default: false)
|
=item B<hide_duplicates_count> (values: [true/false], default: false)
|
||||||
|
|
||||||
Hide the count of stacked duplicate notifications.
|
Hide the count of stacked duplicate notifications.
|
||||||
|
|
||||||
@ -409,28 +384,14 @@ ACTIONS below for further details.
|
|||||||
Defines the position of the icon in the notification window. Setting it to off
|
Defines the position of the icon in the notification window. Setting it to off
|
||||||
disables icons.
|
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)
|
=item B<max_icon_size> (default: 0)
|
||||||
|
|
||||||
Defines the maximum size in pixels for the icons.
|
Defines the maximum size in pixels for the icons.
|
||||||
If the icon is smaller than or equal to the specified value it won't be affected.
|
If the icon is smaller than 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
|
If it's larger then it will be scaled down so that the larger axis is equivalent
|
||||||
to the specified size.
|
to the specified size.
|
||||||
|
|
||||||
Set to 0 to disable icon downscaling. (default)
|
Set to 0 to disable icon scaling. (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.
|
If B<icon_position> is set to off, this setting is ignored.
|
||||||
|
|
||||||
@ -483,13 +444,7 @@ WM_CLASS). There should be no need to modify this setting for regular use.
|
|||||||
Display a notification on startup. This is usually used for debugging and there
|
Display a notification on startup. This is usually used for debugging and there
|
||||||
shouldn't be any need to use this option.
|
shouldn't be any need to use this option.
|
||||||
|
|
||||||
=item B<verbosity> (values: 'crit', 'warn', 'mesg', 'info', 'debug' default 'mesg')
|
=item B<force_xinerama> (values: [true/false], default: false)
|
||||||
|
|
||||||
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) (X11 only)
|
|
||||||
|
|
||||||
Use the Xinerama extension instead of RandR for multi-monitor support. This
|
Use the Xinerama extension instead of RandR for multi-monitor support. This
|
||||||
setting is provided for compatibility with older nVidia drivers that do not
|
setting is provided for compatibility with older nVidia drivers that do not
|
||||||
@ -499,67 +454,9 @@ By enabling this setting dunst will not be able to detect when a monitor is
|
|||||||
connected or disconnected which might break follow mode if the screen layout
|
connected or disconnected which might break follow mode if the screen layout
|
||||||
changes.
|
changes.
|
||||||
|
|
||||||
=item B<corner_radius> (default: 0)
|
|
||||||
|
|
||||||
Define the corner radius in pixels. A corner radius of 0 will result in
|
|
||||||
rectangular shaped notifications.
|
|
||||||
|
|
||||||
By enabling this setting the outer border and the frame will be shaped.
|
|
||||||
If you have multiple notifications, the whole window is shaped, not every
|
|
||||||
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/context/context_all])
|
|
||||||
|
|
||||||
Defines action of mouse click. A touch input in Wayland acts as a mouse left
|
|
||||||
click.
|
|
||||||
|
|
||||||
=over 4
|
|
||||||
|
|
||||||
=item B<none>
|
|
||||||
|
|
||||||
Don't do anything.
|
|
||||||
|
|
||||||
=item B<do_action> (default for mouse_middle_click)
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
Close current notification.
|
|
||||||
|
|
||||||
=item B<close_all> (default for mouse_right_click)
|
|
||||||
|
|
||||||
Close all notifications.
|
|
||||||
|
|
||||||
=item B<context>
|
|
||||||
|
|
||||||
Open context menu for the notification.
|
|
||||||
|
|
||||||
=item B<context_all>
|
|
||||||
|
|
||||||
Open context menu for all notifications.
|
|
||||||
|
|
||||||
=back
|
=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
|
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.
|
modifier is one of ctrl,mod1,mod2,mod3,mod4 and key is any keyboard key.
|
||||||
@ -596,8 +493,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
|
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,
|
attributes for the different urgency levels of notifications (low, normal,
|
||||||
critical). Currently only the background, foreground, hightlight, timeout,
|
critical). Currently only the background, foreground, timeout, frame_color and
|
||||||
frame_color and icon attributes can be modified.
|
icon attributes can be modified.
|
||||||
|
|
||||||
The urgency sections are urgency_low, urgency_normal, urgency_critical for low,
|
The urgency sections are urgency_low, urgency_normal, urgency_critical for low,
|
||||||
normal and critical urgency respectively.
|
normal and critical urgency respectively.
|
||||||
@ -630,12 +527,6 @@ Defines the background color for low, normal and critical notifications respecti
|
|||||||
|
|
||||||
See COLORS for the value format.
|
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>
|
=item B<-lfr/nfr/cfr color>
|
||||||
|
|
||||||
Defines the frame color for low, normal and critical notifications respectively.
|
Defines the frame color for low, normal and critical notifications respectively.
|
||||||
@ -650,53 +541,18 @@ See TIME FORMAT for valid times.
|
|||||||
|
|
||||||
=back
|
=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
|
=head1 HISTORY
|
||||||
|
|
||||||
Dunst saves a number of notifications (specified by B<history_length>) in memory.
|
Dunst saves a number of notifications (specified by B<history_length>) in memory.
|
||||||
These notifications can be recalled (i.e. redisplayed) by calling
|
These notifications can be recalled (i.e. redesiplayed) by pressing the
|
||||||
B<dunstctl history> (see dunstctl(1)). Whether these notifications will time out
|
B<history_key> (see the shortcuts section), whether these notifications will
|
||||||
like if they have been just send depends on the value of the B<sticky_history>
|
time out like if they have been just send depends on the value of the
|
||||||
setting.
|
B<sticky_history> setting.
|
||||||
|
|
||||||
Past notifications are redisplayed in a first-in-last-out order, meaning that
|
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
|
pressing the history key once will bring up the most recent notification that
|
||||||
had been closed/timed out.
|
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
|
=head1 RULES
|
||||||
|
|
||||||
Rules allow the conditional modification of notifications. They are defined by
|
Rules allow the conditional modification of notifications. They are defined by
|
||||||
@ -712,64 +568,11 @@ matched.
|
|||||||
|
|
||||||
=item B<filtering>
|
=item B<filtering>
|
||||||
|
|
||||||
Notifications can be matched for any of the following attributes:
|
Notifications can be matched for any of the following attributes: appname,
|
||||||
|
summary, body, icon, category, match_transient and msg_urgency where each is
|
||||||
=over 4
|
the respective notification attribute to be matched and 'msg_urgency' is the
|
||||||
|
urgency of the notification, it is named so to not conflict with trying to
|
||||||
=item C<appname> (discouraged, see desktop_entry)
|
modify the urgency.
|
||||||
|
|
||||||
The name of the application as reported by the client. Be aware that the name
|
|
||||||
can often differ depending on the locale used.
|
|
||||||
|
|
||||||
=item C<body>
|
|
||||||
|
|
||||||
The body of the notification
|
|
||||||
|
|
||||||
=item C<category>
|
|
||||||
|
|
||||||
The category of the notification as defined by the notification spec. See
|
|
||||||
https://developer.gnome.org/notification-spec/#categories
|
|
||||||
|
|
||||||
=item C<desktop_entry>
|
|
||||||
|
|
||||||
GLib based applications export their desktop-entry name. In comparison to the appname,
|
|
||||||
the desktop-entry won't get localized.
|
|
||||||
|
|
||||||
=item C<icon>
|
|
||||||
|
|
||||||
The icon of the notification in the form of a file path. Can be empty if no icon
|
|
||||||
is available or a raw icon is used instead.
|
|
||||||
|
|
||||||
=item C<match_transient>
|
|
||||||
|
|
||||||
Match if the notification has been declared as transient by the client or by
|
|
||||||
some other rule.
|
|
||||||
|
|
||||||
See C<set_transient> for more details about this attribute.
|
|
||||||
|
|
||||||
=item C<msg_urgency>
|
|
||||||
|
|
||||||
Matches the urgency of the notification as set by the client or by some other
|
|
||||||
rule.
|
|
||||||
|
|
||||||
=item C<stack_tag>
|
|
||||||
|
|
||||||
Matches the stack tag of the notification as set by the client or by some other
|
|
||||||
rule.
|
|
||||||
|
|
||||||
See set_stack_tag for more information about stack tags.
|
|
||||||
|
|
||||||
=item C<summary>
|
|
||||||
|
|
||||||
Matches the summary, 'title', of the notification.
|
|
||||||
|
|
||||||
=back
|
|
||||||
|
|
||||||
C<msg_urgency> is the urgency of the notification, it is named so to not conflict
|
|
||||||
with trying to modify the urgency.
|
|
||||||
|
|
||||||
Instead of the appname filter, it's recommended to use the desktop_entry filter.
|
|
||||||
|
|
||||||
|
|
||||||
To define a matching rule simply assign the specified value to the value that
|
To define a matching rule simply assign the specified value to the value that
|
||||||
should be matched, for example:
|
should be matched, for example:
|
||||||
@ -784,107 +587,9 @@ Shell-like globing is supported.
|
|||||||
|
|
||||||
=item B<modifying>
|
=item B<modifying>
|
||||||
|
|
||||||
The following attributes can be overridden:
|
The following attributes can be overridden: timeout, urgency, foreground,
|
||||||
|
background, new_icon, set_transient, format where, as with the filtering attributes,
|
||||||
=over 4
|
each one corresponds to the respective notification attribute to be modified.
|
||||||
|
|
||||||
=item C<background>
|
|
||||||
|
|
||||||
The background color of the notification. See COLORS for possible values.
|
|
||||||
|
|
||||||
=item C<foreground>
|
|
||||||
|
|
||||||
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>
|
|
||||||
|
|
||||||
Equivalent to the C<format> setting.
|
|
||||||
|
|
||||||
=item C<frame_color>
|
|
||||||
|
|
||||||
The frame color color of the notification. See COLORS for possible values.
|
|
||||||
|
|
||||||
=item C<fullscreen>
|
|
||||||
|
|
||||||
One of show, delay, or pushback.
|
|
||||||
|
|
||||||
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
|
|
||||||
displayed until they are dismissed or time out but new notifications will be
|
|
||||||
held back and displayed when the focus to the fullscreen window is lost.
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
=item C<set_stack_tag>
|
|
||||||
|
|
||||||
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 you only want one of
|
|
||||||
the same type visible.
|
|
||||||
|
|
||||||
The stack tag can be set by the client with the 'synchronous',
|
|
||||||
'private-synchronous' 'x-canonical-private-synchronous' or the
|
|
||||||
'x-dunst-stack-tag' hints.
|
|
||||||
|
|
||||||
=item C<set_transient>
|
|
||||||
|
|
||||||
Sets whether the notification is considered transient.
|
|
||||||
Transient notifications will bypass the idle_threshold setting.
|
|
||||||
|
|
||||||
By default notifications are _not_ considered transient but clients can set the
|
|
||||||
value of this by specifying the 'transient' hint when sending notifications.
|
|
||||||
|
|
||||||
=item C<timeout>
|
|
||||||
|
|
||||||
Equivalent to the C<timeout> setting in the urgency sections.
|
|
||||||
|
|
||||||
=item C<urgency>
|
|
||||||
|
|
||||||
This sets the notification urgency.
|
|
||||||
|
|
||||||
B<IMPORTANT NOTE>: This currently DOES NOT re-apply the attributes from the
|
|
||||||
urgency_* sections. The changed urgency will only be visible in rules defined
|
|
||||||
later. Use C<msg_urgency> to match it.
|
|
||||||
|
|
||||||
=item C<skip_display>
|
|
||||||
|
|
||||||
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
|
|
||||||
the respective notification attribute to be modified.
|
|
||||||
|
|
||||||
As with filtering, to make a rule modify an attribute simply assign it in the
|
As with filtering, to make a rule modify an attribute simply assign it in the
|
||||||
rule definition.
|
rule definition.
|
||||||
@ -900,22 +605,11 @@ 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.
|
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
|
When the script is called details of the notification that triggered it will be
|
||||||
passed via environment variables. The following variables are available:
|
passed via command line parameters in the following order: appname, summary,
|
||||||
B<DUNST_APP_NAME>, B<DUNST_SUMMARY>, B<DUNST_BODY>, B<DUNST_ICON_PATH>,
|
body, icon, urgency.
|
||||||
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>.
|
|
||||||
|
|
||||||
Another, less recommended way to get notifcations details from a script is via
|
Where icon is the absolute path to the icon file if there is one and urgency is
|
||||||
command line parameters. These are passed to the script in the following order:
|
one of "LOW", "NORMAL" or "CRITICAL".
|
||||||
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
|
If the notification is suppressed, the script will not be run unless
|
||||||
B<always_run_scripts> is set to true.
|
B<always_run_scripts> is set to true.
|
||||||
@ -930,8 +624,6 @@ 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
|
color names such as "Yellow", "Blue", "White", etc as well as #RGB and #RRGGBB
|
||||||
values.
|
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
|
B<NOTE>: '#' is interpreted as a comment, to use it the entire value needs to
|
||||||
be in quotes like so: separator_color="#123456"
|
be in quotes like so: separator_color="#123456"
|
||||||
|
|
||||||
@ -941,51 +633,11 @@ 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.
|
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.
|
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
|
=over 4
|
||||||
|
|
||||||
=item notify-send -h string:fgcolor:#ff4444
|
=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 string:bgcolor:#4444ff -h string:fgcolor:#ff4444
|
||||||
|
|
||||||
=item notify-send -h int:value:42 "Working ..."
|
=item notify-send -h int:value:42 "Working ..."
|
||||||
|
|
||||||
@ -998,7 +650,7 @@ actions. Dunst has support for both displaying indicators for these, and
|
|||||||
interacting with these actions.
|
interacting with these actions.
|
||||||
|
|
||||||
If "show_indicators" is true and a notification has an action, an "(A)" will be
|
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
|
prepended to the notification format. Likewise, an "(U)" is preneded to
|
||||||
notifications with URLs. It is possible to interact with notifications that
|
notifications with URLs. It is possible to interact with notifications that
|
||||||
have actions regardless of this setting, though it may not be obvious which
|
have actions regardless of this setting, though it may not be obvious which
|
||||||
notifications HAVE actions.
|
notifications HAVE actions.
|
||||||
@ -1023,8 +675,8 @@ Example time: "1000ms" "10m"
|
|||||||
|
|
||||||
=head1 MISCELLANEOUS
|
=head1 MISCELLANEOUS
|
||||||
|
|
||||||
Dunst can be paused via the `dunstctl set-paused true` command. To unpause dunst use
|
Dunst can be paused by sending a notification with a summary of
|
||||||
`dunstctl set-paused false`.
|
"DUNST_COMMAND_PAUSE" and resumed with a summary of "DUNST_COMMAND_RESUME".
|
||||||
Alternatively you can send SIGUSR1 and SIGUSR2 to pause and unpause
|
Alternatively you can send SIGUSR1 and SIGUSR2 to pause and unpause
|
||||||
respectively. For Example:
|
respectively. For Example:
|
||||||
|
|
||||||
@ -1043,27 +695,11 @@ missed notifications after returning to the computer.
|
|||||||
|
|
||||||
=head1 FILES
|
=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
|
$XDG_CONFIG_HOME/dunst/dunstrc
|
||||||
|
|
||||||
$HOME/.config/dunst/dunstrc
|
|
||||||
|
|
||||||
-or-
|
-or-
|
||||||
|
|
||||||
$XDG_CONFIG_HOME/dunst/dunstrc
|
$HOME/.config/dunst/dunstrc
|
||||||
|
|
||||||
/etc/xdg/dunst/dunstrc
|
|
||||||
|
|
||||||
=over 4
|
|
||||||
|
|
||||||
=item /etc/dunst/dunstrc
|
|
||||||
|
|
||||||
This is where the default config file is located
|
|
||||||
|
|
||||||
=back
|
|
||||||
|
|
||||||
=head1 AUTHORS
|
=head1 AUTHORS
|
||||||
|
|
||||||
@ -1081,4 +717,4 @@ If you feel that copyrights are violated, please send me an email.
|
|||||||
|
|
||||||
=head1 SEE ALSO
|
=head1 SEE ALSO
|
||||||
|
|
||||||
dunst(1), dunstctl(1), dmenu(1), notify-send(1)
|
dwm(1), dmenu(1), twmn(1), notify-send(1)
|
||||||
@ -1,64 +0,0 @@
|
|||||||
=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
|
|
||||||
|
|
||||||
@ -1,245 +0,0 @@
|
|||||||
# Doxyfile 1.8.14
|
|
||||||
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
# Project related configuration options
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
DOXYFILE_ENCODING = UTF-8
|
|
||||||
PROJECT_NAME = Dunst
|
|
||||||
PROJECT_NUMBER =
|
|
||||||
PROJECT_BRIEF = "Lightweight notification daemon"
|
|
||||||
PROJECT_LOGO =
|
|
||||||
OUTPUT_DIRECTORY = docs/internal
|
|
||||||
CREATE_SUBDIRS = NO
|
|
||||||
ALLOW_UNICODE_NAMES = NO
|
|
||||||
OUTPUT_LANGUAGE = English
|
|
||||||
BRIEF_MEMBER_DESC = YES
|
|
||||||
REPEAT_BRIEF = YES
|
|
||||||
ALWAYS_DETAILED_SEC = NO
|
|
||||||
INLINE_INHERITED_MEMB = NO
|
|
||||||
FULL_PATH_NAMES = YES
|
|
||||||
STRIP_FROM_PATH =
|
|
||||||
STRIP_FROM_INC_PATH =
|
|
||||||
SHORT_NAMES = NO
|
|
||||||
JAVADOC_AUTOBRIEF = NO
|
|
||||||
QT_AUTOBRIEF = NO
|
|
||||||
MULTILINE_CPP_IS_BRIEF = NO
|
|
||||||
INHERIT_DOCS = YES
|
|
||||||
SEPARATE_MEMBER_PAGES = NO
|
|
||||||
TAB_SIZE = 8
|
|
||||||
ALIASES =
|
|
||||||
TCL_SUBST =
|
|
||||||
OPTIMIZE_OUTPUT_FOR_C = YES
|
|
||||||
OPTIMIZE_OUTPUT_JAVA = NO
|
|
||||||
OPTIMIZE_FOR_FORTRAN = NO
|
|
||||||
OPTIMIZE_OUTPUT_VHDL = NO
|
|
||||||
EXTENSION_MAPPING =
|
|
||||||
MARKDOWN_SUPPORT = YES
|
|
||||||
TOC_INCLUDE_HEADINGS = 0
|
|
||||||
AUTOLINK_SUPPORT = YES
|
|
||||||
BUILTIN_STL_SUPPORT = NO
|
|
||||||
CPP_CLI_SUPPORT = NO
|
|
||||||
SIP_SUPPORT = NO
|
|
||||||
IDL_PROPERTY_SUPPORT = YES
|
|
||||||
DISTRIBUTE_GROUP_DOC = NO
|
|
||||||
GROUP_NESTED_COMPOUNDS = NO
|
|
||||||
SUBGROUPING = YES
|
|
||||||
INLINE_GROUPED_CLASSES = NO
|
|
||||||
INLINE_SIMPLE_STRUCTS = NO
|
|
||||||
TYPEDEF_HIDES_STRUCT = YES
|
|
||||||
LOOKUP_CACHE_SIZE = 0
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
# Build related configuration options
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
EXTRACT_ALL = YES
|
|
||||||
EXTRACT_PRIVATE = YES
|
|
||||||
EXTRACT_PACKAGE = YES
|
|
||||||
EXTRACT_STATIC = YES
|
|
||||||
EXTRACT_LOCAL_CLASSES = YES
|
|
||||||
EXTRACT_LOCAL_METHODS = YES
|
|
||||||
EXTRACT_ANON_NSPACES = NO
|
|
||||||
HIDE_UNDOC_MEMBERS = NO
|
|
||||||
HIDE_UNDOC_CLASSES = NO
|
|
||||||
HIDE_FRIEND_COMPOUNDS = NO
|
|
||||||
HIDE_IN_BODY_DOCS = NO
|
|
||||||
INTERNAL_DOCS = NO
|
|
||||||
CASE_SENSE_NAMES = NO
|
|
||||||
HIDE_SCOPE_NAMES = YES
|
|
||||||
HIDE_COMPOUND_REFERENCE= NO
|
|
||||||
SHOW_INCLUDE_FILES = YES
|
|
||||||
SHOW_GROUPED_MEMB_INC = NO
|
|
||||||
FORCE_LOCAL_INCLUDES = NO
|
|
||||||
INLINE_INFO = YES
|
|
||||||
SORT_MEMBER_DOCS = YES
|
|
||||||
SORT_BRIEF_DOCS = NO
|
|
||||||
SORT_MEMBERS_CTORS_1ST = NO
|
|
||||||
SORT_GROUP_NAMES = NO
|
|
||||||
SORT_BY_SCOPE_NAME = NO
|
|
||||||
STRICT_PROTO_MATCHING = NO
|
|
||||||
GENERATE_TODOLIST = YES
|
|
||||||
GENERATE_TESTLIST = YES
|
|
||||||
GENERATE_BUGLIST = YES
|
|
||||||
GENERATE_DEPRECATEDLIST= YES
|
|
||||||
ENABLED_SECTIONS =
|
|
||||||
MAX_INITIALIZER_LINES = 30
|
|
||||||
SHOW_USED_FILES = YES
|
|
||||||
SHOW_FILES = YES
|
|
||||||
SHOW_NAMESPACES = YES
|
|
||||||
FILE_VERSION_FILTER =
|
|
||||||
LAYOUT_FILE =
|
|
||||||
CITE_BIB_FILES =
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
# Configuration options related to warning and progress messages
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
QUIET = NO
|
|
||||||
WARNINGS = YES
|
|
||||||
WARN_IF_UNDOCUMENTED = YES
|
|
||||||
WARN_IF_DOC_ERROR = YES
|
|
||||||
WARN_NO_PARAMDOC = YES
|
|
||||||
WARN_AS_ERROR = YES
|
|
||||||
WARN_FORMAT = "$file:$line: $text"
|
|
||||||
WARN_LOGFILE =
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
# Configuration options related to the input files
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
INPUT = . \
|
|
||||||
HACKING.md
|
|
||||||
INPUT_ENCODING = UTF-8
|
|
||||||
FILE_PATTERNS = *.c \
|
|
||||||
*.h
|
|
||||||
RECURSIVE = YES
|
|
||||||
EXCLUDE =
|
|
||||||
EXCLUDE_SYMLINKS = NO
|
|
||||||
EXCLUDE_PATTERNS = */test/greatest.h
|
|
||||||
EXCLUDE_SYMBOLS =
|
|
||||||
EXAMPLE_PATH =
|
|
||||||
EXAMPLE_PATTERNS = *
|
|
||||||
EXAMPLE_RECURSIVE = NO
|
|
||||||
IMAGE_PATH =
|
|
||||||
INPUT_FILTER =
|
|
||||||
FILTER_PATTERNS =
|
|
||||||
FILTER_SOURCE_FILES = NO
|
|
||||||
FILTER_SOURCE_PATTERNS =
|
|
||||||
USE_MDFILE_AS_MAINPAGE = HACKING.md
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
# Configuration options related to source browsing
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
SOURCE_BROWSER = YES
|
|
||||||
INLINE_SOURCES = NO
|
|
||||||
STRIP_CODE_COMMENTS = YES
|
|
||||||
REFERENCED_BY_RELATION = NO
|
|
||||||
REFERENCES_RELATION = NO
|
|
||||||
REFERENCES_LINK_SOURCE = YES
|
|
||||||
SOURCE_TOOLTIPS = YES
|
|
||||||
USE_HTAGS = NO
|
|
||||||
VERBATIM_HEADERS = YES
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
# Configuration options related to the alphabetical class index
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
ALPHABETICAL_INDEX = YES
|
|
||||||
COLS_IN_ALPHA_INDEX = 5
|
|
||||||
IGNORE_PREFIX =
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
# Configuration options related to the HTML output
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
GENERATE_HTML = YES
|
|
||||||
HTML_OUTPUT = html
|
|
||||||
HTML_FILE_EXTENSION = .html
|
|
||||||
HTML_HEADER =
|
|
||||||
HTML_FOOTER =
|
|
||||||
HTML_STYLESHEET =
|
|
||||||
HTML_EXTRA_STYLESHEET =
|
|
||||||
HTML_EXTRA_FILES =
|
|
||||||
HTML_COLORSTYLE_HUE = 220
|
|
||||||
HTML_COLORSTYLE_SAT = 100
|
|
||||||
HTML_COLORSTYLE_GAMMA = 80
|
|
||||||
HTML_TIMESTAMP = YES
|
|
||||||
HTML_DYNAMIC_MENUS = YES
|
|
||||||
HTML_DYNAMIC_SECTIONS = YES
|
|
||||||
HTML_INDEX_NUM_ENTRIES = 0
|
|
||||||
GENERATE_DOCSET = NO
|
|
||||||
GENERATE_HTMLHELP = NO
|
|
||||||
GENERATE_CHI = NO
|
|
||||||
GENERATE_QHP = NO
|
|
||||||
GENERATE_ECLIPSEHELP = NO
|
|
||||||
ECLIPSE_DOC_ID = org.dunst-project.dunst
|
|
||||||
DISABLE_INDEX = NO
|
|
||||||
GENERATE_TREEVIEW = YES
|
|
||||||
ENUM_VALUES_PER_LINE = 4
|
|
||||||
TREEVIEW_WIDTH = 250
|
|
||||||
EXT_LINKS_IN_WINDOW = NO
|
|
||||||
FORMULA_FONTSIZE = 10
|
|
||||||
FORMULA_TRANSPARENT = YES
|
|
||||||
USE_MATHJAX = NO
|
|
||||||
SEARCHENGINE = YES
|
|
||||||
SERVER_BASED_SEARCH = NO
|
|
||||||
EXTERNAL_SEARCH = NO
|
|
||||||
SEARCHENGINE_URL =
|
|
||||||
SEARCHDATA_FILE = searchdata.xml
|
|
||||||
EXTERNAL_SEARCH_ID =
|
|
||||||
EXTRA_SEARCH_MAPPINGS =
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
# Multiple disabled output formats
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
GENERATE_LATEX = NO
|
|
||||||
GENERATE_RTF = NO
|
|
||||||
GENERATE_MAN = NO
|
|
||||||
GENERATE_XML = NO
|
|
||||||
GENERATE_DOCBOOK = NO
|
|
||||||
GENERATE_AUTOGEN_DEF = NO
|
|
||||||
GENERATE_PERLMOD = NO
|
|
||||||
ENABLE_PREPROCESSING = YES
|
|
||||||
MACRO_EXPANSION = NO
|
|
||||||
EXPAND_ONLY_PREDEF = NO
|
|
||||||
SEARCH_INCLUDES = YES
|
|
||||||
INCLUDE_PATH =
|
|
||||||
INCLUDE_FILE_PATTERNS =
|
|
||||||
PREDEFINED =
|
|
||||||
EXPAND_AS_DEFINED =
|
|
||||||
SKIP_FUNCTION_MACROS = YES
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
# Configuration options related to external references
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
TAGFILES =
|
|
||||||
GENERATE_TAGFILE =
|
|
||||||
ALLEXTERNALS = NO
|
|
||||||
EXTERNAL_GROUPS = YES
|
|
||||||
EXTERNAL_PAGES = YES
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
# Configuration options related to the dot tool
|
|
||||||
#---------------------------------------------------------------------------
|
|
||||||
CLASS_DIAGRAMS = YES
|
|
||||||
MSCGEN_PATH =
|
|
||||||
DIA_PATH =
|
|
||||||
HIDE_UNDOC_RELATIONS = YES
|
|
||||||
HAVE_DOT = YES
|
|
||||||
DOT_NUM_THREADS = 0
|
|
||||||
DOT_FONTNAME = Helvetica
|
|
||||||
DOT_FONTSIZE = 10
|
|
||||||
DOT_FONTPATH =
|
|
||||||
CLASS_GRAPH = YES
|
|
||||||
COLLABORATION_GRAPH = YES
|
|
||||||
GROUP_GRAPHS = YES
|
|
||||||
UML_LOOK = NO
|
|
||||||
UML_LIMIT_NUM_FIELDS = 10
|
|
||||||
TEMPLATE_RELATIONS = NO
|
|
||||||
INCLUDE_GRAPH = YES
|
|
||||||
INCLUDED_BY_GRAPH = YES
|
|
||||||
CALL_GRAPH = NO
|
|
||||||
CALLER_GRAPH = NO
|
|
||||||
GRAPHICAL_HIERARCHY = YES
|
|
||||||
DIRECTORY_GRAPH = YES
|
|
||||||
DOT_IMAGE_FORMAT = svg
|
|
||||||
INTERACTIVE_SVG = YES
|
|
||||||
DOT_PATH =
|
|
||||||
DOTFILE_DIRS =
|
|
||||||
MSCFILE_DIRS =
|
|
||||||
DIAFILE_DIRS =
|
|
||||||
PLANTUML_JAR_PATH =
|
|
||||||
PLANTUML_CFG_FILE =
|
|
||||||
PLANTUML_INCLUDE_PATH =
|
|
||||||
DOT_GRAPH_MAX_NODES = 50
|
|
||||||
MAX_DOT_GRAPH_DEPTH = 0
|
|
||||||
DOT_TRANSPARENT = NO
|
|
||||||
DOT_MULTI_TARGETS = NO
|
|
||||||
GENERATE_LEGEND = YES
|
|
||||||
DOT_CLEANUP = YES
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
# 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,3 +7,7 @@ PartOf=graphical-session.target
|
|||||||
Type=dbus
|
Type=dbus
|
||||||
BusName=org.freedesktop.Notifications
|
BusName=org.freedesktop.Notifications
|
||||||
ExecStart=##PREFIX##/bin/dunst
|
ExecStart=##PREFIX##/bin/dunst
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
|
|
||||||
|
|||||||
122
dunstctl
@ -1,122 +0,0 @@
|
|||||||
#!/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
|
|
||||||
49
dunstify.c
@ -36,7 +36,7 @@ static GOptionEntry entries[] =
|
|||||||
{ "serverinfo", 's', 0, G_OPTION_ARG_NONE, &serverinfo, "Print server information and exit", NULL},
|
{ "serverinfo", 's', 0, G_OPTION_ARG_NONE, &serverinfo, "Print server information and exit", NULL},
|
||||||
{ "printid", 'p', 0, G_OPTION_ARG_NONE, &printid, "Print id, which can be used to update/replace this notification", NULL},
|
{ "printid", 'p', 0, G_OPTION_ARG_NONE, &printid, "Print id, which can be used to update/replace this notification", NULL},
|
||||||
{ "replace", 'r', 0, G_OPTION_ARG_INT, &replace_id, "Set id of this notification.", "ID"},
|
{ "replace", 'r', 0, G_OPTION_ARG_INT, &replace_id, "Set id of this notification.", "ID"},
|
||||||
{ "close", 'C', 0, G_OPTION_ARG_INT, &close_id, "Close the notification with the specified ID", "ID"},
|
{ "close", 'C', 0, G_OPTION_ARG_INT, &close_id, "Set id of this notification.", "ID"},
|
||||||
{ "block", 'b', 0, G_OPTION_ARG_NONE, &block, "Block until notification is closed and print close reason", NULL},
|
{ "block", 'b', 0, G_OPTION_ARG_NONE, &block, "Block until notification is closed and print close reason", NULL},
|
||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
@ -76,33 +76,6 @@ void print_serverinfo(void)
|
|||||||
spec_version);
|
spec_version);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Glib leaves the option terminator "--" in the argv after parsing in some
|
|
||||||
* cases. This function gets the specified argv element ignoring the first
|
|
||||||
* terminator.
|
|
||||||
*
|
|
||||||
* See https://developer.gnome.org/glib/stable/glib-Commandline-option-parser.html#g-option-context-parse for details
|
|
||||||
*/
|
|
||||||
char *get_argv(char *argv[], int index)
|
|
||||||
{
|
|
||||||
for (int i = 0; i <= index; i++) {
|
|
||||||
if (strcmp(argv[i], "--") == 0) {
|
|
||||||
return argv[index + 1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return argv[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Count the number of arguments in argv excluding the terminator "--" */
|
|
||||||
int count_args(char *argv[], int argc) {
|
|
||||||
for (int i = 0; i < argc; i++) {
|
|
||||||
if (strcmp(argv[i], "--") == 0)
|
|
||||||
return argc - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return argc;
|
|
||||||
}
|
|
||||||
|
|
||||||
void parse_commandline(int argc, char *argv[])
|
void parse_commandline(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
@ -127,23 +100,17 @@ void parse_commandline(int argc, char *argv[])
|
|||||||
die(0);
|
die(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*appname == '\0') {
|
if (argc < 2 && close_id < 1) {
|
||||||
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");
|
g_printerr("I need at least a summary\n");
|
||||||
die(1);
|
die(1);
|
||||||
} else if (n_args < 2) {
|
} else if (argc < 2) {
|
||||||
summary = g_strdup("These are not the summaries you are looking for");
|
summary = g_strdup("These are not the summaries you are looking for");
|
||||||
} else {
|
} else {
|
||||||
summary = g_strdup(get_argv(argv, 1));
|
summary = g_strdup(argv[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n_args > 2) {
|
if (argc > 2) {
|
||||||
body = g_strcompress(get_argv(argv, 2));
|
body = g_strdup(argv[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urgency_str) {
|
if (urgency_str) {
|
||||||
@ -239,7 +206,7 @@ void add_action(NotifyNotification *n, char *str)
|
|||||||
char *label = strchr(str, ',');
|
char *label = strchr(str, ',');
|
||||||
|
|
||||||
if (!label || *(label+1) == '\0') {
|
if (!label || *(label+1) == '\0') {
|
||||||
g_printerr("Malformed action. Expected \"action,label\", got \"%s\"", str);
|
g_printerr("Malformed action. Excpected \"action,label\", got \"%s\"", str);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,4 +331,4 @@ int main(int argc, char *argv[])
|
|||||||
die(0);
|
die(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
|||||||
185
dunstrc
@ -31,23 +31,6 @@
|
|||||||
# screen width minus the width defined in within the geometry option.
|
# screen width minus the width defined in within the geometry option.
|
||||||
geometry = "300x5-30+20"
|
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).
|
# Show how many messages are currently hidden (because of geometry).
|
||||||
indicate_hidden = yes
|
indicate_hidden = yes
|
||||||
|
|
||||||
@ -76,9 +59,6 @@
|
|||||||
# Horizontal padding.
|
# Horizontal padding.
|
||||||
horizontal_padding = 8
|
horizontal_padding = 8
|
||||||
|
|
||||||
# Padding between text and icon.
|
|
||||||
text_icon_padding = 0
|
|
||||||
|
|
||||||
# Defines width in pixels of frame around the notification window.
|
# Defines width in pixels of frame around the notification window.
|
||||||
# Set to 0 to disable.
|
# Set to 0 to disable.
|
||||||
frame_width = 3
|
frame_width = 3
|
||||||
@ -100,8 +80,7 @@
|
|||||||
# Don't remove messages, if the user is idle (no mouse or keyboard input)
|
# Don't remove messages, if the user is idle (no mouse or keyboard input)
|
||||||
# for longer than idle_threshold seconds.
|
# for longer than idle_threshold seconds.
|
||||||
# Set to 0 to disable.
|
# Set to 0 to disable.
|
||||||
# A client can set the 'transient' hint to bypass this. See the rules
|
# Transient notifications ignore this setting.
|
||||||
# section for how to disable this if necessary
|
|
||||||
idle_threshold = 120
|
idle_threshold = 120
|
||||||
|
|
||||||
### Text ###
|
### Text ###
|
||||||
@ -120,7 +99,7 @@
|
|||||||
# <u>underline</u>
|
# <u>underline</u>
|
||||||
#
|
#
|
||||||
# For a complete reference see
|
# For a complete reference see
|
||||||
# <https://developer.gnome.org/pango/stable/pango-Markup.html>.
|
# <http://developer.gnome.org/pango/stable/PangoMarkupFormat.html>.
|
||||||
#
|
#
|
||||||
# strip: This setting is provided for compatibility with some broken
|
# strip: This setting is provided for compatibility with some broken
|
||||||
# clients that send markup even though it's not enabled on the
|
# clients that send markup even though it's not enabled on the
|
||||||
@ -152,10 +131,6 @@
|
|||||||
# Possible values are "left", "center" and "right".
|
# Possible values are "left", "center" and "right".
|
||||||
alignment = left
|
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
|
# Show age of message if message is older than show_age_threshold
|
||||||
# seconds.
|
# seconds.
|
||||||
# Set to -1 to disable.
|
# Set to -1 to disable.
|
||||||
@ -165,17 +140,17 @@
|
|||||||
# geometry.
|
# geometry.
|
||||||
word_wrap = yes
|
word_wrap = yes
|
||||||
|
|
||||||
# When word_wrap is set to no, specify where to make an ellipsis in long lines.
|
# When word_wrap is set to no, specify where to ellipsize long lines.
|
||||||
# Possible values are "start", "middle" and "end".
|
# Possible values are "start", "middle" and "end".
|
||||||
ellipsize = middle
|
ellipsize = middle
|
||||||
|
|
||||||
# Ignore newlines '\n' in notifications.
|
# Ignore newlines '\n' in notifications.
|
||||||
ignore_newline = no
|
ignore_newline = no
|
||||||
|
|
||||||
# Stack together notifications with the same content
|
# Merge multiple notifications with the same content
|
||||||
stack_duplicates = true
|
stack_duplicates = true
|
||||||
|
|
||||||
# Hide the count of stacked notifications with the same content
|
# Hide the count of merged notifications with the same content
|
||||||
hide_duplicate_count = false
|
hide_duplicate_count = false
|
||||||
|
|
||||||
# Display indicators for URLs (U) and actions (A).
|
# Display indicators for URLs (U) and actions (A).
|
||||||
@ -184,12 +159,7 @@
|
|||||||
### Icons ###
|
### Icons ###
|
||||||
|
|
||||||
# Align icons left/right/off
|
# Align icons left/right/off
|
||||||
icon_position = left
|
icon_position = off
|
||||||
|
|
||||||
# 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
|
# Scale larger icons down to this size, set to 0 to disable
|
||||||
max_icon_size = 32
|
max_icon_size = 32
|
||||||
@ -228,38 +198,6 @@
|
|||||||
# automatically after a crash.
|
# automatically after a crash.
|
||||||
startup_notification = false
|
startup_notification = false
|
||||||
|
|
||||||
# Manage dunst's desire for talking
|
|
||||||
# Can be one of the following values:
|
|
||||||
# crit: Critical features. Dunst aborts
|
|
||||||
# warn: Only non-fatal warnings
|
|
||||||
# mesg: Important Messages
|
|
||||||
# info: all unimportant stuff
|
|
||||||
# debug: all less than unimportant stuff
|
|
||||||
verbosity = mesg
|
|
||||||
|
|
||||||
# Define the corner radius of the notification window
|
|
||||||
# in pixel size. If the radius is 0, you have no rounded
|
|
||||||
# corners.
|
|
||||||
# The radius will be automatically lowered if it exceeds half of the
|
|
||||||
# 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
|
### Legacy
|
||||||
|
|
||||||
# Use the Xinerama extension instead of RandR for multi-monitor support.
|
# Use the Xinerama extension instead of RandR for multi-monitor support.
|
||||||
@ -272,25 +210,6 @@
|
|||||||
# layout changes.
|
# layout changes.
|
||||||
force_xinerama = false
|
force_xinerama = false
|
||||||
|
|
||||||
### mouse
|
|
||||||
|
|
||||||
# Defines list of actions for each mouse event
|
|
||||||
# Possible values are:
|
|
||||||
# * none: Don't do anything.
|
|
||||||
# * 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, close_current
|
|
||||||
mouse_right_click = close_all
|
|
||||||
|
|
||||||
# Experimental features that may or may not work correctly. Do not expect them
|
# Experimental features that may or may not work correctly. Do not expect them
|
||||||
# to have a consistent behaviour across releases.
|
# to have a consistent behaviour across releases.
|
||||||
[experimental]
|
[experimental]
|
||||||
@ -301,10 +220,6 @@
|
|||||||
# where there are multiple screens with very different dpi values.
|
# where there are multiple screens with very different dpi values.
|
||||||
per_monitor_dpi = false
|
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]
|
||||||
|
|
||||||
# Shortcuts are specified as [modifier+][modifier+]...key
|
# Shortcuts are specified as [modifier+][modifier+]...key
|
||||||
@ -312,21 +227,20 @@
|
|||||||
# "mod3" and "mod4" (windows-key).
|
# "mod3" and "mod4" (windows-key).
|
||||||
# Xev might be helpful to find names for keys.
|
# Xev might be helpful to find names for keys.
|
||||||
|
|
||||||
# Close notification. Equivalent dunstctl command:
|
# Close notification.
|
||||||
# dunstctl close
|
close = ctrl+space
|
||||||
# close = ctrl+space
|
|
||||||
|
|
||||||
# Close all notifications. Equivalent dunstctl command:
|
# Close all notifications.
|
||||||
# dunstctl close-all
|
close_all = ctrl+shift+space
|
||||||
# close_all = ctrl+shift+space
|
|
||||||
|
|
||||||
# Redisplay last message(s). Equivalent dunstctl command:
|
# Redisplay last message(s).
|
||||||
# dunstctl history-pop
|
# On the US keyboard layout "grave" is normally above TAB and left
|
||||||
# history = ctrl+grave
|
# of "1". Make sure this key actually exists on your keyboard layout,
|
||||||
|
# e.g. check output of 'xmodmap -pke'
|
||||||
|
history = ctrl+grave
|
||||||
|
|
||||||
# Context menu. Equivalent dunstctl command:
|
# Context menu.
|
||||||
# dunstctl context
|
context = ctrl+shift+period
|
||||||
# context = ctrl+shift+period
|
|
||||||
|
|
||||||
[urgency_low]
|
[urgency_low]
|
||||||
# IMPORTANT: colors have to be defined in quotation marks.
|
# IMPORTANT: colors have to be defined in quotation marks.
|
||||||
@ -354,37 +268,11 @@
|
|||||||
|
|
||||||
# Every section that isn't one of the above is interpreted as a rules to
|
# Every section that isn't one of the above is interpreted as a rules to
|
||||||
# override settings for certain messages.
|
# override settings for certain messages.
|
||||||
#
|
# Messages can be matched by "appname", "summary", "body", "icon", "category",
|
||||||
# Messages can be matched by
|
# "msg_urgency" and you can override the "timeout", "urgency", "foreground",
|
||||||
# appname (discouraged, see desktop_entry)
|
# "background", "new_icon" and "format".
|
||||||
# body
|
|
||||||
# category
|
|
||||||
# desktop_entry
|
|
||||||
# icon
|
|
||||||
# match_transient
|
|
||||||
# msg_urgency
|
|
||||||
# stack_tag
|
|
||||||
# summary
|
|
||||||
#
|
|
||||||
# and you can override the
|
|
||||||
# background
|
|
||||||
# foreground
|
|
||||||
# format
|
|
||||||
# frame_color
|
|
||||||
# fullscreen
|
|
||||||
# new_icon
|
|
||||||
# set_stack_tag
|
|
||||||
# set_transient
|
|
||||||
# timeout
|
|
||||||
# urgency
|
|
||||||
# action_name
|
|
||||||
#
|
|
||||||
# Shell-like globbing will get expanded.
|
# Shell-like globbing will get expanded.
|
||||||
#
|
#
|
||||||
# Instead of the appname filter, it's recommended to use the desktop_entry filter.
|
|
||||||
# GLib based applications export their desktop-entry name. In comparison to the appname,
|
|
||||||
# the desktop-entry won't get localized.
|
|
||||||
#
|
|
||||||
# SCRIPTING
|
# SCRIPTING
|
||||||
# You can specify a script that gets run when the rule matches by
|
# You can specify a script that gets run when the rule matches by
|
||||||
# setting the "script" option.
|
# setting the "script" option.
|
||||||
@ -397,30 +285,6 @@
|
|||||||
# NOTE: It might be helpful to run dunst -print in a terminal in order
|
# NOTE: It might be helpful to run dunst -print in a terminal in order
|
||||||
# to find fitting options for rules.
|
# to find fitting options for rules.
|
||||||
|
|
||||||
# Disable the transient hint so that idle_threshold cannot be bypassed from the
|
|
||||||
# client
|
|
||||||
#[transient_disable]
|
|
||||||
# match_transient = yes
|
|
||||||
# set_transient = no
|
|
||||||
#
|
|
||||||
# Make the handling of transient notifications more strict by making them not
|
|
||||||
# be placed in history.
|
|
||||||
#[transient_history_ignore]
|
|
||||||
# match_transient = yes
|
|
||||||
# history_ignore = yes
|
|
||||||
|
|
||||||
# fullscreen values
|
|
||||||
# show: show the notifications, regardless if there is a fullscreen window opened
|
|
||||||
# delay: displays the new notification, if there is no fullscreen window active
|
|
||||||
# If the notification is already drawn, it won't get undrawn.
|
|
||||||
# pushback: same as delay, but when switching into fullscreen, the notification will get
|
|
||||||
# withdrawn from screen again and will get delayed like a new notification
|
|
||||||
#[fullscreen_delay_everything]
|
|
||||||
# fullscreen = delay
|
|
||||||
#[fullscreen_show_critical]
|
|
||||||
# msg_urgency = critical
|
|
||||||
# fullscreen = show
|
|
||||||
|
|
||||||
#[espeak]
|
#[espeak]
|
||||||
# summary = "*"
|
# summary = "*"
|
||||||
# script = dunst_espeak.sh
|
# script = dunst_espeak.sh
|
||||||
@ -439,11 +303,6 @@
|
|||||||
# summary = "foobar"
|
# summary = "foobar"
|
||||||
# history_ignore = yes
|
# history_ignore = yes
|
||||||
|
|
||||||
#[skip-display]
|
|
||||||
# # This notification will not be displayed, but will be included in the history
|
|
||||||
# summary = "foobar"
|
|
||||||
# skip_display = yes
|
|
||||||
|
|
||||||
#[signed_on]
|
#[signed_on]
|
||||||
# appname = Pidgin
|
# appname = Pidgin
|
||||||
# summary = "*signed on*"
|
# summary = "*signed on*"
|
||||||
@ -464,8 +323,4 @@
|
|||||||
# summary = *twitter.com*
|
# summary = *twitter.com*
|
||||||
# urgency = normal
|
# urgency = normal
|
||||||
#
|
#
|
||||||
#[stack-volumes]
|
|
||||||
# appname = "some_volume_notifiers"
|
|
||||||
# set_stack_tag = "volume"
|
|
||||||
#
|
|
||||||
# vim: ft=cfg
|
# vim: ft=cfg
|
||||||
|
|||||||
2
main.c
@ -4,4 +4,4 @@ int main(int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
return dunst_main(argc, argv);
|
return dunst_main(argc, argv);
|
||||||
}
|
}
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
|||||||
988
src/dbus.c
25
src/dbus.h
@ -3,23 +3,22 @@
|
|||||||
#ifndef DUNST_DBUS_H
|
#ifndef DUNST_DBUS_H
|
||||||
#define DUNST_DBUS_H
|
#define DUNST_DBUS_H
|
||||||
|
|
||||||
#include "dunst.h"
|
|
||||||
#include "notification.h"
|
#include "notification.h"
|
||||||
|
|
||||||
/// The reasons according to the notification spec
|
|
||||||
enum reason {
|
enum reason {
|
||||||
REASON_MIN = 1, /**< Minimum value, useful for boundary checking */
|
REASON_MIN = 1,
|
||||||
REASON_TIME = 1, /**< The notification timed out */
|
REASON_TIME = 1,
|
||||||
REASON_USER = 2, /**< The user closed the notification */
|
REASON_USER = 2,
|
||||||
REASON_SIG = 3, /**< The daemon received a `NotificationClose` signal */
|
REASON_SIG = 3,
|
||||||
REASON_UNDEF = 4, /**< Undefined reason not matching the previous ones */
|
REASON_UNDEF = 4,
|
||||||
REASON_MAX = 4, /**< Maximum value, useful for boundary checking */
|
REASON_MAX = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
int dbus_init(void);
|
int initdbus(void);
|
||||||
void dbus_teardown(int id);
|
void dbus_tear_down(int id);
|
||||||
void signal_notification_closed(struct notification *n, enum reason reason);
|
/* void dbus_poll(int timeout); */
|
||||||
void signal_action_invoked(const struct notification *n, const char *identifier);
|
void notification_closed(notification *n, enum reason reason);
|
||||||
|
void action_invoked(notification *n, const char *identifier);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
|||||||
843
src/draw.c
@ -1,843 +0,0 @@
|
|||||||
#include "draw.h"
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <pango/pango-attributes.h>
|
|
||||||
#include <pango/pangocairo.h>
|
|
||||||
#include <pango/pango-font.h>
|
|
||||||
#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"
|
|
||||||
#include "log.h"
|
|
||||||
#include "markup.h"
|
|
||||||
#include "notification.h"
|
|
||||||
#include "queues.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;
|
|
||||||
cairo_surface_t *icon;
|
|
||||||
const struct notification *n;
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct output *output;
|
|
||||||
window win;
|
|
||||||
|
|
||||||
PangoFontDescription *pango_fdesc;
|
|
||||||
|
|
||||||
#define UINT_MAX_N(bits) ((1 << bits) - 1)
|
|
||||||
|
|
||||||
void draw_setup(void)
|
|
||||||
{
|
|
||||||
const struct output *out = output_create(settings.force_xwayland);
|
|
||||||
output = out;
|
|
||||||
|
|
||||||
win = out->win_create();
|
|
||||||
|
|
||||||
pango_fdesc = pango_font_description_from_string(settings.font);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 >> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct color string_to_color(const char *str)
|
|
||||||
{
|
|
||||||
char *end;
|
|
||||||
uint_fast32_t val = strtoul(str+1, &end, 16);
|
|
||||||
if (end[0] != '\0' && end[1] != '\0') {
|
|
||||||
LOG_W("Invalid color string: '%s'", str);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 inline double color_apply_delta(double base, double delta)
|
|
||||||
{
|
|
||||||
base += delta;
|
|
||||||
if (base > 1)
|
|
||||||
base = 1;
|
|
||||||
if (base < 0)
|
|
||||||
base = 0;
|
|
||||||
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct color calculate_foreground_color(struct color bg)
|
|
||||||
{
|
|
||||||
double c_delta = 0.1;
|
|
||||||
struct color fg = bg;
|
|
||||||
|
|
||||||
/* do we need to darken or brighten the colors? */
|
|
||||||
bool darken = (bg.r + bg.g + bg.b) / 3 > 0.5;
|
|
||||||
|
|
||||||
int signedness = darken ? -1 : 1;
|
|
||||||
|
|
||||||
fg.r = color_apply_delta(fg.r, c_delta * signedness);
|
|
||||||
fg.g = color_apply_delta(fg.g, c_delta * signedness);
|
|
||||||
fg.b = color_apply_delta(fg.b, c_delta * signedness);
|
|
||||||
|
|
||||||
return fg;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct color layout_get_sepcolor(struct colored_layout *cl,
|
|
||||||
struct colored_layout *cl_next)
|
|
||||||
{
|
|
||||||
switch (settings.sep_color.type) {
|
|
||||||
case SEP_FRAME:
|
|
||||||
if (cl_next->n->urgency > cl->n->urgency)
|
|
||||||
return cl_next->frame;
|
|
||||||
else
|
|
||||||
return cl->frame;
|
|
||||||
case SEP_CUSTOM:
|
|
||||||
return string_to_color(settings.sep_color.sep_color);
|
|
||||||
case SEP_FOREGROUND:
|
|
||||||
return cl->fg;
|
|
||||||
case SEP_AUTO:
|
|
||||||
return calculate_foreground_color(cl->bg);
|
|
||||||
default:
|
|
||||||
LOG_E("Invalid %s enum value in %s:%d", "sep_color", __FILE__, __LINE__);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 * scale * PANGO_SCALE);
|
|
||||||
pango_layout_set_font_description(layout, pango_fdesc);
|
|
||||||
pango_layout_set_spacing(layout, settings.line_height * scale * PANGO_SCALE);
|
|
||||||
|
|
||||||
PangoAlignment align;
|
|
||||||
switch (settings.align) {
|
|
||||||
case ALIGN_LEFT:
|
|
||||||
default:
|
|
||||||
align = PANGO_ALIGN_LEFT;
|
|
||||||
break;
|
|
||||||
case ALIGN_CENTER:
|
|
||||||
align = PANGO_ALIGN_CENTER;
|
|
||||||
break;
|
|
||||||
case ALIGN_RIGHT:
|
|
||||||
align = PANGO_ALIGN_RIGHT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pango_layout_set_alignment(layout, align);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void free_colored_layout(void *data)
|
|
||||||
{
|
|
||||||
struct colored_layout *cl = data;
|
|
||||||
g_object_unref(cl->l);
|
|
||||||
pango_attr_list_unref(cl->attr);
|
|
||||||
g_free(cl->text);
|
|
||||||
if (cl->icon) cairo_surface_destroy(cl->icon);
|
|
||||||
g_free(cl);
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
const struct screen_info *scr = output->get_active_screen();
|
|
||||||
if (have_dynamic_width()) {
|
|
||||||
/* dynamic width */
|
|
||||||
dim.w = 0;
|
|
||||||
} else if (settings.geometry.width_set) {
|
|
||||||
/* fixed width */
|
|
||||||
if (settings.geometry.negative_width) {
|
|
||||||
dim.w = scr->w - settings.geometry.w;
|
|
||||||
} else {
|
|
||||||
dim.w = settings.geometry.w;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* across the screen */
|
|
||||||
dim.w = scr->w;
|
|
||||||
}
|
|
||||||
|
|
||||||
dim.h += 2 * settings.frame_width;
|
|
||||||
dim.h += (g_slist_length(layouts) - 1) * settings.separator_height;
|
|
||||||
|
|
||||||
dim.corner_radius = settings.corner_radius;
|
|
||||||
|
|
||||||
int text_width = 0, total_width = 0;
|
|
||||||
for (GSList *iter = layouts; iter; iter = iter->next) {
|
|
||||||
struct colored_layout *cl = iter->data;
|
|
||||||
int w=0,h=0;
|
|
||||||
get_text_size(cl->l, &w, &h, scale);
|
|
||||||
if (cl->icon) {
|
|
||||||
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_dynamic_width() || settings.shrink) {
|
|
||||||
/* dynamic width */
|
|
||||||
total_width = MAX(text_width + 2 * settings.h_padding, total_width);
|
|
||||||
|
|
||||||
/* subtract height from the unwrapped text */
|
|
||||||
dim.h -= h;
|
|
||||||
|
|
||||||
if (total_width > scr->w) {
|
|
||||||
/* set width to screen width */
|
|
||||||
dim.w = scr->w - settings.geometry.x * 2;
|
|
||||||
} else if (have_dynamic_width() || (total_width < settings.geometry.w && settings.shrink)) {
|
|
||||||
/* set width to text width */
|
|
||||||
dim.w = total_width + 2 * settings.frame_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* re-setup the layout */
|
|
||||||
w = dim.w;
|
|
||||||
w -= 2 * settings.h_padding;
|
|
||||||
w -= 2 * settings.frame_width;
|
|
||||||
if (cl->icon) {
|
|
||||||
w -= get_icon_width(cl->icon, scale) + get_text_icon_padding();
|
|
||||||
}
|
|
||||||
layout_setup_pango(cl->l, w);
|
|
||||||
|
|
||||||
/* re-read information */
|
|
||||||
get_text_size(cl->l, &w, &h, scale);
|
|
||||||
if (cl->icon) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dim.w <= 0) {
|
|
||||||
dim.w = text_width + 2 * settings.h_padding;
|
|
||||||
dim.w += 2 * settings.frame_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
return dim;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PangoLayout *layout_create(cairo_t *c)
|
|
||||||
{
|
|
||||||
const struct screen_info *screen = output->get_active_screen();
|
|
||||||
|
|
||||||
PangoContext *context = pango_cairo_create_context(c);
|
|
||||||
pango_cairo_context_set_resolution(context, screen->dpi);
|
|
||||||
|
|
||||||
PangoLayout *layout = pango_layout_new(context);
|
|
||||||
|
|
||||||
g_object_unref(context);
|
|
||||||
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct colored_layout *layout_init_shared(cairo_t *c, const struct notification *n)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
switch (settings.ellipsize) {
|
|
||||||
case ELLIPSE_START:
|
|
||||||
ellipsize = PANGO_ELLIPSIZE_START;
|
|
||||||
break;
|
|
||||||
case ELLIPSE_MIDDLE:
|
|
||||||
ellipsize = PANGO_ELLIPSIZE_MIDDLE;
|
|
||||||
break;
|
|
||||||
case ELLIPSE_END:
|
|
||||||
ellipsize = PANGO_ELLIPSIZE_END;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOG_E("Invalid %s enum value in %s:%d", "ellipsize", __FILE__, __LINE__);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pango_layout_set_ellipsize(cl->l, ellipsize);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settings.icon_position != ICON_OFF && n->icon) {
|
|
||||||
cl->icon = gdk_pixbuf_to_cairo_surface(n->icon);
|
|
||||||
} else {
|
|
||||||
cl->icon = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cl->icon && cairo_surface_status(cl->icon) != CAIRO_STATUS_SUCCESS) {
|
|
||||||
cairo_surface_destroy(cl->icon);
|
|
||||||
cl->icon = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
struct dimensions dim = calculate_dimensions(NULL);
|
|
||||||
int width = dim.w;
|
|
||||||
|
|
||||||
if (have_dynamic_width()) {
|
|
||||||
layout_setup_pango(cl->l, -1);
|
|
||||||
} else {
|
|
||||||
width -= 2 * settings.h_padding;
|
|
||||||
width -= 2 * settings.frame_width;
|
|
||||||
if (cl->icon) {
|
|
||||||
width -= get_icon_width(cl->icon, scale) + get_text_icon_padding();
|
|
||||||
}
|
|
||||||
layout_setup_pango(cl->l, width);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cl;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct colored_layout *layout_derive_xmore(cairo_t *c, const struct notification *n, int qlen)
|
|
||||||
{
|
|
||||||
struct colored_layout *cl = layout_init_shared(c, n);
|
|
||||||
cl->text = g_strdup_printf("(%d more)", qlen);
|
|
||||||
cl->attr = NULL;
|
|
||||||
pango_layout_set_text(cl->l, cl->text, -1);
|
|
||||||
return cl;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct colored_layout *layout_from_notification(cairo_t *c, struct notification *n)
|
|
||||||
{
|
|
||||||
|
|
||||||
struct colored_layout *cl = layout_init_shared(c, n);
|
|
||||||
int scale = output->get_scale();
|
|
||||||
|
|
||||||
/* markup */
|
|
||||||
GError *err = NULL;
|
|
||||||
pango_parse_markup(n->text_to_render, -1, 0, &(cl->attr), &(cl->text), NULL, &err);
|
|
||||||
|
|
||||||
if (!err) {
|
|
||||||
pango_layout_set_text(cl->l, cl->text, -1);
|
|
||||||
pango_layout_set_attributes(cl->l, cl->attr);
|
|
||||||
} else {
|
|
||||||
/* remove markup and display plain message instead */
|
|
||||||
n->text_to_render = markup_strip(n->text_to_render);
|
|
||||||
cl->text = NULL;
|
|
||||||
cl->attr = NULL;
|
|
||||||
pango_layout_set_text(cl->l, n->text_to_render, -1);
|
|
||||||
if (n->first_render) {
|
|
||||||
LOG_W("Unable to parse markup: %s", err->message);
|
|
||||||
}
|
|
||||||
g_error_free(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
static GSList *create_layouts(cairo_t *c)
|
|
||||||
{
|
|
||||||
GSList *layouts = NULL;
|
|
||||||
|
|
||||||
int qlen = queues_length_waiting();
|
|
||||||
bool xmore_is_needed = qlen > 0 && settings.indicate_hidden;
|
|
||||||
|
|
||||||
for (const GList *iter = queues_get_displayed();
|
|
||||||
iter; iter = iter->next)
|
|
||||||
{
|
|
||||||
struct notification *n = iter->data;
|
|
||||||
|
|
||||||
notification_update_text_to_render(n);
|
|
||||||
|
|
||||||
if (!iter->next && xmore_is_needed && settings.geometry.h == 1) {
|
|
||||||
char *new_ttr = g_strdup_printf("%s (%d more)", n->text_to_render, qlen);
|
|
||||||
g_free(n->text_to_render);
|
|
||||||
n->text_to_render = new_ttr;
|
|
||||||
}
|
|
||||||
layouts = g_slist_append(layouts,
|
|
||||||
layout_from_notification(c, n));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xmore_is_needed && settings.geometry.h != 1) {
|
|
||||||
/* append xmore message as new message */
|
|
||||||
layouts = g_slist_append(layouts,
|
|
||||||
layout_derive_xmore(c, queues_get_head_waiting(), qlen));
|
|
||||||
}
|
|
||||||
|
|
||||||
return layouts;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int layout_get_height(struct colored_layout *cl, int scale)
|
|
||||||
{
|
|
||||||
int h;
|
|
||||||
int h_icon = 0;
|
|
||||||
int h_progress_bar = 0;
|
|
||||||
get_text_size(cl->l, NULL, &h, scale);
|
|
||||||
if (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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a path on the given cairo context to draw the background of a notification.
|
|
||||||
* The top corners will get rounded by `corner_radius`, if `first` is set.
|
|
||||||
* Respectably the same for `last` with the bottom corners.
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (last) {
|
|
||||||
// bottom right
|
|
||||||
cairo_arc(c,
|
|
||||||
x + width - corner_radius,
|
|
||||||
y + height - corner_radius,
|
|
||||||
corner_radius,
|
|
||||||
degrees * 0,
|
|
||||||
degrees * 90);
|
|
||||||
// bottom left
|
|
||||||
cairo_arc(c,
|
|
||||||
x + corner_radius,
|
|
||||||
y + height - corner_radius,
|
|
||||||
corner_radius,
|
|
||||||
degrees * 90,
|
|
||||||
degrees * 180);
|
|
||||||
} else {
|
|
||||||
cairo_line_to(c, x + width, y + height);
|
|
||||||
cairo_line_to(c, x, y + height);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (first) {
|
|
||||||
// top left
|
|
||||||
cairo_arc(c,
|
|
||||||
x + corner_radius,
|
|
||||||
y + corner_radius,
|
|
||||||
corner_radius,
|
|
||||||
degrees * 180,
|
|
||||||
degrees * 270);
|
|
||||||
// top right
|
|
||||||
cairo_arc(c,
|
|
||||||
x + width - corner_radius,
|
|
||||||
y + corner_radius,
|
|
||||||
corner_radius,
|
|
||||||
degrees * 270,
|
|
||||||
degrees * 360);
|
|
||||||
} else {
|
|
||||||
cairo_line_to(c, x, y);
|
|
||||||
cairo_line_to(c, x + width, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
int y,
|
|
||||||
int width,
|
|
||||||
int height,
|
|
||||||
int corner_radius,
|
|
||||||
bool first,
|
|
||||||
bool last,
|
|
||||||
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)
|
|
||||||
height += settings.frame_width;
|
|
||||||
else
|
|
||||||
height += settings.separator_height;
|
|
||||||
|
|
||||||
draw_rounded_rect(c, x, y, width, height, corner_radius, scale, first, last);
|
|
||||||
|
|
||||||
/* adding frame */
|
|
||||||
x += settings.frame_width;
|
|
||||||
if (first) {
|
|
||||||
y += settings.frame_width;
|
|
||||||
height -= settings.frame_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
width -= 2 * settings.frame_width;
|
|
||||||
|
|
||||||
if (last)
|
|
||||||
height -= settings.frame_width;
|
|
||||||
else
|
|
||||||
height -= settings.separator_height;
|
|
||||||
|
|
||||||
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_rgba(c, sep_color.r, sep_color.g, sep_color.b, sep_color.a);
|
|
||||||
|
|
||||||
draw_rect(c, settings.frame_width, y + height, width, settings.separator_height, scale);
|
|
||||||
|
|
||||||
cairo_fill(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
cairo_destroy(c);
|
|
||||||
|
|
||||||
if (ret_width)
|
|
||||||
*ret_width = width;
|
|
||||||
|
|
||||||
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, int scale)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
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 = 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 ICON_RIGHT
|
|
||||||
|
|
||||||
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,
|
|
||||||
struct colored_layout *cl,
|
|
||||||
struct colored_layout *cl_next,
|
|
||||||
struct dimensions dim,
|
|
||||||
bool first,
|
|
||||||
bool last)
|
|
||||||
{
|
|
||||||
int scale = output->get_scale();
|
|
||||||
const int cl_h = layout_get_height(cl, scale);
|
|
||||||
|
|
||||||
int h_text = 0;
|
|
||||||
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, scale);
|
|
||||||
cairo_t *c = cairo_create(content);
|
|
||||||
|
|
||||||
render_content(c, cl, bg_width, scale);
|
|
||||||
|
|
||||||
/* adding frame */
|
|
||||||
if (first)
|
|
||||||
dim.y += settings.frame_width;
|
|
||||||
|
|
||||||
if (!last)
|
|
||||||
dim.y += settings.separator_height;
|
|
||||||
|
|
||||||
|
|
||||||
if (settings.notification_height <= (2 * settings.padding) + cl_h)
|
|
||||||
dim.y += cl_h + 2 * settings.padding;
|
|
||||||
else
|
|
||||||
dim.y += settings.notification_height;
|
|
||||||
|
|
||||||
cairo_destroy(c);
|
|
||||||
cairo_surface_destroy(content);
|
|
||||||
return dim;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the position the window should be placed at given its width and
|
|
||||||
* height and stores them in \p ret_x and \p ret_y.
|
|
||||||
*/
|
|
||||||
static void calc_window_pos(int width, int height, int *ret_x, int *ret_y)
|
|
||||||
{
|
|
||||||
const struct screen_info *scr = output->get_active_screen();
|
|
||||||
|
|
||||||
if (ret_x) {
|
|
||||||
if (settings.geometry.negative_x) {
|
|
||||||
*ret_x = (scr->x + (scr->w - width)) + settings.geometry.x;
|
|
||||||
} else {
|
|
||||||
*ret_x = scr->x + settings.geometry.x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret_y) {
|
|
||||||
if (settings.geometry.negative_y) {
|
|
||||||
*ret_y = scr->y + (scr->h + settings.geometry.y) - height;
|
|
||||||
} else {
|
|
||||||
*ret_y = scr->y + settings.geometry.y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw(void)
|
|
||||||
{
|
|
||||||
assert(queues_length_displayed() > 0);
|
|
||||||
|
|
||||||
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 * scale, dim.h * scale);
|
|
||||||
|
|
||||||
bool first = true;
|
|
||||||
for (GSList *iter = layouts; iter; iter = iter->next) {
|
|
||||||
|
|
||||||
struct colored_layout *cl_this = iter->data;
|
|
||||||
struct colored_layout *cl_next = iter->next ? iter->next->data : NULL;
|
|
||||||
|
|
||||||
dim = layout_render(image_surface, cl_this, cl_next, dim, first, !cl_next);
|
|
||||||
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
calc_window_pos(dim.w, dim.h, &dim.x, &dim.y);
|
|
||||||
output->display_surface(image_surface, win, &dim);
|
|
||||||
|
|
||||||
cairo_surface_destroy(image_surface);
|
|
||||||
g_slist_free_full(layouts, free_colored_layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw_deinit(void)
|
|
||||||
{
|
|
||||||
output->win_destroy(win);
|
|
||||||
output->deinit();
|
|
||||||
}
|
|
||||||
|
|
||||||
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: */
|
|
||||||
24
src/draw.h
@ -1,24 +0,0 @@
|
|||||||
#ifndef DUNST_DRAW_H
|
|
||||||
#define DUNST_DRAW_H
|
|
||||||
|
|
||||||
#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 ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
|
||||||
203
src/dunst.c
@ -1,121 +1,98 @@
|
|||||||
/* copyright 2012 - 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
|
/* copyright 2012 - 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
|
||||||
|
|
||||||
|
#define XLIB_ILLEGAL_ACCESS
|
||||||
|
|
||||||
#include "dunst.h"
|
#include "dunst.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <X11/Xlib.h>
|
||||||
#include <glib.h>
|
|
||||||
#include <glib-unix.h>
|
#include <glib-unix.h>
|
||||||
|
#include <glib.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "dbus.h"
|
#include "dbus.h"
|
||||||
#include "draw.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "menu.h"
|
#include "menu.h"
|
||||||
#include "notification.h"
|
#include "notification.h"
|
||||||
#include "option_parser.h"
|
#include "option_parser.h"
|
||||||
#include "queues.h"
|
#include "queues.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "utils.h"
|
#include "x11/screen.h"
|
||||||
#include "output.h"
|
#include "x11/x.h"
|
||||||
|
|
||||||
|
#ifndef VERSION
|
||||||
|
#define VERSION "version info needed"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MSG 1
|
||||||
|
#define INFO 2
|
||||||
|
#define DEBUG 3
|
||||||
|
|
||||||
|
typedef struct _x11_source {
|
||||||
|
GSource source;
|
||||||
|
Display *dpy;
|
||||||
|
Window w;
|
||||||
|
} x11_source_t;
|
||||||
|
|
||||||
|
/* index of colors fit to urgency level */
|
||||||
|
|
||||||
GMainLoop *mainloop = NULL;
|
GMainLoop *mainloop = NULL;
|
||||||
|
|
||||||
static struct dunst_status status;
|
GSList *rules = NULL;
|
||||||
static bool setup_done = false;
|
|
||||||
|
|
||||||
/* see dunst.h */
|
/* misc funtions */
|
||||||
void dunst_status(const enum dunst_status_field field,
|
|
||||||
bool value)
|
|
||||||
{
|
|
||||||
switch (field) {
|
|
||||||
case S_FULLSCREEN:
|
|
||||||
status.fullscreen = value;
|
|
||||||
break;
|
|
||||||
case S_IDLE:
|
|
||||||
status.idle = value;
|
|
||||||
break;
|
|
||||||
case S_RUNNING:
|
|
||||||
status.running = value;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOG_E("Invalid %s enum value in %s:%d", "dunst_status", __FILE__, __LINE__);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* see dunst.h */
|
|
||||||
struct dunst_status dunst_status_get(void)
|
|
||||||
{
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* misc functions */
|
|
||||||
static gboolean run(void *data);
|
|
||||||
|
|
||||||
void wake_up(void)
|
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);
|
run(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean run(void *data)
|
gboolean run(void *data)
|
||||||
{
|
{
|
||||||
|
queues_check_timeouts(x_is_idle());
|
||||||
|
queues_update();
|
||||||
|
|
||||||
|
static int timeout_cnt = 0;
|
||||||
static gint64 next_timeout = 0;
|
static gint64 next_timeout = 0;
|
||||||
|
|
||||||
LOG_D("RUN");
|
if (data && timeout_cnt > 0) {
|
||||||
|
timeout_cnt--;
|
||||||
dunst_status(S_FULLSCREEN, output->have_fullscreen_window());
|
|
||||||
dunst_status(S_IDLE, output->is_idle());
|
|
||||||
|
|
||||||
queues_update(status);
|
|
||||||
|
|
||||||
bool active = queues_length_displayed() > 0;
|
|
||||||
|
|
||||||
if (active) {
|
|
||||||
// Call draw before showing the window to avoid flickering
|
|
||||||
draw();
|
|
||||||
output->win_show(win);
|
|
||||||
} else {
|
|
||||||
output->win_hide(win);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (active) {
|
if (queues_length_displayed() > 0 && !xctx.visible) {
|
||||||
gint64 now = time_monotonic_now();
|
x_win_show();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xctx.visible && queues_length_displayed() == 0) {
|
||||||
|
x_win_hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xctx.visible) {
|
||||||
|
x_win_draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xctx.visible) {
|
||||||
|
gint64 now = g_get_monotonic_time();
|
||||||
gint64 sleep = queues_get_next_datachange(now);
|
gint64 sleep = queues_get_next_datachange(now);
|
||||||
gint64 timeout_at = now + sleep;
|
gint64 timeout_at = now + sleep;
|
||||||
|
|
||||||
LOG_D("Sleeping for %li ms", sleep/1000);
|
|
||||||
|
|
||||||
if (sleep >= 0) {
|
if (sleep >= 0) {
|
||||||
if (next_timeout < now || timeout_at < next_timeout) {
|
if (timeout_cnt == 0 || timeout_at < next_timeout) {
|
||||||
g_timeout_add(sleep/1000, run, NULL);
|
g_timeout_add(sleep/1000, run, mainloop);
|
||||||
next_timeout = timeout_at;
|
next_timeout = timeout_at;
|
||||||
|
timeout_cnt++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the execution got triggered by g_timeout_add,
|
/* always return false to delete timers */
|
||||||
* we have to remove the timeout (which is actually a
|
return false;
|
||||||
* recurring interval), as we have set a new one
|
|
||||||
* by ourselves.
|
|
||||||
*/
|
|
||||||
return G_SOURCE_REMOVE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gboolean pause_signal(gpointer data)
|
gboolean pause_signal(gpointer data)
|
||||||
{
|
{
|
||||||
dunst_status(S_RUNNING, false);
|
queues_pause_on();
|
||||||
wake_up();
|
wake_up();
|
||||||
|
|
||||||
return G_SOURCE_CONTINUE;
|
return G_SOURCE_CONTINUE;
|
||||||
@ -123,7 +100,7 @@ gboolean pause_signal(gpointer data)
|
|||||||
|
|
||||||
gboolean unpause_signal(gpointer data)
|
gboolean unpause_signal(gpointer data)
|
||||||
{
|
{
|
||||||
dunst_status(S_RUNNING, true);
|
queues_pause_off();
|
||||||
wake_up();
|
wake_up();
|
||||||
|
|
||||||
return G_SOURCE_CONTINUE;
|
return G_SOURCE_CONTINUE;
|
||||||
@ -140,32 +117,23 @@ static void teardown(void)
|
|||||||
{
|
{
|
||||||
regex_teardown();
|
regex_teardown();
|
||||||
|
|
||||||
queues_teardown();
|
teardown_queues();
|
||||||
|
|
||||||
draw_deinit();
|
x_free();
|
||||||
}
|
}
|
||||||
|
|
||||||
int dunst_main(int argc, char *argv[])
|
int dunst_main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
|
||||||
dunst_status(S_RUNNING, true);
|
|
||||||
dunst_status(S_IDLE, false);
|
|
||||||
|
|
||||||
queues_init();
|
queues_init();
|
||||||
|
|
||||||
cmdline_load(argc, argv);
|
cmdline_load(argc, argv);
|
||||||
|
|
||||||
dunst_log_init(false);
|
|
||||||
|
|
||||||
if (cmdline_get_bool("-v/-version", false, "Print version")
|
if (cmdline_get_bool("-v/-version", false, "Print version")
|
||||||
|| cmdline_get_bool("--version", false, "Print version")) {
|
|| cmdline_get_bool("--version", false, "Print version")) {
|
||||||
print_version();
|
print_version();
|
||||||
}
|
}
|
||||||
|
|
||||||
char *verbosity = cmdline_get_string("-verbosity", NULL, "Minimum level for message");
|
|
||||||
log_set_level_from_string(verbosity);
|
|
||||||
g_free(verbosity);
|
|
||||||
|
|
||||||
char *cmdline_config_path;
|
char *cmdline_config_path;
|
||||||
cmdline_config_path =
|
cmdline_config_path =
|
||||||
cmdline_get_string("-conf/-config", NULL,
|
cmdline_get_string("-conf/-config", NULL,
|
||||||
@ -177,11 +145,46 @@ int dunst_main(int argc, char *argv[])
|
|||||||
usage(EXIT_SUCCESS);
|
usage(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
int dbus_owner_id = dbus_init();
|
int owner_id = initdbus();
|
||||||
|
|
||||||
|
x_setup();
|
||||||
|
|
||||||
|
if (settings.startup_notification) {
|
||||||
|
notification *n = notification_create();
|
||||||
|
n->appname = g_strdup("dunst");
|
||||||
|
n->summary = g_strdup("startup");
|
||||||
|
n->body = g_strdup("dunst is up and running");
|
||||||
|
n->progress = -1;
|
||||||
|
n->timeout = 10 * G_USEC_PER_SEC;
|
||||||
|
n->markup = MARKUP_NO;
|
||||||
|
n->urgency = URG_LOW;
|
||||||
|
notification_init(n);
|
||||||
|
queues_notification_insert(n, 0);
|
||||||
|
// we do not call wakeup now, wake_up does not work here yet
|
||||||
|
}
|
||||||
|
|
||||||
mainloop = g_main_loop_new(NULL, FALSE);
|
mainloop = g_main_loop_new(NULL, FALSE);
|
||||||
|
|
||||||
draw_setup();
|
GPollFD dpy_pollfd = { xctx.dpy->fd,
|
||||||
|
G_IO_IN | G_IO_HUP | G_IO_ERR, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
GSourceFuncs x11_source_funcs = {
|
||||||
|
x_mainloop_fd_prepare,
|
||||||
|
x_mainloop_fd_check,
|
||||||
|
x_mainloop_fd_dispatch,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
GSource *x11_source =
|
||||||
|
g_source_new(&x11_source_funcs, sizeof(x11_source_t));
|
||||||
|
((x11_source_t *) x11_source)->dpy = xctx.dpy;
|
||||||
|
((x11_source_t *) x11_source)->w = xctx.win;
|
||||||
|
g_source_add_poll(x11_source, &dpy_pollfd);
|
||||||
|
|
||||||
|
g_source_attach(x11_source, NULL);
|
||||||
|
|
||||||
guint pause_src = g_unix_signal_add(SIGUSR1, pause_signal, NULL);
|
guint pause_src = g_unix_signal_add(SIGUSR1, pause_signal, NULL);
|
||||||
guint unpause_src = g_unix_signal_add(SIGUSR2, unpause_signal, NULL);
|
guint unpause_src = g_unix_signal_add(SIGUSR2, unpause_signal, NULL);
|
||||||
@ -191,25 +194,9 @@ int dunst_main(int argc, char *argv[])
|
|||||||
guint term_src = g_unix_signal_add(SIGTERM, quit_signal, NULL);
|
guint term_src = g_unix_signal_add(SIGTERM, quit_signal, NULL);
|
||||||
guint int_src = g_unix_signal_add(SIGINT, quit_signal, NULL);
|
guint int_src = g_unix_signal_add(SIGINT, quit_signal, NULL);
|
||||||
|
|
||||||
if (settings.startup_notification) {
|
|
||||||
struct notification *n = notification_create();
|
|
||||||
n->id = 0;
|
|
||||||
n->appname = g_strdup("dunst");
|
|
||||||
n->summary = g_strdup("startup");
|
|
||||||
n->body = g_strdup("dunst is up and running");
|
|
||||||
n->progress = -1;
|
|
||||||
n->timeout = S2US(10);
|
|
||||||
n->markup = MARKUP_NO;
|
|
||||||
n->urgency = URG_LOW;
|
|
||||||
notification_init(n);
|
|
||||||
queues_notification_insert(n);
|
|
||||||
// we do not call wakeup now, wake_up does not work here yet
|
|
||||||
}
|
|
||||||
|
|
||||||
setup_done = true;
|
|
||||||
run(NULL);
|
run(NULL);
|
||||||
g_main_loop_run(mainloop);
|
g_main_loop_run(mainloop);
|
||||||
g_clear_pointer(&mainloop, g_main_loop_unref);
|
g_main_loop_unref(mainloop);
|
||||||
|
|
||||||
/* remove signal handler watches */
|
/* remove signal handler watches */
|
||||||
g_source_remove(pause_src);
|
g_source_remove(pause_src);
|
||||||
@ -217,7 +204,9 @@ int dunst_main(int argc, char *argv[])
|
|||||||
g_source_remove(term_src);
|
g_source_remove(term_src);
|
||||||
g_source_remove(int_src);
|
g_source_remove(int_src);
|
||||||
|
|
||||||
dbus_teardown(dbus_owner_id);
|
g_source_destroy(x11_source);
|
||||||
|
|
||||||
|
dbus_tear_down(owner_id);
|
||||||
|
|
||||||
teardown();
|
teardown();
|
||||||
|
|
||||||
@ -240,4 +229,4 @@ void print_version(void)
|
|||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
|||||||
37
src/dunst.h
@ -6,39 +6,32 @@
|
|||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
#include "notification.h"
|
#include "notification.h"
|
||||||
|
|
||||||
//!< A structure to describe dunst's global window status
|
#define PERR(msg, errnum) printf("(%d) %s : %s\n", __LINE__, (msg), (strerror(errnum)))
|
||||||
struct dunst_status {
|
|
||||||
bool fullscreen; //!< a fullscreen window is currently focused
|
|
||||||
bool running; //!< set true if dunst is currently running
|
|
||||||
bool idle; //!< set true if the user is idle
|
|
||||||
};
|
|
||||||
|
|
||||||
enum dunst_status_field {
|
#define ColLast 3
|
||||||
S_FULLSCREEN,
|
#define ColFrame 2
|
||||||
S_IDLE,
|
#define ColFG 1
|
||||||
S_RUNNING,
|
#define ColBG 0
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
extern GSList *rules;
|
||||||
* Modify the current status of dunst
|
extern const char *color_strings[3][3];
|
||||||
* @param field The field to change in the global status structure
|
|
||||||
* @param value Anything boolean or DO_TOGGLE to toggle the current value
|
|
||||||
*/
|
|
||||||
void dunst_status(const enum dunst_status_field field,
|
|
||||||
bool value);
|
|
||||||
|
|
||||||
struct dunst_status dunst_status_get(void);
|
|
||||||
|
|
||||||
|
/* return id of notification */
|
||||||
|
gboolean run(void *data);
|
||||||
void wake_up(void);
|
void wake_up(void);
|
||||||
|
|
||||||
int dunst_main(int argc, char *argv[]);
|
int dunst_main(int argc, char *argv[]);
|
||||||
|
|
||||||
|
void check_timeouts(void);
|
||||||
void usage(int exit_status);
|
void usage(int exit_status);
|
||||||
void print_version(void);
|
void print_version(void);
|
||||||
|
char *extract_urls(const char *str);
|
||||||
|
void context_menu(void);
|
||||||
|
void wake_up(void);
|
||||||
|
void pause_signal_handler(int sig);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
|||||||
410
src/icon.c
@ -1,410 +0,0 @@
|
|||||||
#include "icon.h"
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <cairo.h>
|
|
||||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "log.h"
|
|
||||||
#include "notification.h"
|
|
||||||
#include "settings.h"
|
|
||||||
#include "utils.h"
|
|
||||||
|
|
||||||
static bool is_readable_file(const char *filename)
|
|
||||||
{
|
|
||||||
return (access(filename, R_OK) != -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reassemble the data parts of a GdkPixbuf into a cairo_surface_t's data field.
|
|
||||||
*
|
|
||||||
* Requires to call on the surface flush before and mark_dirty after the execution.
|
|
||||||
*/
|
|
||||||
static void pixbuf_data_to_cairo_data(
|
|
||||||
const unsigned char *pixels_p,
|
|
||||||
unsigned char *pixels_c,
|
|
||||||
size_t rowstride_p,
|
|
||||||
size_t rowstride_c,
|
|
||||||
int width,
|
|
||||||
int height,
|
|
||||||
int n_channels)
|
|
||||||
{
|
|
||||||
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
|
||||||
static const size_t CAIRO_B = 0;
|
|
||||||
static const size_t CAIRO_G = 1;
|
|
||||||
static const size_t CAIRO_R = 2;
|
|
||||||
static const size_t CAIRO_A = 3;
|
|
||||||
#elif G_BYTE_ORDER == G_BIG_ENDIAN
|
|
||||||
static const size_t CAIRO_A = 0;
|
|
||||||
static const size_t CAIRO_R = 1;
|
|
||||||
static const size_t CAIRO_G = 2;
|
|
||||||
static const size_t CAIRO_B = 3;
|
|
||||||
#elif G_BYTE_ORDER == G_PDP_ENDIAN
|
|
||||||
static const size_t CAIRO_R = 0;
|
|
||||||
static const size_t CAIRO_A = 1;
|
|
||||||
static const size_t CAIRO_B = 2;
|
|
||||||
static const size_t CAIRO_G = 3;
|
|
||||||
#else
|
|
||||||
// GLib doesn't support any other endiannesses
|
|
||||||
#error Unsupported Endianness
|
|
||||||
#endif
|
|
||||||
|
|
||||||
assert(pixels_p);
|
|
||||||
assert(pixels_c);
|
|
||||||
assert(width > 0);
|
|
||||||
assert(height > 0);
|
|
||||||
|
|
||||||
if (n_channels == 3) {
|
|
||||||
for (int h = 0; h < height; h++) {
|
|
||||||
unsigned char *iter_c = pixels_c + h * rowstride_c;
|
|
||||||
const unsigned char *iter_p = pixels_p + h * rowstride_p;
|
|
||||||
for (int w = 0; w < width; w++) {
|
|
||||||
iter_c[CAIRO_R] = iter_p[0];
|
|
||||||
iter_c[CAIRO_G] = iter_p[1];
|
|
||||||
iter_c[CAIRO_B] = iter_p[2];
|
|
||||||
iter_c[CAIRO_A] = 0xff;
|
|
||||||
iter_c += 4;
|
|
||||||
iter_p += n_channels;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (int h = 0; h < height; h++) {
|
|
||||||
unsigned char *iter_c = pixels_c + h * rowstride_c;
|
|
||||||
const unsigned char *iter_p = pixels_p + h * rowstride_p;
|
|
||||||
for (int w = 0; w < width; w++) {
|
|
||||||
double alpha_factor = iter_p[3] / (double)0xff;
|
|
||||||
iter_c[CAIRO_R] = (unsigned char)(iter_p[0] * alpha_factor + .5);
|
|
||||||
iter_c[CAIRO_G] = (unsigned char)(iter_p[1] * alpha_factor + .5);
|
|
||||||
iter_c[CAIRO_B] = (unsigned char)(iter_p[2] * alpha_factor + .5);
|
|
||||||
iter_c[CAIRO_A] = iter_p[3];
|
|
||||||
iter_c += 4;
|
|
||||||
iter_p += n_channels;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
int width = gdk_pixbuf_get_width(pixbuf);
|
|
||||||
int height = gdk_pixbuf_get_height(pixbuf);
|
|
||||||
|
|
||||||
cairo_format_t fmt = gdk_pixbuf_get_has_alpha(pixbuf) ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24;
|
|
||||||
cairo_surface_t *icon_surface = cairo_image_surface_create(fmt, width, height);
|
|
||||||
|
|
||||||
/* Copy pixel data from pixbuf to surface */
|
|
||||||
cairo_surface_flush(icon_surface);
|
|
||||||
pixbuf_data_to_cairo_data(gdk_pixbuf_read_pixels(pixbuf),
|
|
||||||
cairo_image_surface_get_data(icon_surface),
|
|
||||||
gdk_pixbuf_get_rowstride(pixbuf),
|
|
||||||
cairo_format_stride_for_width(fmt, width),
|
|
||||||
gdk_pixbuf_get_width(pixbuf),
|
|
||||||
gdk_pixbuf_get_height(pixbuf),
|
|
||||||
gdk_pixbuf_get_n_channels(pixbuf));
|
|
||||||
cairo_surface_mark_dirty(icon_surface);
|
|
||||||
|
|
||||||
return icon_surface;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
|
|
||||||
// TODO immediately rescale icon upon scale changes
|
|
||||||
if (icon_size_clamp(&w, &h)) {
|
|
||||||
GdkPixbuf *scaled = gdk_pixbuf_scale_simple(
|
|
||||||
pixbuf,
|
|
||||||
w * dpi_scale,
|
|
||||||
h * dpi_scale,
|
|
||||||
GDK_INTERP_BILINEAR);
|
|
||||||
g_object_unref(pixbuf);
|
|
||||||
pixbuf = scaled;
|
|
||||||
}
|
|
||||||
|
|
||||||
return pixbuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
GdkPixbuf *get_pixbuf_from_file(const char *filename, int scale)
|
|
||||||
{
|
|
||||||
char *path = string_to_path(g_strdup(filename));
|
|
||||||
GError *error = NULL;
|
|
||||||
gint w, h;
|
|
||||||
|
|
||||||
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);
|
|
||||||
g_error_free(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free(path);
|
|
||||||
return pixbuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *get_path_from_icon_name(const char *iconname)
|
|
||||||
{
|
|
||||||
if (STR_EMPTY(iconname))
|
|
||||||
return 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);
|
|
||||||
if (uri_path)
|
|
||||||
iconname = uri_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* absolute path? */
|
|
||||||
if (iconname[0] == '/' || iconname[0] == '~') {
|
|
||||||
new_name = g_strdup(iconname);
|
|
||||||
} else {
|
|
||||||
/* search in icon_path */
|
|
||||||
char *start = settings.icon_path,
|
|
||||||
*end, *current_folder, *maybe_icon_path;
|
|
||||||
do {
|
|
||||||
end = strchr(start, ':');
|
|
||||||
if (!end) end = strchr(settings.icon_path, '\0'); /* end = end of string */
|
|
||||||
|
|
||||||
current_folder = g_strndup(start, end - start);
|
|
||||||
|
|
||||||
for (const char **suf = suffixes; *suf; suf++) {
|
|
||||||
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 (new_name)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free(current_folder);
|
|
||||||
if (new_name)
|
|
||||||
break;
|
|
||||||
|
|
||||||
start = end + 1;
|
|
||||||
} while (STR_FULL(end));
|
|
||||||
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, int scale)
|
|
||||||
{
|
|
||||||
ASSERT_OR_RET(name, NULL);
|
|
||||||
ASSERT_OR_RET(id, NULL);
|
|
||||||
|
|
||||||
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, int dpi_scale)
|
|
||||||
{
|
|
||||||
ASSERT_OR_RET(data, NULL);
|
|
||||||
ASSERT_OR_RET(id, NULL);
|
|
||||||
|
|
||||||
if (!STR_EQ("(iiibiiay)", g_variant_get_type_string(data))) {
|
|
||||||
LOG_W("Invalid data for pixbuf given.");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The raw image is a big array of char data.
|
|
||||||
*
|
|
||||||
* The image is serialised rowwise pixel by pixel. The rows are aligned
|
|
||||||
* by a spacer full of garbage. The overall data length of data + garbage
|
|
||||||
* is called the rowstride.
|
|
||||||
*
|
|
||||||
* Mind the missing spacer at the last row.
|
|
||||||
*
|
|
||||||
* len: |<--------------rowstride---------------->|
|
|
||||||
* len: |<-width*pixelstride->|
|
|
||||||
* row 1: | data for row 1 | spacer of garbage |
|
|
||||||
* row 2: | data for row 2 | spacer of garbage |
|
|
||||||
* | . | spacer of garbage |
|
|
||||||
* | . | spacer of garbage |
|
|
||||||
* | . | spacer of garbage |
|
|
||||||
* row n-1: | data for row n-1 | spacer of garbage |
|
|
||||||
* row n: | data for row n |
|
|
||||||
*/
|
|
||||||
|
|
||||||
GdkPixbuf *pixbuf = NULL;
|
|
||||||
GVariant *data_variant = NULL;
|
|
||||||
unsigned char *data_pb;
|
|
||||||
|
|
||||||
gsize len_expected;
|
|
||||||
gsize len_actual;
|
|
||||||
gsize pixelstride;
|
|
||||||
|
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
int rowstride;
|
|
||||||
int has_alpha;
|
|
||||||
int bits_per_sample;
|
|
||||||
int n_channels;
|
|
||||||
|
|
||||||
g_variant_get(data,
|
|
||||||
"(iiibii@ay)",
|
|
||||||
&width,
|
|
||||||
&height,
|
|
||||||
&rowstride,
|
|
||||||
&has_alpha,
|
|
||||||
&bits_per_sample,
|
|
||||||
&n_channels,
|
|
||||||
&data_variant);
|
|
||||||
|
|
||||||
// note: (A+7)/8 rounds up A to the next byte boundary
|
|
||||||
pixelstride = (n_channels * bits_per_sample + 7)/8;
|
|
||||||
len_expected = (height - 1) * rowstride + width * pixelstride;
|
|
||||||
len_actual = g_variant_get_size(data_variant);
|
|
||||||
|
|
||||||
if (len_actual != len_expected) {
|
|
||||||
LOG_W("Expected image data to be of length %" G_GSIZE_FORMAT
|
|
||||||
" but got a length of %" G_GSIZE_FORMAT,
|
|
||||||
len_expected,
|
|
||||||
len_actual);
|
|
||||||
g_variant_unref(data_variant);
|
|
||||||
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,
|
|
||||||
has_alpha,
|
|
||||||
bits_per_sample,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
rowstride,
|
|
||||||
(GdkPixbufDestroyNotify) g_free,
|
|
||||||
data_pb);
|
|
||||||
if (!pixbuf) {
|
|
||||||
/* Dear user, I'm sorry, I'd like to give you a more specific
|
|
||||||
* error message. But sadly, I can't */
|
|
||||||
LOG_W("Cannot serialise raw icon data into pixbuf.");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* To calculate a checksum of the current image, we have to remove
|
|
||||||
* all excess spacers, so that our checksummed memory only contains
|
|
||||||
* real data. */
|
|
||||||
size_t data_chk_len = pixelstride * width * height;
|
|
||||||
unsigned char *data_chk = g_malloc(data_chk_len);
|
|
||||||
size_t rowstride_short = pixelstride * width;
|
|
||||||
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
memcpy(data_chk + (i*rowstride_short),
|
|
||||||
data_pb + (i*rowstride),
|
|
||||||
rowstride_short);
|
|
||||||
}
|
|
||||||
|
|
||||||
*id = g_compute_checksum_for_data(G_CHECKSUM_MD5, data_chk, data_chk_len);
|
|
||||||
|
|
||||||
g_free(data_chk);
|
|
||||||
g_variant_unref(data_variant);
|
|
||||||
|
|
||||||
pixbuf = icon_pixbuf_scale(pixbuf, dpi_scale);
|
|
||||||
|
|
||||||
return pixbuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
|
||||||
90
src/icon.h
@ -1,90 +0,0 @@
|
|||||||
#ifndef DUNST_ICON_H
|
|
||||||
#define DUNST_ICON_H
|
|
||||||
|
|
||||||
#include <cairo.h>
|
|
||||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
|
||||||
|
|
||||||
#include "notification.h"
|
|
||||||
|
|
||||||
cairo_surface_t *gdk_pixbuf_to_cairo_surface(GdkPixbuf *pixbuf);
|
|
||||||
|
|
||||||
/** 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, int scale);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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, int scale);
|
|
||||||
|
|
||||||
/** 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.
|
|
||||||
*
|
|
||||||
* @param name A string describing and icon. May be a full path, a file path or
|
|
||||||
* just a simple name. If it's a name without a slash, the icon will
|
|
||||||
* 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, int dpi_scale);
|
|
||||||
|
|
||||||
/** 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.
|
|
||||||
*
|
|
||||||
* @param data A GVariant in the format "(iiibii@ay)" filled with values
|
|
||||||
* 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, int scale);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
|
||||||
81
src/input.c
@ -1,81 +0,0 @@
|
|||||||
#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
@ -1,18 +0,0 @@
|
|||||||
#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: */
|
|
||||||
103
src/log.c
@ -1,103 +0,0 @@
|
|||||||
/* copyright 2012 - 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file src/log.c
|
|
||||||
* @brief logging wrapper to use GLib's logging capabilities
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "log.h"
|
|
||||||
|
|
||||||
#include <glib.h>
|
|
||||||
|
|
||||||
#include "utils.h"
|
|
||||||
|
|
||||||
static GLogLevelFlags log_level = G_LOG_LEVEL_WARNING;
|
|
||||||
|
|
||||||
/* see log.h */
|
|
||||||
static const char *log_level_to_string(GLogLevelFlags level)
|
|
||||||
{
|
|
||||||
switch (level) {
|
|
||||||
case G_LOG_LEVEL_ERROR: return "ERROR";
|
|
||||||
case G_LOG_LEVEL_CRITICAL: return "CRITICAL";
|
|
||||||
case G_LOG_LEVEL_WARNING: return "WARNING";
|
|
||||||
case G_LOG_LEVEL_MESSAGE: return "MESSAGE";
|
|
||||||
case G_LOG_LEVEL_INFO: return "INFO";
|
|
||||||
case G_LOG_LEVEL_DEBUG: return "DEBUG";
|
|
||||||
default: return "UNKNOWN";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* see log.h */
|
|
||||||
void log_set_level_from_string(const char *level)
|
|
||||||
{
|
|
||||||
ASSERT_OR_RET(level,);
|
|
||||||
|
|
||||||
if (STR_CASEQ(level, "critical"))
|
|
||||||
log_level = G_LOG_LEVEL_CRITICAL;
|
|
||||||
else if (STR_CASEQ(level, "crit"))
|
|
||||||
log_level = G_LOG_LEVEL_CRITICAL;
|
|
||||||
else if (STR_CASEQ(level, "warning"))
|
|
||||||
log_level = G_LOG_LEVEL_WARNING;
|
|
||||||
else if (STR_CASEQ(level, "warn"))
|
|
||||||
log_level = G_LOG_LEVEL_WARNING;
|
|
||||||
else if (STR_CASEQ(level, "message"))
|
|
||||||
log_level = G_LOG_LEVEL_MESSAGE;
|
|
||||||
else if (STR_CASEQ(level, "mesg"))
|
|
||||||
log_level = G_LOG_LEVEL_MESSAGE;
|
|
||||||
else if (STR_CASEQ(level, "info"))
|
|
||||||
log_level = G_LOG_LEVEL_INFO;
|
|
||||||
else if (STR_CASEQ(level, "debug"))
|
|
||||||
log_level = G_LOG_LEVEL_DEBUG;
|
|
||||||
else if (STR_CASEQ(level, "deb"))
|
|
||||||
log_level = G_LOG_LEVEL_DEBUG;
|
|
||||||
else
|
|
||||||
LOG_W("Unknown log level: '%s'", level);
|
|
||||||
}
|
|
||||||
|
|
||||||
void log_set_level(GLogLevelFlags level)
|
|
||||||
{
|
|
||||||
log_level = level;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log handling function for GLib's logging wrapper
|
|
||||||
*
|
|
||||||
* @param log_domain Used only by GLib
|
|
||||||
* @param message_level Used only by GLib
|
|
||||||
* @param message Used only by GLib
|
|
||||||
* @param testing If not `NULL` (here: `true`), do nothing
|
|
||||||
*/
|
|
||||||
static void dunst_log_handler(
|
|
||||||
const gchar *log_domain,
|
|
||||||
GLogLevelFlags message_level,
|
|
||||||
const gchar *message,
|
|
||||||
gpointer testing)
|
|
||||||
{
|
|
||||||
if (testing)
|
|
||||||
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_masked)
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
const char *log_level_str =
|
|
||||||
log_level_to_string(message_level_masked);
|
|
||||||
|
|
||||||
/* Use stderr for warnings and higher */
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* see log.h */
|
|
||||||
void dunst_log_init(bool testing)
|
|
||||||
{
|
|
||||||
g_log_set_default_handler(dunst_log_handler, (void*)testing);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
|
||||||
48
src/log.h
@ -1,48 +0,0 @@
|
|||||||
/* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
|
|
||||||
|
|
||||||
#include <glib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#ifndef DUNST_LOG_H
|
|
||||||
#define DUNST_LOG_H
|
|
||||||
|
|
||||||
#define LOG_E g_error
|
|
||||||
#define LOG_C g_critical
|
|
||||||
#define LOG_W g_warning
|
|
||||||
#define LOG_M g_message
|
|
||||||
#define LOG_I g_info
|
|
||||||
#define LOG_D g_debug
|
|
||||||
|
|
||||||
#define DIE(...) do { LOG_C(__VA_ARGS__); exit(EXIT_FAILURE); } while (0)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the current loglevel to `level`
|
|
||||||
*
|
|
||||||
* @param level The desired log level
|
|
||||||
*
|
|
||||||
* If `level` is `NULL`, nothing will be done.
|
|
||||||
* If `level` is an invalid value, nothing will be done.
|
|
||||||
*/
|
|
||||||
void log_set_level(GLogLevelFlags level);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the current loglevel to `level`
|
|
||||||
*
|
|
||||||
* @param level The desired log level as a string
|
|
||||||
*
|
|
||||||
* If `level` is `NULL`, nothing will be done.
|
|
||||||
* If `level` is an invalid value, nothing will be done.
|
|
||||||
*/
|
|
||||||
void log_set_level_from_string(const char* level);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialise log handling. Can be called any time.
|
|
||||||
*
|
|
||||||
* @param testing If we're in testing mode and should
|
|
||||||
* suppress all output
|
|
||||||
*/
|
|
||||||
void dunst_log_init(bool testing);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
|
||||||
282
src/markup.c
@ -3,22 +3,14 @@
|
|||||||
#include "markup.h"
|
#include "markup.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <ctype.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "log.h"
|
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert all HTML special symbols to HTML entities.
|
|
||||||
* @param str (nullable)
|
|
||||||
*/
|
|
||||||
static char *markup_quote(char *str)
|
static char *markup_quote(char *str)
|
||||||
{
|
{
|
||||||
ASSERT_OR_RET(str, NULL);
|
assert(str != NULL);
|
||||||
|
|
||||||
str = string_replace_all("&", "&", str);
|
str = string_replace_all("&", "&", str);
|
||||||
str = string_replace_all("\"", """, str);
|
str = string_replace_all("\"", """, str);
|
||||||
@ -29,13 +21,9 @@ static char *markup_quote(char *str)
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert all HTML special entities to their actual char.
|
|
||||||
* @param str (nullable)
|
|
||||||
*/
|
|
||||||
static char *markup_unquote(char *str)
|
static char *markup_unquote(char *str)
|
||||||
{
|
{
|
||||||
ASSERT_OR_RET(str, NULL);
|
assert(str != NULL);
|
||||||
|
|
||||||
str = string_replace_all(""", "\"", str);
|
str = string_replace_all(""", "\"", str);
|
||||||
str = string_replace_all("'", "'", str);
|
str = string_replace_all("'", "'", str);
|
||||||
@ -46,13 +34,9 @@ static char *markup_unquote(char *str)
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert all HTML linebreak tags to a newline character
|
|
||||||
* @param str (nullable)
|
|
||||||
*/
|
|
||||||
static char *markup_br2nl(char *str)
|
static char *markup_br2nl(char *str)
|
||||||
{
|
{
|
||||||
ASSERT_OR_RET(str, NULL);
|
assert(str != NULL);
|
||||||
|
|
||||||
str = string_replace_all("<br>", "\n", str);
|
str = string_replace_all("<br>", "\n", str);
|
||||||
str = string_replace_all("<br/>", "\n", str);
|
str = string_replace_all("<br/>", "\n", str);
|
||||||
@ -60,168 +44,23 @@ static char *markup_br2nl(char *str)
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see markup.h */
|
/*
|
||||||
void markup_strip_a(char **str, char **urls)
|
* Strip any markup from text; turn it in to plain text.
|
||||||
{
|
*
|
||||||
assert(*str);
|
* For well-formed markup, the following two commands should be
|
||||||
char *tag1 = NULL;
|
* roughly equivalent:
|
||||||
|
*
|
||||||
if (urls)
|
* out = markup_strip(in);
|
||||||
*urls = NULL;
|
* pango_parse_markup(in, -1, 0, NULL, &out, NULL, NULL);
|
||||||
|
*
|
||||||
while ((tag1 = strstr(*str, "<a"))) {
|
* However, `pango_parse_markup()` balks at invalid markup;
|
||||||
// use href=" as stated in the notification spec
|
* `markup_strip()` shouldn't care if there is invalid markup.
|
||||||
char *href = strstr(tag1, "href=\"");
|
*/
|
||||||
char *tag1_end = strstr(tag1, ">");
|
|
||||||
char *tag2 = strstr(tag1, "</a>");
|
|
||||||
|
|
||||||
// the tag is broken, ignore it
|
|
||||||
if (!tag1_end) {
|
|
||||||
LOG_W("Given link is broken: '%s'",
|
|
||||||
tag1);
|
|
||||||
string_replace_at(*str, tag1-*str, strlen(tag1), "");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (tag2 && tag2 < tag1_end) {
|
|
||||||
int repl_len = (tag2 - tag1) + strlen("</a>");
|
|
||||||
LOG_W("Given link is broken: '%.*s.'",
|
|
||||||
repl_len, tag1);
|
|
||||||
string_replace_at(*str, tag1-*str, repl_len, "");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// search contents of href attribute
|
|
||||||
char *plain_url = NULL;
|
|
||||||
if (href && href < tag1_end) {
|
|
||||||
|
|
||||||
// shift href to the actual begin of the value
|
|
||||||
href = href+6;
|
|
||||||
|
|
||||||
const char *quote = strstr(href, "\"");
|
|
||||||
|
|
||||||
if (quote && quote < tag1_end) {
|
|
||||||
plain_url = g_strndup(href, quote-href);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// text between a tags
|
|
||||||
int text_len;
|
|
||||||
if (tag2)
|
|
||||||
text_len = tag2 - (tag1_end+1);
|
|
||||||
else
|
|
||||||
text_len = strlen(tag1_end+1);
|
|
||||||
|
|
||||||
char *text = g_strndup(tag1_end+1, text_len);
|
|
||||||
|
|
||||||
int repl_len = text_len + (tag1_end-tag1) + 1;
|
|
||||||
repl_len += tag2 ? strlen("</a>") : 0;
|
|
||||||
|
|
||||||
*str = string_replace_at(*str, tag1-*str, repl_len, text);
|
|
||||||
|
|
||||||
// if there had been a href attribute,
|
|
||||||
// add it to the URLs
|
|
||||||
if (plain_url && urls) {
|
|
||||||
text = string_replace_all("]", "", text);
|
|
||||||
text = string_replace_all("[", "", text);
|
|
||||||
|
|
||||||
char *url = g_strdup_printf("[%s] %s", text, plain_url);
|
|
||||||
|
|
||||||
*urls = string_append(*urls, url, "\n");
|
|
||||||
g_free(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free(plain_url);
|
|
||||||
g_free(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* see markup.h */
|
|
||||||
void markup_strip_img(char **str, char **urls)
|
|
||||||
{
|
|
||||||
const char *start;
|
|
||||||
|
|
||||||
if (urls)
|
|
||||||
*urls = NULL;
|
|
||||||
|
|
||||||
while ((start = strstr(*str, "<img"))) {
|
|
||||||
const char *end = strstr(start, ">");
|
|
||||||
|
|
||||||
// the tag is broken, ignore it
|
|
||||||
if (!end) {
|
|
||||||
LOG_W("Given image is broken: '%s'", start);
|
|
||||||
string_replace_at(*str, start-*str, strlen(start), "");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// use attribute=" as stated in the notification spec
|
|
||||||
const char *alt_s = strstr(start, "alt=\"");
|
|
||||||
const char *src_s = strstr(start, "src=\"");
|
|
||||||
|
|
||||||
char *text_alt = NULL;
|
|
||||||
char *text_src = NULL;
|
|
||||||
|
|
||||||
const char *src_e = NULL, *alt_e = NULL;
|
|
||||||
if (alt_s)
|
|
||||||
alt_e = strstr(alt_s + strlen("alt=\""), "\"");
|
|
||||||
if (src_s)
|
|
||||||
src_e = strstr(src_s + strlen("src=\""), "\"");
|
|
||||||
|
|
||||||
// Move pointer to the actual start
|
|
||||||
alt_s = alt_s ? alt_s + strlen("alt=\"") : NULL;
|
|
||||||
src_s = src_s ? src_s + strlen("src=\"") : NULL;
|
|
||||||
|
|
||||||
/* check if alt and src attribute are given
|
|
||||||
* If both given, check the alignment of all pointers */
|
|
||||||
if ( alt_s && alt_e
|
|
||||||
&& src_s && src_e
|
|
||||||
&& ( (alt_s < src_s && alt_e < src_s-strlen("src=\"") && src_e < end)
|
|
||||||
||(src_s < alt_s && src_e < alt_s-strlen("alt=\"") && alt_e < end)) ) {
|
|
||||||
|
|
||||||
text_alt = g_strndup(alt_s, alt_e-alt_s);
|
|
||||||
text_src = g_strndup(src_s, src_e-src_s);
|
|
||||||
|
|
||||||
/* check if single valid alt attribute is available */
|
|
||||||
} else if (alt_s && alt_e && alt_e < end && (!src_s || src_s < alt_s || alt_e < src_s - strlen("src=\""))) {
|
|
||||||
text_alt = g_strndup(alt_s, alt_e-alt_s);
|
|
||||||
|
|
||||||
/* check if single valid src attribute is available */
|
|
||||||
} else if (src_s && src_e && src_e < end && (!alt_s || alt_s < src_s || src_e < alt_s - strlen("alt=\""))) {
|
|
||||||
text_src = g_strndup(src_s, src_e-src_s);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
LOG_W("Given image argument is broken: '%.*s'",
|
|
||||||
(int)(end-start), start);
|
|
||||||
}
|
|
||||||
|
|
||||||
// replacement text for alt
|
|
||||||
int repl_len = end - start + 1;
|
|
||||||
|
|
||||||
if (!text_alt)
|
|
||||||
text_alt = g_strdup("[image]");
|
|
||||||
|
|
||||||
*str = string_replace_at(*str, start-*str, repl_len, text_alt);
|
|
||||||
|
|
||||||
// if there had been a href attribute,
|
|
||||||
// add it to the URLs
|
|
||||||
if (text_src && urls) {
|
|
||||||
text_alt = string_replace_all("]", "", text_alt);
|
|
||||||
text_alt = string_replace_all("[", "", text_alt);
|
|
||||||
|
|
||||||
char *url = g_strdup_printf("[%s] %s", text_alt, text_src);
|
|
||||||
|
|
||||||
*urls = string_append(*urls, url, "\n");
|
|
||||||
g_free(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free(text_src);
|
|
||||||
g_free(text_alt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* see markup.h */
|
|
||||||
char *markup_strip(char *str)
|
char *markup_strip(char *str)
|
||||||
{
|
{
|
||||||
ASSERT_OR_RET(str, NULL);
|
if (str == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* strip all tags */
|
/* strip all tags */
|
||||||
string_strip_delimited(str, '<', '>');
|
string_strip_delimited(str, '<', '>');
|
||||||
@ -232,83 +71,15 @@ char *markup_strip(char *str)
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Determine if an & character pointed to by \p str is a markup & entity or
|
* Transform the string in accordance with `markup_mode` and
|
||||||
* part of the text
|
* `settings.ignore_newline`
|
||||||
*
|
|
||||||
* @retval true: \p str is an entity
|
|
||||||
* @retval false: It's no valid entity
|
|
||||||
*/
|
*/
|
||||||
static bool markup_is_entity(const char *str)
|
|
||||||
{
|
|
||||||
assert(str);
|
|
||||||
assert(*str == '&');
|
|
||||||
|
|
||||||
char *end = strchr(str, ';');
|
|
||||||
ASSERT_OR_RET(end, false);
|
|
||||||
|
|
||||||
// Parse (hexa)decimal entities with the format Ӓ or ઼
|
|
||||||
if (str[1] == '#') {
|
|
||||||
const char *cur = str + 2;
|
|
||||||
|
|
||||||
if (*cur == 'x') {
|
|
||||||
cur++;
|
|
||||||
|
|
||||||
// Reject &#x;
|
|
||||||
if (*cur == ';')
|
|
||||||
return false;
|
|
||||||
|
|
||||||
while (isxdigit(*cur) && cur < end)
|
|
||||||
cur++;
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// Reject &#;
|
|
||||||
if (*cur == ';')
|
|
||||||
return false;
|
|
||||||
|
|
||||||
while (isdigit(*cur) && cur < end)
|
|
||||||
cur++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (cur == end);
|
|
||||||
} else {
|
|
||||||
const char *supported_tags[] = {"&", "<", ">", """, "'"};
|
|
||||||
for (int i = 0; i < sizeof(supported_tags)/sizeof(*supported_tags); i++) {
|
|
||||||
if (g_str_has_prefix(str, supported_tags[i]))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Escape all unsupported and invalid &-entities in a string. If the resulting
|
|
||||||
* string does not fit it will be reallocated.
|
|
||||||
*
|
|
||||||
* @param str The string to be transformed
|
|
||||||
*/
|
|
||||||
static char *markup_escape_unsupported(char *str)
|
|
||||||
{
|
|
||||||
ASSERT_OR_RET(str, NULL);
|
|
||||||
|
|
||||||
char *match = str;
|
|
||||||
while ((match = strchr(match, '&'))) {
|
|
||||||
if (!markup_is_entity(match)) {
|
|
||||||
int pos = match - str;
|
|
||||||
str = string_replace_at(str, pos, 1, "&");
|
|
||||||
match = str + pos + strlen("&");
|
|
||||||
} else {
|
|
||||||
match++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* see markup.h */
|
|
||||||
char *markup_transform(char *str, enum markup_mode markup_mode)
|
char *markup_transform(char *str, enum markup_mode markup_mode)
|
||||||
{
|
{
|
||||||
ASSERT_OR_RET(str, NULL);
|
if (str == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
switch (markup_mode) {
|
switch (markup_mode) {
|
||||||
case MARKUP_NULL:
|
case MARKUP_NULL:
|
||||||
@ -324,10 +95,7 @@ char *markup_transform(char *str, enum markup_mode markup_mode)
|
|||||||
str = markup_quote(str);
|
str = markup_quote(str);
|
||||||
break;
|
break;
|
||||||
case MARKUP_FULL:
|
case MARKUP_FULL:
|
||||||
str = markup_escape_unsupported(str);
|
|
||||||
str = markup_br2nl(str);
|
str = markup_br2nl(str);
|
||||||
markup_strip_a(&str, NULL);
|
|
||||||
markup_strip_img(&str, NULL);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,4 +106,4 @@ char *markup_transform(char *str, enum markup_mode markup_mode)
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
|||||||
44
src/markup.h
@ -2,50 +2,10 @@
|
|||||||
#ifndef DUNST_MARKUP_H
|
#ifndef DUNST_MARKUP_H
|
||||||
#define DUNST_MARKUP_H
|
#define DUNST_MARKUP_H
|
||||||
|
|
||||||
enum markup_mode {
|
#include "settings.h"
|
||||||
MARKUP_NULL,
|
|
||||||
MARKUP_NO,
|
|
||||||
MARKUP_STRIP,
|
|
||||||
MARKUP_FULL
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Strip any markup from text; turn it in to plain text.
|
|
||||||
*
|
|
||||||
* For well-formed markup, the following two commands should be
|
|
||||||
* roughly equivalent:
|
|
||||||
*
|
|
||||||
* out = markup_strip(in);
|
|
||||||
* pango_parse_markup(in, -1, 0, NULL, &out, NULL, NULL);
|
|
||||||
*
|
|
||||||
* However, `pango_parse_markup()` balks at invalid markup;
|
|
||||||
* `markup_strip()` shouldn't care if there is invalid markup.
|
|
||||||
*/
|
|
||||||
char *markup_strip(char *str);
|
char *markup_strip(char *str);
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove HTML hyperlinks of a string.
|
|
||||||
*
|
|
||||||
* @param str The string to replace a tags
|
|
||||||
* @param urls (nullable) If any href-attributes found, an `\n` concatenated
|
|
||||||
* string of the URLs in format `[<text between tags>] <href>`
|
|
||||||
*/
|
|
||||||
void markup_strip_a(char **str, char **urls);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove img-tags of a string. If alt attribute given, use this as replacement.
|
|
||||||
*
|
|
||||||
* @param str The string to replace img tags
|
|
||||||
* @param urls (nullable) If any src-attributes found, an `\n` concatenated string of
|
|
||||||
* the URLs in format `[<alt>] <src>`
|
|
||||||
*/
|
|
||||||
void markup_strip_img(char **str, char **urls);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transform the string in accordance with `markup_mode` and
|
|
||||||
* `settings.ignore_newline`
|
|
||||||
*/
|
|
||||||
char *markup_transform(char *str, enum markup_mode markup_mode);
|
char *markup_transform(char *str, enum markup_mode markup_mode);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
|||||||
446
src/menu.c
@ -14,81 +14,76 @@
|
|||||||
|
|
||||||
#include "dbus.h"
|
#include "dbus.h"
|
||||||
#include "dunst.h"
|
#include "dunst.h"
|
||||||
#include "log.h"
|
|
||||||
#include "notification.h"
|
#include "notification.h"
|
||||||
#include "queues.h"
|
#include "queues.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
static bool is_initialized = false;
|
static bool is_initialized = false;
|
||||||
static regex_t url_regex;
|
static regex_t cregex;
|
||||||
|
|
||||||
static gpointer context_menu_thread(gpointer data);
|
static int regex_init(void)
|
||||||
|
|
||||||
struct {
|
|
||||||
GList *locked_notifications;
|
|
||||||
} menu_ctx;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes regexes needed for matching.
|
|
||||||
*
|
|
||||||
* @return true if initialization succeeded
|
|
||||||
*/
|
|
||||||
static bool regex_init(void)
|
|
||||||
{
|
{
|
||||||
if (is_initialized)
|
if (is_initialized)
|
||||||
return true;
|
return 1;
|
||||||
|
|
||||||
char *regex =
|
char *regex =
|
||||||
"\\<(https?://|ftps?://|news://|mailto:|file://|www\\.)"
|
"\\b(https?://|ftps?://|news://|mailto:|file://|www\\.)"
|
||||||
"[-[:alnum:]_\\@;/?:&=%$.+!*\x27,~#]*"
|
"[-[:alnum:]_\\@;/?:&=%$.+!*\x27,~#]*"
|
||||||
"(\\([-[:alnum:]_\\@;/?:&=%$.+!*\x27,~#]*\\)|[-[:alnum:]_\\@;/?:&=%$+*~])+";
|
"(\\([-[:alnum:]_\\@;/?:&=%$.+!*\x27,~#]*\\)|[-[:alnum:]_\\@;/?:&=%$+*~])+";
|
||||||
int code = regcomp(&url_regex, regex, REG_EXTENDED | REG_ICASE);
|
int ret = regcomp(&cregex, regex, REG_EXTENDED | REG_ICASE);
|
||||||
if (code != 0) {
|
if (ret != 0) {
|
||||||
char error_buf[120];
|
fputs("failed to compile regex", stderr);
|
||||||
regerror(code, &url_regex, error_buf, sizeof(error_buf));
|
return 0;
|
||||||
LOG_W("Failed to compile URL-matching regex: %s", error_buf);
|
|
||||||
return false;
|
|
||||||
} else {
|
} else {
|
||||||
is_initialized = true;
|
is_initialized = true;
|
||||||
return true;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void regex_teardown(void)
|
void regex_teardown(void)
|
||||||
{
|
{
|
||||||
if (is_initialized) {
|
if (is_initialized) {
|
||||||
regfree(&url_regex);
|
regfree(&cregex);
|
||||||
is_initialized = false;
|
is_initialized = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see menu.h */
|
/*
|
||||||
|
* Exctract all urls from a given string.
|
||||||
|
*
|
||||||
|
* Return: a string of urls separated by \n
|
||||||
|
*
|
||||||
|
*/
|
||||||
char *extract_urls(const char *to_match)
|
char *extract_urls(const char *to_match)
|
||||||
{
|
{
|
||||||
if (!to_match)
|
char *urls = NULL;
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (!regex_init())
|
if (!regex_init())
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
char *urls = NULL;
|
|
||||||
const char *p = to_match;
|
const char *p = to_match;
|
||||||
regmatch_t m;
|
regmatch_t m;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
int nomatch = regexec(&url_regex, p, 1, &m, 0);
|
int nomatch = regexec(&cregex, p, 1, &m, 0);
|
||||||
|
if (nomatch) {
|
||||||
if (nomatch || m.rm_so == -1)
|
return urls;
|
||||||
|
}
|
||||||
|
int start;
|
||||||
|
int finish;
|
||||||
|
if (m.rm_so == -1) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
int start = m.rm_so + (p - to_match);
|
start = m.rm_so + (p - to_match);
|
||||||
int finish = m.rm_eo + (p - to_match);
|
finish = m.rm_eo + (p - to_match);
|
||||||
|
|
||||||
char *match = g_strndup(to_match + start, finish - start);
|
char *match = g_strndup(to_match + start, finish - start);
|
||||||
|
|
||||||
urls = string_append(urls, match, "\n");
|
urls = string_append(urls, match, "\n");
|
||||||
|
|
||||||
g_free(match);
|
g_free(match);
|
||||||
|
|
||||||
p += m.rm_eo;
|
p += m.rm_eo;
|
||||||
}
|
}
|
||||||
return urls;
|
return urls;
|
||||||
@ -100,66 +95,28 @@ char *extract_urls(const char *to_match)
|
|||||||
*/
|
*/
|
||||||
void open_browser(const char *in)
|
void open_browser(const char *in)
|
||||||
{
|
{
|
||||||
if (!settings.browser_cmd) {
|
// remove prefix and test url
|
||||||
LOG_C("Unable to open browser: No browser command set.");
|
char *url = extract_urls(in);
|
||||||
|
if (!url)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
int browser_pid1 = fork();
|
||||||
|
|
||||||
|
if (browser_pid1) {
|
||||||
|
g_free(url);
|
||||||
|
int status;
|
||||||
|
waitpid(browser_pid1, &status, 0);
|
||||||
|
} else {
|
||||||
|
int browser_pid2 = fork();
|
||||||
|
if (browser_pid2) {
|
||||||
|
exit(0);
|
||||||
|
} else {
|
||||||
|
char *browser_cmd =
|
||||||
|
string_append(settings.browser, url, " ");
|
||||||
|
char **cmd = g_strsplit(browser_cmd, " ", 0);
|
||||||
|
execvp(cmd[0], cmd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
char *url, *end;
|
|
||||||
// If any, remove leading [ linktext ] from URL
|
|
||||||
if (*in == '[' && (end = strstr(in, "] ")))
|
|
||||||
url = g_strdup(end + 2);
|
|
||||||
else
|
|
||||||
url = g_strdup(in);
|
|
||||||
|
|
||||||
int argc = 2+g_strv_length(settings.browser_cmd);
|
|
||||||
char **argv = g_malloc_n(argc, sizeof(char*));
|
|
||||||
|
|
||||||
memcpy(argv, settings.browser_cmd, argc * sizeof(char*));
|
|
||||||
argv[argc-2] = url;
|
|
||||||
argv[argc-1] = NULL;
|
|
||||||
|
|
||||||
GError *err = NULL;
|
|
||||||
g_spawn_async(NULL,
|
|
||||||
argv,
|
|
||||||
NULL,
|
|
||||||
G_SPAWN_DEFAULT
|
|
||||||
| G_SPAWN_SEARCH_PATH
|
|
||||||
| G_SPAWN_STDOUT_TO_DEV_NULL
|
|
||||||
| G_SPAWN_STDERR_TO_DEV_NULL,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
&err);
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
LOG_C("Cannot spawn browser: %s", err->message);
|
|
||||||
g_error_free(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free(argv);
|
|
||||||
g_free(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
char *notification_dmenu_string(struct notification *n)
|
|
||||||
{
|
|
||||||
char *dmenu_str = NULL;
|
|
||||||
|
|
||||||
gpointer p_key;
|
|
||||||
gpointer p_value;
|
|
||||||
GHashTableIter iter;
|
|
||||||
g_hash_table_iter_init(&iter, n->actions);
|
|
||||||
while (g_hash_table_iter_next(&iter, &p_key, &p_value)) {
|
|
||||||
|
|
||||||
char *key = (char*) p_key;
|
|
||||||
char *value = (char*) p_value;
|
|
||||||
|
|
||||||
char *act_str = g_strdup_printf("#%s (%s) [%d,%s]", value, n->summary, n->id, key);
|
|
||||||
dmenu_str = string_append(dmenu_str, act_str, "\n");
|
|
||||||
|
|
||||||
g_free(act_str);
|
|
||||||
}
|
|
||||||
return dmenu_str;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -168,225 +125,138 @@ char *notification_dmenu_string(struct notification *n)
|
|||||||
*/
|
*/
|
||||||
void invoke_action(const char *action)
|
void invoke_action(const char *action)
|
||||||
{
|
{
|
||||||
struct notification *invoked = NULL;
|
notification *invoked = NULL;
|
||||||
uint id;
|
char *action_identifier = NULL;
|
||||||
|
|
||||||
char *data_start, *data_comma, *data_end;
|
char *appname_begin = strchr(action, '[');
|
||||||
|
if (!appname_begin) {
|
||||||
/* format: #<human readable> (<summary>)[<id>,<action>] */
|
printf("invalid action: %s\n", action);
|
||||||
data_start = strrchr(action, '[');
|
|
||||||
if (!data_start) {
|
|
||||||
LOG_W("Invalid action: '%s'", action);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
appname_begin++;
|
||||||
id = strtol(++data_start, &data_comma, 10);
|
int appname_len = strlen(appname_begin) - 1; // remove ]
|
||||||
if (*data_comma != ',') {
|
int action_len = strlen(action) - appname_len - 3; // remove space, [, ]
|
||||||
LOG_W("Invalid action: '%s'", action);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
data_end = strchr(data_comma+1, ']');
|
|
||||||
if (!data_end) {
|
|
||||||
LOG_W("Invalid action: '%s'", action);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *action_key = g_strndup(data_comma+1, data_end-data_comma-1);
|
|
||||||
|
|
||||||
for (const GList *iter = queues_get_displayed();
|
|
||||||
iter;
|
|
||||||
iter = iter->next) {
|
|
||||||
struct notification *n = iter->data;
|
|
||||||
if (n->id != id)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (g_hash_table_contains(n->actions, action_key)) {
|
|
||||||
invoked = n;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (invoked && action_key) {
|
|
||||||
signal_action_invoked(invoked, action_key);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free(action_key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dispatch whatever has been returned by dmenu.
|
|
||||||
* If the given result of dmenu is empty or NULL, nothing will be done.
|
|
||||||
*
|
|
||||||
* @param input The result from dmenu.
|
|
||||||
*/
|
|
||||||
void dispatch_menu_result(const char *input)
|
|
||||||
{
|
|
||||||
ASSERT_OR_RET(input,);
|
|
||||||
|
|
||||||
char *in = g_strdup(input);
|
|
||||||
g_strstrip(in);
|
|
||||||
|
|
||||||
if (in[0] == '#')
|
|
||||||
invoke_action(in + 1);
|
|
||||||
else if (in[0] != '\0')
|
|
||||||
open_browser(in);
|
|
||||||
|
|
||||||
g_free(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Call dmenu with the specified input. Blocks until dmenu is finished.
|
|
||||||
*
|
|
||||||
* @param dmenu_input The input string to feed into dmenu
|
|
||||||
* @returns the selected string from dmenu
|
|
||||||
*/
|
|
||||||
char *invoke_dmenu(const char *dmenu_input)
|
|
||||||
{
|
|
||||||
if (!settings.dmenu_cmd) {
|
|
||||||
LOG_C("Unable to open dmenu: No dmenu command set.");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT_OR_RET(STR_FULL(dmenu_input), NULL);
|
|
||||||
|
|
||||||
gint dunst_to_dmenu;
|
|
||||||
gint dmenu_to_dunst;
|
|
||||||
GError *err = NULL;
|
|
||||||
char buf[1024];
|
|
||||||
char *ret = NULL;
|
|
||||||
|
|
||||||
g_spawn_async_with_pipes(NULL,
|
|
||||||
settings.dmenu_cmd,
|
|
||||||
NULL,
|
|
||||||
G_SPAWN_DEFAULT
|
|
||||||
| G_SPAWN_SEARCH_PATH,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
&dunst_to_dmenu,
|
|
||||||
&dmenu_to_dunst,
|
|
||||||
NULL,
|
|
||||||
&err);
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
LOG_C("Cannot spawn dmenu: %s", err->message);
|
|
||||||
g_error_free(err);
|
|
||||||
} else {
|
|
||||||
size_t wlen = strlen(dmenu_input);
|
|
||||||
if (write(dunst_to_dmenu, dmenu_input, wlen) != wlen) {
|
|
||||||
LOG_W("Cannot feed dmenu with input: %s", strerror(errno));
|
|
||||||
}
|
|
||||||
close(dunst_to_dmenu);
|
|
||||||
|
|
||||||
ssize_t rlen = read(dmenu_to_dunst, buf, sizeof(buf));
|
|
||||||
close(dmenu_to_dunst);
|
|
||||||
|
|
||||||
if (rlen > 0)
|
|
||||||
ret = g_strndup(buf, rlen);
|
|
||||||
else
|
|
||||||
LOG_W("Didn't receive input from dmenu.");
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
for (const GList *iter = queues_get_displayed(); iter;
|
||||||
iter = iter->next) {
|
iter = iter->next) {
|
||||||
struct notification *n = iter->data;
|
notification *n = iter->data;
|
||||||
|
if (g_str_has_prefix(appname_begin, n->appname) && strlen(n->appname) == appname_len) {
|
||||||
|
if (!n->actions)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (n->urls || g_hash_table_size(n->actions)) {
|
for (int i = 0; i < n->actions->count; i += 2) {
|
||||||
notification_lock(n);
|
char *a_identifier = n->actions->actions[i];
|
||||||
locked_notifications = g_list_prepend(locked_notifications, n);
|
char *name = n->actions->actions[i + 1];
|
||||||
|
if (g_str_has_prefix(action, name) && strlen(name) == action_len) {
|
||||||
|
invoked = n;
|
||||||
|
action_identifier = a_identifier;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return locked_notifications;
|
if (invoked && action_identifier) {
|
||||||
|
action_invoked(invoked, action_identifier);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see menu.h */
|
/*
|
||||||
|
* Dispatch whatever has been returned
|
||||||
|
* by the menu.
|
||||||
|
*/
|
||||||
|
void dispatch_menu_result(const char *input)
|
||||||
|
{
|
||||||
|
char *in = g_strdup(input);
|
||||||
|
g_strstrip(in);
|
||||||
|
if (in[0] == '#') {
|
||||||
|
invoke_action(in + 1);
|
||||||
|
} else {
|
||||||
|
open_browser(in);
|
||||||
|
}
|
||||||
|
g_free(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open the context menu that let's the user
|
||||||
|
* select urls/actions/etc
|
||||||
|
*/
|
||||||
void context_menu(void)
|
void context_menu(void)
|
||||||
{
|
{
|
||||||
GList *notifications = get_actionable_notifications();
|
if (settings.dmenu_cmd == NULL) {
|
||||||
context_menu_for(notifications);
|
fprintf(stderr, "dmenu command not set properly. Cowardly refusing to open the context menu.\n");
|
||||||
}
|
|
||||||
|
|
||||||
/* see menu.h */
|
|
||||||
void context_menu_for(GList *notifications)
|
|
||||||
{
|
|
||||||
if (menu_ctx.locked_notifications) {
|
|
||||||
LOG_W("Context menu already running, refusing to rerun");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
menu_ctx.locked_notifications = notifications;
|
|
||||||
|
|
||||||
GError *err = NULL;
|
|
||||||
g_thread_unref(g_thread_try_new("dmenu",
|
|
||||||
context_menu_thread,
|
|
||||||
NULL,
|
|
||||||
&err));
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
LOG_C("Cannot start thread to call dmenu: %s", err->message);
|
|
||||||
g_error_free(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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_input = NULL;
|
||||||
char *dmenu_output;
|
|
||||||
|
|
||||||
for (GList *iter = menu_ctx.locked_notifications; iter; iter = iter->next) {
|
for (const GList *iter = queues_get_displayed(); iter;
|
||||||
struct notification *n = iter->data;
|
iter = iter->next) {
|
||||||
|
notification *n = iter->data;
|
||||||
char *dmenu_str = notification_dmenu_string(n);
|
|
||||||
dmenu_input = string_append(dmenu_input, dmenu_str, "\n");
|
|
||||||
g_free(dmenu_str);
|
|
||||||
|
|
||||||
if (n->urls)
|
if (n->urls)
|
||||||
dmenu_input = string_append(dmenu_input, n->urls, "\n");
|
dmenu_input = string_append(dmenu_input, n->urls, "\n");
|
||||||
|
|
||||||
|
if (n->actions)
|
||||||
|
dmenu_input =
|
||||||
|
string_append(dmenu_input, n->actions->dmenu_str,
|
||||||
|
"\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
dmenu_output = invoke_dmenu(dmenu_input);
|
if (!dmenu_input)
|
||||||
g_timeout_add(50, context_menu_result_dispatch, dmenu_output);
|
return;
|
||||||
|
|
||||||
|
char buf[1024] = {0};
|
||||||
|
int child_io[2];
|
||||||
|
int parent_io[2];
|
||||||
|
if (pipe(child_io) != 0) {
|
||||||
|
PERR("pipe()", errno);
|
||||||
|
g_free(dmenu_input);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (pipe(parent_io) != 0) {
|
||||||
|
PERR("pipe()", errno);
|
||||||
|
g_free(dmenu_input);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int pid = fork();
|
||||||
|
|
||||||
|
if (pid == 0) {
|
||||||
|
close(child_io[1]);
|
||||||
|
close(parent_io[0]);
|
||||||
|
close(0);
|
||||||
|
if (dup(child_io[0]) == -1) {
|
||||||
|
PERR("dup()", errno);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
close(1);
|
||||||
|
if (dup(parent_io[1]) == -1) {
|
||||||
|
PERR("dup()", errno);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
execvp(settings.dmenu_cmd[0], settings.dmenu_cmd);
|
||||||
|
} else {
|
||||||
|
close(child_io[0]);
|
||||||
|
close(parent_io[1]);
|
||||||
|
size_t wlen = strlen(dmenu_input);
|
||||||
|
if (write(child_io[1], dmenu_input, wlen) != wlen) {
|
||||||
|
PERR("write()", errno);
|
||||||
|
}
|
||||||
|
close(child_io[1]);
|
||||||
|
|
||||||
|
size_t len = read(parent_io[0], buf, 1023);
|
||||||
|
|
||||||
|
waitpid(pid, NULL, 0);
|
||||||
|
|
||||||
|
if (len == 0) {
|
||||||
|
g_free(dmenu_input);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(parent_io[0]);
|
||||||
|
|
||||||
|
dispatch_menu_result(buf);
|
||||||
|
|
||||||
g_free(dmenu_input);
|
g_free(dmenu_input);
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
|||||||
23
src/menu.h
@ -2,31 +2,10 @@
|
|||||||
#ifndef DUNST_MENU_H
|
#ifndef DUNST_MENU_H
|
||||||
#define DUNST_MENU_H
|
#define DUNST_MENU_H
|
||||||
|
|
||||||
#include <glib.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract all urls from the given string.
|
|
||||||
*
|
|
||||||
* @param to_match (nullable) String to extract URLs
|
|
||||||
* @return a string of urls separated by '\n'
|
|
||||||
* @retval NULL: No URLs found
|
|
||||||
*/
|
|
||||||
char *extract_urls(const char *to_match);
|
char *extract_urls(const char *to_match);
|
||||||
|
|
||||||
void open_browser(const char *in);
|
void open_browser(const char *in);
|
||||||
void invoke_action(const char *action);
|
void invoke_action(const char *action);
|
||||||
void regex_teardown(void);
|
void regex_teardown(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
#endif
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
|||||||
@ -15,156 +15,92 @@
|
|||||||
|
|
||||||
#include "dbus.h"
|
#include "dbus.h"
|
||||||
#include "dunst.h"
|
#include "dunst.h"
|
||||||
#include "icon.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "markup.h"
|
#include "markup.h"
|
||||||
#include "menu.h"
|
#include "menu.h"
|
||||||
#include "queues.h"
|
#include "queues.h"
|
||||||
#include "rules.h"
|
#include "rules.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "draw.h"
|
#include "x11/x.h"
|
||||||
|
|
||||||
static void notification_extract_urls(struct notification *n);
|
/*
|
||||||
static void notification_format_message(struct notification *n);
|
* print a human readable representation
|
||||||
|
* of the given notification to stdout.
|
||||||
/* see notification.h */
|
*/
|
||||||
const char *enum_to_string_fullscreen(enum behavior_fullscreen in)
|
void notification_print(notification *n)
|
||||||
{
|
{
|
||||||
switch (in) {
|
|
||||||
case FS_SHOW: return "show";
|
|
||||||
case FS_DELAY: return "delay";
|
|
||||||
case FS_PUSHBACK: return "pushback";
|
|
||||||
case FS_NULL: return "(null)";
|
|
||||||
default:
|
|
||||||
LOG_E("Invalid %s enum value in %s:%d", "fullscreen", __FILE__, __LINE__);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct _notification_private {
|
|
||||||
gint refcount;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* see notification.h */
|
|
||||||
void notification_print(const struct notification *n)
|
|
||||||
{
|
|
||||||
//TODO: use logging info for this
|
|
||||||
printf("{\n");
|
printf("{\n");
|
||||||
printf("\tappname: '%s'\n", n->appname);
|
printf("\tappname: '%s'\n", n->appname);
|
||||||
printf("\tsummary: '%s'\n", n->summary);
|
printf("\tsummary: '%s'\n", n->summary);
|
||||||
printf("\tbody: '%s'\n", n->body);
|
printf("\tbody: '%s'\n", n->body);
|
||||||
printf("\ticon: '%s'\n", n->iconname);
|
printf("\ticon: '%s'\n", n->icon);
|
||||||
printf("\traw_icon set: %s\n", (n->icon_id && !STR_EQ(n->iconname, n->icon_id)) ? "true" : "false");
|
printf("\traw_icon set: %s\n", (n->raw_icon ? "true" : "false"));
|
||||||
printf("\ticon_id: '%s'\n", n->icon_id);
|
|
||||||
printf("\tdesktop_entry: '%s'\n", n->desktop_entry ? n->desktop_entry : "");
|
|
||||||
printf("\tcategory: %s\n", n->category);
|
printf("\tcategory: %s\n", n->category);
|
||||||
printf("\ttimeout: %ld\n", n->timeout/1000);
|
printf("\ttimeout: %ld\n", n->timeout/1000);
|
||||||
printf("\turgency: %s\n", notification_urgency_to_string(n->urgency));
|
printf("\turgency: %s\n", notification_urgency_to_string(n->urgency));
|
||||||
printf("\ttransient: %d\n", n->transient);
|
printf("\ttransient: %d\n", n->transient);
|
||||||
printf("\tformatted: '%s'\n", n->msg);
|
printf("\tformatted: '%s'\n", n->msg);
|
||||||
printf("\tfg: %s\n", n->colors.fg);
|
printf("\tfg: %s\n", n->color_strings[ColFG]);
|
||||||
printf("\tbg: %s\n", n->colors.bg);
|
printf("\tbg: %s\n", n->color_strings[ColBG]);
|
||||||
printf("\thighlight: %s\n", n->colors.highlight);
|
printf("\tframe: %s\n", n->color_strings[ColFrame]);
|
||||||
printf("\tframe: %s\n", n->colors.frame);
|
|
||||||
printf("\tfullscreen: %s\n", enum_to_string_fullscreen(n->fullscreen));
|
|
||||||
printf("\tprogress: %d\n", n->progress);
|
|
||||||
printf("\tstack_tag: %s\n", (n->stack_tag ? n->stack_tag : ""));
|
|
||||||
printf("\tid: %d\n", n->id);
|
printf("\tid: %d\n", n->id);
|
||||||
if (n->urls) {
|
if (n->urls) {
|
||||||
char *urls = string_replace_all("\n", "\t\t\n", g_strdup(n->urls));
|
|
||||||
printf("\turls:\n");
|
printf("\turls:\n");
|
||||||
printf("\t{\n");
|
printf("\t{\n");
|
||||||
printf("\t\t%s\n", urls);
|
printf("\t\t%s\n", n->urls);
|
||||||
printf("\t}\n");
|
|
||||||
g_free(urls);
|
|
||||||
}
|
|
||||||
if (g_hash_table_size(n->actions) == 0) {
|
|
||||||
printf("\tactions: {}\n");
|
|
||||||
} else {
|
|
||||||
gpointer p_key, p_value;
|
|
||||||
GHashTableIter iter;
|
|
||||||
g_hash_table_iter_init(&iter, n->actions);
|
|
||||||
printf("\tactions: {\n");
|
|
||||||
while (g_hash_table_iter_next(&iter, &p_key, &p_value))
|
|
||||||
printf("\t\t\"%s\": \"%s\"\n", (char*)p_key, (char*)p_value);
|
|
||||||
printf("\t}\n");
|
printf("\t}\n");
|
||||||
}
|
}
|
||||||
printf("\tscript_count: %d\n", n->script_count);
|
|
||||||
if (n->script_count > 0) {
|
if (n->actions) {
|
||||||
printf("\tscripts: ");
|
printf("\tactions:\n");
|
||||||
for (int i = 0; i < n->script_count; i++) {
|
printf("\t{\n");
|
||||||
printf("'%s' ",n->scripts[i]);
|
for (int i = 0; i < n->actions->count; i += 2) {
|
||||||
|
printf("\t\t[%s,%s]\n", n->actions->actions[i],
|
||||||
|
n->actions->actions[i + 1]);
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\t}\n");
|
||||||
|
printf("\tactions_dmenu: %s\n", n->actions->dmenu_str);
|
||||||
}
|
}
|
||||||
|
printf("\tscript: %s\n", n->script);
|
||||||
printf("}\n");
|
printf("}\n");
|
||||||
fflush(stdout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see notification.h */
|
/*
|
||||||
void notification_run_script(struct notification *n)
|
* Run the script associated with the
|
||||||
|
* given notification.
|
||||||
|
*/
|
||||||
|
void notification_run_script(notification *n)
|
||||||
{
|
{
|
||||||
if (n->script_run && !settings.always_run_script)
|
if (!n->script || strlen(n->script) < 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
n->script_run = true;
|
char *appname = n->appname ? n->appname : "";
|
||||||
|
char *summary = n->summary ? n->summary : "";
|
||||||
const char *appname = n->appname ? n->appname : "";
|
char *body = n->body ? n->body : "";
|
||||||
const char *summary = n->summary ? n->summary : "";
|
char *icon = n->icon ? n->icon : "";
|
||||||
const char *body = n->body ? n->body : "";
|
|
||||||
const char *icon = n->iconname ? n->iconname : "";
|
|
||||||
|
|
||||||
const char *urgency = notification_urgency_to_string(n->urgency);
|
const char *urgency = notification_urgency_to_string(n->urgency);
|
||||||
|
|
||||||
for(int i = 0; i < n->script_count; i++) {
|
int pid1 = fork();
|
||||||
|
|
||||||
const char *script = n->scripts[i];
|
if (pid1) {
|
||||||
|
int status;
|
||||||
if (STR_EMPTY(script))
|
waitpid(pid1, &status, 0);
|
||||||
continue;
|
} else {
|
||||||
|
int pid2 = fork();
|
||||||
int pid1 = fork();
|
if (pid2) {
|
||||||
|
exit(0);
|
||||||
if (pid1) {
|
|
||||||
int status;
|
|
||||||
waitpid(pid1, &status, 0);
|
|
||||||
} else {
|
} else {
|
||||||
// second fork to prevent zombie processes
|
int ret = execlp(n->script,
|
||||||
int pid2 = fork();
|
n->script,
|
||||||
if (pid2) {
|
appname,
|
||||||
exit(0);
|
summary,
|
||||||
} else {
|
body,
|
||||||
// Set environment variables
|
icon,
|
||||||
gchar *n_id_str = g_strdup_printf("%i", n->id);
|
urgency,
|
||||||
gchar *n_progress_str = g_strdup_printf("%i", n->progress);
|
(char *)NULL);
|
||||||
gchar *n_timeout_str = g_strdup_printf("%li", n->timeout/1000);
|
if (ret != 0) {
|
||||||
gchar *n_timestamp_str = g_strdup_printf("%li", n->timestamp / 1000);
|
PERR("Unable to run script", errno);
|
||||||
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);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -174,7 +110,7 @@ void notification_run_script(struct notification *n)
|
|||||||
/*
|
/*
|
||||||
* Helper function to convert an urgency to a string
|
* Helper function to convert an urgency to a string
|
||||||
*/
|
*/
|
||||||
const char *notification_urgency_to_string(const enum urgency urgency)
|
const char *notification_urgency_to_string(enum urgency urgency)
|
||||||
{
|
{
|
||||||
switch (urgency) {
|
switch (urgency) {
|
||||||
case URG_NONE:
|
case URG_NONE:
|
||||||
@ -190,145 +126,105 @@ const char *notification_urgency_to_string(const enum urgency urgency)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see notification.h */
|
/*
|
||||||
int notification_cmp(const struct notification *a, const struct notification *b)
|
* Helper function to compare to given
|
||||||
|
* notifications.
|
||||||
|
*/
|
||||||
|
int notification_cmp(const void *va, const void *vb)
|
||||||
{
|
{
|
||||||
if (settings.sort && a->urgency != b->urgency) {
|
notification *a = (notification *) va;
|
||||||
|
notification *b = (notification *) vb;
|
||||||
|
|
||||||
|
if (!settings.sort)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (a->urgency != b->urgency) {
|
||||||
return b->urgency - a->urgency;
|
return b->urgency - a->urgency;
|
||||||
} else {
|
} else {
|
||||||
return a->id - b->id;
|
return a->id - b->id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see notification.h */
|
/*
|
||||||
|
* Wrapper for notification_cmp to match glib's
|
||||||
|
* compare functions signature.
|
||||||
|
*/
|
||||||
int notification_cmp_data(const void *va, const void *vb, void *data)
|
int notification_cmp_data(const void *va, const void *vb, void *data)
|
||||||
{
|
{
|
||||||
struct notification *a = (struct notification *) va;
|
return notification_cmp(va, vb);
|
||||||
struct notification *b = (struct notification *) vb;
|
|
||||||
|
|
||||||
return notification_cmp(a, b);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool notification_is_duplicate(const struct notification *a, const struct notification *b)
|
int notification_is_duplicate(const notification *a, const notification *b)
|
||||||
{
|
{
|
||||||
return STR_EQ(a->appname, b->appname)
|
//Comparing raw icons is not supported, assume they are not identical
|
||||||
&& STR_EQ(a->summary, b->summary)
|
if (settings.icon_position != icons_off
|
||||||
&& STR_EQ(a->body, b->body)
|
&& (a->raw_icon != NULL || b->raw_icon != NULL))
|
||||||
&& (settings.icon_position != ICON_OFF ? STR_EQ(a->icon_id, b->icon_id) : 1)
|
return false;
|
||||||
|
|
||||||
|
return strcmp(a->appname, b->appname) == 0
|
||||||
|
&& strcmp(a->summary, b->summary) == 0
|
||||||
|
&& strcmp(a->body, b->body) == 0
|
||||||
|
&& (settings.icon_position != icons_off ? strcmp(a->icon, b->icon) == 0 : 1)
|
||||||
&& a->urgency == b->urgency;
|
&& a->urgency == b->urgency;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool notification_is_locked(struct notification *n) {
|
/*
|
||||||
assert(n);
|
* Free the actions element
|
||||||
|
* @a: (nullable): Pointer to #Actions
|
||||||
return g_atomic_int_get(&n->locked) != 0;
|
*/
|
||||||
}
|
void actions_free(Actions *a)
|
||||||
|
|
||||||
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);
|
if (!a)
|
||||||
}
|
|
||||||
|
|
||||||
/* see notification.h */
|
|
||||||
gint notification_refcount_get(struct notification *n)
|
|
||||||
{
|
|
||||||
assert(n->priv->refcount > 0);
|
|
||||||
return g_atomic_int_get(&n->priv->refcount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* see notification.h */
|
|
||||||
void notification_ref(struct notification *n)
|
|
||||||
{
|
|
||||||
assert(n->priv->refcount > 0);
|
|
||||||
g_atomic_int_inc(&n->priv->refcount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* see notification.h */
|
|
||||||
void notification_unref(struct notification *n)
|
|
||||||
{
|
|
||||||
ASSERT_OR_RET(n,);
|
|
||||||
|
|
||||||
assert(n->priv->refcount > 0);
|
|
||||||
if (!g_atomic_int_dec_and_test(&n->priv->refcount))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
g_strfreev(a->actions);
|
||||||
|
g_free(a->dmenu_str);
|
||||||
|
g_free(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free a #RawImage
|
||||||
|
* @i: (nullable): pointer to #RawImage
|
||||||
|
*/
|
||||||
|
void rawimage_free(RawImage *i)
|
||||||
|
{
|
||||||
|
if (!i)
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_free(i->data);
|
||||||
|
g_free(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free the memory used by the given notification.
|
||||||
|
*/
|
||||||
|
void notification_free(notification *n)
|
||||||
|
{
|
||||||
|
assert(n != NULL);
|
||||||
g_free(n->appname);
|
g_free(n->appname);
|
||||||
g_free(n->summary);
|
g_free(n->summary);
|
||||||
g_free(n->body);
|
g_free(n->body);
|
||||||
g_free(n->iconname);
|
g_free(n->icon);
|
||||||
g_free(n->msg);
|
g_free(n->msg);
|
||||||
g_free(n->dbus_client);
|
g_free(n->dbus_client);
|
||||||
g_free(n->category);
|
g_free(n->category);
|
||||||
g_free(n->text_to_render);
|
g_free(n->text_to_render);
|
||||||
g_free(n->urls);
|
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);
|
actions_free(n->actions);
|
||||||
g_free(n->default_action_name);
|
rawimage_free(n->raw_icon);
|
||||||
|
|
||||||
if (n->icon)
|
|
||||||
g_object_unref(n->icon);
|
|
||||||
g_free(n->icon_id);
|
|
||||||
|
|
||||||
notification_private_free(n->priv);
|
|
||||||
|
|
||||||
if (n->script_count > 0){
|
|
||||||
g_free(n->scripts);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free(n);
|
g_free(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
void notification_icon_replace_path(struct notification *n, const char *new_icon)
|
/*
|
||||||
{
|
* Replace the two chars where **needle points
|
||||||
ASSERT_OR_RET(n,);
|
* with a quoted "replacement", according to the markup settings.
|
||||||
ASSERT_OR_RET(new_icon,);
|
*
|
||||||
ASSERT_OR_RET(n->iconname != new_icon,);
|
* The needle is a double pointer and gets updated upon return
|
||||||
|
* to point to the first char, which occurs after replacement.
|
||||||
g_free(n->iconname);
|
*
|
||||||
n->iconname = g_strdup(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, draw_get_scale());
|
|
||||||
}
|
|
||||||
|
|
||||||
void notification_icon_replace_data(struct notification *n, GVariant *new_icon)
|
|
||||||
{
|
|
||||||
ASSERT_OR_RET(n,);
|
|
||||||
ASSERT_OR_RET(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, draw_get_scale());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* see notification.h */
|
|
||||||
void notification_replace_single_field(char **haystack,
|
void notification_replace_single_field(char **haystack,
|
||||||
char **needle,
|
char **needle,
|
||||||
const char *replacement,
|
const char *replacement,
|
||||||
@ -352,127 +248,101 @@ void notification_replace_single_field(char **haystack,
|
|||||||
g_free(input);
|
g_free(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
static NotificationPrivate *notification_private_create(void)
|
char *notification_extract_markup_urls(char **str_ptr)
|
||||||
{
|
{
|
||||||
NotificationPrivate *priv = g_malloc0(sizeof(NotificationPrivate));
|
char *start, *end, *replace_buf, *str, *urls = NULL, *url, *index_buf;
|
||||||
g_atomic_int_set(&priv->refcount, 1);
|
int linkno = 1;
|
||||||
|
|
||||||
return priv;
|
str = *str_ptr;
|
||||||
|
while ((start = strstr(str, "<a href")) != NULL) {
|
||||||
|
end = strstr(start, ">");
|
||||||
|
if (end != NULL) {
|
||||||
|
replace_buf = g_strndup(start, end - start + 1);
|
||||||
|
url = extract_urls(replace_buf);
|
||||||
|
if (url != NULL) {
|
||||||
|
str = string_replace(replace_buf, "[", str);
|
||||||
|
|
||||||
|
index_buf = g_strdup_printf("[#%d]", linkno++);
|
||||||
|
if (urls == NULL) {
|
||||||
|
urls = g_strconcat(index_buf, " ", url, NULL);
|
||||||
|
} else {
|
||||||
|
char *tmp = urls;
|
||||||
|
urls = g_strconcat(tmp, "\n", index_buf, " ", url, NULL);
|
||||||
|
g_free(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
index_buf[0] = ' ';
|
||||||
|
str = string_replace("</a>", index_buf, str);
|
||||||
|
g_free(index_buf);
|
||||||
|
g_free(url);
|
||||||
|
} else {
|
||||||
|
str = string_replace(replace_buf, "", str);
|
||||||
|
str = string_replace("</a>", "", str);
|
||||||
|
}
|
||||||
|
g_free(replace_buf);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*str_ptr = str;
|
||||||
|
return urls;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see notification.h */
|
/*
|
||||||
struct notification *notification_create(void)
|
* Create notification struct and initialise everything to NULL,
|
||||||
|
* this function is guaranteed to return a valid pointer.
|
||||||
|
*/
|
||||||
|
notification *notification_create(void)
|
||||||
{
|
{
|
||||||
struct notification *n = g_malloc0(sizeof(struct notification));
|
return g_malloc0(sizeof(notification));
|
||||||
|
}
|
||||||
|
|
||||||
n->priv = notification_private_create();
|
void notification_init_defaults(notification *n)
|
||||||
|
{
|
||||||
|
assert(n != NULL);
|
||||||
|
if(n->appname == NULL) n->appname = g_strdup("unknown");
|
||||||
|
if(n->summary == NULL) n->summary = g_strdup("");
|
||||||
|
if(n->body == NULL) n->body = g_strdup("");
|
||||||
|
if(n->category == NULL) n->category = g_strdup("");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize the given notification
|
||||||
|
*
|
||||||
|
* n should be a pointer to a notification allocated with
|
||||||
|
* notification_create, it is undefined behaviour to pass a notification
|
||||||
|
* allocated some other way.
|
||||||
|
*/
|
||||||
|
void notification_init(notification *n)
|
||||||
|
{
|
||||||
|
assert(n != NULL);
|
||||||
|
|
||||||
|
//Prevent undefined behaviour by initialising required fields
|
||||||
|
notification_init_defaults(n);
|
||||||
|
|
||||||
|
n->script = NULL;
|
||||||
|
n->text_to_render = NULL;
|
||||||
|
|
||||||
/* Unparameterized default values */
|
|
||||||
n->first_render = true;
|
|
||||||
n->markup = settings.markup;
|
|
||||||
n->format = settings.format;
|
n->format = settings.format;
|
||||||
|
|
||||||
n->timestamp = time_monotonic_now();
|
|
||||||
|
|
||||||
n->urgency = URG_NORM;
|
|
||||||
n->timeout = -1;
|
|
||||||
|
|
||||||
n->transient = false;
|
|
||||||
n->progress = -1;
|
|
||||||
|
|
||||||
n->script_run = false;
|
|
||||||
n->dbus_valid = false;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* see notification.h */
|
|
||||||
void notification_init(struct notification *n)
|
|
||||||
{
|
|
||||||
/* default to empty string to avoid further NULL faults */
|
|
||||||
n->appname = n->appname ? n->appname : g_strdup("unknown");
|
|
||||||
n->summary = n->summary ? n->summary : g_strdup("");
|
|
||||||
n->body = n->body ? n->body : g_strdup("");
|
|
||||||
n->category = n->category ? n->category : g_strdup("");
|
|
||||||
|
|
||||||
/* sanitize urgency */
|
|
||||||
if (n->urgency < URG_MIN)
|
|
||||||
n->urgency = URG_LOW;
|
|
||||||
if (n->urgency > URG_MAX)
|
|
||||||
n->urgency = URG_CRIT;
|
|
||||||
|
|
||||||
/* Timeout processing */
|
|
||||||
if (n->timeout < 0)
|
|
||||||
n->timeout = settings.timeouts[n->urgency];
|
|
||||||
|
|
||||||
/* Icon handling */
|
|
||||||
if (STR_EMPTY(n->iconname))
|
|
||||||
g_clear_pointer(&n->iconname, g_free);
|
|
||||||
if (!n->icon && n->iconname) {
|
|
||||||
char *icon = g_strdup(n->iconname);
|
|
||||||
notification_icon_replace_path(n, icon);
|
|
||||||
g_free(icon);
|
|
||||||
}
|
|
||||||
if (!n->icon && !n->iconname)
|
|
||||||
notification_icon_replace_path(n, settings.icons[n->urgency]);
|
|
||||||
|
|
||||||
/* Color hints */
|
|
||||||
struct notification_colors defcolors;
|
|
||||||
switch (n->urgency) {
|
|
||||||
case URG_LOW:
|
|
||||||
defcolors = settings.colors_low;
|
|
||||||
break;
|
|
||||||
case URG_NORM:
|
|
||||||
defcolors = settings.colors_norm;
|
|
||||||
break;
|
|
||||||
case URG_CRIT:
|
|
||||||
defcolors = settings.colors_crit;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
g_error("Unhandled urgency type: %d", n->urgency);
|
|
||||||
}
|
|
||||||
if (!n->colors.fg)
|
|
||||||
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);
|
|
||||||
|
|
||||||
/* Sanitize misc hints */
|
|
||||||
if (n->progress < 0)
|
|
||||||
n->progress = -1;
|
|
||||||
|
|
||||||
/* Process rules */
|
|
||||||
rule_apply_all(n);
|
rule_apply_all(n);
|
||||||
|
|
||||||
if (g_str_has_prefix(n->summary, "DUNST_COMMAND_")) {
|
if (n->icon != NULL && strlen(n->icon) <= 0) {
|
||||||
char *msg = "DUNST_COMMAND_* has been removed, please switch to dunstctl. See #830 for more details. https://github.com/dunst-project/dunst/pull/830";
|
g_free(n->icon);
|
||||||
LOG_W("%s", msg);
|
n->icon = NULL;
|
||||||
n->body = string_append(n->body, msg, "\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* UPDATE derived fields */
|
if (n->raw_icon == NULL && n->icon == NULL) {
|
||||||
notification_extract_urls(n);
|
n->icon = g_strdup(settings.icons[n->urgency]);
|
||||||
notification_format_message(n);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static void notification_format_message(struct notification *n)
|
n->urls = notification_extract_markup_urls(&(n->body));
|
||||||
{
|
|
||||||
g_clear_pointer(&n->msg, g_free);
|
|
||||||
|
|
||||||
n->msg = string_replace_all("\\n", "\n", g_strdup(n->format));
|
n->msg = string_replace_all("\\n", "\n", g_strdup(n->format));
|
||||||
|
|
||||||
/* replace all formatter */
|
/* replace all formatter */
|
||||||
for(char *substr = strchr(n->msg, '%');
|
for(char *substr = strchr(n->msg, '%');
|
||||||
substr && *substr;
|
substr;
|
||||||
substr = strchr(substr, '%')) {
|
substr = strchr(substr, '%')) {
|
||||||
|
|
||||||
char pg[16];
|
char pg[16];
|
||||||
@ -491,7 +361,7 @@ static void notification_format_message(struct notification *n)
|
|||||||
&n->msg,
|
&n->msg,
|
||||||
&substr,
|
&substr,
|
||||||
n->summary,
|
n->summary,
|
||||||
MARKUP_NO);
|
n->markup);
|
||||||
break;
|
break;
|
||||||
case 'b':
|
case 'b':
|
||||||
notification_replace_single_field(
|
notification_replace_single_field(
|
||||||
@ -501,7 +371,7 @@ static void notification_format_message(struct notification *n)
|
|||||||
n->markup);
|
n->markup);
|
||||||
break;
|
break;
|
||||||
case 'I':
|
case 'I':
|
||||||
icon_tmp = g_strdup(n->iconname);
|
icon_tmp = g_strdup(n->icon);
|
||||||
notification_replace_single_field(
|
notification_replace_single_field(
|
||||||
&n->msg,
|
&n->msg,
|
||||||
&substr,
|
&substr,
|
||||||
@ -513,7 +383,7 @@ static void notification_format_message(struct notification *n)
|
|||||||
notification_replace_single_field(
|
notification_replace_single_field(
|
||||||
&n->msg,
|
&n->msg,
|
||||||
&substr,
|
&substr,
|
||||||
n->iconname ? n->iconname : "",
|
n->icon ? n->icon : "",
|
||||||
MARKUP_NO);
|
MARKUP_NO);
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
@ -544,12 +414,12 @@ static void notification_format_message(struct notification *n)
|
|||||||
MARKUP_NO);
|
MARKUP_NO);
|
||||||
break;
|
break;
|
||||||
case '\0':
|
case '\0':
|
||||||
LOG_W("format_string has trailing %% character. "
|
fprintf(stderr, "WARNING: format_string has trailing %% character."
|
||||||
"To escape it use %%%%.");
|
"To escape it use %%%%.");
|
||||||
substr++;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_W("format_string %%%c is unknown.", substr[1]);
|
fprintf(stderr, "WARNING: format_string %%%c"
|
||||||
|
" is unknown\n", substr[1]);
|
||||||
// shift substr pointer forward,
|
// shift substr pointer forward,
|
||||||
// as we can't interpret the format string
|
// as we can't interpret the format string
|
||||||
substr++;
|
substr++;
|
||||||
@ -560,41 +430,73 @@ static void notification_format_message(struct notification *n)
|
|||||||
n->msg = g_strchomp(n->msg);
|
n->msg = g_strchomp(n->msg);
|
||||||
|
|
||||||
/* truncate overlong messages */
|
/* truncate overlong messages */
|
||||||
if (strnlen(n->msg, DUNST_NOTIF_MAX_CHARS + 1) > DUNST_NOTIF_MAX_CHARS) {
|
if (strlen(n->msg) > DUNST_NOTIF_MAX_CHARS) {
|
||||||
char * buffer = g_strndup(n->msg, DUNST_NOTIF_MAX_CHARS);
|
char *buffer = g_malloc(DUNST_NOTIF_MAX_CHARS);
|
||||||
|
strncpy(buffer, n->msg, DUNST_NOTIF_MAX_CHARS);
|
||||||
|
buffer[DUNST_NOTIF_MAX_CHARS-1] = '\0';
|
||||||
|
|
||||||
g_free(n->msg);
|
g_free(n->msg);
|
||||||
n->msg = buffer;
|
n->msg = buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
n->dup_count = 0;
|
||||||
|
|
||||||
|
/* urgency > URG_CRIT -> array out of range */
|
||||||
|
if (n->urgency < URG_MIN)
|
||||||
|
n->urgency = URG_LOW;
|
||||||
|
if (n->urgency > URG_MAX)
|
||||||
|
n->urgency = URG_CRIT;
|
||||||
|
|
||||||
|
if (!n->color_strings[ColFG]) {
|
||||||
|
n->color_strings[ColFG] = xctx.color_strings[ColFG][n->urgency];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!n->color_strings[ColBG]) {
|
||||||
|
n->color_strings[ColBG] = xctx.color_strings[ColBG][n->urgency];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!n->color_strings[ColFrame]) {
|
||||||
|
n->color_strings[ColFrame] = xctx.color_strings[ColFrame][n->urgency];
|
||||||
|
}
|
||||||
|
|
||||||
|
n->timeout =
|
||||||
|
n->timeout < 0 ? settings.timeouts[n->urgency] : n->timeout;
|
||||||
|
n->start = 0;
|
||||||
|
|
||||||
|
n->timestamp = g_get_monotonic_time();
|
||||||
|
|
||||||
|
n->redisplayed = false;
|
||||||
|
|
||||||
|
n->first_render = true;
|
||||||
|
|
||||||
|
char *tmp = g_strconcat(n->summary, " ", n->body, NULL);
|
||||||
|
|
||||||
|
char *tmp_urls = extract_urls(tmp);
|
||||||
|
n->urls = string_append(n->urls, tmp_urls, "\n");
|
||||||
|
g_free(tmp_urls);
|
||||||
|
|
||||||
|
if (n->actions) {
|
||||||
|
n->actions->dmenu_str = NULL;
|
||||||
|
for (int i = 0; i < n->actions->count; i += 2) {
|
||||||
|
char *human_readable = n->actions->actions[i + 1];
|
||||||
|
string_replace_char('[', '(', human_readable); // kill square brackets
|
||||||
|
string_replace_char(']', ')', human_readable);
|
||||||
|
|
||||||
|
char *act_str = g_strdup_printf("#%s [%s]", human_readable, n->appname);
|
||||||
|
if (act_str) {
|
||||||
|
n->actions->dmenu_str = string_append(n->actions->dmenu_str, act_str, "\n");
|
||||||
|
g_free(act_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void notification_extract_urls(struct notification *n)
|
void notification_update_text_to_render(notification *n)
|
||||||
{
|
{
|
||||||
g_clear_pointer(&n->urls, g_free);
|
g_free(n->text_to_render);
|
||||||
|
n->text_to_render = NULL;
|
||||||
char *urls_in = string_append(g_strdup(n->summary), n->body, " ");
|
|
||||||
|
|
||||||
char *urls_a = NULL;
|
|
||||||
char *urls_img = NULL;
|
|
||||||
markup_strip_a(&urls_in, &urls_a);
|
|
||||||
markup_strip_img(&urls_in, &urls_img);
|
|
||||||
// remove links and images first to not confuse
|
|
||||||
// plain urls extraction
|
|
||||||
char *urls_text = extract_urls(urls_in);
|
|
||||||
|
|
||||||
n->urls = string_append(n->urls, urls_a, "\n");
|
|
||||||
n->urls = string_append(n->urls, urls_img, "\n");
|
|
||||||
n->urls = string_append(n->urls, urls_text, "\n");
|
|
||||||
|
|
||||||
g_free(urls_in);
|
|
||||||
g_free(urls_a);
|
|
||||||
g_free(urls_img);
|
|
||||||
g_free(urls_text);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void notification_update_text_to_render(struct notification *n)
|
|
||||||
{
|
|
||||||
g_clear_pointer(&n->text_to_render, g_free);
|
|
||||||
|
|
||||||
char *buf = NULL;
|
char *buf = NULL;
|
||||||
|
|
||||||
@ -602,14 +504,14 @@ void notification_update_text_to_render(struct notification *n)
|
|||||||
|
|
||||||
/* print dup_count and msg */
|
/* print dup_count and msg */
|
||||||
if ((n->dup_count > 0 && !settings.hide_duplicate_count)
|
if ((n->dup_count > 0 && !settings.hide_duplicate_count)
|
||||||
&& (g_hash_table_size(n->actions) || n->urls) && settings.show_indicators) {
|
&& (n->actions || n->urls) && settings.show_indicators) {
|
||||||
buf = g_strdup_printf("(%d%s%s) %s",
|
buf = g_strdup_printf("(%d%s%s) %s",
|
||||||
n->dup_count,
|
n->dup_count,
|
||||||
g_hash_table_size(n->actions) ? "A" : "",
|
n->actions ? "A" : "",
|
||||||
n->urls ? "U" : "", msg);
|
n->urls ? "U" : "", msg);
|
||||||
} else if ((g_hash_table_size(n->actions) || n->urls) && settings.show_indicators) {
|
} else if ((n->actions || n->urls) && settings.show_indicators) {
|
||||||
buf = g_strdup_printf("(%s%s) %s",
|
buf = g_strdup_printf("(%s%s) %s",
|
||||||
g_hash_table_size(n->actions) ? "A" : "",
|
n->actions ? "A" : "",
|
||||||
n->urls ? "U" : "", msg);
|
n->urls ? "U" : "", msg);
|
||||||
} else if (n->dup_count > 0 && !settings.hide_duplicate_count) {
|
} else if (n->dup_count > 0 && !settings.hide_duplicate_count) {
|
||||||
buf = g_strdup_printf("(%d) %s", n->dup_count, msg);
|
buf = g_strdup_printf("(%d) %s", n->dup_count, msg);
|
||||||
@ -619,7 +521,7 @@ void notification_update_text_to_render(struct notification *n)
|
|||||||
|
|
||||||
/* print age */
|
/* print age */
|
||||||
gint64 hours, minutes, seconds;
|
gint64 hours, minutes, seconds;
|
||||||
gint64 t_delta = time_monotonic_now() - n->timestamp;
|
gint64 t_delta = g_get_monotonic_time() - n->timestamp;
|
||||||
|
|
||||||
if (settings.show_age_threshold >= 0
|
if (settings.show_age_threshold >= 0
|
||||||
&& t_delta >= settings.show_age_threshold) {
|
&& t_delta >= settings.show_age_threshold) {
|
||||||
@ -647,48 +549,32 @@ void notification_update_text_to_render(struct notification *n)
|
|||||||
n->text_to_render = buf;
|
n->text_to_render = buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see notification.h */
|
/*
|
||||||
void notification_do_action(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.
|
||||||
|
*/
|
||||||
|
void notification_do_action(notification *n)
|
||||||
{
|
{
|
||||||
assert(n->default_action_name);
|
if (n->actions) {
|
||||||
|
if (n->actions->count == 2) {
|
||||||
if (g_hash_table_size(n->actions)) {
|
action_invoked(n, n->actions->actions[0]);
|
||||||
if (g_hash_table_contains(n->actions, n->default_action_name)) {
|
|
||||||
signal_action_invoked(n, n->default_action_name);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (strcmp(n->default_action_name, "default") == 0 && g_hash_table_size(n->actions) == 1) {
|
for (int i = 0; i < n->actions->count; i += 2) {
|
||||||
GList *keys = g_hash_table_get_keys(n->actions);
|
if (strcmp(n->actions->actions[i], "default") == 0) {
|
||||||
signal_action_invoked(n, keys->data);
|
action_invoked(n, n->actions->actions[i]);
|
||||||
g_list_free(keys);
|
return;
|
||||||
return;
|
}
|
||||||
}
|
}
|
||||||
notification_open_context_menu(n);
|
context_menu();
|
||||||
|
|
||||||
|
} else if (n->urls) {
|
||||||
|
if (strstr(n->urls, "\n") == NULL)
|
||||||
|
open_browser(n->urls);
|
||||||
|
else
|
||||||
|
context_menu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see notification.h */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
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: */
|
|
||||||
|
|||||||
@ -2,237 +2,86 @@
|
|||||||
#ifndef DUNST_NOTIFICATION_H
|
#ifndef DUNST_NOTIFICATION_H
|
||||||
#define DUNST_NOTIFICATION_H
|
#define DUNST_NOTIFICATION_H
|
||||||
|
|
||||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "markup.h"
|
#include "settings.h"
|
||||||
|
|
||||||
#define DUNST_NOTIF_MAX_CHARS 50000
|
#define DUNST_NOTIF_MAX_CHARS 5000
|
||||||
|
|
||||||
enum behavior_fullscreen {
|
|
||||||
FS_NULL, //!< Invalid value
|
|
||||||
FS_DELAY, //!< Delay the notification until leaving fullscreen mode
|
|
||||||
FS_PUSHBACK, //!< When entering fullscreen mode, push the notification back to waiting
|
|
||||||
FS_SHOW, //!< Show the message when in fullscreen mode
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Representing the urgencies according to the notification spec
|
|
||||||
enum urgency {
|
enum urgency {
|
||||||
URG_NONE = -1, /**< Urgency not set (invalid) */
|
URG_NONE = -1,
|
||||||
URG_MIN = 0, /**< Minimum value, useful for boundary checking */
|
URG_MIN = 0,
|
||||||
URG_LOW = 0, /**< Low urgency */
|
URG_LOW = 0,
|
||||||
URG_NORM = 1, /**< Normal urgency */
|
URG_NORM = 1,
|
||||||
URG_CRIT = 2, /**< Critical urgency */
|
URG_CRIT = 2,
|
||||||
URG_MAX = 2, /**< Maximum value, useful for boundary checking */
|
URG_MAX = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct _notification_private NotificationPrivate;
|
typedef struct _raw_image {
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int rowstride;
|
||||||
|
int has_alpha;
|
||||||
|
int bits_per_sample;
|
||||||
|
int n_channels;
|
||||||
|
unsigned char *data;
|
||||||
|
} RawImage;
|
||||||
|
|
||||||
struct notification_colors {
|
typedef struct _actions {
|
||||||
char *frame;
|
char **actions;
|
||||||
char *bg;
|
char *dmenu_str;
|
||||||
char *fg;
|
gsize count;
|
||||||
char *highlight;
|
} Actions;
|
||||||
};
|
|
||||||
|
|
||||||
struct notification {
|
|
||||||
NotificationPrivate *priv;
|
|
||||||
int id;
|
|
||||||
char *dbus_client;
|
|
||||||
bool dbus_valid;
|
|
||||||
|
|
||||||
|
typedef struct _notification {
|
||||||
char *appname;
|
char *appname;
|
||||||
char *summary;
|
char *summary;
|
||||||
char *body;
|
char *body;
|
||||||
|
char *icon;
|
||||||
|
RawImage *raw_icon;
|
||||||
|
char *msg; /* formatted message */
|
||||||
char *category;
|
char *category;
|
||||||
char *desktop_entry; /**< The desktop entry hint sent via every GApplication */
|
char *text_to_render;
|
||||||
enum urgency urgency;
|
|
||||||
|
|
||||||
GdkPixbuf *icon; /**< The raw cached icon data used to draw */
|
|
||||||
char *icon_id; /**< plain icon information, which acts as the pixbuf's id, which is saved in .icon
|
|
||||||
May be a hash for a raw icon or a name/path for a regular app icon. */
|
|
||||||
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 (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 *format;
|
||||||
const char **scripts;
|
char *dbus_client;
|
||||||
int script_count;
|
gint64 start;
|
||||||
struct notification_colors colors;
|
gint64 timestamp;
|
||||||
|
gint64 timeout;
|
||||||
char *stack_tag; /**< stack notifications by tag */
|
enum urgency urgency;
|
||||||
|
enum markup_mode markup;
|
||||||
/* Hints */
|
bool redisplayed; /* has been displayed before? */
|
||||||
bool transient; /**< timeout albeit user is idle */
|
int id;
|
||||||
int progress; /**< percentage (-1: undefined) */
|
int dup_count;
|
||||||
int history_ignore; /**< push to history or free directly */
|
|
||||||
int skip_display; /**< insert notification into history, skipping initial waiting and display */
|
|
||||||
|
|
||||||
/* internal */
|
|
||||||
bool redisplayed; /**< has been displayed before? */
|
|
||||||
bool first_render; /**< markup has been rendered before? */
|
|
||||||
int dup_count; /**< amount of duplicate notifications stacked onto this */
|
|
||||||
int displayed_height;
|
int displayed_height;
|
||||||
enum behavior_fullscreen fullscreen; //!< The instruction what to do with it, when desktop enters fullscreen
|
const char *color_strings[3];
|
||||||
bool script_run; /**< Has the script been executed already? */
|
bool first_render;
|
||||||
guint8 marked_for_closure;
|
bool transient;
|
||||||
|
|
||||||
/* derived fields */
|
int progress; /* percentage (-1: undefined) */
|
||||||
char *msg; /**< formatted message */
|
int history_ignore;
|
||||||
char *text_to_render; /**< formatted message (with age and action indicators) */
|
const char *script;
|
||||||
char *urls; /**< urllist delimited by '\\n' */
|
char *urls;
|
||||||
};
|
Actions *actions;
|
||||||
|
} notification;
|
||||||
|
|
||||||
/**
|
notification *notification_create(void);
|
||||||
* Create notification struct and initialise all fields with either
|
void notification_init(notification *n);
|
||||||
* - the default (if it's not needed to be freed later)
|
void actions_free(Actions *a);
|
||||||
* - its undefined representation (NULL, -1)
|
void rawimage_free(RawImage *i);
|
||||||
*
|
void notification_free(notification *n);
|
||||||
* The reference counter is set to 1.
|
int notification_cmp(const void *a, const void *b);
|
||||||
*
|
int notification_cmp_data(const void *a, const void *b, void *data);
|
||||||
* This function is guaranteed to return a valid pointer.
|
int notification_is_duplicate(const notification *a, const notification *b);
|
||||||
* @returns The generated notification
|
void notification_run_script(notification *n);
|
||||||
*/
|
void notification_print(notification *n);
|
||||||
struct notification *notification_create(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the current reference count of the notification
|
|
||||||
*/
|
|
||||||
gint notification_refcount_get(struct notification *n);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Increase the reference counter of the notification.
|
|
||||||
*/
|
|
||||||
void notification_ref(struct notification *n);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sanitize values of notification, apply all matching rules
|
|
||||||
* and generate derived fields.
|
|
||||||
*
|
|
||||||
* @param n: the notification to sanitize
|
|
||||||
*/
|
|
||||||
void notification_init(struct notification *n);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrease the reference counter of the notification.
|
|
||||||
*
|
|
||||||
* If the reference count drops to 0, the object gets freed.
|
|
||||||
*/
|
|
||||||
void notification_unref(struct notification *n);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function to compare two given notifications.
|
|
||||||
*/
|
|
||||||
int notification_cmp(const struct notification *a, const struct notification *b);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper for notification_cmp to match glib's
|
|
||||||
* compare functions signature.
|
|
||||||
*/
|
|
||||||
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
|
|
||||||
* iconname field. So passing n->iconname as new_icon is invalid.
|
|
||||||
*
|
|
||||||
* @param n the notification to replace the icon
|
|
||||||
* @param new_icon The path of the new icon. May be an absolute path or an icon name.
|
|
||||||
*/
|
|
||||||
void notification_icon_replace_path(struct notification *n, const char *new_icon);
|
|
||||||
|
|
||||||
/**Replace the current notification's icon with the raw icon given in the GVariant.
|
|
||||||
*
|
|
||||||
* Removes the reference for the previous icon automatically.
|
|
||||||
*
|
|
||||||
* @param n the notification to replace the icon
|
|
||||||
* @param new_icon The icon's data. Has to be in the format of the notification spec.
|
|
||||||
*/
|
|
||||||
void notification_icon_replace_data(struct notification *n, GVariant *new_icon);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run the script associated with the
|
|
||||||
* given notification.
|
|
||||||
*
|
|
||||||
* If the script of the notification has been executed already and
|
|
||||||
* settings.always_run_script is not set, do nothing.
|
|
||||||
*/
|
|
||||||
void notification_run_script(struct notification *n);
|
|
||||||
/**
|
|
||||||
* print a human readable representation
|
|
||||||
* of the given notification to stdout.
|
|
||||||
*/
|
|
||||||
void notification_print(const struct notification *n);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace the two chars where **needle points
|
|
||||||
* with a quoted "replacement", according to the markup settings.
|
|
||||||
*
|
|
||||||
* The needle is a double pointer and gets updated upon return
|
|
||||||
* to point to the first char, which occurs after replacement.
|
|
||||||
*/
|
|
||||||
void notification_replace_single_field(char **haystack,
|
void notification_replace_single_field(char **haystack,
|
||||||
char **needle,
|
char **needle,
|
||||||
const char *replacement,
|
const char *replacement,
|
||||||
enum markup_mode markup_mode);
|
enum markup_mode markup_mode);
|
||||||
|
void notification_update_text_to_render(notification *n);
|
||||||
|
void notification_do_action(notification *n);
|
||||||
|
|
||||||
void notification_update_text_to_render(struct notification *n);
|
const char *notification_urgency_to_string(enum urgency urgency);
|
||||||
|
|
||||||
/**
|
|
||||||
* 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(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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the string representation for fullscreen behavior
|
|
||||||
*
|
|
||||||
* @param in the #behavior_fullscreen enum value to represent
|
|
||||||
* @return the string representation for `in`
|
|
||||||
*/
|
|
||||||
const char *enum_to_string_fullscreen(enum behavior_fullscreen in);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
|||||||
@ -8,29 +8,27 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "dunst.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "settings.h"
|
|
||||||
|
|
||||||
struct entry {
|
typedef struct _entry_t {
|
||||||
char *key;
|
char *key;
|
||||||
char *value;
|
char *value;
|
||||||
};
|
} entry_t;
|
||||||
|
|
||||||
struct section {
|
typedef struct _section_t {
|
||||||
char *name;
|
char *name;
|
||||||
int entry_count;
|
int entry_count;
|
||||||
struct entry *entries;
|
entry_t *entries;
|
||||||
};
|
} section_t;
|
||||||
|
|
||||||
static int section_count = 0;
|
static int section_count = 0;
|
||||||
static struct section *sections;
|
static section_t *sections;
|
||||||
|
|
||||||
static struct section *new_section(const char *name);
|
static section_t *new_section(const char *name);
|
||||||
static struct section *get_section(const char *name);
|
static section_t *get_section(const char *name);
|
||||||
static void add_entry(const char *section_name, const char *key, const char *value);
|
static void add_entry(const char *section_name, const char *key, const char *value);
|
||||||
static const char *get_value(const char *section, const char *key);
|
static const char *get_value(const char *section, const char *key);
|
||||||
|
static char *clean_value(const char *value);
|
||||||
|
|
||||||
static int cmdline_argc;
|
static int cmdline_argc;
|
||||||
static char **cmdline_argv;
|
static char **cmdline_argv;
|
||||||
@ -40,180 +38,16 @@ static void cmdline_usage_append(const char *key, const char *type, const char *
|
|||||||
|
|
||||||
static int cmdline_find_option(const char *key);
|
static int cmdline_find_option(const char *key);
|
||||||
|
|
||||||
#define STRING_PARSE_RET(string, value) if (STR_EQ(s, string)) { *ret = value; return true; }
|
section_t *new_section(const char *name)
|
||||||
|
|
||||||
bool string_parse_alignment(const char *s, enum alignment *ret)
|
|
||||||
{
|
|
||||||
ASSERT_OR_RET(STR_FULL(s), false);
|
|
||||||
ASSERT_OR_RET(ret, false);
|
|
||||||
|
|
||||||
STRING_PARSE_RET("left", ALIGN_LEFT);
|
|
||||||
STRING_PARSE_RET("center", ALIGN_CENTER);
|
|
||||||
STRING_PARSE_RET("right", ALIGN_RIGHT);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool string_parse_ellipsize(const char *s, enum ellipsize *ret)
|
|
||||||
{
|
|
||||||
ASSERT_OR_RET(STR_FULL(s), false);
|
|
||||||
ASSERT_OR_RET(ret, false);
|
|
||||||
|
|
||||||
STRING_PARSE_RET("start", ELLIPSE_START);
|
|
||||||
STRING_PARSE_RET("middle", ELLIPSE_MIDDLE);
|
|
||||||
STRING_PARSE_RET("end", ELLIPSE_END);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool string_parse_follow_mode(const char *s, enum follow_mode *ret)
|
|
||||||
{
|
|
||||||
ASSERT_OR_RET(STR_FULL(s), false);
|
|
||||||
ASSERT_OR_RET(ret, false);
|
|
||||||
|
|
||||||
STRING_PARSE_RET("mouse", FOLLOW_MOUSE);
|
|
||||||
STRING_PARSE_RET("keyboard", FOLLOW_KEYBOARD);
|
|
||||||
STRING_PARSE_RET("none", FOLLOW_NONE);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool string_parse_fullscreen(const char *s, enum behavior_fullscreen *ret)
|
|
||||||
{
|
|
||||||
ASSERT_OR_RET(STR_FULL(s), false);
|
|
||||||
ASSERT_OR_RET(ret, false);
|
|
||||||
|
|
||||||
STRING_PARSE_RET("show", FS_SHOW);
|
|
||||||
STRING_PARSE_RET("delay", FS_DELAY);
|
|
||||||
STRING_PARSE_RET("pushback", FS_PUSHBACK);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool string_parse_icon_position(const char *s, enum icon_position *ret)
|
|
||||||
{
|
|
||||||
ASSERT_OR_RET(STR_FULL(s), false);
|
|
||||||
ASSERT_OR_RET(ret, false);
|
|
||||||
|
|
||||||
STRING_PARSE_RET("left", ICON_LEFT);
|
|
||||||
STRING_PARSE_RET("right", ICON_RIGHT);
|
|
||||||
STRING_PARSE_RET("off", ICON_OFF);
|
|
||||||
|
|
||||||
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(STR_FULL(s), false);
|
|
||||||
ASSERT_OR_RET(ret, false);
|
|
||||||
|
|
||||||
STRING_PARSE_RET("strip", MARKUP_STRIP);
|
|
||||||
STRING_PARSE_RET("no", MARKUP_NO);
|
|
||||||
STRING_PARSE_RET("full", MARKUP_FULL);
|
|
||||||
STRING_PARSE_RET("yes", MARKUP_FULL);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool string_parse_mouse_action(const char *s, enum mouse_action *ret)
|
|
||||||
{
|
|
||||||
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_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});
|
|
||||||
|
|
||||||
ret->type = SEP_CUSTOM;
|
|
||||||
ret->sep_color = g_strdup(s);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool string_parse_urgency(const char *s, enum urgency *ret)
|
|
||||||
{
|
|
||||||
ASSERT_OR_RET(STR_FULL(s), false);
|
|
||||||
ASSERT_OR_RET(ret, false);
|
|
||||||
|
|
||||||
STRING_PARSE_RET("low", URG_LOW);
|
|
||||||
STRING_PARSE_RET("normal", URG_NORM);
|
|
||||||
STRING_PARSE_RET("critical", URG_CRIT);
|
|
||||||
|
|
||||||
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++) {
|
for (int i = 0; i < section_count; i++) {
|
||||||
if (STR_EQ(name, sections[i].name)) {
|
if (!strcmp(name, sections[i].name)) {
|
||||||
DIE("Duplicated section in dunstrc detected.");
|
die("Duplicated section in dunstrc detected.\n", -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
section_count++;
|
section_count++;
|
||||||
sections = g_realloc(sections, sizeof(struct section) * section_count);
|
sections = g_realloc(sections, sizeof(section_t) * section_count);
|
||||||
sections[section_count - 1].name = g_strdup(name);
|
sections[section_count - 1].name = g_strdup(name);
|
||||||
sections[section_count - 1].entries = NULL;
|
sections[section_count - 1].entries = NULL;
|
||||||
sections[section_count - 1].entry_count = 0;
|
sections[section_count - 1].entry_count = 0;
|
||||||
@ -230,14 +64,15 @@ void free_ini(void)
|
|||||||
g_free(sections[i].entries);
|
g_free(sections[i].entries);
|
||||||
g_free(sections[i].name);
|
g_free(sections[i].name);
|
||||||
}
|
}
|
||||||
g_clear_pointer(§ions, g_free);
|
g_free(sections);
|
||||||
section_count = 0;
|
section_count = 0;
|
||||||
|
sections = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct section *get_section(const char *name)
|
section_t *get_section(const char *name)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < section_count; i++) {
|
for (int i = 0; i < section_count; i++) {
|
||||||
if (STR_EQ(sections[i].name, name))
|
if (strcmp(sections[i].name, name) == 0)
|
||||||
return §ions[i];
|
return §ions[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,24 +81,27 @@ struct section *get_section(const char *name)
|
|||||||
|
|
||||||
void add_entry(const char *section_name, const char *key, const char *value)
|
void add_entry(const char *section_name, const char *key, const char *value)
|
||||||
{
|
{
|
||||||
struct section *s = get_section(section_name);
|
section_t *s = get_section(section_name);
|
||||||
if (!s)
|
if (s == NULL) {
|
||||||
s = new_section(section_name);
|
s = new_section(section_name);
|
||||||
|
}
|
||||||
|
|
||||||
s->entry_count++;
|
s->entry_count++;
|
||||||
int len = s->entry_count;
|
int len = s->entry_count;
|
||||||
s->entries = g_realloc(s->entries, sizeof(struct entry) * len);
|
s->entries = g_realloc(s->entries, sizeof(entry_t) * len);
|
||||||
s->entries[s->entry_count - 1].key = g_strdup(key);
|
s->entries[s->entry_count - 1].key = g_strdup(key);
|
||||||
s->entries[s->entry_count - 1].value = string_strip_quotes(value);
|
s->entries[s->entry_count - 1].value = clean_value(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *get_value(const char *section, const char *key)
|
const char *get_value(const char *section, const char *key)
|
||||||
{
|
{
|
||||||
struct section *s = get_section(section);
|
section_t *s = get_section(section);
|
||||||
ASSERT_OR_RET(s, NULL);
|
if (!s) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < s->entry_count; i++) {
|
for (int i = 0; i < s->entry_count; i++) {
|
||||||
if (STR_EQ(s->entries[i].key, key)) {
|
if (strcmp(s->entries[i].key, key) == 0) {
|
||||||
return s->entries[i].value;
|
return s->entries[i].value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -296,31 +134,22 @@ gint64 ini_get_time(const char *section, const char *key, gint64 def)
|
|||||||
return val;
|
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)
|
int ini_get_int(const char *section, const char *key, int def)
|
||||||
{
|
{
|
||||||
const char *value = get_value(section, key);
|
const char *value = get_value(section, key);
|
||||||
if (value)
|
if (value == NULL)
|
||||||
return atoi(value);
|
|
||||||
else
|
|
||||||
return def;
|
return def;
|
||||||
|
else
|
||||||
|
return atoi(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
double ini_get_double(const char *section, const char *key, double def)
|
double ini_get_double(const char *section, const char *key, double def)
|
||||||
{
|
{
|
||||||
const char *value = get_value(section, key);
|
const char *value = get_value(section, key);
|
||||||
if (value)
|
if (value == NULL)
|
||||||
return atof(value);
|
|
||||||
else
|
|
||||||
return def;
|
return def;
|
||||||
|
else
|
||||||
|
return atof(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ini_is_set(const char *ini_section, const char *ini_key)
|
bool ini_is_set(const char *ini_section, const char *ini_key)
|
||||||
@ -330,11 +159,15 @@ bool ini_is_set(const char *ini_section, const char *ini_key)
|
|||||||
|
|
||||||
const char *next_section(const char *section)
|
const char *next_section(const char *section)
|
||||||
{
|
{
|
||||||
ASSERT_OR_RET(section_count > 0, NULL);
|
if (section_count == 0)
|
||||||
ASSERT_OR_RET(section, sections[0].name);
|
return NULL;
|
||||||
|
|
||||||
|
if (section == NULL) {
|
||||||
|
return sections[0].name;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < section_count; i++) {
|
for (int i = 0; i < section_count; i++) {
|
||||||
if (STR_EQ(section, sections[i].name)) {
|
if (strcmp(section, sections[i].name) == 0) {
|
||||||
if (i + 1 >= section_count)
|
if (i + 1 >= section_count)
|
||||||
return NULL;
|
return NULL;
|
||||||
else
|
else
|
||||||
@ -347,7 +180,9 @@ const char *next_section(const char *section)
|
|||||||
int ini_get_bool(const char *section, const char *key, int def)
|
int ini_get_bool(const char *section, const char *key, int def)
|
||||||
{
|
{
|
||||||
const char *value = get_value(section, key);
|
const char *value = get_value(section, key);
|
||||||
if (value) {
|
if (value == NULL)
|
||||||
|
return def;
|
||||||
|
else {
|
||||||
switch (value[0]) {
|
switch (value[0]) {
|
||||||
case 'y':
|
case 'y':
|
||||||
case 'Y':
|
case 'Y':
|
||||||
@ -364,14 +199,28 @@ int ini_get_bool(const char *section, const char *key, int def)
|
|||||||
default:
|
default:
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return def;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *clean_value(const char *value)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
if (value[0] == '"')
|
||||||
|
s = g_strdup(value + 1);
|
||||||
|
else
|
||||||
|
s = g_strdup(value);
|
||||||
|
|
||||||
|
if (s[strlen(s) - 1] == '"')
|
||||||
|
s[strlen(s) - 1] = '\0';
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
int load_ini_file(FILE *fp)
|
int load_ini_file(FILE *fp)
|
||||||
{
|
{
|
||||||
ASSERT_OR_RET(fp, 1);
|
if (!fp)
|
||||||
|
return 1;
|
||||||
|
|
||||||
char *line = NULL;
|
char *line = NULL;
|
||||||
size_t line_len = 0;
|
size_t line_len = 0;
|
||||||
@ -383,13 +232,16 @@ int load_ini_file(FILE *fp)
|
|||||||
|
|
||||||
char *start = g_strstrip(line);
|
char *start = g_strstrip(line);
|
||||||
|
|
||||||
if (*start == ';' || *start == '#' || STR_EMPTY(start))
|
if (*start == ';' || *start == '#' || strlen(start) == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (*start == '[') {
|
if (*start == '[') {
|
||||||
char *end = strchr(start + 1, ']');
|
char *end = strchr(start + 1, ']');
|
||||||
if (!end) {
|
if (!end) {
|
||||||
LOG_W("Invalid config file at line %d: Missing ']'.", line_num);
|
fprintf(stderr,
|
||||||
|
"Warning: invalid config file at line %d\n",
|
||||||
|
line_num);
|
||||||
|
fprintf(stderr, "Missing ']'\n");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -403,7 +255,10 @@ int load_ini_file(FILE *fp)
|
|||||||
|
|
||||||
char *equal = strchr(start + 1, '=');
|
char *equal = strchr(start + 1, '=');
|
||||||
if (!equal) {
|
if (!equal) {
|
||||||
LOG_W("Invalid config file at line %d: Missing '='.", line_num);
|
fprintf(stderr,
|
||||||
|
"Warning: invalid config file at line %d\n",
|
||||||
|
line_num);
|
||||||
|
fprintf(stderr, "Missing '='\n");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -412,25 +267,27 @@ int load_ini_file(FILE *fp)
|
|||||||
char *value = g_strstrip(equal + 1);
|
char *value = g_strstrip(equal + 1);
|
||||||
|
|
||||||
char *quote = strchr(value, '"');
|
char *quote = strchr(value, '"');
|
||||||
char *value_end = NULL;
|
|
||||||
if (quote) {
|
if (quote) {
|
||||||
value_end = strchr(quote + 1, '"');
|
char *closing_quote = strchr(quote + 1, '"');
|
||||||
if (!value_end) {
|
if (!closing_quote) {
|
||||||
LOG_W("Invalid config file at line %d: Missing '\"'.", line_num);
|
fprintf(stderr,
|
||||||
|
"Warning: invalid config file at line %d\n",
|
||||||
|
line_num);
|
||||||
|
fprintf(stderr, "Missing '\"'\n");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
value_end = value;
|
char *comment = strpbrk(value, "#;");
|
||||||
|
if (comment)
|
||||||
|
*comment = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
char *comment = strpbrk(value_end, "#;");
|
|
||||||
if (comment)
|
|
||||||
*comment = '\0';
|
|
||||||
|
|
||||||
value = g_strstrip(value);
|
value = g_strstrip(value);
|
||||||
|
|
||||||
if (!current_section) {
|
if (!current_section) {
|
||||||
LOG_W("Invalid config file at line %d: Key value pair without a section.", line_num);
|
fprintf(stderr,
|
||||||
|
"Warning: invalid config file at line %d\n",
|
||||||
|
line_num);
|
||||||
|
fprintf(stderr, "Key value pair without a section\n");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -449,8 +306,9 @@ void cmdline_load(int argc, char *argv[])
|
|||||||
|
|
||||||
int cmdline_find_option(const char *key)
|
int cmdline_find_option(const char *key)
|
||||||
{
|
{
|
||||||
ASSERT_OR_RET(key, -1);
|
if (!key) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
char *key1 = g_strdup(key);
|
char *key1 = g_strdup(key);
|
||||||
char *key2 = strchr(key1, '/');
|
char *key2 = strchr(key1, '/');
|
||||||
|
|
||||||
@ -461,7 +319,7 @@ int cmdline_find_option(const char *key)
|
|||||||
|
|
||||||
/* look for first key */
|
/* look for first key */
|
||||||
for (int i = 0; i < cmdline_argc; i++) {
|
for (int i = 0; i < cmdline_argc; i++) {
|
||||||
if (STR_EQ(key1, cmdline_argv[i])) {
|
if (strcmp(key1, cmdline_argv[i]) == 0) {
|
||||||
g_free(key1);
|
g_free(key1);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
@ -470,7 +328,7 @@ int cmdline_find_option(const char *key)
|
|||||||
/* look for second key if one was specified */
|
/* look for second key if one was specified */
|
||||||
if (key2) {
|
if (key2) {
|
||||||
for (int i = 0; i < cmdline_argc; i++) {
|
for (int i = 0; i < cmdline_argc; i++) {
|
||||||
if (STR_EQ(key2, cmdline_argv[i])) {
|
if (strcmp(key2, cmdline_argv[i]) == 0) {
|
||||||
g_free(key1);
|
g_free(key1);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
@ -490,7 +348,8 @@ static const char *cmdline_get_value(const char *key)
|
|||||||
|
|
||||||
if (idx + 1 >= cmdline_argc) {
|
if (idx + 1 >= cmdline_argc) {
|
||||||
/* the argument is missing */
|
/* the argument is missing */
|
||||||
LOG_W("%s: Missing argument. Ignoring.", key);
|
fprintf(stderr, "Warning: %s, missing argument. Ignoring\n",
|
||||||
|
key);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return cmdline_argv[idx + 1];
|
return cmdline_argv[idx + 1];
|
||||||
@ -503,10 +362,10 @@ char *cmdline_get_string(const char *key, const char *def, const char *descripti
|
|||||||
|
|
||||||
if (str)
|
if (str)
|
||||||
return g_strdup(str);
|
return g_strdup(str);
|
||||||
if (def)
|
if (def == NULL)
|
||||||
return g_strdup(def);
|
|
||||||
else
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
else
|
||||||
|
return g_strdup(def);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *cmdline_get_path(const char *key, const char *def, const char *description)
|
char *cmdline_get_path(const char *key, const char *def, const char *description)
|
||||||
@ -520,17 +379,6 @@ char *cmdline_get_path(const char *key, const char *def, const char *description
|
|||||||
return string_to_path(g_strdup(def));
|
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)
|
gint64 cmdline_get_time(const char *key, gint64 def, const char *description)
|
||||||
{
|
{
|
||||||
cmdline_usage_append(key, "time", description);
|
cmdline_usage_append(key, "time", description);
|
||||||
@ -549,10 +397,10 @@ int cmdline_get_int(const char *key, int def, const char *description)
|
|||||||
cmdline_usage_append(key, "int", description);
|
cmdline_usage_append(key, "int", description);
|
||||||
const char *str = cmdline_get_value(key);
|
const char *str = cmdline_get_value(key);
|
||||||
|
|
||||||
if (str)
|
if (str == NULL)
|
||||||
return atoi(str);
|
|
||||||
else
|
|
||||||
return def;
|
return def;
|
||||||
|
else
|
||||||
|
return atoi(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
double cmdline_get_double(const char *key, double def, const char *description)
|
double cmdline_get_double(const char *key, double def, const char *description)
|
||||||
@ -560,10 +408,10 @@ double cmdline_get_double(const char *key, double def, const char *description)
|
|||||||
cmdline_usage_append(key, "double", description);
|
cmdline_usage_append(key, "double", description);
|
||||||
const char *str = cmdline_get_value(key);
|
const char *str = cmdline_get_value(key);
|
||||||
|
|
||||||
if (str)
|
if (str == NULL)
|
||||||
return atof(str);
|
|
||||||
else
|
|
||||||
return def;
|
return def;
|
||||||
|
else
|
||||||
|
return atof(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cmdline_get_bool(const char *key, int def, const char *description)
|
int cmdline_get_bool(const char *key, int def, const char *description)
|
||||||
@ -630,23 +478,6 @@ gint64 option_get_time(const char *ini_section,
|
|||||||
return cmdline_get_time(cmdline_key, ini_val, description);
|
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,
|
int option_get_int(const char *ini_section,
|
||||||
const char *ini_key,
|
const char *ini_key,
|
||||||
const char *cmdline_key,
|
const char *cmdline_key,
|
||||||
@ -704,7 +535,7 @@ int option_get_bool(const char *ini_section,
|
|||||||
void cmdline_usage_append(const char *key, const char *type, const char *description)
|
void cmdline_usage_append(const char *key, const char *type, const char *description)
|
||||||
{
|
{
|
||||||
char *key_type;
|
char *key_type;
|
||||||
if (STR_FULL(type))
|
if (type && strlen(type) > 0)
|
||||||
key_type = g_strdup_printf("%s (%s)", key, type);
|
key_type = g_strdup_printf("%s (%s)", key, type);
|
||||||
else
|
else
|
||||||
key_type = g_strdup(key);
|
key_type = g_strdup(key);
|
||||||
@ -731,4 +562,4 @@ const char *cmdline_create_usage(void)
|
|||||||
return usage_str;
|
return usage_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
|||||||
@ -6,27 +6,10 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "dunst.h"
|
|
||||||
#include "settings.h"
|
|
||||||
|
|
||||||
bool string_parse_alignment(const char *s, enum alignment *ret);
|
|
||||||
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 *);
|
int load_ini_file(FILE *);
|
||||||
char *ini_get_path(const char *section, const char *key, const char *def);
|
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);
|
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);
|
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);
|
int ini_get_int(const char *section, const char *key, int def);
|
||||||
double ini_get_double(const char *section, const char *key, double 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);
|
int ini_get_bool(const char *section, const char *key, int def);
|
||||||
@ -37,7 +20,6 @@ void cmdline_load(int argc, char *argv[]);
|
|||||||
/* for all cmdline_get_* key can be either "-key" or "-key/-longkey" */
|
/* 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_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_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);
|
int cmdline_get_int(const char *key, int def, const char *description);
|
||||||
double cmdline_get_double(const char *key, double 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);
|
int cmdline_get_bool(const char *key, int def, const char *description);
|
||||||
@ -59,11 +41,6 @@ gint64 option_get_time(const char *ini_section,
|
|||||||
const char *cmdline_key,
|
const char *cmdline_key,
|
||||||
gint64 def,
|
gint64 def,
|
||||||
const char *description);
|
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,
|
int option_get_int(const char *ini_section,
|
||||||
const char *ini_key,
|
const char *ini_key,
|
||||||
const char *cmdline_key,
|
const char *cmdline_key,
|
||||||
@ -87,4 +64,4 @@ int option_get_bool(const char *ini_section,
|
|||||||
const char *next_section(const char *section);
|
const char *next_section(const char *section);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
|||||||
96
src/output.c
@ -1,96 +0,0 @@
|
|||||||
#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
@ -1,61 +0,0 @@
|
|||||||
#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: */
|
|
||||||
592
src/queues.c
@ -1,18 +1,5 @@
|
|||||||
/* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
|
/* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
|
||||||
|
|
||||||
/**
|
|
||||||
* @file src/queues.c
|
|
||||||
* @brief All important functions to handle the notification queues for
|
|
||||||
* history, entrance and currently displayed ones.
|
|
||||||
*
|
|
||||||
* Every method requires to have executed queues_init() at the start.
|
|
||||||
*
|
|
||||||
* A read only representation of the queue with the current notifications
|
|
||||||
* can get acquired by calling queues_get_displayed().
|
|
||||||
*
|
|
||||||
* When ending the program or resetting the queues, tear down the stack with
|
|
||||||
* queues_teardown(). (And reinit with queues_init() if needed.)
|
|
||||||
*/
|
|
||||||
#include "queues.h"
|
#include "queues.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@ -20,24 +7,20 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "dunst.h"
|
|
||||||
#include "log.h"
|
|
||||||
#include "notification.h"
|
#include "notification.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "utils.h"
|
|
||||||
#include "output.h" // For checking if wayland is active.
|
|
||||||
|
|
||||||
/* notification lists */
|
/* notification lists */
|
||||||
static GQueue *waiting = NULL; /**< all new notifications get into here */
|
static GQueue *waiting = NULL; /* all new notifications get into here */
|
||||||
static GQueue *displayed = NULL; /**< currently displayed notifications */
|
static GQueue *displayed = NULL; /* currently displayed notifications */
|
||||||
static GQueue *history = NULL; /**< history of displayed notifications */
|
static GQueue *history = NULL; /* history of displayed notifications */
|
||||||
|
|
||||||
|
unsigned int displayed_limit = 0;
|
||||||
int next_notification_id = 1;
|
int next_notification_id = 1;
|
||||||
|
bool pause_displayed = false;
|
||||||
|
|
||||||
static bool queues_stack_duplicate(struct notification *n);
|
static bool queues_stack_duplicate(notification *n);
|
||||||
static bool queues_stack_by_tag(struct notification *n);
|
|
||||||
|
|
||||||
/* see queues.h */
|
|
||||||
void queues_init(void)
|
void queues_init(void)
|
||||||
{
|
{
|
||||||
history = g_queue_new();
|
history = g_queue_new();
|
||||||
@ -45,314 +28,222 @@ void queues_init(void)
|
|||||||
waiting = g_queue_new();
|
waiting = g_queue_new();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see queues.h */
|
void queues_displayed_limit(unsigned int limit)
|
||||||
GList *queues_get_displayed(void)
|
{
|
||||||
|
displayed_limit = limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* misc getter functions */
|
||||||
|
const GList *queues_get_displayed()
|
||||||
{
|
{
|
||||||
return g_queue_peek_head_link(displayed);
|
return g_queue_peek_head_link(displayed);
|
||||||
}
|
}
|
||||||
|
unsigned int queues_length_waiting()
|
||||||
/* see queues.h */
|
|
||||||
const struct notification *queues_get_head_waiting(void)
|
|
||||||
{
|
|
||||||
if (waiting->length == 0)
|
|
||||||
return NULL;
|
|
||||||
return g_queue_peek_head(waiting);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* see queues.h */
|
|
||||||
unsigned int queues_length_waiting(void)
|
|
||||||
{
|
{
|
||||||
return waiting->length;
|
return waiting->length;
|
||||||
}
|
}
|
||||||
|
unsigned int queues_length_displayed()
|
||||||
/* see queues.h */
|
|
||||||
unsigned int queues_length_displayed(void)
|
|
||||||
{
|
{
|
||||||
return displayed->length;
|
return displayed->length;
|
||||||
}
|
}
|
||||||
|
unsigned int queues_length_history()
|
||||||
/* see queues.h */
|
|
||||||
unsigned int queues_length_history(void)
|
|
||||||
{
|
{
|
||||||
return history->length;
|
return history->length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
int queues_notification_insert(notification *n, int replaces_id)
|
||||||
* Swap two given queue elements. The element's data has to be a notification.
|
|
||||||
*
|
|
||||||
* @pre { elemA has to be part of queueA. }
|
|
||||||
* @pre { elemB has to be part of queueB. }
|
|
||||||
*
|
|
||||||
* @param queueA The queue, which elemB's data will get inserted
|
|
||||||
* @param elemA The element, which will get removed from queueA
|
|
||||||
* @param queueB The queue, which elemA's data will get inserted
|
|
||||||
* @param elemB The element, which will get removed from queueB
|
|
||||||
*/
|
|
||||||
static void queues_swap_notifications(GQueue *queueA,
|
|
||||||
GList *elemA,
|
|
||||||
GQueue *queueB,
|
|
||||||
GList *elemB)
|
|
||||||
{
|
{
|
||||||
struct notification *toB = elemA->data;
|
|
||||||
struct notification *toA = elemB->data;
|
|
||||||
|
|
||||||
g_queue_delete_link(queueA, elemA);
|
|
||||||
g_queue_delete_link(queueB, elemB);
|
|
||||||
|
|
||||||
if (toA)
|
|
||||||
g_queue_insert_sorted(queueA, toA, notification_cmp_data, NULL);
|
|
||||||
if (toB)
|
|
||||||
g_queue_insert_sorted(queueB, toB, notification_cmp_data, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a notification is eligible to get shown.
|
|
||||||
*
|
|
||||||
* @param n The notification to check
|
|
||||||
* @param status The current status of dunst
|
|
||||||
* @param shown True if the notification is currently displayed
|
|
||||||
*/
|
|
||||||
static bool queues_notification_is_ready(const struct notification *n, struct dunst_status status, bool shown)
|
|
||||||
{
|
|
||||||
ASSERT_OR_RET(status.running, false);
|
|
||||||
if (status.fullscreen && shown)
|
|
||||||
return n && n->fullscreen != FS_PUSHBACK;
|
|
||||||
else if (status.fullscreen && !shown)
|
|
||||||
return n && n->fullscreen == FS_SHOW;
|
|
||||||
else
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a notification has timed out
|
|
||||||
*
|
|
||||||
* @param n the notification to check
|
|
||||||
* @param status the current status of dunst
|
|
||||||
* @retval true: the notification is timed out
|
|
||||||
* @retval false: otherwise
|
|
||||||
*/
|
|
||||||
static bool queues_notification_is_finished(struct notification *n, struct dunst_status status)
|
|
||||||
{
|
|
||||||
assert(n);
|
|
||||||
|
|
||||||
if (n->skip_display && !n->redisplayed)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (n->timeout == 0) // sticky
|
|
||||||
return false;
|
|
||||||
|
|
||||||
bool is_idle = status.fullscreen ? false : status.idle;
|
|
||||||
|
|
||||||
/* don't timeout when user is idle */
|
|
||||||
if (is_idle && !n->transient) {
|
|
||||||
n->start = time_monotonic_now();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* remove old message */
|
|
||||||
if (time_monotonic_now() - n->start > n->timeout) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* see queues.h */
|
|
||||||
int queues_notification_insert(struct notification *n)
|
|
||||||
{
|
|
||||||
/* do not display the message, if the message is empty */
|
/* do not display the message, if the message is empty */
|
||||||
if (STR_EMPTY(n->msg)) {
|
if (strlen(n->msg) == 0) {
|
||||||
if (settings.always_run_script) {
|
if (settings.always_run_script) {
|
||||||
notification_run_script(n);
|
notification_run_script(n);
|
||||||
}
|
}
|
||||||
LOG_M("Skipping notification: '%s' '%s'", n->body, n->summary);
|
printf("skipping notification: %s %s\n", n->body, n->summary);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Do not insert the message if it's a command */
|
||||||
|
if (strcmp("DUNST_COMMAND_PAUSE", n->summary) == 0) {
|
||||||
|
pause_displayed = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (strcmp("DUNST_COMMAND_RESUME", n->summary) == 0) {
|
||||||
|
pause_displayed = false;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool inserted = false;
|
if (replaces_id == 0) {
|
||||||
if (n->id != 0) {
|
|
||||||
if (!queues_notification_replace_id(n)) {
|
|
||||||
// Requested id was not valid, but play nice and assign it anyway
|
|
||||||
g_queue_insert_sorted(waiting, n, notification_cmp_data, NULL);
|
|
||||||
}
|
|
||||||
inserted = true;
|
|
||||||
} else {
|
|
||||||
n->id = ++next_notification_id;
|
n->id = ++next_notification_id;
|
||||||
|
if (!settings.stack_duplicates || !queues_stack_duplicate(n))
|
||||||
|
g_queue_insert_sorted(waiting, n, notification_cmp_data, NULL);
|
||||||
|
} else {
|
||||||
|
n->id = replaces_id;
|
||||||
|
if (!queues_notification_replace_id(n))
|
||||||
|
g_queue_insert_sorted(waiting, n, notification_cmp_data, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!inserted && STR_FULL(n->stack_tag) && queues_stack_by_tag(n))
|
|
||||||
inserted = true;
|
|
||||||
|
|
||||||
if (!inserted && settings.stack_duplicates && queues_stack_duplicate(n))
|
|
||||||
inserted = true;
|
|
||||||
|
|
||||||
if (!inserted)
|
|
||||||
g_queue_insert_sorted(waiting, n, notification_cmp_data, NULL);
|
|
||||||
|
|
||||||
if (settings.print_notifications)
|
if (settings.print_notifications)
|
||||||
notification_print(n);
|
notification_print(n);
|
||||||
|
|
||||||
return n->id;
|
return n->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Replaces duplicate notification and stacks it
|
* Replaces duplicate notification and stacks it
|
||||||
*
|
*
|
||||||
* @retval true: notification got stacked
|
* Returns %true, if notification got stacked
|
||||||
* @retval false: notification did not get stacked
|
* Returns %false, if notification did not get stacked
|
||||||
*/
|
*/
|
||||||
static bool queues_stack_duplicate(struct notification *n)
|
static bool queues_stack_duplicate(notification *n)
|
||||||
{
|
{
|
||||||
GQueue *allqueues[] = { displayed, waiting };
|
for (GList *iter = g_queue_peek_head_link(displayed); iter;
|
||||||
for (int i = 0; i < sizeof(allqueues)/sizeof(GQueue*); i++) {
|
iter = iter->next) {
|
||||||
for (GList *iter = g_queue_peek_head_link(allqueues[i]); iter;
|
notification *orig = iter->data;
|
||||||
iter = iter->next) {
|
if (notification_is_duplicate(orig, n)) {
|
||||||
struct notification *orig = iter->data;
|
/* If the progress differs, probably notify-send was used to update the notification
|
||||||
if (notification_is_duplicate(orig, n)) {
|
* So only count it as a duplicate, if the progress was not the same.
|
||||||
/* If the progress differs, probably notify-send was used to update the notification
|
* */
|
||||||
* So only count it as a duplicate, if the progress was not the same.
|
if (orig->progress == n->progress) {
|
||||||
* */
|
orig->dup_count++;
|
||||||
if (orig->progress == n->progress) {
|
} else {
|
||||||
orig->dup_count++;
|
orig->progress = n->progress;
|
||||||
} else {
|
|
||||||
orig->progress = n->progress;
|
|
||||||
}
|
|
||||||
iter->data = n;
|
|
||||||
|
|
||||||
n->dup_count = orig->dup_count;
|
|
||||||
signal_notification_closed(orig, 1);
|
|
||||||
|
|
||||||
if (allqueues[i] == displayed)
|
|
||||||
n->start = time_monotonic_now();
|
|
||||||
|
|
||||||
notification_unref(orig);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iter->data = n;
|
||||||
|
|
||||||
|
n->start = g_get_monotonic_time();
|
||||||
|
|
||||||
|
n->dup_count = orig->dup_count;
|
||||||
|
|
||||||
|
notification_closed(orig, 1);
|
||||||
|
|
||||||
|
notification_free(orig);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (GList *iter = g_queue_peek_head_link(waiting); iter;
|
||||||
|
iter = iter->next) {
|
||||||
|
notification *orig = iter->data;
|
||||||
|
if (notification_is_duplicate(orig, n)) {
|
||||||
|
/* If the progress differs, probably notify-send was used to update the notification
|
||||||
|
* So only count it as a duplicate, if the progress was not the same.
|
||||||
|
* */
|
||||||
|
if (orig->progress == n->progress) {
|
||||||
|
orig->dup_count++;
|
||||||
|
} else {
|
||||||
|
orig->progress = n->progress;
|
||||||
|
}
|
||||||
|
iter->data = n;
|
||||||
|
|
||||||
|
n->dup_count = orig->dup_count;
|
||||||
|
|
||||||
|
notification_closed(orig, 1);
|
||||||
|
|
||||||
|
notification_free(orig);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
bool queues_notification_replace_id(notification *new)
|
||||||
* Replaces the first notification of the same stack_tag
|
|
||||||
*
|
|
||||||
* @retval true: notification got stacked
|
|
||||||
* @retval false: notification did not get stacked
|
|
||||||
*/
|
|
||||||
static bool queues_stack_by_tag(struct notification *new)
|
|
||||||
{
|
{
|
||||||
GQueue *allqueues[] = { displayed, waiting };
|
|
||||||
for (int i = 0; i < sizeof(allqueues)/sizeof(GQueue*); i++) {
|
|
||||||
for (GList *iter = g_queue_peek_head_link(allqueues[i]); iter;
|
|
||||||
iter = iter->next) {
|
|
||||||
struct notification *old = iter->data;
|
|
||||||
if (STR_FULL(old->stack_tag) && STR_EQ(old->stack_tag, new->stack_tag)) {
|
|
||||||
iter->data = new;
|
|
||||||
new->dup_count = old->dup_count;
|
|
||||||
|
|
||||||
signal_notification_closed(old, 1);
|
for (GList *iter = g_queue_peek_head_link(displayed);
|
||||||
|
iter;
|
||||||
|
iter = iter->next) {
|
||||||
|
notification *old = iter->data;
|
||||||
|
if (old->id == new->id) {
|
||||||
|
iter->data = new;
|
||||||
|
new->start = g_get_monotonic_time();
|
||||||
|
new->dup_count = old->dup_count;
|
||||||
|
notification_run_script(new);
|
||||||
|
notification_free(old);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (allqueues[i] == displayed) {
|
for (GList *iter = g_queue_peek_head_link(waiting);
|
||||||
new->start = time_monotonic_now();
|
iter;
|
||||||
notification_run_script(new);
|
iter = iter->next) {
|
||||||
}
|
notification *old = iter->data;
|
||||||
|
if (old->id == new->id) {
|
||||||
notification_unref(old);
|
iter->data = new;
|
||||||
return true;
|
new->dup_count = old->dup_count;
|
||||||
}
|
notification_free(old);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see queues.h */
|
int queues_notification_close_id(int id, enum reason reason)
|
||||||
bool queues_notification_replace_id(struct notification *new)
|
|
||||||
{
|
{
|
||||||
GQueue *allqueues[] = { displayed, waiting };
|
notification *target = NULL;
|
||||||
for (int i = 0; i < sizeof(allqueues)/sizeof(GQueue*); i++) {
|
|
||||||
for (GList *iter = g_queue_peek_head_link(allqueues[i]);
|
|
||||||
iter;
|
|
||||||
iter = iter->next) {
|
|
||||||
struct notification *old = iter->data;
|
|
||||||
if (old->id == new->id) {
|
|
||||||
iter->data = new;
|
|
||||||
new->dup_count = old->dup_count;
|
|
||||||
|
|
||||||
if (allqueues[i] == displayed) {
|
for (GList *iter = g_queue_peek_head_link(displayed); iter;
|
||||||
new->start = time_monotonic_now();
|
iter = iter->next) {
|
||||||
notification_run_script(new);
|
notification *n = iter->data;
|
||||||
}
|
if (n->id == id) {
|
||||||
|
g_queue_remove(displayed, n);
|
||||||
notification_unref(old);
|
target = n;
|
||||||
return true;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* see queues.h */
|
for (GList *iter = g_queue_peek_head_link(waiting); iter;
|
||||||
void queues_notification_close_id(int id, enum reason reason)
|
iter = iter->next) {
|
||||||
{
|
notification *n = iter->data;
|
||||||
struct notification *target = NULL;
|
if (n->id == id) {
|
||||||
|
assert(target == NULL);
|
||||||
GQueue *allqueues[] = { displayed, waiting };
|
g_queue_remove(waiting, n);
|
||||||
for (int i = 0; i < sizeof(allqueues)/sizeof(GQueue*); i++) {
|
target = n;
|
||||||
for (GList *iter = g_queue_peek_head_link(allqueues[i]); iter;
|
break;
|
||||||
iter = iter->next) {
|
|
||||||
struct notification *n = iter->data;
|
|
||||||
if (n->id == id) {
|
|
||||||
g_queue_remove(allqueues[i], n);
|
|
||||||
target = n;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target) {
|
if (target) {
|
||||||
//Don't notify clients if notification was pulled from history
|
notification_closed(target, reason);
|
||||||
if (!target->redisplayed)
|
|
||||||
signal_notification_closed(target, reason);
|
|
||||||
queues_history_push(target);
|
queues_history_push(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see queues.h */
|
int queues_notification_close(notification *n, enum reason reason)
|
||||||
void queues_notification_close(struct notification *n, enum reason reason)
|
|
||||||
{
|
{
|
||||||
assert(n != NULL);
|
assert(n != NULL);
|
||||||
queues_notification_close_id(n->id, reason);
|
return queues_notification_close_id(n->id, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see queues.h */
|
|
||||||
void queues_history_pop(void)
|
void queues_history_pop(void)
|
||||||
{
|
{
|
||||||
if (g_queue_is_empty(history))
|
if (g_queue_is_empty(history))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
struct notification *n = g_queue_pop_tail(history);
|
notification *n = g_queue_pop_tail(history);
|
||||||
n->redisplayed = true;
|
n->redisplayed = true;
|
||||||
|
n->start = 0;
|
||||||
n->timeout = settings.sticky_history ? 0 : n->timeout;
|
n->timeout = settings.sticky_history ? 0 : n->timeout;
|
||||||
g_queue_insert_sorted(waiting, n, notification_cmp_data, NULL);
|
g_queue_push_head(waiting, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see queues.h */
|
void queues_history_push(notification *n)
|
||||||
void queues_history_push(struct notification *n)
|
|
||||||
{
|
{
|
||||||
if (!n->history_ignore) {
|
if (!n->history_ignore) {
|
||||||
if (settings.history_length > 0 && history->length >= settings.history_length) {
|
if (settings.history_length > 0 && history->length >= settings.history_length) {
|
||||||
struct notification *to_free = g_queue_pop_head(history);
|
notification *to_free = g_queue_pop_head(history);
|
||||||
notification_unref(to_free);
|
notification_free(to_free);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_queue_push_tail(history, n);
|
g_queue_push_tail(history, n);
|
||||||
} else {
|
} else {
|
||||||
notification_unref(n);
|
notification_free(n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see queues.h */
|
|
||||||
void queues_history_push_all(void)
|
void queues_history_push_all(void)
|
||||||
{
|
{
|
||||||
while (displayed->length > 0) {
|
while (displayed->length > 0) {
|
||||||
@ -364,125 +255,81 @@ void queues_history_push_all(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see queues.h */
|
void queues_check_timeouts(bool idle)
|
||||||
void queues_update(struct dunst_status status)
|
|
||||||
{
|
{
|
||||||
GList *iter, *nextiter;
|
/* nothing to do */
|
||||||
|
if (displayed->length == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
/* Move back all notifications, which aren't eligible to get shown anymore
|
GList *iter = g_queue_peek_head_link(displayed);
|
||||||
* Will move the notifications back to waiting, if dunst isn't running or fullscreen
|
|
||||||
* and notifications is not eligible to get shown anymore */
|
|
||||||
iter = g_queue_peek_head_link(displayed);
|
|
||||||
while (iter) {
|
while (iter) {
|
||||||
struct notification *n = iter->data;
|
notification *n = iter->data;
|
||||||
nextiter = iter->next;
|
|
||||||
|
|
||||||
if (notification_is_locked(n)) {
|
/*
|
||||||
iter = nextiter;
|
* Update iter to the next item before we either exit the
|
||||||
|
* current iteration of the loop or potentially delete the
|
||||||
|
* notification which would invalidate the pointer.
|
||||||
|
*/
|
||||||
|
iter = iter->next;
|
||||||
|
|
||||||
|
/* don't timeout when user is idle */
|
||||||
|
if (idle && !n->transient) {
|
||||||
|
n->start = g_get_monotonic_time();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n->marked_for_closure) {
|
/* skip hidden and sticky messages */
|
||||||
queues_notification_close(n, n->marked_for_closure);
|
if (n->start == 0 || n->timeout == 0) {
|
||||||
n->marked_for_closure = 0;
|
|
||||||
iter = nextiter;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* remove old message */
|
||||||
if (queues_notification_is_finished(n, status)){
|
if (g_get_monotonic_time() - n->start > n->timeout) {
|
||||||
queues_notification_close(n, REASON_TIME);
|
queues_notification_close(n, REASON_TIME);
|
||||||
iter = nextiter;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!queues_notification_is_ready(n, status, true)) {
|
|
||||||
g_queue_delete_link(displayed, iter);
|
|
||||||
g_queue_insert_sorted(waiting, n, notification_cmp_data, NULL);
|
|
||||||
iter = nextiter;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
iter = nextiter;
|
|
||||||
}
|
|
||||||
|
|
||||||
int cur_displayed_limit;
|
|
||||||
if (settings.geometry.h == 0)
|
|
||||||
cur_displayed_limit = INT_MAX;
|
|
||||||
else if ( settings.indicate_hidden
|
|
||||||
&& settings.geometry.h > 1
|
|
||||||
&& displayed->length + waiting->length > settings.geometry.h)
|
|
||||||
cur_displayed_limit = settings.geometry.h-1;
|
|
||||||
else
|
|
||||||
cur_displayed_limit = settings.geometry.h;
|
|
||||||
|
|
||||||
/* move notifications from queue to displayed */
|
|
||||||
iter = g_queue_peek_head_link(waiting);
|
|
||||||
while (displayed->length < cur_displayed_limit && iter) {
|
|
||||||
struct notification *n = iter->data;
|
|
||||||
nextiter = iter->next;
|
|
||||||
|
|
||||||
ASSERT_OR_RET(n,);
|
|
||||||
|
|
||||||
if (!queues_notification_is_ready(n, status, false)) {
|
|
||||||
iter = nextiter;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
n->start = time_monotonic_now();
|
|
||||||
notification_run_script(n);
|
|
||||||
|
|
||||||
if (n->skip_display && !n->redisplayed) {
|
|
||||||
queues_notification_close(n, REASON_USER);
|
|
||||||
} else {
|
|
||||||
g_queue_delete_link(waiting, iter);
|
|
||||||
g_queue_insert_sorted(displayed, n, notification_cmp_data, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
iter = nextiter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if necessary, push the overhanging notifications from displayed to waiting again */
|
|
||||||
while (displayed->length > cur_displayed_limit) {
|
|
||||||
struct notification *n = g_queue_pop_tail(displayed);
|
|
||||||
g_queue_insert_sorted(waiting, n, notification_cmp_data, NULL); //TODO: actually it should be on the head if unsorted
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If displayed is actually full, let the more important notifications
|
|
||||||
* from waiting seep into displayed.
|
|
||||||
*/
|
|
||||||
if (settings.sort && displayed->length == cur_displayed_limit) {
|
|
||||||
GList *i_waiting, *i_displayed;
|
|
||||||
|
|
||||||
while ( (i_waiting = g_queue_peek_head_link(waiting))
|
|
||||||
&& (i_displayed = g_queue_peek_tail_link(displayed))) {
|
|
||||||
|
|
||||||
while (i_waiting && ! queues_notification_is_ready(i_waiting->data, status, false)) {
|
|
||||||
i_waiting = i_waiting->prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i_waiting && notification_cmp(i_displayed->data, i_waiting->data) > 0) {
|
|
||||||
struct notification *todisp = i_waiting->data;
|
|
||||||
|
|
||||||
todisp->start = time_monotonic_now();
|
|
||||||
notification_run_script(todisp);
|
|
||||||
|
|
||||||
queues_swap_notifications(displayed, i_displayed, waiting, i_waiting);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see queues.h */
|
void queues_update()
|
||||||
|
{
|
||||||
|
if (pause_displayed) {
|
||||||
|
while (displayed->length > 0) {
|
||||||
|
g_queue_insert_sorted(
|
||||||
|
waiting, g_queue_pop_head(displayed), notification_cmp_data, NULL);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* move notifications from queue to displayed */
|
||||||
|
while (waiting->length > 0) {
|
||||||
|
|
||||||
|
if (displayed_limit > 0 && displayed->length >= displayed_limit) {
|
||||||
|
/* the list is full */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
notification *n = g_queue_pop_head(waiting);
|
||||||
|
|
||||||
|
if (!n)
|
||||||
|
return;
|
||||||
|
|
||||||
|
n->start = g_get_monotonic_time();
|
||||||
|
|
||||||
|
if (!n->redisplayed && n->script) {
|
||||||
|
notification_run_script(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_queue_insert_sorted(displayed, n, notification_cmp_data, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gint64 queues_get_next_datachange(gint64 time)
|
gint64 queues_get_next_datachange(gint64 time)
|
||||||
{
|
{
|
||||||
gint64 sleep = G_MAXINT64;
|
gint64 sleep = G_MAXINT64;
|
||||||
|
|
||||||
for (GList *iter = g_queue_peek_head_link(displayed); iter;
|
for (GList *iter = g_queue_peek_head_link(displayed); iter;
|
||||||
iter = iter->next) {
|
iter = iter->next) {
|
||||||
struct notification *n = iter->data;
|
notification *n = iter->data;
|
||||||
gint64 ttl = n->timeout - (time - n->start);
|
gint64 ttl = n->timeout - (time - n->start);
|
||||||
|
|
||||||
if (n->timeout > 0) {
|
if (n->timeout > 0) {
|
||||||
@ -496,59 +343,42 @@ gint64 queues_get_next_datachange(gint64 time)
|
|||||||
if (settings.show_age_threshold >= 0) {
|
if (settings.show_age_threshold >= 0) {
|
||||||
gint64 age = time - n->timestamp;
|
gint64 age = time - n->timestamp;
|
||||||
|
|
||||||
// sleep exactly until the next shift of the second happens
|
if (age > settings.show_age_threshold)
|
||||||
if (age > settings.show_age_threshold - S2US(1))
|
// sleep exactly until the next shift of the second happens
|
||||||
sleep = MIN(sleep, (S2US(1) - (age % S2US(1))));
|
sleep = MIN(sleep, ((G_USEC_PER_SEC) - (age % (G_USEC_PER_SEC))));
|
||||||
else
|
else if (ttl > settings.show_age_threshold)
|
||||||
sleep = MIN(sleep, settings.show_age_threshold - age);
|
sleep = MIN(sleep, settings.show_age_threshold);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sleep != G_MAXINT64 ? sleep : -1;
|
return sleep != G_MAXINT64 ? sleep : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void queues_pause_on(void)
|
||||||
|
|
||||||
|
|
||||||
/* see queues.h */
|
|
||||||
struct notification* queues_get_by_id(int id)
|
|
||||||
{
|
{
|
||||||
assert(id > 0);
|
pause_displayed = true;
|
||||||
|
}
|
||||||
GQueue *recqueues[] = { displayed, waiting, history };
|
|
||||||
for (int i = 0; i < sizeof(recqueues)/sizeof(GQueue*); i++) {
|
void queues_pause_off(void)
|
||||||
for (GList *iter = g_queue_peek_head_link(recqueues[i]); iter;
|
{
|
||||||
iter = iter->next) {
|
pause_displayed = false;
|
||||||
struct notification *cur = iter->data;
|
}
|
||||||
if (cur->id == id)
|
|
||||||
return cur;
|
bool queues_pause_status(void)
|
||||||
}
|
{
|
||||||
}
|
return pause_displayed;
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function for queues_teardown() to free a single notification
|
|
||||||
*
|
|
||||||
* @param data The notification to free
|
|
||||||
*/
|
|
||||||
static void teardown_notification(gpointer data)
|
static void teardown_notification(gpointer data)
|
||||||
{
|
{
|
||||||
struct notification *n = data;
|
notification *n = data;
|
||||||
notification_unref(n);
|
notification_free(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see queues.h */
|
void teardown_queues(void)
|
||||||
void queues_teardown(void)
|
|
||||||
{
|
{
|
||||||
g_queue_free_full(history, teardown_notification);
|
g_queue_free_full(history, teardown_notification);
|
||||||
history = NULL;
|
|
||||||
g_queue_free_full(displayed, teardown_notification);
|
g_queue_free_full(displayed, teardown_notification);
|
||||||
displayed = NULL;
|
|
||||||
g_queue_free_full(waiting, teardown_notification);
|
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: */
|
|
||||||
|
|||||||
168
src/queues.h
@ -1,167 +1,133 @@
|
|||||||
/* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
|
/* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
|
||||||
|
|
||||||
/**
|
|
||||||
* @file src/queues.h
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef DUNST_QUEUE_H
|
#ifndef DUNST_QUEUE_H
|
||||||
#define DUNST_QUEUE_H
|
#define DUNST_QUEUE_H
|
||||||
|
|
||||||
#include "dbus.h"
|
#include "dbus.h"
|
||||||
#include "dunst.h"
|
|
||||||
#include "notification.h"
|
#include "notification.h"
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Initialise necessary queues
|
* Initialise neccessary queues
|
||||||
*
|
|
||||||
* @pre Do not call consecutively to avoid memory leaks
|
|
||||||
* or assure to have queues_teardown() executed before
|
|
||||||
*/
|
*/
|
||||||
void queues_init(void);
|
void queues_init(void);
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Receive the current list of displayed notifications
|
* Set maximum notification count to display
|
||||||
*
|
* and store in displayed queue
|
||||||
* @return read only list of notifications
|
|
||||||
*/
|
*/
|
||||||
GList *queues_get_displayed(void);
|
void queues_displayed_limit(unsigned int limit);
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Get the highest notification in line
|
* Return read only list of notifications
|
||||||
*
|
|
||||||
* @returns the first notification in waiting
|
|
||||||
* @retval NULL: waiting is empty
|
|
||||||
*/
|
*/
|
||||||
const struct notification *queues_get_head_waiting(void);
|
const GList *queues_get_displayed();
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Returns the current amount of notifications,
|
* Returns the current amount of notifications,
|
||||||
* which are waiting to get displayed
|
* which are shown, waiting or already in history
|
||||||
*/
|
*/
|
||||||
unsigned int queues_length_waiting(void);
|
unsigned int queues_length_waiting();
|
||||||
|
unsigned int queues_length_displayed();
|
||||||
|
unsigned int queues_length_history();
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Returns the current amount of notifications,
|
|
||||||
* which are shown in the UI
|
|
||||||
*/
|
|
||||||
unsigned int queues_length_displayed(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current amount of notifications,
|
|
||||||
* which are already in history
|
|
||||||
*/
|
|
||||||
unsigned int queues_length_history(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Insert a fully initialized notification into queues
|
* Insert a fully initialized notification into queues
|
||||||
*
|
|
||||||
* Respects stack_duplicates, and notification replacement
|
* Respects stack_duplicates, and notification replacement
|
||||||
*
|
*
|
||||||
* @param n the notification to insert
|
* If replaces_id != 0, n replaces notification with id replaces_id
|
||||||
|
* If replaces_id == 0, n gets occupies a new position
|
||||||
*
|
*
|
||||||
* - If n->id != 0, n replaces notification n with id n->id
|
* Returns the assigned notification id
|
||||||
* - If n->id == 0, n gets a new id assigned
|
* If returned id == 0, the message was dismissed
|
||||||
*
|
|
||||||
* @returns The new value of `n->id`
|
|
||||||
* @retval 0: the notification was dismissed and freed
|
|
||||||
*/
|
*/
|
||||||
int queues_notification_insert(struct notification *n);
|
int queues_notification_insert(notification *n, int replaces_id);
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Replace the notification which matches the id field of
|
* Replace the notification which matches the id field of
|
||||||
* the new notification. The given notification is inserted
|
* the new notification. The given notification is inserted
|
||||||
* right in the same position as the old notification.
|
* right in the same position as the old notification.
|
||||||
*
|
*
|
||||||
* @param new replacement for the old notification
|
* Returns true, if a matching notification has been found
|
||||||
*
|
* and is replaced. Else false.
|
||||||
* @retval true: a matching notification has been found and is replaced
|
|
||||||
* @retval false: otherwise
|
|
||||||
*/
|
*/
|
||||||
bool queues_notification_replace_id(struct notification *new);
|
bool queues_notification_replace_id(notification *new);
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Close the notification that has n->id == id
|
* Close the notification that has n->id == id
|
||||||
*
|
*
|
||||||
* Sends a signal and pushes the selected notification automatically to history.
|
* Sends a signal and pushes it automatically to history.
|
||||||
*
|
*
|
||||||
* @param id The id of the notification to close
|
* After closing, call wake_up to synchronize the queues with the UI
|
||||||
* @param reason The #reason to close
|
* (which closes the notification on screen)
|
||||||
*
|
|
||||||
* @post Call wake_up() to synchronize the queues with the UI
|
|
||||||
* (which closes the notification on screen)
|
|
||||||
*/
|
*/
|
||||||
void queues_notification_close_id(int id, enum reason reason);
|
int queues_notification_close_id(int id, enum reason reason);
|
||||||
|
|
||||||
/**
|
/* Close the given notification. SEE queues_notification_close_id.
|
||||||
* Close the given notification. \see queues_notification_close_id().
|
|
||||||
*
|
*
|
||||||
* @param n (transfer full) The notification to close
|
* @n: (transfer full): The notification to close
|
||||||
* @param reason The #reason to close
|
* @reason: The reason to close
|
||||||
* */
|
* */
|
||||||
void queues_notification_close(struct notification *n, enum reason reason);
|
int queues_notification_close(notification *n, enum reason reason);
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Pushes the latest notification of history to the displayed queue
|
* Pushed the latest notification of history to the displayed queue
|
||||||
* and removes it from history
|
* and removes it from history
|
||||||
*/
|
*/
|
||||||
void queues_history_pop(void);
|
void queues_history_pop(void);
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Push a single notification to history
|
* Push a single notification to history
|
||||||
* The given notification has to be removed its queue
|
* The given notification has to be removed its queue
|
||||||
*
|
*
|
||||||
* @param n (transfer full) The notification to push to history
|
* @n: (transfer full): The notification to push to history
|
||||||
*/
|
*/
|
||||||
void queues_history_push(struct notification *n);
|
void queues_history_push(notification *n);
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Push all waiting and displayed notifications to history
|
* Push all waiting and displayed notifications to history
|
||||||
*/
|
*/
|
||||||
void queues_history_push_all(void);
|
void queues_history_push_all(void);
|
||||||
|
|
||||||
/**
|
/*
|
||||||
|
* Check timeout of each notification and close it, if neccessary
|
||||||
|
*/
|
||||||
|
void queues_check_timeouts(bool idle);
|
||||||
|
|
||||||
|
/*
|
||||||
* Move inserted notifications from waiting queue to displayed queue
|
* Move inserted notifications from waiting queue to displayed queue
|
||||||
* and show them. In displayed queue, the amount of elements is limited
|
* and show them. In displayed queue, the amount of elements is limited
|
||||||
* to the amount set via queues_displayed_limit()
|
* to the amount set via queues_displayed_limit
|
||||||
*
|
|
||||||
* @post Call wake_up() to synchronize the queues with the UI
|
|
||||||
* (which closes old and shows new notifications on screen)
|
|
||||||
*
|
|
||||||
* @param status the current status of dunst
|
|
||||||
*/
|
*/
|
||||||
void queues_update(struct dunst_status status);
|
void queues_update();
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Calculate the distance to the next event, when an element in the
|
* Return the distance to the next event in the queue,
|
||||||
* queues changes
|
* which forces an update visible to the user
|
||||||
*
|
*
|
||||||
* @param time the current time
|
* This may be:
|
||||||
*
|
*
|
||||||
* @return the distance to the next event in the queue, which forces
|
* - notification hits timeout
|
||||||
* an update visible to the user. This may be:
|
* - notification's age second changes
|
||||||
* - notification hits timeout
|
* - notification's age threshold is hit
|
||||||
* - notification's age second changes
|
|
||||||
* - notification's age threshold is hit
|
|
||||||
*/
|
*/
|
||||||
gint64 queues_get_next_datachange(gint64 time);
|
gint64 queues_get_next_datachange(gint64 time);
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Get the notification which has the given id in the displayed and waiting queue or
|
* Pause queue-management of dunst
|
||||||
* NULL if not found
|
* pause_on = paused (no notifications displayed)
|
||||||
|
* pause_off = running
|
||||||
*
|
*
|
||||||
* @param id the id searched for.
|
* Calling update_lists is neccessary
|
||||||
*
|
|
||||||
* @return the `id` notification or NULL
|
|
||||||
*/
|
*/
|
||||||
struct notification* queues_get_by_id(int id);
|
void queues_pause_on(void);
|
||||||
|
void queues_pause_off(void);
|
||||||
|
bool queues_pause_status(void);
|
||||||
|
|
||||||
|
/*
|
||||||
/**
|
* Remove all notifications from all lists
|
||||||
* Remove all notifications from all list and free the notifications
|
* and free the notifications
|
||||||
*
|
|
||||||
* @pre At least one time queues_init() called
|
|
||||||
*/
|
*/
|
||||||
void queues_teardown(void);
|
void teardown_queues(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
|||||||
100
src/rules.c
@ -7,111 +7,85 @@
|
|||||||
|
|
||||||
#include "dunst.h"
|
#include "dunst.h"
|
||||||
|
|
||||||
GSList *rules = NULL;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Apply rule to notification.
|
* Apply rule to notification.
|
||||||
*/
|
*/
|
||||||
void rule_apply(struct rule *r, struct notification *n)
|
void rule_apply(rule_t *r, notification *n)
|
||||||
{
|
{
|
||||||
if (r->timeout != -1)
|
if (r->timeout != -1)
|
||||||
n->timeout = r->timeout;
|
n->timeout = r->timeout;
|
||||||
if (r->urgency != URG_NONE)
|
if (r->urgency != URG_NONE)
|
||||||
n->urgency = r->urgency;
|
n->urgency = r->urgency;
|
||||||
if (r->fullscreen != FS_NULL)
|
|
||||||
n->fullscreen = r->fullscreen;
|
|
||||||
if (r->history_ignore != -1)
|
if (r->history_ignore != -1)
|
||||||
n->history_ignore = r->history_ignore;
|
n->history_ignore = r->history_ignore;
|
||||||
if (r->set_transient != -1)
|
if (r->set_transient != -1)
|
||||||
n->transient = r->set_transient;
|
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)
|
if (r->markup != MARKUP_NULL)
|
||||||
n->markup = r->markup;
|
n->markup = r->markup;
|
||||||
if (r->new_icon)
|
if (r->new_icon) {
|
||||||
notification_icon_replace_path(n, r->new_icon);
|
g_free(n->icon);
|
||||||
if (r->fg) {
|
n->icon = g_strdup(r->new_icon);
|
||||||
g_free(n->colors.fg);
|
rawimage_free(n->raw_icon);
|
||||||
n->colors.fg = g_strdup(r->fg);
|
n->raw_icon = NULL;
|
||||||
}
|
|
||||||
if (r->bg) {
|
|
||||||
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->fg)
|
||||||
|
n->color_strings[ColFG] = r->fg;
|
||||||
|
if (r->bg)
|
||||||
|
n->color_strings[ColBG] = r->bg;
|
||||||
if (r->format)
|
if (r->format)
|
||||||
n->format = r->format;
|
n->format = r->format;
|
||||||
if (r->script){
|
if (r->script)
|
||||||
n->scripts = g_renew(const char*,n->scripts,n->script_count + 1);
|
n->script = r->script;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check all rules if they match n and apply.
|
* Check all rules if they match n and apply.
|
||||||
*/
|
*/
|
||||||
void rule_apply_all(struct notification *n)
|
void rule_apply_all(notification *n)
|
||||||
{
|
{
|
||||||
for (GSList *iter = rules; iter; iter = iter->next) {
|
for (GSList *iter = rules; iter; iter = iter->next) {
|
||||||
struct rule *r = iter->data;
|
rule_t *r = iter->data;
|
||||||
if (rule_matches_notification(r, n)) {
|
if (rule_matches_notification(r, n)) {
|
||||||
rule_apply(r, n);
|
rule_apply(r, n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct rule *rule_new(void)
|
/*
|
||||||
|
* Initialize rule with default values.
|
||||||
|
*/
|
||||||
|
void rule_init(rule_t *r)
|
||||||
{
|
{
|
||||||
struct rule *r = g_malloc0(sizeof(struct rule));
|
r->name = NULL;
|
||||||
|
r->appname = NULL;
|
||||||
|
r->summary = NULL;
|
||||||
|
r->body = NULL;
|
||||||
|
r->icon = NULL;
|
||||||
|
r->category = NULL;
|
||||||
r->msg_urgency = URG_NONE;
|
r->msg_urgency = URG_NONE;
|
||||||
r->timeout = -1;
|
r->timeout = -1;
|
||||||
r->urgency = URG_NONE;
|
r->urgency = URG_NONE;
|
||||||
r->fullscreen = FS_NULL;
|
|
||||||
r->markup = MARKUP_NULL;
|
r->markup = MARKUP_NULL;
|
||||||
r->history_ignore = -1;
|
r->new_icon = NULL;
|
||||||
|
r->history_ignore = false;
|
||||||
r->match_transient = -1;
|
r->match_transient = -1;
|
||||||
r->set_transient = -1;
|
r->set_transient = -1;
|
||||||
r->skip_display = -1;
|
r->fg = NULL;
|
||||||
|
r->bg = NULL;
|
||||||
return r;
|
r->format = NULL;
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool rule_field_matches_string(const char *value, const char *pattern)
|
|
||||||
{
|
|
||||||
return !pattern || (value && !fnmatch(pattern, value, 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check whether rule should be applied to n.
|
* Check whether rule should be applied to n.
|
||||||
*/
|
*/
|
||||||
bool rule_matches_notification(struct rule *r, struct notification *n)
|
bool rule_matches_notification(rule_t *r, notification *n)
|
||||||
{
|
{
|
||||||
return (r->msg_urgency == URG_NONE || r->msg_urgency == n->urgency)
|
return ((!r->appname || !fnmatch(r->appname, n->appname, 0))
|
||||||
|
&& (!r->summary || !fnmatch(r->summary, n->summary, 0))
|
||||||
|
&& (!r->body || !fnmatch(r->body, n->body, 0))
|
||||||
|
&& (!r->icon || !fnmatch(r->icon, n->icon, 0))
|
||||||
|
&& (!r->category || !fnmatch(r->category, n->category, 0))
|
||||||
&& (r->match_transient == -1 || (r->match_transient == n->transient))
|
&& (r->match_transient == -1 || (r->match_transient == n->transient))
|
||||||
&& rule_field_matches_string(n->appname, r->appname)
|
&& (r->msg_urgency == URG_NONE || r->msg_urgency == n->urgency));
|
||||||
&& rule_field_matches_string(n->desktop_entry, r->desktop_entry)
|
|
||||||
&& rule_field_matches_string(n->summary, r->summary)
|
|
||||||
&& rule_field_matches_string(n->body, r->body)
|
|
||||||
&& rule_field_matches_string(n->iconname, r->icon)
|
|
||||||
&& rule_field_matches_string(n->category, r->category)
|
|
||||||
&& rule_field_matches_string(n->stack_tag, r->stack_tag);
|
|
||||||
}
|
}
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
|||||||
28
src/rules.h
@ -8,7 +8,7 @@
|
|||||||
#include "notification.h"
|
#include "notification.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
struct rule {
|
typedef struct _rule_t {
|
||||||
char *name;
|
char *name;
|
||||||
/* filters */
|
/* filters */
|
||||||
char *appname;
|
char *appname;
|
||||||
@ -16,42 +16,28 @@ struct rule {
|
|||||||
char *body;
|
char *body;
|
||||||
char *icon;
|
char *icon;
|
||||||
char *category;
|
char *category;
|
||||||
char *stack_tag;
|
|
||||||
char *desktop_entry;
|
|
||||||
int msg_urgency;
|
int msg_urgency;
|
||||||
|
|
||||||
/* actions */
|
/* actions */
|
||||||
gint64 timeout;
|
gint64 timeout;
|
||||||
enum urgency urgency;
|
enum urgency urgency;
|
||||||
char *action_name;
|
|
||||||
enum markup_mode markup;
|
enum markup_mode markup;
|
||||||
int history_ignore;
|
int history_ignore;
|
||||||
int match_transient;
|
int match_transient;
|
||||||
int set_transient;
|
int set_transient;
|
||||||
int skip_display;
|
|
||||||
char *new_icon;
|
char *new_icon;
|
||||||
char *fg;
|
char *fg;
|
||||||
char *bg;
|
char *bg;
|
||||||
char *highlight;
|
|
||||||
char *fc;
|
|
||||||
const char *format;
|
const char *format;
|
||||||
const char *script;
|
const char *script;
|
||||||
enum behavior_fullscreen fullscreen;
|
} rule_t;
|
||||||
char *set_stack_tag;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern GSList *rules;
|
extern GSList *rules;
|
||||||
|
|
||||||
/**
|
void rule_init(rule_t *r);
|
||||||
* Allocate a new rule. The rule is fully initialised.
|
void rule_apply(rule_t *r, notification *n);
|
||||||
*
|
void rule_apply_all(notification *n);
|
||||||
* @returns A new initialised rule.
|
bool rule_matches_notification(rule_t *r, notification *n);
|
||||||
*/
|
|
||||||
struct rule *rule_new(void);
|
|
||||||
|
|
||||||
void rule_apply(struct rule *r, struct notification *n);
|
|
||||||
void rule_apply_all(struct notification *n);
|
|
||||||
bool rule_matches_notification(struct rule *r, struct notification *n);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
|||||||
583
src/settings.c
@ -5,112 +5,100 @@
|
|||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#ifndef STATIC_CONFIG
|
||||||
|
#include <basedir.h>
|
||||||
|
#include <basedir_fs.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "rules.h" // put before config.h to fix missing include
|
||||||
|
#include "config.h"
|
||||||
#include "dunst.h"
|
#include "dunst.h"
|
||||||
#include "log.h"
|
|
||||||
#include "notification.h"
|
#include "notification.h"
|
||||||
#include "option_parser.h"
|
#include "option_parser.h"
|
||||||
#include "rules.h"
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "x11/x.h"
|
|
||||||
#include "output.h"
|
|
||||||
|
|
||||||
#include "../config.h"
|
settings_t settings;
|
||||||
|
|
||||||
struct settings settings;
|
static void parse_follow_mode(const char *mode)
|
||||||
|
|
||||||
static enum urgency ini_get_urgency(const char *section, const char *key, const enum urgency def)
|
|
||||||
{
|
{
|
||||||
enum urgency ret;
|
if (strcmp(mode, "mouse") == 0)
|
||||||
char *c = ini_get_string(section, key, NULL);
|
settings.f_mode = FOLLOW_MOUSE;
|
||||||
|
else if (strcmp(mode, "keyboard") == 0)
|
||||||
if (!string_parse_urgency(c, &ret)) {
|
settings.f_mode = FOLLOW_KEYBOARD;
|
||||||
if (c)
|
else if (strcmp(mode, "none") == 0)
|
||||||
LOG_W("Unknown urgency: '%s'", c);
|
settings.f_mode = FOLLOW_NONE;
|
||||||
ret = def;
|
else {
|
||||||
|
fprintf(stderr, "Warning: unknown follow mode: \"%s\"\n", mode);
|
||||||
|
settings.f_mode = FOLLOW_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free(c);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static FILE *xdg_config(const char *filename)
|
static enum markup_mode parse_markup_mode(const char *mode)
|
||||||
{
|
{
|
||||||
const gchar * const * systemdirs = g_get_system_config_dirs();
|
if (strcmp(mode, "strip") == 0) {
|
||||||
const gchar * userdir = g_get_user_config_dir();
|
return MARKUP_STRIP;
|
||||||
|
} else if (strcmp(mode, "no") == 0) {
|
||||||
FILE *f;
|
return MARKUP_NO;
|
||||||
char *path;
|
} else if (strcmp(mode, "full") == 0 || strcmp(mode, "yes") == 0) {
|
||||||
|
return MARKUP_FULL;
|
||||||
path = g_strconcat(userdir, filename, NULL);
|
} else {
|
||||||
f = fopen(path, "r");
|
fprintf(stderr, "Warning: unknown markup mode: \"%s\"\n", mode);
|
||||||
g_free(path);
|
return MARKUP_NO;
|
||||||
|
|
||||||
for (const gchar * const *d = systemdirs;
|
|
||||||
!f && *d;
|
|
||||||
d++) {
|
|
||||||
path = g_strconcat(*d, filename, NULL);
|
|
||||||
f = fopen(path, "r");
|
|
||||||
g_free(path);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!f) {
|
static enum urgency ini_get_urgency(const char *section, const char *key, const int def)
|
||||||
f = fopen("/etc/dunst/dunstrc", "r");
|
{
|
||||||
|
int ret = def;
|
||||||
|
char *urg = ini_get_string(section, key, "");
|
||||||
|
|
||||||
|
if (strlen(urg) > 0) {
|
||||||
|
if (strcmp(urg, "low") == 0)
|
||||||
|
ret = URG_LOW;
|
||||||
|
else if (strcmp(urg, "normal") == 0)
|
||||||
|
ret = URG_NORM;
|
||||||
|
else if (strcmp(urg, "critical") == 0)
|
||||||
|
ret = URG_CRIT;
|
||||||
|
else
|
||||||
|
fprintf(stderr,
|
||||||
|
"unknown urgency: %s, ignoring\n",
|
||||||
|
urg);
|
||||||
}
|
}
|
||||||
|
g_free(urg);
|
||||||
return f;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void load_settings(char *cmdline_config_path)
|
void load_settings(char *cmdline_config_path)
|
||||||
{
|
{
|
||||||
|
|
||||||
#ifndef STATIC_CONFIG
|
#ifndef STATIC_CONFIG
|
||||||
|
xdgHandle xdg;
|
||||||
FILE *config_file = NULL;
|
FILE *config_file = NULL;
|
||||||
|
|
||||||
if (cmdline_config_path) {
|
xdgInitHandle(&xdg);
|
||||||
if (STR_EQ(cmdline_config_path, "-")) {
|
|
||||||
config_file = stdin;
|
|
||||||
} else {
|
|
||||||
config_file = fopen(cmdline_config_path, "r");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!config_file) {
|
if (cmdline_config_path != NULL) {
|
||||||
DIE("Cannot find config file: '%s'", cmdline_config_path);
|
config_file = fopen(cmdline_config_path, "r");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (config_file == NULL) {
|
||||||
if (!config_file) {
|
config_file = xdgConfigOpen("dunst/dunstrc", "r", &xdg);
|
||||||
config_file = xdg_config("/dunst/dunstrc");
|
|
||||||
}
|
}
|
||||||
|
if (config_file == NULL) {
|
||||||
if (!config_file) {
|
|
||||||
/* Fall back to just "dunstrc", which was used before 2013-06-23
|
/* Fall back to just "dunstrc", which was used before 2013-06-23
|
||||||
* (before v0.2). */
|
* (before v0.2). */
|
||||||
config_file = xdg_config("/dunstrc");
|
config_file = xdgConfigOpen("dunstrc", "r", &xdg);
|
||||||
}
|
if (config_file == NULL) {
|
||||||
|
puts("no dunstrc found -> skipping\n");
|
||||||
if (!config_file) {
|
xdgWipeHandle(&xdg);
|
||||||
LOG_W("No dunstrc found.");
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
load_ini_file(config_file);
|
load_ini_file(config_file);
|
||||||
#else
|
#else
|
||||||
LOG_M("dunstrc parsing disabled. "
|
fprintf(stderr, "Warning: dunstrc parsing disabled. "
|
||||||
"Using STATIC_CONFIG is deprecated behavior.");
|
"Using STATIC_CONFIG is deprecated behavior.\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
{
|
|
||||||
char *loglevel = option_get_string(
|
|
||||||
"global",
|
|
||||||
"verbosity", "-verbosity", NULL,
|
|
||||||
"The verbosity to log (one of 'crit', 'warn', 'mesg', 'info', 'debug')"
|
|
||||||
);
|
|
||||||
|
|
||||||
log_set_level_from_string(loglevel);
|
|
||||||
|
|
||||||
g_free(loglevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.per_monitor_dpi = option_get_bool(
|
settings.per_monitor_dpi = option_get_bool(
|
||||||
"experimental",
|
"experimental",
|
||||||
"per_monitor_dpi", NULL, false,
|
"per_monitor_dpi", NULL, false,
|
||||||
@ -123,12 +111,6 @@ void load_settings(char *cmdline_config_path)
|
|||||||
"Force the use of the Xinerama extension"
|
"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(
|
settings.font = option_get_string(
|
||||||
"global",
|
"global",
|
||||||
"font", "-font/-fn", defaults.font,
|
"font", "-font/-fn", defaults.font,
|
||||||
@ -145,8 +127,7 @@ void load_settings(char *cmdline_config_path)
|
|||||||
);
|
);
|
||||||
|
|
||||||
settings.markup = (allow_markup ? MARKUP_FULL : MARKUP_STRIP);
|
settings.markup = (allow_markup ? MARKUP_FULL : MARKUP_STRIP);
|
||||||
LOG_M("'allow_markup' is deprecated, please "
|
fprintf(stderr, "Warning: 'allow_markup' is deprecated, please use 'markup' instead.\n");
|
||||||
"use 'markup' instead.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char *c = option_get_string(
|
char *c = option_get_string(
|
||||||
@ -155,11 +136,13 @@ void load_settings(char *cmdline_config_path)
|
|||||||
"Specify how markup should be handled"
|
"Specify how markup should be handled"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!string_parse_markup_mode(c, &settings.markup)) {
|
//Use markup if set
|
||||||
if (c)
|
//Use default if settings.markup not set yet
|
||||||
LOG_W("Cannot parse markup mode value: '%s'", c);
|
// (=>c empty&&!allow_markup)
|
||||||
if (!settings.markup)
|
if (c) {
|
||||||
settings.markup = defaults.markup;
|
settings.markup = parse_markup_mode(c);
|
||||||
|
} else if (!settings.markup) {
|
||||||
|
settings.markup = defaults.markup;
|
||||||
}
|
}
|
||||||
g_free(c);
|
g_free(c);
|
||||||
}
|
}
|
||||||
@ -179,7 +162,7 @@ void load_settings(char *cmdline_config_path)
|
|||||||
settings.indicate_hidden = option_get_bool(
|
settings.indicate_hidden = option_get_bool(
|
||||||
"global",
|
"global",
|
||||||
"indicate_hidden", "-indicate_hidden", defaults.indicate_hidden,
|
"indicate_hidden", "-indicate_hidden", defaults.indicate_hidden,
|
||||||
"Show how many notifications are hidden"
|
"Show how many notificaitons are hidden?"
|
||||||
);
|
);
|
||||||
|
|
||||||
settings.word_wrap = option_get_bool(
|
settings.word_wrap = option_get_bool(
|
||||||
@ -187,22 +170,24 @@ void load_settings(char *cmdline_config_path)
|
|||||||
"word_wrap", "-word_wrap", defaults.word_wrap,
|
"word_wrap", "-word_wrap", defaults.word_wrap,
|
||||||
"Truncating long lines or do 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(
|
char *c = option_get_string(
|
||||||
"global",
|
"global",
|
||||||
"ellipsize", "-ellipsize", NULL,
|
"ellipsize", "-ellipsize", "",
|
||||||
"Ellipsize truncated lines on the start/middle/end"
|
"Ellipsize truncated lines on the start/middle/end"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!string_parse_ellipsize(c, &settings.ellipsize)) {
|
if (strlen(c) == 0) {
|
||||||
if (c)
|
settings.ellipsize = defaults.ellipsize;
|
||||||
LOG_W("Unknown ellipsize value: '%s'", c);
|
} else if (strcmp(c, "start") == 0) {
|
||||||
|
settings.ellipsize = start;
|
||||||
|
} else if (strcmp(c, "middle") == 0) {
|
||||||
|
settings.ellipsize = middle;
|
||||||
|
} else if (strcmp(c, "end") == 0) {
|
||||||
|
settings.ellipsize = end;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Warning: unknown ellipsize value: \"%s\"\n", c);
|
||||||
settings.ellipsize = defaults.ellipsize;
|
settings.ellipsize = defaults.ellipsize;
|
||||||
}
|
}
|
||||||
g_free(c);
|
g_free(c);
|
||||||
@ -220,22 +205,6 @@ void load_settings(char *cmdline_config_path)
|
|||||||
"Don't timeout notifications if user is longer idle than threshold"
|
"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(
|
settings.monitor = option_get_int(
|
||||||
"global",
|
"global",
|
||||||
"monitor", "-mon/-monitor", defaults.monitor,
|
"monitor", "-mon/-monitor", defaults.monitor,
|
||||||
@ -245,16 +214,14 @@ void load_settings(char *cmdline_config_path)
|
|||||||
{
|
{
|
||||||
char *c = option_get_string(
|
char *c = option_get_string(
|
||||||
"global",
|
"global",
|
||||||
"follow", "-follow", NULL,
|
"follow", "-follow", "",
|
||||||
"Follow mouse, keyboard or none?"
|
"Follow mouse, keyboard or none?"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!string_parse_follow_mode(c, &settings.f_mode)) {
|
if (strlen(c) > 0) {
|
||||||
if (c)
|
parse_follow_mode(c);
|
||||||
LOG_W("Cannot parse follow mode: %s", c);
|
g_free(c);
|
||||||
settings.f_mode = defaults.f_mode;
|
|
||||||
}
|
}
|
||||||
g_free(c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.title = option_get_string(
|
settings.title = option_get_string(
|
||||||
@ -269,24 +236,11 @@ void load_settings(char *cmdline_config_path)
|
|||||||
"Define the class of windows spawned by dunst."
|
"Define the class of windows spawned by dunst."
|
||||||
);
|
);
|
||||||
|
|
||||||
{
|
settings.geom = option_get_string(
|
||||||
|
"global",
|
||||||
char *c = option_get_string(
|
"geometry", "-geom/-geometry", defaults.geom,
|
||||||
"global",
|
"Geometry for the window"
|
||||||
"geometry", "-geom/-geometry", NULL,
|
);
|
||||||
"Geometry for the window"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (c) {
|
|
||||||
// TODO: Implement own geometry parsing to get rid of
|
|
||||||
// the include dependency on X11
|
|
||||||
settings.geometry = x_parse_geometry(c);
|
|
||||||
g_free(c);
|
|
||||||
} else {
|
|
||||||
settings.geometry = defaults.geometry;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.shrink = option_get_bool(
|
settings.shrink = option_get_bool(
|
||||||
"global",
|
"global",
|
||||||
@ -309,17 +263,22 @@ void load_settings(char *cmdline_config_path)
|
|||||||
{
|
{
|
||||||
char *c = option_get_string(
|
char *c = option_get_string(
|
||||||
"global",
|
"global",
|
||||||
"alignment", "-align/-alignment", NULL,
|
"alignment", "-align/-alignment", "",
|
||||||
"Text alignment left/center/right"
|
"Text alignment left/center/right"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!string_parse_alignment(c, &settings.align)) {
|
if (strlen(c) > 0) {
|
||||||
if (c)
|
if (strcmp(c, "left") == 0)
|
||||||
LOG_W("Unknown alignment value: '%s'", c);
|
settings.align = left;
|
||||||
settings.align = defaults.align;
|
else if (strcmp(c, "center") == 0)
|
||||||
|
settings.align = center;
|
||||||
|
else if (strcmp(c, "right") == 0)
|
||||||
|
settings.align = right;
|
||||||
|
else
|
||||||
|
fprintf(stderr,
|
||||||
|
"Warning: unknown alignment\n");
|
||||||
|
g_free(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free(c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.show_age_threshold = option_get_time(
|
settings.show_age_threshold = option_get_time(
|
||||||
@ -331,7 +290,7 @@ void load_settings(char *cmdline_config_path)
|
|||||||
settings.hide_duplicate_count = option_get_bool(
|
settings.hide_duplicate_count = option_get_bool(
|
||||||
"global",
|
"global",
|
||||||
"hide_duplicate_count", "-hide_duplicate_count", false,
|
"hide_duplicate_count", "-hide_duplicate_count", false,
|
||||||
"Hide the count of stacked notifications with the same content"
|
"Hide the count of merged notifications with the same content"
|
||||||
);
|
);
|
||||||
|
|
||||||
settings.sticky_history = option_get_bool(
|
settings.sticky_history = option_get_bool(
|
||||||
@ -370,67 +329,12 @@ void load_settings(char *cmdline_config_path)
|
|||||||
"horizontal padding"
|
"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(
|
settings.transparency = option_get_int(
|
||||||
"global",
|
"global",
|
||||||
"transparency", "-transparency", defaults.transparency,
|
"transparency", "-transparency", defaults.transparency,
|
||||||
"Transparency. Range 0-100"
|
"Transparency. range 0-100"
|
||||||
);
|
);
|
||||||
|
|
||||||
settings.corner_radius = option_get_int(
|
|
||||||
"global",
|
|
||||||
"corner_radius", "-corner_radius", defaults.corner_radius,
|
|
||||||
"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(
|
char *c = option_get_string(
|
||||||
"global",
|
"global",
|
||||||
@ -438,8 +342,17 @@ void load_settings(char *cmdline_config_path)
|
|||||||
"Color of the separator line (or 'auto')"
|
"Color of the separator line (or 'auto')"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!string_parse_sepcolor(c, &settings.sep_color)) {
|
if (strlen(c) > 0) {
|
||||||
settings.sep_color = defaults.sep_color;
|
if (strcmp(c, "auto") == 0)
|
||||||
|
settings.sep_color = AUTO;
|
||||||
|
else if (strcmp(c, "foreground") == 0)
|
||||||
|
settings.sep_color = FOREGROUND;
|
||||||
|
else if (strcmp(c, "frame") == 0)
|
||||||
|
settings.sep_color = FRAME;
|
||||||
|
else {
|
||||||
|
settings.sep_color = CUSTOM;
|
||||||
|
settings.sep_custom_color_str = g_strdup(c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
g_free(c);
|
g_free(c);
|
||||||
}
|
}
|
||||||
@ -447,7 +360,7 @@ void load_settings(char *cmdline_config_path)
|
|||||||
settings.stack_duplicates = option_get_bool(
|
settings.stack_duplicates = option_get_bool(
|
||||||
"global",
|
"global",
|
||||||
"stack_duplicates", "-stack_duplicates", true,
|
"stack_duplicates", "-stack_duplicates", true,
|
||||||
"Stack together notifications with the same content"
|
"Merge multiple notifications with the same content"
|
||||||
);
|
);
|
||||||
|
|
||||||
settings.startup_notification = option_get_bool(
|
settings.startup_notification = option_get_bool(
|
||||||
@ -465,8 +378,8 @@ void load_settings(char *cmdline_config_path)
|
|||||||
{
|
{
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
if (!g_shell_parse_argv(settings.dmenu, NULL, &settings.dmenu_cmd, &error)) {
|
if (!g_shell_parse_argv(settings.dmenu, NULL, &settings.dmenu_cmd, &error)) {
|
||||||
LOG_W("Unable to parse dmenu command: '%s'."
|
fprintf(stderr, "Unable to parse dmenu command: %s\n", error->message);
|
||||||
"dmenu functionality will be disabled.", error->message);
|
fprintf(stderr, "dmenu functionality will be disabled.\n");
|
||||||
g_error_free(error);
|
g_error_free(error);
|
||||||
settings.dmenu_cmd = NULL;
|
settings.dmenu_cmd = NULL;
|
||||||
}
|
}
|
||||||
@ -479,92 +392,33 @@ void load_settings(char *cmdline_config_path)
|
|||||||
"path to browser"
|
"path to browser"
|
||||||
);
|
);
|
||||||
|
|
||||||
{
|
|
||||||
GError *error = NULL;
|
|
||||||
if (!g_shell_parse_argv(settings.browser, NULL, &settings.browser_cmd, &error)) {
|
|
||||||
LOG_W("Unable to parse browser command: '%s'."
|
|
||||||
" URL functionality will be disabled.", error->message);
|
|
||||||
g_error_free(error);
|
|
||||||
settings.browser_cmd = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
char *c = option_get_string(
|
char *c = option_get_string(
|
||||||
"global",
|
"global",
|
||||||
"icon_position", "-icon_position", "left",
|
"icon_position", "-icon_position", "off",
|
||||||
"Align icons left/right/off"
|
"Align icons left/right/off"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!string_parse_icon_position(c, &settings.icon_position)) {
|
if (strlen(c) > 0) {
|
||||||
if (c)
|
if (strcmp(c, "left") == 0)
|
||||||
LOG_W("Unknown icon position: '%s'", c);
|
settings.icon_position = icons_left;
|
||||||
settings.icon_position = defaults.icon_position;
|
else if (strcmp(c, "right") == 0)
|
||||||
|
settings.icon_position = icons_right;
|
||||||
|
else if (strcmp(c, "off") == 0)
|
||||||
|
settings.icon_position = icons_off;
|
||||||
|
else
|
||||||
|
fprintf(stderr,
|
||||||
|
"Warning: unknown icon position: %s\n", c);
|
||||||
|
g_free(c);
|
||||||
}
|
}
|
||||||
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(
|
settings.max_icon_size = option_get_int(
|
||||||
"global",
|
"global",
|
||||||
"max_icon_size", "-max_icon_size", defaults.max_icon_size,
|
"max_icon_size", "-max_icon_size", defaults.max_icon_size,
|
||||||
"Scale larger icons down to this size, set to 0 to disable"
|
"Scale larger icons down to this size, set to 0 to disable"
|
||||||
);
|
);
|
||||||
|
|
||||||
// restrict the icon size to a reasonable limit if we have a fixed width.
|
|
||||||
// Otherwise the layout will be broken by too large icons.
|
|
||||||
// See https://github.com/dunst-project/dunst/issues/540
|
|
||||||
if (settings.geometry.width_set && settings.geometry.w != 0) {
|
|
||||||
const int icon_size_limit = settings.geometry.w / 2;
|
|
||||||
if ( settings.max_icon_size == 0
|
|
||||||
|| settings.max_icon_size > icon_size_limit) {
|
|
||||||
if (settings.max_icon_size != 0) {
|
|
||||||
LOG_W("Max width was set to %d but got a max_icon_size of %d, too large to use. Setting max_icon_size=%d",
|
|
||||||
settings.geometry.w, settings.max_icon_size, icon_size_limit);
|
|
||||||
} else {
|
|
||||||
LOG_I("Max width was set but max_icon_size is unlimited. Limiting icons to %d pixels", icon_size_limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.max_icon_size = icon_size_limit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the deprecated icon_folders option is used,
|
// If the deprecated icon_folders option is used,
|
||||||
// read it and generate its usage string.
|
// read it and generate its usage string.
|
||||||
if (ini_is_set("global", "icon_folders") || cmdline_is_set("-icon_folders")) {
|
if (ini_is_set("global", "icon_folders") || cmdline_is_set("-icon_folders")) {
|
||||||
@ -573,7 +427,7 @@ void load_settings(char *cmdline_config_path)
|
|||||||
"icon_folders", "-icon_folders", defaults.icon_path,
|
"icon_folders", "-icon_folders", defaults.icon_path,
|
||||||
"folders to default icons (deprecated, please use 'icon_path' instead)"
|
"folders to default icons (deprecated, please use 'icon_path' instead)"
|
||||||
);
|
);
|
||||||
LOG_M("The option 'icon_folders' is deprecated, please use 'icon_path' instead.");
|
fprintf(stderr, "Warning: 'icon_folders' is deprecated, please use 'icon_path' instead.\n");
|
||||||
}
|
}
|
||||||
// Read value and generate usage string for icon_path.
|
// Read value and generate usage string for icon_path.
|
||||||
// If icon_path is set, override icon_folder.
|
// If icon_path is set, override icon_folder.
|
||||||
@ -593,9 +447,7 @@ void load_settings(char *cmdline_config_path)
|
|||||||
"width", NULL, defaults.frame_width,
|
"width", NULL, defaults.frame_width,
|
||||||
"Width of frame around the window"
|
"Width of frame around the window"
|
||||||
);
|
);
|
||||||
LOG_M("The frame section is deprecated, width has "
|
fprintf(stderr, "Warning: The frame section is deprecated, width has been renamed to frame_width and moved to the global section.\n");
|
||||||
"been renamed to frame_width and moved to "
|
|
||||||
"the global section.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.frame_width = option_get_int(
|
settings.frame_width = option_get_int(
|
||||||
@ -611,9 +463,7 @@ void load_settings(char *cmdline_config_path)
|
|||||||
"color", NULL, defaults.frame_color,
|
"color", NULL, defaults.frame_color,
|
||||||
"Color of the frame around the window"
|
"Color of the frame around the window"
|
||||||
);
|
);
|
||||||
LOG_M("The frame section is deprecated, color "
|
fprintf(stderr, "Warning: The frame section is deprecated, color has been renamed to frame_color and moved to the global section.\n");
|
||||||
"has been renamed to frame_color and moved "
|
|
||||||
"to the global section.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
settings.frame_color = option_get_string(
|
settings.frame_color = option_get_string(
|
||||||
@ -624,67 +474,21 @@ void load_settings(char *cmdline_config_path)
|
|||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
settings.lowbgcolor = option_get_string(
|
||||||
{
|
|
||||||
char **c = option_get_list(
|
|
||||||
"global",
|
|
||||||
"mouse_left_click", "-mouse_left_click", NULL,
|
|
||||||
"Action of Left click event"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!string_parse_mouse_action_list(c, &settings.mouse_left_click)) {
|
|
||||||
settings.mouse_left_click = defaults.mouse_left_click;
|
|
||||||
}
|
|
||||||
free_string_array(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
char **c = option_get_list(
|
|
||||||
"global",
|
|
||||||
"mouse_middle_click", "-mouse_middle_click", NULL,
|
|
||||||
"Action of middle click event"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!string_parse_mouse_action_list(c, &settings.mouse_middle_click)) {
|
|
||||||
settings.mouse_middle_click = defaults.mouse_middle_click;
|
|
||||||
}
|
|
||||||
free_string_array(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
char **c = option_get_list(
|
|
||||||
"global",
|
|
||||||
"mouse_right_click", "-mouse_right_click", NULL,
|
|
||||||
"Action of right click event"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!string_parse_mouse_action_list(c, &settings.mouse_right_click)) {
|
|
||||||
settings.mouse_right_click = defaults.mouse_right_click;
|
|
||||||
}
|
|
||||||
free_string_array(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.colors_low.bg = option_get_string(
|
|
||||||
"urgency_low",
|
"urgency_low",
|
||||||
"background", "-lb", defaults.colors_low.bg,
|
"background", "-lb", defaults.lowbgcolor,
|
||||||
"Background color for notifications with low urgency"
|
"Background color for notifications with low urgency"
|
||||||
);
|
);
|
||||||
|
|
||||||
settings.colors_low.fg = option_get_string(
|
settings.lowfgcolor = option_get_string(
|
||||||
"urgency_low",
|
"urgency_low",
|
||||||
"foreground", "-lf", defaults.colors_low.fg,
|
"foreground", "-lf", defaults.lowfgcolor,
|
||||||
"Foreground color for notifications with low urgency"
|
"Foreground color for notifications with low urgency"
|
||||||
);
|
);
|
||||||
|
|
||||||
settings.colors_low.highlight = option_get_string(
|
settings.lowframecolor = option_get_string(
|
||||||
"urgency_low",
|
"urgency_low",
|
||||||
"highlight", "-lh", defaults.colors_low.highlight,
|
"frame_color", "-lfr", NULL,
|
||||||
"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,
|
|
||||||
"Frame color for notifications with low urgency"
|
"Frame color for notifications with low urgency"
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -700,27 +504,21 @@ void load_settings(char *cmdline_config_path)
|
|||||||
"Icon for notifications with low urgency"
|
"Icon for notifications with low urgency"
|
||||||
);
|
);
|
||||||
|
|
||||||
settings.colors_norm.bg = option_get_string(
|
settings.normbgcolor = option_get_string(
|
||||||
"urgency_normal",
|
"urgency_normal",
|
||||||
"background", "-nb", defaults.colors_norm.bg,
|
"background", "-nb", defaults.normbgcolor,
|
||||||
"Background color for notifications with normal urgency"
|
"Background color for notifications with normal urgency"
|
||||||
);
|
);
|
||||||
|
|
||||||
settings.colors_norm.fg = option_get_string(
|
settings.normfgcolor = option_get_string(
|
||||||
"urgency_normal",
|
"urgency_normal",
|
||||||
"foreground", "-nf", defaults.colors_norm.fg,
|
"foreground", "-nf", defaults.normfgcolor,
|
||||||
"Foreground color for notifications with normal urgency"
|
"Foreground color for notifications with normal urgency"
|
||||||
);
|
);
|
||||||
|
|
||||||
settings.colors_norm.highlight = option_get_string(
|
settings.normframecolor = option_get_string(
|
||||||
"urgency_normal",
|
"urgency_normal",
|
||||||
"highlight", "-nh", defaults.colors_norm.highlight,
|
"frame_color", "-nfr", NULL,
|
||||||
"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,
|
|
||||||
"Frame color for notifications with normal urgency"
|
"Frame color for notifications with normal urgency"
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -736,27 +534,21 @@ void load_settings(char *cmdline_config_path)
|
|||||||
"Icon for notifications with normal urgency"
|
"Icon for notifications with normal urgency"
|
||||||
);
|
);
|
||||||
|
|
||||||
settings.colors_crit.bg = option_get_string(
|
settings.critbgcolor = option_get_string(
|
||||||
"urgency_critical",
|
"urgency_critical",
|
||||||
"background", "-cb", defaults.colors_crit.bg,
|
"background", "-cb", defaults.critbgcolor,
|
||||||
"Background color for notifications with critical urgency"
|
"Background color for notifications with critical urgency"
|
||||||
);
|
);
|
||||||
|
|
||||||
settings.colors_crit.fg = option_get_string(
|
settings.critfgcolor = option_get_string(
|
||||||
"urgency_critical",
|
"urgency_critical",
|
||||||
"foreground", "-cf", defaults.colors_crit.fg,
|
"foreground", "-cf", defaults.critfgcolor,
|
||||||
"Foreground color for notifications with ciritical urgency"
|
"Foreground color for notifications with ciritical urgency"
|
||||||
);
|
);
|
||||||
|
|
||||||
settings.colors_crit.highlight = option_get_string(
|
settings.critframecolor = option_get_string(
|
||||||
"urgency_critical",
|
"urgency_critical",
|
||||||
"highlight", "-ch", defaults.colors_crit.highlight,
|
"frame_color", "-cfr", NULL,
|
||||||
"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,
|
|
||||||
"Frame color for notifications with critical urgency"
|
"Frame color for notifications with critical urgency"
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -807,6 +599,27 @@ void load_settings(char *cmdline_config_path)
|
|||||||
"Always run rule-defined scripts, even if the notification is suppressed with format = \"\"."
|
"Always run rule-defined scripts, even if the notification is suppressed with format = \"\"."
|
||||||
);
|
);
|
||||||
|
|
||||||
|
{
|
||||||
|
char *c = option_get_string(
|
||||||
|
"global",
|
||||||
|
"centering", "-centering", "off",
|
||||||
|
"Align notifications on screen off/horizontal/vertical/both"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (strcmp(c, "off") == 0)
|
||||||
|
settings.centering = CENTERING_OFF;
|
||||||
|
else if (strcmp(c, "horizontal") == 0)
|
||||||
|
settings.centering = CENTERING_HORIZONTAL;
|
||||||
|
else if (strcmp(c, "vertical") == 0)
|
||||||
|
settings.centering = CENTERING_VERTICAL;
|
||||||
|
else if (strcmp(c, "both") == 0)
|
||||||
|
settings.centering = CENTERING_BOTH;
|
||||||
|
else
|
||||||
|
fprintf(stderr,
|
||||||
|
"Warning: unknown centering option: %s\n", c);
|
||||||
|
g_free(c);
|
||||||
|
}
|
||||||
|
|
||||||
/* push hardcoded default rules into rules list */
|
/* push hardcoded default rules into rules list */
|
||||||
for (int i = 0; i < G_N_ELEMENTS(default_rules); i++) {
|
for (int i = 0; i < G_N_ELEMENTS(default_rules); i++) {
|
||||||
rules = g_slist_insert(rules, &(default_rules[i]), -1);
|
rules = g_slist_insert(rules, &(default_rules[i]), -1);
|
||||||
@ -817,26 +630,27 @@ void load_settings(char *cmdline_config_path)
|
|||||||
cur_section = next_section(cur_section);
|
cur_section = next_section(cur_section);
|
||||||
if (!cur_section)
|
if (!cur_section)
|
||||||
break;
|
break;
|
||||||
if (STR_EQ(cur_section, "global")
|
if (strcmp(cur_section, "global") == 0
|
||||||
|| STR_EQ(cur_section, "frame")
|
|| strcmp(cur_section, "frame") == 0
|
||||||
|| STR_EQ(cur_section, "experimental")
|
|| strcmp(cur_section, "experimental") == 0
|
||||||
|| STR_EQ(cur_section, "shortcuts")
|
|| strcmp(cur_section, "shortcuts") == 0
|
||||||
|| STR_EQ(cur_section, "urgency_low")
|
|| strcmp(cur_section, "urgency_low") == 0
|
||||||
|| STR_EQ(cur_section, "urgency_normal")
|
|| strcmp(cur_section, "urgency_normal") == 0
|
||||||
|| STR_EQ(cur_section, "urgency_critical"))
|
|| strcmp(cur_section, "urgency_critical") == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* check for existing rule with same name */
|
/* check for existing rule with same name */
|
||||||
struct rule *r = NULL;
|
rule_t *r = NULL;
|
||||||
for (GSList *iter = rules; iter; iter = iter->next) {
|
for (GSList *iter = rules; iter; iter = iter->next) {
|
||||||
struct rule *match = iter->data;
|
rule_t *match = iter->data;
|
||||||
if (match->name &&
|
if (match->name &&
|
||||||
STR_EQ(match->name, cur_section))
|
strcmp(match->name, cur_section) == 0)
|
||||||
r = match;
|
r = match;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!r) {
|
if (r == NULL) {
|
||||||
r = rule_new();
|
r = g_malloc(sizeof(rule_t));
|
||||||
|
rule_init(r);
|
||||||
rules = g_slist_insert(rules, r, -1);
|
rules = g_slist_insert(rules, r, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -846,7 +660,6 @@ void load_settings(char *cmdline_config_path)
|
|||||||
r->body = ini_get_string(cur_section, "body", r->body);
|
r->body = ini_get_string(cur_section, "body", r->body);
|
||||||
r->icon = ini_get_string(cur_section, "icon", r->icon);
|
r->icon = ini_get_string(cur_section, "icon", r->icon);
|
||||||
r->category = ini_get_string(cur_section, "category", r->category);
|
r->category = ini_get_string(cur_section, "category", r->category);
|
||||||
r->stack_tag = ini_get_string(cur_section, "stack_tag", r->stack_tag);
|
|
||||||
r->timeout = ini_get_time(cur_section, "timeout", r->timeout);
|
r->timeout = ini_get_time(cur_section, "timeout", r->timeout);
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -855,48 +668,30 @@ void load_settings(char *cmdline_config_path)
|
|||||||
"markup", NULL
|
"markup", NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!string_parse_markup_mode(c, &r->markup)) {
|
if (c != NULL) {
|
||||||
if (c)
|
r->markup = parse_markup_mode(c);
|
||||||
LOG_W("Invalid markup mode value: %s", c);
|
g_free(c);
|
||||||
}
|
}
|
||||||
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->urgency = ini_get_urgency(cur_section, "urgency", r->urgency);
|
||||||
r->msg_urgency = ini_get_urgency(cur_section, "msg_urgency", r->msg_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->fg = ini_get_string(cur_section, "foreground", r->fg);
|
||||||
r->bg = ini_get_string(cur_section, "background", r->bg);
|
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->format = ini_get_string(cur_section, "format", r->format);
|
||||||
r->new_icon = ini_get_string(cur_section, "new_icon", r->new_icon);
|
r->new_icon = ini_get_string(cur_section, "new_icon", r->new_icon);
|
||||||
r->history_ignore = ini_get_bool(cur_section, "history_ignore", r->history_ignore);
|
r->history_ignore = ini_get_bool(cur_section, "history_ignore", r->history_ignore);
|
||||||
r->match_transient = ini_get_bool(cur_section, "match_transient", r->match_transient);
|
r->match_transient = ini_get_bool(cur_section, "match_transient", r->match_transient);
|
||||||
r->set_transient = ini_get_bool(cur_section, "set_transient", r->set_transient);
|
r->set_transient = ini_get_bool(cur_section, "set_transient", r->set_transient);
|
||||||
r->desktop_entry = ini_get_string(cur_section, "desktop_entry", r->desktop_entry);
|
|
||||||
r->skip_display = ini_get_bool(cur_section, "skip_display", r->skip_display);
|
|
||||||
{
|
|
||||||
char *c = ini_get_string(
|
|
||||||
cur_section,
|
|
||||||
"fullscreen", NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!string_parse_fullscreen(c, &r->fullscreen)) {
|
|
||||||
if (c)
|
|
||||||
LOG_W("Invalid fullscreen value: %s", c);
|
|
||||||
}
|
|
||||||
g_free(c);
|
|
||||||
}
|
|
||||||
r->script = ini_get_path(cur_section, "script", NULL);
|
r->script = ini_get_path(cur_section, "script", NULL);
|
||||||
r->set_stack_tag = ini_get_string(cur_section, "set_stack_tag", r->set_stack_tag);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef STATIC_CONFIG
|
#ifndef STATIC_CONFIG
|
||||||
if (config_file) {
|
if (config_file) {
|
||||||
fclose(config_file);
|
fclose(config_file);
|
||||||
free_ini();
|
free_ini();
|
||||||
|
xdgWipeHandle(&xdg);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
|||||||
@ -4,63 +4,38 @@
|
|||||||
|
|
||||||
#include <stdbool.h>
|
#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"
|
#include "x11/x.h"
|
||||||
|
|
||||||
enum alignment { ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT };
|
enum alignment { left, center, right };
|
||||||
enum ellipsize { ELLIPSE_START, ELLIPSE_MIDDLE, ELLIPSE_END };
|
enum ellipsize { start, middle, end };
|
||||||
enum icon_position { ICON_LEFT, ICON_RIGHT, ICON_OFF };
|
enum icon_position_t { icons_left, icons_right, icons_off };
|
||||||
enum vertical_alignment { VERTICAL_TOP, VERTICAL_CENTER, VERTICAL_BOTTOM };
|
enum separator_color { FOREGROUND, AUTO, FRAME, CUSTOM };
|
||||||
enum separator_color { SEP_FOREGROUND, SEP_AUTO, SEP_FRAME, SEP_CUSTOM };
|
|
||||||
enum follow_mode { FOLLOW_NONE, FOLLOW_MOUSE, FOLLOW_KEYBOARD };
|
enum follow_mode { FOLLOW_NONE, FOLLOW_MOUSE, FOLLOW_KEYBOARD };
|
||||||
enum mouse_action { MOUSE_NONE, MOUSE_DO_ACTION, MOUSE_CLOSE_CURRENT, MOUSE_CLOSE_ALL, MOUSE_CONTEXT, MOUSE_CONTEXT_ALL, MOUSE_OPEN_URL };
|
enum markup_mode { MARKUP_NULL, MARKUP_NO, MARKUP_STRIP, MARKUP_FULL };
|
||||||
#ifndef ZWLR_LAYER_SHELL_V1_LAYER_ENUM
|
enum centering { CENTERING_OFF, CENTERING_HORIZONTAL, CENTERING_VERTICAL, CENTERING_BOTH };
|
||||||
#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 {
|
typedef struct _settings {
|
||||||
enum separator_color type;
|
|
||||||
char *sep_color;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct geometry {
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
unsigned int w;
|
|
||||||
unsigned int h;
|
|
||||||
bool negative_x;
|
|
||||||
bool negative_y;
|
|
||||||
bool negative_width;
|
|
||||||
bool width_set;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct settings {
|
|
||||||
bool print_notifications;
|
bool print_notifications;
|
||||||
bool per_monitor_dpi;
|
bool per_monitor_dpi;
|
||||||
enum markup_mode markup;
|
enum markup_mode markup;
|
||||||
bool stack_duplicates;
|
bool stack_duplicates;
|
||||||
bool hide_duplicate_count;
|
bool hide_duplicate_count;
|
||||||
char *font;
|
char *font;
|
||||||
struct notification_colors colors_low;
|
char *normbgcolor;
|
||||||
struct notification_colors colors_norm;
|
char *normfgcolor;
|
||||||
struct notification_colors colors_crit;
|
char *normframecolor;
|
||||||
|
char *critbgcolor;
|
||||||
|
char *critfgcolor;
|
||||||
|
char *critframecolor;
|
||||||
|
char *lowbgcolor;
|
||||||
|
char *lowfgcolor;
|
||||||
|
char *lowframecolor;
|
||||||
char *format;
|
char *format;
|
||||||
gint64 timeouts[3];
|
gint64 timeouts[3];
|
||||||
char *icons[3];
|
char *icons[3];
|
||||||
unsigned int transparency;
|
unsigned int transparency;
|
||||||
struct geometry geometry;
|
char *geom;
|
||||||
|
enum centering centering;
|
||||||
char *title;
|
char *title;
|
||||||
char *class;
|
char *class;
|
||||||
int shrink;
|
int shrink;
|
||||||
@ -73,7 +48,6 @@ struct settings {
|
|||||||
int history_length;
|
int history_length;
|
||||||
int show_indicators;
|
int show_indicators;
|
||||||
int word_wrap;
|
int word_wrap;
|
||||||
int ignore_dbusclose;
|
|
||||||
enum ellipsize ellipsize;
|
enum ellipsize ellipsize;
|
||||||
int ignore_newline;
|
int ignore_newline;
|
||||||
int line_height;
|
int line_height;
|
||||||
@ -81,8 +55,8 @@ struct settings {
|
|||||||
int separator_height;
|
int separator_height;
|
||||||
int padding;
|
int padding;
|
||||||
int h_padding;
|
int h_padding;
|
||||||
int text_icon_padding;
|
enum separator_color sep_color;
|
||||||
struct separator_color_data sep_color;
|
char *sep_custom_color_str;
|
||||||
int frame_width;
|
int frame_width;
|
||||||
char *frame_color;
|
char *frame_color;
|
||||||
int startup_notification;
|
int startup_notification;
|
||||||
@ -90,35 +64,21 @@ struct settings {
|
|||||||
char *dmenu;
|
char *dmenu;
|
||||||
char **dmenu_cmd;
|
char **dmenu_cmd;
|
||||||
char *browser;
|
char *browser;
|
||||||
char **browser_cmd;
|
enum icon_position_t icon_position;
|
||||||
enum icon_position icon_position;
|
|
||||||
enum vertical_alignment vertical_alignment;
|
|
||||||
int min_icon_size;
|
|
||||||
int max_icon_size;
|
int max_icon_size;
|
||||||
char *icon_path;
|
char *icon_path;
|
||||||
enum follow_mode f_mode;
|
enum follow_mode f_mode;
|
||||||
bool always_run_script;
|
bool always_run_script;
|
||||||
struct keyboard_shortcut close_ks;
|
keyboard_shortcut close_ks;
|
||||||
struct keyboard_shortcut close_all_ks;
|
keyboard_shortcut close_all_ks;
|
||||||
struct keyboard_shortcut history_ks;
|
keyboard_shortcut history_ks;
|
||||||
struct keyboard_shortcut context_ks;
|
keyboard_shortcut context_ks;
|
||||||
bool force_xinerama;
|
bool force_xinerama;
|
||||||
bool force_xwayland;
|
} settings_t;
|
||||||
int corner_radius;
|
|
||||||
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;
|
extern settings_t settings;
|
||||||
|
|
||||||
void load_settings(char *cmdline_config_path);
|
void load_settings(char *cmdline_config_path);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
|||||||
169
src/utils.c
@ -2,45 +2,22 @@
|
|||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <ctype.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <pwd.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <string.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)
|
char *string_replace_char(char needle, char replacement, char *haystack)
|
||||||
{
|
{
|
||||||
ASSERT_OR_RET(haystack, NULL);
|
|
||||||
|
|
||||||
char *current = haystack;
|
char *current = haystack;
|
||||||
while ((current = strchr(current, needle)))
|
while ((current = strchr(current, needle)) != NULL)
|
||||||
*current++ = replacement;
|
*current++ = replacement;
|
||||||
return haystack;
|
return haystack;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see utils.h */
|
|
||||||
char *string_replace_at(char *buf, int pos, int len, const char *repl)
|
char *string_replace_at(char *buf, int pos, int len, const char *repl)
|
||||||
{
|
{
|
||||||
assert(buf);
|
|
||||||
assert(repl);
|
|
||||||
|
|
||||||
char *tmp;
|
char *tmp;
|
||||||
int size, buf_len, repl_len;
|
int size, buf_len, repl_len;
|
||||||
|
|
||||||
@ -52,9 +29,9 @@ char *string_replace_at(char *buf, int pos, int len, const char *repl)
|
|||||||
tmp = buf;
|
tmp = buf;
|
||||||
} else {
|
} else {
|
||||||
tmp = g_malloc(size);
|
tmp = g_malloc(size);
|
||||||
memcpy(tmp, buf, pos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memcpy(tmp, buf, pos);
|
||||||
memcpy(tmp + pos, repl, repl_len);
|
memcpy(tmp + pos, repl, repl_len);
|
||||||
memmove(tmp + pos + repl_len, buf + pos + len, buf_len - (pos + len) + 1);
|
memmove(tmp + pos + repl_len, buf + pos + len, buf_len - (pos + len) + 1);
|
||||||
|
|
||||||
@ -65,13 +42,19 @@ char *string_replace_at(char *buf, int pos, int len, const char *repl)
|
|||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see utils.h */
|
char *string_replace(const char *needle, const char *replacement, char *haystack)
|
||||||
|
{
|
||||||
|
char *start;
|
||||||
|
start = strstr(haystack, needle);
|
||||||
|
if (start == NULL) {
|
||||||
|
return haystack;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string_replace_at(haystack, (start - haystack), strlen(needle), replacement);
|
||||||
|
}
|
||||||
|
|
||||||
char *string_replace_all(const char *needle, const char *replacement, char *haystack)
|
char *string_replace_all(const char *needle, const char *replacement, char *haystack)
|
||||||
{
|
{
|
||||||
ASSERT_OR_RET(haystack, NULL);
|
|
||||||
assert(needle);
|
|
||||||
assert(replacement);
|
|
||||||
|
|
||||||
char *start;
|
char *start;
|
||||||
int needle_pos;
|
int needle_pos;
|
||||||
int needle_len, repl_len;
|
int needle_len, repl_len;
|
||||||
@ -84,7 +67,7 @@ char *string_replace_all(const char *needle, const char *replacement, char *hays
|
|||||||
start = strstr(haystack, needle);
|
start = strstr(haystack, needle);
|
||||||
repl_len = strlen(replacement);
|
repl_len = strlen(replacement);
|
||||||
|
|
||||||
while (start) {
|
while (start != NULL) {
|
||||||
needle_pos = start - haystack;
|
needle_pos = start - haystack;
|
||||||
haystack = string_replace_at(haystack, needle_pos, needle_len, replacement);
|
haystack = string_replace_at(haystack, needle_pos, needle_len, replacement);
|
||||||
start = strstr(haystack + needle_pos + repl_len, needle);
|
start = strstr(haystack + needle_pos + repl_len, needle);
|
||||||
@ -92,14 +75,13 @@ char *string_replace_all(const char *needle, const char *replacement, char *hays
|
|||||||
return haystack;
|
return haystack;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see utils.h */
|
|
||||||
char *string_append(char *a, const char *b, const char *sep)
|
char *string_append(char *a, const char *b, const char *sep)
|
||||||
{
|
{
|
||||||
if (STR_EMPTY(a)) {
|
if (!a || *a == '\0') {
|
||||||
g_free(a);
|
g_free(a);
|
||||||
return g_strdup(b);
|
return g_strdup(b);
|
||||||
}
|
}
|
||||||
if (STR_EMPTY(b))
|
if (!b || *b == '\0')
|
||||||
return a;
|
return a;
|
||||||
|
|
||||||
char *new;
|
char *new;
|
||||||
@ -110,29 +92,11 @@ char *string_append(char *a, const char *b, const char *sep)
|
|||||||
g_free(a);
|
g_free(a);
|
||||||
|
|
||||||
return new;
|
return new;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see utils.h */
|
|
||||||
char *string_strip_quotes(const char *value)
|
|
||||||
{
|
|
||||||
ASSERT_OR_RET(value, NULL);
|
|
||||||
|
|
||||||
size_t len = strlen(value);
|
|
||||||
char *s;
|
|
||||||
|
|
||||||
if (value[0] == '"' && value[len-1] == '"')
|
|
||||||
s = g_strndup(value + 1, len-2);
|
|
||||||
else
|
|
||||||
s = g_strdup(value);
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* see utils.h */
|
|
||||||
void string_strip_delimited(char *str, char a, char b)
|
void string_strip_delimited(char *str, char a, char b)
|
||||||
{
|
{
|
||||||
assert(str);
|
|
||||||
|
|
||||||
int iread=-1, iwrite=0, copen=0;
|
int iread=-1, iwrite=0, copen=0;
|
||||||
while (str[++iread] != 0) {
|
while (str[++iread] != 0) {
|
||||||
if (str[iread] == a) {
|
if (str[iread] == a) {
|
||||||
@ -146,27 +110,13 @@ void string_strip_delimited(char *str, char a, char b)
|
|||||||
str[iwrite] = 0;
|
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)
|
char *string_to_path(char *string)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (string && STRN_EQ(string, "~/", 2)) {
|
if (string && 0 == strncmp(string, "~/", 2)) {
|
||||||
char *home = g_strconcat(user_get_home(), "/", NULL);
|
char *home = g_strconcat(getenv("HOME"), "/", NULL);
|
||||||
|
|
||||||
string = string_replace_at(string, 0, 2, home);
|
string = string_replace("~/", home, string);
|
||||||
|
|
||||||
g_free(home);
|
g_free(home);
|
||||||
}
|
}
|
||||||
@ -174,9 +124,9 @@ char *string_to_path(char *string)
|
|||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see utils.h */
|
|
||||||
gint64 string_to_time(const char *string)
|
gint64 string_to_time(const char *string)
|
||||||
{
|
{
|
||||||
|
|
||||||
assert(string);
|
assert(string);
|
||||||
|
|
||||||
errno = 0;
|
errno = 0;
|
||||||
@ -184,79 +134,40 @@ gint64 string_to_time(const char *string)
|
|||||||
gint64 val = strtoll(string, &endptr, 10);
|
gint64 val = strtoll(string, &endptr, 10);
|
||||||
|
|
||||||
if (errno != 0) {
|
if (errno != 0) {
|
||||||
LOG_W("Time: '%s': %s.", string, strerror(errno));
|
fprintf(stderr, "ERROR: Time: '%s': %s.\n", string, strerror(errno));
|
||||||
return 0;
|
return 0;
|
||||||
} else if (string == endptr) {
|
} else if (string == endptr) {
|
||||||
LOG_W("Time: '%s': No digits found.", string);
|
fprintf(stderr, "ERROR: Time: No digits found.\n");
|
||||||
return 0;
|
return 0;
|
||||||
} else if (errno != 0 && val == 0) {
|
} else if (errno != 0 && val == 0) {
|
||||||
LOG_W("Time: '%s': Unknown error.", string);
|
fprintf(stderr, "ERROR: Time: '%s' unknown error.\n", string);
|
||||||
return 0;
|
return 0;
|
||||||
} else if (errno == 0 && !*endptr) {
|
} else if (errno == 0 && !*endptr) {
|
||||||
return S2US(val);
|
return val * G_USEC_PER_SEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
// endptr may point to a separating space
|
// endptr may point to a separating space
|
||||||
while (isspace(*endptr))
|
while (*endptr == ' ')
|
||||||
endptr++;
|
endptr++;
|
||||||
|
|
||||||
if (STRN_EQ(endptr, "ms", 2))
|
if (0 == strncmp(endptr, "ms", 2))
|
||||||
return val * 1000;
|
return val * 1000;
|
||||||
else if (STRN_EQ(endptr, "s", 1))
|
else if (0 == strncmp(endptr, "s", 1))
|
||||||
return S2US(val);
|
return val * G_USEC_PER_SEC;
|
||||||
else if (STRN_EQ(endptr, "m", 1))
|
else if (0 == strncmp(endptr, "m", 1))
|
||||||
return S2US(val) * 60;
|
return val * G_USEC_PER_SEC * 60;
|
||||||
else if (STRN_EQ(endptr, "h", 1))
|
else if (0 == strncmp(endptr, "h", 1))
|
||||||
return S2US(val) * 60 * 60;
|
return val * G_USEC_PER_SEC * 60 * 60;
|
||||||
else if (STRN_EQ(endptr, "d", 1))
|
else if (0 == strncmp(endptr, "d", 1))
|
||||||
return S2US(val) * 60 * 60 * 24;
|
return val * G_USEC_PER_SEC * 60 * 60 * 24;
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see utils.h */
|
void die(char *text, int exit_value)
|
||||||
gint64 time_monotonic_now(void)
|
|
||||||
{
|
{
|
||||||
struct timespec tv_now;
|
fputs(text, stderr);
|
||||||
|
exit(exit_value);
|
||||||
/* On Linux, BOOTTIME is the correct monotonic time,
|
|
||||||
* as BOOTTIME counts onwards during sleep. For all other
|
|
||||||
* POSIX compliant OSes, MONOTONIC should also count onwards
|
|
||||||
* during system sleep. */
|
|
||||||
#ifdef __linux__
|
|
||||||
clock_gettime(CLOCK_BOOTTIME, &tv_now);
|
|
||||||
#else
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &tv_now);
|
|
||||||
#endif
|
|
||||||
return S2US(tv_now.tv_sec) + tv_now.tv_nsec / 1000;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see utils.h */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
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: */
|
|
||||||
|
|||||||
147
src/utils.h
@ -3,151 +3,32 @@
|
|||||||
#define DUNST_UTILS_H
|
#define DUNST_UTILS_H
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <string.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
//! Test if a string is NULL or empty
|
/* replace all occurrences of the character needle with the character replacement in haystack */
|
||||||
#define STR_EMPTY(s) (!s || (*s == '\0'))
|
|
||||||
//! Test if a string is non-NULL and not empty
|
|
||||||
#define STR_FULL(s) !(STR_EMPTY(s))
|
|
||||||
//! Test if string a and b contain the same chars
|
|
||||||
#define STR_EQ(a, b) (g_strcmp0(a, b) == 0)
|
|
||||||
//! Test if string a and b are same up to n chars
|
|
||||||
#define STRN_EQ(a, b, n) (strncmp(a, b, n) == 0)
|
|
||||||
//! Test if string a and b are the same case-insensitively
|
|
||||||
#define STR_CASEQ(a, b) (strcasecmp(a, b) == 0)
|
|
||||||
|
|
||||||
//! Assert that expr evaluates to true, if not return with val
|
|
||||||
#define ASSERT_OR_RET(expr, val) if (!(expr)) return val;
|
|
||||||
|
|
||||||
//! 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.
|
|
||||||
*
|
|
||||||
* Does not allocate a new string.
|
|
||||||
*
|
|
||||||
* @param needle The char to replace with replacement
|
|
||||||
* @param replacement The char which is the new one
|
|
||||||
* @param haystack (nullable) The string to replace
|
|
||||||
*
|
|
||||||
* @returns The exact value of the haystack paramater (to allow nesting)
|
|
||||||
*/
|
|
||||||
char *string_replace_char(char needle, char replacement, char *haystack);
|
char *string_replace_char(char needle, char replacement, char *haystack);
|
||||||
|
|
||||||
/**
|
/* replace all occurrences of needle with replacement in haystack */
|
||||||
* Replace a substring inside a string with another string.
|
|
||||||
*
|
|
||||||
* May reallocate memory. Free the result with `g_free`.
|
|
||||||
*
|
|
||||||
* @param buf The string to replace
|
|
||||||
* @param pos The position of the substring to replace
|
|
||||||
* @param len The length of the substring to replace
|
|
||||||
* @param repl The new contents of the substring.
|
|
||||||
*/
|
|
||||||
char *string_replace_at(char *buf, int pos, int len, const char *repl);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace all occurences of a substring.
|
|
||||||
*
|
|
||||||
* @param needle The substring to search
|
|
||||||
* @param replacement The substring to replace
|
|
||||||
* @param haystack (nullable) The string to search the substring for
|
|
||||||
*/
|
|
||||||
char *string_replace_all(const char *needle, const char *replacement, char *haystack);
|
char *string_replace_all(const char *needle, const char *replacement, char *haystack);
|
||||||
|
|
||||||
/**
|
/* replace <len> characters with <repl> at position <pos> of the string <buf> */
|
||||||
* Append \p b to string \p a. And concatenate both strings with \p concat, if both are non-empty.
|
char *string_replace_at(char *buf, int pos, int len, const char *repl);
|
||||||
*
|
|
||||||
* @param a (nullable) The left side of the string
|
/* replace needle with replacement in haystack */
|
||||||
* @param b (nullable) The right side of the string
|
char *string_replace(const char *needle, const char *replacement, char *haystack);
|
||||||
* @param sep (nullable) The concatenator to concatenate if a and b given
|
|
||||||
*/
|
|
||||||
char *string_append(char *a, const char *b, const char *sep);
|
char *string_append(char *a, const char *b, const char *sep);
|
||||||
|
|
||||||
/**
|
/* strip content between two delimiter characters (inplace) */
|
||||||
* Strip quotes from a string. Won't touch inner quotes.
|
|
||||||
*
|
|
||||||
* @param value The string to strip the quotes from
|
|
||||||
* @returns A copy of the string value with the outer quotes removed (if any)
|
|
||||||
*/
|
|
||||||
char *string_strip_quotes(const char *value);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Strip content between two delimiter characters
|
|
||||||
*
|
|
||||||
* @param str The string to operate in place
|
|
||||||
* @param a Starting delimiter
|
|
||||||
* @param b Ending delimiter
|
|
||||||
*/
|
|
||||||
void string_strip_delimited(char *str, char a, char b);
|
void string_strip_delimited(char *str, char a, char b);
|
||||||
|
|
||||||
/**
|
/* exit with an error message */
|
||||||
* Parse a comma-delimited string into a dynamic array of tokens
|
void die(char *msg, int exit_value);
|
||||||
*
|
|
||||||
* 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 */
|
||||||
* Replace tilde and path-specific values with its equivalents
|
|
||||||
*
|
|
||||||
* The string gets invalidated. The new valid representation is the return value.
|
|
||||||
*
|
|
||||||
* @param string (nullable) The string to convert to a path.
|
|
||||||
* @returns The tilde-replaced string.
|
|
||||||
*/
|
|
||||||
char *string_to_path(char *string);
|
char *string_to_path(char *string);
|
||||||
|
|
||||||
/**
|
/* convert time units (ms, s, m) to internal gint64 microseconds */
|
||||||
* Convert time units (ms, s, m) to the internal `gint64` microseconds format
|
|
||||||
*
|
|
||||||
* If no unit is given, 's' (seconds) is assumed by default.
|
|
||||||
*
|
|
||||||
* @param string The string to parse the time format from.
|
|
||||||
*/
|
|
||||||
gint64 string_to_time(const char *string);
|
gint64 string_to_time(const char *string);
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current monotonic time. In contrast to `g_get_monotonic_time`,
|
|
||||||
* this function respects the real monotonic time of the system and
|
|
||||||
* counts onwards during system sleep.
|
|
||||||
*
|
|
||||||
* @returns: A `gint64` monotonic time representation
|
|
||||||
*/
|
|
||||||
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
|
#endif
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
|||||||
@ -1,192 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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__ */
|
|
||||||
@ -1,150 +0,0 @@
|
|||||||
#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: */
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
#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: */
|
|
||||||
@ -1,233 +0,0 @@
|
|||||||
/* 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
|
|
||||||
@ -1,68 +0,0 @@
|
|||||||
/* 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,
|
|
||||||
};
|
|
||||||
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@ -1,611 +0,0 @@
|
|||||||
/* 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
|
|
||||||
@ -1,106 +0,0 @@
|
|||||||
/* 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,
|
|
||||||
};
|
|
||||||
|
|
||||||
@ -1,270 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@ -1,559 +0,0 @@
|
|||||||
/* 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
|
|
||||||
@ -1,91 +0,0 @@
|
|||||||
/* 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,
|
|
||||||
};
|
|
||||||
|
|
||||||
@ -1,285 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@ -1,409 +0,0 @@
|
|||||||
/* 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
|
|
||||||
@ -1,78 +0,0 @@
|
|||||||
/* 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,
|
|
||||||
};
|
|
||||||
|
|
||||||
@ -1,181 +0,0 @@
|
|||||||
/* 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
@ -1,917 +0,0 @@
|
|||||||
#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: */
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
#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: */
|
|
||||||
381
src/x11/screen.c
@ -1,5 +1,12 @@
|
|||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
|
|
||||||
|
#include <X11/X.h>
|
||||||
|
#include <X11/Xatom.h>
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xresource.h>
|
||||||
|
#include <X11/extensions/Xinerama.h>
|
||||||
|
#include <X11/extensions/Xrandr.h>
|
||||||
|
#include <X11/extensions/randr.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
@ -7,130 +14,96 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <X11/extensions/randr.h>
|
|
||||||
#include <X11/extensions/Xinerama.h>
|
|
||||||
#include <X11/extensions/Xrandr.h>
|
|
||||||
#include <X11/Xatom.h>
|
|
||||||
#include <X11/X.h>
|
|
||||||
#include <X11/Xlib.h>
|
|
||||||
#include <X11/Xresource.h>
|
|
||||||
|
|
||||||
#include "../log.h"
|
#include "src/settings.h"
|
||||||
#include "../settings.h"
|
|
||||||
#include "../utils.h"
|
|
||||||
#include "x.h"
|
#include "x.h"
|
||||||
|
|
||||||
struct screen_info *screens;
|
screen_info *screens;
|
||||||
int screens_len;
|
int screens_len;
|
||||||
|
|
||||||
bool dunst_follow_errored = false;
|
bool dunst_follow_errored = false;
|
||||||
|
|
||||||
|
int randr_event_base = 0;
|
||||||
|
|
||||||
static int randr_major_version = 0;
|
static int randr_major_version = 0;
|
||||||
static int randr_minor_version = 0;
|
static int randr_minor_version = 0;
|
||||||
|
|
||||||
void randr_init(void);
|
void randr_init();
|
||||||
void randr_update(void);
|
void randr_update();
|
||||||
void xinerama_update(void);
|
void xinerama_update();
|
||||||
void screen_update_fallback(void);
|
void screen_update_fallback();
|
||||||
static void x_follow_setup_error_handler(void);
|
static void x_follow_setup_error_handler(void);
|
||||||
static int x_follow_tear_down_error_handler(void);
|
static int x_follow_tear_down_error_handler(void);
|
||||||
static int FollowXErrorHandler(Display *display, XErrorEvent *e);
|
static int FollowXErrorHandler(Display *display, XErrorEvent *e);
|
||||||
static Window get_focused_window(void);
|
static Window get_focused_window(void);
|
||||||
|
|
||||||
|
static double get_xft_dpi_value()
|
||||||
/**
|
|
||||||
* A cache variable to cache the Xft.dpi xrdb values.
|
|
||||||
* We do not expect to change xrdb often, but there's much
|
|
||||||
* overhead to query it once.
|
|
||||||
*
|
|
||||||
* @retval -DBL_MAX: uncached
|
|
||||||
* @retval <=0: Invalid and unusable value
|
|
||||||
* @retval >0: valid
|
|
||||||
*/
|
|
||||||
double screen_dpi_xft_cache = -DBL_MAX;
|
|
||||||
|
|
||||||
void screen_dpi_xft_cache_purge(void)
|
|
||||||
{
|
{
|
||||||
screen_dpi_xft_cache = -DBL_MAX;
|
static double dpi = -1;
|
||||||
}
|
//Only run this once, we don't expect dpi changes during runtime
|
||||||
|
if (dpi <= -1) {
|
||||||
|
XrmInitialize();
|
||||||
|
char *xRMS = XResourceManagerString(xctx.dpy);
|
||||||
|
|
||||||
static double screen_dpi_get_from_xft(void)
|
if (xRMS == NULL) {
|
||||||
{
|
dpi = 0;
|
||||||
if (screen_dpi_xft_cache == -DBL_MAX) {
|
return 0;
|
||||||
screen_dpi_xft_cache = 0;
|
}
|
||||||
|
|
||||||
|
XrmDatabase xDB = XrmGetStringDatabase(xRMS);
|
||||||
char *xrmType;
|
char *xrmType;
|
||||||
XrmValue xrmValue;
|
XrmValue xrmValue;
|
||||||
XrmDatabase db = XrmGetDatabase(xctx.dpy);
|
|
||||||
ASSERT_OR_RET(db, screen_dpi_xft_cache);
|
if (XrmGetResource(xDB, "Xft.dpi", "Xft.dpi", &xrmType, &xrmValue)) {
|
||||||
if (XrmGetResource(db, "Xft.dpi", "Xft.dpi", &xrmType, &xrmValue))
|
dpi = strtod(xrmValue.addr, NULL);
|
||||||
screen_dpi_xft_cache = strtod(xrmValue.addr, NULL);
|
} else {
|
||||||
|
dpi = 0;
|
||||||
|
}
|
||||||
|
XrmDestroyDatabase(xDB);
|
||||||
}
|
}
|
||||||
return screen_dpi_xft_cache;
|
return dpi;
|
||||||
}
|
}
|
||||||
|
|
||||||
static double screen_dpi_get_from_monitor(const struct screen_info *scr)
|
void init_screens()
|
||||||
{
|
{
|
||||||
return (double)scr->h * 25.4 / (double)scr->mmh;
|
if (!settings.force_xinerama) {
|
||||||
}
|
|
||||||
|
|
||||||
double screen_dpi_get(const struct screen_info *scr)
|
|
||||||
{
|
|
||||||
if ( ! settings.force_xinerama
|
|
||||||
&& settings.per_monitor_dpi)
|
|
||||||
return screen_dpi_get_from_monitor(scr);
|
|
||||||
|
|
||||||
if (screen_dpi_get_from_xft() > 0)
|
|
||||||
return screen_dpi_get_from_xft();
|
|
||||||
|
|
||||||
// Calculate the DPI on the overall screen size.
|
|
||||||
// xrandr --dpi <DPI> does only change the overall screen's millimeters,
|
|
||||||
// but not the physical screen's sizes.
|
|
||||||
//
|
|
||||||
// The screen parameter is XDefaultScreen(), as our scr->id references
|
|
||||||
// the xrandr monitor and not the xrandr screen
|
|
||||||
return ((((double)DisplayWidth (xctx.dpy, XDefaultScreen(xctx.dpy))) * 25.4) /
|
|
||||||
((double)DisplayWidthMM(xctx.dpy, XDefaultScreen(xctx.dpy))));
|
|
||||||
}
|
|
||||||
|
|
||||||
void init_screens(void)
|
|
||||||
{
|
|
||||||
if (settings.force_xinerama) {
|
|
||||||
xinerama_update();
|
|
||||||
} else {
|
|
||||||
randr_init();
|
randr_init();
|
||||||
randr_update();
|
randr_update();
|
||||||
|
} else {
|
||||||
|
xinerama_update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void alloc_screen_ar(int n)
|
void alloc_screen_ar(int n)
|
||||||
{
|
{
|
||||||
assert(n > 0);
|
assert(n > 0);
|
||||||
g_free(screens);
|
if (n <= screens_len) return;
|
||||||
screens = g_malloc0(n * sizeof(struct screen_info));
|
|
||||||
|
screens = g_realloc(screens, n * sizeof(screen_info));
|
||||||
|
|
||||||
|
memset(screens, 0, n * sizeof(screen_info));
|
||||||
|
|
||||||
screens_len = n;
|
screens_len = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
void randr_init(void)
|
void randr_init()
|
||||||
{
|
{
|
||||||
int ignored;
|
int randr_error_base = 0;
|
||||||
if (!XRRQueryExtension(xctx.dpy, &ignored, &ignored)) {
|
if (!XRRQueryExtension(xctx.dpy, &randr_event_base, &randr_error_base)) {
|
||||||
LOG_W("Could not initialize the RandR extension. "
|
fprintf(stderr, "Could not initialize the RandR extension, falling back to single monitor mode.\n");
|
||||||
"Falling back to single monitor mode.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
XRRQueryVersion(xctx.dpy, &randr_major_version, &randr_minor_version);
|
XRRQueryVersion(xctx.dpy, &randr_major_version, &randr_minor_version);
|
||||||
XRRSelectInput(xctx.dpy, RootWindow(xctx.dpy, DefaultScreen(xctx.dpy)), RRScreenChangeNotifyMask);
|
XRRSelectInput(xctx.dpy, RootWindow(xctx.dpy, DefaultScreen(xctx.dpy)), RRScreenChangeNotifyMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
void randr_update(void)
|
void randr_update()
|
||||||
{
|
{
|
||||||
if (randr_major_version < 1
|
if (randr_major_version < 1
|
||||||
|| (randr_major_version == 1 && randr_minor_version < 5)) {
|
|| (randr_major_version == 1 && randr_minor_version < 5)) {
|
||||||
LOG_W("Server RandR version too low (%i.%i). "
|
fprintf(stderr, "Server RandR version too low (%i.%i). Falling back to single monitor mode\n",
|
||||||
"Falling back to single monitor mode.",
|
randr_major_version,
|
||||||
randr_major_version,
|
randr_minor_version);
|
||||||
randr_minor_version);
|
|
||||||
screen_update_fallback();
|
screen_update_fallback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -139,8 +112,7 @@ void randr_update(void)
|
|||||||
XRRMonitorInfo *m = XRRGetMonitors(xctx.dpy, RootWindow(xctx.dpy, DefaultScreen(xctx.dpy)), true, &n);
|
XRRMonitorInfo *m = XRRGetMonitors(xctx.dpy, RootWindow(xctx.dpy, DefaultScreen(xctx.dpy)), true, &n);
|
||||||
|
|
||||||
if (n < 1) {
|
if (n < 1) {
|
||||||
LOG_C("Get monitors reported %i monitors. "
|
fprintf(stderr, "Get monitors reported %i monitors, falling back to single monitor mode\n", n);
|
||||||
"Falling back to single monitor mode.", n);
|
|
||||||
screen_update_fallback();
|
screen_update_fallback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -150,37 +122,34 @@ void randr_update(void)
|
|||||||
alloc_screen_ar(n);
|
alloc_screen_ar(n);
|
||||||
|
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
screens[i].id = i;
|
screens[i].dim.x = m[i].x;
|
||||||
screens[i].x = m[i].x;
|
screens[i].dim.y = m[i].y;
|
||||||
screens[i].y = m[i].y;
|
screens[i].dim.w = m[i].width;
|
||||||
screens[i].w = m[i].width;
|
screens[i].dim.h = m[i].height;
|
||||||
screens[i].h = m[i].height;
|
screens[i].dim.mmh = m[i].mheight;
|
||||||
screens[i].mmh = m[i].mheight;
|
|
||||||
screens[i].dpi = screen_dpi_get(&screens[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
XRRFreeMonitors(m);
|
XRRFreeMonitors(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool screen_check_event(XEvent *ev)
|
static int autodetect_dpi(screen_info *scr)
|
||||||
{
|
{
|
||||||
if (XRRUpdateConfiguration(ev)) {
|
return (double)scr->dim.h * 25.4 / (double)scr->dim.mmh;
|
||||||
LOG_D("XEvent: processing 'RRScreenChangeNotify'");
|
|
||||||
randr_update();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void xinerama_update(void)
|
void screen_check_event(XEvent event)
|
||||||
|
{
|
||||||
|
if (event.type == randr_event_base + RRScreenChangeNotify)
|
||||||
|
randr_update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void xinerama_update()
|
||||||
{
|
{
|
||||||
int n;
|
int n;
|
||||||
XineramaScreenInfo *info = XineramaQueryScreens(xctx.dpy, &n);
|
XineramaScreenInfo *info = XineramaQueryScreens(xctx.dpy, &n);
|
||||||
|
|
||||||
if (!info) {
|
if (!info) {
|
||||||
LOG_W("Could not get xinerama screen info. "
|
fprintf(stderr, "(Xinerama) Could not get screen info, falling back to single monitor mode\n");
|
||||||
"Falling back to single monitor mode.");
|
|
||||||
screen_update_fallback();
|
screen_update_fallback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -188,16 +157,15 @@ void xinerama_update(void)
|
|||||||
alloc_screen_ar(n);
|
alloc_screen_ar(n);
|
||||||
|
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
screens[i].id = i;
|
screens[i].dim.x = info[i].x_org;
|
||||||
screens[i].x = info[i].x_org;
|
screens[i].dim.y = info[i].y_org;
|
||||||
screens[i].y = info[i].y_org;
|
screens[i].dim.h = info[i].height;
|
||||||
screens[i].h = info[i].height;
|
screens[i].dim.w = info[i].width;
|
||||||
screens[i].w = info[i].width;
|
|
||||||
}
|
}
|
||||||
XFree(info);
|
XFree(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
void screen_update_fallback(void)
|
void screen_update_fallback()
|
||||||
{
|
{
|
||||||
alloc_screen_ar(1);
|
alloc_screen_ar(1);
|
||||||
|
|
||||||
@ -207,146 +175,36 @@ void screen_update_fallback(void)
|
|||||||
else
|
else
|
||||||
screen = DefaultScreen(xctx.dpy);
|
screen = DefaultScreen(xctx.dpy);
|
||||||
|
|
||||||
screens[0].w = DisplayWidth(xctx.dpy, screen);
|
screens[0].dim.w = DisplayWidth(xctx.dpy, screen);
|
||||||
screens[0].h = DisplayHeight(xctx.dpy, screen);
|
screens[0].dim.h = DisplayHeight(xctx.dpy, screen);
|
||||||
}
|
|
||||||
|
|
||||||
/* see screen.h */
|
|
||||||
bool have_fullscreen_window(void)
|
|
||||||
{
|
|
||||||
return window_is_fullscreen(get_focused_window());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* X11 ErrorHandler to mainly discard BadWindow parameter error
|
|
||||||
*/
|
|
||||||
static int XErrorHandlerFullscreen(Display *display, XErrorEvent *e)
|
|
||||||
{
|
|
||||||
/* Ignore BadWindow errors. Window may have been gone */
|
|
||||||
if (e->error_code == BadWindow) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
char err_buf[BUFSIZ];
|
|
||||||
XGetErrorText(display, e->error_code, err_buf, BUFSIZ);
|
|
||||||
fputs(err_buf, stderr);
|
|
||||||
fputs("\n", stderr);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* see screen.h */
|
|
||||||
bool window_is_fullscreen(Window window)
|
|
||||||
{
|
|
||||||
bool fs = false;
|
|
||||||
|
|
||||||
ASSERT_OR_RET(window, false);
|
|
||||||
|
|
||||||
Atom has_wm_state = XInternAtom(xctx.dpy, "_NET_WM_STATE", True);
|
|
||||||
if (has_wm_state == None){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
XFlush(xctx.dpy);
|
|
||||||
XSetErrorHandler(XErrorHandlerFullscreen);
|
|
||||||
|
|
||||||
Atom actual_type_return;
|
|
||||||
int actual_format_return;
|
|
||||||
unsigned long bytes_after_return;
|
|
||||||
unsigned char *prop_to_return;
|
|
||||||
unsigned long n_items;
|
|
||||||
int result = XGetWindowProperty(
|
|
||||||
xctx.dpy,
|
|
||||||
window,
|
|
||||||
has_wm_state,
|
|
||||||
0, /* long_offset */
|
|
||||||
sizeof(window), /* long_length */
|
|
||||||
false, /* delete */
|
|
||||||
AnyPropertyType, /* req_type */
|
|
||||||
&actual_type_return,
|
|
||||||
&actual_format_return,
|
|
||||||
&n_items,
|
|
||||||
&bytes_after_return,
|
|
||||||
&prop_to_return);
|
|
||||||
|
|
||||||
XFlush(xctx.dpy);
|
|
||||||
XSync(xctx.dpy, false);
|
|
||||||
XSetErrorHandler(NULL);
|
|
||||||
|
|
||||||
if (result == Success) {
|
|
||||||
for(int i = 0; i < n_items; i++) {
|
|
||||||
Atom atom = ((Atom*) prop_to_return)[i];
|
|
||||||
if (!atom)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
char *s = XGetAtomName(xctx.dpy, atom);
|
|
||||||
if (!s)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (STR_EQ(s, "_NET_WM_STATE_FULLSCREEN"))
|
|
||||||
fs = true;
|
|
||||||
XFree(s);
|
|
||||||
if (fs)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prop_to_return)
|
|
||||||
XFree(prop_to_return);
|
|
||||||
|
|
||||||
return fs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Select the screen on which the Window
|
* Select the screen on which the Window
|
||||||
* should be displayed.
|
* should be displayed.
|
||||||
*/
|
*/
|
||||||
const struct screen_info *get_active_screen(void)
|
screen_info *get_active_screen()
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
bool force_follow_mouse = false;
|
if (settings.monitor > 0 && settings.monitor < screens_len) {
|
||||||
|
ret = settings.monitor;
|
||||||
|
goto sc_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
x_follow_setup_error_handler();
|
||||||
|
|
||||||
if (settings.f_mode == FOLLOW_NONE) {
|
if (settings.f_mode == FOLLOW_NONE) {
|
||||||
if (settings.monitor >= 0 && settings.monitor < screens_len) {
|
ret = XDefaultScreen(xctx.dpy);
|
||||||
ret = settings.monitor;
|
|
||||||
}
|
|
||||||
goto sc_cleanup;
|
goto sc_cleanup;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
int x, y;
|
int x, y;
|
||||||
assert(settings.f_mode == FOLLOW_MOUSE
|
assert(settings.f_mode == FOLLOW_MOUSE
|
||||||
|| settings.f_mode == FOLLOW_KEYBOARD);
|
|| settings.f_mode == FOLLOW_KEYBOARD);
|
||||||
|
|
||||||
x_follow_setup_error_handler();
|
|
||||||
|
|
||||||
Window root =
|
Window root =
|
||||||
RootWindow(xctx.dpy, DefaultScreen(xctx.dpy));
|
RootWindow(xctx.dpy, DefaultScreen(xctx.dpy));
|
||||||
|
|
||||||
if (settings.f_mode == FOLLOW_KEYBOARD) {
|
if (settings.f_mode == FOLLOW_MOUSE) {
|
||||||
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;
|
int dummy;
|
||||||
unsigned int dummy_ui;
|
unsigned int dummy_ui;
|
||||||
Window dummy_win;
|
Window dummy_win;
|
||||||
@ -362,9 +220,24 @@ const struct screen_info *get_active_screen(void)
|
|||||||
&dummy_ui);
|
&dummy_ui);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (settings.f_mode == FOLLOW_KEYBOARD) {
|
||||||
|
|
||||||
|
Window focused = get_focused_window();
|
||||||
|
|
||||||
|
if (focused == 0) {
|
||||||
|
/* something went wrong. Fallback 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++) {
|
for (int i = 0; i < screens_len; i++) {
|
||||||
if (INRECT(x, y, screens[i].x, screens[i].y,
|
if (INRECT(x, y, screens[i].dim.x, screens[i].dim.y,
|
||||||
screens[i].w, screens[i].h)) {
|
screens[i].dim.w, screens[i].dim.h)) {
|
||||||
ret = i;
|
ret = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -372,8 +245,8 @@ const struct screen_info *get_active_screen(void)
|
|||||||
if (ret > 0)
|
if (ret > 0)
|
||||||
goto sc_cleanup;
|
goto sc_cleanup;
|
||||||
|
|
||||||
/* something seems to be wrong. Fall back to default */
|
/* something seems to be wrong. Fallback to default */
|
||||||
ret = 0;
|
ret = XDefaultScreen(xctx.dpy);
|
||||||
goto sc_cleanup;
|
goto sc_cleanup;
|
||||||
}
|
}
|
||||||
sc_cleanup:
|
sc_cleanup:
|
||||||
@ -383,19 +256,50 @@ sc_cleanup:
|
|||||||
return &screens[ret];
|
return &screens[ret];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double get_dpi_for_screen(screen_info *scr)
|
||||||
|
{
|
||||||
|
double dpi = 0;
|
||||||
|
if ((!settings.force_xinerama && settings.per_monitor_dpi &&
|
||||||
|
(dpi = autodetect_dpi(scr))))
|
||||||
|
return dpi;
|
||||||
|
else if ((dpi = get_xft_dpi_value()))
|
||||||
|
return dpi;
|
||||||
|
else
|
||||||
|
return 96;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return the window that currently has
|
* Return the window that currently has
|
||||||
* the keyboard focus.
|
* the keyboard focus.
|
||||||
*/
|
*/
|
||||||
static Window get_focused_window(void)
|
static Window get_focused_window(void)
|
||||||
{
|
{
|
||||||
Window focused;
|
Window focused = 0;
|
||||||
int ignored;
|
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);
|
||||||
|
|
||||||
XGetInputFocus(xctx.dpy, &focused, &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);
|
||||||
|
}
|
||||||
|
|
||||||
if (focused == None || focused == PointerRoot)
|
|
||||||
focused = 0;
|
|
||||||
return focused;
|
return focused;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -420,8 +324,9 @@ static int FollowXErrorHandler(Display *display, XErrorEvent *e)
|
|||||||
dunst_follow_errored = true;
|
dunst_follow_errored = true;
|
||||||
char err_buf[BUFSIZ];
|
char err_buf[BUFSIZ];
|
||||||
XGetErrorText(display, e->error_code, err_buf, BUFSIZ);
|
XGetErrorText(display, e->error_code, err_buf, BUFSIZ);
|
||||||
LOG_W("%s", err_buf);
|
fputs(err_buf, stderr);
|
||||||
|
fputs("\n", stderr);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
|||||||
@ -2,38 +2,30 @@
|
|||||||
#ifndef DUNST_SCREEN_H
|
#ifndef DUNST_SCREEN_H
|
||||||
#define DUNST_SCREEN_H
|
#define DUNST_SCREEN_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
|
|
||||||
#define INRECT(x,y,rx,ry,rw,rh) ((x) >= (rx) && (x) < (rx)+(rw) && (y) >= (ry) && (y) < (ry)+(rh))
|
#define INRECT(x,y,rx,ry,rw,rh) ((x) >= (rx) && (x) < (rx)+(rw) && (y) >= (ry) && (y) < (ry)+(rh))
|
||||||
|
|
||||||
void init_screens(void);
|
typedef struct _dimension_t {
|
||||||
void screen_dpi_xft_cache_purge(void);
|
int x;
|
||||||
bool screen_check_event(XEvent *ev);
|
int y;
|
||||||
|
unsigned int h;
|
||||||
|
unsigned int mmh;
|
||||||
|
unsigned int w;
|
||||||
|
int mask;
|
||||||
|
int negative_width;
|
||||||
|
} dimension_t;
|
||||||
|
|
||||||
const struct screen_info *get_active_screen(void);
|
typedef struct _screen_info {
|
||||||
double screen_dpi_get(const struct screen_info *scr);
|
int scr;
|
||||||
|
dimension_t dim;
|
||||||
|
} screen_info;
|
||||||
|
|
||||||
/**
|
void init_screens();
|
||||||
* Find the currently focused window and check if it's in
|
void screen_check_event(XEvent event);
|
||||||
* fullscreen mode
|
|
||||||
*
|
|
||||||
* @see window_is_fullscreen()
|
|
||||||
* @see get_focused_window()
|
|
||||||
*
|
|
||||||
* @retval true: the focused window is in fullscreen mode
|
|
||||||
* @retval false: otherwise
|
|
||||||
*/
|
|
||||||
bool have_fullscreen_window(void);
|
|
||||||
|
|
||||||
/**
|
screen_info *get_active_screen();
|
||||||
* Check if window is in fullscreen mode
|
double get_dpi_for_screen(screen_info *scr);
|
||||||
*
|
|
||||||
* @param window the x11 window object
|
|
||||||
* @retval true: \p window is in fullscreen mode
|
|
||||||
* @retval false: otherwise
|
|
||||||
*/
|
|
||||||
bool window_is_fullscreen(Window window);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
|||||||
1285
src/x11/x.c
67
src/x11/x.h
@ -2,55 +2,66 @@
|
|||||||
#ifndef DUNST_X_H
|
#ifndef DUNST_X_H
|
||||||
#define DUNST_X_H
|
#define DUNST_X_H
|
||||||
|
|
||||||
#define XLIB_ILLEGAL_ACCESS
|
|
||||||
|
|
||||||
#include <cairo.h>
|
|
||||||
#include <glib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <X11/extensions/scrnsaver.h>
|
|
||||||
#include <X11/X.h>
|
#include <X11/X.h>
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/extensions/scrnsaver.h>
|
||||||
#include "../output.h"
|
#include <glib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
|
|
||||||
struct keyboard_shortcut {
|
#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask)
|
||||||
|
#define FONT_HEIGHT_BORDER 2
|
||||||
|
#define DEFFONT "Monospace-11"
|
||||||
|
|
||||||
|
typedef struct _keyboard_shortcut {
|
||||||
const char *str;
|
const char *str;
|
||||||
KeyCode code;
|
KeyCode code;
|
||||||
KeySym sym;
|
KeySym sym;
|
||||||
KeySym mask;
|
KeySym mask;
|
||||||
bool is_valid;
|
bool is_valid;
|
||||||
};
|
} keyboard_shortcut;
|
||||||
|
|
||||||
// Cyclical dependency
|
typedef struct _xctx {
|
||||||
#include "../settings.h"
|
Atom utf8;
|
||||||
|
|
||||||
struct x_context {
|
|
||||||
Display *dpy;
|
Display *dpy;
|
||||||
|
Window win;
|
||||||
|
bool visible;
|
||||||
|
dimension_t geometry;
|
||||||
|
const char *color_strings[3][3];
|
||||||
XScreenSaverInfo *screensaver_info;
|
XScreenSaverInfo *screensaver_info;
|
||||||
};
|
dimension_t window_dim;
|
||||||
|
unsigned long sep_custom_col;
|
||||||
|
} xctx_t;
|
||||||
|
|
||||||
extern struct x_context xctx;
|
typedef struct _color_t {
|
||||||
|
double r;
|
||||||
|
double g;
|
||||||
|
double b;
|
||||||
|
} color_t;
|
||||||
|
|
||||||
|
extern xctx_t xctx;
|
||||||
|
|
||||||
/* window */
|
/* window */
|
||||||
window x_win_create(void);
|
void x_win_draw(void);
|
||||||
void x_win_destroy(window);
|
void x_win_hide(void);
|
||||||
|
void x_win_show(void);
|
||||||
|
|
||||||
void x_win_show(window);
|
/* shortcut */
|
||||||
void x_win_hide(window);
|
void x_shortcut_init(keyboard_shortcut *shortcut);
|
||||||
|
void x_shortcut_ungrab(keyboard_shortcut *ks);
|
||||||
void x_display_surface(cairo_surface_t *srf, window, const struct dimensions *dim);
|
int x_shortcut_grab(keyboard_shortcut *ks);
|
||||||
|
KeySym x_shortcut_string_to_mask(const char *str);
|
||||||
cairo_t* x_win_get_context(window);
|
|
||||||
|
|
||||||
/* X misc */
|
/* X misc */
|
||||||
bool x_is_idle(void);
|
bool x_is_idle(void);
|
||||||
bool x_setup(void);
|
void x_setup(void);
|
||||||
void x_free(void);
|
void x_free(void);
|
||||||
|
|
||||||
struct geometry x_parse_geometry(const char *geom_str);
|
gboolean x_mainloop_fd_dispatch(GSource *source, GSourceFunc callback,
|
||||||
|
gpointer user_data);
|
||||||
|
gboolean x_mainloop_fd_check(GSource *source);
|
||||||
|
gboolean x_mainloop_fd_prepare(GSource *source, gint *timeout);
|
||||||
|
|
||||||
int x_get_scale(void);
|
|
||||||
#endif
|
#endif
|
||||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
Got'cha! This has to be invalid!
|
|
||||||
@ -1 +0,0 @@
|
|||||||
Got'cha! This has to be invalid!
|
|
||||||
@ -1 +0,0 @@
|
|||||||
../../invalid.png
|
|
||||||
@ -1 +0,0 @@
|
|||||||
../../invalid.svg
|
|
||||||
@ -1 +0,0 @@
|
|||||||
../../valid.png
|
|
||||||
@ -1 +0,0 @@
|
|||||||
../../valid.svg
|
|
||||||
@ -1 +0,0 @@
|
|||||||
../../valid.png
|
|
||||||
@ -1 +0,0 @@
|
|||||||
../../valid.svg
|
|
||||||
|
Before Width: | Height: | Size: 193 B |
@ -1,65 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="16px"
|
|
||||||
height="16px"
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
version="1.1"
|
|
||||||
id="SVGRoot"
|
|
||||||
inkscape:version="0.92.2 5c3e80d, 2017-08-06"
|
|
||||||
sodipodi:docname="valid.svg">
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="16"
|
|
||||||
inkscape:cx="4.6127988"
|
|
||||||
inkscape:cy="8"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:window-width="958"
|
|
||||||
inkscape:window-height="1034"
|
|
||||||
inkscape:window-x="960"
|
|
||||||
inkscape:window-y="46"
|
|
||||||
inkscape:window-maximized="0"
|
|
||||||
inkscape:grid-bbox="true" />
|
|
||||||
<defs
|
|
||||||
id="defs20" />
|
|
||||||
<metadata
|
|
||||||
id="metadata23">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
id="layer1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
inkscape:label="Layer 1">
|
|
||||||
<rect
|
|
||||||
style="opacity:0.98999999;fill:#cccccc;fill-opacity:1;stroke:#cccccc;stroke-width:1;stroke-miterlimit:4.19999981;stroke-dasharray:none;stroke-opacity:0.15686275"
|
|
||||||
id="rect36"
|
|
||||||
width="8"
|
|
||||||
height="15.4375"
|
|
||||||
x="3.75"
|
|
||||||
y="0.25"
|
|
||||||
ry="4.875" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.9 KiB |
@ -22,20 +22,6 @@
|
|||||||
simple = A simple string
|
simple = A simple string
|
||||||
quoted = "A quoted string"
|
quoted = "A quoted string"
|
||||||
quoted_with_quotes = "A string "with quotes""
|
quoted_with_quotes = "A string "with quotes""
|
||||||
unquoted_with_quotes = A" string with quotes"
|
|
||||||
quoted_comment = "String with a" # comment
|
|
||||||
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]
|
[path]
|
||||||
expand_tilde = ~/.path/to/tilde
|
expand_tilde = ~/.path/to/tilde
|
||||||
|
|||||||
878
test/dbus.c
@ -1,878 +0,0 @@
|
|||||||
#define wake_up wake_up_void
|
|
||||||
#include "../src/dbus.c"
|
|
||||||
#include "greatest.h"
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
|
||||||
#include <gio/gio.h>
|
|
||||||
|
|
||||||
#include "helpers.h"
|
|
||||||
#include "queues.h"
|
|
||||||
|
|
||||||
extern const char *base;
|
|
||||||
|
|
||||||
void wake_up_void(void) { }
|
|
||||||
|
|
||||||
struct signal_actioninvoked {
|
|
||||||
guint id;
|
|
||||||
gchar *key;
|
|
||||||
guint subscription_id;
|
|
||||||
GDBusConnection *conn;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct signal_closed {
|
|
||||||
guint32 id;
|
|
||||||
guint32 reason;
|
|
||||||
guint subscription_id;
|
|
||||||
GDBusConnection *conn;
|
|
||||||
};
|
|
||||||
|
|
||||||
void dbus_signal_cb_actioninvoked(GDBusConnection *connection,
|
|
||||||
const gchar *sender_name,
|
|
||||||
const gchar *object_path,
|
|
||||||
const gchar *interface_name,
|
|
||||||
const gchar *signal_name,
|
|
||||||
GVariant *parameters,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
g_return_if_fail(user_data);
|
|
||||||
|
|
||||||
guint32 id;
|
|
||||||
gchar *key;
|
|
||||||
|
|
||||||
struct signal_actioninvoked *sig = (struct signal_actioninvoked*) user_data;
|
|
||||||
|
|
||||||
g_variant_get(parameters, "(us)", &id, &key);
|
|
||||||
|
|
||||||
if (id == sig->id) {
|
|
||||||
sig->id = id;
|
|
||||||
sig->key = key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void dbus_signal_subscribe_actioninvoked(struct signal_actioninvoked *actioninvoked)
|
|
||||||
{
|
|
||||||
assert(actioninvoked);
|
|
||||||
|
|
||||||
actioninvoked->conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
|
|
||||||
actioninvoked->subscription_id =
|
|
||||||
g_dbus_connection_signal_subscribe(
|
|
||||||
actioninvoked->conn,
|
|
||||||
FDN_NAME,
|
|
||||||
FDN_IFAC,
|
|
||||||
"ActionInvoked",
|
|
||||||
FDN_PATH,
|
|
||||||
NULL,
|
|
||||||
G_DBUS_SIGNAL_FLAGS_NONE,
|
|
||||||
dbus_signal_cb_actioninvoked,
|
|
||||||
actioninvoked,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dbus_signal_unsubscribe_actioninvoked(struct signal_actioninvoked *actioninvoked)
|
|
||||||
{
|
|
||||||
assert(actioninvoked);
|
|
||||||
|
|
||||||
g_dbus_connection_signal_unsubscribe(actioninvoked->conn, actioninvoked->subscription_id);
|
|
||||||
g_object_unref(actioninvoked->conn);
|
|
||||||
|
|
||||||
actioninvoked->conn = NULL;
|
|
||||||
actioninvoked->subscription_id = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dbus_signal_cb_closed(GDBusConnection *connection,
|
|
||||||
const gchar *sender_name,
|
|
||||||
const gchar *object_path,
|
|
||||||
const gchar *interface_name,
|
|
||||||
const gchar *signal_name,
|
|
||||||
GVariant *parameters,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
g_return_if_fail(user_data);
|
|
||||||
|
|
||||||
guint32 id;
|
|
||||||
guint32 reason;
|
|
||||||
|
|
||||||
struct signal_closed *sig = (struct signal_closed*) user_data;
|
|
||||||
g_variant_get(parameters, "(uu)", &id, &reason);
|
|
||||||
|
|
||||||
if (id == sig->id) {
|
|
||||||
sig->id = id;
|
|
||||||
sig->reason = reason;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void dbus_signal_subscribe_closed(struct signal_closed *closed)
|
|
||||||
{
|
|
||||||
assert(closed);
|
|
||||||
|
|
||||||
closed->conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
|
|
||||||
closed->subscription_id =
|
|
||||||
g_dbus_connection_signal_subscribe(
|
|
||||||
closed->conn,
|
|
||||||
FDN_NAME,
|
|
||||||
FDN_IFAC,
|
|
||||||
"NotificationClosed",
|
|
||||||
FDN_PATH,
|
|
||||||
NULL,
|
|
||||||
G_DBUS_SIGNAL_FLAGS_NONE,
|
|
||||||
dbus_signal_cb_closed,
|
|
||||||
closed,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dbus_signal_unsubscribe_closed(struct signal_closed *closed)
|
|
||||||
{
|
|
||||||
assert(closed);
|
|
||||||
|
|
||||||
g_dbus_connection_signal_unsubscribe(closed->conn, closed->subscription_id);
|
|
||||||
g_object_unref(closed->conn);
|
|
||||||
|
|
||||||
closed->conn = NULL;
|
|
||||||
closed->subscription_id = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
GVariant *dbus_invoke(const char *method, GVariant *params)
|
|
||||||
{
|
|
||||||
GDBusConnection *connection_client;
|
|
||||||
GVariant *retdata;
|
|
||||||
GError *error = NULL;
|
|
||||||
|
|
||||||
connection_client = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
|
|
||||||
retdata = g_dbus_connection_call_sync(
|
|
||||||
connection_client,
|
|
||||||
FDN_NAME,
|
|
||||||
FDN_PATH,
|
|
||||||
FDN_IFAC,
|
|
||||||
method,
|
|
||||||
params,
|
|
||||||
NULL,
|
|
||||||
G_DBUS_CALL_FLAGS_NONE,
|
|
||||||
-1,
|
|
||||||
NULL,
|
|
||||||
&error);
|
|
||||||
if (error) {
|
|
||||||
printf("Error while calling GTestDBus instance: %s\n", error->message);
|
|
||||||
g_error_free(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_object_unref(connection_client);
|
|
||||||
|
|
||||||
return retdata;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct dbus_notification {
|
|
||||||
const char* app_name;
|
|
||||||
guint replaces_id;
|
|
||||||
const char* app_icon;
|
|
||||||
const char* summary;
|
|
||||||
const char* body;
|
|
||||||
GHashTable *actions;
|
|
||||||
GHashTable *hints;
|
|
||||||
int expire_timeout;
|
|
||||||
};
|
|
||||||
|
|
||||||
void g_free_variant_value(gpointer tofree)
|
|
||||||
{
|
|
||||||
g_variant_unref((GVariant*) tofree);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct dbus_notification *dbus_notification_new(void)
|
|
||||||
{
|
|
||||||
struct dbus_notification *n = g_malloc0(sizeof(struct dbus_notification));
|
|
||||||
n->expire_timeout = -1;
|
|
||||||
n->replaces_id = 0;
|
|
||||||
n->actions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
|
|
||||||
n->hints = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free_variant_value);
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dbus_notification_free(struct dbus_notification *n)
|
|
||||||
{
|
|
||||||
g_hash_table_unref(n->hints);
|
|
||||||
g_hash_table_unref(n->actions);
|
|
||||||
g_free(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool dbus_notification_fire(struct dbus_notification *n, uint *id)
|
|
||||||
{
|
|
||||||
assert(n);
|
|
||||||
assert(id);
|
|
||||||
GVariantBuilder b;
|
|
||||||
GVariantType *t;
|
|
||||||
|
|
||||||
gpointer p_key;
|
|
||||||
gpointer p_value;
|
|
||||||
GHashTableIter iter;
|
|
||||||
|
|
||||||
t = g_variant_type_new("(susssasa{sv}i)");
|
|
||||||
g_variant_builder_init(&b, t);
|
|
||||||
g_variant_type_free(t);
|
|
||||||
|
|
||||||
g_variant_builder_add(&b, "s", n->app_name);
|
|
||||||
g_variant_builder_add(&b, "u", n->replaces_id);
|
|
||||||
g_variant_builder_add(&b, "s", n->app_icon);
|
|
||||||
g_variant_builder_add(&b, "s", n->summary);
|
|
||||||
g_variant_builder_add(&b, "s", n->body);
|
|
||||||
|
|
||||||
// Add the actions
|
|
||||||
t = g_variant_type_new("as");
|
|
||||||
g_variant_builder_open(&b, t);
|
|
||||||
g_hash_table_iter_init(&iter, n->actions);
|
|
||||||
while (g_hash_table_iter_next(&iter, &p_key, &p_value)) {
|
|
||||||
g_variant_builder_add(&b, "s", (char*)p_key);
|
|
||||||
g_variant_builder_add(&b, "s", (char*)p_value);
|
|
||||||
}
|
|
||||||
// Add an invalid appendix to cover odd numbered action arrays
|
|
||||||
// Shouldn't interfere with normal testing
|
|
||||||
g_variant_builder_add(&b, "s", "invalid appendix");
|
|
||||||
g_variant_builder_close(&b);
|
|
||||||
g_variant_type_free(t);
|
|
||||||
|
|
||||||
// Add the hints
|
|
||||||
t = g_variant_type_new("a{sv}");
|
|
||||||
g_variant_builder_open(&b, t);
|
|
||||||
g_hash_table_iter_init(&iter, n->hints);
|
|
||||||
while (g_hash_table_iter_next(&iter, &p_key, &p_value)) {
|
|
||||||
g_variant_builder_add(&b, "{sv}", (char*)p_key, (GVariant*)p_value);
|
|
||||||
}
|
|
||||||
g_variant_builder_close(&b);
|
|
||||||
g_variant_type_free(t);
|
|
||||||
|
|
||||||
g_variant_builder_add(&b, "i", n->expire_timeout);
|
|
||||||
|
|
||||||
GVariant *reply = dbus_invoke("Notify", g_variant_builder_end(&b));
|
|
||||||
if (reply) {
|
|
||||||
g_variant_get(reply, "(u)", id);
|
|
||||||
g_variant_unref(reply);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void dbus_notification_set_raw_image(struct dbus_notification *n_dbus, const char *path)
|
|
||||||
{
|
|
||||||
GVariant *hint = notification_setup_raw_image(path);
|
|
||||||
if (!hint)
|
|
||||||
return;
|
|
||||||
|
|
||||||
g_hash_table_insert(n_dbus->hints,
|
|
||||||
g_strdup("image-data"),
|
|
||||||
g_variant_ref_sink(hint));
|
|
||||||
}
|
|
||||||
|
|
||||||
/////// TESTS
|
|
||||||
gint owner_id;
|
|
||||||
|
|
||||||
TEST test_dbus_init(void)
|
|
||||||
{
|
|
||||||
owner_id = dbus_init();
|
|
||||||
uint waiting = 0;
|
|
||||||
while (!dbus_conn && waiting < 2000) {
|
|
||||||
usleep(500);
|
|
||||||
waiting++;
|
|
||||||
}
|
|
||||||
ASSERTm("After 1s, there is still no dbus connection available.",
|
|
||||||
dbus_conn);
|
|
||||||
PASS();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST test_dbus_teardown(void)
|
|
||||||
{
|
|
||||||
dbus_teardown(owner_id);
|
|
||||||
PASS();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST test_invalid_notification(void)
|
|
||||||
{
|
|
||||||
GVariant *faulty = g_variant_new_boolean(true);
|
|
||||||
|
|
||||||
ASSERT(NULL == dbus_message_to_notification(":123", faulty));
|
|
||||||
ASSERT(NULL == dbus_invoke("Notify", faulty));
|
|
||||||
|
|
||||||
g_variant_unref(faulty);
|
|
||||||
PASS();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST test_empty_notification(void)
|
|
||||||
{
|
|
||||||
struct dbus_notification *n = dbus_notification_new();
|
|
||||||
gsize len = queues_length_waiting();
|
|
||||||
|
|
||||||
guint id;
|
|
||||||
ASSERT(dbus_notification_fire(n, &id));
|
|
||||||
ASSERT(id != 0);
|
|
||||||
|
|
||||||
ASSERT_EQ(queues_length_waiting(), len+1);
|
|
||||||
dbus_notification_free(n);
|
|
||||||
PASS();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST test_basic_notification(void)
|
|
||||||
{
|
|
||||||
struct dbus_notification *n = dbus_notification_new();
|
|
||||||
gsize len = queues_length_waiting();
|
|
||||||
n->app_name = "dunstteststack";
|
|
||||||
n->app_icon = "NONE";
|
|
||||||
n->summary = "Headline";
|
|
||||||
n->body = "Text";
|
|
||||||
g_hash_table_insert(n->actions, g_strdup("actionid"), g_strdup("Print this text"));
|
|
||||||
g_hash_table_insert(n->hints,
|
|
||||||
g_strdup("x-dunst-stack-tag"),
|
|
||||||
g_variant_ref_sink(g_variant_new_string("volume")));
|
|
||||||
|
|
||||||
n->replaces_id = 10;
|
|
||||||
|
|
||||||
guint id;
|
|
||||||
ASSERT(dbus_notification_fire(n, &id));
|
|
||||||
ASSERT(id != 0);
|
|
||||||
|
|
||||||
ASSERT_EQ(queues_length_waiting(), len+1);
|
|
||||||
dbus_notification_free(n);
|
|
||||||
PASS();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST test_dbus_notify_colors(void)
|
|
||||||
{
|
|
||||||
const char *color_frame = "I allow all string values for frame!";
|
|
||||||
const char *color_bg = "I allow all string values for background!";
|
|
||||||
const char *color_fg = "I allow all string values for foreground!";
|
|
||||||
struct notification *n;
|
|
||||||
struct dbus_notification *n_dbus;
|
|
||||||
|
|
||||||
gsize len = queues_length_waiting();
|
|
||||||
|
|
||||||
n_dbus = dbus_notification_new();
|
|
||||||
n_dbus->app_name = "dunstteststack";
|
|
||||||
n_dbus->app_icon = "NONE";
|
|
||||||
n_dbus->summary = "test_dbus_notify_colors";
|
|
||||||
n_dbus->body = "Summary of it";
|
|
||||||
g_hash_table_insert(n_dbus->hints,
|
|
||||||
g_strdup("frcolor"),
|
|
||||||
g_variant_ref_sink(g_variant_new_string(color_frame)));
|
|
||||||
g_hash_table_insert(n_dbus->hints,
|
|
||||||
g_strdup("bgcolor"),
|
|
||||||
g_variant_ref_sink(g_variant_new_string(color_bg)));
|
|
||||||
g_hash_table_insert(n_dbus->hints,
|
|
||||||
g_strdup("fgcolor"),
|
|
||||||
g_variant_ref_sink(g_variant_new_string(color_fg)));
|
|
||||||
|
|
||||||
guint id;
|
|
||||||
ASSERT(dbus_notification_fire(n_dbus, &id));
|
|
||||||
ASSERT(id != 0);
|
|
||||||
|
|
||||||
ASSERT_EQ(queues_length_waiting(), len+1);
|
|
||||||
|
|
||||||
n = queues_debug_find_notification_by_id(id);
|
|
||||||
|
|
||||||
ASSERT_STR_EQ(n->colors.frame, color_frame);
|
|
||||||
ASSERT_STR_EQ(n->colors.fg, color_fg);
|
|
||||||
ASSERT_STR_EQ(n->colors.bg, color_bg);
|
|
||||||
|
|
||||||
dbus_notification_free(n_dbus);
|
|
||||||
|
|
||||||
PASS();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST test_hint_transient(void)
|
|
||||||
{
|
|
||||||
static char msg[50];
|
|
||||||
struct notification *n;
|
|
||||||
struct dbus_notification *n_dbus;
|
|
||||||
|
|
||||||
gsize len = queues_length_waiting();
|
|
||||||
|
|
||||||
n_dbus = dbus_notification_new();
|
|
||||||
n_dbus->app_name = "dunstteststack";
|
|
||||||
n_dbus->app_icon = "NONE";
|
|
||||||
n_dbus->summary = "test_hint_transient";
|
|
||||||
n_dbus->body = "Summary of it";
|
|
||||||
|
|
||||||
bool values[] = { true, true, true, false, false, false, false };
|
|
||||||
GVariant *variants[] = {
|
|
||||||
g_variant_new_boolean(true),
|
|
||||||
g_variant_new_int32(1),
|
|
||||||
g_variant_new_uint32(1),
|
|
||||||
g_variant_new_boolean(false),
|
|
||||||
g_variant_new_int32(0),
|
|
||||||
g_variant_new_uint32(0),
|
|
||||||
g_variant_new_int32(-1),
|
|
||||||
};
|
|
||||||
for (size_t i = 0; i < G_N_ELEMENTS(variants); i++) {
|
|
||||||
g_hash_table_insert(n_dbus->hints,
|
|
||||||
g_strdup("transient"),
|
|
||||||
g_variant_ref_sink(variants[i]));
|
|
||||||
|
|
||||||
guint id;
|
|
||||||
ASSERT(dbus_notification_fire(n_dbus, &id));
|
|
||||||
ASSERT(id != 0);
|
|
||||||
|
|
||||||
ASSERT_EQ(queues_length_waiting(), len+1);
|
|
||||||
|
|
||||||
n = queues_debug_find_notification_by_id(id);
|
|
||||||
|
|
||||||
snprintf(msg, sizeof(msg), "In round %ld", i);
|
|
||||||
ASSERT_EQm(msg, values[i], n->transient);
|
|
||||||
}
|
|
||||||
|
|
||||||
dbus_notification_free(n_dbus);
|
|
||||||
|
|
||||||
PASS();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST test_hint_progress(void)
|
|
||||||
{
|
|
||||||
static char msg[50];
|
|
||||||
struct notification *n;
|
|
||||||
struct dbus_notification *n_dbus;
|
|
||||||
|
|
||||||
gsize len = queues_length_waiting();
|
|
||||||
|
|
||||||
n_dbus = dbus_notification_new();
|
|
||||||
n_dbus->app_name = "dunstteststack";
|
|
||||||
n_dbus->app_icon = "NONE";
|
|
||||||
n_dbus->summary = "test_hint_progress";
|
|
||||||
n_dbus->body = "Summary of it";
|
|
||||||
|
|
||||||
int values[] = { 99, 12, 123, 123, -1, -1 };
|
|
||||||
GVariant *variants[] = {
|
|
||||||
g_variant_new_int32(99),
|
|
||||||
g_variant_new_uint32(12),
|
|
||||||
g_variant_new_int32(123), // allow higher than 100
|
|
||||||
g_variant_new_uint32(123),
|
|
||||||
g_variant_new_int32(-192),
|
|
||||||
g_variant_new_uint32(-192),
|
|
||||||
};
|
|
||||||
for (size_t i = 0; i < G_N_ELEMENTS(variants); i++) {
|
|
||||||
g_hash_table_insert(n_dbus->hints,
|
|
||||||
g_strdup("value"),
|
|
||||||
g_variant_ref_sink(variants[i]));
|
|
||||||
|
|
||||||
guint id;
|
|
||||||
ASSERT(dbus_notification_fire(n_dbus, &id));
|
|
||||||
ASSERT(id != 0);
|
|
||||||
|
|
||||||
ASSERT_EQ(queues_length_waiting(), len+1);
|
|
||||||
|
|
||||||
n = queues_debug_find_notification_by_id(id);
|
|
||||||
|
|
||||||
snprintf(msg, sizeof(msg), "In round %ld", i);
|
|
||||||
ASSERT_EQm(msg, values[i], n->progress);
|
|
||||||
}
|
|
||||||
|
|
||||||
dbus_notification_free(n_dbus);
|
|
||||||
|
|
||||||
PASS();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST test_hint_icons(void)
|
|
||||||
{
|
|
||||||
struct notification *n;
|
|
||||||
struct dbus_notification *n_dbus;
|
|
||||||
const char *iconname = "NEWICON";
|
|
||||||
|
|
||||||
gsize len = queues_length_waiting();
|
|
||||||
|
|
||||||
n_dbus = dbus_notification_new();
|
|
||||||
n_dbus->app_name = "dunstteststack";
|
|
||||||
n_dbus->app_icon = "NONE";
|
|
||||||
n_dbus->summary = "test_hint_icons";
|
|
||||||
n_dbus->body = "Summary of it";
|
|
||||||
|
|
||||||
g_hash_table_insert(n_dbus->hints,
|
|
||||||
g_strdup("image-path"),
|
|
||||||
g_variant_ref_sink(g_variant_new_string(iconname)));
|
|
||||||
|
|
||||||
guint id;
|
|
||||||
ASSERT(dbus_notification_fire(n_dbus, &id));
|
|
||||||
ASSERT(id != 0);
|
|
||||||
|
|
||||||
ASSERT_EQ(queues_length_waiting(), len+1);
|
|
||||||
|
|
||||||
n = queues_debug_find_notification_by_id(id);
|
|
||||||
|
|
||||||
ASSERT_STR_EQ(iconname, n->iconname);
|
|
||||||
|
|
||||||
dbus_notification_free(n_dbus);
|
|
||||||
|
|
||||||
PASS();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST test_hint_category(void)
|
|
||||||
{
|
|
||||||
struct notification *n;
|
|
||||||
struct dbus_notification *n_dbus;
|
|
||||||
const char *category = "VOLUME";
|
|
||||||
|
|
||||||
gsize len = queues_length_waiting();
|
|
||||||
|
|
||||||
n_dbus = dbus_notification_new();
|
|
||||||
n_dbus->app_name = "dunstteststack";
|
|
||||||
n_dbus->app_icon = "NONE";
|
|
||||||
n_dbus->summary = "test_hint_category";
|
|
||||||
n_dbus->body = "Summary of it";
|
|
||||||
|
|
||||||
g_hash_table_insert(n_dbus->hints,
|
|
||||||
g_strdup("category"),
|
|
||||||
g_variant_ref_sink(g_variant_new_string(category)));
|
|
||||||
|
|
||||||
guint id;
|
|
||||||
ASSERT(dbus_notification_fire(n_dbus, &id));
|
|
||||||
ASSERT(id != 0);
|
|
||||||
|
|
||||||
ASSERT_EQ(queues_length_waiting(), len+1);
|
|
||||||
|
|
||||||
n = queues_debug_find_notification_by_id(id);
|
|
||||||
|
|
||||||
ASSERT_STR_EQ(category, n->category);
|
|
||||||
|
|
||||||
dbus_notification_free(n_dbus);
|
|
||||||
|
|
||||||
PASS();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST test_hint_desktop_entry(void)
|
|
||||||
{
|
|
||||||
struct notification *n;
|
|
||||||
struct dbus_notification *n_dbus;
|
|
||||||
const char *desktop_entry = "org.dunst-project.dunst";
|
|
||||||
|
|
||||||
gsize len = queues_length_waiting();
|
|
||||||
|
|
||||||
n_dbus = dbus_notification_new();
|
|
||||||
n_dbus->app_name = "dunstteststack";
|
|
||||||
n_dbus->app_icon = "NONE";
|
|
||||||
n_dbus->summary = "test_hint_desktopentry";
|
|
||||||
n_dbus->body = "Summary of my desktop_entry";
|
|
||||||
|
|
||||||
g_hash_table_insert(n_dbus->hints,
|
|
||||||
g_strdup("desktop-entry"),
|
|
||||||
g_variant_ref_sink(g_variant_new_string(desktop_entry)));
|
|
||||||
|
|
||||||
guint id;
|
|
||||||
ASSERT(dbus_notification_fire(n_dbus, &id));
|
|
||||||
ASSERT(id != 0);
|
|
||||||
|
|
||||||
ASSERT_EQ(queues_length_waiting(), len+1);
|
|
||||||
|
|
||||||
n = queues_debug_find_notification_by_id(id);
|
|
||||||
|
|
||||||
ASSERT_STR_EQ(desktop_entry, n->desktop_entry);
|
|
||||||
|
|
||||||
dbus_notification_free(n_dbus);
|
|
||||||
|
|
||||||
PASS();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST test_hint_urgency(void)
|
|
||||||
{
|
|
||||||
static char msg[50];
|
|
||||||
struct notification *n;
|
|
||||||
struct dbus_notification *n_dbus;
|
|
||||||
|
|
||||||
gsize len = queues_length_waiting();
|
|
||||||
|
|
||||||
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";
|
|
||||||
|
|
||||||
enum urgency values[] = { URG_MAX, URG_LOW, URG_NORM, URG_CRIT };
|
|
||||||
GVariant *variants[] = {
|
|
||||||
g_variant_new_byte(10),
|
|
||||||
g_variant_new_byte(0),
|
|
||||||
g_variant_new_byte(1),
|
|
||||||
g_variant_new_byte(2),
|
|
||||||
};
|
|
||||||
for (size_t i = 0; i < G_N_ELEMENTS(variants); i++) {
|
|
||||||
g_hash_table_insert(n_dbus->hints,
|
|
||||||
g_strdup("urgency"),
|
|
||||||
g_variant_ref_sink(variants[i]));
|
|
||||||
|
|
||||||
guint id;
|
|
||||||
ASSERT(dbus_notification_fire(n_dbus, &id));
|
|
||||||
ASSERT(id != 0);
|
|
||||||
|
|
||||||
ASSERT_EQ(queues_length_waiting(), len+1);
|
|
||||||
|
|
||||||
n = queues_debug_find_notification_by_id(id);
|
|
||||||
|
|
||||||
snprintf(msg, sizeof(msg), "In round %ld", i);
|
|
||||||
ASSERT_EQm(msg, values[i], n->urgency);
|
|
||||||
|
|
||||||
queues_notification_close_id(id, REASON_UNDEF);
|
|
||||||
}
|
|
||||||
|
|
||||||
dbus_notification_free(n_dbus);
|
|
||||||
|
|
||||||
PASS();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST test_hint_raw_image(void)
|
|
||||||
{
|
|
||||||
guint id;
|
|
||||||
struct notification *n;
|
|
||||||
struct dbus_notification *n_dbus;
|
|
||||||
|
|
||||||
char *path = g_strconcat(base, "/data/icons/valid.png", NULL);
|
|
||||||
gsize len = queues_length_waiting();
|
|
||||||
|
|
||||||
n_dbus = dbus_notification_new();
|
|
||||||
dbus_notification_set_raw_image(n_dbus, path);
|
|
||||||
n_dbus->app_name = "dunstteststack";
|
|
||||||
n_dbus->app_icon = "NONE";
|
|
||||||
n_dbus->summary = "test_hint_raw_image";
|
|
||||||
n_dbus->body = "Summary of it";
|
|
||||||
|
|
||||||
ASSERT(dbus_notification_fire(n_dbus, &id));
|
|
||||||
ASSERT(id != 0);
|
|
||||||
|
|
||||||
ASSERT_EQ(queues_length_waiting(), len+1);
|
|
||||||
n = queues_debug_find_notification_by_id(id);
|
|
||||||
|
|
||||||
ASSERT(n->icon);
|
|
||||||
ASSERT(!STR_EQ(n->icon_id, n_dbus->app_icon));
|
|
||||||
|
|
||||||
dbus_notification_free(n_dbus);
|
|
||||||
g_free(path);
|
|
||||||
|
|
||||||
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;
|
|
||||||
GVariant *caps = NULL;
|
|
||||||
const char **capsarray;
|
|
||||||
|
|
||||||
settings.markup = markup;
|
|
||||||
|
|
||||||
reply = dbus_invoke("GetCapabilities", NULL);
|
|
||||||
|
|
||||||
caps = g_variant_get_child_value(reply, 0);
|
|
||||||
capsarray = g_variant_get_strv(caps, NULL);
|
|
||||||
|
|
||||||
ASSERT(capsarray);
|
|
||||||
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)
|
|
||||||
ASSERT(g_strv_contains(capsarray, "body-markup"));
|
|
||||||
else
|
|
||||||
ASSERT_FALSE(g_strv_contains(capsarray, "body-markup"));
|
|
||||||
|
|
||||||
g_free(capsarray);
|
|
||||||
g_variant_unref(caps);
|
|
||||||
g_variant_unref(reply);
|
|
||||||
PASS();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST test_signal_actioninvoked(void)
|
|
||||||
{
|
|
||||||
const struct notification *n;
|
|
||||||
struct dbus_notification *n_dbus;
|
|
||||||
struct signal_actioninvoked sig = {0, NULL, -1};
|
|
||||||
|
|
||||||
dbus_signal_subscribe_actioninvoked(&sig);
|
|
||||||
|
|
||||||
n_dbus = dbus_notification_new();
|
|
||||||
n_dbus->app_name = "dunstteststack";
|
|
||||||
n_dbus->app_icon = "NONE2";
|
|
||||||
n_dbus->summary = "Headline for New";
|
|
||||||
n_dbus->body = "Text";
|
|
||||||
g_hash_table_insert(n_dbus->actions, g_strdup("actionkey"), g_strdup("Print this text"));
|
|
||||||
|
|
||||||
dbus_notification_fire(n_dbus, &sig.id);
|
|
||||||
n = queues_debug_find_notification_by_id(sig.id);
|
|
||||||
|
|
||||||
signal_action_invoked(n, "actionkey");
|
|
||||||
|
|
||||||
uint waiting = 0;
|
|
||||||
while (!sig.key && waiting < 2000) {
|
|
||||||
usleep(500);
|
|
||||||
waiting++;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT_STR_EQ("actionkey", sig.key);
|
|
||||||
|
|
||||||
g_free(sig.key);
|
|
||||||
dbus_notification_free(n_dbus);
|
|
||||||
dbus_signal_unsubscribe_actioninvoked(&sig);
|
|
||||||
PASS();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST test_close_and_signal(void)
|
|
||||||
{
|
|
||||||
GVariant *data, *ret;
|
|
||||||
struct dbus_notification *n;
|
|
||||||
struct signal_closed sig = {0, REASON_MIN-1, -1};
|
|
||||||
|
|
||||||
dbus_signal_subscribe_closed(&sig);
|
|
||||||
|
|
||||||
n = dbus_notification_new();
|
|
||||||
n->app_name = "dunstteststack";
|
|
||||||
n->app_icon = "NONE2";
|
|
||||||
n->summary = "Headline for New";
|
|
||||||
n->body = "Text";
|
|
||||||
|
|
||||||
dbus_notification_fire(n, &sig.id);
|
|
||||||
|
|
||||||
data = g_variant_new("(u)", sig.id);
|
|
||||||
ret = dbus_invoke("CloseNotification", data);
|
|
||||||
|
|
||||||
ASSERT(ret);
|
|
||||||
|
|
||||||
uint waiting = 0;
|
|
||||||
while (sig.reason == REASON_MIN-1 && waiting < 2000) {
|
|
||||||
usleep(500);
|
|
||||||
waiting++;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT(sig.reason != REASON_MIN-1);
|
|
||||||
|
|
||||||
dbus_notification_free(n);
|
|
||||||
dbus_signal_unsubscribe_closed(&sig);
|
|
||||||
g_variant_unref(ret);
|
|
||||||
PASS();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST test_get_fdn_daemon_info(void)
|
|
||||||
{
|
|
||||||
unsigned int pid_is;
|
|
||||||
pid_t pid_should;
|
|
||||||
char *name, *vendor;
|
|
||||||
GDBusConnection *conn;
|
|
||||||
|
|
||||||
pid_should = getpid();
|
|
||||||
conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
|
|
||||||
|
|
||||||
ASSERT(dbus_get_fdn_daemon_info(conn, &pid_is, &name, &vendor));
|
|
||||||
|
|
||||||
ASSERT_EQ_FMT(pid_should, pid_is, "%d");
|
|
||||||
ASSERT_STR_EQ("dunst", name);
|
|
||||||
ASSERT_STR_EQ("knopwob", vendor);
|
|
||||||
|
|
||||||
g_free(name);
|
|
||||||
g_free(vendor);
|
|
||||||
|
|
||||||
g_object_unref(conn);
|
|
||||||
PASS();
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST assert_methodlists_sorted(void)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i+1 < G_N_ELEMENTS(methods_fdn); i++) {
|
|
||||||
ASSERT(0 > strcmp(
|
|
||||||
methods_fdn[i].method_name,
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// TESTS END
|
|
||||||
|
|
||||||
GMainLoop *loop;
|
|
||||||
GThread *thread_tests;
|
|
||||||
|
|
||||||
gpointer run_threaded_tests(gpointer data)
|
|
||||||
{
|
|
||||||
RUN_TEST(test_dbus_init);
|
|
||||||
|
|
||||||
RUN_TEST(test_get_fdn_daemon_info);
|
|
||||||
|
|
||||||
RUN_TEST(test_empty_notification);
|
|
||||||
RUN_TEST(test_basic_notification);
|
|
||||||
RUN_TEST(test_invalid_notification);
|
|
||||||
RUN_TEST(test_hint_transient);
|
|
||||||
RUN_TEST(test_hint_progress);
|
|
||||||
RUN_TEST(test_hint_icons);
|
|
||||||
RUN_TEST(test_hint_category);
|
|
||||||
RUN_TEST(test_hint_desktop_entry);
|
|
||||||
RUN_TEST(test_hint_urgency);
|
|
||||||
RUN_TEST(test_hint_raw_image);
|
|
||||||
RUN_TEST(test_dbus_notify_colors);
|
|
||||||
RUN_TESTp(test_server_caps, MARKUP_FULL);
|
|
||||||
RUN_TESTp(test_server_caps, MARKUP_STRIP);
|
|
||||||
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);
|
|
||||||
|
|
||||||
RUN_TEST(test_dbus_teardown);
|
|
||||||
g_main_loop_quit(loop);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
SUITE(suite_dbus)
|
|
||||||
{
|
|
||||||
settings.icon_path = "";
|
|
||||||
|
|
||||||
GTestDBus *dbus_bus;
|
|
||||||
g_test_dbus_unset();
|
|
||||||
queues_init();
|
|
||||||
|
|
||||||
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);
|
|
||||||
g_main_loop_run(loop);
|
|
||||||
|
|
||||||
queues_teardown();
|
|
||||||
g_test_dbus_down(dbus_bus);
|
|
||||||
g_object_unref(dbus_bus);
|
|
||||||
g_thread_unref(thread_tests);
|
|
||||||
g_main_loop_unref(loop);
|
|
||||||
|
|
||||||
settings.icon_path = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
|
||||||
32
test/dunst.c
@ -1,32 +0,0 @@
|
|||||||
#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};
|
|
||||||
|
|
||||||
dunst_status(S_FULLSCREEN, true);
|
|
||||||
ASSERT(status.fullscreen);
|
|
||||||
dunst_status(S_IDLE, true);
|
|
||||||
ASSERT(status.idle);
|
|
||||||
dunst_status(S_RUNNING, true);
|
|
||||||
ASSERT(status.running);
|
|
||||||
|
|
||||||
PASS();
|
|
||||||
}
|
|
||||||
|
|
||||||
SUITE(suite_dunst)
|
|
||||||
{
|
|
||||||
RUN_TEST(test_dunst_status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
[global]
|
[global]
|
||||||
font = Monospace 8
|
font = Monospace 8
|
||||||
allow_markup = no
|
allow_markup = no
|
||||||
format = "%s\n%b"
|
format = "<b>%s</b>\n<i>%b</i>"
|
||||||
sort = yes
|
sort = yes
|
||||||
indicate_hidden = yes
|
indicate_hidden = yes
|
||||||
alignment = left
|
alignment = left
|
||||||
|
|||||||