bac6df9
From 602a41c22ac2df33b4b5e5083719c1cfaf58acf9 Mon Sep 17 00:00:00 2001
bac6df9
From: Lennart Poettering <lennart@poettering.net>
bac6df9
Date: Wed, 17 Jun 2015 00:24:05 +0200
bac6df9
Subject: [PATCH] logind: rework display counting when detecting whether the
bac6df9
 system is docked
bac6df9
bac6df9
Previously, we'd just count connected displays, and if there was 2 or
bac6df9
more we assumed a "docked" state.
bac6df9
bac6df9
With this change we now:
bac6df9
bac6df9
- Only count external displays, ignore internal ones (which we detect by
bac6df9
  checking the connector name against a whitelist of known external plug
bac6df9
  types)
bac6df9
bac6df9
- We ignore connectors which are explicitly disabled
bac6df9
bac6df9
- We then compare the count with >= 1 rather than >= 2 as before
bac6df9
bac6df9
This new logic has the benefit that systems that disconnect the internal
bac6df9
display when the lid is closed are better supported. Also, explicitly
bac6df9
disabled ports do not confuse the algorithm anymore.
bac6df9
bac6df9
This new algorithm has been suggested here:
bac6df9
bac6df9
http://lists.freedesktop.org/archives/intel-gfx/2015-June/068821.html
bac6df9
bac6df9
This also makes two functions static, that are not used outside of their
bac6df9
.c files.
bac6df9
---
bac6df9
 src/login/logind-button.c |  2 +-
bac6df9
 src/login/logind-core.c   | 49 ++++++++++++++++++++++++++++++++++++++++-------
bac6df9
 src/login/logind-dbus.c   |  2 +-
bac6df9
 src/login/logind.h        |  4 +---
bac6df9
 4 files changed, 45 insertions(+), 12 deletions(-)
