xfreerdp: integrate corey's multitouch code

This commit is contained in:
Marc-André Moreau 2013-05-08 21:51:16 -04:00
parent 1b28ef5976
commit 91a55ae741
8 changed files with 497 additions and 72 deletions

View File

@ -30,6 +30,8 @@ set(${MODULE_PREFIX}_SRCS
xf_tsmf.h
xf_event.c
xf_event.h
xf_input.c
xf_input.h
xf_cliprdr.c
xf_cliprdr.h
xf_monitor.c
@ -99,12 +101,17 @@ set(XI_FEATURE_TYPE "RECOMMENDED")
set(XI_FEATURE_PURPOSE "input")
set(XI_FEATURE_DESCRIPTION "X11 input extension")
set(XRENDER_FEATURE_TYPE "RECOMMENDED")
set(XRENDER_FEATURE_PURPOSE "rendering")
set(XRENDER_FEATURE_DESCRIPTION "X11 render extension")
find_feature(XShm ${XSHM_FEATURE_TYPE} ${XSHM_FEATURE_PURPOSE} ${XSHM_FEATURE_DESCRIPTION})
find_feature(Xinerama ${XINERAMA_FEATURE_TYPE} ${XINERAMA_FEATURE_PURPOSE} ${XINERAMA_FEATURE_DESCRIPTION})
find_feature(Xext ${XEXT_FEATURE_TYPE} ${XEXT_FEATURE_PURPOSE} ${XEXT_FEATURE_DESCRIPTION})
find_feature(Xcursor ${XCURSOR_FEATURE_TYPE} ${XCURSOR_FEATURE_PURPOSE} ${XCURSOR_FEATURE_DESCRIPTION})
find_feature(Xv ${XV_FEATURE_TYPE} ${XV_FEATURE_PURPOSE} ${XV_FEATURE_DESCRIPTION})
find_feature(Xi ${XI_FEATURE_TYPE} ${XI_FEATURE_PURPOSE} ${XI_FEATURE_DESCRIPTION})
find_feature(Xrender ${XRENDER_FEATURE_TYPE} ${XRENDER_FEATURE_PURPOSE} ${XRENDER_FEATURE_DESCRIPTION})
if(WITH_XINERAMA)
add_definitions(-DWITH_XINERAMA)
@ -136,6 +143,12 @@ if(WITH_XI)
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${XI_LIBRARIES})
endif()
if(WITH_XRENDER)
add_definitions(-DWITH_XRENDER)
include_directories(${XRENDER_INCLUDE_DIRS})
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${XRENDER_LIBRARIES})
endif()
include_directories(${CMAKE_SOURCE_DIR}/resources)
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp-client)

View File

@ -29,6 +29,7 @@
#include "xf_rail.h"
#include "xf_window.h"
#include "xf_cliprdr.h"
#include "xf_input.h"
#include "xf_event.h"
@ -95,7 +96,15 @@ static BOOL xf_event_Expose(xfInfo* xfi, XEvent* event, BOOL app)
if (!app)
{
XCopyArea(xfi->display, xfi->primary, xfi->window->handle, xfi->gc, x, y, w, h, x, y);
if (xfi->scale != 1.0)
{
xf_draw_screen_scaled(xfi);
}
else
{
XCopyArea(xfi->display, xfi->primary,
xfi->window->handle, xfi->gc, x, y, w, h, x, y);
}
}
else
{
@ -151,6 +160,13 @@ static BOOL xf_event_MotionNotify(xfInfo* xfi, XEvent* event, BOOL app)
x, y, &x, &y, &childWindow);
}
if (xfi->scale != 1.0)
{
/* Take scaling in to consideration */
x = (int) (x * (1.0 / xfi->scale));
y = (int) (y * (1.0 / xfi->scale));
}
input->MouseEvent(input, PTR_FLAGS_MOVE, x, y);
if (xfi->fullscreen)
@ -254,6 +270,13 @@ static BOOL xf_event_ButtonPress(xfInfo* xfi, XEvent* event, BOOL app)
x, y, &x, &y, &childWindow);
}
if (xfi->scale != 1.0)
{
/* Take scaling in to consideration */
x = (int) (x * (1.0 / xfi->scale));
y = (int) (y * (1.0 / xfi->scale));
}
if (extended)
input->ExtendedMouseEvent(input, flags, x, y);
else
@ -337,6 +360,13 @@ static BOOL xf_event_ButtonRelease(xfInfo* xfi, XEvent* event, BOOL app)
x, y, &x, &y, &childWindow);
}
if (xfi->scale != 1.0)
{
/* Take scaling in to consideration */
x = (int) (x * (1.0 / xfi->scale));
y = (int) (y * (1.0 / xfi->scale));
}
if (extended)
input->ExtendedMouseEvent(input, flags, x, y);
else
@ -523,6 +553,15 @@ static BOOL xf_event_ConfigureNotify(xfInfo* xfi, XEvent* event, BOOL app)
rdpWindow* window;
rdpRail* rail = ((rdpContext*) xfi->context)->rail;
if (xfi->width != event->xconfigure.width)
{
xfi->scale = (double) event->xconfigure.width / (double) xfi->originalWidth;
xfi->currentWidth = event->xconfigure.width;
xfi->currentHeight = event->xconfigure.width;
xf_draw_screen_scaled(xfi);
}
window = window_list_get_by_extra_id(rail->list, (void*) event->xconfigure.window);
if (window != NULL)
@ -977,6 +1016,8 @@ BOOL xf_event_process(freerdp* instance, XEvent* event)
break;
}
xf_input_handle_event(xfi, event);
XSync(xfi->display, FALSE);
return status;

