diff --git a/.gitea/issue_template/bug_report.yml b/.gitea/issue_template/bug_report.yml deleted file mode 100644 index 77ce108..0000000 --- a/.gitea/issue_template/bug_report.yml +++ /dev/null @@ -1,62 +0,0 @@ -name: Bug Report -about: Something in dwl isn't working correctly -title: -labels: - - 'Kind/Bug' -body: - - type: markdown - attributes: - value: | - - Only report bugs that can be reproduced on the main (or wlroots-next) branch without patches. - - Proprietary graphics drivers, including nvidia, are not supported. Please use the open source equivalents, such as nouveau, if you would like to use dwl. - - Report patch issues to their respective authors. - - - type: input - id: dwl_version - attributes: - label: 'dwl version:' - placeholder: '`dwl -v`' - validations: - required: true - - - type: input - id: wlroots_version - attributes: - label: 'wlroots version:' - validations: - required: true - - - type: input - id: distro - attributes: - label: What distro (and version) are you using? - validations: - required: false - - - type: textarea - attributes: - label: Description - value: | - The steps you took to reproduce the problem. - validations: - required: false - - - type: textarea - id: debug_log - attributes: - label: Debug Log - value: | - Run `dwl -d 2> ~/dwl.log` from a TTY and attach the **full** (do not truncate it) file here, or upload it to a pastebin. - Please try to keep the reproduction as brief as possible and exit dwl. - validations: - required: false - - - type: textarea - id: backtrace - attributes: - label: Stack Trace - value: | - - Only required if dwl crashes. - - If the lines mentioning dwl or wlroots have `??`. Please compile both dwl and wlroots from source (enabling debug symbols) and try to reproduce. - validations: - required: false diff --git a/.gitea/issue_template/enhancement-idea.yml b/.gitea/issue_template/enhancement-idea.yml deleted file mode 100644 index be1bbf2..0000000 --- a/.gitea/issue_template/enhancement-idea.yml +++ /dev/null @@ -1,9 +0,0 @@ -name: Enhancement idea -about: Suggest a feature or improvement -title: -labels: - - 'Kind/Feature' -body: - - type: textarea - attributes: - label: Description diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..6b60803 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,22 @@ +--- +name: Bug report +about: Something in dwl isn't working correctly +title: '' +labels: 'A: bug' +assignees: '' + +--- + +## Info +dwl version: +wlroots version: +## Description + diff --git a/.github/ISSUE_TEMPLATE/enhancement-idea.md b/.github/ISSUE_TEMPLATE/enhancement-idea.md new file mode 100644 index 0000000..0ac096d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/enhancement-idea.md @@ -0,0 +1,10 @@ +--- +name: Enhancement idea +about: Suggest a feature or improvement +title: '' +labels: 'A: enhancement' +assignees: '' + +--- + + diff --git a/.mailmap b/.mailmap deleted file mode 100644 index 1778cb9..0000000 --- a/.mailmap +++ /dev/null @@ -1,3 +0,0 @@ -Lennart Jablonka -Leonardo Hernández Hernández -Leonardo Hernández Hernández diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 07c9ee4..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,217 +0,0 @@ -# Changelog - -* [Unreleased](#unreleased) -* [0.7](#0.7) -* [0.6](#0.6) -* [0.5](#0.5) - - -## Unreleased -### Added - -* Support for the linux-drm-syncobj-v1 protocol ([wlroots!4715][wlroots!4715], [#685][685]) -* Allow the use of non-system wlroots library ([#646][646]) - -[wlroots!4715]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4715 -[685]: https://codeberg.org/dwl/dwl/pulls/685 -[646]: https://codeberg.org/dwl/dwl/pulls/646 - - -### Changed -### Deprecated -### Removed -### Fixed - -* Crash when a client is created while all outputs are disabled. - -### Security -### Contributors - - -## 0.7 - -This version is just 0.6 with wlroots 0.18 compatibility. - -### Added - -* Add support for the alpha-modifier-v1 protocol ([wlroots!4616][wlroots!4616]). -* dwl now will survive GPU resets ([#601][601]). - -[wlroots!4616]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4616 -[601]: https://codeberg.org/dwl/dwl/issues/601 - - -### Contributors - -Guido Cella - - -## 0.6 - -### Added - -* Add `rootcolor` to change the default background color ([#544][544]). -* Implement the wlr-virtual-pointer-unstable-v1 protocol ([#574][574]). -* Implement the pointer-constraints and relative-pointer protocols ([#317][317]) -* Implement the wlr-output-power-management protocol ([#599][599]) - -[544]: https://codeberg.org/dwl/dwl/pulls/544 -[574]: https://codeberg.org/dwl/dwl/pulls/574 -[317]: https://codeberg.org/dwl/dwl/issues/317 -[599]: https://codeberg.org/dwl/dwl/issues/559 - - -### Changed - -* Keyboards are now managed through keyboard groups ([#549][549]). -* Only the first matched keybinding is executed. -* Allow toggling the layout before selecting a different one ([#570][570]). -* Fullscreen clients are now rendered above wlr_layer_surfaces in the top layer - ([#609][609]). -* The default menu was changed from `bemenu-run` to `wmenu-run` ([#553][553]). -* The option `sloppyfocus` now replicates the dwm behavior ([#599][599]). -* Allow configure position of monitors with negative values. (-1, -1) is - used to auto-configure them ([#635][635]). -* dwl now kills the entire process group of `startup_cmd` -* The O_NONBLOCK flag is set for stdout. - -[549]: https://codeberg.org/dwl/dwl/pulls/549 -[570]: https://codeberg.org/dwl/dwl/pulls/570 -[609]: https://codeberg.org/dwl/dwl/pulls/609 -[553]: https://codeberg.org/dwl/dwl/issues/553 -[599]: https://codeberg.org/dwl/dwl/pulls/599 -[635]: https://codeberg.org/dwl/dwl/pulls/635 - - -### Removed - -* The SLOC limit is now removed ([#497][497]). - -[497]: https://codeberg.org/dwl/dwl/pulls/497 - - -### Fixed - -* Clients not having the correct border color when mapping. -* Compliance with the xdg-decoration-unstable-v1 ([#546][546]). -* dwl no longer sends negative values in xdg_toplevel.configure events. -* Crashes with disabled monitors ([#472][472]). - -[546]: https://codeberg.org/dwl/dwl/pulls/546 -[472]: https://codeberg.org/dwl/dwl/issues/472 - - -### Contributors - -Ben Jargowsky -Benjamin Chausse -David Donahue -Devin J. Pohly -Dima Krasner -Emil Miler -Forrest Bushstone -Guido Cella -Peter Hofmann -Rutherther -Squibid -choc -fictitiousexistence -korei999 -sewn -thanatos - - -## 0.5 - -### Added - -* Allow configure x and y position of outputs ([#301][301]) -* Implement repeatable keybindings ([#368][368]) -* Print app id in printstatus() output ([#381][381]) -* Display client count in monocle symbol ([#387][387]) -* Export XCURSOR_SIZE to fix apps using an older version of Qt ([#425][425]) -* Support for wp-fractional-scale-v1 (through wlr_scene: [wlroots!3511][wlroots!3511]) -* dwl now sends `wl_surface.preferred_buffer_scale` (through wlr_scene: [wlroots!4269][wlroots!4269]) -* Add support for xdg-shell v6 ([#465][465]) -* Add support for wp-cursor-shape-v1 ([#444][444]) -* Add desktop file ([#484][484]) -* Add macro to easily configure colors ([#466][466]) -* Color of urgent clients are now red ([#494][494]) -* New flag `-d` and option `log_level` to change the wlroots debug level -* Add CHANGELOG.md ([#501][501]) - -[301]: https://github.com/djpohly/dwl/pull/301 -[368]: https://github.com/djpohly/dwl/pull/368 -[381]: https://github.com/djpohly/dwl/pull/381 -[387]: https://github.com/djpohly/dwl/issues/387 -[425]: https://github.com/djpohly/dwl/pull/425 -[wlroots!4269]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4269 -[wlroots!3511]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/3511 -[465]: https://github.com/djpohly/dwl/pull/465 -[444]: https://github.com/djpohly/dwl/pull/444 -[484]: https://github.com/djpohly/dwl/pull/484 -[466]: https://github.com/djpohly/dwl/issues/466 -[494]: https://github.com/djpohly/dwl/pull/494 -[501]: https://github.com/djpohly/dwl/pull/501 - - -### Changed - -* Replace `tags` with `TAGCOUNT` in config.def.h ([#403][403]) -* Pop ups are now destroyed when focusing another client ([#408][408]) -* dwl does not longer respect size hints, instead clip windows if they are - larger than they should be ([#455][455]) -* The version of wlr-layer-shell-unstable-v1 was lowered to 3 (from 4) -* Use the same border color as dwm ([#494][494]) - -[403]: https://github.com/djpohly/dwl/pull/403 -[408]: https://github.com/djpohly/dwl/pull/409 -[455]: https://github.com/djpohly/dwl/pull/455 -[494]: https://github.com/djpohly/dwl/pull/494 - - -### Removed - -* Remove unused `rootcolor` option ([#401][401]) -* Remove support for wlr-input-inhibitor-unstable-v1 ([#430][430]) -* Remove support for KDE idle protocol ([#431][431]) - -[401]: https://github.com/djpohly/dwl/pull/401 -[430]: https://github.com/djpohly/dwl/pull/430 -[431]: https://github.com/djpohly/dwl/pull/431 - - -### Fixed - -* Fix crash when creating a layer surface with all outputs disabled - ([#421][421]) -* Fix other clients being shown as focused if the focused client have pop ups - open ([#408][408]) -* Resize fullscreen clients when updating monitor mode -* dwl no longer crash at exit like sometimes did -* Fullscreen background appearing above clients ([#487][487]) -* Fix a segfault when user provides invalid xkb_rules ([#518][518]) - -[421]: https://github.com/djpohly/dwl/pull/421 -[408]: https://github.com/djpohly/dwl/issues/408 -[487]: https://github.com/djpohly/dwl/issues/487 -[518]: https://github.com/djpohly/dwl/pull/518 - - -### Contributors - -* A Frederick Christensen -* Angelo Antony -* Ben Collerson -* Devin J. Pohly -* Forrest Bushstone -* gan-of-culture -* godalming123 -* Job79 -* link2xt -* Micah Gorrell -* Nikita Ivanov -* Palanix -* pino-desktop -* Weiseguy -* Yves Zoundi diff --git a/Makefile b/Makefile index 578194f..ccca079 100644 --- a/Makefile +++ b/Makefile @@ -4,24 +4,19 @@ include config.mk # flags for compiling -DWLCPPFLAGS = -I. -DWLR_USE_UNSTABLE -D_POSIX_C_SOURCE=200809L \ - -DVERSION=\"$(VERSION)\" $(XWAYLAND) -DWLDEVCFLAGS = -g -Wpedantic -Wall -Wextra -Wdeclaration-after-statement \ - -Wno-unused-parameter -Wshadow -Wunused-macros -Werror=strict-prototypes \ - -Werror=implicit -Werror=return-type -Werror=incompatible-pointer-types \ - -Wfloat-conversion +DWLCPPFLAGS = -I. -DWLR_USE_UNSTABLE -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XWAYLAND) +DWLDEVCFLAGS = -pedantic -Wall -Wextra -Wdeclaration-after-statement -Wno-unused-parameter -Wno-sign-compare -Wshadow -Wunused-macros\ + -Werror=strict-prototypes -Werror=implicit -Werror=return-type -Werror=incompatible-pointer-types # CFLAGS / LDFLAGS -PKGS = wayland-server xkbcommon libinput $(XLIBS) -DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(WLR_INCS) $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS) -LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(WLR_LIBS) -lm $(LIBS) +PKGS = wlroots wayland-server xkbcommon libinput $(XLIBS) +DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS) +LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(LIBS) all: dwl dwl: dwl.o util.o - $(CC) dwl.o util.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@ -dwl.o: dwl.c client.h config.h config.mk cursor-shape-v1-protocol.h \ - pointer-constraints-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.h \ - wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h + $(CC) dwl.o util.o $(LDLIBS) $(LDFLAGS) $(DWLCFLAGS) -o $@ +dwl.o: dwl.c config.mk config.h client.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h util.o: util.c util.h # wayland-scanner is a tool which generates C headers and rigging for Wayland @@ -30,21 +25,12 @@ util.o: util.c util.h WAYLAND_SCANNER = `$(PKG_CONFIG) --variable=wayland_scanner wayland-scanner` WAYLAND_PROTOCOLS = `$(PKG_CONFIG) --variable=pkgdatadir wayland-protocols` -cursor-shape-v1-protocol.h: - $(WAYLAND_SCANNER) enum-header \ - $(WAYLAND_PROTOCOLS)/staging/cursor-shape/cursor-shape-v1.xml $@ -pointer-constraints-unstable-v1-protocol.h: - $(WAYLAND_SCANNER) enum-header \ - $(WAYLAND_PROTOCOLS)/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml $@ -wlr-layer-shell-unstable-v1-protocol.h: - $(WAYLAND_SCANNER) enum-header \ - protocols/wlr-layer-shell-unstable-v1.xml $@ -wlr-output-power-management-unstable-v1-protocol.h: - $(WAYLAND_SCANNER) server-header \ - protocols/wlr-output-power-management-unstable-v1.xml $@ xdg-shell-protocol.h: $(WAYLAND_SCANNER) server-header \ $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ +wlr-layer-shell-unstable-v1-protocol.h: + $(WAYLAND_SCANNER) server-header \ + protocols/wlr-layer-shell-unstable-v1.xml $@ config.h: cp config.def.h $@ @@ -53,27 +39,22 @@ clean: dist: clean mkdir -p dwl-$(VERSION) - cp -R LICENSE* Makefile CHANGELOG.md README.md client.h config.def.h \ - config.mk protocols dwl.1 dwl.c util.c util.h dwl.desktop \ + cp -R LICENSE* Makefile README.md client.h config.def.h\ + config.mk protocols dwl.1 dwl.c util.c util.h\ dwl-$(VERSION) tar -caf dwl-$(VERSION).tar.gz dwl-$(VERSION) rm -rf dwl-$(VERSION) install: dwl mkdir -p $(DESTDIR)$(PREFIX)/bin - rm -f $(DESTDIR)$(PREFIX)/bin/dwl cp -f dwl $(DESTDIR)$(PREFIX)/bin chmod 755 $(DESTDIR)$(PREFIX)/bin/dwl mkdir -p $(DESTDIR)$(MANDIR)/man1 cp -f dwl.1 $(DESTDIR)$(MANDIR)/man1 chmod 644 $(DESTDIR)$(MANDIR)/man1/dwl.1 - mkdir -p $(DESTDIR)$(DATADIR)/wayland-sessions - cp -f dwl.desktop $(DESTDIR)$(DATADIR)/wayland-sessions/dwl.desktop - chmod 644 $(DESTDIR)$(DATADIR)/wayland-sessions/dwl.desktop uninstall: - rm -f $(DESTDIR)$(PREFIX)/bin/dwl $(DESTDIR)$(MANDIR)/man1/dwl.1 \ - $(DESTDIR)$(DATADIR)/wayland-sessions/dwl.desktop + rm -f $(DESTDIR)$(PREFIX)/bin/dwl $(DESTDIR)$(MANDIR)/man1/dwl.1 .SUFFIXES: .c .o .c.o: - $(CC) $(CPPFLAGS) $(DWLCFLAGS) -o $@ -c $< + $(CC) $(CPPFLAGS) $(DWLCFLAGS) -c $< diff --git a/README.md b/README.md index 390788d..05f149c 100644 --- a/README.md +++ b/README.md @@ -1,82 +1,87 @@ # dwl - dwm for Wayland -Join us on our IRC channel: [#dwl on Libera Chat] -Or on the community-maintained [Discord server]. +Join us on our [Discord server] or at [#dwl] on irc.libera.chat. dwl is a compact, hackable compositor for [Wayland] based on [wlroots]. It is -intended to fill the same space in the Wayland world that [dwm] does in X11, -primarily in terms of functionality, and secondarily in terms of -philosophy. Like [dwm], dwl is: +intended to fill the same space in the Wayland world that dwm does in X11, +primarily in terms of philosophy, and secondarily in terms of functionality. +Like dwm, dwl is: - Easy to understand, hack on, and extend with patches - One C source file (or a very small number) configurable via `config.h` +- Limited to 2200 SLOC to promote hackability - Tied to as few external dependencies as possible -## Getting Started: +dwl is not meant to provide every feature under the sun. Instead, like dwm, it +sticks to features which are necessary, simple, and straightforward to implement +given the base on which it is built. Implemented default features are: -### Latest semi-stable [release] -This is probably where you want to start. This builds against the [wlroots] -versions currently shipping in major distributions. If your -distribution's `wlroots` version is older, use an earlier dwl [release]. -The `wlroots` version against which a given `dwl` release builds is specified -with each release on the [release] page +- Any features provided by dwm/Xlib: simple window borders, tags, keybindings, + client rules, mouse move/resize. Providing a built-in status bar is an + exception to this goal, to avoid dependencies on font rendering and/or + drawing libraries when an external bar could work well. +- Configurable multi-monitor layout support, including position and rotation +- Configurable HiDPI/multi-DPI support +- Idle-inhibit protocol which lets applications such as mpv disable idle + monitoring +- Provide information to external status bars via stdout/stdin +- Urgency hints via xdg-activate protocol +- Support screen lockers via input-inhibitor protocol +- Various Wayland protocols +- XWayland support as provided by wlroots (can be enabled in `config.mk`) +- Zero flickering - Wayland users naturally expect that "every frame is perfect" +- Layer shell popups (used by Waybar) +- Damage tracking provided by scenegraph API -### Development branch [main] -Active development progresses on the `main` branch. The `main` branch is built -against a late (and often changing) git commit of wlroots. While the adventurous -are welcome to use `main`, it is a rocky road. Using `main` requires that the -user be willing to chase git commits of wlroots. Testing development pull -requests may involve merging unmerged pull requests in [wlroots]' git repository -and/or git commits of wayland. - -### Building dwl -dwl has the following dependencies: -- libinput -- wayland -- wlroots (compiled with the libinput backend) -- xkbcommon -- wayland-protocols (compile-time only) -- pkg-config (compile-time only) +Features under consideration (possibly as patches) are: -dwl has the following additional dependencies if XWayland support is enabled: -- libxcb -- libxcb-wm -- wlroots (compiled with X11 support) -- Xwayland (runtime only) +- Protocols made trivial by wlroots +- Implement the text-input and input-method protocols to support IME once ibus + implements input-method v2 (see https://github.com/ibus/ibus/pull/2256 and + https://github.com/djpohly/dwl/pull/235) -Install these (and their `-devel` versions if your distro has separate -development packages) and run `make`. If you wish to build against a released -version of wlroots (*you probably do*), use a [release] or a [0.x branch]. If -you want to use the unstable development `main` branch, you need to use the git -version of [wlroots]. +Feature *non-goals* for the main codebase include: -To enable XWayland, you should uncomment its flags in `config.mk`. +- Client-side decoration (any more than is necessary to tell the clients not to) +- Client-initiated window management, such as move, resize, and close, which can + be done through the compositor +- Animations and visual effects + +## Building dwl + +dwl has only two dependencies: `wlroots` and `wayland-protocols`. + +Simply install these (and their `-devel` versions if your distro has separate +development packages) and run `make`. If you wish to build against a Git +version of wlroots, check out the [wlroots-next branch]. + +To enable XWayland, you should also install xorg-xwayland and uncomment its flag +in `config.mk`. ## Configuration All configuration is done by editing `config.h` and recompiling, in the same -manner as [dwm]. There is no way to separately restart the window manager in +manner as dwm. There is no way to separately restart the window manager in Wayland without restarting the entire display server, so any changes will take effect the next time dwl is executed. -As in the [dwm] community, we encourage users to share patches they have -created. Check out the [dwl-patches] repository! +As in the dwm community, we encourage users to share patches they have created. +Check out the [patches page on our wiki]! ## Running dwl dwl can be run on any of the backends supported by wlroots. This means you can -run it as a separate window inside either an X11 or Wayland session, as well as -directly from a VT console. Depending on your distro's setup, you may need to -add your user to the `video` and `input` groups before you can run dwl on a -VT. If you are using `elogind` or `systemd-logind` you need to install polkit; -otherwise you need to add yourself in the `seat` group and enable/start the -seatd daemon. +run it as a separate window inside either an X11 or Wayland session, as well +as directly from a VT console. Depending on your distro's setup, you may need +to add your user to the `video` and `input` groups before you can run dwl on +a VT. If you are using `elogind` or `systemd-logind` you need to install +polkit; otherwise you need to add yourself in the `seat` group and +enable/start the seatd daemon. When dwl is run with no arguments, it will launch the server and begin handling any shortcuts configured in `config.h`. There is no status bar or other -decoration initially; these are instead clients that can be run within the -Wayland session. Do note that the default background color is grey. This can be -modified in `config.h`. +decoration initially; these are instead clients that can be run within +the Wayland session. If you would like to run a script or command automatically at startup, you can specify the command using the `-s` option. This command will be executed as a @@ -84,8 +89,7 @@ shell command using `/bin/sh -c`. It serves a similar function to `.xinitrc`, but differs in that the display server will not shut down when this process terminates. Instead, dwl will send this process a SIGTERM at shutdown and wait for it to terminate (if it hasn't already). This makes it ideal for execing into -a user service manager like [s6], [anopa], [runit], [dinit], or [`systemd ---user`]. +a user service manager like [s6], [anopa], [runit], or [`systemd --user`]. Note: The `-s` command is run as a *child process* of dwl, which means that it does not have the ability to affect the environment of dwl or of any processes @@ -101,13 +105,13 @@ automatically, you will need to configure it prior to launching `dwl`, e.g.: ### Status information -Information about selected layouts, current window title, app-id, and +Information about selected layouts, current window title, and selected/occupied/urgent tags is written to the stdin of the `-s` command (see -the `STATUS INFORMATION` section in `_dwl_(1)`). This information can be used to -populate an external status bar with a script that parses the -information. Failing to read this information will cause dwl to block, so if you -do want to run a startup command that does not consume the status information, -you can close standard input with the `<&-` shell redirection, for example: +the `printstatus()` function for details). This information can be used to +populate an external status bar with a script that parses the information. +Failing to read this information will cause dwl to block, so if you do want to +run a startup command that does not consume the status information, you can +close standard input with the `<&-` shell redirection, for example: dwl -s 'foot --server <&-' @@ -118,62 +122,10 @@ script with the line To get a list of status bars that work with dwl consult our [wiki]. -### (Known) Java nonreparenting WM issue -Certain IDEs don't display correctly unless an environmental variable for Java AWT -indicates that the WM is nonreparenting. - -For some Java AWT-based IDEs, such as Xilinx Vivado and Microchip MPLAB X, the -following environment variable needs to be set before running the IDE or dwl: - - export _JAVA_AWT_WM_NONREPARENTING=1 - ## Replacements for X applications You can find a [list of useful resources on our wiki]. -## Background - -dwl is not meant to provide every feature under the sun. Instead, like [dwm], it -sticks to features which are necessary, simple, and straightforward to implement -given the base on which it is built. Implemented default features are: - -- Any features provided by [dwm]/Xlib: simple window borders, tags, keybindings, - client rules, mouse move/resize. Providing a built-in status bar is an - exception to this goal, to avoid dependencies on font rendering and/or drawing - libraries when an external bar could work well. -- Configurable multi-monitor layout support, including position and rotation -- Configurable HiDPI/multi-DPI support -- Idle-inhibit protocol which lets applications such as mpv disable idle - monitoring -- Provide information to external status bars via stdout/stdin -- Urgency hints via xdg-activate protocol -- Support screen lockers via ext-session-lock-v1 protocol -- Various Wayland protocols -- XWayland support as provided by wlroots (can be enabled in `config.mk`) -- Zero flickering - Wayland users naturally expect that "every frame is perfect" -- Layer shell popups (used by Waybar) -- Damage tracking provided by scenegraph API - -Given the Wayland architecture, dwl has to implement features from [dwm] **and** -the xorg-server. Because of this, it is impossible to maintain the original -project goal of 2000 SLOC and have a reasonably complete compositor with -features comparable to [dwm]. However, this does not mean that the code will grow -indiscriminately. We will try to keep the code as small as possible. - -Features under consideration (possibly as patches) are: - -- Protocols made trivial by wlroots -- Implement the text-input and input-method protocols to support IME once ibus - implements input-method v2 (see https://github.com/ibus/ibus/pull/2256 and - https://codeberg.org/dwl/dwl/pulls/235) - -Feature *non-goals* for the main codebase include: - -- Client-side decoration (any more than is necessary to tell the clients not to) -- Client-initiated window management, such as move, resize, and close, which can - be done through the compositor -- Animations and visual effects - ## Acknowledgements dwl began by extending the TinyWL example provided (CC0) by the sway/wlroots @@ -181,31 +133,25 @@ developers. This was made possible in many cases by looking at how sway accomplished something, then trying to do the same in as suckless a way as possible. -Many thanks to suckless.org and the [dwm] developers and community for the +Many thanks to suckless.org and the dwm developers and community for the inspiration, and to the various contributors to the project, including: -- **Devin J. Pohly for creating and nurturing the fledgling project** - Alexander Courtis for the XWayland implementation - Guido Cella for the layer-shell protocol implementation, patch maintenance, and for helping to keep the project running - Stivvo for output management and fullscreen support, and patch maintenance -[wlroots]: https://gitlab.freedesktop.org/wlroots -[dwm]: https://dwm.suckless.org/ -[`systemd --user`]: https://wiki.archlinux.org/title/Systemd/User -[#dwl on Libera Chat]: https://web.libera.chat/?channels=#dwl -[0.7-rc1]: https://codeberg.org/dwl/dwl/releases/tag/v0.7-rc1 -[0.x branch]: https://codeberg.org/dwl/dwl/branches -[anopa]: https://jjacky.com/anopa/ -[dinit]: https://davmac.org/projects/dinit/ -[dwl-patches]: https://codeberg.org/dwl/dwl-patches -[list of useful resources on our wiki]: https://codeberg.org/dwl/dwl/wiki/Home#migrating-from-x -[main]: https://codeberg.org/dwl/dwl/src/branch/main -[release]: https://codeberg.org/dwl/dwl/releases -[runit]: http://smarden.org/runit/faq.html#userservices -[s6]: https://skarnet.org/software/s6/ -[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots/ -[wiki]: https://codeberg.org/dwl/dwl/wiki/Home#compatible-status-bars [Discord server]: https://discord.gg/jJxZnrGPWN +[#dwl]: https://web.libera.chat/?channels=#dwl [Wayland]: https://wayland.freedesktop.org/ +[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots/ +[wlroots-next branch]: https://github.com/djpohly/dwl/tree/wlroots-next +[patches page on our wiki]: https://github.com/djpohly/dwl/wiki/Patches +[s6]: https://skarnet.org/software/s6/ +[anopa]: https://jjacky.com/anopa/ +[runit]: http://smarden.org/runit/faq.html#userservices +[`systemd --user`]: https://wiki.archlinux.org/title/Systemd/User +[wiki]: https://github.com/djpohly/dwl/wiki#compatible-status-bars +[list of useful resources on our wiki]: + https://github.com/djpohly/dwl/wiki#migrating-from-x diff --git a/client.h b/client.h index d9f90bb..5a45edc 100644 --- a/client.h +++ b/client.h @@ -10,9 +10,35 @@ static inline int client_is_x11(Client *c) { #ifdef XWAYLAND - return c->type == X11; -#endif + return c->type == X11Managed || c->type == X11Unmanaged; +#else return 0; +#endif +} + +static inline void +client_get_size_hints(Client *c, struct wlr_box *max, struct wlr_box *min) +{ + struct wlr_xdg_toplevel *toplevel; + struct wlr_xdg_toplevel_state *state; +#ifdef XWAYLAND + if (client_is_x11(c)) { + xcb_size_hints_t *size_hints = c->surface.xwayland->size_hints; + if (size_hints) { + max->width = size_hints->max_width; + max->height = size_hints->max_height; + min->width = size_hints->min_width; + min->height = size_hints->min_height; + } + return; + } +#endif + toplevel = c->surface.xdg->toplevel; + state = &toplevel->current; + max->width = state->max_width; + max->height = state->max_height; + min->width = state->min_width; + min->height = state->min_height; } static inline struct wlr_surface * @@ -28,7 +54,7 @@ client_surface(Client *c) static inline int toplevel_from_wlr_surface(struct wlr_surface *s, Client **pc, LayerSurface **pl) { - struct wlr_xdg_surface *xdg_surface, *tmp_xdg_surface; + struct wlr_xdg_surface *xdg_surface; struct wlr_surface *root_surface; struct wlr_layer_surface_v1 *layer_surface; Client *c = NULL; @@ -39,44 +65,44 @@ toplevel_from_wlr_surface(struct wlr_surface *s, Client **pc, LayerSurface **pl) #endif if (!s) - return -1; + return type; root_surface = wlr_surface_get_root_surface(s); #ifdef XWAYLAND - if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(root_surface))) { + if (wlr_surface_is_xwayland_surface(root_surface) + && (xsurface = wlr_xwayland_surface_from_wlr_surface(root_surface))) { c = xsurface->data; type = c->type; goto end; } #endif - if ((layer_surface = wlr_layer_surface_v1_try_from_wlr_surface(root_surface))) { + if (wlr_surface_is_layer_surface(root_surface) + && (layer_surface = wlr_layer_surface_v1_from_wlr_surface(root_surface))) { l = layer_surface->data; type = LayerShell; goto end; } - xdg_surface = wlr_xdg_surface_try_from_wlr_surface(root_surface); - while (xdg_surface) { - tmp_xdg_surface = NULL; - switch (xdg_surface->role) { - case WLR_XDG_SURFACE_ROLE_POPUP: - if (!xdg_surface->popup || !xdg_surface->popup->parent) + if (wlr_surface_is_xdg_surface(root_surface) + && (xdg_surface = wlr_xdg_surface_from_wlr_surface(root_surface))) { + while (1) { + switch (xdg_surface->role) { + case WLR_XDG_SURFACE_ROLE_POPUP: + if (!xdg_surface->popup->parent) + return -1; + else if (!wlr_surface_is_xdg_surface(xdg_surface->popup->parent)) + return toplevel_from_wlr_surface(xdg_surface->popup->parent, pc, pl); + + xdg_surface = wlr_xdg_surface_from_wlr_surface(xdg_surface->popup->parent); + break; + case WLR_XDG_SURFACE_ROLE_TOPLEVEL: + c = xdg_surface->data; + type = c->type; + goto end; + case WLR_XDG_SURFACE_ROLE_NONE: return -1; - - tmp_xdg_surface = wlr_xdg_surface_try_from_wlr_surface(xdg_surface->popup->parent); - - if (!tmp_xdg_surface) - return toplevel_from_wlr_surface(xdg_surface->popup->parent, pc, pl); - - xdg_surface = tmp_xdg_surface; - break; - case WLR_XDG_SURFACE_ROLE_TOPLEVEL: - c = xdg_surface->data; - type = c->type; - goto end; - case WLR_XDG_SURFACE_ROLE_NONE: - return -1; + } } } @@ -92,16 +118,19 @@ end: static inline void client_activate_surface(struct wlr_surface *s, int activated) { - struct wlr_xdg_toplevel *toplevel; + struct wlr_xdg_surface *surface; #ifdef XWAYLAND struct wlr_xwayland_surface *xsurface; - if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(s))) { + if (wlr_surface_is_xwayland_surface(s) + && (xsurface = wlr_xwayland_surface_from_wlr_surface(s))) { wlr_xwayland_surface_activate(xsurface, activated); return; } #endif - if ((toplevel = wlr_xdg_toplevel_try_from_wlr_surface(s))) - wlr_xdg_toplevel_set_activated(toplevel, activated); + if (wlr_surface_is_xdg_surface(s) + && (surface = wlr_xdg_surface_from_wlr_surface(s)) + && surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) + wlr_xdg_toplevel_set_activated(surface->toplevel, activated); } static inline uint32_t @@ -111,43 +140,31 @@ client_set_bounds(Client *c, int32_t width, int32_t height) if (client_is_x11(c)) return 0; #endif - if (wl_resource_get_version(c->surface.xdg->toplevel->resource) >= - XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION && width >= 0 && height >= 0 - && (c->bounds.width != width || c->bounds.height != height)) { - c->bounds.width = width; - c->bounds.height = height; + if (c->surface.xdg->client->shell->version >= + XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION && width >= 0 && height >= 0) return wlr_xdg_toplevel_set_bounds(c->surface.xdg->toplevel, width, height); - } return 0; } +static inline void +client_for_each_surface(Client *c, wlr_surface_iterator_func_t fn, void *data) +{ + wlr_surface_for_each_surface(client_surface(c), fn, data); +#ifdef XWAYLAND + if (client_is_x11(c)) + return; +#endif + wlr_xdg_surface_for_each_popup_surface(c->surface.xdg, fn, data); +} + static inline const char * client_get_appid(Client *c) { #ifdef XWAYLAND if (client_is_x11(c)) - return c->surface.xwayland->class ? c->surface.xwayland->class : "broken"; + return c->surface.xwayland->class; #endif - return c->surface.xdg->toplevel->app_id ? c->surface.xdg->toplevel->app_id : "broken"; -} - -static inline void -client_get_clip(Client *c, struct wlr_box *clip) -{ - *clip = (struct wlr_box){ - .x = 0, - .y = 0, - .width = c->geom.width - c->bw, - .height = c->geom.height - c->bw, - }; - -#ifdef XWAYLAND - if (client_is_x11(c)) - return; -#endif - - clip->x = c->surface.xdg->geometry.x; - clip->y = c->surface.xdg->geometry.y; + return c->surface.xdg->toplevel->app_id; } static inline void @@ -162,7 +179,7 @@ client_get_geometry(Client *c, struct wlr_box *geom) return; } #endif - *geom = c->surface.xdg->geometry; + wlr_xdg_surface_get_geometry(c->surface.xdg, geom); } static inline Client * @@ -170,27 +187,13 @@ client_get_parent(Client *c) { Client *p = NULL; #ifdef XWAYLAND - if (client_is_x11(c)) { - if (c->surface.xwayland->parent) - toplevel_from_wlr_surface(c->surface.xwayland->parent->surface, &p, NULL); - return p; - } + if (client_is_x11(c) && c->surface.xwayland->parent) + toplevel_from_wlr_surface(c->surface.xwayland->parent->surface, &p, NULL); #endif if (c->surface.xdg->toplevel->parent) toplevel_from_wlr_surface(c->surface.xdg->toplevel->parent->base->surface, &p, NULL); - return p; -} -static inline int -client_has_children(Client *c) -{ -#ifdef XWAYLAND - if (client_is_x11(c)) - return !wl_list_empty(&c->surface.xwayland->children); -#endif - /* surface.xdg->link is never empty because it always contains at least the - * surface itself. */ - return wl_list_length(&c->surface.xdg->link) > 1; + return p; } static inline const char * @@ -198,42 +201,43 @@ client_get_title(Client *c) { #ifdef XWAYLAND if (client_is_x11(c)) - return c->surface.xwayland->title ? c->surface.xwayland->title : "broken"; + return c->surface.xwayland->title; #endif - return c->surface.xdg->toplevel->title ? c->surface.xdg->toplevel->title : "broken"; + return c->surface.xdg->toplevel->title; } static inline int client_is_float_type(Client *c) { - struct wlr_xdg_toplevel *toplevel; - struct wlr_xdg_toplevel_state state; + struct wlr_box min = {0}, max = {0}; + client_get_size_hints(c, &max, &min); #ifdef XWAYLAND if (client_is_x11(c)) { struct wlr_xwayland_surface *surface = c->surface.xwayland; - xcb_size_hints_t *size_hints = surface->size_hints; if (surface->modal) return 1; - if (wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DIALOG) - || wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_SPLASH) - || wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_TOOLBAR) - || wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_UTILITY)) { - return 1; - } - - return size_hints && size_hints->min_width > 0 && size_hints->min_height > 0 - && (size_hints->max_width == size_hints->min_width - || size_hints->max_height == size_hints->min_height); + for (size_t i = 0; i < surface->window_type_len; i++) + if (surface->window_type[i] == netatom[NetWMWindowTypeDialog] + || surface->window_type[i] == netatom[NetWMWindowTypeSplash] + || surface->window_type[i] == netatom[NetWMWindowTypeToolbar] + || surface->window_type[i] == netatom[NetWMWindowTypeUtility]) + return 1; } #endif + return ((min.width > 0 || min.height > 0 || max.width > 0 || max.height > 0) + && (min.width == max.width || min.height == max.height)); +} - toplevel = c->surface.xdg->toplevel; - state = toplevel->current; - return toplevel->parent || (state.min_width != 0 && state.min_height != 0 - && (state.min_width == state.max_width - || state.min_height == state.max_height)); +static inline int +client_is_mapped(Client *c) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) + return c->surface.xwayland->mapped; +#endif + return c->surface.xdg->mapped; } static inline int @@ -243,8 +247,7 @@ client_is_rendered_on_mon(Client *c, Monitor *m) * but rather actual displaying of the pixels. * Usually VISIBLEON suffices and is also faster. */ struct wlr_surface_output *s; - int unused_lx, unused_ly; - if (!wlr_scene_node_coords(&c->scene->node, &unused_lx, &unused_ly)) + if (!c->scene->node.enabled) return 0; wl_list_for_each(s, &client_surface(c)->current_outputs, link) if (s->output == m->wlr_output) @@ -264,8 +267,8 @@ client_is_stopped(Client *c) wl_client_get_credentials(c->surface.xdg->client->client, &pid, NULL, NULL); if (waitid(P_PID, pid, &in, WNOHANG|WCONTINUED|WSTOPPED|WNOWAIT) < 0) { - /* This process is not our child process, while is very unlikely that - * it is stopped, in order to do not skip frames, assume that it is. */ + /* This process is not our child process, while is very unluckely that + * it is stopped, in order to do not skip frames assume that it is. */ if (errno == ECHILD) return 1; } else if (in.si_pid) { @@ -282,8 +285,7 @@ static inline int client_is_unmanaged(Client *c) { #ifdef XWAYLAND - if (client_is_x11(c)) - return c->surface.xwayland->override_redirect; + return c->type == X11Unmanaged; #endif return 0; } @@ -298,6 +300,17 @@ client_notify_enter(struct wlr_surface *s, struct wlr_keyboard *kb) wlr_seat_keyboard_notify_enter(seat, s, NULL, 0, NULL); } +static inline void +client_restack_surface(Client *c) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) + wlr_xwayland_surface_restack(c->surface.xwayland, NULL, + XCB_STACK_MODE_ABOVE); +#endif + return; +} + static inline void client_send_close(Client *c) { @@ -310,14 +323,6 @@ client_send_close(Client *c) wlr_xdg_toplevel_send_close(c->surface.xdg->toplevel); } -static inline void -client_set_border_color(Client *c, const float color[static 4]) -{ - int i; - for (i = 0; i < 4; i++) - wlr_scene_rect_set_color(c->border[i], color); -} - static inline void client_set_fullscreen(Client *c, int fullscreen) { @@ -330,56 +335,41 @@ client_set_fullscreen(Client *c, int fullscreen) wlr_xdg_toplevel_set_fullscreen(c->surface.xdg->toplevel, fullscreen); } -static inline void -client_set_scale(struct wlr_surface *s, float scale) -{ - wlr_fractional_scale_v1_notify_scale(s, scale); - wlr_surface_set_preferred_buffer_scale(s, (int32_t)ceilf(scale)); -} - static inline uint32_t client_set_size(Client *c, uint32_t width, uint32_t height) { #ifdef XWAYLAND if (client_is_x11(c)) { wlr_xwayland_surface_configure(c->surface.xwayland, - c->geom.x + c->bw, c->geom.y + c->bw, width, height); + c->geom.x, c->geom.y, width, height); return 0; } #endif - if ((int32_t)width == c->surface.xdg->toplevel->current.width - && (int32_t)height == c->surface.xdg->toplevel->current.height) + if (width == c->surface.xdg->toplevel->current.width + && height ==c->surface.xdg->toplevel->current.height) return 0; - return wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, (int32_t)width, (int32_t)height); + return wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, width, height); } static inline void client_set_tiled(Client *c, uint32_t edges) { -#ifdef XWAYLAND - if (client_is_x11(c)) { - wlr_xwayland_surface_set_maximized(c->surface.xwayland, - edges != WLR_EDGE_NONE, edges != WLR_EDGE_NONE); - return; - } -#endif - if (wl_resource_get_version(c->surface.xdg->toplevel->resource) - >= XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION) { - wlr_xdg_toplevel_set_tiled(c->surface.xdg->toplevel, edges); - } else { - wlr_xdg_toplevel_set_maximized(c->surface.xdg->toplevel, edges != WLR_EDGE_NONE); - } -} - -static inline void -client_set_suspended(Client *c, int suspended) -{ #ifdef XWAYLAND if (client_is_x11(c)) return; #endif + wlr_xdg_toplevel_set_tiled(c->surface.xdg->toplevel, edges); +} - wlr_xdg_toplevel_set_suspended(c->surface.xdg->toplevel, suspended); +static inline struct wlr_surface * +client_surface_at(Client *c, double cx, double cy, double *sx, double *sy) +{ +#ifdef XWAYLAND + if (client_is_x11(c)) + return wlr_surface_surface_at(c->surface.xwayland->surface, + cx, cy, sx, sy); +#endif + return wlr_xdg_surface_surface_at(c->surface.xdg, cx, cy, sx, sy); } static inline int @@ -387,8 +377,8 @@ client_wants_focus(Client *c) { #ifdef XWAYLAND return client_is_unmanaged(c) - && wlr_xwayland_surface_override_redirect_wants_focus(c->surface.xwayland) - && wlr_xwayland_surface_icccm_input_model(c->surface.xwayland) != WLR_ICCCM_INPUT_MODEL_NONE; + && wlr_xwayland_or_surface_wants_focus(c->surface.xwayland) + && wlr_xwayland_icccm_input_model(c->surface.xwayland) != WLR_ICCCM_INPUT_MODEL_NONE; #endif return 0; } diff --git a/config.def.h b/config.def.h index 95c2afa..a4f7c13 100644 --- a/config.def.h +++ b/config.def.h @@ -1,31 +1,22 @@ -/* Taken from https://github.com/djpohly/dwl/issues/466 */ -#define COLOR(hex) { ((hex >> 24) & 0xFF) / 255.0f, \ - ((hex >> 16) & 0xFF) / 255.0f, \ - ((hex >> 8) & 0xFF) / 255.0f, \ - (hex & 0xFF) / 255.0f } /* appearance */ static const int sloppyfocus = 1; /* focus follows mouse */ static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */ static const unsigned int borderpx = 1; /* border pixel of windows */ -static const float rootcolor[] = COLOR(0x222222ff); -static const float bordercolor[] = COLOR(0x444444ff); -static const float focuscolor[] = COLOR(0x005577ff); -static const float urgentcolor[] = COLOR(0xff0000ff); -/* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */ -static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You can also use glsl colors */ +static const float rootcolor[] = {0.3, 0.3, 0.3, 1.0}; +static const float bordercolor[] = {0.5, 0.5, 0.5, 1.0}; +static const float focuscolor[] = {1.0, 0.0, 0.0, 1.0}; +/* To conform the xdg-protocol, set the alpha to zero to restore the old behavior */ +static const float fullscreen_bg[] = {0.1, 0.1, 0.1, 1.0}; -/* tagging - TAGCOUNT must be no greater than 31 */ -#define TAGCOUNT (9) +/* tagging */ +static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; -/* logging */ -static int log_level = WLR_ERROR; - -/* NOTE: ALWAYS keep a rule declared even if you don't use rules (e.g leave at least one example) */ static const Rule rules[] = { - /* app_id title tags mask isfloating monitor */ - /* examples: */ - { "Gimp_EXAMPLE", NULL, 0, 1, -1 }, /* Start on currently visible tags floating, not tiled */ - { "firefox_EXAMPLE", NULL, 1 << 8, 0, -1 }, /* Start on ONLY tag "9" */ + /* app_id title tags mask isfloating monitor */ + /* examples: + { "Gimp", NULL, 0, 1, -1 }, + */ + { "firefox", NULL, 1 << 8, 0, -1 }, }; /* layout(s) */ @@ -37,18 +28,13 @@ static const Layout layouts[] = { }; /* monitors */ -/* (x=-1, y=-1) is reserved as an "autoconfigure" monitor position indicator - * WARNING: negative values other than (-1, -1) cause problems with Xwayland clients - * https://gitlab.freedesktop.org/xorg/xserver/-/issues/899 -*/ -/* NOTE: ALWAYS add a fallback rule, even if you are completely sure it won't be used */ static const MonitorRule monrules[] = { - /* name mfact nmaster scale layout rotate/reflect x y */ + /* name mfact nmaster scale layout rotate/reflect */ /* example of a HiDPI laptop monitor: - { "eDP-1", 0.5f, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, + { "eDP-1", 0.5, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL }, */ /* defaults */ - { NULL, 0.55f, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, + { NULL, 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL }, }; /* keyboard */ @@ -80,9 +66,9 @@ LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN static const enum libinput_config_scroll_method scroll_method = LIBINPUT_CONFIG_SCROLL_2FG; /* You can choose between: -LIBINPUT_CONFIG_CLICK_METHOD_NONE -LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS -LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER +LIBINPUT_CONFIG_CLICK_METHOD_NONE +LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS +LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER */ static const enum libinput_config_click_method click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; @@ -99,7 +85,6 @@ LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE */ static const enum libinput_config_accel_profile accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE; static const double accel_speed = 0.0; - /* You can choose between: LIBINPUT_CONFIG_TAP_MAP_LRM -- 1/2/3 finger tap maps to left/right/middle LIBINPUT_CONFIG_TAP_MAP_LMR -- 1/2/3 finger tap maps to left/middle/right @@ -120,7 +105,7 @@ static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TA /* commands */ static const char *termcmd[] = { "foot", NULL }; -static const char *menucmd[] = { "wmenu-run", NULL }; +static const char *menucmd[] = { "bemenu-run", NULL }; static const Key keys[] = { /* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */ @@ -131,8 +116,8 @@ static const Key keys[] = { { MODKEY, XKB_KEY_k, focusstack, {.i = -1} }, { MODKEY, XKB_KEY_i, incnmaster, {.i = +1} }, { MODKEY, XKB_KEY_d, incnmaster, {.i = -1} }, - { MODKEY, XKB_KEY_h, setmfact, {.f = -0.05f} }, - { MODKEY, XKB_KEY_l, setmfact, {.f = +0.05f} }, + { MODKEY, XKB_KEY_h, setmfact, {.f = -0.05} }, + { MODKEY, XKB_KEY_l, setmfact, {.f = +0.05} }, { MODKEY, XKB_KEY_Return, zoom, {0} }, { MODKEY, XKB_KEY_Tab, view, {0} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_C, killclient, {0} }, @@ -161,9 +146,6 @@ static const Key keys[] = { /* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */ { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} }, - /* Ctrl-Alt-Fx is used to switch to another VT, if you don't know what a VT is - * do not remove them. - */ #define CHVT(n) { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_XF86Switch_VT_##n, chvt, {.ui = (n)} } CHVT(1), CHVT(2), CHVT(3), CHVT(4), CHVT(5), CHVT(6), CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12), diff --git a/config.mk b/config.mk index eb08a05..f50156f 100644 --- a/config.mk +++ b/config.mk @@ -1,4 +1,4 @@ -_VERSION = 0.8-dev +_VERSION = 0.4 VERSION = `git describe --tags --dirty 2>/dev/null || echo $(_VERSION)` PKG_CONFIG = pkg-config @@ -6,31 +6,9 @@ PKG_CONFIG = pkg-config # paths PREFIX = /usr/local MANDIR = $(PREFIX)/share/man -DATADIR = $(PREFIX)/share - -WLR_INCS = `$(PKG_CONFIG) --cflags wlroots-0.19` -WLR_LIBS = `$(PKG_CONFIG) --libs wlroots-0.19` - -# Allow using an alternative wlroots installation -# This has to have all the includes required by wlroots, e.g: -# Assuming wlroots git repo is "${PWD}/wlroots" and you only ran "meson setup build && ninja -C build" -#WLR_INCS = -I/usr/include/pixman-1 -I/usr/include/elogind -I/usr/include/libdrm \ -# -I$(PWD)/wlroots/include -# Set -rpath to avoid using the wrong library. -#WLR_LIBS = -Wl,-rpath,$(PWD)/wlroots/build -L$(PWD)/wlroots/build -lwlroots-0.19 - -# Assuming you ran "meson setup --prefix ${PWD}/0.19 build && ninja -C build install" -#WLR_INCS = -I/usr/include/pixman-1 -I/usr/include/elogind -I/usr/include/libdrm \ -# -I$(PWD)/wlroots/0.19/include/wlroots-0.19 -#WLR_LIBS = -Wl,-rpath,$(PWD)/wlroots/0.19/lib64 -L$(PWD)/wlroots/0.19/lib64 -lwlroots-0.19 XWAYLAND = XLIBS = # Uncomment to build XWayland support #XWAYLAND = -DXWAYLAND #XLIBS = xcb xcb-icccm - -# dwl itself only uses C99 features, but wlroots' headers use anonymous unions (C11). -# To avoid warnings about them, we do not use -std=c99 and instead of using the -# gmake default 'CC=c99', we use cc. -CC = cc diff --git a/dwl.1 b/dwl.1 index 7fee870..cae1036 100644 --- a/dwl.1 +++ b/dwl.1 @@ -7,7 +7,6 @@ .Sh SYNOPSIS .Nm .Op Fl v -.Op Fl d .Op Fl s Ar startup command .Sh DESCRIPTION .Nm @@ -23,12 +22,6 @@ option, writes its name and version to standard error and exits unsuccessfully. .Pp When given the -.Fl d -option, -.Nm -enables full wlroots logging, including debug information. -.Pp -When given the .Fl s option, .Nm @@ -37,7 +30,7 @@ starts a shell process running when starting. When stopping, it sends .Dv SIGTERM -to the child process group and waits for it to exit. +to the child process and waits for it to exit. .Pp Users are encouraged to customize .Nm @@ -55,10 +48,10 @@ Move window to a single tag. Toggle tag for window. .It Mod-p Spawn -.Xr wmenu-run 1 . +.Nm bemenu-run . .It Mod-Shift-Return Spawn -.Xr foot 1 . +.Nm foot . .It Mod-[jk] Move focus down/up the stack. .It Mod-[id] @@ -100,114 +93,6 @@ Quit .Nm . .El These might differ depending on your keyboard layout. -.Ss Mouse commands -.Bl -tag -width 20n -offset indent -compact -.It Mod-Button1 -Move focused window while dragging. -Tiled windows will be toggled to the floating state. -.It Mod-Button2 -Toggle focused window between floating and tiled state. -.It Mod-Button3 -Resize focused window while dragging. -Tiled windows will be toggled to the floating state. -.El -.Sh STATUS INFORMATION -.Nm -writes its status information to standard output. -If the -.Fl s -option is given, the status information is written to the standard input of the -child process instead. -.Pp -Said information has the following format: -.Bd -ragged -offset indent -.Ar -.Ar -.Ar -.Ed -.Pp -.Bl -tag -width 11n -offset 0 -compact -.It Ar -is the name given to the output. -.It Ar -is one of (in order) -.Em title , -.Em appid , -.Em fullscreen , -.Em floating , -.Em selmon , -.Em tags , -.Em layout . -.It Ar -changes depending on -.Ar . -.Bl -tag -width 10n -compact -.It Em title -The title of the focused window on -.Ar -or nothing if there is no focused window. -.It Em appid -The app_id of the focused window on -.Ar -or nothing if there is no focused window. -.It Em fullscreen -Prints 1 if the focused window on -.Ar -is in fullscreen state, otherwise prints 0. If there is no focused -window it prints nothing. -.It Em floating -Prints 1 if the focused window on -.Ar -is in floating state, otherwise prints 0. If there is no focused -window it prints nothing. -.It Em selmon -Prints 1 if -.Ar -is the selected monitor, otherwise prints 0. -.It Em tags -Prints four bitmasks in the following order: -.Bl -bullet -width 2n -compact -.It -Occupied tags of -.Ar . -.It -Selected tags of -.Ar . -.It -Tags of the focused window on -.Ar . -.It -Tags where a window on -.Ar -requested activation or has urgency hints. -.El -The bitmasks are 32-bit unsigned decimal integers. -.It Em layout -Prints the symbol of the current layout. -.El -.El -.Ss Examples -When there is a selected window: -.Bd -literal -offset indent -HDMI\-A\-1 title \(ti/source/repos/dwl > man \-l dwl.1 -HDMI\-A\-1 appid footclient -HDMI\-A\-1 fullscreen 0 -HDMI\-A\-1 floating 0 -HDMI\-A\-1 selmon 1 -HDMI\-A\-1 tags 271 4 4 0 -HDMI\-A\-1 layout [T] -.Ed -.Pp -When there is no selected window: -.Bd -literal -offset indent -HDMI\-A\-1 title -HDMI\-A\-1 appid -HDMI\-A\-1 fullscreen -HDMI\-A\-1 floating -HDMI\-A\-1 selmon 1 -HDMI\-A\-1 tags 271 512 0 0 -HDMI\-A\-1 layout [T] -.Ed .Sh ENVIRONMENT These environment variables are used by .Nm : @@ -216,7 +101,7 @@ These environment variables are used by A directory where temporary user files, such as the Wayland socket, are stored. .It Ev XDG_CONFIG_DIR -A directory containing configuration of various programs and +A directory containung configuration of various programs and libraries, including libxkbcommon. .It Ev DISPLAY , WAYLAND_DISPLAY , WAYLAND_SOCKET Tell how to connect to an underlying X11 or Wayland server. @@ -248,11 +133,19 @@ server. Start .Nm with s6 in the background: -.Dl dwl \-s \(aqs6\-svscan <&\-\(aq +.Dl dwl -s 's6-svscan <&-' .Sh SEE ALSO -.Xr dwm 1 , .Xr foot 1 , -.Xr wmenu 1 , +.Xr bemenu 1 , +.Xr dwm 1 , .Xr xkeyboard-config 7 +.Sh CAVEATS +The child process's standard input is connected with a pipe to +.Nm . +If the child process neither reads from the pipe nor closes its +standard input, +.Nm +will freeze after a while due to it blocking when writing to the full +pipe buffer. .Sh BUGS All of them. diff --git a/dwl.c b/dwl.c index 12f441e..19bb6ce 100644 --- a/dwl.c +++ b/dwl.c @@ -3,8 +3,8 @@ */ #include #include +#include #include -#include #include #include #include @@ -16,34 +16,26 @@ #include #include #include -#include #include #include -#include #include #include -#include #include -#include #include +#include #include #include #include +#include #include -#include #include -#include -#include #include #include #include -#include #include -#include #include #include #include -#include #include #include #include @@ -53,18 +45,16 @@ #include #include #include -#include #include #include #include #include #include #include -#include #include #ifdef XWAYLAND #include -#include +#include #include #endif @@ -77,18 +67,22 @@ #define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags])) #define LENGTH(X) (sizeof X / sizeof X[0]) #define END(A) ((A) + LENGTH(A)) -#define TAGMASK ((1u << TAGCOUNT) - 1) +#define TAGMASK ((1 << LENGTH(tags)) - 1) #define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L))) -#define LISTEN_STATIC(E, H) do { struct wl_listener *_l = ecalloc(1, sizeof(*_l)); _l->notify = (H); wl_signal_add((E), _l); } while (0) +#define IDLE_NOTIFY_ACTIVITY wlr_idle_notify_activity(idle, seat), wlr_idle_notifier_v1_notify_activity(idle_notifier, seat) /* enums */ enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */ -enum { XDGShell, LayerShell, X11 }; /* client types */ -enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrTop, LyrFS, LyrOverlay, LyrBlock, NUM_LAYERS }; /* scene layers */ +enum { XDGShell, LayerShell, X11Managed, X11Unmanaged }; /* client types */ +enum { LyrBg, LyrBottom, LyrTop, LyrOverlay, LyrTile, LyrFloat, LyrFS, LyrDragIcon, LyrBlock, NUM_LAYERS }; /* scene layers */ +#ifdef XWAYLAND +enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar, + NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */ +#endif typedef union { int i; - uint32_t ui; + unsigned int ui; float f; const void *v; } Arg; @@ -102,23 +96,19 @@ typedef struct { typedef struct Monitor Monitor; typedef struct { - /* Must keep this field first */ + /* Must keep these three elements in this order */ unsigned int type; /* XDGShell or X11* */ - + struct wlr_box geom; /* layout-relative, includes border */ Monitor *mon; struct wlr_scene_tree *scene; struct wlr_scene_rect *border[4]; /* top, bottom, left, right */ struct wlr_scene_tree *scene_surface; struct wl_list link; struct wl_list flink; - struct wlr_box geom; /* layout-relative, includes border */ - struct wlr_box prev; /* layout-relative, includes border */ - struct wlr_box bounds; /* only width and height are used */ union { struct wlr_xdg_surface *xdg; struct wlr_xwayland_surface *xwayland; } surface; - struct wlr_xdg_toplevel_decoration_v1 *decoration; struct wl_listener commit; struct wl_listener map; struct wl_listener maximize; @@ -126,17 +116,14 @@ typedef struct { struct wl_listener destroy; struct wl_listener set_title; struct wl_listener fullscreen; - struct wl_listener set_decoration_mode; - struct wl_listener destroy_decoration; + struct wlr_box prev; /* layout-relative, includes border */ #ifdef XWAYLAND struct wl_listener activate; - struct wl_listener associate; - struct wl_listener dissociate; struct wl_listener configure; struct wl_listener set_hints; #endif unsigned int bw; - uint32_t tags; + unsigned int tags; int isfloating, isurgent, isfullscreen; uint32_t resize; /* configure serial of a pending resize */ } Client; @@ -149,22 +136,18 @@ typedef struct { } Key; typedef struct { - struct wlr_keyboard_group *wlr_group; - - int nsyms; - const xkb_keysym_t *keysyms; /* invalid if nsyms == 0 */ - uint32_t mods; /* invalid if nsyms == 0 */ - struct wl_event_source *key_repeat_source; + struct wl_list link; + struct wlr_keyboard *wlr_keyboard; struct wl_listener modifiers; struct wl_listener key; struct wl_listener destroy; -} KeyboardGroup; +} Keyboard; typedef struct { - /* Must keep this field first */ + /* Must keep these three elements in this order */ unsigned int type; /* LayerShell */ - + struct wlr_box geom; Monitor *mon; struct wlr_scene_tree *scene; struct wlr_scene_tree *popups; @@ -174,6 +157,7 @@ typedef struct { struct wlr_layer_surface_v1 *layer_surface; struct wl_listener destroy; + struct wl_listener map; struct wl_listener unmap; struct wl_listener surface_commit; } LayerSurface; @@ -190,21 +174,17 @@ struct Monitor { struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */ struct wl_listener frame; struct wl_listener destroy; - struct wl_listener request_state; struct wl_listener destroy_lock_surface; struct wlr_session_lock_surface_v1 *lock_surface; - struct wlr_box m; /* monitor area, layout-relative */ - struct wlr_box w; /* window area, layout-relative */ - struct wl_list layers[4]; /* LayerSurface.link */ + struct wlr_box m; /* monitor area, layout-relative */ + struct wlr_box w; /* window area, layout-relative */ + struct wl_list layers[4]; /* LayerSurface::link */ const Layout *lt[2]; unsigned int seltags; unsigned int sellt; - uint32_t tagset[2]; - float mfact; - int gamma_lut_changed; + unsigned int tagset[2]; + double mfact; int nmaster; - char ltsymbol[16]; - int asleep; }; typedef struct { @@ -214,18 +194,12 @@ typedef struct { float scale; const Layout *lt; enum wl_output_transform rr; - int x, y; } MonitorRule; -typedef struct { - struct wlr_pointer_constraint_v1 *constraint; - struct wl_listener destroy; -} PointerConstraint; - typedef struct { const char *id; const char *title; - uint32_t tags; + unsigned int tags; int isfloating; int monitor; } Rule; @@ -251,58 +225,47 @@ static void buttonpress(struct wl_listener *listener, void *data); static void chvt(const Arg *arg); static void checkidleinhibitor(struct wlr_surface *exclude); static void cleanup(void); +static void cleanupkeyboard(struct wl_listener *listener, void *data); static void cleanupmon(struct wl_listener *listener, void *data); -static void cleanuplisteners(void); static void closemon(Monitor *m); static void commitlayersurfacenotify(struct wl_listener *listener, void *data); static void commitnotify(struct wl_listener *listener, void *data); -static void commitpopup(struct wl_listener *listener, void *data); static void createdecoration(struct wl_listener *listener, void *data); static void createidleinhibitor(struct wl_listener *listener, void *data); static void createkeyboard(struct wlr_keyboard *keyboard); -static KeyboardGroup *createkeyboardgroup(void); static void createlayersurface(struct wl_listener *listener, void *data); static void createlocksurface(struct wl_listener *listener, void *data); static void createmon(struct wl_listener *listener, void *data); static void createnotify(struct wl_listener *listener, void *data); static void createpointer(struct wlr_pointer *pointer); -static void createpointerconstraint(struct wl_listener *listener, void *data); -static void createpopup(struct wl_listener *listener, void *data); -static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint); static void cursorframe(struct wl_listener *listener, void *data); -static void cursorwarptohint(void); -static void destroydecoration(struct wl_listener *listener, void *data); static void destroydragicon(struct wl_listener *listener, void *data); static void destroyidleinhibitor(struct wl_listener *listener, void *data); static void destroylayersurfacenotify(struct wl_listener *listener, void *data); static void destroylock(SessionLock *lock, int unlocked); static void destroylocksurface(struct wl_listener *listener, void *data); static void destroynotify(struct wl_listener *listener, void *data); -static void destroypointerconstraint(struct wl_listener *listener, void *data); static void destroysessionlock(struct wl_listener *listener, void *data); -static void destroykeyboardgroup(struct wl_listener *listener, void *data); +static void destroysessionmgr(struct wl_listener *listener, void *data); static Monitor *dirtomon(enum wlr_direction dir); static void focusclient(Client *c, int lift); static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); static Client *focustop(Monitor *m); static void fullscreennotify(struct wl_listener *listener, void *data); -static void gpureset(struct wl_listener *listener, void *data); -static void handlesig(int signo); static void incnmaster(const Arg *arg); static void inputdevice(struct wl_listener *listener, void *data); static int keybinding(uint32_t mods, xkb_keysym_t sym); static void keypress(struct wl_listener *listener, void *data); static void keypressmod(struct wl_listener *listener, void *data); -static int keyrepeat(void *data); static void killclient(const Arg *arg); static void locksession(struct wl_listener *listener, void *data); +static void maplayersurfacenotify(struct wl_listener *listener, void *data); static void mapnotify(struct wl_listener *listener, void *data); static void maximizenotify(struct wl_listener *listener, void *data); static void monocle(Monitor *m); static void motionabsolute(struct wl_listener *listener, void *data); -static void motionnotify(uint32_t time, struct wlr_input_device *device, double sx, - double sy, double sx_unaccel, double sy_unaccel); +static void motionnotify(uint32_t time); static void motionrelative(struct wl_listener *listener, void *data); static void moveresize(const Arg *arg); static void outputmgrapply(struct wl_listener *listener, void *data); @@ -311,21 +274,18 @@ static void outputmgrtest(struct wl_listener *listener, void *data); static void pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, uint32_t time); static void printstatus(void); -static void powermgrsetmode(struct wl_listener *listener, void *data); static void quit(const Arg *arg); +static void quitsignal(int signo); static void rendermon(struct wl_listener *listener, void *data); -static void requestdecorationmode(struct wl_listener *listener, void *data); static void requeststartdrag(struct wl_listener *listener, void *data); -static void requestmonstate(struct wl_listener *listener, void *data); static void resize(Client *c, struct wlr_box geo, int interact); static void run(char *startup_cmd); static void setcursor(struct wl_listener *listener, void *data); -static void setcursorshape(struct wl_listener *listener, void *data); static void setfloating(Client *c, int floating); static void setfullscreen(Client *c, int fullscreen); static void setlayout(const Arg *arg); static void setmfact(const Arg *arg); -static void setmon(Client *c, Monitor *m, uint32_t newtags); +static void setmon(Client *c, Monitor *m, unsigned int newtags); static void setpsel(struct wl_listener *listener, void *data); static void setsel(struct wl_listener *listener, void *data); static void setup(void); @@ -346,57 +306,47 @@ static void updatetitle(struct wl_listener *listener, void *data); static void urgent(struct wl_listener *listener, void *data); static void view(const Arg *arg); static void virtualkeyboard(struct wl_listener *listener, void *data); -static void virtualpointer(struct wl_listener *listener, void *data); static Monitor *xytomon(double x, double y); -static void xytonode(double x, double y, struct wlr_surface **psurface, +static struct wlr_scene_node *xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, LayerSurface **pl, double *nx, double *ny); static void zoom(const Arg *arg); /* variables */ +static const char broken[] = "broken"; +static const char *cursor_image = "left_ptr"; static pid_t child_pid = -1; static int locked; static void *exclusive_focus; static struct wl_display *dpy; -static struct wl_event_loop *event_loop; static struct wlr_backend *backend; static struct wlr_scene *scene; static struct wlr_scene_tree *layers[NUM_LAYERS]; -static struct wlr_scene_tree *drag_icon; -/* Map from ZWLR_LAYER_SHELL_* constants to Lyr* enum */ -static const int layermap[] = { LyrBg, LyrBottom, LyrTop, LyrOverlay }; static struct wlr_renderer *drw; static struct wlr_allocator *alloc; static struct wlr_compositor *compositor; -static struct wlr_session *session; static struct wlr_xdg_shell *xdg_shell; static struct wlr_xdg_activation_v1 *activation; static struct wlr_xdg_decoration_manager_v1 *xdg_decoration_mgr; static struct wl_list clients; /* tiling order */ static struct wl_list fstack; /* focus order */ +static struct wlr_idle *idle; static struct wlr_idle_notifier_v1 *idle_notifier; static struct wlr_idle_inhibit_manager_v1 *idle_inhibit_mgr; +static struct wlr_input_inhibit_manager *input_inhibit_mgr; static struct wlr_layer_shell_v1 *layer_shell; static struct wlr_output_manager_v1 *output_mgr; static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr; -static struct wlr_virtual_pointer_manager_v1 *virtual_pointer_mgr; -static struct wlr_cursor_shape_manager_v1 *cursor_shape_mgr; -static struct wlr_output_power_manager_v1 *power_mgr; - -static struct wlr_pointer_constraints_v1 *pointer_constraints; -static struct wlr_relative_pointer_manager_v1 *relative_pointer_mgr; -static struct wlr_pointer_constraint_v1 *active_constraint; static struct wlr_cursor *cursor; static struct wlr_xcursor_manager *cursor_mgr; -static struct wlr_scene_rect *root_bg; static struct wlr_session_lock_manager_v1 *session_lock_mgr; static struct wlr_scene_rect *locked_bg; static struct wlr_session_lock_v1 *cur_lock; static struct wlr_seat *seat; -static KeyboardGroup *kb_group; +static struct wl_list keyboards; static unsigned int cursor_mode; static Client *grabc; static int grabcx, grabcy; /* client-relative */ @@ -412,41 +362,39 @@ static struct wl_listener cursor_button = {.notify = buttonpress}; static struct wl_listener cursor_frame = {.notify = cursorframe}; static struct wl_listener cursor_motion = {.notify = motionrelative}; static struct wl_listener cursor_motion_absolute = {.notify = motionabsolute}; -static struct wl_listener gpu_reset = {.notify = gpureset}; +static struct wl_listener drag_icon_destroy = {.notify = destroydragicon}; +static struct wl_listener idle_inhibitor_create = {.notify = createidleinhibitor}; +static struct wl_listener idle_inhibitor_destroy = {.notify = destroyidleinhibitor}; static struct wl_listener layout_change = {.notify = updatemons}; -static struct wl_listener new_idle_inhibitor = {.notify = createidleinhibitor}; -static struct wl_listener new_input_device = {.notify = inputdevice}; +static struct wl_listener new_input = {.notify = inputdevice}; static struct wl_listener new_virtual_keyboard = {.notify = virtualkeyboard}; -static struct wl_listener new_virtual_pointer = {.notify = virtualpointer}; -static struct wl_listener new_pointer_constraint = {.notify = createpointerconstraint}; static struct wl_listener new_output = {.notify = createmon}; -static struct wl_listener new_xdg_toplevel = {.notify = createnotify}; -static struct wl_listener new_xdg_popup = {.notify = createpopup}; +static struct wl_listener new_xdg_surface = {.notify = createnotify}; static struct wl_listener new_xdg_decoration = {.notify = createdecoration}; -static struct wl_listener new_layer_surface = {.notify = createlayersurface}; +static struct wl_listener new_layer_shell_surface = {.notify = createlayersurface}; static struct wl_listener output_mgr_apply = {.notify = outputmgrapply}; static struct wl_listener output_mgr_test = {.notify = outputmgrtest}; -static struct wl_listener output_power_mgr_set_mode = {.notify = powermgrsetmode}; static struct wl_listener request_activate = {.notify = urgent}; static struct wl_listener request_cursor = {.notify = setcursor}; static struct wl_listener request_set_psel = {.notify = setpsel}; static struct wl_listener request_set_sel = {.notify = setsel}; -static struct wl_listener request_set_cursor_shape = {.notify = setcursorshape}; static struct wl_listener request_start_drag = {.notify = requeststartdrag}; static struct wl_listener start_drag = {.notify = startdrag}; -static struct wl_listener new_session_lock = {.notify = locksession}; +static struct wl_listener session_lock_create_lock = {.notify = locksession}; +static struct wl_listener session_lock_mgr_destroy = {.notify = destroysessionmgr}; #ifdef XWAYLAND static void activatex11(struct wl_listener *listener, void *data); -static void associatex11(struct wl_listener *listener, void *data); static void configurex11(struct wl_listener *listener, void *data); static void createnotifyx11(struct wl_listener *listener, void *data); -static void dissociatex11(struct wl_listener *listener, void *data); +static Atom getatom(xcb_connection_t *xc, const char *name); static void sethints(struct wl_listener *listener, void *data); +static void sigchld(int unused); static void xwaylandready(struct wl_listener *listener, void *data); static struct wl_listener new_xwayland_surface = {.notify = createnotifyx11}; static struct wl_listener xwayland_ready = {.notify = xwaylandready}; static struct wlr_xwayland *xwayland; +static Atom netatom[NetLast]; #endif /* configuration, allows nested code to access above variables */ @@ -455,21 +403,34 @@ static struct wlr_xwayland *xwayland; /* attempt to encapsulate suck into one file */ #include "client.h" +/* compile-time check if all tags fit into an unsigned int bit array. */ +struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + /* function implementations */ void applybounds(Client *c, struct wlr_box *bbox) { - /* set minimum possible */ - c->geom.width = MAX(1 + 2 * (int)c->bw, c->geom.width); - c->geom.height = MAX(1 + 2 * (int)c->bw, c->geom.height); + if (!c->isfullscreen) { + struct wlr_box min = {0}, max = {0}; + client_get_size_hints(c, &max, &min); + /* try to set size hints */ + c->geom.width = MAX(min.width + (2 * (int)c->bw), c->geom.width); + c->geom.height = MAX(min.height + (2 * (int)c->bw), c->geom.height); + /* Some clients set them max size to INT_MAX, which does not violates + * the protocol but its innecesary, they can set them max size to zero. */ + if (max.width > 0 && !(2 * c->bw > INT_MAX - max.width)) /* Checks for overflow */ + c->geom.width = MIN(max.width + (2 * c->bw), c->geom.width); + if (max.height > 0 && !(2 * c->bw > INT_MAX - max.height)) /* Checks for overflow */ + c->geom.height = MIN(max.height + (2 * c->bw), c->geom.height); + } if (c->geom.x >= bbox->x + bbox->width) c->geom.x = bbox->x + bbox->width - c->geom.width; if (c->geom.y >= bbox->y + bbox->height) c->geom.y = bbox->y + bbox->height - c->geom.height; - if (c->geom.x + c->geom.width <= bbox->x) + if (c->geom.x + c->geom.width + 2 * c->bw <= bbox->x) c->geom.x = bbox->x; - if (c->geom.y + c->geom.height <= bbox->y) + if (c->geom.y + c->geom.height + 2 * c->bw <= bbox->y) c->geom.y = bbox->y; } @@ -478,13 +439,15 @@ applyrules(Client *c) { /* rule matching */ const char *appid, *title; - uint32_t newtags = 0; - int i; + unsigned int i, newtags = 0; const Rule *r; Monitor *mon = selmon, *m; - appid = client_get_appid(c); - title = client_get_title(c); + c->isfloating = client_is_float_type(c); + if (!(appid = client_get_appid(c))) + appid = broken; + if (!(title = client_get_title(c))) + title = broken; for (r = rules; r < END(rules); r++) { if ((!r->title || strstr(title, r->title)) @@ -492,14 +455,12 @@ applyrules(Client *c) c->isfloating = r->isfloating; newtags |= r->tags; i = 0; - wl_list_for_each(m, &mons, link) { + wl_list_for_each(m, &mons, link) if (r->monitor == i++) mon = m; - } } } - - c->isfloating |= client_is_float_type(c); + wlr_scene_node_reparent(&c->scene->node, layers[c->isfloating ? LyrFloat : LyrTile]); setmon(c, mon, newtags); } @@ -507,59 +468,37 @@ void arrange(Monitor *m) { Client *c; - - if (!m->wlr_output->enabled) - return; - - wl_list_for_each(c, &clients, link) { - if (c->mon == m) { + wl_list_for_each(c, &clients, link) + if (c->mon == m) wlr_scene_node_set_enabled(&c->scene->node, VISIBLEON(c, m)); - client_set_suspended(c, !VISIBLEON(c, m)); - } - } wlr_scene_node_set_enabled(&m->fullscreen_bg->node, (c = focustop(m)) && c->isfullscreen); - strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol)); - - /* We move all clients (except fullscreen and unmanaged) to LyrTile while - * in floating layout to avoid "real" floating clients be always on top */ - wl_list_for_each(c, &clients, link) { - if (c->mon != m || c->scene->node.parent == layers[LyrFS]) - continue; - - wlr_scene_node_reparent(&c->scene->node, - (!m->lt[m->sellt]->arrange && c->isfloating) - ? layers[LyrTile] - : (m->lt[m->sellt]->arrange && c->isfloating) - ? layers[LyrFloat] - : c->scene->node.parent); - } - - if (m->lt[m->sellt]->arrange) + if (m && m->lt[m->sellt]->arrange) m->lt[m->sellt]->arrange(m); - motionnotify(0, NULL, 0, 0, 0, 0); + motionnotify(0); checkidleinhibitor(NULL); } void arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int exclusive) { - LayerSurface *l; + LayerSurface *layersurface; struct wlr_box full_area = m->m; - wl_list_for_each(l, list, link) { - struct wlr_layer_surface_v1 *layer_surface = l->layer_surface; + wl_list_for_each(layersurface, list, link) { + struct wlr_layer_surface_v1 *wlr_layer_surface = layersurface->layer_surface; + struct wlr_layer_surface_v1_state *state = &wlr_layer_surface->current; - if (!layer_surface->initialized) + if (exclusive != (state->exclusive_zone > 0)) continue; - if (exclusive != (layer_surface->current.exclusive_zone > 0)) - continue; - - wlr_scene_layer_surface_v1_configure(l->scene_layer, &full_area, usable_area); - wlr_scene_node_set_position(&l->popups->node, l->scene->node.x, l->scene->node.y); + wlr_scene_layer_surface_v1_configure(layersurface->scene_layer, &full_area, usable_area); + wlr_scene_node_set_position(&layersurface->popups->node, + layersurface->scene->node.x, layersurface->scene->node.y); + layersurface->geom.x = layersurface->scene->node.x; + layersurface->geom.y = layersurface->scene->node.y; } } @@ -568,11 +507,11 @@ arrangelayers(Monitor *m) { int i; struct wlr_box usable_area = m->m; - LayerSurface *l; uint32_t layers_above_shell[] = { ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, ZWLR_LAYER_SHELL_V1_LAYER_TOP, }; + LayerSurface *layersurface; if (!m->wlr_output->enabled) return; @@ -580,7 +519,7 @@ arrangelayers(Monitor *m) for (i = 3; i >= 0; i--) arrangelayer(m, &m->layers[i], &usable_area, 1); - if (!wlr_box_equal(&usable_area, &m->w)) { + if (memcmp(&usable_area, &m->w, sizeof(struct wlr_box))) { m->w = usable_area; arrange(m); } @@ -590,15 +529,17 @@ arrangelayers(Monitor *m) arrangelayer(m, &m->layers[i], &usable_area, 0); /* Find topmost keyboard interactive layer, if such a layer exists */ - for (i = 0; i < (int)LENGTH(layers_above_shell); i++) { - wl_list_for_each_reverse(l, &m->layers[layers_above_shell[i]], link) { - if (locked || !l->layer_surface->current.keyboard_interactive || !l->mapped) - continue; - /* Deactivate the focused client. */ - focusclient(NULL, 0); - exclusive_focus = l; - client_notify_enter(l->layer_surface->surface, wlr_seat_get_keyboard(seat)); - return; + for (i = 0; i < LENGTH(layers_above_shell); i++) { + wl_list_for_each_reverse(layersurface, + &m->layers[layers_above_shell[i]], link) { + if (!locked && layersurface->layer_surface->current.keyboard_interactive + && layersurface->mapped) { + /* Deactivate the focused client. */ + focusclient(NULL, 0); + exclusive_focus = layersurface; + client_notify_enter(layersurface->layer_surface->surface, wlr_seat_get_keyboard(seat)); + return; + } } } } @@ -609,13 +550,13 @@ axisnotify(struct wl_listener *listener, void *data) /* This event is forwarded by the cursor when a pointer emits an axis event, * for example when you move the scroll wheel. */ struct wlr_pointer_axis_event *event = data; - wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); - /* TODO: allow usage of scroll wheel for mousebindings, it can be implemented - * by checking the event's orientation and the delta of the event */ + IDLE_NOTIFY_ACTIVITY; + /* TODO: allow usage of scroll whell for mousebindings, it can be implemented + * checking the event's orientation and the delta of the event */ /* Notify the client with pointer focus of the axis event. */ wlr_seat_pointer_notify_axis(seat, event->time_msec, event->orientation, event->delta, - event->delta_discrete, event->source, event->relative_direction); + event->delta_discrete, event->source); } void @@ -627,12 +568,11 @@ buttonpress(struct wl_listener *listener, void *data) Client *c; const Button *b; - wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); + IDLE_NOTIFY_ACTIVITY; switch (event->state) { - case WL_POINTER_BUTTON_STATE_PRESSED: + case WLR_BUTTON_PRESSED: cursor_mode = CurPressed; - selmon = xytomon(cursor->x, cursor->y); if (locked) break; @@ -651,19 +591,22 @@ buttonpress(struct wl_listener *listener, void *data) } } break; - case WL_POINTER_BUTTON_STATE_RELEASED: + case WLR_BUTTON_RELEASED: /* If you released any buttons, we exit interactive move/resize mode. */ - /* TODO: should reset to the pointer focus's current setcursor */ if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) { - wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); cursor_mode = CurNormal; + /* Clear the pointer focus, this way if the cursor is over a surface + * we will send an enter event after which the client will provide us + * a cursor surface */ + wlr_seat_pointer_clear_focus(seat); + motionnotify(0); /* Drop the window off on its new monitor */ selmon = xytomon(cursor->x, cursor->y); setmon(grabc, selmon, 0); - grabc = NULL; return; + } else { + cursor_mode = CurNormal; } - cursor_mode = CurNormal; break; } /* If the event wasn't handled by the compositor, notify the client with @@ -675,52 +618,59 @@ buttonpress(struct wl_listener *listener, void *data) void chvt(const Arg *arg) { - wlr_session_change_vt(session, arg->ui); + wlr_session_change_vt(wlr_backend_get_session(backend), arg->ui); } void checkidleinhibitor(struct wlr_surface *exclude) { - int inhibited = 0, unused_lx, unused_ly; + int inhibited = 0; struct wlr_idle_inhibitor_v1 *inhibitor; wl_list_for_each(inhibitor, &idle_inhibit_mgr->inhibitors, link) { struct wlr_surface *surface = wlr_surface_get_root_surface(inhibitor->surface); struct wlr_scene_tree *tree = surface->data; if (exclude != surface && (bypass_surface_visibility || (!tree - || wlr_scene_node_coords(&tree->node, &unused_lx, &unused_ly)))) { + || tree->node.enabled))) { inhibited = 1; break; } } + wlr_idle_set_enabled(idle, NULL, !inhibited); wlr_idle_notifier_v1_set_inhibited(idle_notifier, inhibited); } void cleanup(void) { - cleanuplisteners(); #ifdef XWAYLAND wlr_xwayland_destroy(xwayland); - xwayland = NULL; #endif wl_display_destroy_clients(dpy); if (child_pid > 0) { - kill(-child_pid, SIGTERM); + kill(child_pid, SIGTERM); waitpid(child_pid, NULL, 0); } - wlr_xcursor_manager_destroy(cursor_mgr); - - destroykeyboardgroup(&kb_group->destroy, NULL); - - /* If it's not destroyed manually, it will cause a use-after-free of wlr_seat. - * Destroy it until it's fixed on the wlroots side */ wlr_backend_destroy(backend); - + wlr_renderer_destroy(drw); + wlr_allocator_destroy(alloc); + wlr_xcursor_manager_destroy(cursor_mgr); + wlr_cursor_destroy(cursor); + wlr_output_layout_destroy(output_layout); + wlr_seat_destroy(seat); wl_display_destroy(dpy); - /* Destroy after the wayland display (when the monitors are already destroyed) - to avoid destroying them with an invalid scene output. */ - wlr_scene_node_destroy(&scene->tree.node); +} + +void +cleanupkeyboard(struct wl_listener *listener, void *data) +{ + Keyboard *kb = wl_container_of(listener, kb, destroy); + + wl_list_remove(&kb->link); + wl_list_remove(&kb->modifiers.link); + wl_list_remove(&kb->key.link); + wl_list_remove(&kb->destroy.link); + free(kb); } void @@ -728,88 +678,43 @@ cleanupmon(struct wl_listener *listener, void *data) { Monitor *m = wl_container_of(listener, m, destroy); LayerSurface *l, *tmp; - size_t i; + int i; - /* m->layers[i] are intentionally not unlinked */ - for (i = 0; i < LENGTH(m->layers); i++) { + for (i = 0; i <= ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY; i++) wl_list_for_each_safe(l, tmp, &m->layers[i], link) wlr_layer_surface_v1_destroy(l->layer_surface); - } wl_list_remove(&m->destroy.link); wl_list_remove(&m->frame.link); wl_list_remove(&m->link); - wl_list_remove(&m->request_state.link); - if (m->lock_surface) - destroylocksurface(&m->destroy_lock_surface, NULL); m->wlr_output->data = NULL; wlr_output_layout_remove(output_layout, m->wlr_output); wlr_scene_output_destroy(m->scene_output); + wlr_scene_node_destroy(&m->fullscreen_bg->node); closemon(m); - wlr_scene_node_destroy(&m->fullscreen_bg->node); free(m); } -void -cleanuplisteners(void) -{ - wl_list_remove(&cursor_axis.link); - wl_list_remove(&cursor_button.link); - wl_list_remove(&cursor_frame.link); - wl_list_remove(&cursor_motion.link); - wl_list_remove(&cursor_motion_absolute.link); - wl_list_remove(&gpu_reset.link); - wl_list_remove(&new_idle_inhibitor.link); - wl_list_remove(&layout_change.link); - wl_list_remove(&new_input_device.link); - wl_list_remove(&new_virtual_keyboard.link); - wl_list_remove(&new_virtual_pointer.link); - wl_list_remove(&new_pointer_constraint.link); - wl_list_remove(&new_output.link); - wl_list_remove(&new_xdg_toplevel.link); - wl_list_remove(&new_xdg_decoration.link); - wl_list_remove(&new_xdg_popup.link); - wl_list_remove(&new_layer_surface.link); - wl_list_remove(&output_mgr_apply.link); - wl_list_remove(&output_mgr_test.link); - wl_list_remove(&output_power_mgr_set_mode.link); - wl_list_remove(&request_activate.link); - wl_list_remove(&request_cursor.link); - wl_list_remove(&request_set_psel.link); - wl_list_remove(&request_set_sel.link); - wl_list_remove(&request_set_cursor_shape.link); - wl_list_remove(&request_start_drag.link); - wl_list_remove(&start_drag.link); - wl_list_remove(&new_session_lock.link); -#ifdef XWAYLAND - wl_list_remove(&new_xwayland_surface.link); - wl_list_remove(&xwayland_ready.link); -#endif -} - void closemon(Monitor *m) { /* update selmon if needed and * move closed monitor's clients to the focused one */ Client *c; - int i = 0, nmons = wl_list_length(&mons); - if (!nmons) { + if (wl_list_empty(&mons)) { selmon = NULL; } else if (m == selmon) { + int nmons = wl_list_length(&mons), i = 0; do /* don't switch to disabled mons */ selmon = wl_container_of(mons.next, selmon, link); while (!selmon->wlr_output->enabled && i++ < nmons); - - if (!selmon->wlr_output->enabled) - selmon = NULL; } wl_list_for_each(c, &clients, link) { if (c->isfloating && c->geom.x > m->m.width) resize(c, (struct wlr_box){.x = c->geom.x - m->w.width, .y = c->geom.y, - .width = c->geom.width, .height = c->geom.height}, 0); + .width = c->geom.width, .height = c->geom.height}, 0); if (c->mon == m) setmon(c, selmon, c->tags); } @@ -820,119 +725,63 @@ closemon(Monitor *m) void commitlayersurfacenotify(struct wl_listener *listener, void *data) { - LayerSurface *l = wl_container_of(listener, l, surface_commit); - struct wlr_layer_surface_v1 *layer_surface = l->layer_surface; - struct wlr_scene_tree *scene_layer = layers[layermap[layer_surface->current.layer]]; - struct wlr_layer_surface_v1_state old_state; + LayerSurface *layersurface = wl_container_of(listener, layersurface, surface_commit); + struct wlr_layer_surface_v1 *wlr_layer_surface = layersurface->layer_surface; + struct wlr_output *wlr_output = wlr_layer_surface->output; - if (l->layer_surface->initial_commit) { - client_set_scale(layer_surface->surface, l->mon->wlr_output->scale); - - /* Temporarily set the layer's current state to pending - * so that we can easily arrange it */ - old_state = l->layer_surface->current; - l->layer_surface->current = l->layer_surface->pending; - arrangelayers(l->mon); - l->layer_surface->current = old_state; + /* For some reason this layersurface have no monitor, this can be because + * its monitor has just been destroyed */ + if (!wlr_output || !(layersurface->mon = wlr_output->data)) return; - } - if (layer_surface->current.committed == 0 && l->mapped == layer_surface->surface->mapped) + if (layers[wlr_layer_surface->current.layer] != layersurface->scene->node.parent) { + wlr_scene_node_reparent(&layersurface->scene->node, + layers[wlr_layer_surface->current.layer]); + wlr_scene_node_reparent(&layersurface->popups->node, + layers[wlr_layer_surface->current.layer]); + wl_list_remove(&layersurface->link); + wl_list_insert(&layersurface->mon->layers[wlr_layer_surface->current.layer], + &layersurface->link); + } + if (wlr_layer_surface->current.layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP) + wlr_scene_node_reparent(&layersurface->popups->node, layers[LyrTop]); + + if (wlr_layer_surface->current.committed == 0 + && layersurface->mapped == wlr_layer_surface->mapped) return; - l->mapped = layer_surface->surface->mapped; + layersurface->mapped = wlr_layer_surface->mapped; - if (scene_layer != l->scene->node.parent) { - wlr_scene_node_reparent(&l->scene->node, scene_layer); - wl_list_remove(&l->link); - wl_list_insert(&l->mon->layers[layer_surface->current.layer], &l->link); - wlr_scene_node_reparent(&l->popups->node, (layer_surface->current.layer - < ZWLR_LAYER_SHELL_V1_LAYER_TOP ? layers[LyrTop] : scene_layer)); - } - - arrangelayers(l->mon); + arrangelayers(layersurface->mon); } void commitnotify(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, commit); + struct wlr_box box = {0}; + client_get_geometry(c, &box); - if (c->surface.xdg->initial_commit) { - /* - * Get the monitor this client will be rendered on - * Note that if the user set a rule in which the client is placed on - * a different monitor based on its title, this will likely select - * a wrong monitor. - */ - applyrules(c); - if (c->mon) { - client_set_scale(client_surface(c), c->mon->wlr_output->scale); - } - setmon(c, NULL, 0); /* Make sure to reapply rules in mapnotify() */ - - wlr_xdg_toplevel_set_wm_capabilities(c->surface.xdg->toplevel, - WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN); - if (c->decoration) - requestdecorationmode(&c->set_decoration_mode, c->decoration); - wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, 0, 0); - return; - } - - resize(c, c->geom, (c->isfloating && !c->isfullscreen)); + if (c->mon && !wlr_box_empty(&box) && (box.width != c->geom.width - 2 * c->bw + || box.height != c->geom.height - 2 * c->bw)) + c->isfloating ? resize(c, c->geom, 1) : arrange(c->mon); /* mark a pending resize as completed */ if (c->resize && c->resize <= c->surface.xdg->current.configure_serial) c->resize = 0; } -void -commitpopup(struct wl_listener *listener, void *data) -{ - struct wlr_surface *surface = data; - struct wlr_xdg_popup *popup = wlr_xdg_popup_try_from_wlr_surface(surface); - LayerSurface *l = NULL; - Client *c = NULL; - struct wlr_box box; - int type = -1; - - if (!popup->base->initial_commit) - return; - - type = toplevel_from_wlr_surface(popup->base->surface, &c, &l); - if (!popup->parent || type < 0) - return; - popup->base->surface->data = wlr_scene_xdg_surface_create( - popup->parent->data, popup->base); - if ((l && !l->mon) || (c && !c->mon)) { - wlr_xdg_popup_destroy(popup); - return; - } - box = type == LayerShell ? l->mon->m : c->mon->w; - box.x -= (type == LayerShell ? l->scene->node.x : c->geom.x); - box.y -= (type == LayerShell ? l->scene->node.y : c->geom.y); - wlr_xdg_popup_unconstrain_from_box(popup, &box); - wl_list_remove(&listener->link); - free(listener); -} - void createdecoration(struct wl_listener *listener, void *data) { - struct wlr_xdg_toplevel_decoration_v1 *deco = data; - Client *c = deco->toplevel->base->data; - c->decoration = deco; - - LISTEN(&deco->events.request_mode, &c->set_decoration_mode, requestdecorationmode); - LISTEN(&deco->events.destroy, &c->destroy_decoration, destroydecoration); - - requestdecorationmode(&c->set_decoration_mode, deco); + struct wlr_xdg_toplevel_decoration_v1 *dec = data; + wlr_xdg_toplevel_decoration_v1_set_mode(dec, WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); } void createidleinhibitor(struct wl_listener *listener, void *data) { struct wlr_idle_inhibitor_v1 *idle_inhibitor = data; - LISTEN_STATIC(&idle_inhibitor->events.destroy, destroyidleinhibitor); + wl_signal_add(&idle_inhibitor->events.destroy, &idle_inhibitor_destroy); checkidleinhibitor(NULL); } @@ -940,80 +789,79 @@ createidleinhibitor(struct wl_listener *listener, void *data) void createkeyboard(struct wlr_keyboard *keyboard) { - /* Set the keymap to match the group keymap */ - wlr_keyboard_set_keymap(keyboard, kb_group->wlr_group->keyboard.keymap); - - /* Add the new keyboard to the group */ - wlr_keyboard_group_add_keyboard(kb_group->wlr_group, keyboard); -} - -KeyboardGroup * -createkeyboardgroup(void) -{ - KeyboardGroup *group = ecalloc(1, sizeof(*group)); struct xkb_context *context; struct xkb_keymap *keymap; + Keyboard *kb = keyboard->data = ecalloc(1, sizeof(*kb)); + kb->wlr_keyboard = keyboard; - group->wlr_group = wlr_keyboard_group_create(); - group->wlr_group->data = group; - - /* Prepare an XKB keymap and assign it to the keyboard group. */ + /* Prepare an XKB keymap and assign it to the keyboard. */ context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - if (!(keymap = xkb_keymap_new_from_names(context, &xkb_rules, - XKB_KEYMAP_COMPILE_NO_FLAGS))) - die("failed to compile keymap"); + keymap = xkb_keymap_new_from_names(context, &xkb_rules, + XKB_KEYMAP_COMPILE_NO_FLAGS); - wlr_keyboard_set_keymap(&group->wlr_group->keyboard, keymap); + wlr_keyboard_set_keymap(keyboard, keymap); xkb_keymap_unref(keymap); xkb_context_unref(context); + wlr_keyboard_set_repeat_info(keyboard, repeat_rate, repeat_delay); - wlr_keyboard_set_repeat_info(&group->wlr_group->keyboard, repeat_rate, repeat_delay); + /* Here we set up listeners for keyboard events. */ + LISTEN(&keyboard->events.modifiers, &kb->modifiers, keypressmod); + LISTEN(&keyboard->events.key, &kb->key, keypress); + LISTEN(&keyboard->base.events.destroy, &kb->destroy, cleanupkeyboard); - /* Set up listeners for keyboard events */ - LISTEN(&group->wlr_group->keyboard.events.key, &group->key, keypress); - LISTEN(&group->wlr_group->keyboard.events.modifiers, &group->modifiers, keypressmod); + wlr_seat_set_keyboard(seat, keyboard); - group->key_repeat_source = wl_event_loop_add_timer(event_loop, keyrepeat, group); - - /* A seat can only have one keyboard, but this is a limitation of the - * Wayland protocol - not wlroots. We assign all connected keyboards to the - * same wlr_keyboard_group, which provides a single wlr_keyboard interface for - * all of them. Set this combined wlr_keyboard as the seat keyboard. - */ - wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard); - return group; + /* And add the keyboard to our list of keyboards */ + wl_list_insert(&keyboards, &kb->link); } void createlayersurface(struct wl_listener *listener, void *data) { - struct wlr_layer_surface_v1 *layer_surface = data; - LayerSurface *l; - struct wlr_surface *surface = layer_surface->surface; - struct wlr_scene_tree *scene_layer = layers[layermap[layer_surface->pending.layer]]; + struct wlr_layer_surface_v1 *wlr_layer_surface = data; + LayerSurface *layersurface; + struct wlr_layer_surface_v1_state old_state; - if (!layer_surface->output - && !(layer_surface->output = selmon ? selmon->wlr_output : NULL)) { - wlr_layer_surface_v1_destroy(layer_surface); - return; - } + if (!wlr_layer_surface->output) + wlr_layer_surface->output = selmon ? selmon->wlr_output : NULL; - l = layer_surface->data = ecalloc(1, sizeof(*l)); - l->type = LayerShell; - LISTEN(&surface->events.commit, &l->surface_commit, commitlayersurfacenotify); - LISTEN(&surface->events.unmap, &l->unmap, unmaplayersurfacenotify); - LISTEN(&layer_surface->events.destroy, &l->destroy, destroylayersurfacenotify); + if (!wlr_layer_surface->output) + wlr_layer_surface_v1_destroy(wlr_layer_surface); - l->layer_surface = layer_surface; - l->mon = layer_surface->output->data; - l->scene_layer = wlr_scene_layer_surface_v1_create(scene_layer, layer_surface); - l->scene = l->scene_layer->tree; - l->popups = surface->data = wlr_scene_tree_create(layer_surface->current.layer - < ZWLR_LAYER_SHELL_V1_LAYER_TOP ? layers[LyrTop] : scene_layer); - l->scene->node.data = l->popups->node.data = l; + layersurface = ecalloc(1, sizeof(LayerSurface)); + layersurface->type = LayerShell; + LISTEN(&wlr_layer_surface->surface->events.commit, + &layersurface->surface_commit, commitlayersurfacenotify); + LISTEN(&wlr_layer_surface->events.destroy, &layersurface->destroy, + destroylayersurfacenotify); + LISTEN(&wlr_layer_surface->events.map, &layersurface->map, + maplayersurfacenotify); + LISTEN(&wlr_layer_surface->events.unmap, &layersurface->unmap, + unmaplayersurfacenotify); - wl_list_insert(&l->mon->layers[layer_surface->pending.layer],&l->link); - wlr_surface_send_enter(surface, layer_surface->output); + layersurface->layer_surface = wlr_layer_surface; + layersurface->mon = wlr_layer_surface->output->data; + wlr_layer_surface->data = layersurface; + + layersurface->scene_layer = wlr_scene_layer_surface_v1_create( + layers[wlr_layer_surface->pending.layer], wlr_layer_surface); + layersurface->scene = layersurface->scene_layer->tree; + layersurface->popups = wlr_layer_surface->surface->data = + wlr_scene_tree_create(layers[wlr_layer_surface->pending.layer]); + + layersurface->scene->node.data = layersurface; + + wl_list_insert(&layersurface->mon->layers[wlr_layer_surface->pending.layer], + &layersurface->link); + + /* Temporarily set the layer's current state to pending + * so that we can easily arrange it + */ + old_state = wlr_layer_surface->current; + wlr_layer_surface->current = wlr_layer_surface->pending; + layersurface->mapped = 1; + arrangelayers(layersurface->mon); + wlr_layer_surface->current = old_state; } void @@ -1022,8 +870,8 @@ createlocksurface(struct wl_listener *listener, void *data) SessionLock *lock = wl_container_of(listener, lock, new_surface); struct wlr_session_lock_surface_v1 *lock_surface = data; Monitor *m = lock_surface->output->data; - struct wlr_scene_tree *scene_tree = lock_surface->surface->data - = wlr_scene_subsurface_tree_create(lock->scene, lock_surface->surface); + struct wlr_scene_tree *scene_tree = lock_surface->surface->data = + wlr_scene_subsurface_tree_create(lock->scene, lock_surface->surface); m->lock_surface = lock_surface; wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y); @@ -1043,32 +891,23 @@ createmon(struct wl_listener *listener, void *data) struct wlr_output *wlr_output = data; const MonitorRule *r; size_t i; - struct wlr_output_state state; - Monitor *m; - - if (!wlr_output_init_render(wlr_output, alloc, drw)) - return; - - m = wlr_output->data = ecalloc(1, sizeof(*m)); + Monitor *m = wlr_output->data = ecalloc(1, sizeof(*m)); m->wlr_output = wlr_output; + wlr_output_init_render(wlr_output, alloc, drw); + + /* Initialize monitor state using configured rules */ for (i = 0; i < LENGTH(m->layers); i++) wl_list_init(&m->layers[i]); - - wlr_output_state_init(&state); - /* Initialize monitor state using configured rules */ m->tagset[0] = m->tagset[1] = 1; for (r = monrules; r < END(monrules); r++) { if (!r->name || strstr(wlr_output->name, r->name)) { - m->m.x = r->x; - m->m.y = r->y; m->mfact = r->mfact; m->nmaster = r->nmaster; - m->lt[0] = r->lt; - m->lt[1] = &layouts[LENGTH(layouts) > 1 && r->lt != &layouts[1]]; - strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol)); - wlr_output_state_set_scale(&state, r->scale); - wlr_output_state_set_transform(&state, r->rr); + wlr_output_set_scale(wlr_output, r->scale); + wlr_xcursor_manager_load(cursor_mgr, r->scale); + m->lt[0] = m->lt[1] = r->lt; + wlr_output_set_transform(wlr_output, r->rr); break; } } @@ -1077,16 +916,20 @@ createmon(struct wl_listener *listener, void *data) * monitor supports only a specific set of modes. We just pick the * monitor's preferred mode; a more sophisticated compositor would let * the user configure it. */ - wlr_output_state_set_mode(&state, wlr_output_preferred_mode(wlr_output)); + wlr_output_set_mode(wlr_output, wlr_output_preferred_mode(wlr_output)); /* Set up event listeners */ LISTEN(&wlr_output->events.frame, &m->frame, rendermon); LISTEN(&wlr_output->events.destroy, &m->destroy, cleanupmon); - LISTEN(&wlr_output->events.request_state, &m->request_state, requestmonstate); - wlr_output_state_set_enabled(&state, 1); - wlr_output_commit_state(wlr_output, &state); - wlr_output_state_finish(&state); + wlr_output_enable(wlr_output, 1); + if (!wlr_output_commit(wlr_output)) + return; + + /* Try to enable adaptive sync, note that not all monitors support it. + * wlr_output_commit() will deactivate it in case it cannot be enabled */ + wlr_output_enable_adaptive_sync(wlr_output, 1); + wlr_output_commit(wlr_output); wl_list_insert(&mons, &m->link); printstatus(); @@ -1103,119 +946,108 @@ createmon(struct wl_listener *listener, void *data) m->fullscreen_bg = wlr_scene_rect_create(layers[LyrFS], 0, 0, fullscreen_bg); wlr_scene_node_set_enabled(&m->fullscreen_bg->node, 0); - /* Adds this to the output layout in the order it was configured. + /* Adds this to the output layout in the order it was configured in. * * The output layout utility automatically adds a wl_output global to the * display, which Wayland clients can see to find out information about the * output (such as DPI, scale factor, manufacturer, etc). */ m->scene_output = wlr_scene_output_create(scene, wlr_output); - if (m->m.x == -1 && m->m.y == -1) - wlr_output_layout_add_auto(output_layout, wlr_output); - else - wlr_output_layout_add(output_layout, wlr_output, m->m.x, m->m.y); + wlr_output_layout_add_auto(output_layout, wlr_output); } void createnotify(struct wl_listener *listener, void *data) { - /* This event is raised when a client creates a new toplevel (application window). */ - struct wlr_xdg_toplevel *toplevel = data; + /* This event is raised when wlr_xdg_shell receives a new xdg surface from a + * client, either a toplevel (application window) or popup, + * or when wlr_layer_shell receives a new popup from a layer. + * If you want to do something tricky with popups you should check if + * its parent is wlr_xdg_shell or wlr_layer_shell */ + struct wlr_xdg_surface *xdg_surface = data; Client *c = NULL; + LayerSurface *l = NULL; + + if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { + struct wlr_box box; + int type = toplevel_from_wlr_surface(xdg_surface->surface, &c, &l); + if (!xdg_surface->popup->parent || type < 0) + return; + xdg_surface->surface->data = wlr_scene_xdg_surface_create( + xdg_surface->popup->parent->data, xdg_surface); + if ((l && !l->mon) || (c && !c->mon)) + return; + box = type == LayerShell ? l->mon->m : c->mon->w; + box.x -= (type == LayerShell ? l->geom.x : c->geom.x); + box.y -= (type == LayerShell ? l->geom.y : c->geom.y); + wlr_xdg_popup_unconstrain_from_box(xdg_surface->popup, &box); + return; + } else if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_NONE) + return; /* Allocate a Client for this surface */ - c = toplevel->base->data = ecalloc(1, sizeof(*c)); - c->surface.xdg = toplevel->base; + c = xdg_surface->data = ecalloc(1, sizeof(*c)); + c->surface.xdg = xdg_surface; c->bw = borderpx; - LISTEN(&toplevel->base->surface->events.commit, &c->commit, commitnotify); - LISTEN(&toplevel->base->surface->events.map, &c->map, mapnotify); - LISTEN(&toplevel->base->surface->events.unmap, &c->unmap, unmapnotify); - LISTEN(&toplevel->events.destroy, &c->destroy, destroynotify); - LISTEN(&toplevel->events.request_fullscreen, &c->fullscreen, fullscreennotify); - LISTEN(&toplevel->events.request_maximize, &c->maximize, maximizenotify); - LISTEN(&toplevel->events.set_title, &c->set_title, updatetitle); + LISTEN(&xdg_surface->events.map, &c->map, mapnotify); + LISTEN(&xdg_surface->events.unmap, &c->unmap, unmapnotify); + LISTEN(&xdg_surface->events.destroy, &c->destroy, destroynotify); + LISTEN(&xdg_surface->toplevel->events.set_title, &c->set_title, updatetitle); + LISTEN(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen, + fullscreennotify); + LISTEN(&xdg_surface->toplevel->events.request_maximize, &c->maximize, + maximizenotify); } void createpointer(struct wlr_pointer *pointer) { - struct libinput_device *device; - if (wlr_input_device_is_libinput(&pointer->base) - && (device = wlr_libinput_get_device_handle(&pointer->base))) { + if (wlr_input_device_is_libinput(&pointer->base)) { + struct libinput_device *libinput_device = (struct libinput_device*) + wlr_libinput_get_device_handle(&pointer->base); - if (libinput_device_config_tap_get_finger_count(device)) { - libinput_device_config_tap_set_enabled(device, tap_to_click); - libinput_device_config_tap_set_drag_enabled(device, tap_and_drag); - libinput_device_config_tap_set_drag_lock_enabled(device, drag_lock); - libinput_device_config_tap_set_button_map(device, button_map); + if (libinput_device_config_tap_get_finger_count(libinput_device)) { + libinput_device_config_tap_set_enabled(libinput_device, tap_to_click); + libinput_device_config_tap_set_drag_enabled(libinput_device, tap_and_drag); + libinput_device_config_tap_set_drag_lock_enabled(libinput_device, drag_lock); + libinput_device_config_tap_set_button_map(libinput_device, button_map); } - if (libinput_device_config_scroll_has_natural_scroll(device)) - libinput_device_config_scroll_set_natural_scroll_enabled(device, natural_scrolling); + if (libinput_device_config_scroll_has_natural_scroll(libinput_device)) + libinput_device_config_scroll_set_natural_scroll_enabled(libinput_device, natural_scrolling); - if (libinput_device_config_dwt_is_available(device)) - libinput_device_config_dwt_set_enabled(device, disable_while_typing); + if (libinput_device_config_dwt_is_available(libinput_device)) + libinput_device_config_dwt_set_enabled(libinput_device, disable_while_typing); - if (libinput_device_config_left_handed_is_available(device)) - libinput_device_config_left_handed_set(device, left_handed); + if (libinput_device_config_left_handed_is_available(libinput_device)) + libinput_device_config_left_handed_set(libinput_device, left_handed); - if (libinput_device_config_middle_emulation_is_available(device)) - libinput_device_config_middle_emulation_set_enabled(device, middle_button_emulation); + if (libinput_device_config_middle_emulation_is_available(libinput_device)) + libinput_device_config_middle_emulation_set_enabled(libinput_device, middle_button_emulation); - if (libinput_device_config_scroll_get_methods(device) != LIBINPUT_CONFIG_SCROLL_NO_SCROLL) - libinput_device_config_scroll_set_method(device, scroll_method); + if (libinput_device_config_scroll_get_methods(libinput_device) != LIBINPUT_CONFIG_SCROLL_NO_SCROLL) + libinput_device_config_scroll_set_method (libinput_device, scroll_method); + + if (libinput_device_config_click_get_methods(libinput_device) != LIBINPUT_CONFIG_CLICK_METHOD_NONE) + libinput_device_config_click_set_method (libinput_device, click_method); - if (libinput_device_config_click_get_methods(device) != LIBINPUT_CONFIG_CLICK_METHOD_NONE) - libinput_device_config_click_set_method(device, click_method); + if (libinput_device_config_send_events_get_modes(libinput_device)) + libinput_device_config_send_events_set_mode(libinput_device, send_events_mode); - if (libinput_device_config_send_events_get_modes(device)) - libinput_device_config_send_events_set_mode(device, send_events_mode); - - if (libinput_device_config_accel_is_available(device)) { - libinput_device_config_accel_set_profile(device, accel_profile); - libinput_device_config_accel_set_speed(device, accel_speed); + if (libinput_device_config_accel_is_available(libinput_device)) { + libinput_device_config_accel_set_profile(libinput_device, accel_profile); + libinput_device_config_accel_set_speed(libinput_device, accel_speed); } } wlr_cursor_attach_input_device(cursor, &pointer->base); } -void -createpointerconstraint(struct wl_listener *listener, void *data) -{ - PointerConstraint *pointer_constraint = ecalloc(1, sizeof(*pointer_constraint)); - pointer_constraint->constraint = data; - LISTEN(&pointer_constraint->constraint->events.destroy, - &pointer_constraint->destroy, destroypointerconstraint); -} - -void -createpopup(struct wl_listener *listener, void *data) -{ - /* This event is raised when a client (either xdg-shell or layer-shell) - * creates a new popup. */ - struct wlr_xdg_popup *popup = data; - LISTEN_STATIC(&popup->base->surface->events.commit, commitpopup); -} - -void -cursorconstrain(struct wlr_pointer_constraint_v1 *constraint) -{ - if (active_constraint == constraint) - return; - - if (active_constraint) - wlr_pointer_constraint_v1_send_deactivated(active_constraint); - - active_constraint = constraint; - wlr_pointer_constraint_v1_send_activated(constraint); -} - void cursorframe(struct wl_listener *listener, void *data) { - /* This event is forwarded by the cursor when a pointer emits a frame + /* This event is forwarded by the cursor when a pointer emits an frame * event. Frame events are sent after regular pointer events to group * multiple events together. For instance, two axis events may happen at the * same time, in which case a frame event won't be sent in between. */ @@ -1223,37 +1055,14 @@ cursorframe(struct wl_listener *listener, void *data) wlr_seat_pointer_notify_frame(seat); } -void -cursorwarptohint(void) -{ - Client *c = NULL; - double sx = active_constraint->current.cursor_hint.x; - double sy = active_constraint->current.cursor_hint.y; - - toplevel_from_wlr_surface(active_constraint->surface, &c, NULL); - if (c && active_constraint->current.cursor_hint.enabled) { - wlr_cursor_warp(cursor, NULL, sx + c->geom.x + c->bw, sy + c->geom.y + c->bw); - wlr_seat_pointer_warp(active_constraint->seat, sx, sy); - } -} - -void -destroydecoration(struct wl_listener *listener, void *data) -{ - Client *c = wl_container_of(listener, c, destroy_decoration); - - wl_list_remove(&c->destroy_decoration.link); - wl_list_remove(&c->set_decoration_mode.link); -} - void destroydragicon(struct wl_listener *listener, void *data) { + struct wlr_drag_icon *icon = data; + wlr_scene_node_destroy(icon->data); /* Focus enter isn't sent during drag, so refocus the focused node. */ focusclient(focustop(selmon), 1); - motionnotify(0, NULL, 0, 0, 0, 0); - wl_list_remove(&listener->link); - free(listener); + motionnotify(0); } void @@ -1262,22 +1071,20 @@ destroyidleinhibitor(struct wl_listener *listener, void *data) /* `data` is the wlr_surface of the idle inhibitor being destroyed, * at this point the idle inhibitor is still in the list of the manager */ checkidleinhibitor(wlr_surface_get_root_surface(data)); - wl_list_remove(&listener->link); - free(listener); } void destroylayersurfacenotify(struct wl_listener *listener, void *data) { - LayerSurface *l = wl_container_of(listener, l, destroy); + LayerSurface *layersurface = wl_container_of(listener, layersurface, destroy); - wl_list_remove(&l->link); - wl_list_remove(&l->destroy.link); - wl_list_remove(&l->unmap.link); - wl_list_remove(&l->surface_commit.link); - wlr_scene_node_destroy(&l->scene->node); - wlr_scene_node_destroy(&l->popups->node); - free(l); + wl_list_remove(&layersurface->link); + wl_list_remove(&layersurface->destroy.link); + wl_list_remove(&layersurface->map.link); + wl_list_remove(&layersurface->unmap.link); + wl_list_remove(&layersurface->surface_commit.link); + wlr_scene_node_destroy(&layersurface->scene->node); + free(layersurface); } void @@ -1290,7 +1097,7 @@ destroylock(SessionLock *lock, int unlock) wlr_scene_node_set_enabled(&locked_bg->node, 0); focusclient(focustop(selmon), 0); - motionnotify(0, NULL, 0, 0, 0, 0); + motionnotify(0); destroy: wl_list_remove(&lock->new_surface.link); @@ -1311,59 +1118,38 @@ destroylocksurface(struct wl_listener *listener, void *data) m->lock_surface = NULL; wl_list_remove(&m->destroy_lock_surface.link); - if (lock_surface->surface != seat->keyboard_state.focused_surface) - return; - - if (locked && cur_lock && !wl_list_empty(&cur_lock->surfaces)) { - surface = wl_container_of(cur_lock->surfaces.next, surface, link); - client_notify_enter(surface->surface, wlr_seat_get_keyboard(seat)); - } else if (!locked) { - focusclient(focustop(selmon), 1); - } else { - wlr_seat_keyboard_clear_focus(seat); + if (lock_surface->surface == seat->keyboard_state.focused_surface) { + if (locked && cur_lock && !wl_list_empty(&cur_lock->surfaces)) { + surface = wl_container_of(cur_lock->surfaces.next, surface, link); + client_notify_enter(surface->surface, wlr_seat_get_keyboard(seat)); + } else if (!locked) { + focusclient(focustop(selmon), 1); + } else { + wlr_seat_keyboard_clear_focus(seat); + } } } void destroynotify(struct wl_listener *listener, void *data) { - /* Called when the xdg_toplevel is destroyed. */ + /* Called when the surface is destroyed and should never be shown again. */ Client *c = wl_container_of(listener, c, destroy); + wl_list_remove(&c->map.link); + wl_list_remove(&c->unmap.link); wl_list_remove(&c->destroy.link); wl_list_remove(&c->set_title.link); wl_list_remove(&c->fullscreen.link); #ifdef XWAYLAND if (c->type != XDGShell) { - wl_list_remove(&c->activate.link); - wl_list_remove(&c->associate.link); wl_list_remove(&c->configure.link); - wl_list_remove(&c->dissociate.link); wl_list_remove(&c->set_hints.link); - } else + wl_list_remove(&c->activate.link); + } #endif - { - wl_list_remove(&c->commit.link); - wl_list_remove(&c->map.link); - wl_list_remove(&c->unmap.link); - wl_list_remove(&c->maximize.link); - } free(c); } -void -destroypointerconstraint(struct wl_listener *listener, void *data) -{ - PointerConstraint *pointer_constraint = wl_container_of(listener, pointer_constraint, destroy); - - if (active_constraint == pointer_constraint->constraint) { - cursorwarptohint(); - active_constraint = NULL; - } - - wl_list_remove(&pointer_constraint->destroy.link); - free(pointer_constraint); -} - void destroysessionlock(struct wl_listener *listener, void *data) { @@ -1372,15 +1158,10 @@ destroysessionlock(struct wl_listener *listener, void *data) } void -destroykeyboardgroup(struct wl_listener *listener, void *data) +destroysessionmgr(struct wl_listener *listener, void *data) { - KeyboardGroup *group = wl_container_of(listener, group, destroy); - wl_event_source_remove(group->key_repeat_source); - wl_list_remove(&group->key.link); - wl_list_remove(&group->modifiers.link); - wl_list_remove(&group->destroy.link); - wlr_keyboard_group_destroy(group->wlr_group); - free(group); + wl_list_remove(&session_lock_create_lock.link); + wl_list_remove(&session_lock_mgr_destroy.link); } Monitor * @@ -1403,9 +1184,7 @@ void focusclient(Client *c, int lift) { struct wlr_surface *old = seat->keyboard_state.focused_surface; - int unused_lx, unused_ly, old_client_type; - Client *old_c = NULL; - LayerSurface *old_l = NULL; + int i; if (locked) return; @@ -1417,23 +1196,19 @@ focusclient(Client *c, int lift) if (c && client_surface(c) == old) return; - if ((old_client_type = toplevel_from_wlr_surface(old, &old_c, &old_l)) == XDGShell) { - struct wlr_xdg_popup *popup, *tmp; - wl_list_for_each_safe(popup, tmp, &old_c->surface.xdg->popups, link) - wlr_xdg_popup_destroy(popup); - } - /* Put the new client atop the focus stack and select its monitor */ if (c && !client_is_unmanaged(c)) { wl_list_remove(&c->flink); wl_list_insert(&fstack, &c->flink); selmon = c->mon; c->isurgent = 0; + client_restack_surface(c); /* Don't change border color if there is an exclusive focus or we are * handling a drag operation */ if (!exclusive_focus && !seat->drag) - client_set_border_color(c, focuscolor); + for (i = 0; i < 4; i++) + wlr_scene_rect_set_color(c->border[i], focuscolor); } /* Deactivate old client if focus is changing */ @@ -1441,16 +1216,19 @@ focusclient(Client *c, int lift) /* If an overlay is focused, don't focus or activate the client, * but only update its position in fstack to render its border with focuscolor * and focus it after the overlay is closed. */ - if (old_client_type == LayerShell && wlr_scene_node_coords( - &old_l->scene->node, &unused_lx, &unused_ly) - && old_l->layer_surface->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { + Client *w = NULL; + LayerSurface *l = NULL; + int type = toplevel_from_wlr_surface(old, &w, &l); + if (type == LayerShell && l->scene->node.enabled + && l->layer_surface->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { return; - } else if (old_c && old_c == exclusive_focus && client_wants_focus(old_c)) { + } else if (w && w == exclusive_focus && client_wants_focus(w)) { return; /* Don't deactivate old client if the new one wants focus, as this causes issues with winecfg * and probably other clients */ - } else if (old_c && !client_is_unmanaged(old_c) && (!c || !client_wants_focus(c))) { - client_set_border_color(old_c, bordercolor); + } else if (w && !client_is_unmanaged(w) && (!c || !client_wants_focus(c))) { + for (i = 0; i < 4; i++) + wlr_scene_rect_set_color(w->border[i], bordercolor); client_activate_surface(old, 0); } @@ -1464,7 +1242,7 @@ focusclient(Client *c, int lift) } /* Change cursor surface */ - motionnotify(0, NULL, 0, 0, 0, 0); + motionnotify(0); /* Have a client, so focus its top-level wlr_surface */ client_notify_enter(client_surface(c), wlr_seat_get_keyboard(seat)); @@ -1477,11 +1255,10 @@ void focusmon(const Arg *arg) { int i = 0, nmons = wl_list_length(&mons); - if (nmons) { + if (nmons) do /* don't switch to disabled mons */ selmon = dirtomon(arg->i); while (!selmon->wlr_output->enabled && i++ < nmons); - } focusclient(focustop(selmon), 1); } @@ -1490,38 +1267,37 @@ focusstack(const Arg *arg) { /* Focus the next or previous client (in tiling order) on selmon */ Client *c, *sel = focustop(selmon); - if (!sel || (sel->isfullscreen && !client_has_children(sel))) + if (!sel || sel->isfullscreen) return; if (arg->i > 0) { wl_list_for_each(c, &sel->link, link) { if (&c->link == &clients) - continue; /* wrap past the sentinel node */ + continue; /* wrap past the sentinel node */ if (VISIBLEON(c, selmon)) - break; /* found it */ + break; /* found it */ } } else { wl_list_for_each_reverse(c, &sel->link, link) { if (&c->link == &clients) - continue; /* wrap past the sentinel node */ + continue; /* wrap past the sentinel node */ if (VISIBLEON(c, selmon)) - break; /* found it */ + break; /* found it */ } } /* If only one client is visible on selmon, then c == sel */ focusclient(c, 1); } -/* We probably should change the name of this: it sounds like it +/* We probably should change the name of this, it sounds like * will focus the topmost client of this mon, when actually will * only return that client */ Client * focustop(Monitor *m) { Client *c; - wl_list_for_each(c, &fstack, flink) { + wl_list_for_each(c, &fstack, flink) if (VISIBLEON(c, m)) return c; - } return NULL; } @@ -1532,40 +1308,6 @@ fullscreennotify(struct wl_listener *listener, void *data) setfullscreen(c, client_wants_fullscreen(c)); } -void -gpureset(struct wl_listener *listener, void *data) -{ - struct wlr_renderer *old_drw = drw; - struct wlr_allocator *old_alloc = alloc; - struct Monitor *m; - if (!(drw = wlr_renderer_autocreate(backend))) - die("couldn't recreate renderer"); - - if (!(alloc = wlr_allocator_autocreate(backend, drw))) - die("couldn't recreate allocator"); - - wl_list_remove(&gpu_reset.link); - wl_signal_add(&drw->events.lost, &gpu_reset); - - wlr_compositor_set_renderer(compositor, drw); - - wl_list_for_each(m, &mons, link) { - wlr_output_init_render(m->wlr_output, alloc, drw); - } - - wlr_allocator_destroy(old_alloc); - wlr_renderer_destroy(old_drw); -} - -void -handlesig(int signo) -{ - if (signo == SIGCHLD) - while (waitpid(-1, NULL, WNOHANG) > 0); - else if (signo == SIGINT || signo == SIGTERM) - quit(NULL); -} - void incnmaster(const Arg *arg) { @@ -1600,7 +1342,7 @@ inputdevice(struct wl_listener *listener, void *data) * there are no pointer devices, so we always include that capability. */ /* TODO do we actually require a cursor? */ caps = WL_SEAT_CAPABILITY_POINTER; - if (!wl_list_empty(&kb_group->wlr_group->devices)) + if (!wl_list_empty(&keyboards)) caps |= WL_SEAT_CAPABILITY_KEYBOARD; wlr_seat_set_capabilities(seat, caps); } @@ -1613,15 +1355,16 @@ keybinding(uint32_t mods, xkb_keysym_t sym) * processing keys, rather than passing them on to the client for its own * processing. */ + int handled = 0; const Key *k; for (k = keys; k < END(keys); k++) { - if (CLEANMASK(mods) == CLEANMASK(k->mod) - && sym == k->keysym && k->func) { + if (CLEANMASK(mods) == CLEANMASK(k->mod) && + sym == k->keysym && k->func) { k->func(&k->arg); - return 1; + handled = 1; } } - return 0; + return handled; } void @@ -1629,7 +1372,7 @@ keypress(struct wl_listener *listener, void *data) { int i; /* This event is raised when a key is pressed or released. */ - KeyboardGroup *group = wl_container_of(listener, group, key); + Keyboard *kb = wl_container_of(listener, kb, key); struct wlr_keyboard_key_event *event = data; /* Translate libinput keycode -> xkbcommon */ @@ -1637,38 +1380,26 @@ keypress(struct wl_listener *listener, void *data) /* Get a list of keysyms based on the keymap for this keyboard */ const xkb_keysym_t *syms; int nsyms = xkb_state_key_get_syms( - group->wlr_group->keyboard.xkb_state, keycode, &syms); + kb->wlr_keyboard->xkb_state, keycode, &syms); int handled = 0; - uint32_t mods = wlr_keyboard_get_modifiers(&group->wlr_group->keyboard); + uint32_t mods = wlr_keyboard_get_modifiers(kb->wlr_keyboard); - wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); + IDLE_NOTIFY_ACTIVITY; /* On _press_ if there is no active screen locker, * attempt to process a compositor keybinding. */ - if (!locked && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { + if (!locked && !input_inhibit_mgr->active_inhibitor + && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) for (i = 0; i < nsyms; i++) handled = keybinding(mods, syms[i]) || handled; - } - if (handled && group->wlr_group->keyboard.repeat_info.delay > 0) { - group->mods = mods; - group->keysyms = syms; - group->nsyms = nsyms; - wl_event_source_timer_update(group->key_repeat_source, - group->wlr_group->keyboard.repeat_info.delay); - } else { - group->nsyms = 0; - wl_event_source_timer_update(group->key_repeat_source, 0); - } - - if (handled) - return; - - wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard); - /* Pass unhandled keycodes along to the client. */ - wlr_seat_keyboard_notify_key(seat, event->time_msec, + if (!handled) { + /* Pass unhandled keycodes along to the client. */ + wlr_seat_set_keyboard(seat, kb->wlr_keyboard); + wlr_seat_keyboard_notify_key(seat, event->time_msec, event->keycode, event->state); + } } void @@ -1676,29 +1407,17 @@ keypressmod(struct wl_listener *listener, void *data) { /* This event is raised when a modifier key, such as shift or alt, is * pressed. We simply communicate this to the client. */ - KeyboardGroup *group = wl_container_of(listener, group, modifiers); - - wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard); + Keyboard *kb = wl_container_of(listener, kb, modifiers); + /* + * A seat can only have one keyboard, but this is a limitation of the + * Wayland protocol - not wlroots. We assign all connected keyboards to the + * same seat. You can swap out the underlying wlr_keyboard like this and + * wlr_seat handles this transparently. + */ + wlr_seat_set_keyboard(seat, kb->wlr_keyboard); /* Send modifiers to the client. */ wlr_seat_keyboard_notify_modifiers(seat, - &group->wlr_group->keyboard.modifiers); -} - -int -keyrepeat(void *data) -{ - KeyboardGroup *group = data; - int i; - if (!group->nsyms || group->wlr_group->keyboard.repeat_info.rate <= 0) - return 0; - - wl_event_source_timer_update(group->key_repeat_source, - 1000 / group->wlr_group->keyboard.repeat_info.rate); - - for (i = 0; i < group->nsyms; i++) - keybinding(group->mods, group->keysyms[i]); - - return 0; + &kb->wlr_keyboard->modifiers); } void @@ -1719,12 +1438,13 @@ locksession(struct wl_listener *listener, void *data) wlr_session_lock_v1_destroy(session_lock); return; } - lock = session_lock->data = ecalloc(1, sizeof(*lock)); + lock = ecalloc(1, sizeof(*lock)); focusclient(NULL, 0); lock->scene = wlr_scene_tree_create(layers[LyrBlock]); cur_lock = lock->lock = session_lock; locked = 1; + session_lock->data = lock; LISTEN(&session_lock->events.new_surface, &lock->new_surface, createlocksurface); LISTEN(&session_lock->events.destroy, &lock->destroy, destroysessionlock); @@ -1733,47 +1453,60 @@ locksession(struct wl_listener *listener, void *data) wlr_session_lock_v1_send_locked(session_lock); } +void +maplayersurfacenotify(struct wl_listener *listener, void *data) +{ + LayerSurface *l = wl_container_of(listener, l, map); + wlr_surface_send_enter(l->layer_surface->surface, l->mon->wlr_output); + motionnotify(0); +} + void mapnotify(struct wl_listener *listener, void *data) { /* Called when the surface is mapped, or ready to display on-screen. */ - Client *p = NULL; - Client *w, *c = wl_container_of(listener, c, map); + Client *p, *w, *c = wl_container_of(listener, c, map); Monitor *m; int i; /* Create scene tree for this client and its border */ - c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LyrTile]); - /* Enabled later by a call to arrange() */ - wlr_scene_node_set_enabled(&c->scene->node, client_is_unmanaged(c)); + c->scene = wlr_scene_tree_create(layers[LyrTile]); + wlr_scene_node_set_enabled(&c->scene->node, c->type != XDGShell); c->scene_surface = c->type == XDGShell ? wlr_scene_xdg_surface_create(c->scene, c->surface.xdg) : wlr_scene_subsurface_tree_create(c->scene, client_surface(c)); + if (client_surface(c)) { + client_surface(c)->data = c->scene; + /* Ideally we should do this in createnotify{,x11} but at that moment + * wlr_xwayland_surface doesn't have wlr_surface yet. */ + LISTEN(&client_surface(c)->events.commit, &c->commit, commitnotify); + } c->scene->node.data = c->scene_surface->node.data = c; - client_get_geometry(c, &c->geom); - +#ifdef XWAYLAND /* Handle unmanaged clients first so we can return prior create borders */ if (client_is_unmanaged(c)) { + client_get_geometry(c, &c->geom); /* Unmanaged clients always are floating */ wlr_scene_node_reparent(&c->scene->node, layers[LyrFloat]); - wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y); - client_set_size(c, c->geom.width, c->geom.height); + wlr_scene_node_set_position(&c->scene->node, c->geom.x + borderpx, + c->geom.y + borderpx); if (client_wants_focus(c)) { focusclient(c, 1); exclusive_focus = c; } goto unset_fullscreen; } +#endif for (i = 0; i < 4; i++) { - c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, - c->isurgent ? urgentcolor : bordercolor); + c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, bordercolor); c->border[i]->node.data = c; } /* Initialize client geometry with room for border */ client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); + client_get_geometry(c, &c->geom); c->geom.width += 2 * c->bw; c->geom.height += 2 * c->bw; @@ -1783,10 +1516,12 @@ mapnotify(struct wl_listener *listener, void *data) /* Set initial monitor, tags, floating status, and focus: * we always consider floating, clients that have parent and thus - * we set the same tags and monitor as its parent. - * If there is no parent, apply rules */ - if ((p = client_get_parent(c))) { + * we set the same tags and monitor than its parent, if not + * try to apply rules for them */ + /* TODO: https://github.com/djpohly/dwl/pull/334#issuecomment-1330166324 */ + if (c->type == XDGShell && (p = client_get_parent(c))) { c->isfloating = 1; + wlr_scene_node_reparent(&c->scene->node, layers[LyrFloat]); setmon(c, p->mon, p->tags); } else { applyrules(c); @@ -1795,10 +1530,9 @@ mapnotify(struct wl_listener *listener, void *data) unset_fullscreen: m = c->mon ? c->mon : xytomon(c->geom.x, c->geom.y); - wl_list_for_each(w, &clients, link) { - if (w != c && w != p && w->isfullscreen && m == w->mon && (w->tags & c->tags)) + wl_list_for_each(w, &clients, link) + if (w != c && w->isfullscreen && m == w->mon && (w->tags & c->tags)) setfullscreen(w, 0); - } } void @@ -1808,31 +1542,21 @@ maximizenotify(struct wl_listener *listener, void *data) * typically because the user clicked on the maximize button on * client-side decorations. dwl doesn't support maximization, but * to conform to xdg-shell protocol we still must send a configure. - * Since xdg-shell protocol v5 we should ignore request of unsupported - * capabilities, just schedule a empty configure when the client uses <5 - * protocol version * wlr_xdg_surface_schedule_configure() is used to send an empty reply. */ Client *c = wl_container_of(listener, c, maximize); - if (c->surface.xdg->initialized - && wl_resource_get_version(c->surface.xdg->toplevel->resource) - < XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) - wlr_xdg_surface_schedule_configure(c->surface.xdg); + wlr_xdg_surface_schedule_configure(c->surface.xdg); } void monocle(Monitor *m) { Client *c; - int n = 0; wl_list_for_each(c, &clients, link) { if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) continue; resize(c, m->w, 0); - n++; } - if (n) - snprintf(m->ltsymbol, LENGTH(m->ltsymbol), "[%d]", n); if ((c = focustop(m))) wlr_scene_node_raise_to_top(&c->scene->node); } @@ -1844,94 +1568,66 @@ motionabsolute(struct wl_listener *listener, void *data) * motion event, from 0..1 on each axis. This happens, for example, when * wlroots is running under a Wayland window rather than KMS+DRM, and you * move the mouse over the window. You could enter the window from any edge, - * so we have to warp the mouse there. Also, some hardware emits these events. */ + * so we have to warp the mouse there. There is also some hardware which + * emits these events. */ struct wlr_pointer_motion_absolute_event *event = data; - double lx, ly, dx, dy; - - if (!event->time_msec) /* this is 0 with virtual pointers */ - wlr_cursor_warp_absolute(cursor, &event->pointer->base, event->x, event->y); - - wlr_cursor_absolute_to_layout_coords(cursor, &event->pointer->base, event->x, event->y, &lx, &ly); - dx = lx - cursor->x; - dy = ly - cursor->y; - motionnotify(event->time_msec, &event->pointer->base, dx, dy, dx, dy); + wlr_cursor_warp_absolute(cursor, &event->pointer->base, event->x, event->y); + motionnotify(event->time_msec); } void -motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy, - double dx_unaccel, double dy_unaccel) +motionnotify(uint32_t time) { - double sx = 0, sy = 0, sx_confined, sy_confined; + double sx = 0, sy = 0; Client *c = NULL, *w = NULL; LayerSurface *l = NULL; + int type; struct wlr_surface *surface = NULL; - struct wlr_pointer_constraint_v1 *constraint; - - /* Find the client under the pointer and send the event along. */ - xytonode(cursor->x, cursor->y, &surface, &c, NULL, &sx, &sy); - - if (cursor_mode == CurPressed && !seat->drag - && surface != seat->pointer_state.focused_surface - && toplevel_from_wlr_surface(seat->pointer_state.focused_surface, &w, &l) >= 0) { - c = w; - surface = seat->pointer_state.focused_surface; - sx = cursor->x - (l ? l->scene->node.x : w->geom.x); - sy = cursor->y - (l ? l->scene->node.y : w->geom.y); - } + struct wlr_drag_icon *icon; /* time is 0 in internal calls meant to restore pointer focus. */ if (time) { - wlr_relative_pointer_manager_v1_send_relative_motion( - relative_pointer_mgr, seat, (uint64_t)time * 1000, - dx, dy, dx_unaccel, dy_unaccel); - - wl_list_for_each(constraint, &pointer_constraints->constraints, link) - cursorconstrain(constraint); - - if (active_constraint && cursor_mode != CurResize && cursor_mode != CurMove) { - toplevel_from_wlr_surface(active_constraint->surface, &c, NULL); - if (c && active_constraint->surface == seat->pointer_state.focused_surface) { - sx = cursor->x - c->geom.x - c->bw; - sy = cursor->y - c->geom.y - c->bw; - if (wlr_region_confine(&active_constraint->region, sx, sy, - sx + dx, sy + dy, &sx_confined, &sy_confined)) { - dx = sx_confined - sx; - dy = sy_confined - sy; - } - - if (active_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) - return; - } - } - - wlr_cursor_move(cursor, device, dx, dy); - wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); + IDLE_NOTIFY_ACTIVITY; /* Update selmon (even while dragging a window) */ if (sloppyfocus) selmon = xytomon(cursor->x, cursor->y); } - /* Update drag icon's position */ - wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y)); - + /* Update drag icon's position if any */ + if (seat->drag && (icon = seat->drag->icon)) + wlr_scene_node_set_position(icon->data, cursor->x + icon->surface->sx, + cursor->y + icon->surface->sy); /* If we are currently grabbing the mouse, handle and return */ if (cursor_mode == CurMove) { /* Move the grabbed client to the new position. */ - resize(grabc, (struct wlr_box){.x = (int)round(cursor->x) - grabcx, .y = (int)round(cursor->y) - grabcy, + resize(grabc, (struct wlr_box){.x = cursor->x - grabcx, .y = cursor->y - grabcy, .width = grabc->geom.width, .height = grabc->geom.height}, 1); return; } else if (cursor_mode == CurResize) { resize(grabc, (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y, - .width = (int)round(cursor->x) - grabc->geom.x, .height = (int)round(cursor->y) - grabc->geom.y}, 1); + .width = cursor->x - grabc->geom.x, .height = cursor->y - grabc->geom.y}, 1); return; } + /* Find the client under the pointer and send the event along. */ + xytonode(cursor->x, cursor->y, &surface, &c, NULL, &sx, &sy); + + if (cursor_mode == CurPressed && !seat->drag) { + if ((type = toplevel_from_wlr_surface( + seat->pointer_state.focused_surface, &w, &l)) >= 0) { + c = w; + surface = seat->pointer_state.focused_surface; + sx = cursor->x - (type == LayerShell ? l->geom.x : w->geom.x); + sy = cursor->y - (type == LayerShell ? l->geom.y : w->geom.y); + } + } + /* If there's no client surface under the cursor, set the cursor image to a * default. This is what makes the cursor image appear when you move it * off of a client or over its border. */ - if (!surface && !seat->drag) - wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); + if (!surface && !seat->drag && (!cursor_image || strcmp(cursor_image, "left_ptr"))) + wlr_xcursor_manager_set_cursor_image(cursor_mgr, (cursor_image = "left_ptr"), cursor); pointerfocus(c, surface, sx, sy, time); } @@ -1947,8 +1643,8 @@ motionrelative(struct wl_listener *listener, void *data) * special configuration applied for the specific input device which * generated the event. You can pass NULL for the device if you want to move * the cursor around without any input. */ - motionnotify(event->time_msec, &event->pointer->base, event->delta_x, event->delta_y, - event->unaccel_dx, event->unaccel_dy); + wlr_cursor_move(cursor, &event->pointer->base, event->delta_x, event->delta_y); + motionnotify(event->time_msec); } void @@ -1964,9 +1660,9 @@ moveresize(const Arg *arg) setfloating(grabc, 1); switch (cursor_mode = arg->ui) { case CurMove: - grabcx = (int)round(cursor->x) - grabc->geom.x; - grabcy = (int)round(cursor->y) - grabc->geom.y; - wlr_cursor_set_xcursor(cursor, cursor_mgr, "all-scroll"); + grabcx = cursor->x - grabc->geom.x; + grabcy = cursor->y - grabc->geom.y; + wlr_xcursor_manager_set_cursor_image(cursor_mgr, (cursor_image = "fleur"), cursor); break; case CurResize: /* Doesn't work for X11 output - the next absolute motion event @@ -1974,7 +1670,8 @@ moveresize(const Arg *arg) wlr_cursor_warp_closest(cursor, NULL, grabc->geom.x + grabc->geom.width, grabc->geom.y + grabc->geom.height); - wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize"); + wlr_xcursor_manager_set_cursor_image(cursor_mgr, + (cursor_image = "bottom_right_corner"), cursor); break; } } @@ -1991,7 +1688,7 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test) { /* * Called when a client such as wlr-randr requests a change in output - * configuration. This is only one way that the layout can be changed, + * configuration. This is only one way that the layout can be changed, * so any Monitor information should be updated by updatemons() after an * output_layout.change event, not here. */ @@ -2001,42 +1698,35 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test) wl_list_for_each(config_head, &config->heads, link) { struct wlr_output *wlr_output = config_head->state.output; Monitor *m = wlr_output->data; - struct wlr_output_state state; - /* Ensure displays previously disabled by wlr-output-power-management-v1 - * are properly handled*/ - m->asleep = 0; - - wlr_output_state_init(&state); - wlr_output_state_set_enabled(&state, config_head->state.enabled); + wlr_output_enable(wlr_output, config_head->state.enabled); if (!config_head->state.enabled) goto apply_or_test; - if (config_head->state.mode) - wlr_output_state_set_mode(&state, config_head->state.mode); + wlr_output_set_mode(wlr_output, config_head->state.mode); else - wlr_output_state_set_custom_mode(&state, + wlr_output_set_custom_mode(wlr_output, config_head->state.custom_mode.width, config_head->state.custom_mode.height, config_head->state.custom_mode.refresh); - wlr_output_state_set_transform(&state, config_head->state.transform); - wlr_output_state_set_scale(&state, config_head->state.scale); - wlr_output_state_set_adaptive_sync_enabled(&state, + /* Don't move monitors if position wouldn't change, this to avoid + * wlroots marking the output as manually configured */ + if (m->m.x != config_head->state.x || m->m.y != config_head->state.y) + wlr_output_layout_move(output_layout, wlr_output, + config_head->state.x, config_head->state.y); + wlr_output_set_transform(wlr_output, config_head->state.transform); + wlr_output_set_scale(wlr_output, config_head->state.scale); + wlr_output_enable_adaptive_sync(wlr_output, config_head->state.adaptive_sync_enabled); apply_or_test: - ok &= test ? wlr_output_test_state(wlr_output, &state) - : wlr_output_commit_state(wlr_output, &state); - - /* Don't move monitors if position wouldn't change. This avoids - * wlroots marking the output as manually configured. - * wlr_output_layout_add does not like disabled outputs */ - if (!test && wlr_output->enabled && (m->m.x != config_head->state.x || m->m.y != config_head->state.y)) - wlr_output_layout_add(output_layout, wlr_output, - config_head->state.x, config_head->state.y); - - wlr_output_state_finish(&state); + if (test) { + ok &= wlr_output_test(wlr_output); + wlr_output_rollback(wlr_output); + } else { + ok &= wlr_output_commit(wlr_output); + } } if (ok) @@ -2045,7 +1735,7 @@ apply_or_test: wlr_output_configuration_v1_send_failed(config); wlr_output_configuration_v1_destroy(config); - /* https://codeberg.org/dwl/dwl/issues/577 */ + /* TODO: use a wrapper function? */ updatemons(NULL, NULL); } @@ -2061,9 +1751,9 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, uint32_t time) { struct timespec now; + int internal_call = !time; - if (surface != seat->pointer_state.focused_surface && - sloppyfocus && time && c && !client_is_unmanaged(c)) + if (sloppyfocus && !internal_call && c && !client_is_unmanaged(c)) focusclient(c, 0); /* If surface is NULL, clear pointer focus */ @@ -2072,7 +1762,7 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, return; } - if (!time) { + if (internal_call) { clock_gettime(CLOCK_MONOTONIC, &now); time = now.tv_sec * 1000 + now.tv_nsec / 1000000; } @@ -2089,7 +1779,7 @@ printstatus(void) { Monitor *m = NULL; Client *c; - uint32_t occ, urg, sel; + unsigned int occ, urg, sel; wl_list_for_each(m, &mons, link) { occ = urg = 0; @@ -2102,50 +1792,36 @@ printstatus(void) } if ((c = focustop(m))) { printf("%s title %s\n", m->wlr_output->name, client_get_title(c)); - printf("%s appid %s\n", m->wlr_output->name, client_get_appid(c)); - printf("%s fullscreen %d\n", m->wlr_output->name, c->isfullscreen); - printf("%s floating %d\n", m->wlr_output->name, c->isfloating); + printf("%s fullscreen %u\n", m->wlr_output->name, c->isfullscreen); + printf("%s floating %u\n", m->wlr_output->name, c->isfloating); sel = c->tags; } else { printf("%s title \n", m->wlr_output->name); - printf("%s appid \n", m->wlr_output->name); printf("%s fullscreen \n", m->wlr_output->name); printf("%s floating \n", m->wlr_output->name); sel = 0; } printf("%s selmon %u\n", m->wlr_output->name, m == selmon); - printf("%s tags %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32"\n", - m->wlr_output->name, occ, m->tagset[m->seltags], sel, urg); - printf("%s layout %s\n", m->wlr_output->name, m->ltsymbol); + printf("%s tags %u %u %u %u\n", m->wlr_output->name, occ, m->tagset[m->seltags], + sel, urg); + printf("%s layout %s\n", m->wlr_output->name, m->lt[m->sellt]->symbol); } fflush(stdout); } -void -powermgrsetmode(struct wl_listener *listener, void *data) -{ - struct wlr_output_power_v1_set_mode_event *event = data; - struct wlr_output_state state = {0}; - Monitor *m = event->output->data; - - if (!m) - return; - - m->gamma_lut_changed = 1; /* Reapply gamma LUT when re-enabling the ouput */ - wlr_output_state_set_enabled(&state, event->mode); - wlr_output_commit_state(m->wlr_output, &state); - - m->asleep = !event->mode; - updatemons(NULL, NULL); -} - void quit(const Arg *arg) { wl_display_terminate(dpy); } +void +quitsignal(int signo) +{ + quit(NULL); +} + void rendermon(struct wl_listener *listener, void *data) { @@ -2153,32 +1829,19 @@ rendermon(struct wl_listener *listener, void *data) * generally at the output's refresh rate (e.g. 60Hz). */ Monitor *m = wl_container_of(listener, m, frame); Client *c; - struct wlr_output_state pending = {0}; struct timespec now; /* Render if no XDG clients have an outstanding resize and are visible on * this monitor. */ - wl_list_for_each(c, &clients, link) { + wl_list_for_each(c, &clients, link) if (c->resize && !c->isfloating && client_is_rendered_on_mon(c, m) && !client_is_stopped(c)) goto skip; - } - - wlr_scene_output_commit(m->scene_output, NULL); - + if (!wlr_scene_output_commit(m->scene_output)) + return; skip: /* Let clients know a frame has been rendered */ clock_gettime(CLOCK_MONOTONIC, &now); wlr_scene_output_send_frame_done(m->scene_output, &now); - wlr_output_state_finish(&pending); -} - -void -requestdecorationmode(struct wl_listener *listener, void *data) -{ - Client *c = wl_container_of(listener, c, set_decoration_mode); - if (c->surface.xdg->initialized) - wlr_xdg_toplevel_decoration_v1_set_mode(c->decoration, - WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); } void @@ -2193,25 +1856,10 @@ requeststartdrag(struct wl_listener *listener, void *data) wlr_data_source_destroy(event->drag->source); } -void -requestmonstate(struct wl_listener *listener, void *data) -{ - struct wlr_output_event_request_state *event = data; - wlr_output_commit_state(event->output, event->state); - updatemons(NULL, NULL); -} - void resize(Client *c, struct wlr_box geo, int interact) { - struct wlr_box *bbox; - struct wlr_box clip; - - if (!c->mon || !client_surface(c)->mapped) - return; - - bbox = interact ? &sgeom : &c->mon->w; - + struct wlr_box *bbox = interact ? &sgeom : &c->mon->w; client_set_bounds(c, geo.width, geo.height); c->geom = geo; applybounds(c, bbox); @@ -2230,8 +1878,6 @@ resize(Client *c, struct wlr_box geo, int interact) /* this is a no-op if size hasn't changed */ c->resize = client_set_size(c, c->geom.width - 2 * c->bw, c->geom.height - 2 * c->bw); - client_get_clip(c, &clip); - wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip); } void @@ -2256,7 +1902,6 @@ run(char *startup_cmd) if ((child_pid = fork()) < 0) die("startup: fork:"); if (child_pid == 0) { - setsid(); dup2(piperw[0], STDIN_FILENO); close(piperw[0]); close(piperw[1]); @@ -2267,14 +1912,8 @@ run(char *startup_cmd) close(piperw[1]); close(piperw[0]); } - - /* Mark stdout as non-blocking to avoid the startup script - * causing dwl to freeze when a user neither closes stdin - * nor consumes standard input in his startup script */ - - if (fd_set_nonblock(STDOUT_FILENO) < 0) - close(STDOUT_FILENO); - + /* If nobody is reading the status output, don't terminate */ + signal(SIGPIPE, SIG_IGN); printstatus(); /* At this point the outputs are initialized, choose initial selmon based on @@ -2282,11 +1921,11 @@ run(char *startup_cmd) selmon = xytomon(cursor->x, cursor->y); /* TODO hack to get cursor to display in its initial location (100, 100) - * instead of (0, 0) and then jumping. Still may not be fully + * instead of (0, 0) and then jumping. still may not be fully * initialized, as the image/coordinates are not transformed for the * monitor when displayed here */ wlr_cursor_warp_closest(cursor, NULL, cursor->x, cursor->y); - wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); + wlr_xcursor_manager_set_cursor_image(cursor_mgr, cursor_image, cursor); /* Run the Wayland event loop. This does not return until you exit the * compositor. Starting the backend rigged up all of the necessary event @@ -2305,7 +1944,8 @@ setcursor(struct wl_listener *listener, void *data) * event, which will result in the client requesting set the cursor surface */ if (cursor_mode != CurNormal && cursor_mode != CurPressed) return; - /* This can be sent by any client, so we check to make sure this one + cursor_image = NULL; + /* This can be sent by any client, so we check to make sure this one is * actually has pointer focus first. If so, we can tell the cursor to * use the provided surface as the cursor image. It will set the * hardware cursor on the output that it's currently on and continue to @@ -2315,31 +1955,11 @@ setcursor(struct wl_listener *listener, void *data) event->hotspot_x, event->hotspot_y); } -void -setcursorshape(struct wl_listener *listener, void *data) -{ - struct wlr_cursor_shape_manager_v1_request_set_shape_event *event = data; - if (cursor_mode != CurNormal && cursor_mode != CurPressed) - return; - /* This can be sent by any client, so we check to make sure this one - * actually has pointer focus first. If so, we can tell the cursor to - * use the provided cursor shape. */ - if (event->seat_client == seat->pointer_state.focused_client) - wlr_cursor_set_xcursor(cursor, cursor_mgr, - wlr_cursor_shape_v1_name(event->shape)); -} - void setfloating(Client *c, int floating) { - Client *p = client_get_parent(c); c->isfloating = floating; - /* If in floating layout do not change the client's layer */ - if (!c->mon || !client_surface(c)->mapped || !c->mon->lt[c->mon->sellt]->arrange) - return; - wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen || - (p && p->isfullscreen) ? LyrFS - : c->isfloating ? LyrFloat : LyrTile]); + wlr_scene_node_reparent(&c->scene->node, layers[c->isfloating ? LyrFloat : LyrTile]); arrange(c->mon); printstatus(); } @@ -2348,11 +1968,11 @@ void setfullscreen(Client *c, int fullscreen) { c->isfullscreen = fullscreen; - if (!c->mon || !client_surface(c)->mapped) + if (!c->mon) return; c->bw = fullscreen ? 0 : borderpx; client_set_fullscreen(c, fullscreen); - wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen + wlr_scene_node_reparent(&c->scene->node, layers[fullscreen ? LyrFS : c->isfloating ? LyrFloat : LyrTile]); if (fullscreen) { @@ -2376,7 +1996,7 @@ setlayout(const Arg *arg) selmon->sellt ^= 1; if (arg && arg->v) selmon->lt[selmon->sellt] = (Layout *)arg->v; - strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, LENGTH(selmon->ltsymbol)); + /* TODO change layout symbol? */ arrange(selmon); printstatus(); } @@ -2389,7 +2009,7 @@ setmfact(const Arg *arg) if (!arg || !selmon || !selmon->lt[selmon->sellt]->arrange) return; - f = arg->f < 1.0f ? arg->f + selmon->mfact : arg->f - 1.0f; + f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; if (f < 0.1 || f > 0.9) return; selmon->mfact = f; @@ -2397,7 +2017,7 @@ setmfact(const Arg *arg) } void -setmon(Client *c, Monitor *m, uint32_t newtags) +setmon(Client *c, Monitor *m, unsigned int newtags) { Monitor *oldmon = c->mon; @@ -2406,15 +2026,17 @@ setmon(Client *c, Monitor *m, uint32_t newtags) c->mon = m; c->prev = c->geom; - /* Scene graph sends surface leave/enter events on move and resize */ - if (oldmon) + /* TODO leave/enter is not optimal but works */ + if (oldmon) { + wlr_surface_send_leave(client_surface(c), oldmon->wlr_output); arrange(oldmon); + } if (m) { /* Make sure window actually overlaps with the monitor */ resize(c, c->geom, 0); + wlr_surface_send_enter(client_surface(c), m->wlr_output); c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */ setfullscreen(c, c->isfullscreen); /* This will call arrange(c->mon) */ - setfloating(c, c->isfloating); } focusclient(focustop(selmon), 1); } @@ -2424,7 +2046,7 @@ setpsel(struct wl_listener *listener, void *data) { /* This event is raised by the seat when a client wants to set the selection, * usually when the user copies something. wlroots allows compositors to - * ignore such requests if they so choose, but in dwl we always honor them + * ignore such requests if they so choose, but in dwl we always honor */ struct wlr_seat_request_set_primary_selection_event *event = data; wlr_seat_set_primary_selection(seat, event->source, event->serial); @@ -2435,7 +2057,7 @@ setsel(struct wl_listener *listener, void *data) { /* This event is raised by the seat when a client wants to set the selection, * usually when the user copies something. wlroots allows compositors to - * ignore such requests if they so choose, but in dwl we always honor them + * ignore such requests if they so choose, but in dwl we always honor */ struct wlr_seat_request_set_selection_event *event = data; wlr_seat_set_selection(seat, event->source, event->serial); @@ -2444,64 +2066,48 @@ setsel(struct wl_listener *listener, void *data) void setup(void) { - int drm_fd, i, sig[] = {SIGCHLD, SIGINT, SIGTERM, SIGPIPE}; - struct sigaction sa = {.sa_flags = SA_RESTART, .sa_handler = handlesig}; - sigemptyset(&sa.sa_mask); - - for (i = 0; i < (int)LENGTH(sig); i++) - sigaction(sig[i], &sa, NULL); - - wlr_log_init(log_level, NULL); - /* The Wayland display is managed by libwayland. It handles accepting * clients from the Unix socket, manging Wayland globals, and so on. */ dpy = wl_display_create(); - event_loop = wl_display_get_event_loop(dpy); + + /* Set up signal handlers */ +#ifdef XWAYLAND + sigchld(0); +#else + signal(SIGCHLD, SIG_IGN); +#endif + signal(SIGINT, quitsignal); + signal(SIGTERM, quitsignal); /* The backend is a wlroots feature which abstracts the underlying input and * output hardware. The autocreate option will choose the most suitable * backend based on the current environment, such as opening an X11 window - * if an X11 server is running. */ - if (!(backend = wlr_backend_autocreate(event_loop, &session))) + * if an X11 server is running. The NULL argument here optionally allows you + * to pass in a custom renderer if wlr_renderer doesn't meet your needs. The + * backend uses the renderer, for example, to fall back to software cursors + * if the backend does not support hardware cursors (some older GPUs + * don't). */ + if (!(backend = wlr_backend_autocreate(dpy))) die("couldn't create backend"); /* Initialize the scene graph used to lay out windows */ scene = wlr_scene_create(); - root_bg = wlr_scene_rect_create(&scene->tree, 0, 0, rootcolor); - for (i = 0; i < NUM_LAYERS; i++) - layers[i] = wlr_scene_tree_create(&scene->tree); - drag_icon = wlr_scene_tree_create(&scene->tree); - wlr_scene_node_place_below(&drag_icon->node, &layers[LyrBlock]->node); + layers[LyrBg] = wlr_scene_tree_create(&scene->tree); + layers[LyrBottom] = wlr_scene_tree_create(&scene->tree); + layers[LyrTile] = wlr_scene_tree_create(&scene->tree); + layers[LyrFloat] = wlr_scene_tree_create(&scene->tree); + layers[LyrFS] = wlr_scene_tree_create(&scene->tree); + layers[LyrTop] = wlr_scene_tree_create(&scene->tree); + layers[LyrOverlay] = wlr_scene_tree_create(&scene->tree); + layers[LyrDragIcon] = wlr_scene_tree_create(&scene->tree); + layers[LyrBlock] = wlr_scene_tree_create(&scene->tree); - /* Autocreates a renderer, either Pixman, GLES2 or Vulkan for us. The user - * can also specify a renderer using the WLR_RENDERER env var. - * The renderer is responsible for defining the various pixel formats it - * supports for shared memory, this configures that for clients. */ + /* Create a renderer with the default implementation */ if (!(drw = wlr_renderer_autocreate(backend))) die("couldn't create renderer"); - wl_signal_add(&drw->events.lost, &gpu_reset); + wlr_renderer_init_wl_display(drw, dpy); - /* Create shm, drm and linux_dmabuf interfaces by ourselves. - * The simplest way is to call: - * wlr_renderer_init_wl_display(drw); - * but we need to create the linux_dmabuf interface manually to integrate it - * with wlr_scene. */ - wlr_renderer_init_wl_shm(drw, dpy); - - if (wlr_renderer_get_texture_formats(drw, WLR_BUFFER_CAP_DMABUF)) { - wlr_drm_create(dpy, drw); - wlr_scene_set_linux_dmabuf_v1(scene, - wlr_linux_dmabuf_v1_create_with_renderer(dpy, 5, drw)); - } - - if ((drm_fd = wlr_renderer_get_drm_fd(drw)) >= 0 && drw->features.timeline - && backend->features.timeline) - wlr_linux_drm_syncobj_manager_v1_create(dpy, 1, drm_fd); - - /* Autocreates an allocator for us. - * The allocator is the bridge between the renderer and the backend. It - * handles the buffer creation, allowing wlroots to render onto the - * screen */ + /* Create a default allocator */ if (!(alloc = wlr_allocator_autocreate(backend, drw))) die("couldn't create allocator"); @@ -2511,41 +2117,33 @@ setup(void) * to dig your fingers in and play with their behavior if you want. Note that * the clients cannot set the selection directly without compositor approval, * see the setsel() function. */ - compositor = wlr_compositor_create(dpy, 6, drw); - wlr_subcompositor_create(dpy); - wlr_data_device_manager_create(dpy); + compositor = wlr_compositor_create(dpy, drw); wlr_export_dmabuf_manager_v1_create(dpy); wlr_screencopy_manager_v1_create(dpy); wlr_data_control_manager_v1_create(dpy); + wlr_data_device_manager_create(dpy); + wlr_gamma_control_manager_v1_create(dpy); wlr_primary_selection_v1_device_manager_create(dpy); wlr_viewporter_create(dpy); wlr_single_pixel_buffer_manager_v1_create(dpy); - wlr_fractional_scale_manager_v1_create(dpy, 1); - wlr_presentation_create(dpy, backend, 2); - wlr_alpha_modifier_v1_create(dpy); + wlr_subcompositor_create(dpy); /* Initializes the interface used to implement urgency hints */ activation = wlr_xdg_activation_v1_create(dpy); wl_signal_add(&activation->events.request_activate, &request_activate); - wlr_scene_set_gamma_control_manager_v1(scene, wlr_gamma_control_manager_v1_create(dpy)); - - power_mgr = wlr_output_power_manager_v1_create(dpy); - wl_signal_add(&power_mgr->events.set_mode, &output_power_mgr_set_mode); - - /* Creates an output layout, which is a wlroots utility for working with an + /* Creates an output layout, which a wlroots utility for working with an * arrangement of screens in a physical layout. */ - output_layout = wlr_output_layout_create(dpy); + output_layout = wlr_output_layout_create(); wl_signal_add(&output_layout->events.change, &layout_change); - - wlr_xdg_output_manager_v1_create(dpy, output_layout); + wlr_xdg_output_manager_v1_create(dpy, output_layout); /* Configure a listener to be notified when new outputs are available on the * backend. */ wl_list_init(&mons); wl_signal_add(&backend->events.new_output, &new_output); - /* Set up our client lists, the xdg-shell and the layer-shell. The xdg-shell is a + /* Set up our client lists and the xdg-shell. The xdg-shell is a * Wayland protocol which is used for application windows. For more * detail on shells, refer to the article: * @@ -2554,22 +2152,24 @@ setup(void) wl_list_init(&clients); wl_list_init(&fstack); - xdg_shell = wlr_xdg_shell_create(dpy, 6); - wl_signal_add(&xdg_shell->events.new_toplevel, &new_xdg_toplevel); - wl_signal_add(&xdg_shell->events.new_popup, &new_xdg_popup); - - layer_shell = wlr_layer_shell_v1_create(dpy, 3); - wl_signal_add(&layer_shell->events.new_surface, &new_layer_surface); - + idle = wlr_idle_create(dpy); idle_notifier = wlr_idle_notifier_v1_create(dpy); idle_inhibit_mgr = wlr_idle_inhibit_v1_create(dpy); - wl_signal_add(&idle_inhibit_mgr->events.new_inhibitor, &new_idle_inhibitor); + wl_signal_add(&idle_inhibit_mgr->events.new_inhibitor, &idle_inhibitor_create); + layer_shell = wlr_layer_shell_v1_create(dpy); + wl_signal_add(&layer_shell->events.new_surface, &new_layer_shell_surface); + + xdg_shell = wlr_xdg_shell_create(dpy, 4); + wl_signal_add(&xdg_shell->events.new_surface, &new_xdg_surface); + + input_inhibit_mgr = wlr_input_inhibit_manager_create(dpy); session_lock_mgr = wlr_session_lock_manager_v1_create(dpy); - wl_signal_add(&session_lock_mgr->events.new_lock, &new_session_lock); + wl_signal_add(&session_lock_mgr->events.new_lock, &session_lock_create_lock); + wl_signal_add(&session_lock_mgr->events.destroy, &session_lock_mgr_destroy); locked_bg = wlr_scene_rect_create(layers[LyrBlock], sgeom.width, sgeom.height, - (float [4]){0.1f, 0.1f, 0.1f, 1.0f}); + (float [4]){0.1, 0.1, 0.1, 1.0}); wlr_scene_node_set_enabled(&locked_bg->node, 0); /* Use decoration protocols to negotiate server-side decorations */ @@ -2579,11 +2179,6 @@ setup(void) xdg_decoration_mgr = wlr_xdg_decoration_manager_v1_create(dpy); wl_signal_add(&xdg_decoration_mgr->events.new_toplevel_decoration, &new_xdg_decoration); - pointer_constraints = wlr_pointer_constraints_v1_create(dpy); - wl_signal_add(&pointer_constraints->events.new_constraint, &new_pointer_constraint); - - relative_pointer_mgr = wlr_relative_pointer_manager_v1_create(dpy); - /* * Creates a cursor, which is a wlroots utility for tracking the cursor * image shown on screen. @@ -2596,14 +2191,15 @@ setup(void) * images are available at all scale factors on the screen (necessary for * HiDPI support). Scaled cursors will be loaded with each output. */ cursor_mgr = wlr_xcursor_manager_create(NULL, 24); - setenv("XCURSOR_SIZE", "24", 1); /* * wlr_cursor *only* displays an image on screen. It does not move around * when the pointer moves. However, we can attach input devices to it, and * it will generate aggregate events for all of them. In these events, we * can choose how we want to process them, forwarding them to clients and - * moving the cursor around. More detail on this process is described in + * moving the cursor around. More detail on this process is described in my + * input handling blog post: + * * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html * * And more comments are sprinkled throughout the notify functions above. @@ -2614,23 +2210,17 @@ setup(void) wl_signal_add(&cursor->events.axis, &cursor_axis); wl_signal_add(&cursor->events.frame, &cursor_frame); - cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1); - wl_signal_add(&cursor_shape_mgr->events.request_set_shape, &request_set_cursor_shape); - /* * Configures a seat, which is a single "seat" at which a user sits and * operates the computer. This conceptually includes up to one keyboard, * pointer, touch, and drawing tablet device. We also rig up a listener to * let us know when new input devices are available on the backend. */ - wl_signal_add(&backend->events.new_input, &new_input_device); + wl_list_init(&keyboards); + wl_signal_add(&backend->events.new_input, &new_input); virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(dpy); wl_signal_add(&virtual_keyboard_mgr->events.new_virtual_keyboard, &new_virtual_keyboard); - virtual_pointer_mgr = wlr_virtual_pointer_manager_v1_create(dpy); - wl_signal_add(&virtual_pointer_mgr->events.new_virtual_pointer, - &new_virtual_pointer); - seat = wlr_seat_create(dpy, "seat0"); wl_signal_add(&seat->events.request_set_cursor, &request_cursor); wl_signal_add(&seat->events.request_set_selection, &request_set_sel); @@ -2638,23 +2228,19 @@ setup(void) wl_signal_add(&seat->events.request_start_drag, &request_start_drag); wl_signal_add(&seat->events.start_drag, &start_drag); - kb_group = createkeyboardgroup(); - wl_list_init(&kb_group->destroy.link); - output_mgr = wlr_output_manager_v1_create(dpy); wl_signal_add(&output_mgr->events.apply, &output_mgr_apply); wl_signal_add(&output_mgr->events.test, &output_mgr_test); - /* Make sure XWayland clients don't connect to the parent X server, - * e.g when running in the x11 backend or the wayland backend and the - * compositor has Xwayland support */ - unsetenv("DISPLAY"); + wlr_scene_set_presentation(scene, wlr_presentation_create(dpy, backend)); + #ifdef XWAYLAND /* * Initialise the XWayland X server. * It will be started when the first X client is started. */ - if ((xwayland = wlr_xwayland_create(dpy, compositor, 1))) { + xwayland = wlr_xwayland_create(dpy, compositor, 1); + if (xwayland) { wl_signal_add(&xwayland->events.ready, &xwayland_ready); wl_signal_add(&xwayland->events.new_surface, &new_xwayland_surface); @@ -2680,23 +2266,24 @@ void startdrag(struct wl_listener *listener, void *data) { struct wlr_drag *drag = data; + if (!drag->icon) return; - drag->icon->data = &wlr_scene_drag_icon_create(drag_icon, drag->icon)->node; - LISTEN_STATIC(&drag->icon->events.destroy, destroydragicon); + drag->icon->data = wlr_scene_subsurface_tree_create(layers[LyrDragIcon], drag->icon->surface); + motionnotify(0); + wl_signal_add(&drag->icon->events.destroy, &drag_icon_destroy); } void tag(const Arg *arg) { Client *sel = focustop(selmon); - if (!sel || (arg->ui & TAGMASK) == 0) - return; - - sel->tags = arg->ui & TAGMASK; - focusclient(focustop(selmon), 1); - arrange(selmon); + if (sel && arg->ui & TAGMASK) { + sel->tags = arg->ui & TAGMASK; + focusclient(focustop(selmon), 1); + arrange(selmon); + } printstatus(); } @@ -2711,8 +2298,7 @@ tagmon(const Arg *arg) void tile(Monitor *m) { - unsigned int mw, my, ty; - int i, n = 0; + unsigned int i, n = 0, mw, my, ty; Client *c; wl_list_for_each(c, &clients, link) @@ -2722,7 +2308,7 @@ tile(Monitor *m) return; if (n > m->nmaster) - mw = m->nmaster ? (int)roundf(m->w.width * m->mfact) : 0; + mw = m->nmaster ? m->w.width * m->mfact : 0; else mw = m->w.width; i = my = ty = 0; @@ -2762,27 +2348,29 @@ togglefullscreen(const Arg *arg) void toggletag(const Arg *arg) { - uint32_t newtags; + unsigned int newtags; Client *sel = focustop(selmon); - if (!sel || !(newtags = sel->tags ^ (arg->ui & TAGMASK))) + if (!sel) return; - - sel->tags = newtags; - focusclient(focustop(selmon), 1); - arrange(selmon); + newtags = sel->tags ^ (arg->ui & TAGMASK); + if (newtags) { + sel->tags = newtags; + focusclient(focustop(selmon), 1); + arrange(selmon); + } printstatus(); } void toggleview(const Arg *arg) { - uint32_t newtagset; - if (!(newtagset = selmon ? selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK) : 0)) - return; + unsigned int newtagset = selmon ? selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK) : 0; - selmon->tagset[selmon->seltags] = newtagset; - focusclient(focustop(selmon), 1); - arrange(selmon); + if (newtagset) { + selmon->tagset[selmon->seltags] = newtagset; + focusclient(focustop(selmon), 1); + arrange(selmon); + } printstatus(); } @@ -2796,17 +2384,19 @@ unlocksession(struct wl_listener *listener, void *data) void unmaplayersurfacenotify(struct wl_listener *listener, void *data) { - LayerSurface *l = wl_container_of(listener, l, unmap); + LayerSurface *layersurface = wl_container_of(listener, layersurface, unmap); - l->mapped = 0; - wlr_scene_node_set_enabled(&l->scene->node, 0); - if (l == exclusive_focus) + layersurface->mapped = 0; + wlr_scene_node_set_enabled(&layersurface->scene->node, 0); + if (layersurface == exclusive_focus) exclusive_focus = NULL; - if (l->layer_surface->output && (l->mon = l->layer_surface->output->data)) - arrangelayers(l->mon); - if (l->layer_surface->surface == seat->keyboard_state.focused_surface) + if (layersurface->layer_surface->output + && (layersurface->mon = layersurface->layer_surface->output->data)) + arrangelayers(layersurface->mon); + if (layersurface->layer_surface->surface == + seat->keyboard_state.focused_surface) focusclient(focustop(selmon), 1); - motionnotify(0, NULL, 0, 0, 0, 0); + motionnotify(0); } void @@ -2820,19 +2410,20 @@ unmapnotify(struct wl_listener *listener, void *data) } if (client_is_unmanaged(c)) { - if (c == exclusive_focus) { + if (c == exclusive_focus) exclusive_focus = NULL; + if (client_surface(c) == seat->keyboard_state.focused_surface) focusclient(focustop(selmon), 1); - } } else { wl_list_remove(&c->link); setmon(c, NULL, 0); wl_list_remove(&c->flink); } + wl_list_remove(&c->commit.link); wlr_scene_node_destroy(&c->scene->node); printstatus(); - motionnotify(0, NULL, 0, 0, 0, 0); + motionnotify(0); } void @@ -2840,41 +2431,38 @@ updatemons(struct wl_listener *listener, void *data) { /* * Called whenever the output layout changes: adding or removing a - * monitor, changing an output's mode or position, etc. This is where + * monitor, changing an output's mode or position, etc. This is where * the change officially happens and we update geometry, window * positions, focus, and the stored configuration in wlroots' * output-manager implementation. */ - struct wlr_output_configuration_v1 *config - = wlr_output_configuration_v1_create(); + struct wlr_output_configuration_v1 *config = + wlr_output_configuration_v1_create(); Client *c; struct wlr_output_configuration_head_v1 *config_head; Monitor *m; /* First remove from the layout the disabled monitors */ wl_list_for_each(m, &mons, link) { - if (m->wlr_output->enabled || m->asleep) + if (m->wlr_output->enabled) continue; config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output); config_head->state.enabled = 0; /* Remove this output from the layout to avoid cursor enter inside it */ wlr_output_layout_remove(output_layout, m->wlr_output); closemon(m); - m->m = m->w = (struct wlr_box){0}; + memset(&m->m, 0, sizeof(m->m)); + memset(&m->w, 0, sizeof(m->w)); } /* Insert outputs that need to */ - wl_list_for_each(m, &mons, link) { + wl_list_for_each(m, &mons, link) if (m->wlr_output->enabled && !wlr_output_layout_get(output_layout, m->wlr_output)) wlr_output_layout_add_auto(output_layout, m->wlr_output); - } /* Now that we update the output layout we can get its box */ wlr_output_layout_get_box(output_layout, NULL, &sgeom); - wlr_scene_node_set_position(&root_bg->node, sgeom.x, sgeom.y); - wlr_scene_rect_set_size(root_bg, sgeom.width, sgeom.height); - /* Make sure the clients are hidden when dwl is locked */ wlr_scene_node_set_position(&locked_bg->node, sgeom.x, sgeom.y); wlr_scene_rect_set_size(locked_bg, sgeom.width, sgeom.height); @@ -2885,8 +2473,8 @@ updatemons(struct wl_listener *listener, void *data) config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output); /* Get the effective monitor geometry to use for surfaces */ - wlr_output_layout_get_box(output_layout, m->wlr_output, &m->m); - m->w = m->m; + wlr_output_layout_get_box(output_layout, m->wlr_output, &(m->m)); + wlr_output_layout_get_box(output_layout, m->wlr_output, &(m->w)); wlr_scene_output_set_position(m->scene_output, m->m.x, m->m.y); wlr_scene_node_set_position(&m->fullscreen_bg->node, m->m.x, m->m.y); @@ -2895,49 +2483,30 @@ updatemons(struct wl_listener *listener, void *data) if (m->lock_surface) { struct wlr_scene_tree *scene_tree = m->lock_surface->surface->data; wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y); - wlr_session_lock_surface_v1_configure(m->lock_surface, m->m.width, m->m.height); + wlr_session_lock_surface_v1_configure(m->lock_surface, m->m.width, + m->m.height); } /* Calculate the effective monitor geometry to use for clients */ arrangelayers(m); /* Don't move clients to the left output when plugging monitors */ arrange(m); - /* make sure fullscreen clients have the right size */ - if ((c = focustop(m)) && c->isfullscreen) - resize(c, m->m, 0); - - /* Try to re-set the gamma LUT when updating monitors, - * it's only really needed when enabling a disabled output, but meh. */ - m->gamma_lut_changed = 1; + config_head->state.enabled = 1; + config_head->state.mode = m->wlr_output->current_mode; config_head->state.x = m->m.x; config_head->state.y = m->m.y; - - if (!selmon) { - selmon = m; - } } if (selmon && selmon->wlr_output->enabled) { - wl_list_for_each(c, &clients, link) { - if (!c->mon && client_surface(c)->mapped) + wl_list_for_each(c, &clients, link) + if (!c->mon && client_is_mapped(c)) setmon(c, selmon, c->tags); - } - focusclient(focustop(selmon), 1); - if (selmon->lock_surface) { + if (selmon->lock_surface) client_notify_enter(selmon->lock_surface->surface, wlr_seat_get_keyboard(seat)); - client_activate_surface(selmon->lock_surface->surface, 1); - } } - /* FIXME: figure out why the cursor image is at 0,0 after turning all - * the monitors on. - * Move the cursor image where it used to be. It does not generate a - * wl_pointer.motion event for the clients, it's only the image what it's - * at the wrong position after all. */ - wlr_cursor_move(cursor, NULL, 0, 0); - wlr_output_manager_v1_set_configuration(output_mgr, config); } @@ -2955,14 +2524,10 @@ urgent(struct wl_listener *listener, void *data) struct wlr_xdg_activation_v1_request_activate_event *event = data; Client *c = NULL; toplevel_from_wlr_surface(event->surface, &c, NULL); - if (!c || c == focustop(selmon)) - return; - - c->isurgent = 1; - printstatus(); - - if (client_surface(c)->mapped) - client_set_border_color(c, urgentcolor); + if (c && c != focustop(selmon)) { + c->isurgent = 1; + printstatus(); + } } void @@ -2981,26 +2546,8 @@ view(const Arg *arg) void virtualkeyboard(struct wl_listener *listener, void *data) { - struct wlr_virtual_keyboard_v1 *kb = data; - /* virtual keyboards shouldn't share keyboard group */ - KeyboardGroup *group = createkeyboardgroup(); - /* Set the keymap to match the group keymap */ - wlr_keyboard_set_keymap(&kb->keyboard, group->wlr_group->keyboard.keymap); - LISTEN(&kb->keyboard.base.events.destroy, &group->destroy, destroykeyboardgroup); - - /* Add the new keyboard to the group */ - wlr_keyboard_group_add_keyboard(group->wlr_group, &kb->keyboard); -} - -void -virtualpointer(struct wl_listener *listener, void *data) -{ - struct wlr_virtual_pointer_v1_new_pointer_event *event = data; - struct wlr_input_device *device = &event->new_pointer->pointer.base; - - wlr_cursor_attach_input_device(cursor, device); - if (event->suggested_output) - wlr_cursor_map_input_to_output(cursor, device, event->suggested_output); + struct wlr_virtual_keyboard_v1 *keyboard = data; + createkeyboard(&keyboard->keyboard); } Monitor * @@ -3010,7 +2557,7 @@ xytomon(double x, double y) return o ? o->data : NULL; } -void +struct wlr_scene_node * xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, LayerSurface **pl, double *nx, double *ny) { @@ -3018,27 +2565,30 @@ xytonode(double x, double y, struct wlr_surface **psurface, struct wlr_surface *surface = NULL; Client *c = NULL; LayerSurface *l = NULL; - int layer; + const int *layer; + int focus_order[] = { LyrBlock, LyrOverlay, LyrTop, LyrFS, LyrFloat, LyrTile, LyrBottom, LyrBg }; - for (layer = NUM_LAYERS - 1; !surface && layer >= 0; layer--) { - if (!(node = wlr_scene_node_at(&layers[layer]->node, x, y, nx, ny))) - continue; - - if (node->type == WLR_SCENE_NODE_BUFFER) - surface = wlr_scene_surface_try_from_buffer( - wlr_scene_buffer_from_node(node))->surface; - /* Walk the tree to find a node that knows the client */ - for (pnode = node; pnode && !c; pnode = &pnode->parent->node) - c = pnode->data; - if (c && c->type == LayerShell) { - c = NULL; - l = pnode->data; + for (layer = focus_order; layer < END(focus_order); layer++) { + if ((node = wlr_scene_node_at(&layers[*layer]->node, x, y, nx, ny))) { + if (node->type == WLR_SCENE_NODE_BUFFER) + surface = wlr_scene_surface_from_buffer( + wlr_scene_buffer_from_node(node))->surface; + /* Walk the tree to find a node that knows the client */ + for (pnode = node; pnode && !c; pnode = &pnode->parent->node) + c = pnode->data; + if (c && c->type == LayerShell) { + c = NULL; + l = pnode->data; + } } + if (surface) + break; } if (psurface) *psurface = surface; if (pc) *pc = c; if (pl) *pl = l; + return node; } void @@ -3051,13 +2601,12 @@ zoom(const Arg *arg) /* Search for the first tiled window that is not sel, marking sel as * NULL if we pass it along the way */ - wl_list_for_each(c, &clients, link) { + wl_list_for_each(c, &clients, link) if (VISIBLEON(c, selmon) && !c->isfloating) { if (c != sel) break; sel = NULL; } - } /* Return if no other tiled window was found */ if (&c->link == &clients) @@ -3081,42 +2630,22 @@ activatex11(struct wl_listener *listener, void *data) Client *c = wl_container_of(listener, c, activate); /* Only "managed" windows can be activated */ - if (!client_is_unmanaged(c)) + if (c->type == X11Managed) wlr_xwayland_surface_activate(c->surface.xwayland, 1); } -void -associatex11(struct wl_listener *listener, void *data) -{ - Client *c = wl_container_of(listener, c, associate); - - LISTEN(&client_surface(c)->events.map, &c->map, mapnotify); - LISTEN(&client_surface(c)->events.unmap, &c->unmap, unmapnotify); -} - void configurex11(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, configure); struct wlr_xwayland_surface_configure_event *event = data; - if (!client_surface(c) || !client_surface(c)->mapped) { - wlr_xwayland_surface_configure(c->surface.xwayland, - event->x, event->y, event->width, event->height); + if (!c->mon) return; - } - if (client_is_unmanaged(c)) { - wlr_scene_node_set_position(&c->scene->node, event->x, event->y); - wlr_xwayland_surface_configure(c->surface.xwayland, - event->x, event->y, event->width, event->height); - return; - } - if ((c->isfloating && c != grabc) || !c->mon->lt[c->mon->sellt]->arrange) { - resize(c, (struct wlr_box){.x = event->x - c->bw, - .y = event->y - c->bw, .width = event->width + c->bw * 2, - .height = event->height + c->bw * 2}, 0); - } else { + if (c->isfloating || c->type == X11Unmanaged) + resize(c, (struct wlr_box){.x = event->x, .y = event->y, + .width = event->width, .height = event->height}, 0); + else arrange(c->mon); - } } void @@ -3128,57 +2657,91 @@ createnotifyx11(struct wl_listener *listener, void *data) /* Allocate a Client for this surface */ c = xsurface->data = ecalloc(1, sizeof(*c)); c->surface.xwayland = xsurface; - c->type = X11; - c->bw = client_is_unmanaged(c) ? 0 : borderpx; + c->type = xsurface->override_redirect ? X11Unmanaged : X11Managed; + c->bw = borderpx; /* Listen to the various events it can emit */ - LISTEN(&xsurface->events.associate, &c->associate, associatex11); - LISTEN(&xsurface->events.destroy, &c->destroy, destroynotify); - LISTEN(&xsurface->events.dissociate, &c->dissociate, dissociatex11); + LISTEN(&xsurface->events.map, &c->map, mapnotify); + LISTEN(&xsurface->events.unmap, &c->unmap, unmapnotify); LISTEN(&xsurface->events.request_activate, &c->activate, activatex11); LISTEN(&xsurface->events.request_configure, &c->configure, configurex11); - LISTEN(&xsurface->events.request_fullscreen, &c->fullscreen, fullscreennotify); LISTEN(&xsurface->events.set_hints, &c->set_hints, sethints); LISTEN(&xsurface->events.set_title, &c->set_title, updatetitle); + LISTEN(&xsurface->events.destroy, &c->destroy, destroynotify); + LISTEN(&xsurface->events.request_fullscreen, &c->fullscreen, fullscreennotify); } -void -dissociatex11(struct wl_listener *listener, void *data) +Atom +getatom(xcb_connection_t *xc, const char *name) { - Client *c = wl_container_of(listener, c, dissociate); - wl_list_remove(&c->map.link); - wl_list_remove(&c->unmap.link); + Atom atom = 0; + xcb_intern_atom_reply_t *reply; + xcb_intern_atom_cookie_t cookie = xcb_intern_atom(xc, 0, strlen(name), name); + if ((reply = xcb_intern_atom_reply(xc, cookie, NULL))) + atom = reply->atom; + free(reply); + + return atom; } void sethints(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, set_hints); - struct wlr_surface *surface = client_surface(c); - if (c == focustop(selmon) || !c->surface.xwayland->hints) - return; + if (c != focustop(selmon)) { + c->isurgent = xcb_icccm_wm_hints_get_urgency(c->surface.xwayland->hints); + printstatus(); + } +} - c->isurgent = xcb_icccm_wm_hints_get_urgency(c->surface.xwayland->hints); - printstatus(); - - if (c->isurgent && surface && surface->mapped) - client_set_border_color(c, urgentcolor); +void +sigchld(int unused) +{ + siginfo_t in; + /* We should be able to remove this function in favor of a simple + * signal(SIGCHLD, SIG_IGN); + * but the Xwayland implementation in wlroots currently prevents us from + * setting our own disposition for SIGCHLD. + */ + if (signal(SIGCHLD, sigchld) == SIG_ERR) + die("can't install SIGCHLD handler:"); + /* WNOWAIT leaves the child in a waitable state, in case this is the + * XWayland process + */ + while (!waitid(P_ALL, 0, &in, WEXITED|WNOHANG|WNOWAIT) && in.si_pid + && (!xwayland || in.si_pid != xwayland->server->pid)) + waitpid(in.si_pid, NULL, 0); } void xwaylandready(struct wl_listener *listener, void *data) { struct wlr_xcursor *xcursor; + xcb_connection_t *xc = xcb_connect(xwayland->display_name, NULL); + int err = xcb_connection_has_error(xc); + if (err) { + fprintf(stderr, "xcb_connect to X server failed with code %d\n. Continuing with degraded functionality.\n", err); + return; + } + + /* Collect atoms we are interested in. If getatom returns 0, we will + * not detect that window type. */ + netatom[NetWMWindowTypeDialog] = getatom(xc, "_NET_WM_WINDOW_TYPE_DIALOG"); + netatom[NetWMWindowTypeSplash] = getatom(xc, "_NET_WM_WINDOW_TYPE_SPLASH"); + netatom[NetWMWindowTypeToolbar] = getatom(xc, "_NET_WM_WINDOW_TYPE_TOOLBAR"); + netatom[NetWMWindowTypeUtility] = getatom(xc, "_NET_WM_WINDOW_TYPE_UTILITY"); /* assign the one and only seat */ wlr_xwayland_set_seat(xwayland, seat); /* Set the default XWayland cursor to match the rest of dwl. */ - if ((xcursor = wlr_xcursor_manager_get_xcursor(cursor_mgr, "default", 1))) + if ((xcursor = wlr_xcursor_manager_get_xcursor(cursor_mgr, "left_ptr", 1))) wlr_xwayland_set_cursor(xwayland, xcursor->images[0]->buffer, xcursor->images[0]->width * 4, xcursor->images[0]->width, xcursor->images[0]->height, xcursor->images[0]->hotspot_x, xcursor->images[0]->hotspot_y); + + xcb_disconnect(xc); } #endif @@ -3188,11 +2751,9 @@ main(int argc, char *argv[]) char *startup_cmd = NULL; int c; - while ((c = getopt(argc, argv, "s:hdv")) != -1) { + while ((c = getopt(argc, argv, "s:hv")) != -1) { if (c == 's') startup_cmd = optarg; - else if (c == 'd') - log_level = WLR_DEBUG; else if (c == 'v') die("dwl " VERSION); else @@ -3210,5 +2771,5 @@ main(int argc, char *argv[]) return EXIT_SUCCESS; usage: - die("Usage: %s [-v] [-d] [-s startup command]", argv[0]); + die("Usage: %s [-v] [-s startup command]", argv[0]); } diff --git a/dwl.desktop b/dwl.desktop deleted file mode 100644 index e1380f7..0000000 --- a/dwl.desktop +++ /dev/null @@ -1,5 +0,0 @@ -[Desktop Entry] -Name=dwl -Comment=dwm for Wayland -Exec=dwl -Type=Application diff --git a/protocols/wlr-output-power-management-unstable-v1.xml b/protocols/wlr-output-power-management-unstable-v1.xml deleted file mode 100644 index a977839..0000000 --- a/protocols/wlr-output-power-management-unstable-v1.xml +++ /dev/null @@ -1,128 +0,0 @@ - - - - Copyright © 2019 Purism SPC - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice (including the next - paragraph) shall be included in all copies or substantial portions of the - Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. - - - - This protocol allows clients to control power management modes - of outputs that are currently part of the compositor space. The - intent is to allow special clients like desktop shells to power - down outputs when the system is idle. - - To modify outputs not currently part of the compositor space see - wlr-output-management. - - Warning! The protocol described in this file is experimental and - backward incompatible changes may be made. Backward compatible changes - may be added together with the corresponding interface version bump. - Backward incompatible changes are done by bumping the version number in - the protocol and interface names and resetting the interface version. - Once the protocol is to be declared stable, the 'z' prefix and the - version number in the protocol and interface names are removed and the - interface version number is reset. - - - - - This interface is a manager that allows creating per-output power - management mode controls. - - - - - Create a output power management mode control that can be used to - adjust the power management mode for a given output. - - - - - - - - All objects created by the manager will still remain valid, until their - appropriate destroy request has been called. - - - - - - - This object offers requests to set the power management mode of - an output. - - - - - - - - - - - - - - Set an output's power save mode to the given mode. The mode change - is effective immediately. If the output does not support the given - mode a failed event is sent. - - - - - - - Report the power management mode change of an output. - - The mode event is sent after an output changed its power - management mode. The reason can be a client using set_mode or the - compositor deciding to change an output's mode. - This event is also sent immediately when the object is created - so the client is informed about the current power management mode. - - - - - - - This event indicates that the output power management mode control - is no longer valid. This can happen for a number of reasons, - including: - - The output doesn't support power management - - Another client already has exclusive power management mode control - for this output - - The output disappeared - - Upon receiving this event, the client should destroy this object. - - - - - - Destroys the output power management mode control object. - - - - diff --git a/util.c b/util.c index b925987..cca7c19 100644 --- a/util.c +++ b/util.c @@ -3,7 +3,6 @@ #include #include #include -#include #include "util.h" @@ -34,18 +33,3 @@ ecalloc(size_t nmemb, size_t size) die("calloc:"); return p; } - -int -fd_set_nonblock(int fd) { - int flags = fcntl(fd, F_GETFL); - if (flags < 0) { - perror("fcntl(F_GETFL):"); - return -1; - } - if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { - perror("fcntl(F_SETFL):"); - return -1; - } - - return 0; -} diff --git a/util.h b/util.h index 226980d..4c94117 100644 --- a/util.h +++ b/util.h @@ -2,4 +2,3 @@ void die(const char *fmt, ...); void *ecalloc(size_t nmemb, size_t size); -int fd_set_nonblock(int fd);