Compare commits

..

54 Commits

Author SHA1 Message Date
fwSmit
6ee460335d
Merge pull request #883 from FuJa0815/fixed_doxygen
fixed doxygen generation
2021-06-29 17:31:19 +02:00
Jan Führer
988b8d2747
fixed doxygen generation 2021-06-28 10:22:33 +02:00
Nikos Tsipinakis
9f4f110c53
Merge pull request #862 from fwSmit/dbus-highlight
Add dbus hightlight hint
2021-05-29 22:20:32 +02:00
fwsmit
e898fa589a docs: Document all dbus hints 2021-05-28 12:59:23 +02:00
fwsmit
702abc7a03 dbus: Add a hint for changing the highlight color 2021-05-28 12:59:23 +02:00
Nikos Tsipinakis
b77f76f02e
Merge pull request #854 from fwSmit/wayland-hidpi
wayland hidpi support
2021-05-26 20:31:50 +02:00
Nikos Tsipinakis
cf091bea37
Merge pull request #840 from fwSmit/master
Small fixes
2021-05-26 20:09:28 +02:00
Nikos Tsipinakis
1040febfb9
Merge pull request #847 from fwSmit/log-testing
logging: Set loglevel to error when testing.
2021-05-26 20:06:13 +02:00
Nikos Tsipinakis
652cb7efb4
Merge pull request #848 from lukasrad02/feature-mouse-action-context
Add mouse action to show context menu
2021-05-26 20:04:39 +02:00
Nikos Tsipinakis
7d91f978eb
Merge pull request #849 from fwSmit/wayland-surface
wayland: fix NULL pointer dereference
2021-05-26 19:52:39 +02:00
Nikos Tsipinakis
0994c57b3e
Merge pull request #856 from mgsloan/patch-1
Fix link to "Building" section in readme
2021-05-26 19:51:58 +02:00
fwSmit
cb6f2ca3b3
Merge pull request #867 from hastinbe/icon-static-capability
Add icon-static server capability
2021-05-16 11:19:19 +02:00
Beau Hastings
bd6abfcc5d
Add icon-static server capability
Dunst supports displaying the first frame of an icon, thus it should
announce that capability.
2021-05-14 16:54:51 +08:00
fwsmit
1d3822ee8b icon: Replace deprecated g_memdup with g_memdup2 where possible
This avoids a potential conversion from gsize to guint and silences a
warning.
2021-05-02 18:13:46 +02:00
fwsmit
ea531ca9ae wayland: Return the largest scale when output cannot be found
When follow mode != none dunst cannot detect what output is being used
to display the notification (yet). With this commit dunst falls back to
using the largest scale from any ouput. The compositor should scale the
buffer down again if the scale of the output is smaller that the used
scale.
2021-05-02 13:22:07 +02:00
fwsmit
e69adcefea wayland: Add better detection for the current output
When there's only one output, just return that output.
2021-05-02 13:21:48 +02:00
fwSmit
f2017eb3d6
Merge pull request #858 from bebehei/werror-on-ci
Add CFLAGS with -Werror for CI
2021-05-02 11:44:00 +02:00
Benedikt Heine
d9282b7f86 Add EXTRA_CFLAGS with -Werror for CI 2021-05-01 14:18:35 +02:00
Lukas Radermacher
efac6e70b0 Update docs
Document new mouse action open_url and rule action_name in dunstrc and docs/dunst.5.pod.
2021-04-28 21:43:21 +02:00
Lukas Radermacher
0dfc4ed738 Implement open_url and action_name 2021-04-28 21:43:20 +02:00
Lukas Radermacher
b49925c86d Add "open_url" mouse action 2021-04-28 21:43:20 +02:00
Lukas Radermacher
8e80871c50 Add rule action_name 2021-04-28 21:43:20 +02:00
Lukas Radermacher
d5ee1febca Add notification property default_action_name 2021-04-28 21:43:20 +02:00
Lukas Radermacher
941c527af9 Update docs
Document new mouse actions in dunstrc and docs/dunst.5.pod.
2021-04-28 21:43:20 +02:00
Lukas Radermacher
b66f8f362d Change input.c to handle new actions 2021-04-28 21:43:11 +02:00
Michael Sloan
85ff05062c
Fix link to "Building" section in readme 2021-04-24 16:56:35 -06:00
fwsmit
db5e6ce8f4 wayland: Add HiDPI support
This commit implements support for HiDPI rendering for wayland. X11
should be unaffected by this.
It is implemented by scaling everything that's rendered by a scale
factor that's obtained from the wayland protocol. All sizes before
rendering remain the same, so the same settings should provide the same
output for different scaling factors, only scaled by that factor.
2021-04-22 13:09:35 +02:00
fwsmit
aad4dbaf3d wayland: Some minor cleanup 2021-04-22 13:05:31 +02:00
fwsmit
98a61f0896 wayland: implement get_scale function for hidpi support 2021-04-22 13:04:49 +02:00
fwsmit
7c6620c92d wayland: Remove accidental if statement 2021-04-20 21:55:37 +02:00
fwsmit
75af42c83a doc: internal: Add note about the way tests are being compiled 2021-04-15 18:35:37 +02:00
fwsmit
c4e428e9d5 build: Clean up docs/dunst.5 on make clean 2021-04-09 16:25:06 +02:00
fwsmit
5c0d7ea662 docs: Update dependencies in README
The Gtk3 dependencie was dropped #376, so we better remove it from the
list
2021-04-08 17:44:13 +02:00
fwsmit
176aad4f3c input: Wake on action close_all
This makes sure the window also gets updated after closing all
notifications. Otherwise this actions seems to do nothing until dunst
wakes up from something else.
2021-04-06 19:02:10 +02:00
fwsmit
af49b76586 wayland: fix NULL pointer dereference 2021-04-06 17:18:48 +02:00
Lukas Radermacher
6c61f3e5e2 Add notification-specific context menu 2021-04-05 16:06:15 +02:00
Lukas Radermacher
bec5e9d25a Add "context_all" mouse action 2021-04-05 16:00:08 +02:00
Lukas Radermacher
790342b913 Add "context" mouse action 2021-04-05 14:14:52 +02:00
fwsmit
4234813417 logging: Set loglevel to error when testing.
It's useful to see what crashed your program when testing. Unfortunately
there's no way to distinguish the DIE way of logging from critical
logging.
This commit also fixes a bug where loglevel error wouldn't function,
because the message level also contains other flags.
2021-04-03 19:52:25 +02:00
fwsmit
d65f69b4db Work around glib bug for better test output 2021-03-16 16:09:09 +01:00
fwsmit
b75d35adb4 queues: correctly sort notifications even when sort is false.
The sort name is a bit misleading. It should actually be called
sort_urgency or something.
Notifications are still not sorted based on time, but ID.
2021-03-16 16:09:00 +01:00
Nikos Tsipinakis
3acffdb194
Merge pull request #814 from fwSmit/wayland-improvements
Wayland improvements
2021-03-08 09:19:26 +02:00
Nikos Tsipinakis
7e22272ebb
Merge pull request #837 from fwSmit/dunst-readme
Dunst readme
2021-03-08 09:16:36 +02:00
fwsmit
7292bfcd89 docs: add section for autostarting dunst 2021-02-27 23:14:34 +01:00
fwsmit
8413ade9d7 readme: add screenshots and improve presentation 2021-02-27 23:14:34 +01:00
Nikos Tsipinakis
54b665898f
Merge pull request #834 from fwSmit/fallback-x11
Wayland: fallback to X11 output when initialization fails
2021-02-27 14:53:55 +02:00
fwsmit
f8a2ff48b3 output: remove unused win_visible function
This was used nowhere, and not even implemented for the wayland output.
2021-02-24 13:32:06 +01:00
fwsmit
a8b2058fcf Wayland: fallback to X11 output when initialization fails 2021-02-24 13:32:06 +01:00
Nikos Tsipinakis
ceca7cfcc7 Add release checklist 2021-02-21 14:37:57 +02:00
Nikos Tsipinakis
598ec5797e Start new release cycle 2021-02-21 14:28:37 +02:00
fwsmit
0f588998fe Wayland: fix "follow=none" not working. 2021-02-06 00:42:26 +01:00
fwsmit
500b00b344 Wayland: fix lots of messages when idle_threshold=0 2021-02-06 00:42:15 +01:00
fwsmit
36186d37ea Wayland: Implement foreign toplevel manager protocol
Used for detecting if there is a fullscreen application open.
2021-02-06 00:42:04 +01:00
fwsmit
d7f93a3a69 Wayland: added basic touch support
Copy pasted from mako. I could not test.
2021-02-02 23:33:26 +01:00
49 changed files with 1955 additions and 246 deletions

View File

@ -25,6 +25,7 @@ jobs:
env:
CC: ${{ matrix.CC }}
EXTRA_CFLAGS: "-Werror"
steps:
- uses: actions/checkout@v2
with:

View File

@ -1,5 +1,11 @@
# Dunst changelog
## Unreleased
### Added
### Changed
### Fixed
## 1.6.1 - 2021-02-21:
### Fixed

View File

@ -15,6 +15,10 @@
- Add the comments to the prototype. Doxygen will merge the protoype and implementation documentation anyways.
Except for **static** methods, add the documentation header to the implementation and *not to the prototype*.
- Member documentation should happen with `/**<` and should span to the right side of the member
- Test files that have the same name as a file in src/\* can include the
associated .c file. This is because they are being compiled INSTEAD of the src
file.
## Log messages

View File

@ -3,7 +3,7 @@
include config.mk
VERSION := "1.6.1 (2021-02-21)"
VERSION := "1.6.1-non-git"
ifneq ($(wildcard ./.git/),)
VERSION := $(shell ${GIT} describe --tags)
endif
@ -148,7 +148,8 @@ service-systemd:
endif
ifneq (0,${WAYLAND})
wayland-protocols: src/wayland/protocols/wlr-layer-shell-unstable-v1.xml
wayland-protocols: src/wayland/protocols/wlr-layer-shell-unstable-v1.xml src/wayland/protocols/wlr-foreign-toplevel-management-unstable-v1.xml
# TODO: write this shorter
mkdir -p src/wayland/protocols
wayland-scanner private-code ${DATA_DIR_WAYLAND_PROTOCOLS}/stable/xdg-shell/xdg-shell.xml src/wayland/protocols/xdg-shell.h
wayland-scanner client-header ${DATA_DIR_WAYLAND_PROTOCOLS}/stable/xdg-shell/xdg-shell.xml src/wayland/protocols/xdg-shell-client-header.h
@ -158,6 +159,8 @@ wayland-protocols: src/wayland/protocols/wlr-layer-shell-unstable-v1.xml
wayland-scanner private-code src/wayland/protocols/wlr-layer-shell-unstable-v1.xml src/wayland/protocols/wlr-layer-shell-unstable-v1.h
wayland-scanner client-header src/wayland/protocols/idle.xml src/wayland/protocols/idle-client-header.h
wayland-scanner private-code src/wayland/protocols/idle.xml src/wayland/protocols/idle.h
wayland-scanner client-header src/wayland/protocols/wlr-foreign-toplevel-management-unstable-v1.xml src/wayland/protocols/wlr-foreign-toplevel-management-unstable-v1-client-header.h
wayland-scanner private-code src/wayland/protocols/wlr-foreign-toplevel-management-unstable-v1.xml src/wayland/protocols/wlr-foreign-toplevel-management-unstable-v1.h
endif
.PHONY: clean clean-dunst clean-dunstify clean-doc clean-tests clean-coverage clean-coverage-run clean-wayland-protocols
@ -175,6 +178,7 @@ clean-dunstify:
clean-doc:
rm -f docs/dunst.1
rm -f docs/dunst.5
rm -f docs/dunstctl.1
rm -fr docs/internal/html
rm -fr docs/internal/coverage

106
README.md
View File

