chiark / gitweb /
logind: ignore lid switch if more than 1 display is connected
authorLennart Poettering <lennart@poettering.net>
Mon, 3 Mar 2014 18:30:16 +0000 (19:30 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 3 Mar 2014 18:32:43 +0000 (19:32 +0100)
Previously we expected the desktop environment to take an inhibitor
lock, but this opened a race on boot-up where logind might already be
running but no DE is active.

Hence, let's move checking for additional displays into logind. This
also opens up this logic for other DEs, given that only GNOME
implemented the inhibitor logic so far.

man/logind.conf.xml
src/login/logind-action.c
src/login/logind-core.c
src/login/logind.h

index 54cc3790486d8498eb0067c86ed45d122e2ca953..7673201664a2e0038e0dcd2da81702b82ea7de81 100644 (file)
                                 <literal>ignore</literal>, logind will
                                 never handle these keys. If
                                 <literal>lock</literal>, all running
-                                sessions will be screen-locked; otherwise,
-                                the specified action
-                                will be taken in the respective
-                                event. Only input devices with the
+                                sessions will be screen-locked;
+                                otherwise, the specified action will
+                                be taken in the respective event. Only
+                                input devices with the
                                 <literal>power-switch</literal> udev
                                 tag will be watched for key/lid switch
                                 events. <varname>HandlePowerKey=</varname>
                                 default to <literal>suspend</literal>.
                                 <varname>HandleHibernateKey=</varname>
                                 defaults to
-                                <literal>hibernate</literal>.</para></listitem>
+                                <literal>hibernate</literal>. Note
+                                that the lid switch is ignored if the
+                                system is inserted in a docking
+                                station, or if more than one display
+                                is connected.</para></listitem>
                         </varlistentry>
 
                         <varlistentry>
index c04f2107d1787d6f861c21a2568f383d88b157eb..da5a8305fa9af4f1ec248e0fadaa4e086c09a2c5 100644 (file)
@@ -72,10 +72,18 @@ int manager_handle_action(
 
         /* If we are docked don't react to lid closing */
         if (inhibit_key == INHIBIT_HANDLE_LID_SWITCH) {
+                int n;
+
                 if (manager_is_docked(m)) {
                         log_debug("Ignoring lid switch request, system is docked.");
                         return 0;
                 }
+
+                n = manager_count_displays(m);
+                if (n != 1) {
+                        log_debug("Ignoring lid switch request, %s displays connected.");
+                        return 0;
+                }
         }
 
         /* If the key handling is inhibited, don't do anything */
index e4e593fa5b6d8ec8b9ba2233e97c9a21d294c3a7..b8d03c3a59f3dcbd0c02198204759466e214b518 100644 (file)
@@ -32,6 +32,7 @@
 #include "audit.h"
 #include "bus-util.h"
 #include "bus-error.h"
+#include "udev-util.h"
 #include "logind.h"
 
 int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device) {
@@ -276,9 +277,11 @@ int manager_process_seat_device(Manager *m, struct udev_device *d) {
                         return 0;
                 }
 
-                /* ignore non-master devices for unknown seats */
+                seat = hashmap_get(m->seats, sn);
                 master = udev_device_has_tag(d, "master-of-seat");
-                if (!master && !(seat = hashmap_get(m->seats, sn)))
+
+                /* Ignore non-master devices for unknown seats */
+                if (!master && !seat)
                         return 0;
 
                 r = manager_add_device(m, udev_device_get_syspath(d), master, &device);
@@ -514,3 +517,51 @@ bool manager_is_docked(Manager *m) {
 
         return false;
 }
+
+int manager_count_displays(Manager *m) {
+        _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
+        struct udev_list_entry *item = NULL, *first = NULL;
+        int r;
+        int n = 0;
+
+        e = udev_enumerate_new(m->udev);
+        if (!e)
+                return -ENOMEM;
+
+        r = udev_enumerate_add_match_subsystem(e, "drm");
+        if (r < 0)
+                return r;
+
+        r = udev_enumerate_scan_devices(e);
+        if (r < 0)
+                return r;
+
+        first = udev_enumerate_get_list_entry(e);
+        udev_list_entry_foreach(item, first) {
+                _cleanup_udev_device_unref_ struct udev_device *d = NULL;
+                struct udev_device *p;
+                const char *status;
+
+                d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
+                if (!d)
+                        return -ENOMEM;
+
+                p = udev_device_get_parent(d);
+                if (!p)
+                        return -ENOMEM;
+
+                /* If the parent shares the same subsystem as the
+                 * device we are looking at then it is a connector,
+                 * which is what we are interested in. */
+                if (!streq_ptr(udev_device_get_subsystem(p), "drm"))
+                        continue;
+
+                /* We count any connector which is not explicitly
+                 * "disconnected" as connected. */
+                status = udev_device_get_sysattr_value(d, "status");
+                if (!streq_ptr(status, "disconnected"))
+                        n++;
+        }
+
+        return n;
+}
index 0344acc8bd513845b45ca6c4c4c85165178f389d..74d66415ee6f35e6e323d5a45325247026266789 100644 (file)
@@ -149,6 +149,7 @@ int manager_get_user_by_pid(Manager *m, pid_t pid, User **user);
 int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session);
 
 bool manager_is_docked(Manager *m);
+int manager_count_displays(Manager *m);
 
 extern const sd_bus_vtable manager_vtable[];