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:
|
||||
`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`
|
||||
|
||||
* 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
|
||||
*.d
|
||||
*.gcda
|
||||
*.gcno
|
||||
*.gcov
|
||||
/lcov.info
|
||||
|
||||
core
|
||||
vgcore.*
|
||||
|
||||
/docs/*.1
|
||||
/docs/*.5
|
||||
/docs/internal/coverage
|
||||
/docs/internal/html
|
||||
/dunst
|
||||
/dunstify
|
||||
/dunst.systemd.service
|
||||
/org.knopwob.dunst.service
|
||||
/test/test
|
||||
dunst.1
|
||||
org.knopwob.dunst.service
|
||||
dunst.systemd.service
|
||||
dunstify
|
||||
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
|
||||
|
||||
### Added
|
||||
### Changed
|
||||
### Fixed
|
||||
|
||||
## 1.6.1 - 2021-02-21:
|
||||
- `ellipsize` option to control how long lines should be ellipsized when `word_wrap` is set to `false`
|
||||
|
||||
### 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:
|
||||
|
||||
### 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)
|
||||
## Changed
|
||||
- transient hints are now handled
|
||||
An additional rule option (`match_transient` and `set_transient`) is added
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
|
||||
VERSION := "1.6.1-non-git"
|
||||
ifneq ($(wildcard ./.git/),)
|
||||
VERSION := $(shell ${GIT} describe --tags)
|
||||
VERSION := "1.2.0-non-git"
|
||||
ifneq ($(wildcard ./.git/.),)
|
||||
VERSION := $(shell git describe --tags)
|
||||
endif
|
||||
|
||||
ifeq (,${SYSTEMD})
|
||||
# Check for systemctl to avoid discrepancies on systems, where
|
||||
# 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})
|
||||
LIBS := $(shell pkg-config --libs ${pkg_config_packs})
|
||||
INCS := $(shell pkg-config --cflags ${pkg_config_packs})
|
||||
|
||||
ifneq (clean, $(MAKECMDGOALS))
|
||||
ifeq ($(and $(INCS),$(LIBS)),)
|
||||
$(error "$(PKG_CONFIG) failed!")
|
||||
$(error "pkg-config failed!")
|
||||
endif
|
||||
endif
|
||||
|
||||
CFLAGS := ${DEFAULT_CPPFLAGS} ${CPPFLAGS} ${DEFAULT_CFLAGS} ${CFLAGS} ${INCS} -MMD -MP
|
||||
LDFLAGS := ${DEFAULT_LDFLAGS} ${LDFLAGS} ${LIBS}
|
||||
CFLAGS += -I. ${INCS}
|
||||
LDFLAGS+= -L. ${LIBS}
|
||||
|
||||
|
||||
ifeq (0,${WAYLAND})
|
||||
# without wayland support
|
||||
SRC := $(sort $(shell ${FIND} src/ -not \( -path src/wayland -prune \) -name '*.c'))
|
||||
else
|
||||
# with Wayland support
|
||||
SRC := $(sort $(shell ${FIND} src/ -name '*.c'))
|
||||
endif
|
||||
SRC := $(sort $(shell find src/ -name '*.c'))
|
||||
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)
|
||||
DEPS := ${SRC:.c=.d} ${TEST_SRC:.c=.d}
|
||||
|
||||
|
||||
.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: CPPFLAGS += ${CPPFLAGS_DEBUG}
|
||||
debug: all
|
||||
|
||||
-include $(DEPS)
|
||||
|
||||
${OBJ} ${TEST_OBJ}: Makefile config.mk
|
||||
|
||||
%.o: %.c
|
||||
.c.o:
|
||||
${CC} -o $@ -c $< ${CFLAGS}
|
||||
|
||||
${OBJ}: config.mk
|
||||
|
||||
dunst: ${OBJ} main.o
|
||||
${CC} -o ${@} ${OBJ} main.o ${CFLAGS} ${LDFLAGS}
|
||||
${CC} ${CFLAGS} -o $@ ${OBJ} main.o ${LDFLAGS}
|
||||
|
||||
dunstify: dunstify.o
|
||||
${CC} -o ${@} dunstify.o ${CFLAGS} ${LDFLAGS}
|
||||
${CC} ${CFLAGS} -o $@ dunstify.o ${LDFLAGS}
|
||||
|
||||
.PHONY: test test-valgrind test-coverage
|
||||
test: test/test clean-coverage-run
|
||||
# Make sure an error code is returned when the test fails
|
||||
/usr/bin/env bash -c 'set -euo pipefail;\
|
||||
./test/test -v | ./test/greenest.awk '
|
||||
|
||||
test-valgrind: test/test
|
||||
${VALGRIND} \
|
||||
--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}
|
||||
.PHONY: test
|
||||
test: test/test
|
||||
cd test && ./test
|
||||
|
||||
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
|
||||
doc: docs/dunst.1 docs/dunst.5 docs/dunstctl.1
|
||||
.PHONY: doc
|
||||
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
|
||||
# pod2man :(
|
||||
docs/dunst.1: docs/dunst.1.pod
|
||||
${POD2MAN} --name=dunst -c "Dunst Reference" --section=1 --release=${VERSION} $< > $@
|
||||
docs/dunst.5: docs/dunst.5.pod
|
||||
${POD2MAN} --name=dunst -c "Dunst Reference" --section=5 --release=${VERSION} $< > $@
|
||||
docs/dunstctl.1: docs/dunstctl.pod
|
||||
${POD2MAN} --name=dunstctl -c "dunstctl reference" --section=1 --release=${VERSION} $< > $@
|
||||
.PHONY: service
|
||||
service:
|
||||
@sed "s|##PREFIX##|$(PREFIX)|" org.knopwob.dunst.service.in > org.knopwob.dunst.service
|
||||
@sed "s|##PREFIX##|$(PREFIX)|" dunst.systemd.service.in > dunst.systemd.service
|
||||
|
||||
doc-doxygen:
|
||||
${DOXYGEN} docs/internal/Doxyfile
|
||||
|
||||
.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
|
||||
.PHONY: clean clean-dunst clean-dunstify clean-doc clean-tests
|
||||
clean: clean-dunst clean-dunstify clean-doc clean-tests
|
||||
|
||||
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 dunst.systemd.service
|
||||
|
||||
clean-dunstify:
|
||||
rm -f dunstify.o
|
||||
rm -f dunstify.d
|
||||
rm -f dunstify
|
||||
|
||||
clean-doc:
|
||||
rm -f docs/dunst.1
|
||||
rm -f docs/dunst.5
|
||||
rm -f docs/dunstctl.1
|
||||
rm -fr docs/internal/html
|
||||
rm -fr docs/internal/coverage
|
||||
|
||||
clean-tests:
|
||||
rm -f test/test test/*.o test/*.d
|
||||
rm -f test/test test/*.o
|
||||
|
||||
clean-coverage: clean-coverage-run
|
||||
${FIND} . -type f -name '*.gcno' -delete
|
||||
${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
|
||||
.PHONY: install install-dunst install-doc install-service uninstall
|
||||
install: install-dunst install-doc install-service
|
||||
|
||||
install-dunst: dunst doc
|
||||
install -Dm755 dunst ${DESTDIR}${BINDIR}/dunst
|
||||
install -Dm644 docs/dunst.1 ${DESTDIR}${MANPREFIX}/man1/dunst.1
|
||||
install -Dm644 docs/dunst.5 ${DESTDIR}${MANPREFIX}/man5/dunst.5
|
||||
install -Dm644 docs/dunstctl.1 ${DESTDIR}${MANPREFIX}/man1/dunstctl.1
|
||||
|
||||
install-dunstctl: dunstctl
|
||||
install -Dm755 dunstctl ${DESTDIR}${BINDIR}/dunstctl
|
||||
mkdir -p ${DESTDIR}${PREFIX}/bin
|
||||
install -m755 dunst ${DESTDIR}${PREFIX}/bin
|
||||
mkdir -p ${DESTDIR}${MANPREFIX}/man1
|
||||
install -m644 docs/dunst.1 ${DESTDIR}${MANPREFIX}/man1
|
||||
|
||||
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-dbus: service-dbus
|
||||
install -Dm644 org.knopwob.dunst.service ${DESTDIR}${SERVICEDIR_DBUS}/org.knopwob.dunst.service
|
||||
ifneq (0,${SYSTEMD})
|
||||
install-service: install-service-systemd
|
||||
install-service-systemd: service-systemd
|
||||
install -Dm644 dunst.systemd.service ${DESTDIR}${SERVICEDIR_SYSTEMD}/dunst.service
|
||||
endif
|
||||
install-service: service
|
||||
mkdir -p ${DESTDIR}${PREFIX}/share/dbus-1/services/
|
||||
install -m644 org.knopwob.dunst.service ${DESTDIR}${PREFIX}/share/dbus-1/services
|
||||
install -Dm644 dunst.systemd.service ${DESTDIR}${PREFIX}/lib/systemd/user/dunst.service
|
||||
|
||||
install-dunstify: dunstify
|
||||
install -Dm755 dunstify ${DESTDIR}${BINDIR}/dunstify
|
||||
|
||||
uninstall: uninstall-service uninstall-dunstctl
|
||||
rm -f ${DESTDIR}${BINDIR}/dunst
|
||||
rm -f ${DESTDIR}${BINDIR}/dunstify
|
||||
uninstall:
|
||||
rm -f ${DESTDIR}${PREFIX}/bin/dunst
|
||||
rm -f ${DESTDIR}${MANPREFIX}/man1/dunst.1
|
||||
rm -f ${DESTDIR}${MANPREFIX}/man5/dunst.5
|
||||
rm -f ${DESTDIR}${MANPREFIX}/man1/dunstctl.1
|
||||
rm -rf ${DESTDIR}${SYSCONFDIR}/dunst
|
||||
|
||||
uninstall-dunstctl:
|
||||
rm -f ${DESTDIR}${BINDIR}/dunstctl
|
||||
|
||||
uninstall-service: uninstall-service-dbus
|
||||
uninstall-service-dbus:
|
||||
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
|
||||
rm -f ${DESTDIR}${PREFIX}/share/dbus-1/services/org.knopwob.dunst.service
|
||||
rm -f ${DESTDIR}${PREFIX}/lib/systemd/user/dunst.service
|
||||
rm -rf ${DESTDIR}${PREFIX}/share/dunst
|
||||
|
||||
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>
|
||||
|
||||

|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Features](#features)
|
||||
* [Building](#building)
|
||||
* [Documentation](#documentation)
|
||||
* [Wiki][wiki]
|
||||
* [Description](#description)
|
||||
* [Compiling](#compiling)
|
||||
* [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">
|
||||
<img alt="screenshot1" src="contrib/screenshots/screenshot1_cut.png">
|
||||
</a>
|
||||
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):
|
||||
|
||||
<a href="https://gist.github.com/fwSmit/9127d988b07bcec9d869f2c927d0f616">
|
||||
<img alt="screenshot2" src="contrib/screenshots/screenshot2_cut.png">
|
||||
</a>
|
||||
|
||||
## 📜 Scripting
|
||||
|
||||
<a href="https://gitlab.manjaro.org/profiles-and-settings/manjaro-theme-settings/-/blob/master/skel/.config/dunst/dunstrc">
|
||||
<img alt="screenshot_urgency" src="contrib/screenshots/screenshot_urgency.png">
|
||||
</a>
|
||||
|
||||
Run custom scripts on notifications matching a specified pattern. Have espeak
|
||||
read out your notifications, or play a song when your significant other signs on
|
||||
in pidgin!
|
||||
|
||||
## 📋 Rules
|
||||
|
||||
Change the look or behavior of notifications matching a specified pattern. You
|
||||
could use this to change the color of message notifications from your favorite
|
||||
jabber buddies, or to prevent important work email notifications from
|
||||
disappearing until you manually dismiss them.
|
||||
|
||||
## ⏸️ Pause
|
||||
|
||||
If you want to take a break and not receive any notifications for a while, just
|
||||
pause dunst. All notifications will be saved for you to catch up
|
||||
later.
|
||||
|
||||
## 🕘 History
|
||||
|
||||
Catch an unread notification disappearing from the corner of your eye? Just tap
|
||||
a keyboard shortcut to replay the last notification, or continue tapping to see
|
||||
your notification history.
|
||||
|
||||
# Documentation
|
||||
|
||||
Most documentation can be found in dunst's man pages. In
|
||||
[**dunst(1)**](docs/dunst.1.pod) contains some general instructions on how
|
||||
to run dunst and in
|
||||
[**dunst(5)**](docs/dunst.5.pod) all of dunst's configuration options are
|
||||
explained.
|
||||
|
||||
On the dunst [wiki][wiki] you can find guides and installation instructions and
|
||||
on the dunst [website][website] there is a [FAQ][FAQ] with common issues.
|
||||
|
||||
## Installation
|
||||
|
||||
Dunst is available in many package repositories. If it's not available in your
|
||||
distro's repositories, don't worry, it's not hard to build it yourself.
|
||||
|
||||
### Dependencies
|
||||
|
||||
- dbus (runtime)
|
||||
- dbus
|
||||
- libxinerama
|
||||
- libxrandr
|
||||
- libxss
|
||||
- libxdg-basedir
|
||||
- glib
|
||||
- pango/cairo
|
||||
- libnotify (optional, for dunstify)
|
||||
- wayland-client (can build without, see [make parameters](#make-parameters))
|
||||
- wayland-protocols (optional, for recompiling protocols)
|
||||
- libgtk-3-dev
|
||||
|
||||
The names will be different depending on your [distribution](https://github.com/dunst-project/dunst/wiki/Dependencies).
|
||||
|
||||
### 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.**
|
||||
Checkout the [wiki][wiki] for more information.
|
||||
|
||||
## Bug reports
|
||||
|
||||
Please use the [issue tracker][issue-tracker] provided by GitHub to send us bug reports or feature requests.
|
||||
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">
|
||||
<img alt="screenshot3" src="contrib/screenshots/screenshot3_cut.png">
|
||||
</a>
|
||||
Nikos Tsipinakis <nikos@tsipinakis.com>
|
||||
|
||||
<a href="https://gitlab.manjaro.org/profiles-and-settings/manjaro-theme-settings/-/blob/master/skel/.config/dunst/dunstrc">
|
||||
<img alt="progress" src="https://user-images.githubusercontent.com/23078054/102542111-98b01e00-40b1-11eb-967e-bc952430bd06.png">
|
||||
</a>
|
||||
|
||||
## Maintainers
|
||||
|
||||
- [Nikos Tsipinakis](https://github.com/tsipinakis) <nikos@tsipinakis.com>
|
||||
- [Benedikt Heine](https://github.com/bebehei) <bebe@bebehei.de>
|
||||
Jonathan Lusso <jonilusso@gmail.com>
|
||||
|
||||
## Author
|
||||
|
||||
Written by Sascha Kruse <dunst@knopwob.de>
|
||||
written by Sascha Kruse <dunst@knopwob.de>
|
||||
|
||||
## Copyright
|
||||
|
||||
Copyright 2013 Sascha Kruse and contributors (see [`LICENSE`](./LICENSE) for licensing information)
|
||||
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
|
||||
[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
|
||||
===================================================================================
|
||||
|
||||
94
config.h
@ -1,33 +1,22 @@
|
||||
/* see example dunstrc for additional explanations about these options */
|
||||
|
||||
struct settings defaults = {
|
||||
settings_t defaults = {
|
||||
|
||||
.font = "-*-terminus-medium-r-*-*-16-*-*-*-*-*-*-*",
|
||||
.markup = MARKUP_NO,
|
||||
.colors_norm.bg = "#1793D1",
|
||||
.colors_norm.fg = "#DDDDDD",
|
||||
.colors_norm.highlight = "#1745d1",
|
||||
.colors_crit.bg = "#ffaaaa",
|
||||
.colors_crit.fg = "#000000",
|
||||
.colors_crit.highlight = "#ff6666",
|
||||
.colors_low.bg = "#aaaaff",
|
||||
.colors_low.fg = "#000000",
|
||||
.colors_low.highlight = "#7f7fff",
|
||||
.normbgcolor = "#1793D1",
|
||||
.normfgcolor = "#DDDDDD",
|
||||
.critbgcolor = "#ffaaaa",
|
||||
.critfgcolor = "#000000",
|
||||
.lowbgcolor = "#aaaaff",
|
||||
.lowfgcolor = "#000000",
|
||||
.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 */
|
||||
|
||||
.transparency = 0, /* transparency */
|
||||
.geometry = { .x = 0, /* geometry */
|
||||
.y = 0,
|
||||
.w = 0,
|
||||
.h = 0,
|
||||
.negative_x = 0,
|
||||
.negative_y = 0,
|
||||
.negative_width = 0,
|
||||
.width_set = 0
|
||||
},
|
||||
.geom = "0x0", /* geometry */
|
||||
.title = "Dunst", /* the title of dunst notification windows */
|
||||
.class = "Dunst", /* the class of dunst notification windows */
|
||||
.shrink = false, /* shrinking */
|
||||
@ -35,27 +24,21 @@ struct settings defaults = {
|
||||
.indicate_hidden = true, /* show count of hidden messages */
|
||||
.idle_threshold = 0, /* don't timeout notifications when idle for x seconds */
|
||||
.show_age_threshold = -1, /* show age of notification, when notification is older than x seconds */
|
||||
.align = ALIGN_LEFT, /* text alignment ALIGN_[LEFT|CENTER|RIGHT] */
|
||||
.vertical_alignment = VERTICAL_CENTER, /* vertical content alignment VERTICAL_[TOP|CENTER|BOTTOM] */
|
||||
.align = left, /* text alignment [left/center/right] */
|
||||
.sticky_history = true,
|
||||
.history_length = 20, /* max amount of notifications kept in history */
|
||||
.show_indicators = true,
|
||||
.word_wrap = false,
|
||||
.ignore_dbusclose = false,
|
||||
.ellipsize = ELLIPSE_MIDDLE,
|
||||
.ellipsize = middle,
|
||||
.ignore_newline = false,
|
||||
.line_height = 0, /* if line height < font height, it will be raised to font height */
|
||||
.notification_height = 0, /* if notification height < font height and padding, it will be raised */
|
||||
.corner_radius = 0,
|
||||
|
||||
.force_xinerama = false,
|
||||
.force_xwayland = false,
|
||||
|
||||
.separator_height = 2, /* height of the separator line between two notifications */
|
||||
.padding = 0,
|
||||
.h_padding = 0, /* horizontal padding */
|
||||
.text_icon_padding = 0, /* padding between icon and text*/
|
||||
.sep_color = {SEP_AUTO}, /* SEP_AUTO, SEP_FOREGROUND, SEP_FRAME, SEP_CUSTOM */
|
||||
.sep_color = AUTO, /* AUTO, FOREGROUND, FRAME, CUSTOM */
|
||||
.sep_custom_color_str = NULL,/* custom color if sep_color is set to CUSTOM */
|
||||
|
||||
.frame_width = 0,
|
||||
.frame_color = "#888888",
|
||||
@ -74,7 +57,6 @@ struct settings defaults = {
|
||||
|
||||
.browser = "/usr/bin/firefox",
|
||||
|
||||
.min_icon_size = 0,
|
||||
.max_icon_size = 0,
|
||||
|
||||
/* paths to default icons */
|
||||
@ -111,26 +93,9 @@ struct settings defaults = {
|
||||
.code = 0,.sym = NoSymbol,.is_valid = false
|
||||
}, /* 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
|
||||
* the rule in dunstrc to override it there
|
||||
*/
|
||||
@ -147,16 +112,37 @@ struct rule default_rules[] = {
|
||||
.timeout = -1,
|
||||
.urgency = -1,
|
||||
.markup = MARKUP_NULL,
|
||||
.history_ignore = -1,
|
||||
.match_transient = -1,
|
||||
.history_ignore = 1,
|
||||
.match_transient = 1,
|
||||
.set_transient = -1,
|
||||
.skip_display = -1,
|
||||
.new_icon = NULL,
|
||||
.fg = NULL,
|
||||
.bg = NULL,
|
||||
.format = 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
|
||||
PREFIX ?= /usr/local
|
||||
BINDIR ?= ${PREFIX}/bin
|
||||
SYSCONFDIR ?= /etc
|
||||
DATADIR ?= ${PREFIX}/share
|
||||
# around for backwards compatibility
|
||||
MANPREFIX ?= ${DATADIR}/man
|
||||
MANDIR ?= ${MANPREFIX}
|
||||
EXTRA_CFLAGS ?=
|
||||
|
||||
DOXYGEN ?= doxygen
|
||||
FIND ?= find
|
||||
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
|
||||
MANPREFIX = ${PREFIX}/share/man
|
||||
|
||||
# uncomment to disable parsing of dunstrc
|
||||
# or use "CFLAGS=-DSTATIC_CONFIG make" to build
|
||||
#STATIC= -DSTATIC_CONFIG # Warning: This is deprecated behavior
|
||||
|
||||
# flags
|
||||
DEFAULT_CPPFLAGS = -D_DEFAULT_SOURCE -DVERSION=\"${VERSION}\"
|
||||
DEFAULT_CFLAGS = -g --std=gnu99 -pedantic -Wall -Wno-overlength-strings -Os ${STATIC} ${ENABLE_WAYLAND} ${EXTRA_CFLAGS}
|
||||
DEFAULT_LDFLAGS = -lm -lrt
|
||||
CPPFLAGS += -D_DEFAULT_SOURCE -DVERSION=\"${VERSION}\"
|
||||
CFLAGS += -g --std=gnu99 -pedantic -Wall -Wno-overlength-strings -Os ${STATIC} ${CPPFLAGS}
|
||||
LDFLAGS += -lm -L${X11LIB}
|
||||
|
||||
CPPFLAGS_DEBUG := -DDEBUG_BUILD
|
||||
CFLAGS_DEBUG := -O0
|
||||
LDFLAGS_DEBUG :=
|
||||
|
||||
pkg_config_packs := gio-2.0 \
|
||||
gdk-pixbuf-2.0 \
|
||||
"glib-2.0 >= 2.44" \
|
||||
pkg_config_packs := dbus-1 \
|
||||
gio-2.0 \
|
||||
gdk-3.0 \
|
||||
"glib-2.0 >= 2.36" \
|
||||
pangocairo \
|
||||
x11 \
|
||||
xinerama \
|
||||
xext \
|
||||
"xrandr >= 1.5" \
|
||||
xscrnsaver \
|
||||
xscrnsaver
|
||||
|
||||
|
||||
# dunstify also needs libnotify
|
||||
pkg_config_packs += libnotify
|
||||
|
||||
ifneq (0,${WAYLAND})
|
||||
pkg_config_packs += wayland-client
|
||||
endif
|
||||
|
||||
ifneq (,$(findstring STATIC_CONFIG,$(CFLAGS)))
|
||||
# check if we need libxdg-basedir
|
||||
ifeq (,$(findstring STATIC_CONFIG,$(CFLAGS)))
|
||||
pkg_config_packs += libxdg-basedir
|
||||
else
|
||||
$(warning STATIC_CONFIG is deprecated behavior. It will get removed in future releases)
|
||||
endif
|
||||
|
||||
# dunstify also needs libnotify
|
||||
ifneq (,$(findstring dunstify,${MAKECMDGOALS}))
|
||||
pkg_config_packs += libnotify
|
||||
endif
|
||||
|
||||
@ -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
|
||||
|
||||
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
|
||||
|
||||
An example configuration file is included (usually /etc/dunst/dunstrc). Note:
|
||||
this was previously /usr/share/dunst/dunstrc.
|
||||
Dunst is a highly configurable and lightweight notification daemon.
|
||||
|
||||
=head1 COMMAND LINE OPTIONS
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<-h/--help>
|
||||
|
||||
List all command line flags
|
||||
|
||||
=item B<-conf/-config file>
|
||||
|
||||
Use alternative config file.
|
||||
|
||||
=item B<-v/--version>
|
||||
|
||||
Print version information.
|
||||
|
||||
=item B<-print>
|
||||
|
||||
Print notifications to stdout. This might be useful for logging, setting up
|
||||
rules or using the output in other scripts.
|
||||
|
||||
=back
|
||||
|
||||
=head1 CONFIGURATION
|
||||
|
||||
An example configuration file is included (usually /usr/share/dunst/dunstrc).
|
||||
To change the configuration, copy this file to ~/.config/dunst/dunstrc and edit
|
||||
it accordingly.
|
||||
|
||||
The configuration is divided into sections in an ini-like format. The 'global'
|
||||
section contains most general settings while the setions 'urgency_low',
|
||||
'urgency_normal' and 'urgency_critical' are for low, normal and critical urgency
|
||||
notifications respectively. The 'shortcuts' section (deprecated) contains all
|
||||
keyboard configuration and the 'experimental' section all the features that have
|
||||
not yet been tested thoroughly.
|
||||
section contains most general settings while the 'shortcuts' sections contains
|
||||
all keyboard configuration and the 'experimental' section all the features that
|
||||
have not yet been tested thoroughly.
|
||||
|
||||
Any section that is not one of the above is assumed to be a rule, see RULES for
|
||||
more details.
|
||||
@ -22,6 +50,24 @@ more details.
|
||||
For backwards compatibility reasons the section name 'frame' is considered bound
|
||||
and can't be used as a rule.
|
||||
|
||||
=head2 Command line
|
||||
|
||||
Each configuration option in the global section can be overridden from the
|
||||
command line by adding a single dash in front of it's name.
|
||||
For example the font option can be overridden by running
|
||||
|
||||
$ dunst -font "LiberationSans Mono 4"
|
||||
|
||||
Configuration options that take boolean values can only currently be set to
|
||||
"true" through the command line via the same method. e.g.
|
||||
|
||||
$ dunst -shrink
|
||||
|
||||
This is a known limitation of the way command line parameters are parsed and
|
||||
will be changed in the future.
|
||||
|
||||
Available settings per section:
|
||||
|
||||
=head2 Global section
|
||||
|
||||
=over 4
|
||||
@ -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
|
||||
values except I<none> override the B<monitor> setting.
|
||||
|
||||
On Wayland there is no difference between mouse and keyboard focus. When either
|
||||
of the is used, the compositor will choose an output. This will generally be
|
||||
the output last interacted with.
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<none>
|
||||
@ -100,32 +142,6 @@ the notification on the left border of the screen while a horizontal offset of
|
||||
|
||||
=back
|
||||
|
||||
=item B<progress_bar> (values: [true/false], default: true)
|
||||
|
||||
When an integer value is passed to dunst as a hint (see B<NOTIFY-SEND>), a
|
||||
progress bar will be drawn at the bottom of the notification. This
|
||||
behavior can be turned off by setting this setting to false.
|
||||
|
||||
=item B<progress_bar_height> (default: 10)
|
||||
|
||||
The height of the progress bar in pixel. This includes the frame. Make sure
|
||||
this value is bigger than twice the frame width.
|
||||
|
||||
=item B<progress_bar_min_width> (default: 150)
|
||||
|
||||
The minimum width of the progress bar in pixels. The notification is rescaled
|
||||
to fit the bar.
|
||||
|
||||
=item B<progress_bar_max_width> (default: 300)
|
||||
|
||||
The maximum width of the progress bar in pixels. The notification is resized
|
||||
to fit the progress bar.
|
||||
|
||||
=item B<progress_bar_frame_width> (default: 1)
|
||||
|
||||
The frame width of the progress bar in pixels. This value should be smaller
|
||||
than half of the progress bar height.
|
||||
|
||||
=item B<indicate_hidden> (values: [true/false], default: true)
|
||||
|
||||
If this is set to true, a notification indicating how many notifications are
|
||||
@ -172,23 +188,6 @@ in the vertical axis
|
||||
The distance in pixels from the content to the border of the window
|
||||
in the horizontal axis
|
||||
|
||||
=item B<text_icon_padding> (default: 0)
|
||||
|
||||
The distance in pixels from the text to the icon (or vice versa)
|
||||
in the horizontal axis.
|
||||
|
||||
Setting this to a non-zero value overwrites any padding that horizontal_padding was adding between the notification text and icon.
|
||||
|
||||
So for example setting
|
||||
|
||||
text_icon_padding=10
|
||||
horizontal_padding=10
|
||||
|
||||
is equivalent to
|
||||
|
||||
text_icon_padding=0
|
||||
horizontal_padding=10
|
||||
|
||||
=item B<frame_width> (default: 0)
|
||||
|
||||
Defines width in pixels of frame around the notification window. Set to 0 to
|
||||
@ -236,27 +235,8 @@ See TIME FORMAT for valid times.
|
||||
|
||||
Set to 0 to disable.
|
||||
|
||||
A client can mark a notification as transient to bypass this setting and timeout
|
||||
anyway. Use a rule with 'set_transient = no' to disable this behavior.
|
||||
|
||||
Note: this doesn't work on xwayland.
|
||||
|
||||
=item B<layer> (Wayland only)
|
||||
|
||||
One of bottom, top or overlay.
|
||||
|
||||
Place dunst notifications on the selected layer. Using overlay
|
||||
will cause notifications to be displayed above fullscreen windows, though
|
||||
this may also occur at top depending on your compositor.
|
||||
|
||||
The bottom layer is below all windows and above the background.
|
||||
|
||||
Default: overlay
|
||||
|
||||
=item B<force_xwayland> (values: [true/false], default: false) (Wayland only)
|
||||
|
||||
Force the use of X11 output, even on a wayland compositor. This setting
|
||||
has no effect when not using a Wayland compositor.
|
||||
Transient notifications will ignore this setting and timeout anyway.
|
||||
Use a rule overwriting with 'set_transient = no' to disable this behavior.
|
||||
|
||||
=item B<font> (default: "Monospace 8")
|
||||
|
||||
@ -292,7 +272,7 @@ Allow a small subset of html markup in notifications
|
||||
<u>underline</u>
|
||||
|
||||
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>
|
||||
|
||||
@ -357,11 +337,6 @@ removed from the format.
|
||||
|
||||
Defines how the text should be aligned within the notification.
|
||||
|
||||
=item B<vertical_alignment> (values: [top/center/bottom], default: center)
|
||||
|
||||
Defines how the text and icon should be aligned vertically within the
|
||||
notification. If icons are disabled, this option has no effect.
|
||||
|
||||
=item B<show_age_threshold> (default: -1)
|
||||
|
||||
Show age of message if message is older than this time.
|
||||
@ -373,7 +348,7 @@ Set to -1 to disable.
|
||||
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
@ -409,28 +384,14 @@ ACTIONS below for further details.
|
||||
Defines the position of the icon in the notification window. Setting it to off
|
||||
disables icons.
|
||||
|
||||
=item B<min_icon_size> (default: 0)
|
||||
|
||||
Defines the minimum size in pixels for the icons.
|
||||
If the icon is larger than or equal to the specified value it won't be affected.
|
||||
If it's smaller then it will be scaled up so that the smaller axis is equivalent
|
||||
to the specified size.
|
||||
|
||||
Set to 0 to disable icon upscaling. (default)
|
||||
|
||||
If B<icon_position> is set to off, this setting is ignored.
|
||||
|
||||
=item B<max_icon_size> (default: 0)
|
||||
|
||||
Defines the maximum size in pixels for the icons.
|
||||
If the icon is smaller than 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
|
||||
to the specified size.
|
||||
|
||||
Set to 0 to disable icon downscaling. (default)
|
||||
|
||||
If both B<min_icon_size> and B<max_icon_size> are enabled, the latter
|
||||
gets the last say.
|
||||
Set to 0 to disable icon scaling. (default)
|
||||
|
||||
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
|
||||
shouldn't be any need to use this option.
|
||||
|
||||
=item B<verbosity> (values: 'crit', 'warn', 'mesg', 'info', 'debug' default 'mesg')
|
||||
|
||||
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)
|
||||
=item B<force_xinerama> (values: [true/false], default: false)
|
||||
|
||||
Use the Xinerama extension instead of RandR for multi-monitor support. This
|
||||
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
|
||||
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
|
||||
|
||||
|
||||
=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)
|
||||
=head2 Shortcut section
|
||||
|
||||
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.
|
||||
@ -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
|
||||
attributes for the different urgency levels of notifications (low, normal,
|
||||
critical). Currently only the background, foreground, hightlight, timeout,
|
||||
frame_color and icon attributes can be modified.
|
||||
critical). Currently only the background, foreground, timeout, frame_color and
|
||||
icon attributes can be modified.
|
||||
|
||||
The urgency sections are urgency_low, urgency_normal, urgency_critical for low,
|
||||
normal and critical urgency respectively.
|
||||
@ -630,12 +527,6 @@ Defines the background color for low, normal and critical notifications respecti
|
||||
|
||||
See COLORS for the value format.
|
||||
|
||||
=item B<-lh/nh/ch color>
|
||||
|
||||
Defines the highlight color for low, normal and critical notifications respectively.
|
||||
|
||||
See COLORS for the value format.
|
||||
|
||||
=item B<-lfr/nfr/cfr color>
|
||||
|
||||
Defines the frame color for low, normal and critical notifications respectively.
|
||||
@ -650,53 +541,18 @@ See TIME FORMAT for valid times.
|
||||
|
||||
=back
|
||||
|
||||
=head1 DUNSTCTL
|
||||
|
||||
Dunst now contains a command line control command that can be used to interact
|
||||
with it. It supports all functions previously done only via keyboard shortcuts
|
||||
but also has a lot of extra functionality. So see more see dunstctl(1).
|
||||
|
||||
=head1 HISTORY
|
||||
|
||||
Dunst saves a number of notifications (specified by B<history_length>) in memory.
|
||||
These notifications can be recalled (i.e. redisplayed) by calling
|
||||
B<dunstctl history> (see dunstctl(1)). Whether these notifications will time out
|
||||
like if they have been just send depends on the value of the B<sticky_history>
|
||||
setting.
|
||||
These notifications can be recalled (i.e. redesiplayed) by pressing the
|
||||
B<history_key> (see the shortcuts section), whether these notifications will
|
||||
time out like if they have been just send depends on the value of the
|
||||
B<sticky_history> setting.
|
||||
|
||||
Past notifications are redisplayed in a first-in-last-out order, meaning that
|
||||
pressing the history key once will bring up the most recent notification that
|
||||
had been closed/timed out.
|
||||
|
||||
=head1 WAYLAND
|
||||
|
||||
Dunst has Wayland support since version 1.6.0. Because the Wayland protocol
|
||||
is more focused on security, some things that are possible in X11 are not
|
||||
possible in Wayland. Those differences are reflected in the configuration.
|
||||
The main things that change are that dunst on Wayland cannot use global
|
||||
hotkeys (they are deprecated anyways, use dunstctl).
|
||||
|
||||
Some dunst features on wayland might need your compositor to support a certain
|
||||
protocol. Dunst will warn you if an optional feature isn't supported and will
|
||||
disable the corresponding functionality.
|
||||
|
||||
Fullscreen detection works on wayland with some limitations (see B<fullscreen>).
|
||||
If you want notifications to appear over fullscreen windows, set
|
||||
B<layer = overlay> in the global options.
|
||||
|
||||
Note that the same limitations exist when using xwayland.
|
||||
If something doesn't quite work in Wayland, please file a bug report. In the
|
||||
mean time, you can try if the X11 output does work on wayland. Use
|
||||
B<force_xwayland = true> for that.
|
||||
|
||||
If you have your dunst notifications on the same side of your display as your
|
||||
status bar, you might notice that your notifications appear a bit higher or
|
||||
lower than on X11. This is because the notification cannot be placed on top of
|
||||
your status bar. The notifications are placed relative to your status bar,
|
||||
making them appear higher or lower by the height of your status bar. We cannot
|
||||
do anything about that behavior, so you will need to change your B<geometry>
|
||||
variable accordingly.
|
||||
|
||||
=head1 RULES
|
||||
|
||||
Rules allow the conditional modification of notifications. They are defined by
|
||||
@ -712,64 +568,11 @@ matched.
|
||||
|
||||
=item B<filtering>
|
||||
|
||||
Notifications can be matched for any of the following attributes:
|
||||
|
||||
=over 4
|
||||
|
||||
=item C<appname> (discouraged, see desktop_entry)
|
||||
|
||||
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.
|
||||
|
||||
Notifications can be matched for any of the following attributes: appname,
|
||||
summary, body, icon, category, match_transient and msg_urgency where each is
|
||||
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
|
||||
modify the urgency.
|
||||
|
||||
To define a matching rule simply assign the specified value to the value that
|
||||
should be matched, for example:
|
||||
@ -784,107 +587,9 @@ Shell-like globing is supported.
|
||||
|
||||
=item B<modifying>
|
||||
|
||||
The following attributes can be overridden:
|
||||
|
||||
=over 4
|
||||
|
||||
=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.
|
||||
The following attributes can be overridden: timeout, urgency, foreground,
|
||||
background, new_icon, set_transient, format where, 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
|
||||
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.
|
||||
|
||||
When the script is called details of the notification that triggered it will be
|
||||
passed via environment variables. The following variables are available:
|
||||
B<DUNST_APP_NAME>, B<DUNST_SUMMARY>, B<DUNST_BODY>, B<DUNST_ICON_PATH>,
|
||||
B<DUNST_URGENCY>, B<DUNST_ID>, B<DUNST_PROGRESS>, B<DUNST_CATEGORY>,
|
||||
B<DUNST_STACK_TAG>, B<DUNST_URLS>, B<DUNST_TIMEOUT>, B<DUNST_TIMESTAMP>
|
||||
and B<DUNST_STACK_TAG>.
|
||||
passed via command line parameters in the following order: appname, summary,
|
||||
body, icon, urgency.
|
||||
|
||||
Another, less recommended way to get notifcations details from a script is via
|
||||
command line parameters. These are passed to the script in the following order:
|
||||
B<appname>, B<summary>, B<body>, B<icon_path>, B<urgency>.
|
||||
|
||||
Where B<DUNST_ICON_PATH> or B<icon_path> is the absolute path to the icon file
|
||||
if there is one. B<DUNST_URGENCY> or B<urgency> is one of "LOW", "NORMAL" or
|
||||
"CRITICAL". B<DUNST_URLS> is a newline-separated list of urls associated with
|
||||
the notification.
|
||||
|
||||
Note that some variables may be empty.
|
||||
Where icon is the absolute path to the icon file if there is one and urgency is
|
||||
one of "LOW", "NORMAL" or "CRITICAL".
|
||||
|
||||
If the notification is suppressed, the script will not be run unless
|
||||
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
|
||||
values.
|
||||
|
||||
You may also specify a transparency component in #RGBA or #RRGGBBAA format.
|
||||
|
||||
B<NOTE>: '#' is interpreted as a comment, to use it the entire value needs to
|
||||
be in quotes like so: separator_color="#123456"
|
||||
|
||||
@ -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.
|
||||
The progress value can be set with a hint, too.
|
||||
|
||||
B<All hints>
|
||||
|
||||
See RULES for more detailed explanations for some options.
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<fgcolor>:
|
||||
Foreground cololor
|
||||
|
||||
=item B<bgcolor>:
|
||||
Background color
|
||||
|
||||
=item B<frcolor>:
|
||||
Frame color
|
||||
|
||||
=item B<hlcolor>:
|
||||
Highlight color
|
||||
|
||||
=item B<value>:
|
||||
Progress value.
|
||||
|
||||
=item B<image-path>:
|
||||
Icon name. This may be a path or just the icon name.
|
||||
|
||||
=item B<image-data>:
|
||||
A stream of raw image data.
|
||||
|
||||
=item B<category>:
|
||||
The category.
|
||||
|
||||
=item B<desktop_entry>:
|
||||
The desktop entry.
|
||||
|
||||
=item B<transient>:
|
||||
The transient value.
|
||||
|
||||
=back
|
||||
|
||||
B<Examples>
|
||||
|
||||
=over 4
|
||||
|
||||
=item notify-send -h string:fgcolor:#ff4444
|
||||
|
||||
=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 ..."
|
||||
|
||||
@ -998,7 +650,7 @@ 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
|
||||
prepended to the notification format. Likewise, an "(U)" is preneded 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.
|
||||
@ -1023,8 +675,8 @@ Example time: "1000ms" "10m"
|
||||
|
||||
=head1 MISCELLANEOUS
|
||||
|
||||
Dunst can be paused via the `dunstctl set-paused true` command. To unpause dunst use
|
||||
`dunstctl set-paused false`.
|
||||
Dunst can be paused by sending a notification with a summary of
|
||||
"DUNST_COMMAND_PAUSE" and resumed with a summary of "DUNST_COMMAND_RESUME".
|
||||
Alternatively you can send SIGUSR1 and SIGUSR2 to pause and unpause
|
||||
respectively. For Example:
|
||||
|
||||
@ -1043,27 +695,11 @@ 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
|
||||
$HOME/.config/dunst/dunstrc
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
@ -1081,4 +717,4 @@ If you feel that copyrights are violated, please send me an email.
|
||||
|
||||
=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
|
||||
BusName=org.freedesktop.Notifications
|
||||
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},
|
||||
{ "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"},
|
||||
{ "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},
|
||||
{ NULL }
|
||||
};
|
||||
@ -76,33 +76,6 @@ void print_serverinfo(void)
|
||||
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[])
|
||||
{
|
||||
GError *error = NULL;
|
||||
@ -127,23 +100,17 @@ void parse_commandline(int argc, char *argv[])
|
||||
die(0);
|
||||
}
|
||||
|
||||
if (*appname == '\0') {
|
||||
g_printerr("Provided appname was empty\n");
|
||||
die(1);
|
||||
}
|
||||
|
||||
int n_args = count_args(argv, argc);
|
||||
if (n_args < 2 && close_id < 1) {
|
||||
if (argc < 2 && close_id < 1) {
|
||||
g_printerr("I need at least a summary\n");
|
||||
die(1);
|
||||
} else if (n_args < 2) {
|
||||
} else if (argc < 2) {
|
||||
summary = g_strdup("These are not the summaries you are looking for");
|
||||
} else {
|
||||
summary = g_strdup(get_argv(argv, 1));
|
||||
summary = g_strdup(argv[1]);
|
||||
}
|
||||
|
||||
if (n_args > 2) {
|
||||
body = g_strcompress(get_argv(argv, 2));
|
||||
if (argc > 2) {
|
||||
body = g_strdup(argv[2]);
|
||||
}
|
||||
|
||||
if (urgency_str) {
|
||||
@ -239,7 +206,7 @@ void add_action(NotifyNotification *n, char *str)
|
||||
char *label = strchr(str, ',');
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -364,4 +331,4 @@ int main(int argc, char *argv[])
|
||||
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.
|
||||
geometry = "300x5-30+20"
|
||||
|
||||
# Turn on the progess bar
|
||||
progress_bar = true
|
||||
|
||||
# Set the progress bar height. This includes the frame, so make sure
|
||||
# it's at least twice as big as the frame width.
|
||||
progress_bar_height = 10
|
||||
|
||||
# Set the frame width of the progress bar
|
||||
progress_bar_frame_width = 1
|
||||
|
||||
# Set the minimum width for the progress bar
|
||||
progress_bar_min_width = 150
|
||||
|
||||
# Set the maximum width for the progress bar
|
||||
progress_bar_max_width = 300
|
||||
|
||||
|
||||
# Show how many messages are currently hidden (because of geometry).
|
||||
indicate_hidden = yes
|
||||
|
||||
@ -76,9 +59,6 @@
|
||||
# Horizontal padding.
|
||||
horizontal_padding = 8
|
||||
|
||||
# Padding between text and icon.
|
||||
text_icon_padding = 0
|
||||
|
||||
# Defines width in pixels of frame around the notification window.
|
||||
# Set to 0 to disable.
|
||||
frame_width = 3
|
||||
@ -100,8 +80,7 @@
|
||||
# Don't remove messages, if the user is idle (no mouse or keyboard input)
|
||||
# for longer than idle_threshold seconds.
|
||||
# Set to 0 to disable.
|
||||
# A client can set the 'transient' hint to bypass this. See the rules
|
||||
# section for how to disable this if necessary
|
||||
# Transient notifications ignore this setting.
|
||||
idle_threshold = 120
|
||||
|
||||
### Text ###
|
||||
@ -120,7 +99,7 @@
|
||||
# <u>underline</u>
|
||||
#
|
||||
# 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
|
||||
# clients that send markup even though it's not enabled on the
|
||||
@ -152,10 +131,6 @@
|
||||
# Possible values are "left", "center" and "right".
|
||||
alignment = left
|
||||
|
||||
# Vertical alignment of message text and icon.
|
||||
# Possible values are "top", "center" and "bottom".
|
||||
vertical_alignment = center
|
||||
|
||||
# Show age of message if message is older than show_age_threshold
|
||||
# seconds.
|
||||
# Set to -1 to disable.
|
||||
@ -165,17 +140,17 @@
|
||||
# geometry.
|
||||
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".
|
||||
ellipsize = middle
|
||||
|
||||
# Ignore newlines '\n' in notifications.
|
||||
ignore_newline = no
|
||||
|
||||
# Stack together notifications with the same content
|
||||
# Merge multiple notifications with the same content
|
||||
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
|
||||
|
||||
# Display indicators for URLs (U) and actions (A).
|
||||
@ -184,12 +159,7 @@
|
||||
### Icons ###
|
||||
|
||||
# Align icons left/right/off
|
||||
icon_position = left
|
||||
|
||||
# Scale small icons up to this size, set to 0 to disable. Helpful
|
||||
# for e.g. small files or high-dpi screens. In case of conflict,
|
||||
# max_icon_size takes precedence over this.
|
||||
min_icon_size = 0
|
||||
icon_position = off
|
||||
|
||||
# Scale larger icons down to this size, set to 0 to disable
|
||||
max_icon_size = 32
|
||||
@ -228,38 +198,6 @@
|
||||
# automatically after a crash.
|
||||
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
|
||||
|
||||
# Use the Xinerama extension instead of RandR for multi-monitor support.
|
||||
@ -272,25 +210,6 @@
|
||||
# layout changes.
|
||||
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
|
||||
# to have a consistent behaviour across releases.
|
||||
[experimental]
|
||||
@ -301,10 +220,6 @@
|
||||
# where there are multiple screens with very different dpi values.
|
||||
per_monitor_dpi = false
|
||||
|
||||
# The internal keyboard shortcut support in dunst is now considered deprecated
|
||||
# and should be replaced by dunstctl calls. You can use the configuration of your
|
||||
# WM or DE to bind these to shortcuts of your choice.
|
||||
# Check the dunstctl manual page for more info.
|
||||
[shortcuts]
|
||||
|
||||
# Shortcuts are specified as [modifier+][modifier+]...key
|
||||
@ -312,21 +227,20 @@
|
||||
# "mod3" and "mod4" (windows-key).
|
||||
# Xev might be helpful to find names for keys.
|
||||
|
||||
# Close notification. Equivalent dunstctl command:
|
||||
# dunstctl close
|
||||
# close = ctrl+space
|
||||
# Close notification.
|
||||
close = ctrl+space
|
||||
|
||||
# Close all notifications. Equivalent dunstctl command:
|
||||
# dunstctl close-all
|
||||
# close_all = ctrl+shift+space
|
||||
# Close all notifications.
|
||||
close_all = ctrl+shift+space
|
||||
|
||||
# Redisplay last message(s). Equivalent dunstctl command:
|
||||
# dunstctl history-pop
|
||||
# history = ctrl+grave
|
||||
# Redisplay last message(s).
|
||||
# On the US keyboard layout "grave" is normally above TAB and left
|
||||
# of "1". Make sure this key actually exists on your keyboard layout,
|
||||
# e.g. check output of 'xmodmap -pke'
|
||||
history = ctrl+grave
|
||||
|
||||
# Context menu. Equivalent dunstctl command:
|
||||
# dunstctl context
|
||||
# context = ctrl+shift+period
|
||||
# Context menu.
|
||||
context = ctrl+shift+period
|
||||
|
||||
[urgency_low]
|
||||
# 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
|
||||
# override settings for certain messages.
|
||||
#
|
||||
# Messages can be matched by
|
||||
# appname (discouraged, see desktop_entry)
|
||||
# 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
|
||||
#
|
||||
# Messages can be matched by "appname", "summary", "body", "icon", "category",
|
||||
# "msg_urgency" and you can override the "timeout", "urgency", "foreground",
|
||||
# "background", "new_icon" and "format".
|
||||
# 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
|
||||
# You can specify a script that gets run when the rule matches by
|
||||
# setting the "script" option.
|
||||
@ -397,30 +285,6 @@
|
||||
# NOTE: It might be helpful to run dunst -print in a terminal in order
|
||||
# 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]
|
||||
# summary = "*"
|
||||
# script = dunst_espeak.sh
|
||||
@ -439,11 +303,6 @@
|
||||
# summary = "foobar"
|
||||
# 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]
|
||||
# appname = Pidgin
|
||||
# summary = "*signed on*"
|
||||
@ -464,8 +323,4 @@
|
||||
# summary = *twitter.com*
|
||||
# urgency = normal
|
||||
#
|
||||
#[stack-volumes]
|
||||
# appname = "some_volume_notifiers"
|
||||
# set_stack_tag = "volume"
|
||||
#
|
||||
# vim: ft=cfg
|
||||
|
||||
2
main.c
@ -4,4 +4,4 @@ int main(int argc, char *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
|
||||
#define DUNST_DBUS_H
|
||||
|
||||
#include "dunst.h"
|
||||
#include "notification.h"
|
||||
|
||||
/// The reasons according to the notification spec
|
||||
enum reason {
|
||||
REASON_MIN = 1, /**< Minimum value, useful for boundary checking */
|
||||
REASON_TIME = 1, /**< The notification timed out */
|
||||
REASON_USER = 2, /**< The user closed the notification */
|
||||
REASON_SIG = 3, /**< The daemon received a `NotificationClose` signal */
|
||||
REASON_UNDEF = 4, /**< Undefined reason not matching the previous ones */
|
||||
REASON_MAX = 4, /**< Maximum value, useful for boundary checking */
|
||||
REASON_MIN = 1,
|
||||
REASON_TIME = 1,
|
||||
REASON_USER = 2,
|
||||
REASON_SIG = 3,
|
||||
REASON_UNDEF = 4,
|
||||
REASON_MAX = 4,
|
||||
};
|
||||
|
||||
int dbus_init(void);
|
||||
void dbus_teardown(int id);
|
||||
void signal_notification_closed(struct notification *n, enum reason reason);
|
||||
void signal_action_invoked(const struct notification *n, const char *identifier);
|
||||
int initdbus(void);
|
||||
void dbus_tear_down(int id);
|
||||
/* void dbus_poll(int timeout); */
|
||||
void notification_closed(notification *n, enum reason reason);
|
||||
void action_invoked(notification *n, const char *identifier);
|
||||
|
||||
#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) */
|
||||
|
||||
#define XLIB_ILLEGAL_ACCESS
|
||||
|
||||
#include "dunst.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <glib.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <glib-unix.h>
|
||||
#include <glib.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "dbus.h"
|
||||
#include "draw.h"
|
||||
#include "log.h"
|
||||
#include "menu.h"
|
||||
#include "notification.h"
|
||||
#include "option_parser.h"
|
||||
#include "queues.h"
|
||||
#include "settings.h"
|
||||
#include "utils.h"
|
||||
#include "output.h"
|
||||
#include "x11/screen.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;
|
||||
|
||||
static struct dunst_status status;
|
||||
static bool setup_done = false;
|
||||
GSList *rules = NULL;
|
||||
|
||||
/* see dunst.h */
|
||||
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);
|
||||
/* misc funtions */
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
LOG_D("RUN");
|
||||
|
||||
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 (data && timeout_cnt > 0) {
|
||||
timeout_cnt--;
|
||||
}
|
||||
|
||||
if (active) {
|
||||
gint64 now = time_monotonic_now();
|
||||
if (queues_length_displayed() > 0 && !xctx.visible) {
|
||||
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 timeout_at = now + sleep;
|
||||
|
||||
LOG_D("Sleeping for %li ms", sleep/1000);
|
||||
|
||||
if (sleep >= 0) {
|
||||
if (next_timeout < now || timeout_at < next_timeout) {
|
||||
g_timeout_add(sleep/1000, run, NULL);
|
||||
if (timeout_cnt == 0 || timeout_at < next_timeout) {
|
||||
g_timeout_add(sleep/1000, run, mainloop);
|
||||
next_timeout = timeout_at;
|
||||
timeout_cnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If the execution got triggered by g_timeout_add,
|
||||
* we have to remove the timeout (which is actually a
|
||||
* recurring interval), as we have set a new one
|
||||
* by ourselves.
|
||||
*/
|
||||
return G_SOURCE_REMOVE;
|
||||
/* always return false to delete timers */
|
||||
return false;
|
||||
}
|
||||
|
||||
gboolean pause_signal(gpointer data)
|
||||
{
|
||||
dunst_status(S_RUNNING, false);
|
||||
queues_pause_on();
|
||||
wake_up();
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
@ -123,7 +100,7 @@ gboolean pause_signal(gpointer data)
|
||||
|
||||
gboolean unpause_signal(gpointer data)
|
||||
{
|
||||
dunst_status(S_RUNNING, true);
|
||||
queues_pause_off();
|
||||
wake_up();
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
@ -140,32 +117,23 @@ static void teardown(void)
|
||||
{
|
||||
regex_teardown();
|
||||
|
||||
queues_teardown();
|
||||
teardown_queues();
|
||||
|
||||
draw_deinit();
|
||||
x_free();
|
||||
}
|
||||
|
||||
int dunst_main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
dunst_status(S_RUNNING, true);
|
||||
dunst_status(S_IDLE, false);
|
||||
|
||||
queues_init();
|
||||
|
||||
cmdline_load(argc, argv);
|
||||
|
||||
dunst_log_init(false);
|
||||
|
||||
if (cmdline_get_bool("-v/-version", false, "Print version")
|
||||
|| cmdline_get_bool("--version", false, "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;
|
||||
cmdline_config_path =
|
||||
cmdline_get_string("-conf/-config", NULL,
|
||||
@ -177,11 +145,46 @@ int dunst_main(int argc, char *argv[])
|
||||
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);
|
||||
|
||||
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 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 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);
|
||||
g_main_loop_run(mainloop);
|
||||
g_clear_pointer(&mainloop, g_main_loop_unref);
|
||||
g_main_loop_unref(mainloop);
|
||||
|
||||
/* remove signal handler watches */
|
||||
g_source_remove(pause_src);
|
||||
@ -217,7 +204,9 @@ int dunst_main(int argc, char *argv[])
|
||||
g_source_remove(term_src);
|
||||
g_source_remove(int_src);
|
||||
|
||||
dbus_teardown(dbus_owner_id);
|
||||
g_source_destroy(x11_source);
|
||||
|
||||
dbus_tear_down(owner_id);
|
||||
|
||||
teardown();
|
||||
|
||||
@ -240,4 +229,4 @@ void print_version(void)
|
||||
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 <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "notification.h"
|
||||
|
||||
//!< A structure to describe dunst's global window status
|
||||
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
|
||||
};
|
||||
#define PERR(msg, errnum) printf("(%d) %s : %s\n", __LINE__, (msg), (strerror(errnum)))
|
||||
|
||||
enum dunst_status_field {
|
||||
S_FULLSCREEN,
|
||||
S_IDLE,
|
||||
S_RUNNING,
|
||||
};
|
||||
#define ColLast 3
|
||||
#define ColFrame 2
|
||||
#define ColFG 1
|
||||
#define ColBG 0
|
||||
|
||||
/**
|
||||
* Modify the current status of dunst
|
||||
* @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);
|
||||
extern GSList *rules;
|
||||
extern const char *color_strings[3][3];
|
||||
|
||||
/* return id of notification */
|
||||
gboolean run(void *data);
|
||||
void wake_up(void);
|
||||
|
||||
int dunst_main(int argc, char *argv[]);
|
||||
|
||||
void check_timeouts(void);
|
||||
void usage(int exit_status);
|
||||
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
|
||||
/* 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 <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "settings.h"
|
||||
#include "utils.h"
|
||||
|
||||
/**
|
||||
* Convert all HTML special symbols to HTML entities.
|
||||
* @param str (nullable)
|
||||
*/
|
||||
static char *markup_quote(char *str)
|
||||
{
|
||||
ASSERT_OR_RET(str, NULL);
|
||||
assert(str != NULL);
|
||||
|
||||
str = string_replace_all("&", "&", str);
|
||||
str = string_replace_all("\"", """, str);
|
||||
@ -29,13 +21,9 @@ static char *markup_quote(char *str)
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert all HTML special entities to their actual char.
|
||||
* @param str (nullable)
|
||||
*/
|
||||
static char *markup_unquote(char *str)
|
||||
{
|
||||
ASSERT_OR_RET(str, NULL);
|
||||
assert(str != NULL);
|
||||
|
||||
str = string_replace_all(""", "\"", str);
|
||||
str = string_replace_all("'", "'", str);
|
||||
@ -46,13 +34,9 @@ static char *markup_unquote(char *str)
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert all HTML linebreak tags to a newline character
|
||||
* @param str (nullable)
|
||||
*/
|
||||
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);
|
||||
@ -60,168 +44,23 @@ static char *markup_br2nl(char *str)
|
||||
return str;
|
||||
}
|
||||
|
||||
/* see markup.h */
|
||||
void markup_strip_a(char **str, char **urls)
|
||||
{
|
||||
assert(*str);
|
||||
char *tag1 = NULL;
|
||||
|
||||
if (urls)
|
||||
*urls = NULL;
|
||||
|
||||
while ((tag1 = strstr(*str, "<a"))) {
|
||||
// use href=" as stated in the notification spec
|
||||
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 */
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
ASSERT_OR_RET(str, NULL);
|
||||
if (str == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* strip all tags */
|
||||
string_strip_delimited(str, '<', '>');
|
||||
@ -232,83 +71,15 @@ char *markup_strip(char *str)
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an & character pointed to by \p str is a markup & entity or
|
||||
* part of the text
|
||||
*
|
||||
* @retval true: \p str is an entity
|
||||
* @retval false: It's no valid entity
|
||||
/*
|
||||
* Transform the string in accordance with `markup_mode` and
|
||||
* `settings.ignore_newline`
|
||||
*/
|
||||
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)
|
||||
{
|
||||
ASSERT_OR_RET(str, NULL);
|
||||
if (str == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (markup_mode) {
|
||||
case MARKUP_NULL:
|
||||
@ -324,10 +95,7 @@ char *markup_transform(char *str, enum markup_mode markup_mode)
|
||||
str = markup_quote(str);
|
||||
break;
|
||||
case MARKUP_FULL:
|
||||
str = markup_escape_unsupported(str);
|
||||
str = markup_br2nl(str);
|
||||
markup_strip_a(&str, NULL);
|
||||
markup_strip_img(&str, NULL);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -338,4 +106,4 @@ char *markup_transform(char *str, enum markup_mode markup_mode)
|
||||
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
|
||||
#define DUNST_MARKUP_H
|
||||
|
||||
enum markup_mode {
|
||||
MARKUP_NULL,
|
||||
MARKUP_NO,
|
||||
MARKUP_STRIP,
|
||||
MARKUP_FULL
|
||||
};
|
||||
#include "settings.h"
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
#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 "dunst.h"
|
||||
#include "log.h"
|
||||
#include "notification.h"
|
||||
#include "queues.h"
|
||||
#include "settings.h"
|
||||
#include "utils.h"
|
||||
|
||||
static bool is_initialized = false;
|
||||
static regex_t url_regex;
|
||||
static regex_t cregex;
|
||||
|
||||
static gpointer context_menu_thread(gpointer data);
|
||||
|
||||
struct {
|
||||
GList *locked_notifications;
|
||||
} menu_ctx;
|
||||
|
||||
/**
|
||||
* Initializes regexes needed for matching.
|
||||
*
|
||||
* @return true if initialization succeeded
|
||||
*/
|
||||
static bool regex_init(void)
|
||||
static int regex_init(void)
|
||||
{
|
||||
if (is_initialized)
|
||||
return true;
|
||||
return 1;
|
||||
|
||||
char *regex =
|
||||
"\\<(https?://|ftps?://|news://|mailto:|file://|www\\.)"
|
||||
"\\b(https?://|ftps?://|news://|mailto:|file://|www\\.)"
|
||||
"[-[:alnum:]_\\@;/?:&=%$.+!*\x27,~#]*"
|
||||
"(\\([-[:alnum:]_\\@;/?:&=%$.+!*\x27,~#]*\\)|[-[:alnum:]_\\@;/?:&=%$+*~])+";
|
||||
int code = regcomp(&url_regex, regex, REG_EXTENDED | REG_ICASE);
|
||||
if (code != 0) {
|
||||
char error_buf[120];
|
||||
regerror(code, &url_regex, error_buf, sizeof(error_buf));
|
||||
LOG_W("Failed to compile URL-matching regex: %s", error_buf);
|
||||
return false;
|
||||
int ret = regcomp(&cregex, regex, REG_EXTENDED | REG_ICASE);
|
||||
if (ret != 0) {
|
||||
fputs("failed to compile regex", stderr);
|
||||
return 0;
|
||||
} else {
|
||||
is_initialized = true;
|
||||
return true;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void regex_teardown(void)
|
||||
{
|
||||
if (is_initialized) {
|
||||
regfree(&url_regex);
|
||||
regfree(&cregex);
|
||||
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)
|
||||
{
|
||||
if (!to_match)
|
||||
return NULL;
|
||||
char *urls = NULL;
|
||||
|
||||
if (!regex_init())
|
||||
return NULL;
|
||||
|
||||
char *urls = NULL;
|
||||
const char *p = to_match;
|
||||
regmatch_t m;
|
||||
|
||||
while (1) {
|
||||
int nomatch = regexec(&url_regex, p, 1, &m, 0);
|
||||
|
||||
if (nomatch || m.rm_so == -1)
|
||||
int nomatch = regexec(&cregex, p, 1, &m, 0);
|
||||
if (nomatch) {
|
||||
return urls;
|
||||
}
|
||||
int start;
|
||||
int finish;
|
||||
if (m.rm_so == -1) {
|
||||
break;
|
||||
|
||||
int start = m.rm_so + (p - to_match);
|
||||
int finish = m.rm_eo + (p - to_match);
|
||||
}
|
||||
start = m.rm_so + (p - to_match);
|
||||
finish = m.rm_eo + (p - to_match);
|
||||
|
||||
char *match = g_strndup(to_match + start, finish - start);
|
||||
|
||||
urls = string_append(urls, match, "\n");
|
||||
|
||||
g_free(match);
|
||||
|
||||
p += m.rm_eo;
|
||||
}
|
||||
return urls;
|
||||
@ -100,66 +95,28 @@ char *extract_urls(const char *to_match)
|
||||
*/
|
||||
void open_browser(const char *in)
|
||||
{
|
||||
if (!settings.browser_cmd) {
|
||||
LOG_C("Unable to open browser: No browser command set.");
|
||||
// remove prefix and test url
|
||||
char *url = extract_urls(in);
|
||||
if (!url)
|
||||
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)
|
||||
{
|
||||
struct notification *invoked = NULL;
|
||||
uint id;
|
||||
notification *invoked = NULL;
|
||||
char *action_identifier = NULL;
|
||||
|
||||
char *data_start, *data_comma, *data_end;
|
||||
|
||||
/* format: #<human readable> (<summary>)[<id>,<action>] */
|
||||
data_start = strrchr(action, '[');
|
||||
if (!data_start) {
|
||||
LOG_W("Invalid action: '%s'", action);
|
||||
char *appname_begin = strchr(action, '[');
|
||||
if (!appname_begin) {
|
||||
printf("invalid action: %s\n", action);
|
||||
return;
|
||||
}
|
||||
|
||||
id = strtol(++data_start, &data_comma, 10);
|
||||
if (*data_comma != ',') {
|
||||
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;
|
||||
appname_begin++;
|
||||
int appname_len = strlen(appname_begin) - 1; // remove ]
|
||||
int action_len = strlen(action) - appname_len - 3; // remove space, [, ]
|
||||
|
||||
for (const GList *iter = queues_get_displayed(); iter;
|
||||
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)) {
|
||||
notification_lock(n);
|
||||
locked_notifications = g_list_prepend(locked_notifications, n);
|
||||
for (int i = 0; i < n->actions->count; i += 2) {
|
||||
char *a_identifier = n->actions->actions[i];
|
||||
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)
|
||||
{
|
||||
GList *notifications = get_actionable_notifications();
|
||||
context_menu_for(notifications);
|
||||
}
|
||||
|
||||
/* see menu.h */
|
||||
void context_menu_for(GList *notifications)
|
||||
{
|
||||
if (menu_ctx.locked_notifications) {
|
||||
LOG_W("Context menu already running, refusing to rerun");
|
||||
if (settings.dmenu_cmd == NULL) {
|
||||
fprintf(stderr, "dmenu command not set properly. Cowardly refusing to open the context menu.\n");
|
||||
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_output;
|
||||
|
||||
for (GList *iter = menu_ctx.locked_notifications; iter; iter = iter->next) {
|
||||
struct notification *n = iter->data;
|
||||
|
||||
char *dmenu_str = notification_dmenu_string(n);
|
||||
dmenu_input = string_append(dmenu_input, dmenu_str, "\n");
|
||||
g_free(dmenu_str);
|
||||
for (const GList *iter = queues_get_displayed(); iter;
|
||||
iter = iter->next) {
|
||||
notification *n = iter->data;
|
||||
|
||||
if (n->urls)
|
||||
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);
|
||||
g_timeout_add(50, context_menu_result_dispatch, dmenu_output);
|
||||
if (!dmenu_input)
|
||||
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);
|
||||
|
||||
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
|
||||
#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);
|
||||
|
||||
void open_browser(const char *in);
|
||||
void invoke_action(const char *action);
|
||||
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
|
||||
/* 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 "dunst.h"
|
||||
#include "icon.h"
|
||||
#include "log.h"
|
||||
#include "markup.h"
|
||||
#include "menu.h"
|
||||
#include "queues.h"
|
||||
#include "rules.h"
|
||||
#include "settings.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);
|
||||
|
||||
/* see notification.h */
|
||||
const char *enum_to_string_fullscreen(enum behavior_fullscreen in)
|
||||
/*
|
||||
* print a human readable representation
|
||||
* of the given notification to stdout.
|
||||
*/
|
||||
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("\tappname: '%s'\n", n->appname);
|
||||
printf("\tsummary: '%s'\n", n->summary);
|
||||
printf("\tbody: '%s'\n", n->body);
|
||||
printf("\ticon: '%s'\n", n->iconname);
|
||||
printf("\traw_icon set: %s\n", (n->icon_id && !STR_EQ(n->iconname, n->icon_id)) ? "true" : "false");
|
||||
printf("\ticon_id: '%s'\n", n->icon_id);
|
||||
printf("\tdesktop_entry: '%s'\n", n->desktop_entry ? n->desktop_entry : "");
|
||||
printf("\ticon: '%s'\n", n->icon);
|
||||
printf("\traw_icon set: %s\n", (n->raw_icon ? "true" : "false"));
|
||||
printf("\tcategory: %s\n", n->category);
|
||||
printf("\ttimeout: %ld\n", n->timeout/1000);
|
||||
printf("\turgency: %s\n", notification_urgency_to_string(n->urgency));
|
||||
printf("\ttransient: %d\n", n->transient);
|
||||
printf("\tformatted: '%s'\n", n->msg);
|
||||
printf("\tfg: %s\n", n->colors.fg);
|
||||
printf("\tbg: %s\n", n->colors.bg);
|
||||
printf("\thighlight: %s\n", n->colors.highlight);
|
||||
printf("\tframe: %s\n", n->colors.frame);
|
||||
printf("\tfullscreen: %s\n", enum_to_string_fullscreen(n->fullscreen));
|
||||
printf("\tprogress: %d\n", n->progress);
|
||||
printf("\tstack_tag: %s\n", (n->stack_tag ? n->stack_tag : ""));
|
||||
printf("\tfg: %s\n", n->color_strings[ColFG]);
|
||||
printf("\tbg: %s\n", n->color_strings[ColBG]);
|
||||
printf("\tframe: %s\n", n->color_strings[ColFrame]);
|
||||
printf("\tid: %d\n", n->id);
|
||||
if (n->urls) {
|
||||
char *urls = string_replace_all("\n", "\t\t\n", g_strdup(n->urls));
|
||||
printf("\turls:\n");
|
||||
printf("\t{\n");
|
||||
printf("\t\t%s\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\t%s\n", n->urls);
|
||||
printf("\t}\n");
|
||||
}
|
||||
printf("\tscript_count: %d\n", n->script_count);
|
||||
if (n->script_count > 0) {
|
||||
printf("\tscripts: ");
|
||||
for (int i = 0; i < n->script_count; i++) {
|
||||
printf("'%s' ",n->scripts[i]);
|
||||
|
||||
if (n->actions) {
|
||||
printf("\tactions:\n");
|
||||
printf("\t{\n");
|
||||
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");
|
||||
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;
|
||||
|
||||
n->script_run = true;
|
||||
|
||||
const char *appname = n->appname ? n->appname : "";
|
||||
const char *summary = n->summary ? n->summary : "";
|
||||
const char *body = n->body ? n->body : "";
|
||||
const char *icon = n->iconname ? n->iconname : "";
|
||||
char *appname = n->appname ? n->appname : "";
|
||||
char *summary = n->summary ? n->summary : "";
|
||||
char *body = n->body ? n->body : "";
|
||||
char *icon = n->icon ? n->icon : "";
|
||||
|
||||
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 (STR_EMPTY(script))
|
||||
continue;
|
||||
|
||||
int pid1 = fork();
|
||||
|
||||
if (pid1) {
|
||||
int status;
|
||||
waitpid(pid1, &status, 0);
|
||||
if (pid1) {
|
||||
int status;
|
||||
waitpid(pid1, &status, 0);
|
||||
} else {
|
||||
int pid2 = fork();
|
||||
if (pid2) {
|
||||
exit(0);
|
||||
} else {
|
||||
// second fork to prevent zombie processes
|
||||
int pid2 = fork();
|
||||
if (pid2) {
|
||||
exit(0);
|
||||
} else {
|
||||
// Set environment variables
|
||||
gchar *n_id_str = g_strdup_printf("%i", n->id);
|
||||
gchar *n_progress_str = g_strdup_printf("%i", n->progress);
|
||||
gchar *n_timeout_str = g_strdup_printf("%li", n->timeout/1000);
|
||||
gchar *n_timestamp_str = g_strdup_printf("%li", n->timestamp / 1000);
|
||||
char* icon_path = get_path_from_icon_name(icon);
|
||||
safe_setenv("DUNST_APP_NAME", appname);
|
||||
safe_setenv("DUNST_SUMMARY", summary);
|
||||
safe_setenv("DUNST_BODY", body);
|
||||
safe_setenv("DUNST_ICON_PATH", icon_path);
|
||||
safe_setenv("DUNST_URGENCY", urgency);
|
||||
safe_setenv("DUNST_ID", n_id_str);
|
||||
safe_setenv("DUNST_PROGRESS", n_progress_str);
|
||||
safe_setenv("DUNST_CATEGORY", n->category);
|
||||
safe_setenv("DUNST_STACK_TAG", n->stack_tag);
|
||||
safe_setenv("DUNST_URLS", n->urls);
|
||||
safe_setenv("DUNST_TIMEOUT", n_timeout_str);
|
||||
safe_setenv("DUNST_TIMESTAMP", n_timestamp_str);
|
||||
safe_setenv("DUNST_STACK_TAG", n->stack_tag);
|
||||
|
||||
execlp(script,
|
||||
script,
|
||||
appname,
|
||||
summary,
|
||||
body,
|
||||
icon,
|
||||
urgency,
|
||||
(char *)NULL);
|
||||
|
||||
LOG_W("Unable to run script %s: %s", n->scripts[i], strerror(errno));
|
||||
int ret = execlp(n->script,
|
||||
n->script,
|
||||
appname,
|
||||
summary,
|
||||
body,
|
||||
icon,
|
||||
urgency,
|
||||
(char *)NULL);
|
||||
if (ret != 0) {
|
||||
PERR("Unable to run script", errno);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
@ -174,7 +110,7 @@ void notification_run_script(struct notification *n)
|
||||
/*
|
||||
* 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) {
|
||||
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;
|
||||
} else {
|
||||
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)
|
||||
{
|
||||
struct notification *a = (struct notification *) va;
|
||||
struct notification *b = (struct notification *) vb;
|
||||
|
||||
return notification_cmp(a, b);
|
||||
return notification_cmp(va, vb);
|
||||
}
|
||||
|
||||
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)
|
||||
&& STR_EQ(a->summary, b->summary)
|
||||
&& STR_EQ(a->body, b->body)
|
||||
&& (settings.icon_position != ICON_OFF ? STR_EQ(a->icon_id, b->icon_id) : 1)
|
||||
//Comparing raw icons is not supported, assume they are not identical
|
||||
if (settings.icon_position != icons_off
|
||||
&& (a->raw_icon != NULL || b->raw_icon != NULL))
|
||||
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;
|
||||
}
|
||||
|
||||
bool notification_is_locked(struct notification *n) {
|
||||
assert(n);
|
||||
|
||||
return g_atomic_int_get(&n->locked) != 0;
|
||||
}
|
||||
|
||||
struct notification* notification_lock(struct notification *n) {
|
||||
assert(n);
|
||||
|
||||
g_atomic_int_set(&n->locked, 1);
|
||||
notification_ref(n);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
struct notification* notification_unlock(struct notification *n) {
|
||||
assert(n);
|
||||
|
||||
g_atomic_int_set(&n->locked, 0);
|
||||
notification_unref(n);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static void notification_private_free(NotificationPrivate *p)
|
||||
/*
|
||||
* Free the actions element
|
||||
* @a: (nullable): Pointer to #Actions
|
||||
*/
|
||||
void actions_free(Actions *a)
|
||||
{
|
||||
g_free(p);
|
||||
}
|
||||
|
||||
/* 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))
|
||||
if (!a)
|
||||
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->summary);
|
||||
g_free(n->body);
|
||||
g_free(n->iconname);
|
||||
g_free(n->icon);
|
||||
g_free(n->msg);
|
||||
g_free(n->dbus_client);
|
||||
g_free(n->category);
|
||||
g_free(n->text_to_render);
|
||||
g_free(n->urls);
|
||||
g_free(n->colors.fg);
|
||||
g_free(n->colors.bg);
|
||||
g_free(n->colors.highlight);
|
||||
g_free(n->colors.frame);
|
||||
g_free(n->stack_tag);
|
||||
g_free(n->desktop_entry);
|
||||
|
||||
g_hash_table_unref(n->actions);
|
||||
g_free(n->default_action_name);
|
||||
|
||||
if (n->icon)
|
||||
g_object_unref(n->icon);
|
||||
g_free(n->icon_id);
|
||||
|
||||
notification_private_free(n->priv);
|
||||
|
||||
if (n->script_count > 0){
|
||||
g_free(n->scripts);
|
||||
}
|
||||
actions_free(n->actions);
|
||||
rawimage_free(n->raw_icon);
|
||||
|
||||
g_free(n);
|
||||
}
|
||||
|
||||
void notification_icon_replace_path(struct notification *n, const char *new_icon)
|
||||
{
|
||||
ASSERT_OR_RET(n,);
|
||||
ASSERT_OR_RET(new_icon,);
|
||||
ASSERT_OR_RET(n->iconname != new_icon,);
|
||||
|
||||
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 */
|
||||
/*
|
||||
* 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,
|
||||
char **needle,
|
||||
const char *replacement,
|
||||
@ -352,127 +248,101 @@ void notification_replace_single_field(char **haystack,
|
||||
g_free(input);
|
||||
}
|
||||
|
||||
static NotificationPrivate *notification_private_create(void)
|
||||
char *notification_extract_markup_urls(char **str_ptr)
|
||||
{
|
||||
NotificationPrivate *priv = g_malloc0(sizeof(NotificationPrivate));
|
||||
g_atomic_int_set(&priv->refcount, 1);
|
||||
char *start, *end, *replace_buf, *str, *urls = NULL, *url, *index_buf;
|
||||
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->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);
|
||||
|
||||
if (g_str_has_prefix(n->summary, "DUNST_COMMAND_")) {
|
||||
char *msg = "DUNST_COMMAND_* has been removed, please switch to dunstctl. See #830 for more details. https://github.com/dunst-project/dunst/pull/830";
|
||||
LOG_W("%s", msg);
|
||||
n->body = string_append(n->body, msg, "\n");
|
||||
if (n->icon != NULL && strlen(n->icon) <= 0) {
|
||||
g_free(n->icon);
|
||||
n->icon = NULL;
|
||||
}
|
||||
|
||||
/* UPDATE derived fields */
|
||||
notification_extract_urls(n);
|
||||
notification_format_message(n);
|
||||
}
|
||||
if (n->raw_icon == NULL && n->icon == NULL) {
|
||||
n->icon = g_strdup(settings.icons[n->urgency]);
|
||||
}
|
||||
|
||||
static void notification_format_message(struct notification *n)
|
||||
{
|
||||
g_clear_pointer(&n->msg, g_free);
|
||||
n->urls = notification_extract_markup_urls(&(n->body));
|
||||
|
||||
n->msg = string_replace_all("\\n", "\n", g_strdup(n->format));
|
||||
|
||||
/* replace all formatter */
|
||||
for(char *substr = strchr(n->msg, '%');
|
||||
substr && *substr;
|
||||
substr;
|
||||
substr = strchr(substr, '%')) {
|
||||
|
||||
char pg[16];
|
||||
@ -491,7 +361,7 @@ static void notification_format_message(struct notification *n)
|
||||
&n->msg,
|
||||
&substr,
|
||||
n->summary,
|
||||
MARKUP_NO);
|
||||
n->markup);
|
||||
break;
|
||||
case 'b':
|
||||
notification_replace_single_field(
|
||||
@ -501,7 +371,7 @@ static void notification_format_message(struct notification *n)
|
||||
n->markup);
|
||||
break;
|
||||
case 'I':
|
||||
icon_tmp = g_strdup(n->iconname);
|
||||
icon_tmp = g_strdup(n->icon);
|
||||
notification_replace_single_field(
|
||||
&n->msg,
|
||||
&substr,
|
||||
@ -513,7 +383,7 @@ static void notification_format_message(struct notification *n)
|
||||
notification_replace_single_field(
|
||||
&n->msg,
|
||||
&substr,
|
||||
n->iconname ? n->iconname : "",
|
||||
n->icon ? n->icon : "",
|
||||
MARKUP_NO);
|
||||
break;
|
||||
case 'p':
|
||||
@ -544,12 +414,12 @@ static void notification_format_message(struct notification *n)
|
||||
MARKUP_NO);
|
||||
break;
|
||||
case '\0':
|
||||
LOG_W("format_string has trailing %% character. "
|
||||
"To escape it use %%%%.");
|
||||
substr++;
|
||||
fprintf(stderr, "WARNING: format_string has trailing %% character."
|
||||
"To escape it use %%%%.");
|
||||
break;
|
||||
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,
|
||||
// as we can't interpret the format string
|
||||
substr++;
|
||||
@ -560,41 +430,73 @@ static void notification_format_message(struct notification *n)
|
||||
n->msg = g_strchomp(n->msg);
|
||||
|
||||
/* truncate overlong messages */
|
||||
if (strnlen(n->msg, DUNST_NOTIF_MAX_CHARS + 1) > DUNST_NOTIF_MAX_CHARS) {
|
||||
char * buffer = g_strndup(n->msg, DUNST_NOTIF_MAX_CHARS);
|
||||
if (strlen(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);
|
||||
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);
|
||||
|
||||
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);
|
||||
g_free(n->text_to_render);
|
||||
n->text_to_render = NULL;
|
||||
|
||||
char *buf = NULL;
|
||||
|
||||
@ -602,14 +504,14 @@ void notification_update_text_to_render(struct notification *n)
|
||||
|
||||
/* print dup_count and msg */
|
||||
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",
|
||||
n->dup_count,
|
||||
g_hash_table_size(n->actions) ? "A" : "",
|
||||
n->actions ? "A" : "",
|
||||
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",
|
||||
g_hash_table_size(n->actions) ? "A" : "",
|
||||
n->actions ? "A" : "",
|
||||
n->urls ? "U" : "", msg);
|
||||
} else if (n->dup_count > 0 && !settings.hide_duplicate_count) {
|
||||
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 */
|
||||
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
|
||||
&& t_delta >= settings.show_age_threshold) {
|
||||
@ -647,48 +549,32 @@ void notification_update_text_to_render(struct notification *n)
|
||||
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 (g_hash_table_size(n->actions)) {
|
||||
if (g_hash_table_contains(n->actions, n->default_action_name)) {
|
||||
signal_action_invoked(n, n->default_action_name);
|
||||
if (n->actions) {
|
||||
if (n->actions->count == 2) {
|
||||
action_invoked(n, n->actions->actions[0]);
|
||||
return;
|
||||
}
|
||||
if (strcmp(n->default_action_name, "default") == 0 && g_hash_table_size(n->actions) == 1) {
|
||||
GList *keys = g_hash_table_get_keys(n->actions);
|
||||
signal_action_invoked(n, keys->data);
|
||||
g_list_free(keys);
|
||||
return;
|
||||
for (int i = 0; i < n->actions->count; i += 2) {
|
||||
if (strcmp(n->actions->actions[i], "default") == 0) {
|
||||
action_invoked(n, n->actions->actions[i]);
|
||||
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 */
|
||||
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: */
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
@ -2,237 +2,86 @@
|
||||
#ifndef DUNST_NOTIFICATION_H
|
||||
#define DUNST_NOTIFICATION_H
|
||||
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
#include <glib.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 {
|
||||
URG_NONE = -1, /**< Urgency not set (invalid) */
|
||||
URG_MIN = 0, /**< Minimum value, useful for boundary checking */
|
||||
URG_LOW = 0, /**< Low urgency */
|
||||
URG_NORM = 1, /**< Normal urgency */
|
||||
URG_CRIT = 2, /**< Critical urgency */
|
||||
URG_MAX = 2, /**< Maximum value, useful for boundary checking */
|
||||
URG_NONE = -1,
|
||||
URG_MIN = 0,
|
||||
URG_LOW = 0,
|
||||
URG_NORM = 1,
|
||||
URG_CRIT = 2,
|
||||
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 {
|
||||
char *frame;
|
||||
char *bg;
|
||||
char *fg;
|
||||
char *highlight;
|
||||
};
|
||||
|
||||
struct notification {
|
||||
NotificationPrivate *priv;
|
||||
int id;
|
||||
char *dbus_client;
|
||||
bool dbus_valid;
|
||||
typedef struct _actions {
|
||||
char **actions;
|
||||
char *dmenu_str;
|
||||
gsize count;
|
||||
} Actions;
|
||||
|
||||
typedef struct _notification {
|
||||
char *appname;
|
||||
char *summary;
|
||||
char *body;
|
||||
char *icon;
|
||||
RawImage *raw_icon;
|
||||
char *msg; /* formatted message */
|
||||
char *category;
|
||||
char *desktop_entry; /**< The desktop entry hint sent via every GApplication */
|
||||
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;
|
||||
char *text_to_render;
|
||||
const char *format;
|
||||
const char **scripts;
|
||||
int script_count;
|
||||
struct notification_colors colors;
|
||||
|
||||
char *stack_tag; /**< stack notifications by tag */
|
||||
|
||||
/* Hints */
|
||||
bool transient; /**< timeout albeit user is idle */
|
||||
int progress; /**< percentage (-1: undefined) */
|
||||
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 */
|
||||
char *dbus_client;
|
||||
gint64 start;
|
||||
gint64 timestamp;
|
||||
gint64 timeout;
|
||||
enum urgency urgency;
|
||||
enum markup_mode markup;
|
||||
bool redisplayed; /* has been displayed before? */
|
||||
int id;
|
||||
int dup_count;
|
||||
int displayed_height;
|
||||
enum behavior_fullscreen fullscreen; //!< The instruction what to do with it, when desktop enters fullscreen
|
||||
bool script_run; /**< Has the script been executed already? */
|
||||
guint8 marked_for_closure;
|
||||
const char *color_strings[3];
|
||||
bool first_render;
|
||||
bool transient;
|
||||
|
||||
/* derived fields */
|
||||
char *msg; /**< formatted message */
|
||||
char *text_to_render; /**< formatted message (with age and action indicators) */
|
||||
char *urls; /**< urllist delimited by '\\n' */
|
||||
};
|
||||
int progress; /* percentage (-1: undefined) */
|
||||
int history_ignore;
|
||||
const char *script;
|
||||
char *urls;
|
||||
Actions *actions;
|
||||
} notification;
|
||||
|
||||
/**
|
||||
* Create notification struct and initialise all fields with either
|
||||
* - the default (if it's not needed to be freed later)
|
||||
* - its undefined representation (NULL, -1)
|
||||
*
|
||||
* The reference counter is set to 1.
|
||||
*
|
||||
* This function is guaranteed to return a valid pointer.
|
||||
* @returns The generated notification
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
notification *notification_create(void);
|
||||
void notification_init(notification *n);
|
||||
void actions_free(Actions *a);
|
||||
void rawimage_free(RawImage *i);
|
||||
void notification_free(notification *n);
|
||||
int notification_cmp(const void *a, const void *b);
|
||||
int notification_cmp_data(const void *a, const void *b, void *data);
|
||||
int notification_is_duplicate(const notification *a, const notification *b);
|
||||
void notification_run_script(notification *n);
|
||||
void notification_print(notification *n);
|
||||
void notification_replace_single_field(char **haystack,
|
||||
char **needle,
|
||||
const char *replacement,
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
const char *notification_urgency_to_string(enum urgency urgency);
|
||||
#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 <string.h>
|
||||
|
||||
#include "dunst.h"
|
||||
#include "log.h"
|
||||
#include "utils.h"
|
||||
#include "settings.h"
|
||||
|
||||
struct entry {
|
||||
typedef struct _entry_t {
|
||||
char *key;
|
||||
char *value;
|
||||
};
|
||||
} entry_t;
|
||||
|
||||
struct section {
|
||||
typedef struct _section_t {
|
||||
char *name;
|
||||
int entry_count;
|
||||
struct entry *entries;
|
||||
};
|
||||
entry_t *entries;
|
||||
} section_t;
|
||||
|
||||
static int section_count = 0;
|
||||
static struct section *sections;
|
||||
static section_t *sections;
|
||||
|
||||
static struct section *new_section(const char *name);
|
||||
static struct section *get_section(const char *name);
|
||||
static section_t *new_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 const char *get_value(const char *section, const char *key);
|
||||
static char *clean_value(const char *value);
|
||||
|
||||
static int cmdline_argc;
|
||||
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);
|
||||
|
||||
#define STRING_PARSE_RET(string, value) if (STR_EQ(s, string)) { *ret = value; return true; }
|
||||
|
||||
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)
|
||||
section_t *new_section(const char *name)
|
||||
{
|
||||
for (int i = 0; i < section_count; i++) {
|
||||
if (STR_EQ(name, sections[i].name)) {
|
||||
DIE("Duplicated section in dunstrc detected.");
|
||||
if (!strcmp(name, sections[i].name)) {
|
||||
die("Duplicated section in dunstrc detected.\n", -1);
|
||||
}
|
||||
}
|
||||
|
||||
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].entries = NULL;
|
||||
sections[section_count - 1].entry_count = 0;
|
||||
@ -230,14 +64,15 @@ void free_ini(void)
|
||||
g_free(sections[i].entries);
|
||||
g_free(sections[i].name);
|
||||
}
|
||||
g_clear_pointer(§ions, g_free);
|
||||
g_free(sections);
|
||||
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++) {
|
||||
if (STR_EQ(sections[i].name, name))
|
||||
if (strcmp(sections[i].name, name) == 0)
|
||||
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)
|
||||
{
|
||||
struct section *s = get_section(section_name);
|
||||
if (!s)
|
||||
section_t *s = get_section(section_name);
|
||||
if (s == NULL) {
|
||||
s = new_section(section_name);
|
||||
}
|
||||
|
||||
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].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)
|
||||
{
|
||||
struct section *s = get_section(section);
|
||||
ASSERT_OR_RET(s, NULL);
|
||||
section_t *s = get_section(section);
|
||||
if (!s) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -296,31 +134,22 @@ gint64 ini_get_time(const char *section, const char *key, gint64 def)
|
||||
return val;
|
||||
}
|
||||
|
||||
char **ini_get_list(const char *section, const char *key, const char *def)
|
||||
{
|
||||
const char *value = get_value(section, key);
|
||||
if (value)
|
||||
return string_to_array(value);
|
||||
else
|
||||
return string_to_array(def);
|
||||
}
|
||||
|
||||
int ini_get_int(const char *section, const char *key, int def)
|
||||
{
|
||||
const char *value = get_value(section, key);
|
||||
if (value)
|
||||
return atoi(value);
|
||||
else
|
||||
if (value == NULL)
|
||||
return def;
|
||||
else
|
||||
return atoi(value);
|
||||
}
|
||||
|
||||
double ini_get_double(const char *section, const char *key, double def)
|
||||
{
|
||||
const char *value = get_value(section, key);
|
||||
if (value)
|
||||
return atof(value);
|
||||
else
|
||||
if (value == NULL)
|
||||
return def;
|
||||
else
|
||||
return atof(value);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
ASSERT_OR_RET(section_count > 0, NULL);
|
||||
ASSERT_OR_RET(section, sections[0].name);
|
||||
if (section_count == 0)
|
||||
return NULL;
|
||||
|
||||
if (section == NULL) {
|
||||
return sections[0].name;
|
||||
}
|
||||
|
||||
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)
|
||||
return NULL;
|
||||
else
|
||||
@ -347,7 +180,9 @@ const char *next_section(const char *section)
|
||||
int ini_get_bool(const char *section, const char *key, int def)
|
||||
{
|
||||
const char *value = get_value(section, key);
|
||||
if (value) {
|
||||
if (value == NULL)
|
||||
return def;
|
||||
else {
|
||||
switch (value[0]) {
|
||||
case 'y':
|
||||
case 'Y':
|
||||
@ -364,14 +199,28 @@ int ini_get_bool(const char *section, const char *key, int def)
|
||||
default:
|
||||
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)
|
||||
{
|
||||
ASSERT_OR_RET(fp, 1);
|
||||
if (!fp)
|
||||
return 1;
|
||||
|
||||
char *line = NULL;
|
||||
size_t line_len = 0;
|
||||
@ -383,13 +232,16 @@ int load_ini_file(FILE *fp)
|
||||
|
||||
char *start = g_strstrip(line);
|
||||
|
||||
if (*start == ';' || *start == '#' || STR_EMPTY(start))
|
||||
if (*start == ';' || *start == '#' || strlen(start) == 0)
|
||||
continue;
|
||||
|
||||
if (*start == '[') {
|
||||
char *end = strchr(start + 1, ']');
|
||||
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;
|
||||
}
|
||||
|
||||
@ -403,7 +255,10 @@ int load_ini_file(FILE *fp)
|
||||
|
||||
char *equal = strchr(start + 1, '=');
|
||||
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;
|
||||
}
|
||||
|
||||
@ -412,25 +267,27 @@ int load_ini_file(FILE *fp)
|
||||
char *value = g_strstrip(equal + 1);
|
||||
|
||||
char *quote = strchr(value, '"');
|
||||
char *value_end = NULL;
|
||||
if (quote) {
|
||||
value_end = strchr(quote + 1, '"');
|
||||
if (!value_end) {
|
||||
LOG_W("Invalid config file at line %d: Missing '\"'.", line_num);
|
||||
char *closing_quote = strchr(quote + 1, '"');
|
||||
if (!closing_quote) {
|
||||
fprintf(stderr,
|
||||
"Warning: invalid config file at line %d\n",
|
||||
line_num);
|
||||
fprintf(stderr, "Missing '\"'\n");
|
||||
continue;
|
||||
}
|
||||
} 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -449,8 +306,9 @@ void cmdline_load(int argc, char *argv[])
|
||||
|
||||
int cmdline_find_option(const char *key)
|
||||
{
|
||||
ASSERT_OR_RET(key, -1);
|
||||
|
||||
if (!key) {
|
||||
return -1;
|
||||
}
|
||||
char *key1 = g_strdup(key);
|
||||
char *key2 = strchr(key1, '/');
|
||||
|
||||
@ -461,7 +319,7 @@ int cmdline_find_option(const char *key)
|
||||
|
||||
/* look for first key */
|
||||
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);
|
||||
return i;
|
||||
}
|
||||
@ -470,7 +328,7 @@ int cmdline_find_option(const char *key)
|
||||
/* look for second key if one was specified */
|
||||
if (key2) {
|
||||
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);
|
||||
return i;
|
||||
}
|
||||
@ -490,7 +348,8 @@ static const char *cmdline_get_value(const char *key)
|
||||
|
||||
if (idx + 1 >= cmdline_argc) {
|
||||
/* the argument is missing */
|
||||
LOG_W("%s: Missing argument. Ignoring.", key);
|
||||
fprintf(stderr, "Warning: %s, missing argument. Ignoring\n",
|
||||
key);
|
||||
return NULL;
|
||||
}
|
||||
return cmdline_argv[idx + 1];
|
||||
@ -503,10 +362,10 @@ char *cmdline_get_string(const char *key, const char *def, const char *descripti
|
||||
|
||||
if (str)
|
||||
return g_strdup(str);
|
||||
if (def)
|
||||
return g_strdup(def);
|
||||
else
|
||||
if (def == NULL)
|
||||
return NULL;
|
||||
else
|
||||
return g_strdup(def);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
char **cmdline_get_list(const char *key, const char *def, const char *description)
|
||||
{
|
||||
cmdline_usage_append(key, "list", description);
|
||||
const char *str = cmdline_get_value(key);
|
||||
|
||||
if (str)
|
||||
return string_to_array(str);
|
||||
else
|
||||
return string_to_array(def);
|
||||
}
|
||||
|
||||
gint64 cmdline_get_time(const char *key, gint64 def, const char *description)
|
||||
{
|
||||
cmdline_usage_append(key, "time", description);
|
||||
@ -549,10 +397,10 @@ int cmdline_get_int(const char *key, int def, const char *description)
|
||||
cmdline_usage_append(key, "int", description);
|
||||
const char *str = cmdline_get_value(key);
|
||||
|
||||
if (str)
|
||||
return atoi(str);
|
||||
else
|
||||
if (str == NULL)
|
||||
return def;
|
||||
else
|
||||
return atoi(str);
|
||||
}
|
||||
|
||||
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);
|
||||
const char *str = cmdline_get_value(key);
|
||||
|
||||
if (str)
|
||||
return atof(str);
|
||||
else
|
||||
if (str == NULL)
|
||||
return def;
|
||||
else
|
||||
return atof(str);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
char **option_get_list(const char *ini_section,
|
||||
const char *ini_key,
|
||||
const char *cmdline_key,
|
||||
const char *def,
|
||||
const char *description)
|
||||
{
|
||||
char **val = NULL;
|
||||
if (cmdline_key)
|
||||
val = cmdline_get_list(cmdline_key, NULL, description);
|
||||
|
||||
if (val)
|
||||
return val;
|
||||
else
|
||||
return ini_get_list(ini_section, ini_key, def);
|
||||
}
|
||||
|
||||
int option_get_int(const char *ini_section,
|
||||
const char *ini_key,
|
||||
const char *cmdline_key,
|
||||
@ -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)
|
||||
{
|
||||
char *key_type;
|
||||
if (STR_FULL(type))
|
||||
if (type && strlen(type) > 0)
|
||||
key_type = g_strdup_printf("%s (%s)", key, type);
|
||||
else
|
||||
key_type = g_strdup(key);
|
||||
@ -731,4 +562,4 @@ const char *cmdline_create_usage(void)
|
||||
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 <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 *);
|
||||
char *ini_get_path(const char *section, const char *key, const char *def);
|
||||
char *ini_get_string(const char *section, const char *key, const char *def);
|
||||
gint64 ini_get_time(const char *section, const char *key, gint64 def);
|
||||
char **ini_get_list(const char *section, const char *key, const char *def);
|
||||
int ini_get_int(const char *section, const char *key, int def);
|
||||
double ini_get_double(const char *section, const char *key, double def);
|
||||
int ini_get_bool(const char *section, const char *key, int def);
|
||||
@ -37,7 +20,6 @@ void cmdline_load(int argc, char *argv[]);
|
||||
/* for all cmdline_get_* key can be either "-key" or "-key/-longkey" */
|
||||
char *cmdline_get_string(const char *key, const char *def, const char *description);
|
||||
char *cmdline_get_path(const char *key, const char *def, const char *description);
|
||||
char **cmdline_get_list(const char *key, const char *def, const char *description);
|
||||
int cmdline_get_int(const char *key, int def, const char *description);
|
||||
double cmdline_get_double(const char *key, double def, const char *description);
|
||||
int cmdline_get_bool(const char *key, int def, const char *description);
|
||||
@ -59,11 +41,6 @@ gint64 option_get_time(const char *ini_section,
|
||||
const char *cmdline_key,
|
||||
gint64 def,
|
||||
const char *description);
|
||||
char **option_get_list(const char *ini_section,
|
||||
const char *ini_key,
|
||||
const char *cmdline_key,
|
||||
const char *def,
|
||||
const char *description);
|
||||
int option_get_int(const char *ini_section,
|
||||
const char *ini_key,
|
||||
const char *cmdline_key,
|
||||
@ -87,4 +64,4 @@ int option_get_bool(const char *ini_section,
|
||||
const char *next_section(const char *section);
|
||||
|
||||
#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) */
|
||||
|
||||
/**
|
||||
* @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 <assert.h>
|
||||
@ -20,24 +7,20 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dunst.h"
|
||||
#include "log.h"
|
||||
#include "notification.h"
|
||||
#include "settings.h"
|
||||
#include "utils.h"
|
||||
#include "output.h" // For checking if wayland is active.
|
||||
|
||||
/* notification lists */
|
||||
static GQueue *waiting = NULL; /**< all new notifications get into here */
|
||||
static GQueue *displayed = NULL; /**< currently displayed notifications */
|
||||
static GQueue *history = NULL; /**< history of displayed notifications */
|
||||
static GQueue *waiting = NULL; /* all new notifications get into here */
|
||||
static GQueue *displayed = NULL; /* currently displayed notifications */
|
||||
static GQueue *history = NULL; /* history of displayed notifications */
|
||||
|
||||
unsigned int displayed_limit = 0;
|
||||
int next_notification_id = 1;
|
||||
bool pause_displayed = false;
|
||||
|
||||
static bool queues_stack_duplicate(struct notification *n);
|
||||
static bool queues_stack_by_tag(struct notification *n);
|
||||
static bool queues_stack_duplicate(notification *n);
|
||||
|
||||
/* see queues.h */
|
||||
void queues_init(void)
|
||||
{
|
||||
history = g_queue_new();
|
||||
@ -45,314 +28,222 @@ void queues_init(void)
|
||||
waiting = g_queue_new();
|
||||
}
|
||||
|
||||
/* see queues.h */
|
||||
GList *queues_get_displayed(void)
|
||||
void queues_displayed_limit(unsigned int limit)
|
||||
{
|
||||
displayed_limit = limit;
|
||||
}
|
||||
|
||||
/* misc getter functions */
|
||||
const GList *queues_get_displayed()
|
||||
{
|
||||
return g_queue_peek_head_link(displayed);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
unsigned int queues_length_waiting()
|
||||
{
|
||||
return waiting->length;
|
||||
}
|
||||
|
||||
/* see queues.h */
|
||||
unsigned int queues_length_displayed(void)
|
||||
unsigned int queues_length_displayed()
|
||||
{
|
||||
return displayed->length;
|
||||
}
|
||||
|
||||
/* see queues.h */
|
||||
unsigned int queues_length_history(void)
|
||||
unsigned int queues_length_history()
|
||||
{
|
||||
return history->length;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
int queues_notification_insert(notification *n, int replaces_id)
|
||||
{
|
||||
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 */
|
||||
if (STR_EMPTY(n->msg)) {
|
||||
if (strlen(n->msg) == 0) {
|
||||
if (settings.always_run_script) {
|
||||
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;
|
||||
}
|
||||
|
||||
bool inserted = false;
|
||||
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 {
|
||||
if (replaces_id == 0) {
|
||||
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)
|
||||
notification_print(n);
|
||||
|
||||
return n->id;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* Replaces duplicate notification and stacks it
|
||||
*
|
||||
* @retval true: notification got stacked
|
||||
* @retval false: notification did not get stacked
|
||||
* Returns %true, if notification got 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 (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 *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;
|
||||
signal_notification_closed(orig, 1);
|
||||
|
||||
if (allqueues[i] == displayed)
|
||||
n->start = time_monotonic_now();
|
||||
|
||||
notification_unref(orig);
|
||||
return true;
|
||||
for (GList *iter = g_queue_peek_head_link(displayed); 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->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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
bool queues_notification_replace_id(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) {
|
||||
new->start = time_monotonic_now();
|
||||
notification_run_script(new);
|
||||
}
|
||||
|
||||
notification_unref(old);
|
||||
return true;
|
||||
}
|
||||
for (GList *iter = g_queue_peek_head_link(waiting);
|
||||
iter;
|
||||
iter = iter->next) {
|
||||
notification *old = iter->data;
|
||||
if (old->id == new->id) {
|
||||
iter->data = new;
|
||||
new->dup_count = old->dup_count;
|
||||
notification_free(old);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* see queues.h */
|
||||
bool queues_notification_replace_id(struct notification *new)
|
||||
int queues_notification_close_id(int id, enum reason reason)
|
||||
{
|
||||
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 (old->id == new->id) {
|
||||
iter->data = new;
|
||||
new->dup_count = old->dup_count;
|
||||
notification *target = NULL;
|
||||
|
||||
if (allqueues[i] == displayed) {
|
||||
new->start = time_monotonic_now();
|
||||
notification_run_script(new);
|
||||
}
|
||||
|
||||
notification_unref(old);
|
||||
return true;
|
||||
}
|
||||
for (GList *iter = g_queue_peek_head_link(displayed); iter;
|
||||
iter = iter->next) {
|
||||
notification *n = iter->data;
|
||||
if (n->id == id) {
|
||||
g_queue_remove(displayed, n);
|
||||
target = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* see queues.h */
|
||||
void queues_notification_close_id(int id, enum reason reason)
|
||||
{
|
||||
struct notification *target = NULL;
|
||||
|
||||
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 *n = iter->data;
|
||||
if (n->id == id) {
|
||||
g_queue_remove(allqueues[i], n);
|
||||
target = n;
|
||||
break;
|
||||
}
|
||||
for (GList *iter = g_queue_peek_head_link(waiting); iter;
|
||||
iter = iter->next) {
|
||||
notification *n = iter->data;
|
||||
if (n->id == id) {
|
||||
assert(target == NULL);
|
||||
g_queue_remove(waiting, n);
|
||||
target = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (target) {
|
||||
//Don't notify clients if notification was pulled from history
|
||||
if (!target->redisplayed)
|
||||
signal_notification_closed(target, reason);
|
||||
notification_closed(target, reason);
|
||||
queues_history_push(target);
|
||||
}
|
||||
|
||||
return reason;
|
||||
}
|
||||
|
||||
/* see queues.h */
|
||||
void queues_notification_close(struct notification *n, enum reason reason)
|
||||
int queues_notification_close(notification *n, enum reason reason)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (g_queue_is_empty(history))
|
||||
return;
|
||||
|
||||
struct notification *n = g_queue_pop_tail(history);
|
||||
notification *n = g_queue_pop_tail(history);
|
||||
n->redisplayed = true;
|
||||
n->start = 0;
|
||||
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(struct notification *n)
|
||||
void queues_history_push(notification *n)
|
||||
{
|
||||
if (!n->history_ignore) {
|
||||
if (settings.history_length > 0 && history->length >= settings.history_length) {
|
||||
struct notification *to_free = g_queue_pop_head(history);
|
||||
notification_unref(to_free);
|
||||
notification *to_free = g_queue_pop_head(history);
|
||||
notification_free(to_free);
|
||||
}
|
||||
|
||||
g_queue_push_tail(history, n);
|
||||
} else {
|
||||
notification_unref(n);
|
||||
notification_free(n);
|
||||
}
|
||||
}
|
||||
|
||||
/* see queues.h */
|
||||
void queues_history_push_all(void)
|
||||
{
|
||||
while (displayed->length > 0) {
|
||||
@ -364,125 +255,81 @@ void queues_history_push_all(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* see queues.h */
|
||||
void queues_update(struct dunst_status status)
|
||||
void queues_check_timeouts(bool idle)
|
||||
{
|
||||
GList *iter, *nextiter;
|
||||
/* nothing to do */
|
||||
if (displayed->length == 0)
|
||||
return;
|
||||
|
||||
/* Move back all notifications, which aren't eligible to get shown anymore
|
||||
* 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);
|
||||
GList *iter = g_queue_peek_head_link(displayed);
|
||||
while (iter) {
|
||||
struct notification *n = iter->data;
|
||||
nextiter = iter->next;
|
||||
notification *n = iter->data;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (n->marked_for_closure) {
|
||||
queues_notification_close(n, n->marked_for_closure);
|
||||
n->marked_for_closure = 0;
|
||||
iter = nextiter;
|
||||
/* skip hidden and sticky messages */
|
||||
if (n->start == 0 || n->timeout == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (queues_notification_is_finished(n, status)){
|
||||
/* remove old message */
|
||||
if (g_get_monotonic_time() - n->start > n->timeout) {
|
||||
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 sleep = G_MAXINT64;
|
||||
|
||||
for (GList *iter = g_queue_peek_head_link(displayed); iter;
|
||||
iter = iter->next) {
|
||||
struct notification *n = iter->data;
|
||||
notification *n = iter->data;
|
||||
gint64 ttl = n->timeout - (time - n->start);
|
||||
|
||||
if (n->timeout > 0) {
|
||||
@ -496,59 +343,42 @@ gint64 queues_get_next_datachange(gint64 time)
|
||||
if (settings.show_age_threshold >= 0) {
|
||||
gint64 age = time - n->timestamp;
|
||||
|
||||
// sleep exactly until the next shift of the second happens
|
||||
if (age > settings.show_age_threshold - S2US(1))
|
||||
sleep = MIN(sleep, (S2US(1) - (age % S2US(1))));
|
||||
else
|
||||
sleep = MIN(sleep, settings.show_age_threshold - age);
|
||||
if (age > settings.show_age_threshold)
|
||||
// sleep exactly until the next shift of the second happens
|
||||
sleep = MIN(sleep, ((G_USEC_PER_SEC) - (age % (G_USEC_PER_SEC))));
|
||||
else if (ttl > settings.show_age_threshold)
|
||||
sleep = MIN(sleep, settings.show_age_threshold);
|
||||
}
|
||||
}
|
||||
|
||||
return sleep != G_MAXINT64 ? sleep : -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* see queues.h */
|
||||
struct notification* queues_get_by_id(int id)
|
||||
void queues_pause_on(void)
|
||||
{
|
||||
assert(id > 0);
|
||||
|
||||
GQueue *recqueues[] = { displayed, waiting, history };
|
||||
for (int i = 0; i < sizeof(recqueues)/sizeof(GQueue*); i++) {
|
||||
for (GList *iter = g_queue_peek_head_link(recqueues[i]); iter;
|
||||
iter = iter->next) {
|
||||
struct notification *cur = iter->data;
|
||||
if (cur->id == id)
|
||||
return cur;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
pause_displayed = true;
|
||||
}
|
||||
|
||||
void queues_pause_off(void)
|
||||
{
|
||||
pause_displayed = false;
|
||||
}
|
||||
|
||||
bool queues_pause_status(void)
|
||||
{
|
||||
return pause_displayed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for queues_teardown() to free a single notification
|
||||
*
|
||||
* @param data The notification to free
|
||||
*/
|
||||
static void teardown_notification(gpointer data)
|
||||
{
|
||||
struct notification *n = data;
|
||||
notification_unref(n);
|
||||
notification *n = data;
|
||||
notification_free(n);
|
||||
}
|
||||
|
||||
/* see queues.h */
|
||||
void queues_teardown(void)
|
||||
void teardown_queues(void)
|
||||
{
|
||||
g_queue_free_full(history, teardown_notification);
|
||||
history = NULL;
|
||||
g_queue_free_full(displayed, teardown_notification);
|
||||
displayed = NULL;
|
||||
g_queue_free_full(waiting, teardown_notification);
|
||||
waiting = NULL;
|
||||
}
|
||||
|
||||
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set 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) */
|
||||
|
||||
/**
|
||||
* @file src/queues.h
|
||||
*/
|
||||
|
||||
#ifndef DUNST_QUEUE_H
|
||||
#define DUNST_QUEUE_H
|
||||
|
||||
#include "dbus.h"
|
||||
#include "dunst.h"
|
||||
#include "notification.h"
|
||||
|
||||
/**
|
||||
* Initialise necessary queues
|
||||
*
|
||||
* @pre Do not call consecutively to avoid memory leaks
|
||||
* or assure to have queues_teardown() executed before
|
||||
/*
|
||||
* Initialise neccessary queues
|
||||
*/
|
||||
void queues_init(void);
|
||||
|
||||
/**
|
||||
* Receive the current list of displayed notifications
|
||||
*
|
||||
* @return read only list of notifications
|
||||
/*
|
||||
* Set maximum notification count to display
|
||||
* and store in displayed queue
|
||||
*/
|
||||
GList *queues_get_displayed(void);
|
||||
void queues_displayed_limit(unsigned int limit);
|
||||
|
||||
/**
|
||||
* Get the highest notification in line
|
||||
*
|
||||
* @returns the first notification in waiting
|
||||
* @retval NULL: waiting is empty
|
||||
/*
|
||||
* Return read only list of notifications
|
||||
*/
|
||||
const struct notification *queues_get_head_waiting(void);
|
||||
const GList *queues_get_displayed();
|
||||
|
||||
/**
|
||||
/*
|
||||
* 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
|
||||
*
|
||||
* 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
|
||||
* - If n->id == 0, n gets a new id assigned
|
||||
*
|
||||
* @returns The new value of `n->id`
|
||||
* @retval 0: the notification was dismissed and freed
|
||||
* Returns the assigned notification id
|
||||
* If returned id == 0, the message was dismissed
|
||||
*/
|
||||
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
|
||||
* the new notification. The given notification is inserted
|
||||
* right in the same position as the old notification.
|
||||
*
|
||||
* @param new replacement for the old notification
|
||||
*
|
||||
* @retval true: a matching notification has been found and is replaced
|
||||
* @retval false: otherwise
|
||||
* Returns true, if a matching notification has been found
|
||||
* and is replaced. Else false.
|
||||
*/
|
||||
bool queues_notification_replace_id(struct notification *new);
|
||||
bool queues_notification_replace_id(notification *new);
|
||||
|
||||
/**
|
||||
/*
|
||||
* 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
|
||||
* @param reason The #reason to close
|
||||
*
|
||||
* @post Call wake_up() to synchronize the queues with the UI
|
||||
* (which closes the notification on screen)
|
||||
* After closing, 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
|
||||
* @param reason The #reason to close
|
||||
* @n: (transfer full): The notification 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
|
||||
*/
|
||||
void queues_history_pop(void);
|
||||
|
||||
/**
|
||||
/*
|
||||
* Push a single notification to history
|
||||
* 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
|
||||
*/
|
||||
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
|
||||
* and show them. In displayed queue, the amount of elements is limited
|
||||
* 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
|
||||
* to the amount set via queues_displayed_limit
|
||||
*/
|
||||
void queues_update(struct dunst_status status);
|
||||
void queues_update();
|
||||
|
||||
/**
|
||||
* Calculate the distance to the next event, when an element in the
|
||||
* queues changes
|
||||
/*
|
||||
* Return the distance to the next event in the queue,
|
||||
* 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
|
||||
* an update visible to the user. This may be:
|
||||
* - notification hits timeout
|
||||
* - notification's age second changes
|
||||
* - notification's age threshold is hit
|
||||
* - notification hits timeout
|
||||
* - notification's age second changes
|
||||
* - notification's age threshold is hit
|
||||
*/
|
||||
gint64 queues_get_next_datachange(gint64 time);
|
||||
|
||||
/**
|
||||
* Get the notification which has the given id in the displayed and waiting queue or
|
||||
* NULL if not found
|
||||
/*
|
||||
* Pause queue-management of dunst
|
||||
* pause_on = paused (no notifications displayed)
|
||||
* pause_off = running
|
||||
*
|
||||
* @param id the id searched for.
|
||||
*
|
||||
* @return the `id` notification or NULL
|
||||
* Calling update_lists is neccessary
|
||||
*/
|
||||
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 list and free the notifications
|
||||
*
|
||||
* @pre At least one time queues_init() called
|
||||
/*
|
||||
* Remove all notifications from all lists
|
||||
* and free the notifications
|
||||
*/
|
||||
void queues_teardown(void);
|
||||
void teardown_queues(void);
|
||||
|
||||
#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"
|
||||
|
||||
GSList *rules = NULL;
|
||||
|
||||
/*
|
||||
* 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)
|
||||
n->timeout = r->timeout;
|
||||
if (r->urgency != URG_NONE)
|
||||
n->urgency = r->urgency;
|
||||
if (r->fullscreen != FS_NULL)
|
||||
n->fullscreen = r->fullscreen;
|
||||
if (r->history_ignore != -1)
|
||||
n->history_ignore = r->history_ignore;
|
||||
if (r->set_transient != -1)
|
||||
n->transient = r->set_transient;
|
||||
if (r->skip_display != -1)
|
||||
n->skip_display = r->skip_display;
|
||||
if (r->action_name) {
|
||||
g_free(n->default_action_name);
|
||||
n->default_action_name = g_strdup(r->action_name);
|
||||
}
|
||||
if (r->markup != MARKUP_NULL)
|
||||
n->markup = r->markup;
|
||||
if (r->new_icon)
|
||||
notification_icon_replace_path(n, r->new_icon);
|
||||
if (r->fg) {
|
||||
g_free(n->colors.fg);
|
||||
n->colors.fg = g_strdup(r->fg);
|
||||
}
|
||||
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->new_icon) {
|
||||
g_free(n->icon);
|
||||
n->icon = g_strdup(r->new_icon);
|
||||
rawimage_free(n->raw_icon);
|
||||
n->raw_icon = NULL;
|
||||
}
|
||||
if (r->fg)
|
||||
n->color_strings[ColFG] = r->fg;
|
||||
if (r->bg)
|
||||
n->color_strings[ColBG] = r->bg;
|
||||
if (r->format)
|
||||
n->format = r->format;
|
||||
if (r->script){
|
||||
n->scripts = g_renew(const char*,n->scripts,n->script_count + 1);
|
||||
n->scripts[n->script_count] = r->script;
|
||||
|
||||
n->script_count++;
|
||||
}
|
||||
if (r->set_stack_tag) {
|
||||
g_free(n->stack_tag);
|
||||
n->stack_tag = g_strdup(r->set_stack_tag);
|
||||
}
|
||||
if (r->script)
|
||||
n->script = r->script;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
struct rule *r = iter->data;
|
||||
rule_t *r = iter->data;
|
||||
if (rule_matches_notification(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->timeout = -1;
|
||||
r->urgency = URG_NONE;
|
||||
r->fullscreen = FS_NULL;
|
||||
r->markup = MARKUP_NULL;
|
||||
r->history_ignore = -1;
|
||||
r->new_icon = NULL;
|
||||
r->history_ignore = false;
|
||||
r->match_transient = -1;
|
||||
r->set_transient = -1;
|
||||
r->skip_display = -1;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline bool rule_field_matches_string(const char *value, const char *pattern)
|
||||
{
|
||||
return !pattern || (value && !fnmatch(pattern, value, 0));
|
||||
r->fg = NULL;
|
||||
r->bg = NULL;
|
||||
r->format = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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))
|
||||
&& rule_field_matches_string(n->appname, r->appname)
|
||||
&& 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);
|
||||
&& (r->msg_urgency == URG_NONE || r->msg_urgency == n->urgency));
|
||||
}
|
||||
/* 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 "settings.h"
|
||||
|
||||
struct rule {
|
||||
typedef struct _rule_t {
|
||||
char *name;
|
||||
/* filters */
|
||||
char *appname;
|
||||
@ -16,42 +16,28 @@ struct rule {
|
||||
char *body;
|
||||
char *icon;
|
||||
char *category;
|
||||
char *stack_tag;
|
||||
char *desktop_entry;
|
||||
int msg_urgency;
|
||||
|
||||
/* actions */
|
||||
gint64 timeout;
|
||||
enum urgency urgency;
|
||||
char *action_name;
|
||||
enum markup_mode markup;
|
||||
int history_ignore;
|
||||
int match_transient;
|
||||
int set_transient;
|
||||
int skip_display;
|
||||
char *new_icon;
|
||||
char *fg;
|
||||
char *bg;
|
||||
char *highlight;
|
||||
char *fc;
|
||||
const char *format;
|
||||
const char *script;
|
||||
enum behavior_fullscreen fullscreen;
|
||||
char *set_stack_tag;
|
||||
};
|
||||
} rule_t;
|
||||
|
||||
extern GSList *rules;
|
||||
|
||||
/**
|
||||
* Allocate a new rule. The rule is fully initialised.
|
||||
*
|
||||
* @returns A new initialised rule.
|
||||
*/
|
||||
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);
|
||||
void rule_init(rule_t *r);
|
||||
void rule_apply(rule_t *r, notification *n);
|
||||
void rule_apply_all(notification *n);
|
||||
bool rule_matches_notification(rule_t *r, notification *n);
|
||||
|
||||
#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 <stdio.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 "log.h"
|
||||
#include "notification.h"
|
||||
#include "option_parser.h"
|
||||
#include "rules.h"
|
||||
#include "utils.h"
|
||||
#include "x11/x.h"
|
||||
#include "output.h"
|
||||
|
||||
#include "../config.h"
|
||||
settings_t settings;
|
||||
|
||||
struct settings settings;
|
||||
|
||||
static enum urgency ini_get_urgency(const char *section, const char *key, const enum urgency def)
|
||||
static void parse_follow_mode(const char *mode)
|
||||
{
|
||||
enum urgency ret;
|
||||
char *c = ini_get_string(section, key, NULL);
|
||||
|
||||
if (!string_parse_urgency(c, &ret)) {
|
||||
if (c)
|
||||
LOG_W("Unknown urgency: '%s'", c);
|
||||
ret = def;
|
||||
if (strcmp(mode, "mouse") == 0)
|
||||
settings.f_mode = FOLLOW_MOUSE;
|
||||
else if (strcmp(mode, "keyboard") == 0)
|
||||
settings.f_mode = FOLLOW_KEYBOARD;
|
||||
else if (strcmp(mode, "none") == 0)
|
||||
settings.f_mode = FOLLOW_NONE;
|
||||
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();
|
||||
const gchar * userdir = g_get_user_config_dir();
|
||||
|
||||
FILE *f;
|
||||
char *path;
|
||||
|
||||
path = g_strconcat(userdir, filename, NULL);
|
||||
f = fopen(path, "r");
|
||||
g_free(path);
|
||||
|
||||
for (const gchar * const *d = systemdirs;
|
||||
!f && *d;
|
||||
d++) {
|
||||
path = g_strconcat(*d, filename, NULL);
|
||||
f = fopen(path, "r");
|
||||
g_free(path);
|
||||
if (strcmp(mode, "strip") == 0) {
|
||||
return MARKUP_STRIP;
|
||||
} else if (strcmp(mode, "no") == 0) {
|
||||
return MARKUP_NO;
|
||||
} else if (strcmp(mode, "full") == 0 || strcmp(mode, "yes") == 0) {
|
||||
return MARKUP_FULL;
|
||||
} else {
|
||||
fprintf(stderr, "Warning: unknown markup mode: \"%s\"\n", mode);
|
||||
return MARKUP_NO;
|
||||
}
|
||||
}
|
||||
|
||||
if (!f) {
|
||||
f = fopen("/etc/dunst/dunstrc", "r");
|
||||
static enum urgency ini_get_urgency(const char *section, const char *key, const int def)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
return f;
|
||||
g_free(urg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void load_settings(char *cmdline_config_path)
|
||||
{
|
||||
|
||||
#ifndef STATIC_CONFIG
|
||||
xdgHandle xdg;
|
||||
FILE *config_file = NULL;
|
||||
|
||||
if (cmdline_config_path) {
|
||||
if (STR_EQ(cmdline_config_path, "-")) {
|
||||
config_file = stdin;
|
||||
} else {
|
||||
config_file = fopen(cmdline_config_path, "r");
|
||||
}
|
||||
xdgInitHandle(&xdg);
|
||||
|
||||
if (!config_file) {
|
||||
DIE("Cannot find config file: '%s'", cmdline_config_path);
|
||||
}
|
||||
if (cmdline_config_path != NULL) {
|
||||
config_file = fopen(cmdline_config_path, "r");
|
||||
}
|
||||
|
||||
if (!config_file) {
|
||||
config_file = xdg_config("/dunst/dunstrc");
|
||||
if (config_file == NULL) {
|
||||
config_file = xdgConfigOpen("dunst/dunstrc", "r", &xdg);
|
||||
}
|
||||
|
||||
if (!config_file) {
|
||||
if (config_file == NULL) {
|
||||
/* Fall back to just "dunstrc", which was used before 2013-06-23
|
||||
* (before v0.2). */
|
||||
config_file = xdg_config("/dunstrc");
|
||||
}
|
||||
|
||||
if (!config_file) {
|
||||
LOG_W("No dunstrc found.");
|
||||
config_file = xdgConfigOpen("dunstrc", "r", &xdg);
|
||||
if (config_file == NULL) {
|
||||
puts("no dunstrc found -> skipping\n");
|
||||
xdgWipeHandle(&xdg);
|
||||
}
|
||||
}
|
||||
|
||||
load_ini_file(config_file);
|
||||
#else
|
||||
LOG_M("dunstrc parsing disabled. "
|
||||
"Using STATIC_CONFIG is deprecated behavior.");
|
||||
fprintf(stderr, "Warning: dunstrc parsing disabled. "
|
||||
"Using STATIC_CONFIG is deprecated behavior.\n");
|
||||
#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(
|
||||
"experimental",
|
||||
"per_monitor_dpi", NULL, false,
|
||||
@ -123,12 +111,6 @@ void load_settings(char *cmdline_config_path)
|
||||
"Force the use of the Xinerama extension"
|
||||
);
|
||||
|
||||
settings.force_xwayland = option_get_bool(
|
||||
"global",
|
||||
"force_xwayland", "-force_xwayland", false,
|
||||
"Force the use of the xwayland output"
|
||||
);
|
||||
|
||||
settings.font = option_get_string(
|
||||
"global",
|
||||
"font", "-font/-fn", defaults.font,
|
||||
@ -145,8 +127,7 @@ void load_settings(char *cmdline_config_path)
|
||||
);
|
||||
|
||||
settings.markup = (allow_markup ? MARKUP_FULL : MARKUP_STRIP);
|
||||
LOG_M("'allow_markup' is deprecated, please "
|
||||
"use 'markup' instead.");
|
||||
fprintf(stderr, "Warning: 'allow_markup' is deprecated, please use 'markup' instead.\n");
|
||||
}
|
||||
|
||||
char *c = option_get_string(
|
||||
@ -155,11 +136,13 @@ void load_settings(char *cmdline_config_path)
|
||||
"Specify how markup should be handled"
|
||||
);
|
||||
|
||||
if (!string_parse_markup_mode(c, &settings.markup)) {
|
||||
if (c)
|
||||
LOG_W("Cannot parse markup mode value: '%s'", c);
|
||||
if (!settings.markup)
|
||||
settings.markup = defaults.markup;
|
||||
//Use markup if set
|
||||
//Use default if settings.markup not set yet
|
||||
// (=>c empty&&!allow_markup)
|
||||
if (c) {
|
||||
settings.markup = parse_markup_mode(c);
|
||||
} else if (!settings.markup) {
|
||||
settings.markup = defaults.markup;
|
||||
}
|
||||
g_free(c);
|
||||
}
|
||||
@ -179,7 +162,7 @@ void load_settings(char *cmdline_config_path)
|
||||
settings.indicate_hidden = option_get_bool(
|
||||
"global",
|
||||
"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(
|
||||
@ -187,22 +170,24 @@ void load_settings(char *cmdline_config_path)
|
||||
"word_wrap", "-word_wrap", defaults.word_wrap,
|
||||
"Truncating long lines or do word wrap"
|
||||
);
|
||||
settings.ignore_dbusclose = option_get_bool(
|
||||
"global",
|
||||
"ignore_dbusclose", "-ignore_dbusclose", defaults.ignore_dbusclose,
|
||||
"Ignore dbus CloseNotification events"
|
||||
);
|
||||
|
||||
{
|
||||
char *c = option_get_string(
|
||||
"global",
|
||||
"ellipsize", "-ellipsize", NULL,
|
||||
"ellipsize", "-ellipsize", "",
|
||||
"Ellipsize truncated lines on the start/middle/end"
|
||||
);
|
||||
|
||||
if (!string_parse_ellipsize(c, &settings.ellipsize)) {
|
||||
if (c)
|
||||
LOG_W("Unknown ellipsize value: '%s'", c);
|
||||
if (strlen(c) == 0) {
|
||||
settings.ellipsize = defaults.ellipsize;
|
||||
} 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;
|
||||
}
|
||||
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"
|
||||
);
|
||||
|
||||
#ifndef ENABLE_WAYLAND
|
||||
if (is_running_wayland()){
|
||||
/* We are using xwayland now. Setting force_xwayland to make sure
|
||||
* the idle workaround below is activated */
|
||||
settings.force_xwayland = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (settings.force_xwayland && is_running_wayland()) {
|
||||
if (settings.idle_threshold > 0)
|
||||
LOG_W("Using xwayland. Disabling idle.");
|
||||
/* There is no way to detect if the user is idle
|
||||
* on xwayland, so turn this feature off */
|
||||
settings.idle_threshold = 0;
|
||||
}
|
||||
|
||||
settings.monitor = option_get_int(
|
||||
"global",
|
||||
"monitor", "-mon/-monitor", defaults.monitor,
|
||||
@ -245,16 +214,14 @@ void load_settings(char *cmdline_config_path)
|
||||
{
|
||||
char *c = option_get_string(
|
||||
"global",
|
||||
"follow", "-follow", NULL,
|
||||
"follow", "-follow", "",
|
||||
"Follow mouse, keyboard or none?"
|
||||
);
|
||||
|
||||
if (!string_parse_follow_mode(c, &settings.f_mode)) {
|
||||
if (c)
|
||||
LOG_W("Cannot parse follow mode: %s", c);
|
||||
settings.f_mode = defaults.f_mode;
|
||||
if (strlen(c) > 0) {
|
||||
parse_follow_mode(c);
|
||||
g_free(c);
|
||||
}
|
||||
g_free(c);
|
||||
}
|
||||
|
||||
settings.title = option_get_string(
|
||||
@ -269,24 +236,11 @@ void load_settings(char *cmdline_config_path)
|
||||
"Define the class of windows spawned by dunst."
|
||||
);
|
||||
|
||||
{
|
||||
|
||||
char *c = option_get_string(
|
||||
"global",
|
||||
"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.geom = option_get_string(
|
||||
"global",
|
||||
"geometry", "-geom/-geometry", defaults.geom,
|
||||
"Geometry for the window"
|
||||
);
|
||||
|
||||
settings.shrink = option_get_bool(
|
||||
"global",
|
||||
@ -309,17 +263,22 @@ void load_settings(char *cmdline_config_path)
|
||||
{
|
||||
char *c = option_get_string(
|
||||
"global",
|
||||
"alignment", "-align/-alignment", NULL,
|
||||
"alignment", "-align/-alignment", "",
|
||||
"Text alignment left/center/right"
|
||||
);
|
||||
|
||||
if (!string_parse_alignment(c, &settings.align)) {
|
||||
if (c)
|
||||
LOG_W("Unknown alignment value: '%s'", c);
|
||||
settings.align = defaults.align;
|
||||
if (strlen(c) > 0) {
|
||||
if (strcmp(c, "left") == 0)
|
||||
settings.align = left;
|
||||
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(
|
||||
@ -331,7 +290,7 @@ void load_settings(char *cmdline_config_path)
|
||||
settings.hide_duplicate_count = option_get_bool(
|
||||
"global",
|
||||
"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(
|
||||
@ -370,67 +329,12 @@ void load_settings(char *cmdline_config_path)
|
||||
"horizontal padding"
|
||||
);
|
||||
|
||||
settings.text_icon_padding = option_get_int(
|
||||
"global",
|
||||
"text_icon_padding", "-text_icon_padding", defaults.text_icon_padding,
|
||||
"Padding between text and icon"
|
||||
);
|
||||
|
||||
settings.transparency = option_get_int(
|
||||
"global",
|
||||
"transparency", "-transparency", defaults.transparency,
|
||||
"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(
|
||||
"global",
|
||||
@ -438,8 +342,17 @@ void load_settings(char *cmdline_config_path)
|
||||
"Color of the separator line (or 'auto')"
|
||||
);
|
||||
|
||||
if (!string_parse_sepcolor(c, &settings.sep_color)) {
|
||||
settings.sep_color = defaults.sep_color;
|
||||
if (strlen(c) > 0) {
|
||||
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);
|
||||
}
|
||||
@ -447,7 +360,7 @@ void load_settings(char *cmdline_config_path)
|
||||
settings.stack_duplicates = option_get_bool(
|
||||
"global",
|
||||
"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(
|
||||
@ -465,8 +378,8 @@ void load_settings(char *cmdline_config_path)
|
||||
{
|
||||
GError *error = NULL;
|
||||
if (!g_shell_parse_argv(settings.dmenu, NULL, &settings.dmenu_cmd, &error)) {
|
||||
LOG_W("Unable to parse dmenu command: '%s'."
|
||||
"dmenu functionality will be disabled.", error->message);
|
||||
fprintf(stderr, "Unable to parse dmenu command: %s\n", error->message);
|
||||
fprintf(stderr, "dmenu functionality will be disabled.\n");
|
||||
g_error_free(error);
|
||||
settings.dmenu_cmd = NULL;
|
||||
}
|
||||
@ -479,92 +392,33 @@ void load_settings(char *cmdline_config_path)
|
||||
"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(
|
||||
"global",
|
||||
"icon_position", "-icon_position", "left",
|
||||
"icon_position", "-icon_position", "off",
|
||||
"Align icons left/right/off"
|
||||
);
|
||||
|
||||
if (!string_parse_icon_position(c, &settings.icon_position)) {
|
||||
if (c)
|
||||
LOG_W("Unknown icon position: '%s'", c);
|
||||
settings.icon_position = defaults.icon_position;
|
||||
if (strlen(c) > 0) {
|
||||
if (strcmp(c, "left") == 0)
|
||||
settings.icon_position = icons_left;
|
||||
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(
|
||||
"global",
|
||||
"max_icon_size", "-max_icon_size", defaults.max_icon_size,
|
||||
"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,
|
||||
// read it and generate its usage string.
|
||||
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,
|
||||
"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.
|
||||
// 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 of frame around the window"
|
||||
);
|
||||
LOG_M("The frame section is deprecated, width has "
|
||||
"been renamed to frame_width and moved to "
|
||||
"the global section.");
|
||||
fprintf(stderr, "Warning: The frame section is deprecated, width has been renamed to frame_width and moved to the global section.\n");
|
||||
}
|
||||
|
||||
settings.frame_width = option_get_int(
|
||||
@ -611,9 +463,7 @@ void load_settings(char *cmdline_config_path)
|
||||
"color", NULL, defaults.frame_color,
|
||||
"Color of the frame around the window"
|
||||
);
|
||||
LOG_M("The frame section is deprecated, color "
|
||||
"has been renamed to frame_color and moved "
|
||||
"to the global section.");
|
||||
fprintf(stderr, "Warning: The frame section is deprecated, color has been renamed to frame_color and moved to the global section.\n");
|
||||
}
|
||||
|
||||
settings.frame_color = option_get_string(
|
||||
@ -624,67 +474,21 @@ void load_settings(char *cmdline_config_path)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
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(
|
||||
settings.lowbgcolor = option_get_string(
|
||||
"urgency_low",
|
||||
"background", "-lb", defaults.colors_low.bg,
|
||||
"background", "-lb", defaults.lowbgcolor,
|
||||
"Background color for notifications with low urgency"
|
||||
);
|
||||
|
||||
settings.colors_low.fg = option_get_string(
|
||||
settings.lowfgcolor = option_get_string(
|
||||
"urgency_low",
|
||||
"foreground", "-lf", defaults.colors_low.fg,
|
||||
"foreground", "-lf", defaults.lowfgcolor,
|
||||
"Foreground color for notifications with low urgency"
|
||||
);
|
||||
|
||||
settings.colors_low.highlight = option_get_string(
|
||||
settings.lowframecolor = option_get_string(
|
||||
"urgency_low",
|
||||
"highlight", "-lh", defaults.colors_low.highlight,
|
||||
"Highlight color for notifications with low urgency"
|
||||
);
|
||||
|
||||
settings.colors_low.frame = option_get_string(
|
||||
"urgency_low",
|
||||
"frame_color", "-lfr", settings.frame_color ? settings.frame_color : defaults.colors_low.frame,
|
||||
"frame_color", "-lfr", NULL,
|
||||
"Frame color for notifications with low urgency"
|
||||
);
|
||||
|
||||
@ -700,27 +504,21 @@ void load_settings(char *cmdline_config_path)
|
||||
"Icon for notifications with low urgency"
|
||||
);
|
||||
|
||||
settings.colors_norm.bg = option_get_string(
|
||||
settings.normbgcolor = option_get_string(
|
||||
"urgency_normal",
|
||||
"background", "-nb", defaults.colors_norm.bg,
|
||||
"background", "-nb", defaults.normbgcolor,
|
||||
"Background color for notifications with normal urgency"
|
||||
);
|
||||
|
||||
settings.colors_norm.fg = option_get_string(
|
||||
settings.normfgcolor = option_get_string(
|
||||
"urgency_normal",
|
||||
"foreground", "-nf", defaults.colors_norm.fg,
|
||||
"foreground", "-nf", defaults.normfgcolor,
|
||||
"Foreground color for notifications with normal urgency"
|
||||
);
|
||||
|
||||
settings.colors_norm.highlight = option_get_string(
|
||||
settings.normframecolor = option_get_string(
|
||||
"urgency_normal",
|
||||
"highlight", "-nh", defaults.colors_norm.highlight,
|
||||
"Highlight color for notifications with normal urgency"
|
||||
);
|
||||
|
||||
settings.colors_norm.frame = option_get_string(
|
||||
"urgency_normal",
|
||||
"frame_color", "-nfr", settings.frame_color ? settings.frame_color : defaults.colors_norm.frame,
|
||||
"frame_color", "-nfr", NULL,
|
||||
"Frame color for notifications with normal urgency"
|
||||
);
|
||||
|
||||
@ -736,27 +534,21 @@ void load_settings(char *cmdline_config_path)
|
||||
"Icon for notifications with normal urgency"
|
||||
);
|
||||
|
||||
settings.colors_crit.bg = option_get_string(
|
||||
settings.critbgcolor = option_get_string(
|
||||
"urgency_critical",
|
||||
"background", "-cb", defaults.colors_crit.bg,
|
||||
"background", "-cb", defaults.critbgcolor,
|
||||
"Background color for notifications with critical urgency"
|
||||
);
|
||||
|
||||
settings.colors_crit.fg = option_get_string(
|
||||
settings.critfgcolor = option_get_string(
|
||||
"urgency_critical",
|
||||
"foreground", "-cf", defaults.colors_crit.fg,
|
||||
"foreground", "-cf", defaults.critfgcolor,
|
||||
"Foreground color for notifications with ciritical urgency"
|
||||
);
|
||||
|
||||
settings.colors_crit.highlight = option_get_string(
|
||||
settings.critframecolor = option_get_string(
|
||||
"urgency_critical",
|
||||
"highlight", "-ch", defaults.colors_crit.highlight,
|
||||
"Highlight color for notifications with ciritical urgency"
|
||||
);
|
||||
|
||||
settings.colors_crit.frame = option_get_string(
|
||||
"urgency_critical",
|
||||
"frame_color", "-cfr", settings.frame_color ? settings.frame_color : defaults.colors_crit.frame,
|
||||
"frame_color", "-cfr", NULL,
|
||||
"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 = \"\"."
|
||||
);
|
||||
|
||||
{
|
||||
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 */
|
||||
for (int i = 0; i < G_N_ELEMENTS(default_rules); i++) {
|
||||
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);
|
||||
if (!cur_section)
|
||||
break;
|
||||
if (STR_EQ(cur_section, "global")
|
||||
|| STR_EQ(cur_section, "frame")
|
||||
|| STR_EQ(cur_section, "experimental")
|
||||
|| STR_EQ(cur_section, "shortcuts")
|
||||
|| STR_EQ(cur_section, "urgency_low")
|
||||
|| STR_EQ(cur_section, "urgency_normal")
|
||||
|| STR_EQ(cur_section, "urgency_critical"))
|
||||
if (strcmp(cur_section, "global") == 0
|
||||
|| strcmp(cur_section, "frame") == 0
|
||||
|| strcmp(cur_section, "experimental") == 0
|
||||
|| strcmp(cur_section, "shortcuts") == 0
|
||||
|| strcmp(cur_section, "urgency_low") == 0
|
||||
|| strcmp(cur_section, "urgency_normal") == 0
|
||||
|| strcmp(cur_section, "urgency_critical") == 0)
|
||||
continue;
|
||||
|
||||
/* check for existing rule with same name */
|
||||
struct rule *r = NULL;
|
||||
rule_t *r = NULL;
|
||||
for (GSList *iter = rules; iter; iter = iter->next) {
|
||||
struct rule *match = iter->data;
|
||||
rule_t *match = iter->data;
|
||||
if (match->name &&
|
||||
STR_EQ(match->name, cur_section))
|
||||
strcmp(match->name, cur_section) == 0)
|
||||
r = match;
|
||||
}
|
||||
|
||||
if (!r) {
|
||||
r = rule_new();
|
||||
if (r == NULL) {
|
||||
r = g_malloc(sizeof(rule_t));
|
||||
rule_init(r);
|
||||
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->icon = ini_get_string(cur_section, "icon", r->icon);
|
||||
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);
|
||||
|
||||
{
|
||||
@ -855,48 +668,30 @@ void load_settings(char *cmdline_config_path)
|
||||
"markup", NULL
|
||||
);
|
||||
|
||||
if (!string_parse_markup_mode(c, &r->markup)) {
|
||||
if (c)
|
||||
LOG_W("Invalid markup mode value: %s", c);
|
||||
if (c != NULL) {
|
||||
r->markup = parse_markup_mode(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->msg_urgency = ini_get_urgency(cur_section, "msg_urgency", r->msg_urgency);
|
||||
r->fg = ini_get_string(cur_section, "foreground", r->fg);
|
||||
r->bg = ini_get_string(cur_section, "background", r->bg);
|
||||
r->highlight = ini_get_string(cur_section, "highlight", r->highlight);
|
||||
r->fc = ini_get_string(cur_section, "frame_color", r->fc);
|
||||
r->format = ini_get_string(cur_section, "format", r->format);
|
||||
r->new_icon = ini_get_string(cur_section, "new_icon", r->new_icon);
|
||||
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->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->set_stack_tag = ini_get_string(cur_section, "set_stack_tag", r->set_stack_tag);
|
||||
}
|
||||
|
||||
#ifndef STATIC_CONFIG
|
||||
if (config_file) {
|
||||
fclose(config_file);
|
||||
free_ini();
|
||||
xdgWipeHandle(&xdg);
|
||||
}
|
||||
#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>
|
||||
|
||||
#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"
|
||||
|
||||
enum alignment { ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT };
|
||||
enum ellipsize { ELLIPSE_START, ELLIPSE_MIDDLE, ELLIPSE_END };
|
||||
enum icon_position { ICON_LEFT, ICON_RIGHT, ICON_OFF };
|
||||
enum vertical_alignment { VERTICAL_TOP, VERTICAL_CENTER, VERTICAL_BOTTOM };
|
||||
enum separator_color { SEP_FOREGROUND, SEP_AUTO, SEP_FRAME, SEP_CUSTOM };
|
||||
enum alignment { left, center, right };
|
||||
enum ellipsize { start, middle, end };
|
||||
enum icon_position_t { icons_left, icons_right, icons_off };
|
||||
enum separator_color { FOREGROUND, AUTO, FRAME, CUSTOM };
|
||||
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 };
|
||||
#ifndef ZWLR_LAYER_SHELL_V1_LAYER_ENUM
|
||||
#define ZWLR_LAYER_SHELL_V1_LAYER_ENUM
|
||||
// Needed for compiling without wayland dependency
|
||||
enum zwlr_layer_shell_v1_layer {
|
||||
ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND = 0,
|
||||
ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM = 1,
|
||||
ZWLR_LAYER_SHELL_V1_LAYER_TOP = 2,
|
||||
ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY = 3,
|
||||
};
|
||||
#endif /* ZWLR_LAYER_SHELL_V1_LAYER_ENUM */
|
||||
enum markup_mode { MARKUP_NULL, MARKUP_NO, MARKUP_STRIP, MARKUP_FULL };
|
||||
enum centering { CENTERING_OFF, CENTERING_HORIZONTAL, CENTERING_VERTICAL, CENTERING_BOTH };
|
||||
|
||||
struct separator_color_data {
|
||||
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 {
|
||||
typedef struct _settings {
|
||||
bool print_notifications;
|
||||
bool per_monitor_dpi;
|
||||
enum markup_mode markup;
|
||||
bool stack_duplicates;
|
||||
bool hide_duplicate_count;
|
||||
char *font;
|
||||
struct notification_colors colors_low;
|
||||
struct notification_colors colors_norm;
|
||||
struct notification_colors colors_crit;
|
||||
char *normbgcolor;
|
||||
char *normfgcolor;
|
||||
char *normframecolor;
|
||||
char *critbgcolor;
|
||||
char *critfgcolor;
|
||||
char *critframecolor;
|
||||
char *lowbgcolor;
|
||||
char *lowfgcolor;
|
||||
char *lowframecolor;
|
||||
char *format;
|
||||
gint64 timeouts[3];
|
||||
char *icons[3];
|
||||
unsigned int transparency;
|
||||
struct geometry geometry;
|
||||
char *geom;
|
||||
enum centering centering;
|
||||
char *title;
|
||||
char *class;
|
||||
int shrink;
|
||||
@ -73,7 +48,6 @@ struct settings {
|
||||
int history_length;
|
||||
int show_indicators;
|
||||
int word_wrap;
|
||||
int ignore_dbusclose;
|
||||
enum ellipsize ellipsize;
|
||||
int ignore_newline;
|
||||
int line_height;
|
||||
@ -81,8 +55,8 @@ struct settings {
|
||||
int separator_height;
|
||||
int padding;
|
||||
int h_padding;
|
||||
int text_icon_padding;
|
||||
struct separator_color_data sep_color;
|
||||
enum separator_color sep_color;
|
||||
char *sep_custom_color_str;
|
||||
int frame_width;
|
||||
char *frame_color;
|
||||
int startup_notification;
|
||||
@ -90,35 +64,21 @@ struct settings {
|
||||
char *dmenu;
|
||||
char **dmenu_cmd;
|
||||
char *browser;
|
||||
char **browser_cmd;
|
||||
enum icon_position icon_position;
|
||||
enum vertical_alignment vertical_alignment;
|
||||
int min_icon_size;
|
||||
enum icon_position_t icon_position;
|
||||
int max_icon_size;
|
||||
char *icon_path;
|
||||
enum follow_mode f_mode;
|
||||
bool always_run_script;
|
||||
struct keyboard_shortcut close_ks;
|
||||
struct keyboard_shortcut close_all_ks;
|
||||
struct keyboard_shortcut history_ks;
|
||||
struct keyboard_shortcut context_ks;
|
||||
keyboard_shortcut close_ks;
|
||||
keyboard_shortcut close_all_ks;
|
||||
keyboard_shortcut history_ks;
|
||||
keyboard_shortcut context_ks;
|
||||
bool force_xinerama;
|
||||
bool force_xwayland;
|
||||
int corner_radius;
|
||||
enum mouse_action *mouse_left_click;
|
||||
enum mouse_action *mouse_middle_click;
|
||||
enum mouse_action *mouse_right_click;
|
||||
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;
|
||||
};
|
||||
} settings_t;
|
||||
|
||||
extern struct settings settings;
|
||||
extern settings_t settings;
|
||||
|
||||
void load_settings(char *cmdline_config_path);
|
||||
|
||||
#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 <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <glib.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <string.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)
|
||||
{
|
||||
ASSERT_OR_RET(haystack, NULL);
|
||||
|
||||
char *current = haystack;
|
||||
while ((current = strchr(current, needle)))
|
||||
while ((current = strchr(current, needle)) != NULL)
|
||||
*current++ = replacement;
|
||||
return haystack;
|
||||
}
|
||||
|
||||
/* see utils.h */
|
||||
char *string_replace_at(char *buf, int pos, int len, const char *repl)
|
||||
{
|
||||
assert(buf);
|
||||
assert(repl);
|
||||
|
||||
char *tmp;
|
||||
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;
|
||||
} else {
|
||||
tmp = g_malloc(size);
|
||||
memcpy(tmp, buf, pos);
|
||||
}
|
||||
|
||||
memcpy(tmp, buf, pos);
|
||||
memcpy(tmp + pos, repl, repl_len);
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
ASSERT_OR_RET(haystack, NULL);
|
||||
assert(needle);
|
||||
assert(replacement);
|
||||
|
||||
char *start;
|
||||
int needle_pos;
|
||||
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);
|
||||
repl_len = strlen(replacement);
|
||||
|
||||
while (start) {
|
||||
while (start != NULL) {
|
||||
needle_pos = start - haystack;
|
||||
haystack = string_replace_at(haystack, needle_pos, needle_len, replacement);
|
||||
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;
|
||||
}
|
||||
|
||||
/* see utils.h */
|
||||
char *string_append(char *a, const char *b, const char *sep)
|
||||
{
|
||||
if (STR_EMPTY(a)) {
|
||||
if (!a || *a == '\0') {
|
||||
g_free(a);
|
||||
return g_strdup(b);
|
||||
}
|
||||
if (STR_EMPTY(b))
|
||||
if (!b || *b == '\0')
|
||||
return a;
|
||||
|
||||
char *new;
|
||||
@ -110,29 +92,11 @@ char *string_append(char *a, const char *b, const char *sep)
|
||||
g_free(a);
|
||||
|
||||
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)
|
||||
{
|
||||
assert(str);
|
||||
|
||||
int iread=-1, iwrite=0, copen=0;
|
||||
while (str[++iread] != 0) {
|
||||
if (str[iread] == a) {
|
||||
@ -146,27 +110,13 @@ void string_strip_delimited(char *str, char a, char b)
|
||||
str[iwrite] = 0;
|
||||
}
|
||||
|
||||
/* see utils.h */
|
||||
char **string_to_array(const char *string)
|
||||
{
|
||||
char **arr = NULL;
|
||||
if (string) {
|
||||
arr = g_strsplit(string, ",", 0);
|
||||
for (int i = 0; arr[i]; i++){
|
||||
g_strstrip(arr[i]);
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
/* see utils.h */
|
||||
char *string_to_path(char *string)
|
||||
{
|
||||
|
||||
if (string && STRN_EQ(string, "~/", 2)) {
|
||||
char *home = g_strconcat(user_get_home(), "/", NULL);
|
||||
if (string && 0 == strncmp(string, "~/", 2)) {
|
||||
char *home = g_strconcat(getenv("HOME"), "/", NULL);
|
||||
|
||||
string = string_replace_at(string, 0, 2, home);
|
||||
string = string_replace("~/", home, string);
|
||||
|
||||
g_free(home);
|
||||
}
|
||||
@ -174,9 +124,9 @@ char *string_to_path(char *string)
|
||||
return string;
|
||||
}
|
||||
|
||||
/* see utils.h */
|
||||
gint64 string_to_time(const char *string)
|
||||
{
|
||||
|
||||
assert(string);
|
||||
|
||||
errno = 0;
|
||||
@ -184,79 +134,40 @@ gint64 string_to_time(const char *string)
|
||||
gint64 val = strtoll(string, &endptr, 10);
|
||||
|
||||
if (errno != 0) {
|
||||
LOG_W("Time: '%s': %s.", string, strerror(errno));
|
||||
fprintf(stderr, "ERROR: Time: '%s': %s.\n", string, strerror(errno));
|
||||
return 0;
|
||||
} else if (string == endptr) {
|
||||
LOG_W("Time: '%s': No digits found.", string);
|
||||
fprintf(stderr, "ERROR: Time: No digits found.\n");
|
||||
return 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;
|
||||
} else if (errno == 0 && !*endptr) {
|
||||
return S2US(val);
|
||||
return val * G_USEC_PER_SEC;
|
||||
}
|
||||
|
||||
// endptr may point to a separating space
|
||||
while (isspace(*endptr))
|
||||
while (*endptr == ' ')
|
||||
endptr++;
|
||||
|
||||
if (STRN_EQ(endptr, "ms", 2))
|
||||
if (0 == strncmp(endptr, "ms", 2))
|
||||
return val * 1000;
|
||||
else if (STRN_EQ(endptr, "s", 1))
|
||||
return S2US(val);
|
||||
else if (STRN_EQ(endptr, "m", 1))
|
||||
return S2US(val) * 60;
|
||||
else if (STRN_EQ(endptr, "h", 1))
|
||||
return S2US(val) * 60 * 60;
|
||||
else if (STRN_EQ(endptr, "d", 1))
|
||||
return S2US(val) * 60 * 60 * 24;
|
||||
else if (0 == strncmp(endptr, "s", 1))
|
||||
return val * G_USEC_PER_SEC;
|
||||
else if (0 == strncmp(endptr, "m", 1))
|
||||
return val * G_USEC_PER_SEC * 60;
|
||||
else if (0 == strncmp(endptr, "h", 1))
|
||||
return val * G_USEC_PER_SEC * 60 * 60;
|
||||
else if (0 == strncmp(endptr, "d", 1))
|
||||
return val * G_USEC_PER_SEC * 60 * 60 * 24;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* see utils.h */
|
||||
gint64 time_monotonic_now(void)
|
||||
void die(char *text, int exit_value)
|
||||
{
|
||||
struct timespec tv_now;
|
||||
|
||||
/* 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;
|
||||
fputs(text, stderr);
|
||||
exit(exit_value);
|
||||
}
|
||||
|
||||
/* see utils.h */
|
||||
const char *user_get_home(void)
|
||||
{
|
||||
static const char *home_directory = NULL;
|
||||
ASSERT_OR_RET(!home_directory, home_directory);
|
||||
|
||||
// Check the HOME variable for the user's home
|
||||
home_directory = getenv("HOME");
|
||||
ASSERT_OR_RET(!home_directory, home_directory);
|
||||
|
||||
// Check the /etc/passwd entry for the user's home
|
||||
home_directory = getpwuid(getuid())->pw_dir;
|
||||
|
||||
return home_directory;
|
||||
}
|
||||
|
||||
bool safe_setenv(const char* key, const char* value){
|
||||
if (!key)
|
||||
return false;
|
||||
|
||||
if (!value)
|
||||
setenv(key, "", 1);
|
||||
else
|
||||
setenv(key, value, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
||||
147
src/utils.h
@ -3,151 +3,32 @@
|
||||
#define DUNST_UTILS_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
//! Test if a string is NULL or empty
|
||||
#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)
|
||||
*/
|
||||
/* replace all occurrences of the character needle with the character replacement in haystack */
|
||||
char *string_replace_char(char needle, char replacement, char *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
|
||||
*/
|
||||
/* replace all occurrences of needle with replacement in haystack */
|
||||
char *string_replace_all(const char *needle, const char *replacement, char *haystack);
|
||||
|
||||
/**
|
||||
* Append \p b to string \p a. And concatenate both strings with \p concat, if both are non-empty.
|
||||
*
|
||||
* @param a (nullable) The left side of the string
|
||||
* @param b (nullable) The right side of the string
|
||||
* @param sep (nullable) The concatenator to concatenate if a and b given
|
||||
*/
|
||||
/* replace <len> characters with <repl> at position <pos> of the string <buf> */
|
||||
char *string_replace_at(char *buf, int pos, int len, const char *repl);
|
||||
|
||||
/* replace needle with replacement in haystack */
|
||||
char *string_replace(const char *needle, const char *replacement, char *haystack);
|
||||
|
||||
char *string_append(char *a, const char *b, const char *sep);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
/* strip content between two delimiter characters (inplace) */
|
||||
void string_strip_delimited(char *str, char a, char b);
|
||||
|
||||
/**
|
||||
* Parse a comma-delimited string into a dynamic array of tokens
|
||||
*
|
||||
* The string is split on commas and strips spaces from tokens. The last element
|
||||
* of the array is NULL in order to avoid passing around a length variable.
|
||||
*
|
||||
* @param string The string to convert to an array
|
||||
* @returns The array of tokens.
|
||||
*/
|
||||
char **string_to_array(const char *string);
|
||||
/* exit with an error message */
|
||||
void die(char *msg, int exit_value);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
/* replace tilde and path-specific values with its equivalents */
|
||||
char *string_to_path(char *string);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
/* convert time units (ms, s, m) to internal gint64 microseconds */
|
||||
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
|
||||
/* 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 <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 <glib.h>
|
||||
#include <locale.h>
|
||||
@ -7,130 +14,96 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.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 "../settings.h"
|
||||
#include "../utils.h"
|
||||
#include "src/settings.h"
|
||||
#include "x.h"
|
||||
|
||||
struct screen_info *screens;
|
||||
screen_info *screens;
|
||||
int screens_len;
|
||||
|
||||
bool dunst_follow_errored = false;
|
||||
|
||||
int randr_event_base = 0;
|
||||
|
||||
static int randr_major_version = 0;
|
||||
static int randr_minor_version = 0;
|
||||
|
||||
void randr_init(void);
|
||||
void randr_update(void);
|
||||
void xinerama_update(void);
|
||||
void screen_update_fallback(void);
|
||||
void randr_init();
|
||||
void randr_update();
|
||||
void xinerama_update();
|
||||
void screen_update_fallback();
|
||||
static void x_follow_setup_error_handler(void);
|
||||
static int x_follow_tear_down_error_handler(void);
|
||||
static int FollowXErrorHandler(Display *display, XErrorEvent *e);
|
||||
static Window get_focused_window(void);
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
static double get_xft_dpi_value()
|
||||
{
|
||||
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 (screen_dpi_xft_cache == -DBL_MAX) {
|
||||
screen_dpi_xft_cache = 0;
|
||||
if (xRMS == NULL) {
|
||||
dpi = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
XrmDatabase xDB = XrmGetStringDatabase(xRMS);
|
||||
char *xrmType;
|
||||
XrmValue xrmValue;
|
||||
XrmDatabase db = XrmGetDatabase(xctx.dpy);
|
||||
ASSERT_OR_RET(db, screen_dpi_xft_cache);
|
||||
if (XrmGetResource(db, "Xft.dpi", "Xft.dpi", &xrmType, &xrmValue))
|
||||
screen_dpi_xft_cache = strtod(xrmValue.addr, NULL);
|
||||
|
||||
if (XrmGetResource(xDB, "Xft.dpi", "Xft.dpi", &xrmType, &xrmValue)) {
|
||||
dpi = 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;
|
||||
}
|
||||
|
||||
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 {
|
||||
if (!settings.force_xinerama) {
|
||||
randr_init();
|
||||
randr_update();
|
||||
} else {
|
||||
xinerama_update();
|
||||
}
|
||||
}
|
||||
|
||||
void alloc_screen_ar(int n)
|
||||
{
|
||||
assert(n > 0);
|
||||
g_free(screens);
|
||||
screens = g_malloc0(n * sizeof(struct screen_info));
|
||||
if (n <= screens_len) return;
|
||||
|
||||
screens = g_realloc(screens, n * sizeof(screen_info));
|
||||
|
||||
memset(screens, 0, n * sizeof(screen_info));
|
||||
|
||||
screens_len = n;
|
||||
}
|
||||
|
||||
void randr_init(void)
|
||||
void randr_init()
|
||||
{
|
||||
int ignored;
|
||||
if (!XRRQueryExtension(xctx.dpy, &ignored, &ignored)) {
|
||||
LOG_W("Could not initialize the RandR extension. "
|
||||
"Falling back to single monitor mode.");
|
||||
int randr_error_base = 0;
|
||||
if (!XRRQueryExtension(xctx.dpy, &randr_event_base, &randr_error_base)) {
|
||||
fprintf(stderr, "Could not initialize the RandR extension, falling back to single monitor mode.\n");
|
||||
return;
|
||||
}
|
||||
XRRQueryVersion(xctx.dpy, &randr_major_version, &randr_minor_version);
|
||||
XRRSelectInput(xctx.dpy, RootWindow(xctx.dpy, DefaultScreen(xctx.dpy)), RRScreenChangeNotifyMask);
|
||||
}
|
||||
|
||||
void randr_update(void)
|
||||
void randr_update()
|
||||
{
|
||||
if (randr_major_version < 1
|
||||
|| (randr_major_version == 1 && randr_minor_version < 5)) {
|
||||
LOG_W("Server RandR version too low (%i.%i). "
|
||||
"Falling back to single monitor mode.",
|
||||
randr_major_version,
|
||||
randr_minor_version);
|
||||
fprintf(stderr, "Server RandR version too low (%i.%i). Falling back to single monitor mode\n",
|
||||
randr_major_version,
|
||||
randr_minor_version);
|
||||
screen_update_fallback();
|
||||
return;
|
||||
}
|
||||
@ -139,8 +112,7 @@ void randr_update(void)
|
||||
XRRMonitorInfo *m = XRRGetMonitors(xctx.dpy, RootWindow(xctx.dpy, DefaultScreen(xctx.dpy)), true, &n);
|
||||
|
||||
if (n < 1) {
|
||||
LOG_C("Get monitors reported %i monitors. "
|
||||
"Falling back to single monitor mode.", n);
|
||||
fprintf(stderr, "Get monitors reported %i monitors, falling back to single monitor mode\n", n);
|
||||
screen_update_fallback();
|
||||
return;
|
||||
}
|
||||
@ -150,37 +122,34 @@ void randr_update(void)
|
||||
alloc_screen_ar(n);
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
screens[i].id = i;
|
||||
screens[i].x = m[i].x;
|
||||
screens[i].y = m[i].y;
|
||||
screens[i].w = m[i].width;
|
||||
screens[i].h = m[i].height;
|
||||
screens[i].mmh = m[i].mheight;
|
||||
screens[i].dpi = screen_dpi_get(&screens[i]);
|
||||
screens[i].dim.x = m[i].x;
|
||||
screens[i].dim.y = m[i].y;
|
||||
screens[i].dim.w = m[i].width;
|
||||
screens[i].dim.h = m[i].height;
|
||||
screens[i].dim.mmh = m[i].mheight;
|
||||
}
|
||||
|
||||
XRRFreeMonitors(m);
|
||||
}
|
||||
|
||||
bool screen_check_event(XEvent *ev)
|
||||
static int autodetect_dpi(screen_info *scr)
|
||||
{
|
||||
if (XRRUpdateConfiguration(ev)) {
|
||||
LOG_D("XEvent: processing 'RRScreenChangeNotify'");
|
||||
randr_update();
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return (double)scr->dim.h * 25.4 / (double)scr->dim.mmh;
|
||||
}
|
||||
|
||||
void xinerama_update(void)
|
||||
void screen_check_event(XEvent event)
|
||||
{
|
||||
if (event.type == randr_event_base + RRScreenChangeNotify)
|
||||
randr_update();
|
||||
}
|
||||
|
||||
void xinerama_update()
|
||||
{
|
||||
int n;
|
||||
XineramaScreenInfo *info = XineramaQueryScreens(xctx.dpy, &n);
|
||||
|
||||
if (!info) {
|
||||
LOG_W("Could not get xinerama screen info. "
|
||||
"Falling back to single monitor mode.");
|
||||
fprintf(stderr, "(Xinerama) Could not get screen info, falling back to single monitor mode\n");
|
||||
screen_update_fallback();
|
||||
return;
|
||||
}
|
||||
@ -188,16 +157,15 @@ void xinerama_update(void)
|
||||
alloc_screen_ar(n);
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
screens[i].id = i;
|
||||
screens[i].x = info[i].x_org;
|
||||
screens[i].y = info[i].y_org;
|
||||
screens[i].h = info[i].height;
|
||||
screens[i].w = info[i].width;
|
||||
screens[i].dim.x = info[i].x_org;
|
||||
screens[i].dim.y = info[i].y_org;
|
||||
screens[i].dim.h = info[i].height;
|
||||
screens[i].dim.w = info[i].width;
|
||||
}
|
||||
XFree(info);
|
||||
}
|
||||
|
||||
void screen_update_fallback(void)
|
||||
void screen_update_fallback()
|
||||
{
|
||||
alloc_screen_ar(1);
|
||||
|
||||
@ -207,146 +175,36 @@ void screen_update_fallback(void)
|
||||
else
|
||||
screen = DefaultScreen(xctx.dpy);
|
||||
|
||||
screens[0].w = DisplayWidth(xctx.dpy, screen);
|
||||
screens[0].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;
|
||||
screens[0].dim.w = DisplayWidth(xctx.dpy, screen);
|
||||
screens[0].dim.h = DisplayHeight(xctx.dpy, screen);
|
||||
}
|
||||
|
||||
/*
|
||||
* Select the screen on which the Window
|
||||
* should be displayed.
|
||||
*/
|
||||
const struct screen_info *get_active_screen(void)
|
||||
screen_info *get_active_screen()
|
||||
{
|
||||
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.monitor >= 0 && settings.monitor < screens_len) {
|
||||
ret = settings.monitor;
|
||||
}
|
||||
ret = XDefaultScreen(xctx.dpy);
|
||||
goto sc_cleanup;
|
||||
|
||||
} else {
|
||||
int x, y;
|
||||
assert(settings.f_mode == FOLLOW_MOUSE
|
||||
|| settings.f_mode == FOLLOW_KEYBOARD);
|
||||
|
||||
x_follow_setup_error_handler();
|
||||
|
||||
Window root =
|
||||
RootWindow(xctx.dpy, DefaultScreen(xctx.dpy));
|
||||
|
||||
if (settings.f_mode == FOLLOW_KEYBOARD) {
|
||||
Window focused = get_focused_window();
|
||||
|
||||
if (!focused) {
|
||||
/*
|
||||
* If no window is focused, or the focus is set
|
||||
* to dynamically change to the root window of
|
||||
* the screen the pointer is on, force following
|
||||
* the mouse.
|
||||
*/
|
||||
force_follow_mouse = true;
|
||||
} else {
|
||||
Window child_return;
|
||||
/*
|
||||
* The window with input focus might be on a
|
||||
* different X screen. Use the mouse location
|
||||
* in that case.
|
||||
*/
|
||||
force_follow_mouse = !XTranslateCoordinates(
|
||||
xctx.dpy, focused,root,
|
||||
0, 0, &x, &y,
|
||||
&child_return);
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.f_mode == FOLLOW_MOUSE || force_follow_mouse) {
|
||||
if (settings.f_mode == FOLLOW_MOUSE) {
|
||||
int dummy;
|
||||
unsigned int dummy_ui;
|
||||
Window dummy_win;
|
||||
@ -362,9 +220,24 @@ const struct screen_info *get_active_screen(void)
|
||||
&dummy_ui);
|
||||
}
|
||||
|
||||
if (settings.f_mode == FOLLOW_KEYBOARD) {
|
||||
|
||||
Window focused = get_focused_window();
|
||||
|
||||
if (focused == 0) {
|
||||
/* something went wrong. 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++) {
|
||||
if (INRECT(x, y, screens[i].x, screens[i].y,
|
||||
screens[i].w, screens[i].h)) {
|
||||
if (INRECT(x, y, screens[i].dim.x, screens[i].dim.y,
|
||||
screens[i].dim.w, screens[i].dim.h)) {
|
||||
ret = i;
|
||||
}
|
||||
}
|
||||
@ -372,8 +245,8 @@ const struct screen_info *get_active_screen(void)
|
||||
if (ret > 0)
|
||||
goto sc_cleanup;
|
||||
|
||||
/* something seems to be wrong. Fall back to default */
|
||||
ret = 0;
|
||||
/* something seems to be wrong. Fallback to default */
|
||||
ret = XDefaultScreen(xctx.dpy);
|
||||
goto sc_cleanup;
|
||||
}
|
||||
sc_cleanup:
|
||||
@ -383,19 +256,50 @@ sc_cleanup:
|
||||
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
|
||||
* the keyboard focus.
|
||||
*/
|
||||
static Window get_focused_window(void)
|
||||
{
|
||||
Window focused;
|
||||
int ignored;
|
||||
Window focused = 0;
|
||||
Atom type;
|
||||
int format;
|
||||
unsigned long nitems, bytes_after;
|
||||
unsigned char *prop_return = NULL;
|
||||
Window root = RootWindow(xctx.dpy, DefaultScreen(xctx.dpy));
|
||||
Atom netactivewindow =
|
||||
XInternAtom(xctx.dpy, "_NET_ACTIVE_WINDOW", false);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -420,8 +324,9 @@ static int FollowXErrorHandler(Display *display, XErrorEvent *e)
|
||||
dunst_follow_errored = true;
|
||||
char 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;
|
||||
}
|
||||
/* 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
|
||||
#define DUNST_SCREEN_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
#define INRECT(x,y,rx,ry,rw,rh) ((x) >= (rx) && (x) < (rx)+(rw) && (y) >= (ry) && (y) < (ry)+(rh))
|
||||
|
||||
void init_screens(void);
|
||||
void screen_dpi_xft_cache_purge(void);
|
||||
bool screen_check_event(XEvent *ev);
|
||||
typedef struct _dimension_t {
|
||||
int x;
|
||||
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);
|
||||
double screen_dpi_get(const struct screen_info *scr);
|
||||
typedef struct _screen_info {
|
||||
int scr;
|
||||
dimension_t dim;
|
||||
} screen_info;
|
||||
|
||||
/**
|
||||
* Find the currently focused window and check if it's in
|
||||
* 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);
|
||||
void init_screens();
|
||||
void screen_check_event(XEvent event);
|
||||
|
||||
/**
|
||||
* Check if window is in fullscreen mode
|
||||
*
|
||||
* @param window the x11 window object
|
||||
* @retval true: \p window is in fullscreen mode
|
||||
* @retval false: otherwise
|
||||
*/
|
||||
bool window_is_fullscreen(Window window);
|
||||
screen_info *get_active_screen();
|
||||
double get_dpi_for_screen(screen_info *scr);
|
||||
|
||||
#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
|
||||
#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/Xlib.h>
|
||||
|
||||
#include "../output.h"
|
||||
#include <X11/extensions/scrnsaver.h>
|
||||
#include <glib.h>
|
||||
#include <stdbool.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;
|
||||
KeyCode code;
|
||||
KeySym sym;
|
||||
KeySym mask;
|
||||
bool is_valid;
|
||||
};
|
||||
} keyboard_shortcut;
|
||||
|
||||
// Cyclical dependency
|
||||
#include "../settings.h"
|
||||
|
||||
struct x_context {
|
||||
typedef struct _xctx {
|
||||
Atom utf8;
|
||||
Display *dpy;
|
||||
Window win;
|
||||
bool visible;
|
||||
dimension_t geometry;
|
||||
const char *color_strings[3][3];
|
||||
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 x_win_create(void);
|
||||
void x_win_destroy(window);
|
||||
void x_win_draw(void);
|
||||
void x_win_hide(void);
|
||||
void x_win_show(void);
|
||||
|
||||
void x_win_show(window);
|
||||
void x_win_hide(window);
|
||||
|
||||
void x_display_surface(cairo_surface_t *srf, window, const struct dimensions *dim);
|
||||
|
||||
cairo_t* x_win_get_context(window);
|
||||
/* shortcut */
|
||||
void x_shortcut_init(keyboard_shortcut *shortcut);
|
||||
void x_shortcut_ungrab(keyboard_shortcut *ks);
|
||||
int x_shortcut_grab(keyboard_shortcut *ks);
|
||||
KeySym x_shortcut_string_to_mask(const char *str);
|
||||
|
||||
/* X misc */
|
||||
bool x_is_idle(void);
|
||||
bool x_setup(void);
|
||||
void x_setup(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
|
||||
/* 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
|
||||
quoted = "A quoted string"
|
||||
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]
|
||||
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]
|
||||
font = Monospace 8
|
||||
allow_markup = no
|
||||
format = "%s\n%b"
|
||||
format = "<b>%s</b>\n<i>%b</i>"
|
||||
sort = yes
|
||||
indicate_hidden = yes
|
||||
alignment = left
|
||||
|
||||