Skip to content

Commit

Permalink
Merge pull request #25 from rharish101/multi_monitor
Browse files Browse the repository at this point in the history
This PR adds initial multi-monitor support, by choosing the first available valid monitor, both at initialisation and when any monitor is disconnected.
  • Loading branch information
rharish101 authored May 7, 2023
2 parents ccffff8 + c96fb52 commit f8022a0
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 8 deletions.
30 changes: 28 additions & 2 deletions src/gui/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ impl AsyncComponent for Greeter {

view! {
// The `view!` macro needs a proper widget, not a template, as the root.
#[name = "window"]
gtk::ApplicationWindow {
set_fullscreened: true,
set_visible: true,

// Name the UI widget, otherwise the inner children cannot be accessed by name.
Expand Down Expand Up @@ -327,13 +327,23 @@ impl AsyncComponent for Greeter {
}
}

fn post_view() {
if model.updates.changed(Updates::monitor()) {
if let Some(monitor) = &model.updates.monitor {
widgets.window.fullscreen_on_monitor(monitor);
// For some reason, the GTK settings are reset when changing monitors, so re-apply them.
setup_settings(self, &widgets.window);
}
}
}

/// Initialize the greeter.
async fn init(
input: Self::Init,
root: Self::Root,
sender: AsyncComponentSender<Self>,
) -> AsyncComponentParts<Self> {
let model = Self::new(&input.config_path).await;
let mut model = Self::new(&input.config_path).await;
let widgets = view_output!();

// Make the info bar permanently visible, since it was made invisible during init. The
Expand All @@ -357,9 +367,22 @@ impl AsyncComponent for Greeter {
warn!("Couldn't cancel greetd session: {err}");
};

model.choose_monitor(widgets.ui.display().name().as_str(), &sender);
if let Some(monitor) = &model.updates.monitor {
// The window needs to be manually fullscreened, since the monitor is `None` at widget
// init.
root.fullscreen_on_monitor(monitor);
} else {
// Couldn't choose a monitor, so let the compositor choose it for us.
root.fullscreen();
}

// For some reason, the GTK settings are reset when changing monitors, so apply them after
// full-screening.
setup_settings(&model, &root);
setup_users_sessions(&model, &widgets);
setup_datetime_display(&sender);

if input.css_path.exists() {
debug!("Loading custom CSS from file: {}", input.css_path.display());
relm4::set_global_css_from_file(input.css_path);
Expand Down Expand Up @@ -425,6 +448,9 @@ impl AsyncComponent for Greeter {
Self::CommandOutput::HandleGreetdResponse(response) => {
self.handle_greetd_response(&sender, response).await
}
Self::CommandOutput::MonitorRemoved(display_name) => {
self.choose_monitor(display_name.as_str(), &sender)
}
};
}
}
13 changes: 8 additions & 5 deletions src/gui/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@

use derivative::Derivative;
use greetd_ipc::Response;
use relm4::gtk::{glib, prelude::*, ComboBoxText, Entry};
use relm4::gtk::{glib::GString, prelude::*, ComboBoxText, Entry};

#[derive(Debug)]
/// Info about the current user and chosen session
pub struct UserSessInfo {
/// The ID for the currently chosen user
pub(super) user_id: Option<glib::GString>,
pub(super) user_id: Option<GString>,
/// The entry text for the currently chosen user
pub(super) user_text: glib::GString,
pub(super) user_text: GString,
/// The ID for the currently chosen session
pub(super) sess_id: Option<glib::GString>,
pub(super) sess_id: Option<GString>,
/// The entry text for the currently chosen session
pub(super) sess_text: glib::GString,
pub(super) sess_text: GString,
}

impl UserSessInfo {
Expand Down Expand Up @@ -69,4 +69,7 @@ pub enum CommandMsg {
ClearErr,
/// Handle a response received from greetd
HandleGreetdResponse(Response),
/// Notify the greeter that a monitor was removed.
// The Gstring is the name of the display.
MonitorRemoved(GString),
}
51 changes: 50 additions & 1 deletion src/gui/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ use std::sync::Arc;
use std::time::Duration;

use greetd_ipc::{AuthMessageType, ErrorType, Response};
use relm4::AsyncComponentSender;
use relm4::{
gtk::{
gdk::{Display, Monitor},
prelude::*,
},
AsyncComponentSender,
};
use tokio::{sync::Mutex, time::sleep};
use tracing::{debug, error, info, instrument, warn};

Expand Down Expand Up @@ -56,6 +62,8 @@ pub(super) struct Updates {
pub(super) active_session_id: Option<String>,
/// Time that is displayed
pub(super) time: String,
/// Monitor where the window is displayed
pub(super) monitor: Option<Monitor>,
}

impl Updates {
Expand Down Expand Up @@ -98,6 +106,7 @@ impl Greeter {
active_session_id: None,
tracker: 0,
time: "".to_string(),
monitor: None,
};
let greetd_client = Arc::new(Mutex::new(
GreetdClient::new()
Expand All @@ -114,6 +123,46 @@ impl Greeter {
}
}

/// Make the greeter full screen over the first monitor.
#[instrument(skip(self, sender))]
pub(super) fn choose_monitor(
&mut self,
display_name: &str,
sender: &AsyncComponentSender<Self>,
) {
let display = match Display::open(display_name) {
Some(display) => display,
None => {
error!("Couldn't get display with name: {display_name}");
return;
}
};

let mut chosen_monitor = None;
for monitor in display
.monitors()
.into_iter()
.filter_map(|item| {
item.ok()
.and_then(|object| object.downcast::<Monitor>().ok())
})
.filter(Monitor::is_valid)
{
debug!("Found monitor: {monitor}");
let sender = sender.clone();
monitor.connect_invalidate(move |monitor| {
let display_name = monitor.display().name();
sender.oneshot_command(async move { CommandMsg::MonitorRemoved(display_name) })
});
if chosen_monitor.is_none() {
// Choose the first monitor.
chosen_monitor = Some(monitor);
}
}

self.updates.set_monitor(chosen_monitor);
}

/// Run a command and log any errors in a background thread.
fn run_cmd(command: &[String], sender: &AsyncComponentSender<Self>) {
let mut process = Command::new(&command[0]);
Expand Down

0 comments on commit f8022a0

Please sign in to comment.