308
client/X11/xf_input.c Normal file
View File

@ -0,0 +1,308 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Input
*
* Copyright 2013 Corey Clayton <can.of.tuna@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WITH_XI
#include <X11/extensions/XInput2.h>
#endif
#include <math.h>
#include "xf_input.h"
#ifdef WITH_XI
#define MAX_CONTACTS 2
typedef struct touch_contact
{
int id;
int count;
double pos_x;
double pos_y;
double last_x;
double last_y;
} touchContact;
touchContact contacts[MAX_CONTACTS];
int active_contacts;
XIDeviceEvent lastEvent;
double firstDist = -1.0;
double lastDist;
double z_vector;
int xinput_opcode;
int scale_cnt;
int xf_input_init(xfInfo* xfi, Window window)
{
int i, j;
int ndevices;
int major = 2;
int minor = 2;
Status xstatus;
XIEventMask evmask;
XIDeviceInfo* info;
int opcode, event, error;
unsigned char mask[XIMaskLen(XI_LASTEVENT)];
active_contacts = 0;
ZeroMemory(contacts, sizeof(touchContact) * MAX_CONTACTS);
if (!XQueryExtension(xfi->display, "XInputExtension", &opcode, &event, &error))
{
printf("XInput extension not available.\n");
return -1;
}
xfi->XInputOpcode = opcode;
XIQueryVersion(xfi->display, &major, &minor);
if (major * 1000 + minor < 2002)
{
printf("Server does not support XI 2.2\n");
return -1;
}
info = XIQueryDevice(xfi->display, XIAllDevices, &ndevices);
for (i = 0; i < ndevices; i++)
{
XIDeviceInfo* dev = &info[i];
for (j = 0; j < dev->num_classes; j++)
{
XIAnyClassInfo* class = dev->classes[j];
//XITouchClassInfo* t = (XITouchClassInfo*) class;
if (class->type != XITouchClass)
continue;
//printf("%s %s touch device, supporting %d touches.\n",
// dev->name, (t->mode == XIDirectTouch) ? "direct" : "dependent", t->num_touches);
}
}
evmask.mask = mask;
evmask.mask_len = sizeof(mask);
ZeroMemory(mask, sizeof(mask));
evmask.deviceid = XIAllDevices;
XISetMask(mask, XI_TouchBegin);
XISetMask(mask, XI_TouchUpdate);
XISetMask(mask, XI_TouchEnd);
xstatus = XISelectEvents(xfi->display, window, &evmask, 1);
return -1;
}
BOOL xf_input_is_duplicate(XIDeviceEvent* event)
{
if ( (lastEvent.time == event->time) &&
(lastEvent.detail == event->detail) &&
(lastEvent.event_x == event->event_x) &&
(lastEvent.event_y == event->event_y) )
{
return TRUE;
}
return FALSE;
}
void xf_input_save_last_event(XIDeviceEvent* event)
{
lastEvent.time = event->time;
lastEvent.detail = event->detail;
lastEvent.event_x = event->event_x;
lastEvent.event_y = event->event_y;
}
void xf_input_detect_pinch(xfInfo* xfi)
{
double dist;
double zoom;
double delta;
if (active_contacts != 2)
{
firstDist = -1.0;
return;
}
/* first calculate the distance */
dist = sqrt(pow(contacts[1].pos_x - contacts[0].last_x, 2.0) +
pow(contacts[1].pos_y - contacts[0].last_y, 2.0));
/* if this is the first 2pt touch */
if (firstDist <= 0)
{
firstDist = dist;
lastDist = firstDist;
scale_cnt = 0;
z_vector = 0;
}
else
{
delta = lastDist - dist;
/* compare the current distance to the first one */
zoom = (dist / firstDist);
z_vector += delta;
//printf("d: %.2f\n", delta);
lastDist = dist;
if (z_vector > 10)
{
xfi->scale -= 0.05;
if (xfi->scale < 0.5)
xfi->scale = 0.5;
XResizeWindow(xfi->display, xfi->window->handle, xfi->originalWidth * xfi->scale, xfi->originalHeight * xfi->scale);
IFCALL(xfi->client->OnResizeWindow, xfi->instance, xfi->originalWidth * xfi->scale, xfi->originalHeight * xfi->scale);
z_vector = 0;
}
if (z_vector < -10)
{
xfi->scale += 0.05;
if (xfi->scale > 1.5)
xfi->scale = 1.5;
XResizeWindow(xfi->display, xfi->window->handle, xfi->originalWidth * xfi->scale, xfi->originalHeight * xfi->scale);
IFCALL(xfi->client->OnResizeWindow, xfi->instance, xfi->originalWidth * xfi->scale, xfi->originalHeight * xfi->scale);
z_vector = 0;
}
}
}
#endif
int xf_input_handle_event(xfInfo* xfi, XEvent* event)
{
#ifdef WITH_XI
XGenericEventCookie* cookie = &event->xcookie;
XGetEventData(xfi->display, cookie);
if ((cookie->type == GenericEvent) && (cookie->extension == xfi->XInputOpcode))
{
switch(cookie->evtype)
{
case XI_TouchBegin:
if (xf_input_is_duplicate(cookie->data) == FALSE)
xf_input_touch_begin(xfi, cookie->data);
xf_input_save_last_event(cookie->data);
break;
case XI_TouchUpdate:
if (xf_input_is_duplicate(cookie->data) == FALSE)
xf_input_touch_update(xfi, cookie->data);
xf_input_save_last_event(cookie->data);
break;
case XI_TouchEnd:
if (xf_input_is_duplicate(cookie->data) == FALSE)
xf_input_touch_end(xfi, cookie->data);
xf_input_save_last_event(cookie->data);
break;
default:
printf("unhandled xi type= %d\n", cookie->evtype);
break;
}
}
XFreeEventData(xfi->display,cookie);
#endif
return 0;
}
#ifdef WITH_XI
void xf_input_touch_begin(xfInfo* xfi, XIDeviceEvent* event)
{
int i;
for (i = 0; i < MAX_CONTACTS; i++)
{
if (contacts[i].id == 0)
{
contacts[i].id = event->detail;
contacts[i].count = 1;
contacts[i].pos_x = event->event_x;
contacts[i].pos_y = event->event_y;
active_contacts++;
break;
}
}
}
void xf_input_touch_update(xfInfo* xfi, XIDeviceEvent* event)
{
int i;
for (i = 0; i < MAX_CONTACTS; i++)
{
if (contacts[i].id == event->detail)
{
contacts[i].count++;
contacts[i].last_x = contacts[i].pos_x;
contacts[i].last_y = contacts[i].pos_y;
contacts[i].pos_x = event->event_x;
contacts[i].pos_y = event->event_y;
xf_input_detect_pinch(xfi);
break;
}
}
}
void xf_input_touch_end(xfInfo* xfi, XIDeviceEvent* event)
{
int i;
for (i = 0; i < MAX_CONTACTS; i++)
{
if (contacts[i].id == event->detail)
{
contacts[i].id = 0;
contacts[i].count = 0;
//contacts[i].pos_x = (int)event->event_x;
//contacts[i].pos_y = (int)event->event_y;
active_contacts--;
break;
}
}
}
#endif

