├── .github
├── ISSUE_TEMPLATE.md
└── workflows
│ └── main.yml
├── .gitignore
├── .valgrind.suppressions
├── AUTHORS
├── CHANGELOG.md
├── HACKING.md
├── LICENSE
├── Makefile
├── README.md
├── RELEASE_NOTES
├── config.mk
├── contrib
├── _dunst.zshcomp
├── _dunstctl.zshcomp
├── dunst_espeak.sh
├── dunst_xr_theme_changer.sh
├── notification-history.sh
├── progress-notify.sh
└── screenshots
│ ├── default_config.png
│ ├── music.png
│ ├── screenshot1.png
│ ├── screenshot1_cut.png
│ ├── screenshot2.png
│ ├── screenshot2_cut.png
│ ├── screenshot3.png
│ ├── screenshot3_cut.png
│ └── screenshot_urgency.png
├── docs
├── dunst.1.pod
├── dunst.5.pod
├── dunst_layout.png
├── dunst_layout.xcf
├── dunstctl.pod
└── internal
│ ├── Doxyfile
│ └── release-checklist.md
├── dunst.systemd.service.in
├── dunstctl
├── dunstify.c
├── dunstrc
├── main.c
├── org.knopwob.dunst.service.in
├── src
├── dbus.c
├── dbus.h
├── draw.c
├── draw.h
├── dunst.c
├── dunst.h
├── icon-lookup.c
├── icon-lookup.h
├── icon.c
├── icon.h
├── ini.c
├── ini.h
├── input.c
├── input.h
├── log.c
├── log.h
├── markup.c
├── markup.h
├── menu.c
├── menu.h
├── notification.c
├── notification.h
├── option_parser.c
├── option_parser.h
├── output.c
├── output.h
├── queues.c
├── queues.h
├── rules.c
├── rules.h
├── settings.c
├── settings.h
├── settings_data.h
├── utils.c
├── utils.h
├── wayland
│ ├── foreign_toplevel.c
│ ├── foreign_toplevel.h
│ ├── libgwater-wayland.c
│ ├── libgwater-wayland.h
│ ├── pool-buffer.c
│ ├── pool-buffer.h
│ ├── protocols
│ │ ├── idle-client-header.h
│ │ ├── idle.h
│ │ ├── idle.xml
│ │ ├── wlr-foreign-toplevel-management-unstable-v1-client-header.h
│ │ ├── wlr-foreign-toplevel-management-unstable-v1.h
│ │ ├── wlr-foreign-toplevel-management-unstable-v1.xml
│ │ ├── wlr-layer-shell-unstable-v1-client-header.h
│ │ ├── wlr-layer-shell-unstable-v1.h
│ │ ├── wlr-layer-shell-unstable-v1.xml
│ │ ├── xdg-output-unstable-v1-client-header.h
│ │ ├── xdg-output-unstable-v1.h
│ │ ├── xdg-shell-client-header.h
│ │ └── xdg-shell.h
│ ├── wl.c
│ ├── wl.h
│ ├── wl_output.c
│ └── wl_output.h
└── x11
│ ├── screen.c
│ ├── screen.h
│ ├── x.c
│ └── x.h
└── test
├── data
├── dunstrc.default
├── dunstrc.markup
├── dunstrc.nomarkup
├── dunstrc.show_age
├── icons
│ ├── theme
│ │ ├── 16x16
│ │ │ ├── actions
│ │ │ │ └── edit.png
│ │ │ └── apps
│ │ │ │ └── preferences.png
│ │ ├── 16x16@2x
│ │ │ ├── actions
│ │ │ │ └── edit.png
│ │ │ └── apps
│ │ │ │ └── preferences.png
│ │ ├── 32x32
│ │ │ ├── actions
│ │ │ │ └── edit.png
│ │ │ └── apps
│ │ │ │ └── preferences.png
│ │ ├── 32x32@2x
│ │ │ ├── actions
│ │ │ │ └── edit.png
│ │ │ └── apps
│ │ │ │ └── preferences.png
│ │ └── index.theme
│ ├── valid.png
│ └── valid.svg
└── test-ini
├── dbus.c
├── draw.c
├── dunst.c
├── functional-tests
├── dunstrc.default
├── dunstrc.gaps
├── dunstrc.hide_text
├── dunstrc.icon_position
├── dunstrc.markup
├── dunstrc.nomarkup
├── dunstrc.nowrap
├── dunstrc.progress_bar
├── dunstrc.run_script
├── dunstrc.separator_click
├── dunstrc.show_age
├── script_test.sh
└── test.sh
├── greatest.h
├── greenest.awk
├── helpers.c
├── helpers.h
├── icon-lookup.c
├── icon.c
├── ini.c
├── input.c
├── log.c
├── markup.c
├── menu.c
├── misc.c
├── notification.c
├── option_parser.c
├── queues.c
├── queues.h
├── rules.c
├── setting.c
├── settings_data.c
├── test-install.sh
├── test.c
└── utils.c
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
12 |
13 | ### Issue description
14 |
15 |
16 | ### Installation info
17 |
18 | - Version: ``
19 | - Install type: ``
20 | - Window manager / Desktop environment: ``
21 |
22 |
23 | Minimal dunstrc
24 |
25 |
26 | ```ini
27 | # Dunstrc here
28 | ```
29 |
30 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: main
2 |
3 | on:
4 | push:
5 | pull_request:
6 | branches:
7 | - master
8 |
9 | jobs:
10 | build:
11 | strategy:
12 | matrix:
13 | CC:
14 | - clang
15 | - gcc
16 | distro:
17 | - alpine
18 | - archlinux
19 | - debian-stretch
20 | - debian-buster
21 | - fedora
22 | - ubuntu-xenial
23 | - ubuntu-bionic
24 | - ubuntu-focal
25 |
26 | env:
27 | CC: ${{ matrix.CC }}
28 | EXTRA_CFLAGS: "-Werror"
29 | steps:
30 | - uses: actions/checkout@v2
31 | with:
32 | # Clone the whole branch, we have to fetch tags later
33 | fetch-depth: 0
34 |
35 | # Fetch tags to determine proper version number inside git
36 | - name: fetch tags
37 | run: git fetch --tags
38 | # We cannot pull tags with old distros, since there is no `.git`. See below.
39 | if: "! (matrix.distro == 'ubuntu-bionic' || matrix.distro == 'ubuntu-xenial' || matrix.distro == 'debian-stretch')"
40 |
41 | # The Github checkout Action doesn't support distros with git older than 2.18
42 | # With git<2.18 it downloads the code via API and does not clone it via git :facepalm:
43 | # To succeed the tests, we have to manually replace the VERSION macro
44 | - name: fix version number for old distros
45 | run: 'sed -i "s/-non-git/-ci-oldgit-$GITHUB_SHA/" Makefile'
46 | if: " (matrix.distro == 'ubuntu-bionic' || matrix.distro == 'ubuntu-xenial' || matrix.distro == 'debian-stretch')"
47 |
48 | - name: build
49 | run: make -j all dunstify test/test
50 |
51 | - name: test
52 | run: make -j test
53 |
54 | - name: installation
55 | run: ./test/test-install.sh
56 |
57 | - name: valgrind memleaks
58 | run: |
59 | make clean
60 | make -j test-valgrind
61 |
62 | - name: coverage
63 | run: |
64 | make clean
65 | make -j test-coverage
66 |
67 | - name: Generate coverage report
68 | run: lcov -c -d . -o lcov.info
69 | if: "matrix.CC == 'gcc'"
70 |
71 | - name: Upload coverage to Codecov
72 | uses: codecov/codecov-action@v1
73 | with:
74 | token: ${{ secrets.CODECOV_TOKEN }}
75 | flags: unittests
76 | name: ${{ matrix.distro }}-${{ matrix.CC }}
77 | fail_ci_if_error: true
78 | if: "matrix.CC == 'gcc'"
79 |
80 | runs-on: ubuntu-latest
81 | container:
82 | image: dunst/ci:${{ matrix.distro }}
83 |
84 | doxygen:
85 | strategy:
86 | matrix:
87 | distro:
88 | - misc-doxygen
89 |
90 | steps:
91 | - uses: actions/checkout@v2
92 | with:
93 | # Clone the whole branch, we have to fetch tags later
94 | fetch-depth: 0
95 |
96 | - name: doxygen
97 | run: make -j doc-doxygen
98 |
99 | runs-on: ubuntu-latest
100 | container:
101 | image: dunst/ci:${{ matrix.distro }}
102 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.o
2 | *.d
3 | *.gcda
4 | *.gcno
5 | *.gcov
6 | /lcov.info
7 |
8 | core
9 | vgcore.*
10 |
11 | /docs/*.1
12 | /docs/*.5
13 | /docs/internal/coverage
14 | /docs/internal/html
15 | /dunst
16 | /dunstify
17 | /dunst.systemd.service
18 | /org.knopwob.dunst.service
19 | /test/test
20 |
--------------------------------------------------------------------------------
/.valgrind.suppressions:
--------------------------------------------------------------------------------
1 | # Ignore musls' weird error
2 | {
3 | musl_alpine_libc
4 | Memcheck:Free
5 | fun:free
6 | obj:/lib/ld-musl-x86_64.so.1
7 | }
8 |
9 | # rsvg_error_handle_close got fixed in
10 | # - GNOME/librsvg@7bf1014
11 | # (2018-11-12, first tags: v2.45.0, v2.44.9)
12 | # but the release has to seep into the distros
13 | {
14 | rsvg_error_handle_close
15 | Memcheck:Leak
16 | match-leak-kinds: definite
17 | fun:malloc
18 | fun:g_malloc
19 | fun:g_slice_alloc
20 | fun:g_error_new_valist
21 | fun:g_set_error
22 | obj:*/librsvg-2.so*
23 | fun:rsvg_handle_close
24 | obj:*/loaders/libpixbufloader-svg.so
25 | fun:gdk_pixbuf_loader_close
26 | fun:gdk_pixbuf_get_file_info
27 | fun:get_pixbuf_from_file
28 | ...
29 | }
30 |
31 | # same as above, but as occurs in CI environment
32 | {
33 | rsvg_error_handle_close2
34 | Memcheck:Leak
35 | match-leak-kinds: definite
36 | fun:malloc
37 | fun:g_malloc
38 | fun:g_slice_alloc
39 | fun:g_error_new_valist
40 | fun:g_set_error
41 | obj:*/librsvg-2.so*
42 | obj:*/librsvg-2.so*
43 | obj:*/loaders/libpixbufloader-svg.so
44 | obj:*/libgdk_pixbuf-2.0.so*
45 | fun:gdk_pixbuf_loader_close
46 | fun:gdk_pixbuf_get_file_info
47 | fun:get_pixbuf_from_file
48 | ...
49 | }
50 |
51 | # Some new in ArchLinux
52 | {
53 | rsvg_rust_handle_close
54 | Memcheck:Leak
55 | match-leak-kinds: definite
56 | fun:malloc
57 | ...
58 | fun:rsvg_rust_handle_close
59 | obj:*/loaders/libpixbufloader-svg.so
60 | ...
61 | fun:gdk_pixbuf_new_from_file
62 | ...
63 | }
64 |
65 | # rsvg_error_writehandler got fixed in
66 | # - GNOME/librsvg@7b4cc9b
67 | # (2018-11-12, first tags: v2.45.0, v2.44.9)
68 | # but the release has to seep into the distros
69 | {
70 | rsvg_error_writehandler
71 | Memcheck:Leak
72 | match-leak-kinds: definite
73 | fun:malloc
74 | fun:g_malloc
75 | fun:g_slice_alloc
76 | fun:g_error_new_valist
77 | fun:g_set_error
78 | obj:*/librsvg-2.so*
79 | fun:rsvg_handle_write
80 | obj:*/loaders/libpixbufloader-svg.so
81 | obj:*/libgdk_pixbuf-2.0.so*
82 | fun:gdk_pixbuf_loader_close
83 | fun:gdk_pixbuf_get_file_info
84 | fun:get_pixbuf_from_file
85 | ...
86 | }
87 |
88 | # a librsvg memoryleak that shows up in arch, but not in the CI environment
89 | {
90 |
91 | Memcheck:Leak
92 | match-leak-kinds: definite
93 | fun:malloc
94 | obj:/usr/lib/librsvg-2.so.2.48.0
95 | ...
96 | fun:rsvg_handle_close
97 | obj:/usr/lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-svg.so
98 | obj:/usr/lib/libgdk_pixbuf-2.0.so.0.4200.6
99 | fun:gdk_pixbuf_new_from_file
100 | ...
101 | }
102 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | Sascha Kruse (http://github.com/knopwob)
2 |
3 | contributors:
4 | See `git shortlog` for a list of contributors and their contributions
5 |
--------------------------------------------------------------------------------
/HACKING.md:
--------------------------------------------------------------------------------
1 | # Important notes on the code
2 |
3 | **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`. You will also need `graphviz` for this.**
4 |
5 |
6 | For people wanting to develop new features or fix bugs for dunst, here are the
7 | steps you should take.
8 |
9 | # Running dunst
10 |
11 | For building dunst, you should take a look at the README. After dunst is build,
12 | you can run it with:
13 |
14 | ./dunst
15 |
16 | This might not work, however, since dunst will abort when another instance of
17 | dunst or another notification daemon is running. You will see a message like:
18 |
19 | CRITICAL: [dbus_cb_name_lost:1044] Cannot acquire 'org.freedesktop.Notifications': Name is acquired by 'dunst' with PID '20937'.
20 |
21 | So it's best to kill any running instance of dunst before trying to run the
22 | version you just built. You can do that by making a shell function as follows
23 | and put it in your bashrc/zshrc/config.fish:
24 |
25 | ```sh
26 | run_dunst() {
27 | if make -j dunst; then
28 | pkill dunst
29 | else
30 | return 1
31 | fi
32 | ./dunst $@
33 | }
34 | ```
35 |
36 | If you run this function is the root directory of the repository, it will build
37 | dunst, kill any running instances and run your freshly built version of dunst.
38 |
39 | # Testing dunst
40 |
41 | To test dunst, it's good to know the following commands. This way you can test
42 | dunst on your local system and you don't have to wait for CI to finish.
43 |
44 | ## Run test suite
45 |
46 | This will build dunst if there were any changes and run the test suite. You will
47 | need `awk` for this to work (to color the output of the tests).
48 |
49 | make test
50 |
51 | ## Run memory leak tests
52 |
53 | This will build dunst if there were any changes and run the test suite with
54 | valgrind to make sure there aren't any memory leaks. You will have to build your
55 | tests so that they free all allocated memory after you are done, otherwise this
56 | test will fail. You will need to have `valgrind` installed for this.
57 |
58 | make test-valgrind
59 |
60 |
61 | ## Build the doxygen documentation
62 |
63 | The internal documentation can be built with (`doxygen` and `graphviz` required):
64 |
65 | make doc-doxygen
66 |
67 | To open them in your browser you can run something like:
68 |
69 | firefox docs/internal/html/index.html
70 |
71 |
72 | # Running the tests with docker
73 |
74 | Dunst has a few docker images for running tests on different distributions. The
75 | documentation for this can be found at https://github.com/dunst-project/docker-images
76 |
77 | # Comments
78 |
79 | - Comment system is held similar to JavaDoc
80 | - Use `@param` to describe all input parameters
81 | - Use `@return` to describe the output value
82 | - Use `@retval` to describe special return values (like `NULL`)
83 | - Documentation comments should start with a double star (`/**`)
84 | - Append `()` to function names and prepend variables with `#` to properly reference them in the docs
85 | - Add comments to all functions and methods
86 | - Markdown inside the comments is allowed and also desired
87 | - Add the comments to the prototype. Doxygen will merge the protoype and implementation documentation anyways.
88 | Except for **static** methods, add the documentation header to the implementation and *not to the prototype*.
89 | - Member documentation should happen with `/**<` and should span to the right side of the member
90 | - Test files that have the same name as a file in src/\* can include the
91 | associated .c file. This is because they are being compiled INSTEAD of the src
92 | file.
93 |
94 |
95 | ## Log messages
96 |
97 | ### Messages
98 |
99 | - Keep your message in common format: `: `
100 | - If you have to write text, single quote values in your sentence.
101 |
102 | ### Levels
103 |
104 | For logging, there are printf-like macros `LOG_(E|C|W|M|I|D)`.
105 |
106 | - `LOG_E` (ERROR):
107 | - 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.
108 | - e.g.: Switching over an enum, `LOG_E` would go into the default case.
109 | - `LOG_C` (CRITICAL):
110 | - The program cannot continue to work. It is used in the wrong manner or some outer conditions are not met.
111 | - e.g.: `-config` parameter value is unreadable file
112 | - `DIE` (CRITICAL):
113 | - A shorthand for `LOG_C` and terminating the program after. This does not dump the core (unlike `LOG_E`).
114 | - `LOG_W` (WARNING):
115 | - Something is not in shape, but it's recoverable.
116 | - e.g.: A value is not parsable in the config file, which will default.
117 | - `LOG_M` (MESSAGE):
118 | - Important info, which informs about the state.
119 | - e.g.: An empty notification does get removed immediately.
120 | - `LOG_I` (INFO):
121 | - Mostly unneccessary info, but important to debug (as the user) some use cases.
122 | - e.g.: print the notification contents after arriving
123 | - `LOG_D` (DEBUG):
124 | - Only important during development or tracing some bugs (as the developer).
125 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright © 2013, Sascha Kruse and contributors
2 | All rights reserved.
3 |
4 | All files (unless otherwise noted) are licensed under the BSD license:
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright
10 | notice, this list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright
13 | notice, this list of conditions and the following disclaimer in the
14 | documentation and/or other materials provided with the distribution.
15 |
16 | * Neither the name of Sascha Kruse nor the
17 | names of contributors may be used to endorse or promote products
18 | derived from this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY Sascha Kruse ''AS IS'' AND ANY
21 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL Sascha Kruse BE LIABLE FOR ANY
24 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/config.mk:
--------------------------------------------------------------------------------
1 | # paths
2 | PREFIX ?= /usr/local
3 | BINDIR ?= ${PREFIX}/bin
4 | SYSCONFDIR ?= ${PREFIX}/etc/xdg
5 | SYSCONFFILE ?= ${SYSCONFDIR}/dunst/dunstrc
6 | DATADIR ?= ${PREFIX}/share
7 | # around for backwards compatibility
8 | MANPREFIX ?= ${DATADIR}/man
9 | MANDIR ?= ${MANPREFIX}
10 | SERVICEDIR_DBUS ?= ${DATADIR}/dbus-1/services
11 | SERVICEDIR_SYSTEMD ?= ${PREFIX}/lib/systemd/user
12 | EXTRA_CFLAGS ?=
13 |
14 | DOXYGEN ?= doxygen
15 | FIND ?= find
16 | GCOVR ?= gcovr
17 | GIT ?= git
18 | PKG_CONFIG ?= pkg-config
19 | POD2MAN ?= pod2man
20 | SED ?= sed
21 | SYSTEMDAEMON ?= systemd
22 | VALGRIND ?= valgrind
23 |
24 | # Disable systemd service file installation,
25 | # if you don't want to use systemd albeit installed
26 | #SYSTEMD ?= 0
27 |
28 | # Disable dependency on wayland. This will force dunst to use
29 | # xwayland on wayland compositors
30 | # You can also use "make WAYLAND=0" to build without wayland
31 | # WAYLAND ?= 0
32 |
33 | ifneq (0, ${WAYLAND})
34 | ENABLE_WAYLAND= -DENABLE_WAYLAND
35 | endif
36 |
37 | # flags
38 | DEFAULT_CPPFLAGS = -Wno-gnu-zero-variadic-macro-arguments -D_DEFAULT_SOURCE -DVERSION=\"${VERSION}\" -DSYSCONFDIR=\"${SYSCONFDIR}\"
39 | DEFAULT_CFLAGS = -g -std=gnu99 -pedantic -Wall -Wno-overlength-strings -Os ${ENABLE_WAYLAND} ${EXTRA_CFLAGS}
40 | DEFAULT_LDFLAGS = -lm -lrt
41 |
42 | CPPFLAGS_DEBUG := -DDEBUG_BUILD
43 | CFLAGS_DEBUG := -O0
44 | LDFLAGS_DEBUG :=
45 |
46 | pkg_config_packs := gio-2.0 \
47 | gdk-pixbuf-2.0 \
48 | "glib-2.0 >= 2.44" \
49 | pangocairo \
50 | x11 \
51 | xinerama \
52 | xext \
53 | "xrandr >= 1.5" \
54 | xscrnsaver \
55 |
56 |
57 | # dunstify also needs libnotify
58 | pkg_config_packs += libnotify
59 |
60 | ifneq (0,${WAYLAND})
61 | pkg_config_packs += wayland-client
62 | pkg_config_packs += wayland-cursor
63 | endif
64 |
--------------------------------------------------------------------------------
/contrib/_dunst.zshcomp:
--------------------------------------------------------------------------------
1 | #compdef _dunst dunst
2 |
3 | # ZSH arguments completion script for the dunst command
4 |
5 | local curcontext="$curcontext" ret=1
6 | local -a state line subs
7 |
8 | _arguments -C \
9 | '1:opt:->opts' \
10 | '2:param:->params' \
11 | && ret=0
12 |
13 | case $state in
14 | (opts)
15 | _arguments \
16 | {-v,--version,-version}"[Print version]" \
17 | '(-verbosity)-verbosity[Minimum level for message]' \
18 | {-conf,--config}"[Path to configuration file]:file:_files" \
19 | {-h,-help,--help}"[Print help]"
20 | ;;
21 |
22 | (params)
23 | case $line[1] in
24 | -verbosity)
25 | local -a verbosity_params;
26 | verbosity_params=(
27 | "crit"
28 | "warn"
29 | "mesg"
30 | "info"
31 | "debug"
32 | )
33 | _describe verbosity_params verbosity_params && ret=0
34 | ;;
35 |
36 | -conf|--config)
37 | _arguments '*:file:_files' && ret=0
38 | ;;
39 |
40 | esac
41 | esac
42 |
--------------------------------------------------------------------------------
/contrib/_dunstctl.zshcomp:
--------------------------------------------------------------------------------
1 | #compdef _dunstctl dunstctl
2 |
3 | # ZSH arguments completion script for the dunstctl commnd
4 | # Depends on: gAWK (rule), jq (history-pop)
5 |
6 | local curcontext="$curcontext" ret=1
7 | local -a state line subs
8 |
9 | local DUNSTRC="${XDG_CONFIG_HOME:-$HOME/.config}/dunst/dunstrc"
10 |
11 | _arguments -C \
12 | '1:cmd:->cmds' \
13 | '2:opt:->opts' \
14 | '3:third:->thirds' \
15 | && ret=0
16 |
17 | case $state in
18 | (cmds)
19 | local -a commands
20 | commands=(
21 | 'action:Perform the default action, or open the context menu of the notification at the given position'
22 | 'close:Close the last notification'
23 | 'close-all:Close the all notifications'
24 | 'context:Open context menu'
25 | 'count:Show the number of notifications'
26 | 'history:Display notification history (in JSON)'
27 | 'history-pop:Pop the latest notification from history or optionally the notification with given ID.'
28 | 'is-paused:Check if dunst is running or paused'
29 | 'set-paused:Set the pause status'
30 | 'rule:Enable or disable a rule by its name'
31 | 'debug:Print debugging information'
32 | 'help:Show this help'
33 | )
34 | _describe commands commands && ret=0
35 | ;;
36 |
37 | (opts)
38 | case $line[1] in
39 | count)
40 | local -a count_opts;
41 | count_opts=(
42 | "displayed"
43 | "history"
44 | "waiting"
45 | )
46 |
47 | _describe count_opts count_opts && ret=0
48 | ;;
49 |
50 | set-paused)
51 | local -a setpaused_opts;
52 | setpaused_opts=(
53 | "true"
54 | "false"
55 | "toggle"
56 | )
57 |
58 | _describe setpaused_opts setpaused_opts && ret=0
59 | ;;
60 |
61 | rule)
62 | local -a rules;
63 | rules=(
64 | `awk '/^\[.*\]/{ if ( match($0, /^\[global|urgency|experimental/) == 0 ) { print substr($0, 2, length($0)-2) } }' < "$DUNSTRC"`
65 | )
66 | _describe rules_opts rules && ret=0
67 | ;;
68 |
69 | history-pop)
70 | local -a history_ids;
71 | history_ids=(
72 | `dunstctl history | jq -M '.data[0][].id.data'`
73 | )
74 | _describe history_ids history_ids && ret=0
75 | ;;
76 |
77 | esac
78 | ;;
79 |
80 | (thirds)
81 | case $line[1] in
82 | rule)
83 | local -a rulestates_opts;
84 | rulestates_opts=(
85 | "enable"
86 | "disable"
87 | "toggle"
88 | )
89 |
90 | _describe rulestates_opts rulestates_opts && ret=0
91 | ;;
92 |
93 | esac
94 | esac
95 |
96 | return ret
97 |
--------------------------------------------------------------------------------
/contrib/dunst_espeak.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | summary="$2"
4 | body="$3"
5 |
6 | echo "$summary $body" | espeak
7 |
--------------------------------------------------------------------------------
/contrib/notification-history.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | history_json=$(dunstctl history)
4 |
5 | history_length=$(echo $history_json|jq -r '.data[] | length')
6 |
7 | options=""
8 |
9 | for iter in $(seq $history_length); do
10 | i=$((iter-1))
11 | application_name=$(echo $history_json|jq -r .data[][$i].appname.data)
12 | notification_summary=$(echo $history_json|jq -r .data[][$i].summary.data)
13 | notification_timestamp=$(echo $history_json|jq -r .data[][$i].timestamp.data)
14 | system_timestamp=$(cat /proc/uptime|cut -d'.' -f1)
15 |
16 | how_long_ago=$((system_timestamp-notification_timestamp/1000000))
17 |
18 | notification_time=$(date +%X -d "$(date) - $how_long_ago seconds")
19 |
20 | option=$(printf '%04d - %s: "%s" (at %s)' "$iter" "$application_name" \
21 | "$notification_summary" "$notification_time")
22 |
23 | options="$options$option\n"
24 | done
25 | options="$options""Cancel"
26 |
27 | result=$(echo -e $options|rofi -dmenu -i)
28 | if [ "$result" = "Cancel" ]; then
29 | # Exit if cancelled
30 | exit 0
31 | fi
32 | if [ "$result" = "" ]; then
33 | # Exit on empty strings
34 | exit 0
35 | fi
36 | if [ "$?" -ne 0 ]; then
37 | # Exit on non-zero return values
38 | exit 0
39 | fi
40 |
41 | # Get the internal notification ID
42 | selection_index=$((${result:0:4}-1))
43 | notification_id=$(echo $history_json|jq -r .data[][$selection_index].id.data)
44 |
45 | # Tell dunst to revive said notification
46 | dunstctl history-pop $notification_id
47 |
48 |
--------------------------------------------------------------------------------
/contrib/progress-notify.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | # progress-notify - Send audio and brightness notifications for dunst
4 |
5 | # dependencies: dunstify, ponymix, Papirus (icons)
6 |
7 | ### How to use: ###
8 | # Pass the values via stdin and provide the notification type
9 | # as an argument. Options are audio, brightness and muted
10 |
11 | ### Audio notifications ###
12 | # ponymix increase 5 | notify audio
13 | # ponymix decrease 5 | notify audio
14 | # pulsemixer --toggle-mute --get-mute | notify muted
15 | ### Brightness notifications ###
16 | # xbacklight -inc 5 && xbacklight -get | notify brightness
17 | # xbacklight -dec 5 && xbacklight -get | notify brightness
18 |
19 | notifyMuted() {
20 | volume="$1"
21 | dunstify -h string:x-canonical-private-synchronous:audio "Muted" -h int:value:"$volume" -t 1500 --icon audio-volume-muted
22 | }
23 |
24 | notifyAudio() {
25 | volume="$1"
26 | ponymix is-muted && notifyMuted "$volume" && return
27 |
28 | if [ $volume -eq 0 ]; then
29 | notifyMuted "$volume"
30 | elif [ $volume -le 30 ]; then
31 | dunstify -h string:x-canonical-private-synchronous:audio "Volume: " -h int:value:"$volume" -t 1500 --icon audio-volume-low
32 | elif [ $volume -le 70 ]; then
33 | dunstify -h string:x-canonical-private-synchronous:audio "Volume: " -h int:value:"$volume" -t 1500 --icon audio-volume-medium
34 | else
35 | dunstify -h string:x-canonical-private-synchronous:audio "Volume: " -h int:value:"$volume" -t 1500 --icon audio-volume-high
36 | fi
37 | }
38 |
39 | notifyBrightness() {
40 | brightness="$1"
41 | if [ $brightness -eq 0 ]; then
42 | dunstify -h string:x-canonical-private-synchronous:brightness "Brightness: " -h int:value:"$brightness" -t 1500 --icon display-brightness-off-symbolic
43 | elif [ $brightness -le 30 ]; then
44 | dunstify -h string:x-canonical-private-synchronous:brightness "Brightness: " -h int:value:"$brightness" -t 1500 --icon display-brightness-low-symbolic
45 | elif [ $brightness -le 70 ]; then
46 | dunstify -h string:x-canonical-private-synchronous:brightness "Brightness: " -h int:value:"$brightness" -t 1500 --icon display-brightness-medium-symbolic
47 | else
48 | dunstify -h string:x-canonical-private-synchronous:brightness "Brightness: " -h int:value:"$brightness" -t 1500 --icon display-brightness-high-symbolic
49 | fi
50 | }
51 |
52 | input=`cat /dev/stdin`
53 |
54 | case "$1" in
55 | muted)
56 | volume=`ponymix get-volume`
57 | if [ "$input" -eq 0 ]
58 | then
59 | notifyAudio "$volume"
60 | else
61 | notifyMuted "$volume"
62 | fi
63 | ;;
64 | audio)
65 | notifyAudio "$input"
66 | ;;
67 | brightness)
68 | notifyBrightness "$input"
69 | ;;
70 |
71 | *)
72 | echo "Not the right arguments"
73 | echo "$1"
74 | exit 2
75 | esac
76 |
--------------------------------------------------------------------------------
/contrib/screenshots/default_config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FT-Labs/phyOS-dunst/aeb275a998778b05cee495efb9b61db66cdc0a7e/contrib/screenshots/default_config.png
--------------------------------------------------------------------------------
/contrib/screenshots/music.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FT-Labs/phyOS-dunst/aeb275a998778b05cee495efb9b61db66cdc0a7e/contrib/screenshots/music.png
--------------------------------------------------------------------------------
/contrib/screenshots/screenshot1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FT-Labs/phyOS-dunst/aeb275a998778b05cee495efb9b61db66cdc0a7e/contrib/screenshots/screenshot1.png
--------------------------------------------------------------------------------
/contrib/screenshots/screenshot1_cut.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FT-Labs/phyOS-dunst/aeb275a998778b05cee495efb9b61db66cdc0a7e/contrib/screenshots/screenshot1_cut.png
--------------------------------------------------------------------------------
/contrib/screenshots/screenshot2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FT-Labs/phyOS-dunst/aeb275a998778b05cee495efb9b61db66cdc0a7e/contrib/screenshots/screenshot2.png
--------------------------------------------------------------------------------
/contrib/screenshots/screenshot2_cut.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FT-Labs/phyOS-dunst/aeb275a998778b05cee495efb9b61db66cdc0a7e/contrib/screenshots/screenshot2_cut.png
--------------------------------------------------------------------------------
/contrib/screenshots/screenshot3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FT-Labs/phyOS-dunst/aeb275a998778b05cee495efb9b61db66cdc0a7e/contrib/screenshots/screenshot3.png
--------------------------------------------------------------------------------
/contrib/screenshots/screenshot3_cut.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FT-Labs/phyOS-dunst/aeb275a998778b05cee495efb9b61db66cdc0a7e/contrib/screenshots/screenshot3_cut.png
--------------------------------------------------------------------------------
/contrib/screenshots/screenshot_urgency.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FT-Labs/phyOS-dunst/aeb275a998778b05cee495efb9b61db66cdc0a7e/contrib/screenshots/screenshot_urgency.png
--------------------------------------------------------------------------------
/docs/dunst.1.pod:
--------------------------------------------------------------------------------
1 | =head1 NAME
2 |
3 | dunst - A customizable and lightweight notification-daemon
4 |
5 | =head1 SYNOPSIS
6 |
7 | dunst [-conf file] [-verbosity v] [-print] [--startup-notification]
8 |
9 | =head1 DESCRIPTION
10 |
11 | Dunst is a highly configurable and lightweight notification daemon.
12 |
13 | =head2 Autostarting dunst
14 |
15 | On most installations dunst should be able to automatically be started by D-Bus
16 | when a notification is sent. This is not recommended when multiple notification
17 | deamons are installed, because D-Bus will not know which one to start.
18 | Other ways of autostarting dunst include starting dunst with your desktop
19 | environment or window manager's autostart functionality or via the provided
20 | systemd service.
21 |
22 | =head1 COMMAND LINE OPTIONS
23 |
24 | =over 4
25 |
26 | =item B<-h/--help>
27 |
28 | List all command line flags
29 |
30 | =item B<-conf/-config file>
31 |
32 | Use alternative config file.
33 | This disables the search for other config files.
34 | If it cannot be opened Dunst will issue a warning and fall back on its internal
35 | defaults.
36 | (Hint: `dunst -conf -
40 |
41 | Print version information.
42 |
43 | =item B<-verbosity> (values: 'crit', 'warn', 'mesg', 'info', 'debug' default 'mesg')
44 |
45 | Do not display log messages, which have lower precedence than specified
46 | verbosity. This won't affect printing notifications on the terminal. Use
47 | the '-print' option for this.
48 |
49 | =item B<-print>
50 |
51 | Print notifications to stdout. This might be useful for logging, setting up
52 | rules or using the output in other scripts.
53 |
54 | =item B<--startup_notification> (values: [true/false], default: false)
55 |
56 | Display a notification on startup.
57 |
58 | =back
59 |
60 | =head1 CONFIGURATION
61 |
62 | A default configuration file is included (usually ##SYSCONFDIR##/dunst/dunstrc)
63 | and serves as the least important configuration file. Note: this was previously
64 | /usr/share/dunst/dunstrc. You can edit this file to change the system-wide
65 | defaults or copy it to a more important location to override its settings. See
66 | the FILES section for more details on where dunst searches for its
67 | configuration files and how settings get applied.
68 |
69 | See dunst(5) for all possible settings.
70 |
71 | =head2 NOTIFY-SEND
72 |
73 | dunst is able to get different colors for a message via notify-send.
74 | In order to do that you have to add a hint via the -h option.
75 | The progress value can be set with a hint, too.
76 |
77 | =over 4
78 |
79 | =item notify-send -h string:fgcolor:#ff4444
80 |
81 | =item notify-send -h string:bgcolor:#4444ff -h string:fgcolor:#ff4444 -h string:frcolor:#44ff44
82 |
83 | =item notify-send -h int:value:42 "Working ..."
84 |
85 | =back
86 |
87 | =head1 MISCELLANEOUS
88 |
89 | Dunst can be paused via the `dunstctl set-paused true` command. To unpause dunst use
90 | `dunstctl set-paused false`.
91 | Another way is to send SIGUSR1 and SIGUSR2 to pause and unpause
92 | respectively. Pausing using dunstctl is recommended over using signals, because
93 | the meaning of the signals is not be stable and might change in the future.
94 |
95 | When paused dunst will not display any notifications but keep all notifications
96 | in a queue. This can for example be wrapped around a screen locker (i3lock,
97 | slock) to prevent flickering of notifications through the lock and to read all
98 | missed notifications after returning to the computer.
99 |
100 | =head1 FILES
101 |
102 | These are the base directories dunst searches for configuration files in
103 | I:
104 |
105 | =over 8
106 |
107 | =item C<$XDG_CONFIG_HOME>
108 |
109 | This is the most important directory. (C<$HOME/.config> if unset or empty)
110 |
111 | =item C<$XDG_CONFIG_DIRS>
112 |
113 | This, like C<$PATH> for instance, is a :-separated list of base directories
114 | in I.
115 | (F<##SYSCONFDIR##> if unset or empty)
116 |
117 | =back
118 |
119 | Dunst will search these directories for the following relative file paths:
120 |
121 | =over 8
122 |
123 | =item F
124 |
125 | This is the base config and as such the least important in a particular base
126 | directory.
127 |
128 | =item F
129 |
130 | These are "drop-ins" (mind the ".d" suffix of the directory).
131 | They are more important than the base dunstrc in the parent directory, as they
132 | are considered to be small snippets to override settings.
133 | The last in lexical order is the most important one, so you can easily change
134 | the order by renaming them.
135 | A common approach to naming drop-ins is to prefix them with numbers, i.e.:
136 |
137 | 00-least-important.conf
138 | 01-foo.conf
139 | 20-bar.conf
140 | 99-most-important.conf
141 |
142 | Only files with the B<.conf> suffix will be read.
143 |
144 | =back
145 |
146 | Only settings from the last base config the corresponding drop-ins get applied.
147 | So if a dunstrc is first found in F<~/.config/dunst/dunstrc>, drop-ins will be
148 | searched in F<~/.config/dunst/dunstrc.d/*>. Settings in more important files
149 | override those in less important ones.
150 |
151 | =head1 AUTHORS
152 |
153 | Written by Sascha Kruse
154 |
155 | =head1 REPORTING BUGS
156 |
157 | Bugs and suggestions should be reported on GitHub at https://github.com/dunst-project/dunst/issues
158 |
159 | =head1 COPYRIGHT
160 |
161 | Copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information)
162 |
163 | If you feel that copyrights are violated, please send me an email.
164 |
165 | =head1 SEE ALSO
166 |
167 | dunst(5), dunstctl(1), dmenu(1), notify-send(1)
168 |
--------------------------------------------------------------------------------
/docs/dunst_layout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FT-Labs/phyOS-dunst/aeb275a998778b05cee495efb9b61db66cdc0a7e/docs/dunst_layout.png
--------------------------------------------------------------------------------
/docs/dunst_layout.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FT-Labs/phyOS-dunst/aeb275a998778b05cee495efb9b61db66cdc0a7e/docs/dunst_layout.xcf
--------------------------------------------------------------------------------
/docs/dunstctl.pod:
--------------------------------------------------------------------------------
1 | =head1 NAME
2 |
3 | dunstctl - Command line control utility for dunst, a customizable and lightweight notification-daemon
4 |
5 | =head1 SYNOPSIS
6 |
7 | dunstctl COMMAND [PARAMETER]
8 |
9 | =head1 COMMANDS
10 |
11 | =over 4
12 |
13 | =item B notification_position
14 |
15 | Performs the default action or, if not available, opens the context menu of the
16 | notification at the given position (starting count at the top, first
17 | notification being 0).
18 |
19 | =item B
20 |
21 | Close the topmost notification currently being displayed.
22 |
23 | =item B
24 |
25 | Close all notifications currently being displayed
26 |
27 | =item B
28 |
29 | Open the context menu, presenting all available actions and urls for the
30 | currently open notifications.
31 |
32 | =item B [displayed/history/waiting]
33 |
34 | Returns the number of displayed, shown and waiting notifications. If no argument
35 | is provided, everything will be printed.
36 |
37 | =item B [ID]
38 |
39 | Redisplay the notification that was most recently closed. This can be called
40 | multiple times to show older notifications, up to the history limit configured
41 | in dunst. You can optionally pass an ID to history-pop, in which case the
42 | notification with the given ID will be shown.
43 |
44 | =item B
45 |
46 | Check if dunst is currently running or paused. If dunst is paused notifications
47 | will be kept but not shown until it is unpaused.
48 |
49 | =item B true/false/toggle
50 |
51 | Set the paused status of dunst. If false, dunst is running normally, if true,
52 | dunst is paused. See the is-paused command and the dunst man page for more
53 | information.
54 |
55 | =item B
56 |
57 | Tries to contact dunst and checks for common faults between dunstctl and dunst.
58 | Useful if something isn't working
59 |
60 | =item B
61 |
62 | Show all available commands with a brief description
63 |
64 | =back
65 |
66 |
--------------------------------------------------------------------------------
/docs/internal/release-checklist.md:
--------------------------------------------------------------------------------
1 | # Main repo
2 | - [ ] Update the changelog
3 | - [ ] Write release notes (Only if non-patch release)
4 |
5 | - [ ] Verify that the working directory is clean and on the master branch
6 | - [ ] Change the version in the Makefile to "x.x.x (iso-date)"
7 | - [ ] Commit changes (Commit title: `Dunst vX.X.X`)
8 | - [ ] Tag commit, make sure it's an annotated tag (`git tag -a "vX.X.X" -m "Dunst vX.X.X"`)
9 | - [ ] Push commits
10 | - [ ] Push tags
11 |
12 | # Dunst-project.org
13 | - [ ] Run the update script (`REPO=../dunst ./update_new_release.sh OLDVER NEWVER`)
14 | - [ ] Verify that they look fine when rendered (`hugo serve`)
15 | - [ ] Commit changes
16 | - [ ] Run deploy script and push (`./deploy.sh -p`)
17 |
18 | # Main repo
19 | - [ ] Copy release notes to githubs release feature
20 | - [ ] Publish release on github
21 | - [ ] Update maint branch to point to master
22 |
23 | - [ ] Update Makefile version to "x.x.x-non-git"
24 | - [ ] Commit & push
25 |
--------------------------------------------------------------------------------
/dunst.systemd.service.in:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Dunst notification daemon
3 | Documentation=man:dunst(1)
4 | PartOf=graphical-session.target
5 |
6 | [Service]
7 | Type=dbus
8 | BusName=org.freedesktop.Notifications
9 | ExecStart=##PREFIX##/bin/dunst
10 |
--------------------------------------------------------------------------------
/dunstctl:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -eu
4 |
5 | DBUS_NAME="org.freedesktop.Notifications"
6 | DBUS_PATH="/org/freedesktop/Notifications"
7 | DBUS_IFAC_DUNST="org.dunstproject.cmd0"
8 | DBUS_IFAC_PROP="org.freedesktop.DBus.Properties"
9 | DBUS_IFAC_FDN="org.freedesktop.Notifications"
10 |
11 | die(){ printf "%s\n" "${1}" >&2; exit 1; }
12 |
13 | show_help() {
14 | # Below, each line starts with a tab character
15 | cat <<-EOH
16 | Usage: dunstctl [parameters]"
17 | Commands:
18 | action Perform the default action, or open the
19 | context menu of the notification at the
20 | given position
21 | close Close the last notification
22 | close-all Close the all notifications
23 | context Open context menu
24 | count [displayed|history|waiting] Show the number of notifications
25 | history Display notification history (in JSON)
26 | history-pop [ID] Pop the latest notification from
27 | history or optionally the
28 | notification with given ID.
29 | is-paused Check if dunst is running or paused
30 | set-paused [true|false|toggle] Set the pause status
31 | rule name [enable|disable|toggle] Enable or disable a rule by its name
32 | debug Print debugging information
33 | help Show this help
34 | EOH
35 | }
36 | dbus_send_checked() {
37 | dbus-send "$@" \
38 | || 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."
39 | }
40 |
41 | method_call() {
42 | dbus_send_checked --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "$@"
43 | }
44 |
45 | property_get() {
46 | dbus_send_checked --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_PROP}.Get" "string:${DBUS_IFAC_DUNST}" "string:${1}"
47 | }
48 |
49 | property_set() {
50 | dbus_send_checked --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_PROP}.Set" "string:${DBUS_IFAC_DUNST}" "string:${1}" "${2}"
51 | }
52 |
53 | command -v dbus-send >/dev/null 2>/dev/null || \
54 | die "Command dbus-send not found"
55 |
56 |
57 | case "${1:-}" in
58 | "action")
59 | method_call "${DBUS_IFAC_DUNST}.NotificationAction" "uint32:${2:-0}" >/dev/null
60 | ;;
61 | "close")
62 | method_call "${DBUS_IFAC_DUNST}.NotificationCloseLast" >/dev/null
63 | ;;
64 | "close-all")
65 | method_call "${DBUS_IFAC_DUNST}.NotificationCloseAll" >/dev/null
66 | ;;
67 | "context")
68 | method_call "${DBUS_IFAC_DUNST}.ContextMenuCall" >/dev/null
69 | ;;
70 | "count")
71 | [ $# -eq 1 ] || [ "${2}" = "displayed" ] || [ "${2}" = "history" ] || [ "${2}" = "waiting" ] \
72 | || die "Please give either 'displayed', 'history', 'waiting' or none as count parameter."
73 | if [ $# -eq 1 ]; then
74 | property_get waitingLength | ( read -r _ _ waiting; printf " Waiting: %s\n" "${waiting}" )
75 | property_get displayedLength | ( read -r _ _ displayed; printf " Currently displayed: %s\n" "${displayed}" )
76 | property_get historyLength | ( read -r _ _ history; printf " History: %s\n" "${history}")
77 | else
78 | property_get ${2}Length | ( read -r _ _ notifications; printf "%s\n" "${notifications}"; )
79 | fi
80 | ;;
81 | "history-pop")
82 | if [ "$#" -eq 1 ]
83 | then
84 | method_call "${DBUS_IFAC_DUNST}.NotificationShow" >/dev/null
85 | elif [ "$#" -eq 2 ]
86 | then
87 | method_call "${DBUS_IFAC_DUNST}.NotificationPopHistory" "uint32:${2:-0}" >/dev/null
88 | else
89 | die "Please pass the right number of arguments. History-pop takes 0 or 1 arguments"
90 | fi
91 | ;;
92 | "is-paused")
93 | property_get paused | ( read -r _ _ paused; printf "%s\n" "${paused}"; )
94 | ;;
95 | "set-paused")
96 | [ "${2:-}" ] \
97 | || die "No status parameter specified. Please give either 'true', 'false' or 'toggle' as paused parameter."
98 | [ "${2}" = "true" ] || [ "${2}" = "false" ] || [ "${2}" = "toggle" ] \
99 | || die "Please give either 'true', 'false' or 'toggle' as paused parameter."
100 | if [ "${2}" = "toggle" ]; then
101 | paused=$(property_get paused | ( read -r _ _ paused; printf "%s\n" "${paused}"; ))
102 | if [ "${paused}" = "true" ]; then
103 | property_set paused variant:boolean:false
104 | else
105 | property_set paused variant:boolean:true
106 | fi
107 | else
108 | property_set paused variant:boolean:"$2"
109 | fi
110 | ;;
111 | "rule")
112 | [ "${2:-}" ] \
113 | || die "No rule name parameter specified. Please give the rule name"
114 | state=nope
115 | [ "${3}" = "disable" ] && state=0
116 | [ "${3}" = "enable" ] && state=1
117 | [ "${3}" = "toggle" ] && state=2
118 | [ "${state}" = "nope" ] \
119 | && die "No valid rule state parameter specified. Please give either 'enable', 'disable' or 'toggle'"
120 | method_call "${DBUS_IFAC_DUNST}.RuleEnable" "string:${2:-1}" "int32:${state}" >/dev/null
121 | ;;
122 | "help"|"--help"|"-h")
123 | show_help
124 | ;;
125 | "debug")
126 | dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_FDN}.GetServerInformation" >/dev/null 2>/dev/null \
127 | || die "Dunst is not running."
128 |
129 | dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_FDN}.GetServerInformation" \
130 | | (
131 | read -r name _ version _
132 | [ "${name}" = "dunst" ]
133 | printf "dunst version: %s\n" "${version}"
134 | ) \
135 | || die "Another notification manager is running. It's not dunst"
136 |
137 | dbus-send --print-reply=literal --dest="${DBUS_NAME}" "${DBUS_PATH}" "${DBUS_IFAC_DUNST}.Ping" >/dev/null 2>/dev/null \
138 | || die "Dunst controlling interface not available. Is the version too old?"
139 | ;;
140 | "history")
141 | busctl --user --json=pretty --no-pager call org.freedesktop.Notifications /org/freedesktop/Notifications org.dunstproject.cmd0 NotificationListHistory 2>/dev/null \
142 | || die "Dunst is not running."
143 | ;;
144 | "")
145 | die "dunstctl: No command specified. Please consult the usage."
146 | ;;
147 | *)
148 | die "dunstctl: unrecognized command '${1:-}'. Please consult the usage."
149 | ;;
150 | esac
151 | # vim: noexpandtab
152 |
--------------------------------------------------------------------------------
/main.c:
--------------------------------------------------------------------------------
1 | #include "src/dunst.h"
2 |
3 | int main(int argc, char *argv[])
4 | {
5 | return dunst_main(argc, argv);
6 | }
7 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
8 |
--------------------------------------------------------------------------------
/org.knopwob.dunst.service.in:
--------------------------------------------------------------------------------
1 | [D-BUS Service]
2 | Name=org.freedesktop.Notifications
3 | Exec=##PREFIX##/bin/dunst
4 | SystemdService=dunst.service
5 |
--------------------------------------------------------------------------------
/src/dbus.h:
--------------------------------------------------------------------------------
1 | /* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
2 |
3 | #ifndef DUNST_DBUS_H
4 | #define DUNST_DBUS_H
5 |
6 | #include "dunst.h"
7 | #include "notification.h"
8 |
9 | /// The reasons according to the notification spec
10 | enum reason {
11 | REASON_MIN = 1, /**< Minimum value, useful for boundary checking */
12 | REASON_TIME = 1, /**< The notification timed out */
13 | REASON_USER = 2, /**< The user closed the notification */
14 | REASON_SIG = 3, /**< The daemon received a `NotificationClose` signal */
15 | REASON_UNDEF = 4, /**< Undefined reason not matching the previous ones */
16 | REASON_MAX = 4, /**< Maximum value, useful for boundary checking */
17 | };
18 |
19 | int dbus_init(void);
20 | void dbus_teardown(int id);
21 | void signal_notification_closed(struct notification *n, enum reason reason);
22 | void signal_action_invoked(const struct notification *n, const char *identifier);
23 |
24 | #endif
25 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
26 |
--------------------------------------------------------------------------------
/src/draw.h:
--------------------------------------------------------------------------------
1 | #ifndef DUNST_DRAW_H
2 | #define DUNST_DRAW_H
3 |
4 | #include
5 | #include
6 | #include "output.h"
7 |
8 | extern window win; // Temporary
9 | extern const struct output *output;
10 |
11 | void draw_setup(void);
12 |
13 | void draw(void);
14 |
15 | void draw_rounded_rect(cairo_t *c, int x, int y, int width, int height, int corner_radius, double scale, bool first, bool last);
16 |
17 | // TODO get rid of this function by passing scale to everything that needs it.
18 | double draw_get_scale(void);
19 |
20 | void draw_deinit(void);
21 |
22 | void calc_window_pos(const struct screen_info *scr, int width, int height, int *ret_x, int *ret_y);
23 |
24 | #endif
25 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
26 |
--------------------------------------------------------------------------------
/src/dunst.h:
--------------------------------------------------------------------------------
1 | /* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
2 |
3 | #ifndef DUNST_DUNST_H
4 | #define DUNST_DUNST_H
5 |
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | #include "notification.h"
12 |
13 | //!< A structure to describe dunst's global window status
14 | struct dunst_status {
15 | bool fullscreen; //!< a fullscreen window is currently focused
16 | bool running; //!< set true if dunst is currently running
17 | bool idle; //!< set true if the user is idle
18 | };
19 |
20 | enum dunst_status_field {
21 | S_FULLSCREEN,
22 | S_IDLE,
23 | S_RUNNING,
24 | };
25 |
26 | /**
27 | * Modify the current status of dunst
28 | * @param field The field to change in the global status structure
29 | * @param value Anything boolean or DO_TOGGLE to toggle the current value
30 | */
31 | void dunst_status(const enum dunst_status_field field,
32 | bool value);
33 |
34 | struct dunst_status dunst_status_get(void);
35 |
36 | void wake_up(void);
37 |
38 | int dunst_main(int argc, char *argv[]);
39 |
40 | void usage(int exit_status);
41 | void print_version(void);
42 |
43 | gboolean quit_signal(gpointer data);
44 |
45 | #endif
46 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
47 |
--------------------------------------------------------------------------------
/src/icon-lookup.h:
--------------------------------------------------------------------------------
1 | #ifndef DUNST_ICON_LOOKUP_H
2 | #define DUNST_ICON_LOOKUP_H
3 |
4 | struct icon_theme {
5 | char *name;
6 | char *location; // full path to the theme
7 | char *subdir_theme; // name of the directory in which the theme is located
8 |
9 | int inherits_count;
10 | int *inherits_index;
11 |
12 | int dirs_count;
13 | struct icon_theme_dir *dirs;
14 | };
15 |
16 | enum theme_dir_type { THEME_DIR_FIXED, THEME_DIR_SCALABLE, THEME_DIR_THRESHOLD };
17 |
18 | struct icon_theme_dir {
19 | char *name;
20 | int size;
21 | int scale;
22 | int min_size, max_size;
23 | int threshold;
24 | enum theme_dir_type type;
25 | };
26 |
27 |
28 | /**
29 | * Load a theme with given name from a standard icon directory. Don't call this
30 | * function if the theme is already loaded.
31 | *
32 | * @param name Name of the directory in which the theme is located. Note that
33 | * it is NOT the name of the theme as specified in index.theme.
34 | * @returns The index of the theme, which can be used to set it as default.
35 | * @retval -1 if the icon theme cannot be loaded.
36 | */
37 | int load_icon_theme(char *name);
38 |
39 |
40 | /**
41 | * Add theme to the list of default themes. The theme that's added first will
42 | * be used first for lookup. After that the inherited themes will be used and
43 | * only after that the next default theme will be used.
44 | *
45 | * @param theme_index The index of the theme as returned by #load_icon_theme
46 | */
47 | void add_default_theme(int theme_index);
48 |
49 | /**
50 | * Find icon of specified size in selected theme. This function will not return
51 | * icons that cannot be scaled to \p size according to index.theme.
52 | *
53 | * @param name Name of the icon or full path to it.
54 | * @param theme_index Index of the theme to use.
55 | * @param size Size of the icon.
56 | * @returns The full path to the icon.
57 | * @retval NULL if the icon cannot be found or is not readable.
58 | */
59 | char *find_icon_in_theme(const char *name, int theme_index, int size);
60 | char *find_icon_path(const char *name, int size);
61 | void set_default_theme(int theme_index);
62 |
63 | /**
64 | * Find icon of specified size in the default theme or an inherited theme. This
65 | * function will not return icons that cannot be scaled to \p size according to
66 | * index.theme.
67 |
68 | *
69 | * @param name Name of the icon or full path to it.
70 | * @param size Size of the icon.
71 | * @returns The full path to the icon.
72 | * @retval NULL if the icon cannot be found or is not readable.
73 | */
74 | char *find_icon_path(const char *name, int size);
75 |
76 | /**
77 | * Free all icon themes.
78 | */
79 | void free_all_themes();
80 |
81 | #endif
82 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
83 |
--------------------------------------------------------------------------------
/src/icon.h:
--------------------------------------------------------------------------------
1 | #ifndef DUNST_ICON_H
2 | #define DUNST_ICON_H
3 |
4 | #include
5 | #include
6 |
7 | #include "notification.h"
8 |
9 | cairo_surface_t *gdk_pixbuf_to_cairo_surface(GdkPixbuf *pixbuf);
10 |
11 | /** Retrieve an icon by its full filepath, scaled according to settings.
12 | *
13 | * @param filename A string representing a readable file path
14 | * @param min_size An iteger representing the desired minimum unscaled icon size.
15 | * @param max_size An iteger representing the desired maximum unscaled icon size.
16 | * @param scale An integer representing the output dpi scaling.
17 | *
18 | * @return an instance of `GdkPixbuf`
19 | * @retval NULL: file does not exist, not readable, etc..
20 | */
21 | GdkPixbuf *get_pixbuf_from_file(const char *filename, int min_size, int max_size, double scale);
22 |
23 |
24 | /**
25 | * Get the unscaled icon width.
26 | *
27 | * If scale is 2 for example, the icon will render in twice the size, but
28 | * get_icon_width still returns the same size as when scale is 1.
29 | */
30 | int get_icon_width(cairo_surface_t *icon, double scale);
31 |
32 | /**
33 | * Get the unscaled icon height, see get_icon_width.
34 | */
35 | int get_icon_height(cairo_surface_t *icon, double scale);
36 |
37 | /** Retrieve a path from an icon name.
38 | *
39 | * @param iconname A string describing a `file://` URL, an arbitary filename
40 | * or an icon name, which then gets searched for in the
41 | * settings.icon_path
42 | * @param size Size of the icon to look for. This is only used when
43 | * recursive icon lookup is enabled.
44 | *
45 | * @return a newly allocated string with the icon path
46 | * @retval NULL: file does not exist, not readable, etc..
47 | */
48 | char *get_path_from_icon_name(const char *iconname, int size);
49 |
50 | /** Convert a GVariant like described in GdkPixbuf, scaled according to settings
51 | *
52 | * The returned id will be a unique identifier. To check if two given
53 | * GdkPixbufs are equal, it's sufficient to just compare the id strings.
54 | *
55 | * @param data A GVariant in the format "(iiibii@ay)" filled with values
56 | * like described in the notification spec.
57 | * @param id (necessary) A unique identifier of the returned pixbuf.
58 | * Only filled, if the return value is non-NULL.
59 | * @param dpi_scale An integer representing the output dpi scaling.
60 | * @param min_size An integer representing the desired minimum unscaled icon size.
61 | * @param max_size An integer representing the desired maximum unscaled icon size.
62 | * @return an instance of `GdkPixbuf` derived from the GVariant
63 | * @retval NULL: GVariant parameter nulled, invalid or in wrong format
64 | */
65 | GdkPixbuf *icon_get_for_data(GVariant *data, char **id, double dpi_scale, int min_size, int max_size);
66 |
67 | #endif
68 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
69 |
--------------------------------------------------------------------------------
/src/ini.c:
--------------------------------------------------------------------------------
1 | #include "ini.h"
2 |
3 | #include "utils.h"
4 | #include "log.h"
5 | #include "settings.h"
6 |
7 | struct section *get_section(struct ini *ini, const char *name)
8 | {
9 | for (int i = 0; i < ini->section_count; i++) {
10 | if (STR_EQ(ini->sections[i].name, name))
11 | return &ini->sections[i];
12 | }
13 |
14 | return NULL;
15 | }
16 |
17 | struct section *get_or_create_section(struct ini *ini, const char *name)
18 | {
19 | struct section *s = get_section(ini, name);
20 | if (!s) {
21 | ini->section_count++;
22 | ini->sections = g_realloc(ini->sections, sizeof(struct section) * ini->section_count);
23 |
24 | s = &ini->sections[ini->section_count - 1];
25 | s->name = g_strdup(name);
26 | s->entries = NULL;
27 | s->entry_count = 0;
28 | }
29 | return s;
30 | }
31 |
32 | void add_entry(struct ini *ini, const char *section_name, const char *key, const char *value)
33 | {
34 | struct section *s = get_or_create_section(ini, section_name);
35 |
36 | s->entry_count++;
37 | int len = s->entry_count;
38 | s->entries = g_realloc(s->entries, sizeof(struct entry) * len);
39 | s->entries[s->entry_count - 1].key = g_strdup(key);
40 | s->entries[s->entry_count - 1].value = string_strip_quotes(value);
41 | }
42 |
43 | const char *section_get_value(struct ini *ini, const struct section *s, const char *key)
44 | {
45 | ASSERT_OR_RET(s, NULL);
46 |
47 | for (int i = 0; i < s->entry_count; i++) {
48 | if (STR_EQ(s->entries[i].key, key)) {
49 | return s->entries[i].value;
50 | }
51 | }
52 | return NULL;
53 | }
54 |
55 | const char *get_value(struct ini *ini, const char *section, const char *key)
56 | {
57 | struct section *s = get_section(ini, section);
58 | return section_get_value(ini, s, key);
59 | }
60 |
61 | bool ini_is_set(struct ini *ini, const char *ini_section, const char *ini_key)
62 | {
63 | return get_value(ini, ini_section, ini_key) != NULL;
64 | }
65 |
66 | const char *next_section(const struct ini *ini,const char *section)
67 | {
68 | ASSERT_OR_RET(ini->section_count > 0, NULL);
69 | ASSERT_OR_RET(section, ini->sections[0].name);
70 |
71 | for (int i = 0; i < ini->section_count; i++) {
72 | if (STR_EQ(section, ini->sections[i].name)) {
73 | if (i + 1 >= ini->section_count)
74 | return NULL;
75 | else
76 | return ini->sections[i + 1].name;
77 | }
78 | }
79 | return NULL;
80 | }
81 |
82 | struct ini *load_ini_file(FILE *fp)
83 | {
84 | if (!fp)
85 | return NULL;
86 |
87 | struct ini *ini = calloc(1, sizeof(struct ini));
88 | char *line = NULL;
89 | size_t line_len = 0;
90 |
91 | int line_num = 0;
92 | char *current_section = NULL;
93 | while (getline(&line, &line_len, fp) != -1) {
94 | line_num++;
95 |
96 | char *start = g_strstrip(line);
97 |
98 | if (*start == ';' || *start == '#' || STR_EMPTY(start))
99 | continue;
100 |
101 | if (*start == '[') {
102 | char *end = strchr(start + 1, ']');
103 | if (!end) {
104 | LOG_W("Invalid config file at line %d: Missing ']'.", line_num);
105 | continue;
106 | }
107 |
108 | *end = '\0';
109 |
110 | g_free(current_section);
111 | current_section = g_strdup(start + 1);
112 | continue;
113 | }
114 |
115 | char *equal = strchr(start + 1, '=');
116 | if (!equal) {
117 | LOG_W("Invalid config file at line %d: Missing '='.", line_num);
118 | continue;
119 | }
120 |
121 | *equal = '\0';
122 | char *key = g_strstrip(start);
123 | char *value = g_strstrip(equal + 1);
124 |
125 | char *quote = strchr(value, '"');
126 | char *value_end = NULL;
127 | if (quote) {
128 | value_end = strchr(quote + 1, '"');
129 | if (!value_end) {
130 | LOG_W("Invalid config file at line %d: Missing '\"'.", line_num);
131 | continue;
132 | }
133 | } else {
134 | value_end = value;
135 | }
136 |
137 | char *comment = strpbrk(value_end, "#;");
138 | if (comment)
139 | *comment = '\0';
140 |
141 | value = g_strstrip(value);
142 |
143 | if (!current_section) {
144 | LOG_W("Invalid config file at line %d: Key value pair without a section.", line_num);
145 | continue;
146 | }
147 |
148 | add_entry(ini, current_section, key, value);
149 | }
150 | free(line);
151 | g_free(current_section);
152 | return ini;
153 | }
154 |
155 |
156 | void finish_ini(struct ini *ini)
157 | {
158 | for (int i = 0; i < ini->section_count; i++) {
159 | for (int j = 0; j < ini->sections[i].entry_count; j++) {
160 | g_free(ini->sections[i].entries[j].key);
161 | g_free(ini->sections[i].entries[j].value);
162 | }
163 | g_free(ini->sections[i].entries);
164 | g_free(ini->sections[i].name);
165 | }
166 | g_clear_pointer(&ini->sections, g_free);
167 | ini->section_count = 0;
168 | }
169 |
170 |
--------------------------------------------------------------------------------
/src/ini.h:
--------------------------------------------------------------------------------
1 | /* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
2 | #ifndef DUNST_INI_H
3 | #define DUNST_INI_H
4 |
5 | #include
6 | #include
7 |
8 | struct entry {
9 | char *key;
10 | char *value;
11 | };
12 |
13 | struct section {
14 | char *name;
15 | int entry_count;
16 | struct entry *entries;
17 | };
18 |
19 | struct ini {
20 | int section_count;
21 | struct section *sections;
22 | };
23 |
24 | /* returns the next known section.
25 | * if section == NULL returns first section.
26 | * returns NULL if no more sections are available
27 | */
28 | const char *next_section(const struct ini *ini,const char *section);
29 | const char *section_get_value(struct ini *ini, const struct section *s, const char *key);
30 | const char *get_value(struct ini *ini, const char *section, const char *key);
31 | struct ini *load_ini_file(FILE *fp);
32 | void finish_ini(struct ini *ini);
33 |
34 | #endif
35 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
36 |
--------------------------------------------------------------------------------
/src/input.c:
--------------------------------------------------------------------------------
1 | #include "input.h"
2 | #include "log.h"
3 | #include "menu.h"
4 | #include "settings.h"
5 | #include "queues.h"
6 | #include
7 | #include
8 |
9 | int get_notification_clickable_height(struct notification *n, bool first, bool last)
10 | {
11 | int notification_size = n->displayed_height;
12 | if (settings.gap_size) {
13 | notification_size += settings.frame_width * 2;
14 | } else {
15 | double half_separator = settings.separator_height / 2.0;
16 | notification_size += settings.separator_height;
17 | if(first)
18 | notification_size += (settings.frame_width - half_separator);
19 | if(last)
20 | notification_size += (settings.frame_width - half_separator);
21 | }
22 | return notification_size;
23 | }
24 |
25 | struct notification *get_notification_at(const int y) {
26 | int curr_y = 0;
27 | bool first = true;
28 | bool last;
29 | for (const GList *iter = queues_get_displayed(); iter;
30 | iter = iter->next) {
31 | struct notification *current = iter->data;
32 | struct notification *next = iter->next ? iter->next->data : NULL;
33 |
34 | last = !next;
35 | int notification_size = get_notification_clickable_height(current, first, last);
36 |
37 | if (y >= curr_y && y < curr_y + notification_size) {
38 | return current;
39 | }
40 |
41 | curr_y += notification_size;
42 | if (settings.gap_size)
43 | curr_y += settings.gap_size;
44 |
45 | first = false;
46 | }
47 | // no matching notification was found
48 | return NULL;
49 | }
50 |
51 | void input_handle_click(unsigned int button, bool button_down, int mouse_x, int mouse_y){
52 | LOG_I("Pointer handle button %i: %i", button, button_down);
53 |
54 | if (button_down) {
55 | // make sure it only reacts on button release
56 | return;
57 | }
58 |
59 | enum mouse_action *acts;
60 |
61 | switch (button) {
62 | case BTN_LEFT:
63 | acts = settings.mouse_left_click;
64 | break;
65 | case BTN_MIDDLE:
66 | acts = settings.mouse_middle_click;
67 | break;
68 | case BTN_RIGHT:
69 | acts = settings.mouse_right_click;
70 | break;
71 | case BTN_TOUCH:
72 | // TODO Add separate action for touch
73 | acts = settings.mouse_left_click;
74 | break;
75 | default:
76 | LOG_W("Unsupported mouse button: '%d'", button);
77 | return;
78 | }
79 |
80 | // if other list types are added, make sure they have the same end value
81 | for (int i = 0; acts[i] != MOUSE_ACTION_END; i++) {
82 | enum mouse_action act = acts[i];
83 | if (act == MOUSE_CLOSE_ALL) {
84 | queues_history_push_all();
85 | continue;
86 | }
87 |
88 | if (act == MOUSE_CONTEXT_ALL) {
89 | context_menu();
90 | continue;
91 | }
92 |
93 | if (act == MOUSE_DO_ACTION || act == MOUSE_CLOSE_CURRENT || act == MOUSE_CONTEXT || act == MOUSE_OPEN_URL) {
94 | struct notification *n = get_notification_at(mouse_y);
95 |
96 | if (n) {
97 | if (act == MOUSE_CLOSE_CURRENT) {
98 | n->marked_for_closure = REASON_USER;
99 | } else if (act == MOUSE_DO_ACTION) {
100 | notification_do_action(n);
101 | } else if (act == MOUSE_OPEN_URL) {
102 | notification_open_url(n);
103 | } else {
104 | notification_open_context_menu(n);
105 | }
106 | }
107 | }
108 | }
109 |
110 | wake_up();
111 | }
112 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
113 |
--------------------------------------------------------------------------------
/src/input.h:
--------------------------------------------------------------------------------
1 | #ifndef DUNST_INPUT_H
2 | #define DUNST_INPUT_H
3 |
4 | #include
5 |
6 | /**
7 | * Handle incoming mouse click events
8 | *
9 | * @param button code, A linux input event code
10 | * @param button_down State of the button
11 | * @param mouse_x X-position of the mouse, relative to the window
12 | * @param mouse_y Y-position of the mouse, relative to the window
13 | *
14 | */
15 | void input_handle_click(unsigned int button, bool button_down, int mouse_x, int mouse_y);
16 |
17 | #endif
18 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
19 |
--------------------------------------------------------------------------------
/src/log.c:
--------------------------------------------------------------------------------
1 | /* copyright 2012 - 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
2 |
3 | /**
4 | * @file src/log.c
5 | * @brief logging wrapper to use GLib's logging capabilities
6 | */
7 |
8 | #include "log.h"
9 |
10 | #include
11 |
12 | #include "utils.h"
13 |
14 | static GLogLevelFlags log_level = G_LOG_LEVEL_WARNING;
15 |
16 | /* see log.h */
17 | static const char *log_level_to_string(GLogLevelFlags level)
18 | {
19 | switch (level) {
20 | case G_LOG_LEVEL_ERROR: return "ERROR";
21 | case G_LOG_LEVEL_CRITICAL: return "CRITICAL";
22 | case G_LOG_LEVEL_WARNING: return "WARNING";
23 | case G_LOG_LEVEL_MESSAGE: return "MESSAGE";
24 | case G_LOG_LEVEL_INFO: return "INFO";
25 | case G_LOG_LEVEL_DEBUG: return "DEBUG";
26 | default: return "UNKNOWN";
27 | }
28 | }
29 |
30 | /* see log.h */
31 | void log_set_level_from_string(const char *level)
32 | {
33 | ASSERT_OR_RET(level,);
34 |
35 | if (STR_CASEQ(level, "critical"))
36 | log_level = G_LOG_LEVEL_CRITICAL;
37 | else if (STR_CASEQ(level, "crit"))
38 | log_level = G_LOG_LEVEL_CRITICAL;
39 | else if (STR_CASEQ(level, "warning"))
40 | log_level = G_LOG_LEVEL_WARNING;
41 | else if (STR_CASEQ(level, "warn"))
42 | log_level = G_LOG_LEVEL_WARNING;
43 | else if (STR_CASEQ(level, "message"))
44 | log_level = G_LOG_LEVEL_MESSAGE;
45 | else if (STR_CASEQ(level, "mesg"))
46 | log_level = G_LOG_LEVEL_MESSAGE;
47 | else if (STR_CASEQ(level, "info"))
48 | log_level = G_LOG_LEVEL_INFO;
49 | else if (STR_CASEQ(level, "debug"))
50 | log_level = G_LOG_LEVEL_DEBUG;
51 | else if (STR_CASEQ(level, "deb"))
52 | log_level = G_LOG_LEVEL_DEBUG;
53 | else
54 | LOG_W("Unknown log level: '%s'", level);
55 | }
56 |
57 | void log_set_level(GLogLevelFlags level)
58 | {
59 | log_level = level;
60 | }
61 |
62 | /**
63 | * Log handling function for GLib's logging wrapper
64 | *
65 | * @param log_domain Used only by GLib
66 | * @param message_level Used only by GLib
67 | * @param message Used only by GLib
68 | * @param testing If not `NULL` (here: `true`), do nothing
69 | */
70 | static void dunst_log_handler(
71 | const gchar *log_domain,
72 | GLogLevelFlags message_level,
73 | const gchar *message,
74 | gpointer testing)
75 | {
76 | if (testing)
77 | log_level = G_LOG_LEVEL_ERROR;
78 |
79 | GLogLevelFlags message_level_masked = message_level & G_LOG_LEVEL_MASK;
80 |
81 | /* if you want to have a debug build, you want to log anything,
82 | * unconditionally, without specifying debug log level again */
83 | #ifndef DEBUG_BUILD
84 | if (log_level < message_level_masked)
85 | return;
86 | #endif
87 | const char *log_level_str =
88 | log_level_to_string(message_level_masked);
89 |
90 | /* Use stderr for warnings and higher */
91 | if (message_level_masked <= G_LOG_LEVEL_WARNING)
92 | g_printerr("%s: %s\n", log_level_str, message);
93 | else
94 | g_print("%s: %s\n", log_level_str, message);
95 | }
96 |
97 | /* see log.h */
98 | void dunst_log_init(bool testing)
99 | {
100 | g_log_set_default_handler(dunst_log_handler, (void*)testing);
101 | }
102 |
103 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
104 |
--------------------------------------------------------------------------------
/src/log.h:
--------------------------------------------------------------------------------
1 | /* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #ifndef DUNST_LOG_H
10 | #define DUNST_LOG_H
11 |
12 | /**
13 | * Prefix message with "[::] "
14 | *
15 | * @param format is either a format string like the first argument
16 | * of printf() or a string literal.
17 | * @... are the arguments to above format string.
18 | *
19 | * This requires -Wno-gnu-zero-variadic-macro-arguments with clang
20 | * because of token pasting ',' and %__VA_ARGS__ being a GNU extension.
21 | * However, the result is the same with both gcc and clang and since we are
22 | * compiling with '-std=gnu99', this should be fine.
23 | */
24 | #if __GNUC__ >= 8 || __clang_major__ >= 6
25 | #define MSG(format, ...) "[%16s:%04d] " format, __func__, __LINE__, ## __VA_ARGS__
26 | #endif
27 |
28 | #ifdef MSG
29 | // These should benefit from more context
30 | #define LOG_E(...) g_error(MSG(__VA_ARGS__))
31 | #define LOG_C(...) g_critical(MSG(__VA_ARGS__))
32 | #define LOG_D(...) g_debug(MSG(__VA_ARGS__))
33 | #else
34 | #define LOG_E g_error
35 | #define LOG_C g_critical
36 | #define LOG_D g_debug
37 | #endif
38 |
39 | #define LOG_W g_warning
40 | #define LOG_M g_message
41 | #define LOG_I g_info
42 |
43 | #define DIE(...) do { LOG_C(__VA_ARGS__); exit(EXIT_FAILURE); } while (0)
44 |
45 | // unified fopen() result messages
46 | #define MSG_FOPEN_SUCCESS(path, fp) "'%s' open, fd: '%d'", path, fileno(fp)
47 | #define MSG_FOPEN_FAILURE(path) "Cannot open '%s': '%s'", path, strerror(errno)
48 |
49 | /**
50 | * Set the current loglevel to `level`
51 | *
52 | * @param level The desired log level
53 | *
54 | * If `level` is `NULL`, nothing will be done.
55 | * If `level` is an invalid value, nothing will be done.
56 | */
57 | void log_set_level(GLogLevelFlags level);
58 |
59 | /**
60 | * Set the current loglevel to `level`
61 | *
62 | * @param level The desired log level as a string
63 | *
64 | * If `level` is `NULL`, nothing will be done.
65 | * If `level` is an invalid value, nothing will be done.
66 | */
67 | void log_set_level_from_string(const char* level);
68 |
69 | /**
70 | * Initialise log handling. Can be called any time.
71 | *
72 | * @param testing If we're in testing mode and should
73 | * suppress all output
74 | */
75 | void dunst_log_init(bool testing);
76 |
77 | #endif
78 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
79 |
--------------------------------------------------------------------------------
/src/markup.h:
--------------------------------------------------------------------------------
1 | /* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
2 | #ifndef DUNST_MARKUP_H
3 | #define DUNST_MARKUP_H
4 |
5 | enum markup_mode {
6 | MARKUP_NULL,
7 | MARKUP_NO,
8 | MARKUP_STRIP,
9 | MARKUP_FULL
10 | };
11 |
12 | /**
13 | * Strip any markup from text; turn it in to plain text.
14 | *
15 | * For well-formed markup, the following two commands should be
16 | * roughly equivalent:
17 | *
18 | * out = markup_strip(in);
19 | * pango_parse_markup(in, -1, 0, NULL, &out, NULL, NULL);
20 | *
21 | * However, `pango_parse_markup()` balks at invalid markup;
22 | * `markup_strip()` shouldn't care if there is invalid markup.
23 | */
24 | char *markup_strip(char *str);
25 |
26 | /**
27 | * Remove HTML hyperlinks of a string.
28 | *
29 | * @param str The string to replace a tags
30 | * @param urls (nullable) If any href-attributes found, an `\n` concatenated
31 | * string of the URLs in format `[] `
32 | */
33 | void markup_strip_a(char **str, char **urls);
34 |
35 | /**
36 | * Remove img-tags of a string. If alt attribute given, use this as replacement.
37 | *
38 | * @param str The string to replace img tags
39 | * @param urls (nullable) If any src-attributes found, an `\n` concatenated string of
40 | * the URLs in format `[] `
41 | */
42 | void markup_strip_img(char **str, char **urls);
43 |
44 | /**
45 | * Transform the string in accordance with `markup_mode` and
46 | * `settings.ignore_newline`
47 | */
48 | char *markup_transform(char *str, enum markup_mode markup_mode);
49 |
50 | #endif
51 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
52 |
--------------------------------------------------------------------------------
/src/menu.h:
--------------------------------------------------------------------------------
1 | /* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
2 | #ifndef DUNST_MENU_H
3 | #define DUNST_MENU_H
4 |
5 | #include
6 |
7 | /**
8 | * Extract all urls from the given string.
9 | *
10 | * @param to_match (nullable) String to extract URLs
11 | * @return a string of urls separated by '\n'
12 | * @retval NULL: No URLs found
13 | */
14 | char *extract_urls(const char *to_match);
15 |
16 | void open_browser(const char *in);
17 | void invoke_action(const char *action);
18 | void regex_teardown(void);
19 |
20 | /**
21 | * Open the context menu that lets the user select urls/actions/etc for all displayed notifications.
22 | */
23 | void context_menu(void);
24 |
25 | /**
26 | * Open the context menu that lets the user select urls/actions/etc for the specified notifications.
27 | * @param notifications (nullable) List of notifications for which the context menu should be opened
28 | */
29 | void context_menu_for(GList *notifications);
30 |
31 | #endif
32 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
33 |
--------------------------------------------------------------------------------
/src/option_parser.h:
--------------------------------------------------------------------------------
1 | /* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
2 | #ifndef DUNST_OPTION_PARSER_H
3 | #define DUNST_OPTION_PARSER_H
4 |
5 | #include
6 | #include
7 | #include
8 |
9 | #include "dunst.h"
10 | #include "settings.h"
11 | #include "ini.h"
12 |
13 | int string_parse_enum(const void* data, const char *s, void * ret);
14 | int string_parse_sepcolor(const void *data, const char *s, void *ret);
15 | int string_parse_bool(const void *data, const char *s, void *ret);
16 |
17 | void set_defaults();
18 | void save_settings(struct ini *ini);
19 |
20 | void cmdline_load(int argc, char *argv[]);
21 | /* for all cmdline_get_* key can be either "-key" or "-key/-longkey" */
22 | char *cmdline_get_string(const char *key, const char *def, const char *description);
23 | char *cmdline_get_path(const char *key, const char *def, const char *description);
24 | char **cmdline_get_list(const char *key, const char *def, const char *description);
25 | int cmdline_get_int(const char *key, int def, const char *description);
26 | double cmdline_get_double(const char *key, double def, const char *description);
27 | int cmdline_get_bool(const char *key, int def, const char *description);
28 | bool cmdline_is_set(const char *key);
29 | const char *cmdline_create_usage(void);
30 |
31 | #endif
32 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
33 |
--------------------------------------------------------------------------------
/src/output.c:
--------------------------------------------------------------------------------
1 | #include "output.h"
2 |
3 | #include "log.h"
4 | #include "x11/x.h"
5 | #include "x11/screen.h"
6 |
7 | #ifdef ENABLE_WAYLAND
8 | #include "wayland/wl.h"
9 | #endif
10 |
11 | bool is_running_wayland(void) {
12 | char* wayland_display = getenv("WAYLAND_DISPLAY");
13 | return !(wayland_display == NULL);
14 | }
15 |
16 | const struct output output_x11 = {
17 | x_setup,
18 | x_free,
19 |
20 | x_win_create,
21 | x_win_destroy,
22 |
23 | x_win_show,
24 | x_win_hide,
25 |
26 | x_display_surface,
27 | x_win_get_context,
28 |
29 | get_active_screen,
30 |
31 | x_is_idle,
32 | have_fullscreen_window,
33 |
34 | x_get_scale,
35 | };
36 |
37 | #ifdef ENABLE_WAYLAND
38 | const struct output output_wl = {
39 | wl_init,
40 | wl_deinit,
41 |
42 | wl_win_create,
43 | wl_win_destroy,
44 |
45 | wl_win_show,
46 | wl_win_hide,
47 |
48 | wl_display_surface,
49 | wl_win_get_context,
50 |
51 | wl_get_active_screen,
52 |
53 | wl_is_idle,
54 | wl_have_fullscreen_window,
55 |
56 | wl_get_scale,
57 | };
58 | #endif
59 |
60 | const struct output* get_x11_output() {
61 | const struct output* output = &output_x11;
62 | if (output->init()) {
63 | return output;
64 | } else {
65 | LOG_E("Couldn't initialize X11 output. Aborting...");
66 | }
67 | }
68 |
69 | #ifdef ENABLE_WAYLAND
70 | const struct output* get_wl_output() {
71 | const struct output* output = &output_wl;
72 | if (output->init()) {
73 | return output;
74 | } else {
75 | LOG_W("Couldn't initialize wayland output. Falling back to X11 output.");
76 | output->deinit();
77 | return get_x11_output();
78 | }
79 | }
80 | #endif
81 |
82 | const struct output* output_create(bool force_xwayland)
83 | {
84 | #ifdef ENABLE_WAYLAND
85 | if (!force_xwayland && is_running_wayland()) {
86 | LOG_I("Using Wayland output");
87 | return get_wl_output();
88 | } else {
89 | LOG_I("Using X11 output");
90 | return get_x11_output();
91 | }
92 | #else
93 | return get_x11_output();
94 | #endif
95 | }
96 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
97 |
--------------------------------------------------------------------------------
/src/output.h:
--------------------------------------------------------------------------------
1 | #ifndef DUNST_OUTPUT_H
2 | #define DUNST_OUTPUT_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | typedef gpointer window;
9 |
10 | struct dimensions {
11 | int x;
12 | int y;
13 | int w;
14 | int h;
15 | int text_width;
16 | int text_height;
17 |
18 | int corner_radius;
19 | };
20 |
21 | struct screen_info {
22 | int id;
23 | int x;
24 | int y;
25 | unsigned int h;
26 | unsigned int mmh;
27 | unsigned int w;
28 | int dpi;
29 | };
30 |
31 | struct output {
32 | bool (*init)(void);
33 | void (*deinit)(void);
34 |
35 | window (*win_create)(void);
36 | void (*win_destroy)(window);
37 |
38 | void (*win_show)(window);
39 | void (*win_hide)(window);
40 |
41 | void (*display_surface)(cairo_surface_t *srf, window win, const struct dimensions*);
42 |
43 | cairo_t* (*win_get_context)(window);
44 |
45 | const struct screen_info* (*get_active_screen)(void);
46 |
47 | bool (*is_idle)(void);
48 | bool (*have_fullscreen_window)(void);
49 |
50 | double (*get_scale)(void);
51 | };
52 |
53 | /**
54 | * return an initialized output, selecting the correct output type from either
55 | * wayland or X11 according to the settings and environment.
56 | * When the wayland output fails to initilize, it falls back to X11 output.
57 | */
58 | const struct output* output_create(bool force_xwayland);
59 |
60 | bool is_running_wayland(void);
61 |
62 | #endif
63 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
64 |
--------------------------------------------------------------------------------
/src/queues.h:
--------------------------------------------------------------------------------
1 | /* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
2 |
3 | /**
4 | * @file src/queues.h
5 | */
6 |
7 | #ifndef DUNST_QUEUE_H
8 | #define DUNST_QUEUE_H
9 |
10 | #include "dbus.h"
11 | #include "dunst.h"
12 | #include "notification.h"
13 |
14 | /**
15 | * Initialise necessary queues
16 | *
17 | * @pre Do not call consecutively to avoid memory leaks
18 | * or assure to have queues_teardown() executed before
19 | */
20 | void queues_init(void);
21 |
22 | /**
23 | * Receive the current list of displayed notifications
24 | *
25 | * @return read only list of notifications
26 | */
27 | GList *queues_get_displayed(void);
28 |
29 | /**
30 | * Recieve the list of all notifications encountered
31 | *
32 | * @return read only list of notifications
33 | */
34 | GList *queues_get_history(void);
35 |
36 | /**
37 | * Get the highest notification in line
38 | *
39 | * @returns the first notification in waiting
40 | * @retval NULL: waiting is empty
41 | */
42 | struct notification *queues_get_head_waiting(void);
43 |
44 | /**
45 | * Returns the current amount of notifications,
46 | * which are waiting to get displayed
47 | */
48 | unsigned int queues_length_waiting(void);
49 |
50 | /**
51 | * Returns the current amount of notifications,
52 | * which are shown in the UI
53 | */
54 | unsigned int queues_length_displayed(void);
55 |
56 | /**
57 | * Returns the current amount of notifications,
58 | * which are already in history
59 | */
60 | unsigned int queues_length_history(void);
61 |
62 | /**
63 | * Insert a fully initialized notification into queues
64 | *
65 | * Respects stack_duplicates, and notification replacement
66 | *
67 | * @param n the notification to insert
68 | *
69 | * - If n->id != 0, n replaces notification n with id n->id
70 | * - If n->id == 0, n gets a new id assigned
71 | *
72 | * @returns The new value of `n->id`
73 | * @retval 0: the notification was dismissed and freed
74 | */
75 | int queues_notification_insert(struct notification *n);
76 |
77 | /**
78 | * Replace the notification which matches the id field of
79 | * the new notification. The given notification is inserted
80 | * right in the same position as the old notification.
81 | *
82 | * @param new replacement for the old notification
83 | *
84 | * @retval true: a matching notification has been found and is replaced
85 | * @retval false: otherwise
86 | */
87 | bool queues_notification_replace_id(struct notification *new);
88 |
89 | /**
90 | * Close the notification that has n->id == id
91 | *
92 | * Sends a signal and pushes the selected notification automatically to history.
93 | *
94 | * @param id The id of the notification to close
95 | * @param reason The #reason to close
96 | *
97 | * @post Call wake_up() to synchronize the queues with the UI
98 | * (which closes the notification on screen)
99 | */
100 | void queues_notification_close_id(int id, enum reason reason);
101 |
102 | /**
103 | * Close the given notification. \see queues_notification_close_id().
104 | *
105 | * @param n (transfer full) The notification to close
106 | * @param reason The #reason to close
107 | * */
108 | void queues_notification_close(struct notification *n, enum reason reason);
109 |
110 | /**
111 | * Pushes the latest notification of history to the displayed queue
112 | * and removes it from history
113 | */
114 | void queues_history_pop(void);
115 |
116 | /**
117 | * Pushes the latest notification found in the history buffer identified by
118 | * it's assigned id
119 | */
120 | void queues_history_pop_by_id(unsigned int id);
121 |
122 | /**
123 | * Push a single notification to history
124 | * The given notification has to be removed its queue
125 | *
126 | * @param n (transfer full) The notification to push to history
127 | */
128 | void queues_history_push(struct notification *n);
129 |
130 | /**
131 | * Push all waiting and displayed notifications to history
132 | */
133 | void queues_history_push_all(void);
134 |
135 | /**
136 | * Move inserted notifications from waiting queue to displayed queue
137 | * and show them. In displayed queue, the amount of elements is limited
138 | * to the amount set via queues_displayed_limit()
139 | *
140 | * @post Call wake_up() to synchronize the queues with the UI
141 | * (which closes old and shows new notifications on screen)
142 | *
143 | * @param status the current status of dunst
144 | * @param time the current time
145 | */
146 | void queues_update(struct dunst_status status, gint64 time);
147 |
148 | /**
149 | * Calculate the distance to the next event, when an element in the
150 | * queues changes
151 | *
152 | * @param time the current time
153 | *
154 | * @return the distance to the next event in the queue, which forces
155 | * an update visible to the user. This may be:
156 | * - notification hits timeout
157 | * - notification's age second changes
158 | * - notification's age threshold is hit
159 | */
160 | gint64 queues_get_next_datachange(gint64 time);
161 |
162 | /**
163 | * Get the notification which has the given id in the displayed and waiting queue or
164 | * NULL if not found
165 | *
166 | * @param id the id searched for.
167 | *
168 | * @return the `id` notification or NULL
169 | */
170 | struct notification* queues_get_by_id(int id);
171 |
172 |
173 | /**
174 | * Remove all notifications from all list and free the notifications
175 | *
176 | * @pre At least one time queues_init() called
177 | */
178 | void queues_teardown(void);
179 |
180 | #endif
181 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
182 |
--------------------------------------------------------------------------------
/src/rules.h:
--------------------------------------------------------------------------------
1 | /* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
2 | #ifndef DUNST_RULES_H
3 | #define DUNST_RULES_H
4 |
5 | #include
6 | #include
7 |
8 | #include "notification.h"
9 | #include "settings.h"
10 |
11 | struct rule {
12 | // Since there's heavy use of offsets from this class, both in rules.c
13 | // and in settings_data.h the layout of the class should not be
14 | // changed, unless it's well considered and tested. See the comments
15 | // below for what should not be changed.
16 |
17 | // This has to be the first member, see struct setting.rule_offset.
18 | char *name;
19 |
20 | /* filters */
21 | char *appname; // this has to be the first filter, see rules.c
22 | char *summary;
23 | char *body;
24 | char *icon;
25 | char *category;
26 | char *stack_tag;
27 | char *desktop_entry;
28 | int msg_urgency;
29 | gint64 match_dbus_timeout;
30 |
31 | /* modifying */
32 | gint64 timeout; // this has to be the first modifying rule
33 | gint64 override_dbus_timeout;
34 | enum urgency urgency;
35 | char *action_name;
36 | enum markup_mode markup;
37 | int history_ignore;
38 | int match_transient;
39 | int set_transient;
40 | int skip_display;
41 | int word_wrap;
42 | int ellipsize;
43 | int alignment;
44 | int hide_text;
45 | int icon_position;
46 | int min_icon_size;
47 | int max_icon_size;
48 | char *new_icon;
49 | char *fg;
50 | char *bg;
51 | char *highlight;
52 | char *default_icon;
53 | char *fc;
54 | char *set_category;
55 | const char *format;
56 | const char *script;
57 | enum behavior_fullscreen fullscreen;
58 | bool enabled;
59 | int progress_bar_alignment;
60 | char *set_stack_tag; // this has to be the last modifying rule
61 | };
62 |
63 | extern GSList *rules;
64 |
65 | /**
66 | * Allocate a new rule with given name. The rule is fully initialised. If the
67 | * name is one of a special section (see settings_data.h), the rule is
68 | * initialized with some filters, and you should not add any filters after
69 | * that.
70 | *
71 | * @param name Name of the rule.
72 | *
73 | * @returns A new initialised rule.
74 | */
75 | struct rule *rule_new(const char *name);
76 |
77 | void rule_apply(struct rule *r, struct notification *n);
78 | void rule_apply_all(struct notification *n);
79 | bool rule_matches_notification(struct rule *r, struct notification *n);
80 |
81 | /**
82 | * Get rule with this name from rules
83 | *
84 | * @returns the rule that matches. Null if no rule matches
85 | */
86 | struct rule *get_rule(const char* name);
87 |
88 | /**
89 | * Check if a rule is an action
90 | *
91 | * @returns a boolean if the rule is an action
92 | */
93 | bool rule_offset_is_modifying(const size_t offset);
94 |
95 | /**
96 | * Check if a rule is an filter
97 | *
98 | * @returns a boolean if the rule is an filter
99 | */
100 | bool rule_offset_is_filter(const size_t offset);
101 |
102 | #endif
103 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
104 |
--------------------------------------------------------------------------------
/src/settings.h:
--------------------------------------------------------------------------------
1 | /* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
2 | #ifndef DUNST_SETTINGS_H
3 | #define DUNST_SETTINGS_H
4 |
5 | #include
6 |
7 | #ifdef ENABLE_WAYLAND
8 | #include "wayland/protocols/wlr-layer-shell-unstable-v1-client-header.h"
9 | #endif
10 |
11 | #include "notification.h"
12 | #include "x11/x.h"
13 |
14 | #define LIST_END (-1)
15 |
16 | enum alignment { ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT };
17 | enum vertical_alignment { VERTICAL_TOP, VERTICAL_CENTER, VERTICAL_BOTTOM };
18 | enum separator_color { SEP_FOREGROUND, SEP_AUTO, SEP_FRAME, SEP_CUSTOM };
19 | enum follow_mode { FOLLOW_NONE, FOLLOW_MOUSE, FOLLOW_KEYBOARD };
20 | enum mouse_action { MOUSE_NONE, MOUSE_DO_ACTION, MOUSE_CLOSE_CURRENT,
21 | MOUSE_CLOSE_ALL, MOUSE_CONTEXT, MOUSE_CONTEXT_ALL, MOUSE_OPEN_URL,
22 | MOUSE_ACTION_END = LIST_END /* indicates the end of a list of mouse actions */};
23 | #ifndef ZWLR_LAYER_SHELL_V1_LAYER_ENUM
24 | #define ZWLR_LAYER_SHELL_V1_LAYER_ENUM
25 | // Needed for compiling without wayland dependency
26 | enum zwlr_layer_shell_v1_layer {
27 | ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND = 0,
28 | ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM = 1,
29 | ZWLR_LAYER_SHELL_V1_LAYER_TOP = 2,
30 | ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY = 3,
31 | };
32 | #endif /* ZWLR_LAYER_SHELL_V1_LAYER_ENUM */
33 |
34 | #ifndef ZWLR_LAYER_SURFACE_V1_ANCHOR_ENUM
35 | #define ZWLR_LAYER_SURFACE_V1_ANCHOR_ENUM
36 | enum zwlr_layer_surface_v1_anchor {
37 | /**
38 | * the top edge of the anchor rectangle
39 | */
40 | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP = 1,
41 | /**
42 | * the bottom edge of the anchor rectangle
43 | */
44 | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM = 2,
45 | /**
46 | * the left edge of the anchor rectangle
47 | */
48 | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT = 4,
49 | /**
50 | * the right edge of the anchor rectangle
51 | */
52 | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT = 8,
53 | };
54 | #endif /* ZWLR_LAYER_SURFACE_V1_ANCHOR_ENUM */
55 |
56 | enum origin_values {
57 | ORIGIN_TOP_LEFT = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT,
58 | ORIGIN_TOP_CENTER = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP,
59 | ORIGIN_TOP_RIGHT = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT,
60 | ORIGIN_BOTTOM_LEFT = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT,
61 | ORIGIN_BOTTOM_CENTER = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
62 | ORIGIN_BOTTOM_RIGHT = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT,
63 | ORIGIN_LEFT_CENTER = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT,
64 | ORIGIN_RIGHT_CENTER = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT,
65 | ORIGIN_CENTER = 0,
66 | };
67 |
68 | // TODO make a TYPE_CMD, instead of using TYPE_PATH for settings like dmenu and browser
69 | enum setting_type { TYPE_MIN = 0, TYPE_INT, TYPE_DOUBLE, TYPE_STRING,
70 | TYPE_PATH, TYPE_TIME, TYPE_LIST, TYPE_CUSTOM, TYPE_LENGTH,
71 | TYPE_DEPRECATED, TYPE_MAX = TYPE_DEPRECATED + 1 }; // to be implemented
72 |
73 | struct separator_color_data {
74 | enum separator_color type;
75 | char *sep_color;
76 | };
77 |
78 | struct length {
79 | int min;
80 | int max;
81 | };
82 |
83 | struct position {
84 | int x;
85 | int y;
86 | };
87 |
88 | struct settings {
89 | bool print_notifications;
90 | bool per_monitor_dpi;
91 | bool stack_duplicates;
92 | bool hide_duplicate_count;
93 | char *font;
94 | struct notification_colors colors_low;
95 | struct notification_colors colors_norm;
96 | struct notification_colors colors_crit;
97 | char *format;
98 | gint64 timeouts[3];
99 | char *icons[3];
100 | unsigned int transparency;
101 | char *title;
102 | char *class;
103 | int shrink;
104 | int sort;
105 | int indicate_hidden;
106 | gint64 idle_threshold;
107 | gint64 show_age_threshold;
108 | enum alignment align;
109 | int sticky_history;
110 | int history_length;
111 | int show_indicators;
112 | int ignore_dbusclose;
113 | int ignore_newline;
114 | int line_height;
115 | int separator_height;
116 | int padding;
117 | int h_padding;
118 | int text_icon_padding;
119 | struct separator_color_data sep_color;
120 | int frame_width;
121 | char *frame_color;
122 | int startup_notification;
123 | int monitor;
124 | double scale;
125 | char *dmenu;
126 | char **dmenu_cmd;
127 | char *browser;
128 | char **browser_cmd;
129 | enum vertical_alignment vertical_alignment;
130 | char **icon_theme; // experimental
131 | bool enable_recursive_icon_lookup; // experimental
132 | bool enable_regex; // experimental
133 | char *icon_path;
134 | enum follow_mode f_mode;
135 | bool always_run_script;
136 | struct keyboard_shortcut close_ks;
137 | struct keyboard_shortcut close_all_ks;
138 | struct keyboard_shortcut history_ks;
139 | struct keyboard_shortcut context_ks;
140 | bool force_xinerama;
141 | bool force_xwayland;
142 | int corner_radius;
143 | enum mouse_action *mouse_left_click;
144 | enum mouse_action *mouse_middle_click;
145 | enum mouse_action *mouse_right_click;
146 | int progress_bar_height;
147 | int progress_bar_min_width;
148 | int progress_bar_max_width;
149 | int progress_bar_frame_width;
150 | bool progress_bar;
151 | enum zwlr_layer_shell_v1_layer layer;
152 | enum origin_values origin;
153 | struct length width;
154 | int height;
155 | struct position offset;
156 | int notification_limit;
157 | int gap_size;
158 | };
159 |
160 | extern struct settings settings;
161 |
162 | void load_settings(const char * const path);
163 |
164 | #endif
165 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
166 |
--------------------------------------------------------------------------------
/src/wayland/foreign_toplevel.c:
--------------------------------------------------------------------------------
1 | #define _POSIX_C_SOURCE 200112L
2 | #include
3 | #include
4 | #include
5 | #include "protocols/wlr-foreign-toplevel-management-unstable-v1-client-header.h"
6 | /* #include "protocols/wlr-foreign-toplevel-management-unstable-v1.h" */
7 |
8 | #include "foreign_toplevel.h"
9 | #include "../dunst.h"
10 | #include "wl_output.h"
11 | #include "wl.h"
12 |
13 | struct wl_list toplevel_list;
14 |
15 | static void noop() {
16 | // This space intentionally left blank
17 | }
18 |
19 | static void copy_state(struct toplevel_state *current,
20 | struct toplevel_state *pending) {
21 | if (!(pending->state & TOPLEVEL_STATE_INVALID)) {
22 | current->state = pending->state;
23 | }
24 |
25 | pending->state = TOPLEVEL_STATE_INVALID;
26 | }
27 |
28 | static uint32_t global_id = 0;
29 |
30 | static void toplevel_handle_output_enter(void *data,
31 | struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel,
32 | struct wl_output *wl_output) {
33 | struct toplevel_v1 *toplevel = data;
34 | struct toplevel_output *toplevel_output = calloc(1, sizeof(struct toplevel_output));
35 | struct dunst_output *dunst_output = wl_output_get_user_data(wl_output);
36 | toplevel_output->dunst_output = dunst_output;
37 |
38 | wl_list_insert(&toplevel->output_list, &toplevel_output->link);
39 | }
40 |
41 | static void toplevel_handle_output_leave(void *data,
42 | struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel,
43 | struct wl_output *wl_output) {
44 | struct toplevel_v1 *toplevel = data;
45 |
46 | struct dunst_output *output = wl_output_get_user_data(wl_output);
47 | struct toplevel_output *pos, *tmp;
48 | wl_list_for_each_safe(pos, tmp, &toplevel->output_list, link){
49 | if (pos->dunst_output->name == output->name){
50 | wl_list_remove(&pos->link);
51 | }
52 | }
53 | }
54 |
55 | static uint32_t array_to_state(struct wl_array *array) {
56 | uint32_t state = 0;
57 | uint32_t *entry;
58 | wl_array_for_each(entry, array) {
59 | if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED)
60 | state |= TOPLEVEL_STATE_ACTIVATED;
61 | if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN)
62 | state |= TOPLEVEL_STATE_FULLSCREEN;
63 | }
64 |
65 | return state;
66 | }
67 |
68 | static void toplevel_handle_state(void *data,
69 | struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel,
70 | struct wl_array *state) {
71 | struct toplevel_v1 *toplevel = data;
72 | toplevel->pending.state = array_to_state(state);
73 | }
74 |
75 | static void toplevel_handle_done(void *data,
76 | struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) {
77 | struct toplevel_v1 *toplevel = data;
78 |
79 | bool was_fullscreen = wl_have_fullscreen_window();
80 | copy_state(&toplevel->current, &toplevel->pending);
81 | bool is_fullscreen = wl_have_fullscreen_window();
82 |
83 | if (was_fullscreen != is_fullscreen) {
84 | wake_up();
85 | }
86 | }
87 |
88 | static void toplevel_handle_closed(void *data,
89 | struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) {
90 | struct toplevel_v1 *toplevel = data;
91 |
92 | wl_list_remove(&toplevel->link);
93 | struct toplevel_output *pos, *tmp;
94 | wl_list_for_each_safe(pos, tmp, &toplevel->output_list, link){
95 | free(pos);
96 | }
97 | free(toplevel);
98 | zwlr_foreign_toplevel_handle_v1_destroy(zwlr_toplevel);
99 | }
100 |
101 | static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_impl = {
102 | .title = noop,
103 | .app_id = noop,
104 | .output_enter = toplevel_handle_output_enter,
105 | .output_leave = toplevel_handle_output_leave,
106 | .state = toplevel_handle_state,
107 | .done = toplevel_handle_done,
108 | .closed = toplevel_handle_closed,
109 | };
110 |
111 | static void toplevel_manager_handle_toplevel(void *data,
112 | struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager,
113 | struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) {
114 | struct toplevel_v1 *toplevel = calloc(1, sizeof(struct toplevel_v1));
115 | if (!toplevel) {
116 | fprintf(stderr, "Failed to allocate memory for toplevel\n");
117 | return;
118 | }
119 |
120 | toplevel->id = global_id++;
121 | toplevel->zwlr_toplevel = zwlr_toplevel;
122 | wl_list_insert(&toplevel_list, &toplevel->link);
123 | wl_list_init(&toplevel->output_list);
124 |
125 | zwlr_foreign_toplevel_handle_v1_add_listener(zwlr_toplevel, &toplevel_impl,
126 | toplevel);
127 | }
128 |
129 | static void toplevel_manager_handle_finished(void *data,
130 | struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager) {
131 | zwlr_foreign_toplevel_manager_v1_destroy(toplevel_manager);
132 | }
133 |
134 | const struct zwlr_foreign_toplevel_manager_v1_listener toplevel_manager_impl = {
135 | .toplevel = toplevel_manager_handle_toplevel,
136 | .finished = toplevel_manager_handle_finished,
137 | };
138 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
139 |
--------------------------------------------------------------------------------
/src/wayland/foreign_toplevel.h:
--------------------------------------------------------------------------------
1 | #ifndef DUNST_FOREIGN_TOPLEVEL_H
2 | #define DUNST_FOREIGN_TOPLEVEL_H
3 | #include
4 |
5 | enum toplevel_state_field {
6 | TOPLEVEL_STATE_ACTIVATED = (1 << 0),
7 | TOPLEVEL_STATE_FULLSCREEN = (1 << 1),
8 | TOPLEVEL_STATE_INVALID = (1 << 2),
9 | };
10 |
11 | struct toplevel_state {
12 | uint32_t state;
13 | };
14 |
15 | struct toplevel_v1 {
16 | struct wl_list link;
17 | struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel;
18 | struct wl_list output_list;
19 |
20 | uint32_t id;
21 | struct toplevel_state current, pending;
22 | };
23 |
24 | struct toplevel_output {
25 | struct dunst_output *dunst_output;
26 | struct wl_list link;
27 | };
28 |
29 | extern const struct zwlr_foreign_toplevel_manager_v1_listener toplevel_manager_impl;
30 |
31 | extern struct wl_list toplevel_list;
32 | #endif
33 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
34 |
--------------------------------------------------------------------------------
/src/wayland/libgwater-wayland.c:
--------------------------------------------------------------------------------
1 | /*
2 | * libgwater-wayland - Wayland GSource
3 | *
4 | * Copyright © 2014-2017 Quentin "Sardem FF7" Glidic
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | *
24 | */
25 |
26 | #ifdef HAVE_CONFIG_H
27 | #include "config.h"
28 | #endif /* HAVE_CONFIG_H */
29 |
30 | #ifdef G_LOG_DOMAIN
31 | #undef G_LOG_DOMAIN
32 | #endif /* G_LOG_DOMAIN */
33 | #define G_LOG_DOMAIN "GWaterWayland"
34 |
35 | #include
36 |
37 | #include
38 |
39 | #include
40 |
41 | #include "libgwater-wayland.h"
42 |
43 | struct _GWaterWaylandSource {
44 | GSource source;
45 | gboolean display_owned;
46 | struct wl_display *display;
47 | gpointer fd;
48 | int error;
49 | };
50 |
51 | static gboolean
52 | _g_water_wayland_source_prepare(GSource *source, gint *timeout)
53 | {
54 | GWaterWaylandSource *self = (GWaterWaylandSource *)source;
55 |
56 | *timeout = 0;
57 | if ( wl_display_prepare_read(self->display) != 0 )
58 | return TRUE;
59 | else if ( wl_display_flush(self->display) < 0 )
60 | {
61 | self->error = errno;
62 | return TRUE;
63 | }
64 |
65 | *timeout = -1;
66 | return FALSE;
67 | }
68 |
69 | static gboolean
70 | _g_water_wayland_source_check(GSource *source)
71 | {
72 | GWaterWaylandSource *self = (GWaterWaylandSource *)source;
73 |
74 | if ( self->error > 0 )
75 | return TRUE;
76 |
77 | GIOCondition revents;
78 | revents = g_source_query_unix_fd(source, self->fd);
79 |
80 | if ( revents & G_IO_IN )
81 | {
82 | if ( wl_display_read_events(self->display) < 0 )
83 | self->error = errno;
84 | }
85 | else
86 | wl_display_cancel_read(self->display);
87 |
88 | return ( revents > 0 );
89 | }
90 |
91 | static gboolean
92 | _g_water_wayland_source_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
93 | {
94 | GWaterWaylandSource *self = (GWaterWaylandSource *)source;
95 | GIOCondition revents;
96 |
97 | revents = g_source_query_unix_fd(source, self->fd);
98 | if ( ( self->error > 0 ) || ( revents & (G_IO_ERR | G_IO_HUP) ) )
99 | {
100 | errno = self->error;
101 | self->error = 0;
102 | if ( callback != NULL )
103 | return callback(user_data);
104 | return G_SOURCE_REMOVE;
105 | }
106 |
107 | if ( wl_display_dispatch_pending(self->display) < 0 )
108 | {
109 | if ( callback != NULL )
110 | return callback(user_data);
111 | return G_SOURCE_REMOVE;
112 | }
113 |
114 | return G_SOURCE_CONTINUE;
115 | }
116 |
117 | static void
118 | _g_water_wayland_source_finalize(GSource *source)
119 | {
120 | GWaterWaylandSource *self = (GWaterWaylandSource *)source;
121 |
122 | if ( self->display_owned )
123 | wl_display_disconnect(self->display);
124 | }
125 |
126 | static GSourceFuncs _g_water_wayland_source_funcs = {
127 | .prepare = _g_water_wayland_source_prepare,
128 | .check = _g_water_wayland_source_check,
129 | .dispatch = _g_water_wayland_source_dispatch,
130 | .finalize = _g_water_wayland_source_finalize,
131 | };
132 |
133 | GWaterWaylandSource *
134 | g_water_wayland_source_new(GMainContext *context, const gchar *name)
135 | {
136 | struct wl_display *display;
137 | GWaterWaylandSource *self;
138 |
139 | display = wl_display_connect(name);
140 | if ( display == NULL )
141 | return NULL;
142 |
143 | self = g_water_wayland_source_new_for_display(context, display);
144 | self->display_owned = TRUE;
145 | return self;
146 | }
147 |
148 | GWaterWaylandSource *
149 | g_water_wayland_source_new_for_display(GMainContext *context, struct wl_display *display)
150 | {
151 | g_return_val_if_fail(display != NULL, NULL);
152 |
153 | GSource *source;
154 | GWaterWaylandSource *self;
155 |
156 | source = g_source_new(&_g_water_wayland_source_funcs, sizeof(GWaterWaylandSource));
157 | self = (GWaterWaylandSource *)source;
158 | self->display = display;
159 |
160 | self->fd = g_source_add_unix_fd(source, wl_display_get_fd(self->display), G_IO_IN | G_IO_ERR | G_IO_HUP);
161 |
162 | g_source_attach(source, context);
163 |
164 | return self;
165 | }
166 |
167 | void
168 | g_water_wayland_source_free(GWaterWaylandSource *self)
169 | {
170 | GSource * source = (GSource *)self;
171 | g_return_if_fail(self != NULL);
172 |
173 | g_source_destroy(source);
174 |
175 | g_source_unref(source);
176 | }
177 |
178 | void
179 | g_water_wayland_source_set_error_callback(GWaterWaylandSource *self, GSourceFunc callback, gpointer user_data, GDestroyNotify destroy_notify)
180 | {
181 | g_return_if_fail(self != NULL);
182 |
183 | g_source_set_callback((GSource *)self, callback, user_data, destroy_notify);
184 | }
185 |
186 | struct wl_display *
187 | g_water_wayland_source_get_display(GWaterWaylandSource *self)
188 | {
189 | g_return_val_if_fail(self != NULL, NULL);
190 |
191 | return self->display;
192 | }
193 |
--------------------------------------------------------------------------------
/src/wayland/libgwater-wayland.h:
--------------------------------------------------------------------------------
1 | /*
2 | * libgwater-wayland - Wayland GSource
3 | *
4 | * Copyright © 2014-2017 Quentin "Sardem FF7" Glidic
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy
7 | * of this software and associated documentation files (the "Software"), to deal
8 | * in the Software without restriction, including without limitation the rights
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | * copies of the Software, and to permit persons to whom the Software is
11 | * furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice shall be included in
14 | * all copies or substantial portions of the Software.
15 | *
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | * THE SOFTWARE.
23 | *
24 | */
25 |
26 | #ifndef __G_WATER_WAYLAND_H__
27 | #define __G_WATER_WAYLAND_H__
28 |
29 | G_BEGIN_DECLS
30 |
31 | typedef struct _GWaterWaylandSource GWaterWaylandSource;
32 |
33 | GWaterWaylandSource *g_water_wayland_source_new(GMainContext *context, const gchar *name);
34 | GWaterWaylandSource *g_water_wayland_source_new_for_display(GMainContext *context, struct wl_display *display);
35 | void g_water_wayland_source_free(GWaterWaylandSource *self);
36 |
37 | void g_water_wayland_source_set_error_callback(GWaterWaylandSource *self, GSourceFunc callback, gpointer user_data, GDestroyNotify destroy_notify);
38 | struct wl_display *g_water_wayland_source_get_display(GWaterWaylandSource *source);
39 |
40 | G_END_DECLS
41 |
42 | #endif /* __G_WATER_WAYLAND_H__ */
43 |
--------------------------------------------------------------------------------
/src/wayland/pool-buffer.c:
--------------------------------------------------------------------------------
1 | #define _POSIX_C_SOURCE 200112L
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | #include "pool-buffer.h"
12 |
13 | static void randname(char *buf) {
14 | struct timespec ts;
15 | clock_gettime(CLOCK_REALTIME, &ts);
16 | long r = ts.tv_nsec;
17 | for (int i = 0; i < 6; ++i) {
18 | buf[i] = 'A'+(r&15)+(r&16)*2;
19 | r >>= 5;
20 | }
21 | }
22 |
23 | static int anonymous_shm_open(void) {
24 | char name[] = "/dunst-XXXXXX";
25 | int retries = 100;
26 |
27 | do {
28 | randname(name + strlen(name) - 6);
29 |
30 | --retries;
31 | // shm_open guarantees that O_CLOEXEC is set
32 | int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
33 | if (fd >= 0) {
34 | shm_unlink(name);
35 | return fd;
36 | }
37 | } while (retries > 0 && errno == EEXIST);
38 |
39 | return -1;
40 | }
41 |
42 | static int create_shm_file(off_t size) {
43 | int fd = anonymous_shm_open();
44 | if (fd < 0) {
45 | return fd;
46 | }
47 |
48 | if (ftruncate(fd, size) < 0) {
49 | close(fd);
50 | return -1;
51 | }
52 |
53 | return fd;
54 | }
55 |
56 | static void buffer_handle_release(void *data, struct wl_buffer *wl_buffer) {
57 | struct pool_buffer *buffer = data;
58 | buffer->busy = false;
59 | }
60 |
61 | static const struct wl_buffer_listener buffer_listener = {
62 | .release = buffer_handle_release,
63 | };
64 |
65 | static struct pool_buffer *create_buffer(struct wl_shm *shm,
66 | struct pool_buffer *buf, int32_t width, int32_t height) {
67 | const enum wl_shm_format wl_fmt = WL_SHM_FORMAT_ARGB8888;
68 | const cairo_format_t cairo_fmt = CAIRO_FORMAT_ARGB32;
69 |
70 | uint32_t stride = cairo_format_stride_for_width(cairo_fmt, width);
71 | size_t size = stride * height;
72 |
73 | void *data = NULL;
74 | if (size > 0) {
75 | int fd = create_shm_file(size);
76 | if (fd == -1) {
77 | return NULL;
78 | }
79 |
80 | data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
81 | if (data == MAP_FAILED) {
82 | close(fd);
83 | return NULL;
84 | }
85 |
86 | struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
87 | buf->buffer =
88 | wl_shm_pool_create_buffer(pool, 0, width, height, stride, wl_fmt);
89 | wl_buffer_add_listener(buf->buffer, &buffer_listener, buf);
90 | wl_shm_pool_destroy(pool);
91 |
92 | close(fd);
93 | }
94 |
95 | buf->data = data;
96 | buf->size = size;
97 | buf->width = width;
98 | buf->height = height;
99 | buf->surface = cairo_image_surface_create_for_data(data, cairo_fmt, width,
100 | height, stride);
101 | buf->cairo = cairo_create(buf->surface);
102 | buf->pango = pango_cairo_create_context(buf->cairo);
103 | return buf;
104 | }
105 |
106 | void finish_buffer(struct pool_buffer *buffer) {
107 | if (buffer->buffer) {
108 | wl_buffer_destroy(buffer->buffer);
109 | }
110 | if (buffer->cairo) {
111 | cairo_destroy(buffer->cairo);
112 | }
113 | if (buffer->surface) {
114 | cairo_surface_destroy(buffer->surface);
115 | }
116 | if (buffer->pango) {
117 | g_object_unref(buffer->pango);
118 | }
119 | if (buffer->data) {
120 | munmap(buffer->data, buffer->size);
121 | }
122 | memset(buffer, 0, sizeof(struct pool_buffer));
123 | }
124 |
125 | struct pool_buffer *get_next_buffer(struct wl_shm *shm,
126 | struct pool_buffer pool[static 2], uint32_t width, uint32_t height) {
127 | struct pool_buffer *buffer = NULL;
128 | for (size_t i = 0; i < 2; ++i) {
129 | if (pool[i].busy) {
130 | continue;
131 | }
132 | buffer = &pool[i];
133 | }
134 | if (!buffer) {
135 | return NULL;
136 | }
137 |
138 | if (buffer->width != width || buffer->height != height) {
139 | finish_buffer(buffer);
140 | }
141 |
142 | if (!buffer->buffer) {
143 | if (!create_buffer(shm, buffer, width, height)) {
144 | return NULL;
145 | }
146 | }
147 |
148 | return buffer;
149 | }
150 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
151 |
--------------------------------------------------------------------------------
/src/wayland/pool-buffer.h:
--------------------------------------------------------------------------------
1 | #ifndef DUNST_POOL_BUFFER_H
2 | #define DUNST_POOL_BUFFER_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | struct pool_buffer {
11 | struct wl_buffer *buffer;
12 | cairo_surface_t *surface;
13 | cairo_t *cairo;
14 | PangoContext *pango;
15 | uint32_t width, height;
16 | void *data;
17 | size_t size;
18 | bool busy;
19 | };
20 |
21 | struct pool_buffer *get_next_buffer(struct wl_shm *shm,
22 | struct pool_buffer pool[static 2], uint32_t width, uint32_t height);
23 | void finish_buffer(struct pool_buffer *buffer);
24 |
25 | #endif
26 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
27 |
--------------------------------------------------------------------------------
/src/wayland/protocols/idle.h:
--------------------------------------------------------------------------------
1 | /* Generated by wayland-scanner 1.19.0 */
2 |
3 | /*
4 | * Copyright (C) 2015 Martin Gräßlin
5 | *
6 | * This program is free software: you can redistribute it and/or modify
7 | * it under the terms of the GNU Lesser General Public License as published by
8 | * the Free Software Foundation, either version 2.1 of the License, or
9 | * (at your option) any later version.
10 | *
11 | * This program is distributed in the hope that it will be useful,
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | * GNU Lesser General Public License for more details.
15 | *
16 | * You should have received a copy of the GNU Lesser General Public License
17 | * along with this program. If not, see .
18 | */
19 |
20 | #include
21 | #include
22 | #include "wayland-util.h"
23 |
24 | #ifndef __has_attribute
25 | # define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
26 | #endif
27 |
28 | #if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
29 | #define WL_PRIVATE __attribute__ ((visibility("hidden")))
30 | #else
31 | #define WL_PRIVATE
32 | #endif
33 |
34 | extern const struct wl_interface org_kde_kwin_idle_timeout_interface;
35 | extern const struct wl_interface wl_seat_interface;
36 |
37 | static const struct wl_interface *idle_types[] = {
38 | &org_kde_kwin_idle_timeout_interface,
39 | &wl_seat_interface,
40 | NULL,
41 | };
42 |
43 | static const struct wl_message org_kde_kwin_idle_requests[] = {
44 | { "get_idle_timeout", "nou", idle_types + 0 },
45 | };
46 |
47 | WL_PRIVATE const struct wl_interface org_kde_kwin_idle_interface = {
48 | "org_kde_kwin_idle", 1,
49 | 1, org_kde_kwin_idle_requests,
50 | 0, NULL,
51 | };
52 |
53 | static const struct wl_message org_kde_kwin_idle_timeout_requests[] = {
54 | { "release", "", idle_types + 0 },
55 | { "simulate_user_activity", "", idle_types + 0 },
56 | };
57 |
58 | static const struct wl_message org_kde_kwin_idle_timeout_events[] = {
59 | { "idle", "", idle_types + 0 },
60 | { "resumed", "", idle_types + 0 },
61 | };
62 |
63 | WL_PRIVATE const struct wl_interface org_kde_kwin_idle_timeout_interface = {
64 | "org_kde_kwin_idle_timeout", 1,
65 | 2, org_kde_kwin_idle_timeout_requests,
66 | 2, org_kde_kwin_idle_timeout_events,
67 | };
68 |
69 |
--------------------------------------------------------------------------------
/src/wayland/protocols/idle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | .
18 | ]]>
19 |
20 |
21 | This interface allows to monitor user idle time on a given seat. The interface
22 | allows to register timers which trigger after no user activity was registered
23 | on the seat for a given interval. It notifies when user activity resumes.
24 |
25 | This is useful for applications wanting to perform actions when the user is not
26 | interacting with the system, e.g. chat applications setting the user as away, power
27 | management features to dim screen, etc..
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/wayland/protocols/wlr-foreign-toplevel-management-unstable-v1.h:
--------------------------------------------------------------------------------
1 | /* Generated by wayland-scanner 1.19.0 */
2 |
3 | /*
4 | * Copyright © 2018 Ilia Bozhinov
5 | *
6 | * Permission to use, copy, modify, distribute, and sell this
7 | * software and its documentation for any purpose is hereby granted
8 | * without fee, provided that the above copyright notice appear in
9 | * all copies and that both that copyright notice and this permission
10 | * notice appear in supporting documentation, and that the name of
11 | * the copyright holders not be used in advertising or publicity
12 | * pertaining to distribution of the software without specific,
13 | * written prior permission. The copyright holders make no
14 | * representations about the suitability of this software for any
15 | * purpose. It is provided "as is" without express or implied
16 | * warranty.
17 | *
18 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
19 | * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 | * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 | * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
22 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
23 | * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
24 | * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
25 | * THIS SOFTWARE.
26 | */
27 |
28 | #include
29 | #include
30 | #include "wayland-util.h"
31 |
32 | #ifndef __has_attribute
33 | # define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
34 | #endif
35 |
36 | #if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
37 | #define WL_PRIVATE __attribute__ ((visibility("hidden")))
38 | #else
39 | #define WL_PRIVATE
40 | #endif
41 |
42 | extern const struct wl_interface wl_output_interface;
43 | extern const struct wl_interface wl_seat_interface;
44 | extern const struct wl_interface wl_surface_interface;
45 | extern const struct wl_interface zwlr_foreign_toplevel_handle_v1_interface;
46 |
47 | static const struct wl_interface *wlr_foreign_toplevel_management_unstable_v1_types[] = {
48 | NULL,
49 | &zwlr_foreign_toplevel_handle_v1_interface,
50 | &wl_seat_interface,
51 | &wl_surface_interface,
52 | NULL,
53 | NULL,
54 | NULL,
55 | NULL,
56 | &wl_output_interface,
57 | &wl_output_interface,
58 | &wl_output_interface,
59 | &zwlr_foreign_toplevel_handle_v1_interface,
60 | };
61 |
62 | static const struct wl_message zwlr_foreign_toplevel_manager_v1_requests[] = {
63 | { "stop", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
64 | };
65 |
66 | static const struct wl_message zwlr_foreign_toplevel_manager_v1_events[] = {
67 | { "toplevel", "n", wlr_foreign_toplevel_management_unstable_v1_types + 1 },
68 | { "finished", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
69 | };
70 |
71 | WL_PRIVATE const struct wl_interface zwlr_foreign_toplevel_manager_v1_interface = {
72 | "zwlr_foreign_toplevel_manager_v1", 3,
73 | 1, zwlr_foreign_toplevel_manager_v1_requests,
74 | 2, zwlr_foreign_toplevel_manager_v1_events,
75 | };
76 |
77 | static const struct wl_message zwlr_foreign_toplevel_handle_v1_requests[] = {
78 | { "set_maximized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
79 | { "unset_maximized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
80 | { "set_minimized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
81 | { "unset_minimized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
82 | { "activate", "o", wlr_foreign_toplevel_management_unstable_v1_types + 2 },
83 | { "close", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
84 | { "set_rectangle", "oiiii", wlr_foreign_toplevel_management_unstable_v1_types + 3 },
85 | { "destroy", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
86 | { "set_fullscreen", "2?o", wlr_foreign_toplevel_management_unstable_v1_types + 8 },
87 | { "unset_fullscreen", "2", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
88 | };
89 |
90 | static const struct wl_message zwlr_foreign_toplevel_handle_v1_events[] = {
91 | { "title", "s", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
92 | { "app_id", "s", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
93 | { "output_enter", "o", wlr_foreign_toplevel_management_unstable_v1_types + 9 },
94 | { "output_leave", "o", wlr_foreign_toplevel_management_unstable_v1_types + 10 },
95 | { "state", "a", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
96 | { "done", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
97 | { "closed", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
98 | { "parent", "3?o", wlr_foreign_toplevel_management_unstable_v1_types + 11 },
99 | };
100 |
101 | WL_PRIVATE const struct wl_interface zwlr_foreign_toplevel_handle_v1_interface = {
102 | "zwlr_foreign_toplevel_handle_v1", 3,
103 | 10, zwlr_foreign_toplevel_handle_v1_requests,
104 | 8, zwlr_foreign_toplevel_handle_v1_events,
105 | };
106 |
107 |
--------------------------------------------------------------------------------
/src/wayland/protocols/wlr-layer-shell-unstable-v1.h:
--------------------------------------------------------------------------------
1 | /* Generated by wayland-scanner 1.19.0 */
2 |
3 | /*
4 | * Copyright © 2017 Drew DeVault
5 | *
6 | * Permission to use, copy, modify, distribute, and sell this
7 | * software and its documentation for any purpose is hereby granted
8 | * without fee, provided that the above copyright notice appear in
9 | * all copies and that both that copyright notice and this permission
10 | * notice appear in supporting documentation, and that the name of
11 | * the copyright holders not be used in advertising or publicity
12 | * pertaining to distribution of the software without specific,
13 | * written prior permission. The copyright holders make no
14 | * representations about the suitability of this software for any
15 | * purpose. It is provided "as is" without express or implied
16 | * warranty.
17 | *
18 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
19 | * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 | * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 | * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
22 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
23 | * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
24 | * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
25 | * THIS SOFTWARE.
26 | */
27 |
28 | #include
29 | #include
30 | #include "wayland-util.h"
31 |
32 | #ifndef __has_attribute
33 | # define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
34 | #endif
35 |
36 | #if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
37 | #define WL_PRIVATE __attribute__ ((visibility("hidden")))
38 | #else
39 | #define WL_PRIVATE
40 | #endif
41 |
42 | extern const struct wl_interface wl_output_interface;
43 | extern const struct wl_interface wl_surface_interface;
44 | extern const struct wl_interface xdg_popup_interface;
45 | extern const struct wl_interface zwlr_layer_surface_v1_interface;
46 |
47 | static const struct wl_interface *wlr_layer_shell_unstable_v1_types[] = {
48 | NULL,
49 | NULL,
50 | NULL,
51 | NULL,
52 | &zwlr_layer_surface_v1_interface,
53 | &wl_surface_interface,
54 | &wl_output_interface,
55 | NULL,
56 | NULL,
57 | &xdg_popup_interface,
58 | };
59 |
60 | static const struct wl_message zwlr_layer_shell_v1_requests[] = {
61 | { "get_layer_surface", "no?ous", wlr_layer_shell_unstable_v1_types + 4 },
62 | };
63 |
64 | WL_PRIVATE const struct wl_interface zwlr_layer_shell_v1_interface = {
65 | "zwlr_layer_shell_v1", 1,
66 | 1, zwlr_layer_shell_v1_requests,
67 | 0, NULL,
68 | };
69 |
70 | static const struct wl_message zwlr_layer_surface_v1_requests[] = {
71 | { "set_size", "uu", wlr_layer_shell_unstable_v1_types + 0 },
72 | { "set_anchor", "u", wlr_layer_shell_unstable_v1_types + 0 },
73 | { "set_exclusive_zone", "i", wlr_layer_shell_unstable_v1_types + 0 },
74 | { "set_margin", "iiii", wlr_layer_shell_unstable_v1_types + 0 },
75 | { "set_keyboard_interactivity", "u", wlr_layer_shell_unstable_v1_types + 0 },
76 | { "get_popup", "o", wlr_layer_shell_unstable_v1_types + 9 },
77 | { "ack_configure", "u", wlr_layer_shell_unstable_v1_types + 0 },
78 | { "destroy", "", wlr_layer_shell_unstable_v1_types + 0 },
79 | };
80 |
81 | static const struct wl_message zwlr_layer_surface_v1_events[] = {
82 | { "configure", "uuu", wlr_layer_shell_unstable_v1_types + 0 },
83 | { "closed", "", wlr_layer_shell_unstable_v1_types + 0 },
84 | };
85 |
86 | WL_PRIVATE const struct wl_interface zwlr_layer_surface_v1_interface = {
87 | "zwlr_layer_surface_v1", 1,
88 | 8, zwlr_layer_surface_v1_requests,
89 | 2, zwlr_layer_surface_v1_events,
90 | };
91 |
92 |
--------------------------------------------------------------------------------
/src/wayland/protocols/xdg-output-unstable-v1.h:
--------------------------------------------------------------------------------
1 | /* Generated by wayland-scanner 1.19.0 */
2 |
3 | /*
4 | * Copyright © 2017 Red Hat Inc.
5 | *
6 | * Permission is hereby granted, free of charge, to any person obtaining a
7 | * copy of this software and associated documentation files (the "Software"),
8 | * to deal in the Software without restriction, including without limitation
9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 | * and/or sell copies of the Software, and to permit persons to whom the
11 | * Software is furnished to do so, subject to the following conditions:
12 | *
13 | * The above copyright notice and this permission notice (including the next
14 | * paragraph) shall be included in all copies or substantial portions of the
15 | * Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 | * DEALINGS IN THE SOFTWARE.
24 | */
25 |
26 | #include
27 | #include
28 | #include "wayland-util.h"
29 |
30 | #ifndef __has_attribute
31 | # define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
32 | #endif
33 |
34 | #if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
35 | #define WL_PRIVATE __attribute__ ((visibility("hidden")))
36 | #else
37 | #define WL_PRIVATE
38 | #endif
39 |
40 | extern const struct wl_interface wl_output_interface;
41 | extern const struct wl_interface zxdg_output_v1_interface;
42 |
43 | static const struct wl_interface *xdg_output_unstable_v1_types[] = {
44 | NULL,
45 | NULL,
46 | &zxdg_output_v1_interface,
47 | &wl_output_interface,
48 | };
49 |
50 | static const struct wl_message zxdg_output_manager_v1_requests[] = {
51 | { "destroy", "", xdg_output_unstable_v1_types + 0 },
52 | { "get_xdg_output", "no", xdg_output_unstable_v1_types + 2 },
53 | };
54 |
55 | WL_PRIVATE const struct wl_interface zxdg_output_manager_v1_interface = {
56 | "zxdg_output_manager_v1", 3,
57 | 2, zxdg_output_manager_v1_requests,
58 | 0, NULL,
59 | };
60 |
61 | static const struct wl_message zxdg_output_v1_requests[] = {
62 | { "destroy", "", xdg_output_unstable_v1_types + 0 },
63 | };
64 |
65 | static const struct wl_message zxdg_output_v1_events[] = {
66 | { "logical_position", "ii", xdg_output_unstable_v1_types + 0 },
67 | { "logical_size", "ii", xdg_output_unstable_v1_types + 0 },
68 | { "done", "", xdg_output_unstable_v1_types + 0 },
69 | { "name", "2s", xdg_output_unstable_v1_types + 0 },
70 | { "description", "2s", xdg_output_unstable_v1_types + 0 },
71 | };
72 |
73 | WL_PRIVATE const struct wl_interface zxdg_output_v1_interface = {
74 | "zxdg_output_v1", 3,
75 | 1, zxdg_output_v1_requests,
76 | 5, zxdg_output_v1_events,
77 | };
78 |
79 |
--------------------------------------------------------------------------------
/src/wayland/protocols/xdg-shell.h:
--------------------------------------------------------------------------------
1 | /* Generated by wayland-scanner 1.19.0 */
2 |
3 | /*
4 | * Copyright © 2008-2013 Kristian Høgsberg
5 | * Copyright © 2013 Rafael Antognolli
6 | * Copyright © 2013 Jasper St. Pierre
7 | * Copyright © 2010-2013 Intel Corporation
8 | * Copyright © 2015-2017 Samsung Electronics Co., Ltd
9 | * Copyright © 2015-2017 Red Hat Inc.
10 | *
11 | * Permission is hereby granted, free of charge, to any person obtaining a
12 | * copy of this software and associated documentation files (the "Software"),
13 | * to deal in the Software without restriction, including without limitation
14 | * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 | * and/or sell copies of the Software, and to permit persons to whom the
16 | * Software is furnished to do so, subject to the following conditions:
17 | *
18 | * The above copyright notice and this permission notice (including the next
19 | * paragraph) shall be included in all copies or substantial portions of the
20 | * Software.
21 | *
22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 | * DEALINGS IN THE SOFTWARE.
29 | */
30 |
31 | #include
32 | #include
33 | #include "wayland-util.h"
34 |
35 | #ifndef __has_attribute
36 | # define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
37 | #endif
38 |
39 | #if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
40 | #define WL_PRIVATE __attribute__ ((visibility("hidden")))
41 | #else
42 | #define WL_PRIVATE
43 | #endif
44 |
45 | extern const struct wl_interface wl_output_interface;
46 | extern const struct wl_interface wl_seat_interface;
47 | extern const struct wl_interface wl_surface_interface;
48 | extern const struct wl_interface xdg_popup_interface;
49 | extern const struct wl_interface xdg_positioner_interface;
50 | extern const struct wl_interface xdg_surface_interface;
51 | extern const struct wl_interface xdg_toplevel_interface;
52 |
53 | static const struct wl_interface *xdg_shell_types[] = {
54 | NULL,
55 | NULL,
56 | NULL,
57 | NULL,
58 | &xdg_positioner_interface,
59 | &xdg_surface_interface,
60 | &wl_surface_interface,
61 | &xdg_toplevel_interface,
62 | &xdg_popup_interface,
63 | &xdg_surface_interface,
64 | &xdg_positioner_interface,
65 | &xdg_toplevel_interface,
66 | &wl_seat_interface,
67 | NULL,
68 | NULL,
69 | NULL,
70 | &wl_seat_interface,
71 | NULL,
72 | &wl_seat_interface,
73 | NULL,
74 | NULL,
75 | &wl_output_interface,
76 | &wl_seat_interface,
77 | NULL,
78 | &xdg_positioner_interface,
79 | NULL,
80 | };
81 |
82 | static const struct wl_message xdg_wm_base_requests[] = {
83 | { "destroy", "", xdg_shell_types + 0 },
84 | { "create_positioner", "n", xdg_shell_types + 4 },
85 | { "get_xdg_surface", "no", xdg_shell_types + 5 },
86 | { "pong", "u", xdg_shell_types + 0 },
87 | };
88 |
89 | static const struct wl_message xdg_wm_base_events[] = {
90 | { "ping", "u", xdg_shell_types + 0 },
91 | };
92 |
93 | WL_PRIVATE const struct wl_interface xdg_wm_base_interface = {
94 | "xdg_wm_base", 3,
95 | 4, xdg_wm_base_requests,
96 | 1, xdg_wm_base_events,
97 | };
98 |
99 | static const struct wl_message xdg_positioner_requests[] = {
100 | { "destroy", "", xdg_shell_types + 0 },
101 | { "set_size", "ii", xdg_shell_types + 0 },
102 | { "set_anchor_rect", "iiii", xdg_shell_types + 0 },
103 | { "set_anchor", "u", xdg_shell_types + 0 },
104 | { "set_gravity", "u", xdg_shell_types + 0 },
105 | { "set_constraint_adjustment", "u", xdg_shell_types + 0 },
106 | { "set_offset", "ii", xdg_shell_types + 0 },
107 | { "set_reactive", "3", xdg_shell_types + 0 },
108 | { "set_parent_size", "3ii", xdg_shell_types + 0 },
109 | { "set_parent_configure", "3u", xdg_shell_types + 0 },
110 | };
111 |
112 | WL_PRIVATE const struct wl_interface xdg_positioner_interface = {
113 | "xdg_positioner", 3,
114 | 10, xdg_positioner_requests,
115 | 0, NULL,
116 | };
117 |
118 | static const struct wl_message xdg_surface_requests[] = {
119 | { "destroy", "", xdg_shell_types + 0 },
120 | { "get_toplevel", "n", xdg_shell_types + 7 },
121 | { "get_popup", "n?oo", xdg_shell_types + 8 },
122 | { "set_window_geometry", "iiii", xdg_shell_types + 0 },
123 | { "ack_configure", "u", xdg_shell_types + 0 },
124 | };
125 |
126 | static const struct wl_message xdg_surface_events[] = {
127 | { "configure", "u", xdg_shell_types + 0 },
128 | };
129 |
130 | WL_PRIVATE const struct wl_interface xdg_surface_interface = {
131 | "xdg_surface", 3,
132 | 5, xdg_surface_requests,
133 | 1, xdg_surface_events,
134 | };
135 |
136 | static const struct wl_message xdg_toplevel_requests[] = {
137 | { "destroy", "", xdg_shell_types + 0 },
138 | { "set_parent", "?o", xdg_shell_types + 11 },
139 | { "set_title", "s", xdg_shell_types + 0 },
140 | { "set_app_id", "s", xdg_shell_types + 0 },
141 | { "show_window_menu", "ouii", xdg_shell_types + 12 },
142 | { "move", "ou", xdg_shell_types + 16 },
143 | { "resize", "ouu", xdg_shell_types + 18 },
144 | { "set_max_size", "ii", xdg_shell_types + 0 },
145 | { "set_min_size", "ii", xdg_shell_types + 0 },
146 | { "set_maximized", "", xdg_shell_types + 0 },
147 | { "unset_maximized", "", xdg_shell_types + 0 },
148 | { "set_fullscreen", "?o", xdg_shell_types + 21 },
149 | { "unset_fullscreen", "", xdg_shell_types + 0 },
150 | { "set_minimized", "", xdg_shell_types + 0 },
151 | };
152 |
153 | static const struct wl_message xdg_toplevel_events[] = {
154 | { "configure", "iia", xdg_shell_types + 0 },
155 | { "close", "", xdg_shell_types + 0 },
156 | };
157 |
158 | WL_PRIVATE const struct wl_interface xdg_toplevel_interface = {
159 | "xdg_toplevel", 3,
160 | 14, xdg_toplevel_requests,
161 | 2, xdg_toplevel_events,
162 | };
163 |
164 | static const struct wl_message xdg_popup_requests[] = {
165 | { "destroy", "", xdg_shell_types + 0 },
166 | { "grab", "ou", xdg_shell_types + 22 },
167 | { "reposition", "3ou", xdg_shell_types + 24 },
168 | };
169 |
170 | static const struct wl_message xdg_popup_events[] = {
171 | { "configure", "iiii", xdg_shell_types + 0 },
172 | { "popup_done", "", xdg_shell_types + 0 },
173 | { "repositioned", "3u", xdg_shell_types + 0 },
174 | };
175 |
176 | WL_PRIVATE const struct wl_interface xdg_popup_interface = {
177 | "xdg_popup", 3,
178 | 3, xdg_popup_requests,
179 | 3, xdg_popup_events,
180 | };
181 |
182 |
--------------------------------------------------------------------------------
/src/wayland/wl.h:
--------------------------------------------------------------------------------
1 | #ifndef DUNST_WL_H
2 | #define DUNST_WL_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | #include "../output.h"
9 |
10 | bool wl_init(void);
11 | void wl_deinit(void);
12 |
13 | window wl_win_create(void);
14 | void wl_win_destroy(window);
15 |
16 | void wl_win_show(window);
17 | void wl_win_hide(window);
18 |
19 | void wl_display_surface(cairo_surface_t *srf, window win, const struct dimensions*);
20 | cairo_t* wl_win_get_context(window);
21 |
22 | const struct screen_info* wl_get_active_screen(void);
23 |
24 | bool wl_is_idle(void);
25 | bool wl_have_fullscreen_window(void);
26 |
27 | // Return the dpi scaling of the current output. Everything that's rendered
28 | // should be multiplied by this value, but don't use it to multiply other
29 | // values. All sizes should be in unscaled units.
30 | double wl_get_scale(void);
31 | #endif
32 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
33 |
--------------------------------------------------------------------------------
/src/wayland/wl_output.c:
--------------------------------------------------------------------------------
1 | #include "wl_output.h"
2 |
3 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
4 |
--------------------------------------------------------------------------------
/src/wayland/wl_output.h:
--------------------------------------------------------------------------------
1 | #ifndef DUNST_WL_OUTPUT_H
2 | #define DUNST_WL_OUTPUT_H
3 | #include
4 | #include
5 | #include
6 |
7 | struct dunst_output {
8 | uint32_t global_name;
9 | char *name;
10 | struct wl_output *wl_output;
11 | struct wl_list link;
12 |
13 | uint32_t scale;
14 | uint32_t subpixel; // TODO do something with it
15 | int32_t width, height;
16 | bool fullscreen;
17 | struct zwlr_foreign_toplevel_handle_v1 *fullscreen_toplevel; // the toplevel that is fullscreened on this output
18 | };
19 |
20 | #endif
21 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
22 |
--------------------------------------------------------------------------------
/src/x11/screen.h:
--------------------------------------------------------------------------------
1 | /* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
2 | #ifndef DUNST_SCREEN_H
3 | #define DUNST_SCREEN_H
4 |
5 | #include
6 | #include
7 |
8 | #define INRECT(x,y,rx,ry,rw,rh) ((x) >= (rx) && (x) < (rx)+(rw) && (y) >= (ry) && (y) < (ry)+(rh))
9 |
10 | void init_screens(void);
11 | void screen_dpi_xft_cache_purge(void);
12 | bool screen_check_event(XEvent *ev);
13 |
14 | const struct screen_info *get_active_screen(void);
15 | double screen_dpi_get(const struct screen_info *scr);
16 |
17 | /**
18 | * Find the currently focused window and check if it's in
19 | * fullscreen mode
20 | *
21 | * @see window_is_fullscreen()
22 | * @see get_focused_window()
23 | *
24 | * @retval true: the focused window is in fullscreen mode
25 | * @retval false: otherwise
26 | */
27 | bool have_fullscreen_window(void);
28 |
29 | /**
30 | * Check if window is in fullscreen mode
31 | *
32 | * @param window the x11 window object
33 | * @retval true: \p window is in fullscreen mode
34 | * @retval false: otherwise
35 | */
36 | bool window_is_fullscreen(Window window);
37 |
38 | #endif
39 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
40 |
--------------------------------------------------------------------------------
/src/x11/x.h:
--------------------------------------------------------------------------------
1 | /* copyright 2013 Sascha Kruse and contributors (see LICENSE for licensing information) */
2 | #ifndef DUNST_X_H
3 | #define DUNST_X_H
4 |
5 | #define XLIB_ILLEGAL_ACCESS
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #include "../output.h"
15 |
16 | #include "screen.h"
17 |
18 | struct keyboard_shortcut {
19 | const char *str;
20 | KeyCode code;
21 | KeySym sym;
22 | KeySym mask;
23 | bool is_valid;
24 | };
25 |
26 | // Cyclical dependency
27 | #include "../settings.h"
28 |
29 | struct x_context {
30 | Display *dpy;
31 | XScreenSaverInfo *screensaver_info;
32 | };
33 |
34 | extern struct x_context xctx;
35 |
36 | /* window */
37 | window x_win_create(void);
38 | void x_win_destroy(window);
39 |
40 | void x_win_show(window);
41 | void x_win_hide(window);
42 |
43 | void x_display_surface(cairo_surface_t *srf, window, const struct dimensions *dim);
44 |
45 | cairo_t* x_win_get_context(window);
46 |
47 | /* X misc */
48 | bool x_is_idle(void);
49 | bool x_setup(void);
50 | void x_free(void);
51 |
52 | void loadxrdb(void);
53 |
54 | double x_get_scale(void);
55 | #endif
56 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
57 |
--------------------------------------------------------------------------------
/test/data/dunstrc.default:
--------------------------------------------------------------------------------
1 | ../../dunstrc
--------------------------------------------------------------------------------
/test/data/dunstrc.markup:
--------------------------------------------------------------------------------
1 | ../functional-tests/dunstrc.markup
--------------------------------------------------------------------------------
/test/data/dunstrc.nomarkup:
--------------------------------------------------------------------------------
1 | ../functional-tests/dunstrc.nomarkup
--------------------------------------------------------------------------------
/test/data/dunstrc.show_age:
--------------------------------------------------------------------------------
1 | ../functional-tests/dunstrc.show_age
--------------------------------------------------------------------------------
/test/data/icons/theme/16x16/actions/edit.png:
--------------------------------------------------------------------------------
1 | ../../../valid.png
--------------------------------------------------------------------------------
/test/data/icons/theme/16x16/apps/preferences.png:
--------------------------------------------------------------------------------
1 | ../../../valid.png
--------------------------------------------------------------------------------
/test/data/icons/theme/16x16@2x/actions/edit.png:
--------------------------------------------------------------------------------
1 | ../../../valid.png
--------------------------------------------------------------------------------
/test/data/icons/theme/16x16@2x/apps/preferences.png:
--------------------------------------------------------------------------------
1 | ../../../valid.png
--------------------------------------------------------------------------------
/test/data/icons/theme/32x32/actions/edit.png:
--------------------------------------------------------------------------------
1 | ../../../valid.png
--------------------------------------------------------------------------------
/test/data/icons/theme/32x32/apps/preferences.png:
--------------------------------------------------------------------------------
1 | ../../../valid.png
--------------------------------------------------------------------------------
/test/data/icons/theme/32x32@2x/actions/edit.png:
--------------------------------------------------------------------------------
1 | ../../../valid.png
--------------------------------------------------------------------------------
/test/data/icons/theme/32x32@2x/apps/preferences.png:
--------------------------------------------------------------------------------
1 | ../../../valid.png
--------------------------------------------------------------------------------
/test/data/icons/theme/index.theme:
--------------------------------------------------------------------------------
1 | [Icon Theme]
2 | Name=theme
3 | Comment=Test theme for dunst
4 | Example=folder
5 |
6 | X-ignore-this=something
7 | # ignore this
8 |
9 | # Directory list
10 | Directories=16x16/actions,16x16/apps,16x16@2x/actions,16x16@2x/apps,32x32/actions,32x32/apps,32x32@2x/actions,32x32@2x/apps
11 |
12 | [16x16/actions]
13 | Size=16
14 | Type=Threshold
15 | Threshold=3
16 |
17 | [16x16/apps]
18 | Size=16
19 | Type=Threshold
20 |
21 | [16x16@2x/actions]
22 | Size=16
23 | Scale=2
24 | Type=Scalable
25 | MinSize=16
26 | MaxSize=32
27 |
28 | [16x16@2x/apps]
29 | Size=16
30 | Scale=2
31 | Type=Fixed
32 |
33 | [32x32/actions]
34 | Size=32
35 | Type=Scalable
36 | MinSize=32
37 | MaxSize=64
38 |
39 | [32x32/apps]
40 | Size=32
41 | Type=Fixed
42 |
43 | [32x32@2x/actions]
44 | Size=32
45 | Scale=2
46 | Type=Fixed
47 |
48 | [32x32@2x/apps]
49 | Size=32
50 | Scale=2
51 | Type=Fixed
52 |
--------------------------------------------------------------------------------
/test/data/icons/valid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FT-Labs/phyOS-dunst/aeb275a998778b05cee495efb9b61db66cdc0a7e/test/data/icons/valid.png
--------------------------------------------------------------------------------
/test/data/icons/valid.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
66 |
--------------------------------------------------------------------------------
/test/data/test-ini:
--------------------------------------------------------------------------------
1 | #General comment
2 | [bool]
3 | booltrue = true #This is a test inline comment
4 | booltrue_capital = TRUE
5 |
6 | #This is a comment
7 | boolfalse = false
8 | boolfalse_capital = FALSE
9 |
10 | boolyes = yes
11 | boolyes_capital = YES
12 |
13 | boolno = no
14 | boolno_capital = NO
15 |
16 | boolbin0 = 0
17 | boolbin1 = 1
18 |
19 | boolinvalid = invalidbool
20 |
21 | [string]
22 | simple = A simple string
23 | quoted = "A quoted string"
24 | quoted_with_quotes = "A string "with quotes""
25 | unquoted_with_quotes = A" string with quotes"
26 | quoted_comment = "String with a" # comment
27 | unquoted_comment = String with a # comment
28 | color_comment = "#ffffff" # comment
29 |
30 | [list]
31 | simple = A,simple,list
32 | spaces = A, list, with, spaces
33 | multiword = A list, with, multiword entries
34 | quoted = "A, quoted, list"
35 | quoted_with_quotes = "A, list, "with quotes""
36 | unquoted_with_quotes = A, list, "with quotes"
37 | quoted_comment = "List, with, a" # comment
38 | unquoted_comment = List, with, a # comment
39 |
40 | [path]
41 | expand_tilde = ~/.path/to/tilde
42 |
43 | [int]
44 | simple = 5
45 | negative = -10
46 | decimal = 2.71828
47 | leading_zeroes = 007
48 | multi_char = 1024
49 |
50 | [double]
51 | simple = 1
52 | decimal = 1.5
53 | negative = -1.2
54 | zeroes = 0.005
55 | long = 3.141592653589793
56 |
--------------------------------------------------------------------------------
/test/dunst.c:
--------------------------------------------------------------------------------
1 | #include "../src/dunst.c"
2 | #include "greatest.h"
3 |
4 | TEST test_dunst_status(void)
5 | {
6 | status = (struct dunst_status) {false, false, false};
7 |
8 | dunst_status(S_FULLSCREEN, true);
9 | ASSERT(status.fullscreen);
10 | dunst_status(S_IDLE, true);
11 | ASSERT(status.idle);
12 | dunst_status(S_RUNNING, true);
13 | ASSERT(status.running);
14 |
15 | PASS();
16 | }
17 |
18 | SUITE(suite_dunst)
19 | {
20 | RUN_TEST(test_dunst_status);
21 | }
22 |
23 | /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
24 |
--------------------------------------------------------------------------------
/test/functional-tests/dunstrc.default:
--------------------------------------------------------------------------------
1 | [urgency_low]
2 | background = "#222222"
3 | foreground = "#888888"
4 | timeout = 10
5 |
6 | [urgency_normal]
7 | background = "#285577"
8 | foreground = "#ffffff"
9 | timeout = 10
10 |
11 | [urgency_critical]
12 | background = "#900000"
13 | foreground = "#ffffff"
14 | timeout = 0
15 |
16 | [global]
17 | # can be appended by script
18 |
--------------------------------------------------------------------------------
/test/functional-tests/dunstrc.gaps:
--------------------------------------------------------------------------------
1 | [urgency_low]
2 | background = "#222222"
3 | foreground = "#888888"
4 | timeout = 10
5 |
6 | [urgency_normal]
7 | background = "#285577"
8 | foreground = "#ffffff"
9 | timeout = 10
10 |
11 | [urgency_critical]
12 | background = "#900000"
13 | foreground = "#ffffff"
14 | timeout = 0
15 |
16 | [global]
17 | gap_size = 35
18 | frame_width = 15
19 |
20 | mouse_left_click = do_action
21 | mouse_middle_click = close_current
22 | mouse_right_click = context
23 |
--------------------------------------------------------------------------------
/test/functional-tests/dunstrc.hide_text:
--------------------------------------------------------------------------------
1 | [urgency_low]
2 | background = "#222222"
3 | foreground = "#888888"
4 | timeout = 10
5 | icon_position = top
6 | hide_text = yes
7 |
8 | [urgency_normal]
9 | background = "#285577"
10 | foreground = "#ffffff"
11 | timeout = 10
12 | icon_position = top
13 | hide_text = yes
14 |
15 | [urgency_critical]
16 | background = "#900000"
17 | foreground = "#ffffff"
18 | timeout = 0
19 |
20 | [global]
21 | font = Monospace 8
22 | markup = full
23 | format = "%s\n%b"
24 | progress_bar_min_width = 100
25 | progress_bar_max_width = 200
26 | progress_bar_frame_width = 5
27 | progress_bar_height = 30
28 | icon_path = /usr/share/icons/Papirus/24x24/status/:/usr/share/icons/Papirus/24x24/devices/:/usr/share/icons/gnome/16x16/status/:/usr/share/icons/gnome/16x16/devices/
--------------------------------------------------------------------------------
/test/functional-tests/dunstrc.icon_position:
--------------------------------------------------------------------------------
1 | [urgency_low]
2 | background = "#222222"
3 | foreground = "#888888"
4 | timeout = 10
5 |
6 | [urgency_normal]
7 | background = "#285577"
8 | foreground = "#ffffff"
9 | timeout = 0
10 |
11 | [urgency_critical]
12 | background = "#900000"
13 | foreground = "#ffffff"
14 | timeout = 0
15 |
16 | [icon-left-alignment-left]
17 | category = "icon-left-alignment-left"
18 | icon_position = left
19 | alignment = left
20 |
21 | [icon-left-alignment-right]
22 | category = "icon-left-alignment-right"
23 | icon_position = left
24 | alignment = right
25 |
26 | [icon-left-alignment-center]
27 | category = "icon-left-alignment-center"
28 | icon_position = left
29 | alignment = center
30 |
31 | [icon-right-alignment-left]
32 | category = "icon-right-alignment-left"
33 | icon_position = right
34 | alignment = left
35 |
36 | [icon-right-alignment-right]
37 | category = "icon-right-alignment-right"
38 | icon_position = right
39 | alignment = right
40 |
41 | [icon-right-alignment-center]
42 | category = "icon-right-alignment-center"
43 | icon_position = right
44 | alignment = center
45 |
46 | [icon-top-alignment-left]
47 | category = "icon-top-alignment-left"
48 | icon_position = top
49 | alignment = left
50 |
51 | [icon-top-alignment-right]
52 | category = "icon-top-alignment-right"
53 | icon_position = top
54 | alignment = right
55 |
56 | [icon-top-alignment-center]
57 | category = "icon-top-alignment-center"
58 | icon_position = top
59 | alignment = center
60 |
61 | [icon-off-alignment-left]
62 | category = "icon-off-alignment-left"
63 | icon_position = off
64 | alignment = left
65 |
66 | [icon-off-alignment-right]
67 | category = "icon-off-alignment-right"
68 | icon_position = off
69 | alignment = right
70 |
71 | [icon-off-alignment-center]
72 | category = "icon-off-alignment-center"
73 | icon_position = off
74 | alignment = center
75 |
76 | [global]
77 | font = Monospace 8
78 | markup = full
79 | format = "%s\n%b"
80 | progress_bar_min_width = 100
81 | progress_bar_max_width = 200
82 | progress_bar_frame_width = 5
83 | progress_bar_height = 30
84 | width = (500,750)
85 | icon_path = /usr/share/icons/Papirus/24x24/status/:/usr/share/icons/Papirus/24x24/devices/:/usr/share/icons/gnome/16x16/status/:/usr/share/icons/gnome/16x16/devices/
--------------------------------------------------------------------------------
/test/functional-tests/dunstrc.markup:
--------------------------------------------------------------------------------
1 | [urgency_low]
2 | background = "#222222"
3 | foreground = "#888888"
4 | timeout = 10
5 |
6 | [urgency_normal]
7 | background = "#285577"
8 | foreground = "#ffffff"
9 | timeout = 10
10 |
11 | [urgency_critical]
12 | background = "#900000"
13 | foreground = "#ffffff"
14 | timeout = 0
15 |
--------------------------------------------------------------------------------
/test/functional-tests/dunstrc.nomarkup:
--------------------------------------------------------------------------------
1 | [global]
2 | markup = no
3 | format = "%s\n%b"
4 |
5 | [urgency_low]
6 | background = "#222222"
7 | foreground = "#888888"
8 | timeout = 10
9 |
10 | [urgency_normal]
11 | background = "#285577"
12 | foreground = "#ffffff"
13 | timeout = 10
14 |
15 | [urgency_critical]
16 | background = "#900000"
17 | foreground = "#ffffff"
18 | timeout = 0
19 |
--------------------------------------------------------------------------------
/test/functional-tests/dunstrc.nowrap:
--------------------------------------------------------------------------------
1 | [global]
2 | font = Monospace-10
3 | allow_markup = yes
4 | format = "%s\n%b"
5 | sort = yes
6 | indicate_hidden = yes
7 | alignment = left
8 | show_age_threshold = 60
9 | ignore_newline = no
10 | geometry = "300x5-30+20"
11 | transparency = 0
12 | idle_threshold = 120
13 | monitor = 0
14 | follow = mouse
15 | sticky_history = yes
16 | line_height = 0
17 | separator_height = 2;
18 | padding = 8
19 | horizontal_padding = 8
20 | separator_color = frame
21 | startup_notification = false
22 | dmenu = /usr/bin/dmenu -p dunst:
23 | browser = /usr/bin/firefox -new-tab
24 |
25 | [frame]
26 | width = 3
27 | color = "#aaaaaa"
28 |
29 | [shortcuts]
30 | close = ctrl+space
31 | close_all = ctrl+shift+space
32 | history = ctrl+grave
33 | context = ctrl+shift+period
34 |
35 | [urgency_low]
36 | background = "#222222"
37 | foreground = "#888888"
38 | timeout = 10
39 |
40 | [urgency_normal]
41 | background = "#285577"
42 | foreground = "#ffffff"
43 | timeout = 10
44 |
45 | [urgency_critical]
46 | background = "#900000"
47 | foreground = "#ffffff"
48 | timeout = 0
49 |
--------------------------------------------------------------------------------
/test/functional-tests/dunstrc.progress_bar:
--------------------------------------------------------------------------------
1 | [urgency_low]
2 | background = "#222222"
3 | foreground = "#888888"
4 | timeout = 10
5 |
6 | [urgency_normal]
7 | background = "#285577"
8 | foreground = "#ffffff"
9 | timeout = 10
10 |
11 | [urgency_critical]
12 | background = "#900000"
13 | foreground = "#ffffff"
14 | timeout = 0
15 |
16 | [global]
17 | font = Monospace 8
18 | allow_markup = yes
19 | format = "%s\n%b"
20 | geometry = "0x5-30+20"
21 | icon_position = left
22 | progress_bar_min_width = 100
23 | progress_bar_max_width = 200
24 | progress_bar_frame_width = 5
25 | progress_bar_height = 30
26 |
--------------------------------------------------------------------------------
/test/functional-tests/dunstrc.run_script:
--------------------------------------------------------------------------------
1 | [urgency_low]
2 | background = "#222222"
3 | foreground = "#888888"
4 | timeout = 10
5 |
6 | [urgency_normal]
7 | background = "#285577"
8 | foreground = "#ffffff"
9 | timeout = 10
10 |
11 | [urgency_critical]
12 | background = "#900000"
13 | foreground = "#ffffff"
14 | timeout = 0
15 |
16 | [script test]
17 | summary = trigger
18 | script = script_test.sh
19 |
--------------------------------------------------------------------------------
/test/functional-tests/dunstrc.separator_click:
--------------------------------------------------------------------------------
1 | [urgency_low]
2 | background = "#222222"
3 | foreground = "#888888"
4 | timeout = 10
5 |
6 | [urgency_normal]
7 | background = "#285577"
8 | foreground = "#ffffff"
9 | timeout = 10
10 |
11 | [urgency_critical]
12 | background = "#900000"
13 | foreground = "#ffffff"
14 | timeout = 0
15 |
16 | [global]
17 | frame_width = 5
18 | separator_height = 50
19 | separator_color = "#000000"
20 |
21 | mouse_left_click = do_action
22 | mouse_middle_click = close_current
23 | mouse_right_click = context
24 |
--------------------------------------------------------------------------------
/test/functional-tests/dunstrc.show_age:
--------------------------------------------------------------------------------
1 | [global]
2 | font = Monospace 8
3 | allow_markup = yes
4 | format = "%s\n%b"
5 | sort = yes
6 | indicate_hidden = yes
7 | alignment = left
8 | show_age_threshold = 2
9 | ignore_newline = no
10 | geometry = "300x5-30+20"
11 | transparency = 0
12 | idle_threshold = 120
13 | monitor = 0
14 | follow = mouse
15 | sticky_history = yes
16 | line_height = 0
17 | separator_height = 2
18 | padding = 8
19 | horizontal_padding = 8
20 | separator_color = frame
21 | startup_notification = false
22 | dmenu = /usr/bin/dmenu -p dunst
23 | browser = /usr/bin/firefox -new-tab
24 |
25 | [frame]
26 | width = 3
27 | color = "#aaaaaa"
28 |
29 | [shortcuts]
30 | close = ctrl+space
31 | close_all = ctrl+shift+space
32 | history = ctrl+grave
33 | context = ctrl+shift+period
34 |
35 | [urgency_low]
36 | background = "#222222"
37 | foreground = "#888888"
38 | timeout = 10
39 |
40 | [urgency_normal]
41 | background = "#285577"
42 | foreground = "#ffffff"
43 | timeout = 10
44 |
45 | [urgency_critical]
46 | background = "#900000"
47 | foreground = "#ffffff"
48 | timeout = 0
49 |
--------------------------------------------------------------------------------
/test/functional-tests/script_test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ../../dunstify "Success" "ooooh yeah"
4 |
--------------------------------------------------------------------------------
/test/greenest.awk:
--------------------------------------------------------------------------------
1 | #!/usr/bin/awk -f
2 | # Copyright (c) 2016 Scott Vokes
3 | #
4 | # Permission to use, copy, modify, and/or distribute this software for any
5 | # purpose with or without fee is hereby granted, provided that the above
6 | # copyright notice and this permission notice appear in all copies.
7 | #
8 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 |
16 | BEGIN {
17 | GREEN = "\033[32m"
18 | RED = "\033[31m"
19 | YELLOW = "\033[33m"
20 | RESET = "\033[m"
21 | }
22 |
23 | /^PASS/ { sub("PASS", GREEN "PASS" RESET) }
24 | /^SKIP/ { sub("SKIP", YELLOW "SKIP" RESET) }
25 | /^FAIL/ { sub("FAIL", RED "FAIL" RESET) }
26 |
27 | # highlight hexdump difference markers
28 | /^[0-9a-f]/ {
29 | sub("X", GREEN "X" RESET, $2)
30 | gsub("<", GREEN "<" RESET, $0)
31 | }
32 |
33 | { print($0) }
34 |
--------------------------------------------------------------------------------
/test/helpers.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include "helpers.h"
4 | #include "../src/notification.h"
5 | #include "../src/utils.h"
6 |
7 | GVariant *notification_setup_raw_image(const char *path)
8 | {
9 | GdkPixbuf *pb = gdk_pixbuf_new_from_file(path, NULL);
10 |
11 | if (!pb)
12 | return NULL;
13 |
14 | GVariant *hint_data = g_variant_new_from_data(
15 | G_VARIANT_TYPE("ay"),
16 | gdk_pixbuf_read_pixels(pb),
17 | gdk_pixbuf_get_byte_length(pb),
18 | TRUE,
19 | (GDestroyNotify) g_object_unref,
20 | g_object_ref(pb));
21 |
22 | GVariant *hint = g_variant_new(
23 | "(iiibii@ay)",
24 | gdk_pixbuf_get_width(pb),
25 | gdk_pixbuf_get_height(pb),
26 | gdk_pixbuf_get_rowstride(pb),
27 | gdk_pixbuf_get_has_alpha(pb),
28 | gdk_pixbuf_get_bits_per_sample(pb),
29 | gdk_pixbuf_get_n_channels(pb),
30 | hint_data);
31 |
32 | g_object_unref(pb);
33 |
34 | return hint;
35 | }
36 |
37 | struct notification *test_notification_uninitialized(const char *name)
38 | {
39 | struct notification *n = notification_create();
40 |
41 | n->dbus_client = g_strconcat(":", name, NULL);
42 | n->appname = g_strconcat("app of ", name, NULL);
43 | n->summary = g_strconcat(name, NULL);
44 | n->body = g_strconcat("See, ", name, ", I've got a body for you!", NULL);
45 |
46 | return n;
47 | }
48 |
49 | struct notification *test_notification(const char *name, gint64 timeout)
50 | {
51 | struct notification *n = test_notification_uninitialized(name);
52 |
53 | notification_init(n);
54 |
55 | n->format = "%s\n%b";
56 |
57 | if (timeout != -1)
58 | n->timeout = S2US(timeout);
59 |
60 | return n;
61 | }
62 |
63 | struct notification *test_notification_with_icon(const char *name, gint64 timeout)
64 | {
65 | struct notification *n = test_notification(name, timeout);
66 | n->icon = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
67 | return n;
68 | }
69 |
70 | GSList *get_dummy_notifications(int count)
71 | {
72 | GSList *notifications = NULL;
73 |
74 | int message_size = 24;
75 | for (int i = 0; i < count; i++) {
76 | char msg[message_size];
77 | snprintf(msg, message_size, "test %d", i);
78 | struct notification *n = test_notification(msg, 10);
79 | n->icon_position = ICON_LEFT;
80 | n->text_to_render = g_strdup("dummy layout");
81 | notifications = g_slist_append(notifications, n);
82 | }
83 | return notifications;
84 | }
85 |
86 | void free_dummy_notification(void *notification)
87 | {
88 | // wrapper function to work with g_slist_free_full
89 | notification_unref((struct notification *) notification);
90 | }
91 |
92 | /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
93 |
--------------------------------------------------------------------------------
/test/helpers.h:
--------------------------------------------------------------------------------
1 | #ifndef DUNST_TEST_HELPERS_H
2 | #define DUNST_TEST_HELPERS_H
3 |
4 | #include
5 |
6 | GVariant *notification_setup_raw_image(const char *path);
7 | struct notification *test_notification_uninitialized(const char *name);
8 | struct notification *test_notification(const char *name, gint64 timeout);
9 | struct notification *test_notification_with_icon(const char *name, gint64 timeout);
10 | GSList *get_dummy_notifications(int count);
11 | void free_dummy_notification(void *notification);
12 |
13 | #endif
14 | /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
15 |
--------------------------------------------------------------------------------
/test/icon-lookup.c:
--------------------------------------------------------------------------------
1 | #include "greatest.h"
2 | #include "../src/icon-lookup.c"
3 | #include "helpers.h"
4 | #include "../src/notification.h"
5 | #include "../src/settings_data.h"
6 |
7 | extern const char *base;
8 | #define ICONPREFIX "data", "icons"
9 |
10 | int setup_test_theme(){
11 | char *theme_path = g_build_filename(base, ICONPREFIX, NULL);
12 | int theme_index = load_icon_theme_from_dir(theme_path, "theme");
13 | add_default_theme(theme_index);
14 | g_free(theme_path);
15 | return theme_index;
16 | }
17 |
18 | #define find_icon_test(iconname, size, ...) { \
19 | char *icon = find_icon_path(iconname, size); \
20 | char *expected = g_build_filename(base, ICONPREFIX, "theme", __VA_ARGS__, NULL); \
21 | ASSERTm("Could not find icon", icon); \
22 | ASSERT_STR_EQ(expected, icon); \
23 | g_free(expected); \
24 | g_free(icon); \
25 | }
26 |
27 | #define find_path_test(path, ...) { \
28 | char *icon = find_icon_path(path, 42); /* size doesn't matter */ \
29 | char *expected = g_build_filename(base, ICONPREFIX, "theme", __VA_ARGS__, NULL); \
30 | ASSERTm("Could not find icon", icon); \
31 | ASSERT_STR_EQ(expected, icon); \
32 | g_free(expected); \
33 | g_free(icon); \
34 | }
35 |
36 | TEST test_load_theme_from_dir(void)
37 | {
38 | setup_test_theme();
39 | free_all_themes();
40 | PASS();
41 | }
42 |
43 | TEST test_find_icon(void)
44 | {
45 | setup_test_theme();
46 | find_icon_test("edit", 8, "16x16", "actions", "edit.png");
47 | find_icon_test("edit", 16, "16x16", "actions", "edit.png");
48 | find_icon_test("edit", 32, "16x16", "actions", "edit.png");
49 | find_icon_test("edit", 48, "16x16", "actions", "edit.png");
50 | find_icon_test("edit", 49, "32x32", "actions", "edit.png");
51 | find_icon_test("edit", 64, "32x32", "actions", "edit.png");
52 | find_icon_test("preferences", 16, "16x16", "apps", "preferences.png");
53 | find_icon_test("preferences", 32, "16x16", "apps", "preferences.png");
54 | free_all_themes();
55 | PASS();
56 | }
57 |
58 | TEST test_find_path(void)
59 | {
60 | setup_test_theme();
61 | char *path = g_build_filename(base, ICONPREFIX, "theme", "16x16", "actions", "edit.png", NULL);
62 | find_path_test(path, "16x16", "actions", "edit.png");
63 | char *path2 = g_strconcat("file://", path, NULL);
64 | find_path_test(path2, "16x16", "actions", "edit.png");
65 | g_free(path2);
66 | g_free(path);
67 | free_all_themes();
68 | PASS();
69 | }
70 |
71 |
72 | TEST test_new_icon_overrides_raw_icon(void) {
73 | setup_test_theme();
74 |
75 | struct notification *n = test_notification_with_icon("new_icon", 10);
76 | struct rule *rule = malloc(sizeof(struct rule));
77 | *rule = empty_rule;
78 | rule->summary = g_strdup("new_icon");
79 | rule->new_icon = g_strdup("edit");
80 |
81 | ASSERT(n->icon);
82 |
83 | int old_width = cairo_image_surface_get_width(n->icon);
84 | rule_apply(rule, n);
85 | ASSERT(old_width != cairo_image_surface_get_width(n->icon));
86 |
87 | cairo_surface_destroy(n->icon);
88 | n->icon = NULL;
89 |
90 | notification_unref(n);
91 | g_free(rule->summary);
92 | g_free(rule->new_icon);
93 | g_free(rule);
94 | free_all_themes();
95 | PASS();
96 | }
97 |
98 | // TODO move this out of the test suite, since this isn't a real test
99 | TEST test_bench_search(void)
100 | {
101 | printf("Starting benchmark\n");
102 | // At the time of committing, I get numbers like 0.25 second for 1000 icon lookups
103 | int index = load_icon_theme("Papirus");
104 | add_default_theme(index);
105 | printf("Benchmarking icons\n");
106 | clock_t start_time = clock();
107 | for (int i = 0; i < 1000; i++){
108 | // icon is part of papirus, right at the end of index.theme
109 | char *icon = find_icon_path("weather-windy-symbolic", 512);
110 | ASSERT(icon);
111 | g_free(icon);
112 | }
113 | double elapsed_time = (double)(clock() - start_time) / CLOCKS_PER_SEC;
114 | printf("Done in %f seconds\n", elapsed_time);
115 | free_all_themes();
116 | PASS();
117 | }
118 |
119 | TEST test_bench_multiple_search(void)
120 | {
121 | printf("Starting benchmark\n");
122 | // At the time of committing, I get numbers like 2 second for 1000 icon lookups
123 | int index = load_icon_theme("Adwaita");
124 | add_default_theme(index);
125 | index = load_icon_theme("Papirus");
126 | add_default_theme(index);
127 | printf("Benchmarking icons\n");
128 | clock_t start_time = clock();
129 | for (int i = 0; i < 1000; i++){
130 | // icon is part of papirus, right at the end of index.theme
131 | char *icon = find_icon_path("view-wrapped-rtl-symbolic", 512);
132 | /* printf("%s\n", icon); */
133 | ASSERT(icon);
134 | g_free(icon);
135 | }
136 | double elapsed_time = (double)(clock() - start_time) / CLOCKS_PER_SEC;
137 | printf("Done in %f seconds\n", elapsed_time);
138 | free_all_themes();
139 | PASS();
140 | }
141 |
142 | TEST test_bench_doesnt_exist(void)
143 | {
144 | printf("Starting benchmark\n");
145 | // At the time of committing, I get numbers like 9 seconds for 1000 icon lookups
146 | int index = load_icon_theme("Adwaita");
147 | add_default_theme(index);
148 | index = load_icon_theme("Papirus");
149 | add_default_theme(index);
150 | printf("Benchmarking icons\n");
151 | clock_t start_time = clock();
152 | for (int i = 0; i < 1000; i++){
153 | // Icon size is chosen as some common icon size.
154 | char *icon = find_icon_path("doesn't exist", 48);
155 | /* printf("%s\n", icon); */
156 | ASSERT_FALSE(icon);
157 | g_free(icon);
158 | }
159 | double elapsed_time = (double)(clock() - start_time) / CLOCKS_PER_SEC;
160 | printf("Done in %f seconds\n", elapsed_time);
161 | free_all_themes();
162 | PASS();
163 | }
164 |
165 |
166 | SUITE (suite_icon_lookup)
167 | {
168 | RUN_TEST(test_load_theme_from_dir);
169 | RUN_TEST(test_find_icon);
170 | RUN_TEST(test_find_path);
171 | RUN_TEST(test_new_icon_overrides_raw_icon);
172 | bool bench = false;
173 | if (bench) {
174 | RUN_TEST(test_bench_search);
175 | RUN_TEST(test_bench_multiple_search);
176 | RUN_TEST(test_bench_doesnt_exist);
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/test/icon.c:
--------------------------------------------------------------------------------
1 | #include "../src/icon.c"
2 | #include "greatest.h"
3 |
4 | #define DATAPREFIX "/data"
5 | #define ICONPATH "/data/icons/theme"
6 |
7 | /* As there are no hints to test if the loaded GdkPixbuf is
8 | * read from a PNG or an SVG file, the sample icons in the
9 | * test structure have different sizes
10 | */
11 | #define IS_ICON_PNG(pb) 4 == gdk_pixbuf_get_width(pb)
12 | #define IS_ICON_SVG(pb) 16 == gdk_pixbuf_get_width(pb)
13 |
14 | extern const char *base;
15 |
16 | int scale = 1;
17 | int size = 16;
18 |
19 | TEST test_get_path_from_icon_null(void)
20 | {
21 | char *result = get_path_from_icon_name(NULL, 16);
22 | ASSERT_EQ(result, NULL);
23 | PASS();
24 | }
25 |
26 | TEST test_get_path_from_icon_name_full(void)
27 | {
28 | const char *iconpath = ICONPATH;
29 |
30 | gchar *path = g_build_filename(base, iconpath, "16x16", "actions", "edit.png", NULL);
31 |
32 | char *result = get_path_from_icon_name(path, size);
33 | ASSERT(result);
34 | ASSERT_STR_EQ(result, path);
35 |
36 | g_free(path);
37 | g_free(result);
38 | PASS();
39 | }
40 |
41 | TEST test_icon_size_clamp_too_small(int min_icon_size, int max_icon_size)
42 | {
43 | int w = 12, h = 24;
44 | bool resized = icon_size_clamp(&w, &h, min_icon_size, max_icon_size);
45 | ASSERT(resized);
46 | ASSERT_EQ(w, 16);
47 | ASSERT_EQ(h, 32);
48 |
49 | PASS();
50 | }
51 |
52 | TEST test_icon_size_clamp_not_necessary(int min_icon_size, int max_icon_size)
53 | {
54 | int w = 20, h = 30;
55 | bool resized = icon_size_clamp(&w, &h, min_icon_size, max_icon_size);
56 | ASSERT(!resized);
57 | ASSERT_EQ(w, 20);
58 | ASSERT_EQ(h, 30);
59 |
60 | PASS();
61 | }
62 |
63 | TEST test_icon_size_clamp_too_big(int min_icon_size, int max_icon_size)
64 | {
65 | int w = 75, h = 150;
66 | bool resized = icon_size_clamp(&w, &h, min_icon_size, max_icon_size);
67 | ASSERT(resized);
68 | ASSERT_EQ(w, 50);
69 | ASSERT_EQ(h, 100);
70 |
71 | PASS();
72 | }
73 |
74 | TEST test_icon_size_clamp_too_small_then_too_big(int min_icon_size, int max_icon_size)
75 | {
76 | int w = 8, h = 80;
77 | bool resized = icon_size_clamp(&w, &h, min_icon_size, max_icon_size);
78 | ASSERT(resized);
79 | ASSERT_EQ(w, 10);
80 | ASSERT_EQ(h, 100);
81 |
82 | PASS();
83 | }
84 |
85 | SUITE(suite_icon)
86 | {
87 | // set only valid icons in the path
88 | char *icon_path = g_build_filename(base, DATAPREFIX, NULL);
89 | setenv("XDG_DATA_HOME", icon_path, 1);
90 | printf("Icon path: %s\n", icon_path);
91 | RUN_TEST(test_get_path_from_icon_null);
92 | RUN_TEST(test_get_path_from_icon_name_full);
93 | RUN_TESTp(test_icon_size_clamp_not_necessary, 0, 100);
94 |
95 | RUN_TESTp(test_icon_size_clamp_too_small, 16, 100);
96 | RUN_TESTp(test_icon_size_clamp_not_necessary, 16, 100);
97 | RUN_TESTp(test_icon_size_clamp_too_big, 16, 100);
98 | RUN_TESTp(test_icon_size_clamp_too_small_then_too_big, 16, 100);
99 |
100 | RUN_TESTp(test_icon_size_clamp_too_small, 16, 0);
101 | RUN_TESTp(test_icon_size_clamp_not_necessary, 16, 0);
102 |
103 | RUN_TESTp(test_icon_size_clamp_not_necessary, 0, 100);
104 | RUN_TESTp(test_icon_size_clamp_too_big, 0, 100);
105 |
106 | g_clear_pointer(&icon_path, g_free);
107 | }
108 | /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
109 |
--------------------------------------------------------------------------------
/test/ini.c:
--------------------------------------------------------------------------------
1 | #include "greatest.h"
2 | #include "../src/ini.c"
3 | #include
4 |
5 | extern const char *base;
6 |
7 | TEST test_next_section(void)
8 | {
9 | char *config_path = g_strconcat(base, "/data/test-ini", NULL);
10 | FILE *config_file = fopen(config_path, "r");
11 | if (!config_file) {
12 | ASSERTm(false, "Test config file 'data/test-ini' couldn't be opened, failing.\n");
13 | }
14 | struct ini * ini = load_ini_file(config_file);
15 | ASSERT(ini);
16 | fclose(config_file);
17 | free(config_path);
18 |
19 | const char *section = NULL;
20 | ASSERT_STR_EQ("bool", (section = next_section(ini, section)));
21 | ASSERT_STR_EQ("string", (section = next_section(ini, section)));
22 | ASSERT_STR_EQ("list", (section = next_section(ini, section)));
23 | ASSERT_STR_EQ("path", (section = next_section(ini, section)));
24 | ASSERT_STR_EQ("int", (section = next_section(ini, section)));
25 | ASSERT_STR_EQ("double", (section = next_section(ini, section)));
26 | finish_ini(ini);
27 | free(ini);
28 | PASS();
29 | }
30 |
31 |
32 | SUITE(suite_ini)
33 | {
34 | RUN_TEST(test_next_section);
35 | }
36 |
--------------------------------------------------------------------------------
/test/input.c:
--------------------------------------------------------------------------------
1 | #include "../src/input.c"
2 | #include "queues.h"
3 | #include "greatest.h"
4 | #include "helpers.h"
5 | #include "../src/utils.h"
6 |
7 | TEST test_get_notification_clickable_height_first(void)
8 | {
9 | bool orginal_gap_size = settings.gap_size;
10 | settings.gap_size = 0;
11 |
12 | struct notification *n = test_notification("test", 10);
13 | n->displayed_height = 12;
14 |
15 | int expected_size = n->displayed_height + settings.frame_width;
16 | expected_size += (settings.separator_height / 2.0);
17 | int result = get_notification_clickable_height(n, true, false);
18 |
19 | ASSERT(result == expected_size);
20 |
21 | settings.gap_size = orginal_gap_size;
22 | notification_unref(n);
23 | PASS();
24 | }
25 |
26 | TEST test_get_notification_clickable_height_middle(void)
27 | {
28 | bool orginal_gap_size = settings.gap_size;
29 | settings.gap_size = 0;
30 |
31 | struct notification *n = test_notification("test", 10);
32 | n->displayed_height = 12;
33 |
34 | int expected_size = n->displayed_height + settings.separator_height;
35 | int result = get_notification_clickable_height(n, false, false);
36 |
37 | ASSERT(result == expected_size);
38 |
39 | settings.gap_size = orginal_gap_size;
40 | notification_unref(n);
41 | PASS();
42 | }
43 |
44 | TEST test_get_notification_clickable_height_last(void)
45 | {
46 | bool orginal_gap_size = settings.gap_size;
47 | settings.gap_size = 0;
48 |
49 | struct notification *n = test_notification("test", 10);
50 | n->displayed_height = 12;
51 |
52 | int expected_size = n->displayed_height + settings.frame_width;
53 | expected_size += (settings.separator_height / 2.0);
54 | int result = get_notification_clickable_height(n, false, true);
55 |
56 | ASSERT(result == expected_size);
57 |
58 | settings.gap_size = orginal_gap_size;
59 | notification_unref(n);
60 | PASS();
61 | }
62 |
63 | TEST test_get_notification_clickable_height_gaps(void)
64 | {
65 | bool orginal_gap_size = settings.gap_size;
66 | settings.gap_size = 7;
67 |
68 | struct notification *n = test_notification("test", 10);
69 | n->displayed_height = 12;
70 |
71 | int expected_size = n->displayed_height + (settings.frame_width * 2);
72 |
73 | int result_first = get_notification_clickable_height(n, true, false);
74 | ASSERT(result_first == expected_size);
75 |
76 | int result_middle = get_notification_clickable_height(n, false, false);
77 | ASSERT(result_middle == expected_size);
78 |
79 | int result_last = get_notification_clickable_height(n, false, true);
80 | ASSERT(result_last == expected_size);
81 |
82 | settings.gap_size = orginal_gap_size;
83 | notification_unref(n);
84 | PASS();
85 | }
86 |
87 | TEST test_notification_at(void)
88 | {
89 | int total_notifications = 3;
90 | GSList *notifications = get_dummy_notifications(total_notifications);
91 |
92 | queues_init();
93 |
94 | int display_height = 12;
95 | struct notification *n;
96 | for (GSList *iter = notifications; iter; iter = iter->next) {
97 | n = iter->data;
98 | n->displayed_height = display_height;
99 | queues_notification_insert(n);
100 | }
101 |
102 | queues_update(STATUS_NORMAL, time_monotonic_now());
103 |
104 | struct notification *top_notification = g_slist_nth_data(notifications, 0);
105 | int top_notification_height = get_notification_clickable_height(top_notification, true, false);
106 |
107 | struct notification *middle_notification = g_slist_nth_data(notifications, 1);
108 | int middle_notification_height = get_notification_clickable_height(middle_notification, false, false);
109 |
110 | struct notification *bottom_notification = g_slist_nth_data(notifications, 2);
111 | int bottom_notification_height = get_notification_clickable_height(bottom_notification, false, true);
112 |
113 | struct notification *result;
114 |
115 | int top_y_coord;
116 | top_y_coord = 0;
117 | result = get_notification_at(top_y_coord);
118 | ASSERT(result != NULL);
119 | ASSERT(result == top_notification);
120 |
121 | top_y_coord = top_notification_height - 1;
122 | result = get_notification_at(top_y_coord);
123 | ASSERT(result != NULL);
124 | ASSERT(result == top_notification);
125 |
126 | int middle_y_coord;
127 | middle_y_coord = top_notification_height;
128 | result = get_notification_at(middle_y_coord);
129 | ASSERT(result != NULL);
130 | ASSERT(result == middle_notification);
131 |
132 | middle_y_coord = top_notification_height;
133 | result = get_notification_at(middle_y_coord);
134 | ASSERT(result != NULL);
135 | ASSERT(result == middle_notification);
136 |
137 | int bottom_y_coord;
138 | bottom_y_coord = top_notification_height + middle_notification_height;
139 | result = get_notification_at(bottom_y_coord);
140 | ASSERT(result != NULL);
141 | ASSERT(result == bottom_notification);
142 |
143 | bottom_y_coord = top_notification_height + middle_notification_height + bottom_notification_height - 1;
144 | result = get_notification_at(bottom_y_coord);
145 | ASSERT(result != NULL);
146 | ASSERT(result == bottom_notification);
147 |
148 | g_slist_free_full(notifications, free_dummy_notification);
149 | PASS();
150 | }
151 |
152 | SUITE(suite_input)
153 | {
154 | SHUFFLE_TESTS(time(NULL), {
155 | RUN_TEST(test_get_notification_clickable_height_first);
156 | RUN_TEST(test_get_notification_clickable_height_middle);
157 | RUN_TEST(test_get_notification_clickable_height_last);
158 | RUN_TEST(test_get_notification_clickable_height_gaps);
159 | RUN_TEST(test_notification_at);
160 | });
161 | }
162 |
--------------------------------------------------------------------------------
/test/log.c:
--------------------------------------------------------------------------------
1 | #include "../src/log.c"
2 | #include "greatest.h"
3 |
4 | TEST test_log_level(GLogLevelFlags level, const char *shortstr, const char *longstr)
5 | {
6 | ASSERT_STR_EQ(log_level_to_string(level), longstr);
7 |
8 | log_set_level_from_string(shortstr);
9 |
10 | if (level != G_LOG_LEVEL_ERROR)
11 | ASSERT_ENUM_EQ(level, log_level, log_level_to_string);
12 |
13 | log_set_level_from_string(longstr);
14 |
15 | if (level != G_LOG_LEVEL_ERROR)
16 | ASSERT_ENUM_EQ(level, log_level, log_level_to_string);
17 |
18 | PASS();
19 | }
20 |
21 | SUITE(suite_log)
22 | {
23 | GLogLevelFlags oldlevel = log_level;
24 |
25 | RUN_TESTp(test_log_level, G_LOG_LEVEL_ERROR, NULL, "ERROR");
26 | RUN_TESTp(test_log_level, G_LOG_LEVEL_CRITICAL, "crit", "CRITICAL");
27 | RUN_TESTp(test_log_level, G_LOG_LEVEL_WARNING, "warn", "WARNING");
28 | RUN_TESTp(test_log_level, G_LOG_LEVEL_MESSAGE, "mesg", "MESSAGE");
29 | RUN_TESTp(test_log_level, G_LOG_LEVEL_INFO, "info", "INFO");
30 | RUN_TESTp(test_log_level, G_LOG_LEVEL_DEBUG, "deb", "DEBUG");
31 |
32 | log_level = oldlevel;
33 | }
34 |
35 | /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
36 |
--------------------------------------------------------------------------------
/test/markup.c:
--------------------------------------------------------------------------------
1 | #include "../src/markup.c"
2 | #include "greatest.h"
3 |
4 | TEST test_markup_strip(void)
5 | {
6 | char *ptr;
7 |
8 | ASSERT_STR_EQ(""", (ptr=markup_strip(g_strdup("""))));
9 | g_free(ptr);
10 | ASSERT_STR_EQ("'", (ptr=markup_strip(g_strdup("'"))));
11 | g_free(ptr);
12 | ASSERT_STR_EQ("<", (ptr=markup_strip(g_strdup("<"))));
13 | g_free(ptr);
14 | ASSERT_STR_EQ(">", (ptr=markup_strip(g_strdup(">"))));
15 | g_free(ptr);
16 | ASSERT_STR_EQ("&", (ptr=markup_strip(g_strdup("&"))));
17 | g_free(ptr);
18 | ASSERT_STR_EQ(">A A
foo
bar\nbaz"), MARKUP_NO)));
30 | g_free(ptr);
31 | ASSERT_STR_EQ("foo\nbar\nbaz", (ptr=markup_transform(g_strdup("foo
bar\nbaz"), MARKUP_STRIP)));
32 | g_free(ptr);
33 | ASSERT_STR_EQ("foo\nbar\nbaz", (ptr=markup_transform(g_strdup("foo
bar\nbaz"), MARKUP_FULL)));
34 | g_free(ptr);
35 |
36 | settings.ignore_newline = true;
37 | ASSERT_STR_EQ("<i>foo</i><br>bar baz", (ptr=markup_transform(g_strdup("foo
bar\nbaz"), MARKUP_NO)));
38 | g_free(ptr);
39 | ASSERT_STR_EQ("foo bar baz", (ptr=markup_transform(g_strdup("foo
bar\nbaz"), MARKUP_STRIP)));
40 | g_free(ptr);
41 | ASSERT_STR_EQ("foo bar baz", (ptr=markup_transform(g_strdup("foo
bar\nbaz"), MARKUP_FULL)));
42 | g_free(ptr);
43 |
44 | // Test replacement of img and a tags, not renderable by pango
45 | ASSERT_STR_EQ("foo bar bar baz", (ptr=markup_transform(g_strdup("![\"foo]()
bar\nbaz"), MARKUP_FULL)));
46 | g_free(ptr);
47 | ASSERT_STR_EQ("test ", (ptr=markup_transform(g_strdup("test
image"), MARKUP_FULL)));
50 | g_free(ptr);
51 | ASSERT_STR_EQ("bar baz", (ptr=markup_transform(g_strdup("bar baz"), MARKUP_FULL)));
52 | g_free(ptr);
53 |
54 | ASSERT_STR_EQ("Ψ", (ptr=markup_transform(g_strdup("Ψ"), MARKUP_FULL)));
55 | free(ptr);
56 | ASSERT_STR_EQ("Ψ Ψ", (ptr=markup_transform(g_strdup("Ψ Ψ"), MARKUP_FULL)));
57 | free(ptr);
58 | ASSERT_STR_EQ("> <", (ptr=markup_transform(g_strdup("> <"), MARKUP_FULL)));
59 | free(ptr);
60 | ASSERT_STR_EQ("&invalid; &#abc; &#xG;", (ptr=markup_transform(g_strdup("&invalid; abc; G;"), MARKUP_FULL)));
61 | free(ptr);
62 | ASSERT_STR_EQ("&; &#; &#x;", (ptr=markup_transform(g_strdup("&; "), MARKUP_FULL)));
63 | free(ptr);
64 |
65 | PASS();
66 | }
67 |
68 | TEST helper_markup_strip_a (const char *in, const char *exp, const char *urls)
69 | {
70 | // out_urls is a return parameter and the content should be ignored
71 | char *out_urls = (char *)0x04; //Chosen by a fair dice roll
72 | char *out = g_strdup(in);
73 | char *msg = g_strconcat("url: ", in, NULL);
74 |
75 | markup_strip_a(&out, &out_urls);
76 |
77 | ASSERT_STR_EQm(msg, exp, out);
78 |
79 | if (urls) {
80 | ASSERT_STR_EQm(msg, urls, out_urls);
81 | } else {
82 | ASSERT_EQm(msg, urls, out_urls);
83 | }
84 |
85 | g_free(out_urls);
86 | g_free(out);
87 | g_free(msg);
88 |
89 | PASS();
90 | }
91 |
92 | TEST test_markup_strip_a(void)
93 | {
94 | RUN_TESTp(helper_markup_strip_a, "valid link", "valid link", "[valid] https://url.com");
95 | RUN_TESTp(helper_markup_strip_a, "valid link", "valid link", "[valid] ");
96 | RUN_TESTp(helper_markup_strip_a, "valid link", "valid link", NULL);
97 | RUN_TESTp(helper_markup_strip_a, "valid link", "valid link", "[valid link] https://url.com");
98 |
99 | RUN_TESTp(helper_markup_strip_a, " link", " link", NULL);
100 | RUN_TESTp(helper_markup_strip_a, " link", " link", NULL);
101 |
102 | PASS();
103 | }
104 |
105 | TEST helper_markup_strip_img (const char *in, const char *exp, const char *urls)
106 | {
107 | // out_urls is a return parameter and the content should be ignored
108 | char *out_urls = (char *)0x04; //Chosen by a fair dice roll
109 | char *out = g_strdup(in);
110 | char *msg = g_strconcat("url: ", in, NULL);
111 |
112 | markup_strip_img(&out, &out_urls);
113 |
114 | ASSERT_STR_EQm(msg, exp, out);
115 |
116 | if (urls) {
117 | ASSERT_STR_EQm(msg, urls, out_urls);
118 | } else {
119 | ASSERT_EQm(msg, urls, out_urls);
120 | }
121 |
122 | g_free(out_urls);
123 | g_free(out);
124 | g_free(msg);
125 |
126 | PASS();
127 | }
128 |
129 | TEST test_markup_strip_img(void)
130 | {
131 | RUN_TESTp(helper_markup_strip_img, "v
img", "v [image] img", NULL);
132 | RUN_TESTp(helper_markup_strip_img, "v
img", "v valid img", NULL);
133 | RUN_TESTp(helper_markup_strip_img, "v
img", "v [image] img", "[image] url.com");
134 |
135 | RUN_TESTp(helper_markup_strip_img, "v
img", "v valid img", "[valid] url.com");
136 | RUN_TESTp(helper_markup_strip_img, "v
img", "v valid img", "[valid] url.com");
137 | RUN_TESTp(helper_markup_strip_img, "v
img", "v valid img", "[valid] url.com");
138 |
139 | RUN_TESTp(helper_markup_strip_img, "i
img", "i [image] img", "[image] https://url.com");
140 | RUN_TESTp(helper_markup_strip_img, "i
img", "i broken img", NULL);
141 | RUN_TESTp(helper_markup_strip_img, "i
img", "i [image] img", NULL);
142 |
143 | RUN_TESTp(helper_markup_strip_img, "i
img", "i broken img", NULL);
144 | RUN_TESTp(helper_markup_strip_img, "i
img", "i [image] img", "[image] url.com");
145 | RUN_TESTp(helper_markup_strip_img, "i
img", "i [image] img", NULL);
146 |
147 | RUN_TESTp(helper_markup_strip_img, "i
6 |
7 | TEST test_extract_urls_from_empty_string(void)
8 | {
9 | char *urls = extract_urls("");
10 | ASSERT_EQ_FMT(NULL, (void*)urls, "%p");
11 |
12 | urls = extract_urls(NULL);
13 | ASSERT(!urls);
14 | PASS();
15 | }
16 |
17 | TEST test_extract_urls_from_no_urls_string(void)
18 | {
19 | char *urls = extract_urls("You got a new message from your friend");
20 | ASSERT(!urls);
21 | PASS();
22 | }
23 |
24 | TEST test_extract_urls_from_one_url_string(void)
25 | {
26 | char *urls = extract_urls("Hi from https://www.example.com!");
27 | ASSERT_STR_EQ("https://www.example.com", urls);
28 | g_free(urls);
29 | PASS();
30 | }
31 |
32 | TEST test_extract_urls_from_two_url_string(void)
33 | {
34 | char *urls = extract_urls("Hi from https://www.example.com and ftp://www.example.com!");
35 | ASSERT_STR_EQ("https://www.example.com\nftp://www.example.com", urls);
36 | g_free(urls);
37 | PASS();
38 | }
39 |
40 | TEST test_extract_urls_from_one_url_port(void)
41 | {
42 | char *urls = extract_urls("Hi from https://www.example.com:8100 and have a nice day!");
43 | ASSERT_STR_EQ("https://www.example.com:8100", urls);
44 | g_free(urls);
45 | PASS();
46 | }
47 |
48 | TEST test_extract_urls_from_one_url_path(void)
49 | {
50 | char *urls = extract_urls("Hi from https://www.example.com:8100/testpath and have a nice day!");
51 | ASSERT_STR_EQ("https://www.example.com:8100/testpath", urls);
52 | g_free(urls);
53 | PASS();
54 | }
55 |
56 | TEST test_extract_urls_from_one_url_anchor(void)
57 | {
58 | char *urls = extract_urls("Hi from https://www.example.com:8100/testpath#anchor and have a nice day!");
59 | ASSERT_STR_EQ("https://www.example.com:8100/testpath#anchor", urls);
60 | g_free(urls);
61 | PASS();
62 | }
63 |
64 | SUITE(suite_menu)
65 | {
66 | RUN_TEST(test_extract_urls_from_empty_string);
67 | RUN_TEST(test_extract_urls_from_no_urls_string);
68 | RUN_TEST(test_extract_urls_from_one_url_string);
69 | RUN_TEST(test_extract_urls_from_two_url_string);
70 | RUN_TEST(test_extract_urls_from_one_url_port);
71 | RUN_TEST(test_extract_urls_from_one_url_path);
72 | RUN_TEST(test_extract_urls_from_one_url_anchor);
73 | }
74 | /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
75 |
--------------------------------------------------------------------------------
/test/misc.c:
--------------------------------------------------------------------------------
1 | #include "greatest.h"
2 |
3 | // This actually tests the buildsystem to make sure,
4 | // the build system hands over a correct version number
5 | // This is not testable via macros
6 | TEST assert_version_number(void)
7 | {
8 | ASSERTm("Version number is empty",
9 | 0 != strcmp(VERSION, ""));
10 |
11 | ASSERTm("Version number is not seeded by git",
12 | NULL == strstr(VERSION, "non-git"));
13 | PASS();
14 | }
15 |
16 | SUITE(suite_misc)
17 | {
18 | RUN_TEST(assert_version_number);
19 | }
20 |
21 | /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
22 |
--------------------------------------------------------------------------------
/test/queues.h:
--------------------------------------------------------------------------------
1 | #include "greatest.h"
2 |
3 | #ifndef DUNST_TEST_QUEUES_H
4 | #define DUNST_TEST_QUEUES_H
5 |
6 | #include
7 | #include
8 |
9 | #include "../src/notification.h"
10 | #include "../src/queues.h"
11 |
12 | #define STATUS_NORMAL ((struct dunst_status) {.fullscreen=false, .running=true, .idle=false})
13 | #define STATUS_IDLE ((struct dunst_status) {.fullscreen=false, .running=true, .idle=true})
14 | #define STATUS_FSIDLE ((struct dunst_status) {.fullscreen=true, .running=true, .idle=true})
15 | #define STATUS_FS ((struct dunst_status) {.fullscreen=true, .running=true, .idle=false})
16 | #define STATUS_PAUSE ((struct dunst_status) {.fullscreen=false, .running=false, .idle=false})
17 |
18 | #define QUEUE_WAIT waiting
19 | #define QUEUE_DISP displayed
20 | #define QUEUE_HIST history
21 | #define QUEUE(q) QUEUE_##q
22 |
23 | #define QUEUE_LEN_ALL(wait, disp, hist) do { \
24 | if (wait >= 0) ASSERTm("Waiting is not " #wait, wait == g_queue_get_length(QUEUE(WAIT))); \
25 | if (disp >= 0) ASSERTm("Displayed is not " #disp, disp == g_queue_get_length(QUEUE(DISP))); \
26 | if (disp >= 0) ASSERTm("History is not " #hist, hist == g_queue_get_length(QUEUE(HIST))); \
27 | } while (0)
28 |
29 | #define QUEUE_CONTAINS(q, n) QUEUE_CONTAINSm("QUEUE_CONTAINS(" #q "," #n ")", q, n)
30 | #define QUEUE_CONTAINSm(msg, q, n) ASSERTm(msg, g_queue_find(QUEUE(q), n))
31 |
32 | #define NOT_LAST(n) do {ASSERT_EQm("Notification " #n " should have been deleted.", 1, notification_refcount_get(n)); g_clear_pointer(&n, notification_unref); } while(0)
33 |
34 | /* Retrieve a notification by its id. Solely for debugging purposes */
35 | struct notification *queues_debug_find_notification_by_id(int id);
36 |
37 | #endif
38 | /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
39 |
--------------------------------------------------------------------------------
/test/rules.c:
--------------------------------------------------------------------------------
1 | #include "../src/rules.c"
2 |
3 | #include "greatest.h"
4 | #include
5 |
6 | extern const char *base;
7 |
8 | // test filtering rules matching
9 | TEST test_pattern_match(void) {
10 | // NULL should match everything
11 | ASSERT(rule_field_matches_string("anything", NULL));
12 |
13 | // Literal matches
14 | ASSERT(rule_field_matches_string("asdf", "asdf"));
15 | ASSERT(rule_field_matches_string("test123", "test123"));
16 |
17 | ASSERT(rule_field_matches_string("!", "!"));
18 | ASSERT(rule_field_matches_string("!asd", "!asd"));
19 | ASSERT(rule_field_matches_string("/as/d", "/as/d"));
20 | ASSERT(rule_field_matches_string("/as/d", "/as/d"));
21 |
22 | // ranges
23 | ASSERT(rule_field_matches_string("ac", "[a-z][a-z]"));
24 |
25 | // Non-matches
26 | ASSERT_FALSE(rule_field_matches_string("asd", "!asd"));
27 | ASSERT_FALSE(rule_field_matches_string("ffff", "*asd"));
28 | ASSERT_FALSE(rule_field_matches_string("ffff", "?"));
29 | ASSERT_FALSE(rule_field_matches_string("Ac", "[a-z][a-z]"));
30 |
31 | // Things that differ between fnmatch(3) and regex(3)
32 |
33 | if (settings.enable_regex) {
34 | // Single character matching
35 | ASSERT(rule_field_matches_string("a", "."));
36 |
37 | // Wildcard matching
38 | ASSERT(rule_field_matches_string("anything", ".*"));
39 | ASSERT(rule_field_matches_string("*", ".*"));
40 | ASSERT(rule_field_matches_string("", ".*"));
41 | ASSERT(rule_field_matches_string("ffffasd", ".*asd"));
42 |
43 | // Substring matching
44 | ASSERT(rule_field_matches_string("asd", ""));
45 | ASSERT(rule_field_matches_string("asd", "sd"));
46 | ASSERT(rule_field_matches_string("asd", "a"));
47 | ASSERT(rule_field_matches_string("asd", "d"));
48 | ASSERT(rule_field_matches_string("asd", "asd"));
49 |
50 | // Match multiple strings
51 | ASSERT(rule_field_matches_string("ghj", "asd|dfg|ghj"));
52 | ASSERT(rule_field_matches_string("asd", "asd|dfg|ghj"));
53 | ASSERT(rule_field_matches_string("dfg", "asd|dfg|ghj"));
54 | ASSERT_FALSE(rule_field_matches_string("azd", "asd|dfg|ghj"));
55 |
56 | // Special characters
57 | ASSERT_FALSE(rule_field_matches_string("{", "{"));
58 | ASSERT(rule_field_matches_string("{", "\\{"));
59 | ASSERT(rule_field_matches_string("a", "(a)"));
60 | } else {
61 | // Single character matching
62 | ASSERT(rule_field_matches_string("a", "?"));
63 |
64 | // Wildcard matching
65 | ASSERT(rule_field_matches_string("anything", "*"));
66 | ASSERT(rule_field_matches_string("*", "*"));
67 | ASSERT(rule_field_matches_string("", "*"));
68 | ASSERT(rule_field_matches_string("ffffasd", "*asd"));
69 |
70 | // Substring matching
71 | ASSERT_FALSE(rule_field_matches_string("asd", ""));
72 | ASSERT_FALSE(rule_field_matches_string("asd", "sd"));
73 | ASSERT_FALSE(rule_field_matches_string("asd", "a"));
74 | ASSERT_FALSE(rule_field_matches_string("asd", "d"));
75 | ASSERT(rule_field_matches_string("asd", "asd"));
76 | }
77 | PASS();
78 | }
79 |
80 | SUITE(suite_rules) {
81 | bool store = settings.enable_regex;
82 |
83 | settings.enable_regex = false;
84 | RUN_TEST(test_pattern_match);
85 |
86 | settings.enable_regex = true;
87 | RUN_TEST(test_pattern_match);
88 |
89 | settings.enable_regex = store;
90 | }
91 |
--------------------------------------------------------------------------------
/test/setting.c:
--------------------------------------------------------------------------------
1 | #include "../src/settings.h"
2 | #include "../src/option_parser.h"
3 | #include "../src/settings_data.h"
4 |
5 | #include "greatest.h"
6 |
7 | extern const char *base;
8 |
9 | // In this suite a few dunstrc's are tested to see if the settings code works
10 | // This file is called setting.c, since the name settings.c caused issues.
11 |
12 | char *config_path;
13 |
14 | TEST test_dunstrc_markup(void) {
15 | config_path = g_strconcat(base, "/data/dunstrc.markup", NULL);
16 | load_settings(config_path);
17 |
18 | ASSERT_STR_EQ(settings.font, "Monospace 8");
19 |
20 |
21 | const char *e_format = "%s\\n%b"; // escape the \n since it would otherwise result in the newline character
22 | const struct rule * r = get_rule("global");
23 | const char *got_format = r->format;
24 | ASSERT_STR_EQ(e_format, got_format);
25 | ASSERT(settings.indicate_hidden);
26 |
27 | g_free(config_path);
28 | PASS();
29 | }
30 |
31 | TEST test_dunstrc_nomarkup(void) {
32 | config_path = g_strconcat(base, "/data/dunstrc.nomarkup", NULL);
33 | load_settings(config_path);
34 |
35 | ASSERT_STR_EQ(settings.font, "Monospace 8");
36 |
37 |
38 | const char *e_format = "%s\\n%b"; // escape the \n since it would otherwise result in the newline character
39 | const struct rule * r = get_rule("global");
40 | const char *got_format = r->format;
41 | ASSERT_STR_EQ(e_format, got_format);
42 | ASSERT(settings.indicate_hidden);
43 |
44 | g_free(config_path);
45 | PASS();
46 | }
47 |
48 | // Test if the defaults in code and in dunstrc match
49 | TEST test_dunstrc_defaults(void) {
50 | struct settings s_default;
51 | struct settings s_dunstrc;
52 |
53 | config_path = g_strconcat(base, "/data/dunstrc.default", NULL);
54 | set_defaults();
55 | s_default = settings;
56 |
57 | load_settings(config_path);
58 | s_dunstrc = settings;
59 |
60 | ASSERT_EQ(s_default.corner_radius, s_dunstrc.corner_radius);
61 | char message[500];
62 |
63 | for (int i = 0; i < G_N_ELEMENTS(allowed_settings); i++) {
64 | if (!allowed_settings[i].value) {
65 | continue; // it's a rule, that's harder to test
66 | }
67 | if (allowed_settings[i].different_default) {
68 | continue; // Skip testing, since it's an intended difference.
69 | }
70 | size_t offset = (char*)allowed_settings[i].value - (char*)&settings;
71 | enum setting_type type = allowed_settings[i].type;
72 | snprintf(message, 500, "The default of setting %s does not match. Different defaults are set in code and dunstrc"
73 | , allowed_settings[i].name);
74 | switch (type) {
75 | case TYPE_CUSTOM:
76 | if (allowed_settings[i].parser == string_parse_bool) {
77 | {
78 | bool a = *(bool*) ((char*) &s_default + offset);
79 | bool b = *(bool*) ((char*) &s_dunstrc + offset);
80 | ASSERT_EQm(message, a, b);
81 | }
82 | break;
83 | } // else fall through
84 | case TYPE_TIME:
85 | case TYPE_INT:;
86 | {
87 | int a = *(int*) ((char*) &s_default + offset);
88 | int b = *(int*) ((char*) &s_dunstrc + offset);
89 | ASSERT_EQm(message, a, b);
90 | }
91 | break;
92 | case TYPE_DOUBLE:
93 | case TYPE_STRING:
94 | case TYPE_PATH:
95 | case TYPE_LIST:
96 | case TYPE_LENGTH:
97 | break; // TODO implement these checks as well
98 | default:
99 | printf("Type unknown %s:%d\n", __FILE__, __LINE__);
100 | }
101 | /* printf("%zu\n", offset); */
102 | }
103 |
104 | g_free(config_path);
105 | PASS();
106 | }
107 |
108 | SUITE(suite_setting) {
109 | RUN_TEST(test_dunstrc_markup);
110 | RUN_TEST(test_dunstrc_nomarkup);
111 | RUN_TEST(test_dunstrc_defaults);
112 | }
113 |
--------------------------------------------------------------------------------
/test/settings_data.c:
--------------------------------------------------------------------------------
1 | #include "../src/settings_data.h"
2 | #include "greatest.h"
3 |
4 | extern const char *base;
5 |
6 | // TODO check enums on NULL-termination
7 |
8 | TEST test_names_valid(void)
9 | {
10 | for (size_t i = 0; i < G_N_ELEMENTS(allowed_settings); i++) {
11 | gchar *error1 = g_strdup_printf("Setting name is null (setting description is \"%s\")", allowed_settings[i].description);
12 | gchar *error2 = g_strdup_printf("Setting name is empty (setting description is \"%s\")", allowed_settings[i].description);
13 | ASSERTm(error1, allowed_settings[i].name);
14 | ASSERTm(error2, strlen(allowed_settings[i].name));
15 | g_free(error1);
16 | g_free(error2);
17 | }
18 | PASS();
19 | }
20 |
21 | TEST test_description_valid(void)
22 | {
23 | for (size_t i = 0; i < G_N_ELEMENTS(allowed_settings); i++) {
24 | gchar *error1 = g_strdup_printf("Description of setting %s is null", allowed_settings[i].name);
25 | gchar *error2 = g_strdup_printf("Description of setting %s is empty", allowed_settings[i].name);
26 | ASSERTm(error1, allowed_settings[i].description);
27 | ASSERTm(error2, strlen(allowed_settings[i].description));
28 | g_free(error1);
29 | g_free(error2);
30 | }
31 | PASS();
32 | }
33 |
34 | #define BETWEEN(arg, low, high) (((arg) > (low) ) && ((arg) < (high)))
35 |
36 | TEST test_type_valid(void)
37 | {
38 | for (size_t i = 0; i < G_N_ELEMENTS(allowed_settings); i++) {
39 | gchar *error1 = g_strdup_printf("Type of setting %s is not valid: %i", allowed_settings[i].name, allowed_settings[i].type);
40 | ASSERTm(error1, BETWEEN(allowed_settings[i].type, TYPE_MIN, TYPE_MAX));
41 | g_free(error1);
42 | }
43 | PASS();
44 | }
45 |
46 | TEST test_section_valid(void)
47 | {
48 | for (size_t i = 0; i < G_N_ELEMENTS(allowed_settings); i++) {
49 | gchar *error1 = g_strdup_printf("Section of setting %s is null", allowed_settings[i].name);
50 | gchar *error2 = g_strdup_printf("Section of setting %s is empty", allowed_settings[i].name);
51 | ASSERTm(error1, allowed_settings[i].section);
52 | ASSERTm(error2, strlen(allowed_settings[i].section));
53 | g_free(error1);
54 | g_free(error2);
55 | }
56 | PASS();
57 | }
58 |
59 | TEST test_default_value_valid(void)
60 | {
61 | for (size_t i = 0; i < G_N_ELEMENTS(allowed_settings); i++) {
62 | gchar *error1 = g_strdup_printf("Default_value of setting %s is null", allowed_settings[i].name);
63 | gchar *error2 = g_strdup_printf("Default_value of setting %s is empty", allowed_settings[i].name);
64 | ASSERTm(error1, allowed_settings[i].default_value);
65 | if (allowed_settings[i].type != TYPE_STRING)
66 | ASSERTm(error2, strlen(allowed_settings[i].default_value));
67 | g_free(error1);
68 | g_free(error2);
69 | }
70 | PASS();
71 | }
72 |
73 | TEST test_value_non_null(void)
74 | {
75 | for (size_t i = 0; i < G_N_ELEMENTS(allowed_settings); i++) {
76 | gchar *error1 = g_strdup_printf("Error in settting %s. A setting must have a 'value' or a 'rule_offset', or both.",
77 | allowed_settings[i].name);
78 | ASSERTm(error1, allowed_settings[i].value ||
79 | allowed_settings[i].rule_offset);
80 | g_free(error1);
81 | }
82 | PASS();
83 | }
84 |
85 | TEST test_valid_parser_and_data_per_type(void)
86 | {
87 | for (size_t i = 0; i < G_N_ELEMENTS(allowed_settings); i++) {
88 | struct setting curr = allowed_settings[i];
89 | switch (curr.type) {
90 | case TYPE_STRING:
91 | case TYPE_TIME:
92 | case TYPE_DOUBLE:
93 | case TYPE_LENGTH:
94 | case TYPE_INT: ; // no parser and no parser data needed
95 | gchar *error1 = g_strdup_printf("Parser of setting %s should be NULL. It's not needed for this type", curr.name);
96 | gchar *error2 = g_strdup_printf("Parser data of setting %s should be NULL. It's not needed for this type", curr.name);
97 | ASSERTm(error1, !curr.parser);
98 | ASSERTm(error2, !curr.parser_data);
99 | g_free(error1);
100 | g_free(error2);
101 | break;
102 | case TYPE_CUSTOM: ; // both parser data and parser are needed
103 | gchar *error3 = g_strdup_printf("Parser of setting %s should not be NULL. It's needed for this type", curr.name);
104 | gchar *error4 = g_strdup_printf("Parser data of setting %s should not be NULL. It's needed for this type", curr.name);
105 | ASSERTm(error3, curr.parser);
106 | ASSERTm(error4, curr.parser_data);
107 | g_free(error3);
108 | g_free(error4);
109 | break;
110 | case TYPE_LIST: ; // only parser data is needed
111 | gchar *error5 = g_strdup_printf("Parser of setting %s should be NULL. It's needed not for this type", curr.name);
112 | gchar *error6 = g_strdup_printf("Parser data of setting %s should not be NULL. It's needed for this type", curr.name);
113 | ASSERTm(error5, !curr.parser);
114 | ASSERTm(error6, curr.parser_data);
115 | g_free(error5);
116 | g_free(error6);
117 | break;
118 | case TYPE_PATH: ; // only parser data is neede, but when it's a rule none is needed.
119 | gchar *error7 = g_strdup_printf("Parser of setting %s should be NULL. It's needed not for this type", curr.name);
120 | gchar *error8 = g_strdup_printf("Parser data of setting %s should not be NULL. It's needed for this type", curr.name);
121 | bool is_rule = !curr.value; // if it doesn't have a 'value' it's a rule
122 | ASSERTm(error7, !curr.parser);
123 | ASSERTm(error8, is_rule || curr.parser_data);
124 | g_free(error7);
125 | g_free(error8);
126 | break;
127 | default: ;
128 | gchar *error20 = g_strdup_printf("You should make a test for type %i", curr.type);
129 | FAILm(error20);
130 | break;
131 | }
132 | }
133 | PASS();
134 | }
135 |
136 | SUITE(suite_settings_data)
137 | {
138 | RUN_TEST(test_names_valid);
139 | RUN_TEST(test_description_valid);
140 | RUN_TEST(test_type_valid);
141 | RUN_TEST(test_section_valid);
142 | RUN_TEST(test_default_value_valid);
143 | RUN_TEST(test_value_non_null);
144 | RUN_TEST(test_valid_parser_and_data_per_type);
145 | }
146 | /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
147 |
--------------------------------------------------------------------------------
/test/test-install.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Throw error any time a command fails
4 | set -euo pipefail
5 |
6 | # Export parameters so they are useable by subshells and make
7 | export BASE="$(dirname "$(dirname "$(readlink -f "$0")")")"
8 | export DESTDIR="${BASE}/install"
9 | export PREFIX="/testprefix"
10 | export SYSCONFDIR="/sysconfdir"
11 | export SYSCONFFILE="${SYSCONFDIR}/dunst/dunstrc"
12 | export SYSTEMD=1
13 | export SERVICEDIR_SYSTEMD="/systemd"
14 | export SERVICEDIR_DBUS="/dbus"
15 |
16 | do_make() { # for convenience/conciseness
17 | make -C "${BASE}" "$@"
18 | }
19 |
20 | check_dest() {
21 | # Check file list given on stdin and see if all are actually present
22 | diff -u <(find "${DESTDIR}" -type f -printf "%P\n" | sort) <(sort -)
23 | }
24 |
25 | do_make install
26 |
27 | check_dest <
4 | #include
5 | #include
6 | #include
7 |
8 | #include "../src/log.h"
9 | #include "../src/settings.h"
10 | #include "helpers.h"
11 |
12 | const char *base;
13 |
14 | SUITE_EXTERN(suite_settings_data);
15 | SUITE_EXTERN(suite_utils);
16 | SUITE_EXTERN(suite_option_parser);
17 | SUITE_EXTERN(suite_notification);
18 | SUITE_EXTERN(suite_markup);
19 | SUITE_EXTERN(suite_misc);
20 | SUITE_EXTERN(suite_icon);
21 | SUITE_EXTERN(suite_queues);
22 | SUITE_EXTERN(suite_dunst);
23 | SUITE_EXTERN(suite_log);
24 | SUITE_EXTERN(suite_menu);
25 | SUITE_EXTERN(suite_dbus);
26 | SUITE_EXTERN(suite_setting);
27 | SUITE_EXTERN(suite_ini);
28 | SUITE_EXTERN(suite_icon_lookup);
29 | SUITE_EXTERN(suite_draw);
30 | SUITE_EXTERN(suite_rules);
31 | SUITE_EXTERN(suite_input);
32 |
33 | GREATEST_MAIN_DEFS();
34 |
35 | int main(int argc, char *argv[]) {
36 | char *prog = realpath(argv[0], NULL);
37 | if (!prog) {
38 | fprintf(stderr, "Cannot determine actual path of test executable: %s\n", strerror(errno));
39 | exit(1);
40 | }
41 | base = dirname(prog);
42 |
43 | /* By default do not print out warning messages, when executing tests.
44 | * But allow, if DUNST_TEST_LOG=1 is set in environment. */
45 | const char *log = getenv("DUNST_TEST_LOG");
46 | bool printlog = log && atoi(log) ? true : false;
47 | dunst_log_init(!printlog);
48 |
49 |
50 | // initialize settings
51 | char *config_path = g_strconcat(base, "/data/dunstrc.default", NULL);
52 | load_settings(config_path);
53 |
54 | GREATEST_MAIN_BEGIN();
55 | RUN_SUITE(suite_utils);
56 | RUN_SUITE(suite_option_parser);
57 | RUN_SUITE(suite_notification);
58 | RUN_SUITE(suite_markup);
59 | RUN_SUITE(suite_misc);
60 | RUN_SUITE(suite_icon);
61 | RUN_SUITE(suite_queues);
62 | RUN_SUITE(suite_dunst);
63 | RUN_SUITE(suite_log);
64 | RUN_SUITE(suite_menu);
65 | RUN_SUITE(suite_settings_data);
66 | RUN_SUITE(suite_dbus);
67 | RUN_SUITE(suite_setting);
68 | RUN_SUITE(suite_icon_lookup);
69 | RUN_SUITE(suite_draw);
70 | RUN_SUITE(suite_rules);
71 | RUN_SUITE(suite_input);
72 |
73 | base = NULL;
74 | g_free(config_path);
75 | free(prog);
76 |
77 | // this returns the error code
78 | GREATEST_MAIN_END();
79 | }
80 | /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
81 |
--------------------------------------------------------------------------------
/test/utils.c:
--------------------------------------------------------------------------------
1 | #include "../src/utils.c"
2 | #include "greatest.h"
3 |
4 | TEST test_string_replace_char(void)
5 | {
6 | char *text = g_malloc(128 * sizeof(char));
7 |
8 | strcpy(text, "a aa aaa");
9 | ASSERT_STR_EQ("b bb bbb", string_replace_char('a', 'b', text));
10 |
11 | strcpy(text, "Nothing to replace");
12 | ASSERT_STR_EQ("Nothing to replace", string_replace_char('s', 'a', text));
13 |
14 | strcpy(text, "");
15 | ASSERT_STR_EQ("", string_replace_char('a', 'b', text));
16 |
17 | g_free(text);
18 | PASS();
19 | }
20 |
21 | /*
22 | * We trust that string_replace_all and string_replace properly reallocate
23 | * memory if the result is longer than the given string, no real way to test for
24 | * that far as I know.
25 | */
26 |
27 | TEST test_string_replace_all(void)
28 | {
29 | char *text = g_malloc(128 * sizeof(char));
30 |
31 | strcpy(text, "aaaaa");
32 | ASSERT_STR_EQ("bbbbb", (text = string_replace_all("a", "b", text)));
33 |
34 | strcpy(text, "");
35 | ASSERT_STR_EQ("", (text = string_replace_all("a", "b", text)));
36 |
37 | strcpy(text, "Nothing to replace");
38 | ASSERT_STR_EQ((text = string_replace_all("z", "a", text)), "Nothing to replace");
39 |
40 | strcpy(text, "Reverse this");
41 | ASSERT_STR_EQ("Reverse sith", (text = string_replace_all("this", "sith", text)));
42 |
43 | strcpy(text, "abcdabc");
44 | ASSERT_STR_EQ("xyzabcdxyzabc", (text = string_replace_all("a", "xyza", text)));
45 |
46 | g_free(text);
47 | PASS();
48 | }
49 |
50 | TEST test_string_append(void)
51 | {
52 | char *exp;
53 |
54 | ASSERT_STR_EQ("text_sep_bit", (exp = string_append(g_strdup("text"), "bit", "_sep_")));
55 | g_free(exp);
56 | ASSERT_STR_EQ("textbit", (exp = string_append(g_strdup("text"), "bit", NULL)));
57 | g_free(exp);
58 | ASSERT_STR_EQ("textbit", (exp = string_append(g_strdup("text"), "bit", "")));
59 | g_free(exp);
60 |
61 | ASSERT_STR_EQ("text", (exp = string_append(g_strdup("text"), "", NULL)));
62 | g_free(exp);
63 | ASSERT_STR_EQ("text", (exp = string_append(g_strdup("text"), "", "_sep_")));
64 | g_free(exp);
65 |
66 | ASSERT_STR_EQ("b", (exp = string_append(g_strdup(""), "b", NULL)));
67 | g_free(exp);
68 | ASSERT_STR_EQ("b", (exp = string_append(NULL, "b", "_sep_")));
69 | g_free(exp);
70 |
71 | ASSERT_STR_EQ("a", (exp = string_append(g_strdup("a"), "", NULL)));
72 | g_free(exp);
73 | ASSERT_STR_EQ("a", (exp = string_append(g_strdup("a"), NULL, "_sep_")));
74 | g_free(exp);
75 |
76 | ASSERT_STR_EQ("", (exp = string_append(g_strdup(""), "", "_sep_")));
77 | g_free(exp);
78 | ASSERT_EQ(NULL, (exp = string_append(NULL, NULL, "_sep_")));
79 | g_free(exp);
80 |
81 | PASS();
82 | }
83 |
84 | TEST test_string_strip_quotes(void)
85 | {
86 | char *exp = string_strip_quotes(NULL);
87 | ASSERT_FALSE(exp);
88 |
89 | ASSERT_STR_EQ("NewString", (exp = string_strip_quotes("NewString")));
90 | g_free(exp);
91 |
92 | ASSERT_STR_EQ("becomes unquoted", (exp = string_strip_quotes("\"becomes unquoted\"")));
93 | g_free(exp);
94 |
95 | ASSERT_STR_EQ("\"stays quoted", (exp = string_strip_quotes("\"stays quoted")));
96 | g_free(exp);
97 |
98 | ASSERT_STR_EQ("stays quoted\"", (exp = string_strip_quotes("stays quoted\"")));
99 | g_free(exp);
100 |
101 | ASSERT_STR_EQ("stays \"quoted\"", (exp = string_strip_quotes("stays \"quoted\"")));
102 | g_free(exp);
103 |
104 | ASSERT_STR_EQ(" \"stays quoted\"", (exp = string_strip_quotes(" \"stays quoted\"")));
105 | g_free(exp);
106 |
107 | PASS();
108 | }
109 |
110 | TEST test_string_strip_delimited(void)
111 | {
112 | char *text = g_malloc(128 * sizeof(char));
113 |
114 | strcpy(text, "A string_strip_delimited test");
115 | string_strip_delimited(text, '<', '>');
116 | ASSERT_STR_EQ("A string_strip_delimited test", text);
117 |
118 | strcpy(text, "Remove ");
119 | string_strip_delimited(text, '<', '>');
120 | ASSERT_STR_EQ("Remove html tags", text);
121 |
122 | strcpy(text, "");
123 | string_strip_delimited(text, '<', '>');
124 | ASSERT_STR_EQ("", text);
125 |
126 | strcpy(text, "Nothing is done if there are no delimiters in the string");
127 | string_strip_delimited(text, '<', '>');
128 | ASSERT_STR_EQ("Nothing is done if there are no delimiters in the string", text);
129 |
130 | strcpy(text, "We <3 dunst");
131 | string_strip_delimited(text, '<', '>');
132 | ASSERT_STR_EQ("We <3 dunst", text);
133 |
134 | strcpy(text, "We <3 dunst");
135 | string_strip_delimited(text, '<', '>');
136 | ASSERT_STR_EQ("We <3 dunst", text);
137 |
138 | strcpy(text, "dunst > the rest");
139 | string_strip_delimited(text, '<', '>');
140 | ASSERT_STR_EQ("dunst > the rest", text);
141 |
142 | g_free(text);
143 | PASS();
144 | }
145 |
146 | TEST test_string_to_path(void)
147 | {
148 | char *ptr, *exp;
149 | char *home = getenv("HOME");
150 |
151 | exp = "/usr/local/bin/script";
152 | ASSERT_STR_EQ(exp, (ptr = string_to_path(g_strdup(exp))));
153 | free(ptr);
154 |
155 | exp = "~path/with/wrong/tilde";
156 | ASSERT_STR_EQ(exp, (ptr = string_to_path(g_strdup(exp))));
157 | free(ptr);
158 |
159 | ASSERT_STR_EQ((exp = g_strconcat(home, "/.path/with/tilde", NULL)),
160 | (ptr = string_to_path(g_strdup("~/.path/with/tilde"))));
161 | free(exp);
162 | free(ptr);
163 |
164 | ASSERT_STR_EQ((exp = g_strconcat(home, "/.path/with/tilde and some space", NULL)),
165 | (ptr = string_to_path(g_strdup("~/.path/with/tilde and some space"))));
166 | free(exp);
167 | free(ptr);
168 |
169 | PASS();
170 | }
171 |
172 | TEST test_string_to_time(void)
173 | {
174 | char *input[] = { "5000 ms", "5000ms", "100", "10s", "2m", "11h", "9d", " 5 ms ", NULL };
175 | gint64 exp[] = { 5000, 5000, 100000, 10000, 120000, 39600000, 777600000, 5, 0};
176 |
177 | int i = 0;
178 | while (input[i]){
179 | ASSERT_EQ_FMT(string_to_time(input[i]), exp[i]*1000, "%ld");
180 | i++;
181 | }
182 |
183 | PASS();
184 | }
185 |
186 | SUITE(suite_utils)
187 | {
188 | RUN_TEST(test_string_replace_char);
189 | RUN_TEST(test_string_replace_all);
190 | RUN_TEST(test_string_append);
191 | RUN_TEST(test_string_strip_quotes);
192 | RUN_TEST(test_string_strip_delimited);
193 | RUN_TEST(test_string_to_path);
194 | RUN_TEST(test_string_to_time);
195 | }
196 | /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
197 |
--------------------------------------------------------------------------------