about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock241
-rw-r--r--Cargo.toml7
-rw-r--r--po/Furtherance.pot2
-rw-r--r--po/de.po4
-rw-r--r--po/fi.po4
-rw-r--r--po/fr.po4
-rw-r--r--po/he.po2
-rw-r--r--po/it.po4
-rw-r--r--po/nb.po2
-rw-r--r--po/nl.po4
-rw-r--r--po/pt.po4
-rw-r--r--po/ru.po4
-rw-r--r--po/sk.po4
-rw-r--r--po/tr.po4
-rw-r--r--src/gtk/preferences_dialog.ui2
-rw-r--r--src/helpers/idle.rs120
-rw-r--r--src/helpers/wayland_idle.rs348
-rw-r--r--src/main.rs6
-rw-r--r--src/ui/window.rs12
19 files changed, 739 insertions, 39 deletions
diff --git a/Cargo.lock b/Cargo.lock
index d5d4564..483bfd6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,6 +1,6 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
-version = 3
+version = 4
 
 [[package]]
 name = "ahash"
@@ -248,6 +248,21 @@ dependencies = [
 ]
 
 [[package]]
+name = "dlib"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
+dependencies = [
+ "libloading",
+]
+
+[[package]]
+name = "downcast-rs"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
+
+[[package]]
 name = "either"
 version = "1.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -260,6 +275,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
 
 [[package]]
