]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
[Wayland DnD] Part1: Add 'fakeMouse' module which is used to simulate the
authorOliver Kurth <okurth@vmware.com>
Tue, 24 Apr 2018 00:08:18 +0000 (17:08 -0700)
committerOliver Kurth <okurth@vmware.com>
Tue, 24 Apr 2018 00:08:18 +0000 (17:08 -0700)
       mouse motion under Wayland

For Linux guest with X11, the VMTools uses the X system APIs to simulate
the mouse motion, but these X11 System APIs do not work in Wayland. Need
to pick up another method to simulate the mouse motion.

Another way to simulate the mouse motion is using uinput module.

uinput is a kernel module that makes it possible to emulate input devices
from userspace. By writing to /dev/uinput (or /dev/input/uinput) device,
 a process can create a virtual input device with specific capabilities.
Once this virtual device is created, the process can send events through
it, that will be delivered to userspace and in-kernel consumers.

Here is the link which contains more information about the uinput module:

https://www.kernel.org/doc/html/v4.12/input/uinput.html

This patch adds a new module named 'fakeMouse' which is used to simulate the mouse motion under Wayland.

This patch is part of the new feature 'Wayland support in Linux guest'.

open-vm-tools/services/plugins/dndcp/Makefile.am
open-vm-tools/services/plugins/dndcp/fakeMouseWayland/fakeMouseWayland.cpp [new file with mode: 0644]
open-vm-tools/services/plugins/dndcp/fakeMouseWayland/fakeMouseWayland.h [new file with mode: 0644]

index d1543054c03b8624be74742e4ee3402f50b69c37..e7eec8619d94180dbb9167d7350d6dbb2dbc726e 100644 (file)
@@ -27,6 +27,7 @@ libdndcp_la_CPPFLAGS += -I$(top_srcdir)/services/plugins/dndcp/dnd
 libdndcp_la_CPPFLAGS += -I$(top_srcdir)/services/plugins/dndcp/dndGuest
 libdndcp_la_CPPFLAGS += -I$(top_srcdir)/services/plugins/dndcp/stringxx
 libdndcp_la_CPPFLAGS += -I$(top_srcdir)/services/plugins/dndcp/xutils
+libdndcp_la_CPPFLAGS += -I$(top_srcdir)/services/plugins/dndcp/fakeMouse
 libdndcp_la_CPPFLAGS += -I$(top_srcdir)/include
 
 # Passing C++ related flags to CPPFLAGS generates error.
@@ -75,6 +76,8 @@ libdndcp_la_SOURCES += dndGuest/dndCPTransportGuestRpc.cpp
 libdndcp_la_SOURCES += stringxx/string.cc
 libdndcp_la_SOURCES += xutils/xutils.cc
 
+libdndcp_la_SOURCES += fakeMouseWayland/fakeMouseWayland.cpp
+
 libdndcp_la_SOURCES += copyPasteCompat.c
 libdndcp_la_SOURCES += copyPasteCompatX11.c
 libdndcp_la_SOURCES += copyPasteDnDWrapper.cpp
