diff --git a/hw/hid.c b/hw/hid.c
index 0fee3b6..89b5415 100644
--- a/hw/hid.c
+++ b/hw/hid.c
@@ -71,12 +71,38 @@ static const uint8_t hid_usage_keys[0x100] = {
 
 bool hid_has_events(HIDState *hs)
 {
-    return hs->n > 0;
+    return hs->n > 0 || hs->idle_pending;
 }
 
-void hid_set_next_idle(HIDState *hs, int64_t curtime)
+static void hid_idle_timer(void *opaque)
 {
-    hs->next_idle_clock = curtime + (get_ticks_per_sec() * hs->idle * 4) / 1000;
+    HIDState *hs = opaque;
+
+    hs->idle_pending = true;
+    hs->event(hs);
+}
+
+static void hid_del_idle_timer(HIDState *hs)
+{
+    if (hs->idle_timer) {
+        qemu_del_timer(hs->idle_timer);
+        qemu_free_timer(hs->idle_timer);
+        hs->idle_timer = NULL;
+    }
+}
+
+void hid_set_next_idle(HIDState *hs)
+{
+    if (hs->idle) {
+        uint64_t expire_time = qemu_get_clock_ns(vm_clock) +
+                               get_ticks_per_sec() * hs->idle * 4 / 1000;
+        if (!hs->idle_timer) {
+            hs->idle_timer = qemu_new_timer_ns(vm_clock, hid_idle_timer, hs);
+        }
+        qemu_mod_timer_ns(hs->idle_timer, expire_time);
+    } else {
+        hid_del_idle_timer(hs);
+    }
 }
 
 static void hid_pointer_event_clear(HIDPointerEvent *e, int buttons)
@@ -232,6 +258,8 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
     int index;
     HIDPointerEvent *e;
 
+    hs->idle_pending = false;
+
     hid_pointer_activate(hs);
 
     /* When the buffer is empty, return the last event.  Relative
@@ -319,6 +347,8 @@ int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len)
 
 int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len)
 {
+    hs->idle_pending = false;
+
     if (len < 2) {
         return 0;
     }
@@ -377,6 +407,8 @@ void hid_reset(HIDState *hs)
     hs->n = 0;
     hs->protocol = 1;
     hs->idle = 0;
+    hs->idle_pending = false;
+    hid_del_idle_timer(hs);
 }
 
 void hid_free(HIDState *hs)
@@ -390,6 +422,7 @@ void hid_free(HIDState *hs)
         qemu_remove_mouse_event_handler(hs->ptr.eh_entry);
         break;
     }
+    hid_del_idle_timer(hs);
 }
 
 void hid_init(HIDState *hs, int kind, HIDEventFunc event)
@@ -412,9 +445,7 @@ static int hid_post_load(void *opaque, int version_id)
 {
     HIDState *s = opaque;
 
-    if (s->idle) {
-        hid_set_next_idle(s, qemu_get_clock_ns(vm_clock));
-    }
+    hid_set_next_idle(s);
     return 0;
 }
 
diff --git a/hw/hid.h b/hw/hid.h
index 100b121..56c71ed 100644
--- a/hw/hid.h
+++ b/hw/hid.h
@@ -43,7 +43,8 @@ struct HIDState {
     int kind;
     int32_t protocol;
     uint8_t idle;
-    int64_t next_idle_clock;
+    bool idle_pending;
+    QEMUTimer *idle_timer;
     HIDEventFunc event;
 };
 
@@ -52,7 +53,7 @@ void hid_reset(HIDState *hs);
 void hid_free(HIDState *hs);
 
 bool hid_has_events(HIDState *hs);
-void hid_set_next_idle(HIDState *hs, int64_t curtime);
+void hid_set_next_idle(HIDState *hs);
 void hid_pointer_activate(HIDState *hs);
 int hid_pointer_poll(HIDState *hs, uint8_t *buf, int len);
 int hid_keyboard_poll(HIDState *hs, uint8_t *buf, int len);
diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c
index ce38fef..b4ace04 100644
--- a/hw/usb/dev-hid.c
+++ b/hw/usb/dev-hid.c
@@ -501,7 +501,7 @@ static void usb_hid_handle_control(USBDevice *dev, USBPacket *p,
         break;
     case SET_IDLE:
         hs->idle = (uint8_t) (value >> 8);
-        hid_set_next_idle(hs, qemu_get_clock_ns(vm_clock));
+        hid_set_next_idle(hs);
         if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
             hid_pointer_activate(hs);
         }
@@ -523,16 +523,14 @@ static void usb_hid_handle_data(USBDevice *dev, USBPacket *p)
     switch (p->pid) {
     case USB_TOKEN_IN:
         if (p->ep->nr == 1) {
-            int64_t curtime = qemu_get_clock_ns(vm_clock);
             if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
                 hid_pointer_activate(hs);
             }
-            if (!hid_has_events(hs) &&
-                (!hs->idle || hs->next_idle_clock - curtime > 0)) {
+            if (!hid_has_events(hs)) {
                 p->status = USB_RET_NAK;
                 return;
             }
-            hid_set_next_idle(hs, curtime);
+            hid_set_next_idle(hs);
             if (hs->kind == HID_MOUSE || hs->kind == HID_TABLET) {
                 len = hid_pointer_poll(hs, buf, p->iov.size);
             } else if (hs->kind == HID_KEYBOARD) {
