Compare commits

..

281 Commits

Author SHA1 Message Date
vaxerski
f27873a6f0 ver: bump to 0.25.0 2023-05-03 17:15:08 +01:00
scorpion-26
c3b9326ba1 Honor debug:enable_stdout_logs on startup (#2197)
* Honor debug:enable_stdout_logs on startup

disableStdout is set via config in CConfigManager::init(), which is
called early in CCompositor::initServer(). initServer() always disables
stdout logs at the end though, even when stdout is enabled is config. With this commit,
the config is respected.

* Don't spam stdout message
2023-05-03 16:08:01 +01:00
vaxerski
fd3e6a3bfd workspaces: restore monitor on re-plug 2023-05-03 15:15:56 +01:00
vaxerski
0155b85950 rules: fix monitor rule with names 2023-05-03 14:58:51 +01:00
jacekpoz
a663823af2 only ignore no_gaps_when_only when the workspace rule specifies a border (#2217)
Co-authored-by: jacekpoz <jacekpoz@cock.li>
2023-05-03 14:48:46 +01:00
outfoxxed
5a3c144919 Add warning about setting hyprland config with home manager 2023-05-03 13:05:21 +03:00
outfoxxed
4fe5827598 Add plugin configuration to home manager module 2023-05-03 13:05:21 +03:00
Mihai Fufezan
2e28e88dfd flake.lock: update nixpkgs
flake.nix, nix/overlays.nix: remove wayland-latest overlay (1.22 now in nixpkgs)
2023-05-03 00:15:58 +03:00
vaxerski
72b118cd8f opengl: don't use new optim with xray off on special tiled 2023-05-02 21:23:53 +01:00
Jacob Birkett
80b2ac1cc5 Nix: fix recursion in package overlays (#2210)
* nix: flake: fix improperly using prev.callPackage

* flake: cleanup with let blocks

* flake: make overlays use recursive packages

flake: separate overlays into multiple, combine into default

* nix: overlays: extract to own file

* flake: devShells: remove stdenv override

* overlays: hl-pkgs: xdph: remove needless overlay

Since the packages are now built with the overlays combined from inputs
and self, overriding specific dependencies (anywhere) is no longer
necessary.

* nix: overlays: extras: include xdph and share-picker

* nix: overlays: hl-pkgs: remove stdenv override
2023-05-02 20:54:29 +03:00
vaxerski
79791c9ed4 internal: fix -Wsign-compare and -Wunused-variable warnings 2023-05-02 14:53:31 +01:00
Yavor Kolev
ac3edec14b Add activeworkspace hyprctl command (#2202)
* Add `activeworkspace` hyprctl command

* fix format in hyprctl

* Make stuff more shared in workspace hyprctl

---------

Co-authored-by: vaxerski <43317083+vaxerski@users.noreply.github.com>
2023-05-02 14:51:52 +01:00
vaxerski
cde7f79af0 xwayland: allow initial focus to dialogs 2023-05-02 14:44:21 +01:00
Jan Beich
609c7ab6b5 Unbreak CMake build on FreeBSD (#2209)
* cmake: unbreak on non-GNU after 474ada9267

CMake Error at CMakeLists.txt:121 (target_link_libraries):
  The keyword signature for target_link_libraries has already been used with
  the target "Hyprland".  All uses of target_link_libraries with a target
  must be either all-keyword or all-plain.

  The uses of the keyword signature are here:

   * CMakeLists.txt:107 (target_link_libraries)

Fixes https://github.com/hyprwm/Hyprland/issues/1780

* cmake: always link with dependencies via imported targets

On BSD systems base compiler by default only looks for headers and
libraries from base system. For dependencies from packages extra flags
are necessary.

ld: error: unable to find library -lxcb
ld: error: unable to find library -lpixman-1
ld: error: unable to find library -lOpenGL
ld: error: unable to find library -lGLESv2

* make: use same make in recursive calls

On BSDs `make` is BSD make while `gmake` is GNU make

* make: work around GNU vs. BSD sed -i incompatibility

https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=254091

* make: replace GNU make extension with POSIX sh

`$(shell ...)` in GNU make is similar to `${:!..!}` in BSD make

* make: fall back when nproc isn't available

Only FreeBSD added nproc for compatibility with Linux.

* make: unbreak hyprctl on Clang-based systems

/bin/sh: g++: not found
error: invalid value 'c++23' in '-std=c++23'

* make: create lib/ before copying libwlroots.so there

$ make install PREFIX=/tmp/test
[...]
cp: directory /tmp/test/lib does not exist

* make: pass cp(1) flags before arguments

cp: -f is not a directory

* make: replace install -Dt with mkdir

install: illegal option -- t

* make: replace cp --parents with cpio -dump

cp: illegal option -- -

* make: limit pkg-config workaround to Linux when run as root

/usr/share/pkgconfig doesn't exist on BSDs or may not be writable.
2023-05-02 14:38:36 +01:00
levnikmyskin
c949173bc9 Added some workspace-specific rules (#1986)
* added some workspace-specific rules

* added some worskpace-specific rules, with windowrule like syntax

* monitor is not mandatory anymore

* pointers to config are now static

* fixed optional WorkspaceRule fields

* Windows can now specify border size

* removed CHyprOpenGLImpl::renderBorder borderSize default value

* stuff

---------

Co-authored-by: Alessio Molinari <alessiomolinari@gmail.com>
Co-authored-by: vaxerski <43317083+vaxerski@users.noreply.github.com>
2023-05-01 22:28:27 +01:00
Jan Beich
250d5cf78c config: add missing header for libc++ after 3a631e40db (#2208)
src/config/ConfigManager.cpp:1980:27: error: implicit instantiation of undefined template 'std::basic_stringstream<char>'
        std::stringstream error;
                          ^
/usr/include/c++/v1/iosfwd:134:32: note: template is declared here
    class _LIBCPP_TEMPLATE_VIS basic_stringstream;
                               ^
2023-05-01 22:24:51 +01:00
vaxerski
45b1e6dc5e keybinds: simulate workspace switch on focusWindow to another ws 2023-05-01 15:39:08 +01:00
vaxerski
d6b069458d input: don't refocus on dragging 2023-05-01 15:15:55 +01:00
outfoxxed
3a631e40db Declarative plugin management (#2180)
* Declarative plugin management

Allow declaring `plugin` entries in the hyprland configuration.

Plugins will be loaded if an entry is added and unloaded if that entry
is removed.

* Replace pointers with copying in updateconfigPlugins

* Include which plugin was declared twice in error
2023-05-01 15:10:53 +01:00
q234rty
dc469dc4c1 Prefer bundled wlroots headers to system ones (#2204)
In the case that the prefix is `/usr`, system wlroots headers installed in `/usr/include/wlr` will be preferred over those bundled by hyprland as `-I` directories are searched [from left to right](https://gcc.gnu.org/onlinedocs/cpp/Search-Path.html), which is not great since the system wlroots headers might not be compatible with hyprland. Fix the order of cflags so bundled wlroots headers will be preferred over system ones.
2023-05-01 13:42:16 +01:00
vaxerski
11b7ce14f8 renderer: fix misused size -> transformed size 2023-05-01 02:53:43 +01:00
vaxerski
ddfeebad3d Renderer: add init animation 2023-05-01 02:49:41 +01:00
vaxerski
11e87986a2 makefile: put pkg-config file in the default dir 2023-05-01 01:13:57 +01:00
Vaxry
dbf0b92de7 Plugin header overhaul 2: fixes (Electric boogaloo) (#2201)
* Add wlroots headers to makefile + fix pluginenv with new headers

* Add wlroots to pc
2023-05-01 01:04:25 +01:00
vaxerski
02312cac59 renderer: more checks for background LS optimizations 2023-04-30 01:15:51 +01:00
vaxerski
6501bceb42 workspace: don't check LS-es in startAnim 2023-04-30 01:13:58 +01:00
vaxerski
3580f845e6 monitor: update fullscreen fade on workspace change 2023-04-30 01:12:20 +01:00
vaxerski
b7e69be51e windows: check for fullscreen after rules 2023-04-30 01:01:47 +01:00
scorpion-26
fdb772832f Keep fullscreen mode in moveWindowToWorkspaceSafe (#2191)
Moving a maximised window would always result in the window being
fullscreen instead of maximised
2023-04-29 23:39:09 +01:00
vaxerski
5c3684d0cc pluginenv: configure cmake to build protocols 2023-04-29 17:41:44 +01:00
vaxerski
5a3e3deb33 internal: warp workspace on change only if old mon is last 2023-04-29 13:32:59 +01:00
vaxerski
2946221195 renderer: fix fadingout render on fs 2023-04-29 13:29:32 +01:00
Jan Beich
ce6c13f86b cmake: sync pkg-config --cflags with meson (#2181) 2023-04-29 11:34:28 +03:00
vaxerski
fbb938fcf2 internal: don't change ws on active swap 2023-04-28 21:40:44 +01:00
Mihai Fufezan
093755d53f flake.lock: update nixpkgs 2023-04-28 19:59:31 +03:00
vaxerski
dbb6d9d174 rules: add noinitialfocus 2023-04-28 15:36:08 +01:00
vaxerski
f23455e592 makefile: use -f in copies to avoid errors on running hl 2023-04-27 14:56:43 +01:00
vaxerski
5ce76cd0b0 internal: add tag to version, send hash in release ci 2023-04-27 14:28:40 +01:00
q234rty
49f9ca06c7 Add subdir for cmake as well (#2163) 2023-04-27 13:56:03 +01:00
vaxerski
1a1656ddbf Revert "internal: include headers from protocols/"
This reverts commit 550700bed0.

We can't cuz meson
2023-04-27 13:55:13 +01:00
vaxerski
550700bed0 internal: include headers from protocols/ 2023-04-27 13:40:38 +01:00
Mihai Fufezan
72d2f33b34 Meson: add subdirs to pkg-config file 2023-04-27 01:34:40 +03:00
Ching Pei Yang
38bdbdb0f5 Plugin header overhaul (#2087)
* meson: install headers

* Meson/CMake: add pkg-config file for headers

* makefile: install headers and pkgconfig

* CMake: move protocols to cmake

Co-authored-by: Ching Pei Yang <badnam3o.0@gmail.com>

---------

Co-authored-by: Mihai Fufezan <fufexan@protonmail.com>
Co-authored-by: vaxerski <43317083+vaxerski@users.noreply.github.com>
2023-04-27 00:59:16 +03:00
vaxerski
622132290f [gha] bump flake inputs 2023-04-26 16:33:53 +00:00
vaxerski
77223e1cad deps: update wlroots 2023-04-26 17:23:50 +01:00
vaxerski
4a1fb3e903 keybinds: fix move to named 2023-04-26 16:58:58 +01:00
vaxerski
28ca434fb5 Revert "input: make overlay layers precede constraints"
This reverts commit 1e526411b6.

Issues with input
2023-04-25 21:50:24 +01:00
vaxerski
1e526411b6 input: make overlay layers precede constraints 2023-04-25 17:02:20 +01:00
outfoxxed
849d657595 Window resizing for pseudotiled windows (#2140)
* Window resizing for pseudotiled windows

* Use `m_vRealSize` to determine scaled window size
2023-04-25 16:53:18 +01:00
vaxerski
bf27066fd7 IHyprLayout: add missing static modifiers to config vars 2023-04-25 16:53:01 +01:00
vaxerski
1f80154823 layouts: add missing static modifiers to config vars 2023-04-25 16:49:06 +01:00
outfoxxed
f40272d509 Add follow mouse mode to avoid refocusing under cursor (#2135)
* Add follow mouse mode to avoid refocusing under cursor
2023-04-25 16:00:34 +01:00
vaxerski
e195a51cd4 internal: update fullscreen fade on workspace move 2023-04-24 23:23:12 +01:00
vaxerski
247ff4e60d internal: improve fullscreen fade 2023-04-24 23:21:51 +01:00
vaxerski
eb570c88e6 screencopy: clamp damage to framebuffer 2023-04-24 20:01:44 +01:00
Mihai Fufezan
1a91c6ee60 flake.nix: override wayland 2023-04-24 01:22:26 +03:00
Tyler Schneider
67c73ec100 Fixed a crash when waking up monitors in power-saving mode (#2139) 2023-04-23 22:28:18 +01:00
Jan Beich
f7579fc923 internal: unhardcode sun_path size after a6cfe70428 (#2137)
hyprctl/main.cpp:83:5: warning: 'strncpy' size argument is too large; destination buffer has size 104, but size argument is 107 [-Wfortify-source]
    strncpy(serverAddress.sun_path, socketPath.c_str(), 107);
    ^
hyprctl/main.cpp:146:5: warning: 'strncpy' size argument is too large; destination buffer has size 104, but size argument is 107 [-Wfortify-source]
    strncpy(serverAddress.sun_path, socketPath.c_str(), 107);
    ^
src/managers/EventManager.cpp:70:9: warning: 'strncpy' size argument is too large; destination buffer has size 104, but size argument is 107 [-Wfortify-source]
        strncpy(SERVERADDRESS.sun_path, socketPath.c_str(), 107);
        ^
2023-04-23 21:20:29 +01:00
mekb
fbcbe947da Added moveCursor dispatcher (#2100)
* Added moveCursor dispatcher

* fix error message for moveCursor
2023-04-23 19:50:53 +01:00
vaxerski
97b0368765 xwayland: crude fix for qt dnds 2023-04-22 22:20:48 +01:00
vaxerski
c0f4e9f52e internal: moveToWorkspace before setting ws 2023-04-22 21:13:06 +01:00
vaxerski
4a92deec54 [gha] bump flake inputs 2023-04-22 12:20:19 +00:00
vaxerski
5bf1c32bc0 deps: update wlroots 2023-04-22 13:18:55 +01:00
vaxerski
49fb4cd94d renderer: improvements to layer render detection 2023-04-22 12:54:57 +01:00
vaxerski
99079f7094 cmake: ignore format-truncation 2023-04-22 12:38:04 +01:00
vaxerski
1911e4262b renderer: skip rendering bottom layers on fullscreen opaque 2023-04-22 12:36:54 +01:00
q234rty
d366fc48b8 Remove wlr_output_damage.h (#2121)
This is [removed](9ef98452a3) upstream and hyprland wasn't using it anyway.

This alone will probably not fix the CI but this will allow to clean build hyprland in many configurations.
2023-04-21 18:28:51 +01:00
vaxerski
7b5b4a1049 crashReporter: log on crash 2023-04-21 16:48:36 +01:00
vaxerski
458ea56b86 [gha] bump flake inputs 2023-04-21 14:43:34 +00:00
vaxerski
d03dcc3d99 deps: update wlroots 2023-04-21 15:42:08 +01:00
outfoxxed
2df0d034bc Fix dragging cursor being forced on fullscreen windows (#2115)
Fix two edge cases causing the dragging mouse cursor to be forced on
fullscreen windows:
- hovering over a window border and running the fullscreen dispatcher
- moving mouse focus from a monitor with the resize cursor set to a
different monitor with a fullscreen window
2023-04-21 13:36:55 +01:00
vaxerski
510db64860 hyprctl: allow spaces in cursor themes 2023-04-20 23:59:31 +01:00
vaxerski
b15803510c input: improve mouse release conditions 2023-04-20 00:46:42 +01:00
vaxerski
f914a5a06d input: release mouse buttons before refocuses 2023-04-19 21:36:08 +01:00
q234rty
6225591dbd Fix apps requesting fullscreen (#2099)
Otherwise e446db02f6 breaks fullscreening of image previews for nheko/telegram-desktop/...
2023-04-19 13:26:27 +01:00
DB
e446db02f6 Add windowrule fakeFullScreen (#2043)
Co-authored-by: xVermillionx <xVermillionx@notvalid>
2023-04-18 21:59:08 +01:00
vaxerski
a4330fe378 misc: scan ppids in exec rules 2023-04-18 11:48:56 +01:00
vaxerski
716d713b04 pluginAPI: add note about API expansion 2023-04-17 23:49:42 +01:00
vaxerski
1c50a11688 opengl: keep current rendered workspace in renderData 2023-04-17 23:47:12 +01:00
vaxerski
385fe4e301 events: add render event for plugins 2023-04-17 23:45:03 +01:00
vaxerski
412d46ff65 monitors: set special monitor ID on open 2023-04-17 22:58:59 +01:00
vaxerski
ae82c3a639 screencopy: improve consistency of share indicator 2023-04-17 22:57:24 +01:00
vaxerski
b4f75525d9 pluginAPI: make symbols static 2023-04-17 18:39:40 +01:00
vaxerski
8b3d8dc792 Format: use %lx for all addresses 2023-04-17 17:35:28 +01:00
vaxerski
5cb5b628b8 crashReporter: fix invalid format string 2023-04-17 17:32:07 +01:00
Vaxry
b0d86a7159 CI: Add CodeQL (#2088) 2023-04-17 17:16:19 +01:00
vaxerski
a6cfe70428 internal: avoid buffer overflows with socket paths 2023-04-17 16:38:52 +01:00
vaxerski
b6a7be7663 dispatchers: fix movetoworkspace with bound ws-es 2023-04-17 16:09:46 +01:00
vaxerski
25f14294a8 formats: fix endian ifdef 2023-04-17 15:36:49 +01:00
vaxerski
7c36a3e167 internal: move workspace special check higher in changeWorkspace 2023-04-17 13:32:35 +01:00
vaxerski
785fc8d669 dispatchers: fix missing log param 2023-04-17 13:30:37 +01:00
vaxerski
c62ab1bee7 internal: use setSpecialWorkspace on destroy in sanityCheck 2023-04-16 21:33:28 +01:00
vaxerski
f80f4f3194 dispatchers: fix named ws-es on changeworkspace 2023-04-16 21:32:32 +01:00
Mihai Fufezan
6e58428336 flake.lock: update nixpkgs
Fixes #2044
2023-04-16 20:55:46 +03:00
vaxerski
b05ff89c76 Render: add cursor_zoom 2023-04-16 14:48:38 +01:00
vaxerski
28dfe21584 blur: fixup optimization bool 2023-04-16 14:18:02 +01:00
dann-merlin
c86f06caa0 Fix possible usage of clamp with lo > hi in Vector2D (#2049) 2023-04-16 01:27:14 +01:00
vaxerski
afc887d941 monitor: recalc layout on switched ws 2023-04-16 01:11:57 +01:00
vaxerski
edad24c257 Screencopy: unify frame and client between impls + event
Adds a new event to both hooks and ipc: screencopy
2023-04-15 23:43:41 +01:00
vaxerski
12604b7676 compositor: ignore contraints on warp in moveWorkspaceToMonitor 2023-04-15 21:27:11 +01:00
Jan Beich
63841c8aac Disable systemctl when built without systemd support (#2066)
/bin/sh: systemctl: not found
2023-04-15 20:03:09 +01:00
vaxerski
8944db49be swallow: fix invalid regexes with empty vals 2023-04-15 19:15:59 +01:00
vaxerski
4c4fcc128b input: fix ls focus in non-input area 2023-04-15 16:53:31 +01:00
vaxerski
d6c4ae71d0 damage: fix damage on moves / workspace changes 2023-04-15 16:16:33 +01:00
vaxerski
a6d94eafba tick: don't tick on invalid session 2023-04-15 12:45:25 +01:00
vaxerski
29fc410a8f crashReporter: avoid segfault in deref plugin system 2023-04-15 10:58:46 +01:00
vaxerski
83f1616a65 keybinds: minor adjustments to workspace 2023-04-14 17:51:10 +01:00
vaxerski
7ec23254fd workspace: don't lose monitor with refocus on no warps 2023-04-14 17:03:12 +01:00
vaxerski
c2b5dd1be6 keybinds: only warp on different monitor ws 2023-04-14 16:22:55 +01:00
vaxerski
727160f0a4 workspaces: fixup workspaces not activating on workspace switch 2023-04-14 15:28:22 +01:00
vaxerski
3f2a18a435 keybinds: remove old comment 2023-04-14 15:16:59 +01:00
vaxerski
e329bc2c7b renderer: fix incorrect shouldRenderWindow calcs 2023-04-14 15:16:43 +01:00
vaxerski
8dd0c4fe74 workspaces: deactivate all on monitor switch 2023-04-14 15:08:27 +01:00
vaxerski
cf7c5e4dff misc: fix a warning 2023-04-14 15:06:22 +01:00
vaxerski
287e6c4ede internal: workspace manip handling rework 2023-04-14 15:03:53 +01:00
vaxerski
011600ac6e keybinds: more intelligent fallback on silent move 2023-04-14 01:42:55 +01:00
vaxerski
70eb74c356 fractional-scale: notify all surfaces on window move 2023-04-14 01:36:07 +01:00
vaxerski
260ef788f5 internal: don't sanity check workspaces on internal ws calls 2023-04-13 22:21:11 +01:00
vaxerski
6131e0bef7 keybinds: refocus properly on silent move 2023-04-13 22:20:31 +01:00
vaxerski
41c7d896e3 internal: prevent premature destroy in moveworkspace 2023-04-13 21:09:50 +01:00
Person1873
33d06fb0e5 Add ability to split master when only 1 additional window (#2025)
* fix: enable master split less than 2 windows

added a config flag  "master:allow_small_split"
added config to minimum windows check.
TODO: check that no bug added (remove all masters?)

* IMPL:FIX: multiple master windows full width

Implemented the ability to have multiple master windows filling the full
monitor width in master mode.
this is controlled by the config option master:allow_small_split
(true/false)
this defaults to false as it was the original behaviour before this
patch

* BUGFIX: corrected issue with blanks re: addmaster

FIX 1: Treat ORIENTATION_CENTER the same as ORIENTATION_LEFT unless
there are enough STACK_WINDOWS to fill both wings.
FIX 2: enforced last window always set as master in
MasterLayout::CHyperMasterLayout::calculateWorkspace();
FIX 3: fix 2, also fixed focus issues previously noted.

* Changes requested by vaxerski

changed how we access config variables (by reference not value)
fixed a regression previously missed prior to requested changes.
I had somehow broken the very functionality i meant to add.

* added static keyword to config variables

* removed superfluous static tags

I made a mistake with making too many variables static.
this made them only evaluate once per runtime breaking things majorly.
My appologies. I haven't touched C++ in nearly 20 years.

* remove annoying comment

---------

Co-authored-by: vaxerski <43317083+vaxerski@users.noreply.github.com>
2023-04-13 15:20:58 +01:00
vaxerski
4bc3f9adbe config: ignore invalid paths in configPaths 2023-04-12 22:00:39 +01:00
vaxerski
a22e1174ee screencopy: implement dmabuf 2023-04-12 21:40:51 +01:00
vaxerski
985764c8db listeners: more safety around change 2023-04-12 20:18:55 +01:00
vaxerski
5f000306f5 popups: send scale info 2023-04-12 18:00:07 +01:00
Kajetan Puchalski
efee6a1cda swallow: Add swallow_exception_regex (#2026)
Currently, if a window class is specified in the swallow_regex (e.g.
Kitty) it will swallow every other window spawned by it automatically.
Many other WMs implementing this functionality allow for defining
exceptions from this rule. For instance, we want Kitty to swallow sxiv
or zathura but we do not want Kitty to swallow something like wev.

This commit adds an additional regex - swallow_exception_regex where
these exceptions can be defined. This regex is then compared against the
title of the window about to be swallowed and if it happens to be a
match, aborts the swallowing.

This works because whenever an application that could be swallowed is
launched by a terminal, the class of the terminal remains the same while
the title changes to whatever the application's name is, thus letting it
be matched against a regex.
2023-04-12 13:38:15 +01:00
vaxerski
a68feb5aa0 internal: guarantee activeWindow event type 2023-04-12 13:11:38 +01:00
vaxerski
293df75b97 renderer: workspace rendering improvements 2023-04-12 13:05:57 +01:00
vaxerski
f00e11d457 renderer: fix incorrect delta calc 2023-04-12 12:50:20 +01:00
vaxerski
0fd09579a1 renderer: reset renderModif on fullscreen render 2023-04-12 12:45:16 +01:00
vaxerski
3ae33b951f renderer: add support for rendering workspaces 2023-04-12 12:41:23 +01:00
vaxerski
92fecb8ad4 internal: don't iterate special workspaces in move 2023-04-12 11:24:36 +01:00
Stanisław Zagórowski
ac2cd0f0dc plugins: Add "tick" event (#2029) 2023-04-12 11:18:33 +01:00
Max Verevkin
c2f29be9ba make ext_workspace_unstable impl more atomic (#2023) 2023-04-11 14:28:32 +01:00
vaxerski
16a034a34a keybinds: send pass with a null keymap 2023-04-10 22:42:05 +01:00
vaxerski
ea77622e04 input: send null keycodes on focusSurface 2023-04-10 22:37:55 +01:00
vaxerski
a38b0e736d hyprctl: don't assume output validity in hyprctl workspaces 2023-04-10 21:52:14 +01:00
Hilmar Wiegand
7b43f9f056 Implement window move (#2018) 2023-04-10 20:07:49 +01:00
vaxerski
fa4aef4531 args: print help on invalid arg 2023-04-10 18:26:36 +01:00
vaxerski
56a307d734 Revert "keybinds: avoid sending release on suppressed press"
Issues with XWayland

This reverts commit a1b1480c21.
2023-04-10 15:47:20 +01:00
Hilmar Wiegand
6a4bda60f2 Allow movefocus for empty workspaces (#2011)
* Allow switching to empty workspaces using movefocus

* Allow switching to other workspaces when no windows are focused

* Implement review feedback

* Add option to disable focus fallback

* Remove unnecessary braces
2023-04-10 14:40:03 +01:00
Mihai Fufezan
16d05a5c8b nix/*module: use mdDoc for documentation
Simplify and add more info to the docs.
2023-04-10 15:51:01 +03:00
Mihai Fufezan
7faead75bd nix: update xdph
workflows/nix-update: update all inputs
2023-04-10 14:53:50 +03:00
vaxerski
a1b1480c21 keybinds: avoid sending release on suppressed press 2023-04-10 00:56:08 +01:00
Mihai Fufezan
f3909cf2bf flake.lock: update hyprland-protocols and xdph 2023-04-09 22:03:00 +03:00
vaxerski
4ae784dc53 input: fix kb focus on top layers without interactive flag 2023-04-09 19:53:31 +01:00
vaxerski
dd2372d2e6 deps: update hyprland-protocols 2023-04-09 19:46:34 +01:00
vaxerski
c03db1a1cd props: bump to 0.24.1 2023-04-09 18:12:00 +01:00
vaxerski
3ade6c4a96 renderer: fixup damage repaint 2023-04-09 17:59:24 +01:00
Vaxry
046ad79d11 GlobalShortcuts protocol impl (#1886)
Implements the `hyprland-global-shortcuts-v1` protocol

---------

Co-authored-by: Mihai Fufezan <fufexan@protonmail.com>
2023-04-09 13:48:20 +01:00
vaxerski
e4e653ada6 socket2: receive bytes to avoid endless loops 2023-04-08 23:14:12 +01:00
vaxerski
b32af6ebfb hyprctl: sanity check icons in notify 2023-04-08 18:53:54 +01:00
vaxerski
86852cdc78 textInput: don't double destroy TI 2023-04-08 15:39:14 +01:00
vaxerski
31963f823b screencopy: fix crash in invalid format reads 2023-04-08 13:35:36 +01:00
vaxerski
3ce19e67fe version: bump to 0.24.0 2023-04-08 13:08:56 +01:00
Mihai Fufezan
10b9e9bbe5 nix/xwayland-hidpi: update patch 2023-04-08 00:01:48 +03:00
scorpion-26
07e4ba9d80 Fix crash in CConfigManager::parseKeyword (#1983)
If debug:manual_crash is set on startup, parseKeyword tries
to call g_pHyprNotificationOverlay->addNotification, but
g_pHyprNotificationOverlay isn't initialized yet (is nullptr)

This commit adds a sanity check for that.
2023-04-07 20:15:11 +01:00
vaxerski
5e2d4d644a screencopy: fix crash 2023-04-07 19:21:47 +01:00
vaxerski
50876f1b15 screencopy: fix read on incorrect monitor render 2023-04-07 19:11:30 +01:00
vaxerski
c2a85c9d36 screencopy: minor fixes for damage_ring 2023-04-07 18:04:02 +01:00
vaxerski
41d1fdedf2 output: handle needs_frame 2023-04-07 17:25:56 +01:00
vaxerski
cd1b982b2a internal: listen to output.damage events 2023-04-07 16:31:55 +01:00
Andrew Nitrogenesis
a35ea4d242 Better and more secure argument parsing, and code reformatting (#1976)
* Better and more secure argument parsing, and code reformatting

* Changes to resolve PR conversation

* Formatted via clang-format, fixed typos

* More typos
2023-04-07 15:03:26 +01:00
vaxerski
d8645cd148 internal: release buttons on unmap 2023-04-07 12:54:11 +01:00
vaxerski
c9f7afbf78 subsurfaces: guard node's surface 2023-04-07 12:36:26 +01:00
Andrew Pritchard
dfb78e0593 Fix swiping onto a new workspace with multiple monitors. (#1971)
The previous code could run into issues into the following circumstances:

* The focused monitor is on its rightmost workspace with ID `i`.
* Another monitor has a workspace with ID `i+1`.
* `workspace_swipe_create_new` is enabled.

Then, swiping rightwards attempts to target a new workspace with ID
`i+1`: completing the swipe gesture unintentionally focuses that
workspace on whichever monitor it's already on while leaving the active
monitor in a broken state where it shows no windows but creates new
windows on the workspace it was previously on; and cancelling the swipe
gesture shifts the entire workspace `i+1` to the right by the width of
the active monitor.

By choosing an ID that doesn't exist, this problematic behavior is
avoided.  More specifically, it's the smallest ID greater than any
existing workspace's ID, because otherwise the new workspace that was
seemingly just created to the right of the rightmost workspace could end
up somewhere in the middle of the workspace order.
2023-04-07 12:18:53 +01:00
vaxerski
24ace03780 internal: migrate to damage_ring 2023-04-07 12:18:40 +01:00
vaxerski
569eaff04c swipe: block on locked session 2023-04-07 11:51:52 +01:00
vaxerski
801a17194c [gha] bump flake inputs 2023-04-06 20:09:01 +00:00
vaxerski
1a5d5bf620 deps: update wlroots 2023-04-06 21:03:53 +01:00
vaxerski
366ebc123b internal: don't remove x11 children on parent remove 2023-04-06 20:59:44 +01:00
vaxerski
bc4a51dbbb internal: make togglefloat better visible on small size deltas 2023-04-06 19:45:59 +01:00
vaxerski
80650b6722 keybinds: allow MOD1 as an alias of ALT 2023-04-06 19:28:09 +01:00
vaxerski
a740e3e517 internal: comply to nofocus on vectorToWindow 2023-04-06 13:17:15 +01:00
vaxerski
19809532df input: ignore constraints on touch 2023-04-06 11:34:18 +01:00
vaxerski
110f3fd658 screencopy: fix incorrect resource error post 2023-04-05 15:19:49 +01:00
vaxerski
a80ba54bbc renderer: don't use simple rect on alphazero stencil 2023-04-04 22:58:58 +01:00
vaxerski
00d199b477 monitors: guard scale in onConnect 2023-04-04 22:54:35 +01:00
vaxerski
eea99abc49 debug: allow manual crash from hyprctl 2023-04-04 22:13:36 +01:00
vaxerski
903d298381 [gha] bump flake inputs 2023-04-04 21:13:11 +00:00
vaxerski
49f0f53f51 deps: update wlroots 2023-04-04 22:11:21 +01:00
vaxerski
2dc02bbb39 [gha] bump flake inputs 2023-04-04 21:06:10 +00:00
vaxerski
e7185b338f debug: minor improvements to manual crash 2023-04-04 22:04:32 +01:00
vaxerski
6519c0308c [gha] bump flake inputs 2023-04-04 13:59:06 +00:00
vaxerski
d154a8da20 deps: update wlroots dep 2023-04-04 14:57:35 +01:00
vaxerski
7d9977d028 debug: added manual_crash 2023-04-04 14:50:03 +01:00
vaxerski
882be7765b toplevelExport: honor overlay_cursor 2023-04-04 00:58:30 +01:00
vaxerski
99314fbe71 render: plug missing software cursor unlocks 2023-04-04 00:46:58 +01:00
vaxerski
bab949599f [gha] build man pages 2023-04-03 22:47:26 +00:00
Vaxry
f81b3eef4f docs: update issue guidelines for asan env 2023-04-03 23:47:01 +01:00
vaxerski
c50df4c0c3 screencopy: allow on legacy renderer 2023-04-03 23:34:08 +01:00
vaxerski
ee85dd6b61 [gha] bump flake inputs 2023-04-03 22:29:28 +00:00
vaxerski
bae19cb10e deps: update wlroots dep 2023-04-03 23:24:09 +01:00
vaxerski
2f7fb2f553 input: don't set icon on held buttons without a drag 2023-04-03 23:17:06 +01:00
vaxerski
23001f6144 input: don't overset resize icons on drag 2023-04-03 23:15:33 +01:00
vaxerski
55d585ce17 input: fix click-to-refocus not working on loose 2023-04-03 23:09:44 +01:00
vaxerski
d3b0c90356 internal: rename ensureDPMS to ensureMonitorStatus 2023-04-03 22:52:09 +01:00
vaxerski
a43b18ae26 Feat: add initial class/title to hyprctl clients 2023-04-03 19:16:51 +01:00
Vaxry
0a099ca2ab Hyprland Screencopy impl (#1800)
---------

Co-authored-by: Mihai Fufezan <fufexan@protonmail.com>
2023-04-03 17:01:05 +01:00
vaxerski
e6211eef00 log: Move stdout log disabling to the end of init 2023-04-03 10:41:49 +01:00
vaxerski
b1426cad28 input: fix minor issue with holding focus 2023-04-02 13:42:57 +01:00
vaxerski
0fc145c52c input: hold focus on mouse buttons 2023-04-02 13:30:45 +01:00
vaxerski
c2b25f4701 swallow: move swallowed on workspace change 2023-04-02 10:24:17 +01:00
vaxerski
88a96110b7 config: default no direct scanout to true 2023-04-01 19:37:30 +01:00
vaxerski
2b4d96e0ef examples: pull correct wlr dirs in example plugin 2023-04-01 02:22:52 +01:00
Jan Beich
16bc5997bb Misc FreeBSD fixes (#1926)
* helpers: drop incomplete GNU/kFreeBSD bits

Debian with FreeBSD kernel lacks Wayland-related packages and is not
officially supported since Jessie.

* KeybindManager: check VT ioctl availability instead of hardcoding

* plugins: add missing header for libc++ after 430778293e

src/plugins/PluginAPI.cpp:299:33: error: implicit instantiation of undefined template 'std::basic_istringstream<char>'
    std::istringstream          inStream(SYMBOLS);
                                ^
/usr/include/c++/v1/iosfwd:140:32: note: template is declared here
    class _LIBCPP_TEMPLATE_VIS basic_istringstream;
                               ^

* plugins: prefer llvm-nm with Clang after 430778293e

nm: invalid option -- j
2023-03-31 20:39:04 +01:00
vaxerski
7680cd549c plugins: mark getFunctionAddressFromSignature deprecated 2023-03-31 18:43:00 +01:00
vaxerski
1df8b1957e plugins: use new lookups in example 2023-03-31 18:34:24 +01:00
vaxerski
430778293e plugins: Add an API entry for finding functions by name 2023-03-31 18:31:11 +01:00
vaxerski
de3b00b5ee renderer: go back to rendering layers without reverse 2023-03-31 17:44:36 +01:00
vaxerski
24ef5d888c [gha] build man pages 2023-03-31 12:17:19 +00:00
vaxerski
5688e24b8a docs: update crash report dirs 2023-03-31 13:16:52 +01:00
NotAShelf
3d9bf17f11 crashReporter: try $XDG_CACHE_HOME before $HOME (#1920) 2023-03-31 13:15:24 +01:00
vaxerski
614ea53ad7 Renderer: fix dim easing 2023-03-30 21:08:20 +01:00
vaxerski
b88de63abb Input: fix always_follow_on_dnd 2023-03-30 00:34:24 +01:00
Alexander Seiler
60527ab180 Fix some typos (#1907)
Signed-off-by: Alexander Seiler <seileralex@gmail.com>
2023-03-29 23:44:25 +01:00
vaxerski
d6241a3086 windows: only connect unmap when mapped 2023-03-28 20:17:47 +01:00
vaxerski
df54ab40ce layer: allow focus on top/overlay surfaces without a window 2023-03-28 17:21:11 +01:00
vaxerski
6fec5bfbeb keybinds: improve movefocus on fullscreen 2023-03-27 15:19:27 +01:00
Oliver Ni
e994b0c8b8 Fix nix build options 2023-03-27 11:00:37 +03:00
vaxerski
3343aac6bf feat: add forcergbx rule 2023-03-26 02:00:24 +01:00
vaxerski
41f7736c85 config: default manual animations to false 2023-03-24 22:24:12 +00:00
vaxerski
c418007c68 shaders: fix missing discardAlphaZero 2023-03-24 19:43:50 +00:00
vaxerski
cc2c270dde log: log wlr logs to stdout 2023-03-24 19:38:09 +00:00
vaxerski
70e3cb8151 feat: add debug:enable_stdout_logs 2023-03-24 19:37:37 +00:00
Vaxry
a80f8f257f Feat: Introduce render_ahead_of_time (#1863) 2023-03-24 19:23:16 +00:00
vaxerski
b3a70b565e subsurfaces: avoid reading destroyed surfaces 2023-03-24 18:44:42 +00:00
vaxerski
e73c6fd3b0 logs: disable stdout after init 2023-03-24 13:00:54 +00:00
vaxerski
a5a0434fff dbus: don't update vars in nests 2023-03-24 03:21:38 +00:00
vaxerski
463690a27a keybinds: allow code: prefix 2023-03-23 13:05:23 +00:00
vaxerski
471ac474a1 core: remove old redundant shutdown stuff 2023-03-23 03:07:57 +00:00
vaxerski
a3fda12ba1 window: unassign surface on unmap 2023-03-23 00:39:32 +00:00
vaxerski
0268bb9888 surface: set to nullptr after destroy() 2023-03-23 00:22:49 +00:00
vaxerski
3a3a3f7bdb popups: fix heap-use-after-free 2023-03-23 00:22:49 +00:00
Mihai Fufezan
cf51a31807 Nix: disable HiDPI for default package
NOTE: the package `hyprland-no-hidpi` was removed, and instead
`hyprland-hidpi` exists now.
2023-03-22 19:10:46 +02:00
Mihai Fufezan
5be42965ff Nix: rebase wlroots-hidpi patch 2023-03-22 19:10:46 +02:00
vaxerski
a8b3be2c9c config: add misc:suppress_portal_warnings 2023-03-22 12:17:16 +00:00
staz
5ce91bb0fd Added overflow check for blur radius (#1847)
* internal: added overflow check for blur radius

---------

Co-authored-by: vaxerski <43317083+vaxerski@users.noreply.github.com>
2023-03-21 19:01:24 +00:00
vaxerski
adf5d8a114 monitors: update surface outputs on recover from unsafe 2023-03-21 17:46:26 +00:00
vaxerski
cb229f6436 compositor: adjust xdp error cases 2023-03-20 22:26:54 +00:00
vaxerski
e80e93fcda [gha] bump flake inputs 2023-03-20 21:57:13 +00:00
vaxerski
37ced6aca4 wlroots: update dep 2023-03-20 21:53:00 +00:00
Mihai Fufezan
5ffe5dd594 Nix: add pango dep 2023-03-20 18:22:34 +02:00
lisuke
dc78c58c77 fix: a fullscreen bug. (#1821) (#1831) 2023-03-20 16:07:18 +00:00
vaxerski
22721a37d5 hyprctl: add notify 2023-03-20 16:00:54 +00:00
vaxerski
dd4270eadf notifs: add ICON_OK to icons 2023-03-20 15:49:46 +00:00
vaxerski
e2923a9385 meson: add pango deps 2023-03-20 15:39:43 +00:00
vaxerski
316674fecf notifs: use empty color for auto 2023-03-20 15:32:11 +00:00
vaxerski
34da16b7e6 plugin api: add addNotificationV2
Allows for issuing fancy notifs via api
2023-03-20 15:23:25 +00:00
vaxerski
71a95a581f feat: add pretty notifications 2023-03-20 15:03:09 +00:00
Vaxry
788a8f7c13 internal: wrap wlr surfaces (#1822) 2023-03-20 15:00:58 +00:00
vaxerski
d23bbd1687 workspaces: preserve pin on moves 2023-03-20 01:50:46 +00:00
vaxerski
7a514f41a3 Focus: warp cursor on movewindow 2023-03-20 01:42:21 +00:00
vaxerski
928de33447 monitors: more guards for safety 2023-03-19 02:19:52 +00:00
Mihai Fufezan
0624455591 Meson: add rdynamic ld flag 2023-03-18 21:01:24 +02:00
vaxerski
2ba5238b8e groups: fix moving between displays 2023-03-18 16:30:29 +00:00
vaxerski
00c2ca4697 config: improve ux on workspace and transform 2023-03-18 16:12:43 +00:00
vaxerski
d544c30551 LS: don't try to get rules on non-existent ls 2023-03-18 15:02:00 +00:00
vaxerski
ef80a69399 config: fix long variables being substrd 2023-03-18 14:57:59 +00:00
vaxerski
6e6971606d windowrules: allow monitor by str 2023-03-18 01:34:06 +00:00
vaxerski
e5ad53ac42 config: make default config use hyphenated dev names 2023-03-18 01:06:03 +00:00
vaxerski
e98ee49aee LS: fix support for legacy blurls 2023-03-17 23:36:36 +00:00
vaxerski
d797d9905d LS: support address: in layerrules 2023-03-17 23:33:03 +00:00
vaxerski
e5870d47c7 LS: add blur and ignorezero rules 2023-03-17 23:16:13 +00:00
vaxerski
91a565c7b0 monitors: don't refocus on apply rule 2023-03-17 20:34:33 +00:00
vaxerski
5b924aaf60 crashReporter: add hl ver 2023-03-17 11:51:16 +00:00
vaxerski
606cb2832a keybinds: remember last workspace on focusmonitor 2023-03-16 16:40:28 +00:00
vaxerski
4b52c1e68f monitors: remove from monitors on unsafe 2023-03-16 16:33:27 +00:00
vaxerski
e77ebec629 monitors: guard output in damageSurface 2023-03-16 16:32:03 +00:00
vaxerski
162f235972 switches: do not fire on no change in toggle 2023-03-16 16:30:22 +00:00
vaxerski
e8adae65fe debug: unbreak debug builds 2023-03-16 15:40:50 +00:00
vaxerski
96718d8b09 dpms: fix keyboard dpms 2023-03-16 15:29:48 +00:00
vaxerski
5d44ea802a monitors: guard output when read 2023-03-16 14:03:40 +00:00
vaxerski
d9d57ce39a monitors: fix segfault on non-unsafe remove 2023-03-16 01:04:54 +00:00
vaxerski
3e261b1fa7 dpms: fix key_press_enables_dpms 2023-03-16 00:30:07 +00:00
vaxerski
cee7f11d8b hyprctl: ignore null output monitors 2023-03-16 00:18:44 +00:00
Mihai Fufezan
1c67849bf1 Nix: fix meson patch again 2023-03-15 20:45:44 +02:00
Mihai Fufezan
595f2052c4 Nix: fix meson patch 2023-03-15 20:29:25 +02:00
vaxerski
f5669a7d6b events: guard output in change 2023-03-15 17:01:20 +00:00
vaxerski
25d3d73dbf monitors: fixes to unsafe mode 2023-03-15 15:11:41 +00:00
Mihai Fufezan
569ae86c90 props.json: update to 0.23.0 2023-03-15 00:29:21 +02:00
107 changed files with 4805 additions and 2091 deletions

View File

@@ -1,30 +0,0 @@
name: Flawfinder
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
flawfinder:
name: Flawfinder Checks
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Scan with Flawfinder
uses: david-a-wheeler/flawfinder@8e4a779ad59dbfaee5da586aa9210853b701959c
with:
arguments: '--sarif ./'
output: 'flawfinder_results.sarif'
- name: Upload analysis results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: ${{github.workspace}}/flawfinder_results.sarif

View File

@@ -17,7 +17,9 @@ jobs:
- name: Create tarball with submodules
id: tar
run: mkdir hyprland-source; mv ./* ./hyprland-source || tar -czv --owner=0 --group=0 --no-same-owner --no-same-permissions -f source.tar.gz *
run: |
sed -i "1s/^/#define GIT_COMMIT_HASH $(git rev-parse HEAD)\n#define GIT_TAG \"$(git describe --tags)\"\n/" ./src/defines.hpp
mkdir hyprland-source; mv ./* ./hyprland-source || tar -czv --owner=0 --group=0 --no-same-owner --no-same-permissions -f source.tar.gz *
- id: whatrelease
name: Get latest release

76
.github/workflows/security-checks.yml vendored Normal file
View File

@@ -0,0 +1,76 @@
name: Security Checks
on: [push, pull_request]
jobs:
flawfinder:
name: Flawfinder Checks
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Scan with Flawfinder
uses: david-a-wheeler/flawfinder@8e4a779ad59dbfaee5da586aa9210853b701959c
with:
arguments: '--sarif ./'
output: 'flawfinder_results.sarif'
- name: Upload analysis results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: ${{github.workspace}}/flawfinder_results.sarif
codeql:
name: CodeQL
runs-on: ubuntu-latest
container:
image: archlinux
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'cpp' ]
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
- name: Init Hyprland build
run: |
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
pacman --noconfirm --noprogressbar -Syyu
pacman --noconfirm --noprogressbar -Sy glslang libepoxy libfontenc libxcvt libxfont2 libxkbfile vulkan-headers vulkan-validation-layers xcb-util-errors xcb-util-renderutil xcb-util-wm xorg-fonts-encodings xorg-server-common xorg-setxkbmap xorg-xkbcomp xorg-xwayland git cmake go clang lld libc++ pkgconf meson ninja wayland wayland-protocols libinput libxkbcommon pixman glm libdrm libglvnd cairo pango systemd scdoc base-devel seatd python libliftoff
useradd -m githubuser
echo -e "root ALL=(ALL:ALL) ALL\ngithubuser ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers
su githubuser -c "cd ~ && git clone https://aur.archlinux.org/libdisplay-info.git && cd ./libdisplay-info && makepkg -si --skippgpcheck --noconfirm --noprogressbar"
git config --global --add safe.directory /__w/Hyprland/Hyprland
- name: Checkout Hyprland
uses: actions/checkout@v3
with:
submodules: recursive
- name: Build Hyprland
run: |
git submodule sync --recursive && git submodule update --init --force --recursive
make all
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"

View File

@@ -10,6 +10,10 @@ project(Hyprland
VERSION ${VER}
)
set(HYPRLAND_VERSION ${VER})
set(PREFIX ${CMAKE_INSTALL_PREFIX})
configure_file(hyprland.pc.in hyprland.pc @ONLY)
set(CMAKE_MESSAGE_LOG_LEVEL "STATUS")
message(STATUS "Gathering git info")
@@ -39,9 +43,24 @@ execute_process(
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_DIRTY
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(
COMMAND sh -c "git describe --tags"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_TAG
OUTPUT_STRIP_TRAILING_WHITESPACE)
#
#
find_program(WaylandScanner NAMES wayland-scanner)
message(STATUS "Found WaylandScanner at ${WaylandScanner}")
execute_process(
COMMAND pkg-config --variable=pkgdatadir wayland-protocols
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE WAYLAND_PROTOCOLS_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE)
message(STATUS "Found wayland-protocols at ${WAYLAND_PROTOCOLS_DIR}")
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
message(STATUS "Configuring Hyprland in Debug with CMake")
add_compile_definitions(HYPRLAND_DEBUG)
@@ -54,7 +73,8 @@ include_directories(
.
"subprojects/wlroots/include/"
"subprojects/wlroots/build/include/"
"subprojects/udis86/")
"subprojects/udis86/"
"protocols/")
set(CMAKE_CXX_STANDARD 23)
add_compile_definitions(WLR_USE_UNSTABLE)
add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-unused-value -Wno-missing-field-initializers -Wno-narrowing -Wno-pointer-arith)
@@ -66,16 +86,26 @@ message(STATUS "Checking deps...")
find_package(Threads REQUIRED)
find_package(PkgConfig REQUIRED)
pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-server wayland-client wayland-cursor wayland-protocols cairo libdrm egl xkbcommon libinput) # we do not check for wlroots, as we provide it ourselves
find_package(OpenGL REQUIRED)
pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-server wayland-client wayland-cursor wayland-protocols cairo libdrm xkbcommon libinput pango pangocairo pixman-1) # we do not check for wlroots, as we provide it ourselves
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
add_executable(Hyprland ${SRCFILES})
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
message(STATUS "Setting debug flags")
target_link_libraries(Hyprland asan)
add_compile_options(-pg -no-pie -fno-builtin -fsanitize=address)
add_link_options(-pg -no-pie -fno-builtin)
endif()
include(CheckLibraryExists)
check_library_exists(execinfo backtrace "" HAVE_LIBEXECINFO)
if(HAVE_LIBEXECINFO)
target_link_libraries(Hyprland PRIVATE execinfo)
target_link_libraries(Hyprland execinfo)
endif()
if(LEGACY_RENDERER)
@@ -88,8 +118,8 @@ if(NO_XWAYLAND)
add_compile_definitions(NO_XWAYLAND)
else()
message(STATUS "XWAYLAND Enabled (NO_XWAYLAND not defined) checking deps...")
pkg_check_modules(xcbdep REQUIRED xcb)
target_link_libraries(Hyprland xcb)
pkg_check_modules(xcbdep REQUIRED IMPORTED_TARGET xcb)
target_link_libraries(Hyprland PkgConfig::xcbdep)
endif()
if(NO_SYSTEMD)
@@ -111,7 +141,8 @@ target_compile_definitions(Hyprland
"GIT_COMMIT_HASH=\"${GIT_COMMIT_HASH}\""
"GIT_BRANCH=\"${GIT_BRANCH}\""
"GIT_COMMIT_MESSAGE=\"${GIT_COMMIT_MESSAGE}\""
"GIT_DIRTY=\"${GIT_DIRTY}\"")
"GIT_DIRTY=\"${GIT_DIRTY}\""
"GIT_TAG=\"${GIT_TAG}\"")
target_link_libraries(Hyprland rt)
@@ -121,28 +152,47 @@ include(CPack)
message(STATUS "Setting link libraries")
function(protocol protoPath protoName external)
if (external)
execute_process(
COMMAND ${WaylandScanner} server-header ${protoPath} protocols/${protoName}-protocol.h
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
execute_process(
COMMAND ${WaylandScanner} private-code ${protoPath} protocols/${protoName}-protocol.c
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
target_sources(Hyprland PRIVATE protocols/${protoName}-protocol.c)
else()
execute_process(
COMMAND ${WaylandScanner} server-header ${WAYLAND_PROTOCOLS_DIR}/${protoPath} protocols/${protoName}-protocol.h
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
execute_process(
COMMAND ${WaylandScanner} private-code ${WAYLAND_PROTOCOLS_DIR}/${protoPath} protocols/${protoName}-protocol.c
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
target_sources(Hyprland PRIVATE protocols/${protoName}-protocol.c)
endif()
endfunction()
target_link_libraries(Hyprland PkgConfig::deps)
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
message(STATUS "Setting debug flags")
target_link_libraries(Hyprland asan)
add_compile_options(-pg -no-pie -fno-builtin -fsanitize=address)
add_link_options(-pg -no-pie -fno-builtin)
endif()
target_link_libraries(Hyprland
${CMAKE_SOURCE_DIR}/subprojects/wlroots/build/libwlroots.so.12032 # wlroots is provided by us
pixman-1
OpenGL
GLESv2
pthread
${CMAKE_THREAD_LIBS_INIT}
${CMAKE_SOURCE_DIR}/ext-workspace-unstable-v1-protocol.o
${CMAKE_SOURCE_DIR}/wlr-foreign-toplevel-management-unstable-v1-protocol.o
${CMAKE_SOURCE_DIR}/hyprland-toplevel-export-v1-protocol.o
${CMAKE_SOURCE_DIR}/fractional-scale-v1-protocol.o
${CMAKE_SOURCE_DIR}/text-input-unstable-v1-protocol.o
OpenGL::EGL
OpenGL::GL
Threads::Threads
${CMAKE_SOURCE_DIR}/subprojects/udis86/build/libudis86/liblibudis86.a
)
protocol("protocols/ext-workspace-unstable-v1.xml" "ext-workspace-unstable-v1" true)
protocol("protocols/idle.xml" "idle" true)
protocol("protocols/pointer-constraints-unstable-v1.xml" "pointer-constraints-unstable-v1" true)
protocol("protocols/tablet-unstable-v2.xml" "tablet-unstable-v2" true)
protocol("protocols/wlr-foreign-toplevel-management-unstable-v1.xml" "wlr-foreign-toplevel-management-unstable-v1" true)
protocol("protocols/wlr-layer-shell-unstable-v1.xml" "wlr-layer-shell-unstable-v1" true)
protocol("protocols/wlr-output-power-management-unstable-v1.xml" "wlr-output-power-management-unstable-v1" true)
protocol("protocols/wlr-screencopy-unstable-v1.xml" "wlr-screencopy-unstable-v1" true)
protocol("subprojects/hyprland-protocols/protocols/hyprland-global-shortcuts-v1.xml" "hyprland-global-shortcuts-v1" true)
protocol("subprojects/hyprland-protocols/protocols/hyprland-toplevel-export-v1.xml" "hyprland-toplevel-export-v1" true)
protocol("stable/xdg-shell/xdg-shell.xml" "xdg-shell" false)
protocol("unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml" "linux-dmabuf-unstable-v1" false)
protocol("staging/fractional-scale/fractional-scale-v1.xml" "fractional-scale-v1" false)
protocol("unstable/text-input/text-input-unstable-v1.xml" "text-input-unstable-v1" false)

234
Makefile
View File

@@ -1,201 +1,70 @@
include config.mk
CFLAGS += -I. -DWLR_USE_UNSTABLE -std=c99
WAYLAND_PROTOCOLS=$(shell pkg-config --variable=pkgdatadir wayland-protocols)
WAYLAND_SCANNER=$(shell pkg-config --variable=wayland_scanner wayland-scanner)
PKGS = wlroots wayland-server xcb xkbcommon libinput
CFLAGS += $(foreach p,$(PKGS),$(shell pkg-config --cflags $(p)))
LDLIBS += $(foreach p,$(PKGS),$(shell pkg-config --libs $(p)))
DATE=$(shell date "+%d %b %Y")
xdg-shell-protocol.h:
$(WAYLAND_SCANNER) server-header \
$(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
xdg-shell-protocol.c:
$(WAYLAND_SCANNER) private-code \
$(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
xdg-shell-protocol.o: xdg-shell-protocol.h
wlr-layer-shell-unstable-v1-protocol.h:
$(WAYLAND_SCANNER) server-header \
protocols/wlr-layer-shell-unstable-v1.xml $@
wlr-layer-shell-unstable-v1-protocol.c:
$(WAYLAND_SCANNER) private-code \
protocols/wlr-layer-shell-unstable-v1.xml $@
wlr-layer-shell-unstable-v1-protocol.o: wlr-layer-shell-unstable-v1-protocol.h
wlr-screencopy-unstable-v1-protocol.h:
$(WAYLAND_SCANNER) server-header \
protocols/wlr-screencopy-unstable-v1.xml $@
wlr-screencopy-unstable-v1-protocol.c:
$(WAYLAND_SCANNER) private-code \
protocols/wlr-screencopy-unstable-v1.xml $@
wlr-screencopy-unstable-v1-protocol.o: wlr-screencopy-unstable-v1-protocol.h
ext-workspace-unstable-v1-protocol.h:
$(WAYLAND_SCANNER) server-header \
protocols/ext-workspace-unstable-v1.xml $@
ext-workspace-unstable-v1-protocol.c:
$(WAYLAND_SCANNER) private-code \
protocols/ext-workspace-unstable-v1.xml $@
ext-workspace-unstable-v1-protocol.o: ext-workspace-unstable-v1-protocol.h
pointer-constraints-unstable-v1-protocol.h:
$(WAYLAND_SCANNER) server-header \
protocols/pointer-constraints-unstable-v1.xml $@
pointer-constraints-unstable-v1-protocol.c:
$(WAYLAND_SCANNER) private-code \
protocols/pointer-constraints-unstable-v1.xml $@
pointer-constraints-unstable-v1-protocol.o: pointer-constraints-unstable-v1-protocol.h
tablet-unstable-v2-protocol.h:
$(WAYLAND_SCANNER) server-header \
protocols/tablet-unstable-v2.xml $@
tablet-unstable-v2-protocol.c:
$(WAYLAND_SCANNER) private-code \
protocols/tablet-unstable-v2.xml $@
tablet-unstable-v2-protocol.o: tablet-unstable-v2-protocol.h
idle-protocol.h:
$(WAYLAND_SCANNER) server-header \
protocols/idle.xml $@
idle-protocol.c:
$(WAYLAND_SCANNER) private-code \
protocols/idle.xml $@
idle-protocol.o: idle-protocol.h
wlr-output-power-management-unstable-v1-protocol.h:
$(WAYLAND_SCANNER) server-header \
protocols/wlr-output-power-management-unstable-v1.xml $@
wlr-output-power-management-unstable-v1-protocol.c:
$(WAYLAND_SCANNER) private-code \
protocols/wlr-output-power-management-unstable-v1.xml $@
wlr-output-power-management-unstable-v1-protocol.o: wlr-output-power-management-unstable-v1-protocol.h
hyprland-toplevel-export-v1-protocol.h:
$(WAYLAND_SCANNER) server-header \
subprojects/hyprland-protocols/protocols/hyprland-toplevel-export-v1.xml $@
hyprland-toplevel-export-v1-protocol.c:
$(WAYLAND_SCANNER) private-code \
subprojects/hyprland-protocols/protocols/hyprland-toplevel-export-v1.xml $@
hyprland-toplevel-export-v1-protocol.o: hyprland-toplevel-export-v1-protocol.h
linux-dmabuf-unstable-v1-protocol.h:
$(WAYLAND_SCANNER) server-header \
$(WAYLAND_PROTOCOLS)/unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml $@
linux-dmabuf-unstable-v1-protocol.c:
$(WAYLAND_SCANNER) private-code \
$(WAYLAND_PROTOCOLS)/unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml $@
linux-dmabuf-unstable-v1-protocol.o: linux-dmabuf-unstable-v1-protocol.h
wlr-foreign-toplevel-management-unstable-v1-protocol.h:
$(WAYLAND_SCANNER) server-header \
protocols/wlr-foreign-toplevel-management-unstable-v1.xml $@
wlr-foreign-toplevel-management-unstable-v1-protocol.c:
$(WAYLAND_SCANNER) private-code \
protocols/wlr-foreign-toplevel-management-unstable-v1.xml $@
wlr-foreign-toplevel-management-unstable-v1-protocol.o: wlr-foreign-toplevel-management-unstable-v1-protocol.h
fractional-scale-v1-protocol.h:
$(WAYLAND_SCANNER) server-header \
$(WAYLAND_PROTOCOLS)/staging/fractional-scale/fractional-scale-v1.xml $@
fractional-scale-v1-protocol.c:
$(WAYLAND_SCANNER) private-code \
$(WAYLAND_PROTOCOLS)/staging/fractional-scale/fractional-scale-v1.xml $@
fractional-scale-v1-protocol.o: fractional-scale-v1-protocol.h
text-input-unstable-v1-protocol.h:
$(WAYLAND_SCANNER) server-header \
$(WAYLAND_PROTOCOLS)/unstable/text-input/text-input-unstable-v1.xml $@
text-input-unstable-v1-protocol.c:
$(WAYLAND_SCANNER) private-code \
$(WAYLAND_PROTOCOLS)/unstable/text-input/text-input-unstable-v1.xml $@
text-input-unstable-v1-protocol.o: text-input-unstable-v1-protocol.h
PREFIX = /usr/local
legacyrenderer:
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -DLEGACY_RENDERER:BOOL=true -S . -B ./build -G Ninja
cmake --build ./build --config Release --target all -j$(shell nproc)
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
legacyrendererdebug:
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -DLEGACY_RENDERER:BOOL=true -S . -B ./build -G Ninja
cmake --build ./build --config Release --target all -j$(shell nproc)
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
release:
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build -G Ninja
cmake --build ./build --config Release --target all -j$(shell nproc)
cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
debug:
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -S . -B ./build -G Ninja
cmake --build ./build --config Debug --target all -j$(shell nproc)
cmake --build ./build --config Debug --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
clear:
rm -rf build
rm -f *.o *-protocol.h *-protocol.c
rm -f ./protocols/*-protocol.h ./protocols/*-protocol.c
rm -f ./hyprctl/hyprctl
rm -rf ./subprojects/wlroots/build
all:
make clear
make fixwlr
cd ./subprojects/wlroots && meson setup build/ --buildtype=release && ninja -C build/ && cp ./build/libwlroots.so.12032 ${PREFIX}/lib/ || echo "Could not install libwlroots to ${PREFIX}/lib/libwlroots.so.12032"
cd subprojects/udis86 && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B./build -G Ninja && cmake --build ./build --config Release --target all -j$(shell nproc)
make protocols
make release
make -C hyprctl all
$(MAKE) clear
$(MAKE) fixwlr
cd ./subprojects/wlroots && meson setup build/ --buildtype=release && ninja -C build/ && mkdir -p ${PREFIX}/lib/ && cp ./build/libwlroots.so.12032 ${PREFIX}/lib/ || echo "Could not install libwlroots to ${PREFIX}/lib/libwlroots.so.12032"
cd subprojects/udis86 && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B./build -G Ninja && cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
$(MAKE) release
$(MAKE) -C hyprctl all
install:
make clear
make fixwlr
cd ./subprojects/wlroots && meson setup build/ --buildtype=release && ninja -C build/ && cp ./build/libwlroots.so.12032 ${PREFIX}/lib/ || echo "Could not install libwlroots to ${PREFIX}/lib/libwlroots.so.12032"
cd subprojects/udis86 && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B./build -G Ninja && cmake --build ./build --config Release --target all -j$(shell nproc) && cd ../..
make protocols
make release
make -C hyprctl all
$(MAKE) clear
$(MAKE) fixwlr
cd ./subprojects/wlroots && meson setup build/ --buildtype=release && ninja -C build/ && mkdir -p ${PREFIX}/lib/ && cp ./build/libwlroots.so.12032 ${PREFIX}/lib/ || echo "Could not install libwlroots to ${PREFIX}/lib/libwlroots.so.12032"
cd subprojects/udis86 && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B./build -G Ninja && cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF` && cd ../..
$(MAKE) release
$(MAKE) -C hyprctl all
mkdir -p ${PREFIX}/share/wayland-sessions
mkdir -p ${PREFIX}/bin
cp ./build/Hyprland ${PREFIX}/bin
cp ./hyprctl/hyprctl ${PREFIX}/bin
cp -f ./build/Hyprland ${PREFIX}/bin
cp -f ./hyprctl/hyprctl ${PREFIX}/bin
if [ ! -f ${PREFIX}/share/wayland-sessions/hyprland.desktop ]; then cp ./example/hyprland.desktop ${PREFIX}/share/wayland-sessions; fi
mkdir -p ${PREFIX}/share/hyprland
cp ./assets/wall_2K.png ${PREFIX}/share/hyprland
cp ./assets/wall_4K.png ${PREFIX}/share/hyprland
cp ./assets/wall_8K.png ${PREFIX}/share/hyprland
install -Dm644 -t ${PREFIX}/share/man/man1 ./docs/*.1
mkdir -p ${PREFIX}/share/man/man1
install -m644 ./docs/*.1 ${PREFIX}/share/man/man1
mkdir -p ${PREFIX}/include/hyprland
mkdir -p ${PREFIX}/include/hyprland/protocols
mkdir -p ${PREFIX}/include/hyprland/wlroots
mkdir -p ${PREFIX}/share/pkgconfig
find src -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland
cd subprojects/wlroots/include && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlroots && cd ../../..
cp ./protocols/*-protocol.h ${PREFIX}/include/hyprland/protocols
cp ./build/hyprland.pc ${PREFIX}/share/pkgconfig
if [ -d /usr/share/pkgconfig ]; then cp ./build/hyprland.pc /usr/share/pkgconfig 2>/dev/null || true; fi
cleaninstall:
echo -en "make cleaninstall has been DEPRECATED, you should avoid using it in the future.\nRunning make install instead...\n"
make install
echo -en "$(MAKE) cleaninstall has been DEPRECATED, you should avoid using it in the future.\nRunning $(MAKE) install instead...\n"
$(MAKE) install
uninstall:
rm -f ${PREFIX}/share/wayland-sessions/hyprland.desktop
@@ -206,39 +75,44 @@ uninstall:
rm -f ${PREFIX}/share/man/man1/Hyprland.1
rm -f ${PREFIX}/share/man/man1/hyprctl.1
protocols: xdg-shell-protocol.o wlr-layer-shell-unstable-v1-protocol.o wlr-screencopy-unstable-v1-protocol.o idle-protocol.o ext-workspace-unstable-v1-protocol.o pointer-constraints-unstable-v1-protocol.o tablet-unstable-v2-protocol.o wlr-output-power-management-unstable-v1-protocol.o linux-dmabuf-unstable-v1-protocol.o hyprland-toplevel-export-v1-protocol.o wlr-foreign-toplevel-management-unstable-v1-protocol.o fractional-scale-v1-protocol.o text-input-unstable-v1-protocol.o
fixwlr:
sed -i -E 's/(soversion = 12)([^032]|$$)/soversion = 12032/g' subprojects/wlroots/meson.build
sed -E -i -e 's/(soversion = 12)([^032]|$$)/soversion = 12032/g' subprojects/wlroots/meson.build
rm -rf ./subprojects/wlroots/build
config:
make protocols
make fixwlr
$(MAKE) fixwlr
meson setup subprojects/wlroots/build subprojects/wlroots --prefix=${PREFIX} --buildtype=release -Dwerror=false -Dexamples=false
ninja -C subprojects/wlroots/build/
ninja -C subprojects/wlroots/build/ install
cd subprojects/udis86 && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build -G Ninja && cmake --build ./build --config Release --target all -j$(shell nproc)
cd subprojects/udis86 && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build -G Ninja && cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
pluginenv:
make protocols
cd subprojects/udis86 && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build -G Ninja && cmake --build ./build --config Release --target all -j`nproc 2>/dev/null || getconf NPROCESSORS_CONF`
cd subprojects/udis86 && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build -G Ninja && cmake --build ./build --config Release --target all -j$(shell nproc)
make fixwlr
$(MAKE) fixwlr
meson setup subprojects/wlroots/build subprojects/wlroots --prefix=${PREFIX} --buildtype=release -Dwerror=false -Dexamples=false
ninja -C subprojects/wlroots/build/
configdebug:
make protocols
cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -S . -B ./build -G Ninja
make fixwlr
mkdir -p ${PREFIX}/include/hyprland
mkdir -p ${PREFIX}/include/hyprland/protocols
mkdir -p ${PREFIX}/include/hyprland/wlroots
mkdir -p ${PREFIX}/share/pkgconfig
find src -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland
cd subprojects/wlroots/include && find . -name '*.h*' -print0 | cpio --quiet -0dump ${PREFIX}/include/hyprland/wlroots && cd ../../..
cp ./protocols/*-protocol.h ${PREFIX}/include/hyprland/protocols
cp ./build/hyprland.pc ${PREFIX}/share/pkgconfig
if [ -d /usr/share/pkgconfig ]; then cp ./build/hyprland.pc /usr/share/pkgconfig 2>/dev/null || true; fi
configdebug:
$(MAKE) fixwlr
meson setup subprojects/wlroots/build subprojects/wlroots --prefix=${PREFIX} --buildtype=debug -Dwerror=false -Dexamples=false -Db_sanitize=address
ninja -C subprojects/wlroots/build/

View File

@@ -1,4 +0,0 @@
PREFIX = /usr/local
CFLAGS ?= -g -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-unused-function -Wno-unused-variable -Wno-unused-result -Wdeclaration-after-statement
CFLAGS += -DXWAYLAND

View File

@@ -1,6 +1,6 @@
.\" Automatically generated by Pandoc 2.9.2.1
.\"
.TH "Hyprland" "1" "09 Mar 2023" "" "Hyprland User Manual"
.TH "Hyprland" "1" "03 Apr 2023" "" "Hyprland User Manual"
.hy
.SH NAME
.PP

View File

@@ -45,6 +45,13 @@ cat /tmp/hypr/$(ls -t /tmp/hypr/ | head -n 2 | tail -n 1)/hyprland.log
basically, directories in /tmp/hypr are your sessions.
## Obtaining the Hyprland Crash Report (v0.22.0beta and up)
If you have `$XDG_CACHE_HOME` set, the crash report directory is `$XDG_CACHE_HOME/hyprland`. If not, it's `~/.hyprland`
Go to the crash report directory and you should find a file named `hyprlandCrashReport[XXXX].txt` where `[XXXX]` is the PID of the process that crashed.
Attach that file to your issue.
## Obtaining the Hyprland coredump (v0.21.0beta and below)
If you are on systemd, you can simply use
```
@@ -58,13 +65,6 @@ coredumpctl info [PID]
```
where `[PID]` is the PID you remembered.
## Obtaining the Hyprland Crash Report (v0.22.0beta and up)
Go to `~/.hyprland/` and you should find a file named `hyprlandCrashReport[XXXX].txt` where `[XXXX]` is the PID of the process that crashed.
If you do not see it, make sure you have "show hidden files" enabled in your file manager.
Attach that file to your issue.
## Obtaining the debug Hyprland coredump
A debug coredump provides more information for debugging and may speed up the process of fixing the bug.
@@ -74,9 +74,7 @@ Make sure you're on latest git. Run `git pull --recurse-submodules` to sync ever
> Note: The config file used will be `hyprlandd.conf` instead of `hyprland.conf`
2. `cd ~`
3. For your own convenience, launch Hyprland from a tty with the envvar `ASAN_OPTIONS="log_path=asan.log"`:
- If using a wrapper, add `export ASAN_OPTIONS="log_path=asan.log"` in a separate line before the `exec Hyprland` line.
- If launching straight from the tty, execute `ASAN_OPTIONS="log_path=asan.log" ~/path/to/Hyprland`
3. For your own convenience, launch Hyprland from a tty with the envvar `ASAN_OPTIONS="log_path=asan.log" ~/path/to/Hyprland`
4. Reproduce the crash. Hyprland should instantly close.
5. Check out your `~` and find a file called `asan.log.XXXXX` where `XXXXX` will be a number corresponding to the PID of the Hyprland instance that crashed.
6. That is your coredump. Attach it to your issue.

View File

@@ -1,6 +1,6 @@
.\" Automatically generated by Pandoc 2.9.2.1
.\"
.TH "hyprctl" "1" "09 Mar 2023" "" "hyprctl User Manual"
.TH "hyprctl" "1" "03 Apr 2023" "" "hyprctl User Manual"
.hy
.SH NAME
.PP

View File

@@ -3,6 +3,6 @@
# and that you have ran `make protocols` in the hl dir.
all:
g++ -shared -fPIC --no-gnu-unique main.cpp customLayout.cpp customDecoration.cpp -o examplePlugin.so -g -I "/usr/include/pixman-1" -I "/usr/include/libdrm" -I "${HYPRLAND_HEADERS}" -std=c++23
g++ -shared -fPIC --no-gnu-unique main.cpp customLayout.cpp customDecoration.cpp -o examplePlugin.so -g -I "/usr/include/pixman-1" -I "/usr/include/libdrm" -I "${HYPRLAND_HEADERS}" -I "${HYPRLAND_HEADERS}/subprojects/wlroots/include" -I "${HYPRLAND_HEADERS}/subprojects/wlroots/include" -I "${HYPRLAND_HEADERS}/subprojects/wlroots/build/include" -std=c++23
clean:
rm ./examplePlugin.so

View File

@@ -73,9 +73,12 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
g_pFocusHook = HyprlandAPI::createFunctionHook(PHANDLE, (void*)&CCompositor::focusWindow, (void*)&hkFocusWindow);
// Hook a public non-member
g_pMotionHook = HyprlandAPI::createFunctionHook(PHANDLE, (void*)&wlr_seat_pointer_notify_motion, (void*)&hkNotifyMotion);
// Hook a private member (!WARNING: the signature may differ in clang. This one is for gcc ONLY.)
g_pMouseDownHook = HyprlandAPI::createFunctionHook(
PHANDLE, HyprlandAPI::getFunctionAddressFromSignature(PHANDLE, "_ZN13CInputManager22processMouseDownNormalEP24wlr_pointer_button_event"), (void*)&hkProcessMouseDownNormal);
// Hook a private member
static const auto METHODS = HyprlandAPI::findFunctionsByName(PHANDLE, "processMouseDownNormal");
g_pMouseDownHook = HyprlandAPI::createFunctionHook(PHANDLE, METHODS[0].address, (void*)&hkProcessMouseDownNormal);
// fancy notifications
HyprlandAPI::addNotificationV2(PHANDLE, {{"text", "Example hint"}, {"time", (uint64_t)10000}, {"color", CColor(0.2, 0.2, 0.9, 1.0)}, {"icon", ICON_HINT}});
// Enable our hooks
g_pFocusHook->hook();

View File

@@ -99,7 +99,7 @@ gestures {
# Example per-device config
# See https://wiki.hyprland.org/Configuring/Keywords/#executing for more
device:epic mouse V1 {
device:epic-mouse-v1 {
sensitivity = -0.5
}

24
flake.lock generated
View File

@@ -7,11 +7,11 @@
]
},
"locked": {
"lastModified": 1671839510,
"narHash": "sha256-+PY1qqJfmZzzROgcIY4I7AkCwpnC+qBIYk2eFoA9RWc=",
"lastModified": 1681065697,
"narHash": "sha256-QPzwwlGKX95tl6ZEshboZbEwwAXww6lNLdVYd6T9Mrc=",
"owner": "hyprwm",
"repo": "hyprland-protocols",
"rev": "b8f55e02a328c47ed373133c52483bbfa20a1b75",
"rev": "4d29e48433270a2af06b8bc711ca1fe5109746cd",
"type": "github"
},
"original": {
@@ -22,11 +22,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1677676435,
"narHash": "sha256-6FxdcmQr5JeZqsQvfinIMr0XcTyTuR7EXX0H3ANShpQ=",
"lastModified": 1683014792,
"narHash": "sha256-6Va9iVtmmsw4raBc3QKvQT2KT/NGRWlvUlJj46zN8B8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "a08d6979dd7c82c4cef0dcc6ac45ab16051c1169",
"rev": "1a411f23ba299db155a5b45d5e145b85a7aafc42",
"type": "github"
},
"original": {
@@ -48,11 +48,11 @@
"flake": false,
"locked": {
"host": "gitlab.freedesktop.org",
"lastModified": 1677789111,
"narHash": "sha256-dWrk+Q3bLdtFe5rkyaAKWCQJCeE/KFNllcu1DvBC38c=",
"lastModified": 1682436395,
"narHash": "sha256-GGEjkQO9m7YLYIXIXM76HWdhjg4Ye+oafOtyaFAYKI4=",
"owner": "wlroots",
"repo": "wlroots",
"rev": "5ae17de23f5fd9bb252a698f3771c840280e2c05",
"rev": "6830bfc17fd94709e2cdd4da0af989f102a26e59",
"type": "gitlab"
},
"original": {
@@ -72,11 +72,11 @@
]
},
"locked": {
"lastModified": 1673116118,
"narHash": "sha256-eR0yDSkR2XYMesfdRWJs25kAdXET2mbNNHu5t+KUcKA=",
"lastModified": 1682439384,
"narHash": "sha256-zHDa8LCZs05TZHQSIZ3ucwyMPglBGHcqTBzfkLjYXTM=",
"owner": "hyprwm",
"repo": "xdg-desktop-portal-hyprland",
"rev": "d479c846531fd0e1d2357c9588b8310a2b859ef2",
"rev": "c0e233955568fbea4e859336f6d3d14d51294d7c",
"type": "github"
},
"original": {

106
flake.nix
View File

@@ -3,6 +3,7 @@
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
wlroots = {
url = "gitlab:wlroots/wlroots?host=gitlab.freedesktop.org";
flake = false;
@@ -25,99 +26,54 @@
nixpkgs,
...
}: let
inherit (nixpkgs) lib;
lib = nixpkgs.lib.extend (import ./nix/lib.nix);
genSystems = lib.genAttrs [
# Add more systems if they are supported
"aarch64-linux"
"x86_64-linux"
];
pkgsFor = nixpkgs.legacyPackages;
props = builtins.fromJSON (builtins.readFile ./props.json);
mkDate = longDate: (lib.concatStringsSep "-" [
(builtins.substring 0 4 longDate)
(builtins.substring 4 2 longDate)
(builtins.substring 6 2 longDate)
]);
in {
overlays.default = _: prev: rec {
wlroots-hyprland = prev.callPackage ./nix/wlroots.nix {
version = mkDate (inputs.wlroots.lastModifiedDate or "19700101") + "_" + (inputs.wlroots.shortRev or "dirty");
src = inputs.wlroots;
libdisplay-info = prev.libdisplay-info.overrideAttrs (old: {
version = "0.1.1+date=2023-03-02";
src = prev.fetchFromGitLab {
domain = "gitlab.freedesktop.org";
owner = "emersion";
repo = old.pname;
rev = "147d6611a64a6ab04611b923e30efacaca6fc678";
sha256 = "sha256-/q79o13Zvu7x02SBGu0W5yQznQ+p7ltZ9L6cMW5t/o4=";
};
});
libliftoff = prev.libliftoff.overrideAttrs (old: {
version = "0.5.0-dev";
src = prev.fetchFromGitLab {
domain = "gitlab.freedesktop.org";
owner = "emersion";
repo = old.pname;
rev = "d98ae243280074b0ba44bff92215ae8d785658c0";
sha256 = "sha256-DjwlS8rXE7srs7A8+tHqXyUsFGtucYSeq6X0T/pVOc8=";
};
NIX_CFLAGS_COMPILE = toString [
"-Wno-error=sign-conversion"
];
});
};
hyprland = prev.callPackage ./nix/default.nix {
stdenv = prev.gcc12Stdenv;
version = props.version + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty");
wlroots = wlroots-hyprland;
commit = self.rev or "";
inherit (inputs.hyprland-protocols.packages.${prev.stdenv.hostPlatform.system}) hyprland-protocols;
inherit udis86;
};
hyprland-debug = hyprland.override {debug = true;};
hyprland-no-hidpi = hyprland.override {hidpiXWayland = false;};
hyprland-nvidia = hyprland.override {nvidiaPatches = true;};
udis86 = prev.callPackage ./nix/udis86.nix {};
waybar-hyprland = prev.waybar.overrideAttrs (oldAttrs: {
postPatch = ''
# use hyprctl to switch workspaces
sed -i 's/zext_workspace_handle_v1_activate(workspace_handle_);/const std::string command = "hyprctl dispatch workspace " + name_;\n\tsystem(command.c_str());/g' src/modules/wlr/workspace_manager.cpp
'';
mesonFlags = oldAttrs.mesonFlags ++ ["-Dexperimental=true"];
pkgsFor = genSystems (system:
import nixpkgs {
inherit system;
overlays = [
self.overlays.hyprland-packages
self.overlays.wlroots-hyprland
inputs.hyprland-protocols.overlays.default
];
});
xdg-desktop-portal-hyprland = inputs.xdph.packages.${prev.stdenv.hostPlatform.system}.default.override {
hyprland-share-picker = inputs.xdph.packages.${prev.stdenv.hostPlatform.system}.hyprland-share-picker.override {inherit hyprland;};
in {
overlays =
(import ./nix/overlays.nix {inherit self lib inputs;})
// {
default =
lib.mkJoinedOverlays
(with self.overlays; [
hyprland-packages
hyprland-extras
wlroots-hyprland
]);
};
};
checks = genSystems (system:
(lib.filterAttrs (n: _: (lib.hasPrefix "hyprland" n) && !(lib.hasSuffix "debug" n)) self.packages.${system})
// {inherit (self.packages.${system}) xdg-desktop-portal-hyprland;});
(lib.filterAttrs
(n: _: (lib.hasPrefix "hyprland" n) && !(lib.hasSuffix "debug" n))
self.packages.${system})
// {
inherit (self.packages.${system}) xdg-desktop-portal-hyprland;
});
packages = genSystems (system:
(self.overlays.default null pkgsFor.${system})
(self.overlays.default pkgsFor.${system} pkgsFor.${system})
// {
default = self.packages.${system}.hyprland;
});
devShells = genSystems (system: {
default = pkgsFor.${system}.mkShell.override {stdenv = pkgsFor.${system}.gcc12Stdenv;} {
default = pkgsFor.${system}.mkShell {
name = "hyprland-shell";
nativeBuildInputs = with pkgsFor.${system}; [
cmake
];
buildInputs = [
self.packages.${system}.wlroots-hyprland
];
nativeBuildInputs = with pkgsFor.${system}; [cmake];
buildInputs = [self.packages.${system}.wlroots-hyprland];
inputsFrom = [
self.packages.${system}.wlroots-hyprland
self.packages.${system}.hyprland

View File

@@ -1,4 +1,4 @@
all:
g++ -std=c++23 ./main.cpp -o ./hyprctl
$(CXX) -std=c++2b ./main.cpp -o ./hyprctl
clean:
rm ./hyprctl

View File

@@ -23,6 +23,7 @@ const std::string USAGE = R"#(usage: hyprctl [(opt)flags] [command] [(opt)args]
commands:
monitors
workspaces
activeworkspace
clients
activewindow
layers
@@ -42,13 +43,17 @@ commands:
seterror
setprop
plugin
notify
globalshortcuts
flags:
-j -> output in JSON
--batch -> execute a batch of commands, separated by ';'
)#";
void request(std::string arg, int minArgs = 0) {
#define PAD
void request(std::string arg, int minArgs = 0) {
const auto SERVERSOCKET = socket(AF_UNIX, SOCK_STREAM, 0);
const auto ARGS = std::count(arg.begin(), arg.end(), ' ');
@@ -78,7 +83,7 @@ void request(std::string arg, int minArgs = 0) {
std::string socketPath = "/tmp/hypr/" + instanceSigStr + "/.socket.sock";
strcpy(serverAddress.sun_path, socketPath.c_str());
strncpy(serverAddress.sun_path, socketPath.c_str(), sizeof(serverAddress.sun_path) - 1);
if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, SUN_LEN(&serverAddress)) < 0) {
std::cout << "Couldn't connect to " << socketPath << ". (3)";
@@ -141,7 +146,7 @@ void requestHyprpaper(std::string arg) {
std::string socketPath = "/tmp/hypr/" + instanceSigStr + "/.hyprpaper.sock";
strcpy(serverAddress.sun_path, socketPath.c_str());
strncpy(serverAddress.sun_path, socketPath.c_str(), sizeof(serverAddress.sun_path) - 1);
if (connect(SERVERSOCKET, (sockaddr*)&serverAddress, SUN_LEN(&serverAddress)) < 0) {
std::cout << "Couldn't connect to " << socketPath << ". (3)";
@@ -225,7 +230,10 @@ int setcursorRequest(int argc, char** argv) {
return 1;
}
std::string rq = "setcursor " + std::string(argv[2]) + " " + std::string(argv[3]);
std::string rq = "setcursor ";
for (size_t i = 2; i < argc; ++i)
rq += std::string(argv[i]) + " ";
rq.pop_back();
request(rq);
return 0;
@@ -322,6 +330,8 @@ int main(int argc, char** argv) {
request(fullRequest);
else if (fullRequest.contains("/workspaces"))
request(fullRequest);
else if (fullRequest.contains("/activeworkspace"))
request(fullRequest);
else if (fullRequest.contains("/activewindow"))
request(fullRequest);
else if (fullRequest.contains("/layers"))
@@ -344,6 +354,8 @@ int main(int argc, char** argv) {
request(fullRequest);
else if (fullRequest.contains("/animations"))
request(fullRequest);
else if (fullRequest.contains("/globalshortcuts"))
request(fullRequest);
else if (fullRequest.contains("/switchxkblayout"))
request(fullRequest, 2);
else if (fullRequest.contains("/seterror"))
@@ -352,6 +364,8 @@ int main(int argc, char** argv) {
request(fullRequest, 3);
else if (fullRequest.contains("/plugin"))
request(fullRequest, 1);
else if (fullRequest.contains("/notify"))
request(fullRequest, 2);
else if (fullRequest.contains("/output"))
exitStatus = outputRequest(argc, argv);
else if (fullRequest.contains("/setcursor"))

8
hyprland.pc.in Normal file
View File

@@ -0,0 +1,8 @@
prefix="@PREFIX@"
includedir="${prefix}/include"
Name: Hyprland
URL: https://github.com/hyprwm/Hyprland
Description: Hyprland header files
Version: @HYPRLAND_VERSION@
Cflags: -I"${includedir}/hyprland/protocols" -I"${includedir}/hyprland/wlroots" -I"${includedir}"

View File

@@ -53,27 +53,48 @@ endif
have_xwayland = xcb_dep.found() and have_xwlr
if not have_xwayland
add_project_arguments('-DNO_XWAYLAND', language: 'cpp')
add_project_arguments('-DNO_XWAYLAND', language: 'cpp')
endif
backtrace_dep = cpp_compiler.find_library('execinfo', required: false)
systemd_dep = dependency('libsystemd', required: get_option('systemd'))
if get_option('systemd').enabled()
if get_option('systemd').enabled()
if systemd_dep.found()
add_project_arguments('-DUSES_SYSTEMD', language: 'cpp')
else
error('Cannot enable systemd in Hyprland: libsystemd was not found')
add_project_arguments('-DUSES_SYSTEMD', language: 'cpp')
else
error('Cannot enable systemd in Hyprland: libsystemd was not found')
endif
endif
if get_option('legacy_renderer').enabled()
add_project_arguments('-DLEGACY_RENDERER', language: 'cpp')
endif
if get_option('buildtype') == 'debug'
add_project_arguments('-DHYPRLAND_DEBUG', language: 'cpp')
endif
globber = run_command('find', 'src', '-name', '*.h*', check: true)
headers = globber.stdout().strip().split('\n')
foreach file : headers
install_headers(file, subdir: 'hyprland', preserve_path: true)
endforeach
subdir('protocols')
subdir('src')
subdir('hyprctl')
subdir('assets')
subdir('example')
subdir('docs')
pkg_install_dir = join_paths(get_option('datadir'), 'pkgconfig')
import('pkgconfig').generate(
name: 'Hyprland',
filebase: 'hyprland',
url: 'https://github.com/hyprwm/Hyprland',
description: 'Hyprland header files',
install_dir: pkg_install_dir,
subdirs: ['', 'hyprland/protocols'],
)

View File

@@ -1,2 +1,3 @@
option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications')
option('systemd', type: 'feature', value: 'auto', description: 'Enable systemd integration')
option('legacy_renderer', type: 'feature', value: 'disabled', description: 'Enable legacy renderer')

View File

@@ -1,8 +1,6 @@
{
lib,
stdenv,
fetchFromGitHub,
fetchpatch,
pkg-config,
meson,
ninja,
@@ -15,7 +13,7 @@
libxcb,
libxkbcommon,
mesa,
mount,
pango,
pciutils,
systemd,
udis86,
@@ -27,7 +25,7 @@
xwayland,
debug ? false,
enableXWayland ? true,
hidpiXWayland ? true,
hidpiXWayland ? false,
legacyRenderer ? false,
nvidiaPatches ? false,
withSystemd ? true,
@@ -63,6 +61,7 @@ in
outputs = [
"out"
"man"
"dev"
];
buildInputs =
@@ -74,6 +73,7 @@ in
libinput
libxkbcommon
mesa
pango
udis86
wayland
wayland-protocols
@@ -90,8 +90,9 @@ in
else "release";
mesonFlags = builtins.concatLists [
(lib.optional (!enableXWayland) "-Dxwayland=disabled")
(lib.optional legacyRenderer "-DLEGACY_RENDERER:STRING=true")
["-Dauto_features=disabled"]
(lib.optional enableXWayland "-Dxwayland=enabled")
(lib.optional legacyRenderer "-Dlegacy_renderer=enabled")
(lib.optional withSystemd "-Dsystemd=enabled")
];
@@ -105,7 +106,11 @@ in
sed -i "s#/usr#$out#" src/render/OpenGL.cpp
substituteInPlace meson.build \
--replace "@GIT_COMMIT_HASH@" '${commit}' \
--replace "@GIT_DIRTY@" '${if commit == "" then "dirty" else ""}'
--replace "@GIT_DIRTY@" '${
if commit == ""
then "dirty"
else ""
}'
'';
passthru.providedSessions = ["hyprland"];

View File

@@ -8,20 +8,42 @@ self: {
defaultHyprlandPackage = self.packages.${pkgs.stdenv.hostPlatform.system}.default.override {
enableXWayland = cfg.xwayland.enable;
hidpiXWayland = cfg.xwayland.hidpi;
nvidiaPatches = cfg.nvidiaPatches;
inherit (cfg) nvidiaPatches;
};
in {
meta.maintainers = [lib.maintainers.fufexan];
options.wayland.windowManager.hyprland = {
enable = lib.mkEnableOption "hyprland wayland compositor";
enable =
lib.mkEnableOption null
// {
description = lib.mdDoc ''
Whether to enable Hyprland, the dynamic tiling Wayland compositor
that doesn't sacrifice on its looks.
You can manually launch Hyprland by executing {command}`Hyprland` on
a TTY.
See <https://wiki.hyprland.org> for more information.
'';
};
package = lib.mkOption {
type = with lib.types; nullOr package;
default = defaultHyprlandPackage;
description = ''
Hyprland package to use. Will override the 'xwayland' option.
defaultText = lib.literalExpression ''
hyprland.packages.''${pkgs.stdenv.hostPlatform.system}.default.override {
enableXWayland = config.wayland.windowManager.hyprland.xwayland.enable;
hidpiXWayland = config.wayland.windowManager.hyprland.xwayland.hidpi;
inherit (config.wayland.windowManager.hyprland) nvidiaPatches;
}
'';
description = lib.mdDoc ''
Hyprland package to use. Will override the 'xwayland' and
'nvidiaPatches' options.
Defaults to the one provided by the flake. Set it to
<literal>pkgs.hyprland</literal> to use the one provided by nixpkgs or
{package}`pkgs.hyprland` to use the one provided by nixpkgs or
if you have an overlay.
Set to null to not add any Hyprland package to your path. This should
@@ -29,103 +51,94 @@ in {
'';
};
plugins = lib.mkOption {
type = with lib.types; listOf (either package path);
default = [];
description = lib.mdDoc ''
List of hyprlad plugins to use. Can either be packages or
absolute plugin paths.
'';
};
systemdIntegration = lib.mkOption {
type = lib.types.bool;
default = pkgs.stdenv.isLinux;
description = ''
Whether to enable <filename>hyprland-session.target</filename> on
hyprland startup. This links to <filename>graphical-session.target</filename>.
description = lib.mdDoc ''
Whether to enable {file}`hyprland-session.target` on
hyprland startup. This links to {file}`graphical-session.target`.
Some important environment variables will be imported to systemd
and dbus user environment before reaching the target, including
<itemizedlist>
<listitem><para><literal>DISPLAY</literal></para></listitem>
<listitem><para><literal>WAYLAND_DISPLAY</literal></para></listitem>
<listitem><para><literal>HYPRLAND_INSTANCE_SIGNATURE</literal></para></listitem>
<listitem><para><literal>XDG_CURRENT_DESKTOP</literal></para></listitem>
</itemizedlist>
- {env}`DISPLAY`
- {env}`HYPRLAND_INSTANCE_SIGNATURE`
- {env}`WAYLAND_DISPLAY`
- {env}`XDG_CURRENT_DESKTOP`
'';
};
disableAutoreload = lib.mkOption {
type = lib.types.bool;
default = false;
defaultText = lib.literalExpression "false";
example = lib.literalExpression "true";
description = ''
Whether to disable automatically reloading Hyprland's configuration when
rebuilding the Home Manager profile.
'';
};
disableAutoreload =
lib.mkEnableOption null
// {
description = lib.mdDoc ''
Whether to disable automatically reloading Hyprland's configuration when
rebuilding the Home Manager profile.
'';
};
xwayland = {
enable = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Enable XWayland.
'';
};
hidpi = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Enable HiDPI XWayland.
'';
};
enable = lib.mkEnableOption (lib.mdDoc "XWayland") // {default = true;};
hidpi =
lib.mkEnableOption null
// {
description = lib.mdDoc ''
Enable HiDPI XWayland, based on [XWayland MR 733](https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/733).
See <https://wiki.hyprland.org/Nix/Options-Overrides/#xwayland-hidpi> for more info.
'';
};
};
nvidiaPatches = lib.mkOption {
type = lib.types.bool;
default = false;
defaultText = lib.literalExpression "false";
example = lib.literalExpression "true";
description = ''
Patch wlroots for better Nvidia support.
'';
};
nvidiaPatches = lib.mkEnableOption (lib.mdDoc "patching wlroots for better Nvidia support.");
extraConfig = lib.mkOption {
type = lib.types.nullOr lib.types.lines;
default = "";
description = ''
Extra configuration lines to add to ~/.config/hypr/hyprland.conf.
description = lib.mdDoc ''
Extra configuration lines to add to {file}`~/.config/hypr/hyprland.conf`.
'';
};
recommendedEnvironment = lib.mkOption {
type = lib.types.bool;
default = true;
defaultText = lib.literalExpression "true";
example = lib.literalExpression "false";
description = ''
Whether to set the recommended environment variables.
'';
};
imports = [
(
lib.mkRenamedOptionModule
["wayland" "windowManager" "hyprland" "xwayland"]
["wayland" "windowManager" "hyprland" "xwayland" "enable"]
)
];
recommendedEnvironment =
lib.mkEnableOption null
// {
description = lib.mdDoc ''
Whether to set the recommended environment variables.
'';
};
};
config = lib.mkIf cfg.enable {
warnings =
if (cfg.systemdIntegration || cfg.plugins != []) && cfg.extraConfig == null then
[ ''You have enabled hyprland.systemdIntegration or listed plugins in hyprland.plugins.
Your hyprland config will be linked by home manager.
Set hyprland.extraConfig or unset hyprland.systemdIntegration and hyprland.plugins to remove this warning.'' ]
else [];
home.packages =
lib.optional (cfg.package != null) cfg.package
++ lib.optional cfg.xwayland.enable pkgs.xwayland;
home.sessionVariables = lib.mkIf cfg.recommendedEnvironment {
NIXOS_OZONE_WL = "1";
};
home.sessionVariables =
lib.mkIf cfg.recommendedEnvironment {NIXOS_OZONE_WL = "1";};
xdg.configFile."hypr/hyprland.conf" = lib.mkIf (cfg.extraConfig != null) {
xdg.configFile."hypr/hyprland.conf" = lib.mkIf (cfg.systemdIntegration || cfg.extraConfig != null || cfg.plugins != []) {
text =
(lib.optionalString cfg.systemdIntegration ''
exec-once=${pkgs.dbus}/bin/dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP && systemctl --user start hyprland-session.target
'')
+ cfg.extraConfig;
+ lib.concatStrings (builtins.map (entry: let
plugin = if lib.types.package.check entry then "${entry}/lib/lib${entry.pname}.so" else entry;
in "plugin = ${plugin}\n") cfg.plugins)
+ (if cfg.extraConfig != null then cfg.extraConfig else "");
onChange = let
hyprlandPackage =
@@ -133,13 +146,15 @@ in {
then defaultHyprlandPackage
else cfg.package;
in
lib.mkIf (!cfg.disableAutoreload) ''( # execute in subshell so that `shopt` won't affect other scripts
shopt -s nullglob # so that nothing is done if /tmp/hypr/ does not exist or is empty
for instance in /tmp/hypr/*; do
HYPRLAND_INSTANCE_SIGNATURE=''${instance##*/} ${hyprlandPackage}/bin/hyprctl reload config-only \
|| true # ignore dead instance(s)
done
)'';
lib.mkIf (!cfg.disableAutoreload) ''
( # execute in subshell so that `shopt` won't affect other scripts
shopt -s nullglob # so that nothing is done if /tmp/hypr/ does not exist or is empty
for instance in /tmp/hypr/*; do
HYPRLAND_INSTANCE_SIGNATURE=''${instance##*/} ${hyprlandPackage}/bin/hyprctl reload config-only \
|| true # ignore dead instance(s)
done
)
'';
};
systemd.user.targets.hyprland-session = lib.mkIf cfg.systemdIntegration {
@@ -151,12 +166,5 @@ in {
After = ["graphical-session-pre.target"];
};
};
systemd.user.targets.tray = {
Unit = {
Description = "Home Manager System Tray";
Requires = ["graphical-session-pre.target"];
};
};
};
}

8
nix/lib.nix Normal file
View File

@@ -0,0 +1,8 @@
final: prev: let
lib = final;
mkJoinedOverlays = overlays: final: prev:
lib.foldl' (attrs: overlay: attrs // (overlay final prev)) {} overlays;
in prev // {
inherit mkJoinedOverlays;
}

View File

@@ -1,5 +1,5 @@
diff --git a/meson.build b/meson.build
index f380255..abd7cd3 100644
index f3802553..6a924a79 100644
--- a/meson.build
+++ b/meson.build
@@ -21,9 +21,9 @@ else
@@ -14,14 +14,14 @@ index f380255..abd7cd3 100644
add_project_arguments(
[
@@ -39,23 +39,8 @@ add_project_arguments(
@@ -39,21 +39,8 @@ add_project_arguments(
],
language: 'cpp')
-wlroots = subproject('wlroots', default_options: ['examples=false'])
-have_xwlr = wlroots.get_variable('features').get('xwayland')
xcb_dep = dependency('xcb', required: get_option('xwayland'))
-xcb_dep = dependency('xcb', required: get_option('xwayland'))
-
-cmake = import('cmake')
-udis = cmake.subproject('udis86')
-udis86 = udis.dependency('libudis86')
@@ -32,17 +32,17 @@ index f380255..abd7cd3 100644
-have_xwayland = xcb_dep.found() and have_xwlr
-
-if not have_xwayland
-add_project_arguments('-DNO_XWAYLAND', language: 'cpp')
-endif
-
backtrace_dep = cpp_compiler.find_library('execinfo', required: false)
systemd_dep = dependency('libsystemd', required: get_option('systemd'))
- add_project_arguments('-DNO_XWAYLAND', language: 'cpp')
+if get_option('xwayland').disabled()
+ add_project_arguments('-DNO_XWAYLAND', language: 'cpp')
endif
backtrace_dep = cpp_compiler.find_library('execinfo', required: false)
diff --git a/src/meson.build b/src/meson.build
index 7b658d3..da8baa5 100644
index 7b658d31..60aa4057 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -7,7 +7,7 @@ executable('Hyprland', src,
@@ -7,16 +7,16 @@ executable('Hyprland', src,
server_protos,
dependency('wayland-server'),
dependency('wayland-client'),
@@ -51,8 +51,10 @@ index 7b658d3..da8baa5 100644
dependency('cairo'),
dependency('libdrm'),
dependency('egl'),
@@ -16,7 +16,7 @@ executable('Hyprland', src,
xcb_dep,
dependency('xkbcommon'),
dependency('libinput'),
- xcb_dep,
+ dependency('xcb', required: get_option('xwayland')),
backtrace_dep,
systemd_dep,
- udis86,

View File

@@ -1,4 +1,3 @@
# Copied from https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/programs/sway.nix
inputs: {
config,
lib,
@@ -11,88 +10,82 @@ with lib; let
defaultHyprlandPackage = inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.default.override {
enableXWayland = cfg.xwayland.enable;
hidpiXWayland = cfg.xwayland.hidpi;
nvidiaPatches = cfg.nvidiaPatches;
inherit (cfg) nvidiaPatches;
};
in {
imports = [
(mkRemovedOptionModule ["programs" "hyprland" "extraPackages"] "extraPackages has been removed. Use environment.systemPackages instead.")
];
# disables Nixpkgs Hyprland module to avoid conflicts
disabledModules = ["programs/hyprland.nix"];
options.programs.hyprland = {
enable = mkEnableOption ''
Hyprland, the dynamic tiling Wayland compositor that doesn't sacrifice on its looks.
You can manually launch Hyprland by executing "exec Hyprland" on a TTY.
A configuration file will be generated in ~/.config/hypr/hyprland.conf.
See <link xlink:href="https://github.com/vaxerski/Hyprland/wiki" /> for
more information.
'';
enable =
mkEnableOption null
// {
description = mdDoc ''
Hyprland, the dynamic tiling Wayland compositor that doesn't sacrifice on its looks.
You can manually launch Hyprland by executing {command}`Hyprland` on a TTY.
A configuration file will be generated in {file}`~/.config/hypr/hyprland.conf`.
See <https://wiki.hyprland.org> for more information.
'';
};
package = mkOption {
type = types.nullOr types.package;
type = types.path;
default = defaultHyprlandPackage;
defaultText = literalExpression "<Hyprland flake>.packages.<system>.default";
example = literalExpression "<Hyprland flake>.packages.<system>.default.override { }";
description = ''
Hyprland package to use.
defaultText = literalExpression ''
hyprland.packages.''${pkgs.stdenv.hostPlatform.system}.default.override {
enableXWayland = config.programs.hyprland.xwayland.enable;
hidpiXWayland = config.programs.hyprland.xwayland.hidpi;
inherit (config.programs.hyprland) nvidiaPatches;
}
'';
example = literalExpression "pkgs.hyprland";
description = mdDoc ''
The Hyprland package to use.
Setting this option will make {option}`programs.hyprland.xwayland` and
{option}`programs.hyprland.nvidiaPatches` not work.
'';
};
xwayland = {
enable = mkOption {
type = types.bool;
default = true;
description = ''
Enable XWayland.
'';
};
hidpi = mkOption {
type = types.bool;
default = true;
description = ''
Enable HiDPI XWayland.
'';
};
enable = mkEnableOption (mdDoc "XWayland") // {default = true;};
hidpi =
mkEnableOption null
// {
description = mdDoc ''
Enable HiDPI XWayland, based on [XWayland MR 733](https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/733).
See <https://wiki.hyprland.org/Nix/Options-Overrides/#xwayland-hidpi> for more info.
'';
};
};
nvidiaPatches = mkOption {
type = types.bool;
default = false;
example = literalExpression "true";
description = ''
Patch wlroots for better Nvidia support.
'';
};
recommendedEnvironment = mkOption {
type = types.bool;
default = true;
defaultText = literalExpression "true";
example = literalExpression "false";
description = ''
Whether to set the recommended environment variables.
'';
};
nvidiaPatches = mkEnableOption (mdDoc "patching wlroots for better Nvidia support");
};
config = mkIf cfg.enable {
environment = {
systemPackages = lib.optional (cfg.package != null) cfg.package;
systemPackages = [cfg.package];
sessionVariables = mkIf cfg.recommendedEnvironment {
NIXOS_OZONE_WL = "1";
sessionVariables = {
NIXOS_OZONE_WL = mkDefault "1";
};
};
fonts.enableDefaultFonts = mkDefault true;
hardware.opengl.enable = mkDefault true;
programs = {
dconf.enable = mkDefault true;
xwayland.enable = mkDefault true;
};
security.polkit.enable = true;
services.xserver.displayManager.sessionPackages = lib.optional (cfg.package != null) cfg.package;
services.xserver.displayManager.sessionPackages = [cfg.package];
xdg.portal = {
enable = mkDefault true;
# xdg-desktop-portal-hyprland
extraPortals = lib.mkIf (cfg.package != null) [
(inputs.xdph.packages.${pkgs.stdenv.hostPlatform.system}.xdg-desktop-portal-hyprland.override {
hyprland-share-picker = inputs.xdph.packages.${pkgs.stdenv.hostPlatform.system}.hyprland-share-picker.override {

97
nix/overlays.nix Normal file
View File

@@ -0,0 +1,97 @@
{
self,
lib,
inputs,
}: let
props = builtins.fromJSON (builtins.readFile ../props.json);
mkDate = longDate: (lib.concatStringsSep "-" [
(builtins.substring 0 4 longDate)
(builtins.substring 4 2 longDate)
(builtins.substring 6 2 longDate)
]);
in {
# Packages for variations of Hyprland, and its dependencies.
hyprland-packages = final: prev: {
hyprland = final.callPackage ./default.nix {
version =
props.version
+ "+date="
+ (mkDate (self.lastModifiedDate or "19700101"))
+ "_"
+ (self.shortRev or "dirty");
wlroots = final.wlroots-hyprland;
commit = self.rev or "";
inherit (final) udis86 hyprland-protocols;
};
hyprland-debug = final.hyprland.override {debug = true;};
hyprland-hidpi = final.hyprland.override {hidpiXWayland = true;};
hyprland-nvidia = final.hyprland.override {nvidiaPatches = true;};
hyprland-no-hidpi =
builtins.trace
"hyprland-no-hidpi was removed. Please use the default package."
final.hyprland;
udis86 = final.callPackage ./udis86.nix {};
};
# Packages for extra software recommended for usage with Hyprland,
# including forked or patched packages for compatibility.
hyprland-extras = lib.mkJoinedOverlays [
# Include any inputs' specific overlays whose attributes should
# be re-exported by the Hyprland flake.
#
inputs.xdph.overlays.default
# Provides:
# - xdg-desktop-portal-hyprland
# - hyprland-share-picker
#
# Attributes for `hyprland-extras` defined by this flake can
# go in the oberlay below.
(final: prev: {
waybar-hyprland = prev.waybar.overrideAttrs (old: {
postPatch = ''
# use hyprctl to switch workspaces
sed -i 's/zext_workspace_handle_v1_activate(workspace_handle_);/const std::string command = "hyprctl dispatch workspace " + name_;\n\tsystem(command.c_str());/g' src/modules/wlr/workspace_manager.cpp
'';
mesonFlags = old.mesonFlags ++ ["-Dexperimental=true"];
});
})
];
# Patched version of wlroots for Hyprland.
# It is under a new package name so as to not conflict with
# the standard version in nixpkgs.
wlroots-hyprland = final: prev: {
wlroots-hyprland = final.callPackage ./wlroots.nix {
version =
mkDate (inputs.wlroots.lastModifiedDate or "19700101")
+ "_"
+ (inputs.wlroots.shortRev or "dirty");
src = inputs.wlroots;
libdisplay-info = prev.libdisplay-info.overrideAttrs (old: {
version = "0.1.1+date=2023-03-02";
src = final.fetchFromGitLab {
domain = "gitlab.freedesktop.org";
owner = "emersion";
repo = old.pname;
rev = "147d6611a64a6ab04611b923e30efacaca6fc678";
sha256 = "sha256-/q79o13Zvu7x02SBGu0W5yQznQ+p7ltZ9L6cMW5t/o4=";
};
});
libliftoff = prev.libliftoff.overrideAttrs (old: {
version = "0.5.0-dev";
src = final.fetchFromGitLab {
domain = "gitlab.freedesktop.org";
owner = "emersion";
repo = old.pname;
rev = "d98ae243280074b0ba44bff92215ae8d785658c0";
sha256 = "sha256-DjwlS8rXE7srs7A8+tHqXyUsFGtucYSeq6X0T/pVOc8=";
};
NIX_CFLAGS_COMPILE = toString ["-Wno-error=sign-conversion"];
});
};
};
}

View File

@@ -5,17 +5,17 @@ set -ex
# get wlroots revision from submodule
SUB_REV=$(git submodule status | rg wlroots | awk '{ print substr($1,2)}')
# and from lockfile
CRT_REV=$(jq < flake.lock '.nodes.wlroots.locked.rev' -r)
CRT_REV=$(jq <flake.lock '.nodes.wlroots.locked.rev' -r)
if [ $SUB_REV != $CRT_REV ]; then
# update nixpkgs to latest version
nix flake lock --update-input nixpkgs
if [ "$SUB_REV" != "$CRT_REV" ]; then
# update inputs to latest versions
nix flake update
# update wlroots to submodule revision
nix flake lock --override-input wlroots "gitlab:wlroots/wlroots/$SUB_REV?host=gitlab.freedesktop.org"
# remove "dirty" mark from lockfile
jq < flake.lock 'del(.nodes.wlroots.original.rev)' | sponge flake.lock
jq <flake.lock 'del(.nodes.wlroots.original.rev)' | sponge flake.lock
else
echo "wlroots is up to date!"
fi

View File

@@ -1,25 +1,25 @@
diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h
index c69504e8..40d8de8c 100644
index 3d540522..1c5a2e37 100644
--- a/include/xwayland/xwm.h
+++ b/include/xwayland/xwm.h
@@ -88,6 +88,7 @@ enum atom_name {
DND_ACTION_PRIVATE,
NET_CLIENT_LIST,
NET_CLIENT_LIST_STACKING,
+ XWAYLAND_GLOBAL_OUTPUT_SCALE,
+ XWAYLAND_GLOBAL_OUTPUT_SCALE,
ATOM_LAST // keep last
};
@@ -98,6 +99,7 @@ struct wlr_xwm {
@@ -96,6 +97,7 @@ struct wlr_xwm {
struct wl_event_source *event_source;
struct wlr_seat *seat;
uint32_t ping_timeout;
+ uint32_t scale;
+ uint32_t scale;
xcb_atom_t atoms[ATOM_LAST];
xcb_connection_t *xcb_conn;
diff --git a/xwayland/xwm.c b/xwayland/xwm.c
index 5a36dc21..83981a87 100644
index 5f857f24..21584ebd 100644
--- a/xwayland/xwm.c
+++ b/xwayland/xwm.c
@@ -19,6 +19,14 @@
@@ -34,10 +34,10 @@ index 5a36dc21..83981a87 100644
+ return (val + xwm->scale/2) / xwm->scale;
+}
+
const char *const atom_map[ATOM_LAST] = {
static const char *const atom_map[ATOM_LAST] = {
[WL_SURFACE_ID] = "WL_SURFACE_ID",
[WL_SURFACE_SERIAL] = "WL_SURFACE_SERIAL",
@@ -90,6 +98,7 @@ const char *const atom_map[ATOM_LAST] = {
@@ -90,6 +98,7 @@ static const char *const atom_map[ATOM_LAST] = {
[DND_ACTION_PRIVATE] = "XdndActionPrivate",
[NET_CLIENT_LIST] = "_NET_CLIENT_LIST",
[NET_CLIENT_LIST_STACKING] = "_NET_CLIENT_LIST_STACKING",
@@ -45,7 +45,7 @@ index 5a36dc21..83981a87 100644
};
#define STARTUP_INFO_REMOVE_PREFIX "remove: ID="
@@ -968,8 +977,8 @@ static void xwm_handle_create_notify(struct wlr_xwm *xwm,
@@ -965,8 +974,8 @@ static void xwm_handle_create_notify(struct wlr_xwm *xwm,
return;
}
@@ -56,7 +56,7 @@ index 5a36dc21..83981a87 100644
}
static void xwm_handle_destroy_notify(struct wlr_xwm *xwm,
@@ -1000,10 +1009,10 @@ static void xwm_handle_configure_request(struct wlr_xwm *xwm,
@@ -997,10 +1006,10 @@ static void xwm_handle_configure_request(struct wlr_xwm *xwm,
struct wlr_xwayland_surface_configure_event wlr_event = {
.surface = surface,
@@ -71,7 +71,7 @@ index 5a36dc21..83981a87 100644
.mask = mask,
};
@@ -1018,14 +1027,14 @@ static void xwm_handle_configure_notify(struct wlr_xwm *xwm,
@@ -1015,14 +1024,14 @@ static void xwm_handle_configure_notify(struct wlr_xwm *xwm,
}
bool geometry_changed =
@@ -92,7 +92,7 @@ index 5a36dc21..83981a87 100644
}
if (xsurface->override_redirect != ev->override_redirect) {
@@ -1136,6 +1145,20 @@ static void xwm_handle_property_notify(struct wlr_xwm *xwm,
@@ -1133,6 +1142,20 @@ static void xwm_handle_property_notify(struct wlr_xwm *xwm,
xcb_property_notify_event_t *ev) {
struct wlr_xwayland_surface *xsurface = lookup_surface(xwm, ev->window);
if (xsurface == NULL) {
@@ -113,7 +113,7 @@ index 5a36dc21..83981a87 100644
return;
}
@@ -1763,16 +1786,17 @@ void wlr_xwayland_surface_configure(struct wlr_xwayland_surface *xsurface,
@@ -1760,16 +1783,17 @@ void wlr_xwayland_surface_configure(struct wlr_xwayland_surface *xsurface,
int old_w = xsurface->width;
int old_h = xsurface->height;
@@ -133,7 +133,7 @@ index 5a36dc21..83981a87 100644
xcb_configure_window(xwm->xcb_conn, xsurface->window_id, mask, values);
// If the window size did not change, then we cannot rely on
@@ -1780,15 +1804,15 @@ void wlr_xwayland_surface_configure(struct wlr_xwayland_surface *xsurface,
@@ -1777,15 +1801,15 @@ void wlr_xwayland_surface_configure(struct wlr_xwayland_surface *xsurface,
// we are supposed to send a synthetic event. See ICCCM part
// 4.1.5. But we ignore override-redirect windows as ICCCM does
// not apply to them.
@@ -154,7 +154,7 @@ index 5a36dc21..83981a87 100644
};
xcb_send_event(xwm->xcb_conn, 0, xsurface->window_id,
@@ -2125,6 +2149,7 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) {
@@ -2122,6 +2146,7 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) {
wl_list_init(&xwm->pending_startup_ids);
xwm->ping_timeout = 10000;

View File

@@ -1,8 +1,8 @@
diff --git a/hw/xwayland/xwayland-cursor.c b/hw/xwayland/xwayland-cursor.c
index c4457cc2a61b2103b47f996b51dbbe9eb87bd715..4a33e1f33e73c35c1691564ef4852e7501b02245 100644
index e3c1aaa50..eba29b5ba 100644
--- a/hw/xwayland/xwayland-cursor.c
+++ b/hw/xwayland/xwayland-cursor.c
@@ -171,6 +171,8 @@ xwl_cursor_attach_pixmap(struct xwl_seat *xwl_seat,
@@ -164,6 +164,8 @@ xwl_cursor_attach_pixmap(struct xwl_seat *xwl_seat,
}
wl_surface_attach(xwl_cursor->surface, buffer, 0, 0);
@@ -11,7 +11,7 @@ index c4457cc2a61b2103b47f996b51dbbe9eb87bd715..4a33e1f33e73c35c1691564ef4852e75
xwl_surface_damage(xwl_seat->xwl_screen, xwl_cursor->surface, 0, 0,
xwl_seat->x_cursor->bits->width,
xwl_seat->x_cursor->bits->height);
@@ -190,6 +192,7 @@ xwl_cursor_attach_pixmap(struct xwl_seat *xwl_seat,
@@ -195,6 +197,7 @@ xwl_cursor_clear_frame_cb(struct xwl_cursor *xwl_cursor)
void
xwl_seat_set_cursor(struct xwl_seat *xwl_seat)
{
@@ -19,7 +19,7 @@ index c4457cc2a61b2103b47f996b51dbbe9eb87bd715..4a33e1f33e73c35c1691564ef4852e75
struct xwl_cursor *xwl_cursor = &xwl_seat->cursor;
PixmapPtr pixmap;
CursorPtr cursor;
@@ -220,8 +223,8 @@ xwl_seat_set_cursor(struct xwl_seat *xwl_seat)
@@ -225,8 +228,8 @@ xwl_seat_set_cursor(struct xwl_seat *xwl_seat)
wl_pointer_set_cursor(xwl_seat->wl_pointer,
xwl_seat->pointer_enter_serial,
xwl_cursor->surface,
@@ -30,7 +30,7 @@ index c4457cc2a61b2103b47f996b51dbbe9eb87bd715..4a33e1f33e73c35c1691564ef4852e75
xwl_cursor_attach_pixmap(xwl_seat, xwl_cursor, pixmap);
}
@@ -230,6 +233,7 @@ void
@@ -235,6 +238,7 @@ void
xwl_tablet_tool_set_cursor(struct xwl_tablet_tool *xwl_tablet_tool)
{
struct xwl_seat *xwl_seat = xwl_tablet_tool->seat;
@@ -38,24 +38,23 @@ index c4457cc2a61b2103b47f996b51dbbe9eb87bd715..4a33e1f33e73c35c1691564ef4852e75
struct xwl_cursor *xwl_cursor = &xwl_tablet_tool->cursor;
PixmapPtr pixmap;
CursorPtr cursor;
@@ -258,9 +262,9 @@ xwl_tablet_tool_set_cursor(struct xwl_tablet_tool *xwl_tablet_tool)
@@ -263,8 +267,9 @@ xwl_tablet_tool_set_cursor(struct xwl_tablet_tool *xwl_tablet_tool)
zwp_tablet_tool_v2_set_cursor(xwl_tablet_tool->tool,
xwl_tablet_tool->proximity_in_serial,
xwl_cursor->surface,
- xwl_seat->x_cursor->bits->xhot,
- xwl_seat->x_cursor->bits->yhot);
-
+ xwl_scale_to(xwl_screen, xwl_seat->x_cursor->bits->xhot),
+ xwl_scale_to(xwl_screen, xwl_seat->x_cursor->bits->yhot));
+ wl_surface_set_buffer_scale(xwl_cursor->surface, xwl_screen->global_output_scale);
xwl_cursor_attach_pixmap(xwl_seat, xwl_cursor, pixmap);
}
diff --git a/hw/xwayland/xwayland-input.c b/hw/xwayland/xwayland-input.c
index 26b3630c73b62514fe3ba7824371f79868e953f3..55cd8d466a55db03948abe93ffa03bf129b5e17a 100644
index 6e0600e4e..4a22ebff0 100644
--- a/hw/xwayland/xwayland-input.c
+++ b/hw/xwayland/xwayland-input.c
@@ -412,8 +412,8 @@ pointer_handle_enter(void *data, struct wl_pointer *pointer,
@@ -507,8 +507,8 @@ pointer_handle_enter(void *data, struct wl_pointer *pointer,
DeviceIntPtr dev = get_pointer_device(xwl_seat);
DeviceIntPtr master;
int i;
@@ -66,7 +65,7 @@ index 26b3630c73b62514fe3ba7824371f79868e953f3..55cd8d466a55db03948abe93ffa03bf1
int dx, dy;
ScreenPtr pScreen = xwl_seat->xwl_screen->screen;
ValuatorMask mask;
@@ -592,13 +592,14 @@ pointer_handle_motion(void *data, struct wl_pointer *pointer,
@@ -731,13 +731,14 @@ pointer_handle_motion(void *data, struct wl_pointer *pointer,
uint32_t time, wl_fixed_t sx_w, wl_fixed_t sy_w)
{
struct xwl_seat *xwl_seat = data;
@@ -83,17 +82,7 @@ index 26b3630c73b62514fe3ba7824371f79868e953f3..55cd8d466a55db03948abe93ffa03bf1
if (wl_proxy_get_version((struct wl_proxy *) xwl_seat->wl_pointer) < 5)
dispatch_pointer_motion_event(xwl_seat);
@@ -672,7 +673,8 @@ pointer_handle_axis(void *data, struct wl_pointer *pointer,
xorg_list_del(&pending->l);
free(pending);
} else {
- valuator_mask_set_double(&mask, index, wl_fixed_to_double(value) / divisor);
+ double scaled_value = wl_fixed_to_double(value);
+ valuator_mask_set_double(&mask, index, scaled_value / divisor);
}
QueuePointerEvents(get_pointer_device(xwl_seat),
@@ -740,12 +742,13 @@ relative_pointer_handle_relative_motion(void *data,
@@ -887,12 +888,13 @@ relative_pointer_handle_relative_motion(void *data,
wl_fixed_t dy_unaccelf)
{
struct xwl_seat *xwl_seat = data;
@@ -111,7 +100,7 @@ index 26b3630c73b62514fe3ba7824371f79868e953f3..55cd8d466a55db03948abe93ffa03bf1
if (!xwl_seat->focus_window)
return;
@@ -1057,8 +1060,8 @@ touch_handle_down(void *data, struct wl_touch *wl_touch,
@@ -1382,8 +1384,8 @@ touch_handle_down(void *data, struct wl_touch *wl_touch,
xwl_touch->window = wl_surface_get_user_data(surface);
xwl_touch->id = id;
@@ -122,18 +111,18 @@ index 26b3630c73b62514fe3ba7824371f79868e953f3..55cd8d466a55db03948abe93ffa03bf1
xorg_list_add(&xwl_touch->link_touch, &xwl_seat->touches);
xwl_touch_send_event(xwl_touch, xwl_seat, XI_TouchBegin);
@@ -1094,8 +1097,8 @@ touch_handle_motion(void *data, struct wl_touch *wl_touch,
@@ -1419,8 +1421,8 @@ touch_handle_motion(void *data, struct wl_touch *wl_touch,
if (!xwl_touch)
return;
- xwl_touch->x = wl_fixed_to_int(sx_w);
- xwl_touch->y = wl_fixed_to_int(sy_w);
+ xwl_touch->x = wl_fixed_to_int(sx_w) * xwl_seat->xwl_screen->global_output_scale;;
+ xwl_touch->y = wl_fixed_to_int(sy_w) * xwl_seat->xwl_screen->global_output_scale;;
+ xwl_touch->x = wl_fixed_to_int(sx_w) * xwl_seat->xwl_screen->global_output_scale;
+ xwl_touch->y = wl_fixed_to_int(sy_w) * xwl_seat->xwl_screen->global_output_scale;
xwl_touch_send_event(xwl_touch, xwl_seat, XI_TouchUpdate);
}
@@ -1726,8 +1729,8 @@ tablet_tool_motion(void *data, struct zwp_tablet_tool_v2 *tool,
@@ -2110,8 +2112,8 @@ tablet_tool_motion(void *data, struct zwp_tablet_tool_v2 *tool,
struct xwl_tablet_tool *xwl_tablet_tool = data;
struct xwl_seat *xwl_seat = xwl_tablet_tool->seat;
int32_t dx, dy;
@@ -144,7 +133,7 @@ index 26b3630c73b62514fe3ba7824371f79868e953f3..55cd8d466a55db03948abe93ffa03bf1
if (!xwl_seat->tablet_focus_window)
return;
@@ -2714,6 +2717,7 @@ xwl_pointer_warp_emulator_set_fake_pos(struct xwl_pointer_warp_emulator *warp_em
@@ -3152,6 +3154,7 @@ xwl_pointer_warp_emulator_set_fake_pos(struct xwl_pointer_warp_emulator *warp_em
int x,
int y)
{
@@ -152,7 +141,7 @@ index 26b3630c73b62514fe3ba7824371f79868e953f3..55cd8d466a55db03948abe93ffa03bf1
struct zwp_locked_pointer_v1 *locked_pointer =
warp_emulator->locked_pointer;
WindowPtr window;
@@ -2725,6 +2729,7 @@ xwl_pointer_warp_emulator_set_fake_pos(struct xwl_pointer_warp_emulator *warp_em
@@ -3163,6 +3166,7 @@ xwl_pointer_warp_emulator_set_fake_pos(struct xwl_pointer_warp_emulator *warp_em
if (!warp_emulator->xwl_seat->focus_window)
return;
@@ -160,7 +149,7 @@ index 26b3630c73b62514fe3ba7824371f79868e953f3..55cd8d466a55db03948abe93ffa03bf1
window = warp_emulator->xwl_seat->focus_window->window;
if (x >= window->drawable.x ||
y >= window->drawable.y ||
@@ -2733,8 +2738,8 @@ xwl_pointer_warp_emulator_set_fake_pos(struct xwl_pointer_warp_emulator *warp_em
@@ -3171,8 +3175,8 @@ xwl_pointer_warp_emulator_set_fake_pos(struct xwl_pointer_warp_emulator *warp_em
sx = x - window->drawable.x;
sy = y - window->drawable.y;
zwp_locked_pointer_v1_set_cursor_position_hint(locked_pointer,
@@ -172,21 +161,21 @@ index 26b3630c73b62514fe3ba7824371f79868e953f3..55cd8d466a55db03948abe93ffa03bf1
}
}
diff --git a/hw/xwayland/xwayland-output.c b/hw/xwayland/xwayland-output.c
index ef705bc01bf8c2d2f170cda9ba21ed8293f50559..b8f6cd51bd240ed5e16271eb4749db18868bea7b 100644
index 661e1828d..6c60aba34 100644
--- a/hw/xwayland/xwayland-output.c
+++ b/hw/xwayland/xwayland-output.c
@@ -191,6 +191,9 @@ update_screen_size(struct xwl_output *xwl_output, int width, int height)
@@ -186,6 +186,9 @@ update_backing_pixmaps(struct xwl_screen *xwl_screen, int width, int height)
static void
update_screen_size(struct xwl_screen *xwl_screen, int width, int height)
{
struct xwl_screen *xwl_screen = xwl_output->xwl_screen;
+ width *= xwl_screen->global_output_scale;
+ height *= xwl_screen->global_output_scale;
+
if (xwl_screen->root_clip_mode == ROOT_CLIP_FULL)
SetRootClip(xwl_screen->screen, ROOT_CLIP_NONE);
xwl_screen->width = width;
xwl_screen->height = height;
@@ -497,14 +500,15 @@ xwl_output_set_emulated_mode(struct xwl_output *xwl_output, ClientPtr client,
xwl_output_set_randr_emu_props(xwl_output->xwl_screen, client);
@@ -597,14 +600,15 @@ xwl_output_set_emulated_mode(struct xwl_output *xwl_output, ClientPtr client,
new_emulated_height);
}
-static void
@@ -203,20 +192,20 @@ index ef705bc01bf8c2d2f170cda9ba21ed8293f50559..b8f6cd51bd240ed5e16271eb4749db18
/* Clear out the "done" received flags */
xwl_output->wl_output_done = FALSE;
@@ -523,10 +527,10 @@ apply_output_change(struct xwl_output *xwl_output)
@@ -623,10 +627,10 @@ apply_output_change(struct xwl_output *xwl_output)
}
/* Build a fresh modes array using the current refresh rate */
- randr_modes = output_get_rr_modes(xwl_output, mode_width, mode_height, &count);
+ randr_modes = output_get_rr_modes(xwl_output, mode_width * scale, mode_height * scale, &count);
RROutputSetModes(xwl_output->randr_output, randr_modes, count, 1);
RRCrtcNotify(xwl_output->randr_crtc, randr_modes[0],
- xwl_output->x, xwl_output->y,
+ xwl_output->x * scale, xwl_output->y * scale,
xwl_output->rotation, NULL, 1, &xwl_output->randr_output);
/* RROutputSetModes takes ownership of the passed in modes, so we only
* have to free the pointer array.
@@ -567,7 +571,7 @@ output_handle_done(void *data, struct wl_output *wl_output)
if (xwl_output->randr_output) {
/* Build a fresh modes array using the current refresh rate */
- randr_modes = output_get_rr_modes(xwl_output, mode_width, mode_height, &count);
+ randr_modes = output_get_rr_modes(xwl_output, mode_width * scale, mode_height * scale, &count);
RROutputSetModes(xwl_output->randr_output, randr_modes, count, 1);
RRCrtcNotify(xwl_output->randr_crtc, randr_modes[0],
- xwl_output->x, xwl_output->y,
+ xwl_output->x * scale, xwl_output->y * scale,
xwl_output->rotation, NULL, 1, &xwl_output->randr_output);
/* RROutputSetModes takes ownership of the passed in modes, so we only
* have to free the pointer array.
@@ -686,7 +690,7 @@ output_handle_done(void *data, struct wl_output *wl_output)
*/
if (xwl_output->xdg_output_done || !xwl_output->xdg_output ||
zxdg_output_v1_get_version(xwl_output->xdg_output) >= 3)
@@ -225,7 +214,7 @@ index ef705bc01bf8c2d2f170cda9ba21ed8293f50559..b8f6cd51bd240ed5e16271eb4749db18
}
static void
@@ -610,7 +614,7 @@ xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output)
@@ -746,7 +750,7 @@ xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output)
xwl_output->xdg_output_done = TRUE;
if (xwl_output->wl_output_done &&
zxdg_output_v1_get_version(xdg_output) < 3)
@@ -234,17 +223,17 @@ index ef705bc01bf8c2d2f170cda9ba21ed8293f50559..b8f6cd51bd240ed5e16271eb4749db18
}
static void
@@ -678,6 +682,8 @@ xwl_output_create(struct xwl_screen *xwl_screen, uint32_t id)
RROutputSetConnection(xwl_output->randr_output, RR_Connected);
RRTellChanged(xwl_screen->screen);
+ xwl_output->scale = 1;
@@ -857,6 +861,8 @@ xwl_output_create(struct xwl_screen *xwl_screen, uint32_t id,
RRCrtcGammaSetSize(xwl_output->randr_crtc, 256);
RROutputSetCrtcs(xwl_output->randr_output, &xwl_output->randr_crtc, 1);
RROutputSetConnection(xwl_output->randr_output, RR_Connected);
+
+ xwl_output->scale = 1;
}
/* We want the output to be in the list as soon as created so we can
* use it when binding to the xdg-output protocol...
*/
diff --git a/hw/xwayland/xwayland-output.h b/hw/xwayland/xwayland-output.h
index 02b9831083e82a33d85d4404e39d00f06f6c56fd..ec089757f44178dcd7f9c48907f790ce1b2a2729 100644
index a95288e4f..46d1ead2a 100644
--- a/hw/xwayland/xwayland-output.h
+++ b/hw/xwayland/xwayland-output.h
@@ -53,7 +53,7 @@ struct xwl_output {
@@ -256,7 +245,7 @@ index 02b9831083e82a33d85d4404e39d00f06f6c56fd..ec089757f44178dcd7f9c48907f790ce
Rotation rotation;
Bool wl_output_done;
Bool xdg_output_done;
@@ -100,6 +100,8 @@ void xwl_output_set_emulated_mode(struct xwl_output *xwl_output,
@@ -102,6 +102,8 @@ void xwl_output_set_emulated_mode(struct xwl_output *xwl_output,
void xwl_output_set_window_randr_emu_props(struct xwl_screen *xwl_screen,
WindowPtr window);
@@ -266,20 +255,20 @@ index 02b9831083e82a33d85d4404e39d00f06f6c56fd..ec089757f44178dcd7f9c48907f790ce
#endif /* XWAYLAND_OUTPUT_H */
diff --git a/hw/xwayland/xwayland-present.c b/hw/xwayland/xwayland-present.c
index c9cf8c2f569a319034e0789e7587414e50237065..5be0c208ca46b1a53a136885fdc8ab44251fe7ff 100644
index 189e7cfd6..555434031 100644
--- a/hw/xwayland/xwayland-present.c
+++ b/hw/xwayland/xwayland-present.c
@@ -680,6 +680,8 @@ xwl_present_flip(WindowPtr present_window,
@@ -764,6 +764,8 @@ xwl_present_flip(WindowPtr present_window,
/* We can flip directly to the main surface (full screen window without clips) */
wl_surface_attach(xwl_window->surface, buffer, 0, 0);
+ wl_surface_set_buffer_scale(xwl_window->surface,
+ xwl_window->xwl_screen->global_output_scale);
+ wl_surface_set_buffer_scale(xwl_window->surface,
+ xwl_window->xwl_screen->global_output_scale);
if (!xwl_window->frame_callback)
xwl_window_create_frame_callback(xwl_window);
if (xorg_list_is_empty(&xwl_present_window->frame_callback_list)) {
xorg_list_add(&xwl_present_window->frame_callback_list,
diff --git a/hw/xwayland/xwayland-screen.c b/hw/xwayland/xwayland-screen.c
index bb18e5c94fbc7134c801e4e1979e8184079d352e..4ec2de7d123dd36315df07a1e95b1f417925f0f8 100644
index 46ab4fed7..b2d7022e6 100644
--- a/hw/xwayland/xwayland-screen.c
+++ b/hw/xwayland/xwayland-screen.c
@@ -51,6 +51,7 @@
@@ -290,7 +279,7 @@ index bb18e5c94fbc7134c801e4e1979e8184079d352e..4ec2de7d123dd36315df07a1e95b1f41
#ifdef MITSHM
#include "shmint.h"
@@ -110,6 +111,12 @@ xwl_screen_has_resolution_change_emulation(struct xwl_screen *xwl_screen)
@@ -111,6 +112,12 @@ xwl_screen_has_resolution_change_emulation(struct xwl_screen *xwl_screen)
return xwl_screen->rootless && xwl_screen_has_viewport_support(xwl_screen);
}
@@ -303,7 +292,7 @@ index bb18e5c94fbc7134c801e4e1979e8184079d352e..4ec2de7d123dd36315df07a1e95b1f41
/* Return the output @ 0x0, falling back to the first output in the list */
struct xwl_output *
xwl_screen_get_first_output(struct xwl_screen *xwl_screen)
@@ -127,6 +134,37 @@ xwl_screen_get_first_output(struct xwl_screen *xwl_screen)
@@ -128,6 +135,38 @@ xwl_screen_get_first_output(struct xwl_screen *xwl_screen)
return xorg_list_first_entry(&xwl_screen->output_list, struct xwl_output, link);
}
@@ -338,10 +327,11 @@ index bb18e5c94fbc7134c801e4e1979e8184079d352e..4ec2de7d123dd36315df07a1e95b1f41
+ }
+}
+
static void
xwl_property_callback(CallbackListPtr *pcbl, void *closure,
void *calldata)
@@ -134,19 +172,24 @@ xwl_property_callback(CallbackListPtr *pcbl, void *closure,
+
struct xwl_output *
xwl_screen_get_fixed_or_first_output(struct xwl_screen *xwl_screen)
{
@@ -144,19 +183,24 @@ xwl_property_callback(CallbackListPtr *pcbl, void *closure,
ScreenPtr screen = closure;
PropertyStateRec *rec = calldata;
struct xwl_screen *xwl_screen;
@@ -357,6 +347,7 @@ index bb18e5c94fbc7134c801e4e1979e8184079d352e..4ec2de7d123dd36315df07a1e95b1f41
xwl_screen = xwl_screen_get(screen);
- if (rec->prop->propertyName == xwl_screen->allow_commits_prop)
- xwl_window_update_property(xwl_window, rec);
+ if (rec->prop->propertyName == xwl_screen->allow_commits_prop) {
+ struct xwl_window *xwl_window;
+
@@ -364,15 +355,15 @@ index bb18e5c94fbc7134c801e4e1979e8184079d352e..4ec2de7d123dd36315df07a1e95b1f41
+ if (!xwl_window)
+ return;
+
xwl_window_update_property(xwl_window, rec);
+ xwl_window_update_property(xwl_window, rec);
+ }
+ else if (rec->prop->propertyName == xwl_screen->global_output_scale_prop) {
+ xwl_screen_update_property(xwl_screen, rec);
+ }
}
Bool
@@ -521,8 +564,14 @@ void xwl_surface_damage(struct xwl_screen *xwl_screen,
static void
@@ -638,8 +682,14 @@ void xwl_surface_damage(struct xwl_screen *xwl_screen,
{
if (wl_surface_get_version(surface) >= WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION)
wl_surface_damage_buffer(surface, x, y, width, height);
@@ -388,7 +379,7 @@ index bb18e5c94fbc7134c801e4e1979e8184079d352e..4ec2de7d123dd36315df07a1e95b1f41
}
void
@@ -538,10 +587,34 @@ xwl_screen_roundtrip(struct xwl_screen *xwl_screen)
@@ -655,6 +705,30 @@ xwl_screen_roundtrip(struct xwl_screen *xwl_screen)
xwl_give_up("could not connect to wayland server\n");
}
@@ -415,7 +406,11 @@ index bb18e5c94fbc7134c801e4e1979e8184079d352e..4ec2de7d123dd36315df07a1e95b1f41
+ }
+}
+
Bool
+
static int
xwl_server_grab(ClientPtr client)
{
@@ -712,6 +786,7 @@ Bool
xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
{
static const char allow_commits[] = "_XWAYLAND_ALLOW_COMMITS";
@@ -423,7 +418,7 @@ index bb18e5c94fbc7134c801e4e1979e8184079d352e..4ec2de7d123dd36315df07a1e95b1f41
struct xwl_screen *xwl_screen;
Pixel red_mask, blue_mask, green_mask;
int ret, bpc, green_bpc, i;
@@ -573,6 +646,7 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
@@ -746,6 +821,7 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
#ifdef XWL_HAS_GLAMOR
xwl_screen->glamor = 1;
#endif
@@ -431,7 +426,7 @@ index bb18e5c94fbc7134c801e4e1979e8184079d352e..4ec2de7d123dd36315df07a1e95b1f41
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "-rootless") == 0) {
@@ -743,6 +817,12 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
@@ -988,6 +1064,13 @@ xwl_screen_init(ScreenPtr pScreen, int argc, char **argv)
if (xwl_screen->allow_commits_prop == BAD_RESOURCE)
return FALSE;
@@ -440,24 +435,24 @@ index bb18e5c94fbc7134c801e4e1979e8184079d352e..4ec2de7d123dd36315df07a1e95b1f41
+ TRUE);
+ if (xwl_screen->global_output_scale_prop == BAD_RESOURCE)
+ return FALSE;
+
+
AddCallback(&PropertyStateCallback, xwl_property_callback, pScreen);
AddCallback(&RootWindowFinalizeCallback, xwl_root_window_finalized_callback, pScreen);
xwl_screen_roundtrip(xwl_screen);
diff --git a/hw/xwayland/xwayland-screen.h b/hw/xwayland/xwayland-screen.h
index b965dddd7f964b1d100bbb9d10da1c35ab39810e..7446829d098fbe235e605084a016daff1a8eaea2 100644
index fadd0526e..2ce6ce5ab 100644
--- a/hw/xwayland/xwayland-screen.h
+++ b/hw/xwayland/xwayland-screen.h
@@ -72,6 +72,8 @@ struct xwl_screen {
@@ -87,6 +87,7 @@ struct xwl_screen {
struct xorg_list damage_window_list;
struct xorg_list window_list;
+ int32_t global_output_scale;
+
int wayland_fd;
struct wl_display *display;
struct wl_registry *registry;
@@ -107,6 +109,7 @@ struct xwl_screen {
@@ -134,6 +135,7 @@ struct xwl_screen {
struct glamor_context *glamor_ctx;
Atom allow_commits_prop;
@@ -465,29 +460,30 @@ index b965dddd7f964b1d100bbb9d10da1c35ab39810e..7446829d098fbe235e605084a016daff
/* The preferred GLVND vendor. If NULL, "mesa" is assumed. */
const char *glvnd_vendor;
@@ -134,5 +137,7 @@ void xwl_screen_roundtrip (struct xwl_screen *xwl_screen);
@@ -166,6 +168,8 @@ void xwl_screen_roundtrip (struct xwl_screen *xwl_screen);
void xwl_surface_damage(struct xwl_screen *xwl_screen,
struct wl_surface *surface,
int32_t x, int32_t y, int32_t width, int32_t height);
+int xwl_scale_to(struct xwl_screen *xwl_screen, int value);
+void xwl_screen_set_global_scale(struct xwl_screen *xwl_screen, int32_t scale);
int xwl_screen_get_next_output_serial(struct xwl_screen * xwl_screen);
#endif /* XWAYLAND_SCREEN_H */
diff --git a/hw/xwayland/xwayland-window.c b/hw/xwayland/xwayland-window.c
index 00f161eda084e335ac07471a2198176d75d9fcf0..ed3903853f0dab1dad390cd8429639541546157d 100644
index 6b7f38605..2f1e0dee1 100644
--- a/hw/xwayland/xwayland-window.c
+++ b/hw/xwayland/xwayland-window.c
@@ -470,7 +470,8 @@ ensure_surface_for_window(WindowPtr window)
}
wl_region_add(region, 0, 0,
- window->drawable.width, window->drawable.height);
+ xwl_scale_to(xwl_screen, window->drawable.width),
+ xwl_scale_to(xwl_screen, window->drawable.height));
wl_surface_set_opaque_region(xwl_window->surface, region);
wl_region_destroy(region);
@@ -788,7 +788,8 @@ xwl_create_root_surface(struct xwl_window *xwl_window)
}
@@ -820,6 +821,7 @@ xwl_window_post_damage(struct xwl_window *xwl_window)
wl_region_add(region, 0, 0,
- window->drawable.width, window->drawable.height);
+ xwl_scale_to(xwl_screen, window->drawable.width),
+ xwl_scale_to(xwl_screen, window->drawable.height));
wl_surface_set_opaque_region(xwl_window->surface, region);
wl_region_destroy(region);
@@ -1322,6 +1323,7 @@ xwl_window_post_damage(struct xwl_window *xwl_window)
#endif
wl_surface_attach(xwl_window->surface, buffer, 0, 0);
@@ -495,4 +491,3 @@ index 00f161eda084e335ac07471a2198176d75d9fcf0..ed3903853f0dab1dad390cd842963954
/* Arbitrary limit to try to avoid flooding the Wayland
* connection. If we flood it too much anyway, this could

View File

@@ -1,3 +1,3 @@
{
"version": "0.22.0"
"version": "0.25.0"
}

View File

@@ -5,7 +5,7 @@ wayland_protos = dependency('wayland-protocols',
)
hyprland_protos = dependency('hyprland-protocols',
version: '>=0.1',
version: '>=0.2',
fallback: 'hyprland-protocols',
)
@@ -26,11 +26,13 @@ protocols = [
['wlr-foreign-toplevel-management-unstable-v1.xml'],
['wlr-layer-shell-unstable-v1.xml'],
['wlr-output-power-management-unstable-v1.xml'],
['wlr-screencopy-unstable-v1.xml'],
['ext-workspace-unstable-v1.xml'],
['pointer-constraints-unstable-v1.xml'],
['tablet-unstable-v2.xml'],
['idle.xml'],
[hl_protocol_dir, 'protocols/hyprland-toplevel-export-v1.xml']
[hl_protocol_dir, 'protocols/hyprland-toplevel-export-v1.xml'],
[hl_protocol_dir, 'protocols/hyprland-global-shortcuts-v1.xml']
]
wl_protos_src = []
wl_protos_headers = []
@@ -45,6 +47,8 @@ foreach p : protocols
wl_protos_headers += custom_target(
xml.underscorify() + '_server_h',
input: xml,
install: true,
install_dir: join_paths(get_option('includedir'), 'hyprland/protocols'),
output: '@BASENAME@-protocol.h',
command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'],
)

View File

@@ -85,6 +85,7 @@ void CCompositor::setRandomSplash() {
}
void CCompositor::initServer() {
m_sWLDisplay = wl_display_create();
m_sWLEventLoop = wl_display_get_event_loop(m_sWLDisplay);
@@ -146,12 +147,11 @@ void CCompositor::initServer() {
throw std::runtime_error("wlr_gles2_renderer_get_egl() failed!");
}
m_sWLRCompositor = wlr_compositor_create(m_sWLDisplay, m_sWLRRenderer);
m_sWLRCompositor = wlr_compositor_create(m_sWLDisplay, 6, m_sWLRRenderer);
m_sWLRSubCompositor = wlr_subcompositor_create(m_sWLDisplay);
m_sWLRDataDevMgr = wlr_data_device_manager_create(m_sWLDisplay);
wlr_export_dmabuf_manager_v1_create(m_sWLDisplay);
wlr_screencopy_manager_v1_create(m_sWLDisplay);
wlr_data_control_manager_v1_create(m_sWLDisplay);
wlr_gamma_control_manager_v1_create(m_sWLDisplay);
wlr_primary_selection_v1_device_manager_create(m_sWLDisplay);
@@ -315,12 +315,6 @@ void CCompositor::cleanup() {
m_pLastFocus = nullptr;
m_pLastWindow = nullptr;
// accumulate all PIDs for killing, also request closing.
for (auto& w : m_vWindows) {
if (w->m_bIsMapped && !w->isHidden())
m_dProcessPIDsOnShutdown.push_back(w->getPID());
}
// end threads
g_pEventManager->m_tThread = std::thread();
@@ -346,9 +340,6 @@ void CCompositor::cleanup() {
wl_display_terminate(m_sWLDisplay);
m_sWLDisplay = nullptr;
g_pKeybindManager->spawn("sleep 5 && kill -9 " + std::to_string(m_iHyprlandPID)); // this is to prevent that random "freezing"
// the PID should not be reused.
}
void CCompositor::initManagers(eManagersInitStage stage) {
@@ -408,6 +399,7 @@ void CCompositor::initManagers(eManagersInitStage stage) {
Debug::log(LOG, "Creating the PluginSystem!");
g_pPluginSystem = std::make_unique<CPluginSystem>();
g_pConfigManager->handlePluginLoads();
} break;
default: UNREACHABLE();
}
@@ -449,7 +441,9 @@ void CCompositor::startCompositor() {
if (m_sWLRSession /* Session-less Hyprland usually means a nest, don't update the env in that case */ && fork() == 0)
execl(
"/bin/sh", "/bin/sh", "-c",
#ifdef USES_SYSTEMD
"systemctl --user import-environment DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP && hash dbus-update-activation-environment 2>/dev/null && "
#endif
"dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE",
nullptr);
@@ -531,21 +525,8 @@ CMonitor* CCompositor::getMonitorFromVector(const Vector2D& point) {
}
void CCompositor::removeWindowFromVectorSafe(CWindow* pWindow) {
if (windowExists(pWindow) && !pWindow->m_bFadingOut) {
// if X11, also check its children
// and delete any needed
if (pWindow->m_bIsX11) {
for (auto& w : m_vWindows) {
if (!w->m_bIsX11)
continue;
if (w->m_pX11Parent == pWindow)
std::erase_if(m_vWindows, [&](std::unique_ptr<CWindow>& el) { return el.get() == w.get(); });
}
}
if (windowExists(pWindow) && !pWindow->m_bFadingOut)
std::erase_if(m_vWindows, [&](std::unique_ptr<CWindow>& el) { return el.get() == pWindow; });
}
}
bool CCompositor::windowExists(CWindow* pWindow) {
@@ -563,13 +544,15 @@ CWindow* CCompositor::vectorToWindow(const Vector2D& pos) {
if (PMONITOR->specialWorkspaceID) {
for (auto& w : m_vWindows | std::views::reverse) {
wlr_box box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if (w->m_bIsFloating && w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && w->m_bIsMapped && wlr_box_contains_point(&box, pos.x, pos.y) && !w->isHidden())
if (w->m_bIsFloating && w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && w->m_bIsMapped && wlr_box_contains_point(&box, pos.x, pos.y) && !w->isHidden() &&
!w->m_bNoFocus)
return w.get();
}
for (auto& w : m_vWindows) {
wlr_box box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if (w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && wlr_box_contains_point(&box, pos.x, pos.y) && w->m_bIsMapped && !w->m_bIsFloating && !w->isHidden())
if (w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && wlr_box_contains_point(&box, pos.x, pos.y) && w->m_bIsMapped && !w->m_bIsFloating && !w->isHidden() &&
!w->m_bNoFocus)
return w.get();
}
}
@@ -577,20 +560,21 @@ CWindow* CCompositor::vectorToWindow(const Vector2D& pos) {
// pinned
for (auto& w : m_vWindows | std::views::reverse) {
wlr_box box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if (wlr_box_contains_point(&box, pos.x, pos.y) && w->m_bIsMapped && w->m_bIsFloating && !w->isHidden() && w->m_bPinned)
if (wlr_box_contains_point(&box, pos.x, pos.y) && w->m_bIsMapped && w->m_bIsFloating && !w->isHidden() && w->m_bPinned && !w->m_bNoFocus)
return w.get();
}
// first loop over floating cuz they're above, m_vWindows should be sorted bottom->top, for tiled it doesn't matter.
for (auto& w : m_vWindows | std::views::reverse) {
wlr_box box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if (wlr_box_contains_point(&box, pos.x, pos.y) && w->m_bIsMapped && w->m_bIsFloating && isWorkspaceVisible(w->m_iWorkspaceID) && !w->isHidden() && !w->m_bPinned)
if (wlr_box_contains_point(&box, pos.x, pos.y) && w->m_bIsMapped && w->m_bIsFloating && isWorkspaceVisible(w->m_iWorkspaceID) && !w->isHidden() && !w->m_bPinned &&
!w->m_bNoFocus)
return w.get();
}
for (auto& w : m_vWindows) {
wlr_box box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if (wlr_box_contains_point(&box, pos.x, pos.y) && w->m_bIsMapped && !w->m_bIsFloating && PMONITOR->activeWorkspace == w->m_iWorkspaceID && !w->isHidden())
if (wlr_box_contains_point(&box, pos.x, pos.y) && w->m_bIsMapped && !w->m_bIsFloating && PMONITOR->activeWorkspace == w->m_iWorkspaceID && !w->isHidden() && !w->m_bNoFocus)
return w.get();
}
@@ -603,14 +587,14 @@ CWindow* CCompositor::vectorToWindowTiled(const Vector2D& pos) {
if (PMONITOR->specialWorkspaceID) {
for (auto& w : m_vWindows) {
wlr_box box = {w->m_vPosition.x, w->m_vPosition.y, w->m_vSize.x, w->m_vSize.y};
if (w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && wlr_box_contains_point(&box, pos.x, pos.y) && !w->m_bIsFloating && !w->isHidden())
if (w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && wlr_box_contains_point(&box, pos.x, pos.y) && !w->m_bIsFloating && !w->isHidden() && !w->m_bNoFocus)
return w.get();
}
}
for (auto& w : m_vWindows) {
wlr_box box = {w->m_vPosition.x, w->m_vPosition.y, w->m_vSize.x, w->m_vSize.y};
if (w->m_bIsMapped && wlr_box_contains_point(&box, pos.x, pos.y) && w->m_iWorkspaceID == PMONITOR->activeWorkspace && !w->m_bIsFloating && !w->isHidden())
if (w->m_bIsMapped && wlr_box_contains_point(&box, pos.x, pos.y) && w->m_iWorkspaceID == PMONITOR->activeWorkspace && !w->m_bIsFloating && !w->isHidden() && !w->m_bNoFocus)
return w.get();
}
@@ -630,14 +614,14 @@ CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos) {
const auto BB = w->getWindowInputBox();
wlr_box box = {BB.x - BORDER_GRAB_AREA, BB.y - BORDER_GRAB_AREA, BB.width + 2 * BORDER_GRAB_AREA, BB.height + 2 * BORDER_GRAB_AREA};
if (w->m_bIsFloating && w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && w->m_bIsMapped && wlr_box_contains_point(&box, pos.x, pos.y) && !w->isHidden() &&
!w->m_bX11ShouldntFocus)
!w->m_bX11ShouldntFocus && !w->m_bNoFocus)
return w.get();
}
for (auto& w : m_vWindows) {
wlr_box box = {w->m_vPosition.x, w->m_vPosition.y, w->m_vSize.x, w->m_vSize.y};
if (!w->m_bIsFloating && w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && w->m_bIsMapped && wlr_box_contains_point(&box, pos.x, pos.y) && !w->isHidden() &&
!w->m_bX11ShouldntFocus)
!w->m_bX11ShouldntFocus && !w->m_bNoFocus)
return w.get();
}
}
@@ -646,7 +630,7 @@ CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos) {
for (auto& w : m_vWindows | std::views::reverse) {
const auto BB = w->getWindowInputBox();
wlr_box box = {BB.x - BORDER_GRAB_AREA, BB.y - BORDER_GRAB_AREA, BB.width + 2 * BORDER_GRAB_AREA, BB.height + 2 * BORDER_GRAB_AREA};
if (w->m_bIsFloating && w->m_bIsMapped && !w->isHidden() && !w->m_bX11ShouldntFocus && w->m_bPinned) {
if (w->m_bIsFloating && w->m_bIsMapped && !w->isHidden() && !w->m_bX11ShouldntFocus && w->m_bPinned && !w->m_bNoFocus) {
if (wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y))
return w.get();
@@ -661,7 +645,7 @@ CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos) {
for (auto& w : m_vWindows | std::views::reverse) {
const auto BB = w->getWindowInputBox();
wlr_box box = {BB.x - BORDER_GRAB_AREA, BB.y - BORDER_GRAB_AREA, BB.width + 2 * BORDER_GRAB_AREA, BB.height + 2 * BORDER_GRAB_AREA};
if (w->m_bIsFloating && w->m_bIsMapped && isWorkspaceVisible(w->m_iWorkspaceID) && !w->isHidden() && !w->m_bPinned) {
if (w->m_bIsFloating && w->m_bIsMapped && isWorkspaceVisible(w->m_iWorkspaceID) && !w->isHidden() && !w->m_bPinned && !w->m_bNoFocus) {
// OR windows should add focus to parent
if (w->m_bX11ShouldntFocus && w->m_iX11Type != 2)
continue;
@@ -686,7 +670,7 @@ CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos) {
// for windows, we need to check their extensions too, first.
for (auto& w : m_vWindows) {
if (!w->m_bIsX11 && !w->m_bIsFloating && w->m_bIsMapped && w->m_iWorkspaceID == PMONITOR->activeWorkspace && !w->isHidden() && !w->m_bX11ShouldntFocus) {
if (!w->m_bIsX11 && !w->m_bIsFloating && w->m_bIsMapped && w->m_iWorkspaceID == PMONITOR->activeWorkspace && !w->isHidden() && !w->m_bX11ShouldntFocus && !w->m_bNoFocus) {
if ((w)->hasPopupAt(pos))
return w.get();
}
@@ -694,7 +678,7 @@ CWindow* CCompositor::vectorToWindowIdeal(const Vector2D& pos) {
for (auto& w : m_vWindows) {
wlr_box box = {w->m_vPosition.x, w->m_vPosition.y, w->m_vSize.x, w->m_vSize.y};
if (!w->m_bIsFloating && w->m_bIsMapped && wlr_box_contains_point(&box, pos.x, pos.y) && w->m_iWorkspaceID == PMONITOR->activeWorkspace && !w->isHidden() &&
!w->m_bX11ShouldntFocus)
!w->m_bX11ShouldntFocus && !w->m_bNoFocus)
return w.get();
}
@@ -708,13 +692,13 @@ CWindow* CCompositor::windowFromCursor() {
for (auto& w : m_vWindows | std::views::reverse) {
wlr_box box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if (w->m_bIsFloating && w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && w->m_bIsMapped && wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y) &&
!w->isHidden())
!w->isHidden() && !w->m_bNoFocus)
return w.get();
}
for (auto& w : m_vWindows) {
wlr_box box = {w->m_vPosition.x, w->m_vPosition.y, w->m_vSize.x, w->m_vSize.y};
if (w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y) && w->m_bIsMapped)
if (w->m_iWorkspaceID == PMONITOR->specialWorkspaceID && wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y) && w->m_bIsMapped && !w->m_bNoFocus)
return w.get();
}
}
@@ -722,20 +706,21 @@ CWindow* CCompositor::windowFromCursor() {
// pinned
for (auto& w : m_vWindows | std::views::reverse) {
wlr_box box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if (wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y) && w->m_bIsMapped && w->m_bIsFloating && w->m_bPinned)
if (wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y) && w->m_bIsMapped && w->m_bIsFloating && w->m_bPinned && !w->m_bNoFocus)
return w.get();
}
// first loop over floating cuz they're above, m_lWindows should be sorted bottom->top, for tiled it doesn't matter.
for (auto& w : m_vWindows | std::views::reverse) {
wlr_box box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if (wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y) && w->m_bIsMapped && w->m_bIsFloating && isWorkspaceVisible(w->m_iWorkspaceID) && !w->m_bPinned)
if (wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y) && w->m_bIsMapped && w->m_bIsFloating && isWorkspaceVisible(w->m_iWorkspaceID) && !w->m_bPinned &&
!w->m_bNoFocus)
return w.get();
}
for (auto& w : m_vWindows) {
wlr_box box = {w->m_vPosition.x, w->m_vPosition.y, w->m_vSize.x, w->m_vSize.y};
if (wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y) && w->m_bIsMapped && w->m_iWorkspaceID == PMONITOR->activeWorkspace)
if (wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y) && w->m_bIsMapped && w->m_iWorkspaceID == PMONITOR->activeWorkspace && !w->m_bNoFocus)
return w.get();
}
@@ -745,14 +730,14 @@ CWindow* CCompositor::windowFromCursor() {
CWindow* CCompositor::windowFloatingFromCursor() {
for (auto& w : m_vWindows | std::views::reverse) {
wlr_box box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if (wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y) && w->m_bIsMapped && w->m_bIsFloating && !w->isHidden() && w->m_bPinned)
if (wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y) && w->m_bIsMapped && w->m_bIsFloating && !w->isHidden() && w->m_bPinned && !w->m_bNoFocus)
return w.get();
}
for (auto& w : m_vWindows | std::views::reverse) {
wlr_box box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
if (wlr_box_contains_point(&box, m_sWLRCursor->x, m_sWLRCursor->y) && w->m_bIsMapped && w->m_bIsFloating && isWorkspaceVisible(w->m_iWorkspaceID) && !w->isHidden() &&
!w->m_bPinned)
!w->m_bPinned && !w->m_bNoFocus)
return w.get();
}
@@ -826,7 +811,7 @@ void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) {
g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", ","});
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", ","});
EMIT_HOOK_EVENT("activeWindow", nullptr);
EMIT_HOOK_EVENT("activeWindow", (CWindow*)nullptr);
g_pLayoutManager->getCurrentLayout()->onWindowFocusChange(nullptr);
@@ -851,7 +836,8 @@ void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) {
// This is to fix incorrect feedback on the focus history.
const auto PWORKSPACE = getWorkspaceByID(pWindow->m_iWorkspaceID);
PWORKSPACE->m_pLastFocusedWindow = pWindow;
g_pKeybindManager->changeworkspace("[internal]" + std::to_string(pWindow->m_iWorkspaceID));
const auto PMONITOR = getMonitorFromID(PWORKSPACE->m_iMonitorID);
PMONITOR->changeWorkspace(PWORKSPACE);
// changeworkspace already calls focusWindow
return;
}
@@ -872,7 +858,7 @@ void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) {
m_pLastWindow = PLASTWINDOW;
const auto PWINDOWSURFACE = pSurface ? pSurface : g_pXWaylandManager->getWindowSurface(pWindow);
const auto PWINDOWSURFACE = pSurface ? pSurface : pWindow->m_pWLSurface.wlr();
focusSurface(PWINDOWSURFACE, pWindow);
@@ -897,7 +883,7 @@ void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) {
// Send an event
g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", g_pXWaylandManager->getAppIDClass(pWindow) + "," + pWindow->m_szTitle});
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", getFormat("%x", pWindow)});
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", getFormat("%lx", pWindow)});
EMIT_HOOK_EVENT("activeWindow", pWindow);
@@ -918,7 +904,7 @@ void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) {
// move to front of the window history
const auto HISTORYPIVOT = std::find_if(m_vWindowFocusHistory.begin(), m_vWindowFocusHistory.end(), [&](const auto& other) { return other == pWindow; });
if (HISTORYPIVOT == m_vWindowFocusHistory.end()) {
Debug::log(ERR, "BUG THIS: Window %x has no pivot in history", pWindow);
Debug::log(ERR, "BUG THIS: Window %lx has no pivot in history", pWindow);
} else {
std::rotate(m_vWindowFocusHistory.begin(), HISTORYPIVOT, HISTORYPIVOT + 1);
}
@@ -926,8 +912,7 @@ void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) {
void CCompositor::focusSurface(wlr_surface* pSurface, CWindow* pWindowOwner) {
if (m_sSeat.seat->keyboard_state.focused_surface == pSurface ||
(pWindowOwner && m_sSeat.seat->keyboard_state.focused_surface == g_pXWaylandManager->getWindowSurface(pWindowOwner)))
if (m_sSeat.seat->keyboard_state.focused_surface == pSurface || (pWindowOwner && m_sSeat.seat->keyboard_state.focused_surface == pWindowOwner->m_pWLSurface.wlr()))
return; // Don't focus when already focused on this.
if (g_pSessionLockManager->isSessionLocked()) {
@@ -953,7 +938,8 @@ void CCompositor::focusSurface(wlr_surface* pSurface, CWindow* pWindowOwner) {
if (!KEYBOARD)
return;
wlr_seat_keyboard_notify_enter(m_sSeat.seat, pSurface, KEYBOARD->keycodes, KEYBOARD->num_keycodes, &KEYBOARD->modifiers);
uint32_t keycodes[WLR_KEYBOARD_KEYS_CAP] = {0}; // TODO: maybe send valid, non-keybind codes?
wlr_seat_keyboard_notify_enter(m_sSeat.seat, pSurface, keycodes, 0, &KEYBOARD->modifiers);
wlr_seat_keyboard_focus_change_event event = {
.seat = m_sSeat.seat,
@@ -963,9 +949,9 @@ void CCompositor::focusSurface(wlr_surface* pSurface, CWindow* pWindowOwner) {
wl_signal_emit_mutable(&m_sSeat.seat->keyboard_state.events.focus_change, &event);
if (pWindowOwner)
Debug::log(LOG, "Set keyboard focus to surface %x, with window name: %s", pSurface, pWindowOwner->m_szTitle.c_str());
Debug::log(LOG, "Set keyboard focus to surface %lx, with window name: %s", pSurface, pWindowOwner->m_szTitle.c_str());
else
Debug::log(LOG, "Set keyboard focus to surface %x", pSurface);
Debug::log(LOG, "Set keyboard focus to surface %lx", pSurface);
g_pXWaylandManager->activateSurface(pSurface, true);
m_pLastFocus = pSurface;
@@ -1009,10 +995,6 @@ wlr_surface* CCompositor::vectorToLayerSurface(const Vector2D& pos, std::vector<
auto SURFACEAT = wlr_layer_surface_v1_surface_at(ls->layerSurface, pos.x - ls->geometry.x, pos.y - ls->geometry.y, &sCoords->x, &sCoords->y);
if (!SURFACEAT && VECINRECT(pos, ls->geometry.x, ls->geometry.y, ls->geometry.x + ls->geometry.width, ls->geometry.y + ls->geometry.height)) {
SURFACEAT = ls->layerSurface->surface;
}
if (ls->layerSurface->current.keyboard_interactive && ls->layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) {
if (!SURFACEAT)
SURFACEAT = ls->layerSurface->surface;
@@ -1038,7 +1020,7 @@ CWindow* CCompositor::getWindowFromSurface(wlr_surface* pSurface) {
if (!w->m_bIsMapped || w->m_bFadingOut || !w->m_bMappedX11)
continue;
if (g_pXWaylandManager->getWindowSurface(w.get()) == pSurface)
if (w->m_pWLSurface.wlr() == pSurface)
return w.get();
}
@@ -1105,10 +1087,14 @@ CWorkspace* CCompositor::getWorkspaceByID(const int& id) {
void CCompositor::sanityCheckWorkspaces() {
auto it = m_vWorkspaces.begin();
while (it != m_vWorkspaces.end()) {
if ((*it)->m_bIndestructible)
continue;
const auto WINDOWSONWORKSPACE = getWindowsOnWorkspace((*it)->m_iID);
if ((*it)->m_bIsSpecialWorkspace && WINDOWSONWORKSPACE == 0) {
getMonitorFromID((*it)->m_iMonitorID)->specialWorkspaceID = 0;
getMonitorFromID((*it)->m_iMonitorID)->setSpecialWorkspace(nullptr);
it = m_vWorkspaces.erase(it);
continue;
@@ -1188,7 +1174,7 @@ bool CCompositor::isWindowActive(CWindow* pWindow) {
if (!windowValidMapped(pWindow))
return false;
const auto PSURFACE = g_pXWaylandManager->getWindowSurface(pWindow);
const auto PSURFACE = pWindow->m_pWLSurface.wlr();
return PSURFACE == m_pLastFocus || pWindow == m_pLastWindow;
}
@@ -1525,11 +1511,11 @@ CWindow* CCompositor::getConstraintWindow(SMouse* pMouse) {
const auto PSURFACE = pMouse->currentConstraint->surface;
for (auto& w : m_vWindows) {
if (w->isHidden() || !w->m_bMappedX11 || !w->m_bIsMapped || !g_pXWaylandManager->getWindowSurface(w.get()))
if (w->isHidden() || !w->m_bMappedX11 || !w->m_bIsMapped || !w->m_pWLSurface.exists())
continue;
if (w->m_bIsX11) {
if (PSURFACE == g_pXWaylandManager->getWindowSurface(w.get()))
if (PSURFACE == w->m_pWLSurface.wlr())
return w.get();
} else {
std::pair<wlr_surface*, bool> check = {PSURFACE, false};
@@ -1715,6 +1701,11 @@ void CCompositor::swapActiveWorkspaces(CMonitor* pMonitorA, CMonitor* pMonitorB)
for (auto& w : m_vWindows) {
if (w->m_iWorkspaceID == PWORKSPACEA->m_iID) {
if (w->m_bPinned) {
w->m_iWorkspaceID = PWORKSPACEB->m_iID;
continue;
}
w->m_iMonitorID = pMonitorB->ID;
// additionally, move floating and fs windows manually
@@ -1735,6 +1726,11 @@ void CCompositor::swapActiveWorkspaces(CMonitor* pMonitorA, CMonitor* pMonitorB)
for (auto& w : m_vWindows) {
if (w->m_iWorkspaceID == PWORKSPACEB->m_iID) {
if (w->m_bPinned) {
w->m_iWorkspaceID = PWORKSPACEA->m_iID;
continue;
}
w->m_iMonitorID = pMonitorA->ID;
// additionally, move floating and fs windows manually
@@ -1750,23 +1746,15 @@ void CCompositor::swapActiveWorkspaces(CMonitor* pMonitorA, CMonitor* pMonitorB)
}
}
// fix pinned windows
for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_iWorkspaceID == pMonitorA->activeWorkspace && w->m_bPinned) {
w->m_iWorkspaceID = PWORKSPACEB->m_iID;
}
if (w->m_iWorkspaceID == pMonitorB->activeWorkspace && w->m_bPinned) {
w->m_iWorkspaceID = PWORKSPACEA->m_iID;
}
}
pMonitorA->activeWorkspace = PWORKSPACEB->m_iID;
pMonitorB->activeWorkspace = PWORKSPACEA->m_iID;
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(pMonitorA->ID);
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(pMonitorB->ID);
updateFullscreenFadeOnWorkspace(PWORKSPACEB);
updateFullscreenFadeOnWorkspace(PWORKSPACEA);
g_pInputManager->refocus();
// event
@@ -1836,6 +1824,9 @@ CMonitor* CCompositor::getMonitorFromString(const std::string& name) {
const auto DESCRIPTION = name.substr(5);
for (auto& m : m_vMonitors) {
if (!m->output)
continue;
if (m->output->description && std::string(m->output->description).find(DESCRIPTION) == 0) {
return m.get();
}
@@ -1876,37 +1867,45 @@ void CCompositor::moveWorkspaceToMonitor(CWorkspace* pWorkspace, CMonitor* pMoni
// fix old mon
int nextWorkspaceOnMonitorID = -1;
for (auto& w : m_vWorkspaces) {
if (w->m_iMonitorID == POLDMON->ID && w->m_iID != pWorkspace->m_iID) {
nextWorkspaceOnMonitorID = w->m_iID;
break;
if (!SWITCHINGISACTIVE)
nextWorkspaceOnMonitorID = pWorkspace->m_iID;
else {
for (auto& w : m_vWorkspaces) {
if (w->m_iMonitorID == POLDMON->ID && w->m_iID != pWorkspace->m_iID && !w->m_bIsSpecialWorkspace) {
nextWorkspaceOnMonitorID = w->m_iID;
break;
}
}
if (nextWorkspaceOnMonitorID == -1) {
nextWorkspaceOnMonitorID = 1;
while (getWorkspaceByID(nextWorkspaceOnMonitorID) || [&]() -> bool {
const auto B = g_pConfigManager->getBoundMonitorForWS(std::to_string(nextWorkspaceOnMonitorID));
return B && B != POLDMON;
}())
nextWorkspaceOnMonitorID++;
Debug::log(LOG, "moveWorkspaceToMonitor: Plugging gap with new %d", nextWorkspaceOnMonitorID);
g_pCompositor->createNewWorkspace(nextWorkspaceOnMonitorID, POLDMON->ID);
}
Debug::log(LOG, "moveWorkspaceToMonitor: Plugging gap with existing %d", nextWorkspaceOnMonitorID);
POLDMON->changeWorkspace(nextWorkspaceOnMonitorID);
}
if (nextWorkspaceOnMonitorID == -1) {
nextWorkspaceOnMonitorID = 1;
while (getWorkspaceByID(nextWorkspaceOnMonitorID) || [&]() -> bool {
const auto B = g_pConfigManager->getBoundMonitorForWS(std::to_string(nextWorkspaceOnMonitorID));
return B && B != POLDMON;
}())
nextWorkspaceOnMonitorID++;
Debug::log(LOG, "moveWorkspaceToMonitor: Plugging gap with new %d", nextWorkspaceOnMonitorID);
}
Debug::log(LOG, "moveWorkspaceToMonitor: Plugging gap with existing %d", nextWorkspaceOnMonitorID);
g_pKeybindManager->focusMonitor(std::to_string(POLDMON->ID));
g_pKeybindManager->changeworkspace(std::to_string(nextWorkspaceOnMonitorID));
// move the workspace
pWorkspace->m_iMonitorID = pMonitor->ID;
pWorkspace->moveToMonitor(pMonitor->ID);
for (auto& w : m_vWindows) {
if (w->m_iWorkspaceID == pWorkspace->m_iID) {
if (w->m_bPinned) {
w->m_iWorkspaceID = nextWorkspaceOnMonitorID;
continue;
}
w->m_iMonitorID = pMonitor->ID;
// additionally, move floating and fs windows manually
@@ -1924,7 +1923,7 @@ void CCompositor::moveWorkspaceToMonitor(CWorkspace* pWorkspace, CMonitor* pMoni
}
}
if (SWITCHINGISACTIVE) { // if it was active, preserve its' status. If it wasn't, don't.
if (SWITCHINGISACTIVE && POLDMON == g_pCompositor->m_pLastMonitor) { // if it was active, preserve its' status. If it wasn't, don't.
Debug::log(LOG, "moveWorkspaceToMonitor: SWITCHINGISACTIVE, active %d -> %d", pMonitor->activeWorkspace, pWorkspace->m_iID);
if (const auto PWORKSPACE = getWorkspaceByID(pMonitor->activeWorkspace); PWORKSPACE)
@@ -1935,13 +1934,15 @@ void CCompositor::moveWorkspaceToMonitor(CWorkspace* pWorkspace, CMonitor* pMoni
pWorkspace->startAnim(true, true, true);
wlr_cursor_warp(m_sWLRCursor, m_sSeat.mouse->mouse, pMonitor->vecPosition.x + pMonitor->vecTransformedSize.x / 2,
pMonitor->vecPosition.y + pMonitor->vecTransformedSize.y / 2);
wlr_cursor_warp(m_sWLRCursor, nullptr, pMonitor->vecPosition.x + pMonitor->vecTransformedSize.x / 2, pMonitor->vecPosition.y + pMonitor->vecTransformedSize.y / 2);
}
// finalize
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(POLDMON->ID);
updateFullscreenFadeOnWorkspace(pWorkspace);
updateFullscreenFadeOnWorkspace(getWorkspaceByID(POLDMON->activeWorkspace));
g_pInputManager->refocus();
// event
@@ -1967,6 +1968,31 @@ bool CCompositor::workspaceIDOutOfBounds(const int& id) {
return std::clamp(id, lowestID, highestID) != id;
}
void CCompositor::updateFullscreenFadeOnWorkspace(CWorkspace* pWorkspace) {
const auto FULLSCREEN = pWorkspace->m_bHasFullscreenWindow;
for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_iWorkspaceID == pWorkspace->m_iID) {
if (w->m_bFadingOut || w->m_bPinned)
continue;
if (!FULLSCREEN)
w->m_fAlpha = 1.f;
else if (!w->m_bIsFullscreen)
w->m_fAlpha = !w->m_bCreatedOverFullscreen ? 0.f : 1.f;
}
}
const auto PMONITOR = getMonitorFromID(pWorkspace->m_iMonitorID);
for (auto& ls : PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) {
if (!ls->fadingOut)
ls->alpha = FULLSCREEN && pWorkspace->m_efFullscreenMode == FULLSCREEN_FULL ? 0.f : 1.f;
}
}
void CCompositor::setWindowFullscreen(CWindow* pWindow, bool on, eFullscreenMode mode) {
if (!windowValidMapped(pWindow))
return;
@@ -1993,18 +2019,11 @@ void CCompositor::setWindowFullscreen(CWindow* pWindow, bool on, eFullscreenMode
g_pCompositor->updateWindowAnimatedDecorationValues(pWindow);
// make all windows on the same workspace under the fullscreen window
for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_iWorkspaceID == pWindow->m_iWorkspaceID) {
for (auto& w : m_vWindows) {
if (w->m_iWorkspaceID == PWORKSPACE->m_iID && !w->m_bIsFullscreen && !w->m_bFadingOut && !w->m_bPinned)
w->m_bCreatedOverFullscreen = false;
if (w.get() != pWindow && !w->m_bFadingOut && !w->m_bPinned)
w->m_fAlpha = pWindow->m_bIsFullscreen ? 0.f : 1.f;
}
}
for (auto& ls : PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) {
if (!ls->fadingOut)
ls->alpha = pWindow->m_bIsFullscreen && mode == FULLSCREEN_FULL ? 0.f : 1.f;
}
updateFullscreenFadeOnWorkspace(PWORKSPACE);
g_pXWaylandManager->setWindowSize(pWindow, pWindow->m_vRealSize.goalv(), true);
@@ -2089,7 +2108,7 @@ CWindow* CCompositor::getWindowByRegex(const std::string& regexp) {
break;
}
case MODE_ADDRESS: {
std::string addr = getFormat("0x%x", w.get());
std::string addr = getFormat("0x%lx", w.get());
if (matchCheck != addr)
continue;
break;
@@ -2149,11 +2168,28 @@ void CCompositor::closeWindow(CWindow* pWindow) {
}
SLayerSurface* CCompositor::getLayerSurfaceFromSurface(wlr_surface* pSurface) {
std::pair<wlr_surface*, bool> result = {pSurface, false};
for (auto& m : m_vMonitors) {
for (auto& lsl : m->m_aLayerSurfaceLayers) {
for (auto& ls : lsl) {
if (ls->layerSurface && ls->layerSurface->surface == pSurface)
return ls.get();
static auto iter = [](wlr_surface* surf, int x, int y, void* data) -> void {
if (surf == ((std::pair<wlr_surface*, bool>*)data)->first) {
*(bool*)data = true;
return;
}
};
if (!ls->layerSurface || !ls->mapped)
continue;
wlr_surface_for_each_surface(ls->layerSurface->surface, iter, &result);
if (result.second)
return ls.get();
}
}
}
@@ -2286,3 +2322,56 @@ int CCompositor::getNewSpecialID() {
return highest + 1;
}
void CCompositor::performUserChecks() {
static constexpr auto BAD_PORTALS = {"kde", "gnome"};
static auto* const PSUPPRESSPORTAL = &g_pConfigManager->getConfigValuePtr("misc:suppress_portal_warnings")->intValue;
if (!*PSUPPRESSPORTAL) {
if (std::ranges::any_of(BAD_PORTALS, [&](const std::string& portal) { return std::filesystem::exists("/usr/share/xdg-desktop-portal/portals/" + portal + ".portal"); })) {
// bad portal detected
g_pHyprNotificationOverlay->addNotification("You have one or more incompatible xdg-desktop-portal impls installed. Please remove incompatible ones to avoid issues.",
CColor(0), 15000, ICON_ERROR);
}
if (std::filesystem::exists("/usr/share/xdg-desktop-portal/portals/hyprland.portal") && std::filesystem::exists("/usr/share/xdg-desktop-portal/portals/wlr.portal")) {
g_pHyprNotificationOverlay->addNotification("You have xdg-desktop-portal-hyprland and -wlr installed simultaneously. Please uninstall one to avoid issues.", CColor(0),
15000, ICON_ERROR);
}
}
}
void CCompositor::moveWindowToWorkspaceSafe(CWindow* pWindow, CWorkspace* pWorkspace) {
if (!pWindow || !pWorkspace)
return;
const bool FULLSCREEN = pWindow->m_bIsFullscreen;
const auto FULLSCREENMODE = getWorkspaceByID(pWindow->m_iWorkspaceID)->m_efFullscreenMode;
if (FULLSCREEN)
setWindowFullscreen(pWindow, false, FULLSCREEN_FULL);
pWindow->moveToWorkspace(pWorkspace->m_iID);
pWindow->updateToplevel();
if (!pWindow->m_bIsFloating) {
g_pLayoutManager->getCurrentLayout()->onWindowRemovedTiling(pWindow);
pWindow->m_iWorkspaceID = pWorkspace->m_iID;
pWindow->m_iMonitorID = pWorkspace->m_iMonitorID;
g_pLayoutManager->getCurrentLayout()->onWindowCreatedTiling(pWindow);
} else {
const auto PWINDOWMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
const auto POSTOMON = pWindow->m_vRealPosition.goalv() - PWINDOWMONITOR->vecPosition;
const auto PWORKSPACEMONITOR = g_pCompositor->getMonitorFromID(pWorkspace->m_iMonitorID);
pWindow->m_iWorkspaceID = pWorkspace->m_iID;
pWindow->m_iMonitorID = pWorkspace->m_iMonitorID;
pWindow->m_vRealPosition = POSTOMON + PWORKSPACEMONITOR->vecPosition;
}
if (FULLSCREEN)
setWindowFullscreen(pWindow, true, FULLSCREENMODE);
}

View File

@@ -116,7 +116,6 @@ class CCompositor {
bool m_bDPMSStateON = true;
bool m_bUnsafeState = false; // unsafe state is when there is no monitors.
bool m_bIsShuttingDown = false;
std::deque<uint64_t> m_dProcessPIDsOnShutdown; // stores PIDs of apps to kill later when shutting down
// ------------------------------------------------- //
@@ -172,6 +171,7 @@ class CCompositor {
CMonitor* getMonitorFromString(const std::string&);
bool workspaceIDOutOfBounds(const int&);
void setWindowFullscreen(CWindow*, bool, eFullscreenMode);
void updateFullscreenFadeOnWorkspace(CWorkspace*);
CWindow* getX11Parent(CWindow*);
void scheduleFrameForMonitor(CMonitor*);
void addToFadingOutSafe(SLayerSurface*);
@@ -189,6 +189,8 @@ class CCompositor {
void setActiveMonitor(CMonitor*);
bool isWorkspaceSpecial(const int&);
int getNewSpecialID();
void performUserChecks();
void moveWindowToWorkspaceSafe(CWindow* pWindow, CWorkspace* pWorkspace);
std::string explicitConfigPath;
@@ -214,4 +216,5 @@ inline std::map<std::string, xcb_atom_t> HYPRATOMS = {HYPRATOM("_NET_WM_WINDOW_T
HYPRATOM("_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"),
HYPRATOM("_NET_WM_WINDOW_TYPE_POPUP_MENU"),
HYPRATOM("_NET_WM_WINDOW_TYPE_TOOLTIP"),
HYPRATOM("_NET_WM_WINDOW_TYPE_NOTIFICATION")};
HYPRATOM("_NET_WM_WINDOW_TYPE_NOTIFICATION"),
HYPRATOM("_KDE_NET_WM_WINDOW_TYPE_OVERRIDE")};

23
src/SharedDefs.hpp Normal file
View File

@@ -0,0 +1,23 @@
#pragma once
enum eIcons
{
ICON_WARNING = 0,
ICON_INFO,
ICON_HINT,
ICON_ERROR,
ICON_CONFUSED,
ICON_OK,
ICON_NONE
};
enum eRenderStage
{
RENDER_PRE = 0, /* Before binding the gl context */
RENDER_BEGIN, /* Just when the rendering begins, nothing has been rendered yet. Damage, current render data in opengl valid. */
RENDER_PRE_WINDOWS, /* Pre windows, post bottom and overlay layers */
RENDER_POST_WINDOWS, /* Post windows, pre top/overlay layers, etc */
RENDER_LAST_MOMENT, /* Last moment to render with the gl context */
RENDER_POST, /* After rendering is finished, gl context not available anymore */
RENDER_POST_MIRROR, /* After rendering a mirror */
};

View File

@@ -265,23 +265,43 @@ void CWindow::updateSurfaceOutputs() {
const auto PNEWMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
if (PLASTMONITOR && PLASTMONITOR->m_bEnabled)
wlr_surface_for_each_surface(g_pXWaylandManager->getWindowSurface(this), sendLeaveIter, PLASTMONITOR->output);
wlr_surface_for_each_surface(m_pWLSurface.wlr(), sendLeaveIter, PLASTMONITOR->output);
wlr_surface_for_each_surface(g_pXWaylandManager->getWindowSurface(this), sendEnterIter, PNEWMONITOR->output);
wlr_surface_for_each_surface(m_pWLSurface.wlr(), sendEnterIter, PNEWMONITOR->output);
}
void CWindow::moveToWorkspace(int workspaceID) {
if (m_iWorkspaceID != workspaceID) {
m_iWorkspaceID = workspaceID;
if (m_iWorkspaceID == workspaceID)
return;
if (const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(m_iWorkspaceID); PWORKSPACE) {
g_pEventManager->postEvent(SHyprIPCEvent{"movewindow", getFormat("%x,%s", this, PWORKSPACE->m_szName.c_str())});
EMIT_HOOK_EVENT("moveWindow", (std::vector<void*>{this, PWORKSPACE}));
}
m_iWorkspaceID = workspaceID;
if (const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID); PMONITOR)
g_pProtocolManager->m_pFractionalScaleProtocolManager->setPreferredScaleForSurface(g_pXWaylandManager->getWindowSurface(this), PMONITOR->scale);
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(m_iWorkspaceID);
if (PWORKSPACE) {
g_pEventManager->postEvent(SHyprIPCEvent{"movewindow", getFormat("%lx,%s", this, PWORKSPACE->m_szName.c_str())});
EMIT_HOOK_EVENT("moveWindow", (std::vector<void*>{this, PWORKSPACE}));
}
if (m_pSwallowed) {
m_pSwallowed->moveToWorkspace(workspaceID);
m_pSwallowed->m_iMonitorID = m_iMonitorID;
}
if (PMONITOR)
g_pProtocolManager->m_pFractionalScaleProtocolManager->setPreferredScaleForSurface(m_pWLSurface.wlr(), PMONITOR->scale);
if (!m_bIsMapped)
return;
wlr_surface_for_each_surface(
m_pWLSurface.wlr(),
[](wlr_surface* surf, int x, int y, void* data) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(((CWindow*)data)->m_iMonitorID);
g_pProtocolManager->m_pFractionalScaleProtocolManager->setPreferredScaleForSurface(surf, PMONITOR ? PMONITOR->scale : 1.f);
},
this);
}
CWindow* CWindow::X11TransientFor() {
@@ -332,10 +352,16 @@ void CWindow::onUnmap() {
m_vRealSize.setCallbackOnBegin(nullptr);
std::erase_if(g_pCompositor->m_vWindowFocusHistory, [&](const auto& other) { return other == this; });
m_pWLSurface.unassign();
hyprListener_unmapWindow.removeCallback();
}
void CWindow::onMap() {
m_pWLSurface.assign(g_pXWaylandManager->getWindowSurface(this));
// JIC, reset the callbacks. If any are set, we'll make sure they are cleared so we don't accidentally unset them. (In case a window got remapped)
m_vRealPosition.resetAllCallbacks();
m_vRealSize.resetAllCallbacks();
@@ -361,6 +387,8 @@ void CWindow::onMap() {
m_fBorderAngleAnimationProgress = 1.f;
g_pCompositor->m_vWindowFocusHistory.push_back(this);
hyprListener_unmapWindow.initCallback(m_bIsX11 ? &m_uSurface.xwayland->events.unmap : &m_uSurface.xdg->events.unmap, &Events::listener_unmapWindow, this, "CWindow");
}
void CWindow::onBorderAngleAnimEnd(void* ptr) {
@@ -398,8 +426,10 @@ void CWindow::applyDynamicRule(const SWindowRule& r) {
m_sAdditionalConfigData.forceNoBorder = true;
} else if (r.szRule == "noshadow") {
m_sAdditionalConfigData.forceNoShadow = true;
} else if (r.szRule == "forcergbx") {
m_sAdditionalConfigData.forceRGBX = true;
} else if (r.szRule == "opaque") {
if (!m_sAdditionalConfigData.forceOpaqueOverriden)
if (!m_sAdditionalConfigData.forceOpaqueOverridden)
m_sAdditionalConfigData.forceOpaque = true;
} else if (r.szRule.find("rounding") == 0) {
try {
@@ -456,12 +486,13 @@ void CWindow::updateDynamicRules() {
m_sAdditionalConfigData.forceNoBlur = false;
m_sAdditionalConfigData.forceNoBorder = false;
m_sAdditionalConfigData.forceNoShadow = false;
if (!m_sAdditionalConfigData.forceOpaqueOverriden)
if (!m_sAdditionalConfigData.forceOpaqueOverridden)
m_sAdditionalConfigData.forceOpaque = false;
m_sAdditionalConfigData.forceNoAnims = false;
m_sAdditionalConfigData.animationStyle = std::string("");
m_sAdditionalConfigData.rounding = -1;
m_sAdditionalConfigData.dimAround = false;
m_sAdditionalConfigData.forceRGBX = false;
const auto WINDOWRULES = g_pConfigManager->getMatchingRules(this);
for (auto& r : WINDOWRULES) {
@@ -615,4 +646,40 @@ void CWindow::insertWindowToGroup(CWindow* pWindow) {
pWindow->m_sGroupData.pNextWindow = PHEAD;
setGroupCurrent(pWindow);
}
}
void CWindow::updateGroupOutputs() {
if (!m_sGroupData.pNextWindow)
return;
CWindow* curr = m_sGroupData.pNextWindow;
while (curr != this) {
curr->m_iMonitorID = m_iMonitorID;
curr->moveToWorkspace(m_iWorkspaceID);
curr->m_vRealPosition = m_vRealPosition.goalv();
curr->m_vRealSize = m_vRealSize.goalv();
curr = curr->m_sGroupData.pNextWindow;
}
}
Vector2D CWindow::middle() {
return m_vRealPosition.goalv() + m_vRealSize.goalv() / 2.f;
}
bool CWindow::opaque() {
if (m_bIsX11)
return !m_uSurface.xwayland->has_alpha;
if (m_uSurface.xdg->surface->opaque)
return true;
const auto EXTENTS = pixman_region32_extents(&m_uSurface.xdg->surface->opaque_region);
if (EXTENTS->x2 - EXTENTS->x1 >= m_uSurface.xdg->surface->current.buffer_width
&& EXTENTS->y2 - EXTENTS->y1 >= m_uSurface.xdg->surface->current.buffer_height)
return true;
return false;
}

View File

@@ -8,9 +8,9 @@
#include <deque>
#include "config/ConfigDataValues.hpp"
#include "helpers/Vector2D.hpp"
#include "helpers/WLSurface.hpp"
enum eIdleInhibitMode
{
enum eIdleInhibitMode {
IDLEINHIBIT_NONE = 0,
IDLEINHIBIT_ALWAYS,
IDLEINHIBIT_FULLSCREEN,
@@ -100,24 +100,26 @@ struct SWindowSpecialRenderData {
CWindowOverridableVar<int64_t> inactiveBorderColor = -1; // -1 means unset
// set by the layout
bool rounding = true;
bool border = true;
bool decorate = true;
int borderSize = -1;
bool rounding = true;
bool border = true;
bool decorate = true;
};
struct SWindowAdditionalConfigData {
std::string animationStyle = std::string("");
CWindowOverridableVar<int> rounding = -1; // -1 means no
CWindowOverridableVar<bool> forceNoBlur = false;
CWindowOverridableVar<bool> forceOpaque = false;
CWindowOverridableVar<bool> forceOpaqueOverriden = false; // if true, a rule will not change the forceOpaque state. This is for the force opaque dispatcher.
CWindowOverridableVar<bool> forceAllowsInput = false;
CWindowOverridableVar<bool> forceNoAnims = false;
CWindowOverridableVar<bool> forceNoBorder = false;
CWindowOverridableVar<bool> forceNoShadow = false;
CWindowOverridableVar<bool> windowDanceCompat = false;
CWindowOverridableVar<bool> noMaxSize = false;
CWindowOverridableVar<bool> dimAround = false;
std::string animationStyle = std::string("");
CWindowOverridableVar<int> rounding = -1; // -1 means no
CWindowOverridableVar<bool> forceNoBlur = false;
CWindowOverridableVar<bool> forceOpaque = false;
CWindowOverridableVar<bool> forceOpaqueOverridden = false; // if true, a rule will not change the forceOpaque state. This is for the force opaque dispatcher.
CWindowOverridableVar<bool> forceAllowsInput = false;
CWindowOverridableVar<bool> forceNoAnims = false;
CWindowOverridableVar<bool> forceNoBorder = false;
CWindowOverridableVar<bool> forceNoShadow = false;
CWindowOverridableVar<bool> windowDanceCompat = false;
CWindowOverridableVar<bool> noMaxSize = false;
CWindowOverridableVar<bool> dimAround = false;
CWindowOverridableVar<bool> forceRGBX = false;
};
struct SWindowRule {
@@ -158,6 +160,9 @@ class CWindow {
DYNLISTENER(setOverrideRedirect);
// DYNLISTENER(newSubsurfaceWindow);
CWLSurface m_pWLSurface;
std::list<CWLSurface> m_lPopupSurfaces;
union {
wlr_xdg_surface* xdg;
wlr_xwayland_surface* xwayland;
@@ -190,6 +195,8 @@ class CWindow {
bool m_bWasMaximized = false;
uint64_t m_iMonitorID = -1;
std::string m_szTitle = "";
std::string m_szInitialTitle = "";
std::string m_szInitialClass = "";
int m_iWorkspaceID = -1;
bool m_bIsMapped = false;
@@ -307,6 +314,8 @@ class CWindow {
void applyDynamicRule(const SWindowRule& r);
void updateDynamicRules();
SWindowDecorationExtents getFullWindowReservedArea();
Vector2D middle();
bool opaque();
void onBorderAngleAnimEnd(void* ptr);
bool isInCurvedCorner(double x, double y);
@@ -317,6 +326,7 @@ class CWindow {
CWindow* getGroupCurrent();
void setGroupCurrent(CWindow* pWindow);
void insertWindowToGroup(CWindow* pWindow);
void updateGroupOutputs();
private:
// For hidden windows and stuff

View File

@@ -9,6 +9,7 @@
#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>
extern "C" char** environ;
@@ -40,11 +41,13 @@ CConfigManager::CConfigManager() {
void CConfigManager::populateEnvironment() {
environmentVariables.clear();
for (char** env = environ; *env; ++env) {
const std::string ENVVAR = *env;
const auto VARIABLE = ENVVAR.substr(0, ENVVAR.find_first_of('='));
const auto VALUE = ENVVAR.substr(ENVVAR.find_first_of('=') + 1);
environmentVariables[VARIABLE] = VALUE;
const std::string ENVVAR = *env;
const auto VARIABLE = ENVVAR.substr(0, ENVVAR.find_first_of('='));
const auto VALUE = ENVVAR.substr(ENVVAR.find_first_of('=') + 1);
environmentVariables.emplace_back(std::make_pair<>(VARIABLE, VALUE));
}
std::sort(environmentVariables.begin(), environmentVariables.end(), [&](const auto& a, const auto& b) { return a.first.length() > b.first.length(); });
}
void CConfigManager::setDefaultVars() {
@@ -61,6 +64,7 @@ void CConfigManager::setDefaultVars() {
((CGradientValueData*)configValues["general:col.group_border_active"].data.get())->reset(0x66ffff00);
configValues["general:cursor_inactive_timeout"].intValue = 0;
configValues["general:no_cursor_warps"].intValue = 0;
configValues["general:no_focus_fallback"].intValue = 0;
configValues["general:resize_on_border"].intValue = 0;
configValues["general:extend_border_grab_area"].intValue = 15;
configValues["general:hover_icon_on_border"].intValue = 1;
@@ -74,23 +78,31 @@ void CConfigManager::setDefaultVars() {
configValues["misc:key_press_enables_dpms"].intValue = 0;
configValues["misc:always_follow_on_dnd"].intValue = 1;
configValues["misc:layers_hog_keyboard_focus"].intValue = 1;
configValues["misc:animate_manual_resizes"].intValue = 1;
configValues["misc:animate_mouse_windowdragging"].intValue = 1;
configValues["misc:animate_manual_resizes"].intValue = 0;
configValues["misc:animate_mouse_windowdragging"].intValue = 0;
configValues["misc:disable_autoreload"].intValue = 0;
configValues["misc:enable_swallow"].intValue = 0;
configValues["misc:swallow_regex"].strValue = STRVAL_EMPTY;
configValues["misc:swallow_exception_regex"].strValue = STRVAL_EMPTY;
configValues["misc:focus_on_activate"].intValue = 0;
configValues["misc:no_direct_scanout"].intValue = 0;
configValues["misc:no_direct_scanout"].intValue = 1;
configValues["misc:hide_cursor_on_touch"].intValue = 1;
configValues["misc:mouse_move_focuses_monitor"].intValue = 1;
configValues["misc:suppress_portal_warnings"].intValue = 0;
configValues["misc:render_ahead_of_time"].intValue = 0;
configValues["misc:render_ahead_safezone"].intValue = 1;
configValues["misc:cursor_zoom_factor"].floatValue = 1.f;
configValues["misc:cursor_zoom_rigid"].intValue = 0;
configValues["debug:int"].intValue = 0;
configValues["debug:log_damage"].intValue = 0;
configValues["debug:overlay"].intValue = 0;
configValues["debug:damage_blink"].intValue = 0;
configValues["debug:disable_logs"].intValue = 0;
configValues["debug:disable_time"].intValue = 1;
configValues["debug:damage_tracking"].intValue = DAMAGE_TRACKING_FULL;
configValues["debug:int"].intValue = 0;
configValues["debug:log_damage"].intValue = 0;
configValues["debug:overlay"].intValue = 0;
configValues["debug:damage_blink"].intValue = 0;
configValues["debug:disable_logs"].intValue = 0;
configValues["debug:disable_time"].intValue = 1;
configValues["debug:enable_stdout_logs"].intValue = 0;
configValues["debug:damage_tracking"].intValue = DAMAGE_TRACKING_FULL;
configValues["debug:manual_crash"].intValue = 0;
configValues["decoration:rounding"].intValue = 0;
configValues["decoration:blur"].intValue = 1;
@@ -135,10 +147,12 @@ void CConfigManager::setDefaultVars() {
configValues["master:no_gaps_when_only"].intValue = 0;
configValues["master:orientation"].strValue = "left";
configValues["master:inherit_fullscreen"].intValue = 1;
configValues["master:allow_small_split"].intValue = 0;
configValues["animations:enabled"].intValue = 1;
configValues["input:follow_mouse"].intValue = 1;
configValues["input:mouse_refocus"].intValue = 1;
configValues["input:sensitivity"].floatValue = 0.f;
configValues["input:accel_profile"].strValue = STRVAL_EMPTY;
configValues["input:kb_file"].strValue = STRVAL_EMPTY;
@@ -293,7 +307,9 @@ void CConfigManager::configSetValueSafe(const std::string& COMMAND, const std::s
if (COMMAND[0] == '$') {
// register a dynamic var
Debug::log(LOG, "Registered dynamic var \"%s\" -> %s", COMMAND.c_str(), VALUE.c_str());
configDynamicVars[COMMAND.substr(1)] = VALUE;
configDynamicVars.emplace_back(std::make_pair<>(COMMAND.substr(1), VALUE));
std::sort(configDynamicVars.begin(), configDynamicVars.end(), [&](const auto& a, const auto& b) { return a.first.length() > b.first.length(); });
} else {
parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">: No such field.";
}
@@ -554,6 +570,21 @@ void CConfigManager::handleMonitor(const std::string& command, const std::string
} else if (ARGS[argno] == "bitdepth") {
newrule.enable10bit = ARGS[argno + 1] == "10";
argno++;
} else if (ARGS[argno] == "transform") {
newrule.transform = (wl_output_transform)std::stoi(ARGS[argno + 1]);
argno++;
} else if (ARGS[argno] == "workspace") {
std::string name = "";
int wsId = getWorkspaceIDFromString(ARGS[argno + 1], name);
SWorkspaceRule wsRule;
wsRule.monitor = newrule.name;
wsRule.workspaceString = ARGS[argno + 1];
wsRule.workspaceName = name;
wsRule.workspaceId = wsId;
m_mWorkspaceRules[wsId] = wsRule;
argno++;
} else {
Debug::log(ERR, "Config error: invalid monitor syntax");
parseError = "invalid syntax at \"" + ARGS[argno] + "\"";
@@ -597,8 +628,8 @@ void CConfigManager::handleBezier(const std::string& command, const std::string&
void CConfigManager::setAnimForChildren(SAnimationPropertyConfig* const ANIM) {
for (auto& [name, anim] : animationConfig) {
if (anim.pParentAnimation == ANIM && !anim.overriden) {
// if a child isnt overriden, set the values of the parent
if (anim.pParentAnimation == ANIM && !anim.overridden) {
// if a child isnt overridden, set the values of the parent
anim.pValues = ANIM->pValues;
setAnimForChildren(&anim);
@@ -621,8 +652,8 @@ void CConfigManager::handleAnimation(const std::string& command, const std::stri
return;
}
PANIM->second.overriden = true;
PANIM->second.pValues = &PANIM->second;
PANIM->second.overridden = true;
PANIM->second.pValues = &PANIM->second;
// on/off
PANIM->second.internalEnabled = ARGS[1] == "1";
@@ -744,6 +775,8 @@ void CConfigManager::handleBind(const std::string& command, const std::string& v
if (KEY != "") {
if (isNumber(KEY) && std::stoi(KEY) > 9)
g_pKeybindManager->addKeybind(SKeybind{"", std::stoi(KEY), MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, repeat, mouse});
else if (KEY.find("code:") == 0 && isNumber(KEY.substr(5)))
g_pKeybindManager->addKeybind(SKeybind{"", std::stoi(KEY.substr(5)), MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, repeat, mouse});
else
g_pKeybindManager->addKeybind(SKeybind{KEY, -1, MOD, HANDLER, COMMAND, locked, m_szCurrentSubmap, release, repeat, mouse});
}
@@ -763,12 +796,13 @@ bool windowRuleValid(const std::string& RULE) {
return !(RULE != "float" && RULE != "tile" && RULE.find("opacity") != 0 && RULE.find("move") != 0 && RULE.find("size") != 0 && RULE.find("minsize") != 0 &&
RULE.find("maxsize") != 0 && RULE.find("pseudo") != 0 && RULE.find("monitor") != 0 && RULE.find("idleinhibit") != 0 && RULE != "nofocus" && RULE != "noblur" &&
RULE != "noshadow" && RULE != "noborder" && RULE != "center" && RULE != "opaque" && RULE != "forceinput" && RULE != "fullscreen" && RULE != "nofullscreenrequest" &&
RULE != "nomaxsize" && RULE != "pin" && RULE != "noanim" && RULE != "dimaround" && RULE != "windowdance" && RULE != "maximize" && RULE.find("animation") != 0 &&
RULE.find("rounding") != 0 && RULE.find("workspace") != 0 && RULE.find("bordercolor") != 0);
RULE != "fakefullscreen" && RULE != "nomaxsize" && RULE != "pin" && RULE != "noanim" && RULE != "dimaround" && RULE != "windowdance" && RULE != "maximize" &&
RULE.find("animation") != 0 && RULE.find("rounding") != 0 && RULE.find("workspace") != 0 && RULE.find("bordercolor") != 0 && RULE != "forcergbx" &&
RULE != "noinitialfocus");
}
bool layerRuleValid(const std::string& RULE) {
return !(RULE != "noanim");
return !(RULE != "noanim" && RULE != "blur" && RULE != "ignorezero");
}
void CConfigManager::handleWindowRule(const std::string& command, const std::string& value) {
@@ -800,9 +834,8 @@ void CConfigManager::handleLayerRule(const std::string& command, const std::stri
const auto VALUE = removeBeginEndSpacesTabs(value.substr(value.find_first_of(',') + 1));
// check rule and value
if (RULE == "" || VALUE == "") {
if (RULE == "" || VALUE == "")
return;
}
if (RULE == "unset") {
std::erase_if(m_dLayerRules, [&](const SLayerRule& other) { return other.targetNamespace == VALUE; });
@@ -816,6 +849,11 @@ void CConfigManager::handleLayerRule(const std::string& command, const std::stri
}
m_dLayerRules.push_back({VALUE, RULE});
for (auto& m : g_pCompositor->m_vMonitors)
for (auto& lsl : m->m_aLayerSurfaceLayers)
for (auto& ls : lsl)
ls->applyRules();
}
void CConfigManager::handleWindowRuleV2(const std::string& command, const std::string& value) {
@@ -950,7 +988,7 @@ void CConfigManager::updateBlurredLS(const std::string& name, const bool forceBl
for (auto& lsl : m->m_aLayerSurfaceLayers) {
for (auto& ls : lsl) {
if (BYADDRESS) {
if (getFormat("0x%x", ls.get()) == matchName)
if (getFormat("0x%lx", ls.get()) == matchName)
ls->forceBlur = forceBlur;
} else if (ls->szNamespace == matchName)
ls->forceBlur = forceBlur;
@@ -971,15 +1009,64 @@ void CConfigManager::handleBlurLS(const std::string& command, const std::string&
updateBlurredLS(value, true);
}
void CConfigManager::handleDefaultWorkspace(const std::string& command, const std::string& value) {
const auto ARGS = CVarList(value);
void CConfigManager::handleWorkspaceRules(const std::string& command, const std::string& value) {
// This can either be the monitor or the workspace identifier
const auto FIRST_DELIM = value.find_first_of(',');
for (auto& mr : m_dMonitorRules) {
if (mr.name == ARGS[0]) {
mr.defaultWorkspace = ARGS[1];
break;
std::string name = "";
auto first_ident = removeBeginEndSpacesTabs(value.substr(0, FIRST_DELIM));
int id = getWorkspaceIDFromString(first_ident, name);
auto rules = value.substr(FIRST_DELIM + 1);
SWorkspaceRule wsRule;
wsRule.workspaceString = first_ident;
if (id == INT_MAX) {
// it could be the monitor. If so, second value MUST be
// the workspace.
const auto WORKSPACE_DELIM = value.find_first_of(',', FIRST_DELIM + 1);
auto wsIdent = removeBeginEndSpacesTabs(value.substr(FIRST_DELIM + 1, (WORKSPACE_DELIM - FIRST_DELIM - 1)));
id = getWorkspaceIDFromString(wsIdent, name);
if (id == INT_MAX) {
Debug::log(ERR, "Invalid workspace identifier found: %s", wsIdent.c_str());
parseError = "Invalid workspace identifier found: " + wsIdent;
return;
}
wsRule.monitor = first_ident;
wsRule.workspaceString = wsIdent;
rules = value.substr(WORKSPACE_DELIM + 1);
}
auto assignRule = [&](std::string rule) {
size_t delim = std::string::npos;
Debug::log(INFO, "found workspacerule: %s", rule.c_str());
if ((delim = rule.find("gapsin:")) != std::string::npos)
wsRule.gapsIn = std::stoi(rule.substr(delim + 7));
else if ((delim = rule.find("gapsout:")) != std::string::npos)
wsRule.gapsOut = std::stoi(rule.substr(delim + 8));
else if ((delim = rule.find("bordersize:")) != std::string::npos)
wsRule.borderSize = std::stoi(rule.substr(delim + 11));
else if ((delim = rule.find("border:")) != std::string::npos)
wsRule.border = configStringToInt(rule.substr(delim + 7));
else if ((delim = rule.find("rounding:")) != std::string::npos)
wsRule.rounding = configStringToInt(rule.substr(delim + 9));
else if ((delim = rule.find("decorate:")) != std::string::npos)
wsRule.decorate = configStringToInt(rule.substr(delim + 9));
else if ((delim = rule.find("monitor:")) != std::string::npos)
wsRule.monitor = rule.substr(delim + 8);
};
size_t pos = 0;
std::string rule;
while ((pos = rules.find(',')) != std::string::npos) {
rule = rules.substr(0, pos);
assignRule(rule);
rules.erase(0, pos + 1);
}
assignRule(rules); // match remaining rule
wsRule.workspaceId = id;
wsRule.workspaceName = name;
m_mWorkspaceRules[id] = wsRule;
}
void CConfigManager::handleSubmap(const std::string& command, const std::string& submap) {
@@ -1071,14 +1158,26 @@ void CConfigManager::handleEnv(const std::string& command, const std::string& va
if (command.back() == 'd') {
// dbus
const auto CMD = "systemctl --user import-environment " + ARGS[0] +
const auto CMD =
#ifdef USES_SYSTEMD
"systemctl --user import-environment " + ARGS[0] +
" && hash dbus-update-activation-environment 2>/dev/null && "
#endif
"dbus-update-activation-environment --systemd " +
ARGS[0];
handleRawExec("", CMD.c_str());
}
}
void CConfigManager::handlePlugin(const std::string& command, const std::string& path) {
if (std::find(m_vDeclaredPlugins.begin(), m_vDeclaredPlugins.end(), path) != m_vDeclaredPlugins.end()) {
parseError = "plugin '" + path + "' declared twice";
return;
}
m_vDeclaredPlugins.push_back(path);
}
std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std::string& VALUE, bool dynamic) {
if (dynamic) {
parseError = "";
@@ -1104,7 +1203,7 @@ std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std::
else if (COMMAND == "unbind")
handleUnbind(COMMAND, VALUE);
else if (COMMAND == "workspace")
handleDefaultWorkspace(COMMAND, VALUE);
handleWorkspaceRules(COMMAND, VALUE);
else if (COMMAND == "windowrule")
handleWindowRule(COMMAND, VALUE);
else if (COMMAND == "windowrulev2")
@@ -1125,6 +1224,8 @@ std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std::
handleBindWS(COMMAND, VALUE);
else if (COMMAND.find("env") == 0)
handleEnv(COMMAND, VALUE);
else if (COMMAND.find("plugin") == 0)
handlePlugin(COMMAND, VALUE);
else {
configSetValueSafe(currentCategory + (currentCategory == "" ? "" : ":") + COMMAND, VALUE);
needsLayoutRecalc = 2;
@@ -1145,6 +1246,18 @@ std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std::
// Update window border colors
g_pCompositor->updateAllWindowsAnimatedDecorationValues();
// manual crash
if (configValues["debug:manual_crash"].intValue && !m_bManualCrashInitiated) {
m_bManualCrashInitiated = true;
if (g_pHyprNotificationOverlay) {
g_pHyprNotificationOverlay->addNotification("Manual crash has been set up. Set debug:manual_crash back to 0 in order to crash the compositor.", CColor(0), 5000,
ICON_INFO);
}
} else if (m_bManualCrashInitiated && !configValues["debug:manual_crash"].intValue) {
// cowabunga it is
g_pHyprRenderer->initiateManualCrash();
}
return retval;
}
@@ -1264,7 +1377,9 @@ void CConfigManager::loadConfigLoadVars() {
deviceConfigs.clear();
m_dBlurLSNamespaces.clear();
boundWorkspaces.clear();
m_mWorkspaceRules.clear();
setDefaultAnimationVars(); // reset anims
m_vDeclaredPlugins.clear();
// paths
configPaths.clear();
@@ -1372,7 +1487,7 @@ void CConfigManager::loadConfigLoadVars() {
if (!isFirstLaunch && !m_bNoMonitorReload) {
// check
performMonitorReload();
ensureDPMS();
ensureMonitorStatus();
ensureVRR();
}
@@ -1382,6 +1497,19 @@ void CConfigManager::loadConfigLoadVars() {
// update layout
g_pLayoutManager->switchToLayout(configValues["general:layout"].strValue);
// manual crash
if (configValues["debug:manual_crash"].intValue && !m_bManualCrashInitiated) {
m_bManualCrashInitiated = true;
g_pHyprNotificationOverlay->addNotification("Manual crash has been set up. Set debug:manual_crash back to 0 in order to crash the compositor.", CColor(0), 5000, ICON_INFO);
} else if (m_bManualCrashInitiated && !configValues["debug:manual_crash"].intValue) {
// cowabunga it is
g_pHyprRenderer->initiateManualCrash();
}
Debug::disableStdout = !configValues["debug:enable_stdout_logs"].intValue;
if (Debug::disableStdout && isFirstLaunch)
Debug::log(LOG, "Disabling stdout logs! Check the log for further logs.");
for (auto& m : g_pCompositor->m_vMonitors) {
// mark blur dirty
g_pHyprOpenGL->markBlurDirtyForMonitor(m.get());
@@ -1392,6 +1520,9 @@ void CConfigManager::loadConfigLoadVars() {
// Reset no monitor reload
m_bNoMonitorReload = false;
// update plugins
handlePluginLoads();
}
void CConfigManager::tick() {
@@ -1415,7 +1546,7 @@ void CConfigManager::tick() {
int err = stat(cf.c_str(), &fileStat);
if (err != 0) {
Debug::log(WARN, "Error at ticking config at %s, error %i: %s", cf.c_str(), err, strerror(err));
return;
continue;
}
// check if we need to reload cfg
@@ -1548,6 +1679,17 @@ SMonitorRule CConfigManager::getMonitorRuleFor(const std::string& name, const st
return SMonitorRule{.name = "", .resolution = Vector2D(0, 0), .offset = Vector2D(-1, -1), .scale = -1}; // 0, 0 is preferred and -1, -1 is auto
}
SWorkspaceRule CConfigManager::getWorkspaceRuleFor(CWorkspace* pWorkspace) {
if (m_mWorkspaceRules.contains(pWorkspace->m_iID)) {
return m_mWorkspaceRules.at(pWorkspace->m_iID);
}
const auto IT = std::find_if(m_mWorkspaceRules.begin(), m_mWorkspaceRules.end(), [&](const auto& other) { return other.second.workspaceName == pWorkspace->m_szName; });
if (IT == m_mWorkspaceRules.end())
return SWorkspaceRule{};
return IT->second;
}
std::vector<SWindowRule> CConfigManager::getMatchingRules(CWindow* pWindow) {
if (!g_pCompositor->windowValidMapped(pWindow))
return std::vector<SWindowRule>();
@@ -1621,23 +1763,27 @@ std::vector<SWindowRule> CConfigManager::getMatchingRules(CWindow* pWindow) {
}
// applies. Read the rule and behave accordingly
Debug::log(LOG, "Window rule %s -> %s matched %x [%s]", rule.szRule.c_str(), rule.szValue.c_str(), pWindow, pWindow->m_szTitle.c_str());
Debug::log(LOG, "Window rule %s -> %s matched %lx [%s]", rule.szRule.c_str(), rule.szValue.c_str(), pWindow, pWindow->m_szTitle.c_str());
returns.push_back(rule);
}
const uint64_t PID = pWindow->getPID();
bool anyExecFound = false;
std::vector<uint64_t> PIDs = {(uint64_t)pWindow->getPID()};
while (getPPIDof(PIDs.back()) > 10)
PIDs.push_back(getPPIDof(PIDs.back()));
bool anyExecFound = false;
for (auto& er : execRequestedRules) {
if (er.iPid == PID) {
if (std::ranges::any_of(PIDs, [&](const auto& pid) { return pid == er.iPid; })) {
returns.push_back({er.szRule, "execRule"});
anyExecFound = true;
}
}
if (anyExecFound) // remove exec rules to unclog searches in the future, why have the garbage here.
execRequestedRules.erase(std::remove_if(execRequestedRules.begin(), execRequestedRules.end(), [&](const SExecRequestedRule& other) { return other.iPid == PID; }));
execRequestedRules.erase(std::remove_if(execRequestedRules.begin(), execRequestedRules.end(),
[&](const SExecRequestedRule& other) { return std::ranges::any_of(PIDs, [&](const auto& pid) { return pid == other.iPid; }); }));
return returns;
}
@@ -1645,16 +1791,27 @@ std::vector<SWindowRule> CConfigManager::getMatchingRules(CWindow* pWindow) {
std::vector<SLayerRule> CConfigManager::getMatchingRules(SLayerSurface* pLS) {
std::vector<SLayerRule> returns;
for (auto& lr : m_dLayerRules) {
std::regex NSCHECK(lr.targetNamespace);
if (!pLS->layerSurface || pLS->fadingOut)
return returns;
if (!pLS->layerSurface->_namespace || !std::regex_search(pLS->layerSurface->_namespace, NSCHECK))
continue;
for (auto& lr : m_dLayerRules) {
if (lr.targetNamespace.find("address:0x") == 0) {
if (getFormat("address:0x%lx", pLS) != lr.targetNamespace)
continue;
} else {
std::regex NSCHECK(lr.targetNamespace);
if (!pLS->layerSurface->_namespace || !std::regex_search(pLS->layerSurface->_namespace, NSCHECK))
continue;
}
// hit
returns.push_back(lr);
}
if (pLS->layerSurface->_namespace && shouldBlurLS(pLS->layerSurface->_namespace))
returns.push_back({pLS->layerSurface->_namespace, "blur"});
return returns;
}
@@ -1663,10 +1820,13 @@ void CConfigManager::dispatchExecOnce() {
return;
// update dbus env
handleRawExec(
"",
"systemctl --user import-environment DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP && hash dbus-update-activation-environment 2>/dev/null && "
"dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE");
if (g_pCompositor->m_sWLRSession)
handleRawExec(
"",
#ifdef USES_SYSTEMD
"systemctl --user import-environment DISPLAY WAYLAND_DISPLAY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP && hash dbus-update-activation-environment 2>/dev/null && "
#endif
"dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP HYPRLAND_INSTANCE_SIGNATURE");
firstExecDispatched = true;
@@ -1686,6 +1846,9 @@ void CConfigManager::dispatchExecOnce() {
for (auto& ws : g_pCompositor->m_vWorkspaces) {
wlr_ext_workspace_handle_v1_set_name(ws->m_pWlrHandle, ws->m_szName.c_str());
}
// check for user's possible errors with their setup and notify them if needed
g_pCompositor->performUserChecks();
}
void CConfigManager::performMonitorReload() {
@@ -1693,6 +1856,9 @@ void CConfigManager::performMonitorReload() {
bool overAgain = false;
for (auto& m : g_pCompositor->m_vRealMonitors) {
if (!m->output)
continue;
auto rule = getMonitorRuleFor(m->szName, m->output->description ? m->output->description : "");
if (!g_pHyprRenderer->applyMonitorRule(m.get(), &rule)) {
@@ -1756,8 +1922,11 @@ bool CConfigManager::shouldBlurLS(const std::string& ns) {
return false;
}
void CConfigManager::ensureDPMS() {
void CConfigManager::ensureMonitorStatus() {
for (auto& rm : g_pCompositor->m_vRealMonitors) {
if (!rm->output)
continue;
auto rule = getMonitorRuleFor(rm->szName, rm->output->description ? rm->output->description : "");
if (rule.disabled == rm->m_bEnabled) {
@@ -1771,6 +1940,9 @@ void CConfigManager::ensureVRR(CMonitor* pMonitor) {
static auto* const PVRR = &getConfigValuePtr("misc:vrr")->intValue;
static auto ensureVRRForDisplay = [&](CMonitor* m) -> void {
if (!m->output)
return;
if (*PVRR == 0) {
if (m->vrrActive) {
wlr_output_enable_adaptive_sync(m->output, 0);
@@ -1875,6 +2047,31 @@ void CConfigManager::addExecRule(const SExecRequestedRule& rule) {
execRequestedRules.push_back(rule);
}
void CConfigManager::handlePluginLoads() {
if (g_pPluginSystem == nullptr)
return;
bool pluginsChanged = false;
auto failedPlugins = g_pPluginSystem->updateConfigPlugins(m_vDeclaredPlugins, pluginsChanged);
if (!failedPlugins.empty()) {
std::stringstream error;
error << "Failed to load the following plugins:";
for (auto path : failedPlugins) {
error << "\n" << path;
}
g_pHyprError->queueCreate(error.str(), CColor(1.0, 50.0 / 255.0, 50.0 / 255.0, 1.0));
}
if (pluginsChanged) {
g_pHyprError->destroy();
m_bForceReload = true;
tick();
}
}
ICustomConfigValueData::~ICustomConfigValueData() {
; // empty
}
@@ -1898,3 +2095,10 @@ void CConfigManager::addPluginConfigVar(HANDLE handle, const std::string& name,
void CConfigManager::removePluginConfig(HANDLE handle) {
std::erase_if(pluginConfigs, [&](const auto& other) { return other.first == handle; });
}
std::string CConfigManager::getDefaultWorkspaceFor(const std::string& name) {
const auto IT = std::find_if(m_mWorkspaceRules.begin(), m_mWorkspaceRules.end(), [&](const auto& other) { return other.second.monitor == name; });
if (IT == m_mWorkspaceRules.end())
return "";
return IT->second.workspaceString;
}

View File

@@ -10,6 +10,7 @@
#include <deque>
#include <algorithm>
#include <regex>
#include <optional>
#include "../Window.hpp"
#include "../helpers/WLClasses.hpp"
@@ -34,16 +35,28 @@ struct SConfigValue {
};
struct SMonitorRule {
std::string name = "";
Vector2D resolution = Vector2D(1280, 720);
Vector2D offset = Vector2D(0, 0);
float scale = 1;
float refreshRate = 60;
std::string defaultWorkspace = "";
bool disabled = false;
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
std::string mirrorOf = "";
bool enable10bit = false;
std::string name = "";
Vector2D resolution = Vector2D(1280, 720);
Vector2D offset = Vector2D(0, 0);
float scale = 1;
float refreshRate = 60;
bool disabled = false;
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
std::string mirrorOf = "";
bool enable10bit = false;
};
struct SWorkspaceRule {
std::string monitor = "";
std::string workspaceString = "";
std::string workspaceName = "";
int workspaceId = -1;
std::optional<int64_t> gapsIn;
std::optional<int64_t> gapsOut;
std::optional<int64_t> borderSize;
std::optional<int> border;
std::optional<int> rounding;
std::optional<int> decorate;
};
struct SMonitorAdditionalReservedArea {
@@ -54,7 +67,7 @@ struct SMonitorAdditionalReservedArea {
};
struct SAnimationPropertyConfig {
bool overriden = true;
bool overridden = true;
std::string internalBezier = "";
std::string internalStyle = "";
@@ -150,6 +163,8 @@ class CConfigManager {
SConfigValue* getConfigValuePtrSafe(const std::string&);
SMonitorRule getMonitorRuleFor(const std::string&, const std::string& displayName = "");
SWorkspaceRule getWorkspaceRuleFor(CWorkspace*);
std::string getDefaultWorkspaceFor(const std::string&);
CMonitor* getBoundMonitorForWS(const std::string&);
std::string getBoundMonitorStringForWS(const std::string&);
@@ -171,7 +186,7 @@ class CConfigManager {
bool m_bWantsMonitorReload = false;
bool m_bForceReload = false;
bool m_bNoMonitorReload = false;
void ensureDPMS();
void ensureMonitorStatus();
void ensureVRR(CMonitor* pMonitor = nullptr);
std::string parseKeyword(const std::string&, const std::string&, bool dynamic = false);
@@ -182,12 +197,14 @@ class CConfigManager {
void addExecRule(const SExecRequestedRule&);
void handlePluginLoads();
std::string configCurrentPath;
private:
std::deque<std::string> configPaths; // stores all the config paths
std::unordered_map<std::string, time_t> configModifyTimes; // stores modify times
std::unordered_map<std::string, std::string> configDynamicVars; // stores dynamic vars declared by the user
std::vector<std::pair<std::string, std::string>> configDynamicVars; // stores dynamic vars declared by the user
std::unordered_map<std::string, SConfigValue> configValues;
std::unordered_map<std::string, std::unordered_map<std::string, SConfigValue>> deviceConfigs; // stores device configs
@@ -203,19 +220,22 @@ class CConfigManager {
std::vector<SExecRequestedRule> execRequestedRules; // rules requested with exec, e.g. [workspace 2] kitty
std::vector<std::string> m_vDeclaredPlugins;
std::unordered_map<HANDLE, std::unique_ptr<std::unordered_map<std::string, SConfigValue>>> pluginConfigs; // stores plugin configs
bool isFirstLaunch = true; // For exec-once
std::deque<SMonitorRule> m_dMonitorRules;
std::unordered_map<int, SWorkspaceRule> m_mWorkspaceRules;
std::deque<SWindowRule> m_dWindowRules;
std::deque<SLayerRule> m_dLayerRules;
std::deque<std::string> m_dBlurLSNamespaces;
bool firstExecDispatched = false;
bool firstExecDispatched = false;
bool m_bManualCrashInitiated = false;
std::deque<std::string> firstExecRequests;
std::unordered_map<std::string, std::string> environmentVariables;
std::vector<std::pair<std::string, std::string>> environmentVariables;
// internal methods
void setDefaultVars();
@@ -240,7 +260,7 @@ class CConfigManager {
void handleWindowRule(const std::string&, const std::string&);
void handleLayerRule(const std::string&, const std::string&);
void handleWindowRuleV2(const std::string&, const std::string&);
void handleDefaultWorkspace(const std::string&, const std::string&);
void handleWorkspaceRules(const std::string&, const std::string&);
void handleBezier(const std::string&, const std::string&);
void handleAnimation(const std::string&, const std::string&);
void handleSource(const std::string&, const std::string&);
@@ -248,6 +268,7 @@ class CConfigManager {
void handleBlurLS(const std::string&, const std::string&);
void handleBindWS(const std::string&, const std::string&);
void handleEnv(const std::string&, const std::string&);
void handlePlugin(const std::string&, const std::string&);
};
inline std::unique_ptr<CConfigManager> g_pConfigManager;

View File

@@ -108,7 +108,7 @@ gestures {
# Example per-device config
# See https://wiki.hyprland.org/Configuring/Keywords/#executing for more
device:epic mouse V1 {
device:epic-mouse-v1 {
sensitivity = -0.5
}

View File

@@ -46,7 +46,9 @@ void CrashReporter::createAndSaveCrash(int sig) {
finalCrashReport += getFormat("Hyprland received signal %d (%s)\n\n", sig, strsignal(sig));
if (!g_pPluginSystem->getAllPlugins().empty()) {
finalCrashReport += getFormat("Version: %s\n\n", GIT_COMMIT_HASH);
if (g_pPluginSystem && !g_pPluginSystem->getAllPlugins().empty()) {
finalCrashReport += "Hyprland seems to be running with plugins. This crash might not be Hyprland's fault.\nPlugins:\n";
for (auto& p : g_pPluginSystem->getAllPlugins()) {
@@ -109,7 +111,7 @@ void CrashReporter::createAndSaveCrash(int sig) {
#endif
for (size_t i = 0; i < btSize; ++i) {
finalCrashReport += getFormat("\t#%i | %s\n", i, btSymbols[i]);
finalCrashReport += getFormat("\t#%lu | %s\n", i, btSymbols[i]);
#ifdef __clang__
const auto CMD = getFormat("llvm-addr2line -e %s -f 0x%lx", FPATH.c_str(), (uint64_t)bt[i]);
@@ -126,19 +128,39 @@ void CrashReporter::createAndSaveCrash(int sig) {
finalCrashReport += execAndGet(("cat \"" + Debug::logFile + "\" | tail -n 50").c_str());
const auto HOME = getenv("HOME");
const auto HOME = getenv("HOME");
const auto CACHE_HOME = getenv("XDG_CACHE_HOME");
if (!HOME)
return;
if (!std::filesystem::exists(std::string(HOME) + "/.hyprland")) {
std::filesystem::create_directory(std::string(HOME) + "/.hyprland");
std::filesystem::permissions(std::string(HOME) + "/.hyprland", std::filesystem::perms::all, std::filesystem::perm_options::replace);
}
std::ofstream ofs;
std::string path;
if (!CACHE_HOME) {
if (!std::filesystem::exists(std::string(HOME) + "/.hyprland")) {
std::filesystem::create_directory(std::string(HOME) + "/.hyprland");
std::filesystem::permissions(std::string(HOME) + "/.hyprland", std::filesystem::perms::all, std::filesystem::perm_options::replace);
}
std::ofstream ofs(std::string(HOME) + "/.hyprland/hyprlandCrashReport" + std::to_string(PID) + ".txt", std::ios::trunc);
path = std::string(HOME) + "/.hyprland/hyprlandCrashReport" + std::to_string(PID) + ".txt";
ofs.open(path, std::ios::trunc);
} else if (CACHE_HOME) {
if (!std::filesystem::exists(std::string(CACHE_HOME) + "/hyprland")) {
std::filesystem::create_directory(std::string(CACHE_HOME) + "/hyprland");
std::filesystem::permissions(std::string(CACHE_HOME) + "/hyprland", std::filesystem::perms::all, std::filesystem::perm_options::replace);
}
path = std::string(CACHE_HOME) + "/hyprland/hyprlandCrashReport" + std::to_string(PID) + ".txt";
ofs.open(path, std::ios::trunc);
} else {
return;
}
ofs << finalCrashReport;
ofs.close();
}
Debug::disableStdout = false;
Debug::log(CRIT, "Hyprland has crashed :( Consult the crash report at %s for more information.", path.c_str());
}

View File

@@ -20,6 +20,9 @@ std::string monitorsRequest(HyprCtl::eHyprCtlOutputFormat format) {
result += "[";
for (auto& m : g_pCompositor->m_vMonitors) {
if (!m->output)
continue;
result += getFormat(
R"#({
"id": %i,
@@ -58,6 +61,9 @@ std::string monitorsRequest(HyprCtl::eHyprCtlOutputFormat format) {
result += "]";
} else {
for (auto& m : g_pCompositor->m_vMonitors) {
if (!m->output)
continue;
result += getFormat("Monitor %s (ID %i):\n\t%ix%i@%f at %ix%i\n\tdescription: %s\n\tmake: %s\n\tmodel: %s\n\tserial: %s\n\tactive workspace: %i (%s)\n\treserved: %i "
"%i %i %i\n\tscale: %.2f\n\ttransform: "
"%i\n\tfocused: %s\n\tdpmsStatus: %i\n\tvrr: %i\n\n",
@@ -87,7 +93,7 @@ static std::string getGroupedData(CWindow* w, HyprCtl::eHyprCtlOutputFormat form
} while (curr != w);
const auto comma = isJson ? ", " : ",";
const auto fmt = isJson ? "\"0x%x\"" : "%x";
const auto fmt = isJson ? "\"0x%lx\"" : "%lx";
std::ostringstream result;
bool first = true;
@@ -107,7 +113,7 @@ static std::string getWindowData(CWindow* w, HyprCtl::eHyprCtlOutputFormat forma
if (format == HyprCtl::FORMAT_JSON) {
return getFormat(
R"#({
"address": "0x%x",
"address": "0x%lx",
"mapped": %s,
"hidden": %s,
"at": [%i, %i],
@@ -120,6 +126,8 @@ static std::string getWindowData(CWindow* w, HyprCtl::eHyprCtlOutputFormat forma
"monitor": %i,
"class": "%s",
"title": "%s",
"initialClass": "%s",
"initialTitle": "%s",
"pid": %i,
"xwayland": %s,
"pinned": %s,
@@ -136,22 +144,23 @@ static std::string getWindowData(CWindow* w, HyprCtl::eHyprCtlOutputFormat forma
std::string("Invalid workspace " + std::to_string(w->m_iWorkspaceID)))
.c_str(),
((int)w->m_bIsFloating == 1 ? "true" : "false"), w->m_iMonitorID, escapeJSONStrings(g_pXWaylandManager->getAppIDClass(w)).c_str(),
escapeJSONStrings(g_pXWaylandManager->getTitle(w)).c_str(), w->getPID(), ((int)w->m_bIsX11 == 1 ? "true" : "false"), (w->m_bPinned ? "true" : "false"),
(w->m_bIsFullscreen ? "true" : "false"),
escapeJSONStrings(g_pXWaylandManager->getTitle(w)).c_str(), escapeJSONStrings(w->m_szInitialClass).c_str(), escapeJSONStrings(w->m_szInitialTitle).c_str(), w->getPID(),
((int)w->m_bIsX11 == 1 ? "true" : "false"), (w->m_bPinned ? "true" : "false"), (w->m_bIsFullscreen ? "true" : "false"),
(w->m_bIsFullscreen ? (g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID) ? g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID)->m_efFullscreenMode : 0) : 0),
w->m_bFakeFullscreenState ? "true" : "false", getGroupedData(w, format).c_str(), (w->m_pSwallowed ? getFormat("\"0x%x\"", w->m_pSwallowed).c_str() : "null"));
w->m_bFakeFullscreenState ? "true" : "false", getGroupedData(w, format).c_str(), (w->m_pSwallowed ? getFormat("\"0x%lx\"", w->m_pSwallowed).c_str() : "null"));
} else {
return getFormat(
"Window %x -> %s:\n\tmapped: %i\n\thidden: %i\n\tat: %i,%i\n\tsize: %i,%i\n\tworkspace: %i (%s)\n\tfloating: %i\n\tmonitor: %i\n\tclass: %s\n\ttitle: %s\n\tpid: "
"Window %lx -> %s:\n\tmapped: %i\n\thidden: %i\n\tat: %i,%i\n\tsize: %i,%i\n\tworkspace: %i (%s)\n\tfloating: %i\n\tmonitor: %i\n\tclass: %s\n\ttitle: "
"%s\n\tinitialClass: %s\n\tinitialTitle: %s\n\tpid: "
"%i\n\txwayland: %i\n\tpinned: "
"%i\n\tfullscreen: %i\n\tfullscreenmode: %i\n\tfakefullscreen: %i\n\tgrouped: %s\n\tswallowing: %x\n\n",
"%i\n\tfullscreen: %i\n\tfullscreenmode: %i\n\tfakefullscreen: %i\n\tgrouped: %s\n\tswallowing: %lx\n\n",
w, w->m_szTitle.c_str(), (int)w->m_bIsMapped, (int)w->isHidden(), (int)w->m_vRealPosition.goalv().x, (int)w->m_vRealPosition.goalv().y, (int)w->m_vRealSize.goalv().x,
(int)w->m_vRealSize.goalv().y, w->m_iWorkspaceID,
(w->m_iWorkspaceID == -1 ? "" :
g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID) ? g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID)->m_szName.c_str() :
std::string("Invalid workspace " + std::to_string(w->m_iWorkspaceID)).c_str()),
(int)w->m_bIsFloating, w->m_iMonitorID, g_pXWaylandManager->getAppIDClass(w).c_str(), g_pXWaylandManager->getTitle(w).c_str(), w->getPID(), (int)w->m_bIsX11,
(int)w->m_bPinned, (int)w->m_bIsFullscreen,
(int)w->m_bIsFloating, w->m_iMonitorID, g_pXWaylandManager->getAppIDClass(w).c_str(), g_pXWaylandManager->getTitle(w).c_str(), w->m_szInitialClass.c_str(),
w->m_szInitialTitle.c_str(), w->getPID(), (int)w->m_bIsX11, (int)w->m_bPinned, (int)w->m_bIsFullscreen,
(w->m_bIsFullscreen ? (g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID) ? g_pCompositor->getWorkspaceByID(w->m_iWorkspaceID)->m_efFullscreenMode : 0) : 0),
(int)w->m_bFakeFullscreenState, getGroupedData(w, format).c_str(), w->m_pSwallowed);
}
@@ -179,41 +188,53 @@ std::string clientsRequest(HyprCtl::eHyprCtlOutputFormat format) {
return result;
}
std::string workspacesRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string result = "";
static std::string getWorkspaceData(CWorkspace* w, HyprCtl::eHyprCtlOutputFormat format) {
const auto PLASTW = w->getLastFocusedWindow();
const auto PMONITOR = g_pCompositor->getMonitorFromID(w->m_iMonitorID);
if (format == HyprCtl::FORMAT_JSON) {
result += "[";
for (auto& w : g_pCompositor->m_vWorkspaces) {
const auto PLASTW = w->getLastFocusedWindow();
result += getFormat(
R"#({
return getFormat(R"#({
"id": %i,
"name": "%s",
"monitor": "%s",
"windows": %i,
"hasfullscreen": %s,
"lastwindow": "0x%x",
"lastwindow": "0x%lx",
"lastwindowtitle": "%s"
},)#",
w->m_iID, escapeJSONStrings(w->m_szName).c_str(), escapeJSONStrings(g_pCompositor->getMonitorFromID(w->m_iMonitorID)->szName).c_str(),
g_pCompositor->getWindowsOnWorkspace(w->m_iID), ((int)w->m_bHasFullscreenWindow == 1 ? "true" : "false"), PLASTW,
PLASTW ? escapeJSONStrings(PLASTW->m_szTitle).c_str() : "");
})#",
w->m_iID, escapeJSONStrings(w->m_szName).c_str(), escapeJSONStrings(PMONITOR ? PMONITOR->szName : "?").c_str(),
g_pCompositor->getWindowsOnWorkspace(w->m_iID), ((int)w->m_bHasFullscreenWindow == 1 ? "true" : "false"), PLASTW,
PLASTW ? escapeJSONStrings(PLASTW->m_szTitle).c_str() : "");
} else {
return getFormat("workspace ID %i (%s) on monitor %s:\n\twindows: %i\n\thasfullscreen: %i\n\tlastwindow: 0x%lx\n\tlastwindowtitle: %s\n\n", w->m_iID, w->m_szName.c_str(),
PMONITOR ? PMONITOR->szName.c_str() : "?", g_pCompositor->getWindowsOnWorkspace(w->m_iID), (int)w->m_bHasFullscreenWindow, PLASTW,
PLASTW ? PLASTW->m_szTitle.c_str() : "");
}
}
std::string activeWorkspaceRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string result = "";
auto w = g_pCompositor->getWorkspaceByID(g_pCompositor->m_pLastMonitor->activeWorkspace);
return getWorkspaceData(w, format);
}
std::string workspacesRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string result = "";
if (format == HyprCtl::FORMAT_JSON) {
result += "[";
for (auto& w : g_pCompositor->m_vWorkspaces) {
result += getWorkspaceData(w.get(), format);
result += ",";
}
// remove trailing comma
result.pop_back();
result += "]";
} else {
for (auto& w : g_pCompositor->m_vWorkspaces) {
const auto PLASTW = w->getLastFocusedWindow();
result += getFormat("workspace ID %i (%s) on monitor %s:\n\twindows: %i\n\thasfullscreen: %i\n\tlastwindow: 0x%x\n\tlastwindowtitle: %s\n\n", w->m_iID,
w->m_szName.c_str(), g_pCompositor->getMonitorFromID(w->m_iMonitorID)->szName.c_str(), g_pCompositor->getWindowsOnWorkspace(w->m_iID),
(int)w->m_bHasFullscreenWindow, PLASTW, PLASTW ? PLASTW->m_szTitle.c_str() : "");
result += getWorkspaceData(w.get(), format);
}
}
return result;
}
@@ -254,7 +275,7 @@ std::string layersRequest(HyprCtl::eHyprCtlOutputFormat format) {
for (auto& layer : level) {
result += getFormat(
R"#( {
"address": "0x%x",
"address": "0x%lx",
"x": %i,
"y": %i,
"w": %i,
@@ -295,7 +316,7 @@ std::string layersRequest(HyprCtl::eHyprCtlOutputFormat format) {
result += getFormat("\tLayer level %i (%s):\n", layerLevel, levelNames[layerLevel].c_str());
for (auto& layer : level) {
result += getFormat("\t\tLayer %x: xywh: %i %i %i %i, namespace: %s\n", layer.get(), layer->geometry.x, layer->geometry.y, layer->geometry.width,
result += getFormat("\t\tLayer %lx: xywh: %i %i %i %i, namespace: %s\n", layer.get(), layer->geometry.x, layer->geometry.y, layer->geometry.width,
layer->geometry.height, layer->szNamespace.c_str());
}
@@ -318,7 +339,7 @@ std::string devicesRequest(HyprCtl::eHyprCtlOutputFormat format) {
for (auto& m : g_pInputManager->m_lMice) {
result += getFormat(
R"#( {
"address": "0x%x",
"address": "0x%lx",
"name": "%s",
"defaultSpeed": %f
},)#",
@@ -335,7 +356,7 @@ std::string devicesRequest(HyprCtl::eHyprCtlOutputFormat format) {
const auto KM = g_pInputManager->getActiveLayoutForKeyboard(&k);
result += getFormat(
R"#( {
"address": "0x%x",
"address": "0x%lx",
"name": "%s",
"rules": "%s",
"model": "%s",
@@ -359,10 +380,10 @@ std::string devicesRequest(HyprCtl::eHyprCtlOutputFormat format) {
for (auto& d : g_pInputManager->m_lTabletPads) {
result += getFormat(
R"#( {
"address": "0x%x",
"address": "0x%lx",
"type": "tabletPad",
"belongsTo": {
"address": "0x%x",
"address": "0x%lx",
"name": "%s"
}
},)#",
@@ -372,7 +393,7 @@ std::string devicesRequest(HyprCtl::eHyprCtlOutputFormat format) {
for (auto& d : g_pInputManager->m_lTablets) {
result += getFormat(
R"#( {
"address": "0x%x",
"address": "0x%lx",
"name": "%s"
},)#",
&d, escapeJSONStrings(d.name).c_str());
@@ -381,9 +402,9 @@ std::string devicesRequest(HyprCtl::eHyprCtlOutputFormat format) {
for (auto& d : g_pInputManager->m_lTabletTools) {
result += getFormat(
R"#( {
"address": "0x%x",
"address": "0x%lx",
"type": "tabletTool",
"belongsTo": "0x%x"
"belongsTo": "0x%lx"
},)#",
&d, d.wlrTabletTool ? d.wlrTabletTool->data : 0);
}
@@ -397,7 +418,7 @@ std::string devicesRequest(HyprCtl::eHyprCtlOutputFormat format) {
for (auto& d : g_pInputManager->m_lTouchDevices) {
result += getFormat(
R"#( {
"address": "0x%x",
"address": "0x%lx",
"name": "%s"
},)#",
&d, d.name.c_str());
@@ -413,7 +434,7 @@ std::string devicesRequest(HyprCtl::eHyprCtlOutputFormat format) {
for (auto& d : g_pInputManager->m_lSwitches) {
result += getFormat(
R"#( {
"address": "0x%x",
"address": "0x%lx",
"name": "%s"
},)#",
&d, d.pWlrDevice ? d.pWlrDevice->name : "");
@@ -431,7 +452,7 @@ std::string devicesRequest(HyprCtl::eHyprCtlOutputFormat format) {
for (auto& m : g_pInputManager->m_lMice) {
result += getFormat(
"\tMouse at %x:\n\t\t%s\n\t\t\tdefault speed: %f\n", &m, m.name.c_str(),
"\tMouse at %lx:\n\t\t%s\n\t\t\tdefault speed: %f\n", &m, m.name.c_str(),
(wlr_input_device_is_libinput(m.mouse) ? libinput_device_config_accel_get_default_speed((libinput_device*)wlr_libinput_get_device_handle(m.mouse)) : 0.f));
}
@@ -439,7 +460,7 @@ std::string devicesRequest(HyprCtl::eHyprCtlOutputFormat format) {
for (auto& k : g_pInputManager->m_lKeyboards) {
const auto KM = g_pInputManager->getActiveLayoutForKeyboard(&k);
result += getFormat("\tKeyboard at %x:\n\t\t%s\n\t\t\trules: r \"%s\", m \"%s\", l \"%s\", v \"%s\", o \"%s\"\n\t\t\tactive keymap: %s\n\t\t\tmain: %s\n", &k,
result += getFormat("\tKeyboard at %lx:\n\t\t%s\n\t\t\trules: r \"%s\", m \"%s\", l \"%s\", v \"%s\", o \"%s\"\n\t\t\tactive keymap: %s\n\t\t\tmain: %s\n", &k,
k.name.c_str(), k.currentRules.rules.c_str(), k.currentRules.model.c_str(), k.currentRules.layout.c_str(), k.currentRules.variant.c_str(),
k.currentRules.options.c_str(), KM.c_str(), (k.active ? "yes" : "no"));
}
@@ -447,27 +468,27 @@ std::string devicesRequest(HyprCtl::eHyprCtlOutputFormat format) {
result += "\n\nTablets:\n";
for (auto& d : g_pInputManager->m_lTabletPads) {
result += getFormat("\tTablet Pad at %x (belongs to %x -> %s)\n", &d, d.pTabletParent, d.pTabletParent ? d.pTabletParent->name.c_str() : "");
result += getFormat("\tTablet Pad at %lx (belongs to %lx -> %s)\n", &d, d.pTabletParent, d.pTabletParent ? d.pTabletParent->name.c_str() : "");
}
for (auto& d : g_pInputManager->m_lTablets) {
result += getFormat("\tTablet at %x:\n\t\t%s\n", &d, d.name.c_str());
result += getFormat("\tTablet at %lx:\n\t\t%s\n", &d, d.name.c_str());
}
for (auto& d : g_pInputManager->m_lTabletTools) {
result += getFormat("\tTablet Tool at %x (belongs to %x)\n", &d, d.wlrTabletTool ? d.wlrTabletTool->data : 0);
result += getFormat("\tTablet Tool at %lx (belongs to %lx)\n", &d, d.wlrTabletTool ? d.wlrTabletTool->data : 0);
}
result += "\n\nTouch:\n";
for (auto& d : g_pInputManager->m_lTouchDevices) {
result += getFormat("\tTouch Device at %x:\n\t\t%s\n", &d, d.name.c_str());
result += getFormat("\tTouch Device at %lx:\n\t\t%s\n", &d, d.name.c_str());
}
result += "\n\nSwitches:\n";
for (auto& d : g_pInputManager->m_lSwitches) {
result += getFormat("\tSwitch Device at %x:\n\t\t%s\n", &d, d.pWlrDevice ? d.pWlrDevice->name : "");
result += getFormat("\tSwitch Device at %lx:\n\t\t%s\n", &d, d.pWlrDevice ? d.pWlrDevice->name : "");
}
}
@@ -480,7 +501,7 @@ std::string animationsRequest(HyprCtl::eHyprCtlOutputFormat format) {
ret += "animations:\n";
for (auto& ac : g_pConfigManager->getAnimationConfig()) {
ret += getFormat("\n\tname: %s\n\t\toverriden: %i\n\t\tbezier: %s\n\t\tenabled: %i\n\t\tspeed: %.2f\n\t\tstyle: %s\n", ac.first.c_str(), (int)ac.second.overriden,
ret += getFormat("\n\tname: %s\n\t\toverriden: %i\n\t\tbezier: %s\n\t\tenabled: %i\n\t\tspeed: %.2f\n\t\tstyle: %s\n", ac.first.c_str(), (int)ac.second.overridden,
ac.second.internalBezier.c_str(), ac.second.internalEnabled, ac.second.internalSpeed, ac.second.internalStyle.c_str());
}
@@ -497,13 +518,13 @@ std::string animationsRequest(HyprCtl::eHyprCtlOutputFormat format) {
ret += getFormat(R"#(
{
"name": "%s",
"overriden": %s,
"overridden": %s,
"bezier": "%s",
"enabled": %s,
"speed": %.2f,
"style": "%s"
},)#",
ac.first.c_str(), ac.second.overriden ? "true" : "false", ac.second.internalBezier.c_str(), ac.second.internalEnabled ? "true" : "false",
ac.first.c_str(), ac.second.overridden ? "true" : "false", ac.second.internalBezier.c_str(), ac.second.internalEnabled ? "true" : "false",
ac.second.internalSpeed, ac.second.internalStyle.c_str());
}
@@ -527,6 +548,29 @@ std::string animationsRequest(HyprCtl::eHyprCtlOutputFormat format) {
return ret;
}
std::string globalShortcutsRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string ret = "";
const auto SHORTCUTS = g_pProtocolManager->m_pGlobalShortcutsProtocolManager->getAllShortcuts();
if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL) {
for (auto& sh : SHORTCUTS)
ret += getFormat("%s:%s -> %s\n", sh.appid.c_str(), sh.id.c_str(), sh.description.c_str());
} else {
ret += "[";
for (auto& sh : SHORTCUTS) {
ret += getFormat(R"#(
{
"name": "%s",
"description": "%s"
},)#",
escapeJSONStrings(sh.appid + ":" + sh.id).c_str(), escapeJSONStrings(sh.description).c_str());
}
ret.pop_back();
ret += "]\n";
}
return ret;
}
std::string bindsRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string ret = "";
if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL) {
@@ -575,8 +619,8 @@ std::string bindsRequest(HyprCtl::eHyprCtlOutputFormat format) {
std::string versionRequest(HyprCtl::eHyprCtlOutputFormat format) {
if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL) {
std::string result = "Hyprland, built from branch " + std::string(GIT_BRANCH) + " at commit " + GIT_COMMIT_HASH + GIT_DIRTY + " (" +
removeBeginEndSpacesTabs(GIT_COMMIT_MESSAGE).c_str() + ").\nflags: (if any)\n";
std::string result = "Hyprland, built from branch " + std::string(GIT_BRANCH) + " at commit " + GIT_COMMIT_HASH + " " + GIT_DIRTY + " (" +
removeBeginEndSpacesTabs(GIT_COMMIT_MESSAGE).c_str() + ").\nTag: " + GIT_TAG + "\n\nflags: (if any)\n";
#ifdef LEGACY_RENDERER
result += "legacyrenderer\n";
@@ -771,47 +815,28 @@ std::string dispatchBatch(std::string request) {
}
std::string dispatchSetCursor(std::string request) {
std::string curitem = "";
CVarList vars(request, 0, ' ');
auto nextItem = [&]() {
auto idx = request.find_first_of(' ');
const auto SIZESTR = vars[vars.size() - 1];
std::string theme = "";
for (size_t i = 1; i < vars.size() - 1; ++i)
theme += vars[i] + " ";
theme.pop_back();
if (idx != std::string::npos) {
curitem = request.substr(0, idx);
request = request.substr(idx + 1);
} else {
curitem = request;
request = "";
}
int size = 0;
try {
size = std::stoi(SIZESTR);
} catch (...) { return "size not int"; }
curitem = removeBeginEndSpacesTabs(curitem);
};
nextItem();
nextItem();
const auto THEME = curitem;
nextItem();
const auto SIZE = curitem;
if (!isNumber(SIZE)) {
return "size not int";
}
const auto SIZEINT = std::stoi(SIZE);
if (SIZEINT < 1) {
return "size must be positive";
}
if (size <= 0)
return "size not positive";
wlr_xcursor_manager_destroy(g_pCompositor->m_sWLRXCursorMgr);
g_pCompositor->m_sWLRXCursorMgr = wlr_xcursor_manager_create(THEME.c_str(), SIZEINT);
g_pCompositor->m_sWLRXCursorMgr = wlr_xcursor_manager_create(theme.c_str(), size);
setenv("XCURSOR_SIZE", SIZE.c_str(), true);
setenv("XCURSOR_THEME", THEME.c_str(), true);
setenv("XCURSOR_SIZE", SIZESTR.c_str(), true);
setenv("XCURSOR_THEME", theme.c_str(), true);
for (auto& m : g_pCompositor->m_vMonitors) {
wlr_xcursor_manager_load(g_pCompositor->m_sWLRXCursorMgr, m->scale);
@@ -927,7 +952,7 @@ std::string dispatchSetProp(std::string request) {
} else if (PROP == "forceopaque") {
PWINDOW->m_sAdditionalConfigData.forceOpaque.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "forceopaqueoverriden") {
PWINDOW->m_sAdditionalConfigData.forceOpaqueOverriden.forceSetIgnoreLocked(configStringToInt(VAL), lock);
PWINDOW->m_sAdditionalConfigData.forceOpaqueOverridden.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "forceallowsinput") {
PWINDOW->m_sAdditionalConfigData.forceAllowsInput.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "forcenoanims") {
@@ -954,6 +979,8 @@ std::string dispatchSetProp(std::string request) {
PWINDOW->m_sSpecialRenderData.activeBorderColor.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "inactivebordercolor") {
PWINDOW->m_sSpecialRenderData.inactiveBorderColor.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else if (PROP == "forcergbx") {
PWINDOW->m_sAdditionalConfigData.forceRGBX.forceSetIgnoreLocked(configStringToInt(VAL), lock);
} else {
return "prop not found";
}
@@ -990,7 +1017,7 @@ std::string dispatchGetOption(std::string request, HyprCtl::eHyprCtlOutputFormat
return "no such option";
if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL)
return getFormat("option %s\n\tint: %lld\n\tfloat: %f\n\tstr: \"%s\"\n\tdata: %x", curitem.c_str(), PCFGOPT->intValue, PCFGOPT->floatValue, PCFGOPT->strValue.c_str(),
return getFormat("option %s\n\tint: %lld\n\tfloat: %f\n\tstr: \"%s\"\n\tdata: %lx", curitem.c_str(), PCFGOPT->intValue, PCFGOPT->floatValue, PCFGOPT->strValue.c_str(),
PCFGOPT->data.get());
else {
return getFormat(
@@ -1000,7 +1027,7 @@ std::string dispatchGetOption(std::string request, HyprCtl::eHyprCtlOutputFormat
"int": %lld,
"float": %f,
"str": "%s",
"data": "0x%x"
"data": "0x%lx"
}
)#",
curitem.c_str(), PCFGOPT->intValue, PCFGOPT->floatValue, PCFGOPT->strValue.c_str(), PCFGOPT->data.get());
@@ -1131,6 +1158,47 @@ std::string dispatchPlugin(std::string request) {
return "ok";
}
std::string dispatchNotify(std::string request) {
CVarList vars(request, 0, ' ');
if (vars.size() < 5)
return "not enough args";
const auto ICON = vars[1];
if (!isNumber(ICON))
return "invalid arg 1";
int icon = -1;
try {
icon = std::stoi(ICON);
} catch (std::exception& e) { return "invalid arg 1"; }
if (icon > ICON_NONE || icon < 0) {
icon = ICON_NONE;
}
const auto TIME = vars[2];
int time = 0;
try {
time = std::stoi(TIME);
} catch (std::exception& e) { return "invalid arg 2"; }
CColor color = configStringToInt(vars[3]);
std::string message = "";
for (size_t i = 4; i < vars.size(); ++i) {
message += vars[i] + " ";
}
message.pop_back();
g_pHyprNotificationOverlay->addNotification(message, color, time, (eIcons)icon);
return "ok";
}
std::string getReply(std::string request) {
auto format = HyprCtl::FORMAT_NORMAL;
@@ -1156,6 +1224,8 @@ std::string getReply(std::string request) {
return monitorsRequest(format);
else if (request == "workspaces")
return workspacesRequest(format);
else if (request == "activeworkspace")
return activeWorkspaceRequest(format);
else if (request == "clients")
return clientsRequest(format);
else if (request == "kill")
@@ -1176,10 +1246,14 @@ std::string getReply(std::string request) {
return cursorPosRequest(format);
else if (request == "binds")
return bindsRequest(format);
else if (request == "globalshortcuts")
return globalShortcutsRequest(format);
else if (request == "animations")
return animationsRequest(format);
else if (request.find("plugin") == 0)
return dispatchPlugin(request);
else if (request.find("notify") == 0)
return dispatchNotify(request);
else if (request.find("setprop") == 0)
return dispatchSetProp(request);
else if (request.find("seterror") == 0)
@@ -1236,7 +1310,7 @@ int hyprCtlFDTick(int fd, uint32_t mask, void* data) {
close(ACCEPTEDCONNECTION);
if (g_pConfigManager->m_bWantsMonitorReload) {
g_pConfigManager->ensureDPMS();
g_pConfigManager->ensureMonitorStatus();
}
return 0;

View File

@@ -7,6 +7,8 @@
#include <cairo/cairo.h>
#include <unordered_map>
class CHyprRenderer;
class CHyprMonitorDebugOverlay {
public:
int draw(int offset);
@@ -23,6 +25,8 @@ class CHyprMonitorDebugOverlay {
std::chrono::high_resolution_clock::time_point m_tpLastFrame;
CMonitor* m_pMonitor = nullptr;
wlr_box m_wbLastDrawnBox;
friend class CHyprRenderer;
};
class CHyprDebugOverlay {
@@ -41,6 +45,7 @@ class CHyprDebugOverlay {
CTexture m_tTexture;
friend class CHyprMonitorDebugOverlay;
friend class CHyprRenderer;
};
inline std::unique_ptr<CHyprDebugOverlay> g_pDebugOverlay;

View File

@@ -1,5 +1,6 @@
#include "HyprNotificationOverlay.hpp"
#include "../Compositor.hpp"
#include <pango/pangocairo.h>
CHyprNotificationOverlay::CHyprNotificationOverlay() {
g_pHookSystem->hookDynamic("focusedMon", [&](void* self, std::any param) {
@@ -8,38 +9,77 @@ CHyprNotificationOverlay::CHyprNotificationOverlay() {
g_pHyprRenderer->damageBox(&m_bLastDamage);
});
// check for the icon backend
std::string fonts = execAndGet("fc-list");
std::string fontsLower = fonts;
std::transform(fontsLower.begin(), fontsLower.end(), fontsLower.begin(), [&](char& i) { return std::tolower(i); });
size_t index = 0;
if (index = fontsLower.find("nerd"); index != std::string::npos) {
m_eIconBackend = ICONS_BACKEND_NF;
} else if (index = fontsLower.find("font awesome"); index != std::string::npos) {
m_eIconBackend = ICONS_BACKEND_FA;
} else if (index = fontsLower.find("fontawesome"); index != std::string::npos) {
m_eIconBackend = ICONS_BACKEND_FA;
} else {
return;
}
const auto LASTNEWLINE = fonts.find_last_of('\n', index);
const auto COLON = fonts.find(':', LASTNEWLINE);
const auto COMMA = fonts.find(',', COLON);
const auto NEWLINE = fonts.find('\n', COLON);
const auto LASTCHAR = COMMA < NEWLINE ? COMMA : NEWLINE;
m_szIconFontName = fonts.substr(COLON + 2, LASTCHAR - (COLON + 2));
}
void CHyprNotificationOverlay::addNotification(const std::string& text, const CColor& color, const float timeMs) {
void CHyprNotificationOverlay::addNotification(const std::string& text, const CColor& color, const float timeMs, const eIcons icon) {
const auto PNOTIF = m_dNotifications.emplace_back(std::make_unique<SNotification>()).get();
PNOTIF->text = text;
PNOTIF->color = color;
PNOTIF->color = color == CColor(0) ? ICONS_COLORS[icon] : color;
PNOTIF->started.reset();
PNOTIF->timeMs = timeMs;
PNOTIF->icon = icon;
}
wlr_box CHyprNotificationOverlay::drawNotifications(CMonitor* pMonitor) {
static constexpr auto ANIM_DURATION_MS = 600.0;
static constexpr auto ANIM_LAG_MS = 100.0;
static constexpr auto NOTIF_LEFTBAR_SIZE = 5.0;
static constexpr auto ICON_PAD = 3.0;
static constexpr auto ICON_SCALE = 0.9;
static constexpr auto GRADIENT_SIZE = 60.0;
float offsetY = 10;
float maxWidth = 0;
const auto SCALE = pMonitor->scale;
const auto FONTSIZE = std::clamp((int)(10.f * ((pMonitor->vecPixelSize.x * SCALE) / 1920.f)), 8, 40);
const auto FONTSIZE = std::clamp((int)(13.f * ((pMonitor->vecPixelSize.x * SCALE) / 1920.f)), 8, 40);
const auto MONSIZE = pMonitor->vecPixelSize;
cairo_select_font_face(m_pCairo, "Noto Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
cairo_text_extents_t cairoExtents;
int iconW = 0, iconH = 0;
PangoLayout* pangoLayout;
PangoFontDescription* pangoFD;
pangoLayout = pango_cairo_create_layout(m_pCairo);
pangoFD = pango_font_description_from_string(("Sans " + std::to_string(FONTSIZE * ICON_SCALE)).c_str());
pango_layout_set_font_description(pangoLayout, pangoFD);
cairo_select_font_face(m_pCairo, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
cairo_set_font_size(m_pCairo, FONTSIZE);
cairo_text_extents_t cairoExtents;
const auto PBEZIER = g_pAnimationManager->getBezier("default");
const auto PBEZIER = g_pAnimationManager->getBezier("default");
for (auto& notif : m_dNotifications) {
const auto ICONPADFORNOTIF = notif->icon == ICON_NONE ? 0 : ICON_PAD;
// first rect (bg, col)
const float FIRSTRECTANIMP =
(notif->started.getMillis() > (ANIM_DURATION_MS - ANIM_LAG_MS) ?
@@ -62,10 +102,18 @@ wlr_box CHyprNotificationOverlay::drawNotifications(CMonitor* pMonitor) {
// get text size
cairo_text_extents(m_pCairo, notif->text.c_str(), &cairoExtents);
const auto ICON = ICONS_ARRAY[m_eIconBackend][notif->icon];
const auto ICONCOLOR = ICONS_COLORS[notif->icon];
pango_layout_set_text(pangoLayout, ICON.c_str(), -1);
pango_layout_set_font_description(pangoLayout, pangoFD);
pango_cairo_update_layout(m_pCairo, pangoLayout);
pango_layout_get_size(pangoLayout, &iconW, &iconH);
iconW /= PANGO_SCALE;
iconH /= PANGO_SCALE;
cairo_set_source_rgba(m_pCairo, notif->color.r, notif->color.g, notif->color.b, notif->color.a);
const auto NOTIFSIZE = Vector2D{cairoExtents.width + 20, cairoExtents.height + 10};
const auto NOTIFSIZE = Vector2D{cairoExtents.width + 20 + iconW + 2 * ICONPADFORNOTIF, cairoExtents.height + 10};
// draw rects
cairo_rectangle(m_pCairo, MONSIZE.x - (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC, offsetY, (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC, NOTIFSIZE.y);
@@ -81,9 +129,28 @@ wlr_box CHyprNotificationOverlay::drawNotifications(CMonitor* pMonitor) {
cairo_rectangle(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + 3, offsetY + NOTIFSIZE.y - 4, THIRDRECTPERC * (NOTIFSIZE.x - 6), 2);
cairo_fill(m_pCairo);
// draw gradient
if (notif->icon != ICON_NONE) {
cairo_pattern_t* pattern;
pattern = cairo_pattern_create_linear(MONSIZE.x - (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC, offsetY,
MONSIZE.x - (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC + GRADIENT_SIZE, offsetY);
cairo_pattern_add_color_stop_rgba(pattern, 0, ICONCOLOR.r, ICONCOLOR.g, ICONCOLOR.b, ICONCOLOR.a / 3.0);
cairo_pattern_add_color_stop_rgba(pattern, 1, ICONCOLOR.r, ICONCOLOR.g, ICONCOLOR.b, 0);
cairo_rectangle(m_pCairo, MONSIZE.x - (NOTIFSIZE.x + NOTIF_LEFTBAR_SIZE) * FIRSTRECTPERC, offsetY, GRADIENT_SIZE, NOTIFSIZE.y);
cairo_set_source(m_pCairo, pattern);
cairo_fill(m_pCairo);
cairo_pattern_destroy(pattern);
// draw icon
cairo_set_source_rgb(m_pCairo, 1.f, 1.f, 1.f);
cairo_move_to(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + NOTIF_LEFTBAR_SIZE + ICONPADFORNOTIF - 1, offsetY + std::round((NOTIFSIZE.y - iconH - 4) / 2.0));
pango_cairo_show_layout(m_pCairo, pangoLayout);
}
// draw text
cairo_set_font_size(m_pCairo, FONTSIZE);
cairo_set_source_rgb(m_pCairo, 1.f, 1.f, 1.f);
cairo_move_to(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + NOTIF_LEFTBAR_SIZE, offsetY + FONTSIZE + (FONTSIZE / 10.0));
cairo_move_to(m_pCairo, MONSIZE.x - NOTIFSIZE.x * SECONDRECTPERC + NOTIF_LEFTBAR_SIZE + iconW + 2 * ICONPADFORNOTIF, offsetY + FONTSIZE + (FONTSIZE / 10.0));
cairo_show_text(m_pCairo, notif->text.c_str());
// adjust offset and move on
@@ -93,6 +160,9 @@ wlr_box CHyprNotificationOverlay::drawNotifications(CMonitor* pMonitor) {
maxWidth = NOTIFSIZE.x;
}
pango_font_description_free(pangoFD);
g_object_unref(pangoLayout);
// cleanup notifs
std::erase_if(m_dNotifications, [](const auto& notif) { return notif->started.getMillis() > notif->timeMs; });

View File

@@ -4,16 +4,36 @@
#include "../helpers/Timer.hpp"
#include "../helpers/Monitor.hpp"
#include "../render/Texture.hpp"
#include "../SharedDefs.hpp"
#include <deque>
#include <cairo/cairo.h>
enum eIconBackend
{
ICONS_BACKEND_NONE = 0,
ICONS_BACKEND_NF,
ICONS_BACKEND_FA
};
static const std::array<std::array<std::string, ICON_NONE + 1>, 3 /* backends */> ICONS_ARRAY = {
std::array<std::string, ICON_NONE + 1>{"[!]", "[i]", "[Hint]", "[Err]", "[?]", "[ok]", ""}, std::array<std::string, ICON_NONE + 1>{"", "", "", "", "", "󰸞", ""},
std::array<std::string, ICON_NONE + 1>{"", "", "", "", "", ""}};
static const std::array<CColor, ICON_NONE + 1> ICONS_COLORS = {CColor{255.0 / 255.0, 204 / 255.0, 102 / 255.0, 1.0},
CColor{128 / 255.0, 255 / 255.0, 255 / 255.0, 1.0},
CColor{179 / 255.0, 255 / 255.0, 204 / 255.0, 1.0},
CColor{255 / 255.0, 77 / 255.0, 77 / 255.0, 1.0},
CColor{255 / 255.0, 204 / 255.0, 153 / 255.0, 1.0},
CColor{128 / 255.0, 255 / 255.0, 128 / 255.0, 1.0},
CColor{0, 0, 0, 1.0}};
struct SNotification {
std::string text = "";
CColor color;
CTimer started;
float timeMs = 0;
eIcons icon = ICON_NONE;
};
class CHyprNotificationOverlay {
@@ -21,7 +41,7 @@ class CHyprNotificationOverlay {
CHyprNotificationOverlay();
void draw(CMonitor* pMonitor);
void addNotification(const std::string& text, const CColor& color, const float timeMs);
void addNotification(const std::string& text, const CColor& color, const float timeMs, const eIcons icon = ICON_NONE);
private:
wlr_box drawNotifications(CMonitor* pMonitor);
@@ -35,6 +55,9 @@ class CHyprNotificationOverlay {
CMonitor* m_pLastMonitor = nullptr;
CTexture m_tTexture;
eIconBackend m_eIconBackend = ICONS_BACKEND_NONE;
std::string m_szIconFontName = "Sans";
};
inline std::unique_ptr<CHyprNotificationOverlay> g_pHyprNotificationOverlay;

View File

@@ -23,6 +23,9 @@ void Debug::wlrLog(wlr_log_importance level, const char* fmt, va_list args) {
ofs << "[wlr] " << output << "\n";
ofs.close();
if (!disableStdout)
std::cout << output << "\n";
}
void Debug::log(LogLevel level, const char* fmt, ...) {
@@ -75,5 +78,6 @@ void Debug::log(LogLevel level, const char* fmt, ...) {
ofs.close();
// log it to the stdout too.
std::cout << output << "\n";
if (!disableStdout)
std::cout << output << "\n";
}

View File

@@ -20,6 +20,7 @@ namespace Debug {
void wlrLog(wlr_log_importance level, const char* fmt, va_list args);
inline std::string logFile;
inline int64_t* disableLogs = nullptr;
inline int64_t* disableTime = nullptr;
inline int64_t* disableLogs = nullptr;
inline int64_t* disableTime = nullptr;
inline bool disableStdout = false;
};

View File

@@ -86,6 +86,9 @@
#ifndef GIT_DIRTY
#define GIT_DIRTY "?"
#endif
#ifndef GIT_TAG
#define GIT_TAG "?"
#endif
#define SPECIAL_WORKSPACE_START (-99)

View File

@@ -19,7 +19,7 @@ void Events::listener_keyboardDestroy(void* owner, void* data) {
SKeyboard* PKEYBOARD = (SKeyboard*)owner;
g_pInputManager->destroyKeyboard(PKEYBOARD);
Debug::log(LOG, "Destroyed keyboard %x", PKEYBOARD);
Debug::log(LOG, "Destroyed keyboard %lx", PKEYBOARD);
}
void Events::listener_keyboardKey(void* owner, void* data) {
@@ -95,7 +95,7 @@ void Events::listener_newInput(wl_listener* listener, void* data) {
void Events::listener_newConstraint(wl_listener* listener, void* data) {
const auto PCONSTRAINT = (wlr_pointer_constraint_v1*)data;
Debug::log(LOG, "New mouse constraint at %x", PCONSTRAINT);
Debug::log(LOG, "New mouse constraint at %lx", PCONSTRAINT);
g_pInputManager->m_lConstraints.emplace_back();
const auto CONSTRAINT = &g_pInputManager->m_lConstraints.back();
@@ -139,7 +139,7 @@ void Events::listener_destroyConstraint(void* owner, void* data) {
PCONSTRAINT->pMouse->currentConstraint = nullptr;
}
Debug::log(LOG, "Unconstrained mouse from %x", PCONSTRAINT->constraint);
Debug::log(LOG, "Unconstrained mouse from %lx", PCONSTRAINT->constraint);
g_pInputManager->m_lConstraints.remove(*PCONSTRAINT);
}

View File

@@ -98,6 +98,9 @@ namespace Events {
DYNLISTENFUNC(monitorFrame);
DYNLISTENFUNC(monitorDestroy);
DYNLISTENFUNC(monitorStateRequest);
DYNLISTENFUNC(monitorDamage);
DYNLISTENFUNC(monitorNeedsFrame);
DYNLISTENFUNC(monitorCommit);
// XWayland
LISTENER(readyXWayland);

View File

@@ -55,14 +55,14 @@ void Events::listener_newLayerSurface(wl_listener* listener, void* data) {
layerSurface->forceBlur = g_pConfigManager->shouldBlurLS(layerSurface->szNamespace);
Debug::log(LOG, "LayerSurface %x (namespace %s layer %d) created on monitor %s", layerSurface->layerSurface, layerSurface->layerSurface->_namespace, layerSurface->layer,
Debug::log(LOG, "LayerSurface %lx (namespace %s layer %d) created on monitor %s", layerSurface->layerSurface, layerSurface->layerSurface->_namespace, layerSurface->layer,
PMONITOR->szName.c_str());
}
void Events::listener_destroyLayerSurface(void* owner, void* data) {
SLayerSurface* layersurface = (SLayerSurface*)owner;
Debug::log(LOG, "LayerSurface %x destroyed", layersurface->layerSurface);
Debug::log(LOG, "LayerSurface %lx destroyed", layersurface->layerSurface);
const auto PMONITOR = g_pCompositor->getMonitorFromID(layersurface->monitorID);
@@ -107,11 +107,13 @@ void Events::listener_destroyLayerSurface(void* owner, void* data) {
void Events::listener_mapLayerSurface(void* owner, void* data) {
SLayerSurface* layersurface = (SLayerSurface*)owner;
Debug::log(LOG, "LayerSurface %x mapped", layersurface->layerSurface);
Debug::log(LOG, "LayerSurface %lx mapped", layersurface->layerSurface);
layersurface->layerSurface->mapped = true;
layersurface->mapped = true;
layersurface->surface = layersurface->layerSurface->surface;
// anim
layersurface->alpha.setConfig(g_pConfigManager->getAnimationPropertyConfig("fadeIn"));
@@ -121,10 +123,7 @@ void Events::listener_mapLayerSurface(void* owner, void* data) {
if (!PMONITOR)
return;
for (auto& rule : g_pConfigManager->getMatchingRules(layersurface)) {
if (rule.rule == "noanim")
layersurface->noAnimations = true;
}
layersurface->applyRules();
if ((uint64_t)layersurface->monitorID != PMONITOR->ID) {
const auto POLDMON = g_pCompositor->getMonitorFromID(layersurface->monitorID);
@@ -174,7 +173,7 @@ void Events::listener_mapLayerSurface(void* owner, void* data) {
void Events::listener_unmapLayerSurface(void* owner, void* data) {
SLayerSurface* layersurface = (SLayerSurface*)owner;
Debug::log(LOG, "LayerSurface %x unmapped", layersurface->layerSurface);
Debug::log(LOG, "LayerSurface %lx unmapped", layersurface->layerSurface);
g_pEventManager->postEvent(SHyprIPCEvent{"closelayer", std::string(layersurface->layerSurface->_namespace ? layersurface->layerSurface->_namespace : "")});
EMIT_HOOK_EVENT("closeLayer", layersurface);
@@ -210,11 +209,16 @@ void Events::listener_unmapLayerSurface(void* owner, void* data) {
const auto PMONITOR = g_pCompositor->getMonitorFromOutput(layersurface->layerSurface->output);
const bool WASLASTFOCUS = g_pCompositor->m_pLastFocus == layersurface->layerSurface->surface;
layersurface->surface = nullptr;
if (!PMONITOR)
return;
// refocus if needed
if (layersurface->layerSurface->surface == g_pCompositor->m_pLastFocus) {
if (WASLASTFOCUS) {
g_pInputManager->releaseAllMouseButtons();
Vector2D surfaceCoords;
SLayerSurface* pFoundLayerSurface = nullptr;

View File

@@ -98,14 +98,14 @@ void Events::listener_startDrag(wl_listener* listener, void* data) {
wlr_drag* wlrDrag = (wlr_drag*)data;
Debug::log(LOG, "Started drag %x", wlrDrag);
Debug::log(LOG, "Started drag %lx", wlrDrag);
wlrDrag->data = data;
g_pInputManager->m_sDrag.hyprListener_destroy.initCallback(&wlrDrag->events.destroy, &Events::listener_destroyDrag, &g_pInputManager->m_sDrag, "Drag");
if (wlrDrag->icon) {
Debug::log(LOG, "Drag started with an icon %x", wlrDrag->icon);
Debug::log(LOG, "Drag started with an icon %lx", wlrDrag->icon);
g_pInputManager->m_sDrag.dragIcon = wlrDrag->icon;
wlrDrag->icon->data = g_pInputManager->m_sDrag.dragIcon;
@@ -116,20 +116,11 @@ void Events::listener_startDrag(wl_listener* listener, void* data) {
g_pInputManager->m_sDrag.hyprListener_commitIcon.initCallback(&wlrDrag->icon->surface->events.commit, &Events::listener_commitDragIcon, &g_pInputManager->m_sDrag,
"DragIcon");
}
static auto* const PFOLLOWONDND = &g_pConfigManager->getConfigValuePtr("misc:always_follow_on_dnd")->intValue;
if (*PFOLLOWONDND)
g_pInputManager->m_pFollowOnDnDBegin = g_pCompositor->m_pLastWindow;
else
g_pInputManager->m_pFollowOnDnDBegin = nullptr;
}
void Events::listener_destroyDrag(void* owner, void* data) {
Debug::log(LOG, "Drag destroyed.");
static auto* const PFOLLOWMOUSE = &g_pConfigManager->getConfigValuePtr("input:follow_mouse")->intValue;
if (g_pInputManager->m_sDrag.drag && g_pInputManager->m_sDrag.dragIcon && g_pInputManager->m_sDrag.dragIcon->surface)
g_pHyprRenderer->damageBox(g_pInputManager->m_sDrag.pos.x - 2, g_pInputManager->m_sDrag.pos.y - 2, g_pInputManager->m_sDrag.dragIcon->surface->current.width + 4,
g_pInputManager->m_sDrag.dragIcon->surface->current.height + 4);
@@ -137,13 +128,6 @@ void Events::listener_destroyDrag(void* owner, void* data) {
g_pInputManager->m_sDrag.drag = nullptr;
g_pInputManager->m_sDrag.dragIcon = nullptr;
g_pInputManager->m_sDrag.hyprListener_destroy.removeCallback();
g_pInputManager->refocus();
if (g_pInputManager->m_pFollowOnDnDBegin && *PFOLLOWMOUSE != 1)
g_pCompositor->focusWindow(g_pInputManager->m_pFollowOnDnDBegin);
g_pInputManager->m_pFollowOnDnDBegin = nullptr;
}
void Events::listener_mapDragIcon(void* owner, void* data) {
@@ -173,7 +157,7 @@ void Events::listener_commitDragIcon(void* owner, void* data) {
}
void Events::listener_InhibitActivate(wl_listener* listener, void* data) {
Debug::log(LOG, "Activated exclusive for %x.", g_pCompositor->m_sSeat.exclusiveClient);
Debug::log(LOG, "Activated exclusive for %lx.", g_pCompositor->m_sSeat.exclusiveClient);
g_pInputManager->refocus();
g_pCompositor->m_sSeat.exclusiveClient = g_pCompositor->m_sWLRInhibitMgr->active_client;

View File

@@ -19,7 +19,13 @@ void Events::listener_change(wl_listener* listener, void* data) {
// layout got changed, let's update monitors.
const auto CONFIG = wlr_output_configuration_v1_create();
if (!CONFIG)
return;
for (auto& m : g_pCompositor->m_vMonitors) {
if (!m->output)
continue;
const auto CONFIGHEAD = wlr_output_configuration_head_v1_create(CONFIG, m->output);
// TODO: clients off of disabled
@@ -41,7 +47,7 @@ void Events::listener_change(wl_listener* listener, void* data) {
}
void Events::listener_newOutput(wl_listener* listener, void* data) {
// new monitor added, let's accomodate for that.
// new monitor added, let's accommodate for that.
const auto OUTPUT = (wlr_output*)data;
// for warping the cursor on launch
@@ -98,6 +104,13 @@ void Events::listener_newOutput(wl_listener* listener, void* data) {
const auto POS = PNEWMONITOR->vecPosition + PNEWMONITOR->vecSize / 2.f;
if (g_pCompositor->m_sSeat.mouse)
wlr_cursor_warp(g_pCompositor->m_sWLRCursor, g_pCompositor->m_sSeat.mouse->mouse, POS.x, POS.y);
} else {
for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_iMonitorID == PNEWMONITOR->ID) {
w->m_iLastSurfaceMonitorID = -1;
w->updateSurfaceOutputs();
}
}
}
}
@@ -106,229 +119,47 @@ void Events::listener_monitorFrame(void* owner, void* data) {
if ((g_pCompositor->m_sWLRSession && !g_pCompositor->m_sWLRSession->active) || !g_pCompositor->m_bSessionActive || g_pCompositor->m_bUnsafeState) {
Debug::log(WARN, "Attempted to render frame on inactive session!");
if (g_pCompositor->m_bUnsafeState)
g_pConfigManager->performMonitorReload();
return; // cannot draw on session inactive (different tty)
}
if (!PMONITOR->m_bEnabled)
return;
static std::chrono::high_resolution_clock::time_point startRender = std::chrono::high_resolution_clock::now();
static std::chrono::high_resolution_clock::time_point startRenderOverlay = std::chrono::high_resolution_clock::now();
static std::chrono::high_resolution_clock::time_point endRenderOverlay = std::chrono::high_resolution_clock::now();
static auto* const PENABLERAT = &g_pConfigManager->getConfigValuePtr("misc:render_ahead_of_time")->intValue;
static auto* const PRATSAFE = &g_pConfigManager->getConfigValuePtr("misc:render_ahead_safezone")->intValue;
static auto* const PDEBUGOVERLAY = &g_pConfigManager->getConfigValuePtr("debug:overlay")->intValue;
static auto* const PDAMAGETRACKINGMODE = &g_pConfigManager->getConfigValuePtr("debug:damage_tracking")->intValue;
static auto* const PDAMAGEBLINK = &g_pConfigManager->getConfigValuePtr("debug:damage_blink")->intValue;
static auto* const PNODIRECTSCANOUT = &g_pConfigManager->getConfigValuePtr("misc:no_direct_scanout")->intValue;
static auto* const PVFR = &g_pConfigManager->getConfigValuePtr("misc:vfr")->intValue;
PMONITOR->lastPresentationTimer.reset();
static int damageBlinkCleanup = 0; // because double-buffered
if (!*PDAMAGEBLINK)
damageBlinkCleanup = 0;
if (*PDEBUGOVERLAY == 1) {
startRender = std::chrono::high_resolution_clock::now();
g_pDebugOverlay->frameData(PMONITOR);
}
if (PMONITOR->framesToSkip > 0) {
PMONITOR->framesToSkip -= 1;
if (!PMONITOR->noFrameSchedule)
g_pCompositor->scheduleFrameForMonitor(PMONITOR);
else {
Debug::log(LOG, "NoFrameSchedule hit for %s.", PMONITOR->szName.c_str());
if (*PENABLERAT) {
if (!PMONITOR->RATScheduled) {
// render
g_pHyprRenderer->renderMonitor(PMONITOR);
}
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PMONITOR->ID);
if (PMONITOR->framesToSkip > 10)
PMONITOR->framesToSkip = 0;
return;
}
PMONITOR->RATScheduled = false;
// checks //
if (PMONITOR->ID == g_pHyprRenderer->m_pMostHzMonitor->ID ||
*PVFR == 1) { // unfortunately with VFR we don't have the guarantee mostHz is going to be updated all the time, so we have to ignore that
g_pCompositor->sanityCheckWorkspaces();
const auto& [avg, max, min] = g_pHyprRenderer->getRenderTimes(PMONITOR);
g_pConfigManager->dispatchExecOnce(); // We exec-once when at least one monitor starts refreshing, meaning stuff has init'd
if (g_pConfigManager->m_bWantsMonitorReload)
g_pConfigManager->performMonitorReload();
g_pHyprRenderer->ensureCursorRenderingMode(); // so that the cursor gets hidden/shown if the user requested timeouts
}
// //
if (PMONITOR->scheduledRecalc) {
PMONITOR->scheduledRecalc = false;
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PMONITOR->ID);
}
// Direct scanout first
if (!*PNODIRECTSCANOUT) {
if (g_pHyprRenderer->attemptDirectScanout(PMONITOR)) {
if (max + *PRATSAFE > 1000.0 / PMONITOR->refreshRate)
return;
} else if (g_pHyprRenderer->m_pLastScanout) {
Debug::log(LOG, "Left a direct scanout.");
g_pHyprRenderer->m_pLastScanout = nullptr;
}
}
EMIT_HOOK_EVENT("preRender", PMONITOR);
const auto MSLEFT = 1000.0 / PMONITOR->refreshRate - PMONITOR->lastPresentationTimer.getMillis();
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
PMONITOR->RATScheduled = true;
// check the damage
pixman_region32_t damage;
bool hasChanged;
pixman_region32_init(&damage);
const auto ESTRENDERTIME = std::ceil(avg + *PRATSAFE);
const auto TIMETOSLEEP = std::floor(MSLEFT - ESTRENDERTIME);
if (*PDAMAGETRACKINGMODE == -1) {
Debug::log(CRIT, "Damage tracking mode -1 ????");
return;
}
if (!wlr_output_damage_attach_render(PMONITOR->damage, &hasChanged, &damage)) {
Debug::log(ERR, "Couldn't attach render to display %s ???", PMONITOR->szName.c_str());
return;
}
PMONITOR->renderingActive = true;
// we need to cleanup fading out when rendering the appropriate context
g_pCompositor->cleanupFadingOut(PMONITOR->ID);
if (!hasChanged && *PDAMAGETRACKINGMODE != DAMAGE_TRACKING_NONE && PMONITOR->forceFullFrames == 0 && damageBlinkCleanup == 0) {
pixman_region32_fini(&damage);
wlr_output_rollback(PMONITOR->output);
if (*PDAMAGEBLINK || *PVFR == 0)
g_pCompositor->scheduleFrameForMonitor(PMONITOR);
PMONITOR->renderingActive = false;
return;
}
// if we have no tracking or full tracking, invalidate the entire monitor
if (*PDAMAGETRACKINGMODE == DAMAGE_TRACKING_NONE || *PDAMAGETRACKINGMODE == DAMAGE_TRACKING_MONITOR || PMONITOR->forceFullFrames > 0 || damageBlinkCleanup > 0 ||
PMONITOR->isMirror() /* why??? */) {
pixman_region32_union_rect(&damage, &damage, 0, 0, (int)PMONITOR->vecTransformedSize.x * 10, (int)PMONITOR->vecTransformedSize.y * 10); // wot?
pixman_region32_copy(&g_pHyprOpenGL->m_rOriginalDamageRegion, &damage);
if (MSLEFT < 1 || MSLEFT < ESTRENDERTIME || TIMETOSLEEP < 1)
g_pHyprRenderer->renderMonitor(PMONITOR);
else
wl_event_source_timer_update(PMONITOR->renderTimer, TIMETOSLEEP);
} else {
static auto* const PBLURENABLED = &g_pConfigManager->getConfigValuePtr("decoration:blur")->intValue;
// if we use blur we need to expand the damage for proper blurring
if (*PBLURENABLED == 1) {
// TODO: can this be optimized?
static auto* const PBLURSIZE = &g_pConfigManager->getConfigValuePtr("decoration:blur_size")->intValue;
static auto* const PBLURPASSES = &g_pConfigManager->getConfigValuePtr("decoration:blur_passes")->intValue;
const auto BLURRADIUS = *PBLURSIZE * pow(2, *PBLURPASSES); // is this 2^pass? I don't know but it works... I think.
// now, prep the damage, get the extended damage region
wlr_region_expand(&damage, &damage, BLURRADIUS); // expand for proper blurring
pixman_region32_copy(&g_pHyprOpenGL->m_rOriginalDamageRegion, &damage);
wlr_region_expand(&damage, &damage, BLURRADIUS); // expand for proper blurring 2
} else {
pixman_region32_copy(&g_pHyprOpenGL->m_rOriginalDamageRegion, &damage);
}
}
if (PMONITOR->forceFullFrames > 0) {
PMONITOR->forceFullFrames -= 1;
if (PMONITOR->forceFullFrames > 10)
PMONITOR->forceFullFrames = 0;
}
// TODO: this is getting called with extents being 0,0,0,0 should it be?
// potentially can save on resources.
g_pHyprOpenGL->begin(PMONITOR, &damage);
if (PMONITOR->isMirror()) {
g_pHyprOpenGL->renderMirrored();
} else {
g_pHyprOpenGL->clear(CColor(17.0 / 255.0, 17.0 / 255.0, 17.0 / 255.0, 1.0));
g_pHyprOpenGL->clearWithTex(); // will apply the hypr "wallpaper"
g_pHyprRenderer->renderAllClientsForMonitor(PMONITOR->ID, &now);
if (PMONITOR == g_pCompositor->m_pLastMonitor) {
g_pHyprNotificationOverlay->draw(PMONITOR);
g_pHyprError->draw();
}
// for drawing the debug overlay
if (PMONITOR == g_pCompositor->m_vMonitors.front().get() && *PDEBUGOVERLAY == 1) {
startRenderOverlay = std::chrono::high_resolution_clock::now();
g_pDebugOverlay->draw();
endRenderOverlay = std::chrono::high_resolution_clock::now();
}
if (*PDAMAGEBLINK && damageBlinkCleanup == 0) {
wlr_box monrect = {0, 0, PMONITOR->vecTransformedSize.x, PMONITOR->vecTransformedSize.y};
g_pHyprOpenGL->renderRect(&monrect, CColor(1.0, 0.0, 1.0, 100.0 / 255.0), 0);
damageBlinkCleanup = 1;
} else if (*PDAMAGEBLINK) {
damageBlinkCleanup++;
if (damageBlinkCleanup > 3)
damageBlinkCleanup = 0;
}
if (wlr_renderer_begin(g_pCompositor->m_sWLRRenderer, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y)) {
wlr_output_render_software_cursors(PMONITOR->output, NULL);
wlr_renderer_end(g_pCompositor->m_sWLRRenderer);
}
}
g_pHyprOpenGL->end();
// calc frame damage
pixman_region32_t frameDamage;
pixman_region32_init(&frameDamage);
const auto TRANSFORM = wlr_output_transform_invert(PMONITOR->output->transform);
wlr_region_transform(&frameDamage, &g_pHyprOpenGL->m_rOriginalDamageRegion, TRANSFORM, (int)PMONITOR->vecTransformedSize.x, (int)PMONITOR->vecTransformedSize.y);
if (*PDAMAGETRACKINGMODE == DAMAGE_TRACKING_NONE || *PDAMAGETRACKINGMODE == DAMAGE_TRACKING_MONITOR)
pixman_region32_union_rect(&frameDamage, &frameDamage, 0, 0, (int)PMONITOR->vecTransformedSize.x, (int)PMONITOR->vecTransformedSize.y);
if (*PDAMAGEBLINK)
pixman_region32_union(&frameDamage, &frameDamage, &damage);
wlr_output_set_damage(PMONITOR->output, &frameDamage);
if (!PMONITOR->mirrors.empty())
g_pHyprRenderer->damageMirrorsWith(PMONITOR, &frameDamage);
pixman_region32_fini(&frameDamage);
pixman_region32_fini(&damage);
PMONITOR->renderingActive = false;
if (!wlr_output_commit(PMONITOR->output))
return;
if (*PDAMAGEBLINK || *PVFR == 0 || PMONITOR->pendingFrame)
g_pCompositor->scheduleFrameForMonitor(PMONITOR);
PMONITOR->pendingFrame = false;
if (*PDEBUGOVERLAY == 1) {
const float µs = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now() - startRender).count() / 1000.f;
g_pDebugOverlay->renderData(PMONITOR, µs);
if (PMONITOR == g_pCompositor->m_vMonitors.front().get()) {
const float µsNoOverlay = µs - std::chrono::duration_cast<std::chrono::nanoseconds>(endRenderOverlay - startRenderOverlay).count() / 1000.f;
g_pDebugOverlay->renderDataNoOverlay(PMONITOR, µsNoOverlay);
} else {
g_pDebugOverlay->renderDataNoOverlay(PMONITOR, µs);
}
g_pHyprRenderer->renderMonitor(PMONITOR);
}
}
@@ -351,9 +182,12 @@ void Events::listener_monitorDestroy(void* owner, void* data) {
pMonitor->onDisconnect();
pMonitor->output = nullptr;
pMonitor->m_bRenderingInitPassed = false;
// cleanup if not unsafe
if (!g_pCompositor->m_bUnsafeState) {
Debug::log(LOG, "Removing monitor %s from realMonitors", pMonitor->output->name);
Debug::log(LOG, "Removing monitor %s from realMonitors", pMonitor->szName.c_str());
std::erase_if(g_pCompositor->m_vRealMonitors, [&](std::shared_ptr<CMonitor>& el) { return el.get() == pMonitor; });
}
@@ -365,3 +199,24 @@ void Events::listener_monitorStateRequest(void* owner, void* data) {
wlr_output_commit_state(PMONITOR->output, E->state);
}
void Events::listener_monitorDamage(void* owner, void* data) {
const auto PMONITOR = (CMonitor*)owner;
const auto E = (wlr_output_event_damage*)data;
PMONITOR->addDamage(E->damage);
}
void Events::listener_monitorNeedsFrame(void* owner, void* data) {
const auto PMONITOR = (CMonitor*)owner;
g_pCompositor->scheduleFrameForMonitor(PMONITOR);
}
void Events::listener_monitorCommit(void* owner, void* data) {
const auto PMONITOR = (CMonitor*)owner;
const auto E = (wlr_output_event_commit*)data;
g_pProtocolManager->m_pScreencopyProtocolManager->onOutputCommit(PMONITOR, E);
}

View File

@@ -78,7 +78,7 @@ void Events::listener_newPopup(void* owner, void* data) {
ASSERT(layersurface);
Debug::log(LOG, "New layer popup created from surface %x", layersurface);
Debug::log(LOG, "New layer popup created from surface %lx", layersurface);
const auto WLRPOPUP = (wlr_xdg_popup*)data;
@@ -86,10 +86,11 @@ void Events::listener_newPopup(void* owner, void* data) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(layersurface->monitorID);
PNEWPOPUP->popup = WLRPOPUP;
PNEWPOPUP->lx = layersurface->position.x;
PNEWPOPUP->ly = layersurface->position.y;
PNEWPOPUP->monitor = PMONITOR;
PNEWPOPUP->popup = WLRPOPUP;
PNEWPOPUP->lx = layersurface->position.x;
PNEWPOPUP->ly = layersurface->position.y;
PNEWPOPUP->monitor = PMONITOR;
PNEWPOPUP->parentLS = layersurface;
createNewPopup(WLRPOPUP, PNEWPOPUP);
}
@@ -101,7 +102,7 @@ void Events::listener_newPopupXDG(void* owner, void* data) {
if (!PWINDOW->m_bIsMapped)
return;
Debug::log(LOG, "New layer popup created from XDG window %x -> %s", PWINDOW, PWINDOW->m_szTitle.c_str());
Debug::log(LOG, "New layer popup created from XDG window %lx -> %s", PWINDOW, PWINDOW->m_szTitle.c_str());
const auto WLRPOPUP = (wlr_xdg_popup*)data;
@@ -123,9 +124,9 @@ void Events::listener_newPopupFromPopupXDG(void* owner, void* data) {
ASSERT(PPOPUP);
if (PPOPUP->parentWindow)
Debug::log(LOG, "New popup created from XDG Window popup %x -> %s", PPOPUP, PPOPUP->parentWindow->m_szTitle.c_str());
Debug::log(LOG, "New popup created from XDG Window popup %lx -> %s", PPOPUP, PPOPUP->parentWindow->m_szTitle.c_str());
else
Debug::log(LOG, "New popup created from Non-Window popup %x", PPOPUP);
Debug::log(LOG, "New popup created from Non-Window popup %lx", PPOPUP);
const auto WLRPOPUP = (wlr_xdg_popup*)data;
@@ -148,6 +149,11 @@ void Events::listener_mapPopupXDG(void* owner, void* data) {
Debug::log(LOG, "New XDG Popup mapped at %d %d", (int)PPOPUP->lx, (int)PPOPUP->ly);
if (PPOPUP->parentWindow)
PPOPUP->parentWindow->m_lPopupSurfaces.emplace_back(PPOPUP->popup->base->surface);
else if (PPOPUP->parentLS)
PPOPUP->parentLS->popupSurfaces.emplace_back(PPOPUP->popup->base->surface);
PPOPUP->pSurfaceTree = SubsurfaceTree::createTreeRoot(PPOPUP->popup->base->surface, addPopupGlobalCoords, PPOPUP, PPOPUP->parentWindow);
int lx = 0, ly = 0;
@@ -158,7 +164,10 @@ void Events::listener_mapPopupXDG(void* owner, void* data) {
g_pHyprRenderer->damageBox(lx - extents.x, ly - extents.y, extents.width + 2, extents.height + 2);
Debug::log(LOG, "XDG Popup got assigned a surfaceTreeNode %x", PPOPUP->pSurfaceTree);
if (PPOPUP->monitor)
g_pProtocolManager->m_pFractionalScaleProtocolManager->setPreferredScaleForSurface(PPOPUP->popup->base->surface, PPOPUP->monitor->scale);
Debug::log(LOG, "XDG Popup got assigned a surfaceTreeNode %lx", PPOPUP->pSurfaceTree);
}
void Events::listener_unmapPopupXDG(void* owner, void* data) {
@@ -167,6 +176,9 @@ void Events::listener_unmapPopupXDG(void* owner, void* data) {
ASSERT(PPOPUP);
if (PPOPUP->popup->base->surface == g_pCompositor->m_pLastFocus)
g_pInputManager->releaseAllMouseButtons();
SubsurfaceTree::destroySurfaceTree(PPOPUP->pSurfaceTree);
int lx = 0, ly = 0;
@@ -177,6 +189,11 @@ void Events::listener_unmapPopupXDG(void* owner, void* data) {
g_pHyprRenderer->damageBox(lx - extents.x, ly - extents.y, extents.width + 2, extents.height + 2);
if (PPOPUP->parentWindow)
std::erase(PPOPUP->parentWindow->m_lPopupSurfaces, PPOPUP->popup->base->surface);
else if (PPOPUP->parentLS)
std::erase(PPOPUP->parentLS->popupSurfaces, PPOPUP->popup->base->surface);
PPOPUP->pSurfaceTree = nullptr;
g_pInputManager->simulateMouseMovement(); // to focus and return back to an appropriate surface
@@ -196,7 +213,7 @@ void Events::listener_destroyPopupXDG(void* owner, void* data) {
ASSERT(PPOPUP);
Debug::log(LOG, "Destroyed popup XDG %x", PPOPUP);
Debug::log(LOG, "Destroyed popup XDG %lx", PPOPUP);
if (PPOPUP->pSurfaceTree) {
SubsurfaceTree::destroySurfaceTree(PPOPUP->pSurfaceTree);

View File

@@ -40,11 +40,12 @@ void setAnimToMove(void* data) {
void Events::listener_mapWindow(void* owner, void* data) {
CWindow* PWINDOW = (CWindow*)owner;
static auto* const PINACTIVEALPHA = &g_pConfigManager->getConfigValuePtr("decoration:inactive_opacity")->floatValue;
static auto* const PACTIVEALPHA = &g_pConfigManager->getConfigValuePtr("decoration:active_opacity")->floatValue;
static auto* const PDIMSTRENGTH = &g_pConfigManager->getConfigValuePtr("decoration:dim_strength")->floatValue;
static auto* const PSWALLOW = &g_pConfigManager->getConfigValuePtr("misc:enable_swallow")->intValue;
static auto* const PSWALLOWREGEX = &g_pConfigManager->getConfigValuePtr("misc:swallow_regex")->strValue;
static auto* const PINACTIVEALPHA = &g_pConfigManager->getConfigValuePtr("decoration:inactive_opacity")->floatValue;
static auto* const PACTIVEALPHA = &g_pConfigManager->getConfigValuePtr("decoration:active_opacity")->floatValue;
static auto* const PDIMSTRENGTH = &g_pConfigManager->getConfigValuePtr("decoration:dim_strength")->floatValue;
static auto* const PSWALLOW = &g_pConfigManager->getConfigValuePtr("misc:enable_swallow")->intValue;
static auto* const PSWALLOWREGEX = &g_pConfigManager->getConfigValuePtr("misc:swallow_regex")->strValue;
static auto* const PSWALLOWEXREGEX = &g_pConfigManager->getConfigValuePtr("misc:swallow_exception_regex")->strValue;
auto PMONITOR = g_pCompositor->m_pLastMonitor;
const auto PWORKSPACE =
@@ -67,13 +68,13 @@ void Events::listener_mapWindow(void* owner, void* data) {
// Foreign Toplevel
PWINDOW->createToplevelHandle();
// checks if the window wants borders and sets the appriopriate flag
// checks if the window wants borders and sets the appropriate flag
g_pXWaylandManager->checkBorders(PWINDOW);
// registers the animated vars and stuff
PWINDOW->onMap();
const auto PWINDOWSURFACE = g_pXWaylandManager->getWindowSurface(PWINDOW);
const auto PWINDOWSURFACE = PWINDOW->m_pWLSurface.wlr();
if (!PWINDOWSURFACE) {
g_pCompositor->removeWindowFromVectorSafe(PWINDOW);
@@ -98,14 +99,6 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_vPseudoSize = Vector2D(desiredGeometry.width, desiredGeometry.height);
}
CWindow* pFullscreenWindow = nullptr;
if (PWORKSPACE->m_bHasFullscreenWindow && !PWINDOW->m_bIsFloating) {
const auto PFULLWINDOW = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID);
pFullscreenWindow = PFULLWINDOW;
g_pCompositor->setWindowFullscreen(PFULLWINDOW, false, PWORKSPACE->m_efFullscreenMode);
}
// window rules
const auto WINDOWRULES = g_pConfigManager->getMatchingRules(PWINDOW);
std::string requestedWorkspace = "";
@@ -113,23 +106,37 @@ void Events::listener_mapWindow(void* owner, void* data) {
bool requestsFullscreen = PWINDOW->m_bWantsInitialFullscreen ||
(!PWINDOW->m_bIsX11 && PWINDOW->m_uSurface.xdg->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL && PWINDOW->m_uSurface.xdg->toplevel->requested.fullscreen) ||
(PWINDOW->m_bIsX11 && PWINDOW->m_uSurface.xwayland->fullscreen);
bool requestsMaximize = false;
bool shouldFocus = true;
bool workspaceSpecial = false;
bool requestsFakeFullscreen = false;
bool requestsMaximize = false;
bool shouldFocus = true;
bool workspaceSpecial = false;
PWINDOW->m_szInitialTitle = g_pXWaylandManager->getTitle(PWINDOW);
PWINDOW->m_szInitialClass = g_pXWaylandManager->getAppIDClass(PWINDOW);
for (auto& r : WINDOWRULES) {
if (r.szRule.find("monitor") == 0) {
try {
const auto MONITORSTR = r.szRule.substr(r.szRule.find(' '));
const auto MONITORSTR = removeBeginEndSpacesTabs(r.szRule.substr(r.szRule.find(' ')));
if (MONITORSTR == "unset") {
PWINDOW->m_iMonitorID = PMONITOR->ID;
} else {
const long int MONITOR = std::stoi(MONITORSTR);
if (!g_pCompositor->getMonitorFromID(MONITOR))
PWINDOW->m_iMonitorID = 0;
else
PWINDOW->m_iMonitorID = MONITOR;
if (isNumber(MONITORSTR)) {
const long int MONITOR = std::stoi(MONITORSTR);
if (!g_pCompositor->getMonitorFromID(MONITOR))
PWINDOW->m_iMonitorID = 0;
else
PWINDOW->m_iMonitorID = MONITOR;
} else {
const auto PMONITOR = g_pCompositor->getMonitorFromName(MONITORSTR);
if (PMONITOR)
PWINDOW->m_iMonitorID = PMONITOR->ID;
else {
Debug::log(ERR, "No monitor in monitor %s rule", MONITORSTR.c_str());
continue;
}
}
}
PWINDOW->m_iWorkspaceID = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID)->activeWorkspace;
@@ -138,7 +145,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
}
Debug::log(ERR, "Rule monitor, applying to window %x -> mon: %i, workspace: %i", PWINDOW, PWINDOW->m_iMonitorID, PWINDOW->m_iWorkspaceID);
Debug::log(ERR, "Rule monitor, applying to window %lx -> mon: %i, workspace: %i", PWINDOW, PWINDOW->m_iMonitorID, PWINDOW->m_iWorkspaceID);
} catch (std::exception& e) { Debug::log(ERR, "Rule monitor failed, rule: %s -> %s | err: %s", r.szRule.c_str(), r.szValue.c_str(), e.what()); }
} else if (r.szRule.find("workspace") == 0) {
// check if it isnt unset
@@ -155,7 +162,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
if (JUSTWORKSPACE == PWORKSPACE->m_szName || JUSTWORKSPACE == "name:" + PWORKSPACE->m_szName)
requestedWorkspace = "";
Debug::log(LOG, "Rule workspace matched by window %x, %s applied.", PWINDOW, r.szValue.c_str());
Debug::log(LOG, "Rule workspace matched by window %lx, %s applied.", PWINDOW, r.szValue.c_str());
} else if (r.szRule.find("float") == 0) {
PWINDOW->m_bIsFloating = true;
} else if (r.szRule.find("tile") == 0) {
@@ -164,10 +171,14 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_bIsPseudotiled = true;
} else if (r.szRule.find("nofocus") == 0) {
PWINDOW->m_bNoFocus = true;
} else if (r.szRule.find("noinitialfocus") == 0) {
PWINDOW->m_bNoInitialFocus = true;
} else if (r.szRule.find("nofullscreenrequest") == 0) {
PWINDOW->m_bNoFullscreenRequest = true;
} else if (r.szRule == "fullscreen") {
requestsFullscreen = true;
} else if (r.szRule == "fakefullscreen") {
requestsFakeFullscreen = true;
} else if (r.szRule == "windowdance") {
PWINDOW->m_sAdditionalConfigData.windowDanceCompat = true;
} else if (r.szRule == "nomaxsize") {
@@ -196,6 +207,13 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->applyDynamicRule(r);
}
CWindow* pFullscreenWindow = nullptr;
if (PWORKSPACE->m_bHasFullscreenWindow && !PWINDOW->m_bIsFloating) {
const auto PFULLWINDOW = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID);
pFullscreenWindow = PFULLWINDOW;
g_pCompositor->setWindowFullscreen(PFULLWINDOW, false, PWORKSPACE->m_efFullscreenMode);
}
// disallow tiled pinned
if (PWINDOW->m_bPinned && !PWINDOW->m_bIsFloating)
PWINDOW->m_bPinned = false;
@@ -289,7 +307,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
std::clamp(MAXSIZE.y, 20.0, PMONITOR->vecSize.y) :
(!SIZEYSTR.contains('%') ? std::stoi(SIZEYSTR) : std::stof(SIZEYSTR.substr(0, SIZEYSTR.length() - 1)) * 0.01 * PMONITOR->vecSize.y);
Debug::log(LOG, "Rule size, applying to window %x", PWINDOW);
Debug::log(LOG, "Rule size, applying to window %lx", PWINDOW);
PWINDOW->m_vRealSize = Vector2D(SIZEX, SIZEY);
g_pXWaylandManager->setWindowSize(PWINDOW, PWINDOW->m_vRealSize.goalv());
@@ -379,7 +397,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
}
}
Debug::log(LOG, "Rule move, applying to window %x", PWINDOW);
Debug::log(LOG, "Rule move, applying to window %lx", PWINDOW);
PWINDOW->m_vRealPosition = Vector2D(posX, posY) + PMONITOR->vecPosition;
@@ -425,7 +443,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_fDimPercent.setValueAndWarp(0);
}
Debug::log(LOG, "Window got assigned a surfaceTreeNode %x", PWINDOW->m_pSurfaceTree);
Debug::log(LOG, "Window got assigned a surfaceTreeNode %lx", PWINDOW->m_pSurfaceTree);
if (!PWINDOW->m_bIsX11) {
PWINDOW->hyprListener_commitWindow.initCallback(&PWINDOW->m_uSurface.xdg->surface->events.commit, &Events::listener_commitWindow, PWINDOW, "XDG Window Late");
@@ -462,17 +480,21 @@ void Events::listener_mapWindow(void* owner, void* data) {
PWINDOW->m_vRealPosition.setCallbackOnEnd(setAnimToMove);
PWINDOW->m_vRealSize.setCallbackOnEnd(setAnimToMove);
if ((requestsFullscreen || requestsMaximize) && !PWINDOW->m_bNoFullscreenRequest) {
if ((requestsFullscreen || requestsMaximize || requestsFakeFullscreen) && !PWINDOW->m_bNoFullscreenRequest) {
// fix fullscreen on requested (basically do a switcheroo)
if (PWORKSPACE->m_bHasFullscreenWindow) {
const auto PFULLWINDOW = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID);
g_pCompositor->setWindowFullscreen(PFULLWINDOW, false, FULLSCREEN_FULL);
}
PWINDOW->m_vRealPosition.warp();
PWINDOW->m_vRealSize.warp();
g_pCompositor->setWindowFullscreen(PWINDOW, true, requestsFullscreen ? FULLSCREEN_FULL : FULLSCREEN_MAXIMIZED);
if (requestsFakeFullscreen && !PWINDOW->m_bFakeFullscreenState) {
PWINDOW->m_bFakeFullscreenState = !PWINDOW->m_bFakeFullscreenState;
g_pXWaylandManager->setWindowFullscreen(PWINDOW, true);
} else {
PWINDOW->m_vRealPosition.warp();
PWINDOW->m_vRealSize.warp();
g_pCompositor->setWindowFullscreen(PWINDOW, true, requestsFullscreen ? FULLSCREEN_FULL : FULLSCREEN_MAXIMIZED);
}
}
if (pFullscreenWindow && workspaceSilent) {
@@ -482,7 +504,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
// recheck idle inhibitors
g_pInputManager->recheckIdleInhibitorStatus();
PWINDOW->m_pSurfaceTree = SubsurfaceTree::createTreeRoot(g_pXWaylandManager->getWindowSurface(PWINDOW), addViewCoords, PWINDOW, PWINDOW);
PWINDOW->m_pSurfaceTree = SubsurfaceTree::createTreeRoot(PWINDOW->m_pWLSurface.wlr(), addViewCoords, PWINDOW, PWINDOW);
PWINDOW->updateToplevel();
@@ -495,7 +517,7 @@ void Events::listener_mapWindow(void* owner, void* data) {
}
// verify swallowing
if (*PSWALLOW) {
if (*PSWALLOW && *PSWALLOWREGEX != STRVAL_EMPTY) {
// don't swallow ourselves
std::regex rgx(*PSWALLOWREGEX);
if (!std::regex_match(g_pXWaylandManager->getAppIDClass(PWINDOW), rgx)) {
@@ -540,8 +562,16 @@ void Events::listener_mapWindow(void* owner, void* data) {
}
if (finalFound) {
// check if it's the window we want
if (std::regex_match(g_pXWaylandManager->getAppIDClass(finalFound), rgx)) {
bool valid = std::regex_match(g_pXWaylandManager->getAppIDClass(finalFound), rgx);
if (*PSWALLOWEXREGEX != STRVAL_EMPTY) {
std::regex exc(*PSWALLOWEXREGEX);
valid = valid && !std::regex_match(g_pXWaylandManager->getTitle(finalFound), exc);
}
// check if it's the window we want & not exempt from getting swallowed
if (valid) {
// swallow
PWINDOW->m_pSwallowed = finalFound;
@@ -559,21 +589,27 @@ void Events::listener_mapWindow(void* owner, void* data) {
auto workspaceID = requestedWorkspace != "" ? requestedWorkspace : PWORKSPACE->m_szName;
g_pEventManager->postEvent(
SHyprIPCEvent{"openwindow", getFormat("%x,%s,%s,%s", PWINDOW, workspaceID.c_str(), g_pXWaylandManager->getAppIDClass(PWINDOW).c_str(), PWINDOW->m_szTitle.c_str())});
SHyprIPCEvent{"openwindow", getFormat("%lx,%s,%s,%s", PWINDOW, workspaceID.c_str(), g_pXWaylandManager->getAppIDClass(PWINDOW).c_str(), PWINDOW->m_szTitle.c_str())});
EMIT_HOOK_EVENT("openWindow", PWINDOW);
// recalc the values for this window
g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW);
g_pProtocolManager->m_pFractionalScaleProtocolManager->setPreferredScaleForSurface(g_pXWaylandManager->getWindowSurface(PWINDOW), PMONITOR->scale);
g_pProtocolManager->m_pFractionalScaleProtocolManager->setPreferredScaleForSurface(PWINDOW->m_pWLSurface.wlr(), PMONITOR->scale);
}
void Events::listener_unmapWindow(void* owner, void* data) {
CWindow* PWINDOW = (CWindow*)owner;
Debug::log(LOG, "Window %x unmapped (class %s)", PWINDOW, g_pXWaylandManager->getAppIDClass(PWINDOW).c_str());
Debug::log(LOG, "Window %lx unmapped (class %s)", PWINDOW, g_pXWaylandManager->getAppIDClass(PWINDOW).c_str());
g_pEventManager->postEvent(SHyprIPCEvent{"closewindow", getFormat("%x", PWINDOW)});
if (!PWINDOW->m_pWLSurface.exists() || !PWINDOW->m_bIsMapped) {
Debug::log(WARN, "Window %lx unmapped without being mapped??", PWINDOW);
PWINDOW->m_bFadingOut = false;
return;
}
g_pEventManager->postEvent(SHyprIPCEvent{"closewindow", getFormat("%lx", PWINDOW)});
EMIT_HOOK_EVENT("closeWindow", PWINDOW);
g_pProtocolManager->m_pToplevelExportProtocolManager->onWindowUnmap(PWINDOW);
@@ -618,6 +654,8 @@ void Events::listener_unmapWindow(void* owner, void* data) {
wasLastWindow = true;
g_pCompositor->m_pLastWindow = nullptr;
g_pCompositor->m_pLastFocus = nullptr;
g_pInputManager->releaseAllMouseButtons();
}
PWINDOW->m_bMappedX11 = false;
@@ -637,7 +675,7 @@ void Events::listener_unmapWindow(void* owner, void* data) {
if (wasLastWindow) {
const auto PWINDOWCANDIDATE = g_pLayoutManager->getCurrentLayout()->getNextWindowCandidate(PWINDOW);
Debug::log(LOG, "On closed window, new focused candidate is %x", PWINDOWCANDIDATE);
Debug::log(LOG, "On closed window, new focused candidate is %lx", PWINDOWCANDIDATE);
if (PWINDOWCANDIDATE != g_pCompositor->m_pLastWindow) {
if (!PWINDOWCANDIDATE)
@@ -651,7 +689,7 @@ void Events::listener_unmapWindow(void* owner, void* data) {
Debug::log(LOG, "Unmapped was not focused, ignoring a refocus.");
}
Debug::log(LOG, "Destroying the SubSurface tree of unmapped window %x", PWINDOW);
Debug::log(LOG, "Destroying the SubSurface tree of unmapped window %lx", PWINDOW);
SubsurfaceTree::destroySurfaceTree(PWINDOW->m_pSurfaceTree);
PWINDOW->m_pSurfaceTree = nullptr;
@@ -665,8 +703,10 @@ void Events::listener_unmapWindow(void* owner, void* data) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
// do the animation thing
PWINDOW->m_vOriginalClosedPos = PWINDOW->m_vRealPosition.vec() - PMONITOR->vecPosition;
PWINDOW->m_vOriginalClosedSize = PWINDOW->m_vRealSize.vec();
if (PMONITOR) {
PWINDOW->m_vOriginalClosedPos = PWINDOW->m_vRealPosition.vec() - PMONITOR->vecPosition;
PWINDOW->m_vOriginalClosedSize = PWINDOW->m_vRealSize.vec();
}
if (!PWINDOW->m_bX11DoesntWantBorders) // don't animate out if they weren't animated in.
PWINDOW->m_vRealPosition = PWINDOW->m_vRealPosition.vec() + Vector2D(0.01f, 0.01f); // it has to be animated, otherwise onWindowPostCreateClose will ignore it
@@ -694,15 +734,17 @@ void Events::listener_commitWindow(void* owner, void* data) {
if (!PWINDOW->m_bMappedX11 || PWINDOW->isHidden() || (PWINDOW->m_bIsX11 && !PWINDOW->m_bMappedX11))
return;
g_pHyprRenderer->damageSurface(g_pXWaylandManager->getWindowSurface(PWINDOW), PWINDOW->m_vRealPosition.goalv().x, PWINDOW->m_vRealPosition.goalv().y);
PWINDOW->updateSurfaceOutputs();
// Debug::log(LOG, "Window %x committed", PWINDOW); // SPAM!
g_pHyprRenderer->damageSurface(PWINDOW->m_pWLSurface.wlr(), PWINDOW->m_vRealPosition.goalv().x, PWINDOW->m_vRealPosition.goalv().y);
// Debug::log(LOG, "Window %lx committed", PWINDOW); // SPAM!
}
void Events::listener_destroyWindow(void* owner, void* data) {
CWindow* PWINDOW = (CWindow*)owner;
Debug::log(LOG, "Window %x destroyed, queueing. (class %s)", PWINDOW, g_pXWaylandManager->getAppIDClass(PWINDOW).c_str());
Debug::log(LOG, "Window %lx destroyed, queueing. (class %s)", PWINDOW, g_pXWaylandManager->getAppIDClass(PWINDOW).c_str());
if (PWINDOW->m_bIsX11)
Debug::log(LOG, "XWayland class raw: %s", PWINDOW->m_uSurface.xwayland->_class);
@@ -721,7 +763,7 @@ void Events::listener_destroyWindow(void* owner, void* data) {
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(PWINDOW);
if (PWINDOW->m_pSurfaceTree) {
Debug::log(LOG, "Destroying Subsurface tree of %x in destroyWindow", PWINDOW);
Debug::log(LOG, "Destroying Subsurface tree of %lx in destroyWindow", PWINDOW);
SubsurfaceTree::destroySurfaceTree(PWINDOW->m_pSurfaceTree);
PWINDOW->m_pSurfaceTree = nullptr;
}
@@ -730,7 +772,7 @@ void Events::listener_destroyWindow(void* owner, void* data) {
if (!PWINDOW->m_bFadingOut) {
g_pCompositor->removeWindowFromVectorSafe(PWINDOW); // most likely X11 unmanaged or sumn
Debug::log(LOG, "Unmapped window %x removed instantly", PWINDOW);
Debug::log(LOG, "Unmapped window %lx removed instantly", PWINDOW);
}
}
@@ -744,7 +786,7 @@ void Events::listener_setTitleWindow(void* owner, void* data) {
if (PWINDOW == g_pCompositor->m_pLastWindow) { // if it's the active, let's post an event to update others
g_pEventManager->postEvent(SHyprIPCEvent{"activewindow", g_pXWaylandManager->getAppIDClass(PWINDOW) + "," + PWINDOW->m_szTitle});
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", getFormat("%x", PWINDOW)});
g_pEventManager->postEvent(SHyprIPCEvent{"activewindowv2", getFormat("%lx", PWINDOW)});
EMIT_HOOK_EVENT("activeWindow", PWINDOW);
}
@@ -752,7 +794,7 @@ void Events::listener_setTitleWindow(void* owner, void* data) {
g_pCompositor->updateWindowAnimatedDecorationValues(PWINDOW);
PWINDOW->updateToplevel();
Debug::log(LOG, "Window %x set title to %s", PWINDOW, PWINDOW->m_szTitle.c_str());
Debug::log(LOG, "Window %lx set title to %s", PWINDOW, PWINDOW->m_szTitle.c_str());
}
void Events::listener_fullscreenWindow(void* owner, void* data) {
@@ -788,7 +830,7 @@ void Events::listener_fullscreenWindow(void* owner, void* data) {
}
}
// Disable the maximize flag when we recieve a de-fullscreen request
// Disable the maximize flag when we receive a de-fullscreen request
PWINDOW->m_bWasMaximized &= REQUESTED->fullscreen;
requestedFullState = REQUESTED->fullscreen;
@@ -811,7 +853,7 @@ void Events::listener_fullscreenWindow(void* owner, void* data) {
PWINDOW->updateToplevel();
Debug::log(LOG, "Window %x fullscreen to %i", PWINDOW, PWINDOW->m_bIsFullscreen);
Debug::log(LOG, "Window %lx fullscreen to %i", PWINDOW, PWINDOW->m_bIsFullscreen);
}
void Events::listener_activateXDG(wl_listener* listener, void* data) {
@@ -819,7 +861,7 @@ void Events::listener_activateXDG(wl_listener* listener, void* data) {
static auto* const PFOCUSONACTIVATE = &g_pConfigManager->getConfigValuePtr("misc:focus_on_activate")->intValue;
Debug::log(LOG, "Activate request for surface at %x", E->surface);
Debug::log(LOG, "Activate request for surface at %lx", E->surface);
if (!wlr_xdg_surface_try_from_wlr_surface(E->surface))
return;
@@ -829,7 +871,7 @@ void Events::listener_activateXDG(wl_listener* listener, void* data) {
if (!PWINDOW || PWINDOW == g_pCompositor->m_pLastWindow)
return;
g_pEventManager->postEvent(SHyprIPCEvent{"urgent", getFormat("%x", PWINDOW)});
g_pEventManager->postEvent(SHyprIPCEvent{"urgent", getFormat("%lx", PWINDOW)});
EMIT_HOOK_EVENT("urgent", PWINDOW);
PWINDOW->m_bIsUrgent = true;
@@ -855,11 +897,11 @@ void Events::listener_activateX11(void* owner, void* data) {
static auto* const PFOCUSONACTIVATE = &g_pConfigManager->getConfigValuePtr("misc:focus_on_activate")->intValue;
Debug::log(LOG, "X11 Activate request for window %x", PWINDOW);
Debug::log(LOG, "X11 Activate request for window %lx", PWINDOW);
if (PWINDOW->m_iX11Type == 2) {
Debug::log(LOG, "Unmanaged X11 %x requests activate", PWINDOW);
Debug::log(LOG, "Unmanaged X11 %lx requests activate", PWINDOW);
if (g_pCompositor->m_pLastWindow && g_pCompositor->m_pLastWindow->getPID() != PWINDOW->getPID())
return;
@@ -871,7 +913,7 @@ void Events::listener_activateX11(void* owner, void* data) {
if (PWINDOW == g_pCompositor->m_pLastWindow)
return;
g_pEventManager->postEvent(SHyprIPCEvent{"urgent", getFormat("%x", PWINDOW)});
g_pEventManager->postEvent(SHyprIPCEvent{"urgent", getFormat("%lx", PWINDOW)});
EMIT_HOOK_EVENT("urgent", PWINDOW);
if (!*PFOCUSONACTIVATE)
@@ -952,7 +994,7 @@ void Events::listener_unmanagedSetGeometry(void* owner, void* data) {
if (abs(std::floor(POS.x) - PWINDOW->m_uSurface.xwayland->x) > 2 || abs(std::floor(POS.y) - PWINDOW->m_uSurface.xwayland->y) > 2 ||
abs(std::floor(SIZ.x) - PWINDOW->m_uSurface.xwayland->width) > 2 || abs(std::floor(SIZ.y) - PWINDOW->m_uSurface.xwayland->height) > 2) {
Debug::log(LOG, "Unmanaged window %x requests geometry update to %i %i %i %i", PWINDOW, (int)PWINDOW->m_uSurface.xwayland->x, (int)PWINDOW->m_uSurface.xwayland->y,
Debug::log(LOG, "Unmanaged window %lx requests geometry update to %i %i %i %i", PWINDOW, (int)PWINDOW->m_uSurface.xwayland->x, (int)PWINDOW->m_uSurface.xwayland->y,
(int)PWINDOW->m_uSurface.xwayland->width, (int)PWINDOW->m_uSurface.xwayland->height);
g_pHyprRenderer->damageWindow(PWINDOW);
@@ -982,7 +1024,7 @@ void Events::listener_surfaceXWayland(wl_listener* listener, void* data) {
Debug::log(LOG, "New XWayland Surface created (class %s).", XWSURFACE->_class);
if (XWSURFACE->parent)
Debug::log(LOG, "Window parent data: %s at %x", XWSURFACE->parent->_class, XWSURFACE->parent);
Debug::log(LOG, "Window parent data: %s at %lx", XWSURFACE->parent->_class, XWSURFACE->parent);
const auto PNEWWINDOW = (CWindow*)g_pCompositor->m_vWindows.emplace_back(std::make_unique<CWindow>()).get();
@@ -993,7 +1035,6 @@ void Events::listener_surfaceXWayland(wl_listener* listener, void* data) {
PNEWWINDOW->m_pX11Parent = g_pCompositor->getX11Parent(PNEWWINDOW);
PNEWWINDOW->hyprListener_mapWindow.initCallback(&XWSURFACE->events.map, &Events::listener_mapWindow, PNEWWINDOW, "XWayland Window");
PNEWWINDOW->hyprListener_unmapWindow.initCallback(&XWSURFACE->events.unmap, &Events::listener_unmapWindow, PNEWWINDOW, "XWayland Window");
PNEWWINDOW->hyprListener_destroyWindow.initCallback(&XWSURFACE->events.destroy, &Events::listener_destroyWindow, PNEWWINDOW, "XWayland Window");
PNEWWINDOW->hyprListener_setOverrideRedirect.initCallback(&XWSURFACE->events.set_override_redirect, &Events::listener_setOverrideRedirect, PNEWWINDOW, "XWayland Window");
PNEWWINDOW->hyprListener_configureX11.initCallback(&XWSURFACE->events.request_configure, &Events::listener_configureX11, PNEWWINDOW, "XWayland Window");
@@ -1012,7 +1053,6 @@ void Events::listener_newXDGSurface(wl_listener* listener, void* data) {
PNEWWINDOW->m_uSurface.xdg = XDGSURFACE;
PNEWWINDOW->hyprListener_mapWindow.initCallback(&XDGSURFACE->events.map, &Events::listener_mapWindow, PNEWWINDOW, "XDG Window");
PNEWWINDOW->hyprListener_unmapWindow.initCallback(&XDGSURFACE->events.unmap, &Events::listener_unmapWindow, PNEWWINDOW, "XDG Window");
PNEWWINDOW->hyprListener_destroyWindow.initCallback(&XDGSURFACE->events.destroy, &Events::listener_destroyWindow, PNEWWINDOW, "XDG Window");
}
@@ -1027,7 +1067,7 @@ void Events::listener_requestMaximize(void* owner, void* data) {
if (PWINDOW->m_bNoFullscreenRequest)
return;
Debug::log(LOG, "Maximize request for %x", PWINDOW);
Debug::log(LOG, "Maximize request for %lx", PWINDOW);
if (!PWINDOW->m_bIsX11) {
const auto EV = (wlr_foreign_toplevel_handle_v1_maximized_event*)data;
@@ -1046,7 +1086,7 @@ void Events::listener_requestMaximize(void* owner, void* data) {
void Events::listener_requestMinimize(void* owner, void* data) {
const auto PWINDOW = (CWindow*)owner;
Debug::log(LOG, "Minimize request for %x", PWINDOW);
Debug::log(LOG, "Minimize request for %lx", PWINDOW);
if (PWINDOW->m_bIsX11) {
if (!PWINDOW->m_bMappedX11 || PWINDOW->m_iX11Type != 1)
@@ -1054,13 +1094,13 @@ void Events::listener_requestMinimize(void* owner, void* data) {
const auto E = (wlr_xwayland_minimize_event*)data;
g_pEventManager->postEvent({"minimize", getFormat("%x,%i", PWINDOW, (int)E->minimize)});
g_pEventManager->postEvent({"minimize", getFormat("%lx,%i", PWINDOW, (int)E->minimize)});
EMIT_HOOK_EVENT("minimize", (std::vector<void*>{PWINDOW, (void*)E->minimize}));
wlr_xwayland_surface_set_minimized(PWINDOW->m_uSurface.xwayland, E->minimize && g_pCompositor->m_pLastWindow != PWINDOW); // fucking DXVK
} else {
const auto E = (wlr_foreign_toplevel_handle_v1_minimized_event*)data;
g_pEventManager->postEvent({"minimize", getFormat("%x,%i", PWINDOW, E ? (int)E->minimized : 1)});
g_pEventManager->postEvent({"minimize", getFormat("%lx,%i", PWINDOW, E ? (int)E->minimized : 1)});
EMIT_HOOK_EVENT("minimize", (std::vector<void*>{PWINDOW, (void*)(E ? (uint64_t)E->minimized : 1)}));
}
}

View File

@@ -6,11 +6,11 @@
#include <iomanip>
#include <sstream>
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__)
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
#include <sys/sysctl.h>
#if defined(__DragonFly__)
#include <sys/kinfo.h> // struct kinfo_proc
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#elif defined(__FreeBSD__)
#include <sys/user.h> // struct kinfo_proc
#endif
@@ -23,7 +23,7 @@
#endif
#if defined(__DragonFly__)
#define KP_PPID(kp) kp.kp_ppid
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#elif defined(__FreeBSD__)
#define KP_PPID(kp) kp.ki_ppid
#else
#define KP_PPID(kp) kp.p_ppid
@@ -149,7 +149,7 @@ void addWLSignal(wl_signal* pSignal, wl_listener* pListener, void* pOwner, const
wl_signal_add(pSignal, pListener);
Debug::log(LOG, "Registered signal for owner %x: %x -> %x (owner: %s)", pOwner, pSignal, pListener, ownerString.c_str());
Debug::log(LOG, "Registered signal for owner %lx: %lx -> %lx (owner: %s)", pOwner, pSignal, pListener, ownerString.c_str());
}
void handleNoop(struct wl_listener* listener, void* data) {

View File

@@ -2,13 +2,33 @@
#include "../Compositor.hpp"
int ratHandler(void* data) {
g_pHyprRenderer->renderMonitor((CMonitor*)data);
return 1;
}
CMonitor::CMonitor() {
wlr_damage_ring_init(&damage);
}
CMonitor::~CMonitor() {
wlr_damage_ring_finish(&damage);
}
void CMonitor::onConnect(bool noRule) {
hyprListener_monitorDestroy.removeCallback();
hyprListener_monitorFrame.removeCallback();
hyprListener_monitorStateRequest.removeCallback();
hyprListener_monitorDamage.removeCallback();
hyprListener_monitorNeedsFrame.removeCallback();
hyprListener_monitorCommit.removeCallback();
hyprListener_monitorFrame.initCallback(&output->events.frame, &Events::listener_monitorFrame, this);
hyprListener_monitorDestroy.initCallback(&output->events.destroy, &Events::listener_monitorDestroy, this);
hyprListener_monitorStateRequest.initCallback(&output->events.request_state, &Events::listener_monitorStateRequest, this);
hyprListener_monitorDamage.initCallback(&output->events.damage, &Events::listener_monitorDamage, this);
hyprListener_monitorNeedsFrame.initCallback(&output->events.needs_frame, &Events::listener_monitorNeedsFrame, this);
hyprListener_monitorCommit.initCallback(&output->events.commit, &Events::listener_monitorCommit, this);
if (m_bEnabled) {
wlr_output_enable(output, 1);
@@ -108,13 +128,13 @@ void CMonitor::onConnect(bool noRule) {
if (!noRule)
g_pHyprRenderer->applyMonitorRule(this, &monitorRule, true);
wlr_damage_ring_set_bounds(&damage, vecTransformedSize.x, vecTransformedSize.y);
wlr_xcursor_manager_load(g_pCompositor->m_sWLRXCursorMgr, scale);
Debug::log(LOG, "Added new monitor with name %s at %i,%i with size %ix%i, pointer %x", output->name, (int)vecPosition.x, (int)vecPosition.y, (int)vecPixelSize.x,
Debug::log(LOG, "Added new monitor with name %s at %i,%i with size %ix%i, pointer %lx", output->name, (int)vecPosition.x, (int)vecPosition.y, (int)vecPixelSize.x,
(int)vecPixelSize.y, output);
damage = wlr_output_damage_create(output);
// add a WLR workspace group
if (!pWLRWorkspaceGroupHandle) {
pWLRWorkspaceGroupHandle = wlr_ext_workspace_group_handle_v1_create(g_pCompositor->m_sWLREXTWorkspaceMgr);
@@ -124,7 +144,17 @@ void CMonitor::onConnect(bool noRule) {
setupDefaultWS(monitorRule);
for (auto& ws : g_pCompositor->m_vWorkspaces) {
if (ws->m_szLastMonitor == szName) {
g_pCompositor->moveWorkspaceToMonitor(ws.get(), this);
ws->startAnim(true, true, true);
ws->m_szLastMonitor = "";
}
}
scale = monitorRule.scale;
if (scale < 0.1)
scale = getDefaultScale();
m_pThisWrap = nullptr;
@@ -156,10 +186,17 @@ void CMonitor::onConnect(bool noRule) {
if (!found)
g_pCompositor->setActiveMonitor(this);
renderTimer = wl_event_loop_add_timer(g_pCompositor->m_sWLEventLoop, ratHandler, this);
}
void CMonitor::onDisconnect() {
if (renderTimer) {
wl_event_source_remove(renderTimer);
renderTimer = nullptr;
}
if (!m_bEnabled || g_pCompositor->m_bIsShuttingDown)
return;
@@ -192,6 +229,9 @@ void CMonitor::onDisconnect() {
m_bRenderingInitPassed = false;
hyprListener_monitorFrame.removeCallback();
hyprListener_monitorDamage.removeCallback();
hyprListener_monitorNeedsFrame.removeCallback();
hyprListener_monitorCommit.removeCallback();
for (size_t i = 0; i < 4; ++i) {
for (auto& ls : m_aLayerSurfaceLayers[i]) {
@@ -214,6 +254,8 @@ void CMonitor::onDisconnect() {
g_pCompositor->m_bUnsafeState = true;
std::erase_if(g_pCompositor->m_vMonitors, [&](std::shared_ptr<CMonitor>& el) { return el.get() == this; });
return;
}
@@ -230,14 +272,13 @@ void CMonitor::onDisconnect() {
}
for (auto& w : wspToMove) {
w->m_szLastMonitor = szName;
g_pCompositor->moveWorkspaceToMonitor(w, BACKUPMON);
w->startAnim(true, true, true);
}
activeWorkspace = -1;
wlr_output_damage_destroy(damage);
wlr_output_layout_remove(g_pCompositor->m_sWLROutputLayout, output);
wlr_output_enable(output, false);
@@ -266,12 +307,26 @@ void CMonitor::onDisconnect() {
std::erase_if(g_pCompositor->m_vMonitors, [&](std::shared_ptr<CMonitor>& el) { return el.get() == this; });
}
void CMonitor::addDamage(pixman_region32_t* rg) {
wlr_output_damage_add(damage, rg);
void CMonitor::addDamage(const pixman_region32_t* rg) {
static auto* const PZOOMFACTOR = &g_pConfigManager->getConfigValuePtr("misc:cursor_zoom_factor")->floatValue;
if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == this) {
wlr_damage_ring_add_whole(&damage);
g_pCompositor->scheduleFrameForMonitor(this);
}
if (wlr_damage_ring_add(&damage, rg))
g_pCompositor->scheduleFrameForMonitor(this);
}
void CMonitor::addDamage(wlr_box* box) {
wlr_output_damage_add_box(damage, box);
void CMonitor::addDamage(const wlr_box* box) {
static auto* const PZOOMFACTOR = &g_pConfigManager->getConfigValuePtr("misc:cursor_zoom_factor")->floatValue;
if (*PZOOMFACTOR != 1.f && g_pCompositor->getMonitorFromCursor() == this) {
wlr_damage_ring_add_whole(&damage);
g_pCompositor->scheduleFrameForMonitor(this);
}
if (wlr_damage_ring_add_box(&damage, box))
g_pCompositor->scheduleFrameForMonitor(this);
}
bool CMonitor::isMirror() {
@@ -295,13 +350,15 @@ int CMonitor::findAvailableDefaultWS() {
void CMonitor::setupDefaultWS(const SMonitorRule& monitorRule) {
// Workspace
std::string newDefaultWorkspaceName = "";
int64_t WORKSPACEID = monitorRule.defaultWorkspace == "" ? findAvailableDefaultWS() : getWorkspaceIDFromString(monitorRule.defaultWorkspace, newDefaultWorkspaceName);
int64_t WORKSPACEID = g_pConfigManager->getDefaultWorkspaceFor(szName).empty() ?
findAvailableDefaultWS() :
getWorkspaceIDFromString(g_pConfigManager->getDefaultWorkspaceFor(szName), newDefaultWorkspaceName);
if (WORKSPACEID == INT_MAX || (WORKSPACEID >= SPECIAL_WORKSPACE_START && WORKSPACEID <= -2)) {
WORKSPACEID = g_pCompositor->m_vWorkspaces.size() + 1;
newDefaultWorkspaceName = std::to_string(WORKSPACEID);
Debug::log(LOG, "Invalid workspace= directive name in monitor parsing, workspace name \"%s\" is invalid.", monitorRule.defaultWorkspace.c_str());
Debug::log(LOG, "Invalid workspace= directive name in monitor parsing, workspace name \"%s\" is invalid.", g_pConfigManager->getDefaultWorkspaceFor(szName).c_str());
}
auto PNEWWORKSPACE = g_pCompositor->getWorkspaceByID(WORKSPACEID);
@@ -330,6 +387,7 @@ void CMonitor::setupDefaultWS(const SMonitorRule& monitorRule) {
g_pCompositor->deactivateAllWLRWorkspaces(PNEWWORKSPACE->m_pWlrHandle);
PNEWWORKSPACE->setActive(true);
PNEWWORKSPACE->m_szLastMonitor = "";
}
void CMonitor::setMirror(const std::string& mirrorOf) {
@@ -439,3 +497,98 @@ float CMonitor::getDefaultScale() {
return 1.5;
return 1;
}
void CMonitor::changeWorkspace(CWorkspace* const pWorkspace, bool internal) {
if (!pWorkspace)
return;
if (pWorkspace->m_bIsSpecialWorkspace) {
Debug::log(ERR, "BUG THIS: Attempted to changeWorkspace to special!");
return;
}
if (pWorkspace->m_iID == activeWorkspace) {
// in some cases (e.g. workspace from one monitor to another)
// we need to send this
g_pCompositor->deactivateAllWLRWorkspaces(pWorkspace->m_pWlrHandle);
pWorkspace->setActive(true);
return;
}
const auto POLDWORKSPACE = g_pCompositor->getWorkspaceByID(activeWorkspace);
activeWorkspace = pWorkspace->m_iID;
if (!internal) {
const auto ANIMTOLEFT = pWorkspace->m_iID > POLDWORKSPACE->m_iID;
POLDWORKSPACE->startAnim(false, ANIMTOLEFT);
pWorkspace->startAnim(true, ANIMTOLEFT);
// move pinned windows
for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_iWorkspaceID == POLDWORKSPACE->m_iID && w->m_bPinned) {
w->m_iWorkspaceID = pWorkspace->m_iID;
}
}
if (const auto PLASTWINDOW = pWorkspace->getLastFocusedWindow(); PLASTWINDOW)
g_pCompositor->focusWindow(PLASTWINDOW);
else {
g_pCompositor->focusWindow(nullptr);
g_pInputManager->refocus();
}
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID);
// set some flags and fire event
g_pCompositor->deactivateAllWLRWorkspaces(pWorkspace->m_pWlrHandle);
pWorkspace->setActive(true);
g_pEventManager->postEvent(SHyprIPCEvent{"workspace", pWorkspace->m_szName});
EMIT_HOOK_EVENT("workspace", pWorkspace);
}
g_pHyprRenderer->damageMonitor(this);
g_pCompositor->updateFullscreenFadeOnWorkspace(pWorkspace);
}
void CMonitor::changeWorkspace(const int& id, bool internal) {
changeWorkspace(g_pCompositor->getWorkspaceByID(id), internal);
}
void CMonitor::setSpecialWorkspace(CWorkspace* const pWorkspace) {
g_pHyprRenderer->damageMonitor(this);
if (!pWorkspace) {
// remove special if exists
if (const auto EXISTINGSPECIAL = g_pCompositor->getWorkspaceByID(specialWorkspaceID); EXISTINGSPECIAL)
EXISTINGSPECIAL->startAnim(false, false);
specialWorkspaceID = 0;
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID);
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(activeWorkspace);
if (const auto PLAST = PWORKSPACE->getLastFocusedWindow(); PLAST)
g_pCompositor->focusWindow(PLAST);
else
g_pInputManager->refocus();
return;
}
// open special
pWorkspace->m_iMonitorID = ID;
specialWorkspaceID = pWorkspace->m_iID;
pWorkspace->startAnim(true, true);
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(ID);
if (const auto PLAST = pWorkspace->getLastFocusedWindow(); PLAST)
g_pCompositor->focusWindow(PLAST);
else
g_pInputManager->refocus();
}
void CMonitor::setSpecialWorkspace(const int& id) {
setSpecialWorkspace(g_pCompositor->getWorkspaceByID(id));
}

View File

@@ -6,11 +6,15 @@
#include <vector>
#include <array>
#include <memory>
#include "Timer.hpp"
struct SMonitorRule;
class CMonitor {
public:
CMonitor();
~CMonitor();
Vector2D vecPosition = Vector2D(-1, -1); // means unset
Vector2D vecSize = Vector2D(0, 0);
Vector2D vecPixelSize = Vector2D(0, 0);
@@ -28,9 +32,9 @@ class CMonitor {
Vector2D vecReservedBottomRight = Vector2D(0, 0);
// WLR stuff
wlr_damage_ring damage;
wlr_output* output = nullptr;
float refreshRate = 60;
wlr_output_damage* damage = nullptr;
int framesToSkip = 0;
int forceFullFrames = 0;
bool noFrameSchedule = false;
@@ -45,6 +49,10 @@ class CMonitor {
bool pendingFrame = false; // if we schedule a frame during rendering, reschedule it after
bool renderingActive = false;
wl_event_source* renderTimer = nullptr; // for RAT
bool RATScheduled = false;
CTimer lastPresentationTimer;
// mirroring
CMonitor* pMirrorOf = nullptr;
std::vector<CMonitor*> mirrors;
@@ -57,6 +65,9 @@ class CMonitor {
DYNLISTENER(monitorFrame);
DYNLISTENER(monitorDestroy);
DYNLISTENER(monitorStateRequest);
DYNLISTENER(monitorDamage);
DYNLISTENER(monitorNeedsFrame);
DYNLISTENER(monitorCommit);
// hack: a group = workspaces on a monitor.
// I don't really care lol :P
@@ -65,11 +76,15 @@ class CMonitor {
// methods
void onConnect(bool noRule);
void onDisconnect();
void addDamage(pixman_region32_t* rg);
void addDamage(wlr_box* box);
void addDamage(const pixman_region32_t* rg);
void addDamage(const wlr_box* box);
void setMirror(const std::string&);
bool isMirror();
float getDefaultScale();
void changeWorkspace(CWorkspace* const pWorkspace, bool internal = false);
void changeWorkspace(const int& id, bool internal = false);
void setSpecialWorkspace(CWorkspace* const pWorkspace);
void setSpecialWorkspace(const int& id);
std::shared_ptr<CMonitor>* m_pThisWrap = nullptr;
bool m_bEnabled = false;

View File

@@ -3,8 +3,11 @@
#include "../Compositor.hpp"
void addSurfaceGlobalOffset(SSurfaceTreeNode* node, int* lx, int* ly) {
*lx += node->pSurface->current.dx;
*ly += node->pSurface->current.dy;
if (!node->pSurface || !node->pSurface->exists())
return;
*lx += node->pSurface->wlr()->current.dx;
*ly += node->pSurface->wlr()->current.dy;
if (node->offsetfn) {
// This is the root node
@@ -23,7 +26,13 @@ void addSurfaceGlobalOffset(SSurfaceTreeNode* node, int* lx, int* ly) {
SSurfaceTreeNode* createTree(wlr_surface* pSurface, CWindow* pWindow) {
const auto PNODE = &SubsurfaceTree::surfaceTreeNodes.emplace_back();
PNODE->pSurface = pSurface;
if (pSurface->data)
PNODE->pSurface = (CWLSurface*)pSurface->data;
else {
PNODE->pInternalSurface = pSurface;
PNODE->pSurface = &PNODE->pInternalSurface;
}
PNODE->pWindowOwner = pWindow;
PNODE->hyprListener_newSubsurface.initCallback(&pSurface->events.new_subsurface, &Events::listener_newSubsurfaceNode, PNODE, "SurfaceTreeNode");
@@ -46,7 +55,7 @@ SSurfaceTreeNode* createSubsurfaceNode(SSurfaceTreeNode* pParent, SSubsurface* p
PNODE->pParent = pParent;
PNODE->pSubsurface = pSubsurface;
Debug::log(LOG, "Creating a subsurface Node! (pWindow: %x)", pWindow);
Debug::log(LOG, "Creating a subsurface Node! (pWindow: %lx)", pWindow);
return PNODE;
}
@@ -54,7 +63,7 @@ SSurfaceTreeNode* createSubsurfaceNode(SSurfaceTreeNode* pParent, SSubsurface* p
SSurfaceTreeNode* SubsurfaceTree::createTreeRoot(wlr_surface* pSurface, applyGlobalOffsetFn fn, void* data, CWindow* pWindow) {
const auto PNODE = createTree(pSurface, pWindow);
Debug::log(LOG, "Creating a surfaceTree Root! (pWindow: %x)", pWindow);
Debug::log(LOG, "Creating a surfaceTree Root! (pWindow: %lx)", pWindow);
PNODE->offsetfn = fn;
PNODE->globalOffsetData = data;
@@ -74,7 +83,7 @@ void SubsurfaceTree::destroySurfaceTree(SSurfaceTreeNode* pNode) {
}
if (!exists) {
Debug::log(ERR, "Tried to remove a SurfaceTreeNode that doesn't exist?? (Node %x)", pNode);
Debug::log(ERR, "Tried to remove a SurfaceTreeNode that doesn't exist?? (Node %lx)", pNode);
return;
}
@@ -88,9 +97,9 @@ void SubsurfaceTree::destroySurfaceTree(SSurfaceTreeNode* pNode) {
pNode->hyprListener_newSubsurface.removeCallback();
// damage
if (pNode->pSurface) {
if (pNode->pSurface && pNode->pSurface->exists()) {
wlr_box extents = {};
wlr_surface_get_extends(pNode->pSurface, &extents);
wlr_surface_get_extends(pNode->pSurface->wlr(), &extents);
int lx = 0, ly = 0;
addSurfaceGlobalOffset(pNode, &lx, &ly);
@@ -136,7 +145,7 @@ void Events::listener_newSubsurfaceNode(void* owner, void* data) {
const auto PNEWSUBSURFACE = &pNode->childSubsurfaces.emplace_back();
Debug::log(LOG, "Added a new subsurface %x", PSUBSURFACE);
Debug::log(LOG, "Added a new subsurface %lx", PSUBSURFACE);
PNEWSUBSURFACE->pSubsurface = PSUBSURFACE;
PNEWSUBSURFACE->pParent = pNode;
@@ -165,7 +174,7 @@ void Events::listener_mapSubsurface(void* owner, void* data) {
if (subsurface->pChild)
return;
Debug::log(LOG, "Subsurface %x mapped", subsurface->pSubsurface);
Debug::log(LOG, "Subsurface %lx mapped", subsurface->pSubsurface);
subsurface->pChild = createSubsurfaceNode(subsurface->pParent, subsurface, subsurface->pSubsurface->surface, subsurface->pWindowOwner);
}
@@ -173,7 +182,10 @@ void Events::listener_mapSubsurface(void* owner, void* data) {
void Events::listener_unmapSubsurface(void* owner, void* data) {
SSubsurface* subsurface = (SSubsurface*)owner;
Debug::log(LOG, "Subsurface %x unmapped", subsurface);
Debug::log(LOG, "Subsurface %lx unmapped", subsurface);
if (subsurface->pSubsurface->surface == g_pCompositor->m_pLastFocus)
g_pInputManager->releaseAllMouseButtons();
if (subsurface->pChild) {
const auto PNODE = subsurface->pChild;
@@ -182,13 +194,14 @@ void Events::listener_unmapSubsurface(void* owner, void* data) {
std::find_if(SubsurfaceTree::surfaceTreeNodes.begin(), SubsurfaceTree::surfaceTreeNodes.end(), [&](const SSurfaceTreeNode& other) { return &other == PNODE; });
if (IT != SubsurfaceTree::surfaceTreeNodes.end()) {
int lx = 0, ly = 0;
addSurfaceGlobalOffset(PNODE, &lx, &ly);
if (PNODE->pSurface && PNODE->pSurface->exists()) {
int lx = 0, ly = 0;
addSurfaceGlobalOffset(PNODE, &lx, &ly);
wlr_box extents = {lx, ly, 0, 0};
if (PNODE->pSurface) {
extents.width = PNODE->pSurface->current.width;
extents.height = PNODE->pSurface->current.height;
wlr_box extents = {lx, ly, 0, 0};
extents.width = PNODE->pSurface->wlr()->current.width;
extents.height = PNODE->pSurface->wlr()->current.height;
g_pHyprRenderer->damageBox(&extents);
}
@@ -208,7 +221,7 @@ void Events::listener_commitSubsurface(void* owner, void* data) {
if (!g_pHyprRenderer->shouldRenderWindow(pNode->pWindowOwner)) {
static auto* const PLOGDAMAGE = &g_pConfigManager->getConfigValuePtr("debug:log_damage")->intValue;
if (*PLOGDAMAGE)
Debug::log(LOG, "Refusing to commit damage from %x because it's invisible.", pNode->pWindowOwner);
Debug::log(LOG, "Refusing to commit damage from %lx because it's invisible.", pNode->pWindowOwner);
return;
}
@@ -228,7 +241,8 @@ void Events::listener_commitSubsurface(void* owner, void* data) {
}
}
g_pHyprRenderer->damageSurface(pNode->pSurface, lx, ly);
if (pNode->pSurface && pNode->pSurface->exists())
g_pHyprRenderer->damageSurface(pNode->pSurface->wlr(), lx, ly);
}
void Events::listener_destroySubsurface(void* owner, void* data) {
@@ -238,7 +252,7 @@ void Events::listener_destroySubsurface(void* owner, void* data) {
SubsurfaceTree::destroySurfaceTree(subsurface->pChild);
}
Debug::log(LOG, "Subsurface %x destroyed", subsurface);
Debug::log(LOG, "Subsurface %lx destroyed", subsurface);
subsurface->hyprListener_destroy.removeCallback();
subsurface->hyprListener_map.removeCallback();
@@ -250,7 +264,7 @@ void Events::listener_destroySubsurface(void* owner, void* data) {
void Events::listener_destroySubsurfaceNode(void* owner, void* data) {
SSurfaceTreeNode* pNode = (SSurfaceTreeNode*)owner;
Debug::log(LOG, "Subsurface Node %x destroyed", pNode);
Debug::log(LOG, "Subsurface Node %lx destroyed", pNode);
for (auto& c : pNode->childSubsurfaces)
destroySubsurface(&c);

View File

@@ -2,6 +2,7 @@
#include "../defines.hpp"
#include <list>
#include "WLSurface.hpp"
struct SSubsurface;
class CWindow;
@@ -9,7 +10,8 @@ class CWindow;
typedef void (*applyGlobalOffsetFn)(void*, int*, int*);
struct SSurfaceTreeNode {
wlr_surface* pSurface = nullptr;
CWLSurface* pSurface = nullptr; // actual surface
CWLSurface pInternalSurface; // not present for head nodes to not dupe wlr_surface ownership
DYNLISTENER(newSubsurface);
DYNLISTENER(commit);

View File

@@ -29,7 +29,7 @@ Vector2D Vector2D::floor() {
}
Vector2D Vector2D::clamp(const Vector2D& min, const Vector2D& max) {
return Vector2D(std::clamp(this->x, min.x, max.x == 0 ? INFINITY : max.x), std::clamp(this->y, min.y, max.y == 0 ? INFINITY : max.y));
return Vector2D(std::clamp(this->x, min.x, max.x < min.x ? INFINITY : max.x), std::clamp(this->y, min.y, max.y < min.y ? INFINITY : max.y));
}
double Vector2D::distance(const Vector2D& other) {

View File

@@ -5,4 +5,19 @@ SLayerSurface::SLayerSurface() {
alpha.create(AVARTYPE_FLOAT, g_pConfigManager->getAnimationPropertyConfig("fadeIn"), nullptr, AVARDAMAGE_ENTIRE);
alpha.m_pLayer = this;
alpha.registerVar();
}
void SLayerSurface::applyRules() {
noAnimations = false;
forceBlur = false;
ignoreZero = false;
for (auto& rule : g_pConfigManager->getMatchingRules(this)) {
if (rule.rule == "noanim")
noAnimations = true;
else if (rule.rule == "blur")
forceBlur = true;
else if (rule.rule == "ignorezero")
ignoreZero = true;
}
}

View File

@@ -6,6 +6,7 @@
#include "../Window.hpp"
#include "SubsurfaceTree.hpp"
#include "AnimatedVariable.hpp"
#include "WLSurface.hpp"
struct SLayerRule {
std::string targetNamespace = "";
@@ -15,9 +16,14 @@ struct SLayerRule {
struct SLayerSurface {
SLayerSurface();
void applyRules();
wlr_layer_surface_v1* layerSurface;
wl_list link;
CWLSurface surface;
std::list<CWLSurface> popupSurfaces;
DYNLISTENER(destroyLayerSurface);
DYNLISTENER(mapLayerSurface);
DYNLISTENER(unmapLayerSurface);
@@ -40,7 +46,8 @@ struct SLayerSurface {
bool noProcess = false;
bool noAnimations = false;
bool forceBlur = false;
bool forceBlur = false;
bool ignoreZero = false;
// For the list lookup
bool operator==(const SLayerSurface& rhs) const {
@@ -169,6 +176,7 @@ class CMonitor;
struct SXDGPopup {
CWindow* parentWindow = nullptr;
SLayerSurface* parentLS = nullptr;
SXDGPopup* parentPopup = nullptr;
wlr_xdg_popup* popup = nullptr;
CMonitor* monitor = nullptr;
@@ -358,6 +366,8 @@ struct STouchDevice {
struct SSwitchDevice {
wlr_input_device* pWlrDevice = nullptr;
int status = -1; // uninitialized
DYNLISTENER(destroy);
DYNLISTENER(toggle);

View File

@@ -25,7 +25,7 @@ CHyprWLListener::~CHyprWLListener() {
void CHyprWLListener::removeCallback() {
if (isConnected()) {
Debug::log(LOG, "Callback %x -> %x, %s removed.", &m_pCallback, &m_pOwner, m_szAuthor.c_str());
Debug::log(LOG, "Callback %lx -> %lx, %s removed.", &m_pCallback, &m_pOwner, m_szAuthor.c_str());
wl_list_remove(&m_swWrapper.m_sListener.link);
wl_list_init(&m_swWrapper.m_sListener.link);
}

57
src/helpers/WLSurface.cpp Normal file
View File

@@ -0,0 +1,57 @@
#include "WLSurface.hpp"
#include "../Compositor.hpp"
CWLSurface::CWLSurface(wlr_surface* pSurface) {
m_pWLRSurface = pSurface;
init();
}
void CWLSurface::assign(wlr_surface* pSurface) {
m_pWLRSurface = pSurface;
init();
}
void CWLSurface::unassign() {
destroy();
}
CWLSurface::~CWLSurface() {
destroy();
}
bool CWLSurface::exists() const {
return m_pWLRSurface;
}
wlr_surface* CWLSurface::wlr() const {
return m_pWLRSurface;
}
void CWLSurface::destroy() {
if (!m_pWLRSurface)
return;
hyprListener_destroy.removeCallback();
m_pWLRSurface->data = nullptr;
if (g_pCompositor->m_pLastFocus == m_pWLRSurface)
g_pCompositor->m_pLastFocus = nullptr;
m_pWLRSurface = nullptr;
Debug::log(LOG, "CWLSurface %lx called destroy()", this);
}
void CWLSurface::init() {
if (!m_pWLRSurface)
return;
RASSERT(!m_pWLRSurface->data, "Attempted to duplicate CWLSurface ownership!");
m_pWLRSurface->data = this;
hyprListener_destroy.initCallback(
&m_pWLRSurface->events.destroy, [&](void* owner, void* data) { destroy(); }, this, "CWLSurface");
Debug::log(LOG, "CWLSurface %lx called init()", this);
}

49
src/helpers/WLSurface.hpp Normal file
View File

@@ -0,0 +1,49 @@
#pragma once
#include "../defines.hpp"
class CWLSurface {
public:
CWLSurface() = default;
CWLSurface(wlr_surface* pSurface);
~CWLSurface();
void assign(wlr_surface* pSurface);
void unassign();
CWLSurface(const CWLSurface&) = delete;
CWLSurface(CWLSurface&&) = delete;
CWLSurface& operator=(const CWLSurface&) = delete;
CWLSurface& operator=(CWLSurface&&) = delete;
wlr_surface* wlr() const;
bool exists() const;
CWLSurface& operator=(wlr_surface* pSurface) {
destroy();
m_pWLRSurface = pSurface;
init();
return *this;
}
bool operator==(const CWLSurface& other) const {
return other.wlr() == wlr();
}
bool operator==(const wlr_surface* other) const {
return other == wlr();
}
explicit operator bool() const {
return exists();
}
private:
wlr_surface* m_pWLRSurface = nullptr;
void destroy();
void init();
DYNLISTENER(destroy);
};

View File

@@ -98,15 +98,6 @@ void CWorkspace::startAnim(bool in, bool left, bool instant) {
m_vRenderOffset.warp();
m_fAlpha.warp();
}
// check LS-es
if (in && !m_bIsSpecialWorkspace) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_iMonitorID);
for (auto& ls : PMONITOR->m_aLayerSurfaceLayers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) {
if (!ls->fadingOut)
ls->alpha = m_bHasFullscreenWindow && m_efFullscreenMode == FULLSCREEN_FULL ? 0.f : 1.f;
}
}
}
void CWorkspace::setActive(bool on) {
@@ -143,3 +134,30 @@ CWindow* CWorkspace::getLastFocusedWindow() {
return m_pLastFocusedWindow;
}
void CWorkspace::rememberPrevWorkspace(const CWorkspace* prev) {
if (!prev) {
m_sPrevWorkspace.iID = -1;
m_sPrevWorkspace.name = "";
return;
}
if (prev->m_sPrevWorkspace.iID == m_sPrevWorkspace.iID) {
Debug::log(LOG, "Tried to set prev workspace to the same as current one");
return;
}
m_sPrevWorkspace.iID = prev->m_iID;
m_sPrevWorkspace.name = prev->m_szName;
}
std::string CWorkspace::getConfigName() {
if (m_bIsSpecialWorkspace) {
return "special:" + m_szName;
}
if (m_iID > 0)
return std::to_string(m_iID);
return "name:" + m_szName;
}

View File

@@ -47,13 +47,22 @@ class CWorkspace {
CWindow* m_pLastFocusedWindow = nullptr;
// user-set
bool m_bDefaultFloating = false;
bool m_bDefaultPseudo = false;
bool m_bDefaultFloating = false;
bool m_bDefaultPseudo = false;
void startAnim(bool in, bool left, bool instant = false);
void setActive(bool on);
// don't destroy in sanity check
bool m_bIndestructible = false;
void moveToMonitor(const int&);
// last monitor (used on reconnect)
std::string m_szLastMonitor = "";
CWindow* getLastFocusedWindow();
void startAnim(bool in, bool left, bool instant = false);
void setActive(bool on);
void moveToMonitor(const int&);
CWindow* getLastFocusedWindow();
void rememberPrevWorkspace(const CWorkspace* prevWorkspace);
std::string getConfigName();
};

View File

@@ -33,7 +33,6 @@ void CHyprError::queueCreate(std::string message, const CColor& color) {
void CHyprError::createQueued() {
if (m_bIsCreated) {
m_bQueuedDestroy = false;
m_tTexture.destroyTexture();
}
@@ -171,4 +170,6 @@ void CHyprError::draw() {
void CHyprError::destroy() {
if (m_bIsCreated)
m_bQueuedDestroy = true;
else
m_szQueued = "";
}

View File

@@ -71,7 +71,6 @@ extern "C" {
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/types/wlr_subcompositor.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_output_damage.h>
#include <wlr/types/wlr_input_inhibitor.h>
#include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h>
#include <wlr/types/wlr_virtual_pointer_v1.h>
@@ -145,4 +144,4 @@ extern "C" {
#include "helpers/Vector2D.hpp"
#include "ext-workspace-unstable-v1-protocol.h"
#include "ext-workspace-unstable-v1-protocol.h"

View File

@@ -98,7 +98,7 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for
}
if (!PMONITOR) {
Debug::log(ERR, "Orphaned Node %x (workspace ID: %i)!!", pNode, pNode->workspaceID);
Debug::log(ERR, "Orphaned Node %lx (workspace ID: %i)!!", pNode, pNode->workspaceID);
return;
}
@@ -108,14 +108,21 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for
const bool DISPLAYTOP = STICKS(pNode->position.y, PMONITOR->vecPosition.y + PMONITOR->vecReservedTopLeft.y);
const bool DISPLAYBOTTOM = STICKS(pNode->position.y + pNode->size.y, PMONITOR->vecPosition.y + PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y);
const auto PBORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue;
const auto PGAPSIN = &g_pConfigManager->getConfigValuePtr("general:gaps_in")->intValue;
const auto PGAPSOUT = &g_pConfigManager->getConfigValuePtr("general:gaps_out")->intValue;
const auto PWINDOW = pNode->pWindow;
// get specific gaps and rules for this workspace,
// if user specified them in config
const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(g_pCompositor->getWorkspaceByID(PWINDOW->m_iWorkspaceID));
static auto* const PGAPSIN = &g_pConfigManager->getConfigValuePtr("general:gaps_in")->intValue;
static auto* const PGAPSOUT = &g_pConfigManager->getConfigValuePtr("general:gaps_out")->intValue;
static auto* const PBORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue;
auto gapsIn = WORKSPACERULE.gapsIn.value_or(*PGAPSIN);
auto gapsOut = WORKSPACERULE.gapsOut.value_or(*PGAPSOUT);
auto borderSize = WORKSPACERULE.borderSize.value_or(*PBORDERSIZE);
if (!g_pCompositor->windowExists(PWINDOW) || !PWINDOW->m_bIsMapped) {
Debug::log(ERR, "Node %x holding invalid window %x!!", pNode, PWINDOW);
Debug::log(ERR, "Node %lx holding invalid window %lx!!", pNode, PWINDOW);
onWindowRemovedTiling(PWINDOW);
return;
}
@@ -125,15 +132,15 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for
static auto* const PNOGAPSWHENONLY = &g_pConfigManager->getConfigValuePtr("dwindle:no_gaps_when_only")->intValue;
auto calcPos = PWINDOW->m_vPosition + Vector2D(*PBORDERSIZE, *PBORDERSIZE);
auto calcSize = PWINDOW->m_vSize - Vector2D(2 * *PBORDERSIZE, 2 * *PBORDERSIZE);
auto calcPos = PWINDOW->m_vPosition + Vector2D(borderSize, borderSize);
auto calcSize = PWINDOW->m_vSize - Vector2D(2 * borderSize, 2 * borderSize);
const auto NODESONWORKSPACE = getNodesOnWorkspace(PWINDOW->m_iWorkspaceID);
if (*PNOGAPSWHENONLY && !g_pCompositor->isWorkspaceSpecial(PWINDOW->m_iWorkspaceID) &&
if (*PNOGAPSWHENONLY && !WORKSPACERULE.border && !g_pCompositor->isWorkspaceSpecial(PWINDOW->m_iWorkspaceID) &&
(NODESONWORKSPACE == 1 || (PWINDOW->m_bIsFullscreen && g_pCompositor->getWorkspaceByID(PWINDOW->m_iWorkspaceID)->m_efFullscreenMode == FULLSCREEN_MAXIMIZED))) {
PWINDOW->m_vRealPosition = calcPos - Vector2D(*PBORDERSIZE, *PBORDERSIZE);
PWINDOW->m_vRealSize = calcSize + Vector2D(2 * *PBORDERSIZE, 2 * *PBORDERSIZE);
PWINDOW->m_vRealPosition = calcPos - Vector2D(borderSize, borderSize);
PWINDOW->m_vRealSize = calcSize + Vector2D(2 * borderSize, 2 * borderSize);
PWINDOW->updateWindowDecos();
@@ -144,13 +151,14 @@ void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode, bool for
return;
}
PWINDOW->m_sSpecialRenderData.rounding = true;
PWINDOW->m_sSpecialRenderData.border = true;
PWINDOW->m_sSpecialRenderData.decorate = true;
PWINDOW->m_sSpecialRenderData.rounding = WORKSPACERULE.rounding.value_or(true);
PWINDOW->m_sSpecialRenderData.decorate = WORKSPACERULE.decorate.value_or(true);
PWINDOW->m_sSpecialRenderData.border = WORKSPACERULE.border.value_or(true);
PWINDOW->m_sSpecialRenderData.borderSize = WORKSPACERULE.borderSize.value_or(-1);
const auto OFFSETTOPLEFT = Vector2D(DISPLAYLEFT ? *PGAPSOUT : *PGAPSIN, DISPLAYTOP ? *PGAPSOUT : *PGAPSIN);
const auto OFFSETTOPLEFT = Vector2D(DISPLAYLEFT ? gapsOut : gapsIn, DISPLAYTOP ? gapsOut : gapsIn);
const auto OFFSETBOTTOMRIGHT = Vector2D(DISPLAYRIGHT ? *PGAPSOUT : *PGAPSIN, DISPLAYBOTTOM ? *PGAPSOUT : *PGAPSIN);
const auto OFFSETBOTTOMRIGHT = Vector2D(DISPLAYRIGHT ? gapsOut : gapsIn, DISPLAYBOTTOM ? gapsOut : gapsIn);
calcPos = calcPos + OFFSETTOPLEFT;
calcSize = calcSize - OFFSETTOPLEFT - OFFSETBOTTOMRIGHT;
@@ -252,7 +260,7 @@ void CHyprDwindleLayout::onWindowCreatedTiling(CWindow* pWindow) {
} else
OPENINGON = getFirstNodeOnWorkspace(pWindow->m_iWorkspaceID);
Debug::log(LOG, "OPENINGON: %x, Workspace: %i, Monitor: %i", OPENINGON, PNODE->workspaceID, PMONITOR->ID);
Debug::log(LOG, "OPENINGON: %lx, Workspace: %i, Monitor: %i", OPENINGON, PNODE->workspaceID, PMONITOR->ID);
if (OPENINGON && OPENINGON->workspaceID != PNODE->workspaceID) {
// special workspace handling
@@ -462,17 +470,14 @@ void CHyprDwindleLayout::recalculateMonitor(const int& monid) {
}
}
// Ignore any recalc events if we have a fullscreen window, but process if fullscreen mode 2
if (PWORKSPACE->m_bHasFullscreenWindow) {
if (PWORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL)
return;
// massive hack from the fullscreen func
const auto PFULLWINDOW = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID);
if (!PFULLWINDOW) { // ????
PWORKSPACE->m_bHasFullscreenWindow = false;
} else {
if (PWORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL) {
PFULLWINDOW->m_vRealPosition = PMONITOR->vecPosition;
PFULLWINDOW->m_vRealSize = PMONITOR->vecSize;
} else if (PWORKSPACE->m_efFullscreenMode == FULLSCREEN_MAXIMIZED) {
SDwindleNodeData fakeNode;
fakeNode.pWindow = PFULLWINDOW;
fakeNode.position = PMONITOR->vecPosition + PMONITOR->vecReservedTopLeft;
@@ -482,9 +487,9 @@ void CHyprDwindleLayout::recalculateMonitor(const int& monid) {
PFULLWINDOW->m_vSize = fakeNode.size;
applyNodeDataToWindow(&fakeNode);
return;
}
return;
}
const auto TOPNODE = getMasterNodeOnWorkspace(PMONITOR->activeWorkspace);
@@ -500,6 +505,12 @@ bool CHyprDwindleLayout::isWindowTiled(CWindow* pWindow) {
return getNodeFromWindow(pWindow) != nullptr;
}
void CHyprDwindleLayout::onBeginDragWindow() {
m_PseudoDragFlags.started = false;
m_PseudoDragFlags.pseudo = false;
IHyprLayout::onBeginDragWindow();
}
void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, CWindow* pWindow) {
const auto PWINDOW = pWindow ? pWindow : g_pCompositor->m_pLastWindow;
@@ -524,6 +535,44 @@ void CHyprDwindleLayout::resizeActiveWindow(const Vector2D& pixResize, CWindow*
const bool DISPLAYTOP = STICKS(PWINDOW->m_vPosition.y, PMONITOR->vecPosition.y + PMONITOR->vecReservedTopLeft.y);
const bool DISPLAYBOTTOM = STICKS(PWINDOW->m_vPosition.y + PWINDOW->m_vSize.y, PMONITOR->vecPosition.y + PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y);
if (PWINDOW->m_bIsPseudotiled) {
if (!m_PseudoDragFlags.started) {
m_PseudoDragFlags.started = true;
const auto pseudoSize = PWINDOW->m_vRealSize.goalv();
const auto mouseOffset = g_pInputManager->getMouseCoordsInternal() - (PNODE->position + ((PNODE->size / 2) - (pseudoSize / 2)));
if (mouseOffset.x > 0 && mouseOffset.x < pseudoSize.x && mouseOffset.y > 0 && mouseOffset.y < pseudoSize.y) {
m_PseudoDragFlags.pseudo = true;
m_PseudoDragFlags.xExtent = mouseOffset.x > pseudoSize.x / 2;
m_PseudoDragFlags.yExtent = mouseOffset.y > pseudoSize.y / 2;
PWINDOW->m_vPseudoSize = pseudoSize;
} else {
m_PseudoDragFlags.pseudo = false;
}
}
if (m_PseudoDragFlags.pseudo) {
if (m_PseudoDragFlags.xExtent)
PWINDOW->m_vPseudoSize.x += pixResize.x * 2;
else
PWINDOW->m_vPseudoSize.x -= pixResize.x * 2;
if (m_PseudoDragFlags.yExtent)
PWINDOW->m_vPseudoSize.y += pixResize.y * 2;
else
PWINDOW->m_vPseudoSize.y -= pixResize.y * 2;
PWINDOW->m_vPseudoSize.x = std::clamp(PWINDOW->m_vPseudoSize.x, 30.0, PNODE->size.x);
PWINDOW->m_vPseudoSize.y = std::clamp(PWINDOW->m_vPseudoSize.y, 30.0, PNODE->size.y);
PWINDOW->m_vLastFloatingSize = PWINDOW->m_vPseudoSize;
PNODE->recalcSizePosRecursive(*PANIMATE == 0);
return;
}
}
// construct allowed movement
Vector2D allowedMovement = pixResize;
if (DISPLAYLEFT && DISPLAYRIGHT)

View File

@@ -46,6 +46,7 @@ class CHyprDwindleLayout : public IHyprLayout {
virtual bool isWindowTiled(CWindow*);
virtual void recalculateMonitor(const int&);
virtual void recalculateWindow(CWindow*);
virtual void onBeginDragWindow();
virtual void resizeActiveWindow(const Vector2D&, CWindow* pWindow = nullptr);
virtual void fullscreenRequestForWindow(CWindow*, eFullscreenMode, bool);
virtual std::any layoutMessage(SLayoutMessageHeader, std::string);
@@ -61,13 +62,20 @@ class CHyprDwindleLayout : public IHyprLayout {
private:
std::list<SDwindleNodeData> m_lDwindleNodesData;
int getNodesOnWorkspace(const int&);
void applyNodeDataToWindow(SDwindleNodeData*, bool force = false);
SDwindleNodeData* getNodeFromWindow(CWindow*);
SDwindleNodeData* getFirstNodeOnWorkspace(const int&);
SDwindleNodeData* getMasterNodeOnWorkspace(const int&);
struct {
bool started = false;
bool pseudo = false;
bool xExtent = false;
bool yExtent = false;
} m_PseudoDragFlags;
void toggleSplit(CWindow*);
int getNodesOnWorkspace(const int&);
void applyNodeDataToWindow(SDwindleNodeData*, bool force = false);
SDwindleNodeData* getNodeFromWindow(CWindow*);
SDwindleNodeData* getFirstNodeOnWorkspace(const int&);
SDwindleNodeData* getMasterNodeOnWorkspace(const int&);
void toggleSplit(CWindow*);
friend struct SDwindleNodeData;
};

View File

@@ -76,12 +76,12 @@ void IHyprLayout::onWindowCreatedFloating(CWindow* pWindow) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
if (!PMONITOR) {
Debug::log(ERR, "Window %x (%s) has an invalid monitor in onWindowCreatedFloating!!!", pWindow, pWindow->m_szTitle.c_str());
Debug::log(ERR, "Window %lx (%s) has an invalid monitor in onWindowCreatedFloating!!!", pWindow, pWindow->m_szTitle.c_str());
return;
}
if (desiredGeometry.width <= 5 || desiredGeometry.height <= 5) {
const auto PWINDOWSURFACE = g_pXWaylandManager->getWindowSurface(pWindow);
const auto PWINDOWSURFACE = pWindow->m_pWLSurface.wlr();
pWindow->m_vRealSize = Vector2D(PWINDOWSURFACE->current.width, PWINDOWSURFACE->current.height);
if ((desiredGeometry.width <= 1 || desiredGeometry.height <= 1) && pWindow->m_bIsX11 &&
@@ -267,15 +267,15 @@ void IHyprLayout::onMouseMove(const Vector2D& mousePos) {
return;
}
static auto TIMER = std::chrono::high_resolution_clock::now();
static auto TIMER = std::chrono::high_resolution_clock::now();
const auto SPECIAL = g_pCompositor->isWorkspaceSpecial(DRAGGINGWINDOW->m_iWorkspaceID);
const auto SPECIAL = g_pCompositor->isWorkspaceSpecial(DRAGGINGWINDOW->m_iWorkspaceID);
const auto DELTA = Vector2D(mousePos.x - m_vBeginDragXY.x, mousePos.y - m_vBeginDragXY.y);
const auto TICKDELTA = Vector2D(mousePos.x - m_vLastDragXY.x, mousePos.y - m_vLastDragXY.y);
const auto DELTA = Vector2D(mousePos.x - m_vBeginDragXY.x, mousePos.y - m_vBeginDragXY.y);
const auto TICKDELTA = Vector2D(mousePos.x - m_vLastDragXY.x, mousePos.y - m_vLastDragXY.y);
const auto PANIMATEMOUSE = &g_pConfigManager->getConfigValuePtr("misc:animate_mouse_windowdragging")->intValue;
const auto PANIMATE = &g_pConfigManager->getConfigValuePtr("misc:animate_manual_resizes")->intValue;
static auto* const PANIMATEMOUSE = &g_pConfigManager->getConfigValuePtr("misc:animate_mouse_windowdragging")->intValue;
static auto* const PANIMATE = &g_pConfigManager->getConfigValuePtr("misc:animate_manual_resizes")->intValue;
if ((abs(TICKDELTA.x) < 1.f && abs(TICKDELTA.y) < 1.f) ||
(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - TIMER).count() <
@@ -343,6 +343,7 @@ void IHyprLayout::onMouseMove(const Vector2D& mousePos) {
if (PMONITOR && !SPECIAL) {
DRAGGINGWINDOW->m_iMonitorID = PMONITOR->ID;
DRAGGINGWINDOW->moveToWorkspace(PMONITOR->activeWorkspace);
DRAGGINGWINDOW->updateGroupOutputs();
DRAGGINGWINDOW->updateToplevel();
}
@@ -367,13 +368,14 @@ void IHyprLayout::changeWindowFloatingMode(CWindow* pWindow) {
const auto TILED = isWindowTiled(pWindow);
// event
g_pEventManager->postEvent(SHyprIPCEvent{"changefloatingmode", getFormat("%x,%d", pWindow, (int)TILED)});
g_pEventManager->postEvent(SHyprIPCEvent{"changefloatingmode", getFormat("%lx,%d", pWindow, (int)TILED)});
EMIT_HOOK_EVENT("changeFloatingMode", pWindow);
if (!TILED) {
const auto PNEWMON = g_pCompositor->getMonitorFromVector(pWindow->m_vRealPosition.vec() + pWindow->m_vRealSize.vec() / 2.f);
pWindow->m_iMonitorID = PNEWMON->ID;
pWindow->moveToWorkspace(PNEWMON->activeWorkspace);
pWindow->updateGroupOutputs();
// save real pos cuz the func applies the default 5,5 mid
const auto PSAVEDPOS = pWindow->m_vRealPosition.goalv();
@@ -402,6 +404,11 @@ void IHyprLayout::changeWindowFloatingMode(CWindow* pWindow) {
g_pCompositor->moveWindowToTop(pWindow);
if (DELTALESSTHAN(pWindow->m_vRealSize.vec().x, pWindow->m_vLastFloatingSize.x, 10) && DELTALESSTHAN(pWindow->m_vRealSize.vec().y, pWindow->m_vLastFloatingSize.y, 10)) {
pWindow->m_vRealPosition = pWindow->m_vRealPosition.goalv() + (pWindow->m_vRealSize.goalv() - pWindow->m_vLastFloatingSize) / 2.f + Vector2D{10, 10};
pWindow->m_vRealSize = pWindow->m_vLastFloatingSize - Vector2D{20, 20};
}
pWindow->m_vRealPosition = pWindow->m_vRealPosition.goalv() + (pWindow->m_vRealSize.goalv() - pWindow->m_vLastFloatingSize) / 2.f;
pWindow->m_vRealSize = pWindow->m_vLastFloatingSize;

View File

@@ -153,6 +153,10 @@ void CHyprMasterLayout::onWindowRemovedTiling(CWindow* pWindow) {
if (!PNODE)
return;
const auto WORKSPACEID = PNODE->workspaceID;
const auto MASTERSLEFT = getMastersOnWorkspace(WORKSPACEID);
static const auto* SMALLSPLIT = &g_pConfigManager->getConfigValuePtr("master:allow_small_split")->intValue;
pWindow->m_sSpecialRenderData.rounding = true;
pWindow->m_sSpecialRenderData.border = true;
pWindow->m_sSpecialRenderData.decorate = true;
@@ -160,12 +164,10 @@ void CHyprMasterLayout::onWindowRemovedTiling(CWindow* pWindow) {
if (pWindow->m_bIsFullscreen)
g_pCompositor->setWindowFullscreen(pWindow, false, FULLSCREEN_FULL);
const auto MASTERSLEFT = getMastersOnWorkspace(PNODE->workspaceID);
if (PNODE->isMaster && MASTERSLEFT < 2) {
// find new one
if (PNODE->isMaster && (MASTERSLEFT <= 1 || *SMALLSPLIT == 1)) {
// find a new master from top of the list
for (auto& nd : m_lMasterNodesData) {
if (!nd.isMaster && nd.workspaceID == PNODE->workspaceID) {
if (!nd.isMaster && nd.workspaceID == WORKSPACEID) {
nd.isMaster = true;
nd.percMaster = PNODE->percMaster;
break;
@@ -173,8 +175,6 @@ void CHyprMasterLayout::onWindowRemovedTiling(CWindow* pWindow) {
}
}
const auto WORKSPACEID = PNODE->workspaceID;
m_lMasterNodesData.remove(*PNODE);
if (getMastersOnWorkspace(WORKSPACEID) == getNodesOnWorkspace(WORKSPACEID) && MASTERSLEFT > 1) {
@@ -185,7 +185,16 @@ void CHyprMasterLayout::onWindowRemovedTiling(CWindow* pWindow) {
}
}
}
// BUGFIX: correct bug where closing one master in a stack of 2 would leave
// the screen half bare, and make it difficult to select remaining window
if (getNodesOnWorkspace(WORKSPACEID) == 1) {
for (auto& nd : m_lMasterNodesData) {
if (nd.workspaceID == WORKSPACEID && nd.isMaster == false) {
nd.isMaster = true;
break;
}
}
}
recalculateMonitor(pWindow->m_iMonitorID);
}
@@ -203,21 +212,23 @@ void CHyprMasterLayout::recalculateMonitor(const int& monid) {
}
if (PWORKSPACE->m_bHasFullscreenWindow) {
if (PWORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL)
return;
// massive hack from the fullscreen func
const auto PFULLWINDOW = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID);
const auto PFULLWINDOW = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID);
SMasterNodeData fakeNode;
fakeNode.pWindow = PFULLWINDOW;
fakeNode.position = PMONITOR->vecPosition + PMONITOR->vecReservedTopLeft;
fakeNode.size = PMONITOR->vecSize - PMONITOR->vecReservedTopLeft - PMONITOR->vecReservedBottomRight;
fakeNode.workspaceID = PWORKSPACE->m_iID;
PFULLWINDOW->m_vPosition = fakeNode.position;
PFULLWINDOW->m_vSize = fakeNode.size;
if (PWORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL) {
PFULLWINDOW->m_vRealPosition = PMONITOR->vecPosition;
PFULLWINDOW->m_vRealSize = PMONITOR->vecSize;
} else if (PWORKSPACE->m_efFullscreenMode == FULLSCREEN_MAXIMIZED) {
SMasterNodeData fakeNode;
fakeNode.pWindow = PFULLWINDOW;
fakeNode.position = PMONITOR->vecPosition + PMONITOR->vecReservedTopLeft;
fakeNode.size = PMONITOR->vecSize - PMONITOR->vecReservedTopLeft - PMONITOR->vecReservedBottomRight;
fakeNode.workspaceID = PWORKSPACE->m_iID;
PFULLWINDOW->m_vPosition = fakeNode.position;
PFULLWINDOW->m_vSize = fakeNode.size;
applyNodeDataToWindow(&fakeNode);
applyNodeDataToWindow(&fakeNode);
}
return;
}
@@ -252,20 +263,26 @@ void CHyprMasterLayout::calculateWorkspace(const int& ws) {
}
}
const auto MASTERS = getMastersOnWorkspace(PWORKSPACE->m_iID);
const auto MASTERS = getMastersOnWorkspace(PWORKSPACE->m_iID);
const auto WINDOWS = getNodesOnWorkspace(PWORKSPACE->m_iID);
const auto STACKWINDOWS = WINDOWS - MASTERS;
//compute placement of master window(s)
if (getNodesOnWorkspace(PWORKSPACE->m_iID) < 2 && !centerMasterWindow) {
if ((WINDOWS < 2) && !centerMasterWindow) {
PMASTERNODE->position = PMONITOR->vecReservedTopLeft + PMONITOR->vecPosition;
PMASTERNODE->size = Vector2D(PMONITOR->vecSize.x - PMONITOR->vecReservedTopLeft.x - PMONITOR->vecReservedBottomRight.x,
PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y - PMONITOR->vecReservedTopLeft.y);
applyNodeDataToWindow(PMASTERNODE);
return;
} else if (orientation == ORIENTATION_LEFT || orientation == ORIENTATION_RIGHT) {
float heightLeft = PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y - PMONITOR->vecReservedTopLeft.y;
int nodesLeft = MASTERS;
float nextY = 0;
const float WIDTH = (PMONITOR->vecSize.x - PMONITOR->vecReservedTopLeft.x - PMONITOR->vecReservedBottomRight.x) * PMASTERNODE->percMaster;
} else if (orientation == ORIENTATION_LEFT || orientation == ORIENTATION_RIGHT || (orientation == ORIENTATION_CENTER && STACKWINDOWS <= 1)) {
float heightLeft = PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y - PMONITOR->vecReservedTopLeft.y;
int nodesLeft = MASTERS;
float nextY = 0;
float WIDTH = 0;
if (STACKWINDOWS == 0 && MASTERS > 0)
WIDTH = (PMONITOR->vecSize.x - PMONITOR->vecReservedTopLeft.x - PMONITOR->vecReservedBottomRight.x);
else
WIDTH = (PMONITOR->vecSize.x - PMONITOR->vecReservedTopLeft.x - PMONITOR->vecReservedBottomRight.x) * PMASTERNODE->percMaster;
for (auto& n : m_lMasterNodesData) {
if (n.workspaceID == PWORKSPACE->m_iID && n.isMaster) {
@@ -313,7 +330,7 @@ void CHyprMasterLayout::calculateWorkspace(const int& ws) {
applyNodeDataToWindow(&n);
}
}
} else if (orientation == ORIENTATION_CENTER) {
} else if (orientation == ORIENTATION_CENTER && STACKWINDOWS >= 2) {
float heightLeft = PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y - PMONITOR->vecReservedTopLeft.y;
int nodesLeft = MASTERS;
float nextY = 0;
@@ -339,7 +356,7 @@ void CHyprMasterLayout::calculateWorkspace(const int& ws) {
//compute placement of slave window(s)
int slavesLeft = getNodesOnWorkspace(PWORKSPACE->m_iID) - MASTERS;
if (orientation == ORIENTATION_LEFT || orientation == ORIENTATION_RIGHT) {
if (orientation == ORIENTATION_LEFT || orientation == ORIENTATION_RIGHT || (orientation == ORIENTATION_CENTER && STACKWINDOWS <= 1)) {
float heightLeft = PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y - PMONITOR->vecReservedTopLeft.y;
float nextY = 0;
const float WIDTH = PMONITOR->vecSize.x - PMONITOR->vecReservedBottomRight.x - PMONITOR->vecReservedTopLeft.x - PMASTERNODE->size.x;
@@ -348,7 +365,7 @@ void CHyprMasterLayout::calculateWorkspace(const int& ws) {
if (nd.workspaceID != PWORKSPACE->m_iID || nd.isMaster)
continue;
if (orientation == ORIENTATION_LEFT) {
if (orientation == ORIENTATION_LEFT || (orientation == ORIENTATION_CENTER && STACKWINDOWS <= 1)) {
nd.position = PMONITOR->vecReservedTopLeft + PMONITOR->vecPosition +
Vector2D(PMASTERNODE->percMaster * (PMONITOR->vecSize.x - PMONITOR->vecReservedTopLeft.x - PMONITOR->vecReservedBottomRight.x), nextY);
} else {
@@ -390,7 +407,7 @@ void CHyprMasterLayout::calculateWorkspace(const int& ws) {
applyNodeDataToWindow(&nd);
}
} else if (orientation == ORIENTATION_CENTER) {
} else if (orientation == ORIENTATION_CENTER && STACKWINDOWS >= 2) {
float heightLeftL = PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y - PMONITOR->vecReservedTopLeft.y;
float heightLeftR = heightLeftL;
float heightLeft = 0;
@@ -451,7 +468,7 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) {
}
if (!PMONITOR) {
Debug::log(ERR, "Orphaned Node %x (workspace ID: %i)!!", pNode, pNode->workspaceID);
Debug::log(ERR, "Orphaned Node %lx (workspace ID: %i)!!", pNode, pNode->workspaceID);
return;
}
@@ -461,14 +478,21 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) {
const bool DISPLAYTOP = STICKS(pNode->position.y, PMONITOR->vecPosition.y + PMONITOR->vecReservedTopLeft.y);
const bool DISPLAYBOTTOM = STICKS(pNode->position.y + pNode->size.y, PMONITOR->vecPosition.y + PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y);
const auto PBORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue;
const auto PGAPSIN = &g_pConfigManager->getConfigValuePtr("general:gaps_in")->intValue;
const auto PGAPSOUT = &g_pConfigManager->getConfigValuePtr("general:gaps_out")->intValue;
const auto PWINDOW = pNode->pWindow;
// get specific gaps and rules for this workspace,
// if user specified them in config
const auto WORKSPACERULE = g_pConfigManager->getWorkspaceRuleFor(g_pCompositor->getWorkspaceByID(PWINDOW->m_iWorkspaceID));
static auto* const PGAPSIN = &g_pConfigManager->getConfigValuePtr("general:gaps_in")->intValue;
static auto* const PGAPSOUT = &g_pConfigManager->getConfigValuePtr("general:gaps_out")->intValue;
static auto* const PBORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue;
auto gapsIn = WORKSPACERULE.gapsIn.value_or(*PGAPSIN);
auto gapsOut = WORKSPACERULE.gapsOut.value_or(*PGAPSOUT);
auto borderSize = WORKSPACERULE.borderSize.value_or(*PBORDERSIZE);
if (!g_pCompositor->windowValidMapped(PWINDOW)) {
Debug::log(ERR, "Node %x holding invalid window %x!!", pNode, PWINDOW);
Debug::log(ERR, "Node %lx holding invalid window %lx!!", pNode, PWINDOW);
return;
}
@@ -477,14 +501,14 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) {
PWINDOW->m_vSize = pNode->size;
PWINDOW->m_vPosition = pNode->position;
auto calcPos = PWINDOW->m_vPosition + Vector2D(*PBORDERSIZE, *PBORDERSIZE);
auto calcSize = PWINDOW->m_vSize - Vector2D(2 * *PBORDERSIZE, 2 * *PBORDERSIZE);
auto calcPos = PWINDOW->m_vPosition + Vector2D(borderSize, borderSize);
auto calcSize = PWINDOW->m_vSize - Vector2D(2 * borderSize, 2 * borderSize);
if (*PNOGAPSWHENONLY && !g_pCompositor->isWorkspaceSpecial(PWINDOW->m_iWorkspaceID) &&
if (*PNOGAPSWHENONLY && !WORKSPACERULE.border && !g_pCompositor->isWorkspaceSpecial(PWINDOW->m_iWorkspaceID) &&
(getNodesOnWorkspace(PWINDOW->m_iWorkspaceID) == 1 ||
(PWINDOW->m_bIsFullscreen && g_pCompositor->getWorkspaceByID(PWINDOW->m_iWorkspaceID)->m_efFullscreenMode == FULLSCREEN_MAXIMIZED))) {
PWINDOW->m_vRealPosition = calcPos - Vector2D(*PBORDERSIZE, *PBORDERSIZE);
PWINDOW->m_vRealSize = calcSize + Vector2D(2 * *PBORDERSIZE, 2 * *PBORDERSIZE);
PWINDOW->m_vRealPosition = calcPos - Vector2D(borderSize, borderSize);
PWINDOW->m_vRealSize = calcSize + Vector2D(2 * borderSize, 2 * borderSize);
PWINDOW->updateWindowDecos();
@@ -495,13 +519,14 @@ void CHyprMasterLayout::applyNodeDataToWindow(SMasterNodeData* pNode) {
return;
}
PWINDOW->m_sSpecialRenderData.rounding = true;
PWINDOW->m_sSpecialRenderData.border = true;
PWINDOW->m_sSpecialRenderData.decorate = true;
PWINDOW->m_sSpecialRenderData.rounding = WORKSPACERULE.rounding.value_or(true);
PWINDOW->m_sSpecialRenderData.decorate = WORKSPACERULE.decorate.value_or(true);
PWINDOW->m_sSpecialRenderData.border = WORKSPACERULE.border.value_or(true);
PWINDOW->m_sSpecialRenderData.borderSize = WORKSPACERULE.borderSize.value_or(-1);
const auto OFFSETTOPLEFT = Vector2D(DISPLAYLEFT ? *PGAPSOUT : *PGAPSIN, DISPLAYTOP ? *PGAPSOUT : *PGAPSIN);
const auto OFFSETTOPLEFT = Vector2D(DISPLAYLEFT ? gapsOut : gapsIn, DISPLAYTOP ? gapsOut : gapsIn);
const auto OFFSETBOTTOMRIGHT = Vector2D(DISPLAYRIGHT ? *PGAPSOUT : *PGAPSIN, DISPLAYBOTTOM ? *PGAPSOUT : *PGAPSIN);
const auto OFFSETBOTTOMRIGHT = Vector2D(DISPLAYRIGHT ? gapsOut : gapsIn, DISPLAYBOTTOM ? gapsOut : gapsIn);
calcPos = calcPos + OFFSETTOPLEFT;
calcSize = calcSize - OFFSETTOPLEFT - OFFSETBOTTOMRIGHT;
@@ -949,14 +974,14 @@ std::any CHyprMasterLayout::layoutMessage(SLayoutMessageHeader header, std::stri
if (header.pWindow->m_bIsFloating)
return 0;
const auto PNODE = getNodeFromWindow(header.pWindow);
const auto PNODE = getNodeFromWindow(header.pWindow);
const auto WINDOWS = getNodesOnWorkspace(header.pWindow->m_iWorkspaceID);
const auto MASTERS = getMastersOnWorkspace(header.pWindow->m_iWorkspaceID);
const auto WINDOWS = getNodesOnWorkspace(header.pWindow->m_iWorkspaceID);
const auto MASTERS = getMastersOnWorkspace(header.pWindow->m_iWorkspaceID);
static const auto* SMALLSPLIT = &g_pConfigManager->getConfigValuePtr("master:allow_small_split")->intValue;
if (MASTERS + 2 > WINDOWS)
if (MASTERS + 2 > WINDOWS && *SMALLSPLIT == 0)
return 0;
prepareLoseFocus(header.pWindow);
if (!PNODE || PNODE->isMaster) {

View File

@@ -3,11 +3,26 @@
#include "Compositor.hpp"
#include "config/ConfigManager.hpp"
#include "init/initHelpers.hpp"
#include <iostream>
#include <iterator>
#include <vector>
#include <stdexcept>
#include <string>
#include <filesystem>
#ifdef USES_SYSTEMD
#include <systemd/sd-daemon.h> // for sd_notify
#endif
void help() {
std::cout << "usage: Hyprland [arg [...]].\n";
std::cout << "\nArguments:\n";
std::cout << " --help -h - Show this message again\n";
std::cout << " --config FILE -c FILE - Specify config file to use\n";
std::cout << " --i-am-really-stupid - Omits root user privileges check (why would you do that?)\n";
}
int main(int argc, char** argv) {
if (!getenv("XDG_RUNTIME_DIR"))
@@ -17,6 +32,7 @@ int main(int argc, char** argv) {
std::string cmd = "";
for (auto i = 0; i < argc; ++i)
cmd += std::string(i == 0 ? "" : " ") + argv[i];
setenv("HYPRLAND_CMD", cmd.c_str(), 1);
setenv("XDG_BACKEND", "wayland", 1);
setenv("_JAVA_AWT_WM_NONREPARENTING", "1", 1);
@@ -24,30 +40,49 @@ int main(int argc, char** argv) {
setenv("XDG_CURRENT_DESKTOP", "Hyprland", 1);
// parse some args
std::string configPath;
bool ignoreSudo = false;
for (int i = 1; i < argc; ++i) {
if (!strcmp(argv[i], "--i-am-really-stupid"))
std::string configPath;
bool ignoreSudo = false;
std::vector<std::string> args{argv + 1, argv + argc};
for (auto it = args.begin(); it != args.end(); it++) {
if (it->compare("--i-am-really-stupid") == 0 && !ignoreSudo) {
std::cout << "[ WARNING ] Running Hyprland with superuser privileges might damage your system\n";
ignoreSudo = true;
else if ((!strcmp(argv[i], "-c") || !strcmp(argv[i], "--config")) && argc >= i + 2) {
configPath = std::string(argv[++i]);
Debug::log(LOG, "Using config location %s.", configPath.c_str());
} else if (it->compare("-c") == 0 || it->compare("--config") == 0) {
if (std::next(it)->c_str() == nullptr) {
help();
return 1;
}
std::string next_arg = std::next(it)->c_str();
if (!std::filesystem::exists(next_arg)) {
std::cerr << "[ ERROR ] Config path '" << next_arg << "' doesn't exist!\n";
help();
return 1;
}
configPath = next_arg;
Debug::log(LOG, "User-specified config location: '%s'", configPath.c_str());
it++;
continue;
} else {
std::cout << "Hyprland usage: Hyprland [arg [...]].\n\nArguments:\n"
<< "--help -h | Show this help message\n"
<< "--config -c | Specify config file to use\n";
return 1;
help();
return 0;
}
}
if (!ignoreSudo) {
if (Init::isSudo()) {
std::cout << "Hyprland shall not be run as the root user. If you really want to, use the --i-am-really-stupid flag.\n";
return 1;
}
} else {
std::cout << "Running with ignored root checks, I surely hope you know what you're doing.\n";
sleep(1);
if (!ignoreSudo && Init::isSudo()) {
std::cerr << "[ ERROR ] Hyprland was launched with superuser priveleges, but the privileges check is not omitted.\n";
std::cerr << " Hint: Use the --i-am-really-stupid flag to omit that check.\n";
return 1;
} else if (ignoreSudo && Init::isSudo()) {
std::cout << "Superuser privileges check is omitted. I hope you know what you're doing.\n";
}
std::cout << "Welcome to Hyprland!\n";
@@ -76,10 +111,6 @@ int main(int argc, char** argv) {
if (g_pCompositor->m_sWLDisplay)
wl_display_destroy_clients(g_pCompositor->m_sWLDisplay);
// kill all clients
for (auto& c : g_pCompositor->m_dProcessPIDsOnShutdown)
kill(c, SIGKILL);
if (g_pCompositor->m_sWLDisplay)
wl_display_destroy(g_pCompositor->m_sWLDisplay);

View File

@@ -1,5 +1,6 @@
#include "AnimationManager.hpp"
#include "../Compositor.hpp"
#include "HookSystemManager.hpp"
int wlTick(void* data) {
@@ -7,7 +8,10 @@ int wlTick(void* data) {
wl_event_source_timer_update(g_pAnimationManager->m_pAnimationTick, 1000 / refreshRate);
g_pAnimationManager->tick();
if (g_pCompositor->m_bSessionActive && std::ranges::any_of(g_pCompositor->m_vMonitors, [](const auto& mon) { return mon->m_bEnabled && mon->output; })) {
g_pAnimationManager->tick();
EMIT_HOOK_EVENT("tick", nullptr);
}
return 0;
}
@@ -78,13 +82,19 @@ void CAnimationManager::tick() {
if (PWINDOW) {
WLRBOXPREV = PWINDOW->getFullWindowBoundingBox();
PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
if (!PMONITOR)
continue;
animationsDisabled = animationsDisabled || PWINDOW->m_sAdditionalConfigData.forceNoAnims;
} else if (PWORKSPACE) {
PMONITOR = g_pCompositor->getMonitorFromID(PWORKSPACE->m_iMonitorID);
if (!PMONITOR)
continue;
WLRBOXPREV = {(int)PMONITOR->vecPosition.x, (int)PMONITOR->vecPosition.y, (int)PMONITOR->vecSize.x, (int)PMONITOR->vecSize.y};
} else if (PLAYER) {
WLRBOXPREV = PLAYER->geometry;
PMONITOR = g_pCompositor->getMonitorFromVector(Vector2D(PLAYER->geometry.x, PLAYER->geometry.y) + Vector2D(PLAYER->geometry.width, PLAYER->geometry.height) / 2.f);
if (!PMONITOR)
continue;
animationsDisabled = animationsDisabled || PLAYER->noAnimations;
}

View File

@@ -12,14 +12,15 @@
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string>
CEventManager::CEventManager() {}
int fdHandleWrite(int fd, uint32_t mask, void* data) {
if (mask & WL_EVENT_ERROR || mask & WL_EVENT_HANGUP) {
// remove, hanged up
auto removeFD = [&](int fd) -> void {
const auto ACCEPTEDFDS = (std::deque<std::pair<int, wl_event_source*>>*)data;
for (auto it = ACCEPTEDFDS->begin(); it != ACCEPTEDFDS->end();) {
if (it->first == fd) {
@@ -29,6 +30,27 @@ int fdHandleWrite(int fd, uint32_t mask, void* data) {
it++;
}
}
};
if (mask & WL_EVENT_ERROR || mask & WL_EVENT_HANGUP) {
// remove, hanged up
removeFD(fd);
return 0;
}
int availableBytes;
if (ioctl(fd, FIONREAD, &availableBytes) == -1) {
Debug::log(ERR, "fd %d sent invalid data (1)", fd);
removeFD(fd);
return 0;
}
char buf[availableBytes];
const auto RECEIVED = recv(fd, buf, availableBytes, 0);
if (RECEIVED == -1) {
Debug::log(ERR, "fd %d sent invalid data (2)", fd);
removeFD(fd);
return 0;
}
return 0;
@@ -45,7 +67,7 @@ void CEventManager::startThread() {
sockaddr_un SERVERADDRESS = {.sun_family = AF_UNIX};
std::string socketPath = "/tmp/hypr/" + g_pCompositor->m_szInstanceSignature + "/.socket2.sock";
strcpy(SERVERADDRESS.sun_path, socketPath.c_str());
strncpy(SERVERADDRESS.sun_path, socketPath.c_str(), sizeof(SERVERADDRESS.sun_path) - 1);
bind(SOCKET, (sockaddr*)&SERVERADDRESS, SUN_LEN(&SERVERADDRESS));

View File

@@ -28,6 +28,7 @@ CKeybindManager::CKeybindManager() {
m_mDispatchers["pseudo"] = toggleActivePseudo;
m_mDispatchers["movefocus"] = moveFocusTo;
m_mDispatchers["movewindow"] = moveActiveTo;
m_mDispatchers["swapwindow"] = swapActive;
m_mDispatchers["centerwindow"] = centerWindow;
m_mDispatchers["togglegroup"] = toggleGroup;
m_mDispatchers["changegroupactive"] = changeGroupActive;
@@ -35,6 +36,7 @@ CKeybindManager::CKeybindManager() {
m_mDispatchers["splitratio"] = alterSplitRatio;
m_mDispatchers["focusmonitor"] = focusMonitor;
m_mDispatchers["movecursortocorner"] = moveCursorToCorner;
m_mDispatchers["movecursor"] = moveCursor;
m_mDispatchers["workspaceopt"] = workspaceOpt;
m_mDispatchers["exit"] = exitHyprland;
m_mDispatchers["movecurrentworkspacetomonitor"] = moveCurrentWorkspaceToMonitor;
@@ -63,6 +65,7 @@ CKeybindManager::CKeybindManager() {
m_mDispatchers["lockgroups"] = lockGroups;
m_mDispatchers["moveintogroup"] = moveIntoGroup;
m_mDispatchers["moveoutofgroup"] = moveOutOfGroup;
m_mDispatchers["global"] = global;
m_tScrollTimer.reset();
}
@@ -103,7 +106,7 @@ uint32_t CKeybindManager::stringToModMask(std::string mods) {
modMask |= WLR_MODIFIER_CAPS;
if (mods.contains("CTRL") || mods.contains("CONTROL"))
modMask |= WLR_MODIFIER_CTRL;
if (mods.contains("ALT"))
if (mods.contains("ALT") || mods.contains("MOD1"))
modMask |= WLR_MODIFIER_ALT;
if (mods.contains("MOD2"))
modMask |= WLR_MODIFIER_MOD2;
@@ -173,6 +176,38 @@ bool CKeybindManager::ensureMouseBindState() {
return false;
}
bool CKeybindManager::tryMoveFocusToMonitor(CMonitor* monitor) {
if (!monitor)
return false;
const auto LASTMONITOR = g_pCompositor->m_pLastMonitor;
if (LASTMONITOR == monitor) {
Debug::log(LOG, "Tried to move to active monitor");
return false;
}
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(g_pCompositor->m_pLastMonitor->activeWorkspace);
const auto PNEWWORKSPACE = g_pCompositor->getWorkspaceByID(monitor->activeWorkspace);
g_pCompositor->setActiveMonitor(monitor);
g_pCompositor->deactivateAllWLRWorkspaces(PNEWWORKSPACE->m_pWlrHandle);
PNEWWORKSPACE->setActive(true);
PNEWWORKSPACE->rememberPrevWorkspace(PWORKSPACE);
const auto PNEWWINDOW = PNEWWORKSPACE->getLastFocusedWindow();
if (PNEWWINDOW) {
g_pCompositor->focusWindow(PNEWWINDOW);
Vector2D middle = PNEWWINDOW->m_vRealPosition.goalv() + PNEWWINDOW->m_vRealSize.goalv() / 2.f;
g_pCompositor->warpCursorTo(middle);
} else {
g_pCompositor->focusWindow(nullptr);
Vector2D middle = monitor->vecPosition + monitor->vecSize / 2.f;
g_pCompositor->warpCursorTo(middle);
}
return true;
}
bool CKeybindManager::onKeyEvent(wlr_keyboard_key_event* e, SKeyboard* pKeyboard) {
if (!g_pCompositor->m_bSessionActive) {
m_dPressedKeycodes.clear();
@@ -352,7 +387,7 @@ bool CKeybindManager::handleKeybinds(const uint32_t& modmask, const std::string&
for (auto& k : m_lKeybinds) {
if (modmask != k.modmask || (g_pCompositor->m_sSeat.exclusiveClient && !k.locked) || k.submap != m_szCurrentSelectedSubmap ||
(!pressed && !k.release && k.handler != "pass" && k.handler != "mouse") || k.shadowed)
(!pressed && !k.release && k.handler != "pass" && k.handler != "mouse" && k.handler != "global") || k.shadowed)
continue;
if (!key.empty()) {
@@ -385,7 +420,7 @@ bool CKeybindManager::handleKeybinds(const uint32_t& modmask, const std::string&
// Should never happen, as we check in the ConfigManager, but oh well
if (DISPATCHER == m_mDispatchers.end()) {
Debug::log(ERR, "Inavlid handler in a keybind! (handler %s does not exist)", k.handler.c_str());
Debug::log(ERR, "Invalid handler in a keybind! (handler %s does not exist)", k.handler.c_str());
} else {
// call the dispatcher
Debug::log(LOG, "Keybind triggered, calling dispatcher (%d, %s, %d)", modmask, key.c_str(), keysym);
@@ -425,7 +460,10 @@ void CKeybindManager::shadowKeybinds(const xkb_keysym_t& doesntHave, const int&
for (auto& k : m_lKeybinds) {
bool shadow = false;
bool shadow = false;
if (k.handler == "global")
continue; // can't be shadowed
const auto KBKEY = xkb_keysym_from_name(k.key.c_str(), XKB_KEYSYM_CASE_INSENSITIVE);
const auto KBKEYUPPER = xkb_keysym_to_upper(KBKEY);
@@ -469,11 +507,11 @@ bool CKeybindManager::handleVT(xkb_keysym_t keysym) {
// vtnr is bugged for some reason.
unsigned int ttynum = 0;
#if defined(__linux__) || defined(__NetBSD__) || defined(__OpenBSD__)
#if defined(VT_GETSTATE)
struct vt_stat st;
if (!ioctl(0, VT_GETSTATE, &st))
ttynum = st.v_active;
#elif defined(__DragonFly__) || defined(__FreeBSD__)
#elif defined(VT_GETACTIVE)
int vt;
if (!ioctl(0, VT_GETACTIVE, &vt))
ttynum = vt;
@@ -691,41 +729,29 @@ void CKeybindManager::changeworkspace(std::string args) {
int workspaceToChangeTo = 0;
std::string workspaceName = "";
// Flag needed so that the previous workspace is not recorded when switching
// to a previous workspace.
bool isSwitchingToPrevious = false;
// Workspace_back_and_forth being enabled means that an attempt to switch to
// the current workspace will instead switch to the previous.
static auto* const PBACKANDFORTH = &g_pConfigManager->getConfigValuePtr("binds:workspace_back_and_forth")->intValue;
static auto* const PALLOWWORKSPACECYCLES = &g_pConfigManager->getConfigValuePtr("binds:allow_workspace_cycles")->intValue;
bool internal = false;
const auto PMONITOR = g_pCompositor->m_pLastMonitor;
const auto PCURRENTWORKSPACE = g_pCompositor->getWorkspaceByID(PMONITOR->activeWorkspace);
if (args.find("[internal]") == 0) {
workspaceToChangeTo = std::stoi(args.substr(10));
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(workspaceToChangeTo);
if (PWORKSPACE)
workspaceName = PWORKSPACE->m_szName;
internal = true;
} else if (args.find("previous") == 0) {
const auto PCURRENTWORKSPACE = g_pCompositor->getWorkspaceByID(g_pCompositor->m_pLastMonitor->activeWorkspace);
const bool EXPLICITPREVIOUS = args.find("previous") == 0;
if (args.find("previous") == 0) {
// Do nothing if there's no previous workspace, otherwise switch to it.
if (PCURRENTWORKSPACE->m_sPrevWorkspace.iID == -1) {
Debug::log(LOG, "No previous workspace to change to");
return;
} else {
workspaceToChangeTo = PCURRENTWORKSPACE->m_sPrevWorkspace.iID;
workspaceToChangeTo = PCURRENTWORKSPACE->m_iID;
if (const auto PWORKSPACETOCHANGETO = g_pCompositor->getWorkspaceByID(workspaceToChangeTo); PWORKSPACETOCHANGETO)
if (const auto PWORKSPACETOCHANGETO = g_pCompositor->getWorkspaceByID(PCURRENTWORKSPACE->m_sPrevWorkspace.iID); PWORKSPACETOCHANGETO)
workspaceName = PWORKSPACETOCHANGETO->m_szName;
else
workspaceName = PCURRENTWORKSPACE->m_sPrevWorkspace.name.empty() ? std::to_string(workspaceToChangeTo) : PCURRENTWORKSPACE->m_sPrevWorkspace.name;
// If the previous workspace ID isn't reset, cycles can form when continually going
// to the previous workspace again and again.
static auto* const PALLOWWORKSPACECYCLES = &g_pConfigManager->getConfigValuePtr("binds:allow_workspace_cycles")->intValue;
if (!*PALLOWWORKSPACECYCLES)
PCURRENTWORKSPACE->m_sPrevWorkspace = {-1, ""};
else
isSwitchingToPrevious = true;
workspaceName =
PCURRENTWORKSPACE->m_sPrevWorkspace.name.empty() ? std::to_string(PCURRENTWORKSPACE->m_sPrevWorkspace.iID) : PCURRENTWORKSPACE->m_sPrevWorkspace.name;
}
} else {
workspaceToChangeTo = getWorkspaceIDFromString(args, workspaceName);
@@ -736,194 +762,81 @@ void CKeybindManager::changeworkspace(std::string args) {
return;
}
// Workspace_back_and_forth being enabled means that an attempt to switch to
// the current workspace will instead switch to the previous.
const auto PCURRENTWORKSPACE = g_pCompositor->getWorkspaceByID(g_pCompositor->m_pLastMonitor->activeWorkspace);
static auto* const PBACKANDFORTH = &g_pConfigManager->getConfigValuePtr("binds:workspace_back_and_forth")->intValue;
if (*PBACKANDFORTH && PCURRENTWORKSPACE && PCURRENTWORKSPACE->m_iID == workspaceToChangeTo && PCURRENTWORKSPACE->m_sPrevWorkspace.iID != -1 && !internal) {
const auto PPREVWORKSPACE = g_pCompositor->getWorkspaceByID(PCURRENTWORKSPACE->m_sPrevWorkspace.iID);
workspaceToChangeTo = PCURRENTWORKSPACE->m_sPrevWorkspace.iID;
if (PPREVWORKSPACE)
workspaceName = PPREVWORKSPACE->m_szName;
else
workspaceName = PCURRENTWORKSPACE->m_sPrevWorkspace.name.empty() ? std::to_string(workspaceToChangeTo) : PCURRENTWORKSPACE->m_sPrevWorkspace.name;
// If the previous workspace ID isn't reset, cycles can form when continually going
// to the previous workspace again and again.
static auto* const PALLOWWORKSPACECYCLES = &g_pConfigManager->getConfigValuePtr("binds:allow_workspace_cycles")->intValue;
if (!*PALLOWWORKSPACECYCLES)
PCURRENTWORKSPACE->m_sPrevWorkspace = {-1, ""};
else
isSwitchingToPrevious = true;
} else if (PCURRENTWORKSPACE && PCURRENTWORKSPACE->m_iID == workspaceToChangeTo && !internal)
return;
// remove constraints
g_pInputManager->unconstrainMouse();
g_pInputManager->m_bEmptyFocusCursorSet = false;
// if it's not internal, we will unfocus to prevent stuck focus
if (!internal)
g_pCompositor->focusWindow(nullptr);
if (workspaceToChangeTo == PCURRENTWORKSPACE->m_iID) {
if ((!*PBACKANDFORTH && !EXPLICITPREVIOUS) || PCURRENTWORKSPACE->m_sPrevWorkspace.iID == -1)
return;
// if it exists, we warp to it
if (g_pCompositor->getWorkspaceByID(workspaceToChangeTo)) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(g_pCompositor->getWorkspaceByID(workspaceToChangeTo)->m_iMonitorID);
auto pWorkspaceToChangeTo = g_pCompositor->getWorkspaceByID(PCURRENTWORKSPACE->m_sPrevWorkspace.iID);
const auto PWORKSPACETOCHANGETO = g_pCompositor->getWorkspaceByID(workspaceToChangeTo);
g_pInputManager->releaseAllMouseButtons();
if (!isSwitchingToPrevious && !internal) {
// Remember previous workspace.
PWORKSPACETOCHANGETO->m_sPrevWorkspace.iID = g_pCompositor->m_pLastMonitor->activeWorkspace;
PWORKSPACETOCHANGETO->m_sPrevWorkspace.name = g_pCompositor->getWorkspaceByID(g_pCompositor->m_pLastMonitor->activeWorkspace)->m_szName;
}
if (pWorkspaceToChangeTo) {
const auto PMONITORWORKSPACEOWNER = PMONITOR->ID == pWorkspaceToChangeTo->m_iMonitorID ? PMONITOR : g_pCompositor->getMonitorFromID(pWorkspaceToChangeTo->m_iMonitorID);
if (g_pCompositor->isWorkspaceSpecial(workspaceToChangeTo))
PWORKSPACETOCHANGETO->m_iMonitorID = PMONITOR->ID;
if (!PMONITORWORKSPACEOWNER)
return;
// if it's not visible, make it visible.
if (!g_pCompositor->isWorkspaceVisible(workspaceToChangeTo)) {
const auto OLDWORKSPACEID = PMONITOR->activeWorkspace;
g_pCompositor->setActiveMonitor(PMONITORWORKSPACEOWNER);
// fix pinned windows
for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_iWorkspaceID == PMONITOR->activeWorkspace && w->m_bPinned) {
w->m_iWorkspaceID = workspaceToChangeTo;
}
const auto PREVWSDATA = pWorkspaceToChangeTo->m_sPrevWorkspace;
PMONITORWORKSPACEOWNER->changeWorkspace(pWorkspaceToChangeTo);
if (PMONITOR != PMONITORWORKSPACEOWNER) {
g_pCompositor->warpCursorTo(PMONITORWORKSPACEOWNER->vecPosition + PMONITORWORKSPACEOWNER->vecSize / 2.f);
g_pCompositor->setActiveMonitor(PMONITORWORKSPACEOWNER);
if (const auto PLASTWINDOW = pWorkspaceToChangeTo->getLastFocusedWindow(); PLASTWINDOW)
g_pCompositor->focusWindow(PLASTWINDOW);
else if (const auto PFIRSTWINDOW = g_pCompositor->getFirstWindowOnWorkspace(pWorkspaceToChangeTo->m_iID); PFIRSTWINDOW)
g_pCompositor->focusWindow(PFIRSTWINDOW);
else
g_pCompositor->focusWindow(nullptr);
}
// change it
if (!g_pCompositor->isWorkspaceSpecial(workspaceToChangeTo))
PMONITOR->activeWorkspace = workspaceToChangeTo;
else
PMONITOR->specialWorkspaceID = workspaceToChangeTo;
// here and only here begin anim. we don't want to anim visible workspaces on other monitors.
// check if anim left or right
const auto ANIMTOLEFT = workspaceToChangeTo > OLDWORKSPACEID;
// start anim on old workspace
g_pCompositor->getWorkspaceByID(OLDWORKSPACEID)->startAnim(false, ANIMTOLEFT);
// start anim on new workspace
PWORKSPACETOCHANGETO->startAnim(true, ANIMTOLEFT);
g_pEventManager->postEvent(SHyprIPCEvent{"workspace", PWORKSPACETOCHANGETO->m_szName});
EMIT_HOOK_EVENT("workspace", PWORKSPACETOCHANGETO);
} else {
pWorkspaceToChangeTo = g_pCompositor->createNewWorkspace(PCURRENTWORKSPACE->m_sPrevWorkspace.iID, PMONITOR->ID, PCURRENTWORKSPACE->m_sPrevWorkspace.name);
PMONITOR->changeWorkspace(pWorkspaceToChangeTo);
}
// If the monitor is not the one our cursor's at, warp to it.
const bool anotherMonitor = PMONITOR != g_pCompositor->m_pLastMonitor;
if (anotherMonitor)
g_pCompositor->warpCursorTo(PMONITOR->vecPosition + PMONITOR->vecSize / 2.f);
// set active and deactivate all other in wlr
g_pCompositor->deactivateAllWLRWorkspaces(PWORKSPACETOCHANGETO->m_pWlrHandle);
PWORKSPACETOCHANGETO->setActive(true);
// recalc layout
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PWORKSPACETOCHANGETO->m_iMonitorID);
Debug::log(LOG, "Changed to workspace %i", workspaceToChangeTo);
// focus
if (const auto PWINDOW = PWORKSPACETOCHANGETO->getLastFocusedWindow(); PWINDOW) {
// warp and focus
if (anotherMonitor)
g_pCompositor->warpCursorTo(PWINDOW->m_vRealPosition.vec() + PWINDOW->m_vRealSize.vec() / 2.f);
g_pCompositor->focusWindow(PWINDOW, g_pXWaylandManager->getWindowSurface(PWINDOW));
if (g_pCompositor->cursorOnReservedArea()) // fix focus on bars etc
g_pInputManager->refocus();
} else if (g_pCompositor->getWindowsOnWorkspace(PWORKSPACETOCHANGETO->m_iID) > 0)
g_pInputManager->refocus();
else {
// if there are no windows on the workspace, just unfocus the window on the previous workspace
g_pCompositor->focusWindow(nullptr);
}
// set the new monitor as the last (no warps would bug otherwise)
g_pCompositor->setActiveMonitor(g_pCompositor->getMonitorFromID(PWORKSPACETOCHANGETO->m_iMonitorID));
// mark the monitor dirty
g_pHyprRenderer->damageMonitor(PMONITOR);
if (*PALLOWWORKSPACECYCLES)
pWorkspaceToChangeTo->m_sPrevWorkspace = {PCURRENTWORKSPACE->m_iID, PCURRENTWORKSPACE->m_szName};
else if (!EXPLICITPREVIOUS)
pWorkspaceToChangeTo->m_sPrevWorkspace = {-1, ""};
return;
}
// Workspace doesn't exist, create and switch
const auto BOUNDMON = g_pConfigManager->getBoundMonitorForWS(workspaceName);
auto pWorkspaceToChangeTo = g_pCompositor->getWorkspaceByID(workspaceToChangeTo);
if (!pWorkspaceToChangeTo)
pWorkspaceToChangeTo = g_pCompositor->createNewWorkspace(workspaceToChangeTo, PMONITOR->ID, workspaceName);
const auto PMONITOR = BOUNDMON ? BOUNDMON : g_pCompositor->getMonitorFromCursor();
const auto OLDWORKSPACE = PMONITOR->activeWorkspace;
// get anim direction
const auto ANIMTOLEFT = workspaceToChangeTo > OLDWORKSPACE;
// start anim on old workspace
if (const auto POLDWORKSPACE = g_pCompositor->getWorkspaceByID(OLDWORKSPACE); POLDWORKSPACE)
POLDWORKSPACE->startAnim(false, ANIMTOLEFT);
const auto PWORKSPACE = g_pCompositor->createNewWorkspace(workspaceToChangeTo, PMONITOR->ID, workspaceName);
const bool ANOTHERMONITOR = PMONITOR != g_pCompositor->m_pLastMonitor;
if (!isSwitchingToPrevious) {
// Remember previous workspace.
PWORKSPACE->m_sPrevWorkspace.iID = OLDWORKSPACE;
if (const auto POLDWORKSPACE = g_pCompositor->getWorkspaceByID(OLDWORKSPACE); POLDWORKSPACE)
PWORKSPACE->m_sPrevWorkspace.name = POLDWORKSPACE->m_szName;
if (pWorkspaceToChangeTo->m_bIsSpecialWorkspace) {
PMONITOR->setSpecialWorkspace(pWorkspaceToChangeTo);
return;
}
// start anim on new workspace
PWORKSPACE->startAnim(true, ANIMTOLEFT);
g_pInputManager->releaseAllMouseButtons();
PMONITOR->specialWorkspaceID = 0;
const auto PMONITORWORKSPACEOWNER = PMONITOR->ID == pWorkspaceToChangeTo->m_iMonitorID ? PMONITOR : g_pCompositor->getMonitorFromID(pWorkspaceToChangeTo->m_iMonitorID);
// fix pinned windows
for (auto& w : g_pCompositor->m_vWindows) {
if (w->m_iWorkspaceID == PMONITOR->activeWorkspace && w->m_bPinned) {
w->m_iWorkspaceID = workspaceToChangeTo;
}
g_pCompositor->setActiveMonitor(PMONITORWORKSPACEOWNER);
PMONITORWORKSPACEOWNER->changeWorkspace(pWorkspaceToChangeTo);
if (PMONITOR != PMONITORWORKSPACEOWNER) {
g_pCompositor->warpCursorTo(PMONITORWORKSPACEOWNER->vecPosition + PMONITORWORKSPACEOWNER->vecSize / 2.f);
if (const auto PLASTWINDOW = pWorkspaceToChangeTo->getLastFocusedWindow(); PLASTWINDOW)
g_pCompositor->focusWindow(PLASTWINDOW);
else if (const auto PFIRSTWINDOW = g_pCompositor->getFirstWindowOnWorkspace(pWorkspaceToChangeTo->m_iID); PFIRSTWINDOW)
g_pCompositor->focusWindow(PFIRSTWINDOW);
else
g_pCompositor->focusWindow(nullptr);
}
if (!g_pCompositor->isWorkspaceSpecial(workspaceToChangeTo))
PMONITOR->activeWorkspace = workspaceToChangeTo;
else
PMONITOR->specialWorkspaceID = workspaceToChangeTo;
// set active and deactivate all other
g_pCompositor->deactivateAllWLRWorkspaces(PWORKSPACE->m_pWlrHandle);
PWORKSPACE->setActive(true);
// mark the monitor dirty
g_pHyprRenderer->damageMonitor(PMONITOR);
// some stuf with the cursor and focus
if (g_pCompositor->m_pLastMonitor != PMONITOR)
g_pCompositor->warpCursorTo(PMONITOR->vecPosition + PMONITOR->vecSize / 2.f);
g_pEventManager->postEvent(SHyprIPCEvent{"workspace", PWORKSPACE->m_szName});
EMIT_HOOK_EVENT("workspace", PWORKSPACE);
g_pCompositor->setActiveMonitor(PMONITOR);
// focus (clears the last)
g_pInputManager->refocus();
// Events
if (ANOTHERMONITOR)
g_pCompositor->warpCursorTo(PMONITOR->vecPosition + PMONITOR->vecSize / 2.f);
Debug::log(LOG, "Changed to workspace %i", workspaceToChangeTo);
pWorkspaceToChangeTo->m_sPrevWorkspace = {PCURRENTWORKSPACE->m_iID, PCURRENTWORKSPACE->m_szName};
}
void CKeybindManager::fullscreenActive(std::string args) {
@@ -952,8 +865,6 @@ void CKeybindManager::moveActiveToWorkspace(std::string args) {
if (!PWINDOW)
return;
const auto OLDWORKSPACE = g_pCompositor->getWorkspaceByID(PWINDOW->m_iWorkspaceID);
// hack
std::string workspaceName;
const auto WORKSPACEID = getWorkspaceIDFromString(args, workspaceName);
@@ -968,64 +879,27 @@ void CKeybindManager::moveActiveToWorkspace(std::string args) {
return;
}
auto PSAVEDSIZE = PWINDOW->m_bIsFloating && PWINDOW->m_bIsFullscreen ? PWINDOW->m_vLastFloatingSize : PWINDOW->m_vRealSize.goalv();
auto PSAVEDPOS = PWINDOW->m_bIsFloating && PWINDOW->m_bIsFullscreen ? PWINDOW->m_vLastFloatingPosition : PWINDOW->m_vRealPosition.goalv();
const bool WASFULLSCREEN = PWINDOW->m_bIsFullscreen;
auto pWorkspace = g_pCompositor->getWorkspaceByID(WORKSPACEID);
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(PWINDOW);
g_pHyprRenderer->damageWindow(PWINDOW);
auto PWORKSPACE = g_pCompositor->getWorkspaceByID(WORKSPACEID);
if (PWORKSPACE == OLDWORKSPACE) {
Debug::log(LOG, "Not moving to workspace because it didn't change.");
return;
}
if (!PWORKSPACE) {
// create
PWORKSPACE = g_pCompositor->createNewWorkspace(WORKSPACEID, OLDWORKSPACE->m_iMonitorID, workspaceName);
}
PWINDOW->moveToWorkspace(PWORKSPACE->m_iID);
PWINDOW->m_iMonitorID = PWORKSPACE->m_iMonitorID;
if (PWORKSPACE->m_bHasFullscreenWindow) {
g_pCompositor->setWindowFullscreen(g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID), false, FULLSCREEN_FULL);
}
// Hack: So that the layout doesnt find our window at the cursor
PWINDOW->m_vPosition = Vector2D(-42069, -42069);
g_pLayoutManager->getCurrentLayout()->onWindowCreated(PWINDOW);
// and restore it
if (PWINDOW->m_bIsFloating) {
PWINDOW->m_vRealSize.setValueAndWarp(PSAVEDSIZE);
PWINDOW->m_vRealPosition.setValueAndWarp(PSAVEDPOS - g_pCompositor->getMonitorFromID(OLDWORKSPACE->m_iMonitorID)->vecPosition +
g_pCompositor->getMonitorFromID(PWORKSPACE->m_iMonitorID)->vecPosition);
PWINDOW->m_vPosition = PWINDOW->m_vRealPosition.vec();
}
if (WASFULLSCREEN)
g_pCompositor->setWindowFullscreen(PWINDOW, true, OLDWORKSPACE->m_efFullscreenMode);
if (!g_pCompositor->isWorkspaceSpecial(WORKSPACEID)) {
g_pKeybindManager->changeworkspace(args);
g_pCompositor->focusWindow(PWINDOW);
if (pWorkspace) {
g_pCompositor->moveWindowToWorkspaceSafe(PWINDOW, pWorkspace);
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWorkspace->m_iMonitorID);
g_pCompositor->setActiveMonitor(PMONITOR);
PMONITOR->changeWorkspace(pWorkspace);
} else {
g_pHyprRenderer->damageMonitor(g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID));
g_pInputManager->refocus();
pWorkspace = g_pCompositor->createNewWorkspace(WORKSPACEID, PWINDOW->m_iMonitorID, workspaceName);
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWorkspace->m_iMonitorID);
g_pCompositor->moveWindowToWorkspaceSafe(PWINDOW, pWorkspace);
PMONITOR->changeWorkspace(pWorkspace);
}
PWINDOW->updateToplevel();
g_pHyprRenderer->damageMonitor(g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID));
g_pCompositor->focusWindow(PWINDOW);
g_pCompositor->warpCursorTo(PWINDOW->middle());
}
void CKeybindManager::moveActiveToWorkspaceSilent(std::string args) {
// hacky, but works lol
// TODO: this sucks
CWindow* PWINDOW = nullptr;
const auto ORIGINALARGS = args;
@@ -1040,67 +914,34 @@ void CKeybindManager::moveActiveToWorkspaceSilent(std::string args) {
if (!PWINDOW)
return;
int workspaceToMoveTo = 0;
std::string workspaceName = "";
std::string workspaceName = "";
workspaceToMoveTo = getWorkspaceIDFromString(args, workspaceName);
const int WORKSPACEID = getWorkspaceIDFromString(args, workspaceName);
if (workspaceToMoveTo == INT_MAX) {
if (WORKSPACEID == INT_MAX) {
Debug::log(ERR, "Error in moveActiveToWorkspaceSilent, invalid value");
return;
}
const auto PMONITOR = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID);
if (workspaceToMoveTo == PWINDOW->m_iWorkspaceID)
if (WORKSPACEID == PWINDOW->m_iWorkspaceID)
return;
// may be null until later!
auto PWORKSPACE = g_pCompositor->getWorkspaceByID(workspaceToMoveTo);
g_pHyprRenderer->damageWindow(PWINDOW);
auto PMONITORNEW = PWORKSPACE ? g_pCompositor->getMonitorFromID(PWORKSPACE->m_iMonitorID) : PMONITOR;
if (!PWORKSPACE) {
const auto BOUNDMON = g_pConfigManager->getBoundMonitorForWS(workspaceName);
if (BOUNDMON)
PMONITORNEW = BOUNDMON;
auto pWorkspace = g_pCompositor->getWorkspaceByID(WORKSPACEID);
const auto OLDMIDDLE = PWINDOW->middle();
if (pWorkspace) {
g_pCompositor->moveWindowToWorkspaceSafe(PWINDOW, pWorkspace);
} else {
pWorkspace = g_pCompositor->createNewWorkspace(WORKSPACEID, PWINDOW->m_iMonitorID, workspaceName);
g_pCompositor->moveWindowToWorkspaceSafe(PWINDOW, pWorkspace);
}
const auto OLDWORKSPACEIDONMONITOR = PMONITORNEW->activeWorkspace;
const auto OLDWORKSPACEIDRETURN = PMONITOR->activeWorkspace;
const auto POLDWORKSPACEONMON = g_pCompositor->getWorkspaceByID(OLDWORKSPACEIDONMONITOR);
const auto POLDWORKSPACEIDRETURN = g_pCompositor->getWorkspaceByID(OLDWORKSPACEIDRETURN);
g_pEventManager->m_bIgnoreEvents = true;
moveActiveToWorkspace(ORIGINALARGS);
PWORKSPACE = g_pCompositor->getWorkspaceByID(workspaceToMoveTo);
changeworkspace("[internal]" + std::to_string(OLDWORKSPACEIDONMONITOR));
changeworkspace("[internal]" + std::to_string(OLDWORKSPACEIDRETURN));
// revert animations
PWORKSPACE->m_vRenderOffset.setValueAndWarp(Vector2D(0, 0));
PWORKSPACE->m_fAlpha.setValueAndWarp(0.f);
POLDWORKSPACEIDRETURN->m_vRenderOffset.setValueAndWarp(Vector2D(0, 0));
POLDWORKSPACEIDRETURN->m_fAlpha.setValueAndWarp(1.f);
POLDWORKSPACEONMON->m_vRenderOffset.setValueAndWarp(Vector2D(0, 0));
POLDWORKSPACEONMON->m_fAlpha.setValueAndWarp(1.f);
g_pEventManager->m_bIgnoreEvents = false;
// manually post event cuz it got ignored above
g_pEventManager->postEvent(SHyprIPCEvent{"movewindow", getFormat("%x,%s", PWINDOW, PWORKSPACE->m_szName.c_str())});
EMIT_HOOK_EVENT("moveWindow", (std::vector<void*>{PWINDOW, PWORKSPACE}));
PWINDOW->m_iWorkspaceID = OLDWORKSPACEIDRETURN;
const auto PNEXTCANDIDATE = g_pLayoutManager->getCurrentLayout()->getNextWindowCandidate(PWINDOW);
PWINDOW->m_iWorkspaceID = workspaceToMoveTo;
g_pCompositor->focusWindow(PNEXTCANDIDATE);
if (const auto PATCOORDS = g_pCompositor->vectorToWindowIdeal(OLDMIDDLE); PATCOORDS && PATCOORDS != PWINDOW)
g_pCompositor->focusWindow(PATCOORDS);
else
g_pInputManager->refocus();
}
void CKeybindManager::moveFocusTo(std::string args) {
@@ -1112,9 +953,10 @@ void CKeybindManager::moveFocusTo(std::string args) {
}
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow;
if (!PLASTWINDOW)
if (!PLASTWINDOW) {
tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(arg));
return;
}
// remove constraints
g_pInputManager->unconstrainMouse();
@@ -1149,16 +991,29 @@ void CKeybindManager::moveFocusTo(std::string args) {
}
};
const auto PWINDOWTOCHANGETO = g_pCompositor->getWindowInDirection(PLASTWINDOW, arg);
const auto PWINDOWTOCHANGETO = PLASTWINDOW->m_bIsFullscreen ? g_pCompositor->getNextWindowOnWorkspace(PLASTWINDOW, arg == 'u' || arg == 't' || arg == 'r') :
g_pCompositor->getWindowInDirection(PLASTWINDOW, arg);
// Found window in direction, switch to it
if (PWINDOWTOCHANGETO) {
switchToWindow(PWINDOWTOCHANGETO);
} else {
const auto PWINDOWNEXT = g_pCompositor->getNextWindowOnWorkspace(PLASTWINDOW, true);
if (PWINDOWNEXT) {
switchToWindow(PWINDOWNEXT);
}
return;
}
Debug::log(LOG, "No window found in direction %c, looking for a monitor", arg);
if (tryMoveFocusToMonitor(g_pCompositor->getMonitorInDirection(arg)))
return;
static auto* const PNOFALLBACK = &g_pConfigManager->getConfigValuePtr("general:no_focus_fallback")->intValue;
if (*PNOFALLBACK)
return;
Debug::log(LOG, "No monitor found in direction %c, falling back to next window on current workspace", arg);
const auto PWINDOWNEXT = g_pCompositor->getNextWindowOnWorkspace(PLASTWINDOW, true);
if (PWINDOWNEXT)
switchToWindow(PWINDOWNEXT);
}
void CKeybindManager::focusUrgentOrLast(std::string args) {
@@ -1232,28 +1087,36 @@ void CKeybindManager::focusCurrentOrLast(std::string args) {
switchToWindow(PWINDOWPREV);
}
void CKeybindManager::moveActiveTo(std::string args) {
char arg = args[0];
void CKeybindManager::swapActive(std::string args) {
char arg = args[0];
const auto LASTMONITOR = g_pCompositor->m_pLastMonitor;
if (!isDirection(args)) {
Debug::log(ERR, "Cannot move window in direction %c, unsupported direction. Supported: l,r,u/t,d/b", arg);
return;
}
Debug::log(LOG, "Swapping active window in direction %c", arg);
const auto PLASTWINDOW = g_pCompositor->m_pLastWindow;
if (!PLASTWINDOW || PLASTWINDOW->m_bIsFullscreen)
return;
const auto PWINDOWTOCHANGETO = g_pCompositor->getWindowInDirection(PLASTWINDOW, arg);
if (!PWINDOWTOCHANGETO)
return;
g_pLayoutManager->getCurrentLayout()->switchWindows(PLASTWINDOW, PWINDOWTOCHANGETO);
g_pCompositor->warpCursorTo(PLASTWINDOW->m_vRealPosition.vec() + PLASTWINDOW->m_vRealSize.vec() / 2.0);
}
void CKeybindManager::moveActiveTo(std::string args) {
char arg = args[0];
if (args.find("mon:") == 0) {
// hack: save the active window
const auto PACTIVE = g_pCompositor->m_pLastWindow;
// monitor
focusMonitor(args.substr(4));
if (LASTMONITOR == g_pCompositor->m_pLastMonitor) {
Debug::log(ERR, "moveActiveTo: moving to an invalid mon");
const auto PNEWMONITOR = g_pCompositor->getMonitorFromString(args.substr(4));
if (!PNEWMONITOR)
return;
}
// restore the active
g_pCompositor->focusWindow(PACTIVE);
moveActiveToWorkspace(std::to_string(g_pCompositor->m_pLastMonitor->activeWorkspace));
moveActiveToWorkspace(std::to_string(PNEWMONITOR->activeWorkspace));
return;
}
@@ -1267,12 +1130,22 @@ void CKeybindManager::moveActiveTo(std::string args) {
if (!PLASTWINDOW || PLASTWINDOW->m_bIsFullscreen)
return;
// If the window to change to is on the same workspace, switch them
const auto PWINDOWTOCHANGETO = g_pCompositor->getWindowInDirection(PLASTWINDOW, arg);
if (PWINDOWTOCHANGETO && PWINDOWTOCHANGETO->m_iWorkspaceID == PLASTWINDOW->m_iWorkspaceID) {
g_pLayoutManager->getCurrentLayout()->switchWindows(PLASTWINDOW, PWINDOWTOCHANGETO);
g_pCompositor->warpCursorTo(PLASTWINDOW->m_vRealPosition.vec() + PLASTWINDOW->m_vRealSize.vec() / 2.0);
return;
}
if (!PWINDOWTOCHANGETO)
// Otherwise, we always want to move to the next monitor in that direction
const auto PMONITORTOCHANGETO = g_pCompositor->getMonitorInDirection(arg);
if (!PMONITORTOCHANGETO)
return;
g_pLayoutManager->getCurrentLayout()->switchWindows(PLASTWINDOW, PWINDOWTOCHANGETO);
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(PMONITORTOCHANGETO->activeWorkspace);
moveActiveToWorkspace(PWORKSPACE->getConfigName());
}
void CKeybindManager::toggleGroup(std::string args) {
@@ -1390,11 +1263,7 @@ void CKeybindManager::alterSplitRatio(std::string args) {
void CKeybindManager::focusMonitor(std::string arg) {
const auto PMONITOR = g_pCompositor->getMonitorFromString(arg);
if (!PMONITOR || PMONITOR == g_pCompositor->m_pLastMonitor)
return;
changeworkspace("[internal]" + std::to_string(PMONITOR->activeWorkspace));
tryMoveFocusToMonitor(PMONITOR);
}
void CKeybindManager::moveCursorToCorner(std::string arg) {
@@ -1438,6 +1307,34 @@ void CKeybindManager::moveCursorToCorner(std::string arg) {
}
}
void CKeybindManager::moveCursor(std::string args) {
std::string x_str, y_str;
int x, y;
size_t i = args.find_first_of(' ');
if (i == std::string::npos) {
Debug::log(ERR, "moveCursor, takes 2 arguments.");
return;
}
x_str = args.substr(0, i);
y_str = args.substr(i + 1);
if (!isNumber(x_str)) {
Debug::log(ERR, "moveCursor, x argument has to be a number.");
return;
}
if (!isNumber(y_str)) {
Debug::log(ERR, "moveCursor, y argument has to be a number.");
return;
}
x = std::stoi(x_str);
y = std::stoi(y_str);
wlr_cursor_warp(g_pCompositor->m_sWLRCursor, g_pCompositor->m_sSeat.mouse->mouse, x, y);
}
void CKeybindManager::workspaceOpt(std::string args) {
// current workspace
@@ -1589,22 +1486,13 @@ void CKeybindManager::toggleSpecialWorkspace(std::string args) {
}
if (requestedWorkspaceIsAlreadyOpen && specialOpenOnMonitor == workspaceID)
Debug::log(LOG, "Toggling special workspace %d to closed");
Debug::log(LOG, "Toggling special workspace %d to closed", workspaceID);
else
Debug::log(LOG, "Toggling special workspace %d to open");
Debug::log(LOG, "Toggling special workspace %d to open", workspaceID);
if (requestedWorkspaceIsAlreadyOpen && specialOpenOnMonitor == workspaceID) {
// already open on this monitor
PMONITOR->specialWorkspaceID = 0;
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PMONITOR->ID);
g_pCompositor->getWorkspaceByID(workspaceID)->startAnim(false, false);
if (const auto PWINDOW = g_pCompositor->getWorkspaceByID(PMONITOR->activeWorkspace)->getLastFocusedWindow(); PWINDOW)
g_pCompositor->focusWindow(PWINDOW);
else
g_pInputManager->refocus();
PMONITOR->setSpecialWorkspace(nullptr);
} else if (requestedWorkspaceIsAlreadyOpen) {
// already open on another monitor
@@ -1630,13 +1518,6 @@ void CKeybindManager::toggleSpecialWorkspace(std::string args) {
g_pInputManager->refocus();
} else {
// not open anywhere
if (specialOpenOnMonitor) {
g_pCompositor->getWorkspaceByID(PMONITOR->specialWorkspaceID)->startAnim(false, false);
PMONITOR->specialWorkspaceID = 0;
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PMONITOR->ID);
}
auto PSPECIALWORKSPACE = g_pCompositor->getWorkspaceByID(workspaceID);
if (!PSPECIALWORKSPACE) {
@@ -1644,16 +1525,7 @@ void CKeybindManager::toggleSpecialWorkspace(std::string args) {
PSPECIALWORKSPACE = g_pCompositor->createNewWorkspace(workspaceID, PMONITOR->ID, workspaceName);
}
PMONITOR->specialWorkspaceID = workspaceID;
g_pLayoutManager->getCurrentLayout()->recalculateMonitor(PMONITOR->ID);
PSPECIALWORKSPACE->m_iMonitorID = PMONITOR->ID;
PSPECIALWORKSPACE->startAnim(true, true);
if (const auto PWINDOW = PSPECIALWORKSPACE->getLastFocusedWindow(); PWINDOW)
g_pCompositor->focusWindow(PWINDOW);
else
g_pInputManager->refocus();
PMONITOR->setSpecialWorkspace(PSPECIALWORKSPACE);
}
}
@@ -1661,6 +1533,9 @@ void CKeybindManager::forceRendererReload(std::string args) {
bool overAgain = false;
for (auto& m : g_pCompositor->m_vMonitors) {
if (!m->output)
continue;
auto rule = g_pConfigManager->getMonitorRuleFor(m->szName, m->output->description ? m->output->description : "");
if (!g_pHyprRenderer->applyMonitorRule(m.get(), &rule, true)) {
overAgain = true;
@@ -1784,6 +1659,16 @@ void CKeybindManager::focusWindow(std::string regexp) {
Debug::log(LOG, "Focusing to window name: %s", PWINDOW->m_szTitle.c_str());
if (g_pCompositor->m_pLastMonitor->activeWorkspace != PWINDOW->m_iWorkspaceID) {
Debug::log(LOG, "Fake executing workspace to move focus");
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(PWINDOW->m_iWorkspaceID);
if (!PWORKSPACE) {
Debug::log(ERR, "BUG THIS: null workspace in focusWindow");
return;
}
changeworkspace(PWORKSPACE->getConfigName());
}
if (PWINDOW->isHidden() && PWINDOW->m_sGroupData.pNextWindow) {
// grouped, change the current to us
PWINDOW->setGroupCurrent(PWINDOW);
@@ -1837,15 +1722,16 @@ void CKeybindManager::pass(std::string regexp) {
return;
}
const auto XWTOXW = PWINDOW->m_bIsX11 && g_pCompositor->m_pLastWindow && g_pCompositor->m_pLastWindow->m_bIsX11;
const auto SL = Vector2D(g_pCompositor->m_sSeat.seat->pointer_state.sx, g_pCompositor->m_sSeat.seat->pointer_state.sy);
const auto XWTOXW = PWINDOW->m_bIsX11 && g_pCompositor->m_pLastWindow && g_pCompositor->m_pLastWindow->m_bIsX11;
const auto SL = Vector2D(g_pCompositor->m_sSeat.seat->pointer_state.sx, g_pCompositor->m_sSeat.seat->pointer_state.sy);
uint32_t keycodes[32] = {0};
// pass all mf shit
if (!XWTOXW) {
if (g_pKeybindManager->m_uLastCode != 0)
wlr_seat_keyboard_enter(g_pCompositor->m_sSeat.seat, g_pXWaylandManager->getWindowSurface(PWINDOW), KEYBOARD->keycodes, KEYBOARD->num_keycodes, &KEYBOARD->modifiers);
wlr_seat_keyboard_enter(g_pCompositor->m_sSeat.seat, PWINDOW->m_pWLSurface.wlr(), keycodes, 0, &KEYBOARD->modifiers);
else
wlr_seat_pointer_enter(g_pCompositor->m_sSeat.seat, g_pXWaylandManager->getWindowSurface(PWINDOW), 1, 1);
wlr_seat_pointer_enter(g_pCompositor->m_sSeat.seat, PWINDOW->m_pWLSurface.wlr(), 1, 1);
}
wlr_keyboard_modifiers kbmods = {g_pInputManager->accumulateModsFromAllKBs(), 0, 0, 0};
@@ -1891,7 +1777,7 @@ void CKeybindManager::pass(std::string regexp) {
if (g_pKeybindManager->m_uLastCode != 0)
wlr_seat_keyboard_enter(g_pCompositor->m_sSeat.seat, PLASTSRF, KEYBOARD->keycodes, KEYBOARD->num_keycodes, &KEYBOARD->modifiers);
else
wlr_seat_pointer_enter(g_pCompositor->m_sSeat.seat, g_pXWaylandManager->getWindowSurface(PWINDOW), SL.x, SL.y);
wlr_seat_pointer_enter(g_pCompositor->m_sSeat.seat, PWINDOW->m_pWLSurface.wlr(), SL.x, SL.y);
}
void CKeybindManager::layoutmsg(std::string msg) {
@@ -1905,8 +1791,8 @@ void CKeybindManager::toggleOpaque(std::string unused) {
if (!PWINDOW)
return;
PWINDOW->m_sAdditionalConfigData.forceOpaque = !PWINDOW->m_sAdditionalConfigData.forceOpaque;
PWINDOW->m_sAdditionalConfigData.forceOpaqueOverriden = true;
PWINDOW->m_sAdditionalConfigData.forceOpaque = !PWINDOW->m_sAdditionalConfigData.forceOpaque;
PWINDOW->m_sAdditionalConfigData.forceOpaqueOverridden = true;
g_pHyprRenderer->damageWindow(PWINDOW);
}
@@ -2121,3 +2007,16 @@ void CKeybindManager::moveOutOfGroup(std::string args) {
g_pKeybindManager->m_bGroupsLocked = GROUPSLOCKEDPREV;
}
void CKeybindManager::global(std::string args) {
const auto APPID = args.substr(0, args.find_first_of(':'));
const auto NAME = args.substr(args.find_first_of(':') + 1);
if (APPID.empty() || NAME.empty())
return;
if (!g_pProtocolManager->m_pGlobalShortcutsProtocolManager->globalShortcutExists(APPID, NAME))
return;
g_pProtocolManager->m_pGlobalShortcutsProtocolManager->sendGlobalShortcutEvent(APPID, NAME, g_pKeybindManager->m_iPassPressed);
}

View File

@@ -26,8 +26,7 @@ struct SKeybind {
bool shadowed = false;
};
enum eFocusWindowMode
{
enum eFocusWindowMode {
MODE_CLASS_REGEX = 0,
MODE_TITLE_REGEX,
MODE_ADDRESS,
@@ -90,6 +89,8 @@ class CKeybindManager {
void updateXKBTranslationState();
bool ensureMouseBindState();
static bool tryMoveFocusToMonitor(CMonitor* monitor);
// -------------- Dispatchers -------------- //
static void killActive(std::string);
static void kill(std::string);
@@ -107,12 +108,14 @@ class CKeybindManager {
static void focusCurrentOrLast(std::string);
static void centerWindow(std::string);
static void moveActiveTo(std::string);
static void swapActive(std::string);
static void toggleGroup(std::string);
static void changeGroupActive(std::string);
static void alterSplitRatio(std::string);
static void focusMonitor(std::string);
static void toggleSplit(std::string);
static void moveCursorToCorner(std::string);
static void moveCursor(std::string);
static void workspaceOpt(std::string);
static void renameWorkspace(std::string);
static void exitHyprland(std::string);
@@ -139,6 +142,7 @@ class CKeybindManager {
static void lockGroups(std::string);
static void moveIntoGroup(std::string);
static void moveOutOfGroup(std::string);
static void global(std::string);
friend class CCompositor;
friend class CInputManager;

View File

@@ -4,4 +4,6 @@ CProtocolManager::CProtocolManager() {
m_pToplevelExportProtocolManager = std::make_unique<CToplevelExportProtocolManager>();
m_pFractionalScaleProtocolManager = std::make_unique<CFractionalScaleProtocolManager>();
m_pTextInputV1ProtocolManager = std::make_unique<CTextInputV1ProtocolManager>();
m_pGlobalShortcutsProtocolManager = std::make_unique<CGlobalShortcutsProtocolManager>();
m_pScreencopyProtocolManager = std::make_unique<CScreencopyProtocolManager>();
}

View File

@@ -4,6 +4,8 @@
#include "../protocols/ToplevelExport.hpp"
#include "../protocols/FractionalScale.hpp"
#include "../protocols/TextInputV1.hpp"
#include "../protocols/GlobalShortcuts.hpp"
#include "../protocols/Screencopy.hpp"
class CProtocolManager {
public:
@@ -12,6 +14,8 @@ class CProtocolManager {
std::unique_ptr<CToplevelExportProtocolManager> m_pToplevelExportProtocolManager;
std::unique_ptr<CFractionalScaleProtocolManager> m_pFractionalScaleProtocolManager;
std::unique_ptr<CTextInputV1ProtocolManager> m_pTextInputV1ProtocolManager;
std::unique_ptr<CGlobalShortcutsProtocolManager> m_pGlobalShortcutsProtocolManager;
std::unique_ptr<CScreencopyProtocolManager> m_pScreencopyProtocolManager;
};
inline std::unique_ptr<CProtocolManager> g_pProtocolManager;

View File

@@ -177,13 +177,16 @@ bool CHyprXWaylandManager::shouldBeFloated(CWindow* pWindow) {
pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_POPUP_MENU"] ||
pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_DOCK"] ||
pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"] ||
pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_MENU"]) {
pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_MENU"] ||
pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_KDE_NET_WM_WINDOW_TYPE_OVERRIDE"]) {
if (pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"] ||
pWindow->m_uSurface.xwayland->window_type[i] == HYPRATOMS["_NET_WM_WINDOW_TYPE_MENU"])
pWindow->m_bX11ShouldntFocus = true;
pWindow->m_bNoInitialFocus = true;
if (pWindow->m_uSurface.xwayland->window_type[i] != HYPRATOMS["_NET_WM_WINDOW_TYPE_DIALOG"])
pWindow->m_bNoInitialFocus = true;
return true;
}

View File

@@ -33,7 +33,7 @@ void CInputManager::newIdleInhibitor(wlr_idle_inhibitor_v1* pInhibitor) {
PINHIBIT->pWindow = g_pCompositor->getWindowFromSurface(pInhibitor->surface);
if (PINHIBIT->pWindow)
Debug::log(LOG, "IdleInhibitor got window %x (%s)", PINHIBIT->pWindow, PINHIBIT->pWindow->m_szTitle.c_str());
Debug::log(LOG, "IdleInhibitor got window %lx (%s)", PINHIBIT->pWindow, PINHIBIT->pWindow->m_szTitle.c_str());
recheckIdleInhibitorStatus();
}

View File

@@ -47,6 +47,7 @@ void CInputManager::simulateMouseMovement() {
void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
static auto* const PFOLLOWMOUSE = &g_pConfigManager->getConfigValuePtr("input:follow_mouse")->intValue;
static auto* const PMOUSEREFOCUS = &g_pConfigManager->getConfigValuePtr("input:mouse_refocus")->intValue;
static auto* const PMOUSEDPMS = &g_pConfigManager->getConfigValuePtr("misc:mouse_move_enables_dpms")->intValue;
static auto* const PFOLLOWONDND = &g_pConfigManager->getConfigValuePtr("misc:always_follow_on_dnd")->intValue;
static auto* const PHOGFOCUS = &g_pConfigManager->getConfigValuePtr("misc:layers_hog_keyboard_focus")->intValue;
@@ -56,8 +57,11 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
static auto* const PBORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue;
static auto* const PBORDERGRABEXTEND = &g_pConfigManager->getConfigValuePtr("general:extend_border_grab_area")->intValue;
static auto* const PRESIZECURSORICON = &g_pConfigManager->getConfigValuePtr("general:hover_icon_on_border")->intValue;
static auto* const PZOOMFACTOR = &g_pConfigManager->getConfigValuePtr("misc:cursor_zoom_factor")->floatValue;
const auto BORDER_GRAB_AREA = *PRESIZEONBORDER ? *PBORDERSIZE + *PBORDERGRABEXTEND : 0;
const auto FOLLOWMOUSE = *PFOLLOWONDND && m_sDrag.drag ? 1 : *PFOLLOWMOUSE;
m_pFoundSurfaceToFocus = nullptr;
m_pFoundLSToFocus = nullptr;
m_pFoundWindowToFocus = nullptr;
@@ -87,6 +91,9 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
const auto PMONITOR = g_pCompositor->getMonitorFromCursor();
if (*PZOOMFACTOR != 1.f)
g_pHyprRenderer->damageMonitor(PMONITOR);
// constraints
// All constraints TODO: multiple mice?
if (g_pCompositor->m_sSeat.mouse && g_pCompositor->m_sSeat.mouse->currentConstraint && !g_pCompositor->m_sSeat.exclusiveClient && !g_pSessionLockManager->isSessionLocked()) {
@@ -130,7 +137,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
}
if (CONSTRAINTWINDOW->m_bIsX11) {
foundSurface = g_pXWaylandManager->getWindowSurface(CONSTRAINTWINDOW);
foundSurface = CONSTRAINTWINDOW->m_pWLSurface.wlr();
surfacePos = CONSTRAINTWINDOW->m_vRealPosition.vec();
} else {
g_pCompositor->vectorWindowToSurface(mouseCoords, CONSTRAINTWINDOW, surfaceCoords);
@@ -143,6 +150,33 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
// update stuff
updateDragIcon();
if (!m_sDrag.drag && !m_lCurrentlyHeldButtons.empty() && g_pCompositor->m_pLastFocus) {
if (m_bLastFocusOnLS) {
foundSurface = g_pCompositor->m_pLastFocus;
pFoundLayerSurface = g_pCompositor->getLayerSurfaceFromSurface(foundSurface);
if (pFoundLayerSurface) {
surfacePos = g_pCompositor->getLayerSurfaceFromSurface(foundSurface)->position;
m_bFocusHeldByButtons = true;
m_bRefocusHeldByButtons = refocus;
} else {
// ?
foundSurface = nullptr;
pFoundLayerSurface = nullptr;
}
} else if (g_pCompositor->m_pLastWindow) {
foundSurface = g_pCompositor->m_pLastFocus;
pFoundWindow = g_pCompositor->m_pLastWindow;
if (!g_pCompositor->m_pLastWindow->m_bIsX11)
foundSurface = g_pCompositor->vectorWindowToSurface(mouseCoords, g_pCompositor->m_pLastWindow, surfaceCoords);
else
surfacePos = g_pCompositor->m_pLastWindow->m_vRealPosition.vec();
m_bFocusHeldByButtons = true;
m_bRefocusHeldByButtons = refocus;
}
}
g_pLayoutManager->getCurrentLayout()->onMouseMove(getMouseCoordsInternal());
if (PMONITOR && PMONITOR != g_pCompositor->m_pLastMonitor && (*PMOUSEFOCUSMON || refocus)) {
@@ -198,7 +232,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
foundSurface = g_pCompositor->vectorWindowToSurface(mouseCoords, pFoundWindow, surfaceCoords);
surfacePos = Vector2D(-1337, -1337);
} else {
foundSurface = g_pXWaylandManager->getWindowSurface(pFoundWindow);
foundSurface = pFoundWindow->m_pWLSurface.wlr();
surfacePos = pFoundWindow->m_vRealPosition.vec();
}
}
@@ -232,7 +266,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
if (!pFoundWindow->m_bIsX11) {
foundSurface = g_pCompositor->vectorWindowToSurface(mouseCoords, pFoundWindow, surfaceCoords);
} else {
foundSurface = g_pXWaylandManager->getWindowSurface(pFoundWindow);
foundSurface = pFoundWindow->m_pWLSurface.wlr();
surfacePos = pFoundWindow->m_vRealPosition.vec();
}
}
@@ -303,14 +337,24 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
m_pFoundSurfaceToFocus = foundSurface;
}
if (currentlyDraggedWindow && pFoundWindow != currentlyDraggedWindow) {
wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, foundSurface, surfaceLocal.x, surfaceLocal.y);
wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, time, surfaceLocal.x, surfaceLocal.y);
return;
}
if (pFoundWindow) {
// change cursor icon if hovering over border
if (*PRESIZEONBORDER && *PRESIZECURSORICON && !pFoundWindow->m_bIsFullscreen && !pFoundWindow->hasPopupAt(mouseCoords)) {
setCursorIconOnBorder(pFoundWindow);
if (*PRESIZEONBORDER && *PRESIZECURSORICON) {
if (!pFoundWindow->m_bIsFullscreen && !pFoundWindow->hasPopupAt(mouseCoords)) {
setCursorIconOnBorder(pFoundWindow);
} else if (m_eBorderIconDirection != BORDERICON_NONE) {
unsetCursorImage();
}
}
// if we're on an input deco, reset cursor. Don't on overriden
// if (!m_bCursorImageOverriden) {
// if we're on an input deco, reset cursor. Don't on overridden
// if (!m_bCursorImageOverridden) {
// if (!VECINRECT(m_vLastCursorPosFloored, pFoundWindow->m_vRealPosition.vec().x, pFoundWindow->m_vRealPosition.vec().y,
// pFoundWindow->m_vRealPosition.vec().x + pFoundWindow->m_vRealSize.vec().x, pFoundWindow->m_vRealPosition.vec().y + pFoundWindow->m_vRealSize.vec().y)) {
// wlr_xcursor_manager_set_cursor_image(g_pCompositor->m_sWLRXCursorMgr, "left_ptr", g_pCompositor->m_sWLRCursor);
@@ -321,14 +365,14 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
// }
// }
if (*PFOLLOWMOUSE != 1 && !refocus) {
if (FOLLOWMOUSE != 1 && !refocus) {
if (pFoundWindow != g_pCompositor->m_pLastWindow && g_pCompositor->m_pLastWindow &&
((pFoundWindow->m_bIsFloating && *PFLOATBEHAVIOR == 2) || (g_pCompositor->m_pLastWindow->m_bIsFloating != pFoundWindow->m_bIsFloating && *PFLOATBEHAVIOR != 0))) {
// enter if change floating style
if (*PFOLLOWMOUSE != 3 && allowKeyboardRefocus)
if (FOLLOWMOUSE != 3 && allowKeyboardRefocus)
g_pCompositor->focusWindow(pFoundWindow, foundSurface);
wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, foundSurface, surfaceLocal.x, surfaceLocal.y);
} else if (*PFOLLOWMOUSE == 2 || *PFOLLOWMOUSE == 3) {
} else if (FOLLOWMOUSE == 2 || FOLLOWMOUSE == 3) {
wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, foundSurface, surfaceLocal.x, surfaceLocal.y);
}
@@ -339,18 +383,16 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
}
}
if (*PFOLLOWONDND && m_sDrag.dragIcon) {
wlr_seat_pointer_notify_enter(g_pCompositor->m_sSeat.seat, foundSurface, surfaceLocal.x, surfaceLocal.y);
}
if (*PFOLLOWMOUSE != 0 || pFoundWindow == g_pCompositor->m_pLastWindow)
if (FOLLOWMOUSE != 0 || pFoundWindow == g_pCompositor->m_pLastWindow)
wlr_seat_pointer_notify_motion(g_pCompositor->m_sSeat.seat, time, surfaceLocal.x, surfaceLocal.y);
m_bLastFocusOnLS = false;
return; // don't enter any new surfaces
} else {
if ((*PFOLLOWMOUSE != 3 && allowKeyboardRefocus) || refocus)
if (((FOLLOWMOUSE != 3 && allowKeyboardRefocus) && (*PMOUSEREFOCUS || m_pLastMouseFocus != pFoundWindow)) || refocus) {
m_pLastMouseFocus = pFoundWindow;
g_pCompositor->focusWindow(pFoundWindow, foundSurface);
}
}
m_bLastFocusOnLS = false;
@@ -360,7 +402,9 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
unsetCursorImage();
}
if (pFoundLayerSurface && pFoundLayerSurface->layerSurface->current.keyboard_interactive && *PFOLLOWMOUSE != 3 && allowKeyboardRefocus) {
if (pFoundLayerSurface &&
(pFoundLayerSurface->layerSurface->current.keyboard_interactive || (pFoundLayerSurface->layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP && !g_pCompositor->m_pLastWindow)) &&
FOLLOWMOUSE != 3 && allowKeyboardRefocus) {
g_pCompositor->focusSurface(foundSurface);
}
@@ -392,6 +436,16 @@ void CInputManager::onMouseButton(wlr_pointer_button_event* e) {
case CLICKMODE_KILL: processMouseDownKill(e); break;
default: break;
}
if (m_bFocusHeldByButtons && m_lCurrentlyHeldButtons.empty() && e->state == WLR_BUTTON_RELEASED) {
if (m_bRefocusHeldByButtons)
refocus();
else
simulateMouseMovement();
m_bFocusHeldByButtons = false;
m_bRefocusHeldByButtons = false;
}
}
void CInputManager::processMouseRequest(wlr_seat_pointer_request_set_cursor_event* e) {
@@ -404,7 +458,7 @@ void CInputManager::processMouseRequest(wlr_seat_pointer_request_set_cursor_even
g_pHyprRenderer->m_bWindowRequestedCursorHide = false;
}
if (m_bCursorImageOverriden) {
if (m_bCursorImageOverridden) {
return;
}
@@ -483,8 +537,17 @@ void CInputManager::processMouseDownNormal(wlr_pointer_button_event* e) {
if (*PFOLLOWMOUSE == 3) // don't refocus on full loose
break;
if (!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->currentConstraint)
refocus();
if (!g_pCompositor->m_sSeat.mouse || !g_pCompositor->m_sSeat.mouse->currentConstraint) {
// a bit hacky
// if we only pressed one button, allow us to refocus. m_lCurrentlyHeldButtons.size() > 0 will stick the focus
if (m_lCurrentlyHeldButtons.size() == 1) {
const auto COPY = m_lCurrentlyHeldButtons;
m_lCurrentlyHeldButtons.clear();
refocus();
m_lCurrentlyHeldButtons = COPY;
} else
refocus();
}
// if clicked on a floating window make it top
if (g_pCompositor->m_pLastWindow && g_pCompositor->m_pLastWindow->m_bIsFloating)
@@ -576,7 +639,7 @@ void CInputManager::newKeyboard(wlr_input_device* keyboard) {
wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, wlr_keyboard_from_input_device(keyboard));
Debug::log(LOG, "New keyboard created, pointers Hypr: %x and WLR: %x", PNEWKEYBOARD, keyboard);
Debug::log(LOG, "New keyboard created, pointers Hypr: %lx and WLR: %lx", PNEWKEYBOARD, keyboard);
}
void CInputManager::newVirtualKeyboard(wlr_input_device* keyboard) {
@@ -615,7 +678,7 @@ void CInputManager::newVirtualKeyboard(wlr_input_device* keyboard) {
wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, wlr_keyboard_from_input_device(keyboard));
Debug::log(LOG, "New virtual keyboard created, pointers Hypr: %x and WLR: %x", PNEWKEYBOARD, keyboard);
Debug::log(LOG, "New virtual keyboard created, pointers Hypr: %lx and WLR: %lx", PNEWKEYBOARD, keyboard);
}
void CInputManager::setKeyboardLayout() {
@@ -782,7 +845,7 @@ void CInputManager::newMouse(wlr_input_device* mouse, bool virt) {
m_tmrLastCursorMovement.reset();
Debug::log(LOG, "New mouse created, pointer WLR: %x", mouse);
Debug::log(LOG, "New mouse created, pointer WLR: %lx", mouse);
}
void CInputManager::setPointerConfigs() {
@@ -963,8 +1026,8 @@ void CInputManager::onKeyboardKey(wlr_keyboard_key_event* e, SKeyboard* pKeyboar
if (!pKeyboard->enabled)
return;
static auto* const PDPMS = &g_pConfigManager->getConfigValuePtr("key_press_enables_dpms")->intValue;
if (*PDPMS && g_pCompositor->m_bDPMSStateON) {
static auto* const PDPMS = &g_pConfigManager->getConfigValuePtr("misc:key_press_enables_dpms")->intValue;
if (*PDPMS && !g_pCompositor->m_bDPMSStateON) {
// enable dpms
g_pKeybindManager->dpms("on");
}
@@ -1117,7 +1180,7 @@ void CInputManager::constrainMouse(SMouse* pMouse, wlr_pointer_constraint_v1* co
pMouse->hyprListener_commitConstraint.initCallback(&pMouse->currentConstraint->surface->events.commit, &Events::listener_commitConstraint, pMouse, "Mouse constraint commit");
Debug::log(LOG, "Constrained mouse to %x", pMouse->currentConstraint);
Debug::log(LOG, "Constrained mouse to %lx", pMouse->currentConstraint);
}
void CInputManager::unconstrainMouse() {
@@ -1127,7 +1190,7 @@ void CInputManager::unconstrainMouse() {
const auto CONSTRAINTWINDOW = g_pCompositor->getConstraintWindow(g_pCompositor->m_sSeat.mouse);
if (CONSTRAINTWINDOW) {
g_pXWaylandManager->activateSurface(g_pXWaylandManager->getWindowSurface(CONSTRAINTWINDOW), false);
g_pXWaylandManager->activateSurface(CONSTRAINTWINDOW->m_pWLSurface.wlr(), false);
}
wlr_pointer_constraint_v1_send_deactivated(g_pCompositor->m_sSeat.mouse->currentConstraint);
@@ -1236,7 +1299,7 @@ void CInputManager::newTouchDevice(wlr_input_device* pDevice) {
setTouchDeviceConfigs();
wlr_cursor_attach_input_device(g_pCompositor->m_sWLRCursor, pDevice);
Debug::log(LOG, "New touch device added at %x", PNEWDEV);
Debug::log(LOG, "New touch device added at %lx", PNEWDEV);
PNEWDEV->hyprListener_destroy.initCallback(
&pDevice->events.destroy, [&](void* owner, void* data) { destroyTouchDevice((STouchDevice*)data); }, PNEWDEV, "TouchDevice");
@@ -1286,7 +1349,7 @@ void CInputManager::setTabletConfigs() {
}
void CInputManager::destroyTouchDevice(STouchDevice* pDevice) {
Debug::log(LOG, "Touch device at %x removed", pDevice);
Debug::log(LOG, "Touch device at %lx removed", pDevice);
m_lTouchDevices.remove(*pDevice);
}
@@ -1307,13 +1370,16 @@ void CInputManager::newSwitch(wlr_input_device* pDevice) {
[&](void* owner, void* data) {
const auto PDEVICE = (SSwitchDevice*)owner;
const auto NAME = std::string(PDEVICE->pWlrDevice->name);
const auto E = (wlr_switch_toggle_event*)data;
if (PDEVICE->status != -1 && PDEVICE->status == E->switch_state)
return;
Debug::log(LOG, "Switch %s fired, triggering binds.", NAME.c_str());
g_pKeybindManager->onSwitchEvent(NAME);
const auto event_data = (wlr_switch_toggle_event*)data;
switch (event_data->switch_state) {
switch (E->switch_state) {
case WLR_SWITCH_STATE_ON:
Debug::log(LOG, "Switch %s turn on, triggering binds.", NAME.c_str());
g_pKeybindManager->onSwitchOnEvent(NAME);
@@ -1323,6 +1389,8 @@ void CInputManager::newSwitch(wlr_input_device* pDevice) {
g_pKeybindManager->onSwitchOffEvent(NAME);
break;
}
PDEVICE->status = E->switch_state;
},
PNEWDEV, "SwitchDevice");
}
@@ -1333,14 +1401,14 @@ void CInputManager::destroySwitch(SSwitchDevice* pDevice) {
void CInputManager::setCursorImageUntilUnset(std::string name) {
wlr_xcursor_manager_set_cursor_image(g_pCompositor->m_sWLRXCursorMgr, name.c_str(), g_pCompositor->m_sWLRCursor);
m_bCursorImageOverriden = true;
m_bCursorImageOverridden = true;
}
void CInputManager::unsetCursorImage() {
if (!m_bCursorImageOverriden)
if (!m_bCursorImageOverridden)
return;
m_bCursorImageOverriden = false;
m_bCursorImageOverridden = false;
if (!g_pHyprRenderer->m_bWindowRequestedCursorHide)
wlr_xcursor_manager_set_cursor_image(g_pCompositor->m_sWLRXCursorMgr, "left_ptr", g_pCompositor->m_sWLRCursor);
}
@@ -1412,14 +1480,19 @@ void CInputManager::setCursorIconOnBorder(CWindow* w) {
return;
}
static auto* const PROUNDING = &g_pConfigManager->getConfigValuePtr("decoration:rounding")->intValue;
static const auto* PBORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue;
static auto* const PROUNDING = &g_pConfigManager->getConfigValuePtr("decoration:rounding")->intValue;
static const auto* PBORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue;
static const auto* PEXTENDBORDERGRAB = &g_pConfigManager->getConfigValuePtr("general:extend_border_grab_area")->intValue;
// give a small leeway (10 px) for corner icon
const auto CORNER = *PROUNDING + *PBORDERSIZE + 10;
const auto mouseCoords = getMouseCoordsInternal();
wlr_box box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
eBorderIconDirection direction = BORDERICON_NONE;
if (wlr_box_contains_point(&box, mouseCoords.x, mouseCoords.y)) {
const auto CORNER = *PROUNDING + *PBORDERSIZE + 10;
const auto mouseCoords = getMouseCoordsInternal();
wlr_box box = {w->m_vRealPosition.vec().x, w->m_vRealPosition.vec().y, w->m_vRealSize.vec().x, w->m_vRealSize.vec().y};
eBorderIconDirection direction = BORDERICON_NONE;
wlr_box boxFullGrabInput = {box.x - *PEXTENDBORDERGRAB, box.y - *PEXTENDBORDERGRAB, box.width + 2 * *PEXTENDBORDERGRAB, box.height + 2 * *PEXTENDBORDERGRAB};
if (!wlr_box_contains_point(&boxFullGrabInput, mouseCoords.x, mouseCoords.y) || (!m_lCurrentlyHeldButtons.empty() && !currentlyDraggedWindow)) {
direction = BORDERICON_NONE;
} else if (wlr_box_contains_point(&box, mouseCoords.x, mouseCoords.y)) {
if (!w->isInCurvedCorner(mouseCoords.x, mouseCoords.y)) {
direction = BORDERICON_NONE;
} else {

View File

@@ -7,18 +7,21 @@
#include "../../helpers/Timer.hpp"
#include "InputMethodRelay.hpp"
enum eClickBehaviorMode {
enum eClickBehaviorMode
{
CLICKMODE_DEFAULT = 0,
CLICKMODE_KILL
};
enum eMouseBindMode {
enum eMouseBindMode
{
MBIND_INVALID = -1,
MBIND_MOVE = 0,
MBIND_RESIZE
};
enum eBorderIconDirection {
enum eBorderIconDirection
{
BORDERICON_NONE,
BORDERICON_UP,
BORDERICON_DOWN,
@@ -154,8 +157,6 @@ class CInputManager {
// for shared mods
uint32_t accumulateModsFromAllKBs();
CWindow* m_pFollowOnDnDBegin = nullptr;
// for virtual keyboards: whether we should respect them as normal ones
bool shouldIgnoreVirtualKeyboard(SKeyboard*);
@@ -174,9 +175,12 @@ class CInputManager {
// for hiding cursor on touch
bool m_bLastInputTouch = false;
// for tracking mouse refocus
CWindow* m_pLastMouseFocus = nullptr;
private:
bool m_bCursorImageOverriden = false;
eBorderIconDirection m_eBorderIconDirection = BORDERICON_NONE;
bool m_bCursorImageOverridden = false;
eBorderIconDirection m_eBorderIconDirection = BORDERICON_NONE;
// for click behavior override
eClickBehaviorMode m_ecbClickBehavior = CLICKMODE_DEFAULT;
@@ -201,6 +205,10 @@ class CInputManager {
SLayerSurface* m_pFoundLSToFocus = nullptr;
CWindow* m_pFoundWindowToFocus = nullptr;
// for holding focus on buttons held
bool m_bFocusHeldByButtons = false;
bool m_bRefocusHeldByButtons = false;
// for releasing mouse buttons
std::list<uint32_t> m_lCurrentlyHeldButtons;

View File

@@ -6,7 +6,7 @@ void CInputManager::onSwipeBegin(wlr_pointer_swipe_begin_event* e) {
static auto* const PSWIPEFINGERS = &g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_fingers")->intValue;
static auto* const PSWIPENEW = &g_pConfigManager->getConfigValuePtr("gestures:workspace_swipe_create_new")->intValue;
if (e->fingers != *PSWIPEFINGERS || *PSWIPE == 0)
if (e->fingers != *PSWIPEFINGERS || *PSWIPE == 0 || g_pSessionLockManager->isSessionLocked())
return;
int onMonitor = 0;
@@ -56,9 +56,21 @@ void CInputManager::onSwipeEnd(wlr_pointer_swipe_end_event* e) {
auto workspaceIDLeft = getWorkspaceIDFromString(*PSWIPENUMBER ? "-1" : "m-1", wsname);
auto workspaceIDRight = getWorkspaceIDFromString(*PSWIPENUMBER ? "+1" : "m+1", wsname);
if ((workspaceIDRight <= m_sActiveSwipe.pWorkspaceBegin->m_iID || (workspaceIDRight == workspaceIDLeft && workspaceIDLeft == m_sActiveSwipe.pWorkspaceBegin->m_iID)) &&
*PSWIPENEW) {
workspaceIDRight = m_sActiveSwipe.pWorkspaceBegin->m_iID > 0 ? m_sActiveSwipe.pWorkspaceBegin->m_iID + 1 : 1;
// If we've been swiping off the right end with PSWIPENEW enabled, there is
// no workspace there yet, and we need to choose an ID for a new one now.
// With multiple monitors, it might not be appropriate to choose one more
// than the ID of the workspace we're swiping from, because that ID might
// just be on another monitor. It's also not just the smallest unused ID,
// because that could be a gap in the existing workspace numbers, and it'd
// be counterintuitive to swipe rightwards onto a new workspace and end up
// left of where we started. Instead, it's one more than the greatest
// workspace ID that currently exists.
if (workspaceIDRight <= m_sActiveSwipe.pWorkspaceBegin->m_iID && *PSWIPENEW) {
int maxWorkspace = 0;
for (const auto& ws : g_pCompositor->m_vWorkspaces) {
maxWorkspace = std::max(maxWorkspace, ws->m_iID);
}
workspaceIDRight = maxWorkspace + 1;
}
auto PWORKSPACER = g_pCompositor->getWorkspaceByID(workspaceIDRight); // not guaranteed if PSWIPENEW || PSWIPENUMBER
@@ -103,9 +115,9 @@ void CInputManager::onSwipeEnd(wlr_pointer_swipe_end_event* e) {
const auto RENDEROFFSET = PWORKSPACEL ? PWORKSPACEL->m_vRenderOffset.vec() : Vector2D();
if (PWORKSPACEL)
g_pKeybindManager->m_mDispatchers["workspace"]("[internal]" + std::to_string(workspaceIDLeft));
m_sActiveSwipe.pMonitor->changeWorkspace(workspaceIDLeft);
else {
g_pKeybindManager->m_mDispatchers["workspace"](std::to_string(workspaceIDLeft)); // so that the ID is created properly
m_sActiveSwipe.pMonitor->changeWorkspace(g_pCompositor->createNewWorkspace(workspaceIDLeft, m_sActiveSwipe.pMonitor->ID));
PWORKSPACEL = g_pCompositor->getWorkspaceByID(workspaceIDLeft);
}
@@ -129,9 +141,9 @@ void CInputManager::onSwipeEnd(wlr_pointer_swipe_end_event* e) {
const auto RENDEROFFSET = PWORKSPACER ? PWORKSPACER->m_vRenderOffset.vec() : Vector2D();
if (PWORKSPACER)
g_pKeybindManager->m_mDispatchers["workspace"]("[internal]" + std::to_string(workspaceIDRight));
m_sActiveSwipe.pMonitor->changeWorkspace(workspaceIDRight);
else {
g_pKeybindManager->m_mDispatchers["workspace"](std::to_string(workspaceIDRight)); // so that the ID is created properly
m_sActiveSwipe.pMonitor->changeWorkspace(g_pCompositor->createNewWorkspace(workspaceIDRight, m_sActiveSwipe.pMonitor->ID));
PWORKSPACER = g_pCompositor->getWorkspaceByID(workspaceIDRight);
}
@@ -305,4 +317,4 @@ void CInputManager::onSwipeUpdate(wlr_pointer_swipe_update_event* e) {
beginWorkspaceSwipe();
}
}
}
}

View File

@@ -145,7 +145,7 @@ STabletTool* CInputManager::ensureTabletToolPresent(wlr_tablet_tool* pTool) {
if (pTool->data == nullptr) {
const auto PTOOL = &m_lTabletTools.emplace_back();
Debug::log(LOG, "Creating tablet tool v2 for %x", pTool);
Debug::log(LOG, "Creating tablet tool v2 for %lx", pTool);
PTOOL->wlrTabletTool = pTool;
pTool->data = PTOOL;

View File

@@ -62,7 +62,7 @@ void CInputManager::onTouchMove(wlr_touch_motion_event* e) {
if (m_sTouchData.touchFocusWindow && g_pCompositor->windowValidMapped(m_sTouchData.touchFocusWindow)) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_sTouchData.touchFocusWindow->m_iMonitorID);
wlr_cursor_warp(g_pCompositor->m_sWLRCursor, g_pCompositor->m_sSeat.mouse->mouse, PMONITOR->vecPosition.x + e->x * PMONITOR->vecSize.x,
wlr_cursor_warp(g_pCompositor->m_sWLRCursor, nullptr, PMONITOR->vecPosition.x + e->x * PMONITOR->vecSize.x,
PMONITOR->vecPosition.y + e->y * PMONITOR->vecSize.y);
const auto local = g_pInputManager->getMouseCoordsInternal() - m_sTouchData.touchSurfaceOrigin;
@@ -72,7 +72,7 @@ void CInputManager::onTouchMove(wlr_touch_motion_event* e) {
} else if (m_sTouchData.touchFocusLS) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(m_sTouchData.touchFocusLS->monitorID);
wlr_cursor_warp(g_pCompositor->m_sWLRCursor, g_pCompositor->m_sSeat.mouse->mouse, PMONITOR->vecPosition.x + e->x * PMONITOR->vecSize.x,
wlr_cursor_warp(g_pCompositor->m_sWLRCursor, nullptr, PMONITOR->vecPosition.x + e->x * PMONITOR->vecSize.x,
PMONITOR->vecPosition.y + e->y * PMONITOR->vecSize.y);
const auto local = g_pInputManager->getMouseCoordsInternal() - m_sTouchData.touchSurfaceOrigin;

View File

@@ -3,6 +3,7 @@ src = globber.stdout().strip().split('\n')
executable('Hyprland', src,
cpp_args: ['-DWLR_USE_UNSTABLE'],
link_args: '-rdynamic',
dependencies: [
server_protos,
dependency('wayland-server'),
@@ -20,7 +21,9 @@ executable('Hyprland', src,
dependency('pixman-1'),
dependency('gl', 'opengl'),
dependency('threads')
dependency('threads'),
dependency('pango'),
dependency('pangocairo')
],
install : true
)

View File

@@ -3,6 +3,12 @@
#include "../debug/HyprCtl.hpp"
#include <dlfcn.h>
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
#include <sys/sysctl.h>
#endif
#include <sstream>
APICALL bool HyprlandAPI::registerCallbackStatic(HANDLE handle, const std::string& event, HOOK_CALLBACK_FN* fn) {
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
@@ -193,3 +199,124 @@ APICALL bool HyprlandAPI::removeDispatcher(HANDLE handle, const std::string& nam
return true;
}
APICALL bool addNotificationV2(HANDLE handle, const std::unordered_map<std::string, std::any>& data) {
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
if (!PLUGIN)
return false;
try {
auto iterator = data.find("text");
if (iterator == data.end())
return false;
// mandatory
std::string text;
try {
text = std::any_cast<std::string>(iterator->second);
} catch (std::exception& e) {
// attempt const char*
text = std::any_cast<const char*>(iterator->second);
}
iterator = data.find("time");
if (iterator == data.end())
return false;
const auto TIME = std::any_cast<uint64_t>(iterator->second);
iterator = data.find("color");
if (iterator == data.end())
return false;
const auto COLOR = std::any_cast<CColor>(iterator->second);
// optional
eIcons icon = ICON_NONE;
iterator = data.find("icon");
if (iterator != data.end())
icon = std::any_cast<eIcons>(iterator->second);
g_pHyprNotificationOverlay->addNotification(text, COLOR, TIME, icon);
} catch (std::exception& e) {
// bad any_cast most likely, plugin error
return false;
}
return true;
}
APICALL std::vector<SFunctionMatch> HyprlandAPI::findFunctionsByName(HANDLE handle, const std::string& name) {
auto* const PLUGIN = g_pPluginSystem->getPluginByHandle(handle);
if (!PLUGIN)
return std::vector<SFunctionMatch>{};
#if defined(KERN_PROC_PATHNAME)
int mib[] = {
CTL_KERN,
#if defined(__NetBSD__)
KERN_PROC_ARGS,
-1,
KERN_PROC_PATHNAME,
#else
KERN_PROC,
KERN_PROC_PATHNAME,
-1,
#endif
};
u_int miblen = sizeof(mib) / sizeof(mib[0]);
char exe[PATH_MAX] = "";
size_t sz = sizeof(exe);
sysctl(mib, miblen, &exe, &sz, NULL, 0);
const auto FPATH = std::filesystem::canonical(exe);
#elif defined(__OpenBSD__)
// Neither KERN_PROC_PATHNAME nor /proc are supported
const auto FPATH = std::filesystem::canonical("/usr/local/bin/Hyprland");
#else
const auto FPATH = std::filesystem::canonical("/proc/self/exe");
#endif
#ifdef __clang__
static const auto SYMBOLS = execAndGet(("llvm-nm -D -j " + FPATH.string()).c_str());
static const auto SYMBOLSDEMANGLED = execAndGet(("llvm-nm -D -j --demangle " + FPATH.string()).c_str());
#else
static const auto SYMBOLS = execAndGet(("nm -D -j " + FPATH.string()).c_str());
static const auto SYMBOLSDEMANGLED = execAndGet(("nm -D -j --demangle=auto " + FPATH.string()).c_str());
#endif
auto demangledFromID = [&](size_t id) -> std::string {
size_t pos = 0;
size_t count = 0;
while (count < id) {
pos++;
pos = SYMBOLSDEMANGLED.find('\n', pos);
if (pos == std::string::npos)
return "";
count++;
}
return SYMBOLSDEMANGLED.substr(pos, SYMBOLSDEMANGLED.find('\n', pos + 1) - pos);
};
std::vector<SFunctionMatch> matches;
std::istringstream inStream(SYMBOLS);
std::string line;
int lineNo = 0;
while (std::getline(inStream, line)) {
if (line.contains(name)) {
void* address = dlsym(nullptr, line.c_str());
if (!address)
continue;
matches.push_back({address, line, demangledFromID(lineNo)});
}
lineNo++;
}
return matches;
}

View File

@@ -13,12 +13,16 @@ on the same machine.
See examples/examplePlugin for an example plugin
* NOTE:
Feel like the API is missing something you'd like to use in your plugin? Open an issue on github!
*/
#define HYPRLAND_API_VERSION "0.1"
#include "../helpers/Color.hpp"
#include "HookSystem.hpp"
#include "../SharedDefs.hpp"
#include <any>
#include <functional>
@@ -32,6 +36,12 @@ typedef struct {
std::string version;
} PLUGIN_DESCRIPTION_INFO;
struct SFunctionMatch {
void* address = nullptr;
std::string signature;
std::string demangled;
};
#define APICALL extern "C"
#define EXPORT __attribute__((visibility("default")))
#define REQUIRED
@@ -184,8 +194,10 @@ namespace HyprlandAPI {
This is useful for hooking private functions.
returns: function address, or nullptr on fail.
Deprecated because of findFunctionsByName.
*/
APICALL void* getFunctionAddressFromSignature(HANDLE handle, const std::string& sig);
APICALL [[deprecated]] void* getFunctionAddressFromSignature(HANDLE handle, const std::string& sig);
/*
Adds a window decoration to a window
@@ -214,4 +226,28 @@ namespace HyprlandAPI {
returns: true on success. False otherwise.
*/
APICALL bool removeDispatcher(HANDLE handle, const std::string& name);
/*
Adds a notification.
data has to contain:
- text: std::string or const char*
- time: uint64_t
- color: CColor -> CColor(0) will apply the default color for the notification icon
data may contain:
- icon: eIcons
returns: true on success. False otherwise.
*/
APICALL bool addNotificationV2(HANDLE handle, const std::unordered_map<std::string, std::any>& data);
/*
Returns a vector of found functions matching the provided name.
These addresses will not change, and should be made static. Lookups are slow.
Empty means either none found or handle was invalid
*/
APICALL std::vector<SFunctionMatch> findFunctionsByName(HANDLE handle, const std::string& name);
};

View File

@@ -119,6 +119,35 @@ void CPluginSystem::unloadAllPlugins() {
unloadPlugin(p.get(), false); // Unload remaining plugins gracefully
}
std::vector<std::string> CPluginSystem::updateConfigPlugins(const std::vector<std::string>& plugins, bool& changed) {
std::vector<std::string> failures;
// unload all plugins that are no longer present
for (auto& p : m_vLoadedPlugins | std::views::reverse) {
if (p->m_bLoadedWithConfig && std::find(plugins.begin(), plugins.end(), p->path) == plugins.end()) {
Debug::log(LOG, "Unloading plugin %s which is no longer present in config", p->path.c_str());
unloadPlugin(p.get(), false);
changed = true;
}
}
// load all new plugins
for (auto& path : plugins) {
if (std::find_if(m_vLoadedPlugins.begin(), m_vLoadedPlugins.end(), [&](const auto& other) { return other->path == path; }) == m_vLoadedPlugins.end()) {
Debug::log(LOG, "Loading plugin %s which is now present in config", path.c_str());
const auto plugin = loadPlugin(path);
if (plugin) {
plugin->m_bLoadedWithConfig = true;
changed = true;
} else
failures.push_back(path);
}
}
return failures;
}
CPlugin* CPluginSystem::getPluginByPath(const std::string& path) {
for (auto& p : m_vLoadedPlugins) {
if (p->path == path)

View File

@@ -15,6 +15,8 @@ class CPlugin {
std::string path = "";
bool m_bLoadedWithConfig = false;
HANDLE m_pHandle = nullptr;
std::vector<IHyprLayout*> registeredLayouts;
@@ -27,14 +29,15 @@ class CPluginSystem {
public:
CPluginSystem();
CPlugin* loadPlugin(const std::string& path);
void unloadPlugin(const CPlugin* plugin, bool eject = false);
void unloadAllPlugins();
CPlugin* getPluginByPath(const std::string& path);
CPlugin* getPluginByHandle(HANDLE handle);
std::vector<CPlugin*> getAllPlugins();
CPlugin* loadPlugin(const std::string& path);
void unloadPlugin(const CPlugin* plugin, bool eject = false);
void unloadAllPlugins();
std::vector<std::string> updateConfigPlugins(const std::vector<std::string>& plugins, bool& changed);
CPlugin* getPluginByPath(const std::string& path);
CPlugin* getPluginByHandle(HANDLE handle);
std::vector<CPlugin*> getAllPlugins();
bool m_bAllowConfigVars = false;
bool m_bAllowConfigVars = false;
private:
std::vector<std::unique_ptr<CPlugin>> m_vLoadedPlugins;

View File

@@ -0,0 +1,151 @@
#include "GlobalShortcuts.hpp"
#include "../Compositor.hpp"
#define GLOBAL_SHORTCUTS_VERSION 1
static void bindManagerInt(wl_client* client, void* data, uint32_t version, uint32_t id) {
g_pProtocolManager->m_pGlobalShortcutsProtocolManager->bindManager(client, data, version, id);
}
static void handleDisplayDestroy(struct wl_listener* listener, void* data) {
g_pProtocolManager->m_pGlobalShortcutsProtocolManager->displayDestroy();
}
void CGlobalShortcutsProtocolManager::displayDestroy() {
wl_global_destroy(m_pGlobal);
}
CGlobalShortcutsProtocolManager::CGlobalShortcutsProtocolManager() {
m_pGlobal = wl_global_create(g_pCompositor->m_sWLDisplay, &hyprland_global_shortcuts_manager_v1_interface, GLOBAL_SHORTCUTS_VERSION, this, bindManagerInt);
if (!m_pGlobal) {
Debug::log(ERR, "GlobalShortcutsManager could not start!");
return;
}
m_liDisplayDestroy.notify = handleDisplayDestroy;
wl_display_add_destroy_listener(g_pCompositor->m_sWLDisplay, &m_liDisplayDestroy);
Debug::log(LOG, "GlobalShortcutsManager started successfully!");
}
static void handleRegisterShortcut(wl_client* client, wl_resource* resource, uint32_t shortcut, const char* id, const char* app_id, const char* description,
const char* trigger_description) {
g_pProtocolManager->m_pGlobalShortcutsProtocolManager->registerShortcut(client, resource, shortcut, id, app_id, description, trigger_description);
}
static void handleDestroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
static const struct hyprland_global_shortcuts_manager_v1_interface globalShortcutsManagerImpl = {
.register_shortcut = handleRegisterShortcut,
.destroy = handleDestroy,
};
static const struct hyprland_global_shortcut_v1_interface shortcutImpl = {
.destroy = handleDestroy,
};
void CGlobalShortcutsProtocolManager::bindManager(wl_client* client, void* data, uint32_t version, uint32_t id) {
const auto RESOURCE = wl_resource_create(client, &hyprland_global_shortcuts_manager_v1_interface, version, id);
wl_resource_set_implementation(RESOURCE, &globalShortcutsManagerImpl, this, nullptr);
Debug::log(LOG, "GlobalShortcutsManager bound successfully!");
m_vClients.emplace_back(std::make_unique<SShortcutClient>(client));
}
SShortcutClient* CGlobalShortcutsProtocolManager::clientFromWlClient(wl_client* client) {
for (auto& c : m_vClients) {
if (c->client == client) {
return c.get();
}
}
return nullptr;
}
static void onShortcutDestroy(wl_resource* pResource) {
g_pProtocolManager->m_pGlobalShortcutsProtocolManager->destroyShortcut(pResource);
}
void CGlobalShortcutsProtocolManager::registerShortcut(wl_client* client, wl_resource* resource, uint32_t shortcut, const char* id, const char* app_id, const char* description,
const char* trigger_description) {
const auto PCLIENT = clientFromWlClient(client);
if (!PCLIENT) {
Debug::log(ERR, "Error at global shortcuts: no client in register?");
return;
}
for (auto& c : m_vClients) {
for (auto& sh : c->shortcuts) {
if (sh->appid == app_id && sh->id == id) {
wl_resource_post_error(resource, HYPRLAND_GLOBAL_SHORTCUTS_MANAGER_V1_ERROR_ALREADY_TAKEN, "Combination is taken");
return;
}
}
}
const auto PSHORTCUT = PCLIENT->shortcuts.emplace_back(std::make_unique<SShortcut>()).get();
PSHORTCUT->id = id;
PSHORTCUT->description = description;
PSHORTCUT->appid = app_id;
PSHORTCUT->shortcut = shortcut;
PSHORTCUT->resource = wl_resource_create(client, &hyprland_global_shortcut_v1_interface, 1, shortcut);
if (!PSHORTCUT->resource) {
wl_client_post_no_memory(client);
std::erase_if(PCLIENT->shortcuts, [&](const auto& other) { return other.get() == PSHORTCUT; });
return;
}
wl_resource_set_implementation(PSHORTCUT->resource, &shortcutImpl, this, &onShortcutDestroy);
}
bool CGlobalShortcutsProtocolManager::globalShortcutExists(std::string appid, std::string trigger) {
for (auto& c : m_vClients) {
for (auto& sh : c->shortcuts) {
if (sh->appid == appid && sh->id == trigger) {
return true;
}
}
}
return false;
}
void CGlobalShortcutsProtocolManager::sendGlobalShortcutEvent(std::string appid, std::string trigger, bool pressed) {
for (auto& c : m_vClients) {
for (auto& sh : c->shortcuts) {
if (sh->appid == appid && sh->id == trigger) {
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
uint32_t tvSecHi = (sizeof(now.tv_sec) > 4) ? now.tv_sec >> 32 : 0;
uint32_t tvSecLo = now.tv_sec & 0xFFFFFFFF;
if (pressed)
hyprland_global_shortcut_v1_send_pressed(sh->resource, tvSecHi, tvSecLo, now.tv_nsec);
else
hyprland_global_shortcut_v1_send_released(sh->resource, tvSecHi, tvSecLo, now.tv_nsec);
}
}
}
}
std::vector<SShortcut> CGlobalShortcutsProtocolManager::getAllShortcuts() {
std::vector<SShortcut> copy;
for (auto& c : m_vClients) {
for (auto& sh : c->shortcuts) {
copy.push_back(*sh);
}
}
return copy;
}
void CGlobalShortcutsProtocolManager::destroyShortcut(wl_resource* resource) {
for (auto& c : m_vClients) {
std::erase_if(c->shortcuts, [&](const auto& other) { return other->resource == resource; });
}
}

View File

@@ -0,0 +1,39 @@
#pragma once
#include "../defines.hpp"
#include "hyprland-global-shortcuts-v1-protocol.h"
#include <vector>
struct SShortcut {
wl_resource* resource;
std::string id, description, appid;
uint32_t shortcut = 0;
};
struct SShortcutClient {
wl_client* client = nullptr;
std::vector<std::unique_ptr<SShortcut>> shortcuts;
};
class CGlobalShortcutsProtocolManager {
public:
CGlobalShortcutsProtocolManager();
void bindManager(wl_client* client, void* data, uint32_t version, uint32_t id);
void displayDestroy();
void registerShortcut(wl_client* client, wl_resource* resource, uint32_t shortcut, const char* id, const char* app_id, const char* description,
const char* trigger_description);
void destroyShortcut(wl_resource* resource);
bool globalShortcutExists(std::string appid, std::string trigger);
void sendGlobalShortcutEvent(std::string appid, std::string trigger, bool pressed);
std::vector<SShortcut> getAllShortcuts();
private:
std::vector<std::unique_ptr<SShortcutClient>> m_vClients;
SShortcutClient* clientFromWlClient(wl_client* client);
wl_global* m_pGlobal = nullptr;
wl_listener m_liDisplayDestroy;
};

View File

@@ -0,0 +1,490 @@
#include "Screencopy.hpp"
#include "../Compositor.hpp"
#include <drm_fourcc.h>
#include <algorithm>
#include "ToplevelExportWlrFuncs.hpp"
#define SCREENCOPY_VERSION 3
static void bindManagerInt(wl_client* client, void* data, uint32_t version, uint32_t id) {
g_pProtocolManager->m_pScreencopyProtocolManager->bindManager(client, data, version, id);
}
static void handleDisplayDestroy(struct wl_listener* listener, void* data) {
g_pProtocolManager->m_pScreencopyProtocolManager->displayDestroy();
}
void CScreencopyProtocolManager::displayDestroy() {
wl_global_destroy(m_pGlobal);
}
static SScreencopyFrame* frameFromResource(wl_resource*);
CScreencopyProtocolManager::CScreencopyProtocolManager() {
m_pGlobal = wl_global_create(g_pCompositor->m_sWLDisplay, &zwlr_screencopy_manager_v1_interface, SCREENCOPY_VERSION, this, bindManagerInt);
if (!m_pGlobal) {
Debug::log(ERR, "ScreencopyProtocolManager could not start! Screensharing will not work!");
return;
}
m_liDisplayDestroy.notify = handleDisplayDestroy;
wl_display_add_destroy_listener(g_pCompositor->m_sWLDisplay, &m_liDisplayDestroy);
Debug::log(LOG, "ScreencopyProtocolManager started successfully!");
}
static void handleCaptureOutput(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, wl_resource* output) {
g_pProtocolManager->m_pScreencopyProtocolManager->captureOutput(client, resource, frame, overlay_cursor, output);
}
static void handleCaptureRegion(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, wl_resource* output, int32_t x, int32_t y, int32_t width,
int32_t height) {
g_pProtocolManager->m_pScreencopyProtocolManager->captureOutput(client, resource, frame, overlay_cursor, output, {x, y, width, height});
}
static void handleDestroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
static void handleCopyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer) {
const auto PFRAME = frameFromResource(resource);
if (!PFRAME)
return;
g_pProtocolManager->m_pScreencopyProtocolManager->copyFrame(client, resource, buffer);
}
static void handleCopyWithDamage(wl_client* client, wl_resource* resource, wl_resource* buffer) {
const auto PFRAME = frameFromResource(resource);
if (!PFRAME)
return;
PFRAME->withDamage = true;
handleCopyFrame(client, resource, buffer);
}
static void handleDestroyFrame(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
static const struct zwlr_screencopy_manager_v1_interface screencopyMgrImpl = {
.capture_output = handleCaptureOutput,
.capture_output_region = handleCaptureRegion,
.destroy = handleDestroy,
};
static const struct zwlr_screencopy_frame_v1_interface screencopyFrameImpl = {
.copy = handleCopyFrame,
.destroy = handleDestroyFrame,
.copy_with_damage = handleCopyWithDamage,
};
static CScreencopyClient* clientFromResource(wl_resource* resource) {
ASSERT(wl_resource_instance_of(resource, &zwlr_screencopy_manager_v1_interface, &screencopyMgrImpl));
return (CScreencopyClient*)wl_resource_get_user_data(resource);
}
static SScreencopyFrame* frameFromResource(wl_resource* resource) {
ASSERT(wl_resource_instance_of(resource, &zwlr_screencopy_frame_v1_interface, &screencopyFrameImpl));
return (SScreencopyFrame*)wl_resource_get_user_data(resource);
}
void CScreencopyProtocolManager::removeClient(CScreencopyClient* client, bool force) {
if (!force) {
if (!client || client->ref <= 0)
return;
if (--client->ref != 0)
return;
}
m_lClients.remove(*client); // TODO: this doesn't get cleaned up after sharing app exits???
}
static void handleManagerResourceDestroy(wl_resource* resource) {
const auto PCLIENT = clientFromResource(resource);
g_pProtocolManager->m_pScreencopyProtocolManager->removeClient(PCLIENT, true);
}
CScreencopyClient::~CScreencopyClient() {
g_pHookSystem->unhook(tickCallback);
}
CScreencopyClient::CScreencopyClient() {
lastMeasure.reset();
lastFrame.reset();
tickCallback = g_pHookSystem->hookDynamic("tick", [&](void* self, std::any data) { onTick(); });
}
void CScreencopyClient::onTick() {
if (lastMeasure.getMillis() < 500)
return;
framesInLastHalfSecond = frameCounter;
frameCounter = 0;
lastMeasure.reset();
const auto LASTFRAMEDELTA = lastFrame.getMillis() / 1000.0;
const bool FRAMEAWAITING = std::ranges::any_of(g_pProtocolManager->m_pScreencopyProtocolManager->m_lFrames, [&](const auto& frame) { return frame.client == this; }) ||
std::ranges::any_of(g_pProtocolManager->m_pToplevelExportProtocolManager->m_lFrames, [&](const auto& frame) { return frame.client == this; });
if (framesInLastHalfSecond > 3 && !sentScreencast) {
EMIT_HOOK_EVENT("screencast", (std::vector<uint64_t>{1, (uint64_t)framesInLastHalfSecond, (uint64_t)clientOwner}));
g_pEventManager->postEvent(SHyprIPCEvent{"screencast", "1," + std::to_string(clientOwner)});
sentScreencast = true;
} else if (framesInLastHalfSecond < 4 && sentScreencast && LASTFRAMEDELTA > 1.0 && !FRAMEAWAITING) {
EMIT_HOOK_EVENT("screencast", (std::vector<uint64_t>{0, (uint64_t)framesInLastHalfSecond, (uint64_t)clientOwner}));
g_pEventManager->postEvent(SHyprIPCEvent{"screencast", "0," + std::to_string(clientOwner)});
sentScreencast = false;
}
}
void CScreencopyProtocolManager::bindManager(wl_client* client, void* data, uint32_t version, uint32_t id) {
const auto PCLIENT = &m_lClients.emplace_back();
PCLIENT->resource = wl_resource_create(client, &zwlr_screencopy_manager_v1_interface, version, id);
if (!PCLIENT->resource) {
Debug::log(ERR, "ScreencopyProtocolManager could not bind! (out of memory?)");
m_lClients.remove(*PCLIENT);
wl_client_post_no_memory(client);
return;
}
PCLIENT->ref = 1;
wl_resource_set_implementation(PCLIENT->resource, &screencopyMgrImpl, PCLIENT, handleManagerResourceDestroy);
Debug::log(LOG, "ScreencopyProtocolManager bound successfully!");
}
static void handleFrameResourceDestroy(wl_resource* resource) {
const auto PFRAME = frameFromResource(resource);
g_pProtocolManager->m_pScreencopyProtocolManager->removeFrame(PFRAME);
}
void CScreencopyProtocolManager::removeFrame(SScreencopyFrame* frame, bool force) {
if (!frame)
return;
std::erase_if(m_vFramesAwaitingWrite, [&](const auto& other) { return other == frame; });
wl_resource_set_user_data(frame->resource, nullptr);
if (frame->buffer && frame->buffer->n_locks > 0)
wlr_buffer_unlock(frame->buffer);
removeClient(frame->client, force);
m_lFrames.remove(*frame);
}
void CScreencopyProtocolManager::captureOutput(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, wl_resource* output, wlr_box box) {
const auto PCLIENT = clientFromResource(resource);
const auto PFRAME = &m_lFrames.emplace_back();
PFRAME->overlayCursor = !!overlay_cursor;
PFRAME->resource = wl_resource_create(client, &zwlr_screencopy_frame_v1_interface, wl_resource_get_version(resource), frame);
PFRAME->pMonitor = g_pCompositor->getMonitorFromOutput(wlr_output_from_resource(output));
if (!PFRAME->pMonitor) {
Debug::log(ERR, "client requested sharing of a monitor that doesnt exist");
zwlr_screencopy_frame_v1_send_failed(PFRAME->resource);
removeFrame(PFRAME);
return;
}
if (!PFRAME->resource) {
Debug::log(ERR, "Couldn't alloc frame for sharing! (no memory)");
removeFrame(PFRAME);
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(PFRAME->resource, &screencopyFrameImpl, PFRAME, handleFrameResourceDestroy);
PFRAME->client = PCLIENT;
PCLIENT->ref++;
PFRAME->shmFormat = wlr_output_preferred_read_format(PFRAME->pMonitor->output);
if (PFRAME->shmFormat == DRM_FORMAT_INVALID) {
Debug::log(ERR, "No format supported by renderer in capture output");
zwlr_screencopy_frame_v1_send_failed(PFRAME->resource);
removeFrame(PFRAME);
return;
}
const auto PSHMINFO = drm_get_pixel_format_info(PFRAME->shmFormat);
if (!PSHMINFO) {
Debug::log(ERR, "No pixel format supported by renderer in capture output");
zwlr_screencopy_frame_v1_send_failed(PFRAME->resource);
removeFrame(PFRAME);
return;
}
if (PFRAME->pMonitor->output->allocator && (PFRAME->pMonitor->output->allocator->buffer_caps & WLR_BUFFER_CAP_DMABUF)) {
PFRAME->dmabufFormat = PFRAME->pMonitor->output->render_format;
} else {
PFRAME->dmabufFormat = DRM_FORMAT_INVALID;
}
if (box.width == 0 && box.height == 0)
PFRAME->box = {0, 0, (int)(PFRAME->pMonitor->vecSize.x * PFRAME->pMonitor->scale), (int)(PFRAME->pMonitor->vecSize.y * PFRAME->pMonitor->scale)};
else {
PFRAME->box = box;
scaleBox(&PFRAME->box, PFRAME->pMonitor->scale);
}
int ow, oh;
wlr_output_effective_resolution(PFRAME->pMonitor->output, &ow, &oh);
wlr_box_transform(&PFRAME->box, &PFRAME->box, PFRAME->pMonitor->transform, ow, oh);
PFRAME->shmStride = (PSHMINFO->bpp / 8) * PFRAME->box.width;
zwlr_screencopy_frame_v1_send_buffer(PFRAME->resource, convert_drm_format_to_wl_shm(PFRAME->shmFormat), PFRAME->box.width, PFRAME->box.height, PFRAME->shmStride);
if (wl_resource_get_version(resource) >= 3) {
if (PFRAME->dmabufFormat != DRM_FORMAT_INVALID) {
zwlr_screencopy_frame_v1_send_linux_dmabuf(PFRAME->resource, PFRAME->dmabufFormat, PFRAME->box.width, PFRAME->box.height);
}
zwlr_screencopy_frame_v1_send_buffer_done(PFRAME->resource);
}
}
void CScreencopyProtocolManager::copyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer) {
const auto PFRAME = frameFromResource(resource);
if (!PFRAME) {
Debug::log(ERR, "No frame in copyFrame??");
return;
}
const auto PBUFFER = wlr_buffer_from_resource(buffer);
if (!PBUFFER) {
wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer");
removeFrame(PFRAME);
return;
}
if (PBUFFER->width != PFRAME->box.width || PBUFFER->height != PFRAME->box.height) {
wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer dimensions");
removeFrame(PFRAME);
return;
}
if (PFRAME->buffer) {
wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_ALREADY_USED, "frame already used");
removeFrame(PFRAME);
return;
}
wlr_dmabuf_attributes dmabufAttrs;
void* wlrBufferAccessData;
uint32_t wlrBufferAccessFormat;
size_t wlrBufferAccessStride;
if (wlr_buffer_get_dmabuf(PBUFFER, &dmabufAttrs)) {
PFRAME->bufferCap = WLR_BUFFER_CAP_DMABUF;
if (dmabufAttrs.format != PFRAME->dmabufFormat) {
wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format");
removeFrame(PFRAME);
return;
}
} else if (wlr_buffer_begin_data_ptr_access(PBUFFER, WLR_BUFFER_DATA_PTR_ACCESS_WRITE, &wlrBufferAccessData, &wlrBufferAccessFormat, &wlrBufferAccessStride)) {
wlr_buffer_end_data_ptr_access(PBUFFER);
if (wlrBufferAccessFormat != PFRAME->shmFormat) {
wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer format");
removeFrame(PFRAME);
return;
} else if ((int)wlrBufferAccessStride != PFRAME->shmStride) {
wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer stride");
removeFrame(PFRAME);
return;
}
} else {
wl_resource_post_error(PFRAME->resource, ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, "invalid buffer type");
removeFrame(PFRAME);
return;
}
PFRAME->buffer = PBUFFER;
m_vFramesAwaitingWrite.emplace_back(PFRAME);
g_pHyprRenderer->m_bDirectScanoutBlocked = true;
if (PFRAME->overlayCursor)
g_pHyprRenderer->m_bSoftwareCursorsLocked = true;
if (!PFRAME->withDamage)
g_pCompositor->scheduleFrameForMonitor(PFRAME->pMonitor);
}
void CScreencopyProtocolManager::onOutputCommit(CMonitor* pMonitor, wlr_output_event_commit* e) {
m_pLastMonitorBackBuffer = e->buffer;
shareAllFrames(pMonitor, true);
m_pLastMonitorBackBuffer = nullptr;
}
void CScreencopyProtocolManager::onRenderEnd(CMonitor* pMonitor) {
shareAllFrames(pMonitor, false);
}
void CScreencopyProtocolManager::shareAllFrames(CMonitor* pMonitor, bool dmabuf) {
if (m_vFramesAwaitingWrite.empty())
return; // nothing to share
std::vector<SScreencopyFrame*> framesToRemove;
// share frame if correct output
for (auto& f : m_vFramesAwaitingWrite) {
if (!f->pMonitor) {
framesToRemove.push_back(f);
continue;
}
if (f->pMonitor != pMonitor || dmabuf != (f->bufferCap == WLR_BUFFER_CAP_DMABUF))
continue;
shareFrame(f);
f->client->lastFrame.reset();
++f->client->frameCounter;
framesToRemove.push_back(f);
}
for (auto& f : framesToRemove) {
removeFrame(f);
}
g_pHyprRenderer->m_bSoftwareCursorsLocked = false;
if (m_vFramesAwaitingWrite.empty()) {
g_pHyprRenderer->m_bDirectScanoutBlocked = false;
} else {
for (auto& f : m_vFramesAwaitingWrite) {
if (f->overlayCursor) {
g_pHyprRenderer->m_bSoftwareCursorsLocked = true;
break;
}
}
}
}
void CScreencopyProtocolManager::shareFrame(SScreencopyFrame* frame) {
if (!frame->buffer)
return;
timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
uint32_t flags = 0;
if (frame->bufferCap == WLR_BUFFER_CAP_DMABUF) {
if (!copyFrameDmabuf(frame)) {
zwlr_screencopy_frame_v1_send_failed(frame->resource);
return;
}
} else {
if (!copyFrameShm(frame, &now)) {
zwlr_screencopy_frame_v1_send_failed(frame->resource);
return;
}
}
zwlr_screencopy_frame_v1_send_flags(frame->resource, flags);
sendFrameDamage(frame);
uint32_t tvSecHi = (sizeof(now.tv_sec) > 4) ? now.tv_sec >> 32 : 0;
uint32_t tvSecLo = now.tv_sec & 0xFFFFFFFF;
zwlr_screencopy_frame_v1_send_ready(frame->resource, tvSecHi, tvSecLo, now.tv_nsec);
}
void CScreencopyProtocolManager::sendFrameDamage(SScreencopyFrame* frame) {
if (!frame->withDamage)
return;
const auto RECT = pixman_region32_extents(g_pHyprOpenGL->m_RenderData.pDamage);
zwlr_screencopy_frame_v1_send_damage(frame->resource, std::clamp(RECT->x1, 0, frame->buffer->width), std::clamp(RECT->y1, 0, frame->buffer->height),
std::clamp(RECT->x2 - RECT->x1, 0, frame->buffer->width - RECT->x1), std::clamp(RECT->y2 - RECT->y1, 0, frame->buffer->height - RECT->y1));
}
bool CScreencopyProtocolManager::copyFrameShm(SScreencopyFrame* frame, timespec* now) {
void* data;
uint32_t format;
size_t stride;
if (!wlr_buffer_begin_data_ptr_access(frame->buffer, WLR_BUFFER_DATA_PTR_ACCESS_WRITE, &data, &format, &stride))
return false;
// render the client
const auto PMONITOR = frame->pMonitor;
pixman_region32_t fakeDamage;
pixman_region32_init_rect(&fakeDamage, 0, 0, PMONITOR->vecPixelSize.x * 10, PMONITOR->vecPixelSize.y * 10);
if (!wlr_output_attach_render(PMONITOR->output, nullptr)) {
Debug::log(ERR, "[screencopy] Couldn't attach render");
pixman_region32_fini(&fakeDamage);
wlr_buffer_end_data_ptr_access(frame->buffer);
return false;
}
const auto PFORMAT = get_gles2_format_from_drm(format);
if (!PFORMAT) {
Debug::log(ERR, "[screencopy] Cannot read pixels, unsupported format %lx", PFORMAT);
wlr_output_rollback(PMONITOR->output);
pixman_region32_fini(&fakeDamage);
wlr_buffer_end_data_ptr_access(frame->buffer);
return false;
}
g_pHyprOpenGL->begin(PMONITOR, &fakeDamage, true);
// we should still have the last frame by this point in the original fb
glBindFramebuffer(GL_FRAMEBUFFER, g_pHyprOpenGL->m_RenderData.pCurrentMonData->primaryFB.m_iFb);
glFinish(); // flush
glReadPixels(frame->box.x, frame->box.y, frame->box.width, frame->box.height, PFORMAT->gl_format, PFORMAT->gl_type, data);
glBindFramebuffer(GL_FRAMEBUFFER, g_pHyprOpenGL->m_iWLROutputFb);
g_pHyprOpenGL->end();
wlr_output_rollback(PMONITOR->output);
pixman_region32_fini(&fakeDamage);
wlr_buffer_end_data_ptr_access(frame->buffer);
return true;
}
bool CScreencopyProtocolManager::copyFrameDmabuf(SScreencopyFrame* frame) {
wlr_texture* sourceTex = wlr_texture_from_buffer(g_pCompositor->m_sWLRRenderer, m_pLastMonitorBackBuffer);
if (!sourceTex)
return false;
float glMatrix[9];
wlr_matrix_identity(glMatrix);
wlr_matrix_scale(glMatrix, frame->pMonitor->vecPixelSize.x, frame->pMonitor->vecPixelSize.y);
wlr_matrix_translate(glMatrix, -frame->box.x, -frame->box.y);
if (!wlr_renderer_begin_with_buffer(g_pCompositor->m_sWLRRenderer, frame->buffer)) {
wlr_texture_destroy(sourceTex);
return false;
}
float color[] = {0, 0, 0, 0};
wlr_renderer_clear(g_pCompositor->m_sWLRRenderer, color);
wlr_render_texture_with_matrix(g_pCompositor->m_sWLRRenderer, sourceTex, glMatrix, 1.0f);
wlr_texture_destroy(sourceTex);
wlr_renderer_end(g_pCompositor->m_sWLRRenderer);
return true;
}

View File

@@ -0,0 +1,101 @@
#pragma once
#include "../defines.hpp"
#include "wlr-screencopy-unstable-v1-protocol.h"
#include <list>
#include <vector>
#include "../managers/HookSystemManager.hpp"
#include "../helpers/Timer.hpp"
class CMonitor;
enum eClientOwners
{
CLIENT_SCREENCOPY = 0,
CLIENT_TOPLEVEL_EXPORT
};
class CScreencopyClient {
public:
CScreencopyClient();
~CScreencopyClient();
int ref = 0;
wl_resource* resource = nullptr;
eClientOwners clientOwner = CLIENT_SCREENCOPY;
int frameCounter = 0;
int framesInLastHalfSecond = 0;
CTimer lastMeasure;
CTimer lastFrame;
bool sentScreencast = false;
void onTick();
HOOK_CALLBACK_FN* tickCallback = nullptr;
bool operator==(const CScreencopyClient& other) const {
return resource == other.resource;
}
};
struct SScreencopyFrame {
wl_resource* resource = nullptr;
CScreencopyClient* client = nullptr;
uint32_t shmFormat = 0;
uint32_t dmabufFormat = 0;
wlr_box box = {0};
int shmStride = 0;
bool overlayCursor = false;
bool withDamage = false;
wlr_buffer_cap bufferCap = WLR_BUFFER_CAP_SHM;
wlr_buffer* buffer = nullptr;
CMonitor* pMonitor = nullptr;
CWindow* pWindow = nullptr;
bool operator==(const SScreencopyFrame& other) const {
return resource == other.resource && client == other.client;
}
};
class CScreencopyProtocolManager {
public:
CScreencopyProtocolManager();
void bindManager(wl_client* client, void* data, uint32_t version, uint32_t id);
void removeClient(CScreencopyClient* client, bool force = false);
void removeFrame(SScreencopyFrame* frame, bool force = false);
void displayDestroy();
void captureOutput(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, wl_resource* output, wlr_box box = {0, 0, 0, 0});
void copyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer);
void onRenderEnd(CMonitor* pMonitor);
void onOutputCommit(CMonitor* pMonitor, wlr_output_event_commit* e);
private:
wl_global* m_pGlobal = nullptr;
std::list<SScreencopyFrame> m_lFrames;
std::list<CScreencopyClient> m_lClients;
wl_listener m_liDisplayDestroy;
std::vector<SScreencopyFrame*> m_vFramesAwaitingWrite;
wlr_buffer* m_pLastMonitorBackBuffer = nullptr;
void shareAllFrames(CMonitor* pMonitor, bool dmabuf);
void shareFrame(SScreencopyFrame* frame);
void sendFrameDamage(SScreencopyFrame* frame);
bool copyFrameDmabuf(SScreencopyFrame* frame);
bool copyFrameShm(SScreencopyFrame* frame, timespec* now);
friend class CScreencopyClient;
};

View File

@@ -135,13 +135,12 @@ static void destroyTI(wl_resource* resource) {
TI->pTextInput->hyprListener_textInputDestroy.emit(nullptr);
g_pInputManager->m_sIMERelay.removeTextInput(TI->pTextInput);
g_pProtocolManager->m_pTextInputV1ProtocolManager->removeTI(TI);
}
void CTextInputV1ProtocolManager::createTI(wl_client* client, wl_resource* resource, uint32_t id) {
const auto PTI = m_pClients.emplace_back(std::make_unique<STextInputV1>()).get();
Debug::log(LOG, "New TI V1 at %x", PTI);
Debug::log(LOG, "New TI V1 at %lx", PTI);
PTI->client = client;
PTI->resourceCaller = resource;

View File

@@ -47,11 +47,11 @@ wlr_foreign_toplevel_handle_v1* zwlrHandleFromResource(wl_resource* resource) {
return (wlr_foreign_toplevel_handle_v1*)wl_resource_get_user_data(resource);
}
void handleCaptureToplevel(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, uint32_t handle) {
static void handleCaptureToplevel(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, uint32_t handle) {
g_pProtocolManager->m_pToplevelExportProtocolManager->captureToplevel(client, resource, frame, overlay_cursor, g_pCompositor->getWindowFromHandle(handle));
}
void handleCaptureToplevelWithWlr(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, wl_resource* handle) {
static void handleCaptureToplevelWithWlr(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, wl_resource* handle) {
g_pProtocolManager->m_pToplevelExportProtocolManager->captureToplevel(client, resource, frame, overlay_cursor, g_pCompositor->getWindowFromZWLRHandle(handle));
}
@@ -59,11 +59,11 @@ static void handleDestroy(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
void handleCopyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer, int32_t ignore_damage) {
static void handleCopyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer, int32_t ignore_damage) {
g_pProtocolManager->m_pToplevelExportProtocolManager->copyFrame(client, resource, buffer, ignore_damage);
}
void handleDestroyFrame(wl_client* client, wl_resource* resource) {
static void handleDestroyFrame(wl_client* client, wl_resource* resource) {
wl_resource_destroy(resource);
}
@@ -75,17 +75,17 @@ static const struct hyprland_toplevel_export_manager_v1_interface toplevelExport
static const struct hyprland_toplevel_export_frame_v1_interface toplevelFrameImpl = {.copy = handleCopyFrame, .destroy = handleDestroyFrame};
SToplevelClient* clientFromResource(wl_resource* resource) {
static CScreencopyClient* clientFromResource(wl_resource* resource) {
ASSERT(wl_resource_instance_of(resource, &hyprland_toplevel_export_manager_v1_interface, &toplevelExportManagerImpl));
return (SToplevelClient*)wl_resource_get_user_data(resource);
return (CScreencopyClient*)wl_resource_get_user_data(resource);
}
SToplevelFrame* frameFromResource(wl_resource* resource) {
static SScreencopyFrame* frameFromResource(wl_resource* resource) {
ASSERT(wl_resource_instance_of(resource, &hyprland_toplevel_export_frame_v1_interface, &toplevelFrameImpl));
return (SToplevelFrame*)wl_resource_get_user_data(resource);
return (SScreencopyFrame*)wl_resource_get_user_data(resource);
}
void CToplevelExportProtocolManager::removeClient(SToplevelClient* client, bool force) {
void CToplevelExportProtocolManager::removeClient(CScreencopyClient* client, bool force) {
if (!force) {
if (!client || client->ref <= 0)
return;
@@ -106,7 +106,8 @@ static void handleManagerResourceDestroy(wl_resource* resource) {
void CToplevelExportProtocolManager::bindManager(wl_client* client, void* data, uint32_t version, uint32_t id) {
const auto PCLIENT = &m_lClients.emplace_back();
PCLIENT->resource = wl_resource_create(client, &hyprland_toplevel_export_manager_v1_interface, version, id);
PCLIENT->clientOwner = CLIENT_TOPLEVEL_EXPORT;
PCLIENT->resource = wl_resource_create(client, &hyprland_toplevel_export_manager_v1_interface, version, id);
if (!PCLIENT->resource) {
Debug::log(ERR, "ToplevelExportManager could not bind! (out of memory?)");
@@ -122,13 +123,13 @@ void CToplevelExportProtocolManager::bindManager(wl_client* client, void* data,
Debug::log(LOG, "ToplevelExportManager bound successfully!");
}
void handleFrameResourceDestroy(wl_resource* resource) {
static void handleFrameResourceDestroy(wl_resource* resource) {
const auto PFRAME = frameFromResource(resource);
g_pProtocolManager->m_pToplevelExportProtocolManager->removeFrame(PFRAME);
}
void CToplevelExportProtocolManager::removeFrame(SToplevelFrame* frame, bool force) {
void CToplevelExportProtocolManager::removeFrame(SScreencopyFrame* frame, bool force) {
if (!frame)
return;
@@ -150,14 +151,14 @@ void CToplevelExportProtocolManager::captureToplevel(wl_client* client, wl_resou
PFRAME->pWindow = pWindow;
if (!PFRAME->pWindow) {
Debug::log(ERR, "Client requested sharing of window handle %x which does not exist!", PFRAME->pWindow);
Debug::log(ERR, "Client requested sharing of window handle %lx which does not exist!", PFRAME->pWindow);
hyprland_toplevel_export_frame_v1_send_failed(PFRAME->resource);
removeFrame(PFRAME);
return;
}
if (!PFRAME->pWindow->m_bIsMapped || PFRAME->pWindow->isHidden()) {
Debug::log(ERR, "Client requested sharing of window handle %x which is not shareable!", PFRAME->pWindow);
Debug::log(ERR, "Client requested sharing of window handle %lx which is not shareable!", PFRAME->pWindow);
hyprland_toplevel_export_frame_v1_send_failed(PFRAME->resource);
removeFrame(PFRAME);
return;
@@ -218,7 +219,7 @@ void CToplevelExportProtocolManager::copyFrame(wl_client* client, wl_resource* r
}
if (!PFRAME->pWindow->m_bIsMapped || PFRAME->pWindow->isHidden()) {
Debug::log(ERR, "Client requested sharing of window handle %x which is not shareable (2)!", PFRAME->pWindow);
Debug::log(ERR, "Client requested sharing of window handle %lx which is not shareable (2)!", PFRAME->pWindow);
hyprland_toplevel_export_frame_v1_send_failed(PFRAME->resource);
removeFrame(PFRAME);
return;
@@ -283,7 +284,7 @@ void CToplevelExportProtocolManager::onMonitorRender(CMonitor* pMonitor) {
if (m_vFramesAwaitingWrite.empty())
return; // nothing to share
std::vector<SToplevelFrame*> framesToRemove;
std::vector<SScreencopyFrame*> framesToRemove;
// share frame if correct output
for (auto& f : m_vFramesAwaitingWrite) {
@@ -299,6 +300,9 @@ void CToplevelExportProtocolManager::onMonitorRender(CMonitor* pMonitor) {
shareFrame(f);
f->client->lastFrame.reset();
++f->client->frameCounter;
framesToRemove.push_back(f);
}
@@ -307,7 +311,7 @@ void CToplevelExportProtocolManager::onMonitorRender(CMonitor* pMonitor) {
}
}
void CToplevelExportProtocolManager::shareFrame(SToplevelFrame* frame) {
void CToplevelExportProtocolManager::shareFrame(SScreencopyFrame* frame) {
if (!frame->buffer) {
return;
}
@@ -337,7 +341,7 @@ void CToplevelExportProtocolManager::shareFrame(SToplevelFrame* frame) {
hyprland_toplevel_export_frame_v1_send_ready(frame->resource, tvSecHi, tvSecLo, now.tv_nsec);
}
bool CToplevelExportProtocolManager::copyFrameShm(SToplevelFrame* frame, timespec* now) {
bool CToplevelExportProtocolManager::copyFrameShm(SScreencopyFrame* frame, timespec* now) {
void* data;
uint32_t format;
size_t stride;
@@ -349,10 +353,15 @@ bool CToplevelExportProtocolManager::copyFrameShm(SToplevelFrame* frame, timespe
pixman_region32_t fakeDamage;
pixman_region32_init_rect(&fakeDamage, 0, 0, PMONITOR->vecPixelSize.x * 10, PMONITOR->vecPixelSize.y * 10);
if (frame->overlayCursor)
wlr_output_lock_software_cursors(PMONITOR->output, true);
if (!wlr_output_attach_render(PMONITOR->output, nullptr)) {
Debug::log(ERR, "[toplevel_export] Couldn't attach render");
pixman_region32_fini(&fakeDamage);
wlr_buffer_end_data_ptr_access(frame->buffer);
if (frame->overlayCursor)
wlr_output_lock_software_cursors(PMONITOR->output, false);
return false;
}
@@ -364,13 +373,37 @@ bool CToplevelExportProtocolManager::copyFrameShm(SToplevelFrame* frame, timespe
g_pHyprRenderer->renderWindow(frame->pWindow, PMONITOR, now, false, RENDER_PASS_ALL, true, true);
g_pHyprRenderer->m_bBlockSurfaceFeedback = false;
if (frame->overlayCursor && wlr_renderer_begin(g_pCompositor->m_sWLRRenderer, PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y)) {
// hack le massive
wlr_output_cursor* cursor;
const auto OFFSET = frame->pWindow->m_vRealPosition.vec() - PMONITOR->vecPosition;
wl_list_for_each(cursor, &PMONITOR->output->cursors, link) {
if (!cursor->enabled || !cursor->visible || PMONITOR->output->hardware_cursor == cursor) {
continue;
}
cursor->x -= OFFSET.x;
cursor->y -= OFFSET.y;
}
wlr_output_render_software_cursors(PMONITOR->output, NULL);
wl_list_for_each(cursor, &PMONITOR->output->cursors, link) {
if (!cursor->enabled || !cursor->visible || PMONITOR->output->hardware_cursor == cursor) {
continue;
}
cursor->x += OFFSET.x;
cursor->y += OFFSET.y;
}
wlr_renderer_end(g_pCompositor->m_sWLRRenderer);
}
// copy pixels
const auto PFORMAT = get_gles2_format_from_drm(format);
if (!PFORMAT) {
Debug::log(ERR, "[toplevel_export] Cannot read pixels, unsupported format %x", PFORMAT);
Debug::log(ERR, "[toplevel_export] Cannot read pixels, unsupported format %lx", PFORMAT);
g_pHyprOpenGL->end();
pixman_region32_fini(&fakeDamage);
wlr_buffer_end_data_ptr_access(frame->buffer);
if (frame->overlayCursor)
wlr_output_lock_software_cursors(PMONITOR->output, false);
return false;
}
@@ -388,10 +421,13 @@ bool CToplevelExportProtocolManager::copyFrameShm(SToplevelFrame* frame, timespe
wlr_buffer_end_data_ptr_access(frame->buffer);
if (frame->overlayCursor)
wlr_output_lock_software_cursors(PMONITOR->output, false);
return true;
}
bool CToplevelExportProtocolManager::copyFrameDmabuf(SToplevelFrame* frame) {
bool CToplevelExportProtocolManager::copyFrameDmabuf(SScreencopyFrame* frame) {
// todo
Debug::log(ERR, "DMABUF copying not impl'd!");
return false;

View File

@@ -3,6 +3,7 @@
#include "../defines.hpp"
#include "wlr-foreign-toplevel-management-unstable-v1-protocol.h"
#include "hyprland-toplevel-export-v1-protocol.h"
#include "Screencopy.hpp"
#include <list>
#include <vector>
@@ -10,61 +11,32 @@
class CMonitor;
class CWindow;
struct SToplevelClient {
int ref = 0;
wl_resource* resource = nullptr;
bool operator==(const SToplevelClient& other) const {
return resource == other.resource;
}
};
struct SToplevelFrame {
wl_resource* resource = nullptr;
SToplevelClient* client = nullptr;
uint32_t shmFormat = 0;
uint32_t dmabufFormat = 0;
wlr_box box = {0};
int shmStride = 0;
bool overlayCursor = false;
wlr_buffer_cap bufferCap = WLR_BUFFER_CAP_SHM;
wlr_buffer* buffer = nullptr;
CWindow* pWindow = nullptr;
bool operator==(const SToplevelFrame& other) const {
return resource == other.resource && client == other.client;
}
};
class CToplevelExportProtocolManager {
public:
CToplevelExportProtocolManager();
void bindManager(wl_client* client, void* data, uint32_t version, uint32_t id);
void captureToplevel(wl_client* client, wl_resource* resource, uint32_t frame, int32_t overlay_cursor, CWindow* handle);
void removeClient(SToplevelClient* client, bool force = false);
void removeFrame(SToplevelFrame* frame, bool force = false);
void removeClient(CScreencopyClient* client, bool force = false);
void removeFrame(SScreencopyFrame* frame, bool force = false);
void copyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer, int32_t ignore_damage);
void displayDestroy();
void onWindowUnmap(CWindow* pWindow);
private:
wl_global* m_pGlobal = nullptr;
std::list<SToplevelFrame> m_lFrames;
std::list<SToplevelClient> m_lClients;
wl_global* m_pGlobal = nullptr;
std::list<SScreencopyFrame> m_lFrames;
std::list<CScreencopyClient> m_lClients;
wl_listener m_liDisplayDestroy;
wl_listener m_liDisplayDestroy;
std::vector<SToplevelFrame*> m_vFramesAwaitingWrite;
std::vector<SScreencopyFrame*> m_vFramesAwaitingWrite;
void shareFrame(SToplevelFrame* frame);
bool copyFrameDmabuf(SToplevelFrame* frame);
bool copyFrameShm(SToplevelFrame* frame, timespec* now);
void shareFrame(SScreencopyFrame* frame);
bool copyFrameDmabuf(SScreencopyFrame* frame);
bool copyFrameShm(SScreencopyFrame* frame, timespec* now);
void onMonitorRender(CMonitor* pMonitor);
void onMonitorRender(CMonitor* pMonitor);
friend class CScreencopyClient;
};

View File

@@ -1,5 +1,8 @@
#include <GLES2/gl2ext.h>
#ifndef DRM_WLR_FUNCS
#define DRM_WLR_FUNCS
struct wlr_pixel_format_info {
uint32_t drm_format;
@@ -156,9 +159,9 @@ static const struct wlr_pixel_format_info pixel_format_info[] = {
},
};
static const size_t pixel_format_info_size = sizeof(pixel_format_info) / sizeof(pixel_format_info[0]);
static const size_t pixel_format_info_size = sizeof(pixel_format_info) / sizeof(pixel_format_info[0]);
const struct wlr_pixel_format_info* drm_get_pixel_format_info(uint32_t fmt) {
static const struct wlr_pixel_format_info* drm_get_pixel_format_info(uint32_t fmt) {
for (size_t i = 0; i < pixel_format_info_size; ++i) {
if (pixel_format_info[i].drm_format == fmt) {
return &pixel_format_info[i];
@@ -168,15 +171,15 @@ const struct wlr_pixel_format_info* drm_get_pixel_format_info(uint32_t fmt) {
return NULL;
}
uint32_t convert_wl_shm_format_to_drm(enum wl_shm_format fmt) {
/*static uint32_t convert_wl_shm_format_to_drm(enum wl_shm_format fmt) {
switch (fmt) {
case WL_SHM_FORMAT_XRGB8888: return DRM_FORMAT_XRGB8888;
case WL_SHM_FORMAT_ARGB8888: return DRM_FORMAT_ARGB8888;
default: return (uint32_t)fmt;
}
}
}*/
enum wl_shm_format convert_drm_format_to_wl_shm(uint32_t fmt) {
static enum wl_shm_format convert_drm_format_to_wl_shm(uint32_t fmt) {
switch (fmt) {
case DRM_FORMAT_XRGB8888: return WL_SHM_FORMAT_XRGB8888;
case DRM_FORMAT_ARGB8888: return WL_SHM_FORMAT_ARGB8888;
@@ -223,7 +226,7 @@ static const struct wlr_gles2_pixel_format formats[] = {
.gl_type = GL_UNSIGNED_BYTE,
.has_alpha = false,
},
#if WLR_LITTLE_ENDIAN
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
{
.drm_format = DRM_FORMAT_RGBX4444,
.gl_format = GL_RGBA,
@@ -295,7 +298,7 @@ static const struct wlr_gles2_pixel_format formats[] = {
#endif
};
const struct wlr_gles2_pixel_format* get_gles2_format_from_drm(uint32_t fmt) {
static const struct wlr_gles2_pixel_format* get_gles2_format_from_drm(uint32_t fmt) {
for (size_t i = 0; i < sizeof(formats) / sizeof(*formats); ++i) {
if (formats[i].drm_format == fmt) {
return &formats[i];
@@ -303,3 +306,5 @@ const struct wlr_gles2_pixel_format* get_gles2_format_from_drm(uint32_t fmt) {
}
return NULL;
}
#endif

View File

@@ -145,6 +145,8 @@ void CHyprOpenGLImpl::begin(CMonitor* pMonitor, pixman_region32_t* pDamage, bool
}
void CHyprOpenGLImpl::end() {
static auto* const PZOOMRIGID = &g_pConfigManager->getConfigValuePtr("misc:cursor_zoom_rigid")->intValue;
// end the render, copy the data to the WLR framebuffer
if (!m_bFakeFrame) {
pixman_region32_copy(m_RenderData.pDamage, &m_rOriginalDamageRegion);
@@ -155,20 +157,44 @@ void CHyprOpenGLImpl::end() {
glBindFramebuffer(GL_FRAMEBUFFER, m_iWLROutputFb);
wlr_box monbox = {0, 0, m_RenderData.pMonitor->vecTransformedSize.x, m_RenderData.pMonitor->vecTransformedSize.y};
if (m_RenderData.mouseZoomFactor != 1.f) {
const auto ZOOMCENTER =
m_RenderData.mouseZoomUseMouse ? g_pInputManager->getMouseCoordsInternal() - m_RenderData.pMonitor->vecPosition : m_RenderData.pMonitor->vecTransformedSize / 2.f;
monbox.x -= ZOOMCENTER.x;
monbox.y -= ZOOMCENTER.y;
scaleBox(&monbox, m_RenderData.mouseZoomFactor);
monbox.x += *PZOOMRIGID ? m_RenderData.pMonitor->vecTransformedSize.x / 2 : ZOOMCENTER.x;
monbox.y += *PZOOMRIGID ? m_RenderData.pMonitor->vecTransformedSize.y / 2 : ZOOMCENTER.y;
if (monbox.x > 0)
monbox.x = 0;
if (monbox.y > 0)
monbox.y = 0;
if (monbox.x + monbox.width < m_RenderData.pMonitor->vecTransformedSize.x)
monbox.x = m_RenderData.pMonitor->vecTransformedSize.x - monbox.width;
if (monbox.y + monbox.height < m_RenderData.pMonitor->vecTransformedSize.y)
monbox.y = m_RenderData.pMonitor->vecTransformedSize.y - monbox.height;
}
clear(CColor(11.0 / 255.0, 11.0 / 255.0, 11.0 / 255.0, 1.0));
m_bEndFrame = true;
m_bApplyFinalShader = true;
if (m_RenderData.mouseZoomUseMouse)
m_RenderData.useNearestNeighbor = true;
renderTexture(m_RenderData.pCurrentMonData->primaryFB.m_cTex, &monbox, 1.f, 0);
m_bApplyFinalShader = false;
m_bEndFrame = false;
m_RenderData.useNearestNeighbor = false;
m_bApplyFinalShader = false;
m_bEndFrame = false;
}
// reset our data
m_RenderData.pMonitor = nullptr;
m_iWLROutputFb = 0;
m_RenderData.pMonitor = nullptr;
m_iWLROutputFb = 0;
m_RenderData.mouseZoomFactor = 1.f;
m_RenderData.mouseZoomUseMouse = true;
}
void CHyprOpenGLImpl::initShaders() {
@@ -190,6 +216,7 @@ void CHyprOpenGLImpl::initShaders() {
m_RenderData.pCurrentMonData->m_shRGBA.texAttrib = glGetAttribLocation(prog, "texcoord");
m_RenderData.pCurrentMonData->m_shRGBA.posAttrib = glGetAttribLocation(prog, "pos");
m_RenderData.pCurrentMonData->m_shRGBA.discardOpaque = glGetUniformLocation(prog, "discardOpaque");
m_RenderData.pCurrentMonData->m_shRGBA.discardAlphaZero = glGetUniformLocation(prog, "discardAlphaZero");
m_RenderData.pCurrentMonData->m_shRGBA.topLeft = glGetUniformLocation(prog, "topLeft");
m_RenderData.pCurrentMonData->m_shRGBA.fullSize = glGetUniformLocation(prog, "fullSize");
m_RenderData.pCurrentMonData->m_shRGBA.radius = glGetUniformLocation(prog, "radius");
@@ -204,6 +231,16 @@ void CHyprOpenGLImpl::initShaders() {
m_RenderData.pCurrentMonData->m_shPASSTHRURGBA.texAttrib = glGetAttribLocation(prog, "texcoord");
m_RenderData.pCurrentMonData->m_shPASSTHRURGBA.posAttrib = glGetAttribLocation(prog, "pos");
prog = createProgram(TEXVERTSRC, FRAGGLITCH);
m_RenderData.pCurrentMonData->m_shGLITCH.program = prog;
m_RenderData.pCurrentMonData->m_shGLITCH.proj = glGetUniformLocation(prog, "proj");
m_RenderData.pCurrentMonData->m_shGLITCH.tex = glGetUniformLocation(prog, "tex");
m_RenderData.pCurrentMonData->m_shGLITCH.texAttrib = glGetAttribLocation(prog, "texcoord");
m_RenderData.pCurrentMonData->m_shGLITCH.posAttrib = glGetAttribLocation(prog, "pos");
m_RenderData.pCurrentMonData->m_shGLITCH.distort = glGetUniformLocation(prog, "distort");
m_RenderData.pCurrentMonData->m_shGLITCH.time = glGetUniformLocation(prog, "time");
m_RenderData.pCurrentMonData->m_shGLITCH.fullSize = glGetUniformLocation(prog, "screenSize");
prog = createProgram(TEXVERTSRC, TEXFRAGSRCRGBX);
m_RenderData.pCurrentMonData->m_shRGBX.program = prog;
m_RenderData.pCurrentMonData->m_shRGBX.tex = glGetUniformLocation(prog, "tex");
@@ -212,6 +249,7 @@ void CHyprOpenGLImpl::initShaders() {
m_RenderData.pCurrentMonData->m_shRGBX.texAttrib = glGetAttribLocation(prog, "texcoord");
m_RenderData.pCurrentMonData->m_shRGBX.posAttrib = glGetAttribLocation(prog, "pos");
m_RenderData.pCurrentMonData->m_shRGBX.discardOpaque = glGetUniformLocation(prog, "discardOpaque");
m_RenderData.pCurrentMonData->m_shRGBX.discardAlphaZero = glGetUniformLocation(prog, "discardAlphaZero");
m_RenderData.pCurrentMonData->m_shRGBX.topLeft = glGetUniformLocation(prog, "topLeft");
m_RenderData.pCurrentMonData->m_shRGBX.fullSize = glGetUniformLocation(prog, "fullSize");
m_RenderData.pCurrentMonData->m_shRGBX.radius = glGetUniformLocation(prog, "radius");
@@ -227,6 +265,7 @@ void CHyprOpenGLImpl::initShaders() {
m_RenderData.pCurrentMonData->m_shEXT.posAttrib = glGetAttribLocation(prog, "pos");
m_RenderData.pCurrentMonData->m_shEXT.texAttrib = glGetAttribLocation(prog, "texcoord");
m_RenderData.pCurrentMonData->m_shEXT.discardOpaque = glGetUniformLocation(prog, "discardOpaque");
m_RenderData.pCurrentMonData->m_shEXT.discardAlphaZero = glGetUniformLocation(prog, "discardAlphaZero");
m_RenderData.pCurrentMonData->m_shEXT.topLeft = glGetUniformLocation(prog, "topLeft");
m_RenderData.pCurrentMonData->m_shEXT.fullSize = glGetUniformLocation(prog, "fullSize");
m_RenderData.pCurrentMonData->m_shEXT.radius = glGetUniformLocation(prog, "radius");
@@ -315,7 +354,7 @@ void CHyprOpenGLImpl::applyScreenShader(const std::string& path) {
m_sFinalScreenShader.proj = glGetUniformLocation(m_sFinalScreenShader.program, "proj");
m_sFinalScreenShader.tex = glGetUniformLocation(m_sFinalScreenShader.program, "tex");
m_sFinalScreenShader.time = glGetUniformLocation(m_sFinalScreenShader.program, "time");
if (m_sFinalScreenShader.time != -1 && g_pConfigManager->getInt("debug:damage_tracking") != 0) {
if (m_sFinalScreenShader.time != -1 && g_pConfigManager->getInt("debug:damage_tracking") != 0 && !g_pHyprRenderer->m_bCrashingInProgress) {
// The screen shader uses the "time" uniform
// Since the screen shader could change every frame, damage tracking *needs* to be disabled
g_pConfigManager->addParseError("Screen shader: Screen shader uses uniform 'time', which requires debug:damage_tracking to be switched off.\n"
@@ -391,6 +430,13 @@ void CHyprOpenGLImpl::renderRectWithDamage(wlr_box* box, const CColor& col, pixm
RASSERT((box->width > 0 && box->height > 0), "Tried to render rect with width/height < 0!");
RASSERT(m_RenderData.pMonitor, "Tried to render rect without begin()!");
wlr_box newBox = *box;
scaleBox(&newBox, m_RenderData.renderModif.scale);
newBox.x += m_RenderData.renderModif.translate.x;
newBox.y += m_RenderData.renderModif.translate.y;
box = &newBox;
float matrix[9];
wlr_matrix_project_box(matrix, box, wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform), 0,
m_RenderData.pMonitor->output->transform_matrix); // TODO: write own, don't use WLR here
@@ -466,15 +512,15 @@ void CHyprOpenGLImpl::renderTexture(wlr_texture* tex, wlr_box* pBox, float alpha
renderTexture(CTexture(tex), pBox, alpha, round, false, allowCustomUV);
}
void CHyprOpenGLImpl::renderTexture(const CTexture& tex, wlr_box* pBox, float alpha, int round, bool discardopaque, bool allowCustomUV) {
void CHyprOpenGLImpl::renderTexture(const CTexture& tex, wlr_box* pBox, float alpha, int round, bool discardActive, bool allowCustomUV) {
RASSERT(m_RenderData.pMonitor, "Tried to render texture without begin()!");
renderTextureInternalWithDamage(tex, pBox, alpha, m_RenderData.pDamage, round, discardopaque, false, allowCustomUV, true);
renderTextureInternalWithDamage(tex, pBox, alpha, m_RenderData.pDamage, round, discardActive, false, allowCustomUV, true);
scissor((wlr_box*)nullptr);
}
void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, wlr_box* pBox, float alpha, pixman_region32_t* damage, int round, bool discardOpaque, bool noAA,
void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, wlr_box* pBox, float alpha, pixman_region32_t* damage, int round, bool discardActive, bool noAA,
bool allowCustomUV, bool allowDim) {
RASSERT(m_RenderData.pMonitor, "Tried to render texture without begin()!");
RASSERT((tex.m_iTexID > 0), "Attempted to draw NULL texture!");
@@ -484,12 +530,17 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, wlr_b
if (!pixman_region32_not_empty(m_RenderData.pDamage))
return;
wlr_box newBox = *pBox;
scaleBox(&newBox, m_RenderData.renderModif.scale);
newBox.x += m_RenderData.renderModif.translate.x;
newBox.y += m_RenderData.renderModif.translate.y;
static auto* const PDIMINACTIVE = &g_pConfigManager->getConfigValuePtr("decoration:dim_inactive")->intValue;
// get transform
const auto TRANSFORM = wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform);
float matrix[9];
wlr_matrix_project_box(matrix, pBox, TRANSFORM, 0, m_RenderData.pMonitor->output->transform_matrix);
wlr_matrix_project_box(matrix, &newBox, TRANSFORM, 0, m_RenderData.pMonitor->output->transform_matrix);
float glMatrix[9];
wlr_matrix_multiply(glMatrix, m_RenderData.projection, matrix);
@@ -499,9 +550,14 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, wlr_b
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
bool usingFinalShader = false;
bool usingFinalShader = false;
if (m_bApplyFinalShader && m_sFinalScreenShader.program) {
const bool CRASHING = m_bApplyFinalShader && g_pHyprRenderer->m_bCrashingInProgress;
if (CRASHING) {
shader = &m_RenderData.pCurrentMonData->m_shGLITCH;
usingFinalShader = true;
} else if (m_bApplyFinalShader && m_sFinalScreenShader.program) {
shader = &m_sFinalScreenShader;
usingFinalShader = true;
} else {
@@ -518,10 +574,19 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, wlr_b
}
}
if (m_pCurrentWindow && m_pCurrentWindow->m_sAdditionalConfigData.forceRGBX)
shader = &m_RenderData.pCurrentMonData->m_shRGBX;
glActiveTexture(GL_TEXTURE0);
glBindTexture(tex.m_iTarget, tex.m_iTexID);
glTexParameteri(tex.m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
if (m_RenderData.useNearestNeighbor) {
glTexParameteri(tex.m_iTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(tex.m_iTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
} else {
glTexParameteri(tex.m_iTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(tex.m_iTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
glUseProgram(shader->program);
@@ -533,20 +598,32 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, wlr_b
#endif
glUniform1i(shader->tex, 0);
if (usingFinalShader && g_pConfigManager->getInt("debug:damage_tracking") == 0) {
if ((usingFinalShader && g_pConfigManager->getInt("debug:damage_tracking") == 0) || CRASHING) {
glUniform1f(shader->time, m_tGlobalTimer.getSeconds());
} else if (usingFinalShader && shader->time > 0) {
// Don't let time be unitialised
glUniform1f(shader->time, 0.f);
}
if (CRASHING) {
glUniform1f(shader->distort, g_pHyprRenderer->m_fCrashingDistort);
glUniform2f(shader->fullSize, m_RenderData.pMonitor->vecPixelSize.x, m_RenderData.pMonitor->vecPixelSize.y);
}
if (!usingFinalShader) {
glUniform1f(shader->alpha, alpha);
glUniform1i(shader->discardOpaque, (int)discardOpaque);
if (discardActive) {
glUniform1i(shader->discardOpaque, !!(m_RenderData.discardMode & DISCARD_OPAQUE));
glUniform1i(shader->discardAlphaZero, !!(m_RenderData.discardMode & DISCARD_ALPHAZERO));
} else {
glUniform1i(shader->discardOpaque, 0);
glUniform1i(shader->discardAlphaZero, 0);
}
}
wlr_box transformedBox;
wlr_box_transform(&transformedBox, pBox, wlr_output_transform_invert(m_RenderData.pMonitor->transform), m_RenderData.pMonitor->vecTransformedSize.x,
wlr_box_transform(&transformedBox, &newBox, wlr_output_transform_invert(m_RenderData.pMonitor->transform), m_RenderData.pMonitor->vecTransformedSize.x,
m_RenderData.pMonitor->vecTransformedSize.y);
const auto TOPLEFT = Vector2D(transformedBox.x, transformedBox.y);
@@ -560,7 +637,7 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(const CTexture& tex, wlr_b
glUniform1f(shader->radius, round);
glUniform1i(shader->primitiveMultisample, (int)(*PMULTISAMPLEEDGES == 1 && round != 0 && !noAA));
if (allowDim && m_pCurrentWindow && *PDIMINACTIVE && m_pCurrentWindow != g_pCompositor->m_pLastWindow) {
if (allowDim && m_pCurrentWindow && *PDIMINACTIVE) {
glUniform1i(shader->applyTint, 1);
const auto DIM = m_pCurrentWindow->m_fDimPercent.fl();
glUniform3f(shader->tint, 1.f - DIM, 1.f - DIM, 1.f - DIM);
@@ -643,7 +720,7 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, wlr_box* p
pixman_region32_copy(&damage, originalDamage);
wlr_region_transform(&damage, &damage, wlr_output_transform_invert(m_RenderData.pMonitor->transform), m_RenderData.pMonitor->vecTransformedSize.x,
m_RenderData.pMonitor->vecTransformedSize.y);
wlr_region_expand(&damage, &damage, pow(2, *PBLURPASSES) * *PBLURSIZE);
wlr_region_expand(&damage, &damage, *PBLURPASSES > 10 ? pow(2, 15) : std::clamp(*PBLURSIZE, (int64_t)1, (int64_t)40) * pow(2, *PBLURPASSES));
// helper
const auto PMIRRORFB = &m_RenderData.pCurrentMonData->mirrorFB;
@@ -742,6 +819,13 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, wlr_box* p
}
void CHyprOpenGLImpl::markBlurDirtyForMonitor(CMonitor* pMonitor) {
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(pMonitor->activeWorkspace);
const auto PFULLWINDOW = g_pCompositor->getFullscreenWindowOnWorkspace(pMonitor->activeWorkspace);
if (PWORKSPACE->m_bHasFullscreenWindow && PWORKSPACE->m_efFullscreenMode == FULLSCREEN_FULL && PFULLWINDOW && !PFULLWINDOW->m_vRealSize.isBeingAnimated() &&
PFULLWINDOW->opaque())
return;
m_mMonitorRenderResources[pMonitor].blurFBDirty = true;
}
@@ -764,7 +848,7 @@ void CHyprOpenGLImpl::preRender(CMonitor* pMonitor) {
if (pWindow->m_sAdditionalConfigData.forceNoBlur)
return false;
const auto PSURFACE = g_pXWaylandManager->getWindowSurface(pWindow);
const auto PSURFACE = pWindow->m_pWLSurface.wlr();
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(pWindow->m_iWorkspaceID);
const float A = pWindow->m_fAlpha.fl() * pWindow->m_fActiveInactiveAlpha.fl() * PWORKSPACE->m_fAlpha.fl();
@@ -814,6 +898,9 @@ void CHyprOpenGLImpl::preRender(CMonitor* pMonitor) {
void CHyprOpenGLImpl::preBlurForCurrentMonitor() {
const auto SAVEDRENDERMODIF = m_RenderData.renderModif;
m_RenderData.renderModif = {}; // fix shit
// make the fake dmg
pixman_region32_t fakeDamage;
pixman_region32_init_rect(&fakeDamage, 0, 0, m_RenderData.pMonitor->vecTransformedSize.x, m_RenderData.pMonitor->vecTransformedSize.y);
@@ -835,6 +922,8 @@ void CHyprOpenGLImpl::preBlurForCurrentMonitor() {
m_RenderData.pCurrentMonData->primaryFB.bind();
m_RenderData.pCurrentMonData->blurFBDirty = false;
m_RenderData.renderModif = SAVEDRENDERMODIF;
}
void CHyprOpenGLImpl::preWindowPass() {
@@ -865,7 +954,7 @@ void CHyprOpenGLImpl::renderTextureWithBlur(const CTexture& tex, wlr_box* pBox,
return;
if (*PBLURENABLED == 0 || (*PNOBLUROVERSIZED && m_RenderData.primarySurfaceUVTopLeft != Vector2D(-1, -1)) ||
(m_pCurrentWindow && m_pCurrentWindow->m_sAdditionalConfigData.forceNoBlur)) {
(m_pCurrentWindow && (m_pCurrentWindow->m_sAdditionalConfigData.forceNoBlur || m_pCurrentWindow->m_sAdditionalConfigData.forceRGBX))) {
renderTexture(tex, pBox, a, round, false, true);
return;
}
@@ -891,9 +980,9 @@ void CHyprOpenGLImpl::renderTextureWithBlur(const CTexture& tex, wlr_box* pBox,
wlr_region_scale(&inverseOpaque, &inverseOpaque, m_RenderData.pMonitor->scale);
// vvv TODO: layered blur fbs?
const bool USENEWOPTIMIZE =
(*PBLURNEWOPTIMIZE && !blockBlurOptimization && ((m_pCurrentWindow && !m_pCurrentWindow->m_bIsFloating) || *PBLURXRAY) &&
m_RenderData.pCurrentMonData->blurFB.m_cTex.m_iTexID && (!m_pCurrentWindow || !g_pCompositor->isWorkspaceSpecial(m_pCurrentWindow->m_iWorkspaceID)));
const bool USENEWOPTIMIZE = (*PBLURNEWOPTIMIZE && !blockBlurOptimization &&
((m_pCurrentWindow && !m_pCurrentWindow->m_bIsFloating && !g_pCompositor->isWorkspaceSpecial(m_pCurrentWindow->m_iWorkspaceID)) || *PBLURXRAY) &&
m_RenderData.pCurrentMonData->blurFB.m_cTex.m_iTexID);
CFramebuffer* POUTFB = nullptr;
if (!USENEWOPTIMIZE) {
@@ -922,7 +1011,7 @@ void CHyprOpenGLImpl::renderTextureWithBlur(const CTexture& tex, wlr_box* pBox,
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
if (USENEWOPTIMIZE)
if (USENEWOPTIMIZE && !(m_RenderData.discardMode & DISCARD_ALPHAZERO))
renderRect(pBox, CColor(0, 0, 0, 0), round);
else
renderTexture(tex, pBox, a, round, true, true); // discard opaque
@@ -936,8 +1025,11 @@ void CHyprOpenGLImpl::renderTextureWithBlur(const CTexture& tex, wlr_box* pBox,
// render our great blurred FB
static auto* const PBLURIGNOREOPACITY = &g_pConfigManager->getConfigValuePtr("decoration:blur_ignore_opacity")->intValue;
m_bEndFrame = true; // fix transformed
const auto SAVEDRENDERMODIF = m_RenderData.renderModif;
m_RenderData.renderModif = {}; // fix shit
renderTextureInternalWithDamage(POUTFB->m_cTex, &MONITORBOX, *PBLURIGNOREOPACITY ? 1.f : a, &damage, 0, false, false, false);
m_bEndFrame = false;
m_bEndFrame = false;
m_RenderData.renderModif = SAVEDRENDERMODIF;
// render the window, but clear stencil
glClearStencil(0);
@@ -960,20 +1052,26 @@ void pushVert2D(float x, float y, float* arr, int& counter, wlr_box* box) {
counter++;
}
void CHyprOpenGLImpl::renderBorder(wlr_box* box, const CGradientValueData& grad, int round, float a) {
void CHyprOpenGLImpl::renderBorder(wlr_box* box, const CGradientValueData& grad, int round, int borderSize, float a) {
RASSERT((box->width > 0 && box->height > 0), "Tried to render rect with width/height < 0!");
RASSERT(m_RenderData.pMonitor, "Tried to render rect without begin()!");
if (!pixman_region32_not_empty(m_RenderData.pDamage) || (m_pCurrentWindow && m_pCurrentWindow->m_sAdditionalConfigData.forceNoBorder))
return;
static auto* const PBORDERSIZE = &g_pConfigManager->getConfigValuePtr("general:border_size")->intValue;
static auto* const PMULTISAMPLE = &g_pConfigManager->getConfigValuePtr("decoration:multisample_edges")->intValue;
if (*PBORDERSIZE < 1)
wlr_box newBox = *box;
scaleBox(&newBox, m_RenderData.renderModif.scale);
newBox.x += m_RenderData.renderModif.translate.x;
newBox.y += m_RenderData.renderModif.translate.y;
box = &newBox;
if (borderSize < 1)
return;
int scaledBorderSize = *PBORDERSIZE * m_RenderData.pMonitor->scale;
int scaledBorderSize = borderSize * m_RenderData.pMonitor->scale * m_RenderData.renderModif.scale;
// adjust box
box->x -= scaledBorderSize;
@@ -1055,17 +1153,15 @@ void CHyprOpenGLImpl::renderBorder(wlr_box* box, const CGradientValueData& grad,
glDisableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBORDER1.texAttrib);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
// fix back box
box->x += scaledBorderSize;
box->y += scaledBorderSize;
box->width -= 2 * scaledBorderSize;
box->height -= 2 * scaledBorderSize;
}
void CHyprOpenGLImpl::makeRawWindowSnapshot(CWindow* pWindow, CFramebuffer* pFramebuffer) {
// we trust the window is valid.
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
if (!PMONITOR || !PMONITOR->output)
return;
wlr_output_attach_render(PMONITOR->output, nullptr);
// we need to "damage" the entire monitor
@@ -1121,6 +1217,10 @@ void CHyprOpenGLImpl::makeRawWindowSnapshot(CWindow* pWindow, CFramebuffer* pFra
void CHyprOpenGLImpl::makeWindowSnapshot(CWindow* pWindow) {
// we trust the window is valid.
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
if (!PMONITOR || !PMONITOR->output)
return;
wlr_output_attach_render(PMONITOR->output, nullptr);
// we need to "damage" the entire monitor
@@ -1181,6 +1281,10 @@ void CHyprOpenGLImpl::makeWindowSnapshot(CWindow* pWindow) {
void CHyprOpenGLImpl::makeLayerSnapshot(SLayerSurface* pLayer) {
// we trust the window is valid.
const auto PMONITOR = g_pCompositor->getMonitorFromID(pLayer->monitorID);
if (!PMONITOR || !PMONITOR->output)
return;
wlr_output_attach_render(PMONITOR->output, nullptr);
// we need to "damage" the entire monitor
@@ -1318,6 +1422,13 @@ void CHyprOpenGLImpl::renderRoundedShadow(wlr_box* box, int round, int range, fl
if (!pixman_region32_not_empty(m_RenderData.pDamage))
return;
wlr_box newBox = *box;
scaleBox(&newBox, m_RenderData.renderModif.scale);
newBox.x += m_RenderData.renderModif.translate.x;
newBox.y += m_RenderData.renderModif.translate.y;
box = &newBox;
static auto* const PSHADOWPOWER = &g_pConfigManager->getConfigValuePtr("decoration:shadow_render_power")->intValue;
const auto SHADOWPOWER = std::clamp((int)*PSHADOWPOWER, 1, 4);
@@ -1549,3 +1660,16 @@ void CHyprOpenGLImpl::destroyMonitorResources(CMonitor* pMonitor) {
wlr_output_rollback(pMonitor->output);
}
void CHyprOpenGLImpl::saveMatrix() {
memcpy(m_RenderData.savedProjection, m_RenderData.projection, 9 * sizeof(float));
}
void CHyprOpenGLImpl::setMatrixScaleTranslate(const Vector2D& translate, const float& scale) {
wlr_matrix_scale(m_RenderData.projection, scale, scale);
wlr_matrix_translate(m_RenderData.projection, translate.x, translate.y);
}
void CHyprOpenGLImpl::restoreMatrix() {
memcpy(m_RenderData.projection, m_RenderData.savedProjection, 9 * sizeof(float));
}

Some files were not shown because too many files have changed in this diff Show More