chiark / gitweb /
logind: ignore lid switch events for 30s after each suspend and 3min after startup
authorLennart Poettering <lennart@poettering.net>
Mon, 3 Mar 2014 19:49:33 +0000 (20:49 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 3 Mar 2014 19:57:09 +0000 (20:57 +0100)
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.

src/login/logind-action.c
src/login/logind-dbus.c
src/login/logind.c
src/login/logind.h

index c9d8bc5413cd4757f0cc22a6a007b1f3eb067086..ae7b35055a86b48013870b1a6057655ad7c1cfc2 100644 (file)
@@ -70,20 +70,33 @@ int manager_handle_action(
                 return 0;
         }
 
                 return 0;
         }
 
-        /* If we are docked don't react to lid closing */
         if (inhibit_key == INHIBIT_HANDLE_LID_SWITCH) {
                 int n;
 
         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 (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;
                 }
                 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 the key handling is inhibited, don't do anything */
index fc8953155d56bcc2a41b2f48d62cd3d058e4c530..c9c58f3f823701f35af3311eb61a3c7205d890b7 100644 (file)
@@ -1337,6 +1337,9 @@ static int execute_shutdown_or_sleep(
         m->action_job = c;
         m->action_what = w;
 
         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;
 }
 
         return 0;
 }
 
index 10f61aba388d8a42c0230239dc751e4e667d8473..fd113b3e79437db5451b63eaeedaf753dcf09ff9 100644 (file)
@@ -144,6 +144,7 @@ void manager_free(Manager *m) {
         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->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->console_active_fd >= 0)
                 close_nointr_nofail(m->console_active_fd);
@@ -959,6 +960,46 @@ static int manager_dispatch_idle_action(sd_event_source *s, uint64_t t, void *us
         return 0;
 }
 
         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;
 int manager_startup(Manager *m) {
         int r;
         Seat *seat;
@@ -994,6 +1035,10 @@ int manager_startup(Manager *m) {
                 return 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)
         /* Deserialize state */
         r = manager_enumerate_devices(m);
         if (r < 0)
index 74d66415ee6f35e6e323d5a45325247026266789..4bb8e7b65abae4f12f01bb8024439ce1772d3bf4 100644 (file)
@@ -42,6 +42,9 @@ typedef struct Manager Manager;
 #include "logind-button.h"
 #include "logind-action.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;
 struct Manager {
         sd_event *event;
         sd_bus *bus;
@@ -118,6 +121,8 @@ struct Manager {
         bool lid_switch_ignore_inhibited;
 
         Hashmap *polkit_registry;
         bool lid_switch_ignore_inhibited;
 
         Hashmap *polkit_registry;
+
+        sd_event_source *lid_switch_ignore_event_source;
 };
 
 Manager *manager_new(void);
 };
 
 Manager *manager_new(void);
@@ -178,3 +183,5 @@ const struct ConfigPerfItem* logind_gperf_lookup(const char *key, unsigned lengt
 
 int manager_watch_busname(Manager *manager, const char *name);
 void manager_drop_busname(Manager *manager, const char *name);
 
 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);