1use derive_more::{Constructor, Display as MDisplay};
2use std::fmt::Display as FDisplay;
3
4use crate::default_instance;
5use crate::instance::Instance;
6use crate::shared::*;
7
8pub mod reload {
10 use super::*;
11
12 pub fn call() -> crate::Result<()> {
14 instance_call(default_instance()?)
15 }
16
17 pub fn instance_call(instance: &Instance) -> crate::Result<()> {
19 instance.write_to_socket(command!(Empty, "reload"))?;
20 Ok(())
21 }
22
23 #[cfg(any(feature = "async-lite", feature = "tokio"))]
25 pub async fn call_async() -> crate::Result<()> {
26 instance_call_async(default_instance()?).await
27 }
28
29 #[cfg(any(feature = "async-lite", feature = "tokio"))]
31 pub async fn instance_call_async(instance: &Instance) -> crate::Result<()> {
32 instance
33 .write_to_socket_async(command!(Empty, "reload"))
34 .await?;
35 Ok(())
36 }
37}
38pub mod kill {
40 use super::*;
41
42 pub fn call() -> crate::Result<()> {
44 instance_call(default_instance()?)
45 }
46
47 pub fn instance_call(instance: &Instance) -> crate::Result<()> {
49 instance.write_to_socket(command!(Empty, "kill"))?;
50 Ok(())
51 }
52
53 #[cfg(any(feature = "async-lite", feature = "tokio"))]
55 pub async fn call_async() -> crate::Result<()> {
56 instance_call_async(default_instance()?).await
57 }
58
59 #[cfg(any(feature = "async-lite", feature = "tokio"))]
61 pub async fn instance_call_async(instance: &Instance) -> crate::Result<()> {
62 instance
63 .write_to_socket_async(command!(Empty, "kill"))
64 .await?;
65 Ok(())
66 }
67}
68
69pub mod set_cursor {
71 use super::*;
72
73 pub fn call<Str: FDisplay>(theme: Str, size: u16) -> crate::Result<()> {
75 instance_call(default_instance()?, theme, size)
76 }
77
78 pub fn instance_call<Str: FDisplay>(
80 instance: &Instance,
81 theme: Str,
82 size: u16,
83 ) -> crate::Result<()> {
84 instance.write_to_socket(command!(Empty, "setcursor {theme} {size}"))?;
85 Ok(())
86 }
87
88 #[cfg(any(feature = "async-lite", feature = "tokio"))]
90 pub async fn call_async<Str: FDisplay>(theme: Str, size: u16) -> crate::Result<()> {
91 instance_call_async(default_instance()?, theme, size).await
92 }
93
94 #[cfg(any(feature = "async-lite", feature = "tokio"))]
96 pub async fn instance_call_async<Str: FDisplay>(
97 instance: &Instance,
98 theme: Str,
99 size: u16,
100 ) -> crate::Result<()> {
101 instance
102 .write_to_socket_async(command!(Empty, "setcursor {theme} {size}"))
103 .await?;
104 Ok(())
105 }
106}
107
108pub mod output {
110 use super::*;
111
112 #[derive(Debug, MDisplay, Clone, Copy, PartialEq, Eq)]
114 pub enum OutputBackends {
115 #[display("wayland")]
117 Wayland,
118 #[display("x11")]
120 X11,
121 #[display("headless")]
123 Headless,
124 #[display("auto")]
126 Auto,
127 }
128
129 pub fn create(backend: OutputBackends, name: Option<&str>) -> crate::Result<()> {
131 instance_create(default_instance()?, backend, name)
132 }
133
134 pub fn remove<Str: FDisplay>(name: Str) -> crate::Result<()> {
136 instance_remove(default_instance()?, name)
137 }
138
139 pub fn instance_create(
141 instance: &Instance,
142 backend: OutputBackends,
143 name: Option<&str>,
144 ) -> crate::Result<()> {
145 let name = name.unwrap_or_default();
146 instance.write_to_socket(command!(Empty, "output create {backend} {name}"))?;
147 Ok(())
148 }
149
150 pub fn instance_remove<Str: FDisplay>(instance: &Instance, name: Str) -> crate::Result<()> {
152 instance.write_to_socket(command!(Empty, "output remove {name}"))?;
153 Ok(())
154 }
155
156 #[cfg(any(feature = "async-lite", feature = "tokio"))]
158 pub async fn create_async(backend: OutputBackends, name: Option<&str>) -> crate::Result<()> {
159 instance_create_async(default_instance()?, backend, name).await
160 }
161
162 #[cfg(any(feature = "async-lite", feature = "tokio"))]
164 pub async fn instance_create_async(
165 instance: &Instance,
166 backend: OutputBackends,
167 name: Option<&str>,
168 ) -> crate::Result<()> {
169 let name = name.unwrap_or_default();
170 instance
171 .write_to_socket_async(command!(Empty, "output create {backend} {name}"))
172 .await?;
173 Ok(())
174 }
175
176 #[cfg(any(feature = "async-lite", feature = "tokio"))]
178 pub async fn remove_async<Str: FDisplay>(name: Str) -> crate::Result<()> {
179 instance_remove_async(default_instance()?, name).await
180 }
181
182 #[cfg(any(feature = "async-lite", feature = "tokio"))]
184 pub async fn instance_remove_async<Str: FDisplay>(
185 instance: &Instance,
186 name: Str,
187 ) -> crate::Result<()> {
188 instance
189 .write_to_socket_async(command!(Empty, "output remove {name}"))
190 .await?;
191 Ok(())
192 }
193}
194
195pub mod switch_xkb_layout {
197 use super::*;
198
199 #[derive(Debug, MDisplay, Clone, Copy, PartialEq, Eq)]
201 pub enum SwitchXKBLayoutCmdTypes {
202 #[display("next")]
204 Next,
205 #[display("prev")]
207 Previous,
208 #[display("{_0}")]
210 Id(u8),
211 }
212
213 pub fn call<Str: FDisplay>(device: Str, cmd: SwitchXKBLayoutCmdTypes) -> crate::Result<()> {
215 instance_call(default_instance()?, device, cmd)
216 }
217
218 pub fn instance_call<Str: FDisplay>(
220 instance: &Instance,
221 device: Str,
222 cmd: SwitchXKBLayoutCmdTypes,
223 ) -> crate::Result<()> {
224 instance.write_to_socket(command!(Empty, "switchxkblayout {device} {cmd}"))?;
225 Ok(())
226 }
227
228 #[cfg(any(feature = "async-lite", feature = "tokio"))]
230 pub async fn call_async<Str: FDisplay>(
231 instance: &Instance,
232 device: Str,
233 cmd: SwitchXKBLayoutCmdTypes,
234 ) -> crate::Result<()> {
235 instance_call_async(instance, device, cmd).await
236 }
237
238 #[cfg(any(feature = "async-lite", feature = "tokio"))]
240 pub async fn instance_call_async<Str: FDisplay>(
241 instance: &Instance,
242 device: Str,
243 cmd: SwitchXKBLayoutCmdTypes,
244 ) -> crate::Result<()> {
245 instance
246 .write_to_socket_async(command!(Empty, "switchxkblayout {device} {cmd}"))
247 .await?;
248 Ok(())
249 }
250}
251
252pub mod set_error {
254 use super::*;
255
256 pub fn call(color: Color, msg: String) -> crate::Result<()> {
258 instance_call(default_instance()?, color, msg)
259 }
260
261 pub fn instance_call(instance: &Instance, color: Color, msg: String) -> crate::Result<()> {
263 instance.write_to_socket(command!(Empty, "seterror {color} {msg}"))?;
264 Ok(())
265 }
266
267 #[cfg(any(feature = "async-lite", feature = "tokio"))]
269 pub async fn call_async(color: Color, msg: String) -> crate::Result<()> {
270 instance_call_async(default_instance()?, color, msg).await
271 }
272
273 #[cfg(any(feature = "async-lite", feature = "tokio"))]
275 pub async fn instance_call_async(
276 instance: &Instance,
277 color: Color,
278 msg: String,
279 ) -> crate::Result<()> {
280 instance
281 .write_to_socket_async(command!(Empty, "seterror {color} {msg}"))
282 .await?;
283 Ok(())
284 }
285}
286
287pub mod notify {
289 use super::*;
290
291 #[allow(missing_docs)]
292 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
293 #[repr(i8)]
294 pub enum Icon {
295 NoIcon = -1,
296 Warning = 0,
297 Info = 1,
298 Hint = 2,
299 Error = 3,
300 Confused = 4,
301 Ok = 5,
302 }
303
304 pub fn call(
306 icon: Icon,
307 time: std::time::Duration,
308 color: Color,
309 msg: String,
310 ) -> crate::Result<()> {
311 instance_call(default_instance()?, icon, time, color, msg)
312 }
313
314 pub fn instance_call(
316 instance: &Instance,
317 icon: Icon,
318 time: std::time::Duration,
319 color: Color,
320 msg: String,
321 ) -> crate::Result<()> {
322 instance.write_to_socket(command!(
323 Empty,
324 "notify {} {} {color} {msg}",
325 icon as i8,
326 time.as_millis()
327 ))?;
328 Ok(())
329 }
330
331 #[cfg(any(feature = "async-lite", feature = "tokio"))]
333 pub async fn call_async(
334 icon: Icon,
335 time: std::time::Duration,
336 color: Color,
337 msg: String,
338 ) -> crate::Result<()> {
339 instance_call_async(default_instance()?, icon, time, color, msg).await
340 }
341
342 #[cfg(any(feature = "async-lite", feature = "tokio"))]
344 pub async fn instance_call_async(
345 instance: &Instance,
346 icon: Icon,
347 time: std::time::Duration,
348 color: Color,
349 msg: String,
350 ) -> crate::Result<()> {
351 instance
352 .write_to_socket_async(command!(
353 Empty,
354 "notify {} {} {color} {msg}",
355 icon as i8,
356 time.as_millis()
357 ))
358 .await?;
359 Ok(())
360 }
361}
362pub mod dismissnotify {
364 use super::*;
365
366 pub fn call(amount: Option<std::num::NonZeroU8>) -> crate::Result<()> {
370 instance_call(default_instance()?, amount)
371 }
372
373 pub fn instance_call(
377 instance: &Instance,
378 amount: Option<std::num::NonZeroU8>,
379 ) -> crate::Result<()> {
380 instance.write_to_socket(command!(
381 Empty,
382 "dismissnotify {}",
383 if let Some(amount) = amount {
384 amount.to_string()
385 } else {
386 (-1).to_string()
387 }
388 ))?;
389 Ok(())
390 }
391
392 #[cfg(any(feature = "async-lite", feature = "tokio"))]
396 pub async fn call_async(amount: Option<std::num::NonZeroU8>) -> crate::Result<()> {
397 instance_call_async(default_instance()?, amount).await
398 }
399
400 #[cfg(any(feature = "async-lite", feature = "tokio"))]
404 pub async fn instance_call_async(
405 instance: &Instance,
406 amount: Option<std::num::NonZeroU8>,
407 ) -> crate::Result<()> {
408 instance
409 .write_to_socket_async(command!(
410 Empty,
411 "dismissnotify {}",
412 if let Some(amount) = amount {
413 amount.to_string()
414 } else {
415 (-1).to_string()
416 }
417 ))
418 .await?;
419 Ok(())
420 }
421}
422
423#[derive(Debug, Copy, Clone, MDisplay, Constructor, PartialEq, Eq)]
425#[display("rgba({_0:02x}{_1:02x}{_2:02x}{_3:02x})")]
426pub struct Color(u8, u8, u8, u8);
427
428pub mod set_prop {
430 use super::*;
431
432 fn l(b: bool) -> &'static str {
433 if b { "lock" } else { "" }
434 }
435
436 #[derive(MDisplay, Clone, PartialEq)]
438 pub enum PropType {
439 #[display("animationstyle {_0}")]
441 AnimationStyle(String),
442 #[display("rounding {_0} {}", l(*_1))]
444 Rounding(
445 i64,
446 bool,
448 ),
449 #[display("forcenoblur {} {}", *_0 as u8, l(*_1))]
451 ForceNoBlur(
452 bool,
453 bool,
455 ),
456 #[display("forceopaque {} {}", *_0 as u8, l(*_1))]
458 ForceOpaque(
459 bool,
460 bool,
462 ),
463 #[display("forceopaqueoverriden {} {}", *_0 as u8, l(*_1))]
465 ForceOpaqueOverriden(
466 bool,
467 bool,
469 ),
470 #[display("forceallowsinput {} {}", *_0 as u8, l(*_1))]
472 ForceAllowsInput(
473 bool,
474 bool,
476 ),
477 #[display("forcenoanims {} {}", *_0 as u8, l(*_1))]
479 ForceNoAnims(
480 bool,
481 bool,
483 ),
484 #[display("forcenoborder {} {}", *_0 as u8, l(*_1))]
486 ForceNoBorder(
487 bool,
488 bool,
490 ),
491 #[display("forcenoshadow {} {}", *_0 as u8, l(*_1))]
493 ForceNoShadow(
494 bool,
495 bool,
497 ),
498 #[display("windowdancecompat {} {}", *_0 as u8, l(*_1))]
500 WindowDanceCompat(
501 bool,
502 bool,
504 ),
505 #[display("nomaxsize {} {}", *_0 as u8, l(*_1))]
507 NoMaxSize(
508 bool,
509 bool,
511 ),
512 #[display("dimaround {} {}", *_0 as u8, l(*_1))]
514 DimAround(
515 bool,
516 bool,
518 ),
519 #[display("alphaoverride {} {}", *_0 as u8, l(*_1))]
521 AlphaOverride(
522 bool,
523 bool,
525 ),
526 #[display("alpha {_0} {}", l(*_1))]
528 Alpha(
529 f32,
530 bool,
532 ),
533 #[display("alphainactiveoverride {} {}", *_0 as u8, l(*_1))]
535 AlphaInactiveOverride(
536 bool,
537 bool,
539 ),
540 #[display("alphainactive {_0} {}", l(*_1))]
542 AlphaInactive(
543 f32,
544 bool,
546 ),
547 #[display("alphabordercolor {_0} {}", l(*_1))]
549 ActiveBorderColor(
550 Color,
551 bool,
553 ),
554 #[display("inalphabordercolor {_0} {}", l(*_1))]
556 InactiveBorderColor(
557 Color,
558 bool,
560 ),
561 }
562
563 pub fn call(ident: String, prop: PropType, lock: bool) -> crate::Result<()> {
565 instance_call(default_instance()?, ident, prop, lock)
566 }
567
568 pub fn instance_call(
570 instance: &Instance,
571 ident: String,
572 prop: PropType,
573 lock: bool,
574 ) -> crate::Result<()> {
575 instance.write_to_socket(command!(
576 Empty,
577 "setprop {ident} {prop} {}",
578 if lock { "lock" } else { "" }
579 ))?;
580 Ok(())
581 }
582
583 #[cfg(any(feature = "async-lite", feature = "tokio"))]
585 pub async fn call_async(ident: String, prop: PropType, lock: bool) -> crate::Result<()> {
586 instance_call_async(default_instance()?, ident, prop, lock).await
587 }
588
589 #[cfg(any(feature = "async-lite", feature = "tokio"))]
591 pub async fn instance_call_async(
592 instance: &Instance,
593 ident: String,
594 prop: PropType,
595 lock: bool,
596 ) -> crate::Result<()> {
597 instance
598 .write_to_socket_async(command!(
599 Empty,
600 "setprop {ident} {prop} {}",
601 if lock { "lock" } else { "" }
602 ))
603 .await?;
604 Ok(())
605 }
606}
607
608pub mod plugin {
610 use super::*;
611 use crate::error::HyprError;
612 use std::path::Path;
613
614 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
616 pub struct Plugin {
617 pub name: String,
619 pub author: String,
621 pub handle: String,
623 pub version: String,
625 pub description: String,
627 }
628
629 pub fn list() -> crate::Result<Vec<Plugin>> {
631 instance_list(default_instance()?)
632 }
633
634 pub fn instance_list(instance: &Instance) -> crate::Result<Vec<Plugin>> {
636 let data = instance.write_to_socket(command!(JSON, "plugin list"))?;
637 let deserialized: Vec<Plugin> = serde_json::from_str(&data)?;
638 Ok(deserialized)
639 }
640
641 #[cfg(any(feature = "async-lite", feature = "tokio"))]
643 pub async fn list_async() -> crate::Result<Vec<Plugin>> {
644 instance_list_async(default_instance()?).await
645 }
646
647 #[cfg(any(feature = "async-lite", feature = "tokio"))]
649 pub async fn instance_list_async(instance: &Instance) -> crate::Result<Vec<Plugin>> {
650 let data = instance
651 .write_to_socket_async(command!(JSON, "plugin list"))
652 .await?;
653 let deserialized: Vec<Plugin> = serde_json::from_str(&data)?;
654 Ok(deserialized)
655 }
656
657 pub fn load(path: &Path) -> crate::Result<()> {
659 instance_load(default_instance()?, path)
660 }
661
662 pub fn instance_load(instance: &Instance, path: &Path) -> crate::Result<()> {
664 let str = instance.write_to_socket(command!(Empty, "plugin load {}", path.display()))?;
665 if str.contains("could not be loaded") {
666 Err(HyprError::Internal(str))
667 } else {
668 Ok(())
669 }
670 }
671
672 #[cfg(any(feature = "async-lite", feature = "tokio"))]
674 pub async fn load_async(path: &Path) -> crate::Result<()> {
675 instance_load_async(default_instance()?, path).await
676 }
677
678 #[cfg(any(feature = "async-lite", feature = "tokio"))]
680 pub async fn instance_load_async(instance: &Instance, path: &Path) -> crate::Result<()> {
681 let str = instance
682 .write_to_socket_async(command!(Empty, "plugin load {}", path.display()))
683 .await?;
684 if str.contains("could not be loaded") {
685 Err(HyprError::Internal(str))
686 } else {
687 Ok(())
688 }
689 }
690
691 pub fn unload(path: &Path) -> crate::Result<()> {
693 instance_unload(default_instance()?, path)
694 }
695
696 pub fn instance_unload(instance: &Instance, path: &Path) -> crate::Result<()> {
698 let str = instance.write_to_socket(command!(Empty, "plugin unload {}", path.display()))?;
699 if str.contains("plugin not loaded") {
700 Err(HyprError::Internal(str))
701 } else {
702 Ok(())
703 }
704 }
705
706 #[cfg(any(feature = "async-lite", feature = "tokio"))]
708 pub async fn unload_async(path: &Path) -> crate::Result<()> {
709 instance_unload_async(default_instance()?, path).await
710 }
711
712 #[cfg(any(feature = "async-lite", feature = "tokio"))]
714 pub async fn instance_unload_async(instance: &Instance, path: &Path) -> crate::Result<()> {
715 let str = instance
716 .write_to_socket_async(command!(Empty, "plugin unload {}", path.display()))
717 .await?;
718 if str.contains("plugin not loaded") {
719 Err(HyprError::Internal(str))
720 } else {
721 Ok(())
722 }
723 }
724}
725
726pub mod instance {
728 use crate::shared::get_hypr_path;
729 use std::fs::{DirEntry, File};
730 use std::io::Read;
731 use std::path::Path;
732
733 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
735 pub struct Instance {
736 pub instance: String,
738 pub time: u64,
740 pub pid: u32,
742 pub wl_socket: String,
744 }
745
746 pub fn instance_list() -> crate::Result<Vec<Instance>> {
748 let buf = get_hypr_path()?;
749 let entries = std::fs::read_dir(buf)?;
750 let mut instances = Vec::new();
751 for entry in entries.flatten() {
752 if let Some(instance) = parse_instance_entry(entry) {
753 instances.push(instance);
754 }
755 }
756 instances.retain(|el| Path::new(&format!("/proc/{}", el.pid)).exists());
757 Ok(instances)
758 }
759
760 fn parse_instance_entry(entry: DirEntry) -> Option<Instance> {
761 let file_name = entry.file_name().to_string_lossy().to_string();
762 let first = file_name.find('_')?;
763 let last = file_name.rfind('_')?;
764 if last <= first {
765 return None;
766 }
767 let time = file_name[first + 1..last].parse::<u64>().ok()?;
768
769 let lock_path = entry.path().join("hyprland.lock");
770 let mut file = File::open(&lock_path).ok()?;
771 if file.metadata().ok()?.len() == 0 {
772 return None; }
774 let mut content = String::new();
775 file.read_to_string(&mut content).ok()?;
776 let data = content
777 .lines()
778 .map(|line| line.trim().to_string())
779 .collect::<Vec<_>>();
780 if data.len() != 2 {
781 return None;
782 }
783
784 let pid = data.first().and_then(|s| s.parse::<u32>().ok())?;
785 let wl_socket = data.get(1).cloned().unwrap_or_default();
786
787 Some(Instance {
788 instance: file_name,
789 time,
790 pid,
791 wl_socket,
792 })
793 }
794}