Compare commits
54 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6ee460335d | ||
![]() |
988b8d2747 | ||
![]() |
9f4f110c53 | ||
![]() |
e898fa589a | ||
![]() |
702abc7a03 | ||
![]() |
b77f76f02e | ||
![]() |
cf091bea37 | ||
![]() |
1040febfb9 | ||
![]() |
652cb7efb4 | ||
![]() |
7d91f978eb | ||
![]() |
0994c57b3e | ||
![]() |
cb6f2ca3b3 | ||
![]() |
bd6abfcc5d | ||
![]() |
1d3822ee8b | ||
![]() |
ea531ca9ae | ||
![]() |
e69adcefea | ||
![]() |
f2017eb3d6 | ||
![]() |
d9282b7f86 | ||
![]() |
efac6e70b0 | ||
![]() |
0dfc4ed738 | ||
![]() |
b49925c86d | ||
![]() |
8e80871c50 | ||
![]() |
d5ee1febca | ||
![]() |
941c527af9 | ||
![]() |
b66f8f362d | ||
![]() |
85ff05062c | ||
![]() |
db5e6ce8f4 | ||
![]() |
aad4dbaf3d | ||
![]() |
98a61f0896 | ||
![]() |
7c6620c92d | ||
![]() |
75af42c83a | ||
![]() |
c4e428e9d5 | ||
![]() |
5c0d7ea662 | ||
![]() |
176aad4f3c | ||
![]() |
af49b76586 | ||
![]() |
6c61f3e5e2 | ||
![]() |
bec5e9d25a | ||
![]() |
790342b913 | ||
![]() |
4234813417 | ||
![]() |
d65f69b4db | ||
![]() |
b75d35adb4 | ||
![]() |
3acffdb194 | ||
![]() |
7e22272ebb | ||
![]() |
7292bfcd89 | ||
![]() |
8413ade9d7 | ||
![]() |
54b665898f | ||
![]() |
f8a2ff48b3 | ||
![]() |
a8b2058fcf | ||
![]() |
ceca7cfcc7 | ||
![]() |
598ec5797e | ||
![]() |
0f588998fe | ||
![]() |
500b00b344 | ||
![]() |
36186d37ea | ||
![]() |
d7f93a3a69 |
1
.github/workflows/main.yml
vendored
@ -25,6 +25,7 @@ jobs:
|
||||
|
||||
env:
|
||||
CC: ${{ matrix.CC }}
|
||||
EXTRA_CFLAGS: "-Werror"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
|
@ -1,5 +1,11 @@
|
||||
# Dunst changelog
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
### Changed
|
||||
### Fixed
|
||||
|
||||
## 1.6.1 - 2021-02-21:
|
||||
|
||||
### Fixed
|
||||
|
@ -15,6 +15,10 @@
|
||||
- Add the comments to the prototype. Doxygen will merge the protoype and implementation documentation anyways.
|
||||
Except for **static** methods, add the documentation header to the implementation and *not to the prototype*.
|
||||
- Member documentation should happen with `/**<` and should span to the right side of the member
|
||||
- Test files that have the same name as a file in src/\* can include the
|
||||
associated .c file. This is because they are being compiled INSTEAD of the src
|
||||
file.
|
||||
|
||||
|
||||
## Log messages
|
||||
|
||||
|
8
Makefile
@ -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
@ -1,34 +1,95 @@
|
||||
[](https://github.com/dunst-project/dunst/actions?query=workflow%3Amain) [](https://codecov.io/gh/dunst-project/dunst)
|
||||
|
||||
## Dunst
|
||||
# Dunst
|
||||
|
||||
* [Wiki][wiki]
|
||||
* [Description](#description)
|
||||
* [Compiling](#compiling)
|
||||
<i>A highly configurable and lightweight notification daemon.</i>
|
||||
|
||||

|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Features](#features)
|
||||
* [Building](#building)
|
||||
* [Documentation](#documentation)
|
||||
* [Copyright](#copyright)
|
||||
|
||||
## Description
|
||||
# Features
|
||||
|
||||
Dunst is a highly configurable and lightweight notification daemon.
|
||||
## ⚙️ Highly customizable
|
||||
|
||||
Customize fonts, icons, timeouts, and more. Are you unhappy with the default
|
||||
shortcuts and colors? No worries, you can change these all with a simple
|
||||
configuration file tweak.
|
||||
|
||||
_click the images to see the dunstrc_
|
||||
|
||||
<a href="https://gist.github.com/NNBnh/5f6e601a6a82a6ed43b1959698758141">
|
||||
<img alt="screenshot1" src="contrib/screenshots/screenshot1_cut.png">
|
||||
</a>
|
||||
|
||||
<a href="https://gist.github.com/fwSmit/9127d988b07bcec9d869f2c927d0f616">
|
||||
<img alt="screenshot2" src="contrib/screenshots/screenshot2_cut.png">
|
||||
</a>
|
||||
|
||||
## 📜 Scripting
|
||||
|
||||
<a href="https://gitlab.manjaro.org/profiles-and-settings/manjaro-theme-settings/-/blob/master/skel/.config/dunst/dunstrc">
|
||||
<img alt="screenshot_urgency" src="contrib/screenshots/screenshot_urgency.png">
|
||||
</a>
|
||||
|
||||
Run custom scripts on notifications matching a specified pattern. Have espeak
|
||||
read out your notifications, or play a song when your significant other signs on
|
||||
in pidgin!
|
||||
|
||||
## 📋 Rules
|
||||
|
||||
Change the look or behavior of notifications matching a specified pattern. You
|
||||
could use this to change the color of message notifications from your favorite
|
||||
jabber buddies, or to prevent important work email notifications from
|
||||
disappearing until you manually dismiss them.
|
||||
|
||||
## ⏸️ Pause
|
||||
|
||||
If you want to take a break and not receive any notifications for a while, just
|
||||
pause dunst. All notifications will be saved for you to catch up
|
||||
later.
|
||||
|
||||
## 🕘 History
|
||||
|
||||
Catch an unread notification disappearing from the corner of your eye? Just tap
|
||||
a keyboard shortcut to replay the last notification, or continue tapping to see
|
||||
your notification history.
|
||||
|
||||
# Documentation
|
||||
|
||||
Most documentation can be found in dunst's man pages. In
|
||||
[**dunst(1)**](docs/dunst.1.pod) contains some general instructions on how
|
||||
to run dunst and in
|
||||
[**dunst(5)**](docs/dunst.5.pod) all of dunst's configuration options are
|
||||
explained.
|
||||
|
||||
On the dunst [wiki][wiki] you can find guides and installation instructions and
|
||||
on the dunst [website][website] there is a [FAQ][FAQ] with common issues.
|
||||
|
||||
## Installation
|
||||
|
||||
Dunst is available in many package repositories. If it's not available in your
|
||||
distro's repositories, don't worry, it's not hard to build it yourself.
|
||||
|
||||
### Dependencies
|
||||
|
||||
Dunst has a number of build dependencies that must be present before attempting configuration. The names are different depending on [distribution](https://github.com/dunst-project/dunst/wiki/Dependencies):
|
||||
|
||||
- dbus
|
||||
- dbus (runtime)
|
||||
- libxinerama
|
||||
- libxrandr
|
||||
- libxss
|
||||
- glib
|
||||
- pango/cairo
|
||||
- libgtk-3-dev
|
||||
- libnotify (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
|
||||
|
@ -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
|
||||
|
BIN
contrib/screenshots/default_config.png
Normal file
After Width: | Height: | Size: 388 KiB |
BIN
contrib/screenshots/music.png
Normal file
After Width: | Height: | Size: 406 KiB |
BIN
contrib/screenshots/screenshot1.png
Normal file
After Width: | Height: | Size: 672 KiB |
BIN
contrib/screenshots/screenshot1_cut.png
Normal file
After Width: | Height: | Size: 214 KiB |
BIN
contrib/screenshots/screenshot2.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
contrib/screenshots/screenshot2_cut.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
contrib/screenshots/screenshot3.png
Normal file
After Width: | Height: | Size: 2.2 MiB |
BIN
contrib/screenshots/screenshot3_cut.png
Normal file
After Width: | Height: | Size: 90 KiB |
BIN
contrib/screenshots/screenshot_urgency.png
Normal file
After Width: | Height: | Size: 393 KiB |
@ -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)
|
||||
|
@ -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)
|
||||
|
33
docs/internal/release-checklist.md
Normal file
@ -0,0 +1,33 @@
|
||||
# Main repo
|
||||
- [ ] Verify that the changelog is up to date
|
||||
- [ ] Write release notes (Only if non-patch release)
|
||||
|
||||
- [ ] Verify that the working directory is clean and on the master branch
|
||||
- [ ] Change the version in the Makefile to "x.x.x (iso-date)"
|
||||
- [ ] Update changelog `Unreleased` entry to the new version
|
||||
- [ ] Commit changes (Commit title: `Dunst vX.X.X`)
|
||||
- [ ] Tag commit, make sure it's an annotated tag (git tag -a) (Tag title: Dunst vX.X.X)
|
||||
- [ ] Push commits
|
||||
- [ ] Push tags
|
||||
|
||||
# Dunst-project.org
|
||||
- [ ] Update release number in the download page
|
||||
- [ ] Update release date in the download page
|
||||
- [ ] Update version number in the download link
|
||||
- [ ] Copy release notes to the download page (Only if non-patch release)
|
||||
- [ ] Copy changelog to the changelog page
|
||||
- [ ] Copy documentation to the documentation page
|
||||
- [ ] Verify that they look fine when rendered
|
||||
- [ ] Commit changes
|
||||
- [ ] Run deploy script
|
||||
- [ ] Push to main website repo
|
||||
- [ ] Push to gh-pages
|
||||
|
||||
# Main repo
|
||||
- [ ] Copy release notes to githubs release feature
|
||||
- [ ] Publish release on github
|
||||
- [ ] Update maint branch to point to master
|
||||
|
||||
- [ ] Create new Unreleased section for the changelog
|
||||
- [ ] Update Makefile version to -non-git
|
||||
- [ ] Commit & push
|
9
dunstrc
@ -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.
|
||||
#
|
||||
|
@ -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);
|
||||
|
122
src/draw.c
@ -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: */
|
||||
|
@ -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: */
|
||||
|
10
src/dunst.c
@ -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);
|
||||
|
43
src/icon.c
@ -86,6 +86,14 @@ static void pixbuf_data_to_cairo_data(
|
||||
}
|
||||
}
|
||||
|
||||
int get_icon_width(cairo_surface_t *icon, int scale) {
|
||||
return cairo_image_surface_get_width(icon) / scale;
|
||||
}
|
||||
|
||||
int get_icon_height(cairo_surface_t *icon, int scale) {
|
||||
return cairo_image_surface_get_height(icon) / scale;
|
||||
}
|
||||
|
||||
cairo_surface_t *gdk_pixbuf_to_cairo_surface(GdkPixbuf *pixbuf)
|
||||
{
|
||||
assert(pixbuf);
|
||||
@ -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;
|
||||
}
|
||||
|
26
src/icon.h
@ -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: */
|
||||
|
20
src/input.c
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
10
src/log.c
@ -74,19 +74,21 @@ static void dunst_log_handler(
|
||||
gpointer testing)
|
||||
{
|
||||
if (testing)
|
||||
return;
|
||||
log_level = G_LOG_LEVEL_ERROR;
|
||||
|
||||
GLogLevelFlags message_level_masked = message_level & G_LOG_LEVEL_MASK;
|
||||
|
||||
/* if you want to have a debug build, you want to log anything,
|
||||
* unconditionally, without specifying debug log level again */
|
||||
#ifndef DEBUG_BUILD
|
||||
if (log_level < message_level)
|
||||
if (log_level < message_level_masked)
|
||||
return;
|
||||
#endif
|
||||
const char *log_level_str =
|
||||
log_level_to_string(message_level & G_LOG_LEVEL_MASK);
|
||||
log_level_to_string(message_level_masked);
|
||||
|
||||
/* Use stderr for warnings and higher */
|
||||
if (message_level <= G_LOG_LEVEL_WARNING)
|
||||
if (message_level_masked <= G_LOG_LEVEL_WARNING)
|
||||
g_printerr("%s: %s\n", log_level_str, message);
|
||||
else
|
||||
g_print("%s: %s\n", log_level_str, message);
|
||||
|
@ -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;
|
||||
|
10
src/menu.h
@ -2,6 +2,8 @@
|
||||
#ifndef DUNST_MENU_H
|
||||
#define DUNST_MENU_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
/**
|
||||
* Extract all urls from the given string.
|
||||
*
|
||||
@ -16,9 +18,15 @@ void invoke_action(const char *action);
|
||||
void regex_teardown(void);
|
||||
|
||||
/**
|
||||
* Open the context menu that lets the user select urls/actions/etc.
|
||||
* Open the context menu that lets the user select urls/actions/etc for all displayed notifications.
|
||||
*/
|
||||
void context_menu(void);
|
||||
|
||||
/**
|
||||
* Open the context menu that lets the user select urls/actions/etc for the specified notifications.
|
||||
* @param notifications (nullable) List of notifications for which the context menu should be opened
|
||||
*/
|
||||
void context_menu_for(GList *notifications);
|
||||
|
||||
#endif
|
||||
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "rules.h"
|
||||
#include "settings.h"
|
||||
#include "utils.h"
|
||||
#include "draw.h"
|
||||
|
||||
static void notification_extract_urls(struct notification *n);
|
||||
static void notification_format_message(struct notification *n);
|
||||
@ -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);
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
40
src/output.c
@ -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: */
|
||||
|
12
src/output.h
@ -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: */
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -0,0 +1,611 @@
|
||||
/* Generated by wayland-scanner 1.18.0 */
|
||||
|
||||
#ifndef WLR_FOREIGN_TOPLEVEL_MANAGEMENT_UNSTABLE_V1_CLIENT_PROTOCOL_H
|
||||
#define WLR_FOREIGN_TOPLEVEL_MANAGEMENT_UNSTABLE_V1_CLIENT_PROTOCOL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "wayland-client.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @page page_wlr_foreign_toplevel_management_unstable_v1 The wlr_foreign_toplevel_management_unstable_v1 protocol
|
||||
* @section page_ifaces_wlr_foreign_toplevel_management_unstable_v1 Interfaces
|
||||
* - @subpage page_iface_zwlr_foreign_toplevel_manager_v1 - list and control opened apps
|
||||
* - @subpage page_iface_zwlr_foreign_toplevel_handle_v1 - an opened toplevel
|
||||
* @section page_copyright_wlr_foreign_toplevel_management_unstable_v1 Copyright
|
||||
* <pre>
|
||||
*
|
||||
* Copyright © 2018 Ilia Bozhinov
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this
|
||||
* software and its documentation for any purpose is hereby granted
|
||||
* without fee, provided that the above copyright notice appear in
|
||||
* all copies and that both that copyright notice and this permission
|
||||
* notice appear in supporting documentation, and that the name of
|
||||
* the copyright holders not be used in advertising or publicity
|
||||
* pertaining to distribution of the software without specific,
|
||||
* written prior permission. The copyright holders make no
|
||||
* representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied
|
||||
* warranty.
|
||||
*
|
||||
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
* THIS SOFTWARE.
|
||||
* </pre>
|
||||
*/
|
||||
struct wl_output;
|
||||
struct wl_seat;
|
||||
struct wl_surface;
|
||||
struct zwlr_foreign_toplevel_handle_v1;
|
||||
struct zwlr_foreign_toplevel_manager_v1;
|
||||
|
||||
/**
|
||||
* @page page_iface_zwlr_foreign_toplevel_manager_v1 zwlr_foreign_toplevel_manager_v1
|
||||
* @section page_iface_zwlr_foreign_toplevel_manager_v1_desc Description
|
||||
*
|
||||
* The purpose of this protocol is to enable the creation of taskbars
|
||||
* and docks by providing them with a list of opened applications and
|
||||
* letting them request certain actions on them, like maximizing, etc.
|
||||
*
|
||||
* After a client binds the zwlr_foreign_toplevel_manager_v1, each opened
|
||||
* toplevel window will be sent via the toplevel event
|
||||
* @section page_iface_zwlr_foreign_toplevel_manager_v1_api API
|
||||
* See @ref iface_zwlr_foreign_toplevel_manager_v1.
|
||||
*/
|
||||
/**
|
||||
* @defgroup iface_zwlr_foreign_toplevel_manager_v1 The zwlr_foreign_toplevel_manager_v1 interface
|
||||
*
|
||||
* The purpose of this protocol is to enable the creation of taskbars
|
||||
* and docks by providing them with a list of opened applications and
|
||||
* letting them request certain actions on them, like maximizing, etc.
|
||||
*
|
||||
* After a client binds the zwlr_foreign_toplevel_manager_v1, each opened
|
||||
* toplevel window will be sent via the toplevel event
|
||||
*/
|
||||
extern const struct wl_interface zwlr_foreign_toplevel_manager_v1_interface;
|
||||
/**
|
||||
* @page page_iface_zwlr_foreign_toplevel_handle_v1 zwlr_foreign_toplevel_handle_v1
|
||||
* @section page_iface_zwlr_foreign_toplevel_handle_v1_desc Description
|
||||
*
|
||||
* A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel
|
||||
* window. Each app may have multiple opened toplevels.
|
||||
*
|
||||
* Each toplevel has a list of outputs it is visible on, conveyed to the
|
||||
* client with the output_enter and output_leave events.
|
||||
* @section page_iface_zwlr_foreign_toplevel_handle_v1_api API
|
||||
* See @ref iface_zwlr_foreign_toplevel_handle_v1.
|
||||
*/
|
||||
/**
|
||||
* @defgroup iface_zwlr_foreign_toplevel_handle_v1 The zwlr_foreign_toplevel_handle_v1 interface
|
||||
*
|
||||
* A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel
|
||||
* window. Each app may have multiple opened toplevels.
|
||||
*
|
||||
* Each toplevel has a list of outputs it is visible on, conveyed to the
|
||||
* client with the output_enter and output_leave events.
|
||||
*/
|
||||
extern const struct wl_interface zwlr_foreign_toplevel_handle_v1_interface;
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_manager_v1
|
||||
* @struct zwlr_foreign_toplevel_manager_v1_listener
|
||||
*/
|
||||
struct zwlr_foreign_toplevel_manager_v1_listener {
|
||||
/**
|
||||
* a toplevel has been created
|
||||
*
|
||||
* This event is emitted whenever a new toplevel window is
|
||||
* created. It is emitted for all toplevels, regardless of the app
|
||||
* that has created them.
|
||||
*
|
||||
* All initial details of the toplevel(title, app_id, states, etc.)
|
||||
* will be sent immediately after this event via the corresponding
|
||||
* events in zwlr_foreign_toplevel_handle_v1.
|
||||
*/
|
||||
void (*toplevel)(void *data,
|
||||
struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *toplevel);
|
||||
/**
|
||||
* the compositor has finished with the toplevel manager
|
||||
*
|
||||
* This event indicates that the compositor is done sending
|
||||
* events to the zwlr_foreign_toplevel_manager_v1. The server will
|
||||
* destroy the object immediately after sending this request, so it
|
||||
* will become invalid and the client should free any resources
|
||||
* associated with it.
|
||||
*/
|
||||
void (*finished)(void *data,
|
||||
struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_manager_v1
|
||||
*/
|
||||
static inline int
|
||||
zwlr_foreign_toplevel_manager_v1_add_listener(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1,
|
||||
const struct zwlr_foreign_toplevel_manager_v1_listener *listener, void *data)
|
||||
{
|
||||
return wl_proxy_add_listener((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1,
|
||||
(void (**)(void)) listener, data);
|
||||
}
|
||||
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_STOP 0
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_manager_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_TOPLEVEL_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_manager_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_FINISHED_SINCE_VERSION 1
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_manager_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_STOP_SINCE_VERSION 1
|
||||
|
||||
/** @ingroup iface_zwlr_foreign_toplevel_manager_v1 */
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_manager_v1_set_user_data(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1, void *user_data)
|
||||
{
|
||||
wl_proxy_set_user_data((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1, user_data);
|
||||
}
|
||||
|
||||
/** @ingroup iface_zwlr_foreign_toplevel_manager_v1 */
|
||||
static inline void *
|
||||
zwlr_foreign_toplevel_manager_v1_get_user_data(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1)
|
||||
{
|
||||
return wl_proxy_get_user_data((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
zwlr_foreign_toplevel_manager_v1_get_version(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1)
|
||||
{
|
||||
return wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1);
|
||||
}
|
||||
|
||||
/** @ingroup iface_zwlr_foreign_toplevel_manager_v1 */
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_manager_v1_destroy(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1)
|
||||
{
|
||||
wl_proxy_destroy((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_manager_v1
|
||||
*
|
||||
* Indicates the client no longer wishes to receive events for new toplevels.
|
||||
* However the compositor may emit further toplevel_created events, until
|
||||
* the finished event is emitted.
|
||||
*
|
||||
* The client must not send any more requests after this one.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_manager_v1_stop(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1,
|
||||
ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_STOP);
|
||||
}
|
||||
|
||||
#ifndef ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ENUM
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ENUM
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
* types of states on the toplevel
|
||||
*
|
||||
* The different states that a toplevel can have. These have the same meaning
|
||||
* as the states with the same names defined in xdg-toplevel
|
||||
*/
|
||||
enum zwlr_foreign_toplevel_handle_v1_state {
|
||||
/**
|
||||
* the toplevel is maximized
|
||||
*/
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED = 0,
|
||||
/**
|
||||
* the toplevel is minimized
|
||||
*/
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED = 1,
|
||||
/**
|
||||
* the toplevel is active
|
||||
*/
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED = 2,
|
||||
/**
|
||||
* the toplevel is fullscreen
|
||||
* @since 2
|
||||
*/
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN = 3,
|
||||
};
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN_SINCE_VERSION 2
|
||||
#endif /* ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ENUM */
|
||||
|
||||
#ifndef ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_ENUM
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_ENUM
|
||||
enum zwlr_foreign_toplevel_handle_v1_error {
|
||||
/**
|
||||
* the provided rectangle is invalid
|
||||
*/
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_INVALID_RECTANGLE = 0,
|
||||
};
|
||||
#endif /* ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_ENUM */
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
* @struct zwlr_foreign_toplevel_handle_v1_listener
|
||||
*/
|
||||
struct zwlr_foreign_toplevel_handle_v1_listener {
|
||||
/**
|
||||
* title change
|
||||
*
|
||||
* This event is emitted whenever the title of the toplevel
|
||||
* changes.
|
||||
*/
|
||||
void (*title)(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
|
||||
const char *title);
|
||||
/**
|
||||
* app-id change
|
||||
*
|
||||
* This event is emitted whenever the app-id of the toplevel
|
||||
* changes.
|
||||
*/
|
||||
void (*app_id)(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
|
||||
const char *app_id);
|
||||
/**
|
||||
* toplevel entered an output
|
||||
*
|
||||
* This event is emitted whenever the toplevel becomes visible on
|
||||
* the given output. A toplevel may be visible on multiple outputs.
|
||||
*/
|
||||
void (*output_enter)(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
|
||||
struct wl_output *output);
|
||||
/**
|
||||
* toplevel left an output
|
||||
*
|
||||
* This event is emitted whenever the toplevel stops being
|
||||
* visible on the given output. It is guaranteed that an
|
||||
* entered-output event with the same output has been emitted
|
||||
* before this event.
|
||||
*/
|
||||
void (*output_leave)(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
|
||||
struct wl_output *output);
|
||||
/**
|
||||
* the toplevel state changed
|
||||
*
|
||||
* This event is emitted immediately after the
|
||||
* zlw_foreign_toplevel_handle_v1 is created and each time the
|
||||
* toplevel state changes, either because of a compositor action or
|
||||
* because of a request in this protocol.
|
||||
*/
|
||||
void (*state)(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
|
||||
struct wl_array *state);
|
||||
/**
|
||||
* all information about the toplevel has been sent
|
||||
*
|
||||
* This event is sent after all changes in the toplevel state
|
||||
* have been sent.
|
||||
*
|
||||
* This allows changes to the zwlr_foreign_toplevel_handle_v1
|
||||
* properties to be seen as atomic, even if they happen via
|
||||
* multiple events.
|
||||
*/
|
||||
void (*done)(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1);
|
||||
/**
|
||||
* this toplevel has been destroyed
|
||||
*
|
||||
* This event means the toplevel has been destroyed. It is
|
||||
* guaranteed there won't be any more events for this
|
||||
* zwlr_foreign_toplevel_handle_v1. The toplevel itself becomes
|
||||
* inert so any requests will be ignored except the destroy
|
||||
* request.
|
||||
*/
|
||||
void (*closed)(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1);
|
||||
/**
|
||||
* parent change
|
||||
*
|
||||
* This event is emitted whenever the parent of the toplevel
|
||||
* changes.
|
||||
*
|
||||
* No event is emitted when the parent handle is destroyed by the
|
||||
* client.
|
||||
* @since 3
|
||||
*/
|
||||
void (*parent)(void *data,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
|
||||
struct zwlr_foreign_toplevel_handle_v1 *parent);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
static inline int
|
||||
zwlr_foreign_toplevel_handle_v1_add_listener(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1,
|
||||
const struct zwlr_foreign_toplevel_handle_v1_listener *listener, void *data)
|
||||
{
|
||||
return wl_proxy_add_listener((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
|
||||
(void (**)(void)) listener, data);
|
||||
}
|
||||
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MAXIMIZED 0
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MAXIMIZED 1
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MINIMIZED 2
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MINIMIZED 3
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ACTIVATE 4
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSE 5
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_RECTANGLE 6
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_DESTROY 7
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_FULLSCREEN 8
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_FULLSCREEN 9
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_TITLE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_APP_ID_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_OUTPUT_ENTER_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_OUTPUT_LEAVE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_DONE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSED_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_PARENT_SINCE_VERSION 3
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MAXIMIZED_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MAXIMIZED_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MINIMIZED_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MINIMIZED_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ACTIVATE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_RECTANGLE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_DESTROY_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_FULLSCREEN_SINCE_VERSION 2
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*/
|
||||
#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_FULLSCREEN_SINCE_VERSION 2
|
||||
|
||||
/** @ingroup iface_zwlr_foreign_toplevel_handle_v1 */
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_handle_v1_set_user_data(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, void *user_data)
|
||||
{
|
||||
wl_proxy_set_user_data((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, user_data);
|
||||
}
|
||||
|
||||
/** @ingroup iface_zwlr_foreign_toplevel_handle_v1 */
|
||||
static inline void *
|
||||
zwlr_foreign_toplevel_handle_v1_get_user_data(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
|
||||
{
|
||||
return wl_proxy_get_user_data((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
zwlr_foreign_toplevel_handle_v1_get_version(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
|
||||
{
|
||||
return wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*
|
||||
* Requests that the toplevel be maximized. If the maximized state actually
|
||||
* changes, this will be indicated by the state event.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_handle_v1_set_maximized(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MAXIMIZED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*
|
||||
* Requests that the toplevel be unmaximized. If the maximized state actually
|
||||
* changes, this will be indicated by the state event.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_handle_v1_unset_maximized(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MAXIMIZED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*
|
||||
* Requests that the toplevel be minimized. If the minimized state actually
|
||||
* changes, this will be indicated by the state event.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_handle_v1_set_minimized(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MINIMIZED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*
|
||||
* Requests that the toplevel be unminimized. If the minimized state actually
|
||||
* changes, this will be indicated by the state event.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_handle_v1_unset_minimized(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MINIMIZED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*
|
||||
* Request that this toplevel be activated on the given seat.
|
||||
* There is no guarantee the toplevel will be actually activated.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_handle_v1_activate(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, struct wl_seat *seat)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ACTIVATE, seat);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*
|
||||
* Send a request to the toplevel to close itself. The compositor would
|
||||
* typically use a shell-specific method to carry out this request, for
|
||||
* example by sending the xdg_toplevel.close event. However, this gives
|
||||
* no guarantees the toplevel will actually be destroyed. If and when
|
||||
* this happens, the zwlr_foreign_toplevel_handle_v1.closed event will
|
||||
* be emitted.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_handle_v1_close(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*
|
||||
* The rectangle of the surface specified in this request corresponds to
|
||||
* the place where the app using this protocol represents the given toplevel.
|
||||
* It can be used by the compositor as a hint for some operations, e.g
|
||||
* minimizing. The client is however not required to set this, in which
|
||||
* case the compositor is free to decide some default value.
|
||||
*
|
||||
* If the client specifies more than one rectangle, only the last one is
|
||||
* considered.
|
||||
*
|
||||
* The dimensions are given in surface-local coordinates.
|
||||
* Setting width=height=0 removes the already-set rectangle.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_handle_v1_set_rectangle(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, struct wl_surface *surface, int32_t x, int32_t y, int32_t width, int32_t height)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_RECTANGLE, surface, x, y, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*
|
||||
* Destroys the zwlr_foreign_toplevel_handle_v1 object.
|
||||
*
|
||||
* This request should be called either when the client does not want to
|
||||
* use the toplevel anymore or after the closed event to finalize the
|
||||
* destruction of the object.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_handle_v1_destroy(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_DESTROY);
|
||||
|
||||
wl_proxy_destroy((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*
|
||||
* Requests that the toplevel be fullscreened on the given output. If the
|
||||
* fullscreen state and/or the outputs the toplevel is visible on actually
|
||||
* change, this will be indicated by the state and output_enter/leave
|
||||
* events.
|
||||
*
|
||||
* The output parameter is only a hint to the compositor. Also, if output
|
||||
* is NULL, the compositor should decide which output the toplevel will be
|
||||
* fullscreened on, if at all.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_handle_v1_set_fullscreen(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, struct wl_output *output)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_FULLSCREEN, output);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_zwlr_foreign_toplevel_handle_v1
|
||||
*
|
||||
* Requests that the toplevel be unfullscreened. If the fullscreen state
|
||||
* actually changes, this will be indicated by the state event.
|
||||
*/
|
||||
static inline void
|
||||
zwlr_foreign_toplevel_handle_v1_unset_fullscreen(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1,
|
||||
ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_FULLSCREEN);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -0,0 +1,106 @@
|
||||
/* Generated by wayland-scanner 1.18.0 */
|
||||
|
||||
/*
|
||||
* Copyright © 2018 Ilia Bozhinov
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this
|
||||
* software and its documentation for any purpose is hereby granted
|
||||
* without fee, provided that the above copyright notice appear in
|
||||
* all copies and that both that copyright notice and this permission
|
||||
* notice appear in supporting documentation, and that the name of
|
||||
* the copyright holders not be used in advertising or publicity
|
||||
* pertaining to distribution of the software without specific,
|
||||
* written prior permission. The copyright holders make no
|
||||
* representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied
|
||||
* warranty.
|
||||
*
|
||||
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
* THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "wayland-util.h"
|
||||
|
||||
#ifndef __has_attribute
|
||||
# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
|
||||
#endif
|
||||
|
||||
#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
|
||||
#define WL_PRIVATE __attribute__ ((visibility("hidden")))
|
||||
#else
|
||||
#define WL_PRIVATE
|
||||
#endif
|
||||
|
||||
extern const struct wl_interface wl_output_interface;
|
||||
extern const struct wl_interface wl_seat_interface;
|
||||
extern const struct wl_interface wl_surface_interface;
|
||||
extern const struct wl_interface zwlr_foreign_toplevel_handle_v1_interface;
|
||||
|
||||
static const struct wl_interface *wlr_foreign_toplevel_management_unstable_v1_types[] = {
|
||||
NULL,
|
||||
&zwlr_foreign_toplevel_handle_v1_interface,
|
||||
&wl_seat_interface,
|
||||
&wl_surface_interface,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&wl_output_interface,
|
||||
&wl_output_interface,
|
||||
&wl_output_interface,
|
||||
&zwlr_foreign_toplevel_handle_v1_interface,
|
||||
};
|
||||
|
||||
static const struct wl_message zwlr_foreign_toplevel_manager_v1_requests[] = {
|
||||
{ "stop", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
};
|
||||
|
||||
static const struct wl_message zwlr_foreign_toplevel_manager_v1_events[] = {
|
||||
{ "toplevel", "n", wlr_foreign_toplevel_management_unstable_v1_types + 1 },
|
||||
{ "finished", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface zwlr_foreign_toplevel_manager_v1_interface = {
|
||||
"zwlr_foreign_toplevel_manager_v1", 3,
|
||||
1, zwlr_foreign_toplevel_manager_v1_requests,
|
||||
2, zwlr_foreign_toplevel_manager_v1_events,
|
||||
};
|
||||
|
||||
static const struct wl_message zwlr_foreign_toplevel_handle_v1_requests[] = {
|
||||
{ "set_maximized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
{ "unset_maximized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
{ "set_minimized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
{ "unset_minimized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
{ "activate", "o", wlr_foreign_toplevel_management_unstable_v1_types + 2 },
|
||||
{ "close", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
{ "set_rectangle", "oiiii", wlr_foreign_toplevel_management_unstable_v1_types + 3 },
|
||||
{ "destroy", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
{ "set_fullscreen", "2?o", wlr_foreign_toplevel_management_unstable_v1_types + 8 },
|
||||
{ "unset_fullscreen", "2", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
};
|
||||
|
||||
static const struct wl_message zwlr_foreign_toplevel_handle_v1_events[] = {
|
||||
{ "title", "s", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
{ "app_id", "s", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
{ "output_enter", "o", wlr_foreign_toplevel_management_unstable_v1_types + 9 },
|
||||
{ "output_leave", "o", wlr_foreign_toplevel_management_unstable_v1_types + 10 },
|
||||
{ "state", "a", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
{ "done", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
{ "closed", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 },
|
||||
{ "parent", "3?o", wlr_foreign_toplevel_management_unstable_v1_types + 11 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface zwlr_foreign_toplevel_handle_v1_interface = {
|
||||
"zwlr_foreign_toplevel_handle_v1", 3,
|
||||
10, zwlr_foreign_toplevel_handle_v1_requests,
|
||||
8, zwlr_foreign_toplevel_handle_v1_events,
|
||||
};
|
||||
|
@ -0,0 +1,270 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="wlr_foreign_toplevel_management_unstable_v1">
|
||||
<copyright>
|
||||
Copyright © 2018 Ilia Bozhinov
|
||||
|
||||
Permission to use, copy, modify, distribute, and sell this
|
||||
software and its documentation for any purpose is hereby granted
|
||||
without fee, provided that the above copyright notice appear in
|
||||
all copies and that both that copyright notice and this permission
|
||||
notice appear in supporting documentation, and that the name of
|
||||
the copyright holders not be used in advertising or publicity
|
||||
pertaining to distribution of the software without specific,
|
||||
written prior permission. The copyright holders make no
|
||||
representations about the suitability of this software for any
|
||||
purpose. It is provided "as is" without express or implied
|
||||
warranty.
|
||||
|
||||
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
||||
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||
THIS SOFTWARE.
|
||||
</copyright>
|
||||
|
||||
<interface name="zwlr_foreign_toplevel_manager_v1" version="3">
|
||||
<description summary="list and control opened apps">
|
||||
The purpose of this protocol is to enable the creation of taskbars
|
||||
and docks by providing them with a list of opened applications and
|
||||
letting them request certain actions on them, like maximizing, etc.
|
||||
|
||||
After a client binds the zwlr_foreign_toplevel_manager_v1, each opened
|
||||
toplevel window will be sent via the toplevel event
|
||||
</description>
|
||||
|
||||
<event name="toplevel">
|
||||
<description summary="a toplevel has been created">
|
||||
This event is emitted whenever a new toplevel window is created. It
|
||||
is emitted for all toplevels, regardless of the app that has created
|
||||
them.
|
||||
|
||||
All initial details of the toplevel(title, app_id, states, etc.) will
|
||||
be sent immediately after this event via the corresponding events in
|
||||
zwlr_foreign_toplevel_handle_v1.
|
||||
</description>
|
||||
<arg name="toplevel" type="new_id" interface="zwlr_foreign_toplevel_handle_v1"/>
|
||||
</event>
|
||||
|
||||
<request name="stop">
|
||||
<description summary="stop sending events">
|
||||
Indicates the client no longer wishes to receive events for new toplevels.
|
||||
However the compositor may emit further toplevel_created events, until
|
||||
the finished event is emitted.
|
||||
|
||||
The client must not send any more requests after this one.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<event name="finished">
|
||||
<description summary="the compositor has finished with the toplevel manager">
|
||||
This event indicates that the compositor is done sending events to the
|
||||
zwlr_foreign_toplevel_manager_v1. The server will destroy the object
|
||||
immediately after sending this request, so it will become invalid and
|
||||
the client should free any resources associated with it.
|
||||
</description>
|
||||
</event>
|
||||
</interface>
|
||||
|
||||
<interface name="zwlr_foreign_toplevel_handle_v1" version="3">
|
||||
<description summary="an opened toplevel">
|
||||
A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel
|
||||
window. Each app may have multiple opened toplevels.
|
||||
|
||||
Each toplevel has a list of outputs it is visible on, conveyed to the
|
||||
client with the output_enter and output_leave events.
|
||||
</description>
|
||||
|
||||
<event name="title">
|
||||
<description summary="title change">
|
||||
This event is emitted whenever the title of the toplevel changes.
|
||||
</description>
|
||||
<arg name="title" type="string"/>
|
||||
</event>
|
||||
|
||||
<event name="app_id">
|
||||
<description summary="app-id change">
|
||||
This event is emitted whenever the app-id of the toplevel changes.
|
||||
</description>
|
||||
<arg name="app_id" type="string"/>
|
||||
</event>
|
||||
|
||||
<event name="output_enter">
|
||||
<description summary="toplevel entered an output">
|
||||
This event is emitted whenever the toplevel becomes visible on
|
||||
the given output. A toplevel may be visible on multiple outputs.
|
||||
</description>
|
||||
<arg name="output" type="object" interface="wl_output"/>
|
||||
</event>
|
||||
|
||||
<event name="output_leave">
|
||||
<description summary="toplevel left an output">
|
||||
This event is emitted whenever the toplevel stops being visible on
|
||||
the given output. It is guaranteed that an entered-output event
|
||||
with the same output has been emitted before this event.
|
||||
</description>
|
||||
<arg name="output" type="object" interface="wl_output"/>
|
||||
</event>
|
||||
|
||||
<request name="set_maximized">
|
||||
<description summary="requests that the toplevel be maximized">
|
||||
Requests that the toplevel be maximized. If the maximized state actually
|
||||
changes, this will be indicated by the state event.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="unset_maximized">
|
||||
<description summary="requests that the toplevel be unmaximized">
|
||||
Requests that the toplevel be unmaximized. If the maximized state actually
|
||||
changes, this will be indicated by the state event.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="set_minimized">
|
||||
<description summary="requests that the toplevel be minimized">
|
||||
Requests that the toplevel be minimized. If the minimized state actually
|
||||
changes, this will be indicated by the state event.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="unset_minimized">
|
||||
<description summary="requests that the toplevel be unminimized">
|
||||
Requests that the toplevel be unminimized. If the minimized state actually
|
||||
changes, this will be indicated by the state event.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="activate">
|
||||
<description summary="activate the toplevel">
|
||||
Request that this toplevel be activated on the given seat.
|
||||
There is no guarantee the toplevel will be actually activated.
|
||||
</description>
|
||||
<arg name="seat" type="object" interface="wl_seat"/>
|
||||
</request>
|
||||
|
||||
<enum name="state">
|
||||
<description summary="types of states on the toplevel">
|
||||
The different states that a toplevel can have. These have the same meaning
|
||||
as the states with the same names defined in xdg-toplevel
|
||||
</description>
|
||||
|
||||
<entry name="maximized" value="0" summary="the toplevel is maximized"/>
|
||||
<entry name="minimized" value="1" summary="the toplevel is minimized"/>
|
||||
<entry name="activated" value="2" summary="the toplevel is active"/>
|
||||
<entry name="fullscreen" value="3" summary="the toplevel is fullscreen" since="2"/>
|
||||
</enum>
|
||||
|
||||
<event name="state">
|
||||
<description summary="the toplevel state changed">
|
||||
This event is emitted immediately after the zlw_foreign_toplevel_handle_v1
|
||||
is created and each time the toplevel state changes, either because of a
|
||||
compositor action or because of a request in this protocol.
|
||||
</description>
|
||||
|
||||
<arg name="state" type="array"/>
|
||||
</event>
|
||||
|
||||
<event name="done">
|
||||
<description summary="all information about the toplevel has been sent">
|
||||
This event is sent after all changes in the toplevel state have been
|
||||
sent.
|
||||
|
||||
This allows changes to the zwlr_foreign_toplevel_handle_v1 properties
|
||||
to be seen as atomic, even if they happen via multiple events.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<request name="close">
|
||||
<description summary="request that the toplevel be closed">
|
||||
Send a request to the toplevel to close itself. The compositor would
|
||||
typically use a shell-specific method to carry out this request, for
|
||||
example by sending the xdg_toplevel.close event. However, this gives
|
||||
no guarantees the toplevel will actually be destroyed. If and when
|
||||
this happens, the zwlr_foreign_toplevel_handle_v1.closed event will
|
||||
be emitted.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<request name="set_rectangle">
|
||||
<description summary="the rectangle which represents the toplevel">
|
||||
The rectangle of the surface specified in this request corresponds to
|
||||
the place where the app using this protocol represents the given toplevel.
|
||||
It can be used by the compositor as a hint for some operations, e.g
|
||||
minimizing. The client is however not required to set this, in which
|
||||
case the compositor is free to decide some default value.
|
||||
|
||||
If the client specifies more than one rectangle, only the last one is
|
||||
considered.
|
||||
|
||||
The dimensions are given in surface-local coordinates.
|
||||
Setting width=height=0 removes the already-set rectangle.
|
||||
</description>
|
||||
|
||||
<arg name="surface" type="object" interface="wl_surface"/>
|
||||
<arg name="x" type="int"/>
|
||||
<arg name="y" type="int"/>
|
||||
<arg name="width" type="int"/>
|
||||
<arg name="height" type="int"/>
|
||||
</request>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="invalid_rectangle" value="0"
|
||||
summary="the provided rectangle is invalid"/>
|
||||
</enum>
|
||||
|
||||
<event name="closed">
|
||||
<description summary="this toplevel has been destroyed">
|
||||
This event means the toplevel has been destroyed. It is guaranteed there
|
||||
won't be any more events for this zwlr_foreign_toplevel_handle_v1. The
|
||||
toplevel itself becomes inert so any requests will be ignored except the
|
||||
destroy request.
|
||||
</description>
|
||||
</event>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
<description summary="destroy the zwlr_foreign_toplevel_handle_v1 object">
|
||||
Destroys the zwlr_foreign_toplevel_handle_v1 object.
|
||||
|
||||
This request should be called either when the client does not want to
|
||||
use the toplevel anymore or after the closed event to finalize the
|
||||
destruction of the object.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<!-- Version 2 additions -->
|
||||
|
||||
<request name="set_fullscreen" since="2">
|
||||
<description summary="request that the toplevel be fullscreened">
|
||||
Requests that the toplevel be fullscreened on the given output. If the
|
||||
fullscreen state and/or the outputs the toplevel is visible on actually
|
||||
change, this will be indicated by the state and output_enter/leave
|
||||
events.
|
||||
|
||||
The output parameter is only a hint to the compositor. Also, if output
|
||||
is NULL, the compositor should decide which output the toplevel will be
|
||||
fullscreened on, if at all.
|
||||
</description>
|
||||
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
|
||||
</request>
|
||||
|
||||
<request name="unset_fullscreen" since="2">
|
||||
<description summary="request that the toplevel be unfullscreened">
|
||||
Requests that the toplevel be unfullscreened. If the fullscreen state
|
||||
actually changes, this will be indicated by the state event.
|
||||
</description>
|
||||
</request>
|
||||
|
||||
<!-- Version 3 additions -->
|
||||
|
||||
<event name="parent" since="3">
|
||||
<description summary="parent change">
|
||||
This event is emitted whenever the parent of the toplevel changes.
|
||||
|
||||
No event is emitted when the parent handle is destroyed by the client.
|
||||
</description>
|
||||
<arg name="parent" type="object" interface="zwlr_foreign_toplevel_handle_v1" allow-null="true"/>
|
||||
</event>
|
||||
</interface>
|
||||
</protocol>
|
423
src/wayland/wl.c
@ -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, ®istry_listener, NULL);
|
||||
wl_registry_add_listener(ctx.registry, ®istry_listener, &count);
|
||||
wl_display_roundtrip(ctx.display);
|
||||
|
||||
count = 1;
|
||||
// we need a second pass to let for foreign_toplevel (look there for more info)
|
||||
ctx.registry = wl_display_get_registry(ctx.display);
|
||||
wl_registry_add_listener(ctx.registry, ®istry_listener, &count);
|
||||
wl_display_roundtrip(ctx.display);
|
||||
|
||||
if (ctx.compositor == NULL) {
|
||||
LOG_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: */
|
||||
|
@ -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: */
|
||||
|
18
src/x11/x.c
@ -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: */
|
||||
|
@ -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: */
|
||||
|
@ -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);
|
||||
|
20
test/icon.c
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|