all: remove control center in its entirety

This commit is contained in:
kossLAN 2026-04-05 20:36:33 -04:00
parent 1dfd6169f8
commit 769d12a525
No known key found for this signature in database
97 changed files with 59 additions and 10580 deletions

258
Cargo.lock generated
View file

@ -2,15 +2,6 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "accesskit"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5351dcebb14b579ccab05f288596b2ae097005be7ee50a7c3d4ca9d0d5a66f6a"
dependencies = [
"uuid",
]
[[package]]
name = "addr2line"
version = "0.25.1"
@ -196,20 +187,6 @@ name = "bytemuck"
version = "1.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec"
dependencies = [
"bytemuck_derive",
]
[[package]]
name = "bytemuck_derive"
version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
]
[[package]]
name = "byteorder"
@ -307,15 +284,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
[[package]]
name = "color"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a18ef4657441fb193b65f34dc39b3781f0dfec23d3bd94d0eeb4e88cde421edb"
dependencies = [
"bytemuck",
]
[[package]]
name = "colorchoice"
version = "1.0.5"
@ -397,77 +365,6 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "ecolor"
version = "0.34.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "137c0ce4ce4152ff7e223a7ce22ee1057cdff61fce0a45c32459c3ccec64868d"
dependencies = [
"emath",
]
[[package]]
name = "egui"
version = "0.34.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f34aaf627da598dfadd64b0fee6101d22e9c451d1e5348157312720b7f459f0f"
dependencies = [
"accesskit",
"ahash",
"bitflags",
"emath",
"epaint",
"log",
"nohash-hasher",
"profiling",
"smallvec",
"unicode-segmentation",
]
[[package]]
name = "egui_tiles"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08e570b77f6cce3292eba4aee9b9c08cf11dfc68430f4dc9613d939628498647"
dependencies = [
"ahash",
"egui",
"itertools",
"log",
]
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "emath"
version = "0.34.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a05cd8bdf3b598488c627ca97c7fe8909448ffa26278dd3c7e535cdb554d721"
[[package]]
name = "epaint"
version = "0.34.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04f3017dd67f147a697ee0c8484fb568fd9553e2a0c114be5020dbbc11962841"
dependencies = [
"ahash",
"ecolor",
"emath",
"font-types",
"log",
"nohash-hasher",
"parking_lot",
"profiling",
"self_cell",
"skrifa",
"smallvec",
"vello_cpu",
]
[[package]]
name = "equivalent"
version = "1.0.2"
@ -490,15 +387,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31ae425815400e5ed474178a7a22e275a9687086a12ca63ec793ff292d8fdae8"
[[package]]
name = "euclid"
version = "0.22.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06"
dependencies = [
"num-traits",
]
[[package]]
name = "fastrand"
version = "2.3.0"
@ -514,15 +402,6 @@ dependencies = [
"simd-adler32",
]
[[package]]
name = "fearless_simd"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fb2907d1f08b2b316b9223ced5b0e89d87028ba8deae9764741dba8ff7f3903"
dependencies = [
"bytemuck",
]
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
@ -545,21 +424,6 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "foldhash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]]
name = "font-types"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73829a7b5c91198af28a99159b7ae4afbb252fb906159ff7f189f3a2ceaa3df2"
dependencies = [
"bytemuck",
]
[[package]]
name = "futures-core"
version = "0.3.32"
@ -672,7 +536,7 @@ version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash 0.1.5",
"foldhash",
]
[[package]]
@ -680,9 +544,6 @@ name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [
"foldhash 0.2.0",
]
[[package]]
name = "heck"
@ -756,15 +617,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1697e6b71679da96d5c41bb9035116141baadbf59a60625fd66cb3c9584e7b0"
[[package]]
name = "itertools"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.17"
@ -805,8 +657,6 @@ dependencies = [
"clap",
"clap_complete",
"dirs",
"egui",
"egui_tiles",
"futures-util",
"gpu-alloc",
"gpu-alloc-types",
@ -925,17 +775,6 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "kurbo"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7564e90fe3c0d5771e1f0bc95322b21baaeaa0d9213fa6a0b61c99f8b17b3bfb"
dependencies = [
"arrayvec",
"euclid",
"smallvec",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
@ -997,12 +836,6 @@ dependencies = [
"syn 2.0.117",
]
[[package]]
name = "linebender_resource_handle"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4a5ff6bcca6c4867b1c4fd4ef63e4db7436ef363e0ad7531d1558856bae64f4"
[[package]]
name = "linux-raw-sys"
version = "0.12.1"
@ -1040,12 +873,6 @@ dependencies = [
"simd-adler32",
]
[[package]]
name = "nohash-hasher"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
[[package]]
name = "num-conv"
version = "0.2.0"
@ -1143,19 +970,6 @@ dependencies = [
"windows-link",
]
[[package]]
name = "peniko"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2b6aadb221872732e87d465213e9be5af2849b0e8cc5300a8ba98fffa2e00a"
dependencies = [
"bytemuck",
"color",
"kurbo",
"linebender_resource_handle",
"smallvec",
]
[[package]]
name = "phf"
version = "0.13.1"
@ -1269,12 +1083,6 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "profiling"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773"
[[package]]
name = "quick-xml"
version = "0.39.2"
@ -1352,16 +1160,6 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba"
[[package]]
name = "read-fonts"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b634fabf032fab15307ffd272149b622260f55974d9fad689292a5d33df02e5"
dependencies = [
"bytemuck",
"font-types",
]
[[package]]
name = "redox_syscall"
version = "0.5.18"
@ -1487,12 +1285,6 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "self_cell"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89"
[[package]]
name = "semver"
version = "1.0.27"
@ -1585,16 +1377,6 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
[[package]]
name = "skrifa"
version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fbdfe3d2475fbd7ddd1f3e5cf8288a30eb3e5f95832829570cd88115a7434ac"
dependencies = [
"bytemuck",
"read-fonts",
]
[[package]]
name = "slab"
version = "0.4.12"
@ -1792,12 +1574,6 @@ version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-segmentation"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
name = "unicode-width"
version = "0.2.2"
@ -1822,38 +1598,6 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9"
[[package]]
name = "vello_common"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bd1a4c633ce09e7d713df1a6e036644a125e15e0c169cfb5180ddf5836ca04b"
dependencies = [
"bytemuck",
"fearless_simd",
"hashbrown 0.16.1",
"log",
"peniko",
"skrifa",
"smallvec",
]
[[package]]
name = "vello_cpu"
version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0162bfe48aabf6a9fdcd401b628c7d9f260c2cbabb343c70a65feba6f7849edc"
dependencies = [
"bytemuck",
"hashbrown 0.16.1",
"vello_common",
]
[[package]]
name = "version_check"
version = "0.9.5"

View file

@ -68,8 +68,6 @@ opera = "1.0.1"
with_builtin_macros = "0.1.0"
blake3 = "1.8.2"
run-on-drop = "1.0.0"
egui = { version = "0.34.1", default-features = false }
egui_tiles = { version = "0.15.0", default-features = false }
numeric-sort = "0.1.5"
[build-dependencies]
@ -89,49 +87,6 @@ opt-level = 3
[profile.dev.package."smallvec"]
opt-level = 3
[profile.dev.package."egui"]
opt-level = 3
debug = "line-tables-only"
[profile.dev.package."emath"]
opt-level = 3
debug = "line-tables-only"
[profile.dev.package."epaint"]
opt-level = 3
debug = "line-tables-only"
[profile.dev.package."ecolor"]
opt-level = 3
debug = "line-tables-only"
[profile.dev.package."font-types"]
opt-level = 3
debug = "line-tables-only"
[profile.dev.package."skrifa"]
opt-level = 3
debug = "line-tables-only"
[profile.dev.package."read-fonts"]
opt-level = 3
debug = "line-tables-only"
[profile.dev.package."vello_cpu"]
opt-level = 3
debug = "line-tables-only"
[profile.dev.package."vello_common"]
opt-level = 3
debug = "line-tables-only"
[profile.dev.package."peniko"]
opt-level = 3
debug = "line-tables-only"
[profile.dev.package."kurbo"]
opt-level = 3
debug = "line-tables-only"
[features]
rc_tracking = []

View file

@ -10,7 +10,6 @@
- [Installation](installation.md)
- [Running Jay](running.md)
- [Control Center](control-center.md)
# Configuration

View file

@ -674,14 +674,6 @@ Show color management status:
## Other Commands
### `jay control-center`
Open the [Control Center](control-center.md) GUI:
```shell
~$ jay control-center
```
### `jay portal`
Run the Jay desktop portal (provides screen sharing and other XDG desktop

View file

@ -134,7 +134,6 @@ alt-shift-r = "reload-config-toml"
- `disable-pointer-constraint` -- release a pointer lock/confinement
- `focus-parent` -- move focus to the parent container
- `toggle-bar`, `show-bar`, `hide-bar` -- control the status bar
- `open-control-center` -- open the Jay control center GUI
- `warp-mouse-to-focus` -- warp the cursor to the center of the focused window
- `kill-client` -- forcefully disconnect a client (in a window rule, kills the
window's client; in a client rule, kills the matched client; has no effect

View file

@ -1,608 +0,0 @@
# Control Center
The control center is Jay's built-in graphical interface for inspecting and
modifying compositor settings. It provides a convenient alternative to editing
configuration files or running CLI commands -- most settings that can be changed
in `config.toml` or via the CLI can also be changed here.
> [!NOTE]
> Changes made in the control center are not persisted across compositor
> restarts. To make settings permanent, add them to your `config.toml`.
> [!TIP]
> The control center consumes GPU and CPU resources while open. Close it when
> not in use to avoid reducing compositor performance.
## Opening the Control Center
- Press `alt-c` (the default shortcut for the `open-control-center` action).
- Run the CLI command:
```shell
~$ jay control-center
```
## Interface Overview
The control center window has a sidebar on the left listing all available panes
and a central panel area on the right.
- **Click** a pane name in the sidebar to open it.
- **Multiple panes** can be open at the same time -- they appear as tabs in the
panel area.
- **Drag** pane tabs to rearrange them or to split the panel area into
side-by-side or stacked layouts.
- **Close** a pane by clicking its X button or middle-clicking its tab.
## Panes
### Compositor
General information and top-level controls for the running compositor.
Repository
: Link to the Jay GitHub repository
Version
: The running Jay version
PID
: The compositor process ID
WAYLAND_DISPLAY
: The Wayland socket name (shown when available)
Config DIR
: Path to the active configuration directory (shown when available)
Libei Socket
: Toggle the libei input emulation socket
LIBEI_SOCKET
: The socket name (shown when the Libei Socket toggle is enabled)
Workspace Display Order
: Dropdown to select how workspaces are ordered in the bar
Log Level
: Dropdown to change the active log level at runtime (shown when the logger is available)
Log File
: Click to copy the log file path to the clipboard (shown when the logger is available)
Buttons at the bottom:
- **Quit** -- stop the compositor.
- **Reload Config** -- reload the configuration file.
- **Switch to VT** -- switch to another virtual terminal (with a numeric input
to select which one).
### Outputs
The Outputs pane is the largest and most interactive pane. It has two sub-views:
a visual **arrangement editor** and per-connector **settings**.
#### Arrangement editor
A 2D preview of your monitor layout. Monitors are drawn as labeled rectangles
at their configured positions and sizes.
- **Click** a monitor to select it (highlighted with a shadow).
- **Drag** a selected monitor to reposition it.
- **Scroll** to zoom in and out.
- **Middle-click drag** or **right-click drag** to pan the viewport.
- **Arrow keys** nudge the selected monitor by 1 pixel.
- **Snap to neighbor** -- when enabled, dragged monitors snap to the edges of
neighboring monitors within a 10-pixel threshold. Hold **Shift** to
temporarily invert the snapping behavior.
- **Guide lines** -- optional horizontal and vertical lines at the edges of all
monitors, helping you align them precisely.
A **Zoom To Fit** checkbox in the top bar auto-scales the view to fit all
monitors. It is disabled when you manually pan or zoom.
Arrangement settings (accessible via the Settings button):
Show guide lines
: Draw alignment guide lines
Snap to neighbor
: Snap edges when dragging (hold Shift to invert)
Show arrangement area
: Toggle the visual arrangement sub-pane
Layout
: How the arrangement and settings are split: Auto, Vertical, or Horizontal
Show disconnected heads
: Include outputs that are no longer connected
Show disabled heads
: Include outputs that are disabled
#### Staged changes
Changes made in the Outputs pane are **staged** -- they are not applied
immediately. Three buttons in the top bar control the workflow:
- **Test** -- validates the staged changes against the display backend without
applying them. Errors are shown in the pane.
- **Commit** -- applies all staged changes. The pane title shows `Outputs (*)`
when there are uncommitted changes.
- **Reset** -- discards all staged changes and reverts to the live state.
This is displayed as a checkbox; checking it resets the staged changes.
When a staged value differs from the live value, the current (live) value is
shown alongside with a `^ current` annotation.
#### Per-connector settings
Each connected display appears as a collapsible section with the connector name,
manufacturer, and model. Inside:
Serial Number
: Read-only identifier
Enabled
: Toggle the connector on or off
Position
: X and Y coordinates in compositor space
Scale
: Fractional scaling factor, with +/- buttons for fine adjustment
Mode
: Resolution and refresh rate (dropdown when multiple modes are available)
Physical Size (mm)
: Read-only physical dimensions in millimeters
Size
: Read-only computed pixel dimensions of the output
Transform
: Rotation and mirroring (none, rotate-90, rotate-180, rotate-270, flip, and flipped rotations)
Custom Brightness
: Toggle whether to use a custom SDR content brightness
Brightness
: Brightness value in cd/m^2 (shown when Custom Brightness is enabled)
Colorimetry
: Color space (depends on monitor capabilities)
EOTF
: Transfer function (depends on monitor capabilities)
Format
: Framebuffer pixel format
Tearing
: Tearing mode. When set to a "Fullscreen" mode, a **Limit Windows** checkbox appears; inside that, a **Requests Tearing** checkbox filters by whether the window has requested tearing.
VRR Active
: Read-only indicator of whether VRR is currently active (only shown when the monitor supports VRR)
VRR
: Variable refresh rate mode (only shown when the monitor supports VRR). When set to a "Fullscreen" mode, a **Limit Windows** checkbox appears; inside that, a **Limit Content Types** checkbox enables filtering by content type (**Photos**, **Videos**, **Games** checkboxes).
Non-desktop
: Read-only indicator (Yes/No) of whether the connector is inherently non-desktop
Override
: Force the connector to be treated as desktop or non-desktop
Blend Space
: How colors are blended during compositing (sRGB or linear)
Use Native Gamut
: Use the display's advertised color primaries instead of assuming sRGB
Native Gamut
: Read-only CIE xy primaries for red, green, blue, and white point
Limit Cursor HZ
: Toggle to limit cursor-triggered refresh rate when VRR is active
Cursor HZ
: Cursor refresh rate value (shown when Limit Cursor HZ is enabled)
Flip Margin (ms)
: Read-only page-flip margin for this connector
### Virtual Outputs
Manage headless virtual outputs. These are useful for screen sharing, testing,
or running applications on a display without a physical monitor.
- View the list of existing virtual outputs.
- **Add** a new virtual output by entering a name and clicking Add.
- **Remove** an existing virtual output by clicking its X button.
### GPUs
Inspect and configure graphics cards (DRM devices). Each GPU appears as a
collapsible section showing its device path and model name.
Vendor
: Read-only GPU vendor name
Model
: Read-only GPU model name
Devnode
: Read-only device path
Syspath
: Read-only sysfs path
PCI ID
: Read-only vendor:model in hex
Dev
: Read-only major:minor device numbers
API
: Dropdown to select the graphics API -- Vulkan (recommended) or the legacy OpenGL renderer
Primary Device
: Checkbox to make this GPU the render device
Direct Scanout
: Toggle direct scanout (bypasses composition for lower latency)
Flip Margin
: Adjust the page-flip margin in milliseconds, with +/- buttons for 0.1 ms steps
Connectors
: List of display connectors attached to this GPU
### Input
The Input pane is divided into per-seat and per-device sections.
#### Per-seat settings
Each seat (typically just `default`) appears as a collapsible section:
Repeat Rate
: Key repeat speed, with +/- 20 buttons
Repeat Delay
: Initial delay before key repeat begins, with +/- 20 buttons
Cursor Size
: Size of the seat cursor in pixels
Simple IM
: Toggle the built-in XCompose-based input method
Hardware Cursor
: Toggle hardware cursor rendering
Pointer Revert Key
: Text field for the keysym name of the cancel key
Focus Follows Mouse
: Toggle whether moving the pointer over a window gives it focus
Fallback Output Mode
: Dropdown to choose between cursor-based and focus-based output selection
Below the settings grid:
- **Focus History** -- checkboxes for "Only Visible" and "Same Workspace".
- **Reload Simple IM** -- button to reload XCompose files without restarting.
##### Keymap management
Each seat has a full keymap management section:
- **Copy Keymap** -- copies the current keymap text to the clipboard.
- **Load Default Keymap** -- restores the compositor's default keymap.
- **Backup / Restore Keymap** -- save and restore a keymap backup.
- **Load Keymap from Clipboard** -- paste a keymap from the clipboard.
- **Create Keymap from Names** -- build a keymap from RMLVO (Rules, Model,
Layout, Variant, Options) fields. Rules and Model have a text input and a
"Default" checkbox; Layouts, Variants, and Options have text inputs only.
Click **Load** to apply.
#### Per-device settings
Each input device appears as a collapsible section. The available settings
depend on the device's capabilities:
Seat
: Dropdown to assign the device to a seat, with a Detach button. Shown for all devices.
Syspath / Devnode
: Read-only device paths. Shown for all devices.
Capabilities
: Read-only list (e.g. Keyboard, Pointer, Touch). Shown for all devices.
Natural Scrolling
: Toggle scroll direction. Shown for devices that support it.
Scroll Distance (px)
: Pixels per legacy scroll event. Shown for pointer devices.
Accel Profile
: Dropdown: Flat or Adaptive. Shown for devices with acceleration.
Accel Speed
: Numeric input (-1.0 to 1.0). Shown for devices with acceleration.
Click Method
: Dropdown: none, button-areas, clickfinger. Shown for devices that support it.
Tap Enabled
: Toggle tap-to-click. Shown for touchpads.
Tap Drag Enabled
: Toggle tap-and-drag. Shown for touchpads.
Tap Drag Lock Enabled
: Toggle tap-drag lock. Shown for touchpads.
Left Handed
: Swap primary and secondary buttons. Shown for devices that support it.
Middle Button Emulation
: Simultaneous left+right produces middle click. Shown for devices that support it.
Output
: Dropdown to map the device to a specific output (only has effect for touch and tablet devices), with a Detach button. Shown for all devices.
Transform Matrix
: 2x2 matrix applied to relative motion. Shown for pointer devices.
Calibration Matrix
: 2x3 matrix for absolute input calibration. Shown for devices that support it.
Device Keymap
: Override the seat keymap for this device, with full keymap management UI and a "Use Seat Keymap" button to revert. Shown for keyboards.
### Idle
Configure the screensaver and idle behavior:
Interval
: Minutes and seconds of inactivity before the on-idle action fires
Grace period
: Minutes and seconds of the warning phase (screen goes black but is not yet locked)
Inhibitors
: Collapsible list showing which applications are currently preventing idle (e.g. video players), with a count in the header
### Look and Feel
Visual customization with live preview. Changes take effect immediately.
Show Bar
: Toggle the status bar
Bar Position
: Dropdown to select the bar position
Show Titles
: Toggle window title bars
Primary Selection
: Toggle middle-click paste (requires application restart to take effect)
UI Drag
: Toggle whether workspaces and tiles can be dragged
UI Drag Threshold (px)
: Minimum distance in pixels before a drag begins
Float Pin Icon
: Show the pin icon on floating windows even when not pinned
Float Above Fullscreen
: Show floating windows above fullscreen windows
Font
: Text field for the main compositor font family
Title Font
: Override font for window title bars (empty = use main font)
Bar Font
: Override font for the status bar (empty = use main font)
Three reset buttons at the bottom: **Reset Sizes**, **Reset Colors**, and
**Reset Fonts**.
#### Sizes
A collapsible section with numeric inputs for every theme size: border widths,
title heights, bar height, gaps, and other spacing values.
#### Colors
A collapsible section with **color pickers** for every theme color. Click a
color swatch to open a full RGBA color picker with sliders and hex input.
This includes colors for backgrounds, borders, text, the status bar, focused
and unfocused windows, attention indicators, and more.
### Clients
Inspect and manage connected Wayland clients.
A **Filter** toggle at the top enables the composable filter builder (see
[Filtering](#filtering) below). When filtering is off, all clients are shown.
Each client appears as a collapsible section showing its ID and process name.
Expand it to see:
ID
: Client identifier
PID
: Process ID
UID
: User ID
comm
: Process name
exe
: Executable path
Sandboxed
: Whether the client is sandboxed (only shown for sandboxed clients)
Secure
: Whether the client uses the privileged socket (only shown for secure clients)
Xwayland
: Shown only for X11 clients
Sandbox Engine
: Sandbox engine name (shown when sandboxed)
App ID
: Sandbox application ID (shown when sandboxed)
Instance ID
: Sandbox instance ID (shown when sandboxed)
Tag
: The connection tag, if any
Kill
: Button to forcefully disconnect the client
Capabilities
: Collapsible list of effective Wayland capabilities
Windows
: Collapsible list of all windows owned by this client
Click the **open in new pane** icon on any client to open a dedicated pane for
that client, allowing you to keep it visible while browsing other panes.
### Window Search
Search and filter windows across the compositor using the composable filter
builder (see [Filtering](#filtering) below).
Each matching window appears as a collapsible section showing its title. Expand
it to see:
ID
: Window identifier
Title
: Window title
Workspace
: Which workspace the window is on
Type
: Container, xdg_toplevel, X Window, or Placeholder
Tag
: Toplevel tag (set via window rules); only shown for xdg_toplevel windows
X11 properties
: Class, Instance, and Role (only shown for Xwayland windows)
App ID
: Application identifier
Floating
: Whether the window is floating
Visible
: Whether the window is visible
Urgent
: Whether the window has the urgency flag
Fullscreen
: Whether the window is fullscreen
Content Type
: The content type hint (photo, video, game), if set
Client
: Full client details (same as the Clients pane)
Click the **open in new pane** icon on any window to open a dedicated pane for
that window.
### Xwayland
Manage the Xwayland compatibility layer for running X11 applications:
Enabled
: Toggle Xwayland on or off
Scaling Mode
: Dropdown: `default` or `downscaled` (renders at highest integer scale then downscales for sharper text on HiDPI)
DISPLAY
: Read-only X11 display number (only shown when Xwayland is running)
Running
: Whether Xwayland is currently running
PID
: Xwayland process ID (only shown when Xwayland is running)
Kill
: Button to forcefully terminate Xwayland (only shown when Xwayland is running)
Client
: Collapsible section with full client details for the Xwayland process (only shown when Xwayland is running)
### Color Management
Configure the Wayland color management protocol:
Enabled
: Toggle the color management protocol for clients
Available
: Read-only indicator of whether color management is available with the current renderer and hardware
## Filtering
The **Clients** and **Window Search** panes share a composable filter system
for narrowing down results. The filter builder works as follows:
At the top level, select a combinator or a leaf criterion from the dropdown:
- **Not** -- inverts a single child criterion.
- **All** -- all child criteria must match (AND).
- **Any** -- at least one child criterion must match (OR).
- **Exactly(n)** -- exactly *n* child criteria must match (with a numeric input
for *n*).
Compound criteria contain a list of children. Click **Add** to append a new
criterion; click the X button on any child to remove it. Criteria can be nested
to arbitrary depth.
Leaf criteria vary by context:
**Client criteria:** Comm, Exe, Tag, Sandbox Engine, Sandbox App ID, Sandbox
Instance ID (all regex-matched text fields with a "Regex" checkbox), Sandboxed,
Is Xwayland (boolean), UID, PID (numeric inputs).
**Window criteria:** Title, App ID, Tag, Workspace, X Class, X Instance, X Role
(all regex-matched text fields), Floating, Visible, Urgent, Fullscreen
(boolean), Content Types (checkboxes for Photo, Video, Game), and **Client**
(a nested client criterion builder for filtering by the owning client's
properties).
Text-matching criteria have a **Regex** checkbox. When unchecked, the input is
matched as a literal string. When checked, it is treated as a regular
expression. Invalid regex patterns show an error message.

View file

@ -71,7 +71,6 @@ Commands:
color-management Inspect/modify the color-management settings
clients Inspect/manipulate the connected clients
tree Inspect the surface tree
control-center Opens the control center
version Prints the Jay version and exits
pid Prints the Jay PID and exits
help Print this message or the help of the given subcommand(s)
@ -85,12 +84,6 @@ Options:
See the full [Command-Line Interface](cli.md) reference for details.
## Control Center
Jay includes a built-in GUI control center (opened with `alt-c`) for managing
outputs, input devices, GPUs, idle settings, color management, and more --
without editing the config file. See [Control Center](control-center.md).
## Multi-Monitor Support
Jay can be used with multiple monitors with hot-plug and hot-unplug support.
@ -133,7 +126,7 @@ automatically by the default configuration.
## Fractional Scaling
Jay supports per-monitor fractional scaling. Scale factors can be set per
output in the config file or at runtime via the control center and CLI.
output in the config file or at runtime via the CLI.
See [Outputs (Monitors)](configuration/outputs.md) for details.

View file

@ -26,12 +26,8 @@ variable refresh rate (VRR), tearing presentation, HDR, and screen sharing via
xdg-desktop-portal. X11 applications are supported through Xwayland.
Jay is configured through a declarative TOML file, with an optional advanced
mode that uses a shared library for programmatic control. A built-in
[control center](control-center.md) (opened with `alt-c`) provides a full GUI
for inspecting and changing compositor settings at runtime -- including output
arrangement, input devices, color management, GPU selection, and more. A
comprehensive command-line interface makes scripting and automation
straightforward.
mode that uses a shared library for programmatic control. A comprehensive
command-line interface makes scripting and automation straightforward.
See the [Features](features.md) chapter for a comprehensive overview of what
Jay can do, or jump straight to [Installation](installation.md) to get started.

View file

@ -44,24 +44,6 @@ Then log out and select **Jay** from the session list.
> ~$ sudo ln -s ~/.cargo/bin/jay /usr/local/bin/jay
> ```
## The Control Center
Once Jay is running, press `alt-c` to open the
[control center](control-center.md) -- a built-in GUI that lets you inspect and
modify most compositor settings without editing config files or running CLI
commands. From the control center you can:
- Rearrange monitors with a visual drag-and-drop editor.
- Configure input devices -- acceleration, tap behavior, keymaps, and more.
- Switch GPUs and graphics APIs.
- Adjust theme colors, fonts, borders, and gaps with live color pickers.
- Manage idle timeouts and screen locking.
- Search and filter windows and clients.
- Toggle Xwayland and color management.
You can also open it from the command line with `jay control-center`. See the
[Control Center](control-center.md) chapter for a full tour of every pane.
## Default Keybindings
Jay ships with a built-in default configuration. The most important default
@ -99,9 +81,6 @@ keybindings are listed below.
`alt-shift-f`
: Toggle floating
`alt-c`
: Open the control center
`alt-shift-c`
: Close focused window

View file

@ -26,12 +26,6 @@ pub const TREES: &[Tree] = &[
"legacy/tex.frag",
],
},
Tree {
root: "src/egui_adapter/shaders",
hash: "src/egui_adapter/shaders_hash.txt",
bin: "src/egui_adapter/shaders_bin",
shaders: &["shader.vert", "shader.frag"],
},
];
fn calculate_hash(tree: &Tree) -> anyhow::Result<String> {

View file

@ -1067,21 +1067,10 @@ impl ConfigClient {
position
}
pub fn set_egui_fonts(&self, proportional: Option<Vec<&str>>, monospace: Option<Vec<&str>>) {
self.send(&ClientMessage::SetEguiFonts {
proportional,
monospace,
});
}
pub fn set_middle_click_paste_enabled(&self, enabled: bool) {
self.send(&ClientMessage::SetMiddleClickPasteEnabled { enabled });
}
pub fn open_control_center(&self) {
self.send(&ClientMessage::OpenControlCenter);
}
pub fn set_workspace_display_order(&self, order: WorkspaceDisplayOrder) {
self.send(&ClientMessage::SetWorkspaceDisplayOrder { order });
}

View file

@ -847,11 +847,6 @@ pub enum ClientMessage<'a> {
fds: Vec<(i32, i32)>,
tag: Option<&'a str>,
},
SetEguiFonts {
proportional: Option<Vec<&'a str>>,
monospace: Option<Vec<&'a str>>,
},
OpenControlCenter,
ConnectorSupportsArbitraryModes {
connector: Connector,
},

View file

@ -381,7 +381,3 @@ pub fn set_middle_click_paste_enabled(enabled: bool) {
get!().set_middle_click_paste_enabled(enabled);
}
/// Opens the control center.
pub fn open_control_center() {
get!().open_control_center();
}

View file

@ -197,20 +197,6 @@ pub fn get_bar_position() -> BarPosition {
get!(BarPosition::Top).get_bar_position()
}
/// Sets the proportional fonts used by egui windows.
///
/// The default is `["sans-serif", "Noto Sans", "Noto Color Emoji"]`.
pub fn set_egui_proportional_fonts<'a>(fonts: impl IntoIterator<Item = &'a str>) {
get!().set_egui_fonts(Some(fonts.into_iter().collect()), None);
}
/// Sets the monospace fonts used by egui windows.
///
/// The default is `["monospace", "Noto Sans Mono", "Noto Color Emoji"]`.
pub fn set_egui_monospace_fonts<'a>(fonts: impl IntoIterator<Item = &'a str>) {
get!().set_egui_fonts(None, Some(fonts.into_iter().collect()));
}
/// Elements of the compositor whose color can be changed.
pub mod colors {
use {

View file

@ -596,9 +596,6 @@ pub trait BackendDrmDevice {
fn set_flip_margin(&self, margin: u64) {
let _ = margin;
}
fn flip_margin(&self) -> Option<u64> {
None
}
}
pub trait BackendDrmLease {

View file

@ -318,9 +318,6 @@ impl BackendDrmDevice for MetalDrmDevice {
}
}
fn flip_margin(&self) -> Option<u64> {
Some(self.min_post_commit_margin.get())
}
}
pub struct HandleEvents {

View file

@ -2,7 +2,6 @@ mod clients;
mod color;
mod color_management;
mod config;
mod control_center;
mod damage_tracking;
mod duration;
mod generate;
@ -112,8 +111,6 @@ pub enum Cmd {
Clients(ClientsArgs),
/// Inspect the surface tree.
Tree(TreeArgs),
/// Opens the control center.
ControlCenter,
/// Prints the Jay version and exits.
Version,
/// Prints the Jay PID and exits.
@ -270,7 +267,6 @@ pub fn main() {
#[cfg(feature = "it")]
Cmd::RunTests => crate::it::run_tests(),
Cmd::Reexec(a) => reexec::main(cli.global, a),
Cmd::ControlCenter => control_center::main(cli.global),
Cmd::Config(a) => config::main(cli.global, a),
}
}

View file

@ -1,32 +0,0 @@
use {
crate::{
cli::GlobalArgs,
tools::tool_client::{Handle, ToolClient, with_tool_client},
wire::{jay_compositor, jay_open_control_center_request},
},
std::rc::Rc,
};
pub fn main(global: GlobalArgs) {
with_tool_client(global.log_level, |tc| async move {
let cc = ControlCenter { tc: tc.clone() };
cc.run().await;
});
}
struct ControlCenter {
tc: Rc<ToolClient>,
}
impl ControlCenter {
async fn run(self) {
let tc = &self.tc;
let comp = tc.jay_compositor().await;
let id = tc.id();
tc.send(jay_compositor::OpenControlCenter { self_id: comp, id });
jay_open_control_center_request::Failed::handle(&tc, id, (), |_, ev| {
fatal!("Could not open the control center: {}", ev.msg);
});
tc.round_trip().await;
}
}

View file

@ -14,7 +14,6 @@ use {
clientmem::{self, ClientMemError},
cmm::{cmm_manager::ColorManager, cmm_primaries::Primaries},
config::ConfigProxy,
control_center::redraw_control_centers,
copy_device::CopyDeviceRegistry,
cpu_worker::{CpuWorker, CpuWorkerError},
criteria::{
@ -395,8 +394,6 @@ fn start_compositor2(
eventfd_cache,
lazy_event_sources: Default::default(),
bo_drop_queue: Rc::new(ObjectDropQueue::new(&ring)),
egg_state: Default::default(),
control_centers: Default::default(),
virtual_outputs: Default::default(),
clean_logs_older_than: Default::default(),
});
@ -605,10 +602,6 @@ fn start_global_event_handlers(state: &Rc<State>) -> Vec<SpawnedFuture<()>> {
"lazy event sources",
handle_lazy_event_sources(state.clone()),
),
eng.spawn(
"redraw control centers",
redraw_control_centers(state.clone()),
),
eng.spawn(
"warp mouse to focus",
handle_warp_mouse_to_focus(state.clone()),

View file

@ -1841,16 +1841,6 @@ impl ConfigProxyHandler {
Ok(())
}
fn handle_set_egui_fonts(&self, proportional: Option<Vec<&str>>, monospace: Option<Vec<&str>>) {
self.state.set_egui_fonts(proportional, monospace);
}
fn handle_open_control_center(&self) {
if let Err(e) = self.state.open_control_center() {
log::error!("Could not open control center: {}", ErrorFmt(e));
}
}
fn handle_set_log_level(&self, level: ConfigLogLevel) {
self.state.set_log_level(level.into());
}
@ -3410,11 +3400,6 @@ impl ConfigProxyHandler {
fds,
tag,
} => self.handle_run(prog, args, env, fds, tag).wrn("run")?,
ClientMessage::SetEguiFonts {
proportional,
monospace,
} => self.handle_set_egui_fonts(proportional, monospace),
ClientMessage::OpenControlCenter => self.handle_open_control_center(),
ClientMessage::ConnectorSupportsArbitraryModes { connector } => self
.handle_connector_supports_arbitrary_modes(connector)
.wrn("connector_supports_arbitrary_modes")?,

View file

@ -1,687 +0,0 @@
use {
crate::{
control_center::{
cc_clients::{ClientPane, ClientsPane},
cc_color_management::ColorManagementPane,
cc_compositor::CompositorPane,
cc_gpus::GpusPane,
cc_idle::IdlePane,
cc_input::InputPane,
cc_look_and_feel::LookAndFeelPane,
cc_outputs::OutputsPane,
cc_virtual_outputs::VirtualOutputsPane,
cc_window::{WindowPane, WindowSearchPane},
cc_xwayland::XwaylandPane,
},
egui_adapter::egui_platform::{
EggError, EggWindow, EggWindowOwner,
icons::{ICON_CLOSE, ICON_DRAG_INDICATOR, ICON_INFO},
},
macros::Bitflag,
state::State,
utils::{
asyncevent::AsyncEvent, copyhashmap::CopyHashMap,
event_listener::LazyEventSourceListener, numcell::NumCell, static_text::StaticText,
},
},
egui::{
Align, CentralPanel, Checkbox, Color32, ComboBox, CursorIcon, DragValue, Frame, Grid, Id,
InnerResponse, Label, Layout, Panel, Response, Rgba, RichText, ScrollArea, Sense, Stroke,
TextBuffer, TextEdit, Ui, UiBuilder, Visuals, Widget, WidgetText, emath::Numeric, vec2,
},
egui_tiles::{ResizeState, TabState, Tile, TileId, Tiles, Tree},
linearize::{Linearize, LinearizeExt},
std::{
cell::RefCell,
hash::Hash,
mem,
ops::{Deref, DerefMut, RangeInclusive},
rc::Rc,
},
thiserror::Error,
};
mod cc_clients;
mod cc_color_management;
mod cc_compositor;
mod cc_criterion;
mod cc_gpus;
mod cc_idle;
mod cc_input;
mod cc_look_and_feel;
mod cc_outputs;
mod cc_sidebar;
mod cc_virtual_outputs;
mod cc_window;
mod cc_xwayland;
#[derive(Debug, Error)]
pub enum ControlCenterError {
#[error("Could not get the egg context")]
GetEggContext(#[source] EggError),
}
linear_ids!(ControlCenterIds, ControlCenterId, u64);
pub async fn redraw_control_centers(state: Rc<State>) {
let cc = &state.control_centers;
loop {
cc.redraw.triggered().await;
let interests = cc.change.take();
for cc in cc.control_centers.lock().values() {
if cc.inner.interests.interests.get().intersects(interests) {
cc.inner.window.request_redraw();
}
}
}
}
#[derive(Default)]
pub struct ControlCenters {
ids: ControlCenterIds,
change: NumCell<ControlCenterInterest>,
redraw: AsyncEvent,
control_centers: CopyHashMap<ControlCenterId, Rc<ControlCenter>>,
}
bitflags! {
ControlCenterInterest: u32;
CCI_COMPOSITOR,
CCI_IDLE,
CCI_COLOR_MANAGEMENT,
CCI_XWAYLAND,
CCI_OUTPUTS,
CCI_GPUS,
CCI_INPUT,
CCI_LOOK_AND_FEEL,
CCI_VIRTUAL_OUTPUTS,
}
pub struct ControlCenter {
inner: Rc<ControlCenterInner>,
}
linear_ids!(PaneIds, PaneId, u64);
struct ControlCenterInner {
id: ControlCenterId,
state: Rc<State>,
tree: RefCell<Settings>,
window: Rc<EggWindow>,
pane_ids: PaneIds,
interests: Rc<Interests>,
}
#[derive(Default)]
struct Interests {
interests: NumCell<ControlCenterInterest>,
interests_array: [NumCell<u64>; <ControlCenterInterest as Bitflag>::Type::BITS as usize],
}
struct Settings {
tree: Tree<Pane>,
}
struct Pane {
id: PaneId,
ps: PaneState,
own_interests: ControlCenterInterest,
cc_interests: Rc<Interests>,
ty: PaneType,
}
struct PaneState {
errors: Vec<String>,
}
enum PaneType {
Compositor(CompositorPane),
Idle(IdlePane),
ColorManagement(ColorManagementPane),
Xwayland(XwaylandPane),
Outputs(Box<OutputsPane>),
GPUs(GpusPane),
Input(InputPane),
LookAndFeel(LookAndFeelPane),
Clients(ClientsPane),
Client(ClientPane),
WindowSearch(WindowSearchPane),
Window(WindowPane),
VirtualOutputs(VirtualOutputsPane),
}
struct CcBehavior<'a> {
cc: &'a Rc<ControlCenterInner>,
close: Option<TileId>,
open: Option<PaneType>,
}
impl ControlCenters {
pub fn clear(&self) {
self.control_centers.clear();
}
}
impl Pane {
fn title(&self, res: &mut String) {
match &self.ty {
PaneType::Compositor(v) => v.title(res),
PaneType::Idle(v) => v.title(res),
PaneType::ColorManagement(v) => v.title(res),
PaneType::Xwayland(v) => v.title(res),
PaneType::Outputs(v) => v.title(res),
PaneType::GPUs(v) => v.title(res),
PaneType::Input(v) => v.title(res),
PaneType::LookAndFeel(v) => v.title(res),
PaneType::Clients(v) => v.title(res),
PaneType::Client(v) => v.title(res),
PaneType::WindowSearch(v) => v.title(res),
PaneType::Window(v) => v.title(res),
PaneType::VirtualOutputs(v) => v.title(res),
}
}
fn show(&mut self, behavior: &mut CcBehavior<'_>, ui: &mut Ui) {
match &mut self.ty {
PaneType::Compositor(p) => p.show(ui),
PaneType::Idle(p) => p.show(ui),
PaneType::ColorManagement(p) => p.show(ui),
PaneType::Xwayland(p) => p.show(behavior, ui),
PaneType::Outputs(p) => p.show(&mut self.ps, ui),
PaneType::GPUs(p) => p.show(ui),
PaneType::Input(p) => p.show(&mut self.ps, ui),
PaneType::LookAndFeel(p) => p.show(ui),
PaneType::Clients(p) => p.show(behavior, ui),
PaneType::Client(p) => p.show(behavior, ui),
PaneType::WindowSearch(p) => p.show(behavior, ui),
PaneType::Window(p) => p.show(behavior, ui),
PaneType::VirtualOutputs(p) => p.show(ui),
}
}
}
impl PaneType {
fn interest(&self) -> ControlCenterInterest {
match self {
PaneType::Compositor(_) => CCI_COMPOSITOR,
PaneType::Idle(_) => CCI_IDLE,
PaneType::ColorManagement(_) => CCI_COLOR_MANAGEMENT,
PaneType::Xwayland(_) => CCI_XWAYLAND,
PaneType::Outputs(_) => CCI_OUTPUTS,
PaneType::GPUs(_) => CCI_GPUS,
PaneType::Input(_) => CCI_INPUT,
PaneType::LookAndFeel(_) => CCI_LOOK_AND_FEEL,
PaneType::Clients(_) => ControlCenterInterest::none(),
PaneType::Client(_) => ControlCenterInterest::none(),
PaneType::WindowSearch(_) => ControlCenterInterest::none(),
PaneType::Window(_) => ControlCenterInterest::none(),
PaneType::VirtualOutputs(_) => CCI_VIRTUAL_OUTPUTS,
}
}
}
impl egui_tiles::Behavior<Pane> for CcBehavior<'_> {
fn pane_ui(&mut self, ui: &mut Ui, tile_id: TileId, pane: &mut Pane) -> egui_tiles::UiResponse {
let mut drag = false;
Frame::central_panel(ui.style()).show(ui, |ui| {
ui.horizontal(|ui| {
drag = ui
.add(icon_label(ICON_DRAG_INDICATOR).sense(Sense::drag()))
.total_drag_delta()
.map(|d| d.length() >= 5.0)
.unwrap_or(false);
let mut title = String::new();
pane.title(&mut title);
if ui
.add(icon_label(&title).sense(Sense::click()))
.middle_clicked()
{
self.close = Some(tile_id);
}
if ui
.add(icon_label(ICON_CLOSE).sense(Sense::click()))
.clicked()
{
self.close = Some(tile_id);
}
});
ui.separator();
show_errors(ui, &mut pane.ps);
ui.scope_builder(UiBuilder::new().id(Id::new(("pane", pane.id))), |ui| {
ScrollArea::vertical().show(ui, |ui| {
ui.allocate_space(vec2(ui.available_width(), 0.0));
pane.show(self, ui);
});
});
});
if drag {
egui_tiles::UiResponse::DragStarted
} else {
egui_tiles::UiResponse::None
}
}
fn tab_title_for_pane(&mut self, _pane: &Pane) -> WidgetText {
"".into()
}
fn tab_hover_cursor_icon(&self) -> CursorIcon {
CursorIcon::Default
}
fn tab_title_for_tile(&mut self, tiles: &Tiles<Pane>, tile_id: TileId) -> WidgetText {
fn add_title(tiles: &Tiles<Pane>, res: &mut String, first: &mut bool, tile_id: TileId) {
if !mem::take(first) {
res.push_str("/");
}
let Some(tile) = tiles.get(tile_id) else {
res.push_str("MISSING TILE");
return;
};
match tile {
Tile::Pane(p) => p.title(res),
Tile::Container(c) => {
let mut first = true;
for &tile_id in c.children() {
add_title(tiles, res, &mut first, tile_id);
}
}
}
}
let mut res = String::new();
let mut first = true;
add_title(tiles, &mut res, &mut first, tile_id);
res.into()
}
fn on_tab_button(
&mut self,
_tiles: &mut Tiles<Pane>,
tile_id: TileId,
button_response: Response,
) -> Response {
if button_response.middle_clicked() {
self.close = Some(tile_id);
}
button_response
}
fn resize_stroke(&self, style: &egui::Style, resize_state: ResizeState) -> Stroke {
match resize_state {
ResizeState::Idle => style.visuals.widgets.noninteractive.bg_stroke,
ResizeState::Hovering => style.visuals.widgets.hovered.fg_stroke,
ResizeState::Dragging => style.visuals.widgets.active.fg_stroke,
}
}
fn tab_bar_color(&self, visuals: &Visuals) -> Color32 {
(Rgba::from(visuals.panel_fill) * Rgba::from_gray(0.8)).into()
}
fn tab_bg_color(
&self,
visuals: &Visuals,
_tiles: &Tiles<Pane>,
_tile_id: TileId,
state: &TabState,
) -> Color32 {
match state.active {
true => visuals.panel_fill,
false => self.tab_bar_color(visuals),
}
}
}
impl EggWindowOwner for ControlCenterInner {
fn close(&self) {
self.close();
}
fn render(self: Rc<Self>, ui: &mut Ui) {
let settings = &mut *self.tree.borrow_mut();
Panel::left("sidebar").show_inside(ui, |ui| self.show_sidebar(&mut settings.tree, ui));
CentralPanel::default()
.frame(
Frame::central_panel(&ui.global_style())
.outer_margin(0.0)
.inner_margin(0.0),
)
.show_inside(ui, |ui| {
let tree = &mut settings.tree;
let mut behavior = CcBehavior {
cc: &self,
close: Default::default(),
open: Default::default(),
};
tree.ui(&mut behavior, ui);
if let Some(close) = behavior.close {
tree.set_visible(close, false);
tree.remove_recursively(close);
ui.request_repaint();
}
if let Some(ty) = behavior.open {
self.open(tree, ty);
ui.request_repaint();
}
});
}
}
impl State {
pub fn open_control_center(self: &Rc<Self>) -> Result<Rc<ControlCenter>, ControlCenterError> {
let ctx = self
.get_egg_context()
.map_err(ControlCenterError::GetEggContext)?;
let window = ctx.create_window("Control Center");
let cc = Rc::new(ControlCenter {
inner: Rc::new(ControlCenterInner {
id: self.control_centers.ids.next(),
window,
state: self.clone(),
tree: RefCell::new(Settings {
tree: Tree::new_tabs("abcd", vec![]),
}),
pane_ids: Default::default(),
interests: Default::default(),
}),
});
cc.inner.window.set_owner(Some(cc.inner.clone()));
self.control_centers
.control_centers
.set(cc.inner.id, cc.clone());
Ok(cc)
}
pub fn trigger_cci(&self, cci: ControlCenterInterest) {
self.control_centers.change.or_assign(cci);
self.control_centers.redraw.trigger();
}
}
impl ControlCenterInner {
fn close(&self) {
self.window.set_owner(None);
self.tree.borrow_mut().tree = Tree::empty("");
self.state.control_centers.control_centers.remove(&self.id);
}
}
impl Drop for ControlCenter {
fn drop(&mut self) {
self.inner.close();
}
}
impl ControlCenterInner {
fn create_pane(&self, ty: PaneType) -> Pane {
let pane = Pane {
id: self.pane_ids.next(),
ps: PaneState {
errors: Default::default(),
},
own_interests: ty.interest(),
cc_interests: self.interests.clone(),
ty,
};
let own = pane.own_interests;
for (idx, v) in pane.cc_interests.interests_array.iter().enumerate() {
let interest = ControlCenterInterest(1 << idx);
if own.intersects(interest) && v.fetch_add(1) == 0 {
pane.cc_interests.interests.or_assign(interest);
}
}
pane
}
fn open(&self, tree: &mut Tree<Pane>, ty: PaneType) {
let pane = self.create_pane(ty);
let id = tree.tiles.insert_pane(pane);
if let Some(root) = tree.root
&& let Some(tile) = tree.tiles.get_mut(root)
{
match tile {
Tile::Container(c) => {
c.add_child(id);
}
Tile::Pane(_) => {
let root = tree.tiles.insert_tab_tile(vec![root, id]);
tree.root = Some(root);
}
}
} else {
tree.root = Some(id);
}
tree.make_active(|t, _| t == id);
}
}
impl Drop for Pane {
fn drop(&mut self) {
let own = self.own_interests;
for (idx, v) in self.cc_interests.interests_array.iter().enumerate() {
let interest = ControlCenterInterest(1 << idx);
if own.intersects(interest) && v.fetch_sub(1) == 1 {
self.cc_interests.interests.and_assign(!interest);
}
}
}
}
fn icon_label(icon: &str) -> Label {
Label::new(icon).selectable(false)
}
fn grid_label(ui: &mut Ui, label: &str) {
ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
ui.label(label);
});
}
fn grid_label_ui<R>(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui) -> R) -> InnerResponse<R> {
ui.with_layout(Layout::right_to_left(Align::Center), add_contents)
}
fn tip(ui: &mut Ui, add_contents: impl FnOnce(&mut Ui)) {
icon_label(ICON_INFO).ui(ui).on_hover_ui(add_contents);
}
fn text_edit(ui: &mut Ui, v: &mut dyn TextBuffer) -> Response {
TextEdit::singleline(v)
.clip_text(false)
.min_size(vec2(200.0, 0.0))
.ui(ui)
}
fn show_errors(ui: &mut Ui, pane: &mut PaneState) {
if pane.errors.is_empty() {
return;
}
let mut to_remove = None;
for (idx, e) in pane.errors.iter().enumerate() {
ui.horizontal(|ui| {
Frame::new().inner_margin(5.0).show(ui, |ui| {
if ui.button(ICON_CLOSE).clicked() {
to_remove = Some(idx);
}
ui.label(
RichText::new("Error:")
.strong()
.color(ui.style().visuals.error_fg_color),
);
ui.add(Label::new(e).wrap());
});
});
}
if let Some(idx) = to_remove {
pane.errors.remove(idx);
ui.request_repaint();
}
ui.separator();
}
fn grid<R>(
ui: &mut Ui,
id_salt: impl Hash,
add_contents: impl FnOnce(&mut Ui) -> R,
) -> InnerResponse<R> {
let mut spacing = ui.spacing().item_spacing;
spacing.x *= 3.0;
Grid::new(id_salt).spacing(spacing).show(ui, add_contents)
}
fn row<R>(ui: &mut Ui, name: &str, add_contents: impl FnOnce(&mut Ui) -> R) -> R {
row_ui(ui, name, |_| (), add_contents)
}
fn row_ui<R, S>(
ui: &mut Ui,
name: &str,
label: impl FnOnce(&mut Ui) -> S,
add_contents: impl FnOnce(&mut Ui) -> R,
) -> R {
let ui = &mut *ui.row();
grid_label_ui(ui, |ui| {
ui.label(name);
label(ui);
});
add_contents(ui)
}
fn bool(ui: &mut Ui, name: &str, old: bool, set: impl FnOnce(bool)) {
bool_ui(ui, name, |_| (), old, set);
}
fn bool_ui<R>(
ui: &mut Ui,
name: &str,
label: impl FnOnce(&mut Ui) -> R,
mut v: bool,
set: impl FnOnce(bool),
) {
row_ui(ui, name, label, |ui| {
if Checkbox::without_text(&mut v).ui(ui).changed() {
set(v);
}
});
}
fn read_only_bool(ui: &mut Ui, name: &str, old: bool) {
read_only_bool_ui(ui, name, |_| (), old);
}
fn read_only_bool_ui<R>(ui: &mut Ui, name: &str, label: impl FnOnce(&mut Ui) -> R, mut v: bool) {
row_ui(ui, name, label, |ui| {
ui.add_enabled_ui(false, |ui| Checkbox::without_text(&mut v).ui(ui));
});
}
fn combo_box<T>(ui: &mut Ui, name: &str, old: T, set: impl FnOnce(T))
where
T: StaticText + Linearize + PartialEq + Copy,
{
combo_box_ui(ui, name, |_| (), old, set);
}
fn combo_box_ui<R, T>(
ui: &mut Ui,
name: &str,
label: impl FnOnce(&mut Ui) -> R,
mut v: T,
set: impl FnOnce(T),
) where
T: StaticText + Linearize + PartialEq + Copy,
{
row_ui(ui, name, label, |ui| {
let old = v;
ComboBox::from_id_salt(name)
.selected_text(v.text())
.show_ui(ui, |ui| {
for s in T::variants() {
ui.selectable_value(&mut v, s, s.text());
}
});
if old != v {
set(v);
}
});
}
fn drag_value<N>(
ui: &mut Ui,
name: &str,
old: N,
range: RangeInclusive<N>,
speed: f64,
set: impl FnOnce(N),
) where
N: Numeric,
{
drag_value_ui(ui, name, |_| (), old, range, speed, set);
}
fn drag_value_ui<R, N>(
ui: &mut Ui,
name: &str,
label: impl FnOnce(&mut Ui) -> R,
mut v: N,
range: RangeInclusive<N>,
speed: f64,
set: impl FnOnce(N),
) where
N: Numeric,
{
row_ui(ui, name, label, |ui| {
if DragValue::new(&mut v)
.range(range)
.speed(speed)
.ui(ui)
.changed()
{
set(v);
}
});
}
fn label(ui: &mut Ui, name: &str, text: impl Into<WidgetText>) {
row(ui, name, |ui| ui.label(text));
}
trait GridExt {
fn row(&mut self) -> impl DerefMut<Target = Ui>;
}
impl GridExt for Ui {
fn row(&mut self) -> impl DerefMut<Target = Ui> {
GridRow { ui: self }
}
}
struct GridRow<'a> {
ui: &'a mut Ui,
}
impl Deref for GridRow<'_> {
type Target = Ui;
fn deref(&self) -> &Self::Target {
self.ui
}
}
impl DerefMut for GridRow<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut *self.ui
}
}
impl Drop for GridRow<'_> {
fn drop(&mut self) {
self.end_row();
}
}
impl LazyEventSourceListener for ControlCenterInner {
fn triggered(self: Rc<Self>) {
self.window.request_redraw();
}
}

View file

@ -1,453 +0,0 @@
use {
crate::{
client::{Client, ClientId},
control_center::{
CcBehavior, ControlCenterInner, PaneType,
cc_criterion::{CcCriterion, CritImpl, CritRegex},
cc_window::show_window_collapsible,
grid, icon_label, label, read_only_bool,
},
criteria::{CritMgrExt, CritUpstreamNode, crit_leaf::CritLeafMatcher},
egui_adapter::egui_platform::icons::ICON_OPEN_IN_NEW,
state::State,
tree::{ToplevelData, ToplevelIdentifier},
utils::{copyhashmap::CopyHashMap, static_text::StaticText},
},
ahash::AHashMap,
egui::{
CollapsingHeader, DragValue, Sense, TextFormat, Ui, Widget, cache::CacheTrait,
text::LayoutJob,
},
linearize::Linearize,
std::rc::{Rc, Weak},
};
pub enum ClientCrit {
SandboxEngine(CritRegex),
SandboxAppId(CritRegex),
SandboxInstanceId(CritRegex),
Sandboxed,
Uid(i32),
Pid(i32),
IsXwayland,
Comm(CritRegex),
Exe(CritRegex),
Tag(CritRegex),
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Linearize)]
pub enum ClientCritTy {
SandboxEngine,
SandboxAppId,
SandboxInstanceId,
Sandboxed,
Uid,
Pid,
IsXwayland,
Comm,
Exe,
Tag,
}
impl Default for ClientCrit {
fn default() -> Self {
ClientCrit::Comm(Default::default())
}
}
impl StaticText for ClientCritTy {
fn text(&self) -> &'static str {
match self {
ClientCritTy::SandboxEngine => "Sandbox Engine",
ClientCritTy::SandboxAppId => "Sandbox App ID",
ClientCritTy::SandboxInstanceId => "Sandbox Instance ID",
ClientCritTy::Sandboxed => "Sandboxed",
ClientCritTy::Uid => "UID",
ClientCritTy::Pid => "PID",
ClientCritTy::IsXwayland => "Is Xwayland",
ClientCritTy::Comm => "Comm",
ClientCritTy::Exe => "Exe",
ClientCritTy::Tag => "Tag",
}
}
}
impl CritImpl for ClientCrit {
type Type = ClientCritTy;
type Target = Rc<Client>;
fn ty(&self) -> Self::Type {
macro_rules! map {
($($n:ident,)*) => {
match self {
$(
Self::$n { .. } => ClientCritTy::$n,
)*
}
};
}
map! {
SandboxEngine,
SandboxAppId,
SandboxInstanceId,
Sandboxed,
Uid,
Pid,
IsXwayland,
Comm,
Exe,
Tag,
}
}
fn from_ty(ty: Self::Type) -> Self {
match ty {
ClientCritTy::SandboxEngine => Self::SandboxEngine(Default::default()),
ClientCritTy::SandboxAppId => Self::SandboxAppId(Default::default()),
ClientCritTy::SandboxInstanceId => Self::SandboxInstanceId(Default::default()),
ClientCritTy::Sandboxed => Self::Sandboxed,
ClientCritTy::Uid => Self::Uid(Default::default()),
ClientCritTy::Pid => Self::Pid(Default::default()),
ClientCritTy::IsXwayland => Self::IsXwayland,
ClientCritTy::Comm => Self::Comm(Default::default()),
ClientCritTy::Exe => Self::Exe(Default::default()),
ClientCritTy::Tag => Self::Tag(Default::default()),
}
}
fn show(&mut self, ui: &mut Ui) -> bool {
match self {
ClientCrit::SandboxEngine(v) => v.show(ui),
ClientCrit::SandboxAppId(v) => v.show(ui),
ClientCrit::SandboxInstanceId(v) => v.show(ui),
ClientCrit::Sandboxed => false,
ClientCrit::Uid(v) => DragValue::new(v).ui(ui).changed(),
ClientCrit::Pid(v) => DragValue::new(v).ui(ui).changed(),
ClientCrit::IsXwayland => false,
ClientCrit::Comm(v) => v.show(ui),
ClientCrit::Exe(v) => v.show(ui),
ClientCrit::Tag(v) => v.show(ui),
}
}
fn to_crit(&self, state: &Rc<State>) -> Option<Rc<dyn CritUpstreamNode<Self::Target>>> {
let m = &state.cl_matcher_manager;
let res = match self {
ClientCrit::SandboxEngine(v) => m.sandbox_engine(v.to_crit()?),
ClientCrit::SandboxAppId(v) => m.sandbox_app_id(v.to_crit()?),
ClientCrit::SandboxInstanceId(v) => m.sandbox_instance_id(v.to_crit()?),
ClientCrit::Sandboxed => m.sandboxed(),
ClientCrit::Uid(v) => m.uid(*v),
ClientCrit::Pid(v) => m.pid(*v),
ClientCrit::IsXwayland => m.is_xwayland(),
ClientCrit::Comm(v) => m.comm(v.to_crit()?),
ClientCrit::Exe(v) => m.exe(v.to_crit()?),
ClientCrit::Tag(v) => m.tag(v.to_crit()?),
};
Some(res)
}
fn not(
state: &State,
upstream: &Rc<dyn CritUpstreamNode<Self::Target>>,
) -> Rc<dyn CritUpstreamNode<Self::Target>> {
state.cl_matcher_manager.not(upstream)
}
fn list(
state: &State,
upstream: &[Rc<dyn CritUpstreamNode<Self::Target>>],
all: bool,
) -> Rc<dyn CritUpstreamNode<Self::Target>> {
state.cl_matcher_manager.list(upstream, all)
}
fn exactly(
state: &State,
n: usize,
upstream: &[Rc<dyn CritUpstreamNode<Self::Target>>],
) -> Rc<dyn CritUpstreamNode<Self::Target>> {
state.cl_matcher_manager.exactly(upstream, n)
}
}
pub struct ClientsPane {
state: Rc<State>,
filter: bool,
criterion: CcCriterion<ClientCrit>,
matched: Rc<Matched>,
leaf: Option<Rc<CritLeafMatcher<Rc<Client>>>>,
}
struct Matched {
slf: Weak<ControlCenterInner>,
clients: CopyHashMap<ClientId, ()>,
}
impl Matched {
fn request_frame(&self) {
if let Some(slf) = self.slf.upgrade() {
slf.window.request_redraw();
}
}
}
impl ControlCenterInner {
pub fn create_clients_pane(self: &Rc<Self>) -> ClientsPane {
let mut pane = ClientsPane {
state: self.state.clone(),
filter: false,
criterion: Default::default(),
matched: Rc::new(Matched {
slf: Rc::downgrade(self),
clients: Default::default(),
}),
leaf: Default::default(),
};
pane.update_matcher();
pane
}
}
impl ClientsPane {
pub fn title(&self, res: &mut String) {
res.push_str("Clients");
}
pub fn show(&mut self, behavior: &mut CcBehavior<'_>, ui: &mut Ui) {
if ui.checkbox(&mut self.filter, "Filter").changed() && !self.filter {
self.criterion = Default::default();
self.update_matcher();
}
let mut clear_clients = false;
if self.filter && self.criterion.show(ui) {
clear_clients = self.update_matcher();
}
ui.separator();
let mut clients: Vec<_> = self.matched.clients.lock().keys().copied().collect();
clients.sort();
for id in clients {
let Ok(client) = self.state.clients.get(id) else {
continue;
};
show_client_collapsible(behavior, ui, &client);
}
if clear_clients {
self.matched.clients.clear();
}
}
fn update_matcher(&mut self) -> bool {
let mut clear_clients = false;
let state = &self.state;
if let Some(new) = self.criterion.to_crit(state) {
clear_clients = true;
let matched = self.matched.clone();
let leaf = state.cl_matcher_manager.leaf(&new, move |data| {
matched.clients.set(data, ());
matched.request_frame();
Box::new({
let matched = matched.clone();
move || {
matched.clients.remove(&data);
matched.request_frame();
}
})
});
state.cl_matcher_manager.rematch_all(state);
self.leaf = Some(leaf);
}
clear_clients
}
}
pub struct ClientPane {
client: Rc<Client>,
}
impl ControlCenterInner {
pub fn create_client_pane(self: &Rc<Self>, client: &Rc<Client>) -> ClientPane {
ClientPane {
client: client.clone(),
}
}
}
impl ClientPane {
pub fn title(&self, res: &mut String) {
res.push_str("Client ");
res.push_str(&self.client.pid_info.comm);
}
pub fn show(&mut self, behavior: &mut CcBehavior<'_>, ui: &mut Ui) {
show_client(behavior, ui, &self.client);
}
}
pub fn show_client_collapsible(behavior: &mut CcBehavior, ui: &mut Ui, client: &Rc<Client>) {
let mut layout_job = LayoutJob::default();
layout_job.append(
"Client",
0.0,
TextFormat {
color: ui.style().visuals.widgets.inactive.text_color(),
..Default::default()
},
);
layout_job.append(
&client.id.to_string(),
10.0,
TextFormat {
color: ui.style().visuals.widgets.active.text_color(),
..Default::default()
},
);
layout_job.append(
&client.pid_info.comm,
10.0,
TextFormat {
color: ui.style().visuals.widgets.inactive.text_color(),
..Default::default()
},
);
CollapsingHeader::new(layout_job)
.id_salt(("client", client.id))
.show(ui, |ui| {
if icon_label(ICON_OPEN_IN_NEW)
.sense(Sense::CLICK)
.ui(ui)
.clicked()
{
behavior.open = Some(PaneType::Client(behavior.cc.create_client_pane(client)));
}
show_client(behavior, ui, client)
});
}
pub fn show_client(behavior: &mut CcBehavior<'_>, ui: &mut Ui, client: &Client) {
grid(ui, ("client", client.id), |ui| {
label(ui, "ID", client.id.to_string());
label(ui, "PID", client.pid_info.pid.to_string());
label(ui, "UID", client.pid_info.uid.to_string());
label(ui, "comm", &client.pid_info.comm);
label(ui, "exe", &client.pid_info.exe);
if client.acceptor.sandboxed {
read_only_bool(ui, "Sandboxed", true);
}
if client.acceptor.secure {
read_only_bool(ui, "Secure", true);
}
if client.is_xwayland {
read_only_bool(ui, "Xwayland", true);
}
if let Some(v) = &client.acceptor.sandbox_engine {
label(ui, "Sandbox Engine", v);
}
if let Some(v) = &client.acceptor.app_id {
label(ui, "App ID", v);
}
if let Some(v) = &client.acceptor.instance_id {
label(ui, "Instance ID", v);
}
if let Some(v) = &client.acceptor.tag {
label(ui, "Tag", v);
}
});
if ui.button("Kill").clicked() {
client.state.clients.kill(client.id);
}
ui.collapsing("Capabilities", |ui| {
ui.add_enabled_ui(false, |ui| {
for (k, v) in client.effective_caps.get().to_map() {
if v {
ui.checkbox(&mut true, k.text());
}
}
});
});
ui.collapsing("Windows", |ui| {
let matcher = ui.memory_mut(|m| {
m.caches
.cache::<ClientWindowMatchersCache>()
.get(behavior.cc, client.id)
.clone()
});
let mut windows: Vec<_> = matcher.windows.lock().keys().copied().collect();
windows.sort();
for id in windows {
let Some(window) = client.state.toplevels.get(&id).and_then(|v| v.upgrade()) else {
continue;
};
show_window_collapsible(behavior, ui, &window);
}
});
}
#[derive(Default)]
struct ClientWindowMatchersCache {
generation: u64,
matchers: AHashMap<ClientId, CachedWindowMatcher>,
}
struct CachedWindowMatcher {
generation: u64,
_matcher: Rc<CritLeafMatcher<ToplevelData>>,
matchers: Rc<WindowMatchers>,
}
struct WindowMatchers {
cc: Weak<ControlCenterInner>,
windows: CopyHashMap<ToplevelIdentifier, ()>,
}
impl ClientWindowMatchersCache {
fn get(&mut self, cc: &Rc<ControlCenterInner>, id: ClientId) -> &Rc<WindowMatchers> {
let res = self.matchers.entry(id).or_insert_with(|| {
let state = &cc.state;
let node = state.cl_matcher_manager.id(id);
let node = state.tl_matcher_manager.client(state, &node);
let matchers = Rc::new(WindowMatchers {
cc: Rc::downgrade(&cc),
windows: Default::default(),
});
let matchers2 = matchers.clone();
let matcher = state.tl_matcher_manager.leaf(&node, move |id| {
matchers2.windows.set(id, ());
if let Some(cc) = matchers2.cc.upgrade() {
cc.window.request_redraw();
}
let matchers2 = matchers2.clone();
Box::new(move || {
matchers2.windows.remove(&id);
if let Some(cc) = matchers2.cc.upgrade() {
cc.window.request_redraw();
}
})
});
let res = CachedWindowMatcher {
generation: 0,
_matcher: matcher,
matchers,
};
state.cl_matcher_manager.rematch_all(state);
state.tl_matcher_manager.rematch_all(state);
res
});
res.generation = self.generation;
&res.matchers
}
}
unsafe impl Sync for ClientWindowMatchersCache {}
unsafe impl Send for ClientWindowMatchersCache {}
impl CacheTrait for ClientWindowMatchersCache {
fn update(&mut self) {
self.matchers.retain(|_, m| m.generation == self.generation);
self.generation += 1;
}
fn len(&self) -> usize {
self.matchers.len()
}
}

View file

@ -1,36 +0,0 @@
use {
crate::{
control_center::{ControlCenterInner, bool, grid, read_only_bool},
state::State,
},
egui::Ui,
std::rc::Rc,
};
pub struct ColorManagementPane {
state: Rc<State>,
}
impl ControlCenterInner {
pub fn create_color_management_pane(self: &Rc<Self>) -> ColorManagementPane {
ColorManagementPane {
state: self.state.clone(),
}
}
}
impl ColorManagementPane {
pub fn title(&self, res: &mut String) {
res.push_str("Color Management");
}
pub fn show(&mut self, ui: &mut Ui) {
let s = &self.state;
grid(ui, "settings", |ui| {
bool(ui, "Enabled", s.color_management_enabled.get(), |b| {
s.set_color_management_enabled(b);
});
read_only_bool(ui, "Available", s.color_management_available());
});
}
}

View file

@ -1,88 +0,0 @@
use {
crate::{
compositor::{LIBEI_SOCKET, WAYLAND_DISPLAY},
control_center::{ControlCenterInner, bool, combo_box, grid, label, row},
state::State,
version::VERSION,
},
egui::{DragValue, OpenUrl, Ui, Widget},
std::rc::Rc,
};
pub struct CompositorPane {
state: Rc<State>,
switch_to_vt: u32,
}
impl ControlCenterInner {
pub fn create_compositor_pane(self: &Rc<Self>) -> CompositorPane {
CompositorPane {
state: self.state.clone(),
switch_to_vt: 1,
}
}
}
impl CompositorPane {
pub fn title(&self, res: &mut String) {
res.push_str("Compositor");
}
pub fn show(&mut self, ui: &mut Ui) {
let s = &self.state;
grid(ui, "compositor", |ui| {
row(ui, "Repository", |ui| {
let url = "https://github.com/mahkoh/jay";
if ui.link(url).clicked() {
ui.open_url(OpenUrl::new_tab(url));
}
});
label(ui, "Version", VERSION);
label(ui, "PID", s.pid.to_string());
if let Some(acceptor) = s.acceptor.get() {
label(ui, WAYLAND_DISPLAY, acceptor.socket_name());
}
if let Some(dir) = &s.config_dir {
label(ui, "Config DIR", dir);
}
bool(ui, "Libei Socket", s.enable_ei_acceptor.get(), |v| {
s.set_ei_socket_enabled(v);
});
if let Some(a) = s.ei_acceptor.get() {
label(ui, LIBEI_SOCKET, a.socket_name());
}
combo_box(
ui,
"Workspace Display Order",
s.workspace_display_order.get(),
|o| s.set_workspace_display_order(o),
);
if let Some(logger) = &s.logger {
combo_box(ui, "Log Level", logger.level(), |l| s.set_log_level(l));
row(ui, "Log File", |ui| {
let path = logger.path().to_string();
if ui
.link(&path)
.on_hover_text_at_pointer("Copy to clipboard")
.clicked()
{
ui.copy_text(path);
}
});
}
});
if ui.button("Quit").clicked() {
s.quit();
}
if ui.button("Reload Config").clicked() {
s.reload_config();
}
ui.horizontal(|ui| {
let button = ui.button("Switch to VT");
DragValue::new(&mut self.switch_to_vt).ui(ui);
if button.clicked() {
s.backend.get().switch_to(self.switch_to_vt);
}
});
}
}

View file

@ -1,253 +0,0 @@
use {
crate::{
criteria::{CritLiteralOrRegex, CritUpstreamNode},
egui_adapter::egui_platform::icons::ICON_CLOSE,
state::State,
utils::{numcell::NumCell, static_text::StaticText},
},
ahash::AHashSet,
egui::{ComboBox, DragValue, Ui, UiBuilder, Widget},
isnt::std_1::collections::IsntHashSetExt,
linearize::{Linearize, LinearizeExt},
regex::Regex,
std::rc::Rc,
};
pub enum CcCriterion<T> {
Not(Box<Self>),
List(Vec<Self>, bool),
Exactly(usize, Vec<Self>),
T(T),
}
impl<T> Default for CcCriterion<T>
where
T: Default,
{
fn default() -> Self {
Self::T(T::default())
}
}
#[derive(Copy, Clone, Eq, PartialEq, Linearize)]
enum CompoundCritTy {
Not,
All,
Any,
Exactly,
}
#[derive(Copy, Clone, Eq, PartialEq)]
enum CritTy<T> {
Compound(CompoundCritTy),
T(T),
}
impl StaticText for CompoundCritTy {
fn text(&self) -> &'static str {
match self {
Self::Not => "Not",
Self::All => "All",
Self::Any => "Any",
Self::Exactly => "Exactly",
}
}
}
impl<T> StaticText for CritTy<T>
where
T: StaticText,
{
fn text(&self) -> &'static str {
match self {
Self::Compound(t) => t.text(),
Self::T(t) => t.text(),
}
}
}
pub trait CritImpl: Default {
type Type: Copy + Eq + PartialEq + StaticText + Linearize;
type Target;
fn ty(&self) -> Self::Type;
fn from_ty(ty: Self::Type) -> Self;
#[must_use]
fn show(&mut self, ui: &mut Ui) -> bool;
fn to_crit(&self, state: &Rc<State>) -> Option<Rc<dyn CritUpstreamNode<Self::Target>>>;
fn not(
state: &State,
upstream: &Rc<dyn CritUpstreamNode<Self::Target>>,
) -> Rc<dyn CritUpstreamNode<Self::Target>>;
fn list(
state: &State,
upstream: &[Rc<dyn CritUpstreamNode<Self::Target>>],
all: bool,
) -> Rc<dyn CritUpstreamNode<Self::Target>>;
fn exactly(
state: &State,
n: usize,
upstream: &[Rc<dyn CritUpstreamNode<Self::Target>>],
) -> Rc<dyn CritUpstreamNode<Self::Target>>;
}
impl<T> CcCriterion<T>
where
T: CritImpl,
{
#[must_use]
pub fn show(&mut self, ui: &mut Ui) -> bool {
let mut changed = false;
ui.vertical(|ui| {
ui.horizontal(|ui| {
let mut v = self.ty();
let old = v;
ComboBox::from_id_salt("ty")
.selected_text(v.text())
.show_ui(ui, |ui| {
for s in CompoundCritTy::variants() {
ui.selectable_value(&mut v, CritTy::Compound(s), s.text());
}
for s in T::Type::variants() {
ui.selectable_value(&mut v, CritTy::T(s), s.text());
}
});
if old != v {
*self = match v {
CritTy::Compound(CompoundCritTy::Not) => {
CcCriterion::Not(Default::default())
}
CritTy::Compound(CompoundCritTy::All) => {
CcCriterion::List(Default::default(), true)
}
CritTy::Compound(CompoundCritTy::Any) => {
CcCriterion::List(Default::default(), false)
}
CritTy::Compound(CompoundCritTy::Exactly) => {
CcCriterion::Exactly(1, Default::default())
}
CritTy::T(t) => CcCriterion::T(T::from_ty(t)),
};
changed = true;
}
match self {
CcCriterion::Not(n) => changed |= n.show(ui),
CcCriterion::List(_, _) => {}
CcCriterion::Exactly(n, _) => {
changed |= DragValue::new(n).ui(ui).changed();
}
CcCriterion::T(t) => changed |= t.show(ui),
}
});
match self {
CcCriterion::Not(_) => {}
CcCriterion::List(v, _) | CcCriterion::Exactly(_, v) => {
ui.indent("compound", |ui| {
let mut to_remove = AHashSet::new();
for (idx, v) in v.iter_mut().enumerate() {
ui.horizontal(|ui| {
if ui.button(ICON_CLOSE).clicked() {
changed = true;
to_remove.insert(idx);
}
ui.scope_builder(UiBuilder::new().id_salt(idx), |ui| {
changed |= v.show(ui);
});
});
}
let i = NumCell::new(0);
v.retain(|_| to_remove.not_contains(&i.fetch_add(1)));
if ui.button("Add").clicked() {
v.push(CcCriterion::default());
changed = true;
}
});
}
CcCriterion::T(_) => {}
}
});
changed
}
fn ty(&self) -> CritTy<T::Type> {
match self {
CcCriterion::Not(_) => CritTy::Compound(CompoundCritTy::Not),
CcCriterion::List(_, true) => CritTy::Compound(CompoundCritTy::All),
CcCriterion::List(_, false) => CritTy::Compound(CompoundCritTy::Any),
CcCriterion::Exactly(_, _) => CritTy::Compound(CompoundCritTy::Exactly),
CcCriterion::T(t) => CritTy::T(t.ty()),
}
}
pub fn to_crit(&self, state: &Rc<State>) -> Option<Rc<dyn CritUpstreamNode<T::Target>>> {
match self {
CcCriterion::Not(t) => Some(T::not(state, &t.to_crit(state)?)),
CcCriterion::List(v, all) => {
let mut upstream = Vec::with_capacity(v.len());
for v in v {
upstream.push(v.to_crit(state)?);
}
Some(T::list(state, &upstream, *all))
}
CcCriterion::Exactly(n, v) => {
let mut upstream = Vec::with_capacity(v.len());
for v in v {
upstream.push(v.to_crit(state)?);
}
Some(T::exactly(state, *n, &upstream))
}
CcCriterion::T(t) => t.to_crit(state),
}
}
pub fn any(&self, mut any: impl FnMut(&T) -> bool) -> bool {
self.any_(&mut any)
}
fn any_(&self, any: &mut impl FnMut(&T) -> bool) -> bool {
match self {
CcCriterion::Not(v) => v.any_(any),
CcCriterion::List(v, _) => v.iter().any(|v| v.any_(any)),
CcCriterion::Exactly(_, v) => v.iter().any(|v| v.any_(any)),
CcCriterion::T(t) => any(t),
}
}
}
pub struct CritRegex {
pub text: String,
pub regex: Option<Option<Regex>>,
}
impl Default for CritRegex {
fn default() -> Self {
Self {
text: Default::default(),
regex: Some(Some(Regex::new("").unwrap())),
}
}
}
impl CritRegex {
pub fn show(&mut self, ui: &mut Ui) -> bool {
let mut is_regex = self.regex.is_some();
let mut changed = false;
changed |= ui.text_edit_singleline(&mut self.text).changed();
changed |= ui.checkbox(&mut is_regex, "Regex").changed();
if changed {
self.regex = is_regex.then(|| Regex::new(&self.text).ok());
}
if let Some(None) = self.regex {
ui.label("Error: Invalid regex");
}
changed
}
pub fn to_crit(&self) -> Option<CritLiteralOrRegex> {
match &self.regex {
None => Some(CritLiteralOrRegex::Literal(self.text.clone())),
Some(v) => Some(CritLiteralOrRegex::Regex(v.clone()?)),
}
}
}

View file

@ -1,146 +0,0 @@
use {
crate::{
control_center::{
ControlCenterInner, GridExt, bool, combo_box, grid, grid_label, label, row,
},
egui_adapter::egui_platform::icons::{ICON_ADD, ICON_REMOVE},
state::{DrmDevData, State},
},
egui::{Checkbox, CollapsingHeader, DragValue, TextFormat, Ui, Widget, text::LayoutJob},
std::rc::Rc,
};
pub struct GpusPane {
state: Rc<State>,
}
impl ControlCenterInner {
pub fn create_gpus_pane(self: &Rc<Self>) -> GpusPane {
GpusPane {
state: self.state.clone(),
}
}
}
impl GpusPane {
pub fn title(&self, res: &mut String) {
res.push_str("GPUs");
}
pub fn show(&mut self, ui: &mut Ui) {
let devs = self.state.drm_devs.lock();
let mut devs: Vec<_> = devs.iter().collect();
devs.sort_by_key(|d| d.0);
for dev in devs {
self.show_dev(ui, dev.1);
}
}
fn show_dev(&self, ui: &mut Ui, dev: &DrmDevData) {
let title_buf;
let title = match dev.devnode.as_deref() {
Some(t) => t,
_ => {
let dev_t = dev.dev.dev_t();
title_buf = format!("{}:{}", uapi::major(dev_t), uapi::minor(dev_t));
&title_buf
}
};
let mut layout_job = LayoutJob::default();
layout_job.append(
title,
0.0,
TextFormat {
color: ui.style().visuals.widgets.active.text_color(),
..Default::default()
},
);
if let Some(model) = &dev.model {
layout_job.append(
model,
10.0,
TextFormat {
color: ui.style().visuals.widgets.inactive.text_color(),
..Default::default()
},
);
}
ui.collapsing(layout_job, |ui| {
grid(ui, ("settings", dev.dev.id()), |ui| {
macro_rules! string {
($field:ident, $name:expr) => {
if let Some(v) = &dev.$field {
label(ui, $name, v);
}
};
}
string!(vendor, "Vendor");
string!(model, "Model");
string!(devnode, "Devnode");
string!(syspath, "Syspath");
if let Some(v) = dev.pci_id {
label(ui, "PCI ID", format!("{:x}:{:x}", v.vendor, v.model));
}
{
let v = dev.dev.dev_t();
label(ui, "Dev", format!("{}:{}", uapi::major(v), uapi::minor(v)));
}
combo_box(ui, "API", dev.dev.gtx_api(), |v| dev.dev.set_gfx_api(v));
row(ui, "Primary Device", |ui| {
let mut v = dev.dev.is_render_device();
let old = v;
ui.add_enabled(!v, Checkbox::without_text(&mut v));
if v != old {
dev.dev.make_render_device();
}
});
bool(
ui,
"Direct Scanout",
dev.dev.direct_scanout_enabled(),
|v| dev.set_direct_scanout_enabled(&self.state, v),
);
if let Some(mut v) = dev.dev.flip_margin() {
let ui = &mut *ui.row();
grid_label(ui, "Flip Margin");
let old = v;
let denom = 1_000_000.0;
ui.horizontal(|ui| {
let mut s = v as f64 / denom;
let res = DragValue::new(&mut s)
.range(0.0..=50.0)
.speed(0.1)
.fixed_decimals(1)
.ui(ui);
if res.changed() {
v = (s * denom) as u64;
}
if ui.button(ICON_REMOVE).clicked() {
v = v.saturating_sub(100_000);
}
if ui.button(ICON_ADD).clicked() {
v += 100_000;
}
});
if old != v {
dev.set_flip_margin(&self.state, v);
}
}
});
CollapsingHeader::new("Connectors")
.default_open(true)
.show(ui, |ui| {
let mut cs: Vec<_> = dev
.connectors
.lock()
.values()
.map(|v| v.name.clone())
.collect::<Vec<_>>();
cs.sort();
for c in cs {
ui.label(&**c);
}
});
});
}
}

View file

@ -1,72 +0,0 @@
use {
crate::{
control_center::{ControlCenterInner, grid, row},
state::State,
},
egui::{CollapsingHeader, DragValue, Ui, Widget},
std::{rc::Rc, time::Duration},
};
pub struct IdlePane {
state: Rc<State>,
}
impl ControlCenterInner {
pub fn create_idle_pane(self: &Rc<Self>) -> IdlePane {
IdlePane {
state: self.state.clone(),
}
}
}
impl IdlePane {
pub fn title(&self, res: &mut String) {
res.push_str("Idle");
}
pub fn show(&mut self, ui: &mut Ui) {
grid(ui, "sliders", |ui| {
for interval in [true, false] {
let label = match interval {
true => "Interval",
false => "Grace period",
};
let idle = &self.state.idle;
let field = match interval {
true => &idle.timeout,
false => &idle.grace_period,
};
row(ui, label, |ui| {
let secs = field.get().as_secs();
let mut minutes = secs / 60;
let mut seconds = secs % 60;
let mut changed = false;
changed |= DragValue::new(&mut minutes).ui(ui).changed();
ui.label("minutes");
changed |= DragValue::new(&mut seconds).range(0..=59).ui(ui).changed();
ui.label("seconds");
if changed {
let duration =
Duration::from_secs(minutes.saturating_mul(60).saturating_add(seconds));
match interval {
true => idle.set_timeout(&self.state, duration),
false => idle.set_grace_period(&self.state, duration),
}
}
});
}
});
let inhibitors = self.state.idle.inhibitors.lock();
let mut is: Vec<_> = inhibitors.values().collect();
is.sort_by_key(|is| is.inhibit_id);
CollapsingHeader::new(format!("Inhibitors ({})", is.len()))
.id_salt("Inhibitors")
.show(ui, |ui| {
for i in is {
ui.horizontal(|ui| {
ui.label(&i.client.pid_info.comm);
});
}
});
}
}

View file

@ -1,685 +0,0 @@
use {
crate::{
backend::{InputDeviceCapability, InputDeviceId},
control_center::{
ControlCenterInner, GridExt, PaneState, bool, bool_ui, combo_box, combo_box_ui,
drag_value, drag_value_ui, grid, grid_label, grid_label_ui, label, text_edit, tip,
},
egui_adapter::egui_platform::icons::ICON_PENDING,
ifs::{
wl_output::WlOutputGlobal,
wl_seat::{SeatId, WlSeatGlobal},
},
kbvm::KbvmMap,
state::{DeviceHandlerData, State},
utils::{errorfmt::ErrorFmt, static_text::StaticText},
},
ahash::AHashMap,
egui::{
CollapsingHeader, ComboBox, DragValue, Event, Grid, Id, TextBuffer, TextFormat, Ui,
UiBuilder, ViewportCommand, Widget, emath::Numeric, text::LayoutJob,
},
isnt::std_1::string::IsntStringExt,
jay_config::keyboard::syms::KeySym,
kbvm::Keysym,
linearize::LinearizeExt,
rand::random,
std::{mem, rc::Rc},
};
pub struct InputPane {
state: Rc<State>,
paste_requested: Option<Id>,
keymaps: AHashMap<Key, KeymapState>,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
enum Key {
Seat(SeatId),
Dev(InputDeviceId),
}
struct KeymapState {
seed: u64,
rules_default: bool,
rules: String,
model_default: bool,
model: String,
layouts: String,
variants: String,
options: String,
backup: Option<Rc<KbvmMap>>,
pointer_revert_key: Keysym,
pointer_revert_key_str: Option<String>,
unknown_pointer_revert_key: bool,
}
impl Default for KeymapState {
fn default() -> Self {
Self {
seed: random(),
rules_default: true,
rules: Default::default(),
model_default: true,
model: Default::default(),
layouts: Default::default(),
variants: Default::default(),
options: Default::default(),
backup: Default::default(),
pointer_revert_key: Default::default(),
pointer_revert_key_str: None,
unknown_pointer_revert_key: false,
}
}
}
impl ControlCenterInner {
pub fn create_input_pane(self: &Rc<Self>) -> InputPane {
InputPane {
state: self.state.clone(),
paste_requested: Default::default(),
keymaps: Default::default(),
}
}
}
impl InputPane {
pub fn title(&self, res: &mut String) {
res.push_str("Input");
}
pub fn show(&mut self, ps: &mut PaneState, ui: &mut Ui) {
let state = self.state.clone();
let seats = state.globals.seats.lock();
let mut seats: Vec<_> = seats.values().collect();
seats.sort_by_key(|d| d.seat_name());
for seat in &seats {
self.show_seat(ps, ui, seat);
}
let outputs = state.globals.outputs.lock();
let mut outputs: Vec<_> = outputs.values().collect();
outputs.sort_by_key(|o| &o.connector.name);
let dev = &*state.input_device_handlers.borrow();
let mut dev: Vec<_> = dev.values().collect();
dev.sort_by_key(|d| d.data.device.name());
for dev in dev {
self.show_device(ps, ui, &dev.data, &seats, &outputs);
}
}
fn show_seat(&mut self, ps: &mut PaneState, ui: &mut Ui, seat: &Rc<WlSeatGlobal>) {
let mut layout_job = LayoutJob::default();
layout_job.append(
"Seat",
0.0,
TextFormat {
color: ui.style().visuals.widgets.inactive.text_color(),
..Default::default()
},
);
layout_job.append(
seat.seat_name(),
10.0,
TextFormat {
color: ui.style().visuals.widgets.active.text_color(),
..Default::default()
},
);
let ks = self.keymaps.entry(Key::Seat(seat.id())).or_default();
CollapsingHeader::new(layout_job)
.id_salt(("seat", seat.id()))
.show(ui, |ui| {
grid(ui, ("seat", seat.id()), |ui| {
let mut dv = |name: &str, get: &dyn Fn(&mut (i32, i32)) -> &mut i32| {
let ui = &mut *ui.row();
grid_label(ui, name);
let mut v = seat.get_rate();
let old = v;
ui.horizontal(|ui| {
let v = get(&mut v);
DragValue::new(v).range(0..=i32::MAX).ui(ui);
if ui.button("-20").clicked() {
*v = v.saturating_sub(20).max(0)
}
if ui.button("+20").clicked() {
*v = v.saturating_add(20);
}
});
if v != old {
seat.set_rate(v.0, v.1);
}
};
dv("Repeat Rate", &|v| &mut v.0);
dv("Repeat Delay", &|v| &mut v.1);
drag_value(
ui,
"Cursor Size",
seat.cursor_group().cursor_size(),
0..=u32::MAX,
1.0,
|v| seat.cursor_group().set_cursor_size(v),
);
bool_ui(
ui,
"Simple IM",
|ui| {
tip(ui, |ui| {
ui.label("A simple input method based on Xcompose files.");
ui.label(concat!(
"If you're not using another input method, you should ",
"leave this enabled as it will work for sandboxed ",
"applications, which regular Xcompose will not.",
));
ui.label(concat!(
"The `enable-unicode-input` action can be used to input ",
"characters by their unicode value.",
));
});
},
seat.simple_im_enabled(),
|b| seat.set_simple_im_enabled(b),
);
bool_ui(
ui,
"Hardware Cursor",
|ui| {
tip(ui, |ui| {
ui.label(
"Allow this seat to use the hardware cursor, if available.",
);
ui.label("Only one seat can use the hardware cursor at a time.");
});
},
seat.cursor_group().hardware_cursor(),
|b| seat.cursor_group().set_hardware_cursor(b),
);
{
let ui = &mut *ui.row();
let v = seat.pointer_revert_key();
let v = Keysym(v.0);
if mem::replace(&mut ks.pointer_revert_key, v) != v {
ks.pointer_revert_key_str = None;
}
let name = ks
.pointer_revert_key_str
.get_or_insert_with(|| v.name().unwrap_or_default().to_string());
grid_label_ui(ui, |ui| {
ui.label("Pointer Revert Key");
tip(ui, |ui| {
ui.label(concat!(
"Pressing this key reverts the pointer to the default state, ",
"breaking grabs, drags, etc.",
));
ui.label(
"Setting this to `NoSymbol` effectively disables this feature.",
);
});
});
ui.horizontal(|ui| {
if ui.text_edit_singleline(name).changed() {
let v = Keysym::from_str(name);
ks.unknown_pointer_revert_key = v.is_none();
if let Some(v) = v {
ks.pointer_revert_key = v;
seat.set_pointer_revert_key(KeySym(v.0));
}
}
if ks.unknown_pointer_revert_key {
ui.label("Error: Unknown key");
}
});
}
bool(ui, "Focus Follows Mouse", seat.focus_follows_mouse(), |v| {
seat.set_focus_follows_mouse(v);
});
bool(ui, "Mouse Follows Focus", seat.mouse_follows_focus(), |v| {
seat.set_mouse_follows_focus(v);
});
combo_box_ui(
ui,
"Fallback Output Mode",
|ui| {
tip(ui, |ui| {
ui.label(concat!(
"This determines the output to use in operations where no ",
"output is explicitly specified.",
));
ui.label(concat!(
"For example, when a new window is opened, this determines ",
"where the window will be opened.",
));
ui.label("`Cursor` refers to the output that contains the cursor.");
ui.label(
"`Focus` refers to the output that has the keyboard focus.",
);
});
},
seat.fallback_output_mode(),
|v| seat.set_fallback_output_mode(v),
);
});
ui.label("Focus History");
ui.indent("focus-history", |ui| {
let mut v = seat.focus_history_visible();
if ui.checkbox(&mut v, "Only Visible").changed() {
seat.focus_history_set_visible(v);
}
let mut v = seat.focus_history_same_workspace();
if ui.checkbox(&mut v, "Same Workspace").changed() {
seat.focus_history_set_same_workspace(v);
}
});
if ui.button("Reload Simple IM").clicked() {
seat.reload_simple_im();
}
show_keymap(
&self.state,
ps,
&mut self.paste_requested,
ks,
ui,
Some(&seat.keymap()),
|m| seat.set_seat_keymap(m),
);
});
}
fn show_device(
&mut self,
ps: &mut PaneState,
ui: &mut Ui,
dev: &Rc<DeviceHandlerData>,
seats: &[&Rc<WlSeatGlobal>],
outputs: &[&Rc<WlOutputGlobal>],
) {
let mut layout_job = LayoutJob::default();
layout_job.append(
"Device",
0.0,
TextFormat {
color: ui.style().visuals.widgets.inactive.text_color(),
..Default::default()
},
);
layout_job.append(
&dev.device.name(),
10.0,
TextFormat {
color: ui.style().visuals.widgets.active.text_color(),
..Default::default()
},
);
let dev_id = dev.device.id();
CollapsingHeader::new(layout_job)
.id_salt(("device", dev_id))
.show(ui, |ui| {
grid(ui, ("device", dev_id), |ui| {
{
let old = dev.seat.get();
let ui = &mut *ui.row();
grid_label(ui, "Seat");
let mut seat = old.as_ref();
ui.horizontal(|ui| {
let mut cb = ComboBox::from_id_salt("seat");
if let Some(seat) = seat {
cb = cb.selected_text(seat.seat_name());
}
cb.show_ui(ui, |ui| {
for s in seats {
ui.selectable_value(&mut seat, Some(s), s.seat_name());
}
});
if ui.button("Detach").clicked() {
seat = None;
}
});
if seat != old.as_ref() {
dev.set_seat(&self.state, seat.cloned());
}
}
macro_rules! string {
($field:ident, $name:expr) => {
if let Some(v) = &dev.$field {
label(ui, $name, v);
}
};
}
string!(syspath, "Syspath");
string!(devnode, "Devnode");
{
let ui = &mut *ui.row();
grid_label(ui, "Capabilities");
let mut s = String::new();
for cap in InputDeviceCapability::variants() {
if dev.device.has_capability(cap) {
if s.is_not_empty() {
s.push_str(" | ");
}
s.push_str(cap.text());
}
}
ui.label(s);
}
if let Some(old) = dev.device.natural_scrolling_enabled() {
bool(ui, "Natural Scrolling", old, |v| {
dev.set_natural_scrolling_enabled(&self.state, v)
});
}
if dev.device.has_capability(InputDeviceCapability::Pointer) {
drag_value_ui(
ui,
"Scroll Distance (px)",
|ui| {
tip(ui, |ui| {
ui.label(concat!(
"This only applies to applications that use the ",
"legacy px scrolling events.",
));
});
},
dev.px_per_scroll_wheel.get(),
-f64::INFINITY..=f64::INFINITY,
0.1,
|v| dev.set_px_per_scroll_wheel(&self.state, v),
);
}
if let Some(old) = dev.device.accel_profile() {
combo_box(ui, "Accel Profile", old, |v| {
dev.set_accel_profile(&self.state, v)
});
}
if let Some(old) = dev.device.accel_speed() {
drag_value(ui, "Accel Speed", old, -1.0..=1.0, 0.01, |v| {
dev.set_accel_speed(&self.state, v)
});
}
if let Some(old) = dev.device.click_method() {
combo_box(ui, "Click Method", old, |v| {
dev.set_click_method(&self.state, v)
});
}
if let Some(old) = dev.device.tap_enabled() {
bool(ui, "Tap Enabled", old, |v| {
dev.set_tap_enabled(&self.state, v)
});
}
if let Some(old) = dev.device.drag_enabled() {
bool(ui, "Tap Drag Enabled", old, |v| {
dev.set_drag_enabled(&self.state, v)
});
}
if let Some(old) = dev.device.drag_lock_enabled() {
bool(ui, "Tap Drag Lock Enabled", old, |v| {
dev.set_drag_lock_enabled(&self.state, v)
});
}
if let Some(old) = dev.device.left_handed() {
bool(ui, "Left Handed", old, |v| {
dev.set_left_handed(&self.state, v)
});
}
if let Some(old) = dev.device.middle_button_emulation_enabled() {
bool(ui, "Middle Button Emulation", old, |v| {
dev.set_middle_button_emulation_enabled(&self.state, v)
});
}
{
let ui = &mut *ui.row();
grid_label_ui(ui, |ui| {
ui.label("Output");
tip(ui, |ui| {
ui.label("This applies to touch and tablet input.");
});
});
ui.horizontal(|ui| {
let old = dev.output.get().and_then(|v| v.global.get());
let old = old.as_ref();
let mut v = old;
let mut cb = ComboBox::from_id_salt("output");
if let Some(v) = v {
cb = cb.selected_text(&*v.connector.name);
}
cb.show_ui(ui, |ui| {
for &output in outputs {
ui.selectable_value(
&mut v,
Some(output),
&*output.connector.name,
);
}
});
if v != old {
dev.set_output(&self.state, v.map(|v| &**v));
}
if ui.button("Detach").clicked() {
dev.set_output(&self.state, None);
}
});
}
matrix_ui(
ui,
"Transform Matrix",
|ui| {
tip(ui, |ui| {
ui.label("This matrix is applied to relative pointer movements.");
});
},
dev.device
.has_capability(InputDeviceCapability::Pointer)
.then(|| {
dev.device
.transform_matrix()
.unwrap_or([[1.0, 0.0], [0.0, 1.0]])
}),
|v| dev.set_transform_matrix(&self.state, v),
);
matrix(
ui,
"Calibration Matrix",
dev.device.calibration_matrix(),
|v| dev.set_calibration_matrix(&self.state, v),
);
});
if dev.device.has_capability(InputDeviceCapability::Keyboard) {
ui.collapsing("Device Keymap", |ui| {
let ks = self.keymaps.entry(Key::Dev(dev_id)).or_default();
let map = dev.keymap.get();
ui.add_enabled_ui(map.is_some(), |ui| {
if ui.button("Use Seat Keymap").clicked() {
ks.backup(map.as_ref());
dev.set_keymap(&self.state, None);
}
});
show_keymap(
&self.state,
ps,
&mut self.paste_requested,
ks,
ui,
map.as_ref(),
|m| {
dev.set_keymap(&self.state, Some(m.clone()));
},
);
});
}
});
}
}
impl KeymapState {
fn backup(&mut self, map: Option<&Rc<KbvmMap>>) {
if self.backup.is_none()
&& let Some(map) = map
{
self.backup = Some(map.clone());
}
}
}
fn show_keymap(
state: &State,
ps: &mut PaneState,
paste_requested: &mut Option<Id>,
ks: &mut KeymapState,
ui: &mut Ui,
map: Option<&Rc<KbvmMap>>,
set_map: impl Fn(&Rc<KbvmMap>),
) {
ui.scope_builder(
UiBuilder::new().id(Id::new(("keymap-settings", ks.seed))),
|ui| {
ui.add_enabled_ui(map.is_some(), |ui| {
if ui.button("Copy Keymap").clicked()
&& let Some(map) = map
{
ui.copy_text(map.map_text.clone());
}
});
let backup = |ks: &mut KeymapState| {
ks.backup(map);
};
if ui.button("Load Default Keymap").clicked() {
backup(ks);
set_map(&state.default_keymap);
}
ui.horizontal(|ui| {
ui.add_enabled_ui(map.is_some(), |ui| {
if ui.button("Backup Keymap").clicked() {
ks.backup = None;
backup(ks);
}
});
if let Some(backup) = &ks.backup
&& ui.button("Restore Keymap").clicked()
{
set_map(backup);
ks.backup = None;
}
});
let mut label = "Load Keymap from Clipboard".to_string();
if *paste_requested == Some(ui.id()) {
label.push_str(" ");
label.push_str(ICON_PENDING);
}
let button = ui.button(label);
if button.clicked() {
*paste_requested = Some(ui.id());
button.request_focus();
ui.send_viewport_cmd(ViewportCommand::RequestPaste);
} else if *paste_requested == Some(ui.id()) && button.has_focus() {
ui.input(|e| {
let map = e
.events
.iter()
.filter_map(|e| match e {
Event::Paste(s) => Some(s),
_ => None,
})
.next();
let Some(map) = map else {
return;
};
*paste_requested = None;
let map = match state.kb_ctx.parse_keymap(map.as_bytes()) {
Ok(m) => m,
Err(e) => {
let error = format!("Could not parse keymap: {}", ErrorFmt(e));
ps.errors.push(error);
return;
}
};
backup(ks);
set_map(&map);
});
} else if *paste_requested == Some(ui.id()) {
*paste_requested = None;
}
ui.collapsing("Create Keymap from Names", |ui| {
grid(ui, ("keymap-from-names", ui.id()), |ui| {
let defaulted =
|ui: &mut Ui, name: &str, default: &mut bool, text: &mut dyn TextBuffer| {
let ui = &mut *ui.row();
grid_label(ui, name);
ui.add_enabled_ui(!*default, |ui| {
text_edit(ui, text);
});
ui.checkbox(default, "Default");
};
let required = |ui: &mut Ui, name, text| {
let ui = &mut *ui.row();
grid_label(ui, name);
text_edit(ui, text);
};
defaulted(ui, "Rules", &mut ks.rules_default, &mut ks.rules);
defaulted(ui, "Model", &mut ks.model_default, &mut ks.model);
required(ui, "Layouts", &mut ks.layouts);
required(ui, "Variants", &mut ks.variants);
required(ui, "Options", &mut ks.options);
});
if ui.button("Load").clicked() {
'set_map: {
let map = state.kb_ctx.keymap_from_rmlvo(
(!ks.rules_default).then_some(&ks.rules),
(!ks.model_default).then_some(&ks.model),
Some(&ks.layouts),
Some(&ks.variants),
Some(&ks.options),
);
let map = match map {
Ok(map) => map,
Err(e) => {
let error = format!("Could not parse keymap: {}", ErrorFmt(e));
ps.errors.push(error);
break 'set_map;
}
};
backup(ks);
set_map(&map);
}
}
});
},
);
}
fn matrix<T, const W: usize>(
ui: &mut Ui,
name: &str,
old: Option<[[T; W]; 2]>,
set: impl FnOnce([[T; W]; 2]),
) where
T: Numeric,
{
matrix_ui(ui, name, |_| (), old, set);
}
fn matrix_ui<R, T, const W: usize>(
ui: &mut Ui,
name: &str,
label: impl FnOnce(&mut Ui) -> R,
old: Option<[[T; W]; 2]>,
set: impl FnOnce([[T; W]; 2]),
) where
T: Numeric,
{
if let Some(mut m) = old {
let old = m;
let ui = &mut *ui.row();
grid_label_ui(ui, |ui| {
ui.label(name);
label(ui);
});
Grid::new(name).show(ui, |ui| {
for row in &mut m {
for cell in row {
DragValue::new(cell).speed(0.01).ui(ui);
}
ui.end_row();
}
});
if old != m {
set(m);
}
}
}

View file

@ -1,163 +0,0 @@
use {
crate::{
cmm::cmm_eotf::Eotf,
control_center::{
ControlCenterInner, bool, bool_ui, combo_box, drag_value, grid, grid_label, row,
text_edit, tip,
},
gfx_api::AlphaMode,
state::State,
theme::{Color, ThemeColor, ThemeSized},
utils::static_text::StaticText,
},
egui::Ui,
isnt::std_1::primitive::IsntStrExt,
linearize::LinearizeExt,
std::rc::Rc,
};
pub struct LookAndFeelPane {
state: Rc<State>,
}
impl ControlCenterInner {
pub fn create_look_and_feel_pane(self: &Rc<Self>) -> LookAndFeelPane {
LookAndFeelPane {
state: self.state.clone(),
}
}
}
impl LookAndFeelPane {
pub fn title(&self, res: &mut String) {
res.push_str("Look and Feel");
}
pub fn show(&mut self, ui: &mut Ui) {
let t = &self.state.theme;
grid(ui, "settings", |ui| {
bool(ui, "Show Bar", self.state.show_bar.get(), |v| {
self.state.set_show_bar(v)
});
combo_box(ui, "Bar Position", t.bar_position.get(), |p| {
self.state.set_bar_position(p);
});
bool(ui, "Show Titles", t.show_titles.get(), |v| {
self.state.set_show_titles(v)
});
bool_ui(
ui,
"Primary Selection",
|ui| {
tip(ui, |ui| {
ui.label("Requires applications to be restarted.");
});
},
self.state.enable_primary_selection.get(),
|v| self.state.set_primary_selection_enabled(v),
);
bool_ui(
ui,
"UI Drag",
|ui| {
tip(ui, |ui| {
ui.label("Allows dragging workspaces and tiled windows with the mouse.");
});
},
self.state.ui_drag_enabled.get(),
|v| self.state.set_ui_drag_enabled(v),
);
drag_value(
ui,
"UI Drag Threshold (px)",
self.state.ui_drag_threshold_squared.get().isqrt(),
1..=i32::MAX,
1.0,
|v| {
self.state.set_ui_drag_threshold(v);
},
);
bool_ui(
ui,
"Float Pin Icon",
|ui| {
tip(ui, |ui| {
ui.label("Show the pin icon even if the window is not pinned.");
ui.label("Pinned floating windows are shown on all workspaces.");
});
},
self.state.show_pin_icon.get(),
|v| self.state.set_show_pin_icon(v),
);
bool_ui(
ui,
"Float Above Fullscreen",
|ui| {
tip(ui, |ui| {
ui.label("Show floating windows above fullscreen windows.");
});
},
self.state.float_above_fullscreen.get(),
|v| self.state.set_float_above_fullscreen(v),
);
row(ui, "Font", |ui| {
let mut v = self.state.theme.font.get().to_string();
if text_edit(ui, &mut v).changed() {
self.state.set_font(&v);
}
});
row(ui, "Title Font", |ui| {
let mut v = t
.title_font
.get()
.map(|v| v.to_string())
.unwrap_or_default();
if text_edit(ui, &mut v).changed() {
self.state.set_title_font(v.is_not_empty().then_some(&v));
}
});
row(ui, "Bar Font", |ui| {
let mut v = t.bar_font.get().map(|v| v.to_string()).unwrap_or_default();
if text_edit(ui, &mut v).changed() {
self.state.set_bar_font(v.is_not_empty().then_some(&v));
}
});
});
if ui.button("Reset Sizes").clicked() {
self.state.reset_sizes();
}
if ui.button("Reset Colors").clicked() {
self.state.reset_colors();
}
if ui.button("Reset Fonts").clicked() {
self.state.reset_fonts();
}
ui.collapsing("Sizes", |ui| {
grid(ui, "Sizes", |ui| {
for v in ThemeSized::variants() {
let f = v.field(&self.state.theme);
drag_value(ui, v.text(), f.get(), v.min()..=v.max(), 1.0, |i| {
self.state.set_size(v, i);
});
}
});
});
ui.collapsing("Colors", |ui| {
grid(ui, "Colors", |ui| {
for tc in ThemeColor::variants() {
let f = tc.field(t);
let mut v = f.get().to_array(Eotf::Linear);
grid_label(ui, tc.text());
let changed = ui.color_edit_button_rgba_premultiplied(&mut v).changed();
ui.end_row();
if changed {
let [r, g, b, a] = v;
let c =
Color::new(Eotf::Linear, AlphaMode::PremultipliedOptical, r, g, b, a);
self.state.set_color(tc, c);
}
}
});
});
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,97 +0,0 @@
use {
crate::control_center::{ControlCenterInner, Pane, PaneType},
egui::{Align, Layout, ScrollArea, Ui, ViewportCommand},
egui_tiles::Tree,
linearize::{Linearize, LinearizeExt},
std::{rc::Rc, sync::LazyLock},
};
#[derive(Copy, Clone, Linearize)]
enum PaneName {
Compositor,
Idle,
ColorManagement,
Xwayland,
Outputs,
GPUs,
Input,
LookAndFeel,
Clients,
WindowSearch,
VirtualOutputs,
}
impl PaneName {
fn name(self) -> &'static str {
match self {
PaneName::Compositor => "Compositor",
PaneName::Idle => "Idle",
PaneName::ColorManagement => "Color Management",
PaneName::Xwayland => "Xwayland",
PaneName::Outputs => "Outputs",
PaneName::GPUs => "GPUs",
PaneName::Input => "Input",
PaneName::LookAndFeel => "Look and Feel",
PaneName::Clients => "Clients",
PaneName::WindowSearch => "Window Search",
PaneName::VirtualOutputs => "Virtual Outputs",
}
}
}
static TYPES: LazyLock<Vec<PaneName>> = LazyLock::new(|| {
let mut res: Vec<_> = PaneName::variants().collect();
res.sort_by_key(|t| t.name());
res
});
impl ControlCenterInner {
pub fn show_sidebar(self: &Rc<Self>, tree: &mut Tree<Pane>, ui: &mut Ui) {
ui.with_layout(
Layout::top_down(Align::Center).with_cross_justify(true),
|ui| {
ui.add_space(6.0);
if ui.button("Close").clicked() {
ui.send_viewport_cmd(ViewportCommand::Close);
}
ui.separator();
ScrollArea::vertical().show(ui, |ui| {
for &ty in &*TYPES {
if ui.button(ty.name()).clicked() {
let ty = match ty {
PaneName::Compositor => {
PaneType::Compositor(self.create_compositor_pane())
}
PaneName::Idle => PaneType::Idle(self.create_idle_pane()),
PaneName::ColorManagement => {
PaneType::ColorManagement(self.create_color_management_pane())
}
PaneName::Xwayland => {
PaneType::Xwayland(self.create_xwayland_pane())
}
PaneName::Outputs => {
PaneType::Outputs(Box::new(self.create_outputs_pane()))
}
PaneName::GPUs => PaneType::GPUs(self.create_gpus_pane()),
PaneName::Input => PaneType::Input(self.create_input_pane()),
PaneName::LookAndFeel => {
PaneType::LookAndFeel(self.create_look_and_feel_pane())
}
PaneName::Clients => PaneType::Clients(self.create_clients_pane()),
PaneName::WindowSearch => {
PaneType::WindowSearch(self.create_window_search_pane())
}
PaneName::VirtualOutputs => {
PaneType::VirtualOutputs(self.create_virtual_outputs_pane())
}
};
self.open(tree, ty);
ui.request_repaint();
}
}
ui.add_space(3.0);
})
},
);
}
}

View file

@ -1,49 +0,0 @@
use {
crate::{
control_center::ControlCenterInner, egui_adapter::egui_platform::icons::ICON_CLOSE,
state::State,
},
egui::Ui,
std::rc::Rc,
};
pub struct VirtualOutputsPane {
state: Rc<State>,
new: String,
}
impl ControlCenterInner {
pub fn create_virtual_outputs_pane(self: &Rc<Self>) -> VirtualOutputsPane {
VirtualOutputsPane {
state: self.state.clone(),
new: Default::default(),
}
}
}
impl VirtualOutputsPane {
pub fn title(&self, res: &mut String) {
res.push_str("Virtual Outputs");
}
pub fn show(&mut self, ui: &mut Ui) {
let s = &self.state;
let mut outputs: Vec<_> = s.virtual_outputs.outputs.lock().keys().cloned().collect();
outputs.sort();
for o in &outputs {
ui.horizontal(|ui| {
if ui.button(ICON_CLOSE).clicked() {
s.virtual_outputs.remove_output(s, o);
}
ui.label(o);
});
}
ui.horizontal(|ui| {
ui.text_edit_singleline(&mut self.new);
if ui.button("Add").clicked() {
s.virtual_outputs.get_or_create(s, &self.new);
ui.request_repaint();
}
});
}
}

View file

@ -1,475 +0,0 @@
use {
crate::{
control_center::{
CcBehavior, ControlCenterInner, PaneType,
cc_clients::{ClientCrit, show_client_collapsible},
cc_criterion::{CcCriterion, CritImpl, CritRegex},
grid, icon_label, label, read_only_bool,
},
criteria::{CritMgrExt, CritUpstreamNode, crit_leaf::CritLeafMatcher},
egui_adapter::egui_platform::icons::ICON_OPEN_IN_NEW,
state::State,
tree::{NodeId, ToplevelData, ToplevelIdentifier, ToplevelNode, ToplevelType},
utils::{
copyhashmap::CopyHashMap,
event_listener::{EventListener, LazyEventSourceListener},
static_text::StaticText,
},
},
ahash::AHashMap,
egui::{CollapsingHeader, Sense, TextFormat, Ui, Widget, cache::CacheTrait, text::LayoutJob},
isnt::std_1::primitive::IsntStrExt,
jay_config::window::{
ContentType, GAME_CONTENT, NO_CONTENT_TYPE, PHOTO_CONTENT, VIDEO_CONTENT,
},
linearize::Linearize,
std::{
mem,
rc::{Rc, Weak},
},
};
enum WindowClit {
Client(CcCriterion<ClientCrit>),
Title(CritRegex),
AppId(CritRegex),
Floating,
Visible,
Urgent,
Fullscreen,
Tag(CritRegex),
XClass(CritRegex),
XInstance(CritRegex),
XRole(CritRegex),
Workspace(CritRegex),
ContentTypes(ContentType),
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Linearize)]
enum WindowCritTy {
Client,
Title,
AppId,
Floating,
Visible,
Urgent,
Fullscreen,
Tag,
XClass,
XInstance,
XRole,
Workspace,
ContentTypes,
}
impl Default for WindowClit {
fn default() -> Self {
WindowClit::Title(Default::default())
}
}
impl StaticText for WindowCritTy {
fn text(&self) -> &'static str {
match self {
WindowCritTy::Client => "Client",
WindowCritTy::Title => "Title",
WindowCritTy::AppId => "App ID",
WindowCritTy::Floating => "Floating",
WindowCritTy::Visible => "Visible",
WindowCritTy::Urgent => "Urgent",
WindowCritTy::Fullscreen => "Fullscreen",
WindowCritTy::Tag => "Tag",
WindowCritTy::XClass => "X Class",
WindowCritTy::XInstance => "X Instance",
WindowCritTy::XRole => "X Role",
WindowCritTy::Workspace => "Workspace",
WindowCritTy::ContentTypes => "Content Types",
}
}
}
impl CritImpl for WindowClit {
type Type = WindowCritTy;
type Target = ToplevelData;
fn ty(&self) -> Self::Type {
macro_rules! map {
($($n:ident,)*) => {
match self {
$(
Self::$n { .. } => WindowCritTy::$n,
)*
}
};
}
map! {
Client,
Title,
AppId,
Floating,
Visible,
Urgent,
Fullscreen,
Tag,
XClass,
XInstance,
XRole,
Workspace,
ContentTypes,
}
}
fn from_ty(ty: Self::Type) -> Self {
match ty {
WindowCritTy::Client => Self::Client(Default::default()),
WindowCritTy::Title => Self::Title(Default::default()),
WindowCritTy::AppId => Self::AppId(Default::default()),
WindowCritTy::Floating => Self::Floating,
WindowCritTy::Visible => Self::Visible,
WindowCritTy::Urgent => Self::Urgent,
WindowCritTy::Fullscreen => Self::Fullscreen,
WindowCritTy::Tag => Self::Tag(Default::default()),
WindowCritTy::XClass => Self::XClass(Default::default()),
WindowCritTy::XInstance => Self::XInstance(Default::default()),
WindowCritTy::XRole => Self::XRole(Default::default()),
WindowCritTy::Workspace => Self::Workspace(Default::default()),
WindowCritTy::ContentTypes => {
Self::ContentTypes(PHOTO_CONTENT | VIDEO_CONTENT | GAME_CONTENT)
}
}
}
fn show(&mut self, ui: &mut Ui) -> bool {
match self {
WindowClit::Client(v) => v.show(ui),
WindowClit::Title(v) => v.show(ui),
WindowClit::AppId(v) => v.show(ui),
WindowClit::Floating => false,
WindowClit::Visible => false,
WindowClit::Urgent => false,
WindowClit::Fullscreen => false,
WindowClit::Tag(v) => v.show(ui),
WindowClit::XClass(v) => v.show(ui),
WindowClit::XInstance(v) => v.show(ui),
WindowClit::XRole(v) => v.show(ui),
WindowClit::Workspace(v) => v.show(ui),
WindowClit::ContentTypes(v) => show_content_types(ui, v),
}
}
fn to_crit(&self, state: &Rc<State>) -> Option<Rc<dyn CritUpstreamNode<Self::Target>>> {
let m = &state.tl_matcher_manager;
let res = match self {
WindowClit::Client(v) => m.client(state, &v.to_crit(state)?),
WindowClit::Title(v) => m.title(v.to_crit()?),
WindowClit::AppId(v) => m.app_id(v.to_crit()?),
WindowClit::Floating => m.floating(),
WindowClit::Visible => m.visible(),
WindowClit::Urgent => m.urgent(),
WindowClit::Fullscreen => m.fullscreen(),
WindowClit::Tag(v) => m.tag(v.to_crit()?),
WindowClit::XClass(v) => m.class(v.to_crit()?),
WindowClit::XInstance(v) => m.instance(v.to_crit()?),
WindowClit::XRole(v) => m.role(v.to_crit()?),
WindowClit::Workspace(v) => m.workspace(v.to_crit()?),
WindowClit::ContentTypes(v) => m.content_type(*v),
};
Some(res)
}
fn not(
state: &State,
upstream: &Rc<dyn CritUpstreamNode<Self::Target>>,
) -> Rc<dyn CritUpstreamNode<Self::Target>> {
state.tl_matcher_manager.not(upstream)
}
fn list(
state: &State,
upstream: &[Rc<dyn CritUpstreamNode<Self::Target>>],
all: bool,
) -> Rc<dyn CritUpstreamNode<Self::Target>> {
state.tl_matcher_manager.list(upstream, all)
}
fn exactly(
state: &State,
n: usize,
upstream: &[Rc<dyn CritUpstreamNode<Self::Target>>],
) -> Rc<dyn CritUpstreamNode<Self::Target>> {
state.tl_matcher_manager.exactly(upstream, n)
}
}
pub struct WindowSearchPane {
state: Rc<State>,
criterion: CcCriterion<WindowClit>,
matched: Rc<Matched>,
leaf: Option<Rc<CritLeafMatcher<ToplevelData>>>,
}
struct Matched {
slf: Weak<ControlCenterInner>,
windows: CopyHashMap<ToplevelIdentifier, ()>,
}
impl Matched {
fn request_frame(&self) {
if let Some(slf) = self.slf.upgrade() {
slf.window.request_redraw();
}
}
}
impl ControlCenterInner {
pub fn create_window_search_pane(self: &Rc<Self>) -> WindowSearchPane {
let mut pane = WindowSearchPane {
state: self.state.clone(),
criterion: Default::default(),
matched: Rc::new(Matched {
slf: Rc::downgrade(self),
windows: Default::default(),
}),
leaf: Default::default(),
};
pane.update_matcher();
pane
}
}
impl WindowSearchPane {
pub fn title(&self, res: &mut String) {
res.push_str("Window Search");
}
pub fn show(&mut self, behavior: &mut CcBehavior<'_>, ui: &mut Ui) {
let mut clear = false;
if self.criterion.show(ui) {
clear = self.update_matcher();
}
ui.separator();
let mut windows: Vec<_> = self.matched.windows.lock().keys().copied().collect();
windows.sort();
for id in windows {
let Some(window) = self.state.toplevels.get(&id).and_then(|v| v.upgrade()) else {
continue;
};
show_window_collapsible(behavior, ui, &window);
}
if clear {
self.matched.windows.clear();
}
}
fn update_matcher(&mut self) -> bool {
let mut clear = false;
let state = &self.state;
if let Some(new) = self.criterion.to_crit(state) {
clear = true;
let matched = self.matched.clone();
let leaf = state.tl_matcher_manager.leaf(&new, move |data| {
matched.windows.set(data, ());
matched.request_frame();
Box::new({
let matched = matched.clone();
move || {
matched.windows.remove(&data);
matched.request_frame();
}
})
});
state.tl_matcher_manager.rematch_all(state);
if self.criterion.any(|c| matches!(c, WindowClit::Client(_))) {
state.cl_matcher_manager.rematch_all(state);
}
self.leaf = Some(leaf);
}
clear
}
}
pub struct WindowPane {
window: Rc<dyn ToplevelNode>,
}
impl ControlCenterInner {
pub fn create_window_pane(self: &Rc<Self>, window: &Rc<dyn ToplevelNode>) -> WindowPane {
WindowPane {
window: window.clone(),
}
}
}
impl WindowPane {
pub fn title(&self, res: &mut String) {
res.push_str("Window");
}
pub fn show(&mut self, behavior: &mut CcBehavior<'_>, ui: &mut Ui) {
show_window(behavior, ui, &*self.window)
}
}
pub fn show_window_collapsible(
behavior: &mut CcBehavior,
ui: &mut Ui,
window: &Rc<dyn ToplevelNode>,
) {
let data = window.tl_data();
let mut layout_job = LayoutJob::default();
layout_job.append(
"Window",
0.0,
TextFormat {
color: ui.style().visuals.widgets.inactive.text_color(),
..Default::default()
},
);
layout_job.append(
&data.title.borrow(),
10.0,
TextFormat {
color: ui.style().visuals.widgets.active.text_color(),
..Default::default()
},
);
let closed = CollapsingHeader::new(layout_job)
.id_salt(("window", data.identifier.get()))
.show(ui, |ui| {
if icon_label(ICON_OPEN_IN_NEW)
.sense(Sense::CLICK)
.ui(ui)
.clicked()
{
behavior.open = Some(PaneType::Window(behavior.cc.create_window_pane(window)));
}
show_window(behavior, ui, &**window)
})
.fully_closed();
if closed {
ensure_listener(ui, behavior, data);
}
}
pub fn show_window(behavior: &mut CcBehavior<'_>, ui: &mut Ui, window: &dyn ToplevelNode) {
let data = window.tl_data();
ensure_listener(ui, behavior, data);
grid(ui, ("window", data.identifier.get()), |ui| {
label(ui, "ID", &*data.identifier.get().to_string());
label(ui, "Title", &*data.title.borrow());
if let Some(w) = data.workspace.get() {
label(ui, "Workspace", &w.name);
}
match &data.kind {
ToplevelType::Container => {
label(ui, "Type", "Container");
}
ToplevelType::Placeholder(_) => {
label(ui, "Type", "Placeholder");
}
ToplevelType::XdgToplevel(t) => {
label(ui, "Type", "xdg_toplevel");
let tag = &*t.tag.borrow();
if tag.is_not_empty() {
label(ui, "Tag", tag);
}
}
ToplevelType::XWindow(t) => {
label(ui, "Type", "X Window");
if let Some(class) = &*t.info.class.borrow() {
label(ui, "Class", class);
}
if let Some(instance) = &*t.info.instance.borrow() {
label(ui, "Instance", instance);
}
if let Some(role) = &*t.info.role.borrow() {
label(ui, "Role", role);
}
}
}
let app_id = &*data.app_id.borrow();
if app_id.is_not_empty() {
label(ui, "App ID", app_id);
}
read_only_bool(ui, "Floating", data.parent_is_float.get());
read_only_bool(ui, "Visible", data.visible.get());
read_only_bool(ui, "Urgent", data.wants_attention.get());
read_only_bool(ui, "Fullscreen", data.is_fullscreen.get());
if let Some(ct) = data.content_type.get() {
label(ui, "Content Type", ct.text());
}
});
if let Some(client) = &data.client {
show_client_collapsible(behavior, ui, client);
}
}
fn ensure_listener(ui: &mut Ui, behavior: &CcBehavior<'_>, data: &ToplevelData) {
ui.memory_mut(|m| {
m.caches
.cache::<WindowPropertyListeners>()
.ensure(behavior.cc, data);
});
}
#[derive(Default)]
struct WindowPropertyListeners {
generation: u64,
listeners: AHashMap<NodeId, WindowPropertyListener>,
}
struct WindowPropertyListener {
_listener: EventListener<dyn LazyEventSourceListener>,
generation: u64,
}
impl WindowPropertyListeners {
fn ensure(&mut self, cc: &Rc<ControlCenterInner>, data: &ToplevelData) {
let listener = self.listeners.entry(data.node_id).or_insert_with(|| {
let listener =
EventListener::new(Rc::downgrade(cc) as Weak<dyn LazyEventSourceListener>);
listener.attach(data.property_changed_source());
WindowPropertyListener {
_listener: listener,
generation: 0,
}
});
listener.generation = self.generation;
}
}
unsafe impl Sync for WindowPropertyListeners {}
unsafe impl Send for WindowPropertyListeners {}
impl CacheTrait for WindowPropertyListeners {
fn update(&mut self) {
self.listeners
.retain(|_, m| m.generation == self.generation);
self.generation += 1;
}
fn len(&self) -> usize {
self.listeners.len()
}
}
fn show_content_types(ui: &mut Ui, ct: &mut ContentType) -> bool {
let mut v = *ct;
let mut photo = (v & PHOTO_CONTENT).0 != 0;
let mut video = (v & VIDEO_CONTENT).0 != 0;
let mut game = (v & GAME_CONTENT).0 != 0;
ui.checkbox(&mut photo, "Photo");
ui.checkbox(&mut video, "Video");
ui.checkbox(&mut game, "Game");
v = NO_CONTENT_TYPE;
if photo {
v |= PHOTO_CONTENT;
}
if video {
v |= VIDEO_CONTENT;
}
if game {
v |= GAME_CONTENT;
}
mem::replace(ct, v) != v
}

View file

@ -1,91 +0,0 @@
use {
crate::{
compositor::DISPLAY,
control_center::{
CcBehavior, ControlCenterInner, bool, cc_clients::show_client_collapsible,
combo_box_ui, grid, label, read_only_bool, tip,
},
state::State,
utils::{errorfmt::ErrorFmt, oserror::OsErrorExt, static_text::StaticText},
},
egui::Ui,
linearize::Linearize,
std::rc::Rc,
uapi::c,
};
pub struct XwaylandPane {
state: Rc<State>,
}
impl ControlCenterInner {
pub fn create_xwayland_pane(self: &Rc<Self>) -> XwaylandPane {
XwaylandPane {
state: self.state.clone(),
}
}
}
#[derive(Copy, Clone, PartialEq, Linearize)]
enum ScalingMode {
Default,
Downscaled,
}
impl StaticText for ScalingMode {
fn text(&self) -> &'static str {
match self {
ScalingMode::Default => "default",
ScalingMode::Downscaled => "downscaled",
}
}
}
impl XwaylandPane {
pub fn title(&self, res: &mut String) {
res.push_str("Xwayland");
}
pub fn show(&mut self, behavior: &mut CcBehavior<'_>, ui: &mut Ui) {
let s = &self.state;
grid(ui, "settings", |ui| {
bool(ui, "Enabled", s.xwayland.enabled.get(), |b| {
s.set_xwayland_enabled(b)
});
let mode = match self.state.xwayland.use_wire_scale.get() {
true => ScalingMode::Downscaled,
false => ScalingMode::Default,
};
combo_box_ui(
ui,
"Scaling Mode",
|ui| {
tip(ui, |ui| {
ui.label(r#"`downscaled` is known as "X applications scale themselves""#);
});
},
mode,
|v| {
self.state
.set_xwayland_use_wire_scale(v == ScalingMode::Downscaled);
},
);
if let Some(display) = self.state.xwayland.display.get() {
label(ui, DISPLAY, &*display);
}
read_only_bool(ui, "Running", self.state.xwayland.running.get());
if let Some(client) = self.state.xwayland.client.get() {
label(ui, "PID", client.pid_info.pid.to_string());
}
});
if let Some(client) = self.state.xwayland.client.get()
&& ui.button("Kill").clicked()
&& let Err(e) = uapi::kill(client.pid_info.pid, c::SIGTERM).to_os_error()
{
log::error!("Could not kill Xwayland: {}", ErrorFmt(e));
}
if let Some(client) = self.state.xwayland.client.get() {
show_client_collapsible(behavior, ui, &client);
}
}
}

View file

@ -234,9 +234,6 @@ impl ClMatcherManager {
self.root(ClmMatchTag::new(string))
}
pub fn id(&self, id: ClientId) -> Rc<ClmUpstreamNode> {
self.root(ClmMatchId(id))
}
}
impl CritTarget for Rc<Client> {

View file

@ -1,7 +1,6 @@
use {
crate::{
backend::HardwareCursorUpdate,
control_center::CCI_INPUT,
cursor::{Cursor, DEFAULT_CURSOR_SIZE, KnownCursor},
fixed::Fixed,
gfx_api::{AcquireSync, ReleaseSync},
@ -184,7 +183,6 @@ impl CursorUserGroup {
self.remove_hardware_cursor();
self.state.cursor_user_group_hardware_cursor.take();
}
self.state.trigger_cci(CCI_INPUT);
}
pub fn hardware_cursor(&self) -> bool {
@ -197,13 +195,9 @@ impl CursorUserGroup {
self.state.remove_cursor_size(old);
self.state.add_cursor_size(size);
self.reload_known_cursor();
self.state.trigger_cci(CCI_INPUT);
}
}
pub fn cursor_size(&self) -> u32 {
self.size.get()
}
fn output_center(&self, output: &Rc<OutputNode>) -> (Fixed, Fixed) {
let pos = output.global.pos.get();

View file

@ -1,3 +0,0 @@
pub mod egui_oklch;
pub mod egui_platform;
mod egui_vulkan;

View file

@ -1,36 +0,0 @@
use {
crate::{
cmm::cmm_eotf::Eotf,
theme::{Color, Oklab, Oklch},
},
egui::{Color32, Rgba},
};
pub trait Color32Ext {
fn to_oklab(self) -> Oklab;
fn to_oklch(self) -> Oklch;
}
impl Color32Ext for Color32 {
fn to_oklab(self) -> Oklab {
let [r, g, b, a] = self.to_array();
Color::from_srgba_premultiplied(r, g, b, a).srgb_to_oklab()
}
fn to_oklch(self) -> Oklch {
self.to_oklab().to_oklch()
}
}
impl Into<Color32> for Oklch {
fn into(self) -> Color32 {
self.to_oklab().into()
}
}
impl Into<Color32> for Oklab {
fn into(self) -> Color32 {
let [r, g, b, a] = self.to_srgb().to_array(Eotf::Linear);
Rgba::from_rgba_premultiplied(r, g, b, a).into()
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Binary file not shown.

View file

@ -1,13 +0,0 @@
#version 450
layout(location = 0) in vec4 color;
layout(location = 1) in vec2 pos;
layout(binding = 0, set = 0) uniform sampler2D tex;
layout(location = 0) out vec4 res;
void main() {
vec4 src = texture(tex, pos);
res = color * src;
}

View file

@ -1,19 +0,0 @@
#version 450
layout(location = 0) in vec2 if_pos;
layout(location = 1) in vec2 it_pos;
layout(location = 2) in vec4 i_color;
layout(location = 0) out vec4 o_color;
layout(location = 1) out vec2 ot_pos;
void main() {
o_color = i_color;
o_color.rgb = mix(
o_color.rgb / vec3(12.92),
pow((o_color.rgb + vec3(0.055)) / vec3(1.055), vec3(2.4)),
greaterThan(o_color.rgb, vec3(0.04045))
);
ot_pos = it_pos;
gl_Position = vec4(if_pos.x, if_pos.y, 0.0, 1.0);
}

View file

@ -1,2 +0,0 @@
7eb8fae39ae513bc4f6973c12227aa4aa43734bdf34c90e1b3b69294ad98db87 src/egui_adapter/shaders/shader.frag
501f4d0c5c5f10a371659b89f12d87abb03e5b57a31dbae5f3c6ca5726e4db01 src/egui_adapter/shaders/shader.vert

View file

@ -1,136 +0,0 @@
use {
crate::fontconfig::consts::{FC_MATCH_PATTERN, FC_RESULT_MATCH},
run_on_drop::on_drop,
std::{
borrow::Cow,
ffi::{CStr, OsStr, c_char},
os::{
raw::{c_int, c_uchar},
unix::ffi::OsStrExt,
},
path::PathBuf,
ptr,
},
thiserror::Error,
uapi::IntoUstr,
};
mod consts;
include!(concat!(env!("OUT_DIR"), "/fontconfig_tys.rs"));
#[derive(Debug, Error)]
pub enum FontconfigError {
#[error("FcConfigGetCurrent returned NULL")]
Init,
#[error("Could not create a pattern")]
CreatePattern,
#[error("Could not find a match")]
NoMatch,
#[error("Match has no name")]
NoName,
#[error("Match has no file")]
NoFile,
}
#[derive(Debug)]
pub struct Font {
pub fullname: String,
pub file: PathBuf,
pub index: Option<i32>,
}
pub fn match_font(family: &str) -> Result<Font, FontconfigError> {
thread_local! {
static CONFIG: *mut FcConfig = FcConfigGetCurrent();
}
let config = CONFIG.with(|c| *c);
if config.is_null() {
return Err(FontconfigError::Init);
}
let family = family.into_ustr();
let p = FcPatternCreate();
if p.is_null() {
return Err(FontconfigError::CreatePattern);
}
let _destroy_pattern = on_drop(|| unsafe { FcPatternDestroy(p) });
let mut result = 0;
let p = unsafe {
FcPatternAddString(p, FC_FAMILY.as_ptr(), family.as_ptr() as _);
FcConfigSubstitute(config, p, FC_MATCH_PATTERN.0 as _);
FcDefaultSubstitute(p);
FcFontMatch(config, p, &mut result)
};
if p.is_null() {
return Err(FontconfigError::NoMatch);
}
let _destroy_pattern = on_drop(|| unsafe { FcPatternDestroy(p) });
if result != FC_RESULT_MATCH.0 as FcResult {
return Err(FontconfigError::NoMatch);
}
let get_cstr = |name: &CStr| {
let mut out = ptr::null_mut();
let res = unsafe { FcPatternGetString(p, name.as_ptr(), 0, &mut out) };
if res != FC_RESULT_MATCH.0 as FcResult || out.is_null() {
return None;
}
let cstr = unsafe { CStr::from_ptr(out.cast()) };
Some(cstr)
};
let get_int = |name: &CStr| {
let mut out = 0;
let res = unsafe { FcPatternGetInteger(p, name.as_ptr(), 0, &mut out) };
if res != FC_RESULT_MATCH.0 as FcResult {
return None;
}
Some(out as i32)
};
Ok(Font {
fullname: get_cstr(FC_FULLNAME)
.map(CStr::to_string_lossy)
.map(Cow::into_owned)
.ok_or(FontconfigError::NoName)?,
file: get_cstr(FC_FILE)
.map(CStr::to_bytes)
.map(OsStr::from_bytes)
.map(Into::into)
.ok_or(FontconfigError::NoFile)?,
index: get_int(FC_INDEX),
})
}
type FcBool = c_int;
type FcPattern = u8;
type FcConfig = u8;
type FcChar8 = c_uchar;
const FC_FAMILY: &CStr = c"family";
const FC_FULLNAME: &CStr = c"fullname";
const FC_FILE: &CStr = c"file";
const FC_INDEX: &CStr = c"index";
#[link(name = "fontconfig")]
unsafe extern "C" {
safe fn FcConfigGetCurrent() -> *mut FcConfig;
safe fn FcPatternCreate() -> *mut FcPattern;
fn FcPatternDestroy(p: *mut FcPattern);
fn FcPatternAddString(p: *mut FcPattern, object: *const c_char, s: *const FcChar8) -> FcBool;
fn FcConfigSubstitute(config: *mut FcConfig, p: *mut FcPattern, kind: FcMatchKind) -> FcBool;
fn FcDefaultSubstitute(p: *mut FcPattern);
fn FcFontMatch(
config: *mut FcConfig,
p: *mut FcPattern,
result: *mut FcResult,
) -> *mut FcPattern;
fn FcPatternGetString(
p: *mut FcPattern,
object: *const c_char,
id: c_int,
s: *mut *mut FcChar8,
) -> FcResult;
fn FcPatternGetInteger(
p: *mut FcPattern,
object: *const c_char,
id: c_int,
i: *mut c_int,
) -> FcResult;
}

View file

@ -21,7 +21,6 @@ pub mod jay_ei_session_builder;
pub mod jay_idle;
pub mod jay_input;
pub mod jay_log_file;
pub mod jay_open_control_center_request;
pub mod jay_output;
pub mod jay_pointer;
pub mod jay_popup_ext_manager_v1;

View file

@ -21,7 +21,7 @@ use {
wire::JayHeadManagerSessionV1Id,
},
std::{
cell::{Cell, Ref, RefCell},
cell::{Cell, RefCell},
collections::hash_map::Entry,
rc::Rc,
},
@ -103,16 +103,6 @@ pub struct HeadState {
pub persistent_state: Option<RcEq<PersistentOutputState>>,
}
pub struct ReadOnlyHeadState {
state: Rc<RefCell<HeadState>>,
}
impl ReadOnlyHeadState {
pub fn borrow(&self) -> Ref<'_, HeadState> {
self.state.borrow()
}
}
impl HeadState {
pub fn update_in_compositor_space(&mut self, state: &State, wl_output: Option<GlobalName>) {
self.in_compositor_space = false;
@ -269,12 +259,6 @@ impl HeadManagers {
}
}
pub fn state(&self) -> ReadOnlyHeadState {
ReadOnlyHeadState {
state: self.state.clone(),
}
}
pub fn handle_removed(&self) {
for head in self.managers.lock().drain_values() {
skip_in_transaction!(head);

View file

@ -11,7 +11,6 @@ use {
jay_idle::JayIdle,
jay_input::JayInput,
jay_log_file::JayLogFile,
jay_open_control_center_request::JayOpenControlCenterRequest,
jay_output::JayOutput,
jay_pointer::JayPointer,
jay_randr::JayRandr,
@ -544,24 +543,6 @@ impl JayCompositorRequestHandler for JayCompositor {
Ok(())
}
fn open_control_center(
&self,
req: OpenControlCenter,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
let obj = Rc::new(JayOpenControlCenterRequest {
id: req.id,
client: self.client.clone(),
tracker: Default::default(),
version: self.version,
});
track!(self.client, obj);
self.client.add_client_obj(&obj)?;
if let Err(e) = self.client.state.open_control_center() {
obj.send_failed(e);
}
Ok(())
}
}
object_base! {

View file

@ -1,53 +0,0 @@
use {
crate::{
client::{Client, ClientError},
leaks::Tracker,
object::{Object, Version},
utils::errorfmt::ErrorFmt,
wire::{JayOpenControlCenterRequestId, jay_open_control_center_request::*},
},
std::{error::Error, rc::Rc},
thiserror::Error,
};
pub struct JayOpenControlCenterRequest {
pub id: JayOpenControlCenterRequestId,
pub client: Rc<Client>,
pub tracker: Tracker<Self>,
pub version: Version,
}
impl JayOpenControlCenterRequest {
pub fn send_failed(&self, err: impl Error) {
let msg = &ErrorFmt(err).to_string();
self.client.event(Failed {
self_id: self.id,
msg,
});
}
}
impl JayOpenControlCenterRequestRequestHandler for JayOpenControlCenterRequest {
type Error = JayOpenControlCenterRequestError;
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.client.remove_obj(self)?;
Ok(())
}
}
object_base! {
self = JayOpenControlCenterRequest;
version = self.version;
}
impl Object for JayOpenControlCenterRequest {}
simple_add_obj!(JayOpenControlCenterRequest);
#[derive(Debug, Error)]
pub enum JayOpenControlCenterRequestError {
#[error(transparent)]
ClientError(Box<ClientError>),
}
efrom!(JayOpenControlCenterRequestError, ClientError);

View file

@ -28,7 +28,6 @@ use {
ButtonState, InputDeviceAccelProfile, InputDeviceClickMethod, Leds, TransformMatrix,
},
client::{Client, ClientError, ClientId},
control_center::CCI_INPUT,
cursor_user::{CursorUser, CursorUserGroup, CursorUserOwner},
ei::ei_ifs::ei_seat::EiSeat,
fixed::Fixed,
@ -141,9 +140,6 @@ const MISSING_CAPABILITY: u32 = 0;
pub const BTN_LEFT: u32 = 0x110;
pub const BTN_RIGHT: u32 = 0x111;
pub const BTN_MIDDLE: u32 = 0x112;
pub const BTN_SIDE: u32 = 0x113;
pub const BTN_EXTRA: u32 = 0x114;
pub const SEAT_NAME_SINCE: Version = Version(2);
@ -788,7 +784,6 @@ impl WlSeatGlobal {
if let Some(grab) = self.input_method_grab.get() {
grab.on_repeat_info();
}
self.state.trigger_cci(CCI_INPUT);
}
pub fn close(self: &Rc<Self>) {
@ -1034,20 +1029,10 @@ impl WlSeatGlobal {
pub fn focus_history_set_visible(&self, visible: bool) {
self.focus_history_visible_only.set(visible);
self.state.trigger_cci(CCI_INPUT);
}
pub fn focus_history_visible(&self) -> bool {
self.focus_history_visible_only.get()
}
pub fn focus_history_set_same_workspace(&self, same_workspace: bool) {
self.focus_history_same_workspace.set(same_workspace);
self.state.trigger_cci(CCI_INPUT);
}
pub fn focus_history_same_workspace(&self) -> bool {
self.focus_history_same_workspace.get()
}
fn focus_layer_rel<LI, SI>(
@ -1585,16 +1570,10 @@ impl WlSeatGlobal {
pub fn set_focus_follows_mouse(&self, focus_follows_mouse: bool) {
self.focus_follows_mouse.set(focus_follows_mouse);
self.state.trigger_cci(CCI_INPUT);
}
pub fn focus_follows_mouse(&self) -> bool {
self.focus_follows_mouse.get()
}
pub fn set_mouse_follows_focus(&self, enabled: bool) {
self.mouse_follows_focus.set(enabled);
self.state.trigger_cci(CCI_INPUT);
}
pub fn mouse_follows_focus(&self) -> bool {
@ -1603,11 +1582,6 @@ impl WlSeatGlobal {
pub fn set_fallback_output_mode(&self, fallback_output_mode: FallbackOutputMode) {
self.fallback_output_mode.set(fallback_output_mode);
self.state.trigger_cci(CCI_INPUT);
}
pub fn fallback_output_mode(&self) -> FallbackOutputMode {
self.fallback_output_mode.get()
}
pub fn set_window_management_enabled(self: &Rc<Self>, enabled: bool) {
@ -1725,12 +1699,8 @@ impl WlSeatGlobal {
pub fn set_pointer_revert_key(&self, key: KeySym) {
self.revert_key.set(key);
self.state.trigger_cci(CCI_INPUT);
}
pub fn pointer_revert_key(&self) -> KeySym {
self.revert_key.get()
}
}
impl CursorUserOwner for WlSeatGlobal {
@ -1924,7 +1894,7 @@ pub fn collect_kb_foci(node: Rc<dyn Node>) -> SmallVec<[Rc<WlSeatGlobal>; 3]> {
}
impl DeviceHandlerData {
pub fn set_seat(&self, state: &State, seat: Option<Rc<WlSeatGlobal>>) {
pub fn set_seat(&self, _state: &State, seat: Option<Rc<WlSeatGlobal>>) {
if let Some(new) = &seat {
if let Some(old) = self.seat.get()
&& old.id() == new.id()
@ -1963,7 +1933,6 @@ impl DeviceHandlerData {
}
}
self.attach_event_listeners();
state.trigger_cci(CCI_INPUT);
}
fn destroy_physical_keyboard_state(&self) {
@ -1985,14 +1954,13 @@ impl DeviceHandlerData {
};
}
pub fn set_keymap(&self, state: &State, keymap: Option<Rc<KbvmMap>>) {
pub fn set_keymap(&self, _state: &State, keymap: Option<Rc<KbvmMap>>) {
self.destroy_physical_keyboard_state();
self.keymap.set(keymap);
self.attach_event_listeners();
state.trigger_cci(CCI_INPUT);
}
pub fn set_output(&self, state: &State, output: Option<&WlOutputGlobal>) {
pub fn set_output(&self, _state: &State, output: Option<&WlOutputGlobal>) {
match output {
None => {
log::info!("Removing output mapping of {}", self.device.name());
@ -2003,7 +1971,6 @@ impl DeviceHandlerData {
self.output.set(Some(o.opt.clone()));
}
}
state.trigger_cci(CCI_INPUT);
}
pub fn get_rect(&self, state: &State) -> Rect {
@ -2015,64 +1982,52 @@ impl DeviceHandlerData {
state.root.extents.get()
}
pub fn set_accel_profile(&self, state: &State, v: InputDeviceAccelProfile) {
pub fn set_accel_profile(&self, _state: &State, v: InputDeviceAccelProfile) {
self.device.set_accel_profile(v);
state.trigger_cci(CCI_INPUT);
}
pub fn set_accel_speed(&self, state: &State, v: f64) {
pub fn set_accel_speed(&self, _state: &State, v: f64) {
self.device.set_accel_speed(v);
state.trigger_cci(CCI_INPUT);
}
pub fn set_tap_enabled(&self, state: &State, v: bool) {
pub fn set_tap_enabled(&self, _state: &State, v: bool) {
self.device.set_tap_enabled(v);
state.trigger_cci(CCI_INPUT);
}
pub fn set_drag_enabled(&self, state: &State, v: bool) {
pub fn set_drag_enabled(&self, _state: &State, v: bool) {
self.device.set_drag_enabled(v);
state.trigger_cci(CCI_INPUT);
}
pub fn set_drag_lock_enabled(&self, state: &State, v: bool) {
pub fn set_drag_lock_enabled(&self, _state: &State, v: bool) {
self.device.set_drag_lock_enabled(v);
state.trigger_cci(CCI_INPUT);
}
pub fn set_left_handed(&self, state: &State, v: bool) {
pub fn set_left_handed(&self, _state: &State, v: bool) {
self.device.set_left_handed(v);
state.trigger_cci(CCI_INPUT);
}
pub fn set_natural_scrolling_enabled(&self, state: &State, v: bool) {
pub fn set_natural_scrolling_enabled(&self, _state: &State, v: bool) {
self.device.set_natural_scrolling_enabled(v);
state.trigger_cci(CCI_INPUT);
}
pub fn set_px_per_scroll_wheel(&self, state: &State, v: f64) {
pub fn set_px_per_scroll_wheel(&self, _state: &State, v: f64) {
self.px_per_scroll_wheel.set(v);
state.trigger_cci(CCI_INPUT);
}
pub fn set_transform_matrix(&self, state: &State, v: TransformMatrix) {
pub fn set_transform_matrix(&self, _state: &State, v: TransformMatrix) {
self.device.set_transform_matrix(v);
state.trigger_cci(CCI_INPUT);
}
pub fn set_calibration_matrix(&self, state: &State, v: [[f32; 3]; 2]) {
pub fn set_calibration_matrix(&self, _state: &State, v: [[f32; 3]; 2]) {
self.device.set_calibration_matrix(v);
state.trigger_cci(CCI_INPUT);
}
pub fn set_click_method(&self, state: &State, v: InputDeviceClickMethod) {
pub fn set_click_method(&self, _state: &State, v: InputDeviceClickMethod) {
self.device.set_click_method(v);
state.trigger_cci(CCI_INPUT);
}
pub fn set_middle_button_emulation_enabled(&self, state: &State, v: bool) {
pub fn set_middle_button_emulation_enabled(&self, _state: &State, v: bool) {
self.device.set_middle_button_emulation_enabled(v);
state.trigger_cci(CCI_INPUT);
}
}

View file

@ -1,7 +1,6 @@
use {
crate::{
backend::KeyState,
control_center::CCI_INPUT,
ifs::{
wl_seat::{
WlSeatGlobal,
@ -90,7 +89,6 @@ impl WlSeatGlobal {
im.cancel_simple(self);
}
}
self.state.trigger_cci(CCI_INPUT);
}
pub fn simple_im_enabled(&self) -> bool {

View file

@ -140,46 +140,6 @@ impl KnownCursor {
Some(cursor)
}
pub fn to_shape(self) -> u32 {
match self {
KnownCursor::Default => DEFAULT,
KnownCursor::ContextMenu => CONTEXT_MENU,
KnownCursor::Help => HELP,
KnownCursor::Pointer => POINTER,
KnownCursor::Progress => PROGRESS,
KnownCursor::Wait => WAIT,
KnownCursor::Cell => CELL,
KnownCursor::Crosshair => CROSSHAIR,
KnownCursor::Text => TEXT,
KnownCursor::VerticalText => VERTICAL_TEXT,
KnownCursor::Alias => ALIAS,
KnownCursor::Copy => COPY,
KnownCursor::Move => MOVE,
KnownCursor::NoDrop => NO_DROP,
KnownCursor::NotAllowed => NOT_ALLOWED,
KnownCursor::Grab => GRAB,
KnownCursor::Grabbing => GRABBING,
KnownCursor::EResize => E_RESIZE,
KnownCursor::NResize => N_RESIZE,
KnownCursor::NeResize => NE_RESIZE,
KnownCursor::NwResize => NW_RESIZE,
KnownCursor::SResize => S_RESIZE,
KnownCursor::SeResize => SE_RESIZE,
KnownCursor::SwResize => SW_RESIZE,
KnownCursor::WResize => W_RESIZE,
KnownCursor::EwResize => EW_RESIZE,
KnownCursor::NsResize => NS_RESIZE,
KnownCursor::NeswResize => NESW_RESIZE,
KnownCursor::NwseResize => NWSE_RESIZE,
KnownCursor::ColResize => COL_RESIZE,
KnownCursor::RowResize => ROW_RESIZE,
KnownCursor::AllScroll => ALL_SCROLL,
KnownCursor::ZoomIn => ZOOM_IN,
KnownCursor::ZoomOut => ZOOM_OUT,
KnownCursor::DndAsk => DND_ASK,
KnownCursor::AllResize => ALL_RESIZE,
}
}
}
object_base! {

View file

@ -56,7 +56,6 @@ pub struct KbvmMap {
pub id: KbvmMapId,
pub state_machine: StateMachine,
pub lookup_table: LookupTable,
pub map_text: String,
pub map: KeymapFd,
pub xwayland_map: KeymapFd,
pub has_indicators: bool,
@ -161,12 +160,11 @@ impl KbvmContext {
}
let builder = map.to_builder();
let (_, xwayland_map) = create_keymap_memfd(&map, true).map_err(KbvmError::KeymapMemfd)?;
let (map_text, map) = create_keymap_memfd(&map, false).map_err(KbvmError::KeymapMemfd)?;
let (_, map) = create_keymap_memfd(&map, false).map_err(KbvmError::KeymapMemfd)?;
Ok(Rc::new(KbvmMap {
id,
state_machine: builder.build_state_machine(),
map,
map_text,
xwayland_map,
lookup_table: builder.build_lookup_table(),
has_indicators,

View file

@ -96,10 +96,6 @@ impl Logger {
});
}
pub fn level(&self) -> LogLevel {
self.level.load(Relaxed)
}
pub fn path(&self) -> Arc<BString> {
self.path.lock().clone()
}

View file

@ -58,7 +58,6 @@ mod clientmem;
mod cmm;
mod compositor;
mod config;
mod control_center;
mod copy_device;
mod cpu_worker;
mod criteria;
@ -68,11 +67,9 @@ mod damage;
mod dbus;
mod drm_feedback;
mod edid;
mod egui_adapter;
mod ei;
mod eventfd_cache;
mod fixed;
mod fontconfig;
mod forker;
mod format;
mod gfx_api;

View file

@ -2,7 +2,6 @@ use {
crate::{
async_engine::AsyncEngine,
backend::HardwareCursor,
control_center::CCI_OUTPUTS,
ifs::wl_output::PersistentOutputState,
io_uring::{IoUring, IoUringError},
state::{ConnectorData, State},
@ -118,7 +117,7 @@ impl OutputSchedule {
self.trigger();
}
pub fn set_cursor_hz(&self, state: &State, hz: f64) {
pub fn set_cursor_hz(&self, _state: &State, hz: f64) {
let (hz, delta) = match map_cursor_hz(hz) {
None => {
log::warn!("Ignoring cursor frequency {hz}");
@ -128,7 +127,6 @@ impl OutputSchedule {
};
self.persistent.vrr_cursor_hz.set(hz);
self.connector.head_managers.handle_cursor_hz_change(hz);
state.trigger_cci(CCI_OUTPUTS);
self.cursor_delta_nsec.set(delta);
self.trigger();
}

View file

@ -42,15 +42,6 @@ pub struct AcceptorMetadata {
pub tag: Option<String>,
}
impl AcceptorMetadata {
pub fn secure() -> Self {
Self {
secure: true,
..Default::default()
}
}
}
impl SecurityContextAcceptors {
pub fn clear(&self) {
for acceptor in self.acceptors.lock().drain_values() {

View file

@ -19,10 +19,6 @@ use {
},
compositor::{LIBEI_SOCKET, LogLevel},
config::ConfigProxy,
control_center::{
CCI_COLOR_MANAGEMENT, CCI_COMPOSITOR, CCI_GPUS, CCI_IDLE, CCI_LOOK_AND_FEEL,
CCI_OUTPUTS, CCI_XWAYLAND, ControlCenters,
},
copy_device::CopyDeviceRegistry,
cpu_worker::CpuWorker,
criteria::{clm::ClMatcherManager, tlm::TlMatcherManager},
@ -31,7 +27,6 @@ use {
damage::DamageVisualizer,
dbus::Dbus,
drm_feedback::{DrmFeedback, DrmFeedbackIds},
egui_adapter::egui_platform::EggState,
ei::{
ei_acceptor::EiAcceptor,
ei_client::{EiClient, EiClients},
@ -304,8 +299,6 @@ pub struct State {
pub eventfd_cache: Rc<EventfdCache>,
pub lazy_event_sources: Rc<LazyEventSources>,
pub bo_drop_queue: Rc<ObjectDropQueue<Rc<dyn BufferObject>>>,
pub egg_state: EggState,
pub control_centers: ControlCenters,
pub virtual_outputs: VirtualOutputs,
pub clean_logs_older_than: Cell<Option<SystemTime>>,
}
@ -366,10 +359,9 @@ impl IdleState {
self.timeout_changed(state);
}
fn timeout_changed(&self, state: &State) {
fn timeout_changed(&self, _state: &State) {
self.timeout_changed.set(true);
self.change.trigger();
state.trigger_cci(CCI_IDLE);
}
pub fn add_inhibitor(&self, state: &State, inhibitor: &Rc<ZwpIdleInhibitorV1>) {
@ -385,10 +377,9 @@ impl IdleState {
}
}
fn inhibitors_changed(&self, state: &State) {
fn inhibitors_changed(&self, _state: &State) {
self.inhibitors_changed.set(true);
self.change.trigger();
state.trigger_cci(CCI_IDLE);
}
fn resume_inhibited_notifications(&self) {
@ -500,39 +491,30 @@ impl ConnectorData {
return;
}
*self.state.borrow_mut() = s.clone();
macro_rules! b {
($expr:expr) => {{
let e = $expr;
if e {
state.trigger_cci(CCI_OUTPUTS);
}
e
}};
}
if b!(old.enabled != s.enabled) {
if old.enabled != s.enabled {
self.head_managers.handle_enabled_change(state, s.enabled);
}
if b!(old.active != s.active) {
if old.active != s.active {
self.head_managers.handle_active_change(s.active);
}
if b!(old.non_desktop_override != s.non_desktop_override) {
if old.non_desktop_override != s.non_desktop_override {
self.head_managers
.handle_non_desktop_override_changed(s.non_desktop_override);
}
if b!(old.vrr != s.vrr) {
if old.vrr != s.vrr {
self.head_managers.handle_vrr_change(s.vrr);
}
if b!(old.tearing != s.tearing) {
if old.tearing != s.tearing {
self.head_managers.handle_tearing_enabled_change(s.tearing);
}
if b!(old.format != s.format) {
if old.format != s.format {
self.head_managers.handle_format_change(s.format);
}
if b!((old.color_space, old.eotf) != (s.color_space, s.eotf)) {
if (old.color_space, old.eotf) != (s.color_space, s.eotf) {
self.head_managers
.handle_colors_change(s.color_space, s.eotf);
}
if b!(old.mode != s.mode) {
if old.mode != s.mode {
self.head_managers.handle_mode_change(s.mode);
for head in self.wlr_output_heads.lock().values() {
head.handle_mode_change(s.mode);
@ -555,14 +537,12 @@ impl DrmDevData {
self.dev.clone().make_render_device();
}
pub fn set_direct_scanout_enabled(&self, state: &State, enabled: bool) {
pub fn set_direct_scanout_enabled(&self, _state: &State, enabled: bool) {
self.dev.set_direct_scanout_enabled(enabled);
state.trigger_cci(CCI_GPUS);
}
pub fn set_flip_margin(&self, state: &State, margin: u64) {
pub fn set_flip_margin(&self, _state: &State, margin: u64) {
self.dev.set_flip_margin(margin);
state.trigger_cci(CCI_GPUS);
}
}
@ -671,7 +651,6 @@ impl State {
}
pub fn set_render_ctx(&self, ctx: Option<Rc<dyn GfxContext>>) {
self.egg_state.clear();
self.explicit_sync_supported.set(false);
self.render_ctx.set(ctx.clone());
self.render_ctx_version.fetch_add(1);
@ -787,7 +766,6 @@ impl State {
}
self.expose_new_singletons();
self.trigger_cci(CCI_COLOR_MANAGEMENT | CCI_GPUS);
}
fn reload_cursors(&self) {
@ -1033,7 +1011,6 @@ impl State {
} else {
self.stop_xwayland();
}
self.trigger_cci(CCI_XWAYLAND);
}
pub fn set_xwayland_use_wire_scale(&self, use_wire_scale: bool) {
@ -1041,7 +1018,6 @@ impl State {
return;
}
self.update_xwayland_wire_scale();
self.trigger_cci(CCI_XWAYLAND);
}
pub fn next_serial(&self, client: Option<&Client>) -> u64 {
@ -1191,8 +1167,6 @@ impl State {
self.xdg_surface_configure_events.clear();
self.lazy_event_sources.clear();
self.bo_drop_queue.kill();
self.egg_state.clear();
self.control_centers.clear();
self.virtual_outputs.clear();
}
@ -1744,13 +1718,11 @@ impl State {
pub fn set_color_management_enabled(&self, enabled: bool) {
self.color_management_enabled.set(enabled);
self.expose_new_singletons();
self.trigger_cci(CCI_COLOR_MANAGEMENT);
}
pub fn set_primary_selection_enabled(&self, enabled: bool) {
self.enable_primary_selection.set(enabled);
self.expose_new_singletons();
self.trigger_cci(CCI_LOOK_AND_FEEL);
}
pub fn set_explicit_sync_enabled(&self, enabled: bool) {
@ -1761,7 +1733,6 @@ impl State {
pub fn set_log_level(&self, level: LogLevel) {
if let Some(logger) = &self.logger {
logger.set_level(level);
self.trigger_cci(CCI_COMPOSITOR);
}
}
@ -1794,7 +1765,6 @@ impl State {
self.root.clone().node_visit(&mut V);
self.damage(self.root.extents.get());
self.icons.clear();
self.trigger_cci(CCI_LOOK_AND_FEEL);
}
pub fn reset_colors(&self) {
@ -1829,7 +1799,6 @@ impl State {
pub fn set_ei_socket_enabled(self: &Rc<Self>, enabled: bool) {
self.enable_ei_acceptor.set(enabled);
self.update_ei_acceptor();
self.trigger_cci(CCI_COMPOSITOR);
}
pub fn set_workspace_display_order(&self, order: WorkspaceDisplayOrder) {
@ -1837,7 +1806,6 @@ impl State {
for output in self.root.outputs.lock().values() {
output.handle_workspace_display_order_update();
}
self.trigger_cci(CCI_COMPOSITOR);
}
fn spaces_changed(&self) {
@ -1859,7 +1827,6 @@ impl State {
self.root.clone().node_visit(&mut V);
self.damage(self.root.extents.get());
self.icons.update_sizes(self);
self.trigger_cci(CCI_LOOK_AND_FEEL);
}
pub fn set_show_bar(&self, show: bool) {
@ -1874,18 +1841,15 @@ impl State {
pub fn set_ui_drag_enabled(&self, enabled: bool) {
self.ui_drag_enabled.set(enabled);
self.trigger_cci(CCI_LOOK_AND_FEEL);
}
pub fn set_ui_drag_threshold(&self, threshold: i32) {
self.ui_drag_threshold_squared
.set(threshold.saturating_mul(threshold));
self.trigger_cci(CCI_LOOK_AND_FEEL);
}
pub fn set_show_pin_icon(&self, show: bool) {
self.show_pin_icon.set(show);
self.trigger_cci(CCI_LOOK_AND_FEEL);
for stacked in self.root.stacked.iter() {
if let Some(float) = stacked.deref().clone().node_into_float() {
float.schedule_render_titles();
@ -1895,7 +1859,6 @@ impl State {
pub fn set_float_above_fullscreen(&self, v: bool) {
self.float_above_fullscreen.set(v);
self.trigger_cci(CCI_LOOK_AND_FEEL);
for seat in self.globals.seats.lock().values() {
seat.emulate_cursor_moved();
seat.trigger_tree_changed(false);
@ -1909,7 +1872,6 @@ impl State {
}
fn fonts_changed(&self) {
self.trigger_cci(CCI_LOOK_AND_FEEL);
struct V;
impl NodeVisitorBase for V {
fn visit_container(&mut self, node: &Rc<ContainerNode>) {
@ -1933,7 +1895,6 @@ impl State {
theme.font.set(self.theme.default_font.clone());
theme.bar_font.set(None);
theme.title_font.set(None);
self.egg_state.reset_fonts();
self.fonts_changed();
}
@ -1954,16 +1915,6 @@ impl State {
self.fonts_changed();
}
pub fn set_egui_fonts(&self, proportional: Option<Vec<&str>>, monospace: Option<Vec<&str>>) {
if let Some(fonts) = &proportional {
self.egg_state.set_proportional_fonts(fonts);
}
if let Some(fonts) = &monospace {
self.egg_state.set_monospace_fonts(fonts);
}
self.fonts_changed();
}
pub fn set_bar_position(&self, p: BarPosition) {
self.theme.bar_position.set(p);
self.spaces_changed();

View file

@ -1,7 +1,6 @@
use {
crate::{
backend::{Connector, ConnectorEvent, ConnectorId, MonitorInfo},
control_center::CCI_OUTPUTS,
globals::GlobalName,
ifs::{
head_management::{HeadManagers, HeadState},
@ -95,7 +94,6 @@ pub fn handle(state: &Rc<State>, connector: &Rc<dyn Connector>) {
for mgr in state.head_managers.lock().values() {
mgr.announce(&data);
}
state.trigger_cci(CCI_OUTPUTS);
if state.connectors.set(id, data).is_some() {
panic!("Connector id has been reused");
}
@ -135,7 +133,6 @@ impl ConnectorHandler {
self.data.handler.set(None);
self.state.connectors.remove(&self.id);
self.data.head_managers.handle_removed();
self.state.trigger_cci(CCI_OUTPUTS);
}
async fn handle_connected(&self, info: MonitorInfo) {
@ -157,7 +154,6 @@ impl ConnectorHandler {
self.data
.head_managers
.handle_output_disconnected(&self.state);
self.state.trigger_cci(CCI_OUTPUTS);
for head in self.data.wlr_output_heads.lock().drain_values() {
head.handle_disconnected();
}
@ -304,7 +300,6 @@ impl ConnectorHandler {
self.data
.head_managers
.handle_output_connected(&self.state, &output_data);
self.state.trigger_cci(CCI_OUTPUTS);
self.state.wlr_output_managers.announce_head(&output_data);
global.add_damage_area(&global.pos.get());
self.data.damage();
@ -319,7 +314,6 @@ impl ConnectorHandler {
}
ConnectorEvent::FormatsChanged(formats) => {
self.data.head_managers.handle_formats_change(&formats);
self.state.trigger_cci(CCI_OUTPUTS);
on.global.formats.set(formats);
}
ConnectorEvent::State(state) => {
@ -433,7 +427,6 @@ impl ConnectorHandler {
self.data
.head_managers
.handle_output_connected(&self.state, &output_data);
self.state.trigger_cci(CCI_OUTPUTS);
self.state.wlr_output_managers.announce_head(&output_data);
'outer: loop {
while let Some(event) = self.data.connector.event() {

View file

@ -6,7 +6,6 @@ use {
},
client::ClientId,
cmm::cmm_description::ColorDescription,
control_center::CCI_OUTPUTS,
cursor::KnownCursor,
fixed::Fixed,
gfx_api::{AcquireSync, BufferResv, GfxTexture, ReleaseSync},
@ -245,7 +244,6 @@ impl OutputNode {
.connector
.head_managers
.handle_tearing_active_change(tearing);
self.state.trigger_cci(CCI_OUTPUTS);
}
}
@ -504,7 +502,6 @@ impl OutputNode {
.connector
.head_managers
.handle_scale_change(scale);
self.state.trigger_cci(CCI_OUTPUTS);
for head in self.global.connector.wlr_output_heads.lock().values() {
head.handle_new_scale(scale);
}
@ -877,7 +874,6 @@ impl OutputNode {
.connector
.head_managers
.handle_transform_change(transform);
self.state.trigger_cci(CCI_OUTPUTS);
for head in self.global.connector.wlr_output_heads.lock().values() {
head.hande_transform_change(transform);
}
@ -940,7 +936,6 @@ impl OutputNode {
.connector
.head_managers
.handle_position_size_change(self);
self.state.trigger_cci(CCI_OUTPUTS);
}
pub fn update_state(self: &Rc<Self>, old: BackendConnectorState, state: BackendConnectorState) {
@ -995,7 +990,6 @@ impl OutputNode {
.connector
.head_managers
.handle_brightness_change(brightness);
self.state.trigger_cci(CCI_OUTPUTS);
}
}
@ -1011,7 +1005,6 @@ impl OutputNode {
.connector
.head_managers
.handle_use_native_gamut_change(use_native_gamut);
self.state.trigger_cci(CCI_OUTPUTS);
}
}
@ -1023,7 +1016,6 @@ impl OutputNode {
.connector
.head_managers
.handle_blend_space_change(blend_space);
self.state.trigger_cci(CCI_OUTPUTS);
}
}
fn find_stacked_at(
@ -1489,7 +1481,6 @@ impl OutputNode {
.connector
.head_managers
.handle_vrr_mode_change(mode);
self.state.trigger_cci(CCI_OUTPUTS);
for head in self.global.connector.wlr_output_heads.lock().values() {
head.handle_vrr_mode_change(mode);
}
@ -1504,7 +1495,6 @@ impl OutputNode {
.connector
.head_managers
.handle_tearing_mode_change(mode);
self.state.trigger_cci(CCI_OUTPUTS);
}
}
@ -1554,7 +1544,6 @@ impl OutputNode {
pub fn set_flip_margin(&self, margin_ns: u64) {
self.flip_margin_ns.set(Some(margin_ns));
self.state.trigger_cci(CCI_OUTPUTS);
}
}

View file

@ -948,10 +948,6 @@ impl ToplevelData {
parent.node_is_workspace()
}
pub fn property_changed_source(&self) -> &Rc<LazyEventSource> {
self.property_changed_source
.get_or_init(|| self.state.lazy_event_sources.create_source())
}
}
impl Drop for ToplevelData {

View file

@ -31,6 +31,7 @@ where
}
}
#[allow(dead_code)]
pub fn load(&self, ordering: Ordering) -> T {
unsafe { T::from_linear_unchecked(self.v.load(ordering)) }
}

View file

@ -141,6 +141,7 @@ impl LazyEventSource {
}
impl LazyEventSources {
#[allow(dead_code)]
pub fn create_source(self: &Rc<Self>) -> Rc<LazyEventSource> {
Rc::new(LazyEventSource {
sources: self.clone(),

View file

@ -11,6 +11,7 @@ use {
};
pub struct ObjectDropQueue<T> {
#[allow(dead_code)]
ring: Rc<IoUring>,
killed: Cell<bool>,
pending: RefCell<Vec<Option<(T, PendingPoll)>>>,
@ -32,6 +33,7 @@ impl<T> ObjectDropQueue<T> {
}
}
#[allow(dead_code)]
pub fn push(self: &Rc<Self>, fd: &Rc<OwnedFd>, t: T)
where
T: 'static,

View file

@ -14,6 +14,7 @@ pub fn pipe() -> Result<Pipe<OwnedFd, OwnedFd>, OsError> {
}
impl<L, R> Pipe<L, R> {
#[allow(dead_code)]
pub fn map_read<Lprime>(self, map: impl FnOnce(L) -> Lprime) -> Pipe<Lprime, R> {
Pipe {
read: map(self.read),
@ -21,6 +22,7 @@ impl<L, R> Pipe<L, R> {
}
}
#[allow(dead_code)]
pub fn map_write<Rprime>(self, map: impl FnOnce(R) -> Rprime) -> Pipe<L, Rprime> {
Pipe {
read: self.read,

View file

@ -1,12 +1,8 @@
use {
crate::{
format::Format,
gfx_api::SyncFile,
utils::{compat::IoctlNumber, oserror::OsError},
video::{
LINEAR_MODIFIER, Modifier,
drm::{DrmError, syncobj::merge_sync_files},
},
video::{LINEAR_MODIFIER, Modifier},
},
arrayvec::ArrayVec,
std::{cell::OnceCell, rc::Rc, sync::OnceLock},
@ -118,21 +114,6 @@ impl DmaBuf {
Ok(())
}
pub fn export_sync_file(&self, flags: u32) -> Result<Option<SyncFile>, DrmError> {
let mut sf = PlaneVec::new();
for plane in &self.planes {
sf.push(
dma_buf_export_sync_file(&plane.fd, flags)
.map(Rc::new)
.map(SyncFile)
.map_err(DrmError::ExportSyncFile)?,
);
if self.is_one_file() {
break;
}
}
merge_sync_files(sf.iter())
}
}
const DMA_BUF_BASE: u64 = b'b' as _;

View file

@ -12,7 +12,6 @@ use {
},
},
cmm::{cmm_description::ColorDescription, cmm_primaries::Primaries},
control_center::CCI_VIRTUAL_OUTPUTS,
format::{Format, XRGB8888},
gfx_api::{
AcquireSync, BufferResv, DirectScanoutPosition, FdSync, GfxBlendBuffer, GfxContext,
@ -285,18 +284,16 @@ impl VirtualOutputs {
self.outputs.set(name.to_string(), vo.clone());
vo.flip_task
.set(Some(state.eng.spawn("vo-flip", vo.clone().flip_task())));
state.trigger_cci(CCI_VIRTUAL_OUTPUTS);
vo
}
pub fn remove_output(&self, state: &Rc<State>, name: &str) {
pub fn remove_output(&self, _state: &Rc<State>, name: &str) {
let Some(o) = self.outputs.remove(name) else {
return;
};
o.clear();
o.events.send_event(ConnectorEvent::Disconnected);
o.events.send_event(ConnectorEvent::Removed);
state.trigger_cci(CCI_VIRTUAL_OUTPUTS);
}
pub fn clear(&self) {

View file

@ -12,9 +12,8 @@ use {
usr_jay_screencast::UsrJayScreencast,
usr_jay_select_toplevel::UsrJaySelectToplevel,
usr_jay_select_workspace::UsrJaySelectWorkspace,
usr_jay_sync_file_surface::UsrJaySyncFileSurface,
usr_jay_workspace_watcher::UsrJayWorkspaceWatcher, usr_wl_output::UsrWlOutput,
usr_wl_seat::UsrWlSeat, usr_wl_surface::UsrWlSurface,
usr_wl_seat::UsrWlSeat,
},
usr_object::UsrObject,
},
@ -189,20 +188,6 @@ impl UsrJayCompositor {
obj
}
pub fn get_sync_file_surface(&self, surface: &UsrWlSurface) -> Rc<UsrJaySyncFileSurface> {
let obj = Rc::new(UsrJaySyncFileSurface {
id: self.con.id(),
con: self.con.clone(),
version: self.version,
});
self.con.request(GetSyncFileSurface {
self_id: self.id,
id: obj.id,
surface: surface.id,
});
self.con.add_object(obj.clone());
obj
}
}
impl JayCompositorEventHandler for UsrJayCompositor {

View file

@ -1,12 +1,8 @@
use {
crate::{
gfx_api::FdSync,
object::Version,
wire::{JaySyncFileSurfaceId, jay_sync_file_surface::*},
wl_usr::{
UsrCon, usr_ifs::usr_jay_sync_file_release::UsrJaySyncFileRelease,
usr_object::UsrObject,
},
wl_usr::{UsrCon, usr_object::UsrObject},
},
std::{convert::Infallible, rc::Rc},
};
@ -17,37 +13,6 @@ pub struct UsrJaySyncFileSurface {
pub version: Version,
}
impl UsrJaySyncFileSurface {
pub fn set_acquire(&self, sf: Option<&FdSync>) {
match sf.and_then(|s| s.get_sync_file()) {
None => {
self.con.request(SetAcquireImmediate { self_id: self.id });
}
Some(sf) => {
self.con.request(SetAcquireAsync {
self_id: self.id,
sync_file: sf.0.clone(),
});
}
}
}
pub fn get_release(&self) -> Rc<UsrJaySyncFileRelease> {
let obj = Rc::new(UsrJaySyncFileRelease {
id: self.con.id(),
con: self.con.clone(),
owner: Default::default(),
version: self.version,
});
self.con.request(GetRelease {
self_id: self.id,
release: obj.id,
});
self.con.add_object(obj.clone());
obj
}
}
impl JaySyncFileSurfaceEventHandler for UsrJaySyncFileSurface {
type Error = Infallible;
}

View file

@ -5,7 +5,7 @@ use {
wire::{WlDataDeviceId, wl_data_device::*},
wl_usr::{
UsrCon,
usr_ifs::{usr_wl_data_offer::UsrWlDataOffer, usr_wl_data_source::UsrWlDataSource},
usr_ifs::usr_wl_data_offer::UsrWlDataOffer,
usr_object::UsrObject,
},
},
@ -20,16 +20,6 @@ pub struct UsrWlDataDevice {
pub selection: CloneCell<Option<Rc<UsrWlDataOffer>>>,
}
impl UsrWlDataDevice {
pub fn set_selection(&self, serial: u32, source: &UsrWlDataSource) {
self.con.request(SetSelection {
self_id: self.id,
source: source.id,
serial,
});
}
}
impl WlDataDeviceEventHandler for UsrWlDataDevice {
type Error = Infallible;

View file

@ -2,14 +2,7 @@ use {
crate::{
object::Version,
wire::{WlDataDeviceManagerId, wl_data_device_manager::*},
wl_usr::{
UsrCon,
usr_ifs::{
usr_wl_data_device::UsrWlDataDevice, usr_wl_data_source::UsrWlDataSource,
usr_wl_seat::UsrWlSeat,
},
usr_object::UsrObject,
},
wl_usr::{UsrCon, usr_object::UsrObject},
},
std::{convert::Infallible, rc::Rc},
};
@ -20,40 +13,6 @@ pub struct UsrWlDataDeviceManager {
pub version: Version,
}
impl UsrWlDataDeviceManager {
pub fn create_data_source(&self) -> Rc<UsrWlDataSource> {
let obj = Rc::new(UsrWlDataSource {
id: self.con.id(),
con: self.con.clone(),
owner: Default::default(),
version: self.version,
});
self.con.request(CreateDataSource {
self_id: self.id,
id: obj.id,
});
self.con.add_object(obj.clone());
obj
}
pub fn get_data_device(&self, seat: &UsrWlSeat) -> Rc<UsrWlDataDevice> {
let obj = Rc::new(UsrWlDataDevice {
id: self.con.id(),
con: self.con.clone(),
version: self.version,
offer: Default::default(),
selection: Default::default(),
});
self.con.request(GetDataDevice {
self_id: self.id,
id: obj.id,
seat: seat.id,
});
self.con.add_object(obj.clone());
obj
}
}
impl WlDataDeviceManagerEventHandler for UsrWlDataDeviceManager {
type Error = Infallible;
}

View file

@ -6,7 +6,6 @@ use {
},
ahash::AHashSet,
std::{cell::RefCell, convert::Infallible, rc::Rc},
uapi::OwnedFd,
};
pub struct UsrWlDataOffer {
@ -16,16 +15,6 @@ pub struct UsrWlDataOffer {
pub mime_types: RefCell<AHashSet<String>>,
}
impl UsrWlDataOffer {
pub fn receive(&self, mime_type: &str, fd: &Rc<OwnedFd>) {
self.con.request(Receive {
self_id: self.id,
mime_type,
fd: fd.clone(),
});
}
}
impl WlDataOfferEventHandler for UsrWlDataOffer {
type Error = Infallible;

View file

@ -20,15 +20,6 @@ pub trait UsrWlDataSourceOwner {
fn send(&self, mime_type: &str, fd: Rc<OwnedFd>);
}
impl UsrWlDataSource {
pub fn offer(&self, mime_type: &str) {
self.con.request(Offer {
self_id: self.id,
mime_type,
});
}
}
impl WlDataSourceEventHandler for UsrWlDataSource {
type Error = Infallible;

View file

@ -3,8 +3,8 @@ use {
ifs::wl_seat::wl_pointer::PendingScroll,
object::Version,
utils::clonecell::CloneCell,
wire::{WlPointerId, WlSurfaceId, wl_pointer::*},
wl_usr::{UsrCon, usr_ifs::usr_wl_surface::UsrWlSurface, usr_object::UsrObject},
wire::{WlPointerId, wl_pointer::*},
wl_usr::{UsrCon, usr_object::UsrObject},
},
std::{cell::Cell, convert::Infallible, rc::Rc},
};
@ -40,18 +40,6 @@ pub trait UsrWlPointerOwner {
}
}
impl UsrWlPointer {
pub fn set_cursor(&self, serial: u32, cursor: Option<&UsrWlSurface>, hot_x: i32, hot_y: i32) {
self.con.request(SetCursor {
self_id: self.id,
serial,
surface: cursor.map(|c| c.id).unwrap_or(WlSurfaceId::NONE),
hotspot_x: hot_x,
hotspot_y: hot_y,
});
}
}
impl WlPointerEventHandler for UsrWlPointer {
type Error = Infallible;

View file

@ -5,7 +5,7 @@ use {
wire::{WlSeatId, wl_seat::*},
wl_usr::{
UsrCon,
usr_ifs::{usr_wl_keyboard::UsrWlKeyboard, usr_wl_pointer::UsrWlPointer},
usr_ifs::usr_wl_pointer::UsrWlPointer,
usr_object::UsrObject,
},
},
@ -47,21 +47,6 @@ impl UsrWlSeat {
ptr
}
pub fn get_keyboard(&self) -> Rc<UsrWlKeyboard> {
let kb = Rc::new(UsrWlKeyboard {
id: self.con.id(),
con: self.con.clone(),
keyboard: Default::default(),
owner: Default::default(),
version: self.version,
});
self.con.add_object(kb.clone());
self.con.request(GetKeyboard {
self_id: self.id,
id: kb.id,
});
kb
}
}
impl WlSeatEventHandler for UsrWlSeat {

View file

@ -1,6 +1,5 @@
use {
crate::{
cursor::KnownCursor,
object::Version,
wire::{WpCursorShapeDeviceV1Id, wp_cursor_shape_device_v1::*},
wl_usr::{UsrCon, usr_object::UsrObject},
@ -14,16 +13,6 @@ pub struct UsrWpCursorShapeDeviceV1 {
pub version: Version,
}
impl UsrWpCursorShapeDeviceV1 {
pub fn set_shape(&self, serial: u32, cursor: KnownCursor) {
self.con.request(SetShape {
self_id: self.id,
serial,
shape: cursor.to_shape(),
});
}
}
impl WpCursorShapeDeviceV1EventHandler for UsrWpCursorShapeDeviceV1 {
type Error = Infallible;
}

View file

@ -2,14 +2,7 @@ use {
crate::{
object::Version,
wire::{WpCursorShapeManagerV1Id, wp_cursor_shape_manager_v1::*},
wl_usr::{
UsrCon,
usr_ifs::{
usr_wl_pointer::UsrWlPointer,
usr_wp_cursor_shape_device_v1::UsrWpCursorShapeDeviceV1,
},
usr_object::UsrObject,
},
wl_usr::{UsrCon, usr_object::UsrObject},
},
std::{convert::Infallible, rc::Rc},
};
@ -20,23 +13,6 @@ pub struct UsrWpCursorShapeManagerV1 {
pub version: Version,
}
impl UsrWpCursorShapeManagerV1 {
pub fn get_pointer(&self, pointer: &UsrWlPointer) -> Rc<UsrWpCursorShapeDeviceV1> {
let obj = Rc::new(UsrWpCursorShapeDeviceV1 {
id: self.con.id(),
con: self.con.clone(),
version: self.version,
});
self.con.request(GetPointer {
self_id: self.id,
cursor_shape_device: obj.id,
pointer: pointer.id,
});
self.con.add_object(obj.clone());
obj
}
}
impl WpCursorShapeManagerV1EventHandler for UsrWpCursorShapeManagerV1 {
type Error = Infallible;
}

View file

@ -3,7 +3,7 @@ use {
object::Version,
utils::clonecell::CloneCell,
wire::{XdgSurfaceId, xdg_surface::*},
wl_usr::{UsrCon, usr_ifs::usr_xdg_toplevel::UsrXdgToplevel, usr_object::UsrObject},
wl_usr::{UsrCon, usr_object::UsrObject},
},
std::{convert::Infallible, rc::Rc},
};
@ -21,23 +21,6 @@ pub trait UsrXdgSurfaceOwner {
}
}
impl UsrXdgSurface {
pub fn get_toplevel(&self) -> Rc<UsrXdgToplevel> {
let obj = Rc::new(UsrXdgToplevel {
id: self.con.id(),
con: self.con.clone(),
owner: Default::default(),
version: self.version,
});
self.con.request(GetToplevel {
self_id: self.id,
id: obj.id,
});
self.con.add_object(obj.clone());
obj
}
}
impl XdgSurfaceEventHandler for UsrXdgSurface {
type Error = Infallible;

View file

@ -2,7 +2,7 @@ use {
crate::{
object::Version,
utils::clonecell::CloneCell,
wire::{WlOutputId, XdgToplevelId, xdg_toplevel::*},
wire::{XdgToplevelId, xdg_toplevel::*},
wl_usr::{UsrCon, usr_object::UsrObject},
},
std::{convert::Infallible, rc::Rc},
@ -15,29 +15,6 @@ pub struct UsrXdgToplevel {
pub version: Version,
}
impl UsrXdgToplevel {
pub fn set_title(&self, title: &str) {
self.con.request(SetTitle {
self_id: self.id,
title,
});
}
pub fn set_fullscreen(&self, fullscreen: bool) {
match fullscreen {
true => {
self.con.request(SetFullscreen {
self_id: self.id,
output: WlOutputId::NONE,
});
}
false => {
self.con.request(UnsetFullscreen { self_id: self.id });
}
}
}
}
pub trait UsrXdgToplevelOwner {
fn configure(&self, width: i32, height: i32) {
let _ = width;
@ -49,8 +26,6 @@ pub trait UsrXdgToplevelOwner {
}
}
impl UsrXdgToplevel {}
impl XdgToplevelEventHandler for UsrXdgToplevel {
type Error = Infallible;

View file

@ -2,11 +2,7 @@ use {
crate::{
object::Version,
wire::{XdgWmBaseId, xdg_wm_base::*},
wl_usr::{
UsrCon,
usr_ifs::{usr_wl_surface::UsrWlSurface, usr_xdg_surface::UsrXdgSurface},
usr_object::UsrObject,
},
wl_usr::{UsrCon, usr_object::UsrObject},
},
std::{convert::Infallible, rc::Rc},
};
@ -17,24 +13,6 @@ pub struct UsrXdgWmBase {
pub version: Version,
}
impl UsrXdgWmBase {
pub fn get_xdg_surface(&self, surface: &UsrWlSurface) -> Rc<UsrXdgSurface> {
let obj = Rc::new(UsrXdgSurface {
id: self.con.id(),
con: self.con.clone(),
owner: Default::default(),
version: self.version,
});
self.con.request(GetXdgSurface {
self_id: self.id,
id: obj.id,
surface: surface.id,
});
self.con.add_object(obj.clone());
obj
}
}
impl XdgWmBaseEventHandler for UsrXdgWmBase {
type Error = Infallible;

View file

@ -1,13 +1,10 @@
use {
crate::{
format::Format,
object::Version,
video::Modifier,
wire::{ZwpLinuxBufferParamsV1Id, zwp_linux_buffer_params_v1::*},
wl_usr::{UsrCon, usr_ifs::usr_wl_buffer::UsrWlBuffer, usr_object::UsrObject},
wl_usr::{UsrCon, usr_object::UsrObject},
},
std::{convert::Infallible, rc::Rc},
uapi::OwnedFd,
};
pub struct UsrZwpLinuxBufferParamsV1 {
@ -16,45 +13,6 @@ pub struct UsrZwpLinuxBufferParamsV1 {
pub version: Version,
}
impl UsrZwpLinuxBufferParamsV1 {
pub fn add(
&self,
fd: &Rc<OwnedFd>,
plane_idx: usize,
offset: u32,
stride: u32,
modifier: Modifier,
) {
self.con.request(Add {
self_id: self.id,
fd: fd.clone(),
plane_idx: plane_idx as u32,
offset,
stride,
modifier,
});
}
pub fn create_immed(&self, width: i32, height: i32, format: &Format) -> Rc<UsrWlBuffer> {
let obj = Rc::new(UsrWlBuffer {
id: self.con.id(),
con: self.con.clone(),
owner: Default::default(),
version: self.version,
});
self.con.request(CreateImmed {
self_id: self.id,
buffer_id: obj.id,
width,
height,
format: format.drm,
flags: 0,
});
self.con.add_object(obj.clone());
obj
}
}
impl ZwpLinuxBufferParamsV1EventHandler for UsrZwpLinuxBufferParamsV1 {
type Error = Infallible;

View file

@ -1,16 +1,8 @@
use {
crate::{
object::Version,
video::dmabuf::DmaBuf,
wire::{ZwpLinuxDmabufV1Id, zwp_linux_dmabuf_v1::*},
wl_usr::{
UsrCon,
usr_ifs::{
usr_wl_buffer::UsrWlBuffer,
usr_zwp_linux_buffer_params_v1::UsrZwpLinuxBufferParamsV1,
},
usr_object::UsrObject,
},
wl_usr::{UsrCon, usr_object::UsrObject},
},
std::{convert::Infallible, rc::Rc},
};
@ -21,27 +13,6 @@ pub struct UsrZwpLinuxDmabufV1 {
pub version: Version,
}
impl UsrZwpLinuxDmabufV1 {
pub fn create_buffer(&self, buffer: &DmaBuf) -> Rc<UsrWlBuffer> {
let params = Rc::new(UsrZwpLinuxBufferParamsV1 {
id: self.con.id(),
con: self.con.clone(),
version: self.version,
});
self.con.request(CreateParams {
self_id: self.id,
params_id: params.id,
});
self.con.add_object(params.clone());
for (idx, plane) in buffer.planes.iter().enumerate() {
params.add(&plane.fd, idx, plane.offset, plane.stride, buffer.modifier);
}
let obj = params.create_immed(buffer.width, buffer.height, &buffer.format);
self.con.remove_obj(&*params);
obj
}
}
impl ZwpLinuxDmabufV1EventHandler for UsrZwpLinuxDmabufV1 {
type Error = Infallible;

View file

@ -5,7 +5,6 @@ use {
crate::{
client::{ClientCaps, ClientError},
compositor::DISPLAY,
control_center::CCI_XWAYLAND,
forker::{ForkerError, ForkerProxy},
ifs::{
ipc::{DataOfferId, DataSourceId, IpcLocation, x_data_offer::XDataOffer},
@ -118,11 +117,9 @@ pub async fn manage(state: Rc<State>) {
let display = Rc::new(format!(":{}", xsocket.id));
forker.setenv(DISPLAY.as_bytes(), display.as_bytes());
state.xwayland.display.set(Some(display.clone()));
state.trigger_cci(CCI_XWAYLAND);
let _unsetenv = on_drop(|| {
forker.unsetenv(DISPLAY.as_bytes());
state.xwayland.display.take();
state.trigger_cci(CCI_XWAYLAND);
});
log::info!("Allocated display :{} for Xwayland", xsocket.id);
log::info!("Waiting for connection attempt");
@ -209,11 +206,9 @@ async fn run(
state.xwayland.queue.clear();
state.xwayland.pidfd.set(Some(pidfd.clone()));
state.xwayland.client.set(Some(client.clone()));
state.trigger_cci(CCI_XWAYLAND);
let _remove_pidfd = on_drop(|| {
state.xwayland.pidfd.take();
state.xwayland.client.take();
state.trigger_cci(CCI_XWAYLAND);
});
{
let shared = Rc::new(XwmShared::default());

View file

@ -91,7 +91,6 @@ pub enum SimpleCommand {
ToggleSimpleImEnabled,
ReloadSimpleIm,
EnableUnicodeInput,
OpenControlCenter,
WarpMouseToFocus,
}
@ -226,12 +225,6 @@ pub struct Theme {
pub bar_separator_width: Option<i32>,
}
#[derive(Debug, Clone, Default)]
pub struct Egui {
pub proportional_fonts: Option<Vec<String>>,
pub monospace_fonts: Option<Vec<String>>,
}
#[derive(Debug, Clone)]
pub struct Status {
pub format: MessageFormat,
@ -532,7 +525,6 @@ pub struct Config {
pub log_level: Option<LogLevel>,
pub clean_logs_older_than: Option<Duration>,
pub theme: Theme,
pub egui: Egui,
pub gfx_api: Option<GfxApi>,
pub direct_scanout_enabled: Option<bool>,
pub drm_devices: Vec<ConfigDrmDevice>,

View file

@ -20,7 +20,6 @@ mod connector_match;
mod content_type;
mod drm_device;
mod drm_device_match;
mod egui;
mod env;
pub mod exec;
mod fallback_output_mode;

View file

@ -168,7 +168,6 @@ impl ActionParser<'_> {
"toggle-simple-im-enabled" => ToggleSimpleImEnabled,
"reload-simple-im" => ReloadSimpleIm,
"enable-unicode-input" => EnableUnicodeInput,
"open-control-center" => OpenControlCenter,
"warp-mouse-to-focus" => WarpMouseToFocus,
_ => {
return Err(

View file

@ -1,7 +1,7 @@
use {
crate::{
config::{
Action, Config, Egui, Libei, Theme, UiDrag,
Action, Config, Libei, Theme, UiDrag,
context::Context,
extractor::{Extractor, ExtractorError, arr, bol, int, opt, recover, str, val},
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
@ -14,7 +14,6 @@ use {
connector::ConnectorsParser,
drm_device::DrmDevicesParser,
drm_device_match::DrmDeviceMatchParser,
egui::EguiParser,
env::EnvParser,
fallback_output_mode::FallbackOutputModeParser,
float::FloatParser,
@ -152,7 +151,6 @@ impl Parser for ConfigParser<'_> {
simple_im_val,
show_titles,
fallback_output_mode_val,
egui_val,
clean_logs_older_than_val,
mouse_follows_focus,
),
@ -213,7 +211,6 @@ impl Parser for ConfigParser<'_> {
opt(val("simple-im")),
recover(opt(bol("show-titles"))),
opt(val("fallback-output-mode")),
opt(val("egui")),
opt(val("clean-logs-older-than")),
recover(opt(bol("unstable-mouse-follows-focus"))),
),
@ -332,15 +329,6 @@ impl Parser for ConfigParser<'_> {
}
}
}
let mut egui = Egui::default();
if let Some(value) = egui_val {
match value.parse(&mut EguiParser(self.0)) {
Ok(v) => egui = v,
Err(e) => {
log::warn!("Could not parse the egui settings: {}", self.0.error(e));
}
}
}
let mut gfx_api = None;
if let Some(value) = gfx_api_val {
match value.parse(&mut GfxApiParser) {
@ -585,7 +573,6 @@ impl Parser for ConfigParser<'_> {
log_level,
clean_logs_older_than,
theme,
egui,
gfx_api,
drm_devices,
direct_scanout_enabled: direct_scanout.despan(),

View file

@ -1,63 +0,0 @@
use {
crate::{
config::{
Egui,
context::Context,
extractor::{Extractor, ExtractorError, arr, opt},
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
},
toml::{
toml_span::{Span, Spanned},
toml_value::Value,
},
},
indexmap::IndexMap,
thiserror::Error,
};
pub struct EguiParser<'a>(pub &'a Context<'a>);
#[derive(Debug, Error)]
pub enum EguiParserError {
#[error(transparent)]
Expected(#[from] UnexpectedDataType),
#[error(transparent)]
Extractor(#[from] ExtractorError),
}
impl Parser for EguiParser<'_> {
type Value = Egui;
type Error = EguiParserError;
const EXPECTED: &'static [DataType] = &[DataType::Table];
fn parse_table(
&mut self,
span: Span,
table: &IndexMap<Spanned<String>, Spanned<Value>>,
) -> ParseResult<Self> {
let mut ext = Extractor::new(self.0, span, table);
let (proportional_fonts_arr, monospace_fonts_arr) =
ext.extract((opt(arr("proportional-fonts")), opt(arr("monospace-fonts"))))?;
let mut proportional_fonts = None;
let mut monospace_fonts = None;
for (out, f) in [
(&mut proportional_fonts, proportional_fonts_arr),
(&mut monospace_fonts, monospace_fonts_arr),
] {
if let Some(f) = f {
let fonts = out.insert(vec![]);
for f in f.value {
let Value::String(s) = &f.value else {
log::error!("Expected a string: {}", self.0.error3(f.span));
continue;
};
fonts.push(s.clone());
}
}
}
Ok(Egui {
proportional_fonts,
monospace_fonts,
})
}
}

View file

@ -31,7 +31,6 @@ alt-m = "toggle-mono"
alt-u = "toggle-fullscreen"
alt-f = "focus-parent"
alt-c = "open-control-center"
alt-shift-c = "close"
alt-shift-f = "toggle-floating"
Super_L = { type = "exec", exec = "alacritty" }

View file

@ -36,7 +36,7 @@ use {
is_reload,
keyboard::Keymap,
logging::{clean_logs_older_than, set_log_level},
on_devices_enumerated, on_idle, on_unload, open_control_center, quit, reload,
on_devices_enumerated, on_idle, on_unload, quit, reload,
set_color_management_enabled, set_default_workspace_capture, set_explicit_sync_enabled,
set_float_above_fullscreen, set_idle, set_idle_grace_period,
set_middle_click_paste_enabled, set_show_bar, set_show_float_pin_icon, set_show_titles,
@ -45,8 +45,8 @@ use {
switch_to_vt,
tasks::{self, JoinHandle},
theme::{
reset_colors, reset_font, reset_sizes, set_bar_font, set_bar_position,
set_egui_monospace_fonts, set_egui_proportional_fonts, set_font, set_title_font,
reset_colors, reset_font, reset_sizes, set_bar_font, set_bar_position, set_font,
set_title_font,
},
toggle_float_above_fullscreen, toggle_show_bar, toggle_show_titles,
video::{
@ -250,7 +250,6 @@ impl Action {
let persistent = state.persistent.clone();
b.new(move || persistent.seat.enable_unicode_input())
}
SimpleCommand::OpenControlCenter => b.new(open_control_center),
SimpleCommand::WarpMouseToFocus => {
let persistent = state.persistent.clone();
b.new(move || persistent.seat.warp_mouse_to_focus())
@ -1662,12 +1661,6 @@ fn load_config(initial_load: bool, auto_reload: bool, persistent: &Rc<Persistent
if let Some(v) = config.fallback_output_mode {
persistent.seat.set_fallback_output_mode(v);
}
if let Some(f) = &config.egui.proportional_fonts {
set_egui_proportional_fonts(f.iter().map(|s| &**s));
}
if let Some(f) = &config.egui.monospace_fonts {
set_egui_monospace_fonts(f.iter().map(|s| &**s));
}
if let Some(mouse_follows_focus) = config.mouse_follows_focus {
#[expect(deprecated)]
persistent

View file

@ -2056,7 +2056,6 @@
"toggle-simple-im-enabled",
"reload-simple-im",
"enable-unicode-input",
"open-control-center",
"warp-mouse-to-focus"
]
},

View file

@ -4713,10 +4713,6 @@ The string should have one of the following values:
This has no effect if the simple IM is not currently active.
- `open-control-center`:
Opens the control center.
- `warp-mouse-to-focus`:
Warps the cursor to the center of the currently focused window.

View file

@ -1229,8 +1229,6 @@ SimpleActionName:
Enables Unicode input in the simple, XCompose based input method.
This has no effect if the simple IM is not currently active.
- value: open-control-center
description: Opens the control center.
- value: warp-mouse-to-focus
description: |
Warps the cursor to the center of the currently focused window.

View file

@ -135,10 +135,6 @@ request get_pid (since = 27) {
}
request open_control_center (since = 28) {
id: id(jay_open_control_center_request),
}
# events
event client_id {

View file

@ -1,7 +0,0 @@
request destroy {
}
event failed {
msg: str,
}