diff --git a/open-vm-tools/services/plugins/dndcp/fakeMouseWayland/fakeMouseWayland.cpp b/open-vm-tools/services/plugins/dndcp/fakeMouseWayland/fakeMouseWayland.cpp
new file mode 100644 (file)
index 0000000..2f30cd2
--- /dev/null
@@ -0,0 +1,332 @@
+/*********************************************************
+ * Copyright (C) 2018 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
+ *
+ *********************************************************/
+
+/**
+ * @file fakeMouseWayland.cpp --
+ *
+ *    Implement the methods that simulates the mouse motion.
+ */
+
+#define G_LOG_DOMAIN "dndcp"
+
+#include "fakeMouseWayland.h"
+
+#include <linux/input.h>
+#include <linux/ioctl.h>
+#include <linux/uinput.h>
+#include <glib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#define UINPUT_DND_POINTER_NAME "VMware DnD UInput pointer"
+
+
+// The handle for the uinput device.
+static int uinput_fd = -1;
+
+// Indicates if the uinput device is created
+static bool isInit = false;
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * FakeMouse_IsInit --
+ *
+ *      Check if the uinput device is created.
+ *
+ * Results:
+ *      True if the uinput device is created.
+ *
+ * Side effects:
+ *      None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+bool
+FakeMouse_IsInit()
+{
+   return isInit;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * FakeMouse_Init --
+ *
+ *      Initialize the uinput device.
+ *
+ * Results:
+ *      True if the uinput device is created successfully.
+ *
+ * Side effects:
+ *      uinput device is created if succeed.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+bool
+FakeMouse_Init(int fd,       // fd for uinput
+               int width,    // width of the screen
+               int height)   // height of the screen
+{
+   if (FakeMouse_IsInit()) {
+      return true;
+   }
+
+   g_debug("%s: Init the uinput device. fd:%d, w:%d, h:%d\n",
+           __FUNCTION__, fd, width, height);
+
+   uinput_fd = fd;
+   if (uinput_fd == -1) {
+      return false;
+   }
+
+   /*
+    * The uinput old interface is used for compatibility.
+    * For more information please refer to:
+    * https://www.kernel.org/doc/html/v4.12/input/uinput.html
+    */
+   struct uinput_user_dev dev;
+   memset(&dev, 0, sizeof(dev));
+   snprintf(dev.name, UINPUT_MAX_NAME_SIZE, UINPUT_DND_POINTER_NAME);
+
+   dev.absmin[ABS_X] = 0;
+   dev.absmax[ABS_X] = width - 1;
+   dev.absmin[ABS_Y] = 0;
+   dev.absmax[ABS_Y] = height - 1;
+
+   if (write(uinput_fd, &dev, sizeof(dev)) < 0) {
+      g_debug("%s: Failed to write\n", __FUNCTION__);
+      goto exit;
+   }
+   if (ioctl(uinput_fd, UI_SET_EVBIT, EV_ABS) < 0) {
+      g_debug("%s: Failed to register UI_SET_EVBIT EV_ABS\n", __FUNCTION__);
+      goto exit;
+   }
+   if (ioctl(uinput_fd, UI_SET_ABSBIT, ABS_X) < 0) {
+      g_debug("%s: Failed to register UI_SET_ABSBIT ABS_X\n", __FUNCTION__);
+      goto exit;
+   }
+   if (ioctl(uinput_fd, UI_SET_ABSBIT, ABS_Y) < 0) {
+      g_debug("%s: Failed to register UI_SET_ABSBIT ABS_Y\n", __FUNCTION__);
+      goto exit;
+   }
+   if (ioctl(uinput_fd, UI_SET_EVBIT, EV_KEY) < 0) {
+      g_debug("%s: Failed to register UI_SET_EVBIT EV_KEY\n", __FUNCTION__);
+      goto exit;
+   }
+   if (ioctl(uinput_fd, UI_SET_KEYBIT, BTN_MOUSE) < 0) {
+      g_debug("%s: Failed to register UI_SET_KEYBIT BTN_MOUSE\n", __FUNCTION__);
+      goto exit;
+   }
+   if (ioctl(uinput_fd, UI_SET_KEYBIT, BTN_LEFT) < 0) {
+      g_debug("%s: Failed to register UI_SET_KEYBIT BTN_LEFT\n", __FUNCTION__);
+      goto exit;
+   }
+   if (ioctl(uinput_fd, UI_DEV_CREATE, 0) < 0) {
+      g_debug("%s: Failed to create UInput device\n", __FUNCTION__);
+      goto exit;
+   }
+
+   /*
+    * On UI_DEV_CREATE the kernel will create the device node for this
+    * device. Insert a pause here so that userspace has time
+    * to detect, initialize the new device, and can start listening to
+    * the event, otherwise it will not notice the event we are about
+    * to send.
+    */
+   usleep(100 * 1000);
+
+   isInit = true;
+   return true;
+
+exit:
+   FakeMouse_Destory();
+   return false;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * FakeMouse_Update --
+ *
+ *      Update the width and height properties of  the uinput device.
+ *
+ * Results:
+ *      True if the uinput device is updated successfully.
+ *
+ * Side effects:
+ *      uinput device is updated.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+bool
+FakeMouse_Update(int width,    // width of the screen
+                 int height)   // height of the screen
+{
+   if (!FakeMouse_IsInit()) {
+      return false;
+   }
+
+   FakeMouse_Destory();
+   return FakeMouse_Init(uinput_fd, width, height);
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * FakeMouse_Destory --
+ *
+ *      Destory the uinput device.
+ *
+ * Results:
+ *      None.
+ *
+ * Side effects:
+ *      uinput device is destory.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+void
+FakeMouse_Destory()
+{
+   if (!FakeMouse_IsInit()) {
+      return;
+   }
+
+   if (ioctl(uinput_fd, UI_DEV_DESTROY, 0) < 0) {
+      g_debug("%s: Failed to destroy uinput device\n", __FUNCTION__);
+   }
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * FakeMouse_Move --
+ *
+ *      Move the pointer to (x, y).
+ *
+ * Results:
+ *      True if success.
+ *
+ * Side effects:
+ *      Pointer position is updated.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+bool
+FakeMouse_Move(int x,    // IN
+               int y)    // IN
+{
+   if (!FakeMouse_IsInit()) {
+      return false;
+   }
+
+   bool retValue = true;
+   struct input_event event;
+
+   event.type = EV_ABS;
+   event.code = ABS_X;
+   event.value = x;
+   gettimeofday(&event.time, NULL);
+   if (write(uinput_fd, &event, sizeof(event)) < 0) {
+      g_debug("Line:%d. Function:%s. Failed to write\n", __LINE__, __FUNCTION__);
+      retValue = false;
+   }
+
+   event.type = EV_ABS;
+   event.code = ABS_Y;
+   event.value = y;
+   gettimeofday(&event.time, NULL);
+   if (write(uinput_fd, &event, sizeof(event)) < 0) {
+      g_debug("Line:%d. Function:%s. Failed to write\n", __LINE__, __FUNCTION__);
+      retValue = false;
+   }
+
+   event.type = EV_SYN;
+   event.code = SYN_REPORT;
+   event.value = 0;
+   gettimeofday(&event.time, NULL);
+   if (write(uinput_fd, &event, sizeof(event)) < 0) {
+      g_debug("Line:%d. Function:%s. Failed to write\n", __LINE__, __FUNCTION__);
+      retValue = false;
+   }
+
+   return retValue;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * FakeMouse_Click --
+ *
+ *      Simulate the pointer down/up event.
+ *
+ * Results:
+ *      True if success.
+ *
+ * Side effects:
+ *      Pointer button event is updated.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+bool
+FakeMouse_Click(bool down)  // IN
+{
+   if (!FakeMouse_IsInit()) {
+      return false;
+   }
+
+   bool retValue = true;
+   struct input_event event;
+
+   event.type = EV_KEY;
+   event.code = BTN_LEFT;
+   event.value = down;
+   gettimeofday(&event.time, NULL);
+   if (write(uinput_fd, &event, sizeof(event)) < 0) {
+      g_debug("Line:%d. Function:%s. Failed to write\n", __LINE__, __FUNCTION__);
+      retValue = false;
+   }
+
+   event.type = EV_SYN;
+   event.code = SYN_REPORT;
+   event.value = 0;
+   if (write(uinput_fd, &event, sizeof(event)) < 0) {
+      g_debug("Line:%d. Function:%s. Failed to write\n", __LINE__, __FUNCTION__);
+      retValue = false;
+   }
+
+   /*
+    * Insert a pause here so that userspace has time to detect this event,
+    * otherwise it will not notice the event we are about to send.
+    */
+   usleep(10 * 1000);
+   return retValue;
+}
diff --git a/open-vm-tools/services/plugins/dndcp/fakeMouseWayland/fakeMouseWayland.h b/open-vm-tools/services/plugins/dndcp/fakeMouseWayland/fakeMouseWayland.h
new file mode 100644 (file)
index 0000000..962c39f
--- /dev/null
@@ -0,0 +1,36 @@
+/*********************************************************
+ * Copyright (C) 2018 VMware, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation version 2.1 and no later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the Lesser GNU General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
+ *
+ *********************************************************/
+
+/**
+ * @file fakeMouseWayland.h
+ *
+ *    Implement the methods that simulates the mouse motion.
+ *
+ */
+
+#ifndef __FAKE_MOUSE_WAYLAND_H__
+#define __FAKE_MOUSE_WAYLAND_H__
+
+bool FakeMouse_Init(int fd, int width, int height);
+bool FakeMouse_IsInit();
+bool FakeMouse_Update(int width, int height);
+void FakeMouse_Destory();
+bool FakeMouse_Move(int x, int y);
+bool FakeMouse_Click(bool down);
+
+#endif // __FAKE_MOUSE_WAYLAND_H__