Skip to content

Commit

Permalink
Fix GNOME/KDE (#4)
Browse files Browse the repository at this point in the history
* monitors: fix offset mismatches and add scaling to prints

* monitors: fix gnome enabling/disabling, fix kde enabling/disabling, fix gnome moves, fix kde primary selection
  • Loading branch information
DashieTM authored Jun 6, 2024
1 parent 161cd2c commit 559eb41
Show file tree
Hide file tree
Showing 9 changed files with 326 additions and 127 deletions.
10 changes: 6 additions & 4 deletions monitors/src/backend/general.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ use super::{

// temporary application of configuration
pub fn apply_monitor_configuration(
serial: u32,
conn: Option<std::sync::Arc<wayland_client::Connection>>,
monitors: &Vec<Monitor>,
) {
match get_environment().as_str() {
"Hyprland" => hy_apply_monitor_information(monitors),
GNOME | "ubuntu:GNOME" => g_apply_monitor_config(1, monitors),
"KDE" => kde_apply_monitor_config(monitors),
GNOME | "ubuntu:GNOME" => g_apply_monitor_config(serial, 1, monitors),
"KDE" => kde_apply_monitor_config(conn, monitors),
// fallback to protocol implementations
_ => match get_wl_backend().as_str() {
"WLR" => wlr_apply_monitor_configuration(conn, monitors),
Expand All @@ -34,13 +35,14 @@ pub fn apply_monitor_configuration(

// persistent application of configuration
pub fn save_monitor_configuration(
serial: u32,
conn: Option<std::sync::Arc<wayland_client::Connection>>,
monitors: &Vec<Monitor>,
) {
match get_environment().as_str() {
"Hyprland" => hy_save_monitor_configuration(monitors),
GNOME | "ubuntu:GNOME" => g_apply_monitor_config(2, monitors),
"KDE" => kde_save_monitor_config(monitors),
GNOME | "ubuntu:GNOME" => g_apply_monitor_config(serial, 2, monitors),
"KDE" => kde_save_monitor_config(conn, monitors),
_ => match get_wl_backend().as_str() {
"KWIN" => kwin_apply_monitor_configuration(conn, monitors),
_ => ERROR!("Unsupported Environment", ErrorLevel::PartialBreakage),
Expand Down
131 changes: 88 additions & 43 deletions monitors/src/backend/gnome.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::{
cmp::Ordering,
collections::{HashMap, HashSet},
hash::{DefaultHasher, Hash, Hasher},
time::Duration,
};

Expand All @@ -16,7 +17,9 @@ use re_set_lib::ERROR;
use re_set_lib::{utils::macros::ErrorLevel, write_log_to_file};

use crate::{
utils::{get_environment, AvailableMode, DragInformation, Monitor, MonitorFeatures, Offset, Size},
utils::{
get_environment, AvailableMode, DragInformation, Monitor, MonitorFeatures, Offset, Size,
},
GNOME_CHECK,
};

Expand Down Expand Up @@ -65,31 +68,32 @@ fn get_variable_refresh_rate_support() -> bool {
false
}

pub fn g_get_monitor_information() -> Vec<Monitor> {
pub fn g_get_monitor_information(serial: &mut u32) -> Vec<Monitor> {
let conn = Connection::new_session().unwrap();
let proxy = conn.with_proxy(BASE, DBUS_PATH, Duration::from_millis(1000));
let res: Result<(u32, Vec<GnomeMonitor>, Vec<GnomeLogicalMonitor>, PropMap), Error> =
proxy.method_call(INTERFACE, "GetCurrentState", ());
if res.is_err() {
ERROR!("Could fetch monitor configuration", ErrorLevel::Recoverable);
}
let (serial, monitors, logical_monitors, _properties) = res.unwrap();
let (fetched_serial, monitors, logical_monitors, _properties) = res.unwrap();
*serial = fetched_serial;
let gnome_monitors = GnomeMonitorConfig {
serial,
serial: fetched_serial,
monitors,
logical_monitors,
_properties,
};
gnome_monitors.inplace_to_regular_monitor()
}

pub fn g_apply_monitor_config(apply_mode: u32, monitors: &Vec<Monitor>) {
pub fn g_apply_monitor_config(serial: u32, apply_mode: u32, monitors: &Vec<Monitor>) {
let conn = Connection::new_session().unwrap();
let proxy = conn.with_proxy(BASE, DBUS_PATH, Duration::from_millis(1000));
let res: Result<(), Error> = proxy.method_call(
INTERFACE,
"ApplyMonitorsConfig",
GnomeMonitorConfig::from_regular_monitor(apply_mode, monitors),
GnomeMonitorConfig::from_regular_monitor(serial, apply_mode, monitors),
);
if let Err(_error) = res {
ERROR!(
Expand All @@ -110,15 +114,23 @@ pub struct GnomeMonitorConfig {
impl GnomeMonitorConfig {
pub fn inplace_to_regular_monitor(self) -> Vec<Monitor> {
let mut monitors = Vec::new();
for (monitor, logical_monitor) in self
.monitors
.into_iter()
.zip(self.logical_monitors.into_iter())
{
let mut monitor_iter = self.monitors.into_iter();
let mut logical_iter = self.logical_monitors.into_iter().peekable();
let mut count = 1;
loop {
let monitor = monitor_iter.next();
if monitor.is_none() {
break;
}
let monitor = monitor.unwrap();
let first_mode = monitor.modes.first();
if first_mode.is_none() {
continue;
}
let empty_mode = GnomeMode {
id: "-1".into(),
width: 0,
height: 0,
id: first_mode.unwrap().id.clone(),
width: 500,
height: 500,
refresh_rate: 0.0,
_scale: 0.0,
supported_scales: Vec::new(),
Expand All @@ -127,13 +139,11 @@ impl GnomeMonitorConfig {
let mut hash_modes: HashMap<Size, (String, HashSet<u32>, Vec<f64>)> = HashMap::new();
let mut modes = Vec::new();
let mut current_mode: Option<&GnomeMode> = None;
let mut enabled = false;
for mode in monitor.modes.iter() {
let flag_opt: Option<&bool> = prop_cast(&mode.properties, "is-current");
if let Some(flag) = flag_opt {
if *flag {
current_mode = Some(mode);
enabled = true;
}
}
if let Some(saved_mode) = hash_modes.get_mut(&Size(mode.width, mode.height)) {
Expand Down Expand Up @@ -188,51 +198,86 @@ impl GnomeMonitorConfig {
}
}

monitors.push(Monitor {
id: self.serial,
enabled,
name: monitor.name.connector,
make: monitor.name.vendor,
model: monitor.name.product,
serial: monitor.name.serial,
refresh_rate: current_mode.refresh_rate.round() as u32,
scale: logical_monitor.scale,
transform: logical_monitor.transform,
vrr,
primary: logical_monitor.primary,
offset: Offset(logical_monitor.x, logical_monitor.y),
size: Size(current_mode.width, current_mode.height),
mode: current_mode.id.clone(),
drag_information: DragInformation::default(),
available_modes: modes,
features: gnome_features(vrr_enabled),
});
let mut hasher = DefaultHasher::new();
monitor.name.connector.hash(&mut hasher);
let id = hasher.finish();

let mut enabled = false;
let maybe_logical_monitor = logical_iter.peek();
if let Some(logical_monitor) = maybe_logical_monitor {
for names in logical_monitor._monitors.iter() {
if names.0 == monitor.name.connector {
enabled = true;
}
}
}
if enabled {
let logical_monitor = logical_iter.next().unwrap();
monitors.push(Monitor {
id: id as u32,
enabled,
name: monitor.name.connector,
make: monitor.name.vendor,
model: monitor.name.product,
serial: monitor.name.serial,
refresh_rate: current_mode.refresh_rate.round() as u32,
scale: logical_monitor.scale,
transform: logical_monitor.transform,
vrr,
primary: logical_monitor.primary,
offset: Offset(logical_monitor.x, logical_monitor.y),
size: Size(current_mode.width, current_mode.height),
mode: current_mode.id.clone(),
drag_information: DragInformation::default(),
available_modes: modes,
features: gnome_features(vrr_enabled),
});
} else {
count += 1;
monitors.push(Monitor {
id: id as u32,
enabled,
name: monitor.name.connector,
make: monitor.name.vendor,
model: monitor.name.product,
serial: monitor.name.serial,
refresh_rate: current_mode.refresh_rate.round() as u32,
scale: 1.0,
transform: 0,
vrr,
primary: false,
offset: Offset(count * -500 + -50, 0),
size: Size(current_mode.width, current_mode.height),
mode: current_mode.id.clone(),
drag_information: DragInformation::default(),
available_modes: modes,
features: gnome_features(vrr_enabled),
});
}
}
monitors
}

pub fn from_regular_monitor(
serial: u32,
apply_mode: u32,
monitors: &Vec<Monitor>,
) -> (u32, u32, Vec<GnomeLogicalMonitorSend>, PropMap) {
let mut g_logical_monitors = Vec::new();
let id = monitors.first().unwrap().id;
for monitor in monitors {
let mode = if monitor.enabled {
monitor.mode.clone()
} else {
"-1".into()
};
if !monitor.enabled {
continue;
}
g_logical_monitors.push(GnomeLogicalMonitorSend {
x: monitor.offset.0,
y: monitor.offset.1,
scale: monitor.scale,
transform: monitor.transform,
primary: monitor.primary,
monitors: vec![(monitor.name.clone(), mode, PropMap::new())],
monitors: vec![(monitor.name.clone(), monitor.mode.clone(), PropMap::new())],
});
}
(id, apply_mode, g_logical_monitors, PropMap::new())
(serial, apply_mode, g_logical_monitors, PropMap::new())
}
}

Expand Down
50 changes: 37 additions & 13 deletions monitors/src/backend/kde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use re_set_lib::{utils::macros::ErrorLevel, write_log_to_file};

use crate::utils::{AvailableMode, Monitor, MonitorFeatures, Offset, Size};

use super::kwin::{kwin_apply_monitor_configuration, kwin_get_monitor_information};

pub const KDE_FEATURES: MonitorFeatures = MonitorFeatures {
// KDE supports all the features!
vrr: true,
Expand All @@ -18,10 +20,18 @@ pub const KDE_FEATURES: MonitorFeatures = MonitorFeatures {
hdr: true,
};

pub fn kde_get_monitor_information() -> Vec<Monitor> {
pub fn kde_get_monitor_information(
conn: Option<std::sync::Arc<wayland_client::Connection>>,
) -> Vec<Monitor> {
let mut monitors = Vec::new();
let json = get_json();
if json.is_none() {
// if kscreen is not installed fall back to protocol
return kwin_get_monitor_information(conn);
}
let json = json.unwrap();
let kde_monitors: KDEMonitorConfiguration =
serde_json::from_str(&String::from_utf8(get_json()).expect("Could not parse json"))
serde_json::from_str(&String::from_utf8(json).expect("Could not parse json"))
.expect("Could not parse json");
for monitor in kde_monitors.outputs {
if !monitor.modes.is_empty() {
Expand All @@ -32,28 +42,34 @@ pub fn kde_get_monitor_information() -> Vec<Monitor> {
monitors
}

fn get_json() -> Vec<u8> {
fn get_json() -> Option<Vec<u8>> {
let command = Command::new("kscreen-doctor").args(["-j"]).output();
if let Ok(command) = command {
return command.stdout;
return Some(command.stdout);
}
ERROR!(
"Kscreen is not installed, please install kscreen for kde.",
ErrorLevel::PartialBreakage
);
Vec::new()
None
}

pub fn kde_apply_monitor_config(monitors: &Vec<Monitor>) {
kde_save_monitor_config(monitors);
pub fn kde_apply_monitor_config(
conn: Option<std::sync::Arc<wayland_client::Connection>>,
monitors: &Vec<Monitor>,
) {
kde_save_monitor_config(conn, monitors);
}

pub fn kde_save_monitor_config(monitors: &Vec<Monitor>) {
pub fn kde_save_monitor_config(
conn: Option<std::sync::Arc<wayland_client::Connection>>,
monitors: &Vec<Monitor>,
) {
let args = convert_modes_to_kscreen_string(monitors);
Command::new("kscreen-doctor")
.args(args)
.spawn()
.expect("Could not retrieve monitor json");
let command = Command::new("kscreen-doctor").args(args).spawn();
if command.is_err() {
kwin_apply_monitor_configuration(conn, monitors);
}
}

#[allow(non_snake_case)]
Expand Down Expand Up @@ -220,6 +236,7 @@ fn convert_modes(

fn convert_modes_to_kscreen_string(monitors: &Vec<Monitor>) -> Vec<String> {
let mut kscreen = Vec::new();
let mut count = 2;

for monitor in monitors {
let rotation = match monitor.transform {
Expand All @@ -235,8 +252,14 @@ fn convert_modes_to_kscreen_string(monitors: &Vec<Monitor>) -> Vec<String> {
};
let start = format!("output.{}.", monitor.name);
if !monitor.enabled {
kscreen.push(start.clone() + &format!("enable.{}", monitor.enabled));
kscreen.push(start.clone() + "disable");
} else {
let mut priority = 1;
if !monitor.primary {
priority = count;
count += 1;
}
kscreen.push(start.clone() + "enable");
kscreen.push(
start.clone()
+ &format!(
Expand All @@ -245,6 +268,7 @@ fn convert_modes_to_kscreen_string(monitors: &Vec<Monitor>) -> Vec<String> {
),
);
kscreen.push(start.clone() + &format!("scale.{}", monitor.scale));
kscreen.push(start.clone() + &format!("priority.{}", priority));
kscreen.push(
start.clone() + &format!("position.{},{}", monitor.offset.0, monitor.offset.1),
);
Expand Down
2 changes: 1 addition & 1 deletion monitors/src/backend/kwin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ use wayland_protocols_plasma::output_management::v2::client::kde_output_configur
use wayland_protocols_plasma::output_management::v2::client::kde_output_management_v2::Event as OutputManagementEvent;
use wayland_protocols_plasma::output_management::v2::client::kde_output_management_v2::KdeOutputManagementV2;

use re_set_lib::{ERROR, LOG};
#[cfg(debug_assertions)]
use re_set_lib::{utils::macros::ErrorLevel, write_log_to_file};
use re_set_lib::{ERROR, LOG};

use crate::utils::{AvailableMode, Monitor, MonitorFeatures, Offset, Size};

Expand Down
Loading

0 comments on commit 559eb41

Please sign in to comment.