41
client/X11/xf_input.h Normal file
View File

@ -0,0 +1,41 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Input
*
* Copyright 2013 Corey Clayton <can.of.tuna@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __XF_INPUT_H
#define __XF_INPUT_H
#include "xf_interface.h"
#include "xfreerdp.h"
#ifdef WITH_XI
#include <X11/extensions/XInput2.h>
#endif
int xf_input_init(xfInfo* xfi, Window win);
int xf_input_handle_event(xfInfo* xfi, XEvent* event);
#ifdef WITH_XI
void xf_input_touch_begin(xfInfo* xfi, XIDeviceEvent* event);
void xf_input_touch_update(xfInfo* xfi, XIDeviceEvent* event);
void xf_input_touch_end(xfInfo* xfi, XIDeviceEvent* event);
#endif
#endif

View File

@ -32,6 +32,14 @@
#include <X11/extensions/Xinerama.h>
#endif
#ifdef WITH_XI
#include <X11/extensions/XInput2.h>
#endif
#ifdef WITH_XRENDER
#include <X11/extensions/Xrender.h>
#endif
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
@ -79,6 +87,35 @@
static long xv_port = 0;
static const size_t password_size = 512;
void xf_draw_screen_scaled(xfInfo* xfi)
{
XTransform transform;
Picture windowPicture;
Picture primaryPicture;
XRenderPictureAttributes pa;
XRenderPictFormat* picFormat;
picFormat = XRenderFindStandardFormat(xfi->display, PictStandardRGB24);
pa.subwindow_mode = IncludeInferiors;
primaryPicture = XRenderCreatePicture(xfi->display, xfi->primary, picFormat, CPSubwindowMode, &pa);
windowPicture = XRenderCreatePicture(xfi->display, xfi->window->handle, picFormat, CPSubwindowMode, &pa);
transform.matrix[0][0] = XDoubleToFixed(1);
transform.matrix[0][1] = XDoubleToFixed(0);
transform.matrix[0][2] = XDoubleToFixed(0);
transform.matrix[1][0] = XDoubleToFixed(0);
transform.matrix[1][1] = XDoubleToFixed(1);
transform.matrix[1][2] = XDoubleToFixed(0);
transform.matrix[2][0] = XDoubleToFixed(0);
transform.matrix[2][1] = XDoubleToFixed(0);
transform.matrix[2][2] = XDoubleToFixed(xfi->scale);
XRenderSetPictureTransform(xfi->display, primaryPicture, &transform);
XRenderComposite(xfi->display, PictOpSrc, primaryPicture, 0, windowPicture, 0, 0, 0, 0, 0, 0, xfi->currentWidth, xfi->currentHeight);
}
void xf_context_new(freerdp* instance, rdpContext* context)
{
context->channels = freerdp_channels_new();
@ -121,7 +158,15 @@ void xf_sw_end_paint(rdpContext* context)
xf_lock_x11(xfi, FALSE);
XPutImage(xfi->display, xfi->primary, xfi->gc, xfi->image, x, y, x, y, w, h);
XCopyArea(xfi->display, xfi->primary, xfi->window->handle, xfi->gc, x, y, w, h, x, y);
if (xfi->scale != 1.0)
{
xf_draw_screen_scaled(xfi);
}
else
{
XCopyArea(xfi->display, xfi->primary, xfi->window->handle, xfi->gc, x, y, w, h, x, y);
}
xf_unlock_x11(xfi, FALSE);
}
@ -147,7 +192,15 @@ void xf_sw_end_paint(rdpContext* context)
h = cinvalid[i].h;
XPutImage(xfi->display, xfi->primary, xfi->gc, xfi->image, x, y, x, y, w, h);
XCopyArea(xfi->display, xfi->primary, xfi->window->handle, xfi->gc, x, y, w, h, x, y);
if (xfi->scale != 1.0)
{
xf_draw_screen_scaled(xfi);
}
else
{
XCopyArea(xfi->display, xfi->primary, xfi->window->handle, xfi->gc, x, y, w, h, x, y);
}
}
XFlush(xfi->display);
@ -231,7 +284,14 @@ void xf_hw_end_paint(rdpContext* context)
xf_lock_x11(xfi, FALSE);
XCopyArea(xfi->display, xfi->primary, xfi->drawable, xfi->gc, x, y, w, h, x, y);
if (xfi->scale != 1.0)
{
xf_draw_screen_scaled(xfi);
}
else
{
XCopyArea(xfi->display, xfi->primary, xfi->drawable, xfi->gc, x, y, w, h, x, y);
}
xf_unlock_x11(xfi, FALSE);
}
@ -256,7 +316,14 @@ void xf_hw_end_paint(rdpContext* context)
w = cinvalid[i].w;
h = cinvalid[i].h;
XCopyArea(xfi->display, xfi->primary, xfi->drawable, xfi->gc, x, y, w, h, x, y);
if (xfi->scale != 1.0)
{
xf_draw_screen_scaled(xfi);
}
else
{
XCopyArea(xfi->display, xfi->primary, xfi->drawable, xfi->gc, x, y, w, h, x, y);
}
}
XFlush(xfi->display);
@ -768,6 +835,12 @@ BOOL xf_post_connect(freerdp* instance)
}
}
xfi->originalWidth = settings->DesktopWidth;
xfi->originalHeight = settings->DesktopHeight;
xfi->currentWidth = xfi->originalWidth;
xfi->currentHeight = xfi->originalWidth;
xfi->scale = 1.0;
xfi->width = settings->DesktopWidth;
xfi->height = settings->DesktopHeight;
@ -1481,6 +1554,11 @@ rdpClient* freerdp_client_get_interface(cfInfo* cfi)
return cfi->client;
}
double freerdp_client_get_scale(xfInfo* xfi)
{
return xfi->scale;
}
xfInfo* freerdp_client_new(int argc, char** argv)
{
int index;

View File

@ -55,6 +55,7 @@ FREERDP_API int freerdp_client_stop(cfInfo* cfi);
FREERDP_API freerdp* freerdp_client_get_instance(cfInfo* cfi);
FREERDP_API HANDLE freerdp_client_get_thread(cfInfo* cfi);
FREERDP_API rdpClient* freerdp_client_get_interface(cfInfo* cfi);
FREERDP_API double freerdp_client_get_scale(xfInfo* xfi);
FREERDP_API cfInfo* freerdp_client_new(int argc, char** argv);
FREERDP_API void freerdp_client_free(cfInfo* cfi);

View File

@ -44,6 +44,7 @@
#ifdef WITH_XI
#include <X11/extensions/XInput2.h>
#include "xf_input.h"
#endif
#ifdef WITH_DEBUG_X11
@ -325,73 +326,6 @@ static void xf_SetWindowPID(xfInfo* xfi, xfWindow* window, pid_t pid)
32, PropModeReplace, (unsigned char *)&pid, 1);
}
int xf_input_init(xfInfo* xfi, Window window)
{
int i, j;
int ndevices;
int major = 2;
int minor = 2;
Status xstatus;
XIEventMask evmask;
XIDeviceInfo* info;
int opcode, event, error;
XIGrabModifiers mods = { 1 };
unsigned char mask[XIMaskLen(XI_LASTEVENT)];
if (!XQueryExtension(xfi->display, "XInputExtension", &opcode, &event, &error))
{
printf("XInput extension not available.\n");
return -1;
}
XIQueryVersion(xfi->display, &major, &minor);
if (major * 1000 + minor < 2002)
{
printf("Server does not support XI 2.2\n");
return -1;
}
info = XIQueryDevice(xfi->display, XIAllDevices, &ndevices);
for (i = 0; i < ndevices; i++)
{
XIDeviceInfo* dev = &info[i];
for (j = 0; j < dev->num_classes; j++)
{
XIAnyClassInfo* class = dev->classes[j];
XITouchClassInfo* t = (XITouchClassInfo*) class;
if (class->type != XITouchClass)
continue;
printf("%s %s touch device, supporting %d touches.\n",
dev->name, (t->mode == XIDirectTouch) ? "direct" : "dependent", t->num_touches);
}
}
evmask.mask = mask;
evmask.mask_len = sizeof(mask);
ZeroMemory(mask, sizeof(mask));
evmask.deviceid = XIAllDevices;
XISetMask(mask, XI_TouchBegin);
XISetMask(mask, XI_TouchUpdate);
XISetMask(mask, XI_TouchEnd);
xstatus = XISelectEvents(xfi->display, window, &evmask, 1);
//XIClearMask(mask, XI_TouchOwnership);
mods.modifiers = XIAnyModifier;
//XIGrabTouchBegin(xfi->display, XIAllMasterDevices, window, XINoOwnerEvents, &evmask, 1, &mods);
XSync(xfi->display, False);
return -1;
}
xfWindow* xf_CreateDesktopWindow(xfInfo* xfi, char* name, int width, int height, BOOL decorations)
{
xfWindow* window;

View File

@ -114,6 +114,13 @@ struct xf_info
UINT16 frame_x2;
UINT16 frame_y2;
double scale;
int originalWidth;
int originalHeight;
int currentWidth;
int currentHeight;
int XInputOpcode;
BOOL focused;
BOOL mouse_active;
BOOL suppress_output;
@ -204,6 +211,8 @@ enum XF_EXIT_CODE
void xf_lock_x11(xfInfo* xfi, BOOL display);
void xf_unlock_x11(xfInfo* xfi, BOOL display);
void xf_draw_screen_scaled(xfInfo* xfi);
DWORD xf_exit_code_from_disconnect_reason(DWORD reason);
#endif /* __XFREERDP_H */