bac6df9
bac6df9
diff --git a/src/login/logind-button.c b/src/login/logind-button.c
bac6df9
index 8079d0b..210b889 100644
bac6df9
--- a/src/login/logind-button.c
bac6df9
+++ b/src/login/logind-button.c
bac6df9
@@ -100,7 +100,7 @@ static void button_lid_switch_handle_action(Manager *manager, bool is_edge) {
bac6df9
         assert(manager);
bac6df9
 
bac6df9
         /* If we are docked, handle the lid switch differently */
bac6df9
-        if (manager_is_docked_or_multiple_displays(manager))
bac6df9
+        if (manager_is_docked_or_external_displays(manager))
bac6df9
                 handle_action = manager->handle_lid_switch_docked;
bac6df9
         else
bac6df9
                 handle_action = manager->handle_lid_switch;
bac6df9
diff --git a/src/login/logind-core.c b/src/login/logind-core.c
bac6df9
index f9e6ddf..a6c01f7 100644
bac6df9
--- a/src/login/logind-core.c
bac6df9
+++ b/src/login/logind-core.c
bac6df9
@@ -477,7 +477,7 @@ int manager_spawn_autovt(Manager *m, unsigned int vtnr) {
bac6df9
         return r;
bac6df9
 }
bac6df9
 
bac6df9
-bool manager_is_docked(Manager *m) {
bac6df9
+static bool manager_is_docked(Manager *m) {
bac6df9
         Iterator i;
bac6df9
         Button *b;
bac6df9
 
bac6df9
@@ -488,7 +488,7 @@ bool manager_is_docked(Manager *m) {
bac6df9
         return false;
bac6df9
 }
bac6df9
 
bac6df9
-int manager_count_displays(Manager *m) {
bac6df9
+static int manager_count_external_displays(Manager *m) {
bac6df9
         _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
bac6df9
         struct udev_list_entry *item = NULL, *first = NULL;
bac6df9
         int r;
bac6df9
@@ -510,7 +510,8 @@ int manager_count_displays(Manager *m) {
bac6df9
         udev_list_entry_foreach(item, first) {
bac6df9
                 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
bac6df9
                 struct udev_device *p;
bac6df9
-                const char *status;
bac6df9
+                const char *status, *enabled, *dash, *nn, *i;
bac6df9
+                bool external = false;
bac6df9
 
bac6df9
                 d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
bac6df9
                 if (!d)
bac6df9
@@ -526,6 +527,40 @@ int manager_count_displays(Manager *m) {
bac6df9
                 if (!streq_ptr(udev_device_get_subsystem(p), "drm"))
bac6df9
                         continue;
bac6df9
 
bac6df9
+                nn = udev_device_get_sysname(d);
bac6df9
+                if (!nn)
bac6df9
+                        continue;
bac6df9
+
bac6df9
+                /* Ignore internal displays: the type is encoded in
bac6df9
+                 * the sysfs name, as the second dash seperated item
bac6df9
+                 * (the first is the card name, the last the connector
bac6df9
+                 * number). We implement a whitelist of external
bac6df9
+                 * displays here, rather than a whitelist, to ensure
bac6df9
+                 * we don't block suspends too eagerly. */
bac6df9
+                dash = strchr(nn, '-');
bac6df9
+                if (!dash)
bac6df9
+                        continue;
bac6df9
+
bac6df9
+                dash++;
bac6df9
+                FOREACH_STRING(i, "VGA-", "DVI-I-", "DVI-D-", "DVI-A-"
bac6df9
+                               "Composite-", "SVIDEO-", "Component-",
bac6df9
+                               "DIN-", "DP-", "HDMI-A-", "HDMI-B-", "TV-") {
bac6df9
+
bac6df9
+                        if (startswith(dash, i)) {
bac6df9
+                                external = true;
bac6df9
+                                break;
bac6df9
+                        }
bac6df9
+                }
bac6df9
+                if (!external)
bac6df9
+                        continue;
bac6df9
+
bac6df9
+                /* Ignore ports that are not enabled */
bac6df9
+                enabled = udev_device_get_sysattr_value(d, "enabled");
bac6df9
+                if (!enabled)
bac6df9
+                        continue;
bac6df9
+                if (!streq_ptr(enabled, "enabled"))
bac6df9
+                        continue;
bac6df9
+
bac6df9
                 /* We count any connector which is not explicitly
bac6df9
                  * "disconnected" as connected. */
bac6df9
                 status = udev_device_get_sysattr_value(d, "status");
bac6df9
@@ -536,7 +571,7 @@ int manager_count_displays(Manager *m) {
bac6df9
         return n;
bac6df9
 }
bac6df9
 
bac6df9
-bool manager_is_docked_or_multiple_displays(Manager *m) {
bac6df9
+bool manager_is_docked_or_external_displays(Manager *m) {
bac6df9
         int n;
bac6df9
 
bac6df9
         /* If we are docked don't react to lid closing */
bac6df9
@@ -547,11 +582,11 @@ bool manager_is_docked_or_multiple_displays(Manager *m) {
bac6df9
 
bac6df9
         /* If we have more than one display connected,
bac6df9
          * assume that we are docked. */
bac6df9
-        n = manager_count_displays(m);
bac6df9
+        n = manager_count_external_displays(m);
bac6df9
         if (n < 0)
bac6df9
                 log_warning_errno(n, "Display counting failed: %m");
bac6df9
-        else if (n > 1) {
bac6df9
-                log_debug("Multiple (%i) displays connected.", n);
bac6df9
+        else if (n >= 1) {
bac6df9
+                log_debug("External (%i) displays connected.", n);
bac6df9
                 return true;
bac6df9
         }
bac6df9
 
bac6df9
diff --git a/src/login/logind.h b/src/login/logind.h
bac6df9
index cd226f5..feb381d 100644
bac6df9
--- a/src/login/logind.h
bac6df9
+++ b/src/login/logind.h
bac6df9
@@ -156,9 +156,7 @@ int manager_get_idle_hint(Manager *m, dual_timestamp *t);
bac6df9
 int manager_get_user_by_pid(Manager *m, pid_t pid, User **user);
bac6df9
 int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session);
bac6df9
 
bac6df9
-bool manager_is_docked(Manager *m);
bac6df9
-int manager_count_displays(Manager *m);
bac6df9
-bool manager_is_docked_or_multiple_displays(Manager *m);
bac6df9
+bool manager_is_docked_or_external_displays(Manager *m);
bac6df9
 
bac6df9
 extern const sd_bus_vtable manager_vtable[];
bac6df9
 
bac6df9
-- 
bac6df9
2.4.3
bac6df9