summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2014-03-03 20:49:33 +0100
committerLennart Poettering <lennart@poettering.net>2014-03-03 20:57:09 +0100
commitf9cd6be10ece07e10488c05e270a0b5860779864 (patch)
treef236bd39b54c2b66276c7ac72a36cb565cf0b53e
parent7e9110a29d90041b0364cb93a84aec9dd72363b6 (diff)
logind: ignore lid switch events for 30s after each suspend and 3min after startup
This is needed to give USB docking stations and suchlike time to settle, so that a display connected to an USB docking station can actually act as a lid swith inhibitor correctly. With this change we should have somewhat reliable docking station support in place.
-rw-r--r--src/login/logind-action.c15
-rw-r--r--src/login/logind-dbus.c3
-rw-r--r--src/login/logind.c45
-rw-r--r--src/login/logind.h7
4 files changed, 69 insertions, 1 deletions
diff --git a/src/login/logind-action.c b/src/login/logind-action.c
index c9d8bc541..ae7b35055 100644
--- a/src/login/logind-action.c
+++ b/src/login/logind-action.c
@@ -67,26 +67,39 @@ int manager_handle_action(
/* If the key handling is turned off, don't do anything */
if (handle == HANDLE_IGNORE) {
log_debug("Refusing operation, as it is turned off.");
return 0;
}
- /* If we are docked don't react to lid closing */
if (inhibit_key == INHIBIT_HANDLE_LID_SWITCH) {
int n;
+ /* If we are docked don't react to lid closing */
if (manager_is_docked(m)) {
log_debug("Ignoring lid switch request, system is docked.");
return 0;
}
+ /* If we have more than one or no displays connected,
+ * don't react to lid closing. The no display case we
+ * treat like this under the assumption that there is
+ * no modern drm driver available. */
n = manager_count_displays(m);
if (n != 1) {
log_debug("Ignoring lid switch request, %i displays connected.", n);
return 0;
}
+
+ /* If the last system suspend or startup is too close,
+ * let's not suspend for now, to give USB docking
+ * stations some time to settle so that we can
+ * properly watch its displays. */
+ if (m->lid_switch_ignore_event_source) {
+ log_debug("Ignoring lid switch request, system startup or resume too close.");
+ return 0;
+ }
}
/* If the key handling is inhibited, don't do anything */
if (inhibit_key > 0) {
if (manager_is_inhibited(m, inhibit_key, INHIBIT_BLOCK, NULL, true, false, 0, NULL)) {
log_debug("Refusing operation, %s is inhibited.", inhibit_what_to_string(inhibit_key));
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index fc8953155..c9c58f3f8 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -1334,12 +1334,15 @@ static int execute_shutdown_or_sleep(
m->action_unit = unit_name;
free(m->action_job);
m->action_job = c;
m->action_what = w;
+ /* Make sure the lid switch is ignored for a while */
+ manager_set_lid_switch_ignore(m, now(CLOCK_MONOTONIC) + IGNORE_LID_SWITCH_SUSPEND_USEC);
+
return 0;
}
static int delay_shutdown_or_sleep(
Manager *m,
InhibitWhat w,
diff --git a/src/login/logind.c b/src/login/logind.c
index 10f61aba3..fd113b3e7 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -141,12 +141,13 @@ void manager_free(Manager *m) {
sd_event_source_unref(m->console_active_event_source);
sd_event_source_unref(m->udev_seat_event_source);
sd_event_source_unref(m->udev_device_event_source);
sd_event_source_unref(m->udev_vcsa_event_source);
sd_event_source_unref(m->udev_button_event_source);
+ sd_event_source_unref(m->lid_switch_ignore_event_source);
if (m->console_active_fd >= 0)
close_nointr_nofail(m->console_active_fd);
if (m->udev_seat_monitor)
udev_monitor_unref(m->udev_seat_monitor);
@@ -956,12 +957,52 @@ static int manager_dispatch_idle_action(sd_event_source *s, uint64_t t, void *us
}
}
return 0;
}
+static int lid_switch_ignore_handler(sd_event_source *e, uint64_t usec, void *userdata) {
+ Manager *m = userdata;
+
+ assert(e);
+ assert(m);
+
+ m->lid_switch_ignore_event_source = sd_event_source_unref(m->lid_switch_ignore_event_source);
+ return 0;
+}
+
+int manager_set_lid_switch_ignore(Manager *m, usec_t until) {
+ int r;
+
+ assert(m);
+
+ if (until <= now(CLOCK_MONOTONIC))
+ return 0;
+
+ /* We want to ignore the lid switch for a while after each
+ * suspend, and after boot-up. Hence let's install a timer for
+ * this. As long as the event source exists we ignore the lid
+ * switch. */
+
+ if (m->lid_switch_ignore_event_source) {
+ usec_t u;
+
+ r = sd_event_source_get_time(m->lid_switch_ignore_event_source, &u);
+ if (r < 0)
+ return r;
+
+ if (until <= u)
+ return 0;
+
+ r = sd_event_source_set_time(m->lid_switch_ignore_event_source, until);
+ } else
+ r = sd_event_add_monotonic(m->event, &m->lid_switch_ignore_event_source, until, 0, lid_switch_ignore_handler, m);
+
+ return r;
+}
+
int manager_startup(Manager *m) {
int r;
Seat *seat;
Session *session;
User *user;
Button *button;
@@ -991,12 +1032,16 @@ int manager_startup(Manager *m) {
r = manager_add_seat(m, "seat0", &m->seat0);
if (r < 0) {
log_error("Failed to add seat0: %s", strerror(-r));
return r;
}
+ r = manager_set_lid_switch_ignore(m, 0 + IGNORE_LID_SWITCH_STARTUP_USEC);
+ if (r < 0)
+ log_warning("Failed to set up lid switch ignore event source: %s", strerror(-r));
+
/* Deserialize state */
r = manager_enumerate_devices(m);
if (r < 0)
log_warning("Device enumeration failed: %s", strerror(-r));
r = manager_enumerate_seats(m);
diff --git a/src/login/logind.h b/src/login/logind.h
index 74d66415e..4bb8e7b65 100644
--- a/src/login/logind.h
+++ b/src/login/logind.h
@@ -39,12 +39,15 @@ typedef struct Manager Manager;
#include "logind-session.h"
#include "logind-user.h"
#include "logind-inhibit.h"
#include "logind-button.h"
#include "logind-action.h"
+#define IGNORE_LID_SWITCH_STARTUP_USEC (3 * USEC_PER_MINUTE)
+#define IGNORE_LID_SWITCH_SUSPEND_USEC (30 * USEC_PER_SEC)
+
struct Manager {
sd_event *event;
sd_bus *bus;
Hashmap *devices;
Hashmap *seats;
@@ -115,12 +118,14 @@ struct Manager {
bool power_key_ignore_inhibited;
bool suspend_key_ignore_inhibited;
bool hibernate_key_ignore_inhibited;
bool lid_switch_ignore_inhibited;
Hashmap *polkit_registry;
+
+ sd_event_source *lid_switch_ignore_event_source;
};
Manager *manager_new(void);
void manager_free(Manager *m);
int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device);
@@ -175,6 +180,8 @@ int manager_job_is_active(Manager *manager, const char *path);
/* gperf lookup function */
const struct ConfigPerfItem* logind_gperf_lookup(const char *key, unsigned length);
int manager_watch_busname(Manager *manager, const char *name);
void manager_drop_busname(Manager *manager, const char *name);
+
+int manager_set_lid_switch_ignore(Manager *m, usec_t until);