+name = "errno"
+version = "0.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
 name = "fallible-iterator"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -296,6 +321,7 @@ dependencies = [
  "gtk4",
  "gtk4-macros",
  "itertools",
+ "lazy_static",
  "libadwaita",
  "log",
  "num-derive",
@@ -303,6 +329,10 @@ dependencies = [
  "once_cell",
  "rusqlite",
  "serde",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-protocols-plasma",
+ "x11rb",
 ]
 
 [[package]]
@@ -426,6 +456,16 @@ dependencies = [
 ]
 
 [[package]]
+name = "gethostname"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818"
+dependencies = [
+ "libc",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
 name = "getrandom"
 version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -744,9 +784,9 @@ dependencies = [
 
 [[package]]
 name = "lazy_static"
-version = "1.4.0"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
 
 [[package]]
 name = "libadwaita"
@@ -782,9 +822,9 @@ dependencies = [
 
 [[package]]
 name = "libc"
-version = "0.2.133"
+version = "0.2.169"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966"
+checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
 
 [[package]]
 name = "libdbus-sys"
@@ -796,6 +836,16 @@ dependencies = [
 ]
 
 [[package]]
+name = "libloading"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
+dependencies = [
+ "cfg-if",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
 name = "libsqlite3-sys"
 version = "0.25.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -806,6 +856,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "linux-raw-sys"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
+
+[[package]]
 name = "locale_config"
 version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -993,6 +1049,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b45c49fc4f91f35bae654f85ebb3a44d60ac64f11b3166ffa609def390c732d8"
 
 [[package]]
+name = "quick-xml"
+version = "0.36.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
 name = "quote"
 version = "1.0.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1062,6 +1127,19 @@ dependencies = [
 ]
 
 [[package]]
+name = "rustix"
+version = "0.38.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
+dependencies = [
+ "bitflags 2.4.0",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
 name = "ryu"
 version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1077,6 +1155,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "scoped-tls"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
+
+[[package]]
 name = "semver"
 version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1373,6 +1457,79 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
 
 [[package]]
+name = "wayland-backend"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "056535ced7a150d45159d3a8dc30f91a2e2d588ca0b23f70e56033622b8016f6"
+dependencies = [
+ "cc",
+ "downcast-rs",
+ "rustix",
+ "scoped-tls",
+ "smallvec",
+ "wayland-sys",
+]
+
+[[package]]
+name = "wayland-client"
+version = "0.31.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b66249d3fc69f76fd74c82cc319300faa554e9d865dab1f7cd66cc20db10b280"
+dependencies = [
+ "bitflags 2.4.0",
+ "rustix",
+ "wayland-backend",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols"
+version = "0.32.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cd0ade57c4e6e9a8952741325c30bf82f4246885dca8bf561898b86d0c1f58e"
+dependencies = [
+ "bitflags 2.4.0",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols-plasma"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b31cab548ee68c7eb155517f2212049dc151f7cd7910c2b66abfd31c3ee12bd"
+dependencies = [
+ "bitflags 2.4.0",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-scanner"
+version = "0.31.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597f2001b2e5fc1121e3d5b9791d3e78f05ba6bfa4641053846248e3a13661c3"
+dependencies = [
+ "proc-macro2",
+ "quick-xml",
+ "quote",
+]
+
+[[package]]
+name = "wayland-sys"
+version = "0.31.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "efa8ac0d8e8ed3e3b5c9fc92c7881406a268e11555abe36493efabe649a29e09"
+dependencies = [
+ "dlib",
+ "log",
+ "pkg-config",
+]
+
+[[package]]
 name = "winapi"
 version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1438,6 +1595,21 @@ dependencies = [
 
 [[package]]
 name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
@@ -1460,6 +1632,12 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
 
 [[package]]
 name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
@@ -1472,6 +1650,12 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
 
 [[package]]
 name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
@@ -1484,6 +1668,12 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
 
 [[package]]
 name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
@@ -1502,6 +1692,12 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
 
 [[package]]
 name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
@@ -1514,6 +1710,12 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
 
 [[package]]
 name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
@@ -1526,6 +1728,12 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
 
 [[package]]
 name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
@@ -1538,6 +1746,12 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
 
 [[package]]
 name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
@@ -1552,6 +1766,23 @@ dependencies = [
 ]
 
 [[package]]
+name = "x11rb"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12"
+dependencies = [
+ "gethostname",
+ "rustix",
+ "x11rb-protocol",
+]
+
+[[package]]
+name = "x11rb-protocol"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d"
+
+[[package]]
 name = "xml-rs"
 version = "0.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 94a010b..7f6b0a4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,12 +15,17 @@ directories = "5.0"
 gettext-rs = { version = "0.7", features = ["gettext-system"] }
 gtk4-macros = "0.8.2"
 itertools = "0.10.5"
+lazy_static = "1.5.0"
 log = "0.4"
 num-derive = "0.3.3"
 num-traits = "0.2.15"
 once_cell = "1.17.1"
 rusqlite = { version = "0.28.0", features = ["backup"] }
 serde = { version = "1.0", features = ["derive"] }
+wayland-client = "0.31.7"
+wayland-protocols = { version = "0.32.5", features = ["client", "staging"] }
+wayland-protocols-plasma = { version = "0.3.5", features = ["client"] }
+x11rb = "0.13.1"
 
 [dependencies.gtk]
 package = "gtk4"
@@ -30,4 +35,4 @@ features = ["gnome_46"]
 [dependencies.adw]
 package = "libadwaita"
 version = "0.6.0"
-features = ["v1_5"]
\ No newline at end of file
+features = ["v1_5"]
diff --git a/po/Furtherance.pot b/po/Furtherance.pot
index 439d145..6fca2aa 100644
--- a/po/Furtherance.pot
+++ b/po/Furtherance.pot
@@ -100,7 +100,7 @@ msgid "Notify of idle"
 msgstr ""
 
 #: src/gtk/preferences_window.ui:33
-msgid "(GNOME only)"
+msgid "(Wayland only)"
 msgstr ""
 
 #: src/gtk/preferences_window.ui:38
diff --git a/po/de.po b/po/de.po
index cf4c94d..66d9812 100644
--- a/po/de.po
+++ b/po/de.po
@@ -272,8 +272,8 @@ msgid "Notify of idle"
 msgstr "Inaktivität melden"
 
 #: src/gtk/preferences_window.ui:33
-msgid "(GNOME only)"
-msgstr "(Nur GNOME)"
+msgid "(Wayland only)"
+msgstr "(Nur Wayland)"
 
 #: src/gtk/preferences_window.ui:38
 msgid "_Minutes to idle"
diff --git a/po/fi.po b/po/fi.po
index a6523f4..a8da004 100644
--- a/po/fi.po
+++ b/po/fi.po
@@ -212,8 +212,8 @@ msgid "Notify of idle"
 msgstr "Ilmoita joutenolosta"
 
 #: src/gtk/preferences_window.ui:33
-msgid "(GNOME only)"
-msgstr "(Vain Gnome)"
+msgid "(Wayland only)"
+msgstr "(Vain Wayland)"
 
 #: src/gtk/preferences_window.ui:38
 msgid "_Minutes to idle"
diff --git a/po/fr.po b/po/fr.po
index 4a9e95a..262f995 100644
--- a/po/fr.po
+++ b/po/fr.po
@@ -200,8 +200,8 @@ msgid "Notify of idle"
 msgstr "Notification d'inactivité"
 
 #: src/gtk/preferences_window.ui:33
-msgid "(GNOME only)"
-msgstr "(Uniquement pour GNOME)"
+msgid "(Wayland only)"
+msgstr "(Uniquement pour Wayland)"
 
 #: src/gtk/preferences_window.ui:38
 msgid "_Minutes to idle"
diff --git a/po/he.po b/po/he.po
index dc97434..3f4f309 100644
--- a/po/he.po
+++ b/po/he.po
@@ -173,7 +173,7 @@ msgid "Notify of idle"
 msgstr ""
 
 #: src/gtk/preferences_window.ui:33
-msgid "(GNOME only)"
+msgid "(Wayland only)"
 msgstr ""
 
 #: src/gtk/preferences_window.ui:38
diff --git a/po/it.po b/po/it.po
index 9ba09dc..5a551bd 100644
--- a/po/it.po
+++ b/po/it.po
@@ -212,8 +212,8 @@ msgid "Notify of idle"
 msgstr "Notifiche inattività"
 
 #: src/gtk/preferences_window.ui:33
-msgid "(GNOME only)"
-msgstr "(Solo per GNOME)"
+msgid "(Wayland only)"
+msgstr "(Solo per Wayland)"
 
 #: src/gtk/preferences_window.ui:38
 msgid "_Minutes to idle"
diff --git a/po/nb.po b/po/nb.po
index b2619ff..d846a53 100644
--- a/po/nb.po
+++ b/po/nb.po
@@ -152,7 +152,7 @@ msgid "Notify of idle"
 msgstr ""
 
 #: src/gtk/preferences_window.ui:33
-msgid "(GNOME only)"
+msgid "(Wayland only)"
 msgstr ""
 
 #: src/gtk/preferences_window.ui:38
diff --git a/po/nl.po b/po/nl.po
index 11d8ad7..b8d260f 100644
--- a/po/nl.po
+++ b/po/nl.po
@@ -164,8 +164,8 @@ msgid "Notify of idle"
 msgstr "Melding tonen bij inactiviteit"
 
 #: src/gtk/preferences_window.ui:33
-msgid "(GNOME only)"
-msgstr "(werkt alleen op GNOME)"
+msgid "(Wayland only)"
+msgstr "(werkt alleen op Wayland)"
 
 #: src/gtk/preferences_window.ui:38
 msgid "_Minutes to idle"
diff --git a/po/pt.po b/po/pt.po
index f0ecc56..c3d5013 100644
--- a/po/pt.po
+++ b/po/pt.po
@@ -257,8 +257,8 @@ msgid "Notify of idle"
 msgstr "Notificação de inatividade"
 
 #: src/gtk/preferences_window.ui:33
-msgid "(GNOME only)"
-msgstr "(apenas GNOME)"
+msgid "(Wayland only)"
+msgstr "(apenas Wayland)"
 
 #: src/gtk/preferences_window.ui:38
 msgid "_Minutes to idle"
diff --git a/po/ru.po b/po/ru.po
index 7e12c6c..4c00882 100644
--- a/po/ru.po
+++ b/po/ru.po
@@ -135,8 +135,8 @@ msgid "Notify of idle"
 msgstr "Уведомлять о бездействии"
 
 #: src/gtk/preferences_window.ui:33
-msgid "(GNOME only)"
-msgstr "(только для GNOME)"
+msgid "(Wayland only)"
+msgstr "(только для Wayland)"
 
 #: src/gtk/preferences_window.ui:38
 msgid "_Minutes to idle"
diff --git a/po/sk.po b/po/sk.po
index f6afc6b..97be639 100644
--- a/po/sk.po
+++ b/po/sk.po
@@ -151,8 +151,8 @@ msgid "Notify of idle"
 msgstr "Upozorniť na nečinnosť"
 
 #: src/gtk/preferences_window.ui:33
-msgid "(GNOME only)"
-msgstr "(Len pre GNOME)"
+msgid "(Wayland only)"
+msgstr "(Len pre Wayland)"
 
 #: src/gtk/preferences_window.ui:38
 msgid "_Minutes to idle"
diff --git a/po/tr.po b/po/tr.po
index ccc939d..db916ba 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -120,8 +120,8 @@ msgid "Notify of idle"
 msgstr "Boştayı bildir"
 
 #: src/gtk/preferences_window.ui:33
-msgid "(GNOME only)"
-msgstr "(Sadece GNOME)"
+msgid "(Wayland only)"
+msgstr "(Sadece Wayland)"
 
 #: src/gtk/preferences_window.ui:38
 msgid "_Minutes to idle"
diff --git a/src/gtk/preferences_dialog.ui b/src/gtk/preferences_dialog.ui
index 2971d88..8def8cb 100644
--- a/src/gtk/preferences_dialog.ui
+++ b/src/gtk/preferences_dialog.ui
@@ -24,7 +24,7 @@
             <child>
               <object class="AdwExpanderRow" id="notify_of_idle_expander">
                 <property name="title" translatable="yes">_Notify of Idle</property>
-                <property name="subtitle" translatable="yes">(GNOME Only)</property>
+                <property name="subtitle" translatable="yes">(Wayland only)</property>
                 <property name="show_enable_switch">True</property>
                 <property name="use_underline">True</property>
                 <child>
diff --git a/src/helpers/idle.rs b/src/helpers/idle.rs
new file mode 100644
index 0000000..074eba9
--- /dev/null
+++ b/src/helpers/idle.rs
@@ -0,0 +1,120 @@
+// Furtherance - Track your time without being tracked
+// Copyright (C) 2024  Ricky Kresslein <rk@unobserved.io>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+use std::env;
+use std::time::Duration;
+
+#[cfg(target_os = "linux")]
+use {
+    std::path::Path, std::sync::Arc
+};
+
+pub fn get_mac_windows_x11_idle_seconds() -> u64 {
+    0 // unimplemented
+}
+
+pub fn get_idle_time() -> Result<u64, Box<dyn std::error::Error>> {
+    match env::consts::OS {
+        "windows" => Ok(get_mac_windows_x11_idle_seconds()),
+        "macos" => Ok(get_mac_windows_x11_idle_seconds()),
+        #[cfg(target_os = "linux")]
+        "linux" => {
+            if is_wayland() {
+                if is_gnome() {
+                    get_gnome_idle_sync()
+                } else {
+                    get_wayland_idle_sync()
+                }
+            } else if is_x11() {
+                Ok(get_mac_windows_x11_idle_seconds())
+            } else {
+                Ok(0)
+            }
+        }
+        _ => Ok(0),
+    }
+}
+
+#[cfg(target_os = "linux")]
+fn is_wayland() -> bool {
+    if let Ok(_) = env::var("XDG_SESSION_TYPE").map(|v| v == "wayland") {
+        return true;
+    } else if let Ok(display) = env::var("WAYLAND_DISPLAY") {
+        if display.chars().next() == Some('/') {
+            return Path::new(&display).exists();
+        }
+        if let Ok(runtime_dir) = env::var("XDG_RUNTIME_DIR") {
+            return Path::new(
+                &format!("{}/{}", runtime_dir, display)
+            ).exists();
+        }
+    }
+    false
+}
+
+#[cfg(target_os = "linux")]
+fn is_x11() -> bool {
+    x11rb::connect(None).is_ok()
+}
+
+#[cfg(target_os = "linux")]
+fn get_wayland_idle_sync() -> Result<u64, Box<dyn std::error::Error>> {
+    use crate::helpers::wayland_idle;
+
+    wayland_idle::initialize_wayland().unwrap();
+
+    Ok(wayland_idle::get_idle_time())
+}
+
+#[cfg(target_os = "linux")]
+fn get_gnome_idle_sync() -> Result<u64, Box<dyn std::error::Error>> {
+    use dbus::blocking::stdintf::org_freedesktop_dbus::Properties;
+    let c = dbus::blocking::Connection::new_session()?;
+
+    let p = c.with_proxy(
+        "org.gnome.Mutter.IdleMonitor",
+        "/org/gnome/Mutter/IdleMonitor/Core",
+        Duration::from_millis(5000),
+    );
+    let (idle_time,): (u64,) =
+        p.method_call("org.gnome.Mutter.IdleMonitor", "GetIdletime", ())?;
+    Ok(idle_time / 1000)
+}
+
+#[cfg(target_os = "linux")]
+pub fn is_kde() -> bool {
+    if let Ok(desktop) = std::env::var("XDG_CURRENT_DESKTOP") {
+        return desktop.to_uppercase().contains("KDE");
+    }
+    false
+}
+
+#[cfg(target_os = "linux")]
+fn is_gnome() -> bool {
+    if let Ok(xdg_current_desktop) = env::var("XDG_CURRENT_DESKTOP") {
+        if xdg_current_desktop.to_lowercase().contains("gnome") {
+            return true;
+        }
+    }
+
+    if let Ok(gdm_session) = env::var("GDMSESSION") {
+        if gdm_session.to_lowercase().contains("gnome") {
+            return true;
+        }
+    }
+
+    false
+}
diff --git a/src/helpers/wayland_idle.rs b/src/helpers/wayland_idle.rs
new file mode 100644
index 0000000..ae2bce7
--- /dev/null
+++ b/src/helpers/wayland_idle.rs
@@ -0,0 +1,348 @@
+// Furtherance - Track your time without being tracked
+// Copyright (C) 2024  Ricky Kresslein <rk@unobserved.io>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+use std::collections::HashMap;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::{
+    mpsc::{channel, Receiver, Sender},
+    Arc, Mutex,
+};
+use std::thread;
+use wayland_client::protocol::wl_registry::{self, WlRegistry};
+use wayland_client::protocol::wl_seat::WlSeat;
+use wayland_client::{Connection, Dispatch, QueueHandle};
+use wayland_protocols::ext::idle_notify::v1::client::ext_idle_notification_v1::{
+    self, ExtIdleNotificationV1,
+};
+use wayland_protocols::ext::idle_notify::v1::client::ext_idle_notifier_v1::ExtIdleNotifierV1;
+use wayland_protocols_plasma::idle::client::org_kde_kwin_idle::OrgKdeKwinIdle;
+use wayland_protocols_plasma::idle::client::org_kde_kwin_idle_timeout::{
+    self, OrgKdeKwinIdleTimeout,
+};
+
+struct IdleState {
+    idle_since: Option<std::time::Instant>,
+}
+
+impl IdleState {
+    fn new() -> Self {
+        Self { idle_since: None }
+    }
+}
+
+lazy_static::lazy_static! {
+    static ref IDLE_STATE: Arc<Mutex<IdleState>> = Arc::new(Mutex::new(IdleState::new()));
+    static ref WAYLAND_INITIALIZED: Arc<Mutex<bool>> = Arc::new(Mutex::new(false));
+    static ref MONITOR_RUNNING: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
+    static ref STOP_SIGNAL: Arc<Mutex<Option<Sender<()>>>> = Arc::new(Mutex::new(None));
+}
+
+enum IdleManager {
+    Kde(OrgKdeKwinIdle),
+    Standard(ExtIdleNotifierV1),
+}
+
+struct WaylandState {
+    idle_state: Arc<Mutex<IdleState>>,
+    seats: HashMap<u32, WlSeat>,
+    idle_manager: Option<IdleManager>,
+}
+
+impl WaylandState {
+    fn new(idle_state: Arc<Mutex<IdleState>>) -> Self {
+        Self {
+            idle_state,
+            seats: HashMap::new(),
+            idle_manager: None,
+        }
+    }
+
+    fn handle_global(
+        &mut self,
+        registry: &WlRegistry,
+        name: u32,
+        interface: String,
+        version: u32,
+        qh: &QueueHandle<Self>,
+    ) {
+        match &interface[..] {
+            "wl_seat" => {
+                let seat = registry.bind::<WlSeat, _, _>(name, version, qh, ());
+                if let Some(idle_manager) = &self.idle_manager {
+                    let timeout_ms = 1000; // 1 second
+                    match idle_manager {
+                        IdleManager::Kde(manager) => {
+                            let _timeout = manager.get_idle_timeout(&seat, timeout_ms, qh, ());
+                        }
+                        IdleManager::Standard(manager) => {
+                            let _notification =
+                                manager.get_idle_notification(timeout_ms, &seat, qh, name);
+                        }
+                    }
+                }
+                self.seats.insert(name, seat);
+            }
+            "org_kde_kwin_idle" => {
+                let idle_manager: OrgKdeKwinIdle = registry.bind(name, version, qh, ());
+                // Set up idle timeouts for existing seats
+                for (_, seat) in &self.seats {
+                    let _timeout = idle_manager.get_idle_timeout(seat, 1000, qh, ());
+                }
+                self.idle_manager = Some(IdleManager::Kde(idle_manager));
+            }
+            "ext_idle_notifier_v1" => {
+                let idle_manager: ExtIdleNotifierV1 = registry.bind(name, version, qh, ());
+                // Set up idle notifications for existing seats
+                for (name, seat) in &self.seats {
+                    let _notification = idle_manager.get_idle_notification(1000, seat, qh, *name);
+                }
+                self.idle_manager = Some(IdleManager::Standard(idle_manager));
+            }
+            _ => {}
+        }
+    }
+}
+
+impl Dispatch<WlRegistry, ()> for WaylandState {
+    fn event(
+        state: &mut Self,
+        registry: &WlRegistry,
+        event: wl_registry::Event,
+        _: &(),
+        _: &Connection,
+        qh: &QueueHandle<Self>,
+    ) {
+        match event {
+            wl_registry::Event::Global {
+                name,
+                interface,
+                version,
+            } => state.handle_global(registry, name, interface, version, qh),
+            _ => {}
+        }
+    }
+}
+
+impl Dispatch<OrgKdeKwinIdleTimeout, ()> for WaylandState {
+    fn event(
+        state: &mut Self,
+        _proxy: &OrgKdeKwinIdleTimeout,
+        event: org_kde_kwin_idle_timeout::Event,
+        _data: &(),
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+    ) {
+        match event {
+            org_kde_kwin_idle_timeout::Event::Idle => {
+                if let Ok(mut state) = state.idle_state.lock() {
+                    state.idle_since = Some(std::time::Instant::now());
+                }
+            }
+            org_kde_kwin_idle_timeout::Event::Resumed => {
+                if let Ok(mut state) = state.idle_state.lock() {
+                    state.idle_since = None;
+                }
+            }
+            _ => {}
+        }
+    }
+}
+
+impl Dispatch<ExtIdleNotificationV1, u32> for WaylandState {
+    fn event(
+        state: &mut Self,
+        _proxy: &ExtIdleNotificationV1,
+        event: ext_idle_notification_v1::Event,
+        _data: &u32,
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+    ) {
+        match event {
+            ext_idle_notification_v1::Event::Idled => {
+                if let Ok(mut state) = state.idle_state.lock() {
+                    state.idle_since = Some(std::time::Instant::now());
+                }
+            }
+            ext_idle_notification_v1::Event::Resumed => {
+                if let Ok(mut state) = state.idle_state.lock() {
+                    state.idle_since = None;
+                }
+            }
+            _ => {}
+        }
+    }
+}
+
+impl Dispatch<WlSeat, ()> for WaylandState {
+    fn event(
+        _state: &mut Self,
+        _proxy: &WlSeat,
+        _event: <WlSeat as wayland_client::Proxy>::Event,
+        _data: &(),
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+    ) {
+    }
+}
+
+impl Dispatch<OrgKdeKwinIdle, ()> for WaylandState {
+    fn event(
+        _state: &mut Self,
+        _proxy: &OrgKdeKwinIdle,
+        _event: <OrgKdeKwinIdle as wayland_client::Proxy>::Event,
+        _data: &(),
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+    ) {
+    }
+}
+
+impl Dispatch<ExtIdleNotifierV1, ()> for WaylandState {
+    fn event(
+        _state: &mut Self,
+        _proxy: &ExtIdleNotifierV1,
+        _event: <ExtIdleNotifierV1 as wayland_client::Proxy>::Event,
+        _data: &(),
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+    ) {
+    }
+}
+
+fn run_wayland_monitor(rx: Receiver<()>) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
+    let conn = Connection::connect_to_env()?;
+    let mut event_queue = conn.new_event_queue();
+    let qh = event_queue.handle();
+
+    let display = conn.display();
+    display.get_registry(&qh, ());
+
+    let state = WaylandState::new(IDLE_STATE.clone());
+    let mut state = state;
+
+    loop {
+        if !MONITOR_RUNNING.load(Ordering::SeqCst) {
+            break;
+        }
+
+        // Check if we received a stop signal
+        if rx.try_recv().is_ok() {
+            break;
+        }
+
+        event_queue.blocking_dispatch(&mut state)?;
+        thread::sleep(std::time::Duration::from_millis(100));
+    }
+    Ok(())
+}
+
+pub fn initialize_wayland() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
+    if let Ok(mut initialized) = WAYLAND_INITIALIZED.lock() {
+        if *initialized {
+            return Ok(());
+        }
+
+        MONITOR_RUNNING.store(true, Ordering::SeqCst);
+
+        // Create a channel for stop signaling
+        let (tx, rx) = channel();
+        if let Ok(mut stop_signal) = STOP_SIGNAL.lock() {
+            *stop_signal = Some(tx);
+        }
+
+        thread::spawn(move || {
+            if let Err(e) = run_wayland_monitor(rx) {
+                eprintln!("Wayland monitor error: {}", e);
+            }
+        });
+
+        *initialized = true;
+    }
+    Ok(())
+}
+
+pub fn get_idle_time() -> u64 {
+    if !MONITOR_RUNNING.load(Ordering::SeqCst) {
+        return 0;
+    }
+
+    if let Ok(state) = IDLE_STATE.lock() {
+        if let Some(idle_since) = state.idle_since {
+            idle_since.elapsed().as_secs()
+        } else {
+            0
+        }
+    } else {
+        0
+    }
+}
+
+pub fn start_idle_monitor() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
+    // Stop any existing monitor and wait for confirmation
+    stop_idle_monitor();
+
+    // Reset idle state
+    if let Ok(mut state) = IDLE_STATE.lock() {
+        state.idle_since = None;
+    }
+
+    if let Ok(mut initialized) = WAYLAND_INITIALIZED.lock() {
+        if !*initialized {
+            MONITOR_RUNNING.store(true, Ordering::SeqCst);
+
+            // Create a channel for stop signaling
+            let (tx, rx) = channel();
+            if let Ok(mut stop_signal) = STOP_SIGNAL.lock() {
+                *stop_signal = Some(tx);
+            }
+
+            thread::spawn(move || {
+                if let Err(e) = run_wayland_monitor(rx) {
+                    eprintln!("Wayland monitor error: {}", e);
+                }
+            });
+
+            *initialized = true;
+        }
+    }
+    Ok(())
+}
+
+pub fn stop_idle_monitor() {
+    MONITOR_RUNNING.store(false, Ordering::SeqCst);
+
+    // Signal the monitor thread to stop
+    if let Ok(stop_signal) = STOP_SIGNAL.lock() {
+        if let Some(tx) = stop_signal.as_ref() {
+            let _ = tx.send(());
+        }
+    }
+
+    // Reset idle state
+    if let Ok(mut state) = IDLE_STATE.lock() {
+        state.idle_since = None;
+    }
+
+    // Reset initialized state
+    if let Ok(mut initialized) = WAYLAND_INITIALIZED.lock() {
+        *initialized = false;
+    }
+
+    // Clear the stop signal
+    if let Ok(mut stop_signal) = STOP_SIGNAL.lock() {
+        *stop_signal = None;
+    }
+}
diff --git a/src/main.rs b/src/main.rs
index abedf64..ac51761 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -19,6 +19,12 @@ mod config;
 mod database;
 mod settings_manager;
 mod ui;
+mod helpers {
+    mod idle;
+    mod wayland_idle;
+
+    pub use idle::get_idle_time;
+}
 
 use self::application::FurtheranceApplication;
 
diff --git a/src/ui/window.rs b/src/ui/window.rs
index bf23729..7fd4450 100644
--- a/src/ui/window.rs
+++ b/src/ui/window.rs
@@ -583,17 +583,7 @@ impl FurtheranceWindow {
     }
 
     fn get_idle_time(&self) -> Result<u64, Box<dyn std::error::Error>> {
-        let c = Connection::new_session()?;
-
-        let p = c.with_proxy(
-            "org.gnome.Mutter.IdleMonitor",
-            "/org/gnome/Mutter/IdleMonitor/Core",
-            Duration::from_millis(5000),
-        );
-        let (idle_time,): (u64,) =
-            p.method_call("org.gnome.Mutter.IdleMonitor", "GetIdletime", ())?;
-
-        Ok(idle_time / 1000)
+        crate::helpers::get_idle_time()
     }
 
     fn check_user_idle(&self) {