@ -1,34 +1,95 @@
[![main](https://github.com/dunst-project/dunst/workflows/main/badge.svg)](https://github.com/dunst-project/dunst/actions?query=workflow%3Amain) [![codecov](https://codecov.io/gh/dunst-project/dunst/branch/master/graph/badge.svg)](https://codecov.io/gh/dunst-project/dunst)
## Dunst
# Dunst
* [Wiki][wiki]
* [Description](#description)
* [Compiling](#compiling)
<i>A highly configurable and lightweight notification daemon.</i>
![music](contrib/screenshots/music.png)
## Table of Contents
* [Features](#features)
* [Building](#building)
* [Documentation](#documentation)
* [Copyright](#copyright)
## Description
# Features
Dunst is a highly configurable and lightweight notification daemon.
## ⚙️ Highly customizable
Customize fonts, icons, timeouts, and more. Are you unhappy with the default
shortcuts and colors? No worries, you can change these all with a simple
configuration file tweak.
_click the images to see the dunstrc_
<a href="https://gist.github.com/NNBnh/5f6e601a6a82a6ed43b1959698758141">
<img alt="screenshot1" src="contrib/screenshots/screenshot1_cut.png">
</a>
<a href="https://gist.github.com/fwSmit/9127d988b07bcec9d869f2c927d0f616">
<img alt="screenshot2" src="contrib/screenshots/screenshot2_cut.png">
</a>
## 📜 Scripting
<a href="https://gitlab.manjaro.org/profiles-and-settings/manjaro-theme-settings/-/blob/master/skel/.config/dunst/dunstrc">
<img alt="screenshot_urgency" src="contrib/screenshots/screenshot_urgency.png">
</a>
Run custom scripts on notifications matching a specified pattern. Have espeak
read out your notifications, or play a song when your significant other signs on
in pidgin!
## 📋 Rules
Change the look or behavior of notifications matching a specified pattern. You
could use this to change the color of message notifications from your favorite
jabber buddies, or to prevent important work email notifications from
disappearing until you manually dismiss them.
## ⏸️ Pause
If you want to take a break and not receive any notifications for a while, just
pause dunst. All notifications will be saved for you to catch up
later.
## 🕘 History
Catch an unread notification disappearing from the corner of your eye? Just tap
a keyboard shortcut to replay the last notification, or continue tapping to see
your notification history.
# Documentation
Most documentation can be found in dunst's man pages. In
[**dunst(1)**](docs/dunst.1.pod) contains some general instructions on how
to run dunst and in
[**dunst(5)**](docs/dunst.5.pod) all of dunst's configuration options are
explained.
On the dunst [wiki][wiki] you can find guides and installation instructions and
on the dunst [website][website] there is a [FAQ][FAQ] with common issues.
## Installation
Dunst is available in many package repositories. If it's not available in your
distro's repositories, don't worry, it's not hard to build it yourself.
### Dependencies
Dunst has a number of build dependencies that must be present before attempting configuration. The names are different depending on [distribution](https://github.com/dunst-project/dunst/wiki/Dependencies):
- dbus
- dbus (runtime)
- libxinerama
- libxrandr
- libxss
- glib
- pango/cairo
- libgtk-3-dev
- libnotify (for dunstify only)
- libnotify (optional, for dunstify)
- wayland-client (can build without, see [make parameters](#make-parameters))
- wayland-protocols (optional, for recompiling protocols)
The names will be different depending on your [distribution](https://github.com/dunst-project/dunst/wiki/Dependencies).
### Building
```
@ -49,14 +110,23 @@ sudo make install
- `WAYLAND=(0|1)`: Disable/Enable wayland support. (Default: 1 (enabled))
- `SERVICEDIR_SYSTEMD=<PATH>`: The path to put the systemd user service file. Unused, if `SYSTEMD=0`. (Default: detected via `pkg-config`)
- `SERVICEDIR_DBUS=<PATH>`: The path to put the dbus service file. (Default: detected via `pkg-config`)
- `EXTRA_CFLAGS=<FLAGS>`: Additional flags for the compiler.
**Make sure to run all make calls with the same parameter set. So when building with `make PREFIX=/usr`, you have to install it with `make PREFIX=/usr install`, too.**
Checkout the [wiki][wiki] for more information.
## Bug reports
Please use the [issue tracker][issue-tracker] provided by GitHub to send us bug reports or feature requests. You can also join us on the IRC channel `#dunst` on Freenode.
Please use the [issue tracker][issue-tracker] provided by GitHub to send us bug reports or feature requests.
## Screenshots
<a href="https://gist.github.com/MCotocel/2b34486ae59ccda4319fcb93454d212c">
<img alt="screenshot3" src="contrib/screenshots/screenshot3_cut.png">
</a>
<a href="https://gitlab.manjaro.org/profiles-and-settings/manjaro-theme-settings/-/blob/master/skel/.config/dunst/dunstrc">
<img alt="progress" src="https://user-images.githubusercontent.com/23078054/102542111-98b01e00-40b1-11eb-967e-bc952430bd06.png">
</a>
## Maintainers
@ -65,13 +135,13 @@ Please use the [issue tracker][issue-tracker] provided by GitHub to send us bug
## Author
written by Sascha Kruse <dunst@knopwob.de>
Written by Sascha Kruse <dunst@knopwob.de>
## Copyright
copyright 2013 Sascha Kruse and contributors (see [`LICENSE`](./LICENSE) for licensing information)
If you feel that copyrights are violated, please send me an email.
Copyright 2013 Sascha Kruse and contributors (see [`LICENSE`](./LICENSE) for licensing information)
[issue-tracker]: https://github.com/dunst-project/dunst/issues
[wiki]: https://github.com/dunst-project/dunst/wiki
[website]: https://dunst-project.org
[FAQ]: https://dunst-project.org/faq

View File

@ -6,6 +6,7 @@ DATADIR ?= ${PREFIX}/share
# around for backwards compatibility
MANPREFIX ?= ${DATADIR}/man
MANDIR ?= ${MANPREFIX}
EXTRA_CFLAGS ?=
DOXYGEN ?= doxygen
FIND ?= find
@ -36,7 +37,7 @@ endif
# flags
DEFAULT_CPPFLAGS = -D_DEFAULT_SOURCE -DVERSION=\"${VERSION}\"
DEFAULT_CFLAGS = -g --std=gnu99 -pedantic -Wall -Wno-overlength-strings -Os ${STATIC} ${ENABLE_WAYLAND}
DEFAULT_CFLAGS = -g --std=gnu99 -pedantic -Wall -Wno-overlength-strings -Os ${STATIC} ${ENABLE_WAYLAND} ${EXTRA_CFLAGS}
DEFAULT_LDFLAGS = -lm -lrt
CPPFLAGS_DEBUG := -DDEBUG_BUILD

Binary file not shown.

After

Width:  |  Height:  |  Size: 388 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 672 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 KiB

View File

@ -10,6 +10,15 @@ dunst [-conf file] [-font font] [-geometry geom] [-format fmt] [-follow mode] [-
Dunst is a highly configurable and lightweight notification daemon.
=head2 Autostarting dunst
On most installations dunst should be able to automatically be started by D-Bus
when a notification is sent. This is not recommended when multiple notification
deamons are installed, because D-Bus will not know which one to start.
Other ways of autostarting dunst include starting dunst with your desktop
environment or window manager's autostart functionality or via the provided
systemd service.
=head1 COMMAND LINE OPTIONS
=over 4
@ -160,4 +169,4 @@ If you feel that copyrights are violated, please send me an email.
=head1 SEE ALSO
dunst(5), dunstctl(1), dwm(1), dmenu(1), twmn(1), notify-send(1)
dunst(5), dunstctl(1), dmenu(1), notify-send(1)

View File

@ -249,9 +249,6 @@ Place dunst notifications on the selected layer. Using overlay
will cause notifications to be displayed above fullscreen windows, though
this may also occur at top depending on your compositor.
In Wayland, Notifications won't be delayed when in fullscreen (like when
setting B<fullscreen> to pushback in X11). This is a Wayland limitation.
The bottom layer is below all windows and above the background.
Default: overlay
@ -514,9 +511,10 @@ single notification.
To avoid the corners clipping the icon or text the corner radius will be
automatically lowered to half of the notification height if it exceeds it.
=item B<mouse_left/middle/right_click> (values: [none/do_action/close_current/close_all])
=item B<mouse_left/middle/right_click> (values: [none/do_action/close_current/close_all/context/context_all])
Defines action of mouse click.
Defines action of mouse click. A touch input in Wayland acts as a mouse left
click.
=over 4
@ -526,7 +524,13 @@ Don't do anything.
=item B<do_action> (default for mouse_middle_click)
If the notification has exactly one action, or one is marked as default, invoke it. If there are multiple and no default, open the context menu.
Invoke the action determined by the action_name rule. If there is no such
action, open the context menu.
=item B<open_url>
If the notification has exactly one url, open it. If there are multiple
ones, open the context menu.
=item B<close_current> (default for mouse_left_click)
@ -536,6 +540,14 @@ Close current notification.
Close all notifications.
=item B<context>
Open context menu for the notification.
=item B<context_all>
Open context menu for all notifications.
=back
@ -647,7 +659,7 @@ but also has a lot of extra functionality. So see more see dunstctl(1).
=head1 HISTORY
Dunst saves a number of notifications (specified by B<history_length>) in memory.
These notifications can be recalled (i.e. redisplayed) by calling
These notifications can be recalled (i.e. redisplayed) by calling
B<dunstctl history> (see dunstctl(1)). Whether these notifications will time out
like if they have been just send depends on the value of the B<sticky_history>
setting.
@ -662,9 +674,15 @@ Dunst has Wayland support since version 1.6.0. Because the Wayland protocol
is more focused on security, some things that are possible in X11 are not
possible in Wayland. Those differences are reflected in the configuration.
The main things that change are that dunst on Wayland cannot use global
hotkeys (they are deprecated anyways, use dunstctl) and it cannot detect
if an application is fullscreen. If you want to see notifications when in
fullscreen, set B<layer = overlay> in the global options.
hotkeys (they are deprecated anyways, use dunstctl).
Some dunst features on wayland might need your compositor to support a certain
protocol. Dunst will warn you if an optional feature isn't supported and will
disable the corresponding functionality.
Fullscreen detection works on wayland with some limitations (see B<fullscreen>).
If you want notifications to appear over fullscreen windows, set
B<layer = overlay> in the global options.
Note that the same limitations exist when using xwayland.
If something doesn't quite work in Wayland, please file a bug report. In the
@ -791,7 +809,7 @@ Equivalent to the C<format> setting.
The frame color color of the notification. See COLORS for possible values.
=item C<fullscreen> (X11 only)
=item C<fullscreen>
One of show, delay, or pushback.
@ -806,7 +824,13 @@ Or pushback which is equivalent to delay with the difference that already
existing notifications are paused and hidden until the focus to the fullscreen
window is lost.
See B<layer> to change fullscreen behavior in Wayland
On wayland, if B<follow> is set to mouse or keyboard, the output where the
notification is located cannot be determined. So dunst will delay or pushback if
any of the outputs is fullscreen. Since the fullscreen protocol is fairly new,
you will need a recent version of a compositor that supports it. At the time of
writing, you will need the git version of sway.
See also B<layer> to change if notifications appear above fullscreen windows in
Wayland.
Default: show
@ -850,6 +874,13 @@ later. Use C<msg_urgency> to match it.
Setting this to true will prevent the notification from being displayed
initially but will be saved in history for later viewing.
=item C<action_name>
Sets the name of the action to be invoked on do_action. If not specified, the
action set as default action or the only available action will be invoked.
Default: "default"
=back
As with the filtering attributes, each one corresponds to
@ -910,6 +941,46 @@ dunst is able to get different colors for a message via notify-send.
In order to do that you have to add a hint via the -h option.
The progress value can be set with a hint, too.
B<All hints>
See RULES for more detailed explanations for some options.
=over 4
=item B<fgcolor>:
Foreground cololor
=item B<bgcolor>:
Background color
=item B<frcolor>:
Frame color
=item B<hlcolor>:
Highlight color
=item B<value>:
Progress value.
=item B<image-path>:
Icon name. This may be a path or just the icon name.
=item B<image-data>:
A stream of raw image data.
=item B<category>:
The category.
=item B<desktop_entry>:
The desktop entry.
=item B<transient>:
The transient value.
=back
B<Examples>
=over 4
=item notify-send -h string:fgcolor:#ff4444
@ -1010,4 +1081,4 @@ If you feel that copyrights are violated, please send me an email.
=head1 SEE ALSO
dunstctl(1), dwm(1), dmenu(1), twmn(1), notify-send(1)
dunst(1), dunstctl(1), dmenu(1), notify-send(1)

View File

@ -0,0 +1,33 @@
# Main repo
- [ ] Verify that the changelog is up to date
- [ ] Write release notes (Only if non-patch release)
- [ ] Verify that the working directory is clean and on the master branch
- [ ] Change the version in the Makefile to "x.x.x (iso-date)"
- [ ] Update changelog `Unreleased` entry to the new version
- [ ] Commit changes (Commit title: `Dunst vX.X.X`)
- [ ] Tag commit, make sure it's an annotated tag (git tag -a) (Tag title: Dunst vX.X.X)
- [ ] Push commits
- [ ] Push tags
# Dunst-project.org
- [ ] Update release number in the download page
- [ ] Update release date in the download page
- [ ] Update version number in the download link
- [ ] Copy release notes to the download page (Only if non-patch release)
- [ ] Copy changelog to the changelog page
- [ ] Copy documentation to the documentation page
- [ ] Verify that they look fine when rendered
- [ ] Commit changes
- [ ] Run deploy script
- [ ] Push to main website repo
- [ ] Push to gh-pages
# Main repo
- [ ] Copy release notes to githubs release feature
- [ ] Publish release on github
- [ ] Update maint branch to point to master
- [ ] Create new Unreleased section for the changelog
- [ ] Update Makefile version to -non-git
- [ ] Commit & push

View File

@ -277,10 +277,14 @@
# Defines list of actions for each mouse event
# Possible values are:
# * none: Don't do anything.
# * do_action: If the notification has exactly one action, or one is marked as default,
# invoke it. If there are multiple and no default, open the context menu.
# * do_action: Invoke the action determined by the action_name rule. If there is no
# such action, open the context menu.
# * open_url: If the notification has exactly one url, open it. If there are multiple
# ones, open the context menu.
# * close_current: Close current notification.
# * close_all: Close all notifications.
# * context: Open context menu for the notification.
# * context_all: Open context menu for all notifications.
# These values can be strung together for each mouse event, and
# will be executed in sequence.
mouse_left_click = close_current
@ -373,6 +377,7 @@
# set_transient
# timeout
# urgency
# action_name
#
# Shell-like globbing will get expanded.
#

View File

@ -307,6 +307,7 @@ static void dbus_cb_GetCapabilities(
g_variant_builder_add(builder, "s", "actions");
g_variant_builder_add(builder, "s", "body");
g_variant_builder_add(builder, "s", "body-hyperlinks");
g_variant_builder_add(builder, "s", "icon-static");
for (int i = 0; i < sizeof(stack_tag_hints)/sizeof(*stack_tag_hints); ++i)
g_variant_builder_add(builder, "s", stack_tag_hints[i]);
@ -385,6 +386,11 @@ static struct notification *dbus_message_to_notification(const gchar *sender, GV
g_variant_unref(dict_value);
}
if ((dict_value = g_variant_lookup_value(hints, "hlcolor", G_VARIANT_TYPE_STRING))) {
n->colors.highlight = g_variant_dup_string(dict_value, NULL);
g_variant_unref(dict_value);
}
if ((dict_value = g_variant_lookup_value(hints, "category", G_VARIANT_TYPE_STRING))) {
n->category = g_variant_dup_string(dict_value, NULL);
g_variant_unref(dict_value);

View File

@ -51,7 +51,6 @@ void draw_setup(void)
const struct output *out = output_create(settings.force_xwayland);
output = out;
out->init();
win = out->win_create();
pango_fdesc = pango_font_description_from_string(settings.font);
@ -142,10 +141,11 @@ static struct color layout_get_sepcolor(struct colored_layout *cl,
static void layout_setup_pango(PangoLayout *layout, int width)
{
int scale = output->get_scale();
pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
pango_layout_set_width(layout, width * PANGO_SCALE);
pango_layout_set_width(layout, width * scale * PANGO_SCALE);
pango_layout_set_font_description(layout, pango_fdesc);
pango_layout_set_spacing(layout, settings.line_height * PANGO_SCALE);
pango_layout_set_spacing(layout, settings.line_height * scale * PANGO_SCALE);
PangoAlignment align;
switch (settings.align) {
@ -193,9 +193,20 @@ static bool have_progress_bar(const struct notification *n)
return (n->progress >= 0 && settings.progress_bar == true);
}
static void get_text_size(PangoLayout *l, int *w, int *h, int scale) {
pango_layout_get_pixel_size(l, w, h);
// scale the size down, because it may be rendered at higher DPI
if (w)
*w /= scale;
if (h)
*h /= scale;
}
static struct dimensions calculate_dimensions(GSList *layouts)
{
struct dimensions dim = { 0 };
int scale = output->get_scale();
const struct screen_info *scr = output->get_active_screen();
if (have_dynamic_width()) {
@ -222,10 +233,10 @@ static struct dimensions calculate_dimensions(GSList *layouts)
for (GSList *iter = layouts; iter; iter = iter->next) {
struct colored_layout *cl = iter->data;
int w=0,h=0;
pango_layout_get_pixel_size(cl->l, &w, &h);
get_text_size(cl->l, &w, &h, scale);
if (cl->icon) {
h = MAX(cairo_image_surface_get_height(cl->icon), h);
w += cairo_image_surface_get_width(cl->icon) + settings.h_padding;
h = MAX(get_icon_height(cl->icon, scale), h);
w += get_icon_width(cl->icon, scale) + settings.h_padding;
}
h = MAX(settings.notification_height, h + settings.padding * 2);
dim.h += h;
@ -251,15 +262,15 @@ static struct dimensions calculate_dimensions(GSList *layouts)
w -= 2 * settings.h_padding;
w -= 2 * settings.frame_width;
if (cl->icon) {
w -= cairo_image_surface_get_width(cl->icon) + get_text_icon_padding();
w -= get_icon_width(cl->icon, scale) + get_text_icon_padding();
}
layout_setup_pango(cl->l, w);
/* re-read information */
pango_layout_get_pixel_size(cl->l, &w, &h);
get_text_size(cl->l, &w, &h, scale);
if (cl->icon) {
h = MAX(cairo_image_surface_get_height(cl->icon), h);
w += cairo_image_surface_get_width(cl->icon) + settings.h_padding;
h = MAX(get_icon_height(cl->icon, scale), h);
w += get_icon_width(cl->icon, scale) + settings.h_padding;
}
h = MAX(settings.notification_height, h + settings.padding * 2);
dim.h += h;
@ -300,6 +311,7 @@ static struct colored_layout *layout_init_shared(cairo_t *c, const struct notifi
{
struct colored_layout *cl = g_malloc(sizeof(struct colored_layout));
cl->l = layout_create(c);
int scale = output->get_scale();
if (!settings.word_wrap) {
PangoEllipsizeMode ellipsize;
@ -347,7 +359,7 @@ static struct colored_layout *layout_init_shared(cairo_t *c, const struct notifi
width -= 2 * settings.h_padding;
width -= 2 * settings.frame_width;
if (cl->icon) {
width -= cairo_image_surface_get_width(cl->icon) + get_text_icon_padding();
width -= get_icon_width(cl->icon, scale) + get_text_icon_padding();
}
layout_setup_pango(cl->l, width);
}
@ -368,6 +380,7 @@ static struct colored_layout *layout_from_notification(cairo_t *c, struct notifi
{
struct colored_layout *cl = layout_init_shared(c, n);
int scale = output->get_scale();
/* markup */
GError *err = NULL;
@ -389,8 +402,8 @@ static struct colored_layout *layout_from_notification(cairo_t *c, struct notifi
}
pango_layout_get_pixel_size(cl->l, NULL, &(n->displayed_height));
if (cl->icon) n->displayed_height = MAX(cairo_image_surface_get_height(cl->icon), n->displayed_height);
get_text_size(cl->l, NULL, &(n->displayed_height), scale);
if (cl->icon) n->displayed_height = MAX(get_icon_height(cl->icon, scale), n->displayed_height);
n->displayed_height = n->displayed_height + settings.padding * 2;
@ -436,14 +449,14 @@ static GSList *create_layouts(cairo_t *c)
}
static int layout_get_height(struct colored_layout *cl)
static int layout_get_height(struct colored_layout *cl, int scale)
{
int h;
int h_icon = 0;
int h_progress_bar = 0;
pango_layout_get_pixel_size(cl->l, NULL, &h);
get_text_size(cl->l, NULL, &h, scale);
if (cl->icon)
h_icon = cairo_image_surface_get_height(cl->icon);
h_icon = get_icon_height(cl->icon, scale);
if (have_progress_bar(cl->n)){
h_progress_bar = settings.progress_bar_height + settings.padding;
}
@ -483,8 +496,14 @@ static int frame_internal_radius (int r, int w, int h)
* The top corners will get rounded by `corner_radius`, if `first` is set.
* Respectably the same for `last` with the bottom corners.
*/
void draw_rounded_rect(cairo_t *c, int x, int y, int width, int height, int corner_radius, bool first, bool last)
void draw_rounded_rect(cairo_t *c, int x, int y, int width, int height, int corner_radius, int scale, bool first, bool last)
{
width *= scale;
height *= scale;
x *= scale;
y *= scale;
corner_radius *= scale;
const float degrees = M_PI / 180.0;
cairo_new_sub_path(c);
@ -532,6 +551,13 @@ void draw_rounded_rect(cairo_t *c, int x, int y, int width, int height, int corn
cairo_close_path(c);
}
/**
* A small wrapper around cairo_rectange for drawing a scaled rectangle.
*/
void draw_rect(cairo_t *c, int x, int y, int width, int height, int scale) {
cairo_rectangle(c, x * scale, y * scale, width * scale, height * scale);
}
static cairo_surface_t *render_background(cairo_surface_t *srf,
struct colored_layout *cl,
struct colored_layout *cl_next,
@ -541,7 +567,8 @@ static cairo_surface_t *render_background(cairo_surface_t *srf,
int corner_radius,
bool first,
bool last,
int *ret_width)
int *ret_width,
int scale)
{
int x = 0;
int radius_int = corner_radius;
@ -561,7 +588,7 @@ static cairo_surface_t *render_background(cairo_surface_t *srf,
else
height += settings.separator_height;
draw_rounded_rect(c, x, y, width, height, corner_radius, first, last);
draw_rounded_rect(c, x, y, width, height, corner_radius, scale, first, last);
/* adding frame */
x += settings.frame_width;
@ -579,11 +606,11 @@ static cairo_surface_t *render_background(cairo_surface_t *srf,
radius_int = frame_internal_radius(corner_radius, settings.frame_width, height);
draw_rounded_rect(c, x, y, width, height, radius_int, first, last);
draw_rounded_rect(c, x, y, width, height, radius_int, scale, first, last);
cairo_set_source_rgba(c, cl->frame.r, cl->frame.g, cl->frame.b, cl->frame.a);
cairo_fill(c);
draw_rounded_rect(c, x, y, width, height, radius_int, first, last);
draw_rounded_rect(c, x, y, width, height, radius_int, scale, first, last);
cairo_set_source_rgba(c, cl->bg.r, cl->bg.g, cl->bg.b, cl->bg.a);
cairo_fill(c);
@ -595,7 +622,7 @@ static cairo_surface_t *render_background(cairo_surface_t *srf,
struct color sep_color = layout_get_sepcolor(cl, cl_next);
cairo_set_source_rgba(c, sep_color.r, sep_color.g, sep_color.b, sep_color.a);
cairo_rectangle(c, settings.frame_width, y + height, width, settings.separator_height);
draw_rect(c, settings.frame_width, y + height, width, settings.separator_height, scale);
cairo_fill(c);
}
@ -605,18 +632,18 @@ static cairo_surface_t *render_background(cairo_surface_t *srf,
if (ret_width)
*ret_width = width;
return cairo_surface_create_for_rectangle(srf, x, y, width, height);
return cairo_surface_create_for_rectangle(srf, x * scale, y * scale, width * scale, height * scale);
}
static void render_content(cairo_t *c, struct colored_layout *cl, int width)
static void render_content(cairo_t *c, struct colored_layout *cl, int width, int scale)
{
const int h = layout_get_height(cl);
const int h = layout_get_height(cl, scale);
int h_without_progress_bar = h;
if (have_progress_bar(cl->n)){
h_without_progress_bar -= settings.progress_bar_height + settings.padding;
}
int h_text;
pango_layout_get_pixel_size(cl->l, NULL, &h_text);
get_text_size(cl->l, NULL, &h_text, scale);
int text_x = settings.h_padding,
text_y = settings.padding + h_without_progress_bar / 2 - h_text / 2;
@ -634,10 +661,10 @@ static void render_content(cairo_t *c, struct colored_layout *cl, int width)
// icon position
if (settings.icon_position == ICON_LEFT) {
text_x = cairo_image_surface_get_width(cl->icon) + settings.h_padding + get_text_icon_padding();
text_x = get_icon_width(cl->icon, scale) + settings.h_padding + get_text_icon_padding();
} // else ICON_RIGHT
}
cairo_move_to(c, text_x, text_y);
cairo_move_to(c, text_x * scale, text_y * scale);
cairo_set_source_rgba(c, cl->fg.r, cl->fg.g, cl->fg.b, cl->fg.a);
pango_cairo_update_layout(c, cl->l);
@ -646,8 +673,8 @@ static void render_content(cairo_t *c, struct colored_layout *cl, int width)
// icon positioning
if (cl->icon) {
unsigned int image_width = cairo_image_surface_get_width(cl->icon),
image_height = cairo_image_surface_get_height(cl->icon),
unsigned int image_width = get_icon_width(cl->icon, scale),
image_height = get_icon_height(cl->icon, scale),
image_x = width - settings.h_padding - image_width,
image_y = settings.padding + h_without_progress_bar/2 - image_height/2;
@ -665,8 +692,8 @@ static void render_content(cairo_t *c, struct colored_layout *cl, int width)
image_x = settings.h_padding;
} // else ICON_RIGHT
cairo_set_source_surface(c, cl->icon, image_x, image_y);
cairo_rectangle(c, image_x, image_y, image_width, image_height);
cairo_set_source_surface(c, cl->icon, image_x * scale, image_y * scale);
draw_rect(c, image_x, image_y, image_width, image_height, scale);
cairo_fill(c);
}
@ -690,16 +717,17 @@ static void render_content(cairo_t *c, struct colored_layout *cl, int width)
// Note: the bar could be drawn a bit smaller, because the frame is drawn on top
// left side
cairo_set_source_rgba(c, cl->highlight.r, cl->highlight.g, cl->highlight.b, cl->highlight.a);
cairo_rectangle(c, x_bar_1, frame_y, progress_width_1, progress_height);
draw_rect(c, x_bar_1, frame_y, progress_width_1, progress_height, scale);
cairo_fill(c);
// right side
cairo_set_source_rgba(c, cl->bg.r, cl->bg.g, cl->bg.b, cl->bg.a);
cairo_rectangle(c, x_bar_2, frame_y, progress_width_2, progress_height);
draw_rect(c, x_bar_2, frame_y, progress_width_2, progress_height, scale);
cairo_fill(c);
// border
cairo_set_source_rgba(c, cl->frame.r, cl->frame.g, cl->frame.b, cl->frame.a);
cairo_rectangle(c, frame_x + half_frame_width, frame_y + half_frame_width, progress_width - frame_width, progress_height);
cairo_set_line_width(c, frame_width);
// TODO draw_rect instead of cairo_rectangle resulted in blurry lines. Why?
cairo_rectangle(c, (frame_x + half_frame_width) * scale, (frame_y + half_frame_width) * scale, (progress_width - frame_width) * scale, progress_height * scale);
cairo_set_line_width(c, frame_width * scale);
cairo_stroke(c);
}
}
@ -711,18 +739,19 @@ static struct dimensions layout_render(cairo_surface_t *srf,
bool first,
bool last)
{
const int cl_h = layout_get_height(cl);
int scale = output->get_scale();
const int cl_h = layout_get_height(cl, scale);
int h_text = 0;
pango_layout_get_pixel_size(cl->l, NULL, &h_text);
get_text_size(cl->l, NULL, &h_text, scale);
int bg_width = 0;
int bg_height = MAX(settings.notification_height, (2 * settings.padding) + cl_h);
cairo_surface_t *content = render_background(srf, cl, cl_next, dim.y, dim.w, bg_height, dim.corner_radius, first, last, &bg_width);
cairo_surface_t *content = render_background(srf, cl, cl_next, dim.y, dim.w, bg_height, dim.corner_radius, first, last, &bg_width, scale);
cairo_t *c = cairo_create(content);
render_content(c, cl, bg_width);
render_content(c, cl, bg_width, scale);
/* adding frame */
if (first)
@ -774,8 +803,9 @@ void draw(void)
GSList *layouts = create_layouts(output->win_get_context(win));
struct dimensions dim = calculate_dimensions(layouts);
int scale = output->get_scale();
cairo_surface_t *image_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, dim.w, dim.h);
cairo_surface_t *image_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, dim.w * scale, dim.h * scale);
bool first = true;
for (GSList *iter = layouts; iter; iter = iter->next) {
@ -800,4 +830,14 @@ void draw_deinit(void)
output->win_destroy(win);
output->deinit();
}
int draw_get_scale(void)
{
if (output) {
return output->get_scale();
} else {
LOG_W("Called draw_get_scale before output init");
return 1;
}
}
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */

View File

@ -12,9 +12,13 @@ void draw_setup(void);
void draw(void);
void draw_rounded_rect(cairo_t *c, int x, int y, int width, int height, int corner_radius, bool first, bool last);
void draw_rounded_rect(cairo_t *c, int x, int y, int width, int height, int corner_radius, int scale, bool first, bool last);
// TODO get rid of this function by passing scale to everything that needs it.
int draw_get_scale(void);
void draw_deinit(void);
#endif
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */

View File

@ -24,6 +24,7 @@
GMainLoop *mainloop = NULL;
static struct dunst_status status;
static bool setup_done = false;
/* see dunst.h */
void dunst_status(const enum dunst_status_field field,
@ -56,6 +57,14 @@ static gboolean run(void *data);
void wake_up(void)
{
// If wake_up is being called before the output has been setup we should
// return.
if (!setup_done)
{
LOG_D("Ignoring wake up");
return;
}
LOG_D("Waking up");
run(NULL);
}
@ -197,6 +206,7 @@ int dunst_main(int argc, char *argv[])
// we do not call wakeup now, wake_up does not work here yet
}
setup_done = true;
run(NULL);
g_main_loop_run(mainloop);
g_clear_pointer(&mainloop, g_main_loop_unref);

View File

@ -86,6 +86,14 @@ static void pixbuf_data_to_cairo_data(
}
}
int get_icon_width(cairo_surface_t *icon, int scale) {
return cairo_image_surface_get_width(icon) / scale;
}
int get_icon_height(cairo_surface_t *icon, int scale) {
return cairo_image_surface_get_height(icon) / scale;
}
cairo_surface_t *gdk_pixbuf_to_cairo_surface(GdkPixbuf *pixbuf)
{
assert(pixbuf);
@ -144,22 +152,26 @@ static bool icon_size_clamp(int *w, int *h) {
*
* @param pixbuf (nullable) The pixbuf, which may be too big.
* Takes ownership of the reference.
* @param dpi_scale An integer for the dpi scaling. That doesn't mean the icon
* is always scaled by dpi_scale.
* @return the scaled version of the pixbuf. If scaling wasn't
* necessary, it returns the same pixbuf. Transfers full
* ownership of the reference.
*/
static GdkPixbuf *icon_pixbuf_scale(GdkPixbuf *pixbuf)
static GdkPixbuf *icon_pixbuf_scale(GdkPixbuf *pixbuf, int dpi_scale)
{
ASSERT_OR_RET(pixbuf, NULL);
int w = gdk_pixbuf_get_width(pixbuf);
int h = gdk_pixbuf_get_height(pixbuf);
// TODO immediately rescale icon upon scale changes
if (icon_size_clamp(&w, &h)) {
GdkPixbuf *scaled = gdk_pixbuf_scale_simple(
pixbuf,
w,
h,
w * dpi_scale,
h * dpi_scale,
GDK_INTERP_BILINEAR);
g_object_unref(pixbuf);
pixbuf = scaled;
@ -168,7 +180,7 @@ static GdkPixbuf *icon_pixbuf_scale(GdkPixbuf *pixbuf)
return pixbuf;
}
GdkPixbuf *get_pixbuf_from_file(const char *filename)
GdkPixbuf *get_pixbuf_from_file(const char *filename, int scale)
{
char *path = string_to_path(g_strdup(filename));
GError *error = NULL;
@ -179,10 +191,11 @@ GdkPixbuf *get_pixbuf_from_file(const char *filename)
g_free(path);
return NULL;
}
// TODO immediately rescale icon upon scale changes
icon_size_clamp(&w, &h);
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_scale(path,
w,
h,
w * scale,
h * scale,
TRUE,
&error);
@ -250,7 +263,7 @@ char *get_path_from_icon_name(const char *iconname)
return new_name;
}
GdkPixbuf *get_pixbuf_from_icon(const char *iconname)
GdkPixbuf *get_pixbuf_from_icon(const char *iconname, int scale)
{
char *path = get_path_from_icon_name(iconname);
if (!path) {
@ -259,7 +272,7 @@ GdkPixbuf *get_pixbuf_from_icon(const char *iconname)
GdkPixbuf *pixbuf = NULL;
pixbuf = get_pixbuf_from_file(path);
pixbuf = get_pixbuf_from_file(path, scale);
g_free(path);
if (!pixbuf)
@ -268,18 +281,18 @@ GdkPixbuf *get_pixbuf_from_icon(const char *iconname)
return pixbuf;
}
GdkPixbuf *icon_get_for_name(const char *name, char **id)
GdkPixbuf *icon_get_for_name(const char *name, char **id, int scale)
{
ASSERT_OR_RET(name, NULL);
ASSERT_OR_RET(id, NULL);
GdkPixbuf *pb = get_pixbuf_from_icon(name);
GdkPixbuf *pb = get_pixbuf_from_icon(name, scale);
if (pb)
*id = g_strdup(name);
return pb;
}
GdkPixbuf *icon_get_for_data(GVariant *data, char **id)
GdkPixbuf *icon_get_for_data(GVariant *data, char **id, int dpi_scale)
{
ASSERT_OR_RET(data, NULL);
ASSERT_OR_RET(id, NULL);
@ -347,7 +360,13 @@ GdkPixbuf *icon_get_for_data(GVariant *data, char **id)
return NULL;
}
// g_memdup is deprecated in glib 2.67.4 and higher.
// g_memdup2 is a safer alternative
#if GLIB_CHECK_VERSION(2,67,3)
data_pb = (guchar *) g_memdup2(g_variant_get_data(data_variant), len_actual);
#else
data_pb = (guchar *) g_memdup(g_variant_get_data(data_variant), len_actual);
#endif
pixbuf = gdk_pixbuf_new_from_data(data_pb,
GDK_COLORSPACE_RGB,
@ -383,7 +402,7 @@ GdkPixbuf *icon_get_for_data(GVariant *data, char **id)
g_free(data_chk);
g_variant_unref(data_variant);
pixbuf = icon_pixbuf_scale(pixbuf);
pixbuf = icon_pixbuf_scale(pixbuf, dpi_scale);
return pixbuf;
}

View File

@ -11,11 +11,26 @@ cairo_surface_t *gdk_pixbuf_to_cairo_surface(GdkPixbuf *pixbuf);
/** Retrieve an icon by its full filepath, scaled according to settings.
*
* @param filename A string representing a readable file path
* @param scale An integer representing the output dpi scaling.
*
* @return an instance of `GdkPixbuf`
* @retval NULL: file does not exist, not readable, etc..
*/
GdkPixbuf *get_pixbuf_from_file(const char *filename);
GdkPixbuf *get_pixbuf_from_file(const char *filename, int scale);
/**
* Get the unscaled icon width.
*
* If scale is 2 for example, the icon will render in twice the size, but
* get_icon_width still returns the same size as when scale is 1.
*/
int get_icon_width(cairo_surface_t *icon, int scale);
/**
* Get the unscaled icon height, see get_icon_width.
*/
int get_icon_height(cairo_surface_t *icon, int scale);
/** Retrieve a path from an icon name.
*
@ -33,11 +48,12 @@ char *get_path_from_icon_name(const char *iconname);
* @param iconname A string describing a `file://` URL, an arbitary filename
* or an icon name, which then gets searched for in the
* settings.icon_path
* @param scale An integer representing the output dpi scaling.
*
* @return an instance of `GdkPixbuf`
* @retval NULL: file does not exist, not readable, etc..
*/
GdkPixbuf *get_pixbuf_from_icon(const char *iconname);
GdkPixbuf *get_pixbuf_from_icon(const char *iconname, int scale);
/** Read an icon from disk and convert it to a GdkPixbuf, scaled according to settings
*
@ -49,10 +65,11 @@ GdkPixbuf *get_pixbuf_from_icon(const char *iconname);
* get searched in the folders of the icon_path setting.
* @param id (necessary) A unique identifier of the returned pixbuf. Only filled,
* if the return value is non-NULL.
* @param dpi_scale An integer representing the output dpi scaling.
* @return an instance of `GdkPixbuf`, representing the name's image
* @retval NULL: Invalid path given
*/
GdkPixbuf *icon_get_for_name(const char *name, char **id);
GdkPixbuf *icon_get_for_name(const char *name, char **id, int dpi_scale);
/** Convert a GVariant like described in GdkPixbuf, scaled according to settings
*
@ -63,10 +80,11 @@ GdkPixbuf *icon_get_for_name(const char *name, char **id);
* like described in the notification spec.
* @param id (necessary) A unique identifier of the returned pixbuf.
* Only filled, if the return value is non-NULL.
* @param scale An integer representing the output dpi scaling.
* @return an instance of `GdkPixbuf` derived from the GVariant
* @retval NULL: GVariant parameter nulled, invalid or in wrong format
*/
GdkPixbuf *icon_get_for_data(GVariant *data, char **id);
GdkPixbuf *icon_get_for_data(GVariant *data, char **id, int scale);
#endif
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */

View File

@ -1,5 +1,6 @@
#include "input.h"
#include "log.h"
#include "menu.h"
#include "settings.h"
#include "queues.h"
#include <stddef.h>
@ -25,6 +26,10 @@ void input_handle_click(unsigned int button, bool button_down, int mouse_x, int
case BTN_RIGHT:
acts = settings.mouse_right_click;
break;
case BTN_TOUCH:
// TODO Add separate action for touch
acts = settings.mouse_left_click;
break;
default:
LOG_W("Unsupported mouse button: '%d'", button);
return;
@ -34,10 +39,15 @@ void input_handle_click(unsigned int button, bool button_down, int mouse_x, int
enum mouse_action act = acts[i];
if (act == MOUSE_CLOSE_ALL) {
queues_history_push_all();
return;
break;
}
if (act == MOUSE_DO_ACTION || act == MOUSE_CLOSE_CURRENT) {
if (act == MOUSE_CONTEXT_ALL) {
context_menu();
continue;
}
if (act == MOUSE_DO_ACTION || act == MOUSE_CLOSE_CURRENT || act == MOUSE_CONTEXT || act == MOUSE_OPEN_URL) {
int y = settings.separator_height;
struct notification *n = NULL;
int first = true;
@ -55,8 +65,12 @@ void input_handle_click(unsigned int button, bool button_down, int mouse_x, int
if (n) {
if (act == MOUSE_CLOSE_CURRENT) {
n->marked_for_closure = REASON_USER;
} else {
} else if (act == MOUSE_DO_ACTION) {
notification_do_action(n);
} else if (act == MOUSE_OPEN_URL) {
notification_open_url(n);
} else {
notification_open_context_menu(n);
}
}
}

View File

@ -74,19 +74,21 @@ static void dunst_log_handler(
gpointer testing)
{
if (testing)
return;
log_level = G_LOG_LEVEL_ERROR;
GLogLevelFlags message_level_masked = message_level & G_LOG_LEVEL_MASK;
/* if you want to have a debug build, you want to log anything,
* unconditionally, without specifying debug log level again */
#ifndef DEBUG_BUILD
if (log_level < message_level)
if (log_level < message_level_masked)
return;
#endif
const char *log_level_str =
log_level_to_string(message_level & G_LOG_LEVEL_MASK);
log_level_to_string(message_level_masked);
/* Use stderr for warnings and higher */
if (message_level <= G_LOG_LEVEL_WARNING)
if (message_level_masked <= G_LOG_LEVEL_WARNING)
g_printerr("%s: %s\n", log_level_str, message);
else
g_print("%s: %s\n", log_level_str, message);

View File

@ -312,13 +312,19 @@ static GList *get_actionable_notifications(void)
/* see menu.h */
void context_menu(void)
{
GList *notifications = get_actionable_notifications();
context_menu_for(notifications);
}
/* see menu.h */
void context_menu_for(GList *notifications)
{
if (menu_ctx.locked_notifications) {
LOG_W("Context menu already running, refusing to rerun");
return;
}
GList *notifications = get_actionable_notifications();
menu_ctx.locked_notifications = notifications;
GError *err = NULL;

View File

@ -2,6 +2,8 @@
#ifndef DUNST_MENU_H
#define DUNST_MENU_H
#include <glib.h>
/**
* Extract all urls from the given string.
*
@ -16,9 +18,15 @@ void invoke_action(const char *action);
void regex_teardown(void);
/**
* Open the context menu that lets the user select urls/actions/etc.
* Open the context menu that lets the user select urls/actions/etc for all displayed notifications.
*/
void context_menu(void);
/**
* Open the context menu that lets the user select urls/actions/etc for the specified notifications.
* @param notifications (nullable) List of notifications for which the context menu should be opened
*/
void context_menu_for(GList *notifications);
#endif
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */

View File

@ -23,6 +23,7 @@
#include "rules.h"
#include "settings.h"
#include "utils.h"
#include "draw.h"
static void notification_extract_urls(struct notification *n);
static void notification_format_message(struct notification *n);
@ -192,7 +193,7 @@ const char *notification_urgency_to_string(const enum urgency urgency)
/* see notification.h */
int notification_cmp(const struct notification *a, const struct notification *b)
{
if (a->urgency != b->urgency) {
if (settings.sort && a->urgency != b->urgency) {
return b->urgency - a->urgency;
} else {
return a->id - b->id;
@ -205,8 +206,6 @@ int notification_cmp_data(const void *va, const void *vb, void *data)
struct notification *a = (struct notification *) va;
struct notification *b = (struct notification *) vb;
ASSERT_OR_RET(settings.sort, 1);
return notification_cmp(a, b);
}
@ -288,6 +287,7 @@ void notification_unref(struct notification *n)
g_free(n->desktop_entry);
g_hash_table_unref(n->actions);
g_free(n->default_action_name);
if (n->icon)
g_object_unref(n->icon);
@ -314,7 +314,7 @@ void notification_icon_replace_path(struct notification *n, const char *new_icon
g_clear_object(&n->icon);
g_clear_pointer(&n->icon_id, g_free);
n->icon = icon_get_for_name(new_icon, &n->icon_id);
n->icon = icon_get_for_name(new_icon, &n->icon_id, draw_get_scale());
}
void notification_icon_replace_data(struct notification *n, GVariant *new_icon)
@ -325,7 +325,7 @@ void notification_icon_replace_data(struct notification *n, GVariant *new_icon)
g_clear_object(&n->icon);
g_clear_pointer(&n->icon_id, g_free);
n->icon = icon_get_for_data(new_icon, &n->icon_id);
n->icon = icon_get_for_data(new_icon, &n->icon_id, draw_get_scale());
}
/* see notification.h */
@ -386,6 +386,7 @@ struct notification *notification_create(void)
n->fullscreen = FS_SHOW;
n->actions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
n->default_action_name = g_strdup("default");
n->script_count = 0;
return n;
@ -647,29 +648,45 @@ void notification_update_text_to_render(struct notification *n)
}
/* see notification.h */
void notification_do_action(const struct notification *n)
void notification_do_action(struct notification *n)
{
assert(n->default_action_name);
if (g_hash_table_size(n->actions)) {
if (g_hash_table_contains(n->actions, "default")) {
signal_action_invoked(n, "default");
if (g_hash_table_contains(n->actions, n->default_action_name)) {
signal_action_invoked(n, n->default_action_name);
return;
}
if (g_hash_table_size(n->actions) == 1) {
if (strcmp(n->default_action_name, "default") == 0 && g_hash_table_size(n->actions) == 1) {
GList *keys = g_hash_table_get_keys(n->actions);
signal_action_invoked(n, keys->data);
g_list_free(keys);
return;
}
context_menu();
notification_open_context_menu(n);
} else if (n->urls) {
if (strstr(n->urls, "\n"))
context_menu();
else
open_browser(n->urls);
}
}
/* see notification.h */
void notification_open_url(struct notification *n)
{
if (strstr(n->urls, "\n"))
notification_open_context_menu(n);
else
open_browser(n->urls);
}
/* see notification.h */
void notification_open_context_menu(struct notification *n)
{
GList *notifications = NULL;
notifications = g_list_append(notifications, n);
notification_lock(n);
context_menu_for(notifications);
}
void notification_invalidate_actions(struct notification *n) {
g_hash_table_remove_all(n->actions);
}

View File

@ -61,6 +61,7 @@ struct notification {
int locked; /**< If non-zero the notification is locked **/
GHashTable *actions;
char *default_action_name; /**< The name of the action to be invoked on do_action */
enum markup_mode markup;
const char *format;
@ -195,11 +196,24 @@ void notification_replace_single_field(char **haystack,
void notification_update_text_to_render(struct notification *n);
/**
* If the notification has exactly one action, or one is marked as default,
* invoke it. If there are multiple and no default, open the context menu. If
* there are no actions, proceed similarly with urls.
* If the notification has an action named n->default_action_name or there is only one
* action and n->default_action_name is set to "default", invoke it. If there is no
* such action, open the context menu if threre are other actions. Otherwise, do nothing.
*/
void notification_do_action(const struct notification *n);
void notification_do_action(struct notification *n);
/**
* If the notification has exactly one url, invoke it. If there are multiple,
* open the context menu. If there are no urls, do nothing.
*/
void notification_open_url(struct notification *n);
/**
* Open the context menu for the notification.
*
* Convenience function that creates the GList and passes it to context_menu_for().
*/
void notification_open_context_menu(struct notification *n);
/**
* Remove all client action data from the notification.

View File

@ -137,6 +137,9 @@ bool string_parse_mouse_action(const char *s, enum mouse_action *ret)
STRING_PARSE_RET("do_action", MOUSE_DO_ACTION);
STRING_PARSE_RET("close_current", MOUSE_CLOSE_CURRENT);
STRING_PARSE_RET("close_all", MOUSE_CLOSE_ALL);
STRING_PARSE_RET("context", MOUSE_CONTEXT);
STRING_PARSE_RET("context_all", MOUSE_CONTEXT_ALL);
STRING_PARSE_RET("open_url", MOUSE_OPEN_URL);
return false;
}

View File

@ -8,7 +8,7 @@
#include "wayland/wl.h"
#endif
const bool is_running_wayland(void) {
bool is_running_wayland(void) {
char* wayland_display = getenv("WAYLAND_DISPLAY");
return !(wayland_display == NULL);
}
@ -24,13 +24,14 @@ const struct output output_x11 = {
x_win_hide,
x_display_surface,
x_win_visible,
x_win_get_context,
get_active_screen,
x_is_idle,
have_fullscreen_window
have_fullscreen_window,
x_get_scale,
};
#ifdef ENABLE_WAYLAND
@ -45,28 +46,51 @@ const struct output output_wl = {
wl_win_hide,
wl_display_surface,
wl_win_visible,
wl_win_get_context,
wl_get_active_screen,
wl_is_idle,
wl_have_fullscreen_window
wl_have_fullscreen_window,
wl_get_scale,
};
#endif
const struct output* get_x11_output() {
const struct output* output = &output_x11;
if (output->init()) {
return output;
} else {
LOG_E("Couldn't initialize X11 output. Aborting...");
}
}
#ifdef ENABLE_WAYLAND
const struct output* get_wl_output() {
const struct output* output = &output_wl;
if (output->init()) {
return output;
} else {
LOG_W("Couldn't initialize wayland output. Falling back to X11 output.");
output->deinit();
return get_x11_output();
}
}
#endif
const struct output* output_create(bool force_xwayland)
{
#ifdef ENABLE_WAYLAND
if (!force_xwayland && is_running_wayland()) {
LOG_I("Using Wayland output");
return &output_wl;
return get_wl_output();
} else {
LOG_I("Using X11 output");
return &output_x11;
return get_x11_output();
}
#else
return &output_x11;
return get_x11_output();
#endif
}
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */

View File

@ -27,7 +27,7 @@ struct screen_info {
};
struct output {
void (*init)(void);
bool (*init)(void);
void (*deinit)(void);
window (*win_create)(void);
@ -38,18 +38,24 @@ struct output {
void (*display_surface)(cairo_surface_t *srf, window win, const struct dimensions*);
bool (*win_visible)(window);
cairo_t* (*win_get_context)(window);
const struct screen_info* (*get_active_screen)(void);
bool (*is_idle)(void);
bool (*have_fullscreen_window)(void);
int (*get_scale)(void);
};
/**
* return an initialized output, selecting the correct output type from either
* wayland or X11 according to the settings and environment.
* When the wayland output fails to initilize, it falls back to X11 output.
*/
const struct output* output_create(bool force_xwayland);
const bool is_running_wayland(void);
bool is_running_wayland(void);
#endif
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */

View File

@ -149,7 +149,7 @@ gint64 queues_get_next_datachange(gint64 time);
* Get the notification which has the given id in the displayed and waiting queue or
* NULL if not found
*
* @param the id searched for.
* @param id the id searched for.
*
* @return the `id` notification or NULL
*/

View File

@ -26,6 +26,10 @@ void rule_apply(struct rule *r, struct notification *n)
n->transient = r->set_transient;
if (r->skip_display != -1)
n->skip_display = r->skip_display;
if (r->action_name) {
g_free(n->default_action_name);
n->default_action_name = g_strdup(r->action_name);
}
if (r->markup != MARKUP_NULL)
n->markup = r->markup;
if (r->new_icon)

View File

@ -23,6 +23,7 @@ struct rule {
/* actions */
gint64 timeout;
enum urgency urgency;
char *action_name;
enum markup_mode markup;
int history_ignore;
int match_transient;

View File

@ -862,6 +862,7 @@ void load_settings(char *cmdline_config_path)
g_free(c);
}
r->action_name = ini_get_string(cur_section, "action_name", NULL);
r->urgency = ini_get_urgency(cur_section, "urgency", r->urgency);
r->msg_urgency = ini_get_urgency(cur_section, "msg_urgency", r->msg_urgency);
r->fg = ini_get_string(cur_section, "foreground", r->fg);

View File

@ -18,7 +18,7 @@ enum icon_position { ICON_LEFT, ICON_RIGHT, ICON_OFF };
enum vertical_alignment { VERTICAL_TOP, VERTICAL_CENTER, VERTICAL_BOTTOM };
enum separator_color { SEP_FOREGROUND, SEP_AUTO, SEP_FRAME, SEP_CUSTOM };
enum follow_mode { FOLLOW_NONE, FOLLOW_MOUSE, FOLLOW_KEYBOARD };
enum mouse_action { MOUSE_NONE, MOUSE_DO_ACTION, MOUSE_CLOSE_CURRENT, MOUSE_CLOSE_ALL };
enum mouse_action { MOUSE_NONE, MOUSE_DO_ACTION, MOUSE_CLOSE_CURRENT, MOUSE_CLOSE_ALL, MOUSE_CONTEXT, MOUSE_CONTEXT_ALL, MOUSE_OPEN_URL };
#ifndef ZWLR_LAYER_SHELL_V1_LAYER_ENUM
#define ZWLR_LAYER_SHELL_V1_LAYER_ENUM
// Needed for compiling without wayland dependency

View File

@ -0,0 +1,611 @@
/* Generated by wayland-scanner 1.18.0 */
#ifndef WLR_FOREIGN_TOPLEVEL_MANAGEMENT_UNSTABLE_V1_CLIENT_PROTOCOL_H
#define WLR_FOREIGN_TOPLEVEL_MANAGEMENT_UNSTABLE_V1_CLIENT_PROTOCOL_H
#include <stdint.h>
#include <stddef.h>
#include "wayland-client.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @page page_wlr_foreign_toplevel_management_unstable_v1 The wlr_foreign_toplevel_management_unstable_v1 protocol
* @section page_ifaces_wlr_foreign_toplevel_management_unstable_v1 Interfaces
* - @subpage page_iface_zwlr_foreign_toplevel_manager_v1 - list and control opened apps
* - @subpage page_iface_zwlr_foreign_toplevel_handle_v1 - an opened toplevel
* @section page_copyright_wlr_foreign_toplevel_management_unstable_v1 Copyright
* <pre>
*
* Copyright © 2018 Ilia Bozhinov
*
* Permission to use, copy, modify, distribute, and sell this
* software and its documentation for any purpose is hereby granted
* without fee, provided that the above copyright notice appear in
* all copies and that both that copyright notice and this permission
* notice appear in supporting documentation, and that the name of
* the copyright holders not be used in advertising or publicity
* pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied
* warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
* THIS SOFTWARE.
* </pre>
*/
struct wl_output;
struct wl_seat;
struct wl_surface;
struct zwlr_foreign_toplevel_handle_v1;
struct zwlr_foreign_toplevel_manager_v1;
/**
* @page page_iface_zwlr_foreign_toplevel_manager_v1 zwlr_foreign_toplevel_manager_v1
* @section page_iface_zwlr_foreign_toplevel_manager_v1_desc Description
*
* The purpose of this protocol is to enable the creation of taskbars
* and docks by providing them with a list of opened applications and
* letting them request certain actions on them, like maximizing, etc.
*
* After a client binds the zwlr_foreign_toplevel_manager_v1, each opened
* toplevel window will be sent via the toplevel event
* @section page_iface_zwlr_foreign_toplevel_manager_v1_api API
* See @ref iface_zwlr_foreign_toplevel_manager_v1.
*/
/**
* @defgroup iface_zwlr_foreign_toplevel_manager_v1 The zwlr_foreign_toplevel_manager_v1 interface
*
* The purpose of this protocol is to enable the creation of taskbars
* and docks by providing them with a list of opened applications and
* letting them request certain actions on them, like maximizing, etc.
*
* After a client binds the zwlr_foreign_toplevel_manager_v1, each opened
* toplevel window will be sent via the toplevel event
*/
extern const struct wl_interface zwlr_foreign_toplevel_manager_v1_interface;
/**
* @page page_iface_zwlr_foreign_toplevel_handle_v1 zwlr_foreign_toplevel_handle_v1
* @section page_iface_zwlr_foreign_toplevel_handle_v1_desc Description
*
* A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel
* window. Each app may have multiple opened toplevels.
*
* Each toplevel has a list of outputs it is visible on, conveyed to the
* client with the output_enter and output_leave events.
* @section page_iface_zwlr_foreign_toplevel_handle_v1_api API
* See @ref iface_zwlr_foreign_toplevel_handle_v1.
*/
/**
* @defgroup iface_zwlr_foreign_toplevel_handle_v1 The zwlr_foreign_toplevel_handle_v1 interface
*
* A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel
* window. Each app may have multiple opened toplevels.
*
* Each toplevel has a list of outputs it is visible on, conveyed to the
* client with the output_enter and output_leave events.
*/
extern const struct wl_interface zwlr_foreign_toplevel_handle_v1_interface;
/**
* @ingroup iface_zwlr_foreign_toplevel_manager_v1
* @struct zwlr_foreign_toplevel_manager_v1_listener
*/
struct zwlr_foreign_toplevel_manager_v1_listener {
/**
* a toplevel has been created
*
* This event is emitted whenever a new toplevel window is
* created. It is emitted for all toplevels, regardless of the app
* that has created them.
*
* All initial details of the toplevel(title, app_id, states, etc.)
* will be sent immediately after this event via the corresponding
* events in zwlr_foreign_toplevel_handle_v1.
*/
void (*toplevel)(void *data,
struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1,
struct zwlr_foreign_toplevel_handle_v1 *toplevel);
/**
* the compositor has finished with the toplevel manager
*
* This event indicates that the compositor is done sending
* events to the zwlr_foreign_toplevel_manager_v1. The server will
* destroy the object immediately after sending this request, so it
* will become invalid and the client should free any resources
* associated with it.
*/
void (*finished)(void *data,
struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1);
};
/**
* @ingroup iface_zwlr_foreign_toplevel_manager_v1
*/
static inline int
zwlr_foreign_toplevel_manager_v1_add_listener(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1,
const struct zwlr_foreign_toplevel_manager_v1_listener *listener, void *data)
{
return wl_proxy_add_listener((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1,
(void (**)(void)) listener, data);
}
#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_STOP 0
/**
* @ingroup iface_zwlr_foreign_toplevel_manager_v1
*/
#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_TOPLEVEL_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_foreign_toplevel_manager_v1
*/
#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_FINISHED_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_foreign_toplevel_manager_v1
*/
#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_STOP_SINCE_VERSION 1
/** @ingroup iface_zwlr_foreign_toplevel_manager_v1 */
static inline void
zwlr_foreign_toplevel_manager_v1_set_user_data(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1, user_data);
}
/** @ingroup iface_zwlr_foreign_toplevel_manager_v1 */
static inline void *
zwlr_foreign_toplevel_manager_v1_get_user_data(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1)
{
return wl_proxy_get_user_data((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1);
}
static inline uint32_t
zwlr_foreign_toplevel_manager_v1_get_version(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1)
{
return wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1);
}
/** @ingroup iface_zwlr_foreign_toplevel_manager_v1 */
static inline void
zwlr_foreign_toplevel_manager_v1_destroy(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1)
{
wl_proxy_destroy((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1);
}
/**
* @ingroup iface_zwlr_foreign_toplevel_manager_v1
*
* Indicates the client no longer wishes to receive events for new toplevels.
* However the compositor may emit further toplevel_created events, until
* the finished event is emitted.
*
* The client must not send any more requests after this one.
*/
static inline void
zwlr_foreign_toplevel_manager_v1_stop(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1)
{
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1,
ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_STOP);
}
#ifndef ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ENUM
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ENUM
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
* types of states on the toplevel
*
* The different states that a toplevel can have. These have the same meaning
* as the states with the same names defined in xdg-toplevel
*/
enum zwlr_foreign_toplevel_handle_v1_state {
/**
* the toplevel is maximized
*/
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED = 0,
/**
* the toplevel is minimized
*/
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED = 1,
/**
* the toplevel is active
*/
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED = 2,
/**
* the toplevel is fullscreen
* @since 2
*/
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN = 3,
};
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*/
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN_SINCE_VERSION 2
#endif /* ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ENUM */
#ifndef ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_ENUM
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_ENUM
enum zwlr_foreign_toplevel_handle_v1_error {
/**
* the provided rectangle is invalid
*/
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_INVALID_RECTANGLE = 0,
};
#endif /* ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_ENUM */
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
* @struct zwlr_foreign_toplevel_handle_v1_listener
*/
struct zwlr_foreign_toplevel_handle_v1_listener {
/**
* title change
*
* This event is emitted whenever the title of the toplevel
* changes.
*/
void (*title)(void *data,
struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
const char *title);
/**
* app-id change
*
* This event is emitted whenever the app-id of the toplevel
* changes.
*/
void (*app_id)(void *data,
struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
const char *app_id);
/**
* toplevel entered an output
*
* This event is emitted whenever the toplevel becomes visible on
* the given output. A toplevel may be visible on multiple outputs.
*/
void (*output_enter)(void *data,
struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
struct wl_output *output);
/**
* toplevel left an output
*
* This event is emitted whenever the toplevel stops being
* visible on the given output. It is guaranteed that an
* entered-output event with the same output has been emitted
* before this event.
*/
void (*output_leave)(void *data,
struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
struct wl_output *output);
/**
* the toplevel state changed
*
* This event is emitted immediately after the
* zlw_foreign_toplevel_handle_v1 is created and each time the
* toplevel state changes, either because of a compositor action or
* because of a request in this protocol.
*/
void (*state)(void *data,
struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
struct wl_array *state);
/**
* all information about the toplevel has been sent
*
* This event is sent after all changes in the toplevel state
* have been sent.
*
* This allows changes to the zwlr_foreign_toplevel_handle_v1
* properties to be seen as atomic, even if they happen via
* multiple events.
*/
void (*done)(void *data,
struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1);
/**
* this toplevel has been destroyed
*
* This event means the toplevel has been destroyed. It is
* guaranteed there won't be any more events for this
* zwlr_foreign_toplevel_handle_v1. The toplevel itself becomes
* inert so any requests will be ignored except the destroy
* request.
*/
void (*closed)(void *data,
struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1);
/**
* parent change
*
* This event is emitted whenever the parent of the toplevel
* changes.
*
* No event is emitted when the parent handle is destroyed by the
* client.
* @since 3
*/
void (*parent)(void *data,
struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
struct zwlr_foreign_toplevel_handle_v1 *parent);
};
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*/
static inline int
zwlr_foreign_toplevel_handle_v1_add_listener(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
const struct zwlr_foreign_toplevel_handle_v1_listener *listener, void *data)
{
return wl_proxy_add_listener((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
(void (**)(void)) listener, data);
}
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MAXIMIZED 0
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MAXIMIZED 1
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MINIMIZED 2
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MINIMIZED 3
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ACTIVATE 4
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSE 5
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_RECTANGLE 6
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_DESTROY 7
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_FULLSCREEN 8
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_FULLSCREEN 9
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*/
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_TITLE_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*/
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_APP_ID_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*/
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_OUTPUT_ENTER_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*/
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_OUTPUT_LEAVE_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*/
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*/
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_DONE_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*/
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSED_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*/
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_PARENT_SINCE_VERSION 3
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*/
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MAXIMIZED_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*/
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MAXIMIZED_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*/
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MINIMIZED_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*/
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MINIMIZED_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*/
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ACTIVATE_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*/
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSE_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*/
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_RECTANGLE_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*/
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_DESTROY_SINCE_VERSION 1
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*/
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_FULLSCREEN_SINCE_VERSION 2
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*/
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_FULLSCREEN_SINCE_VERSION 2
/** @ingroup iface_zwlr_foreign_toplevel_handle_v1 */
static inline void
zwlr_foreign_toplevel_handle_v1_set_user_data(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, user_data);
}
/** @ingroup iface_zwlr_foreign_toplevel_handle_v1 */
static inline void *
zwlr_foreign_toplevel_handle_v1_get_user_data(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
{
return wl_proxy_get_user_data((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1);
}
static inline uint32_t
zwlr_foreign_toplevel_handle_v1_get_version(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
{
return wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1);
}
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*
* Requests that the toplevel be maximized. If the maximized state actually
* changes, this will be indicated by the state event.
*/
static inline void
zwlr_foreign_toplevel_handle_v1_set_maximized(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
{
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MAXIMIZED);
}
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*
* Requests that the toplevel be unmaximized. If the maximized state actually
* changes, this will be indicated by the state event.
*/
static inline void
zwlr_foreign_toplevel_handle_v1_unset_maximized(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
{
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MAXIMIZED);
}
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*
* Requests that the toplevel be minimized. If the minimized state actually
* changes, this will be indicated by the state event.
*/
static inline void
zwlr_foreign_toplevel_handle_v1_set_minimized(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
{
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MINIMIZED);
}
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*
* Requests that the toplevel be unminimized. If the minimized state actually
* changes, this will be indicated by the state event.
*/
static inline void
zwlr_foreign_toplevel_handle_v1_unset_minimized(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
{
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MINIMIZED);
}
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*
* Request that this toplevel be activated on the given seat.
* There is no guarantee the toplevel will be actually activated.
*/
static inline void
zwlr_foreign_toplevel_handle_v1_activate(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, struct wl_seat *seat)
{
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ACTIVATE, seat);
}
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*
* Send a request to the toplevel to close itself. The compositor would
* typically use a shell-specific method to carry out this request, for
* example by sending the xdg_toplevel.close event. However, this gives
* no guarantees the toplevel will actually be destroyed. If and when
* this happens, the zwlr_foreign_toplevel_handle_v1.closed event will
* be emitted.
*/
static inline void
zwlr_foreign_toplevel_handle_v1_close(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
{
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSE);
}
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*
* The rectangle of the surface specified in this request corresponds to
* the place where the app using this protocol represents the given toplevel.
* It can be used by the compositor as a hint for some operations, e.g
* minimizing. The client is however not required to set this, in which
* case the compositor is free to decide some default value.
*
* If the client specifies more than one rectangle, only the last one is
* considered.
*
* The dimensions are given in surface-local coordinates.
* Setting width=height=0 removes the already-set rectangle.
*/
static inline void
zwlr_foreign_toplevel_handle_v1_set_rectangle(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, struct wl_surface *surface, int32_t x, int32_t y, int32_t width, int32_t height)
{
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_RECTANGLE, surface, x, y, width, height);
}
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*
* Destroys the zwlr_foreign_toplevel_handle_v1 object.
*
* This request should be called either when the client does not want to
* use the toplevel anymore or after the closed event to finalize the
* destruction of the object.
*/
static inline void
zwlr_foreign_toplevel_handle_v1_destroy(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
{
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_DESTROY);
wl_proxy_destroy((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1);
}
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*
* Requests that the toplevel be fullscreened on the given output. If the
* fullscreen state and/or the outputs the toplevel is visible on actually
* change, this will be indicated by the state and output_enter/leave
* events.
*
* The output parameter is only a hint to the compositor. Also, if output
* is NULL, the compositor should decide which output the toplevel will be
* fullscreened on, if at all.
*/
static inline void
zwlr_foreign_toplevel_handle_v1_set_fullscreen(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, struct wl_output *output)
{
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_FULLSCREEN, output);
}
/**
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
*
* Requests that the toplevel be unfullscreened. If the fullscreen state
* actually changes, this will be indicated by the state event.
*/
static inline void
zwlr_foreign_toplevel_handle_v1_unset_fullscreen(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
{
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_FULLSCREEN);
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,106 @@
/* Generated by wayland-scanner 1.18.0 */
/*
* Copyright © 2018 Ilia Bozhinov
*
* Permission to use, copy, modify, distribute, and sell this
* software and its documentation for any purpose is hereby granted
* without fee, provided that the above copyright notice appear in
* all copies and that both that copyright notice and this permission
* notice appear in supporting documentation, and that the name of
* the copyright holders not be used in advertising or publicity
* pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied
* warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
* THIS SOFTWARE.
*/
#include <stdlib.h>
#include <stdint.h>
#include "wayland-util.h"
#ifndef __has_attribute
# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
#endif
#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
#define WL_PRIVATE __attribute__ ((visibility("hidden")))
#else
#define WL_PRIVATE
#endif
extern const struct wl_interface wl_output_interface;
extern const struct wl_interface wl_seat_interface;
extern const struct wl_interface wl_surface_interface;
extern const struct wl_interface zwlr_foreign_toplevel_handle_v1_interface;
static const struct wl_interface *wlr_foreign_toplevel_management_unstable_v1_types[] = {
NULL,
&zwlr_foreign_toplevel_handle_v1_interface,
&wl_seat_interface,
&wl_surface_interface,
NULL,
NULL,
NULL,
NULL,
&wl_output_interface,
&wl_output_interface,
&wl_output_interface,
&zwlr_foreign_toplevel_handle_v1_interface,
};
static const struct wl_message zwlr_foreign_toplevel_manager_v1_requests[] = {
{ "stop", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
};
static const struct wl_message zwlr_foreign_toplevel_manager_v1_events[] = {
{ "toplevel", "n", wlr_foreign_toplevel_management_unstable_v1_types + 1 },
{ "finished", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
};
WL_PRIVATE const struct wl_interface zwlr_foreign_toplevel_manager_v1_interface = {
"zwlr_foreign_toplevel_manager_v1", 3,
1, zwlr_foreign_toplevel_manager_v1_requests,
2, zwlr_foreign_toplevel_manager_v1_events,
};
static const struct wl_message zwlr_foreign_toplevel_handle_v1_requests[] = {
{ "set_maximized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
{ "unset_maximized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
{ "set_minimized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
{ "unset_minimized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
{ "activate", "o", wlr_foreign_toplevel_management_unstable_v1_types + 2 },
{ "close", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
{ "set_rectangle", "oiiii", wlr_foreign_toplevel_management_unstable_v1_types + 3 },
{ "destroy", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
{ "set_fullscreen", "2?o", wlr_foreign_toplevel_management_unstable_v1_types + 8 },
{ "unset_fullscreen", "2", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
};
static const struct wl_message zwlr_foreign_toplevel_handle_v1_events[] = {
{ "title", "s", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
{ "app_id", "s", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
{ "output_enter", "o", wlr_foreign_toplevel_management_unstable_v1_types + 9 },
{ "output_leave", "o", wlr_foreign_toplevel_management_unstable_v1_types + 10 },
{ "state", "a", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
{ "done", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
{ "closed", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
{ "parent", "3?o", wlr_foreign_toplevel_management_unstable_v1_types + 11 },
};
WL_PRIVATE const struct wl_interface zwlr_foreign_toplevel_handle_v1_interface = {
"zwlr_foreign_toplevel_handle_v1", 3,
10, zwlr_foreign_toplevel_handle_v1_requests,
8, zwlr_foreign_toplevel_handle_v1_events,
};

View File

@ -0,0 +1,270 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_foreign_toplevel_management_unstable_v1">
<copyright>
Copyright © 2018 Ilia Bozhinov
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that copyright notice and this permission
notice appear in supporting documentation, and that the name of
the copyright holders not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission. The copyright holders make no
representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied
warranty.
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
</copyright>
<interface name="zwlr_foreign_toplevel_manager_v1" version="3">
<description summary="list and control opened apps">
The purpose of this protocol is to enable the creation of taskbars
and docks by providing them with a list of opened applications and
letting them request certain actions on them, like maximizing, etc.
After a client binds the zwlr_foreign_toplevel_manager_v1, each opened
toplevel window will be sent via the toplevel event
</description>
<event name="toplevel">
<description summary="a toplevel has been created">
This event is emitted whenever a new toplevel window is created. It
is emitted for all toplevels, regardless of the app that has created
them.
All initial details of the toplevel(title, app_id, states, etc.) will
be sent immediately after this event via the corresponding events in
zwlr_foreign_toplevel_handle_v1.
</description>
<arg name="toplevel" type="new_id" interface="zwlr_foreign_toplevel_handle_v1"/>
</event>
<request name="stop">
<description summary="stop sending events">
Indicates the client no longer wishes to receive events for new toplevels.
However the compositor may emit further toplevel_created events, until
the finished event is emitted.
The client must not send any more requests after this one.
</description>
</request>
<event name="finished">
<description summary="the compositor has finished with the toplevel manager">
This event indicates that the compositor is done sending events to the
zwlr_foreign_toplevel_manager_v1. The server will destroy the object
immediately after sending this request, so it will become invalid and
the client should free any resources associated with it.
</description>
</event>
</interface>
<interface name="zwlr_foreign_toplevel_handle_v1" version="3">
<description summary="an opened toplevel">
A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel
window. Each app may have multiple opened toplevels.
Each toplevel has a list of outputs it is visible on, conveyed to the
client with the output_enter and output_leave events.
</description>
<event name="title">
<description summary="title change">
This event is emitted whenever the title of the toplevel changes.
</description>
<arg name="title" type="string"/>
</event>
<event name="app_id">
<description summary="app-id change">
This event is emitted whenever the app-id of the toplevel changes.
</description>
<arg name="app_id" type="string"/>
</event>
<event name="output_enter">
<description summary="toplevel entered an output">
This event is emitted whenever the toplevel becomes visible on
the given output. A toplevel may be visible on multiple outputs.
</description>
<arg name="output" type="object" interface="wl_output"/>
</event>
<event name="output_leave">
<description summary="toplevel left an output">
This event is emitted whenever the toplevel stops being visible on
the given output. It is guaranteed that an entered-output event
with the same output has been emitted before this event.
</description>
<arg name="output" type="object" interface="wl_output"/>
</event>
<request name="set_maximized">
<description summary="requests that the toplevel be maximized">
Requests that the toplevel be maximized. If the maximized state actually
changes, this will be indicated by the state event.
</description>
</request>
<request name="unset_maximized">
<description summary="requests that the toplevel be unmaximized">
Requests that the toplevel be unmaximized. If the maximized state actually
changes, this will be indicated by the state event.
</description>
</request>
<request name="set_minimized">
<description summary="requests that the toplevel be minimized">
Requests that the toplevel be minimized. If the minimized state actually
changes, this will be indicated by the state event.
</description>
</request>
<request name="unset_minimized">
<description summary="requests that the toplevel be unminimized">
Requests that the toplevel be unminimized. If the minimized state actually
changes, this will be indicated by the state event.
</description>
</request>
<request name="activate">
<description summary="activate the toplevel">
Request that this toplevel be activated on the given seat.
There is no guarantee the toplevel will be actually activated.
</description>
<arg name="seat" type="object" interface="wl_seat"/>
</request>
<enum name="state">
<description summary="types of states on the toplevel">
The different states that a toplevel can have. These have the same meaning
as the states with the same names defined in xdg-toplevel
</description>
<entry name="maximized" value="0" summary="the toplevel is maximized"/>
<entry name="minimized" value="1" summary="the toplevel is minimized"/>
<entry name="activated" value="2" summary="the toplevel is active"/>
<entry name="fullscreen" value="3" summary="the toplevel is fullscreen" since="2"/>
</enum>
<event name="state">
<description summary="the toplevel state changed">
This event is emitted immediately after the zlw_foreign_toplevel_handle_v1
is created and each time the toplevel state changes, either because of a
compositor action or because of a request in this protocol.
</description>
<arg name="state" type="array"/>
</event>
<event name="done">
<description summary="all information about the toplevel has been sent">
This event is sent after all changes in the toplevel state have been
sent.
This allows changes to the zwlr_foreign_toplevel_handle_v1 properties
to be seen as atomic, even if they happen via multiple events.
</description>
</event>
<request name="close">
<description summary="request that the toplevel be closed">
Send a request to the toplevel to close itself. The compositor would
typically use a shell-specific method to carry out this request, for
example by sending the xdg_toplevel.close event. However, this gives
no guarantees the toplevel will actually be destroyed. If and when
this happens, the zwlr_foreign_toplevel_handle_v1.closed event will
be emitted.
</description>
</request>
<request name="set_rectangle">
<description summary="the rectangle which represents the toplevel">
The rectangle of the surface specified in this request corresponds to
the place where the app using this protocol represents the given toplevel.
It can be used by the compositor as a hint for some operations, e.g
minimizing. The client is however not required to set this, in which
case the compositor is free to decide some default value.
If the client specifies more than one rectangle, only the last one is
considered.
The dimensions are given in surface-local coordinates.
Setting width=height=0 removes the already-set rectangle.
</description>
<arg name="surface" type="object" interface="wl_surface"/>
<arg name="x" type="int"/>
<arg name="y" type="int"/>
<arg name="width" type="int"/>
<arg name="height" type="int"/>
</request>
<enum name="error">
<entry name="invalid_rectangle" value="0"
summary="the provided rectangle is invalid"/>
</enum>
<event name="closed">
<description summary="this toplevel has been destroyed">
This event means the toplevel has been destroyed. It is guaranteed there
won't be any more events for this zwlr_foreign_toplevel_handle_v1. The
toplevel itself becomes inert so any requests will be ignored except the
destroy request.
</description>
</event>
<request name="destroy" type="destructor">
<description summary="destroy the zwlr_foreign_toplevel_handle_v1 object">
Destroys the zwlr_foreign_toplevel_handle_v1 object.
This request should be called either when the client does not want to
use the toplevel anymore or after the closed event to finalize the
destruction of the object.
</description>
</request>
<!-- Version 2 additions -->
<request name="set_fullscreen" since="2">
<description summary="request that the toplevel be fullscreened">
Requests that the toplevel be fullscreened on the given output. If the
fullscreen state and/or the outputs the toplevel is visible on actually
change, this will be indicated by the state and output_enter/leave
events.
The output parameter is only a hint to the compositor. Also, if output
is NULL, the compositor should decide which output the toplevel will be
fullscreened on, if at all.
</description>
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
</request>
<request name="unset_fullscreen" since="2">
<description summary="request that the toplevel be unfullscreened">
Requests that the toplevel be unfullscreened. If the fullscreen state
actually changes, this will be indicated by the state event.
</description>
</request>
<!-- Version 3 additions -->
<event name="parent" since="3">
<description summary="parent change">
This event is emitted whenever the parent of the toplevel changes.
No event is emitted when the parent handle is destroyed by the client.
</description>
<arg name="parent" type="object" interface="zwlr_foreign_toplevel_handle_v1" allow-null="true"/>
</event>
</interface>
</protocol>

View File

@ -19,6 +19,8 @@
#include "protocols/xdg-shell.h"
#include "protocols/wlr-layer-shell-unstable-v1-client-header.h"
#include "protocols/wlr-layer-shell-unstable-v1.h"
#include "protocols/wlr-foreign-toplevel-management-unstable-v1-client-header.h"
#include "protocols/wlr-foreign-toplevel-management-unstable-v1.h"
#include "protocols/idle-client-header.h"
#include "protocols/idle.h"
#include "pool-buffer.h"
@ -30,6 +32,8 @@
#include "../input.h"
#include "libgwater-wayland.h"
#define MAX_TOUCHPOINTS 10
struct window_wl {
cairo_surface_t *c_surface;
cairo_t * c_ctx;
@ -54,6 +58,7 @@ struct wl_ctx {
struct wl_callback *frame_callback;
struct org_kde_kwin_idle *idle_handler;
struct org_kde_kwin_idle_timeout *idle_timeout;
struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager;
bool configured;
bool dirty;
bool is_idle;
@ -64,6 +69,13 @@ struct wl_ctx {
int32_t x, y;
} pointer;
struct {
struct wl_touch *wl_touch;
struct {
int32_t x, y;
} pts[MAX_TOUCHPOINTS];
} touch;
struct dimensions cur_dim;
int32_t width, height;
@ -79,9 +91,10 @@ struct dunst_output {
uint32_t scale;
uint32_t subpixel; // TODO do something with it
bool fullscreen;
struct zwlr_foreign_toplevel_handle_v1 *fullscreen_toplevel; // the toplevel that is fullscreened on this output
};
struct wl_ctx ctx;
static void noop() {
@ -94,7 +107,7 @@ static void output_handle_geometry(void *data, struct wl_output *wl_output,
int32_t x, int32_t y, int32_t phy_width, int32_t phy_height,
int32_t subpixel, const char *make, const char *model,
int32_t transform) {
//TODO
//TODO do something with the subpixel data
struct dunst_output *output = data;
output->subpixel = subpixel;
}
@ -103,6 +116,8 @@ static void output_handle_scale(void *data, struct wl_output *wl_output,
int32_t factor) {
struct dunst_output *output = data;
output->scale = factor;
wake_up();
}
static const struct wl_output_listener output_listener = {
@ -122,8 +137,8 @@ static void create_output( struct wl_output *wl_output, uint32_t global_name) {
LOG_I("New output found - id %i", number);
output->global_name = global_name;
output->wl_output = wl_output;
// TODO: Fix this
output->scale = 1;
output->fullscreen = false;
wl_list_insert(&ctx.outputs, &output->link);
wl_output_set_user_data(wl_output, output);
@ -144,7 +159,36 @@ static void destroy_output(struct dunst_output *output) {
free(output);
}
// FIXME: Snipped touch handling
static void touch_handle_motion(void *data, struct wl_touch *wl_touch,
uint32_t time, int32_t id,
wl_fixed_t surface_x, wl_fixed_t surface_y) {
if (id >= MAX_TOUCHPOINTS) {
return;
}
ctx.touch.pts[id].x = wl_fixed_to_int(surface_x);
ctx.touch.pts[id].y = wl_fixed_to_int(surface_y);
}
static void touch_handle_down(void *data, struct wl_touch *wl_touch,
uint32_t serial, uint32_t time, struct wl_surface *sfc, int32_t id,
wl_fixed_t surface_x, wl_fixed_t surface_y) {
if (id >= MAX_TOUCHPOINTS) {
return;
}
ctx.touch.pts[id].x = wl_fixed_to_int(surface_x);
ctx.touch.pts[id].y = wl_fixed_to_int(surface_y);
}
static void touch_handle_up(void *data, struct wl_touch *wl_touch,
uint32_t serial, uint32_t time, int32_t id) {
if (id >= MAX_TOUCHPOINTS) {
return;
}
input_handle_click(BTN_TOUCH, false,
ctx.touch.pts[id].x, ctx.touch.pts[id].y);
}
static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
ctx.pointer.x = wl_fixed_to_int(surface_x);
@ -165,7 +209,13 @@ static const struct wl_pointer_listener pointer_listener = {
.axis = noop,
};
// FIXME snipped touch listener
static const struct wl_touch_listener touch_listener = {
.down = touch_handle_down,
.up = touch_handle_up,
.motion = touch_handle_motion,
.frame = noop,
.cancel = noop,
};
static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
uint32_t capabilities) {
@ -178,7 +228,15 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
ctx.pointer.wl_pointer = wl_seat_get_pointer(wl_seat);
wl_pointer_add_listener(ctx.pointer.wl_pointer,
&pointer_listener, ctx.seat);
LOG_I("Adding pointer");
}
if (ctx.touch.wl_touch != NULL) {
wl_touch_release(ctx.touch.wl_touch);
ctx.touch.wl_touch = NULL;
}
if (capabilities & WL_SEAT_CAPABILITY_TOUCH) {
ctx.touch.wl_touch = wl_seat_get_touch(wl_seat);
wl_touch_add_listener(ctx.touch.wl_touch,
&touch_listener, ctx.seat);
}
}
@ -226,10 +284,12 @@ static void layer_surface_handle_configure(void *data,
static void layer_surface_handle_closed(void *data,
struct zwlr_layer_surface_v1 *surface) {
LOG_I("Destroying layer");
zwlr_layer_surface_v1_destroy(ctx.layer_surface);
if (ctx.layer_surface)
zwlr_layer_surface_v1_destroy(ctx.layer_surface);
ctx.layer_surface = NULL;
wl_surface_destroy(ctx.surface);
if (ctx.surface)
wl_surface_destroy(ctx.surface);
ctx.surface = NULL;
if (ctx.frame_callback) {
@ -257,11 +317,11 @@ static const struct zwlr_layer_surface_v1_listener layer_surface_listener = {
static void idle_start (void *data, struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout) {
ctx.is_idle = true;
LOG_I("User went idle");
LOG_D("User went idle");
}
static void idle_stop (void *data, struct org_kde_kwin_idle_timeout *org_kde_kwin_idle_timeout) {
ctx.is_idle = false;
LOG_I("User isn't idle anymore");
LOG_D("User isn't idle anymore");
}
static const struct org_kde_kwin_idle_timeout_listener idle_timeout_listener = {
@ -273,34 +333,183 @@ static void add_seat_to_idle_handler(struct wl_seat *seat) {
if (!ctx.idle_handler){
return;
}
uint32_t timeout_ms = settings.idle_threshold/1000;
ctx.idle_timeout = org_kde_kwin_idle_get_idle_timeout(ctx.idle_handler, seat, timeout_ms);
org_kde_kwin_idle_timeout_add_listener(ctx.idle_timeout, &idle_timeout_listener, 0);
ctx.has_idle_monitor = true;
if (settings.idle_threshold > 0) {
uint32_t timeout_ms = settings.idle_threshold/1000;
ctx.idle_timeout = org_kde_kwin_idle_get_idle_timeout(ctx.idle_handler, seat, timeout_ms);
org_kde_kwin_idle_timeout_add_listener(ctx.idle_timeout, &idle_timeout_listener, 0);
ctx.has_idle_monitor = true;
}
}
// Warning, can return NULL
static struct dunst_output *get_configured_output() {
int n = 0;
int target_monitor = settings.monitor;
struct dunst_output *first_output = NULL, *configured_output = NULL,
*tmp_output = NULL;
wl_list_for_each(tmp_output, &ctx.outputs, link) {
if (n == 0)
first_output = tmp_output;
if (n == target_monitor)
configured_output = tmp_output;
n++;
}
// There's only 1 output, so return that
if (n == 1)
return first_output;
switch (settings.f_mode){
case FOLLOW_NONE: ; // this semicolon is neccesary
if (!configured_output) {
LOG_W("Monitor %i doesn't exist, using focused monitor", settings.monitor);
}
return configured_output;
case FOLLOW_MOUSE:
// fallthrough
case FOLLOW_KEYBOARD:
// fallthrough
default:
return NULL;
}
}
// does not do null checking
static void dunst_output_set_fullscreen(struct dunst_output *output,
struct zwlr_foreign_toplevel_handle_v1 *toplevel,
bool fullscreen) {
output->fullscreen = fullscreen;
if (fullscreen)
output->fullscreen_toplevel = toplevel;
else
output->fullscreen_toplevel = NULL;
LOG_D("Set output %i fullscreen state %i", output->global_name, fullscreen);
wake_up();
}
static void toplevel_output_leave(void *data,
struct zwlr_foreign_toplevel_handle_v1 *toplevel,
struct wl_output *output) {
zwlr_foreign_toplevel_handle_v1_set_user_data(toplevel, NULL);
}
static void toplevel_output_enter(void *data,
struct zwlr_foreign_toplevel_handle_v1 *toplevel,
struct wl_output *output) {
// FIXME toplevel can be on multiple outputs, so a list of outputs should be kept
zwlr_foreign_toplevel_handle_v1_set_user_data(toplevel, output);
}
static void toplevel_closed(void *data,
struct zwlr_foreign_toplevel_handle_v1 *toplevel) {
struct wl_output *output_wl = (struct wl_output*) data;
if (output_wl == NULL) {
return;
}
struct dunst_output *output = (struct dunst_output*) wl_output_get_user_data(output_wl);
if (output == NULL) {
return;
}
dunst_output_set_fullscreen(output, toplevel, false);
}
static void toplevel_state(void *data,
struct zwlr_foreign_toplevel_handle_v1 *toplevel,
struct wl_array *state) {
struct wl_output *output_wl = (struct wl_output*) data;
if (output_wl == NULL) {
return;
}
struct dunst_output *output = (struct dunst_output*) wl_output_get_user_data(output_wl);
if (output == NULL) {
return;
}
bool fullscreen = false;
bool activated = false;
enum zwlr_foreign_toplevel_handle_v1_state* element;
wl_array_for_each(element, state){
if (*element == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN) {
fullscreen = true;
}
if (*element == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED) {
activated = true;
}
}
if (fullscreen && activated) {
dunst_output_set_fullscreen(output, toplevel, true);
} else {
if (output->fullscreen_toplevel == toplevel) {
// this toplevel was fullscreen, but isn't anymore
dunst_output_set_fullscreen(output, toplevel, false);
}
}
}
static const struct zwlr_foreign_toplevel_handle_v1_listener foreign_toplevel_handle_listener = {
.title = noop,
.app_id = noop,
.output_enter = toplevel_output_enter,
.output_leave = toplevel_output_leave,
.state = toplevel_state,
.done = noop,
.closed = toplevel_closed,
};
static void toplevel_created(void *data,
struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1,
struct zwlr_foreign_toplevel_handle_v1 *toplevel){
zwlr_foreign_toplevel_handle_v1_add_listener(toplevel, &foreign_toplevel_handle_listener, NULL);
}
static void toplevel_finished(void *data,
struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1){
}
static const struct zwlr_foreign_toplevel_manager_v1_listener foreign_toplevel_manager_listener = {
.toplevel = toplevel_created,
.finished = toplevel_finished,
};
static void handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version) {
if (strcmp(interface, wl_compositor_interface.name) == 0) {
ctx.compositor = wl_registry_bind(registry, name,
&wl_compositor_interface, 4);
} else if (strcmp(interface, wl_shm_interface.name) == 0) {
ctx.shm = wl_registry_bind(registry, name,
&wl_shm_interface, 1);
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
ctx.layer_shell = wl_registry_bind(registry, name,
&zwlr_layer_shell_v1_interface, 1);
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
ctx.seat = wl_registry_bind(registry, name, &wl_seat_interface, 3);
wl_seat_add_listener(ctx.seat, &seat_listener, ctx.seat);
add_seat_to_idle_handler(ctx.seat);
} else if (strcmp(interface, wl_output_interface.name) == 0) {
struct wl_output *output =
wl_registry_bind(registry, name, &wl_output_interface, 3);
create_output(output, name);
} else if (strcmp(interface, org_kde_kwin_idle_interface.name) == 0 &&
version >= ORG_KDE_KWIN_IDLE_TIMEOUT_IDLE_SINCE_VERSION) {
ctx.idle_handler = wl_registry_bind(registry, name, &org_kde_kwin_idle_interface, 1);
int *count = data;
if (*count == 0)
{
if (strcmp(interface, wl_compositor_interface.name) == 0) {
ctx.compositor = wl_registry_bind(registry, name,
&wl_compositor_interface, 4);
} else if (strcmp(interface, wl_shm_interface.name) == 0) {
ctx.shm = wl_registry_bind(registry, name,
&wl_shm_interface, 1);
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
ctx.layer_shell = wl_registry_bind(registry, name,
&zwlr_layer_shell_v1_interface, 1);
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
ctx.seat = wl_registry_bind(registry, name, &wl_seat_interface, 3);
wl_seat_add_listener(ctx.seat, &seat_listener, ctx.seat);
add_seat_to_idle_handler(ctx.seat);
} else if (strcmp(interface, wl_output_interface.name) == 0) {
struct wl_output *output =
wl_registry_bind(registry, name, &wl_output_interface, 3);
create_output(output, name);
} else if (strcmp(interface, org_kde_kwin_idle_interface.name) == 0 &&
version >= ORG_KDE_KWIN_IDLE_TIMEOUT_IDLE_SINCE_VERSION) {
ctx.idle_handler = wl_registry_bind(registry, name, &org_kde_kwin_idle_interface, 1);
}
} else {
if (strcmp(interface, zwlr_foreign_toplevel_manager_v1_interface.name) == 0 &&
version >= ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN_SINCE_VERSION) {
// Only bind after the second pass to bind after binding to all the outputs.
// This is because otherwise toplevel_enter evens won't be sent.
ctx.toplevel_manager = wl_registry_bind(registry, name, &zwlr_foreign_toplevel_manager_v1_interface, 2);
zwlr_foreign_toplevel_manager_v1_add_listener(ctx.toplevel_manager, &foreign_toplevel_manager_listener, NULL);
}
}
}
@ -320,31 +529,38 @@ static const struct wl_registry_listener registry_listener = {
.global_remove = handle_global_remove,
};
bool init_wayland() {
bool wl_init() {
wl_list_init(&ctx.outputs);
//wl_list_init(&ctx.seats);
//wl_list_init(&ctx.seats); // TODO multi-seat support
ctx.display = wl_display_connect(NULL);
if (ctx.display == NULL) {
LOG_E("failed to create display");
LOG_W("failed to create display");
return false;
}
int count = 0;
ctx.registry = wl_display_get_registry(ctx.display);
wl_registry_add_listener(ctx.registry, &registry_listener, NULL);
wl_registry_add_listener(ctx.registry, &registry_listener, &count);
wl_display_roundtrip(ctx.display);
count = 1;
// we need a second pass to let for foreign_toplevel (look there for more info)
ctx.registry = wl_display_get_registry(ctx.display);
wl_registry_add_listener(ctx.registry, &registry_listener, &count);
wl_display_roundtrip(ctx.display);
if (ctx.compositor == NULL) {
LOG_E("compositor doesn't support wl_compositor");
LOG_W("compositor doesn't support wl_compositor");
return false;
}
if (ctx.shm == NULL) {
LOG_E("compositor doesn't support wl_shm");
LOG_W("compositor doesn't support wl_shm");
return false;
}
if (ctx.layer_shell == NULL) {
LOG_E("compositor doesn't support zwlr_layer_shell_v1");
LOG_W("compositor doesn't support zwlr_layer_shell_v1");
return false;
}
if (ctx.seat == NULL) {
@ -359,10 +575,17 @@ bool init_wayland() {
}
}
if (ctx.toplevel_manager == NULL) {
LOG_W("compositor doesn't support zwlr_foreign_toplevel_v1. Fullscreen detection won't work");
}
return true;
}
void finish_wayland() {
void wl_deinit() {
// We need to check if any of these are NULL, since the initialization
// could have been aborted half way through, or the compositor doesn't
// support some of these features.
if (ctx.layer_surface != NULL) {
zwlr_layer_surface_v1_destroy(ctx.layer_surface);
}
@ -372,56 +595,47 @@ void finish_wayland() {
finish_buffer(&ctx.buffers[0]);
finish_buffer(&ctx.buffers[1]);
// The output list is initialized at the start of init, so no need to
// check for NULL
struct dunst_output *output, *output_tmp;
wl_list_for_each_safe(output, output_tmp, &ctx.outputs, link) {
destroy_output(output);
}
if (ctx.seat) {
wl_pointer_release(ctx.pointer.wl_pointer);
if (ctx.pointer.wl_pointer)
wl_pointer_release(ctx.pointer.wl_pointer);
wl_seat_release(ctx.seat);
ctx.seat = NULL;
}
zwlr_layer_shell_v1_destroy(ctx.layer_shell);
wl_compositor_destroy(ctx.compositor);
wl_shm_destroy(ctx.shm);
wl_registry_destroy(ctx.registry);
wl_display_disconnect(ctx.display);
if (ctx.idle_handler)
org_kde_kwin_idle_destroy(ctx.idle_handler);
org_kde_kwin_idle_destroy(ctx.idle_handler);
org_kde_kwin_idle_timeout_release(ctx.idle_timeout);
}
if (ctx.idle_timeout)
org_kde_kwin_idle_timeout_release(ctx.idle_timeout);
static struct dunst_output *get_configured_output() {
struct dunst_output *output;
if (ctx.layer_shell)
zwlr_layer_shell_v1_destroy(ctx.layer_shell);
switch (settings.f_mode){
case FOLLOW_NONE: ; // this semicolon is neccesary
int n = 0;
int target_monitor = settings.monitor;
if (ctx.compositor)
wl_compositor_destroy(ctx.compositor);
wl_list_for_each(output, &ctx.outputs, link) {
if (n == target_monitor)
return output;
n++;
}
LOG_W("Monitor %i doesn't exist, using focused monitor", settings.monitor);
return NULL;
case FOLLOW_MOUSE:
// fallthrough
case FOLLOW_KEYBOARD:
// fallthrough
default:
return NULL;
}
if (ctx.shm)
wl_shm_destroy(ctx.shm);
if (ctx.registry)
wl_registry_destroy(ctx.registry);
if (ctx.display)
wl_display_disconnect(ctx.display);
}
static void schedule_frame_and_commit();
// Draw and commit a new frame.
static void send_frame() {
int scale = 1;
int scale = wl_get_scale();
struct dunst_output *output = get_configured_output();
int height = ctx.cur_dim.h;
@ -455,11 +669,13 @@ static void send_frame() {
// different output), we need to create it.
if (ctx.layer_surface == NULL) {
struct wl_output *wl_output = NULL;
if (output != NULL) {
wl_output = output->wl_output;
}
ctx.layer_surface_output = output;
ctx.surface = wl_compositor_create_surface(ctx.compositor);
wl_surface_add_listener(ctx.surface, &surface_listener, NULL);
if (settings.frame_color)
ctx.layer_surface = zwlr_layer_shell_v1_get_layer_surface(
ctx.layer_shell, ctx.surface, wl_output,
settings.layer, "notifications");
@ -485,8 +701,6 @@ static void send_frame() {
if (ctx.height != height || ctx.width != width) {
struct dimensions dim = ctx.cur_dim;
// Set window size
LOG_D("Window dimensions %ix%i", dim.w, dim.h);
LOG_D("Window position %ix%i", dim.x, dim.y);
zwlr_layer_surface_v1_set_size(ctx.layer_surface,
dim.w, dim.h);
@ -582,13 +796,6 @@ void set_dirty() {
schedule_frame_and_commit();
}
void wl_init(void) {
init_wayland();
}
void wl_deinit(void) {
}
window wl_win_create(void) {
struct window_wl *win = g_malloc0(sizeof(struct window_wl));
@ -605,6 +812,8 @@ void wl_win_destroy(window winptr) {
}
void wl_win_show(window win) {
// This is here for compatibilty with the X11 output. The window is
// already shown in wl_display_surface.
}
void wl_win_hide(window win) {
@ -616,12 +825,15 @@ void wl_win_hide(window win) {
void wl_display_surface(cairo_surface_t *srf, window winptr, const struct dimensions* dim) {
/* struct window_wl *win = (struct window_wl*)winptr; */
ctx.current_buffer = get_next_buffer(ctx.shm, ctx.buffers, dim->w, dim->h);
int scale = wl_get_scale();
LOG_D("Buffer size (scaled) %ix%i", dim->w * scale, dim->h * scale);
ctx.current_buffer = get_next_buffer(ctx.shm, ctx.buffers,
dim->w * scale, dim->h * scale);
cairo_t *c = ctx.current_buffer->cairo;
cairo_save(c);
cairo_set_source_surface(c, srf, 0, 0);
cairo_rectangle(c, 0, 0, dim->w, dim->h);
cairo_rectangle(c, 0, 0, dim->w * scale, dim->h * scale);
cairo_fill(c);
cairo_restore(c);
@ -631,10 +843,6 @@ void wl_display_surface(cairo_surface_t *srf, window winptr, const struct dimens
wl_display_roundtrip(ctx.display);
}
bool wl_win_visible(window win) {
return true;
}
cairo_t* wl_win_get_context(window winptr) {
struct window_wl *win = (struct window_wl*)winptr;
ctx.current_buffer = get_next_buffer(ctx.shm, ctx.buffers, 500, 500);
@ -653,6 +861,7 @@ const struct screen_info* wl_get_active_screen(void) {
.id = 0,
.mmh = 500
};
scr.dpi = wl_get_scale() * 96;
return &scr;
}
@ -666,7 +875,43 @@ bool wl_is_idle(void) {
return ctx.is_idle;
}
}
bool wl_have_fullscreen_window(void) {
return false;
bool have_fullscreen = false;
struct dunst_output *current_output = get_configured_output();
if (!current_output) {
// Cannot detect focused output, so return true if any of the
// outputs is fullscreen. This will work even when unfocused
// outputs have fullscreen toplevels, since a toplevel has to
// be fullscreen and activate to consider an output fullscreen.
struct dunst_output *output;
wl_list_for_each(output, &ctx.outputs, link) {
have_fullscreen |= output->fullscreen;
}
} else {
have_fullscreen = current_output->fullscreen;
}
LOG_D("Fullscreen queried: %i", have_fullscreen);
return have_fullscreen;
}
int wl_get_scale(void) {
int scale = 0;
struct dunst_output *output = get_configured_output();
if (output) {
scale = output->scale;
} else {
// return the largest scale
struct dunst_output *output;
wl_list_for_each(output, &ctx.outputs, link) {
scale = MAX(output->scale, scale);
}
}
if (scale <= 0)
scale = 1;
return scale;
}
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */

View File

@ -7,7 +7,7 @@
#include "../output.h"
void wl_init(void);
bool wl_init(void);
void wl_deinit(void);
window wl_win_create(void);
@ -17,12 +17,16 @@ void wl_win_show(window);
void wl_win_hide(window);
void wl_display_surface(cairo_surface_t *srf, window win, const struct dimensions*);
bool wl_win_visible(window);
cairo_t* wl_win_get_context(window);
const struct screen_info* wl_get_active_screen(void);
bool wl_is_idle(void);
bool wl_have_fullscreen_window(void);
// Return the dpi scaling of the current output. Everything that's rendered
// should be multiplied by this value, but don't use it to multiply other
// values. All sizes should be in unscaled units.
int wl_get_scale(void);
#endif
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */

View File

@ -113,7 +113,7 @@ static void x_win_corners_shape(struct window_x11 *win, const int rad)
draw_rounded_rect(cr, 0, 0,
width, height,
rad,
rad, 1,
true, true);
cairo_fill(cr);
@ -179,11 +179,6 @@ void x_display_surface(cairo_surface_t *srf, window winptr, const struct dimensi
}
bool x_win_visible(window winptr)
{
return ((struct window_x11*)winptr)->visible;
}
cairo_t* x_win_get_context(window winptr)
{
return ((struct window_x11*)win)->c_ctx;
@ -501,14 +496,16 @@ static void XRM_update_db(void)
/*
* Setup X11 stuff
*/
void x_setup(void)
bool x_setup(void)
{
/* initialize xctx.dc, font, keyboard, colors */
if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
LOG_W("No locale support");
if (!(xctx.dpy = XOpenDisplay(NULL))) {
DIE("Cannot open X11 display.");
LOG_W("Cannot open X11 display.");
return false;
}
x_shortcut_init(&settings.close_ks);
@ -532,6 +529,7 @@ void x_setup(void)
init_screens();
x_shortcut_grab(&settings.history_ks);
return true;
}
struct geometry x_parse_geometry(const char *geom_str)
@ -944,4 +942,8 @@ static void x_shortcut_init(struct keyboard_shortcut *ks)
g_free(str_begin);
}
int x_get_scale(void) {
return 1;
}
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */

View File

@ -42,15 +42,15 @@ void x_win_hide(window);
void x_display_surface(cairo_surface_t *srf, window, const struct dimensions *dim);
bool x_win_visible(window);
cairo_t* x_win_get_context(window);
/* X misc */
bool x_is_idle(void);
void x_setup(void);
bool x_setup(void);
void x_free(void);
struct geometry x_parse_geometry(const char *geom_str);
int x_get_scale(void);
#endif
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */

View File

@ -686,6 +686,7 @@ TEST test_server_caps(enum markup_mode markup)
ASSERT(g_strv_contains(capsarray, "actions"));
ASSERT(g_strv_contains(capsarray, "body"));
ASSERT(g_strv_contains(capsarray, "body-hyperlinks"));
ASSERT(g_strv_contains(capsarray, "icon-static"));
ASSERT(g_strv_contains(capsarray, "x-dunst-stack-tag"));
if (settings.markup != MARKUP_NO)
@ -856,6 +857,10 @@ SUITE(suite_dbus)
loop = g_main_loop_new(NULL, false);
dbus_bus = g_test_dbus_new(G_TEST_DBUS_NONE);
// workaround bug in glib where stdout output is duplicated
// See https://gitlab.gnome.org/GNOME/glib/-/issues/2322
fflush(stdout);
g_test_dbus_up(dbus_bus);
thread_tests = g_thread_new("testexecutor", run_threaded_tests, loop);

View File

@ -12,6 +12,8 @@
extern const char *base;
int scale = 1;
TEST test_get_path_from_icon_null(void){
char *result = get_path_from_icon_name(NULL);
ASSERT_EQ(result, NULL);
@ -86,7 +88,7 @@ TEST test_get_pixbuf_from_file_tilde(void)
gchar *path = g_build_filename(base, iconpath, "valid", "icon1.svg", NULL);
path = string_replace_at(path, 0, strlen(home), "~");
GdkPixbuf *pixbuf = get_pixbuf_from_file(path);
GdkPixbuf *pixbuf = get_pixbuf_from_file(path, scale);
g_clear_pointer(&path, g_free);
ASSERT(pixbuf);
@ -101,7 +103,7 @@ TEST test_get_pixbuf_from_file_absolute(void)
gchar *path = g_build_filename(base, iconpath, "valid", "icon1.svg", NULL);
GdkPixbuf *pixbuf = get_pixbuf_from_file(path);
GdkPixbuf *pixbuf = get_pixbuf_from_file(path, scale);
g_clear_pointer(&path, g_free);
ASSERT(pixbuf);
@ -113,7 +115,7 @@ TEST test_get_pixbuf_from_file_absolute(void)
TEST test_get_pixbuf_from_icon_invalid(void)
{
GdkPixbuf *pixbuf = get_pixbuf_from_icon("invalid");
GdkPixbuf *pixbuf = get_pixbuf_from_icon("invalid", scale);
ASSERT(pixbuf == NULL);
g_clear_pointer(&pixbuf, g_object_unref);
@ -122,7 +124,7 @@ TEST test_get_pixbuf_from_icon_invalid(void)
TEST test_get_pixbuf_from_icon_both(void)
{
GdkPixbuf *pixbuf = get_pixbuf_from_icon("icon1");
GdkPixbuf *pixbuf = get_pixbuf_from_icon("icon1", scale);
// the first icon found is invalid, so the pixbuf is empty
ASSERT(!pixbuf);
g_clear_pointer(&pixbuf, g_object_unref);
@ -132,7 +134,7 @@ TEST test_get_pixbuf_from_icon_both(void)
TEST test_get_pixbuf_from_icon_onlysvg(void)
{
GdkPixbuf *pixbuf = get_pixbuf_from_icon("onlysvg");
GdkPixbuf *pixbuf = get_pixbuf_from_icon("onlysvg", scale);
ASSERT(pixbuf);
ASSERTm("SVG pixbuf isn't loaded", IS_ICON_SVG(pixbuf));
g_clear_pointer(&pixbuf, g_object_unref);
@ -142,7 +144,7 @@ TEST test_get_pixbuf_from_icon_onlysvg(void)
TEST test_get_pixbuf_from_icon_onlypng(void)
{
GdkPixbuf *pixbuf = get_pixbuf_from_icon("onlypng");
GdkPixbuf *pixbuf = get_pixbuf_from_icon("onlypng", scale);
ASSERT(pixbuf);
ASSERTm("PNG pixbuf isn't loaded", IS_ICON_PNG(pixbuf));
g_clear_pointer(&pixbuf, g_object_unref);
@ -153,7 +155,7 @@ TEST test_get_pixbuf_from_icon_onlypng(void)
TEST test_get_pixbuf_from_icon_filename(void)
{
char *icon = g_strconcat(base, "/data/icons/valid.png", NULL);
GdkPixbuf *pixbuf = get_pixbuf_from_icon(icon);
GdkPixbuf *pixbuf = get_pixbuf_from_icon(icon, scale);
ASSERT(pixbuf);
ASSERTm("PNG pixbuf isn't loaded", IS_ICON_PNG(pixbuf));
g_clear_pointer(&pixbuf, g_object_unref);
@ -165,7 +167,7 @@ TEST test_get_pixbuf_from_icon_filename(void)
TEST test_get_pixbuf_from_icon_fileuri(void)
{
char *icon = g_strconcat("file://", base, "/data/icons/valid.svg", NULL);
GdkPixbuf *pixbuf = get_pixbuf_from_icon(icon);
GdkPixbuf *pixbuf = get_pixbuf_from_icon(icon, scale);
ASSERT(pixbuf);
ASSERTm("SVG pixbuf isn't loaded", IS_ICON_SVG(pixbuf));
g_clear_pointer(&pixbuf, g_object_unref);
@ -220,7 +222,7 @@ TEST test_icon_size_clamp_too_small_then_too_big(void)
TEST test_get_pixbuf_from_icon_both_is_scaled(void)
{
GdkPixbuf *pixbuf = get_pixbuf_from_icon("onlypng");
GdkPixbuf *pixbuf = get_pixbuf_from_icon("onlypng", scale);
ASSERT(pixbuf);
ASSERT_EQ(gdk_pixbuf_get_width(pixbuf), 16);
ASSERT_EQ(gdk_pixbuf_get_height(pixbuf), 16);

View File

@ -763,6 +763,68 @@ TEST test_queue_find_by_id(void)
PASS();
}
void print_queues() {
printf("\nQueues:\n");
for (GList *iter = g_queue_peek_head_link(QUEUE_WAIT); iter;
iter = iter->next) {
struct notification *notif = iter->data;
printf("waiting %s\n", notif->summary);
}
}
// Test if notifications are correctly sorted, even if dunst is paused in
// between. See #838 for the issue.
TEST test_queue_no_sort_and_pause(void)
{
// Setting sort to false, this means that notifications will only be
// sorted based on time
settings.sort = false;
settings.geometry.h = 0;
struct notification *n;
queues_init();
n = test_notification("n0", 0);
queues_notification_insert(n);
queues_update(STATUS_NORMAL);
n = test_notification("n1", 0);
queues_notification_insert(n);
queues_update(STATUS_NORMAL);
n = test_notification("n2", 0);
queues_notification_insert(n);
queues_update(STATUS_PAUSE);
n = test_notification("n3", 0);
queues_notification_insert(n);
queues_update(STATUS_PAUSE);
/* queues_update(STATUS_NORMAL); */
n = test_notification("n4", 0);
queues_notification_insert(n);
queues_update(STATUS_NORMAL);
QUEUE_LEN_ALL(0, 5, 0);
const char* order[] = {
"n0",
"n1",
"n2",
"n3",
"n4",
};
for (int i = 0; i < g_queue_get_length(QUEUE_DISP); i++) {
struct notification *notif = g_queue_peek_nth(QUEUE_DISP, i);
ASSERTm("Notifications are not in the right order",
STR_EQ(notif->summary, order[i]));
}
queues_teardown();
PASS();
}
SUITE(suite_queues)
{
settings.icon_path = "";
@ -794,6 +856,7 @@ SUITE(suite_queues)
RUN_TEST(test_queues_update_xmore);
RUN_TEST(test_queues_timeout_before_paused);
RUN_TEST(test_queue_find_by_id);
RUN_TEST(test_queue_no_sort_and_pause);
settings.icon_path = NULL;
}