/** @file
* VirtualBox X11 Guest Additions, mouse driver for X.Org server 1.5
*/
/*
* Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
* --------------------------------------------------------------------
*
* This code is based on evdev.c from X.Org with the following copyright
* and permission notice:
*
* Copyright © 2004-2008 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without
* fee, provided that the above copyright notice appear in all copies
* and that both that copyright notice and this permission notice
* appear in supporting documentation, and that the name of Red Hat
* not be used in advertising or publicity pertaining to distribution
* of the software without specific, written prior permission. Red
* Hat makes no representations about the suitability of this software
* for any purpose. It is provided "as is" without express or implied
* warranty.
*
* THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
* NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Authors:
* Kristian Høgsberg (krh@redhat.com)
* Adam Jackson (ajax@redhat.com)
*/
#include <VBox/VMMDev.h>
#include <VBox/VBoxGuestLib.h>
#include <iprt/err.h>
#include <xf86.h>
#include <xf86Xinput.h>
#include <mipointer.h>
#include <xf86Module.h>
#ifdef VBOX_GUESTR3XF86MOD
# define _X_EXPORT
#else
# include <errno.h>
# include <fcntl.h>
# include <unistd.h>
#endif
#include "product-generated.h"
static void
VBoxReadInput(InputInfoPtr pInfo)
{
uint32_t cx, cy, fFeatures;
/* Read a byte from the device to acknowledge the event */
char c;
(void) read(pInfo->fd, &c, 1);
/* The first test here is a workaround for an apparent bug in Xorg Server 1.5 */
if (
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 2
miPointerCurrentScreen() != NULL
#else
miPointerGetScreen(pInfo->dev) != NULL
#endif
&& RT_SUCCESS(VbglR3GetMouseStatus(&fFeatures, &cx, &cy))
&& (fFeatures & VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE))
{
#if ABI_XINPUT_VERSION == SET_ABI_VERSION(2, 0)
/* Bug in the 1.4 X server series - conversion_proc was no longer
* called, but the server didn't yet do the conversion itself. */
cx = (cx * screenInfo.screens[0]->width) / 65535;
cy = (cy * screenInfo.screens[0]->height) / 65535;
#endif
/* send absolute movement */
xf86PostMotionEvent(pInfo->dev, 1, 0, 2, cx, cy);
}
}
static void
VBoxPtrCtrlProc(DeviceIntPtr device, PtrCtrl *ctrl)
{
/* Nothing to do, dix handles all settings */
}
static int
VBoxInit(DeviceIntPtr device)
{
CARD8 map[2] = { 0, 1 };
Atom axis_labels[2] = { 0, 0 };
Atom button_labels[2] = { 0, 0 };
if (!InitPointerDeviceStruct((DevicePtr)device, map, 2,
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
button_labels,
#endif
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 2
miPointerGetMotionEvents, VBoxPtrCtrlProc,
miPointerGetMotionBufferSize()
#elif GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 3
GetMotionHistory, VBoxPtrCtrlProc,
GetMotionHistorySize(), 2 /* Number of axes */
#elif GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 3
VBoxPtrCtrlProc, GetMotionHistorySize(),
2 /* Number of axes */
#else
# error Unsupported version of X.Org
#endif
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
, axis_labels
#endif
))
return !Success;
/* Tell the server about the range of axis values we report */
#if ABI_XINPUT_VERSION <= SET_ABI_VERSION(2, 0)
xf86InitValuatorAxisStruct(device, 0, 0, -1, 1, 0, 1);
xf86InitValuatorAxisStruct(device, 1, 0, -1, 1, 0, 1);
#else
xf86InitValuatorAxisStruct(device, 0,
# if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
axis_labels[0],
# endif
VMMDEV_MOUSE_RANGE_MIN /* min X */, VMMDEV_MOUSE_RANGE_MAX /* max X */,
10000, 0, 10000
# if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
, Absolute
# endif
);
xf86InitValuatorAxisStruct(device, 1,
# if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7
axis_labels[1],
# endif
VMMDEV_MOUSE_RANGE_MIN /* min Y */, VMMDEV_MOUSE_RANGE_MAX /* max Y */,
10000, 0, 10000
# if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 12
, Absolute
# endif
);
#endif
xf86InitValuatorDefaults(device, 0);
xf86InitValuatorDefaults(device, 1);
xf86MotionHistoryAllocate(device->public.devicePrivate);
return Success;
}
static int
VBoxProc(DeviceIntPtr device, int what)
{
InputInfoPtr pInfo;
int rc, xrc;
uint32_t fFeatures = 0;
pInfo = device->public.devicePrivate;
switch (what)
{
case DEVICE_INIT:
xrc = VBoxInit(device);
if (xrc != Success) {
VbglR3Term();
return xrc;
}
break;
case DEVICE_ON:
xf86Msg(X_INFO, "%s: On.\n", pInfo->name);
if (device->public.on)
break;
/* Tell the host that we want absolute co-ordinates */
rc = VbglR3SetMouseStatus( VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE
| VMMDEV_MOUSE_NEW_PROTOCOL);
if (!RT_SUCCESS(rc)) {
xf86Msg(X_ERROR, "%s: Failed to switch guest mouse into absolute mode\n",
pInfo->name);
return !Success;
}
xf86AddEnabledDevice(pInfo);
device->public.on = TRUE;
break;
case DEVICE_OFF:
xf86Msg(X_INFO, "%s: Off.\n", pInfo->name);
rc = VbglR3GetMouseStatus(&fFeatures, NULL, NULL);
if (RT_SUCCESS(rc))
rc = VbglR3SetMouseStatus( fFeatures
& ~VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE
& ~VMMDEV_MOUSE_NEW_PROTOCOL);
xf86RemoveEnabledDevice(pInfo);
device->public.on = FALSE;
break;
case DEVICE_CLOSE:
VbglR3Term();
xf86Msg(X_INFO, "%s: Close\n", pInfo->name);
break;
default:
return BadValue;
}
return Success;
}
static int
VBoxProbe(InputInfoPtr pInfo)
{
int rc = VbglR3Init();
if (!RT_SUCCESS(rc)) {
xf86Msg(X_ERROR, "%s: Failed to open the VirtualBox device (error %d)\n",
pInfo->name, rc);
return BadMatch;
}
return Success;
}
static Bool
VBoxConvert(InputInfoPtr pInfo, int first, int num, int v0, int v1, int v2,
int v3, int v4, int v5, int *x, int *y)
{
if (first == 0) {
*x = xf86ScaleAxis(v0, 0, screenInfo.screens[0]->width, 0, 65536);
*y = xf86ScaleAxis(v1, 0, screenInfo.screens[0]->height, 0, 65536);
return TRUE;
} else
return FALSE;
}
static int
VBoxPreInitInfo(InputDriverPtr drv, InputInfoPtr pInfo, int flags)
{
const char *device;
int rc;
/* Initialise the InputInfoRec. */
pInfo->device_control = VBoxProc;
pInfo->read_input = VBoxReadInput;
/* Unlike evdev, we set this unconditionally, as we don't handle keyboards. */
pInfo->type_name = XI_MOUSE;
pInfo->flags |= XI86_ALWAYS_CORE;
device = xf86SetStrOption(pInfo->options, "Device",
"/dev/vboxguest");
xf86Msg(X_CONFIG, "%s: Device: \"%s\"\n", pInfo->name, device);
do {
pInfo->fd = open(device, O_RDWR, 0);
}
while (pInfo->fd < 0 && errno == EINTR);
if (pInfo->fd < 0) {
xf86Msg(X_ERROR, "Unable to open VirtualBox device \"%s\".\n", device);
return BadMatch;
}
rc = VBoxProbe(pInfo);
if (rc != Success)
return rc;
return Success;
}
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 12
static InputInfoPtr
VBoxPreInit(InputDriverPtr drv, IDevPtr dev, int flags)
{
InputInfoPtr pInfo;
const char *device;
if (!(pInfo = xf86AllocateInput(drv, 0)))
return NULL;
/* Initialise the InputInfoRec. */
pInfo->name = dev->identifier;
pInfo->conf_idev = dev;
pInfo->conversion_proc = VBoxConvert;
pInfo->flags = XI86_POINTER_CAPABLE | XI86_SEND_DRAG_EVENTS;
xf86CollectInputOptions(pInfo, NULL, NULL);
xf86ProcessCommonOptions(pInfo, pInfo->options);
if (VBoxPreInitInfo(drv, pInfo, flags) != Success) {
xf86DeleteInput(pInfo, 0);
return NULL;
}
pInfo->flags |= XI86_CONFIGURED;
return pInfo;
}
#endif
_X_EXPORT InputDriverRec VBOXMOUSE = {
1,
"vboxmouse",
NULL,
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 12
VBoxPreInit,
#else
VBoxPreInitInfo,
#endif
NULL,
NULL,
0
};
static pointer
VBoxPlug(pointer module,
pointer options,
int *errmaj,
int *errmin)
{
xf86AddInputDriver(&VBOXMOUSE, module, 0);
xf86Msg(X_CONFIG, "Load address of symbol \"VBOXMOUSE\" is %p\n",
(void *)&VBOXMOUSE);
return module;
}
static XF86ModuleVersionInfo VBoxVersionRec =
{
"vboxmouse",
VBOX_VENDOR,
MODINFOSTRING1,
MODINFOSTRING2,
0, /* Missing from SDK: XORG_VERSION_CURRENT, */
1, 0, 0,
ABI_CLASS_XINPUT,
ABI_XINPUT_VERSION,
MOD_CLASS_XINPUT,
{0, 0, 0, 0}
};
_X_EXPORT XF86ModuleData vboxmouseModuleData =
{
&VBoxVersionRec,
VBoxPlug,
NULL
};