chiark / gitweb /
terminal: provide display dimensions to API users
[elogind.git] / src / libsystemd-terminal / idev-evdev.c
index 9e2dc811ef426ab5b927064a8bb6efbc4de554f7..63fa89e47d685536de346287e9a884ee11b8d856 100644 (file)
@@ -41,7 +41,7 @@ typedef struct unmanaged_evdev unmanaged_evdev;
 typedef struct managed_evdev managed_evdev;
 
 struct idev_evdev {
-        struct idev_element element;
+        idev_element element;
         struct libevdev *evdev;
         int fd;
         sd_event_source *fd_src;
@@ -49,19 +49,17 @@ struct idev_evdev {
 
         bool unsync : 1;                /* not in-sync with kernel */
         bool resync : 1;                /* re-syncing with kernel */
+        bool running : 1;
 };
 
 struct unmanaged_evdev {
-        struct idev_evdev evdev;
+        idev_evdev evdev;
         char *devnode;
 };
 
 struct managed_evdev {
-        struct idev_evdev evdev;
+        idev_evdev evdev;
         dev_t devnum;
-
-        sd_bus_slot *slot_pause_device;
-        sd_bus_slot *slot_resume_device;
         sd_bus_slot *slot_take_device;
 
         bool requested : 1;             /* TakeDevice() was sent */
@@ -101,7 +99,16 @@ static void idev_evdev_name(char *out, dev_t devnum) {
         sprintf(out, "evdev/%u:%u", major(devnum), minor(devnum));
 }
 
-static int idev_evdev_raise(idev_evdev *evdev, struct input_event *event) {
+static int idev_evdev_feed_resync(idev_evdev *evdev) {
+        idev_data data = {
+                .type = IDEV_DATA_RESYNC,
+                .resync = evdev->resync,
+        };
+
+        return idev_element_feed(&evdev->element, &data);
+}
+
+static int idev_evdev_feed_evdev(idev_evdev *evdev, struct input_event *event) {
         idev_data data = {
                 .type = IDEV_DATA_EVDEV,
                 .resync = evdev->resync,
@@ -155,7 +162,6 @@ static int idev_evdev_io(idev_evdev *evdev) {
          * case we cannot keep up with the kernel.
          * TODO: Make sure libevdev always reports SYN_DROPPED to us, regardless
          * whether any event was synced afterwards.
-         * TODO: Forward SYN_DROPPED to attached devices.
          */
 
         flags = LIBEVDEV_READ_FLAG_NORMAL;
@@ -190,7 +196,7 @@ static int idev_evdev_io(idev_evdev *evdev) {
                 } else if (r == LIBEVDEV_READ_STATUS_SYNC) {
                         if (evdev->resync) {
                                 /* sync-event */
-                                r = idev_evdev_raise(evdev, &ev);
+                                r = idev_evdev_feed_evdev(evdev, &ev);
                                 if (r != 0) {
                                         error = r;
                                         break;
@@ -199,10 +205,15 @@ static int idev_evdev_io(idev_evdev *evdev) {
                                 /* start of sync */
                                 evdev->resync = true;
                                 flags = LIBEVDEV_READ_FLAG_SYNC;
+                                r = idev_evdev_feed_resync(evdev);
+                                if (r != 0) {
+                                        error = r;
+                                        break;
+                                }
                         }
                 } else {
                         /* normal event */
-                        r = idev_evdev_raise(evdev, &ev);
+                        r = idev_evdev_feed_evdev(evdev, &ev);
                         if (r != 0) {
                                 error = r;
                                 break;
@@ -268,6 +279,12 @@ static void idev_evdev_enable(idev_evdev *evdev) {
         assert(evdev->fd_src);
         assert(evdev->idle_src);
 
+        if (evdev->running)
+                return;
+        if (evdev->fd < 0 || evdev->element.n_open < 1 || !evdev->element.enabled)
+                return;
+
+        evdev->running = true;
         sd_event_source_set_enabled(evdev->fd_src, SD_EVENT_ON);
         sd_event_source_set_enabled(evdev->idle_src, SD_EVENT_ONESHOT);
 }
@@ -277,6 +294,11 @@ static void idev_evdev_disable(idev_evdev *evdev) {
         assert(evdev->fd_src);
         assert(evdev->idle_src);
 
+        if (!evdev->running)
+                return;
+
+        evdev->running = false;
+        idev_evdev_feed_resync(evdev);
         sd_event_source_set_enabled(evdev->fd_src, SD_EVENT_OFF);
         sd_event_source_set_enabled(evdev->idle_src, SD_EVENT_OFF);
 }
@@ -288,9 +310,7 @@ static int idev_evdev_resume(idev_evdev *evdev, int dev_fd) {
 
         if (fd < 0 || evdev->fd == fd) {
                 fd = -1;
-                if (evdev->fd >= 0 && e->n_open > 0 && e->enabled)
-                        idev_evdev_enable(evdev);
-
+                idev_evdev_enable(evdev);
                 return 0;
         }
 
@@ -307,18 +327,14 @@ static int idev_evdev_resume(idev_evdev *evdev, int dev_fd) {
 
         flags = fcntl(fd, F_GETFL, 0);
         if (flags < 0)
-                return r;
+                return -errno;
 
         flags &= O_ACCMODE;
         if (flags == O_WRONLY)
                 return -EACCES;
 
         evdev->element.readable = true;
-        evdev->element.writable = true;
-        if (flags == O_RDONLY)
-                evdev->element.writable = false;
-        else if (flags == O_WRONLY)
-                evdev->element.readable = false;
+        evdev->element.writable = !(flags & O_RDONLY);
 
         /*
          * TODO: We *MUST* re-sync the device so we get a delta of the changed
@@ -355,15 +371,14 @@ static int idev_evdev_resume(idev_evdev *evdev, int dev_fd) {
                 return r;
         }
 
-        if (e->n_open < 1 || !e->enabled) {
-                sd_event_source_set_enabled(evdev->fd_src, SD_EVENT_OFF);
-                sd_event_source_set_enabled(evdev->idle_src, SD_EVENT_OFF);
-        }
+        sd_event_source_set_enabled(evdev->fd_src, SD_EVENT_OFF);
+        sd_event_source_set_enabled(evdev->idle_src, SD_EVENT_OFF);
 
         evdev->unsync = true;
         evdev->fd = fd;
-
         fd = -1;
+
+        idev_evdev_enable(evdev);
         return 0;
 }
 
@@ -375,12 +390,11 @@ static void idev_evdev_pause(idev_evdev *evdev, bool release) {
 
         log_debug("idev-evdev: %s/%s: pause", e->session->name, e->name);
 
+        idev_evdev_disable(evdev);
         if (release) {
                 evdev->idle_src = sd_event_source_unref(evdev->idle_src);
                 evdev->fd_src = sd_event_source_unref(evdev->fd_src);
                 evdev->fd = safe_close(evdev->fd);
-        } else {
-                idev_evdev_disable(evdev);
         }
 }
 
@@ -563,7 +577,7 @@ static int managed_evdev_take_device_fn(sd_bus *bus,
         return 0;
 }
 
-static void managed_evdev_resume(idev_element *e) {
+static void managed_evdev_enable(idev_element *e) {
         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
         managed_evdev *em = managed_evdev_from_element(e);
         idev_session *s = e->session;
@@ -611,7 +625,7 @@ error:
                   s->name, e->name, strerror(-r));
 }
 
-static void managed_evdev_pause(idev_element *e) {
+static void managed_evdev_disable(idev_element *e) {
         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
         managed_evdev *em = managed_evdev_from_element(e);
         idev_session *s = e->session;
@@ -669,24 +683,45 @@ static void managed_evdev_pause(idev_element *e) {
                           s->name, e->name, strerror(-r));
 }
 
-static int managed_evdev_pause_device_fn(sd_bus *bus,
-                                         sd_bus_message *signal,
-                                         void *userdata,
-                                         sd_bus_error *ret_error) {
-        managed_evdev *em = userdata;
-        idev_element *e = &em->evdev.element;
+static void managed_evdev_resume(idev_element *e, int fd) {
+        managed_evdev *em = managed_evdev_from_element(e);
+        idev_session *s = e->session;
+        int r;
+
+        /*
+         * We get ResumeDevice signals whenever logind resumed a previously
+         * paused device. The arguments contain the major/minor number of the
+         * related device and a new file-descriptor for the freshly opened
+         * device-node. We take the file-descriptor and immediately resume the
+         * device.
+         */
+
+        fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+        if (fd < 0) {
+                log_debug("idev-evdev: %s/%s: cannot duplicate evdev fd: %m",
+                          s->name, e->name);
+                return;
+        }
+
+        r = idev_evdev_resume(&em->evdev, fd);
+        if (r < 0)
+                log_debug("idev-evdev: %s/%s: cannot resume: %s",
+                          s->name, e->name, strerror(-r));
+
+        return;
+}
+
+static void managed_evdev_pause(idev_element *e, const char *mode) {
+        managed_evdev *em = managed_evdev_from_element(e);
         idev_session *s = e->session;
         idev_context *c = s->context;
-        uint32_t major, minor;
-        const char *mode;
         int r;
 
         /*
          * We get PauseDevice() signals from logind whenever a device we
          * requested was, or is about to be, paused. Arguments are major/minor
          * number of the device and the mode of the operation.
-         * In case the event is not about our device, we ignore it. Otherwise,
-         * we treat it as asynchronous access-revocation (as if we got HUP on
+         * We treat it as asynchronous access-revocation (as if we got HUP on
          * the device fd). Note that we might have already treated the HUP
          * event via EPOLLHUP, whichever comes first.
          *
@@ -711,17 +746,6 @@ static int managed_evdev_pause_device_fn(sd_bus *bus,
          * acknowledge the request.
          */
 
-        r = sd_bus_message_read(signal, "uus", &major, &minor, &mode);
-        if (r < 0) {
-                log_debug("idev-evdev: %s/%s: erroneous PauseDevice signal",
-                          s->name, e->name);
-                return 0;
-        }
-
-        /* not our device? */
-        if (makedev(major, minor) != em->devnum)
-                return 0;
-
         idev_evdev_pause(&em->evdev, true);
 
         if (streq(mode, "pause")) {
@@ -746,7 +770,7 @@ static int managed_evdev_pause_device_fn(sd_bus *bus,
                                                    "org.freedesktop.login1.Session",
                                                    "PauseDeviceComplete");
                 if (r >= 0) {
-                        r = sd_bus_message_append(m, "uu", major, minor);
+                        r = sd_bus_message_append(m, "uu", major(em->devnum), minor(em->devnum));
                         if (r >= 0)
                                 r = sd_bus_send(c->sysbus, m, NULL);
                 }
@@ -755,99 +779,6 @@ static int managed_evdev_pause_device_fn(sd_bus *bus,
                         log_debug("idev-evdev: %s/%s: cannot send PauseDeviceComplete: %s",
                                   s->name, e->name, strerror(-r));
         }
-
-        return 0;
-}
-
-static int managed_evdev_resume_device_fn(sd_bus *bus,
-                                          sd_bus_message *signal,
-                                          void *userdata,
-                                          sd_bus_error *ret_error) {
-        managed_evdev *em = userdata;
-        idev_element *e = &em->evdev.element;
-        idev_session *s = e->session;
-        uint32_t major, minor;
-        int r, fd;
-
-        /*
-         * We get ResumeDevice signals whenever logind resumed a previously
-         * paused device. The arguments contain the major/minor number of the
-         * related device and a new file-descriptor for the freshly opened
-         * device-node.
-         * If the signal is not about our device, we simply ignore it.
-         * Otherwise, we take the file-descriptor and immediately resume the
-         * device.
-         */
-
-        r = sd_bus_message_read(signal, "uuh", &major, &minor, &fd);
-        if (r < 0) {
-                log_debug("idev-evdev: %s/%s: erroneous ResumeDevice signal",
-                          s->name, e->name);
-                return 0;
-        }
-
-        /* not our device? */
-        if (makedev(major, minor) != em->devnum)
-                return 0;
-
-        fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
-        if (fd < 0) {
-                log_debug("idev-evdev: %s/%s: cannot duplicate evdev fd: %m",
-                          s->name, e->name);
-                return 0;
-        }
-
-        r = idev_evdev_resume(&em->evdev, fd);
-        if (r < 0)
-                log_debug("idev-evdev: %s/%s: cannot resume: %s",
-                          s->name, e->name, strerror(-r));
-
-        return 0;
-}
-
-static int managed_evdev_setup_bus(managed_evdev *em) {
-        idev_element *e = &em->evdev.element;
-        idev_session *s = e->session;
-        idev_context *c = s->context;
-        _cleanup_free_ char *match = NULL;
-        int r;
-
-        match = strjoin("type='signal',"
-                        "sender='org.freedesktop.login1',"
-                        "interface='org.freedesktop.login1.Session',"
-                        "member='PauseDevice',"
-                        "path='", s->path, "'",
-                        NULL);
-        if (!match)
-                return -ENOMEM;
-
-        r = sd_bus_add_match(c->sysbus,
-                             &em->slot_pause_device,
-                             match,
-                             managed_evdev_pause_device_fn,
-                             em);
-        if (r < 0)
-                return r;
-
-        free(match);
-        match = strjoin("type='signal',"
-                        "sender='org.freedesktop.login1',"
-                        "interface='org.freedesktop.login1.Session',"
-                        "member='ResumeDevice',"
-                        "path='", s->path, "'",
-                        NULL);
-        if (!match)
-                return -ENOMEM;
-
-        r = sd_bus_add_match(c->sysbus,
-                             &em->slot_resume_device,
-                             match,
-                             managed_evdev_resume_device_fn,
-                             em);
-        if (r < 0)
-                return r;
-
-        return 0;
 }
 
 static int managed_evdev_new(idev_element **out, idev_session *s, struct udev_device *ud) {
@@ -858,7 +789,6 @@ static int managed_evdev_new(idev_element **out, idev_session *s, struct udev_de
         int r;
 
         assert_return(s, -EINVAL);
-        assert_return(s->context->sysbus, -EINVAL);
         assert_return(s->managed, -EINVAL);
         assert_return(s->context->sysbus, -EINVAL);
         assert_return(ud, -EINVAL);
@@ -877,10 +807,6 @@ static int managed_evdev_new(idev_element **out, idev_session *s, struct udev_de
         em->evdev = IDEV_EVDEV_INIT(&managed_evdev_vtable, s);
         em->devnum = devnum;
 
-        r = managed_evdev_setup_bus(em);
-        if (r < 0)
-                return r;
-
         r = idev_element_add(e, name);
         if (r < 0)
                 return r;
@@ -894,18 +820,18 @@ static int managed_evdev_new(idev_element **out, idev_session *s, struct udev_de
 static void managed_evdev_free(idev_element *e) {
         managed_evdev *em = managed_evdev_from_element(e);
 
-        em->slot_resume_device = sd_bus_slot_unref(em->slot_resume_device);
-        em->slot_pause_device = sd_bus_slot_unref(em->slot_pause_device);
         idev_evdev_destroy(&em->evdev);
         free(em);
 }
 
 static const idev_element_vtable managed_evdev_vtable = {
         .free                   = managed_evdev_free,
-        .enable                 = managed_evdev_resume,
-        .disable                = managed_evdev_pause,
-        .open                   = managed_evdev_resume,
-        .close                  = managed_evdev_pause,
+        .enable                 = managed_evdev_enable,
+        .disable                = managed_evdev_disable,
+        .open                   = managed_evdev_enable,
+        .close                  = managed_evdev_disable,
+        .resume                 = managed_evdev_resume,
+        .pause                  = managed_evdev_pause,
 };
 
 /*