moving 1.4 to trunk

This commit is contained in:
Nick Bolton
2012-06-10 16:50:54 +00:00
parent cdeb3a7824
commit 488241850c
1291 changed files with 425650 additions and 12 deletions

View File

@@ -0,0 +1,226 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CMSWindowsClipboard.h"
#include "CMSWindowsClipboardTextConverter.h"
#include "CMSWindowsClipboardUTF16Converter.h"
#include "CMSWindowsClipboardBitmapConverter.h"
#include "CMSWindowsClipboardHTMLConverter.h"
#include "CLog.h"
#include "CArchMiscWindows.h"
#include "CMSWindowsClipboardFacade.h"
//
// CMSWindowsClipboard
//
UINT CMSWindowsClipboard::s_ownershipFormat = 0;
CMSWindowsClipboard::CMSWindowsClipboard(HWND window) :
m_window(window),
m_time(0),
m_facade(new CMSWindowsClipboardFacade()),
m_deleteFacade(true)
{
// add converters, most desired first
m_converters.push_back(new CMSWindowsClipboardUTF16Converter);
if (CArchMiscWindows::isWindows95Family()) {
// windows nt family converts to/from unicode automatically.
// let it do so to avoid text encoding issues.
m_converters.push_back(new CMSWindowsClipboardTextConverter);
}
m_converters.push_back(new CMSWindowsClipboardBitmapConverter);
m_converters.push_back(new CMSWindowsClipboardHTMLConverter);
}
CMSWindowsClipboard::~CMSWindowsClipboard()
{
clearConverters();
// dependency injection causes confusion over ownership, so we need
// logic to decide whether or not we delete the facade. there must
// be a more elegant way of doing this.
if (m_deleteFacade)
delete m_facade;
}
bool
CMSWindowsClipboard::emptyUnowned()
{
LOG((CLOG_DEBUG "empty clipboard"));
// empty the clipboard (and take ownership)
if (!EmptyClipboard()) {
// unable to cause this in integ tests, but this error has never
// actually been reported by users.
LOG((CLOG_DEBUG "failed to grab clipboard"));
return false;
}
return true;
}
bool
CMSWindowsClipboard::empty()
{
if (!emptyUnowned()) {
return false;
}
// mark clipboard as being owned by synergy
HGLOBAL data = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, 1);
SetClipboardData(getOwnershipFormat(), data);
return true;
}
void
CMSWindowsClipboard::add(EFormat format, const CString& data)
{
LOG((CLOG_DEBUG "add %d bytes to clipboard format: %d", data.size(), format));
// convert data to win32 form
for (ConverterList::const_iterator index = m_converters.begin();
index != m_converters.end(); ++index) {
IMSWindowsClipboardConverter* converter = *index;
// skip converters for other formats
if (converter->getFormat() == format) {
HANDLE win32Data = converter->fromIClipboard(data);
if (win32Data != NULL) {
UINT win32Format = converter->getWin32Format();
m_facade->write(win32Data, win32Format);
}
}
}
}
bool
CMSWindowsClipboard::open(Time time) const
{
LOG((CLOG_DEBUG "open clipboard"));
if (!OpenClipboard(m_window)) {
// unable to cause this in integ tests; but this can happen!
// * http://synergy-foss.org/pm/issues/86
// * http://synergy-foss.org/pm/issues/1256
// logging improved to see if we can catch more info next time.
LOG((CLOG_WARN "failed to open clipboard: %d", GetLastError()));
return false;
}
m_time = time;
return true;
}
void
CMSWindowsClipboard::close() const
{
LOG((CLOG_DEBUG "close clipboard"));
CloseClipboard();
}
IClipboard::Time
CMSWindowsClipboard::getTime() const
{
return m_time;
}
bool
CMSWindowsClipboard::has(EFormat format) const
{
for (ConverterList::const_iterator index = m_converters.begin();
index != m_converters.end(); ++index) {
IMSWindowsClipboardConverter* converter = *index;
if (converter->getFormat() == format) {
if (IsClipboardFormatAvailable(converter->getWin32Format())) {
return true;
}
}
}
return false;
}
CString
CMSWindowsClipboard::get(EFormat format) const
{
// find the converter for the first clipboard format we can handle
IMSWindowsClipboardConverter* converter = NULL;
UINT win32Format = EnumClipboardFormats(0);
while (converter == NULL && win32Format != 0) {
for (ConverterList::const_iterator index = m_converters.begin();
index != m_converters.end(); ++index) {
converter = *index;
if (converter->getWin32Format() == win32Format &&
converter->getFormat() == format) {
break;
}
converter = NULL;
}
win32Format = EnumClipboardFormats(win32Format);
}
// if no converter then we don't recognize any formats
if (converter == NULL) {
return CString();
}
// get a handle to the clipboard data
HANDLE win32Data = GetClipboardData(converter->getWin32Format());
if (win32Data == NULL) {
// nb: can't cause this using integ tests; this is only caused when
// the selected converter returns an invalid format -- which you
// cannot cause using public functions.
return CString();
}
// convert
return converter->toIClipboard(win32Data);
}
void
CMSWindowsClipboard::clearConverters()
{
for (ConverterList::iterator index = m_converters.begin();
index != m_converters.end(); ++index) {
delete *index;
}
m_converters.clear();
}
bool
CMSWindowsClipboard::isOwnedBySynergy()
{
// create ownership format if we haven't yet
if (s_ownershipFormat == 0) {
s_ownershipFormat = RegisterClipboardFormat(TEXT("SynergyOwnership"));
}
return (IsClipboardFormatAvailable(getOwnershipFormat()) != 0);
}
UINT
CMSWindowsClipboard::getOwnershipFormat()
{
// create ownership format if we haven't yet
if (s_ownershipFormat == 0) {
s_ownershipFormat = RegisterClipboardFormat(TEXT("SynergyOwnership"));
}
// return the format
return s_ownershipFormat;
}

View File

@@ -0,0 +1,114 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CMSWINDOWSCLIPBOARD_H
#define CMSWINDOWSCLIPBOARD_H
#include "IClipboard.h"
#include "CMSWindowsClipboardFacade.h"
#include "stdvector.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
class IMSWindowsClipboardConverter;
class IMSWindowsClipboardFacade;
//! Microsoft windows clipboard implementation
class CMSWindowsClipboard : public IClipboard {
public:
CMSWindowsClipboard(HWND window);
CMSWindowsClipboard(HWND window, IMSWindowsClipboardFacade &facade);
virtual ~CMSWindowsClipboard();
//! Empty clipboard without ownership
/*!
Take ownership of the clipboard and clear all data from it.
This must be called between a successful open() and close().
Return false if the clipboard ownership could not be taken;
the clipboard should not be emptied in this case. Unlike
empty(), isOwnedBySynergy() will return false when emptied
this way. This is useful when synergy wants to put data on
clipboard but pretend (to itself) that some other app did it.
When using empty(), synergy assumes the data came from the
server and doesn't need to be sent back. emptyUnowned()
makes synergy send the data to the server.
*/
bool emptyUnowned();
//! Test if clipboard is owned by synergy
static bool isOwnedBySynergy();
// IClipboard overrides
virtual bool empty();
virtual void add(EFormat, const CString& data);
virtual bool open(Time) const;
virtual void close() const;
virtual Time getTime() const;
virtual bool has(EFormat) const;
virtual CString get(EFormat) const;
void setFacade(IMSWindowsClipboardFacade& facade) { m_facade = &facade; m_deleteFacade = false; }
private:
void clearConverters();
UINT convertFormatToWin32(EFormat) const;
HANDLE convertTextToWin32(const CString& data) const;
CString convertTextFromWin32(HANDLE) const;
static UINT getOwnershipFormat();
private:
typedef std::vector<IMSWindowsClipboardConverter*> ConverterList;
HWND m_window;
mutable Time m_time;
ConverterList m_converters;
static UINT s_ownershipFormat;
IMSWindowsClipboardFacade* m_facade;
bool m_deleteFacade;
};
//! Clipboard format converter interface
/*!
This interface defines the methods common to all win32 clipboard format
converters.
*/
class IMSWindowsClipboardConverter : public IInterface {
public:
// accessors
// return the clipboard format this object converts from/to
virtual IClipboard::EFormat
getFormat() const = 0;
// return the atom representing the win32 clipboard format that
// this object converts from/to
virtual UINT getWin32Format() const = 0;
// convert from the IClipboard format to the win32 clipboard format.
// the input data must be in the IClipboard format returned by
// getFormat(). the return data will be in the win32 clipboard
// format returned by getWin32Format(), allocated by GlobalAlloc().
virtual HANDLE fromIClipboard(const CString&) const = 0;
// convert from the win32 clipboard format to the IClipboard format
// (i.e., the reverse of fromIClipboard()).
virtual CString toIClipboard(HANDLE data) const = 0;
};
#endif

View File

@@ -0,0 +1,148 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CMSWindowsClipboardAnyTextConverter.h"
//
// CMSWindowsClipboardAnyTextConverter
//
CMSWindowsClipboardAnyTextConverter::CMSWindowsClipboardAnyTextConverter()
{
// do nothing
}
CMSWindowsClipboardAnyTextConverter::~CMSWindowsClipboardAnyTextConverter()
{
// do nothing
}
IClipboard::EFormat
CMSWindowsClipboardAnyTextConverter::getFormat() const
{
return IClipboard::kText;
}
HANDLE
CMSWindowsClipboardAnyTextConverter::fromIClipboard(const CString& data) const
{
// convert linefeeds and then convert to desired encoding
CString text = doFromIClipboard(convertLinefeedToWin32(data));
UInt32 size = (UInt32)text.size();
// copy to memory handle
HGLOBAL gData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, size);
if (gData != NULL) {
// get a pointer to the allocated memory
char* dst = (char*)GlobalLock(gData);
if (dst != NULL) {
memcpy(dst, text.data(), size);
GlobalUnlock(gData);
}
else {
GlobalFree(gData);
gData = NULL;
}
}
return gData;
}
CString
CMSWindowsClipboardAnyTextConverter::toIClipboard(HANDLE data) const
{
// get datator
const char* src = (const char*)GlobalLock(data);
UInt32 srcSize = (UInt32)GlobalSize(data);
if (src == NULL || srcSize <= 1) {
return CString();
}
// convert text
CString text = doToIClipboard(CString(src, srcSize));
// release handle
GlobalUnlock(data);
// convert newlines
return convertLinefeedToUnix(text);
}
CString
CMSWindowsClipboardAnyTextConverter::convertLinefeedToWin32(
const CString& src) const
{
// note -- we assume src is a valid UTF-8 string
// count newlines in string
UInt32 numNewlines = 0;
UInt32 n = (UInt32)src.size();
for (const char* scan = src.c_str(); n > 0; ++scan, --n) {
if (*scan == '\n') {
++numNewlines;
}
}
if (numNewlines == 0) {
return src;
}
// allocate new string
CString dst;
dst.reserve(src.size() + numNewlines);
// copy string, converting newlines
n = (UInt32)src.size();
for (const char* scan = src.c_str(); n > 0; ++scan, --n) {
if (scan[0] == '\n') {
dst += '\r';
}
dst += scan[0];
}
return dst;
}
CString
CMSWindowsClipboardAnyTextConverter::convertLinefeedToUnix(
const CString& src) const
{
// count newlines in string
UInt32 numNewlines = 0;
UInt32 n = (UInt32)src.size();
for (const char* scan = src.c_str(); n > 0; ++scan, --n) {
if (scan[0] == '\r' && scan[1] == '\n') {
++numNewlines;
}
}
if (numNewlines == 0) {
return src;
}
// allocate new string
CString dst;
dst.reserve(src.size());
// copy string, converting newlines
n = (UInt32)src.size();
for (const char* scan = src.c_str(); n > 0; ++scan, --n) {
if (scan[0] != '\r' || scan[1] != '\n') {
dst += scan[0];
}
}
return dst;
}

View File

@@ -0,0 +1,59 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CMSWINDOWSCLIPBOARDANYTEXTCONVERTER_H
#define CMSWINDOWSCLIPBOARDANYTEXTCONVERTER_H
#include "CMSWindowsClipboard.h"
//! Convert to/from some text encoding
class CMSWindowsClipboardAnyTextConverter :
public IMSWindowsClipboardConverter {
public:
CMSWindowsClipboardAnyTextConverter();
virtual ~CMSWindowsClipboardAnyTextConverter();
// IMSWindowsClipboardConverter overrides
virtual IClipboard::EFormat
getFormat() const;
virtual UINT getWin32Format() const = 0;
virtual HANDLE fromIClipboard(const CString&) const;
virtual CString toIClipboard(HANDLE) const;
protected:
//! Convert from IClipboard format
/*!
Do UTF-8 conversion only. Memory handle allocation and
linefeed conversion is done by this class. doFromIClipboard()
must include the nul terminator in the returned string (not
including the CString's nul terminator).
*/
virtual CString doFromIClipboard(const CString&) const = 0;
//! Convert to IClipboard format
/*!
Do UTF-8 conversion only. Memory handle allocation and
linefeed conversion is done by this class.
*/
virtual CString doToIClipboard(const CString&) const = 0;
private:
CString convertLinefeedToWin32(const CString&) const;
CString convertLinefeedToUnix(const CString&) const;
};
#endif

View File

@@ -0,0 +1,150 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CMSWindowsClipboardBitmapConverter.h"
#include "CLog.h"
//
// CMSWindowsClipboardBitmapConverter
//
CMSWindowsClipboardBitmapConverter::CMSWindowsClipboardBitmapConverter()
{
// do nothing
}
CMSWindowsClipboardBitmapConverter::~CMSWindowsClipboardBitmapConverter()
{
// do nothing
}
IClipboard::EFormat
CMSWindowsClipboardBitmapConverter::getFormat() const
{
return IClipboard::kBitmap;
}
UINT
CMSWindowsClipboardBitmapConverter::getWin32Format() const
{
return CF_DIB;
}
HANDLE
CMSWindowsClipboardBitmapConverter::fromIClipboard(const CString& data) const
{
// copy to memory handle
HGLOBAL gData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, data.size());
if (gData != NULL) {
// get a pointer to the allocated memory
char* dst = (char*)GlobalLock(gData);
if (dst != NULL) {
memcpy(dst, data.data(), data.size());
GlobalUnlock(gData);
}
else {
GlobalFree(gData);
gData = NULL;
}
}
return gData;
}
CString
CMSWindowsClipboardBitmapConverter::toIClipboard(HANDLE data) const
{
// get datator
const char* src = (const char*)GlobalLock(data);
if (src == NULL) {
return CString();
}
UInt32 srcSize = (UInt32)GlobalSize(data);
// check image type
const BITMAPINFO* bitmap = reinterpret_cast<const BITMAPINFO*>(src);
LOG((CLOG_INFO "bitmap: %dx%d %d", bitmap->bmiHeader.biWidth, bitmap->bmiHeader.biHeight, (int)bitmap->bmiHeader.biBitCount));
if (bitmap->bmiHeader.biPlanes == 1 &&
(bitmap->bmiHeader.biBitCount == 24 ||
bitmap->bmiHeader.biBitCount == 32) &&
bitmap->bmiHeader.biCompression == BI_RGB) {
// already in canonical form
CString image(src, srcSize);
GlobalUnlock(data);
return image;
}
// create a destination DIB section
LOG((CLOG_INFO "convert image from: depth=%d comp=%d", bitmap->bmiHeader.biBitCount, bitmap->bmiHeader.biCompression));
void* raw;
BITMAPINFOHEADER info;
LONG w = bitmap->bmiHeader.biWidth;
LONG h = bitmap->bmiHeader.biHeight;
info.biSize = sizeof(BITMAPINFOHEADER);
info.biWidth = w;
info.biHeight = h;
info.biPlanes = 1;
info.biBitCount = 32;
info.biCompression = BI_RGB;
info.biSizeImage = 0;
info.biXPelsPerMeter = 1000;
info.biYPelsPerMeter = 1000;
info.biClrUsed = 0;
info.biClrImportant = 0;
HDC dc = GetDC(NULL);
HBITMAP dst = CreateDIBSection(dc, (BITMAPINFO*)&info,
DIB_RGB_COLORS, &raw, NULL, 0);
// find the start of the pixel data
const char* srcBits = (const char*)bitmap + bitmap->bmiHeader.biSize;
if (bitmap->bmiHeader.biBitCount >= 16) {
if (bitmap->bmiHeader.biCompression == BI_BITFIELDS &&
(bitmap->bmiHeader.biBitCount == 16 ||
bitmap->bmiHeader.biBitCount == 32)) {
srcBits += 3 * sizeof(DWORD);
}
}
else if (bitmap->bmiHeader.biClrUsed != 0) {
srcBits += bitmap->bmiHeader.biClrUsed * sizeof(RGBQUAD);
}
else {
//http://msdn.microsoft.com/en-us/library/ke55d167(VS.80).aspx
srcBits += (1i64 << bitmap->bmiHeader.biBitCount) * sizeof(RGBQUAD);
}
// copy source image to destination image
HDC dstDC = CreateCompatibleDC(dc);
HGDIOBJ oldBitmap = SelectObject(dstDC, dst);
SetDIBitsToDevice(dstDC, 0, 0, w, h, 0, 0, 0, h,
srcBits, bitmap, DIB_RGB_COLORS);
SelectObject(dstDC, oldBitmap);
DeleteDC(dstDC);
GdiFlush();
// extract data
CString image((const char*)&info, info.biSize);
image.append((const char*)raw, 4 * w * h);
// clean up GDI
DeleteObject(dst);
ReleaseDC(NULL, dc);
// release handle
GlobalUnlock(data);
return image;
}

View File

@@ -0,0 +1,38 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CMSWINDOWSCLIPBOARDBITMAPCONVERTER_H
#define CMSWINDOWSCLIPBOARDBITMAPCONVERTER_H
#include "CMSWindowsClipboard.h"
//! Convert to/from some text encoding
class CMSWindowsClipboardBitmapConverter :
public IMSWindowsClipboardConverter {
public:
CMSWindowsClipboardBitmapConverter();
virtual ~CMSWindowsClipboardBitmapConverter();
// IMSWindowsClipboardConverter overrides
virtual IClipboard::EFormat
getFormat() const;
virtual UINT getWin32Format() const;
virtual HANDLE fromIClipboard(const CString&) const;
virtual CString toIClipboard(HANDLE) const;
};
#endif

View File

@@ -0,0 +1,29 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CMSWindowsClipboard.h"
#include "CMSWindowsClipboardFacade.h"
void CMSWindowsClipboardFacade::write(HANDLE win32Data, UINT win32Format)
{
if (SetClipboardData(win32Format, win32Data) == NULL) {
// free converted data if we couldn't put it on
// the clipboard.
// nb: couldn't cause this in integ tests.
GlobalFree(win32Data);
}
}

View File

@@ -0,0 +1,30 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CMSWINDOWSCLIPBOARDFACADE_H
#define CMSWINDOWSCLIPBOARDFACADE_H
#include "IMSWindowsClipboardFacade.h"
#include "IClipboard.h"
class CMSWindowsClipboardFacade : public IMSWindowsClipboardFacade
{
public:
virtual void write(HANDLE win32Data, UINT win32Format);
};
#endif

View File

@@ -0,0 +1,118 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CMSWindowsClipboardHTMLConverter.h"
#include "CStringUtil.h"
//
// CMSWindowsClipboardHTMLConverter
//
CMSWindowsClipboardHTMLConverter::CMSWindowsClipboardHTMLConverter()
{
m_format = RegisterClipboardFormat("HTML Format");
}
CMSWindowsClipboardHTMLConverter::~CMSWindowsClipboardHTMLConverter()
{
// do nothing
}
IClipboard::EFormat
CMSWindowsClipboardHTMLConverter::getFormat() const
{
return IClipboard::kHTML;
}
UINT
CMSWindowsClipboardHTMLConverter::getWin32Format() const
{
return m_format;
}
CString
CMSWindowsClipboardHTMLConverter::doFromIClipboard(const CString& data) const
{
// prepare to CF_HTML format prefix and suffix
CString prefix("Version:0.9\r\nStartHTML:0000000105\r\n"
"EndHTML:ZZZZZZZZZZ\r\n"
"StartFragment:XXXXXXXXXX\r\nEndFragment:YYYYYYYYYY\r\n"
"<!DOCTYPE><HTML><BODY><!--StartFragment-->");
CString suffix("<!--EndFragment--></BODY></HTML>\r\n");
// Get byte offsets for header
UInt32 StartFragment = (UInt32)prefix.size();
UInt32 EndFragment = StartFragment + (UInt32)data.size();
// StartHTML is constant by the design of the prefix
UInt32 EndHTML = EndFragment + (UInt32)suffix.size();
prefix.replace(prefix.find("XXXXXXXXXX"), 10,
CStringUtil::print("%010u", StartFragment));
prefix.replace(prefix.find("YYYYYYYYYY"), 10,
CStringUtil::print("%010u", EndFragment));
prefix.replace(prefix.find("ZZZZZZZZZZ"), 10,
CStringUtil::print("%010u", EndHTML));
// concatenate
prefix += data;
prefix += suffix;
return prefix;
}
CString
CMSWindowsClipboardHTMLConverter::doToIClipboard(const CString& data) const
{
// get fragment start/end args
CString startArg = findArg(data, "StartFragment");
CString endArg = findArg(data, "EndFragment");
if (startArg.empty() || endArg.empty()) {
return CString();
}
// convert args to integers
SInt32 start = (SInt32)atoi(startArg.c_str());
SInt32 end = (SInt32)atoi(endArg.c_str());
if (start <= 0 || end <= 0 || start >= end) {
return CString();
}
// extract the fragment
return data.substr(start, end - start);
}
CString
CMSWindowsClipboardHTMLConverter::findArg(
const CString& data, const CString& name) const
{
CString::size_type i = data.find(name);
if (i == CString::npos) {
return CString();
}
i = data.find_first_of(":\r\n", i);
if (i == CString::npos || data[i] != ':') {
return CString();
}
i = data.find_first_of("0123456789\r\n", i + 1);
if (i == CString::npos || data[i] == '\r' || data[i] == '\n') {
return CString();
}
CString::size_type j = data.find_first_not_of("0123456789", i);
if (j == CString::npos) {
j = data.size();
}
return data.substr(i, j - i);
}

View File

@@ -0,0 +1,47 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CMSWINDOWSCLIPBOARDHTMLCONVERTER_H
#define CMSWINDOWSCLIPBOARDHTMLCONVERTER_H
#include "CMSWindowsClipboardAnyTextConverter.h"
//! Convert to/from HTML encoding
class CMSWindowsClipboardHTMLConverter :
public CMSWindowsClipboardAnyTextConverter {
public:
CMSWindowsClipboardHTMLConverter();
virtual ~CMSWindowsClipboardHTMLConverter();
// IMSWindowsClipboardConverter overrides
virtual IClipboard::EFormat
getFormat() const;
virtual UINT getWin32Format() const;
protected:
// CMSWindowsClipboardAnyTextConverter overrides
virtual CString doFromIClipboard(const CString&) const;
virtual CString doToIClipboard(const CString&) const;
private:
CString findArg(const CString& data, const CString& name) const;
private:
UINT m_format;
};
#endif

View File

@@ -0,0 +1,58 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CMSWindowsClipboardTextConverter.h"
#include "CUnicode.h"
//
// CMSWindowsClipboardTextConverter
//
CMSWindowsClipboardTextConverter::CMSWindowsClipboardTextConverter()
{
// do nothing
}
CMSWindowsClipboardTextConverter::~CMSWindowsClipboardTextConverter()
{
// do nothing
}
UINT
CMSWindowsClipboardTextConverter::getWin32Format() const
{
return CF_TEXT;
}
CString
CMSWindowsClipboardTextConverter::doFromIClipboard(const CString& data) const
{
// convert and add nul terminator
return CUnicode::UTF8ToText(data) += '\0';
}
CString
CMSWindowsClipboardTextConverter::doToIClipboard(const CString& data) const
{
// convert and truncate at first nul terminator
CString dst = CUnicode::textToUTF8(data);
CString::size_type n = dst.find('\0');
if (n != CString::npos) {
dst.erase(n);
}
return dst;
}

View File

@@ -0,0 +1,39 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CMSWINDOWSCLIPBOARDTEXTCONVERTER_H
#define CMSWINDOWSCLIPBOARDTEXTCONVERTER_H
#include "CMSWindowsClipboardAnyTextConverter.h"
//! Convert to/from locale text encoding
class CMSWindowsClipboardTextConverter :
public CMSWindowsClipboardAnyTextConverter {
public:
CMSWindowsClipboardTextConverter();
virtual ~CMSWindowsClipboardTextConverter();
// IMSWindowsClipboardConverter overrides
virtual UINT getWin32Format() const;
protected:
// CMSWindowsClipboardAnyTextConverter overrides
virtual CString doFromIClipboard(const CString&) const;
virtual CString doToIClipboard(const CString&) const;
};
#endif

View File

@@ -0,0 +1,58 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CMSWindowsClipboardUTF16Converter.h"
#include "CUnicode.h"
//
// CMSWindowsClipboardUTF16Converter
//
CMSWindowsClipboardUTF16Converter::CMSWindowsClipboardUTF16Converter()
{
// do nothing
}
CMSWindowsClipboardUTF16Converter::~CMSWindowsClipboardUTF16Converter()
{
// do nothing
}
UINT
CMSWindowsClipboardUTF16Converter::getWin32Format() const
{
return CF_UNICODETEXT;
}
CString
CMSWindowsClipboardUTF16Converter::doFromIClipboard(const CString& data) const
{
// convert and add nul terminator
return CUnicode::UTF8ToUTF16(data).append(sizeof(wchar_t), 0);
}
CString
CMSWindowsClipboardUTF16Converter::doToIClipboard(const CString& data) const
{
// convert and strip nul terminator
CString dst = CUnicode::UTF16ToUTF8(data);
CString::size_type n = dst.find('\0');
if (n != CString::npos) {
dst.erase(n);
}
return dst;
}

View File

@@ -0,0 +1,39 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CMSWINDOWSCLIPBOARDUTF16CONVERTER_H
#define CMSWINDOWSCLIPBOARDUTF16CONVERTER_H
#include "CMSWindowsClipboardAnyTextConverter.h"
//! Convert to/from UTF-16 encoding
class CMSWindowsClipboardUTF16Converter :
public CMSWindowsClipboardAnyTextConverter {
public:
CMSWindowsClipboardUTF16Converter();
virtual ~CMSWindowsClipboardUTF16Converter();
// IMSWindowsClipboardConverter overrides
virtual UINT getWin32Format() const;
protected:
// CMSWindowsClipboardAnyTextConverter overrides
virtual CString doFromIClipboard(const CString&) const;
virtual CString doToIClipboard(const CString&) const;
};
#endif

View File

@@ -0,0 +1,58 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012 Nick Bolton
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CMSWindowsDebugOutputter.h"
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <string>
CMSWindowsDebugOutputter::CMSWindowsDebugOutputter()
{
}
CMSWindowsDebugOutputter::~CMSWindowsDebugOutputter()
{
}
void
CMSWindowsDebugOutputter::open(const char* title)
{
}
void
CMSWindowsDebugOutputter::close()
{
}
void
CMSWindowsDebugOutputter::show(bool showIfEmpty)
{
}
bool
CMSWindowsDebugOutputter::write(ELevel level, const char* msg)
{
OutputDebugString((std::string(msg) + "\n").c_str());
return true;
}
void
CMSWindowsDebugOutputter::flush()
{
}

View File

@@ -0,0 +1,38 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012 Nick Bolton
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "ILogOutputter.h"
//! Write log to debugger
/*!
This outputter writes output to the debugger. In Visual Studio, this
can be seen in the Output window.
*/
class CMSWindowsDebugOutputter : public ILogOutputter {
public:
CMSWindowsDebugOutputter();
virtual ~CMSWindowsDebugOutputter();
// ILogOutputter overrides
virtual void open(const char* title);
virtual void close();
virtual void show(bool showIfEmpty);
virtual bool write(ELevel level, const char* message);
virtual void flush();
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,303 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CMSWINDOWSDESKS_H
#define CMSWINDOWSDESKS_H
#include "CSynergyHook.h"
#include "KeyTypes.h"
#include "MouseTypes.h"
#include "OptionTypes.h"
#include "CCondVar.h"
#include "CMutex.h"
#include "CString.h"
#include "stdmap.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
class CEvent;
class CEventQueueTimer;
class CThread;
class IJob;
class IScreenSaver;
//! Microsoft Windows desk handling
/*!
Desks in Microsoft Windows are only remotely like desktops on X11
systems. A desk is another virtual surface for windows but desks
impose serious restrictions: a thread can interact with only one
desk at a time, you can't switch desks if the thread has any hooks
installed or owns any windows, windows cannot exist on multiple
desks at once, etc. Basically, they're useless except for running
the login window or the screensaver, which is what they're used
for. Synergy must deal with them mainly because of the login
window and screensaver but users can create their own desks and
synergy should work on those too.
This class encapsulates all the desk nastiness. Clients of this
object don't have to know anything about desks.
*/
class CMSWindowsDesks {
public:
//! Constructor
/*!
\p isPrimary is true iff the desk is for a primary screen.
\p screensaver points to a screensaver object and it's used
only to check if the screensaver is active. The \p updateKeys
job is adopted and is called when the key state should be
updated in a thread attached to the current desk.
\p hookLibrary must be a handle to the hook library.
*/
CMSWindowsDesks(bool isPrimary, bool noHooks, HINSTANCE hookLibrary,
const IScreenSaver* screensaver, IJob* updateKeys);
~CMSWindowsDesks();
//! @name manipulators
//@{
//! Enable desk tracking
/*!
Enables desk tracking. While enabled, this object checks to see
if the desk has changed and ensures that the hooks are installed
on the new desk. \c setShape should be called at least once
before calling \c enable.
*/
void enable();
//! Disable desk tracking
/*!
Disables desk tracking. \sa enable.
*/
void disable();
//! Notify of entering a desk
/*!
Prepares a desk for when the cursor enters it.
*/
void enter();
//! Notify of leaving a desk
/*!
Prepares a desk for when the cursor leaves it.
*/
void leave(HKL keyLayout);
//! Notify of options changes
/*!
Resets all options to their default values.
*/
void resetOptions();
//! Notify of options changes
/*!
Set options to given values. Ignores unknown options and doesn't
modify options that aren't given in \c options.
*/
void setOptions(const COptionsList& options);
//! Update the key state
/*!
Causes the key state to get updated to reflect the physical keyboard
state and current keyboard mapping.
*/
void updateKeys();
//! Tell desk about new size
/*!
This tells the desks that the display size has changed.
*/
void setShape(SInt32 x, SInt32 y,
SInt32 width, SInt32 height,
SInt32 xCenter, SInt32 yCenter, bool isMultimon);
//! Install/uninstall screensaver hooks
/*!
If \p install is true then the screensaver hooks are installed and,
if desk tracking is enabled, updated whenever the desk changes. If
\p install is false then the screensaver hooks are uninstalled.
*/
void installScreensaverHooks(bool install);
//! Start ignoring user input
/*!
Starts ignoring user input so we don't pick up our own synthesized events.
*/
void fakeInputBegin();
//! Stop ignoring user input
/*!
Undoes whatever \c fakeInputBegin() did.
*/
void fakeInputEnd();
//@}
//! @name accessors
//@{
//! Get cursor position
/*!
Return the current position of the cursor in \c x and \c y.
*/
void getCursorPos(SInt32& x, SInt32& y) const;
//! Fake key press/release
/*!
Synthesize a press or release of key \c button.
*/
void fakeKeyEvent(KeyButton button, UINT virtualKey,
bool press, bool isAutoRepeat) const;
//! Fake mouse press/release
/*!
Synthesize a press or release of mouse button \c id.
*/
void fakeMouseButton(ButtonID id, bool press);
//! Fake mouse move
/*!
Synthesize a mouse move to the absolute coordinates \c x,y.
*/
void fakeMouseMove(SInt32 x, SInt32 y) const;
//! Fake mouse move
/*!
Synthesize a mouse move to the relative coordinates \c dx,dy.
*/
void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const;
//! Fake mouse wheel
/*!
Synthesize a mouse wheel event of amount \c delta in direction \c axis.
*/
void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const;
//@}
private:
class CDesk {
public:
CString m_name;
CThread* m_thread;
DWORD m_threadID;
DWORD m_targetID;
HDESK m_desk;
HWND m_window;
HWND m_foregroundWindow;
bool m_lowLevel;
};
typedef std::map<CString, CDesk*> CDesks;
// initialization and shutdown operations
void queryHookLibrary(HINSTANCE hookLibrary);
HCURSOR createBlankCursor() const;
void destroyCursor(HCURSOR cursor) const;
ATOM createDeskWindowClass(bool isPrimary) const;
void destroyClass(ATOM windowClass) const;
HWND createWindow(ATOM windowClass, const char* name) const;
void destroyWindow(HWND) const;
// message handlers
void deskMouseMove(SInt32 x, SInt32 y) const;
void deskMouseRelativeMove(SInt32 dx, SInt32 dy) const;
void deskEnter(CDesk* desk);
void deskLeave(CDesk* desk, HKL keyLayout);
void deskThread(void* vdesk);
void xinputThread();
// desk switch checking and handling
CDesk* addDesk(const CString& name, HDESK hdesk);
void removeDesks();
void checkDesk();
bool isDeskAccessible(const CDesk* desk) const;
void handleCheckDesk(const CEvent& event, void*);
// communication with desk threads
void waitForDesk() const;
void sendMessage(UINT, WPARAM, LPARAM) const;
// work around for messed up keyboard events from low-level hooks
HWND getForegroundWindow() const;
// desk API wrappers
HDESK openInputDesktop();
void closeDesktop(HDESK);
CString getDesktopName(HDESK);
// our desk window procs
static LRESULT CALLBACK primaryDeskProc(HWND, UINT, WPARAM, LPARAM);
static LRESULT CALLBACK secondaryDeskProc(HWND, UINT, WPARAM, LPARAM);
private:
// true if screen is being used as a primary screen, false otherwise
bool m_isPrimary;
// true if hooks are not to be installed (useful for debugging)
bool m_noHooks;
// true if windows 95/98/me
bool m_is95Family;
// true if windows 98/2k or higher (i.e. not 95/nt)
bool m_isModernFamily;
// true if mouse has entered the screen
bool m_isOnScreen;
// our resources
ATOM m_deskClass;
HCURSOR m_cursor;
// screen shape stuff
SInt32 m_x, m_y;
SInt32 m_w, m_h;
SInt32 m_xCenter, m_yCenter;
// true if system appears to have multiple monitors
bool m_multimon;
// the timer used to check for desktop switching
CEventQueueTimer* m_timer;
// screen saver stuff
DWORD m_threadID;
const IScreenSaver* m_screensaver;
bool m_screensaverNotify;
// the current desk and it's name
CDesk* m_activeDesk;
CString m_activeDeskName;
// one desk per desktop and a cond var to communicate with it
CMutex m_mutex;
CCondVar<bool> m_deskReady;
CDesks m_desks;
// hook library stuff
InstallFunc m_install;
UninstallFunc m_uninstall;
InstallScreenSaverFunc m_installScreensaver;
UninstallScreenSaverFunc m_uninstallScreensaver;
// keyboard stuff
IJob* m_updateKeys;
HKL m_keyLayout;
// options
bool m_leaveForegroundOption;
};
#endif

View File

@@ -0,0 +1,141 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CMSWindowsEventQueueBuffer.h"
#include "CThread.h"
#include "IEventQueue.h"
#include "CArchMiscWindows.h"
//
// CEventQueueTimer
//
class CEventQueueTimer { };
//
// CMSWindowsEventQueueBuffer
//
CMSWindowsEventQueueBuffer::CMSWindowsEventQueueBuffer()
{
// remember thread. we'll be posting messages to it.
m_thread = GetCurrentThreadId();
// create a message type for custom events
m_userEvent = RegisterWindowMessage("SYNERGY_USER_EVENT");
// get message type for daemon quit
m_daemonQuit = CArchMiscWindows::getDaemonQuitMessage();
// make sure this thread has a message queue
MSG dummy;
PeekMessage(&dummy, NULL, WM_USER, WM_USER, PM_NOREMOVE);
}
CMSWindowsEventQueueBuffer::~CMSWindowsEventQueueBuffer()
{
// do nothing
}
void
CMSWindowsEventQueueBuffer::waitForEvent(double timeout)
{
// check if messages are available first. if we don't do this then
// MsgWaitForMultipleObjects() will block even if the queue isn't
// empty if the messages in the queue were there before the last
// call to GetMessage()/PeekMessage().
if (HIWORD(GetQueueStatus(QS_ALLINPUT)) != 0) {
return;
}
// convert timeout
DWORD t;
if (timeout < 0.0) {
t = INFINITE;
}
else {
t = (DWORD)(1000.0 * timeout);
}
// wait for a message. we cannot be interrupted by thread
// cancellation but that's okay because we're run in the main
// thread and we never cancel that thread.
HANDLE dummy[1];
MsgWaitForMultipleObjects(0, dummy, FALSE, t, QS_ALLINPUT);
}
IEventQueueBuffer::Type
CMSWindowsEventQueueBuffer::getEvent(CEvent& event, UInt32& dataID)
{
// peek at messages first. waiting for QS_ALLINPUT will return
// if a message has been sent to our window but GetMessage will
// dispatch that message behind our backs and block. PeekMessage
// will also dispatch behind our backs but won't block.
if (!PeekMessage(&m_event, NULL, 0, 0, PM_NOREMOVE) &&
!PeekMessage(&m_event, (HWND)-1, 0, 0, PM_NOREMOVE)) {
return kNone;
}
// BOOL. yeah, right.
BOOL result = GetMessage(&m_event, NULL, 0, 0);
if (result == -1) {
return kNone;
}
else if (result == 0) {
event = CEvent(CEvent::kQuit);
return kSystem;
}
else if (m_daemonQuit != 0 && m_event.message == m_daemonQuit) {
event = CEvent(CEvent::kQuit);
return kSystem;
}
else if (m_event.message == m_userEvent) {
dataID = static_cast<UInt32>(m_event.wParam);
return kUser;
}
else {
event = CEvent(CEvent::kSystem,
IEventQueue::getSystemTarget(), &m_event);
return kSystem;
}
}
bool
CMSWindowsEventQueueBuffer::addEvent(UInt32 dataID)
{
return (PostThreadMessage(m_thread, m_userEvent,
static_cast<WPARAM>(dataID), 0) != 0);
}
bool
CMSWindowsEventQueueBuffer::isEmpty() const
{
return (HIWORD(GetQueueStatus(QS_ALLINPUT)) == 0);
}
CEventQueueTimer*
CMSWindowsEventQueueBuffer::newTimer(double, bool) const
{
return new CEventQueueTimer;
}
void
CMSWindowsEventQueueBuffer::deleteTimer(CEventQueueTimer* timer) const
{
delete timer;
}

View File

@@ -0,0 +1,47 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CMSWINDOWSEVENTQUEUEBUFFER_H
#define CMSWINDOWSEVENTQUEUEBUFFER_H
#include "IEventQueueBuffer.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
//! Event queue buffer for Win32
class CMSWindowsEventQueueBuffer : public IEventQueueBuffer {
public:
CMSWindowsEventQueueBuffer();
virtual ~CMSWindowsEventQueueBuffer();
// IEventQueueBuffer overrides
virtual void waitForEvent(double timeout);
virtual Type getEvent(CEvent& event, UInt32& dataID);
virtual bool addEvent(UInt32 dataID);
virtual bool isEmpty() const;
virtual CEventQueueTimer*
newTimer(double duration, bool oneShot) const;
virtual void deleteTimer(CEventQueueTimer*) const;
private:
DWORD m_thread;
UINT m_userEvent;
MSG m_event;
UINT m_daemonQuit;
};
#endif

View File

@@ -0,0 +1,69 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2011 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CMSWindowsHookLibraryLoader.h"
#include "XScreen.h"
#include "CLog.h"
CMSWindowsHookLibraryLoader::CMSWindowsHookLibraryLoader() :
m_init(NULL),
m_cleanup(NULL),
m_setSides(NULL),
m_setZone(NULL),
m_setMode(NULL)
{
}
CMSWindowsHookLibraryLoader::~CMSWindowsHookLibraryLoader()
{
// TODO: take ownership of m_ and delete them.
}
HINSTANCE
CMSWindowsHookLibraryLoader::openHookLibrary(const char* name)
{
// load the hook library
HINSTANCE hookLibrary = LoadLibrary(name);
if (hookLibrary == NULL) {
LOG((CLOG_ERR "failed to load hook library, %s.dll is missing", name));
throw XScreenOpenFailure();
}
// look up functions
m_setSides = (SetSidesFunc)GetProcAddress(hookLibrary, "setSides");
m_setZone = (SetZoneFunc)GetProcAddress(hookLibrary, "setZone");
m_setMode = (SetModeFunc)GetProcAddress(hookLibrary, "setMode");
m_init = (InitFunc)GetProcAddress(hookLibrary, "init");
m_cleanup = (CleanupFunc)GetProcAddress(hookLibrary, "cleanup");
if (m_setSides == NULL ||
m_setZone == NULL ||
m_setMode == NULL ||
m_init == NULL ||
m_cleanup == NULL) {
LOG((CLOG_ERR "invalid hook library, use a newer %s.dll", name));
throw XScreenOpenFailure();
}
// initialize hook library
if (m_init(GetCurrentThreadId()) == 0) {
LOG((CLOG_ERR "failed to init %s.dll, another program may be using it", name));
LOG((CLOG_INFO "restarting your computer may solve this error"));
throw XScreenOpenFailure();
}
return hookLibrary;
}

View File

@@ -0,0 +1,45 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2011 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CMSWINDOWSHOOKLIBRARYLOADER_H
#define CMSWINDOWSHOOKLIBRARYLOADER_H
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include "CSynergyHook.h"
//! Loads Windows hook DLLs.
class CMSWindowsHookLibraryLoader
{
public:
CMSWindowsHookLibraryLoader();
virtual ~CMSWindowsHookLibraryLoader();
HINSTANCE openHookLibrary(const char* name);
// TODO: either make these private or expose properly
InitFunc m_init;
CleanupFunc m_cleanup;
SetSidesFunc m_setSides;
SetZoneFunc m_setZone;
SetModeFunc m_setMode;
private:
HINSTANCE m_hookLibrary;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,230 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CMSWINDOWSKEYSTATE_H
#define CMSWINDOWSKEYSTATE_H
#include "CKeyState.h"
#include "CString.h"
#include "stdvector.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
class CEvent;
class CEventQueueTimer;
class CMSWindowsDesks;
class IEventQueue;
//! Microsoft Windows key mapper
/*!
This class maps KeyIDs to keystrokes.
*/
class CMSWindowsKeyState : public CKeyState {
public:
CMSWindowsKeyState(CMSWindowsDesks* desks, void* eventTarget);
CMSWindowsKeyState(CMSWindowsDesks* desks, void* eventTarget, IEventQueue& eventQueue, CKeyMap& keyMap);
virtual ~CMSWindowsKeyState();
//! @name manipulators
//@{
//! Handle screen disabling
/*!
Called when screen is disabled. This is needed to deal with platform
brokenness.
*/
void disable();
//! Set the active keyboard layout
/*!
Uses \p keyLayout when querying the keyboard.
*/
void setKeyLayout(HKL keyLayout);
//! Test and set autorepeat state
/*!
Returns true if the given button is autorepeating and updates internal
state.
*/
bool testAutoRepeat(bool press, bool isRepeat, KeyButton);
//! Remember modifier state
/*!
Records the current non-toggle modifier state.
*/
void saveModifiers();
//! Set effective modifier state
/*!
Temporarily sets the non-toggle modifier state to those saved by the
last call to \c saveModifiers if \p enable is \c true. Restores the
modifier state to the current modifier state if \p enable is \c false.
This is for synthesizing keystrokes on the primary screen when the
cursor is on a secondary screen. When on a secondary screen we capture
all non-toggle modifier state, track the state internally and do not
pass it on. So if Alt+F1 synthesizes Alt+X we need to synthesize
not just X but also Alt, despite the fact that our internal modifier
state indicates Alt is down, because local apps never saw the Alt down
event.
*/
void useSavedModifiers(bool enable);
//@}
//! @name accessors
//@{
//! Map a virtual key to a button
/*!
Returns the button for the \p virtualKey.
*/
KeyButton virtualKeyToButton(UINT virtualKey) const;
//! Map key event to a key
/*!
Converts a key event into a KeyID and the shadow modifier state
to a modifier mask.
*/
KeyID mapKeyFromEvent(WPARAM charAndVirtKey,
LPARAM info, KeyModifierMask* maskOut) const;
//! Check if keyboard groups have changed
/*!
Returns true iff the number or order of the keyboard groups have
changed since the last call to updateKeys().
*/
bool didGroupsChange() const;
//! Map key to virtual key
/*!
Returns the virtual key for key \p key or 0 if there's no such virtual
key.
*/
UINT mapKeyToVirtualKey(KeyID key) const;
//! Map virtual key and button to KeyID
/*!
Returns the KeyID for virtual key \p virtualKey and button \p button
(button should include the extended key bit), or kKeyNone if there is
no such key.
*/
static KeyID getKeyID(UINT virtualKey, KeyButton button);
//@}
// IKeyState overrides
virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
KeyButton button);
virtual bool fakeKeyRepeat(KeyID id, KeyModifierMask mask,
SInt32 count, KeyButton button);
virtual bool fakeCtrlAltDel();
virtual KeyModifierMask
pollActiveModifiers() const;
virtual SInt32 pollActiveGroup() const;
virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const;
// CKeyState overrides
virtual void onKey(KeyButton button, bool down,
KeyModifierMask newState);
virtual void sendKeyEvent(void* target,
bool press, bool isAutoRepeat,
KeyID key, KeyModifierMask mask,
SInt32 count, KeyButton button);
// Unit test accessors
KeyButton getLastDown() const { return m_lastDown; }
void setLastDown(KeyButton value) { m_lastDown = value; }
KeyModifierMask getSavedModifiers() const { return m_savedModifiers; }
void setSavedModifiers(KeyModifierMask value) { m_savedModifiers = value; }
protected:
// CKeyState overrides
virtual void getKeyMap(CKeyMap& keyMap);
virtual void fakeKey(const Keystroke& keystroke);
virtual KeyModifierMask&
getActiveModifiersRValue();
private:
typedef std::vector<HKL> GroupList;
// send ctrl+alt+del hotkey event on NT family
static void ctrlAltDelThread(void*);
bool getGroups(GroupList&) const;
void setWindowGroup(SInt32 group);
void fixKeys();
void handleFixKeys(const CEvent&, void*);
KeyID getIDForKey(CKeyMap::KeyItem& item,
KeyButton button, UINT virtualKey,
PBYTE keyState, HKL hkl) const;
void addKeyEntry(CKeyMap& keyMap, CKeyMap::KeyItem& item);
void init();
private:
// not implemented
CMSWindowsKeyState(const CMSWindowsKeyState&);
CMSWindowsKeyState& operator=(const CMSWindowsKeyState&);
private:
typedef std::map<HKL, SInt32> GroupMap;
typedef std::map<KeyID, UINT> KeyToVKMap;
bool m_is95Family;
void* m_eventTarget;
CMSWindowsDesks* m_desks;
HKL m_keyLayout;
UINT m_buttonToVK[512];
UINT m_buttonToNumpadVK[512];
KeyButton m_virtualKeyToButton[256];
KeyToVKMap m_keyToVKMap;
IEventQueue& m_eventQueue;
// the timer used to check for fixing key state
CEventQueueTimer* m_fixTimer;
// the groups (keyboard layouts)
GroupList m_groups;
GroupMap m_groupMap;
// the last button that we generated a key down event for. this
// is zero if the last key event was a key up. we use this to
// synthesize key repeats since the low level keyboard hook can't
// tell us if an event is a key repeat.
KeyButton m_lastDown;
// modifier tracking
bool m_useSavedModifiers;
KeyModifierMask m_savedModifiers;
KeyModifierMask m_originalSavedModifiers;
// pointer to ToUnicodeEx. on win95 family this will be NULL.
typedef int (WINAPI *ToUnicodeEx_t)(UINT wVirtKey,
UINT wScanCode,
PBYTE lpKeyState,
LPWSTR pwszBuff,
int cchBuff,
UINT wFlags,
HKL dwhkl);
ToUnicodeEx_t m_ToUnicodeEx;
static const KeyID s_virtualKey[];
};
#endif

View File

@@ -0,0 +1,488 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2009 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CMSWindowsRelauncher.h"
#include "CThread.h"
#include "TMethodJob.h"
#include "CLog.h"
#include "CArch.h"
#include "Version.h"
#include "CArchDaemonWindows.h"
#include "XArchWindows.h"
#include "CApp.h"
#include "CArgsBase.h"
#include <Tlhelp32.h>
#include <UserEnv.h>
#include <sstream>
enum {
kOutputBufferSize = 4096
};
typedef VOID (WINAPI *SendSas)(BOOL asUser);
CMSWindowsRelauncher::CMSWindowsRelauncher(bool autoDetectCommand) :
m_thread(NULL),
m_autoDetectCommand(autoDetectCommand),
m_running(true),
m_commandChanged(false),
m_stdOutWrite(NULL),
m_stdOutRead(NULL)
{
}
CMSWindowsRelauncher::~CMSWindowsRelauncher()
{
}
void
CMSWindowsRelauncher::startAsync()
{
m_thread = new CThread(new TMethodJob<CMSWindowsRelauncher>(
this, &CMSWindowsRelauncher::mainLoop, nullptr));
m_outputThread = new CThread(new TMethodJob<CMSWindowsRelauncher>(
this, &CMSWindowsRelauncher::outputLoop, nullptr));
}
void
CMSWindowsRelauncher::stop()
{
m_running = false;
m_thread->wait(5);
}
// this still gets the physical session (the one the keyboard and
// mouse is connected to), sometimes this returns -1 but not sure why
DWORD
CMSWindowsRelauncher::getSessionId()
{
return WTSGetActiveConsoleSessionId();
}
BOOL
CMSWindowsRelauncher::winlogonInSession(DWORD sessionId, PHANDLE process)
{
// first we need to take a snapshot of the running processes
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snapshot == INVALID_HANDLE_VALUE) {
LOG((CLOG_ERR "could not get process snapshot (error: %i)",
GetLastError()));
return 0;
}
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
// get the first process, and if we can't do that then it's
// unlikely we can go any further
BOOL gotEntry = Process32First(snapshot, &entry);
if (!gotEntry) {
LOG((CLOG_ERR "could not get first process entry (error: %i)",
GetLastError()));
return 0;
}
// used to record process names for debug info
std::list<std::string> nameList;
// now just iterate until we can find winlogon.exe pid
DWORD pid = 0;
while(gotEntry) {
// make sure we're not checking the system process
if (entry.th32ProcessID != 0) {
DWORD processSessionId;
BOOL pidToSidRet = ProcessIdToSessionId(
entry.th32ProcessID, &processSessionId);
if (!pidToSidRet) {
LOG((CLOG_ERR "could not get session id for process id %i (error: %i)",
entry.th32ProcessID, GetLastError()));
return 0;
}
// only pay attention to processes in the active session
if (processSessionId == sessionId) {
// store the names so we can record them for debug
nameList.push_back(entry.szExeFile);
if (_stricmp(entry.szExeFile, "winlogon.exe") == 0) {
pid = entry.th32ProcessID;
break;
}
}
}
// now move on to the next entry (if we're not at the end)
gotEntry = Process32Next(snapshot, &entry);
if (!gotEntry) {
DWORD err = GetLastError();
if (err != ERROR_NO_MORE_FILES) {
// only worry about error if it's not the end of the snapshot
LOG((CLOG_ERR "could not get subsiquent process entry (error: %i)",
GetLastError()));
return 0;
}
}
}
std::string nameListJoin;
for(std::list<std::string>::iterator it = nameList.begin();
it != nameList.end(); it++) {
nameListJoin.append(*it);
nameListJoin.append(", ");
}
LOG((CLOG_DEBUG "checked processes while looking for winlogon.exe: %s",
nameListJoin.c_str()));
CloseHandle(snapshot);
if (pid) {
// now get the process so we can get the process, with which
// we'll use to get the process token.
*process = OpenProcess(MAXIMUM_ALLOWED, FALSE, pid);
return true;
}
else {
LOG((CLOG_DEBUG "could not find winlogon.exe in session %i", sessionId));
return false;
}
}
// gets the current user (so we can launch under their session)
HANDLE
CMSWindowsRelauncher::getCurrentUserToken(DWORD sessionId, LPSECURITY_ATTRIBUTES security)
{
HANDLE currentToken;
HANDLE winlogonProcess;
if (winlogonInSession(sessionId, &winlogonProcess)) {
LOG((CLOG_DEBUG "session %i has winlogon.exe", sessionId));
// get the token, so we can re-launch with this token
// -- do we really need all these access bits?
BOOL tokenRet = OpenProcessToken(
winlogonProcess,
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY |
TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY |
TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE,
&currentToken);
if (!tokenRet) {
LOG((CLOG_ERR "could not open token (error: %i)", GetLastError()));
return 0;
}
}
else {
LOG((CLOG_ERR "session %i does not have winlogon.exe "
"which is needed for re-launch", sessionId));
return 0;
}
HANDLE primaryToken;
BOOL duplicateRet = DuplicateTokenEx(
currentToken, MAXIMUM_ALLOWED, security,
SecurityImpersonation, TokenPrimary, &primaryToken);
if (!duplicateRet) {
LOG((CLOG_ERR "could not duplicate token %i (error: %i)",
currentToken, GetLastError()));
return 0;
}
return primaryToken;
}
void
CMSWindowsRelauncher::mainLoop(void*)
{
SendSas sendSasFunc = NULL;
HINSTANCE sasLib = LoadLibrary("sas.dll");
if (sasLib) {
LOG((CLOG_DEBUG "found sas.dll"));
sendSasFunc = (SendSas)GetProcAddress(sasLib, "SendSAS");
}
DWORD sessionId = -1;
bool launched = false;
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if (!CreatePipe(&m_stdOutRead, &m_stdOutWrite, &saAttr, 0)) {
throw XArch(new XArchEvalWindows());
}
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
while (m_running) {
HANDLE sendSasEvent = 0;
if (sasLib && sendSasFunc) {
// can't we just create one event? seems weird creating a new
// event every second...
sendSasEvent = CreateEvent(NULL, FALSE, FALSE, "Global\\SendSAS");
}
DWORD newSessionId = getSessionId();
// only enter here when id changes, and the session isn't -1, which
// may mean that there is no active session.
if (((newSessionId != sessionId) && (newSessionId != -1)) || m_commandChanged) {
m_commandChanged = false;
if (launched) {
LOG((CLOG_DEBUG "closing existing process to make way for new one"));
shutdownProcess(pi, 10);
launched = false;
}
// ok, this is now the active session (forget the old one if any)
sessionId = newSessionId;
SECURITY_ATTRIBUTES sa;
ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
// get the token for the user in active session, which is the
// one receiving input from mouse and keyboard.
HANDLE userToken = getCurrentUserToken(sessionId, &sa);
if (userToken != 0) {
LOG((CLOG_DEBUG "got user token to launch new process"));
std::string cmd = command();
if (cmd == "") {
LOG((CLOG_WARN "nothing to launch, no command specified."));
continue;
}
// in case reusing process info struct
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.lpDesktop = "winsta0\\default";
si.hStdError = m_stdOutWrite;
si.hStdOutput = m_stdOutWrite;
si.dwFlags |= STARTF_USESTDHANDLES;
LPVOID environment;
BOOL blockRet = CreateEnvironmentBlock(&environment, userToken, FALSE);
if (!blockRet) {
LOG((CLOG_ERR "could not create environment block (error: %i)",
GetLastError()));
continue;
}
else {
DWORD creationFlags =
NORMAL_PRIORITY_CLASS |
CREATE_NO_WINDOW |
CREATE_UNICODE_ENVIRONMENT;
// re-launch in current active user session
BOOL createRet = CreateProcessAsUser(
userToken, NULL, LPSTR(cmd.c_str()),
&sa, NULL, TRUE, creationFlags,
environment, NULL, &si, &pi);
DestroyEnvironmentBlock(environment);
CloseHandle(userToken);
if (!createRet) {
LOG((CLOG_ERR "could not launch (error: %i)", GetLastError()));
continue;
}
else {
LOG((CLOG_DEBUG "launched in session %i (cmd: %s)",
sessionId, cmd.c_str()));
launched = true;
}
}
}
}
if (sendSasEvent) {
// use SendSAS event to wait for next session.
if (WaitForSingleObject(sendSasEvent, 1000) == WAIT_OBJECT_0 && sendSasFunc) {
LOG((CLOG_DEBUG "calling SendSAS"));
sendSasFunc(FALSE);
}
CloseHandle(sendSasEvent);
}
else {
// check for session change every second.
ARCH->sleep(1);
}
}
if (launched) {
LOG((CLOG_DEBUG "terminated running process on exit"));
shutdownProcess(pi, 10);
}
}
void
CMSWindowsRelauncher::command(const std::string& command)
{
LOG((CLOG_INFO "service command updated"));
LOG((CLOG_DEBUG "new command: %s", command.c_str()));
m_command = command;
m_commandChanged = true;
}
std::string
CMSWindowsRelauncher::command() const
{
if (!m_autoDetectCommand) {
return m_command;
}
// seems like a fairly convoluted way to get the process name
const char* launchName = CApp::instance().argsBase().m_pname;
std::string args = ARCH->commandLine();
// build up a full command line
std::stringstream cmdTemp;
cmdTemp << launchName << /*" --debug-data session-" << sessionId <<*/ args;
std::string cmd = cmdTemp.str();
size_t i;
std::string find = "--relaunch";
while((i = cmd.find(find)) != std::string::npos) {
cmd.replace(i, find.length(), "");
}
return cmd;
}
void
CMSWindowsRelauncher::outputLoop(void*)
{
CHAR buffer[kOutputBufferSize];
while (true) {
DWORD bytesRead;
BOOL success = ReadFile(m_stdOutRead, buffer, kOutputBufferSize, &bytesRead, NULL);
// assume the process has gone away? slow down
// the reads until another one turns up.
if (!success || bytesRead == 0) {
ARCH->sleep(1);
}
else {
// send process output over IPC to GUI.
buffer[bytesRead] = '\0';
ARCH->ipcLog().writeLog(kINFO, buffer);
}
}
}
void
CMSWindowsRelauncher::shutdownProcess(const PROCESS_INFORMATION& pi, int timeout)
{
DWORD exitCode;
GetExitCodeProcess(pi.hProcess, &exitCode);
if (exitCode != STILL_ACTIVE)
return;
sendIpcMessage(kIpcShutdown, "");
// wait for process to exit gracefully.
double start = ARCH->time();
while (true)
{
GetExitCodeProcess(pi.hProcess, &exitCode);
if (exitCode != STILL_ACTIVE) {
LOG((CLOG_INFO "process %d was shutdown successfully", pi.dwProcessId));
break;
}
else {
if ((ARCH->time() - start) > timeout) {
// if timeout reached, kill forcefully.
// calling TerminateProcess on synergy is very bad!
// it causes the hook DLL to stay loaded in some apps,
// making it impossible to start synergy again.
LOG((CLOG_WARN "shutdown timed out after %d secs, forcefully terminating", pi.dwProcessId));
TerminateProcess(pi.hProcess, kExitSuccess);
break;
}
ARCH->sleep(1);
}
}
}
// TODO: put this in an IPC client class.
void
CMSWindowsRelauncher::sendIpcMessage(int type, const char* data)
{
char message[1024];
message[0] = type;
char* messagePtr = message;
messagePtr++;
strcpy(messagePtr, data);
HANDLE pipe = CreateFile(
_T("\\\\.\\pipe\\SynergyNode"),
GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (pipe == INVALID_HANDLE_VALUE)
{
LOG((CLOG_ERR "could not connect to node, error: %d", GetLastError()));
return;
}
DWORD dwMode = PIPE_READMODE_MESSAGE;
BOOL stateSuccess = SetNamedPipeHandleState(pipe, &dwMode, NULL, NULL);
if (!stateSuccess)
{
LOG((CLOG_ERR "could not set node pipe state, error: %d", GetLastError()));
return;
}
DWORD written;
BOOL writeSuccess = WriteFile(
pipe, message, (DWORD)strlen(message), &written, NULL);
if (!writeSuccess)
{
LOG((CLOG_ERR "could not write to node pipe, error: %d", GetLastError()));
return;
}
CloseHandle(pipe);
}

View File

@@ -0,0 +1,54 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2009 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <string>
#include <list>
class CThread;
class CMSWindowsRelauncher {
public:
CMSWindowsRelauncher(bool autoDetectCommand);
virtual ~CMSWindowsRelauncher();
void startAsync();
std::string command() const;
void command(const std::string& command);
void stop();
private:
void mainLoop(void*);
BOOL winlogonInSession(DWORD sessionId, PHANDLE process);
DWORD getSessionId();
HANDLE getCurrentUserToken(DWORD sessionId, LPSECURITY_ATTRIBUTES security);
void outputLoop(void*);
void sendIpcMessage(int type, const char* data);
void shutdownProcess(const PROCESS_INFORMATION& pi, int timeout);
private:
CThread* m_thread;
bool m_autoDetectCommand;
std::string m_command;
bool m_running;
bool m_commandChanged;
HANDLE m_stdOutWrite;
HANDLE m_stdOutRead;
CThread* m_outputThread;
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,332 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CMSWINDOWSSCREEN_H
#define CMSWINDOWSSCREEN_H
#include "CPlatformScreen.h"
#include "CSynergyHook.h"
#include "CCondVar.h"
#include "CMutex.h"
#include "CString.h"
#include "CMSWindowsHookLibraryLoader.h"
#include "CGameDevice.h"
#include "CMSWindowsXInput.h"
#include "CEventGameDevice.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
class CEventQueueTimer;
class CMSWindowsDesks;
class CMSWindowsKeyState;
class CMSWindowsScreenSaver;
class CThread;
//! Implementation of IPlatformScreen for Microsoft Windows
class CMSWindowsScreen : public CPlatformScreen {
public:
CMSWindowsScreen(bool isPrimary, bool noHooks, const CGameDeviceInfo &gameDevice);
virtual ~CMSWindowsScreen();
//! @name manipulators
//@{
//! Initialize
/*!
Saves the application's HINSTANCE. This \b must be called by
WinMain with the HINSTANCE it was passed.
*/
static void init(HINSTANCE);
//@}
//! @name accessors
//@{
//! Get instance
/*!
Returns the application instance handle passed to init().
*/
static HINSTANCE getWindowInstance();
//@}
// IScreen overrides
virtual void* getEventTarget() const;
virtual bool getClipboard(ClipboardID id, IClipboard*) const;
virtual void getShape(SInt32& x, SInt32& y,
SInt32& width, SInt32& height) const;
virtual void getCursorPos(SInt32& x, SInt32& y) const;
// IPrimaryScreen overrides
virtual void reconfigure(UInt32 activeSides);
virtual void warpCursor(SInt32 x, SInt32 y);
virtual UInt32 registerHotKey(KeyID key,
KeyModifierMask mask);
virtual void unregisterHotKey(UInt32 id);
virtual void fakeInputBegin();
virtual void fakeInputEnd();
virtual SInt32 getJumpZoneSize() const;
virtual bool isAnyMouseButtonDown() const;
virtual void getCursorCenter(SInt32& x, SInt32& y) const;
virtual void gameDeviceTimingResp(UInt16 freq);
virtual void gameDeviceFeedback(GameDeviceID id, UInt16 m1, UInt16 m2);
// ISecondaryScreen overrides
virtual void fakeMouseButton(ButtonID id, bool press);
virtual void fakeMouseMove(SInt32 x, SInt32 y) const;
virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const;
virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const;
virtual void fakeGameDeviceButtons(GameDeviceID id, GameDeviceButton buttons) const;
virtual void fakeGameDeviceSticks(GameDeviceID id, SInt16 x1, SInt16 y1, SInt16 x2, SInt16 y2) const;
virtual void fakeGameDeviceTriggers(GameDeviceID id, UInt8 t1, UInt8 t2) const;
virtual void queueGameDeviceTimingReq() const;
// IKeyState overrides
virtual void updateKeys();
virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
KeyButton button);
virtual bool fakeKeyRepeat(KeyID id, KeyModifierMask mask,
SInt32 count, KeyButton button);
virtual bool fakeKeyUp(KeyButton button);
virtual void fakeAllKeysUp();
// IPlatformScreen overrides
virtual void enable();
virtual void disable();
virtual void enter();
virtual bool leave();
virtual bool setClipboard(ClipboardID, const IClipboard*);
virtual void checkClipboards();
virtual void openScreensaver(bool notify);
virtual void closeScreensaver();
virtual void screensaver(bool activate);
virtual void resetOptions();
virtual void setOptions(const COptionsList& options);
virtual void setSequenceNumber(UInt32);
virtual bool isPrimary() const;
protected:
// IPlatformScreen overrides
virtual void handleSystemEvent(const CEvent&, void*);
virtual void updateButtons();
virtual IKeyState* getKeyState() const;
private:
// initialization and shutdown operations
HINSTANCE openHookLibrary(const char* name);
void closeHookLibrary(HINSTANCE hookLibrary) const;
HCURSOR createBlankCursor() const;
void destroyCursor(HCURSOR cursor) const;
ATOM createWindowClass() const;
ATOM createDeskWindowClass(bool isPrimary) const;
void destroyClass(ATOM windowClass) const;
HWND createWindow(ATOM windowClass, const char* name) const;
void destroyWindow(HWND) const;
// convenience function to send events
public: // HACK
void sendEvent(CEvent::Type type, void* = NULL);
private: // HACK
void sendClipboardEvent(CEvent::Type type, ClipboardID id);
// handle message before it gets dispatched. returns true iff
// the message should not be dispatched.
bool onPreDispatch(HWND, UINT, WPARAM, LPARAM);
// handle message before it gets dispatched. returns true iff
// the message should not be dispatched.
bool onPreDispatchPrimary(HWND, UINT, WPARAM, LPARAM);
// handle message. returns true iff handled and optionally sets
// \c *result (which defaults to 0).
bool onEvent(HWND, UINT, WPARAM, LPARAM, LRESULT* result);
// message handlers
bool onMark(UInt32 mark);
bool onKey(WPARAM, LPARAM);
bool onHotKey(WPARAM, LPARAM);
bool onMouseButton(WPARAM, LPARAM);
bool onMouseMove(SInt32 x, SInt32 y);
bool onMouseWheel(SInt32 xDelta, SInt32 yDelta);
bool onScreensaver(bool activated);
bool onDisplayChange();
bool onClipboardChange();
// warp cursor without discarding queued events
void warpCursorNoFlush(SInt32 x, SInt32 y);
// discard posted messages
void nextMark();
// test if event should be ignored
bool ignore() const;
// update screen size cache
void updateScreenShape();
// fix timer callback
void handleFixes(const CEvent&, void*);
// fix the clipboard viewer chain
void fixClipboardViewer();
// enable/disable special key combinations so we can catch/pass them
void enableSpecialKeys(bool) const;
// map a button event to a button ID
ButtonID mapButtonFromEvent(WPARAM msg, LPARAM button) const;
// map a button event to a press (true) or release (false)
bool mapPressFromEvent(WPARAM msg, LPARAM button) const;
// job to update the key state
void updateKeysCB(void*);
// determine whether the mouse is hidden by the system and force
// it to be displayed if user has entered this secondary screen.
void forceShowCursor();
// forceShowCursor uses MouseKeys to show the cursor. since we
// don't actually want MouseKeys behavior we have to make sure
// it applies when NumLock is in whatever state it's not in now.
// this method does that.
void updateForceShowCursor();
// our window proc
static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM);
private:
struct CHotKeyItem {
public:
CHotKeyItem(UINT vk, UINT modifiers);
UINT getVirtualKey() const;
bool operator<(const CHotKeyItem&) const;
private:
UINT m_keycode;
UINT m_mask;
};
typedef std::map<UInt32, CHotKeyItem> HotKeyMap;
typedef std::vector<UInt32> HotKeyIDList;
typedef std::map<CHotKeyItem, UInt32> HotKeyToIDMap;
static HINSTANCE s_windowInstance;
// true if screen is being used as a primary screen, false otherwise
bool m_isPrimary;
// true if hooks are not to be installed (useful for debugging)
bool m_noHooks;
// true if windows 95/98/me
bool m_is95Family;
// true if mouse has entered the screen
bool m_isOnScreen;
// our resources
ATOM m_class;
// screen shape stuff
SInt32 m_x, m_y;
SInt32 m_w, m_h;
SInt32 m_xCenter, m_yCenter;
// true if system appears to have multiple monitors
bool m_multimon;
// last mouse position
SInt32 m_xCursor, m_yCursor;
// last clipboard
UInt32 m_sequenceNumber;
// used to discard queued messages that are no longer needed
UInt32 m_mark;
UInt32 m_markReceived;
// the main loop's thread id
DWORD m_threadID;
// timer for periodically checking stuff that requires polling
CEventQueueTimer* m_fixTimer;
// the keyboard layout to use when off primary screen
HKL m_keyLayout;
// screen saver stuff
CMSWindowsScreenSaver* m_screensaver;
bool m_screensaverNotify;
bool m_screensaverActive;
// clipboard stuff. our window is used mainly as a clipboard
// owner and as a link in the clipboard viewer chain.
HWND m_window;
HWND m_nextClipboardWindow;
bool m_ownClipboard;
// one desk per desktop and a cond var to communicate with it
CMSWindowsDesks* m_desks;
// hook library stuff
HINSTANCE m_hookLibrary;
// keyboard stuff
CMSWindowsKeyState* m_keyState;
// hot key stuff
HotKeyMap m_hotKeys;
HotKeyIDList m_oldHotKeyIDs;
HotKeyToIDMap m_hotKeyToIDMap;
// map of button state
bool m_buttons[1 + kButtonExtra0 + 1];
// the system shows the mouse cursor when an internal display count
// is >= 0. this count is maintained per application but there's
// apparently a system wide count added to the application's count.
// this system count is 0 if there's a mouse attached to the system
// and -1 otherwise. the MouseKeys accessibility feature can modify
// this system count by making the system appear to have a mouse.
//
// m_hasMouse is true iff there's a mouse attached to the system or
// MouseKeys is simulating one. we track this so we can force the
// cursor to be displayed when the user has entered this screen.
// m_showingMouse is true when we're doing that.
bool m_hasMouse;
bool m_showingMouse;
bool m_gotOldMouseKeys;
MOUSEKEYS m_mouseKeys;
MOUSEKEYS m_oldMouseKeys;
// loads synrgyhk.dll
CMSWindowsHookLibraryLoader
m_hookLibraryLoader;
const CGameDeviceInfo& m_gameDeviceInfo;
CGameDevice* m_gameDevice;
static CMSWindowsScreen* s_screen;
// save last position of mouse to compute next delta movement
void saveMousePosition(SInt32 x, SInt32 y);
};
#endif

View File

@@ -0,0 +1,501 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CMSWindowsScreenSaver.h"
#include "CMSWindowsScreen.h"
#include "CThread.h"
#include "CLog.h"
#include "TMethodJob.h"
#include "CArch.h"
#include "CArchMiscWindows.h"
#include <malloc.h>
#include <tchar.h>
#if !defined(SPI_GETSCREENSAVERRUNNING)
#define SPI_GETSCREENSAVERRUNNING 114
#endif
static const TCHAR* g_isSecureNT = "ScreenSaverIsSecure";
static const TCHAR* g_isSecure9x = "ScreenSaveUsePassword";
static const TCHAR* const g_pathScreenSaverIsSecure[] = {
"Control Panel",
"Desktop",
NULL
};
//
// CMSWindowsScreenSaver
//
CMSWindowsScreenSaver::CMSWindowsScreenSaver() :
m_wasSecure(false),
m_wasSecureAnInt(false),
m_process(NULL),
m_watch(NULL),
m_threadID(0),
m_active(false)
{
// detect OS
m_is95Family = false;
m_is95 = false;
m_isNT = false;
OSVERSIONINFO info;
info.dwOSVersionInfoSize = sizeof(info);
if (GetVersionEx(&info)) {
m_is95Family = (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
if (info.dwPlatformId == VER_PLATFORM_WIN32_NT &&
info.dwMajorVersion <= 4) {
m_isNT = true;
}
else if (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS &&
info.dwMajorVersion == 4 &&
info.dwMinorVersion == 0) {
m_is95 = true;
}
}
// check if screen saver is enabled
SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &m_wasEnabled, 0);
}
CMSWindowsScreenSaver::~CMSWindowsScreenSaver()
{
unwatchProcess();
}
bool
CMSWindowsScreenSaver::checkStarted(UINT msg, WPARAM wParam, LPARAM lParam)
{
// if already started then say it didn't just start
if (m_active) {
return false;
}
// screen saver may have started. look for it and get
// the process. if we can't find it then assume it
// didn't really start. we wait a moment before
// looking to give the screen saver a chance to start.
// this shouldn't be a problem since we only get here
// if the screen saver wants to kick in, meaning that
// the system is idle or the user deliberately started
// the screen saver.
Sleep(250);
// set parameters common to all screen saver handling
m_threadID = GetCurrentThreadId();
m_msg = msg;
m_wParam = wParam;
m_lParam = lParam;
// we handle the screen saver differently for the windows
// 95 and nt families.
if (m_is95Family) {
// on windows 95 we wait for the screen saver process
// to terminate. get the process.
DWORD processID = findScreenSaver();
HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, processID);
if (process == NULL) {
// didn't start
LOG((CLOG_DEBUG2 "can't open screen saver process"));
return false;
}
// watch for the process to exit
watchProcess(process);
}
else {
// on the windows nt family we wait for the desktop to
// change until it's neither the Screen-Saver desktop
// nor a desktop we can't open (the login desktop).
// since windows will send the request-to-start-screen-
// saver message even when the screen saver is disabled
// we first check that the screen saver is indeed active
// before watching for it to stop.
if (!isActive()) {
LOG((CLOG_DEBUG2 "can't open screen saver desktop"));
return false;
}
watchDesktop();
}
return true;
}
void
CMSWindowsScreenSaver::enable()
{
SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, m_wasEnabled, 0, 0);
// restore password protection
if (m_wasSecure) {
setSecure(true, m_wasSecureAnInt);
}
// restore display power down
CArchMiscWindows::removeBusyState(CArchMiscWindows::kDISPLAY);
}
void
CMSWindowsScreenSaver::disable()
{
SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &m_wasEnabled, 0);
SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, FALSE, 0, 0);
// disable password protected screensaver
m_wasSecure = isSecure(&m_wasSecureAnInt);
if (m_wasSecure) {
setSecure(false, m_wasSecureAnInt);
}
// disable display power down
CArchMiscWindows::addBusyState(CArchMiscWindows::kDISPLAY);
}
void
CMSWindowsScreenSaver::activate()
{
// don't activate if already active
if (!isActive()) {
// activate
HWND hwnd = GetForegroundWindow();
if (hwnd != NULL) {
PostMessage(hwnd, WM_SYSCOMMAND, SC_SCREENSAVE, 0);
}
else {
// no foreground window. pretend we got the event instead.
DefWindowProc(NULL, WM_SYSCOMMAND, SC_SCREENSAVE, 0);
}
// restore power save when screen saver activates
CArchMiscWindows::removeBusyState(CArchMiscWindows::kDISPLAY);
}
}
void
CMSWindowsScreenSaver::deactivate()
{
bool killed = false;
if (!m_is95Family) {
// NT runs screen saver in another desktop
HDESK desktop = OpenDesktop("Screen-saver", 0, FALSE,
DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS);
if (desktop != NULL) {
EnumDesktopWindows(desktop,
&CMSWindowsScreenSaver::killScreenSaverFunc,
reinterpret_cast<LPARAM>(&killed));
CloseDesktop(desktop);
}
}
// if above failed or wasn't tried, try the windows 95 way
if (!killed) {
// find screen saver window and close it
HWND hwnd = FindWindow("WindowsScreenSaverClass", NULL);
if (hwnd == NULL) {
// win2k may use a different class
hwnd = FindWindow("Default Screen Saver", NULL);
}
if (hwnd != NULL) {
PostMessage(hwnd, WM_CLOSE, 0, 0);
}
}
// force timer to restart
SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &m_wasEnabled, 0);
SystemParametersInfo(SPI_SETSCREENSAVEACTIVE,
!m_wasEnabled, 0, SPIF_SENDWININICHANGE);
SystemParametersInfo(SPI_SETSCREENSAVEACTIVE,
m_wasEnabled, 0, SPIF_SENDWININICHANGE);
// disable display power down
CArchMiscWindows::removeBusyState(CArchMiscWindows::kDISPLAY);
}
bool
CMSWindowsScreenSaver::isActive() const
{
if (m_is95) {
return (FindWindow("WindowsScreenSaverClass", NULL) != NULL);
}
else if (m_isNT) {
// screen saver runs on a separate desktop
HDESK desktop = OpenDesktop("Screen-saver", 0, FALSE, MAXIMUM_ALLOWED);
if (desktop == NULL && GetLastError() != ERROR_ACCESS_DENIED) {
// desktop doesn't exist so screen saver is not running
return false;
}
// desktop exists. this should indicate that the screen saver
// is running but an OS bug can cause a valid handle to be
// returned even if the screen saver isn't running (Q230117).
// we'll try to enumerate the windows on the desktop and, if
// there are any, we assume the screen saver is running. (note
// that if we don't have permission to enumerate then we'll
// assume that the screen saver is not running.) that'd be
// easy enough except there's another OS bug (Q198590) that can
// cause EnumDesktopWindows() to enumerate the windows of
// another desktop if the requested desktop has no windows. to
// work around that we have to verify that the enumerated
// windows are, in fact, on the expected desktop.
CFindScreenSaverInfo info;
info.m_desktop = desktop;
info.m_window = NULL;
EnumDesktopWindows(desktop,
&CMSWindowsScreenSaver::findScreenSaverFunc,
reinterpret_cast<LPARAM>(&info));
// done with desktop
CloseDesktop(desktop);
// screen saver is running if a window was found
return (info.m_window != NULL);
}
else {
BOOL running;
SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &running, 0);
return (running != FALSE);
}
}
BOOL CALLBACK
CMSWindowsScreenSaver::findScreenSaverFunc(HWND hwnd, LPARAM arg)
{
CFindScreenSaverInfo* info = reinterpret_cast<CFindScreenSaverInfo*>(arg);
if (info->m_desktop != NULL) {
DWORD threadID = GetWindowThreadProcessId(hwnd, NULL);
HDESK desktop = GetThreadDesktop(threadID);
if (desktop != NULL && desktop != info->m_desktop) {
// stop enumerating -- wrong desktop
return FALSE;
}
}
// found a window
info->m_window = hwnd;
// don't need to enumerate further
return FALSE;
}
BOOL CALLBACK
CMSWindowsScreenSaver::killScreenSaverFunc(HWND hwnd, LPARAM arg)
{
if (IsWindowVisible(hwnd)) {
HINSTANCE instance = (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
if (instance != CMSWindowsScreen::getWindowInstance()) {
PostMessage(hwnd, WM_CLOSE, 0, 0);
*reinterpret_cast<bool*>(arg) = true;
}
}
return TRUE;
}
DWORD
CMSWindowsScreenSaver::findScreenSaver()
{
// try windows 95 way
HWND hwnd = FindWindow("WindowsScreenSaverClass", NULL);
// get process ID of process that owns the window, if found
if (hwnd != NULL) {
DWORD processID;
GetWindowThreadProcessId(hwnd, &processID);
return processID;
}
// not found
return 0;
}
void
CMSWindowsScreenSaver::watchDesktop()
{
// stop watching previous process/desktop
unwatchProcess();
// watch desktop in another thread
LOG((CLOG_DEBUG "watching screen saver desktop"));
m_active = true;
m_watch = new CThread(new TMethodJob<CMSWindowsScreenSaver>(this,
&CMSWindowsScreenSaver::watchDesktopThread));
}
void
CMSWindowsScreenSaver::watchProcess(HANDLE process)
{
// stop watching previous process/desktop
unwatchProcess();
// watch new process in another thread
if (process != NULL) {
LOG((CLOG_DEBUG "watching screen saver process"));
m_process = process;
m_active = true;
m_watch = new CThread(new TMethodJob<CMSWindowsScreenSaver>(this,
&CMSWindowsScreenSaver::watchProcessThread));
}
}
void
CMSWindowsScreenSaver::unwatchProcess()
{
if (m_watch != NULL) {
LOG((CLOG_DEBUG "stopped watching screen saver process/desktop"));
m_watch->cancel();
m_watch->wait();
delete m_watch;
m_watch = NULL;
m_active = false;
}
if (m_process != NULL) {
CloseHandle(m_process);
m_process = NULL;
}
}
void
CMSWindowsScreenSaver::watchDesktopThread(void*)
{
DWORD reserved = 0;
TCHAR* name = NULL;
for (;;) {
// wait a bit
ARCH->sleep(0.2);
if (m_isNT) {
// get current desktop
HDESK desk = OpenInputDesktop(0, FALSE, GENERIC_READ);
if (desk == NULL) {
// can't open desktop so keep waiting
continue;
}
// get current desktop name length
DWORD size;
GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size);
// allocate more space for the name, if necessary
if (size > reserved) {
reserved = size;
name = (TCHAR*)alloca(reserved + sizeof(TCHAR));
}
// get current desktop name
GetUserObjectInformation(desk, UOI_NAME, name, size, &size);
CloseDesktop(desk);
// compare name to screen saver desktop name
if (_tcsicmp(name, TEXT("Screen-saver")) == 0) {
// still the screen saver desktop so keep waiting
continue;
}
}
else {
// 2000/XP have a sane way to detect a runnin screensaver.
BOOL running;
SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &running, 0);
if (running) {
continue;
}
}
// send screen saver deactivation message
m_active = false;
PostThreadMessage(m_threadID, m_msg, m_wParam, m_lParam);
return;
}
}
void
CMSWindowsScreenSaver::watchProcessThread(void*)
{
for (;;) {
CThread::testCancel();
if (WaitForSingleObject(m_process, 50) == WAIT_OBJECT_0) {
// process terminated
LOG((CLOG_DEBUG "screen saver died"));
// send screen saver deactivation message
m_active = false;
PostThreadMessage(m_threadID, m_msg, m_wParam, m_lParam);
return;
}
}
}
void
CMSWindowsScreenSaver::setSecure(bool secure, bool saveSecureAsInt)
{
HKEY hkey =
CArchMiscWindows::addKey(HKEY_CURRENT_USER, g_pathScreenSaverIsSecure);
if (hkey == NULL) {
return;
}
const TCHAR* isSecure = m_is95Family ? g_isSecure9x : g_isSecureNT;
if (saveSecureAsInt) {
CArchMiscWindows::setValue(hkey, isSecure, secure ? 1 : 0);
}
else {
CArchMiscWindows::setValue(hkey, isSecure, secure ? "1" : "0");
}
CArchMiscWindows::closeKey(hkey);
}
bool
CMSWindowsScreenSaver::isSecure(bool* wasSecureFlagAnInt) const
{
// get the password protection setting key
HKEY hkey =
CArchMiscWindows::openKey(HKEY_CURRENT_USER, g_pathScreenSaverIsSecure);
if (hkey == NULL) {
return false;
}
// get the value. the value may be an int or a string, depending
// on the version of windows.
bool result;
const TCHAR* isSecure = m_is95Family ? g_isSecure9x : g_isSecureNT;
switch (CArchMiscWindows::typeOfValue(hkey, isSecure)) {
default:
result = false;
break;
case CArchMiscWindows::kUINT: {
DWORD value =
CArchMiscWindows::readValueInt(hkey, isSecure);
*wasSecureFlagAnInt = true;
result = (value != 0);
break;
}
case CArchMiscWindows::kSTRING: {
std::string value =
CArchMiscWindows::readValueString(hkey, isSecure);
*wasSecureFlagAnInt = false;
result = (value != "0");
break;
}
}
CArchMiscWindows::closeKey(hkey);
return result;
}

View File

@@ -0,0 +1,95 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CMSWINDOWSSCREENSAVER_H
#define CMSWINDOWSSCREENSAVER_H
#include "IScreenSaver.h"
#include "CString.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
class CThread;
//! Microsoft windows screen saver implementation
class CMSWindowsScreenSaver : public IScreenSaver {
public:
CMSWindowsScreenSaver();
virtual ~CMSWindowsScreenSaver();
//! @name manipulators
//@{
//! Check if screen saver started
/*!
Check if the screen saver really started. Returns false if it
hasn't, true otherwise. When the screen saver stops, \c msg will
be posted to the current thread's message queue with the given
parameters.
*/
bool checkStarted(UINT msg, WPARAM, LPARAM);
//@}
// IScreenSaver overrides
virtual void enable();
virtual void disable();
virtual void activate();
virtual void deactivate();
virtual bool isActive() const;
private:
class CFindScreenSaverInfo {
public:
HDESK m_desktop;
HWND m_window;
};
static BOOL CALLBACK findScreenSaverFunc(HWND hwnd, LPARAM lParam);
static BOOL CALLBACK killScreenSaverFunc(HWND hwnd, LPARAM lParam);
DWORD findScreenSaver();
void watchDesktop();
void watchProcess(HANDLE process);
void unwatchProcess();
void watchDesktopThread(void*);
void watchProcessThread(void*);
void setSecure(bool secure, bool saveSecureAsInt);
bool isSecure(bool* wasSecureAnInt) const;
private:
bool m_is95Family;
bool m_is95;
bool m_isNT;
BOOL m_wasEnabled;
bool m_wasSecure;
bool m_wasSecureAnInt;
HANDLE m_process;
CThread* m_watch;
DWORD m_threadID;
UINT m_msg;
WPARAM m_wParam;
LPARAM m_lParam;
// checkActive state. true if the screen saver is being watched
// for deactivation (and is therefore active).
bool m_active;
};
#endif

View File

@@ -0,0 +1,78 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CMSWindowsUtil.h"
#include "CStringUtil.h"
#include <stdio.h>
//
// CMSWindowsUtil
//
CString
CMSWindowsUtil::getString(HINSTANCE instance, DWORD id)
{
char buffer[1024];
int size = static_cast<int>(sizeof(buffer) / sizeof(buffer[0]));
char* msg = buffer;
// load string
int n = LoadString(instance, id, msg, size);
msg[n] = '\0';
if (n < size) {
return msg;
}
// not enough buffer space. keep trying larger buffers until
// we get the whole string.
msg = NULL;
do {
size <<= 1;
delete[] msg;
char* msg = new char[size];
n = LoadString(instance, id, msg, size);
} while (n == size);
msg[n] = '\0';
CString result(msg);
delete[] msg;
return result;
}
CString
CMSWindowsUtil::getErrorString(HINSTANCE hinstance, DWORD error, DWORD id)
{
char* buffer;
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_FROM_SYSTEM,
0,
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&buffer,
0,
NULL) == 0) {
CString errorString = CStringUtil::print("%d", error);
return CStringUtil::format(getString(hinstance, id).c_str(),
errorString.c_str());
}
else {
CString result(buffer);
LocalFree(buffer);
return result;
}
}

View File

@@ -0,0 +1,41 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CMSWINDOWSUTIL_H
#define CMSWINDOWSUTIL_H
#include "CString.h"
#define WINDOWS_LEAN_AND_MEAN
#include <windows.h>
class CMSWindowsUtil {
public:
//! Get message string
/*!
Gets a string for \p id from the string table of \p instance.
*/
static CString getString(HINSTANCE instance, DWORD id);
//! Get error string
/*!
Gets a system error message for \p error. If the error cannot be
found return the string for \p id, replacing ${1} with \p error.
*/
static CString getErrorString(HINSTANCE, DWORD error, DWORD id);
};
#endif

View File

@@ -0,0 +1,362 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012 Nick Bolton
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If notsee <http://www.gnu.org/licenses/>.
*/
#include "CMSWindowsXInput.h"
#include "XScreen.h"
#include "CThread.h"
#include "TMethodJob.h"
#include "CLog.h"
#include "XInputHook.h"
#include "CMSWindowsScreen.h"
#include "XInput.h"
typedef DWORD (WINAPI *XInputGetStateFunc)(DWORD, XINPUT_STATE*);
typedef DWORD (WINAPI *XInputSetStateFunc)(DWORD, XINPUT_VIBRATION*);
CMSWindowsXInput::CMSWindowsXInput(CMSWindowsScreen* screen, const CGameDeviceInfo& gameDeviceInfo) :
m_screen(screen),
m_gameDeviceInfo(gameDeviceInfo),
m_xInputPollThread(NULL),
m_xInputTimingThread(NULL),
m_xInputFeedbackThread(NULL),
m_gameButtonsLast(0),
m_gameLeftTriggerLast(0),
m_gameRightTriggerLast(0),
m_gameLeftStickXLast(0),
m_gameLeftStickYLast(0),
m_gameRightStickXLast(0),
m_gameRightStickYLast(0),
m_gameLastTimingSent(0),
m_gameTimingWaiting(false),
m_gameFakeLag(0),
m_gamePollFreq(kGamePollFreqDefault),
m_gamePollFreqAdjust(0),
m_gameFakeLagMin(kGamePollFreqDefault),
m_gameTimingStarted(false),
m_gameTimingFirst(0),
m_gameFakeLagLast(0),
m_gameTimingCalibrated(false),
m_xinputModule(NULL)
{
m_xinputModule = LoadLibrary("xinput1_3.dll");
if (m_xinputModule == NULL)
{
throw XScreenXInputFailure("could not load xinput library");
}
if (screen->isPrimary())
{
// only capture xinput on the server.
m_xInputPollThread = new CThread(new TMethodJob<CMSWindowsXInput>(
this, &CMSWindowsXInput::xInputPollThread));
}
else
{
// check for queued timing requests on client.
m_xInputTimingThread = new CThread(new TMethodJob<CMSWindowsXInput>(
this, &CMSWindowsXInput::xInputTimingThread));
// check for waiting feedback state on client.
m_xInputFeedbackThread = new CThread(new TMethodJob<CMSWindowsXInput>(
this, &CMSWindowsXInput::xInputFeedbackThread));
}
}
CMSWindowsXInput::~CMSWindowsXInput()
{
}
void
CMSWindowsXInput::fakeGameDeviceButtons(GameDeviceID id, GameDeviceButton buttons) const
{
SetXInputButtons(id, buttons);
}
void
CMSWindowsXInput::fakeGameDeviceSticks(GameDeviceID id, SInt16 x1, SInt16 y1, SInt16 x2, SInt16 y2) const
{
SetXInputSticks(id, x1, y1, x2, y2);
}
void
CMSWindowsXInput::fakeGameDeviceTriggers(GameDeviceID id, UInt8 t1, UInt8 t2) const
{
SetXInputTriggers(id, t1, t2);
}
void
CMSWindowsXInput::queueGameDeviceTimingReq() const
{
QueueXInputTimingReq();
}
void
CMSWindowsXInput::gameDeviceTimingResp(UInt16 freq)
{
if (!m_gameTimingStarted)
{
// record when timing started for calibration period.
m_gameTimingFirst = (UInt16)(ARCH->time() * 1000);
m_gameTimingStarted = true;
}
m_gameTimingWaiting = false;
m_gameFakeLagLast = m_gameFakeLag;
m_gameFakeLag = (UInt16)((ARCH->time() - m_gameLastTimingSent) * 1000);
m_gameFakeLagRecord.push_back(m_gameFakeLag);
if (m_gameFakeLag < m_gameFakeLagMin)
{
// record the lowest value so that the poll frequency
// is adjusted to track.
m_gameFakeLagMin = m_gameFakeLag;
}
else if (m_gameFakeLag > (m_gameFakeLagLast * 2))
{
// if fake lag has increased significantly since the last
// timing, then we must have reached the safe minimum.
m_gameFakeLagMin = m_gameFakeLagLast;
}
// only change poll frequency if it's a sensible value.
if (freq > kGamePollFreqMin && freq < kGamePollFreqMax)
{
m_gamePollFreq = freq;
}
UInt16 timeSinceStart = ((UInt16)(ARCH->time() * 1000) - m_gameTimingFirst);
if (!m_gameTimingCalibrated && (timeSinceStart < kGameCalibrationPeriod))
{
// during the calibration period, increase polling speed
// to try and find the lowest lag value.
m_gamePollFreqAdjust = 1;
LOG((CLOG_DEBUG2 "calibrating game device poll frequency, start=%d, now=%d, since=%d",
m_gameTimingFirst, (int)(ARCH->time() * 1000), timeSinceStart));
}
else
{
// @bug - calibration seems to re-occur after a period of time,
// though, this would actually be a nice feature (but could be
// a bit risky -- could cause poor game play)... setting this
// stops calibration from happening again.
m_gameTimingCalibrated = true;
// only adjust poll frequency if outside desired limits.
m_gamePollFreqAdjust = 0;
if (m_gameFakeLag > m_gameFakeLagMin * 3)
{
m_gamePollFreqAdjust = 1;
}
else if (m_gameFakeLag < m_gameFakeLagMin)
{
m_gamePollFreqAdjust = -1;
}
}
LOG((CLOG_DEBUG3 "game device timing, lag=%dms, freq=%dms, adjust=%dms, min=%dms",
m_gameFakeLag, m_gamePollFreq, m_gamePollFreqAdjust, m_gameFakeLagMin));
if (m_gameFakeLagRecord.size() >= kGameLagRecordMax)
{
UInt16 v, min = 65535, max = 0, total = 0;
std::vector<UInt16>::iterator it;
for (it = m_gameFakeLagRecord.begin(); it < m_gameFakeLagRecord.end(); ++it)
{
v = *it;
if (v < min)
{
min = v;
}
if (v > max)
{
max = v;
}
total += v;
}
LOG((CLOG_INFO "game device timing, min=%dms, max=%dms, avg=%dms",
min, max, (UInt16)(total / m_gameFakeLagRecord.size())));
m_gameFakeLagRecord.clear();
}
}
void
CMSWindowsXInput::gameDeviceFeedback(GameDeviceID id, UInt16 m1, UInt16 m2)
{
XInputSetStateFunc xInputSetStateFunc =
(XInputSetStateFunc)GetProcAddress(m_xinputModule, "XInputSetState");
if (xInputSetStateFunc == NULL)
{
throw XScreenXInputFailure("could not get function address: XInputSetState");
}
XINPUT_VIBRATION vibration;
ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION));
vibration.wLeftMotorSpeed = m1;
vibration.wRightMotorSpeed = m2;
xInputSetStateFunc(id, &vibration);
}
void
CMSWindowsXInput::xInputPollThread(void*)
{
LOG((CLOG_DEBUG "xinput poll thread started"));
XInputGetStateFunc xInputGetStateFunc =
(XInputGetStateFunc)GetProcAddress(m_xinputModule, "XInputGetState");
if (xInputGetStateFunc == NULL)
{
throw XScreenXInputFailure("could not get function address: XInputGetState");
}
int index = 0;
XINPUT_STATE state;
bool end = false;
while (!end)
{
ZeroMemory(&state, sizeof(XINPUT_STATE));
DWORD result = xInputGetStateFunc(index, &state);
// timeout the timing request after 10 seconds
if (m_gameTimingWaiting && (ARCH->time() - m_gameLastTimingSent > 2))
{
m_gameTimingWaiting = false;
LOG((CLOG_DEBUG "game device timing request timed out"));
}
// xinput controller is connected
if (result == ERROR_SUCCESS)
{
// @todo game device state class
bool buttonsChanged = state.Gamepad.wButtons != m_gameButtonsLast;
bool leftTriggerChanged = state.Gamepad.bLeftTrigger != m_gameLeftTriggerLast;
bool rightTriggerChanged = state.Gamepad.bRightTrigger != m_gameRightTriggerLast;
bool leftStickXChanged = state.Gamepad.sThumbLX != m_gameLeftStickXLast;
bool leftStickYChanged = state.Gamepad.sThumbLY != m_gameLeftStickYLast;
bool rightStickXChanged = state.Gamepad.sThumbRX != m_gameRightStickXLast;
bool rightStickYChanged = state.Gamepad.sThumbRY != m_gameRightStickYLast;
m_gameButtonsLast = state.Gamepad.wButtons;
m_gameLeftTriggerLast = state.Gamepad.bLeftTrigger;
m_gameRightTriggerLast = state.Gamepad.bRightTrigger;
m_gameLeftStickXLast = state.Gamepad.sThumbLX;
m_gameLeftStickYLast = state.Gamepad.sThumbLY;
m_gameRightStickXLast = state.Gamepad.sThumbRX;
m_gameRightStickYLast = state.Gamepad.sThumbRY;
bool eventSent = false;
if (buttonsChanged)
{
LOG((CLOG_DEBUG "xinput buttons changed"));
// xinput buttons convert exactly to synergy buttons
m_screen->sendEvent(m_screen->getGameDeviceButtonsEvent(),
new IPrimaryScreen::CGameDeviceButtonInfo(index, state.Gamepad.wButtons));
eventSent = true;
}
if (leftStickXChanged || leftStickYChanged || rightStickXChanged || rightStickYChanged)
{
LOG((CLOG_DEBUG "xinput sticks changed"));
m_screen->sendEvent(m_screen->getGameDeviceSticksEvent(),
new IPrimaryScreen::CGameDeviceStickInfo(
index,
m_gameLeftStickXLast, m_gameLeftStickYLast,
m_gameRightStickXLast, m_gameRightStickYLast));
eventSent = true;
}
if (leftTriggerChanged || rightTriggerChanged)
{
LOG((CLOG_DEBUG "xinput triggers changed"));
// @todo seems wrong re-using x/y for a single value...
m_screen->sendEvent(m_screen->getGameDeviceTriggersEvent(),
new IPrimaryScreen::CGameDeviceTriggerInfo(
index,
state.Gamepad.bLeftTrigger,
state.Gamepad.bRightTrigger));
eventSent = true;
}
if (/*eventSent && */!m_gameTimingWaiting && (ARCH->time() - m_gameLastTimingSent > .5))
{
m_screen->sendEvent(m_screen->getGameDeviceTimingReqEvent(), NULL);
m_gameLastTimingSent = ARCH->time();
m_gameTimingWaiting = true;
LOG((CLOG_DEBUG "game device timing request at %.4f", m_gameLastTimingSent));
}
}
UInt16 sleep = m_gamePollFreq + m_gamePollFreqAdjust;
LOG((CLOG_DEBUG5 "xinput poll sleeping for %dms", sleep));
Sleep(sleep);
}
}
void
CMSWindowsXInput::xInputTimingThread(void*)
{
LOG((CLOG_DEBUG "xinput timing thread started"));
bool end = false;
while (!end)
{
// if timing request was queued, a timing response is queued
// when the xinput status is faked; if it was faked, go tell
// the server when this happened.
if (DequeueXInputTimingResp())
{
LOG((CLOG_DEBUG "dequeued game device timing response"));
m_screen->sendEvent(m_screen->getGameDeviceTimingRespEvent(),
new IPrimaryScreen::CGameDeviceTimingRespInfo(GetXInputFakeFreqMillis()));
}
// give the cpu a break.
Sleep(1);
}
}
void
CMSWindowsXInput::xInputFeedbackThread(void*)
{
LOG((CLOG_DEBUG "xinput feedback thread started"));
int index = 0;
bool end = false;
while (!end)
{
WORD leftMotor, rightMotor;
if (DequeueXInputFeedback(&leftMotor, &rightMotor))
{
LOG((CLOG_DEBUG "dequeued game device feedback"));
m_screen->sendEvent(m_screen->getGameDeviceFeedbackEvent(),
new IPrimaryScreen::CGameDeviceFeedbackInfo(index, leftMotor, rightMotor));
}
Sleep(50);
}
}

View File

@@ -0,0 +1,91 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012 Nick Bolton
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "CGameDevice.h"
#include "CThread.h"
#include "CGameDevice.h"
#include "BasicTypes.h"
#include "GameDeviceTypes.h"
#include "IPrimaryScreen.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <vector>
class CMSWindowsScreen;
enum
{
kGamePollFreqDefault = 100,
kGamePollFreqMin = 50,
kGamePollFreqMax = 200,
kGameCalibrationPeriod = 10000, // 10 seconds
kGameLagRecordMax = 10,
};
class CMSWindowsXInput : public CGameDevice
{
public:
CMSWindowsXInput(CMSWindowsScreen* screen, const CGameDeviceInfo& gameDevice);
virtual ~CMSWindowsXInput();
void init(CMSWindowsScreen* screen);
// thread for polling xinput state.
void xInputPollThread(void*);
// thread for checking queued timing requests.
void xInputTimingThread(void*);
// thread for checking pending feedback state.
void xInputFeedbackThread(void*);
virtual void gameDeviceTimingResp(UInt16 freq);
virtual void gameDeviceFeedback(GameDeviceID id, UInt16 m1, UInt16 m2);
virtual void fakeGameDeviceButtons(GameDeviceID id, GameDeviceButton buttons) const;
virtual void fakeGameDeviceSticks(GameDeviceID id, SInt16 x1, SInt16 y1, SInt16 x2, SInt16 y2) const;
virtual void fakeGameDeviceTriggers(GameDeviceID id, UInt8 t1, UInt8 t2) const;
virtual void queueGameDeviceTimingReq() const;
private:
CMSWindowsScreen* m_screen;
const CGameDeviceInfo& m_gameDeviceInfo;
CThread* m_xInputPollThread;
CThread* m_xInputTimingThread;
CThread* m_xInputFeedbackThread;
WORD m_gameButtonsLast;
BYTE m_gameLeftTriggerLast;
BYTE m_gameRightTriggerLast;
SHORT m_gameLeftStickXLast;
SHORT m_gameLeftStickYLast;
SHORT m_gameRightStickXLast;
SHORT m_gameRightStickYLast;
double m_gameLastTimingSent;
bool m_gameTimingWaiting;
UInt16 m_gameFakeLag;
UInt16 m_gameFakeLagMin;
UInt16 m_gamePollFreq;
SInt8 m_gamePollFreqAdjust;
UInt16 m_gameTimingStarted;
UInt16 m_gameTimingFirst;
UInt16 m_gameFakeLagLast;
bool m_gameTimingCalibrated;
std::vector<UInt16> m_gameFakeLagRecord;
HMODULE m_xinputModule;
};

View File

@@ -0,0 +1,195 @@
# synergy -- mouse and keyboard sharing utility
# Copyright (C) 2009 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
#
# This package is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# found in the file COPYING that should have accompanied this file.
#
# This package 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
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
if (WIN32)
set(inc
CMSWindowsClipboard.h
CMSWindowsClipboardAnyTextConverter.h
CMSWindowsClipboardBitmapConverter.h
CMSWindowsClipboardHTMLConverter.h
CMSWindowsClipboardTextConverter.h
CMSWindowsClipboardUTF16Converter.h
CMSWindowsDesks.h
CMSWindowsEventQueueBuffer.h
CMSWindowsKeyState.h
CMSWindowsScreen.h
CMSWindowsScreenSaver.h
CMSWindowsUtil.h
CMSWindowsRelauncher.h
CMSWindowsHookLibraryLoader.h
IMSWindowsClipboardFacade.h
CMSWindowsDebugOutputter.h
CMSWindowsXInput.h
)
set(src
CMSWindowsClipboard.cpp
CMSWindowsClipboardFacade.cpp
CMSWindowsClipboardAnyTextConverter.cpp
CMSWindowsClipboardBitmapConverter.cpp
CMSWindowsClipboardHTMLConverter.cpp
CMSWindowsClipboardTextConverter.cpp
CMSWindowsClipboardUTF16Converter.cpp
CMSWindowsDesks.cpp
CMSWindowsEventQueueBuffer.cpp
CMSWindowsKeyState.cpp
CMSWindowsScreen.cpp
CMSWindowsScreenSaver.cpp
CMSWindowsUtil.cpp
CMSWindowsRelauncher.cpp
CMSWindowsHookLibraryLoader.cpp
CMSWindowsDebugOutputter.cpp
)
set(inc_hook
CSynergyHook.h
)
set(src_hook
CSynergyHook.cpp
)
if (GAME_DEVICE_SUPPORT)
list(APPEND inc
CMSWindowsXInput.h
)
list(APPEND src
CMSWindowsXInput.cpp
)
set(inc_xinhook
XInputHook.h
HookDLL.h
)
set(src_xinhook
XInputHook.cpp
HookDLL.cpp
)
set(inc_xinproxy13
XInput13.h
XInputProxy13.h
)
set(src_xinproxy13
XInputProxy13.cpp
)
endif()
list(APPEND src
${inc}
)
elseif (APPLE)
set(src
COSXClipboard.cpp
COSXClipboardAnyTextConverter.cpp
COSXClipboardTextConverter.cpp
COSXClipboardUTF16Converter.cpp
COSXEventQueueBuffer.cpp
COSXKeyState.cpp
COSXScreen.cpp
COSXScreenSaver.cpp
COSXScreenSaverUtil.m
)
elseif (UNIX)
set(src
CXWindowsClipboard.cpp
CXWindowsClipboardAnyBitmapConverter.cpp
CXWindowsClipboardBMPConverter.cpp
CXWindowsClipboardHTMLConverter.cpp
CXWindowsClipboardTextConverter.cpp
CXWindowsClipboardUCS2Converter.cpp
CXWindowsClipboardUTF8Converter.cpp
CXWindowsEventQueueBuffer.cpp
CXWindowsKeyState.cpp
CXWindowsScreen.cpp
CXWindowsScreenSaver.cpp
CXWindowsUtil.cpp
)
endif()
set(inc
../arch
../base
../common
../mt
../synergy
)
if (UNIX)
list(APPEND inc
../../..
)
endif()
include_directories(${inc})
add_library(platform STATIC ${src})
if (WIN32)
add_library(synrgyhk SHARED ${inc_hook} ${src_hook})
# copy the dlls (and supporting files) from the lib dir to
# the bin dir, so that synergyc and synergys can easily find them.
# we should leave the other libraries compiling to the lib dir,
# so that the bin dir remains tidy. the path is relative to the
# build dir (in this case, that's: build\src\lib\platform).
add_custom_command(
TARGET synrgyhk
POST_BUILD
COMMAND xcopy /Y /Q
..\\..\\..\\..\\lib\\${CMAKE_CFG_INTDIR}\\synrgyhk.*
..\\..\\..\\..\\bin\\${CMAKE_CFG_INTDIR}\\
)
if (GAME_DEVICE_SUPPORT)
include_directories($ENV{DXSDK_DIR}/Include)
add_library(synxinhk SHARED ${inc_xinhook} ${src_xinhook})
add_custom_command(
TARGET synxinhk
POST_BUILD
COMMAND xcopy /Y /Q
..\\..\\..\\..\\lib\\${CMAKE_CFG_INTDIR}\\synxinhk.*
..\\..\\..\\..\\bin\\${CMAKE_CFG_INTDIR}\\
)
# synergy xinput1_3.dll proxy
include_directories($ENV{DXSDK_DIR}/Include)
add_library(sxinpx13 SHARED ${inc_xinproxy13} ${src_xinproxy13})
target_link_libraries(sxinpx13 synxinhk)
add_custom_command(
TARGET sxinpx13
POST_BUILD
COMMAND xcopy /Y /Q
..\\..\\..\\..\\lib\\${CMAKE_CFG_INTDIR}\\sxinpx13.*
..\\..\\..\\..\\bin\\${CMAKE_CFG_INTDIR}\\
)
endif()
endif()
if (UNIX)
target_link_libraries(platform synergy ${libs})
endif()

View File

@@ -0,0 +1,250 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "COSXClipboard.h"
#include "CClipboard.h"
#include "COSXClipboardUTF16Converter.h"
#include "COSXClipboardTextConverter.h"
#include "CLog.h"
#include "XArch.h"
//
// COSXClipboard
//
COSXClipboard::COSXClipboard() :
m_time(0),
m_pboard(NULL)
{
m_converters.push_back(new COSXClipboardUTF16Converter);
m_converters.push_back(new COSXClipboardTextConverter);
OSStatus createErr = PasteboardCreate(kPasteboardClipboard, &m_pboard);
if (createErr != noErr) {
LOG((CLOG_DEBUG "failed to create clipboard reference: error %i", createErr));
LOG((CLOG_ERR "unable to connect to pasteboard, clipboard sharing disabled", createErr));
m_pboard = NULL;
return;
}
OSStatus syncErr = PasteboardSynchronize(m_pboard);
if (syncErr != noErr) {
LOG((CLOG_DEBUG "failed to syncronize clipboard: error %i", syncErr));
}
}
COSXClipboard::~COSXClipboard()
{
clearConverters();
}
bool
COSXClipboard::empty()
{
LOG((CLOG_DEBUG "emptying clipboard"));
if (m_pboard == NULL)
return false;
OSStatus err = PasteboardClear(m_pboard);
if (err != noErr) {
LOG((CLOG_DEBUG "failed to clear clipboard: error %i", err));
return false;
}
return true;
}
bool
COSXClipboard::synchronize()
{
if (m_pboard == NULL)
return false;
PasteboardSyncFlags flags = PasteboardSynchronize(m_pboard);
LOG((CLOG_DEBUG2 "flags: %x", flags));
if (flags & kPasteboardModified) {
return true;
}
return false;
}
void
COSXClipboard::add(EFormat format, const CString & data)
{
bool emptied = false;
if (m_pboard == NULL)
return;
LOG((CLOG_DEBUG "add %d bytes to clipboard format: %d", data.size(), format));
for (ConverterList::const_iterator index = m_converters.begin();
index != m_converters.end(); ++index) {
IOSXClipboardConverter* converter = *index;
// skip converters for other formats
if (converter->getFormat() == format) {
CString osXData = converter->fromIClipboard(data);
CFStringRef flavorType = converter->getOSXFormat();
CFDataRef dataRef = CFDataCreate(kCFAllocatorDefault, (UInt8 *)osXData.data(), osXData.size());
// integ tests showed that if you call add(...) twice, then the
// second call will actually fail to set clipboard data. calling
// empty() seems to solve this problem. but, only clear the clipboard
// for the first converter, otherwise further converters will wipe out
// what we just added.
if (!emptied) {
empty();
emptied = true;
}
PasteboardPutItemFlavor(
m_pboard,
(PasteboardItemID) 0,
flavorType,
dataRef,
kPasteboardFlavorNoFlags);
LOG((CLOG_DEBUG "added %d bytes to clipboard format: %d", data.size(), format));
}
}
}
bool
COSXClipboard::open(Time time) const
{
if (m_pboard == NULL)
return false;
LOG((CLOG_DEBUG "opening clipboard"));
m_time = time;
return true;
}
void
COSXClipboard::close() const
{
LOG((CLOG_DEBUG "closing clipboard"));
/* not needed */
}
IClipboard::Time
COSXClipboard::getTime() const
{
return m_time;
}
bool
COSXClipboard::has(EFormat format) const
{
if (m_pboard == NULL)
return false;
PasteboardItemID item;
PasteboardGetItemIdentifier(m_pboard, (CFIndex) 1, &item);
for (ConverterList::const_iterator index = m_converters.begin();
index != m_converters.end(); ++index) {
IOSXClipboardConverter* converter = *index;
if (converter->getFormat() == format) {
PasteboardFlavorFlags flags;
CFStringRef type = converter->getOSXFormat();
OSStatus res;
if ((res = PasteboardGetItemFlavorFlags(m_pboard, item, type, &flags)) == noErr) {
return true;
}
}
}
return false;
}
CString
COSXClipboard::get(EFormat format) const
{
CFStringRef type;
PasteboardItemID item;
CString result;
if (m_pboard == NULL)
return result;
PasteboardGetItemIdentifier(m_pboard, (CFIndex) 1, &item);
// find the converter for the first clipboard format we can handle
IOSXClipboardConverter* converter = NULL;
for (ConverterList::const_iterator index = m_converters.begin();
index != m_converters.end(); ++index) {
converter = *index;
PasteboardFlavorFlags flags;
type = converter->getOSXFormat();
if (converter->getFormat() == format &&
PasteboardGetItemFlavorFlags(m_pboard, item, type, &flags) == noErr) {
break;
}
converter = NULL;
}
// if no converter then we don't recognize any formats
if (converter == NULL) {
LOG((CLOG_DEBUG "Unable to find converter for data"));
return result;
}
// get the clipboard data.
CFDataRef buffer = NULL;
try {
OSStatus err = PasteboardCopyItemFlavorData(m_pboard, item, type, &buffer);
if (err != noErr) {
throw err;
}
result = CString((char *) CFDataGetBytePtr(buffer), CFDataGetLength(buffer));
}
catch (OSStatus err) {
LOG((CLOG_DEBUG "exception thrown in COSXClipboard::get MacError (%d)", err));
}
catch (...) {
LOG((CLOG_DEBUG "unknown exception in COSXClipboard::get"));
RETHROW_XTHREAD
}
if (buffer != NULL)
CFRelease(buffer);
return converter->toIClipboard(result);
}
void
COSXClipboard::clearConverters()
{
if (m_pboard == NULL)
return;
for (ConverterList::iterator index = m_converters.begin();
index != m_converters.end(); ++index) {
delete *index;
}
m_converters.clear();
}

View File

@@ -0,0 +1,96 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef COSXCLIPBOARD_H
#define COSXCLIPBOARD_H
#include <Carbon/Carbon.h>
#include "IClipboard.h"
#include <vector>
class IOSXClipboardConverter;
//! OS X clipboard implementation
class COSXClipboard : public IClipboard {
public:
COSXClipboard();
virtual ~COSXClipboard();
//! Test if clipboard is owned by synergy
static bool isOwnedBySynergy();
// IClipboard overrides
virtual bool empty();
virtual void add(EFormat, const CString& data);
virtual bool open(Time) const;
virtual void close() const;
virtual Time getTime() const;
virtual bool has(EFormat) const;
virtual CString get(EFormat) const;
bool synchronize();
private:
void clearConverters();
private:
typedef std::vector<IOSXClipboardConverter*> ConverterList;
mutable Time m_time;
ConverterList m_converters;
PasteboardRef m_pboard;
};
//! Clipboard format converter interface
/*!
This interface defines the methods common to all Scrap book format
*/
class IOSXClipboardConverter : public IInterface {
public:
//! @name accessors
//@{
//! Get clipboard format
/*!
Return the clipboard format this object converts from/to.
*/
virtual IClipboard::EFormat
getFormat() const = 0;
//! returns the scrap flavor type that this object converts from/to
virtual CFStringRef
getOSXFormat() const = 0;
//! Convert from IClipboard format
/*!
Convert from the IClipboard format to the Carbon scrap format.
The input data must be in the IClipboard format returned by
getFormat(). The return data will be in the scrap
format returned by getOSXFormat().
*/
virtual CString fromIClipboard(const CString&) const = 0;
//! Convert to IClipboard format
/*!
Convert from the carbon scrap format to the IClipboard format
(i.e., the reverse of fromIClipboard()).
*/
virtual CString toIClipboard(const CString&) const = 0;
//@}
};
#endif

View File

@@ -0,0 +1,88 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "COSXClipboardAnyTextConverter.h"
#include <algorithm>
//
// COSXClipboardAnyTextConverter
//
COSXClipboardAnyTextConverter::COSXClipboardAnyTextConverter()
{
// do nothing
}
COSXClipboardAnyTextConverter::~COSXClipboardAnyTextConverter()
{
// do nothing
}
IClipboard::EFormat
COSXClipboardAnyTextConverter::getFormat() const
{
return IClipboard::kText;
}
CString
COSXClipboardAnyTextConverter::fromIClipboard(const CString& data) const
{
// convert linefeeds and then convert to desired encoding
return doFromIClipboard(convertLinefeedToMacOS(data));
}
CString
COSXClipboardAnyTextConverter::toIClipboard(const CString& data) const
{
// convert text then newlines
return convertLinefeedToUnix(doToIClipboard(data));
}
static
bool
isLF(char ch)
{
return (ch == '\n');
}
static
bool
isCR(char ch)
{
return (ch == '\r');
}
CString
COSXClipboardAnyTextConverter::convertLinefeedToMacOS(const CString& src)
{
// note -- we assume src is a valid UTF-8 string
CString copy = src;
std::replace_if(copy.begin(), copy.end(), isLF, '\r');
return copy;
}
CString
COSXClipboardAnyTextConverter::convertLinefeedToUnix(const CString& src)
{
CString copy = src;
std::replace_if(copy.begin(), copy.end(), isCR, '\n');
return copy;
}

View File

@@ -0,0 +1,55 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef COSXCLIPBOARDANYTEXTCONVERTER_H
#define COSXCLIPBOARDANYTEXTCONVERTER_H
#include "COSXClipboard.h"
//! Convert to/from some text encoding
class COSXClipboardAnyTextConverter : public IOSXClipboardConverter {
public:
COSXClipboardAnyTextConverter();
virtual ~COSXClipboardAnyTextConverter();
// IOSXClipboardConverter overrides
virtual IClipboard::EFormat
getFormat() const;
virtual CFStringRef
getOSXFormat() const = 0;
virtual CString fromIClipboard(const CString &) const;
virtual CString toIClipboard(const CString &) const;
protected:
//! Convert from IClipboard format
/*!
Do UTF-8 conversion and linefeed conversion.
*/
virtual CString doFromIClipboard(const CString&) const = 0;
//! Convert to IClipboard format
/*!
Do UTF-8 conversion and Linefeed conversion.
*/
virtual CString doToIClipboard(const CString&) const = 0;
private:
static CString convertLinefeedToMacOS(const CString&);
static CString convertLinefeedToUnix(const CString&);
};
#endif

View File

@@ -0,0 +1,91 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "COSXClipboardTextConverter.h"
#include "CUnicode.h"
//
// COSXClipboardTextConverter
//
COSXClipboardTextConverter::COSXClipboardTextConverter()
{
// do nothing
}
COSXClipboardTextConverter::~COSXClipboardTextConverter()
{
// do nothing
}
CFStringRef
COSXClipboardTextConverter::getOSXFormat() const
{
return CFSTR("public.plain-text");
}
CString
COSXClipboardTextConverter::convertString(
const CString& data,
CFStringEncoding fromEncoding,
CFStringEncoding toEncoding)
{
CFStringRef stringRef =
CFStringCreateWithCString(kCFAllocatorDefault,
data.c_str(), fromEncoding);
if (stringRef == NULL) {
return CString();
}
CFIndex buffSize;
CFRange entireString = CFRangeMake(0, CFStringGetLength(stringRef));
CFStringGetBytes(stringRef, entireString, toEncoding,
0, false, NULL, 0, &buffSize);
char* buffer = new char[buffSize];
if (buffer == NULL) {
CFRelease(stringRef);
return CString();
}
CFStringGetBytes(stringRef, entireString, toEncoding,
0, false, (UInt8*)buffer, buffSize, NULL);
CString result(buffer, buffSize);
delete[] buffer;
CFRelease(stringRef);
return result;
}
CString
COSXClipboardTextConverter::doFromIClipboard(const CString& data) const
{
return convertString(data, kCFStringEncodingUTF8,
CFStringGetSystemEncoding());
}
CString
COSXClipboardTextConverter::doToIClipboard(const CString& data) const
{
return convertString(data, CFStringGetSystemEncoding(),
kCFStringEncodingUTF8);
}

View File

@@ -0,0 +1,44 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef COSXCLIPBOARDTEXTCONVERTER_H
#define COSXCLIPBOARDTEXTCONVERTER_H
#include "COSXClipboardAnyTextConverter.h"
//! Convert to/from locale text encoding
class COSXClipboardTextConverter : public COSXClipboardAnyTextConverter {
public:
COSXClipboardTextConverter();
virtual ~COSXClipboardTextConverter();
// IOSXClipboardAnyTextConverter overrides
virtual CFStringRef
getOSXFormat() const;
protected:
// COSXClipboardAnyTextConverter overrides
virtual CString doFromIClipboard(const CString&) const;
virtual CString doToIClipboard(const CString&) const;
// generic encoding converter
static CString convertString(const CString& data,
CFStringEncoding fromEncoding,
CFStringEncoding toEncoding);
};
#endif

View File

@@ -0,0 +1,53 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "COSXClipboardUTF16Converter.h"
#include "CUnicode.h"
//
// COSXClipboardUTF16Converter
//
COSXClipboardUTF16Converter::COSXClipboardUTF16Converter()
{
// do nothing
}
COSXClipboardUTF16Converter::~COSXClipboardUTF16Converter()
{
// do nothing
}
CFStringRef
COSXClipboardUTF16Converter::getOSXFormat() const
{
return CFSTR("public.utf16-plain-text");
}
CString
COSXClipboardUTF16Converter::doFromIClipboard(const CString& data) const
{
// convert and add nul terminator
return CUnicode::UTF8ToUTF16(data);
}
CString
COSXClipboardUTF16Converter::doToIClipboard(const CString& data) const
{
// convert and strip nul terminator
return CUnicode::UTF16ToUTF8(data);
}

View File

@@ -0,0 +1,39 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef COSXCLIPBOARDUTF16CONVERTER_H
#define COSXCLIPBOARDUTF16CONVERTER_H
#include "COSXClipboardAnyTextConverter.h"
//! Convert to/from UTF-16 encoding
class COSXClipboardUTF16Converter : public COSXClipboardAnyTextConverter {
public:
COSXClipboardUTF16Converter();
virtual ~COSXClipboardUTF16Converter();
// IOSXClipboardAnyTextConverter overrides
virtual CFStringRef
getOSXFormat() const;
protected:
// COSXClipboardAnyTextConverter overrides
virtual CString doFromIClipboard(const CString&) const;
virtual CString doToIClipboard(const CString&) const;
};
#endif

View File

@@ -0,0 +1,127 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "COSXEventQueueBuffer.h"
#include "CEvent.h"
#include "IEventQueue.h"
//
// CEventQueueTimer
//
class CEventQueueTimer { };
//
// COSXEventQueueBuffer
//
COSXEventQueueBuffer::COSXEventQueueBuffer() :
m_event(NULL)
{
// do nothing
}
COSXEventQueueBuffer::~COSXEventQueueBuffer()
{
// release the last event
if (m_event != NULL) {
ReleaseEvent(m_event);
}
}
void
COSXEventQueueBuffer::waitForEvent(double timeout)
{
EventRef event;
ReceiveNextEvent(0, NULL, timeout, false, &event);
}
IEventQueueBuffer::Type
COSXEventQueueBuffer::getEvent(CEvent& event, UInt32& dataID)
{
// release the previous event
if (m_event != NULL) {
ReleaseEvent(m_event);
m_event = NULL;
}
// get the next event
OSStatus error = ReceiveNextEvent(0, NULL, 0.0, true, &m_event);
// handle the event
if (error == eventLoopQuitErr) {
event = CEvent(CEvent::kQuit);
return kSystem;
}
else if (error != noErr) {
return kNone;
}
else {
UInt32 eventClass = GetEventClass(m_event);
switch (eventClass) {
case 'Syne':
dataID = GetEventKind(m_event);
return kUser;
default:
event = CEvent(CEvent::kSystem,
IEventQueue::getSystemTarget(), &m_event);
return kSystem;
}
}
}
bool
COSXEventQueueBuffer::addEvent(UInt32 dataID)
{
EventRef event;
OSStatus error = CreateEvent(
kCFAllocatorDefault,
'Syne',
dataID,
0,
kEventAttributeNone,
&event);
if (error == noErr) {
error = PostEventToQueue(GetMainEventQueue(), event,
kEventPriorityStandard);
ReleaseEvent(event);
}
return (error == noErr);
}
bool
COSXEventQueueBuffer::isEmpty() const
{
EventRef event;
OSStatus status = ReceiveNextEvent(0, NULL, 0.0, false, &event);
return (status == eventLoopTimedOutErr);
}
CEventQueueTimer*
COSXEventQueueBuffer::newTimer(double, bool) const
{
return new CEventQueueTimer;
}
void
COSXEventQueueBuffer::deleteTimer(CEventQueueTimer* timer) const
{
delete timer;
}

View File

@@ -0,0 +1,43 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef COSXEVENTQUEUEBUFFER_H
#define COSXEVENTQUEUEBUFFER_H
#include <Carbon/Carbon.h>
#include "IEventQueueBuffer.h"
//! Event queue buffer for OS X
class COSXEventQueueBuffer : public IEventQueueBuffer {
public:
COSXEventQueueBuffer();
virtual ~COSXEventQueueBuffer();
// IEventQueueBuffer overrides
virtual void waitForEvent(double timeout);
virtual Type getEvent(CEvent& event, UInt32& dataID);
virtual bool addEvent(UInt32 dataID);
virtual bool isEmpty() const;
virtual CEventQueueTimer*
newTimer(double duration, bool oneShot) const;
virtual void deleteTimer(CEventQueueTimer*) const;
private:
EventRef m_event;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,225 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef COSXKEYSTATE_H
#define COSXKEYSTATE_H
#include <Carbon/Carbon.h>
#include "CKeyState.h"
#include "stdmap.h"
#include "stdset.h"
#include "stdvector.h"
#if defined(MAC_OS_X_VERSION_10_5)
typedef TISInputSourceRef KeyLayout;
#else
typedef KeyboardLayoutRef KeyLayout;
#endif
//! OS X key state
/*!
A key state for OS X.
*/
class COSXKeyState : public CKeyState {
public:
typedef std::vector<KeyID> CKeyIDs;
COSXKeyState();
COSXKeyState(IEventQueue& eventQueue, CKeyMap& keyMap);
virtual ~COSXKeyState();
//! @name modifiers
//@{
//! Handle modifier key change
/*!
Determines which modifier keys have changed and updates the modifier
state and sends key events as appropriate.
*/
void handleModifierKeys(void* target,
KeyModifierMask oldMask, KeyModifierMask newMask);
//@}
//! @name accessors
//@{
//! Convert OS X modifier mask to synergy mask
/*!
Returns the synergy modifier mask corresponding to the OS X modifier
mask in \p mask.
*/
KeyModifierMask mapModifiersFromOSX(UInt32 mask) const;
//! Convert CG flags-style modifier mask to old-style Carbon
/*!
Still required in a few places for translation calls.
*/
KeyModifierMask mapModifiersToCarbon(UInt32 mask) const;
//! Map key event to keys
/*!
Converts a key event into a sequence of KeyIDs and the shadow modifier
state to a modifier mask. The KeyIDs list, in order, the characters
generated by the key press/release. It returns the id of the button
that was pressed or released, or 0 if the button doesn't map to a known
KeyID.
*/
KeyButton mapKeyFromEvent(CKeyIDs& ids,
KeyModifierMask* maskOut, CGEventRef event) const;
//! Map key and mask to native values
/*!
Calculates mac virtual key and mask for a key \p key and modifiers
\p mask. Returns \c true if the key can be mapped, \c false otherwise.
*/
bool mapSynergyHotKeyToMac(KeyID key, KeyModifierMask mask,
UInt32& macVirtualKey,
UInt32& macModifierMask) const;
//@}
// IKeyState overrides
virtual bool fakeCtrlAltDel();
virtual KeyModifierMask
pollActiveModifiers() const;
virtual SInt32 pollActiveGroup() const;
virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const;
protected:
// CKeyState overrides
virtual void getKeyMap(CKeyMap& keyMap);
virtual void fakeKey(const Keystroke& keystroke);
private:
class CKeyResource;
typedef std::vector<KeyLayout> GroupList;
// Add hard coded special keys to a CKeyMap.
void getKeyMapForSpecialKeys(
CKeyMap& keyMap, SInt32 group) const;
// Convert keyboard resource to a key map
bool getKeyMap(CKeyMap& keyMap,
SInt32 group, const CKeyResource& r) const;
// Get the available keyboard groups
bool getGroups(GroupList&) const;
// Change active keyboard group to group
void setGroup(SInt32 group);
// Check if the keyboard layout has changed and update keyboard state
// if so.
void checkKeyboardLayout();
// Send an event for the given modifier key
void handleModifierKey(void* target,
UInt32 virtualKey, KeyID id,
bool down, KeyModifierMask newMask);
// Checks if any in \p ids is a glyph key and if \p isCommand is false.
// If so it adds the AltGr modifier to \p mask. This allows OS X
// servers to use the option key both as AltGr and as a modifier. If
// option is acting as AltGr (i.e. it generates a glyph and there are
// no command modifiers active) then we don't send the super modifier
// to clients because they'd try to match it as a command modifier.
void adjustAltGrModifier(const CKeyIDs& ids,
KeyModifierMask* mask, bool isCommand) const;
// Maps an OS X virtual key id to a KeyButton. This simply remaps
// the ids so we don't use KeyButton 0.
static KeyButton mapVirtualKeyToKeyButton(UInt32 keyCode);
// Maps a KeyButton to an OS X key code. This is the inverse of
// mapVirtualKeyToKeyButton.
static UInt32 mapKeyButtonToVirtualKey(KeyButton keyButton);
void init();
private:
class CKeyResource : public IInterface {
public:
virtual bool isValid() const = 0;
virtual UInt32 getNumModifierCombinations() const = 0;
virtual UInt32 getNumTables() const = 0;
virtual UInt32 getNumButtons() const = 0;
virtual UInt32 getTableForModifier(UInt32 mask) const = 0;
virtual KeyID getKey(UInt32 table, UInt32 button) const = 0;
// Convert a character in the current script to the equivalent KeyID
static KeyID getKeyID(UInt8);
// Convert a unicode character to the equivalent KeyID.
static KeyID unicharToKeyID(UniChar);
};
class CUCHRKeyResource : public CKeyResource {
public:
CUCHRKeyResource(const void*, UInt32 keyboardType);
// CKeyResource overrides
virtual bool isValid() const;
virtual UInt32 getNumModifierCombinations() const;
virtual UInt32 getNumTables() const;
virtual UInt32 getNumButtons() const;
virtual UInt32 getTableForModifier(UInt32 mask) const;
virtual KeyID getKey(UInt32 table, UInt32 button) const;
private:
typedef std::vector<KeyID> KeySequence;
bool getDeadKey(KeySequence& keys, UInt16 index) const;
bool getKeyRecord(KeySequence& keys,
UInt16 index, UInt16& state) const;
bool addSequence(KeySequence& keys, UCKeyCharSeq c) const;
private:
const UCKeyboardLayout* m_resource;
const UCKeyModifiersToTableNum* m_m;
const UCKeyToCharTableIndex* m_cti;
const UCKeySequenceDataIndex* m_sdi;
const UCKeyStateRecordsIndex* m_sri;
const UCKeyStateTerminators* m_st;
UInt16 m_spaceOutput;
};
// OS X uses a physical key if 0 for the 'A' key. synergy reserves
// KeyButton 0 so we offset all OS X physical key ids by this much
// when used as a KeyButton and by minus this much to map a KeyButton
// to a physical button.
enum {
KeyButtonOffset = 1
};
typedef std::map<KeyLayout, SInt32> GroupMap;
typedef std::map<UInt32, KeyID> CVirtualKeyMap;
CVirtualKeyMap m_virtualKeyMap;
mutable UInt32 m_deadKeyState;
GroupList m_groups;
GroupMap m_groupMap;
// Hold the current state of modifier keys
bool shiftPressed;
bool controlPressed;
bool altPressed;
bool superPressed;
bool capsPressed;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,339 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef COSXSCREEN_H
#define COSXSCREEN_H
#include <bitset>
#include "stdmap.h"
#include "stdvector.h"
#include <Carbon/Carbon.h>
#include "COSXClipboard.h"
#include "CPlatformScreen.h"
#include <mach/mach_port.h>
#include <mach/mach_interface.h>
#include <mach/mach_init.h>
#include <IOKit/pwr_mgt/IOPMLib.h>
#include <IOKit/IOMessage.h>
extern "C" {
typedef int CGSConnectionID;
CGError CGSSetConnectionProperty(CGSConnectionID cid, CGSConnectionID targetCID, CFStringRef key, CFTypeRef value);
int _CGSDefaultConnection();
}
template <class T>
class CCondVar;
class CEventQueueTimer;
class CMutex;
class CThread;
class COSXKeyState;
class COSXScreenSaver;
//! Implementation of IPlatformScreen for OS X
class COSXScreen : public CPlatformScreen {
public:
COSXScreen(bool isPrimary);
virtual ~COSXScreen();
// IScreen overrides
virtual void* getEventTarget() const;
virtual bool getClipboard(ClipboardID id, IClipboard*) const;
virtual void getShape(SInt32& x, SInt32& y,
SInt32& width, SInt32& height) const;
virtual void getCursorPos(SInt32& x, SInt32& y) const;
// IPrimaryScreen overrides
virtual void reconfigure(UInt32 activeSides);
virtual void warpCursor(SInt32 x, SInt32 y);
virtual UInt32 registerHotKey(KeyID key, KeyModifierMask mask);
virtual void unregisterHotKey(UInt32 id);
virtual void fakeInputBegin();
virtual void fakeInputEnd();
virtual SInt32 getJumpZoneSize() const;
virtual bool isAnyMouseButtonDown() const;
virtual void getCursorCenter(SInt32& x, SInt32& y) const;
virtual void gameDeviceTimingResp(UInt16 freq) { }
// ISecondaryScreen overrides
virtual void fakeMouseButton(ButtonID id, bool press);
virtual void fakeMouseMove(SInt32 x, SInt32 y) const;
virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const;
virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const;
virtual void fakeGameDeviceButtons(GameDeviceID id, GameDeviceButton buttons) const { }
virtual void fakeGameDeviceSticks(GameDeviceID id, SInt16 x1, SInt16 y1, SInt16 x2, SInt16 y2) const { }
virtual void fakeGameDeviceTriggers(GameDeviceID id, UInt8 t1, UInt8 t2) const { }
virtual void queueGameDeviceTimingReq() const { }
// IPlatformScreen overrides
virtual void enable();
virtual void disable();
virtual void enter();
virtual bool leave();
virtual bool setClipboard(ClipboardID, const IClipboard*);
virtual void checkClipboards();
virtual void openScreensaver(bool notify);
virtual void closeScreensaver();
virtual void screensaver(bool activate);
virtual void resetOptions();
virtual void setOptions(const COptionsList& options);
virtual void setSequenceNumber(UInt32);
virtual bool isPrimary() const;
virtual void gameDeviceFeedback(GameDeviceID id, UInt16 m1, UInt16 m2) { }
protected:
// IPlatformScreen overrides
virtual void handleSystemEvent(const CEvent&, void*);
virtual void updateButtons();
virtual IKeyState* getKeyState() const;
private:
void updateScreenShape();
void updateScreenShape(const CGDirectDisplayID, const CGDisplayChangeSummaryFlags);
void postMouseEvent(CGPoint&) const;
// convenience function to send events
void sendEvent(CEvent::Type type, void* = NULL) const;
void sendClipboardEvent(CEvent::Type type, ClipboardID id) const;
// message handlers
bool onMouseMove(SInt32 mx, SInt32 my);
// mouse button handler. pressed is true if this is a mousedown
// event, false if it is a mouseup event. macButton is the index
// of the button pressed using the mac button mapping.
bool onMouseButton(bool pressed, UInt16 macButton);
bool onMouseWheel(SInt32 xDelta, SInt32 yDelta) const;
#if !defined(MAC_OS_X_VERSION_10_5)
bool onDisplayChange();
#endif
void constructMouseButtonEventMap();
bool onKey(CGEventRef event);
bool onHotKey(EventRef event) const;
// Added here to allow the carbon cursor hack to be called.
void showCursor();
void hideCursor();
// map mac mouse button to synergy buttons
ButtonID mapMacButtonToSynergy(UInt16) const;
// map mac scroll wheel value to a synergy scroll wheel value
SInt32 mapScrollWheelToSynergy(SInt32) const;
// map synergy scroll wheel value to a mac scroll wheel value
SInt32 mapScrollWheelFromSynergy(SInt32) const;
// get the current scroll wheel speed
double getScrollSpeed() const;
// get the current scroll wheel speed
double getScrollSpeedFactor() const;
// enable/disable drag handling for buttons 3 and up
void enableDragTimer(bool enable);
// drag timer handler
void handleDrag(const CEvent&, void*);
// clipboard check timer handler
void handleClipboardCheck(const CEvent&, void*);
#if defined(MAC_OS_X_VERSION_10_5)
// Resolution switch callback
static void displayReconfigurationCallback(CGDirectDisplayID,
CGDisplayChangeSummaryFlags, void*);
#else
static pascal void displayManagerCallback(void* inUserData,
SInt16 inMessage, void* inNotifyData);
#endif
// fast user switch callback
static pascal OSStatus
userSwitchCallback(EventHandlerCallRef nextHandler,
EventRef theEvent, void* inUserData);
// sleep / wakeup support
void watchSystemPowerThread(void*);
static void testCanceled(CFRunLoopTimerRef timer, void*info);
static void powerChangeCallback(void* refcon, io_service_t service,
natural_t messageType, void* messageArgument);
void handlePowerChangeRequest(natural_t messageType,
void* messageArgument);
static CEvent::Type getConfirmSleepEvent();
void handleConfirmSleep(const CEvent& event, void*);
// global hotkey operating mode
static bool isGlobalHotKeyOperatingModeAvailable();
static void setGlobalHotKeysEnabled(bool enabled);
static bool getGlobalHotKeysEnabled();
// Quartz event tap support
static CGEventRef handleCGInputEvent(CGEventTapProxy proxy,
CGEventType type,
CGEventRef event,
void* refcon);
static CGEventRef handleCGInputEventSecondary(CGEventTapProxy proxy,
CGEventType type,
CGEventRef event,
void* refcon);
private:
struct CHotKeyItem {
public:
CHotKeyItem(UInt32, UInt32);
CHotKeyItem(EventHotKeyRef, UInt32, UInt32);
EventHotKeyRef getRef() const;
bool operator<(const CHotKeyItem&) const;
private:
EventHotKeyRef m_ref;
UInt32 m_keycode;
UInt32 m_mask;
};
enum MouseButtonState {
kMouseButtonUp = 0,
kMouseButtonDragged,
kMouseButtonDown,
kMouseButtonStateMax
};
class CMouseButtonState {
public:
void set(UInt32 button, MouseButtonState state);
bool any();
void reset();
void overwrite(UInt32 buttons);
bool test(UInt32 button) const;
SInt8 getFirstButtonDown() const;
private:
std::bitset<NumButtonIDs> m_buttons;
};
typedef std::map<UInt32, CHotKeyItem> HotKeyMap;
typedef std::vector<UInt32> HotKeyIDList;
typedef std::map<KeyModifierMask, UInt32> ModifierHotKeyMap;
typedef std::map<CHotKeyItem, UInt32> HotKeyToIDMap;
// true if screen is being used as a primary screen, false otherwise
bool m_isPrimary;
// true if mouse has entered the screen
bool m_isOnScreen;
// the display
CGDirectDisplayID m_displayID;
// screen shape stuff
SInt32 m_x, m_y;
SInt32 m_w, m_h;
SInt32 m_xCenter, m_yCenter;
// mouse state
mutable SInt32 m_xCursor, m_yCursor;
mutable bool m_cursorPosValid;
/* FIXME: this data structure is explicitly marked mutable due
to a need to track the state of buttons since the remote
side only lets us know of change events, and because the
fakeMouseButton button method is marked 'const'. This is
Evil, and this should be moved to a place where it need not
be mutable as soon as possible. */
mutable CMouseButtonState m_buttonState;
typedef std::map<UInt16, CGEventType> MouseButtonEventMapType;
std::vector<MouseButtonEventMapType> MouseButtonEventMap;
bool m_cursorHidden;
SInt32 m_dragNumButtonsDown;
Point m_dragLastPoint;
CEventQueueTimer* m_dragTimer;
// keyboard stuff
COSXKeyState* m_keyState;
// clipboards
COSXClipboard m_pasteboard;
UInt32 m_sequenceNumber;
// screen saver stuff
COSXScreenSaver* m_screensaver;
bool m_screensaverNotify;
// clipboard stuff
bool m_ownClipboard;
CEventQueueTimer* m_clipboardTimer;
// window object that gets user input events when the server
// has focus.
WindowRef m_hiddenWindow;
// window object that gets user input events when the server
// does not have focus.
WindowRef m_userInputWindow;
#if !defined(MAC_OS_X_VERSION_10_5)
// display manager stuff (to get screen resolution switches).
DMExtendedNotificationUPP m_displayManagerNotificationUPP;
ProcessSerialNumber m_PSN;
#endif
// fast user switching
EventHandlerRef m_switchEventHandlerRef;
// sleep / wakeup
CMutex* m_pmMutex;
CThread* m_pmWatchThread;
CCondVar<bool>* m_pmThreadReady;
CFRunLoopRef m_pmRunloop;
io_connect_t m_pmRootPort;
// hot key stuff
HotKeyMap m_hotKeys;
HotKeyIDList m_oldHotKeyIDs;
ModifierHotKeyMap m_modifierHotKeys;
UInt32 m_activeModifierHotKey;
KeyModifierMask m_activeModifierHotKeyMask;
HotKeyToIDMap m_hotKeyToIDMap;
// global hotkey operating mode
static bool s_testedForGHOM;
static bool s_hasGHOM;
// events
static CEvent::Type s_confirmSleepEvent;
// Quartz input event support
CFMachPortRef m_eventTapPort;
CFRunLoopSourceRef m_eventTapRLSR;
// for double click coalescing.
double m_lastSingleClick;
double m_lastDoubleClick;
SInt32 m_lastSingleClickXCursor;
SInt32 m_lastSingleClickYCursor;
};
#endif

View File

@@ -0,0 +1,178 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#import "COSXScreenSaver.h"
#import "COSXScreenSaverUtil.h"
#import "CLog.h"
#import "IEventQueue.h"
#import "IPrimaryScreen.h"
#import <string.h>
//
// COSXScreenSaver
//
COSXScreenSaver::COSXScreenSaver(void* eventTarget) :
m_eventTarget(eventTarget),
m_enabled(true)
{
m_autoReleasePool = screenSaverUtilCreatePool();
m_screenSaverController = screenSaverUtilCreateController();
// install launch/termination event handlers
EventTypeSpec launchEventTypes[2];
launchEventTypes[0].eventClass = kEventClassApplication;
launchEventTypes[0].eventKind = kEventAppLaunched;
launchEventTypes[1].eventClass = kEventClassApplication;
launchEventTypes[1].eventKind = kEventAppTerminated;
EventHandlerUPP launchTerminationEventHandler =
NewEventHandlerUPP(launchTerminationCallback);
InstallApplicationEventHandler(launchTerminationEventHandler, 2,
launchEventTypes, this,
&m_launchTerminationEventHandlerRef);
DisposeEventHandlerUPP(launchTerminationEventHandler);
m_screenSaverPSN.highLongOfPSN = 0;
m_screenSaverPSN.lowLongOfPSN = 0;
// test if screensaver is running and find process number
if (isActive()) {
ProcessInfoRec procInfo;
Str31 procName; // pascal string. first byte holds length.
memset(&procInfo, 0, sizeof(procInfo));
procInfo.processName = procName;
procInfo.processInfoLength = sizeof(ProcessInfoRec);
ProcessSerialNumber psn;
OSErr err = GetNextProcess(&psn);
while (err == 0) {
memset(procName, 0, sizeof(procName));
err = GetProcessInformation(&psn, &procInfo);
if (err != 0) {
break;
}
if (strcmp("ScreenSaverEngine", (const char*)&procName[1]) == 0) {
m_screenSaverPSN = psn;
break;
}
err = GetNextProcess(&psn);
}
}
}
COSXScreenSaver::~COSXScreenSaver()
{
RemoveEventHandler(m_launchTerminationEventHandlerRef);
// screenSaverUtilReleaseController(m_screenSaverController);
screenSaverUtilReleasePool(m_autoReleasePool);
}
void
COSXScreenSaver::enable()
{
m_enabled = true;
screenSaverUtilEnable(m_screenSaverController);
}
void
COSXScreenSaver::disable()
{
m_enabled = false;
screenSaverUtilDisable(m_screenSaverController);
}
void
COSXScreenSaver::activate()
{
screenSaverUtilActivate(m_screenSaverController);
}
void
COSXScreenSaver::deactivate()
{
screenSaverUtilDeactivate(m_screenSaverController, m_enabled);
}
bool
COSXScreenSaver::isActive() const
{
return (screenSaverUtilIsActive(m_screenSaverController) != 0);
}
void
COSXScreenSaver::processLaunched(ProcessSerialNumber psn)
{
CFStringRef processName;
OSStatus err = CopyProcessName(&psn, &processName);
if (err == 0 && CFEqual(CFSTR("ScreenSaverEngine"), processName)) {
m_screenSaverPSN = psn;
LOG((CLOG_DEBUG1 "ScreenSaverEngine launched. Enabled=%d", m_enabled));
if (m_enabled) {
EVENTQUEUE->addEvent(
CEvent(IPrimaryScreen::getScreensaverActivatedEvent(),
m_eventTarget));
}
}
}
void
COSXScreenSaver::processTerminated(ProcessSerialNumber psn)
{
if (m_screenSaverPSN.highLongOfPSN == psn.highLongOfPSN &&
m_screenSaverPSN.lowLongOfPSN == psn.lowLongOfPSN) {
LOG((CLOG_DEBUG1 "ScreenSaverEngine terminated. Enabled=%d", m_enabled));
if (m_enabled) {
EVENTQUEUE->addEvent(
CEvent(IPrimaryScreen::getScreensaverDeactivatedEvent(),
m_eventTarget));
}
m_screenSaverPSN.highLongOfPSN = 0;
m_screenSaverPSN.lowLongOfPSN = 0;
}
}
pascal OSStatus
COSXScreenSaver::launchTerminationCallback(
EventHandlerCallRef nextHandler,
EventRef theEvent, void* userData)
{
OSStatus result;
ProcessSerialNumber psn;
EventParamType actualType;
UInt32 actualSize;
result = GetEventParameter(theEvent, kEventParamProcessID,
typeProcessSerialNumber, &actualType,
sizeof(psn), &actualSize, &psn);
if ((result == noErr) &&
(actualSize > 0) &&
(actualType == typeProcessSerialNumber)) {
COSXScreenSaver* screenSaver = (COSXScreenSaver*)userData;
UInt32 eventKind = GetEventKind(theEvent);
if (eventKind == kEventAppLaunched) {
screenSaver->processLaunched(psn);
}
else if (eventKind == kEventAppTerminated) {
screenSaver->processTerminated(psn);
}
}
return (CallNextEventHandler(nextHandler, theEvent));
}

View File

@@ -0,0 +1,57 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef COSXSCREENSAVER_H
#define COSXSCREENSAVER_H
#include "IScreenSaver.h"
#include <Carbon/Carbon.h>
//! OSX screen saver implementation
class COSXScreenSaver : public IScreenSaver {
public:
COSXScreenSaver(void* eventTarget);
virtual ~COSXScreenSaver();
// IScreenSaver overrides
virtual void enable();
virtual void disable();
virtual void activate();
virtual void deactivate();
virtual bool isActive() const;
private:
void processLaunched(ProcessSerialNumber psn);
void processTerminated(ProcessSerialNumber psn);
static pascal OSStatus
launchTerminationCallback(
EventHandlerCallRef nextHandler,
EventRef theEvent, void* userData);
private:
// the target for the events we generate
void* m_eventTarget;
bool m_enabled;
void* m_screenSaverController;
void* m_autoReleasePool;
EventHandlerRef m_launchTerminationEventHandlerRef;
ProcessSerialNumber m_screenSaverPSN;
};
#endif

View File

@@ -0,0 +1,42 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef COSXSCREENSAVERUTIL_H
#define COSXSCREENSAVERUTIL_H
#include "common.h"
#if defined(__cplusplus)
extern "C" {
#endif
void* screenSaverUtilCreatePool();
void screenSaverUtilReleasePool(void*);
void* screenSaverUtilCreateController();
void screenSaverUtilReleaseController(void*);
void screenSaverUtilEnable(void*);
void screenSaverUtilDisable(void*);
void screenSaverUtilActivate(void*);
void screenSaverUtilDeactivate(void*, int isEnabled);
int screenSaverUtilIsActive(void*);
#if defined(__cplusplus)
}
#endif
#endif

View File

@@ -0,0 +1,81 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*/
#import "COSXScreenSaverUtil.h"
#import "OSXScreenSaverControl.h"
#import <Foundation/NSAutoreleasePool.h>
//
// screenSaverUtil functions
//
// Note: these helper functions exist only so we can avoid using ObjC++.
// autoconf/automake don't know about ObjC++ and I don't know how to
// teach them about it.
//
void*
screenSaverUtilCreatePool()
{
return [[NSAutoreleasePool alloc] init];
}
void
screenSaverUtilReleasePool(void* pool)
{
[(NSAutoreleasePool*)pool release];
}
void*
screenSaverUtilCreateController()
{
return [[ScreenSaverController controller] retain];
}
void
screenSaverUtilReleaseController(void* controller)
{
[(ScreenSaverController*)controller release];
}
void
screenSaverUtilEnable(void* controller)
{
[(ScreenSaverController*)controller setScreenSaverCanRun:YES];
}
void
screenSaverUtilDisable(void* controller)
{
[(ScreenSaverController*)controller setScreenSaverCanRun:NO];
}
void
screenSaverUtilActivate(void* controller)
{
[(ScreenSaverController*)controller setScreenSaverCanRun:YES];
[(ScreenSaverController*)controller screenSaverStartNow];
}
void
screenSaverUtilDeactivate(void* controller, int isEnabled)
{
[(ScreenSaverController*)controller screenSaverStopNow];
[(ScreenSaverController*)controller setScreenSaverCanRun:isEnabled];
}
int
screenSaverUtilIsActive(void* controller)
{
return [(ScreenSaverController*)controller screenSaverIsRunning];
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,95 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CSYNERGYHOOK_H
#define CSYNERGYHOOK_H
// hack: vs2005 doesn't declare _WIN32_WINNT, so we need to hard code it.
// however, some say that this should be hard coded since it defines the
// target system, but since this is suposed to compile on pre-XP, maybe
// we should just leave it like this.
#if _MSC_VER == 1400
#define _WIN32_WINNT 0x0400
#endif
#include "BasicTypes.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
// fix: cmake defines the library name in lower case (synrgyhk_EXPORTS) as
// opposed to upper case (SYNRGYHK_EXPORTS), so rather than figuring out
// how to change cmake's behaviour, it's easier to just change the code.
#if defined(synrgyhk_EXPORTS)
#define CSYNERGYHOOK_API __declspec(dllexport)
#else
#define CSYNERGYHOOK_API __declspec(dllimport)
#endif
#define SYNERGY_MSG_MARK WM_APP + 0x0011 // mark id; <unused>
#define SYNERGY_MSG_KEY WM_APP + 0x0012 // vk code; key data
#define SYNERGY_MSG_MOUSE_BUTTON WM_APP + 0x0013 // button msg; <unused>
#define SYNERGY_MSG_MOUSE_WHEEL WM_APP + 0x0014 // delta; <unused>
#define SYNERGY_MSG_MOUSE_MOVE WM_APP + 0x0015 // x; y
#define SYNERGY_MSG_POST_WARP WM_APP + 0x0016 // <unused>; <unused>
#define SYNERGY_MSG_PRE_WARP WM_APP + 0x0017 // x; y
#define SYNERGY_MSG_SCREEN_SAVER WM_APP + 0x0018 // activated; <unused>
#define SYNERGY_MSG_DEBUG WM_APP + 0x0019 // data, data
#define SYNERGY_MSG_INPUT_FIRST SYNERGY_MSG_KEY
#define SYNERGY_MSG_INPUT_LAST SYNERGY_MSG_PRE_WARP
#define SYNERGY_HOOK_LAST_MSG SYNERGY_MSG_DEBUG
#define SYNERGY_HOOK_FAKE_INPUT_VIRTUAL_KEY VK_CANCEL
#define SYNERGY_HOOK_FAKE_INPUT_SCANCODE 0
extern "C" {
enum EHookResult {
kHOOK_FAILED,
kHOOK_OKAY,
kHOOK_OKAY_LL
};
enum EHookMode {
kHOOK_DISABLE,
kHOOK_WATCH_JUMP_ZONE,
kHOOK_RELAY_EVENTS
};
typedef int (*InitFunc)(DWORD targetQueueThreadID);
typedef int (*CleanupFunc)(void);
typedef EHookResult (*InstallFunc)(void);
typedef int (*UninstallFunc)(void);
typedef int (*InstallScreenSaverFunc)(void);
typedef int (*UninstallScreenSaverFunc)(void);
typedef void (*SetSidesFunc)(UInt32);
typedef void (*SetZoneFunc)(SInt32, SInt32, SInt32, SInt32, SInt32);
typedef void (*SetModeFunc)(int);
CSYNERGYHOOK_API int init(DWORD);
CSYNERGYHOOK_API int cleanup(void);
CSYNERGYHOOK_API EHookResult install(void);
CSYNERGYHOOK_API int uninstall(void);
CSYNERGYHOOK_API int installScreenSaver(void);
CSYNERGYHOOK_API int uninstallScreenSaver(void);
CSYNERGYHOOK_API void setSides(UInt32 sides);
CSYNERGYHOOK_API void setZone(SInt32 x, SInt32 y, SInt32 w, SInt32 h,
SInt32 jumpZoneSize);
CSYNERGYHOOK_API void setMode(EHookMode mode);
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,379 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CXWINDOWSCLIPBOARD_H
#define CXWINDOWSCLIPBOARD_H
#include "IClipboard.h"
#include "ClipboardTypes.h"
#include "stdmap.h"
#include "stdlist.h"
#include "stdvector.h"
#if X_DISPLAY_MISSING
# error X11 is required to build synergy
#else
# include <X11/Xlib.h>
#endif
class IXWindowsClipboardConverter;
//! X11 clipboard implementation
class CXWindowsClipboard : public IClipboard {
public:
/*!
Use \c window as the window that owns or interacts with the
clipboard identified by \c id.
*/
CXWindowsClipboard(Display*, Window window, ClipboardID id);
virtual ~CXWindowsClipboard();
//! Notify clipboard was lost
/*!
Tells clipboard it lost ownership at the given time.
*/
void lost(Time);
//! Add clipboard request
/*!
Adds a selection request to the request list. If the given
owner window isn't this clipboard's window then this simply
sends a failure event to the requestor.
*/
void addRequest(Window owner,
Window requestor, Atom target,
::Time time, Atom property);
//! Process clipboard request
/*!
Continues processing a selection request. Returns true if the
request was handled, false if the request was unknown.
*/
bool processRequest(Window requestor,
::Time time, Atom property);
//! Cancel clipboard request
/*!
Terminate a selection request. Returns true iff the request
was known and handled.
*/
bool destroyRequest(Window requestor);
//! Get window
/*!
Returns the clipboard's window (passed the c'tor).
*/
Window getWindow() const;
//! Get selection atom
/*!
Returns the selection atom that identifies the clipboard to X11
(e.g. XA_PRIMARY).
*/
Atom getSelection() const;
// IClipboard overrides
virtual bool empty();
virtual void add(EFormat, const CString& data);
virtual bool open(Time) const;
virtual void close() const;
virtual Time getTime() const;
virtual bool has(EFormat) const;
virtual CString get(EFormat) const;
private:
// remove all converters from our list
void clearConverters();
// get the converter for a clipboard format. returns NULL if no
// suitable converter. iff onlyIfNotAdded is true then also
// return NULL if a suitable converter was found but we already
// have data of the converter's clipboard format.
IXWindowsClipboardConverter*
getConverter(Atom target,
bool onlyIfNotAdded = false) const;
// convert target atom to clipboard format
EFormat getFormat(Atom target) const;
// add a non-MULTIPLE request. does not verify that the selection
// was owned at the given time. returns true if the conversion
// could be performed, false otherwise. in either case, the
// reply is inserted.
bool addSimpleRequest(
Window requestor, Atom target,
::Time time, Atom property);
// if not already checked then see if the cache is stale and, if so,
// clear it. this has the side effect of updating m_timeOwned.
void checkCache() const;
// clear the cache, resetting the cached flag and the added flag for
// each format.
void clearCache() const;
void doClearCache();
// cache all formats of the selection
void fillCache() const;
void doFillCache();
//
// helper classes
//
// read an ICCCM conforming selection
class CICCCMGetClipboard {
public:
CICCCMGetClipboard(Window requestor, Time time, Atom property);
~CICCCMGetClipboard();
// convert the given selection to the given type. returns
// true iff the conversion was successful or the conversion
// cannot be performed (in which case *actualTarget == None).
bool readClipboard(Display* display,
Atom selection, Atom target,
Atom* actualTarget, CString* data);
private:
bool processEvent(Display* display, XEvent* event);
private:
Window m_requestor;
Time m_time;
Atom m_property;
bool m_incr;
bool m_failed;
bool m_done;
// atoms needed for the protocol
Atom m_atomNone; // NONE, not None
Atom m_atomIncr;
// true iff we've received the selection notify
bool m_reading;
// the converted selection data
CString* m_data;
// the actual type of the data. if this is None then the
// selection owner cannot convert to the requested type.
Atom* m_actualTarget;
public:
// true iff the selection owner didn't follow ICCCM conventions
bool m_error;
};
// Motif structure IDs
enum { kMotifClipFormat = 1, kMotifClipItem, kMotifClipHeader };
// _MOTIF_CLIP_HEADER structure
class CMotifClipHeader {
public:
SInt32 m_id; // kMotifClipHeader
SInt32 m_pad1[3];
SInt32 m_item;
SInt32 m_pad2[4];
SInt32 m_numItems;
SInt32 m_pad3[3];
SInt32 m_selectionOwner; // a Window
SInt32 m_pad4[2];
};
// Motif clip item structure
class CMotifClipItem {
public:
SInt32 m_id; // kMotifClipItem
SInt32 m_pad1[5];
SInt32 m_size;
SInt32 m_numFormats;
SInt32 m_numDeletedFormats;
SInt32 m_pad2[6];
};
// Motif clip format structure
class CMotifClipFormat {
public:
SInt32 m_id; // kMotifClipFormat
SInt32 m_pad1[6];
SInt32 m_length;
SInt32 m_data;
SInt32 m_type; // an Atom
SInt32 m_pad2[1];
SInt32 m_deleted;
SInt32 m_pad3[4];
};
// stores data needed to respond to a selection request
class CReply {
public:
CReply(Window, Atom target, ::Time);
CReply(Window, Atom target, ::Time, Atom property,
const CString& data, Atom type, int format);
public:
// information about the request
Window m_requestor;
Atom m_target;
::Time m_time;
Atom m_property;
// true iff we've sent the notification for this reply
bool m_replied;
// true iff the reply has sent its last message
bool m_done;
// the data to send and its type and format
CString m_data;
Atom m_type;
int m_format;
// index of next byte in m_data to send
UInt32 m_ptr;
};
typedef std::list<CReply*> CReplyList;
typedef std::map<Window, CReplyList> CReplyMap;
typedef std::map<Window, long> CReplyEventMask;
// ICCCM interoperability methods
void icccmFillCache();
bool icccmGetSelection(Atom target,
Atom* actualTarget, CString* data) const;
Time icccmGetTime() const;
// motif interoperability methods
bool motifLockClipboard() const;
void motifUnlockClipboard() const;
bool motifOwnsClipboard() const;
void motifFillCache();
bool motifGetSelection(const CMotifClipFormat*,
Atom* actualTarget, CString* data) const;
Time motifGetTime() const;
// reply methods
bool insertMultipleReply(Window, ::Time, Atom);
void insertReply(CReply*);
void pushReplies();
void pushReplies(CReplyMap::iterator&,
CReplyList&, CReplyList::iterator);
bool sendReply(CReply*);
void clearReplies();
void clearReplies(CReplyList&);
void sendNotify(Window requestor, Atom selection,
Atom target, Atom property, Time time);
bool wasOwnedAtTime(::Time) const;
// data conversion methods
Atom getTargetsData(CString&, int* format) const;
Atom getTimestampData(CString&, int* format) const;
private:
typedef std::vector<IXWindowsClipboardConverter*> ConverterList;
Display* m_display;
Window m_window;
ClipboardID m_id;
Atom m_selection;
mutable bool m_open;
mutable Time m_time;
bool m_owner;
mutable Time m_timeOwned;
Time m_timeLost;
// true iff open and clipboard owned by a motif app
mutable bool m_motif;
// the added/cached clipboard data
mutable bool m_checkCache;
bool m_cached;
Time m_cacheTime;
bool m_added[kNumFormats];
CString m_data[kNumFormats];
// conversion request replies
CReplyMap m_replies;
CReplyEventMask m_eventMasks;
// clipboard format converters
ConverterList m_converters;
// atoms we'll need
Atom m_atomTargets;
Atom m_atomMultiple;
Atom m_atomTimestamp;
Atom m_atomInteger;
Atom m_atomAtom;
Atom m_atomAtomPair;
Atom m_atomData;
Atom m_atomINCR;
Atom m_atomMotifClipLock;
Atom m_atomMotifClipHeader;
Atom m_atomMotifClipAccess;
Atom m_atomGDKSelection;
};
//! Clipboard format converter interface
/*!
This interface defines the methods common to all X11 clipboard format
converters.
*/
class IXWindowsClipboardConverter : public IInterface {
public:
//! @name accessors
//@{
//! Get clipboard format
/*!
Return the clipboard format this object converts from/to.
*/
virtual IClipboard::EFormat
getFormat() const = 0;
//! Get X11 format atom
/*!
Return the atom representing the X selection format that
this object converts from/to.
*/
virtual Atom getAtom() const = 0;
//! Get X11 property datum size
/*!
Return the size (in bits) of data elements returned by
toIClipboard().
*/
virtual int getDataSize() const = 0;
//! Convert from IClipboard format
/*!
Convert from the IClipboard format to the X selection format.
The input data must be in the IClipboard format returned by
getFormat(). The return data will be in the X selection
format returned by getAtom().
*/
virtual CString fromIClipboard(const CString&) const = 0;
//! Convert to IClipboard format
/*!
Convert from the X selection format to the IClipboard format
(i.e., the reverse of fromIClipboard()).
*/
virtual CString toIClipboard(const CString&) const = 0;
//@}
};
#endif

View File

@@ -0,0 +1,190 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CXWindowsClipboardAnyBitmapConverter.h"
// BMP info header structure
struct CBMPInfoHeader {
public:
UInt32 biSize;
SInt32 biWidth;
SInt32 biHeight;
UInt16 biPlanes;
UInt16 biBitCount;
UInt32 biCompression;
UInt32 biSizeImage;
SInt32 biXPelsPerMeter;
SInt32 biYPelsPerMeter;
UInt32 biClrUsed;
UInt32 biClrImportant;
};
// BMP is little-endian
static
void
toLE(UInt8*& dst, UInt16 src)
{
dst[0] = static_cast<UInt8>(src & 0xffu);
dst[1] = static_cast<UInt8>((src >> 8) & 0xffu);
dst += 2;
}
static
void
toLE(UInt8*& dst, SInt32 src)
{
dst[0] = static_cast<UInt8>(src & 0xffu);
dst[1] = static_cast<UInt8>((src >> 8) & 0xffu);
dst[2] = static_cast<UInt8>((src >> 16) & 0xffu);
dst[3] = static_cast<UInt8>((src >> 24) & 0xffu);
dst += 4;
}
static
void
toLE(UInt8*& dst, UInt32 src)
{
dst[0] = static_cast<UInt8>(src & 0xffu);
dst[1] = static_cast<UInt8>((src >> 8) & 0xffu);
dst[2] = static_cast<UInt8>((src >> 16) & 0xffu);
dst[3] = static_cast<UInt8>((src >> 24) & 0xffu);
dst += 4;
}
static inline
UInt16
fromLEU16(const UInt8* data)
{
return static_cast<UInt16>(data[0]) |
(static_cast<UInt16>(data[1]) << 8);
}
static inline
SInt32
fromLES32(const UInt8* data)
{
return static_cast<SInt32>(static_cast<UInt32>(data[0]) |
(static_cast<UInt32>(data[1]) << 8) |
(static_cast<UInt32>(data[2]) << 16) |
(static_cast<UInt32>(data[3]) << 24));
}
static inline
UInt32
fromLEU32(const UInt8* data)
{
return static_cast<UInt32>(data[0]) |
(static_cast<UInt32>(data[1]) << 8) |
(static_cast<UInt32>(data[2]) << 16) |
(static_cast<UInt32>(data[3]) << 24);
}
//
// CXWindowsClipboardAnyBitmapConverter
//
CXWindowsClipboardAnyBitmapConverter::CXWindowsClipboardAnyBitmapConverter()
{
// do nothing
}
CXWindowsClipboardAnyBitmapConverter::~CXWindowsClipboardAnyBitmapConverter()
{
// do nothing
}
IClipboard::EFormat
CXWindowsClipboardAnyBitmapConverter::getFormat() const
{
return IClipboard::kBitmap;
}
int
CXWindowsClipboardAnyBitmapConverter::getDataSize() const
{
return 8;
}
CString
CXWindowsClipboardAnyBitmapConverter::fromIClipboard(const CString& bmp) const
{
// fill BMP info header with native-endian data
CBMPInfoHeader infoHeader;
const UInt8* rawBMPInfoHeader = reinterpret_cast<const UInt8*>(bmp.data());
infoHeader.biSize = fromLEU32(rawBMPInfoHeader + 0);
infoHeader.biWidth = fromLES32(rawBMPInfoHeader + 4);
infoHeader.biHeight = fromLES32(rawBMPInfoHeader + 8);
infoHeader.biPlanes = fromLEU16(rawBMPInfoHeader + 12);
infoHeader.biBitCount = fromLEU16(rawBMPInfoHeader + 14);
infoHeader.biCompression = fromLEU32(rawBMPInfoHeader + 16);
infoHeader.biSizeImage = fromLEU32(rawBMPInfoHeader + 20);
infoHeader.biXPelsPerMeter = fromLES32(rawBMPInfoHeader + 24);
infoHeader.biYPelsPerMeter = fromLES32(rawBMPInfoHeader + 28);
infoHeader.biClrUsed = fromLEU32(rawBMPInfoHeader + 32);
infoHeader.biClrImportant = fromLEU32(rawBMPInfoHeader + 36);
// check that format is acceptable
if (infoHeader.biSize != 40 ||
infoHeader.biWidth == 0 || infoHeader.biHeight == 0 ||
infoHeader.biPlanes != 0 || infoHeader.biCompression != 0 ||
(infoHeader.biBitCount != 24 && infoHeader.biBitCount != 32)) {
return CString();
}
// convert to image format
const UInt8* rawBMPPixels = rawBMPInfoHeader + 40;
if (infoHeader.biBitCount == 24) {
return doBGRFromIClipboard(rawBMPPixels,
infoHeader.biWidth, infoHeader.biHeight);
}
else {
return doBGRAFromIClipboard(rawBMPPixels,
infoHeader.biWidth, infoHeader.biHeight);
}
}
CString
CXWindowsClipboardAnyBitmapConverter::toIClipboard(const CString& image) const
{
// convert to raw BMP data
UInt32 w, h, depth;
CString rawBMP = doToIClipboard(image, w, h, depth);
if (rawBMP.empty() || w == 0 || h == 0 || (depth != 24 && depth != 32)) {
return CString();
}
// fill BMP info header with little-endian data
UInt8 infoHeader[40];
UInt8* dst = infoHeader;
toLE(dst, static_cast<UInt32>(40));
toLE(dst, static_cast<SInt32>(w));
toLE(dst, static_cast<SInt32>(h));
toLE(dst, static_cast<UInt16>(1));
toLE(dst, static_cast<UInt16>(depth));
toLE(dst, static_cast<UInt32>(0)); // BI_RGB
toLE(dst, static_cast<UInt32>(image.size()));
toLE(dst, static_cast<SInt32>(2834)); // 72 dpi
toLE(dst, static_cast<SInt32>(2834)); // 72 dpi
toLE(dst, static_cast<UInt32>(0));
toLE(dst, static_cast<UInt32>(0));
// construct image
return CString(reinterpret_cast<const char*>(infoHeader),
sizeof(infoHeader)) + rawBMP;
}

View File

@@ -0,0 +1,62 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CXWINDOWSCLIPBOARDANYBITMAPCONVERTER_H
#define CXWINDOWSCLIPBOARDANYBITMAPCONVERTER_H
#include "CXWindowsClipboard.h"
//! Convert to/from some text encoding
class CXWindowsClipboardAnyBitmapConverter :
public IXWindowsClipboardConverter {
public:
CXWindowsClipboardAnyBitmapConverter();
virtual ~CXWindowsClipboardAnyBitmapConverter();
// IXWindowsClipboardConverter overrides
virtual IClipboard::EFormat
getFormat() const;
virtual Atom getAtom() const = 0;
virtual int getDataSize() const;
virtual CString fromIClipboard(const CString&) const;
virtual CString toIClipboard(const CString&) const;
protected:
//! Convert from IClipboard format
/*!
Convert raw BGR pixel data to another image format.
*/
virtual CString doBGRFromIClipboard(const UInt8* bgrData,
UInt32 w, UInt32 h) const = 0;
//! Convert from IClipboard format
/*!
Convert raw BGRA pixel data to another image format.
*/
virtual CString doBGRAFromIClipboard(const UInt8* bgrData,
UInt32 w, UInt32 h) const = 0;
//! Convert to IClipboard format
/*!
Convert an image into raw BGR or BGRA image data and store the
width, height, and image depth (24 or 32).
*/
virtual CString doToIClipboard(const CString&,
UInt32& w, UInt32& h, UInt32& depth) const = 0;
};
#endif

View File

@@ -0,0 +1,142 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CXWindowsClipboardBMPConverter.h"
// BMP file header structure
struct CBMPHeader {
public:
UInt16 type;
UInt32 size;
UInt16 reserved1;
UInt16 reserved2;
UInt32 offset;
};
// BMP is little-endian
static inline
UInt32
fromLEU32(const UInt8* data)
{
return static_cast<UInt32>(data[0]) |
(static_cast<UInt32>(data[1]) << 8) |
(static_cast<UInt32>(data[2]) << 16) |
(static_cast<UInt32>(data[3]) << 24);
}
static
void
toLE(UInt8*& dst, char src)
{
dst[0] = static_cast<UInt8>(src);
dst += 1;
}
static
void
toLE(UInt8*& dst, UInt16 src)
{
dst[0] = static_cast<UInt8>(src & 0xffu);
dst[1] = static_cast<UInt8>((src >> 8) & 0xffu);
dst += 2;
}
static
void
toLE(UInt8*& dst, UInt32 src)
{
dst[0] = static_cast<UInt8>(src & 0xffu);
dst[1] = static_cast<UInt8>((src >> 8) & 0xffu);
dst[2] = static_cast<UInt8>((src >> 16) & 0xffu);
dst[3] = static_cast<UInt8>((src >> 24) & 0xffu);
dst += 4;
}
//
// CXWindowsClipboardBMPConverter
//
CXWindowsClipboardBMPConverter::CXWindowsClipboardBMPConverter(
Display* display) :
m_atom(XInternAtom(display, "image/bmp", False))
{
// do nothing
}
CXWindowsClipboardBMPConverter::~CXWindowsClipboardBMPConverter()
{
// do nothing
}
IClipboard::EFormat
CXWindowsClipboardBMPConverter::getFormat() const
{
return IClipboard::kBitmap;
}
Atom
CXWindowsClipboardBMPConverter::getAtom() const
{
return m_atom;
}
int
CXWindowsClipboardBMPConverter::getDataSize() const
{
return 8;
}
CString
CXWindowsClipboardBMPConverter::fromIClipboard(const CString& bmp) const
{
// create BMP image
UInt8 header[14];
UInt8* dst = header;
toLE(dst, 'B');
toLE(dst, 'M');
toLE(dst, static_cast<UInt32>(14 + bmp.size()));
toLE(dst, static_cast<UInt16>(0));
toLE(dst, static_cast<UInt16>(0));
toLE(dst, static_cast<UInt32>(14 + 40));
return CString(reinterpret_cast<const char*>(header), 14) + bmp;
}
CString
CXWindowsClipboardBMPConverter::toIClipboard(const CString& bmp) const
{
// make sure data is big enough for a BMP file
if (bmp.size() <= 14 + 40) {
return CString();
}
// check BMP file header
const UInt8* rawBMPHeader = reinterpret_cast<const UInt8*>(bmp.data());
if (rawBMPHeader[0] != 'B' || rawBMPHeader[1] != 'M') {
return CString();
}
// get offset to image data
UInt32 offset = fromLEU32(rawBMPHeader + 10);
// construct BMP
if (offset == 14 + 40) {
return bmp.substr(14);
}
else {
return bmp.substr(14, 40) + bmp.substr(offset, bmp.size() - offset);
}
}

View File

@@ -0,0 +1,42 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CXWINDOWSCLIPBOARDBMPCONVERTER_H
#define CXWINDOWSCLIPBOARDBMPCONVERTER_H
#include "CXWindowsClipboard.h"
//! Convert to/from some text encoding
class CXWindowsClipboardBMPConverter :
public IXWindowsClipboardConverter {
public:
CXWindowsClipboardBMPConverter(Display* display);
virtual ~CXWindowsClipboardBMPConverter();
// IXWindowsClipboardConverter overrides
virtual IClipboard::EFormat
getFormat() const;
virtual Atom getAtom() const;
virtual int getDataSize() const;
virtual CString fromIClipboard(const CString&) const;
virtual CString toIClipboard(const CString&) const;
private:
Atom m_atom;
};
#endif

View File

@@ -0,0 +1,65 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CXWindowsClipboardHTMLConverter.h"
#include "CUnicode.h"
//
// CXWindowsClipboardHTMLConverter
//
CXWindowsClipboardHTMLConverter::CXWindowsClipboardHTMLConverter(
Display* display, const char* name) :
m_atom(XInternAtom(display, name, False))
{
// do nothing
}
CXWindowsClipboardHTMLConverter::~CXWindowsClipboardHTMLConverter()
{
// do nothing
}
IClipboard::EFormat
CXWindowsClipboardHTMLConverter::getFormat() const
{
return IClipboard::kHTML;
}
Atom
CXWindowsClipboardHTMLConverter::getAtom() const
{
return m_atom;
}
int
CXWindowsClipboardHTMLConverter::getDataSize() const
{
return 8;
}
CString
CXWindowsClipboardHTMLConverter::fromIClipboard(const CString& data) const
{
return CUnicode::UTF8ToUTF16(data);
}
CString
CXWindowsClipboardHTMLConverter::toIClipboard(const CString& data) const
{
return CUnicode::UTF16ToUTF8(data);
}

View File

@@ -0,0 +1,44 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CXWINDOWSCLIPBOARDHTMLCONVERTER_H
#define CXWINDOWSCLIPBOARDHTMLCONVERTER_H
#include "CXWindowsClipboard.h"
//! Convert to/from HTML encoding
class CXWindowsClipboardHTMLConverter : public IXWindowsClipboardConverter {
public:
/*!
\c name is converted to an atom and that is reported by getAtom().
*/
CXWindowsClipboardHTMLConverter(Display* display, const char* name);
virtual ~CXWindowsClipboardHTMLConverter();
// IXWindowsClipboardConverter overrides
virtual IClipboard::EFormat
getFormat() const;
virtual Atom getAtom() const;
virtual int getDataSize() const;
virtual CString fromIClipboard(const CString&) const;
virtual CString toIClipboard(const CString&) const;
private:
Atom m_atom;
};
#endif

View File

@@ -0,0 +1,77 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CXWindowsClipboardTextConverter.h"
#include "CUnicode.h"
//
// CXWindowsClipboardTextConverter
//
CXWindowsClipboardTextConverter::CXWindowsClipboardTextConverter(
Display* display, const char* name) :
m_atom(XInternAtom(display, name, False))
{
// do nothing
}
CXWindowsClipboardTextConverter::~CXWindowsClipboardTextConverter()
{
// do nothing
}
IClipboard::EFormat
CXWindowsClipboardTextConverter::getFormat() const
{
return IClipboard::kText;
}
Atom
CXWindowsClipboardTextConverter::getAtom() const
{
return m_atom;
}
int
CXWindowsClipboardTextConverter::getDataSize() const
{
return 8;
}
CString
CXWindowsClipboardTextConverter::fromIClipboard(const CString& data) const
{
return CUnicode::UTF8ToText(data);
}
CString
CXWindowsClipboardTextConverter::toIClipboard(const CString& data) const
{
// convert to UTF-8
bool errors;
CString utf8 = CUnicode::textToUTF8(data, &errors);
// if there were decoding errors then, to support old applications
// that don't understand UTF-8 but can report the exact binary
// UTF-8 representation, see if the data appears to be UTF-8. if
// so then use it as is.
if (errors && CUnicode::isUTF8(data)) {
return data;
}
return utf8;
}

View File

@@ -0,0 +1,44 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CXWINDOWSCLIPBOARDTEXTCONVERTER_H
#define CXWINDOWSCLIPBOARDTEXTCONVERTER_H
#include "CXWindowsClipboard.h"
//! Convert to/from locale text encoding
class CXWindowsClipboardTextConverter : public IXWindowsClipboardConverter {
public:
/*!
\c name is converted to an atom and that is reported by getAtom().
*/
CXWindowsClipboardTextConverter(Display* display, const char* name);
virtual ~CXWindowsClipboardTextConverter();
// IXWindowsClipboardConverter overrides
virtual IClipboard::EFormat
getFormat() const;
virtual Atom getAtom() const;
virtual int getDataSize() const;
virtual CString fromIClipboard(const CString&) const;
virtual CString toIClipboard(const CString&) const;
private:
Atom m_atom;
};
#endif

View File

@@ -0,0 +1,65 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CXWindowsClipboardUCS2Converter.h"
#include "CUnicode.h"
//
// CXWindowsClipboardUCS2Converter
//
CXWindowsClipboardUCS2Converter::CXWindowsClipboardUCS2Converter(
Display* display, const char* name) :
m_atom(XInternAtom(display, name, False))
{
// do nothing
}
CXWindowsClipboardUCS2Converter::~CXWindowsClipboardUCS2Converter()
{
// do nothing
}
IClipboard::EFormat
CXWindowsClipboardUCS2Converter::getFormat() const
{
return IClipboard::kText;
}
Atom
CXWindowsClipboardUCS2Converter::getAtom() const
{
return m_atom;
}
int
CXWindowsClipboardUCS2Converter::getDataSize() const
{
return 16;
}
CString
CXWindowsClipboardUCS2Converter::fromIClipboard(const CString& data) const
{
return CUnicode::UTF8ToUCS2(data);
}
CString
CXWindowsClipboardUCS2Converter::toIClipboard(const CString& data) const
{
return CUnicode::UCS2ToUTF8(data);
}

View File

@@ -0,0 +1,44 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CXWINDOWSCLIPBOARDUCS2CONVERTER_H
#define CXWINDOWSCLIPBOARDUCS2CONVERTER_H
#include "CXWindowsClipboard.h"
//! Convert to/from UCS-2 encoding
class CXWindowsClipboardUCS2Converter : public IXWindowsClipboardConverter {
public:
/*!
\c name is converted to an atom and that is reported by getAtom().
*/
CXWindowsClipboardUCS2Converter(Display* display, const char* name);
virtual ~CXWindowsClipboardUCS2Converter();
// IXWindowsClipboardConverter overrides
virtual IClipboard::EFormat
getFormat() const;
virtual Atom getAtom() const;
virtual int getDataSize() const;
virtual CString fromIClipboard(const CString&) const;
virtual CString toIClipboard(const CString&) const;
private:
Atom m_atom;
};
#endif

View File

@@ -0,0 +1,64 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CXWindowsClipboardUTF8Converter.h"
//
// CXWindowsClipboardUTF8Converter
//
CXWindowsClipboardUTF8Converter::CXWindowsClipboardUTF8Converter(
Display* display, const char* name) :
m_atom(XInternAtom(display, name, False))
{
// do nothing
}
CXWindowsClipboardUTF8Converter::~CXWindowsClipboardUTF8Converter()
{
// do nothing
}
IClipboard::EFormat
CXWindowsClipboardUTF8Converter::getFormat() const
{
return IClipboard::kText;
}
Atom
CXWindowsClipboardUTF8Converter::getAtom() const
{
return m_atom;
}
int
CXWindowsClipboardUTF8Converter::getDataSize() const
{
return 8;
}
CString
CXWindowsClipboardUTF8Converter::fromIClipboard(const CString& data) const
{
return data;
}
CString
CXWindowsClipboardUTF8Converter::toIClipboard(const CString& data) const
{
return data;
}

View File

@@ -0,0 +1,44 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CXWINDOWSCLIPBOARDUTF8CONVERTER_H
#define CXWINDOWSCLIPBOARDUTF8CONVERTER_H
#include "CXWindowsClipboard.h"
//! Convert to/from UTF-8 encoding
class CXWindowsClipboardUTF8Converter : public IXWindowsClipboardConverter {
public:
/*!
\c name is converted to an atom and that is reported by getAtom().
*/
CXWindowsClipboardUTF8Converter(Display* display, const char* name);
virtual ~CXWindowsClipboardUTF8Converter();
// IXWindowsClipboardConverter overrides
virtual IClipboard::EFormat
getFormat() const;
virtual Atom getAtom() const;
virtual int getDataSize() const;
virtual CString fromIClipboard(const CString&) const;
virtual CString toIClipboard(const CString&) const;
private:
Atom m_atom;
};
#endif

View File

@@ -0,0 +1,287 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CXWindowsEventQueueBuffer.h"
#include "CLock.h"
#include "CThread.h"
#include "CEvent.h"
#include "IEventQueue.h"
#include <fcntl.h>
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#if HAVE_POLL
# include <poll.h>
#else
# if HAVE_SYS_SELECT_H
# include <sys/select.h>
# endif
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# endif
# if HAVE_SYS_TYPES_H
# include <sys/types.h>
# endif
#endif
//
// CEventQueueTimer
//
class CEventQueueTimer { };
//
// CXWindowsEventQueueBuffer
//
CXWindowsEventQueueBuffer::CXWindowsEventQueueBuffer(
Display* display, Window window) :
m_display(display),
m_window(window),
m_waiting(false)
{
assert(m_display != NULL);
assert(m_window != None);
m_userEvent = XInternAtom(m_display, "SYNERGY_USER_EVENT", False);
// set up for pipe hack
int result = pipe(m_pipefd);
assert(result == 0);
int pipeflags;
pipeflags = fcntl(m_pipefd[0], F_GETFL);
fcntl(m_pipefd[0], F_SETFL, pipeflags | O_NONBLOCK);
pipeflags = fcntl(m_pipefd[1], F_GETFL);
fcntl(m_pipefd[1], F_SETFL, pipeflags | O_NONBLOCK);
}
CXWindowsEventQueueBuffer::~CXWindowsEventQueueBuffer()
{
// release pipe hack resources
close(m_pipefd[0]);
close(m_pipefd[1]);
}
void
CXWindowsEventQueueBuffer::waitForEvent(double dtimeout)
{
CThread::testCancel();
// clear out the pipe in preparation for waiting.
char buf[16];
ssize_t read_response = read(m_pipefd[0], buf, 15);
// with linux automake, warnings are treated as errors by default
if (read_response < 0)
{
// todo: handle read response
}
{
CLock lock(&m_mutex);
// we're now waiting for events
m_waiting = true;
// push out pending events
flush();
}
// calling flush may have queued up a new event.
if (!CXWindowsEventQueueBuffer::isEmpty()) {
CThread::testCancel();
return;
}
// use poll() to wait for a message from the X server or for timeout.
// this is a good deal more efficient than polling and sleeping.
#if HAVE_POLL
struct pollfd pfds[2];
pfds[0].fd = ConnectionNumber(m_display);
pfds[0].events = POLLIN;
pfds[1].fd = m_pipefd[0];
pfds[1].events = POLLIN;
int timeout = (dtimeout < 0.0) ? -1 :
static_cast<int>(1000.0 * dtimeout);
int remaining = timeout;
int retval = 0;
#else
struct timeval timeout;
struct timeval* timeoutPtr;
if (dtimeout < 0.0) {
timeoutPtr = NULL;
}
else {
timeout.tv_sec = static_cast<int>(dtimeout);
timeout.tv_usec = static_cast<int>(1.0e+6 *
(dtimeout - timeout.tv_sec));
timeoutPtr = &timeout;
}
// initialize file descriptor sets
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(ConnectionNumber(m_display), &rfds);
FD_SET(m_pipefd[0], &rfds);
int nfds;
if (ConnectionNumber(m_display) > m_pipefd[0]) {
nfds = ConnectionNumber(m_display) + 1;
}
else {
nfds = m_pipefd[0] + 1;
}
#endif
// It's possible that the X server has queued events locally
// in xlib's event buffer and not pushed on to the fd. Hence we
// can't simply monitor the fd as we may never be woken up.
// ie addEvent calls flush, XFlush may not send via the fd hence
// there is an event waiting to be sent but we must exit the poll
// before it can.
// Instead we poll for a brief period of time (so if events
// queued locally in the xlib buffer can be processed)
// and continue doing this until timeout is reached.
// The human eye can notice 60hz (ansi) which is 16ms, however
// we want to give the cpu a chance s owe up this to 25ms
#define TIMEOUT_DELAY 25
while( ((dtimeout < 0.0) || (remaining > 0)) && QLength(m_display)==0 && retval==0){
#if HAVE_POLL
retval = poll(pfds, 2, TIMEOUT_DELAY); //16ms = 60hz, but we make it > to play nicely with the cpu
if (pfds[1].revents & POLLIN) {
ssize_t read_response = read(m_pipefd[0], buf, 15);
// with linux automake, warnings are treated as errors by default
if (read_response < 0)
{
// todo: handle read response
}
}
#else
retval = select(nfds,
SELECT_TYPE_ARG234 &rfds,
SELECT_TYPE_ARG234 NULL,
SELECT_TYPE_ARG234 NULL,
SELECT_TYPE_ARG5 TIMEOUT_DELAY);
if (FD_SET(m_pipefd[0], &rfds)) {
read(m_pipefd[0], buf, 15);
}
#endif
remaining-=TIMEOUT_DELAY;
}
{
// we're no longer waiting for events
CLock lock(&m_mutex);
m_waiting = false;
}
CThread::testCancel();
}
IEventQueueBuffer::Type
CXWindowsEventQueueBuffer::getEvent(CEvent& event, UInt32& dataID)
{
CLock lock(&m_mutex);
// push out pending events
flush();
// get next event
XNextEvent(m_display, &m_event);
// process event
if (m_event.xany.type == ClientMessage &&
m_event.xclient.message_type == m_userEvent) {
dataID = static_cast<UInt32>(m_event.xclient.data.l[0]);
return kUser;
}
else {
event = CEvent(CEvent::kSystem,
IEventQueue::getSystemTarget(), &m_event);
return kSystem;
}
}
bool
CXWindowsEventQueueBuffer::addEvent(UInt32 dataID)
{
// prepare a message
XEvent xevent;
xevent.xclient.type = ClientMessage;
xevent.xclient.window = m_window;
xevent.xclient.message_type = m_userEvent;
xevent.xclient.format = 32;
xevent.xclient.data.l[0] = static_cast<long>(dataID);
// save the message
CLock lock(&m_mutex);
m_postedEvents.push_back(xevent);
// if we're currently waiting for an event then send saved events to
// the X server now. if we're not waiting then some other thread
// might be using the display connection so we can't safely use it
// too.
if (m_waiting) {
flush();
// Send a character through the round-trip pipe to wake a thread
// that is waiting for a ConnectionNumber() socket to be readable.
// The flush call can read incoming data from the socket and put
// it in Xlib's input buffer. That sneaks it past the other thread.
ssize_t write_response = write(m_pipefd[1], "!", 1);
// with linux automake, warnings are treated as errors by default
if (write_response < 0)
{
// todo: handle read response
}
}
return true;
}
bool
CXWindowsEventQueueBuffer::isEmpty() const
{
CLock lock(&m_mutex);
return (XPending(m_display) == 0 );
}
CEventQueueTimer*
CXWindowsEventQueueBuffer::newTimer(double, bool) const
{
return new CEventQueueTimer;
}
void
CXWindowsEventQueueBuffer::deleteTimer(CEventQueueTimer* timer) const
{
delete timer;
}
void
CXWindowsEventQueueBuffer::flush()
{
// note -- m_mutex must be locked on entry
// flush the posted event list to the X server
for (size_t i = 0; i < m_postedEvents.size(); ++i) {
XSendEvent(m_display, m_window, False, 0, &m_postedEvents[i]);
}
XFlush(m_display);
m_postedEvents.clear();
}

View File

@@ -0,0 +1,61 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CXWINDOWSEVENTQUEUEBUFFER_H
#define CXWINDOWSEVENTQUEUEBUFFER_H
#include "IEventQueueBuffer.h"
#include "CMutex.h"
#include "stdvector.h"
#if X_DISPLAY_MISSING
# error X11 is required to build synergy
#else
# include <X11/Xlib.h>
#endif
//! Event queue buffer for X11
class CXWindowsEventQueueBuffer : public IEventQueueBuffer {
public:
CXWindowsEventQueueBuffer(Display*, Window);
virtual ~CXWindowsEventQueueBuffer();
// IEventQueueBuffer overrides
virtual void waitForEvent(double timeout);
virtual Type getEvent(CEvent& event, UInt32& dataID);
virtual bool addEvent(UInt32 dataID);
virtual bool isEmpty() const;
virtual CEventQueueTimer*
newTimer(double duration, bool oneShot) const;
virtual void deleteTimer(CEventQueueTimer*) const;
private:
void flush();
private:
typedef std::vector<XEvent> CEventList;
CMutex m_mutex;
Display* m_display;
Window m_window;
Atom m_userEvent;
XEvent m_event;
CEventList m_postedEvents;
bool m_waiting;
int m_pipefd[2];
};
#endif

View File

@@ -0,0 +1,861 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CXWindowsKeyState.h"
#include "CXWindowsUtil.h"
#include "CLog.h"
#include "CStringUtil.h"
#include "stdmap.h"
#include <cstddef>
#include <algorithm>
#if X_DISPLAY_MISSING
# error X11 is required to build synergy
#else
# include <X11/X.h>
# include <X11/Xutil.h>
# define XK_MISCELLANY
# define XK_XKB_KEYS
# include <X11/keysymdef.h>
#if HAVE_XKB_EXTENSION
# include <X11/XKBlib.h>
#endif
#endif
static const size_t ModifiersFromXDefaultSize = 32;
CXWindowsKeyState::CXWindowsKeyState(Display* display, bool useXKB) :
m_display(display),
m_modifierFromX(ModifiersFromXDefaultSize)
{
init(display, useXKB);
}
CXWindowsKeyState::CXWindowsKeyState(
Display* display, bool useXKB,
IEventQueue& eventQueue, CKeyMap& keyMap) :
CKeyState(eventQueue, keyMap),
m_display(display),
m_modifierFromX(ModifiersFromXDefaultSize)
{
init(display, useXKB);
}
CXWindowsKeyState::~CXWindowsKeyState()
{
#if HAVE_XKB_EXTENSION
if (m_xkb != NULL) {
XkbFreeKeyboard(m_xkb, 0, True);
}
#endif
}
void
CXWindowsKeyState::init(Display* display, bool useXKB)
{
XGetKeyboardControl(m_display, &m_keyboardState);
#if HAVE_XKB_EXTENSION
if (useXKB) {
m_xkb = XkbGetMap(m_display, XkbKeyActionsMask | XkbKeyBehaviorsMask |
XkbAllClientInfoMask, XkbUseCoreKbd);
}
else {
m_xkb = NULL;
}
#endif
setActiveGroup(kGroupPollAndSet);
}
void
CXWindowsKeyState::setActiveGroup(SInt32 group)
{
if (group == kGroupPollAndSet) {
// we need to set the group to -1 in order for pollActiveGroup() to
// actually poll for the group
m_group = -1;
m_group = pollActiveGroup();
}
else if (group == kGroupPoll) {
m_group = -1;
}
else {
assert(group >= 0);
m_group = group;
}
}
void
CXWindowsKeyState::setAutoRepeat(const XKeyboardState& state)
{
m_keyboardState = state;
}
KeyModifierMask
CXWindowsKeyState::mapModifiersFromX(unsigned int state) const
{
LOG((CLOG_DEBUG2 "mapping state: %i", state));
UInt32 offset = 8 * getGroupFromState(state);
KeyModifierMask mask = 0;
for (int i = 0; i < 8; ++i) {
if ((state & (1u << i)) != 0) {
LOG((CLOG_DEBUG2 "|= modifier: %i", offset + i));
if (offset + i >= m_modifierFromX.size()) {
LOG((CLOG_ERR "m_modifierFromX is too small (%d) for the "
"requested offset (%d)", m_modifierFromX.size(), offset+i));
} else {
mask |= m_modifierFromX[offset + i];
}
}
}
return mask;
}
bool
CXWindowsKeyState::mapModifiersToX(KeyModifierMask mask,
unsigned int& modifiers) const
{
modifiers = 0;
for (SInt32 i = 0; i < kKeyModifierNumBits; ++i) {
KeyModifierMask bit = (1u << i);
if ((mask & bit) != 0) {
KeyModifierToXMask::const_iterator j = m_modifierToX.find(bit);
if (j == m_modifierToX.end()) {
return false;
}
else {
modifiers |= j->second;
}
}
}
return true;
}
void
CXWindowsKeyState::mapKeyToKeycodes(KeyID key, CKeycodeList& keycodes) const
{
keycodes.clear();
std::pair<KeyToKeyCodeMap::const_iterator,
KeyToKeyCodeMap::const_iterator> range =
m_keyCodeFromKey.equal_range(key);
for (KeyToKeyCodeMap::const_iterator i = range.first;
i != range.second; ++i) {
keycodes.push_back(i->second);
}
}
bool
CXWindowsKeyState::fakeCtrlAltDel()
{
// pass keys through unchanged
return false;
}
KeyModifierMask
CXWindowsKeyState::pollActiveModifiers() const
{
Window root = DefaultRootWindow(m_display), window;
int xRoot, yRoot, xWindow, yWindow;
unsigned int state = 0;
if (XQueryPointer(m_display, root, &root, &window,
&xRoot, &yRoot, &xWindow, &yWindow, &state) == False) {
state = 0;
}
return mapModifiersFromX(state);
}
SInt32
CXWindowsKeyState::pollActiveGroup() const
{
// fixed condition where any group < -1 would have undetermined behaviour
if (m_group >= 0) {
return m_group;
}
#if HAVE_XKB_EXTENSION
if (m_xkb != NULL) {
XkbStateRec state;
if (XkbGetState(m_display, XkbUseCoreKbd, &state) == Success) {
return state.group;
}
}
#endif
return 0;
}
void
CXWindowsKeyState::pollPressedKeys(KeyButtonSet& pressedKeys) const
{
char keys[32];
XQueryKeymap(m_display, keys);
for (UInt32 i = 0; i < 32; ++i) {
for (UInt32 j = 0; j < 8; ++j) {
if ((keys[i] & (1u << j)) != 0) {
pressedKeys.insert(8 * i + j);
}
}
}
}
void
CXWindowsKeyState::getKeyMap(CKeyMap& keyMap)
{
// get autorepeat info. we must use the global_auto_repeat told to
// us because it may have modified by synergy.
int oldGlobalAutoRepeat = m_keyboardState.global_auto_repeat;
XGetKeyboardControl(m_display, &m_keyboardState);
m_keyboardState.global_auto_repeat = oldGlobalAutoRepeat;
#if HAVE_XKB_EXTENSION
if (m_xkb != NULL) {
if (XkbGetUpdatedMap(m_display, XkbKeyActionsMask |
XkbKeyBehaviorsMask | XkbAllClientInfoMask, m_xkb) == Success) {
updateKeysymMapXKB(keyMap);
return;
}
}
#endif
updateKeysymMap(keyMap);
}
void
CXWindowsKeyState::fakeKey(const Keystroke& keystroke)
{
switch (keystroke.m_type) {
case Keystroke::kButton:
LOG((CLOG_DEBUG1 " %03x (%08x) %s", keystroke.m_data.m_button.m_button, keystroke.m_data.m_button.m_client, keystroke.m_data.m_button.m_press ? "down" : "up"));
if (keystroke.m_data.m_button.m_repeat) {
int c = keystroke.m_data.m_button.m_button;
int i = (c >> 3);
int b = 1 << (c & 7);
if (m_keyboardState.global_auto_repeat == AutoRepeatModeOff ||
(c!=113 && c!=116 && (m_keyboardState.auto_repeats[i] & b) == 0)) {
LOG((CLOG_DEBUG1 " discard autorepeat"));
break;
}
}
XTestFakeKeyEvent(m_display, keystroke.m_data.m_button.m_button,
keystroke.m_data.m_button.m_press ? True : False,
CurrentTime);
break;
case Keystroke::kGroup:
if (keystroke.m_data.m_group.m_absolute) {
LOG((CLOG_DEBUG1 " group %d", keystroke.m_data.m_group.m_group));
#if HAVE_XKB_EXTENSION
if (m_xkb != NULL) {
if (XkbLockGroup(m_display, XkbUseCoreKbd,
keystroke.m_data.m_group.m_group) == False) {
LOG((CLOG_DEBUG1 "XkbLockGroup request not sent"));
}
}
else
#endif
{
LOG((CLOG_DEBUG1 " ignored"));
}
}
else {
LOG((CLOG_DEBUG1 " group %+d", keystroke.m_data.m_group.m_group));
#if HAVE_XKB_EXTENSION
if (m_xkb != NULL) {
if (XkbLockGroup(m_display, XkbUseCoreKbd,
getEffectiveGroup(pollActiveGroup(),
keystroke.m_data.m_group.m_group)) == False) {
LOG((CLOG_DEBUG1 "XkbLockGroup request not sent"));
}
}
else
#endif
{
LOG((CLOG_DEBUG1 " ignored"));
}
}
break;
}
XFlush(m_display);
}
void
CXWindowsKeyState::updateKeysymMap(CKeyMap& keyMap)
{
// there are up to 4 keysyms per keycode
static const int maxKeysyms = 4;
LOG((CLOG_DEBUG1 "non-XKB mapping"));
// prepare map from X modifier to KeyModifierMask. certain bits
// are predefined.
std::fill(m_modifierFromX.begin(), m_modifierFromX.end(), 0);
m_modifierFromX[ShiftMapIndex] = KeyModifierShift;
m_modifierFromX[LockMapIndex] = KeyModifierCapsLock;
m_modifierFromX[ControlMapIndex] = KeyModifierControl;
m_modifierToX.clear();
m_modifierToX[KeyModifierShift] = ShiftMask;
m_modifierToX[KeyModifierCapsLock] = LockMask;
m_modifierToX[KeyModifierControl] = ControlMask;
// prepare map from KeyID to KeyCode
m_keyCodeFromKey.clear();
// get the number of keycodes
int minKeycode, maxKeycode;
XDisplayKeycodes(m_display, &minKeycode, &maxKeycode);
int numKeycodes = maxKeycode - minKeycode + 1;
// get the keyboard mapping for all keys
int keysymsPerKeycode;
KeySym* allKeysyms = XGetKeyboardMapping(m_display,
minKeycode, numKeycodes,
&keysymsPerKeycode);
// it's more convenient to always have maxKeysyms KeySyms per key
{
KeySym* tmpKeysyms = new KeySym[maxKeysyms * numKeycodes];
for (int i = 0; i < numKeycodes; ++i) {
for (int j = 0; j < maxKeysyms; ++j) {
if (j < keysymsPerKeycode) {
tmpKeysyms[maxKeysyms * i + j] =
allKeysyms[keysymsPerKeycode * i + j];
}
else {
tmpKeysyms[maxKeysyms * i + j] = NoSymbol;
}
}
}
XFree(allKeysyms);
allKeysyms = tmpKeysyms;
}
// get the buttons assigned to modifiers. X11 does not predefine
// the meaning of any modifiers except shift, caps lock, and the
// control key. the meaning of a modifier bit (other than those)
// depends entirely on the KeySyms mapped to that bit. unfortunately
// you cannot map a bit back to the KeySym used to produce it.
// for example, let's say button 1 maps to Alt_L without shift and
// Meta_L with shift. now if mod1 is mapped to button 1 that could
// mean the user used Alt or Meta to turn on that modifier and there's
// no way to know which. it's also possible for one button to be
// mapped to multiple bits so both mod1 and mod2 could be generated
// by button 1.
//
// we're going to ignore any modifier for a button except the first.
// with the above example, that means we'll ignore the mod2 modifier
// bit unless it's also mapped to some other button. we're also
// going to ignore all KeySyms except the first modifier KeySym,
// which means button 1 above won't map to Meta, just Alt.
std::map<KeyCode, unsigned int> modifierButtons;
XModifierKeymap* modifiers = XGetModifierMapping(m_display);
for (unsigned int i = 0; i < 8; ++i) {
const KeyCode* buttons =
modifiers->modifiermap + i * modifiers->max_keypermod;
for (int j = 0; j < modifiers->max_keypermod; ++j) {
modifierButtons.insert(std::make_pair(buttons[j], i));
}
}
XFreeModifiermap(modifiers);
modifierButtons.erase(0);
// Hack to deal with VMware. When a VMware client grabs input the
// player clears out the X modifier map for whatever reason. We're
// notified of the change and arrive here to discover that there
// are no modifiers at all. Since this prevents the modifiers from
// working in the VMware client we'll use the last known good set
// of modifiers when there are no modifiers. If there are modifiers
// we update the last known good set.
if (!modifierButtons.empty()) {
m_lastGoodNonXKBModifiers = modifierButtons;
}
else {
modifierButtons = m_lastGoodNonXKBModifiers;
}
// add entries for each keycode
CKeyMap::KeyItem item;
for (int i = 0; i < numKeycodes; ++i) {
KeySym* keysyms = allKeysyms + maxKeysyms * i;
KeyCode keycode = static_cast<KeyCode>(i + minKeycode);
item.m_button = static_cast<KeyButton>(keycode);
item.m_client = 0;
// determine modifier sensitivity
item.m_sensitive = 0;
// if the keysyms in levels 2 or 3 exist and differ from levels
// 0 and 1 then the key is sensitive AltGr (Mode_switch)
if ((keysyms[2] != NoSymbol && keysyms[2] != keysyms[0]) ||
(keysyms[3] != NoSymbol && keysyms[2] != keysyms[1])) {
item.m_sensitive |= KeyModifierAltGr;
}
// check if the key is caps-lock sensitive. some systems only
// provide one keysym for keys sensitive to caps-lock. if we
// find that then fill in the missing keysym.
if (keysyms[0] != NoSymbol && keysyms[1] == NoSymbol &&
keysyms[2] == NoSymbol && keysyms[3] == NoSymbol) {
KeySym lKeysym, uKeysym;
XConvertCase(keysyms[0], &lKeysym, &uKeysym);
if (lKeysym != uKeysym) {
keysyms[0] = lKeysym;
keysyms[1] = uKeysym;
item.m_sensitive |= KeyModifierCapsLock;
}
}
else if (keysyms[0] != NoSymbol && keysyms[1] != NoSymbol) {
KeySym lKeysym, uKeysym;
XConvertCase(keysyms[0], &lKeysym, &uKeysym);
if (lKeysym != uKeysym &&
lKeysym == keysyms[0] &&
uKeysym == keysyms[1]) {
item.m_sensitive |= KeyModifierCapsLock;
}
else if (keysyms[2] != NoSymbol && keysyms[3] != NoSymbol) {
XConvertCase(keysyms[2], &lKeysym, &uKeysym);
if (lKeysym != uKeysym &&
lKeysym == keysyms[2] &&
uKeysym == keysyms[3]) {
item.m_sensitive |= KeyModifierCapsLock;
}
}
}
// key is sensitive to shift if keysyms in levels 0 and 1 or
// levels 2 and 3 don't match. it's also sensitive to shift
// if it's sensitive to caps-lock.
if ((item.m_sensitive & KeyModifierCapsLock) != 0) {
item.m_sensitive |= KeyModifierShift;
}
else if ((keysyms[0] != NoSymbol && keysyms[1] != NoSymbol &&
keysyms[0] != keysyms[1]) ||
(keysyms[2] != NoSymbol && keysyms[3] != NoSymbol &&
keysyms[2] != keysyms[3])) {
item.m_sensitive |= KeyModifierShift;
}
// key is sensitive to numlock if any keysym on it is
if (IsKeypadKey(keysyms[0]) || IsPrivateKeypadKey(keysyms[0]) ||
IsKeypadKey(keysyms[1]) || IsPrivateKeypadKey(keysyms[1]) ||
IsKeypadKey(keysyms[2]) || IsPrivateKeypadKey(keysyms[2]) ||
IsKeypadKey(keysyms[3]) || IsPrivateKeypadKey(keysyms[3])) {
item.m_sensitive |= KeyModifierNumLock;
}
// do each keysym (shift level)
for (int j = 0; j < maxKeysyms; ++j) {
item.m_id = CXWindowsUtil::mapKeySymToKeyID(keysyms[j]);
if (item.m_id == kKeyNone) {
if (j != 0 && modifierButtons.count(keycode) > 0) {
// pretend the modifier works in other shift levels
// because it probably does.
if (keysyms[1] == NoSymbol || j != 3) {
item.m_id = CXWindowsUtil::mapKeySymToKeyID(keysyms[0]);
}
else {
item.m_id = CXWindowsUtil::mapKeySymToKeyID(keysyms[1]);
}
}
if (item.m_id == kKeyNone) {
continue;
}
}
// group is 0 for levels 0 and 1 and 1 for levels 2 and 3
item.m_group = (j >= 2) ? 1 : 0;
// compute required modifiers
item.m_required = 0;
if ((j & 1) != 0) {
item.m_required |= KeyModifierShift;
}
if ((j & 2) != 0) {
item.m_required |= KeyModifierAltGr;
}
item.m_generates = 0;
item.m_lock = false;
if (modifierButtons.count(keycode) > 0) {
// get flags for modifier keys
CKeyMap::initModifierKey(item);
// add mapping from X (unless we already have)
if (item.m_generates != 0) {
unsigned int bit = modifierButtons[keycode];
if (m_modifierFromX[bit] == 0) {
m_modifierFromX[bit] = item.m_generates;
m_modifierToX[item.m_generates] = (1u << bit);
}
}
}
// add key
keyMap.addKeyEntry(item);
m_keyCodeFromKey.insert(std::make_pair(item.m_id, keycode));
// add other ways to synthesize the key
if ((j & 1) != 0) {
// add capslock version of key is sensitive to capslock
KeySym lKeysym, uKeysym;
XConvertCase(keysyms[j], &lKeysym, &uKeysym);
if (lKeysym != uKeysym &&
lKeysym == keysyms[j - 1] &&
uKeysym == keysyms[j]) {
item.m_required &= ~KeyModifierShift;
item.m_required |= KeyModifierCapsLock;
keyMap.addKeyEntry(item);
item.m_required |= KeyModifierShift;
item.m_required &= ~KeyModifierCapsLock;
}
// add numlock version of key if sensitive to numlock
if (IsKeypadKey(keysyms[j]) || IsPrivateKeypadKey(keysyms[j])) {
item.m_required &= ~KeyModifierShift;
item.m_required |= KeyModifierNumLock;
keyMap.addKeyEntry(item);
item.m_required |= KeyModifierShift;
item.m_required &= ~KeyModifierNumLock;
}
}
}
}
delete[] allKeysyms;
}
#if HAVE_XKB_EXTENSION
void
CXWindowsKeyState::updateKeysymMapXKB(CKeyMap& keyMap)
{
static const XkbKTMapEntryRec defMapEntry = {
True, // active
0, // level
{
0, // mods.mask
0, // mods.real_mods
0 // mods.vmods
}
};
LOG((CLOG_DEBUG1 "XKB mapping"));
// find the number of groups
int maxNumGroups = 0;
for (int i = m_xkb->min_key_code; i <= m_xkb->max_key_code; ++i) {
int numGroups = XkbKeyNumGroups(m_xkb, static_cast<KeyCode>(i));
if (numGroups > maxNumGroups) {
maxNumGroups = numGroups;
}
}
// prepare map from X modifier to KeyModifierMask
std::vector<int> modifierLevel(maxNumGroups * 8, 4);
m_modifierFromX.clear();
m_modifierFromX.resize(maxNumGroups * 8);
m_modifierToX.clear();
// prepare map from KeyID to KeyCode
m_keyCodeFromKey.clear();
// Hack to deal with VMware. When a VMware client grabs input the
// player clears out the X modifier map for whatever reason. We're
// notified of the change and arrive here to discover that there
// are no modifiers at all. Since this prevents the modifiers from
// working in the VMware client we'll use the last known good set
// of modifiers when there are no modifiers. If there are modifiers
// we update the last known good set.
bool useLastGoodModifiers = !hasModifiersXKB();
if (!useLastGoodModifiers) {
m_lastGoodXKBModifiers.clear();
}
// check every button. on this pass we save all modifiers as native
// X modifier masks.
CKeyMap::KeyItem item;
for (int i = m_xkb->min_key_code; i <= m_xkb->max_key_code; ++i) {
KeyCode keycode = static_cast<KeyCode>(i);
item.m_button = static_cast<KeyButton>(keycode);
item.m_client = 0;
// skip keys with no groups (they generate no symbols)
if (XkbKeyNumGroups(m_xkb, keycode) == 0) {
continue;
}
// note half-duplex keys
const XkbBehavior& b = m_xkb->server->behaviors[keycode];
if ((b.type & XkbKB_OpMask) == XkbKB_Lock) {
keyMap.addHalfDuplexButton(item.m_button);
}
// iterate over all groups
for (int group = 0; group < maxNumGroups; ++group) {
item.m_group = group;
int eGroup = getEffectiveGroup(keycode, group);
// get key info
XkbKeyTypePtr type = XkbKeyKeyType(m_xkb, keycode, eGroup);
// set modifiers the item is sensitive to
item.m_sensitive = type->mods.mask;
// iterate over all shift levels for the button (including none)
for (int j = -1; j < type->map_count; ++j) {
const XkbKTMapEntryRec* mapEntry =
((j == -1) ? &defMapEntry : type->map + j);
if (!mapEntry->active) {
continue;
}
int level = mapEntry->level;
// set required modifiers for this item
item.m_required = mapEntry->mods.mask;
if ((item.m_required & LockMask) != 0 &&
j != -1 && type->preserve != NULL &&
(type->preserve[j].mask & LockMask) != 0) {
// sensitive caps lock and we preserve caps-lock.
// preserving caps-lock means we Xlib functions would
// yield the capitialized KeySym so we'll adjust the
// level accordingly.
if ((level ^ 1) < type->num_levels) {
level ^= 1;
}
}
// get the keysym for this item
KeySym keysym = XkbKeySymEntry(m_xkb, keycode, level, eGroup);
// check for group change actions, locking modifiers, and
// modifier masks.
item.m_lock = false;
bool isModifier = false;
UInt32 modifierMask = m_xkb->map->modmap[keycode];
if (XkbKeyHasActions(m_xkb, keycode) == True) {
XkbAction* action =
XkbKeyActionEntry(m_xkb, keycode, level, eGroup);
if (action->type == XkbSA_SetMods ||
action->type == XkbSA_LockMods) {
isModifier = true;
// note toggles
item.m_lock = (action->type == XkbSA_LockMods);
// maybe use action's mask
if ((action->mods.flags & XkbSA_UseModMapMods) == 0) {
modifierMask = action->mods.mask;
}
}
else if (action->type == XkbSA_SetGroup ||
action->type == XkbSA_LatchGroup ||
action->type == XkbSA_LockGroup) {
// ignore group change key
continue;
}
}
level = mapEntry->level;
// VMware modifier hack
if (useLastGoodModifiers) {
XKBModifierMap::const_iterator k =
m_lastGoodXKBModifiers.find(eGroup * 256 + keycode);
if (k != m_lastGoodXKBModifiers.end()) {
// Use last known good modifier
isModifier = true;
level = k->second.m_level;
modifierMask = k->second.m_mask;
item.m_lock = k->second.m_lock;
}
}
else if (isModifier) {
// Save known good modifier
XKBModifierInfo& info =
m_lastGoodXKBModifiers[eGroup * 256 + keycode];
info.m_level = level;
info.m_mask = modifierMask;
info.m_lock = item.m_lock;
}
// record the modifier mask for this key. don't bother
// for keys that change the group.
item.m_generates = 0;
UInt32 modifierBit =
CXWindowsUtil::getModifierBitForKeySym(keysym);
if (isModifier && modifierBit != kKeyModifierBitNone) {
item.m_generates = (1u << modifierBit);
for (SInt32 j = 0; j < 8; ++j) {
// skip modifiers this key doesn't generate
if ((modifierMask & (1u << j)) == 0) {
continue;
}
// skip keys that map to a modifier that we've
// already seen using fewer modifiers. that is
// if this key must combine with other modifiers
// and we know of a key that combines with fewer
// modifiers (or no modifiers) then prefer the
// other key.
if (level >= modifierLevel[8 * group + j]) {
continue;
}
modifierLevel[8 * group + j] = level;
// save modifier
m_modifierFromX[8 * group + j] |= (1u << modifierBit);
m_modifierToX.insert(std::make_pair(
1u << modifierBit, 1u << j));
}
}
// handle special cases of just one keysym for the keycode
if (type->num_levels == 1) {
// if there are upper- and lowercase versions of the
// keysym then add both.
KeySym lKeysym, uKeysym;
XConvertCase(keysym, &lKeysym, &uKeysym);
if (lKeysym != uKeysym) {
if (j != -1) {
continue;
}
item.m_sensitive |= ShiftMask | LockMask;
KeyID lKeyID = CXWindowsUtil::mapKeySymToKeyID(lKeysym);
KeyID uKeyID = CXWindowsUtil::mapKeySymToKeyID(uKeysym);
if (lKeyID == kKeyNone || uKeyID == kKeyNone) {
continue;
}
item.m_id = lKeyID;
item.m_required = 0;
keyMap.addKeyEntry(item);
item.m_id = uKeyID;
item.m_required = ShiftMask;
keyMap.addKeyEntry(item);
item.m_required = LockMask;
keyMap.addKeyEntry(item);
if (group == 0) {
m_keyCodeFromKey.insert(
std::make_pair(lKeyID, keycode));
m_keyCodeFromKey.insert(
std::make_pair(uKeyID, keycode));
}
continue;
}
}
// add entry
item.m_id = CXWindowsUtil::mapKeySymToKeyID(keysym);
keyMap.addKeyEntry(item);
if (group == 0) {
m_keyCodeFromKey.insert(std::make_pair(item.m_id, keycode));
}
}
}
}
// change all modifier masks to synergy masks from X masks
keyMap.foreachKey(&CXWindowsKeyState::remapKeyModifiers, this);
// allow composition across groups
keyMap.allowGroupSwitchDuringCompose();
}
#endif
void
CXWindowsKeyState::remapKeyModifiers(KeyID id, SInt32 group,
CKeyMap::KeyItem& item, void* vself)
{
CXWindowsKeyState* self = reinterpret_cast<CXWindowsKeyState*>(vself);
item.m_required =
self->mapModifiersFromX(XkbBuildCoreState(item.m_required, group));
item.m_sensitive =
self->mapModifiersFromX(XkbBuildCoreState(item.m_sensitive, group));
}
bool
CXWindowsKeyState::hasModifiersXKB() const
{
#if HAVE_XKB_EXTENSION
// iterate over all keycodes
for (int i = m_xkb->min_key_code; i <= m_xkb->max_key_code; ++i) {
KeyCode keycode = static_cast<KeyCode>(i);
if (XkbKeyHasActions(m_xkb, keycode) == True) {
// iterate over all groups
int numGroups = XkbKeyNumGroups(m_xkb, keycode);
for (int group = 0; group < numGroups; ++group) {
// iterate over all shift levels for the button (including none)
XkbKeyTypePtr type = XkbKeyKeyType(m_xkb, keycode, group);
for (int j = -1; j < type->map_count; ++j) {
if (j != -1 && !type->map[j].active) {
continue;
}
int level = ((j == -1) ? 0 : type->map[j].level);
XkbAction* action =
XkbKeyActionEntry(m_xkb, keycode, level, group);
if (action->type == XkbSA_SetMods ||
action->type == XkbSA_LockMods) {
return true;
}
}
}
}
}
#endif
return false;
}
int
CXWindowsKeyState::getEffectiveGroup(KeyCode keycode, int group) const
{
(void)keycode;
#if HAVE_XKB_EXTENSION
// get effective group for key
int numGroups = XkbKeyNumGroups(m_xkb, keycode);
if (group >= numGroups) {
unsigned char groupInfo = XkbKeyGroupInfo(m_xkb, keycode);
switch (XkbOutOfRangeGroupAction(groupInfo)) {
case XkbClampIntoRange:
group = numGroups - 1;
break;
case XkbRedirectIntoRange:
group = XkbOutOfRangeGroupNumber(groupInfo);
if (group >= numGroups) {
group = 0;
}
break;
default:
// wrap
group %= numGroups;
break;
}
}
#endif
return group;
}
UInt32
CXWindowsKeyState::getGroupFromState(unsigned int state) const
{
#if HAVE_XKB_EXTENSION
if (m_xkb != NULL) {
return XkbGroupForCoreState(state);
}
#endif
return 0;
}

View File

@@ -0,0 +1,161 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2003 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CXWINDOWSKEYSTATE_H
#define CXWINDOWSKEYSTATE_H
#include "CKeyState.h"
#include "stdmap.h"
#include "stdvector.h"
#if X_DISPLAY_MISSING
# error X11 is required to build synergy
#else
# include <X11/Xlib.h>
# if HAVE_X11_EXTENSIONS_XTEST_H
# include <X11/extensions/XTest.h>
# else
# error The XTest extension is required to build synergy
# endif
# if HAVE_XKB_EXTENSION
# include <X11/extensions/XKBstr.h>
# endif
#endif
//! X Windows key state
/*!
A key state for X Windows.
*/
class CXWindowsKeyState : public CKeyState {
public:
typedef std::vector<int> CKeycodeList;
enum {
kGroupPoll = -1,
kGroupPollAndSet = -2
};
CXWindowsKeyState(Display*, bool useXKB);
CXWindowsKeyState(Display*, bool useXKB,
IEventQueue& eventQueue, CKeyMap& keyMap);
~CXWindowsKeyState();
//! @name modifiers
//@{
//! Set active group
/*!
Sets the active group to \p group. This is the group returned by
\c pollActiveGroup(). If \p group is \c kGroupPoll then
\c pollActiveGroup() will really poll, but that's a slow operation
on X11. If \p group is \c kGroupPollAndSet then this will poll the
active group now and use it for future calls to \c pollActiveGroup().
*/
void setActiveGroup(SInt32 group);
//! Set the auto-repeat state
/*!
Sets the auto-repeat state.
*/
void setAutoRepeat(const XKeyboardState&);
//@}
//! @name accessors
//@{
//! Convert X modifier mask to synergy mask
/*!
Returns the synergy modifier mask corresponding to the X modifier
mask in \p state.
*/
KeyModifierMask mapModifiersFromX(unsigned int state) const;
//! Convert synergy modifier mask to X mask
/*!
Converts the synergy modifier mask to the corresponding X modifier
mask. Returns \c true if successful and \c false if any modifier
could not be converted.
*/
bool mapModifiersToX(KeyModifierMask, unsigned int&) const;
//! Convert synergy key to all corresponding X keycodes
/*!
Converts the synergy key \p key to all of the keycodes that map to
that key.
*/
void mapKeyToKeycodes(KeyID key,
CKeycodeList& keycodes) const;
//@}
// IKeyState overrides
virtual bool fakeCtrlAltDel();
virtual KeyModifierMask
pollActiveModifiers() const;
virtual SInt32 pollActiveGroup() const;
virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const;
protected:
// CKeyState overrides
virtual void getKeyMap(CKeyMap& keyMap);
virtual void fakeKey(const Keystroke& keystroke);
private:
void init(Display* display, bool useXKB);
void updateKeysymMap(CKeyMap&);
void updateKeysymMapXKB(CKeyMap&);
bool hasModifiersXKB() const;
int getEffectiveGroup(KeyCode, int group) const;
UInt32 getGroupFromState(unsigned int state) const;
static void remapKeyModifiers(KeyID, SInt32,
CKeyMap::KeyItem&, void*);
private:
struct XKBModifierInfo {
public:
unsigned char m_level;
UInt32 m_mask;
bool m_lock;
};
typedef std::vector<KeyModifierMask> KeyModifierMaskList;
typedef std::map<KeyModifierMask, unsigned int> KeyModifierToXMask;
typedef std::multimap<KeyID, KeyCode> KeyToKeyCodeMap;
typedef std::map<KeyCode, unsigned int> NonXKBModifierMap;
typedef std::map<UInt32, XKBModifierInfo> XKBModifierMap;
Display* m_display;
#if HAVE_XKB_EXTENSION
XkbDescPtr m_xkb;
#endif
SInt32 m_group;
XKBModifierMap m_lastGoodXKBModifiers;
NonXKBModifierMap m_lastGoodNonXKBModifiers;
// X modifier (bit number) to synergy modifier (mask) mapping
KeyModifierMaskList m_modifierFromX;
// synergy modifier (mask) to X modifier (mask)
KeyModifierToXMask m_modifierToX;
// map KeyID to all keycodes that can synthesize that KeyID
KeyToKeyCodeMap m_keyCodeFromKey;
// autorepeat state
XKeyboardState m_keyboardState;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,255 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CXWINDOWSSCREEN_H
#define CXWINDOWSSCREEN_H
#include "CPlatformScreen.h"
#include "stdset.h"
#include "stdvector.h"
#if X_DISPLAY_MISSING
# error X11 is required to build synergy
#else
# include <X11/Xlib.h>
#endif
#include "CKeyMap.h"
class CXWindowsClipboard;
class CXWindowsKeyState;
class CXWindowsScreenSaver;
//! Implementation of IPlatformScreen for X11
class CXWindowsScreen : public CPlatformScreen {
public:
CXWindowsScreen(const char* displayName, bool isPrimary, bool disableXInitThreads, int mouseScrollDelta, IEventQueue& eventQueue);
virtual ~CXWindowsScreen();
//! @name manipulators
//@{
//@}
// IScreen overrides
virtual void* getEventTarget() const;
virtual bool getClipboard(ClipboardID id, IClipboard*) const;
virtual void getShape(SInt32& x, SInt32& y,
SInt32& width, SInt32& height) const;
virtual void getCursorPos(SInt32& x, SInt32& y) const;
// IPrimaryScreen overrides
virtual void reconfigure(UInt32 activeSides);
virtual void warpCursor(SInt32 x, SInt32 y);
virtual UInt32 registerHotKey(KeyID key, KeyModifierMask mask);
virtual void unregisterHotKey(UInt32 id);
virtual void fakeInputBegin();
virtual void fakeInputEnd();
virtual SInt32 getJumpZoneSize() const;
virtual bool isAnyMouseButtonDown() const;
virtual void getCursorCenter(SInt32& x, SInt32& y) const;
virtual void gameDeviceTimingResp(UInt16 freq) { }
// ISecondaryScreen overrides
virtual void fakeMouseButton(ButtonID id, bool press);
virtual void fakeMouseMove(SInt32 x, SInt32 y) const;
virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const;
virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const;
virtual void fakeGameDeviceButtons(GameDeviceID id, GameDeviceButton buttons) const { }
virtual void fakeGameDeviceSticks(GameDeviceID id, SInt16 x1, SInt16 y1, SInt16 x2, SInt16 y2) const { }
virtual void fakeGameDeviceTriggers(GameDeviceID id, UInt8 t1, UInt8 t2) const { }
virtual void queueGameDeviceTimingReq() const { }
// IPlatformScreen overrides
virtual void enable();
virtual void disable();
virtual void enter();
virtual bool leave();
virtual bool setClipboard(ClipboardID, const IClipboard*);
virtual void checkClipboards();
virtual void openScreensaver(bool notify);
virtual void closeScreensaver();
virtual void screensaver(bool activate);
virtual void resetOptions();
virtual void setOptions(const COptionsList& options);
virtual void setSequenceNumber(UInt32);
virtual bool isPrimary() const;
virtual void gameDeviceFeedback(GameDeviceID id, UInt16 m1, UInt16 m2) { }
protected:
// IPlatformScreen overrides
virtual void handleSystemEvent(const CEvent&, void*);
virtual void updateButtons();
virtual IKeyState* getKeyState() const;
private:
// event sending
void sendEvent(CEvent::Type, void* = NULL);
void sendClipboardEvent(CEvent::Type, ClipboardID);
// create the transparent cursor
Cursor createBlankCursor() const;
// determine the clipboard from the X selection. returns
// kClipboardEnd if no such clipboard.
ClipboardID getClipboardID(Atom selection) const;
// continue processing a selection request
void processClipboardRequest(Window window,
Time time, Atom property);
// terminate a selection request
void destroyClipboardRequest(Window window);
// X I/O error handler
void onError();
static int ioErrorHandler(Display*);
private:
class CKeyEventFilter {
public:
int m_event;
Window m_window;
Time m_time;
KeyCode m_keycode;
};
Display* openDisplay(const char* displayName);
void saveShape();
Window openWindow() const;
void openIM();
bool grabMouseAndKeyboard();
void onKeyPress(XKeyEvent&);
void onKeyRelease(XKeyEvent&, bool isRepeat);
bool onHotKey(XKeyEvent&, bool isRepeat);
void onMousePress(const XButtonEvent&);
void onMouseRelease(const XButtonEvent&);
void onMouseMove(const XMotionEvent&);
bool detectXI2();
void selectXIRawMotion();
void selectEvents(Window) const;
void doSelectEvents(Window) const;
KeyID mapKeyFromX(XKeyEvent*) const;
ButtonID mapButtonFromX(const XButtonEvent*) const;
unsigned int mapButtonToX(ButtonID id) const;
void warpCursorNoFlush(SInt32 x, SInt32 y);
void refreshKeyboard(XEvent*);
static Bool findKeyEvent(Display*, XEvent* xevent, XPointer arg);
private:
struct CHotKeyItem {
public:
CHotKeyItem(int, unsigned int);
bool operator<(const CHotKeyItem&) const;
private:
int m_keycode;
unsigned int m_mask;
};
typedef std::set<bool> CFilteredKeycodes;
typedef std::vector<std::pair<int, unsigned int> > HotKeyList;
typedef std::map<UInt32, HotKeyList> HotKeyMap;
typedef std::vector<UInt32> HotKeyIDList;
typedef std::map<CHotKeyItem, UInt32> HotKeyToIDMap;
// true if screen is being used as a primary screen, false otherwise
bool m_isPrimary;
int m_mouseScrollDelta;
Display* m_display;
Window m_root;
Window m_window;
// true if mouse has entered the screen
bool m_isOnScreen;
// screen shape stuff
SInt32 m_x, m_y;
SInt32 m_w, m_h;
SInt32 m_xCenter, m_yCenter;
// last mouse position
SInt32 m_xCursor, m_yCursor;
// keyboard stuff
CXWindowsKeyState* m_keyState;
// hot key stuff
HotKeyMap m_hotKeys;
HotKeyIDList m_oldHotKeyIDs;
HotKeyToIDMap m_hotKeyToIDMap;
// input focus stuff
Window m_lastFocus;
int m_lastFocusRevert;
// input method stuff
XIM m_im;
XIC m_ic;
KeyCode m_lastKeycode;
CFilteredKeycodes m_filtered;
// clipboards
CXWindowsClipboard* m_clipboard[kClipboardEnd];
UInt32 m_sequenceNumber;
// screen saver stuff
CXWindowsScreenSaver* m_screensaver;
bool m_screensaverNotify;
// logical to physical button mapping. m_buttons[i] gives the
// physical button for logical button i+1.
std::vector<unsigned char> m_buttons;
// true if global auto-repeat was enabled before we turned it off
bool m_autoRepeat;
// stuff to workaround xtest being xinerama unaware. attempting
// to fake a mouse motion under xinerama may behave strangely,
// especially if screen 0 is not at 0,0 or if faking a motion on
// a screen other than screen 0.
bool m_xtestIsXineramaUnaware;
bool m_xinerama;
// stuff to work around lost focus issues on certain systems
// (ie: a MythTV front-end).
bool m_preserveFocus;
// XKB extension stuff
bool m_xkb;
int m_xkbEventBase;
bool m_xi2detected;
// XRandR extension stuff
bool m_xrandr;
int m_xrandrEventBase;
IEventQueue& m_eventQueue;
CKeyMap m_keyMap;
// pointer to (singleton) screen. this is only needed by
// ioErrorHandler().
static CXWindowsScreen* s_screen;
};
#endif

View File

@@ -0,0 +1,602 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CXWindowsScreenSaver.h"
#include "CXWindowsUtil.h"
#include "IPlatformScreen.h"
#include "CLog.h"
#include "CEvent.h"
#include "IEventQueue.h"
#include "TMethodEventJob.h"
#include <X11/Xatom.h>
#if HAVE_X11_EXTENSIONS_XTEST_H
# include <X11/extensions/XTest.h>
#else
# error The XTest extension is required to build synergy
#endif
#if HAVE_X11_EXTENSIONS_DPMS_H
extern "C" {
# include <X11/Xmd.h>
# include <X11/extensions/dpms.h>
# if !HAVE_DPMS_PROTOTYPES
# undef DPMSModeOn
# undef DPMSModeStandby
# undef DPMSModeSuspend
# undef DPMSModeOff
# define DPMSModeOn 0
# define DPMSModeStandby 1
# define DPMSModeSuspend 2
# define DPMSModeOff 3
extern Bool DPMSQueryExtension(Display *, int *, int *);
extern Bool DPMSCapable(Display *);
extern Status DPMSEnable(Display *);
extern Status DPMSDisable(Display *);
extern Status DPMSForceLevel(Display *, CARD16);
extern Status DPMSInfo(Display *, CARD16 *, BOOL *);
# endif
}
#endif
//
// CXWindowsScreenSaver
//
CXWindowsScreenSaver::CXWindowsScreenSaver(
Display* display, Window window, void* eventTarget, IEventQueue& eventQueue) :
m_display(display),
m_xscreensaverSink(window),
m_eventTarget(eventTarget),
m_xscreensaver(None),
m_xscreensaverActive(false),
m_dpms(false),
m_disabled(false),
m_suppressDisable(false),
m_disableTimer(NULL),
m_disablePos(0),
m_eventQueue(eventQueue)
{
// get atoms
m_atomScreenSaver = XInternAtom(m_display,
"SCREENSAVER", False);
m_atomScreenSaverVersion = XInternAtom(m_display,
"_SCREENSAVER_VERSION", False);
m_atomScreenSaverActivate = XInternAtom(m_display,
"ACTIVATE", False);
m_atomScreenSaverDeactivate = XInternAtom(m_display,
"DEACTIVATE", False);
// check for DPMS extension. this is an alternative screen saver
// that powers down the display.
#if HAVE_X11_EXTENSIONS_DPMS_H
int eventBase, errorBase;
if (DPMSQueryExtension(m_display, &eventBase, &errorBase)) {
if (DPMSCapable(m_display)) {
// we have DPMS
m_dpms = true;
}
}
#endif
// watch top-level windows for changes
bool error = false;
{
CXWindowsUtil::CErrorLock lock(m_display, &error);
Window root = DefaultRootWindow(m_display);
XWindowAttributes attr;
XGetWindowAttributes(m_display, root, &attr);
m_rootEventMask = attr.your_event_mask;
XSelectInput(m_display, root, m_rootEventMask | SubstructureNotifyMask);
}
if (error) {
LOG((CLOG_DEBUG "didn't set root event mask"));
m_rootEventMask = 0;
}
// get the built-in settings
XGetScreenSaver(m_display, &m_timeout, &m_interval,
&m_preferBlanking, &m_allowExposures);
// get the DPMS settings
m_dpmsEnabled = isDPMSEnabled();
// get the xscreensaver window, if any
if (!findXScreenSaver()) {
setXScreenSaver(None);
}
// install disable timer event handler
m_eventQueue.adoptHandler(CEvent::kTimer, this,
new TMethodEventJob<CXWindowsScreenSaver>(this,
&CXWindowsScreenSaver::handleDisableTimer));
}
CXWindowsScreenSaver::~CXWindowsScreenSaver()
{
// done with disable job
if (m_disableTimer != NULL) {
m_eventQueue.deleteTimer(m_disableTimer);
}
m_eventQueue.removeHandler(CEvent::kTimer, this);
if (m_display != NULL) {
enableDPMS(m_dpmsEnabled);
XSetScreenSaver(m_display, m_timeout, m_interval,
m_preferBlanking, m_allowExposures);
clearWatchForXScreenSaver();
CXWindowsUtil::CErrorLock lock(m_display);
XSelectInput(m_display, DefaultRootWindow(m_display), m_rootEventMask);
}
}
void
CXWindowsScreenSaver::destroy()
{
m_display = NULL;
delete this;
}
bool
CXWindowsScreenSaver::handleXEvent(const XEvent* xevent)
{
switch (xevent->type) {
case CreateNotify:
if (m_xscreensaver == None) {
if (isXScreenSaver(xevent->xcreatewindow.window)) {
// found the xscreensaver
setXScreenSaver(xevent->xcreatewindow.window);
}
else {
// another window to watch. to detect the xscreensaver
// window we look for a property but that property may
// not yet exist by the time we get this event so we
// have to watch the window for property changes.
// this would be so much easier if xscreensaver did the
// smart thing and stored its window in a property on
// the root window.
addWatchXScreenSaver(xevent->xcreatewindow.window);
}
}
break;
case DestroyNotify:
if (xevent->xdestroywindow.window == m_xscreensaver) {
// xscreensaver is gone
LOG((CLOG_DEBUG "xscreensaver died"));
setXScreenSaver(None);
return true;
}
break;
case PropertyNotify:
if (xevent->xproperty.state == PropertyNewValue) {
if (isXScreenSaver(xevent->xproperty.window)) {
// found the xscreensaver
setXScreenSaver(xevent->xcreatewindow.window);
}
}
break;
case MapNotify:
if (xevent->xmap.window == m_xscreensaver) {
// xscreensaver has activated
setXScreenSaverActive(true);
return true;
}
break;
case UnmapNotify:
if (xevent->xunmap.window == m_xscreensaver) {
// xscreensaver has deactivated
setXScreenSaverActive(false);
return true;
}
break;
}
return false;
}
void
CXWindowsScreenSaver::enable()
{
// for xscreensaver
m_disabled = false;
updateDisableTimer();
// for built-in X screen saver
XSetScreenSaver(m_display, m_timeout, m_interval,
m_preferBlanking, m_allowExposures);
// for DPMS
enableDPMS(m_dpmsEnabled);
}
void
CXWindowsScreenSaver::disable()
{
// for xscreensaver
m_disabled = true;
updateDisableTimer();
// use built-in X screen saver
XGetScreenSaver(m_display, &m_timeout, &m_interval,
&m_preferBlanking, &m_allowExposures);
XSetScreenSaver(m_display, 0, m_interval,
m_preferBlanking, m_allowExposures);
// for DPMS
m_dpmsEnabled = isDPMSEnabled();
enableDPMS(false);
// FIXME -- now deactivate?
}
void
CXWindowsScreenSaver::activate()
{
// remove disable job timer
m_suppressDisable = true;
updateDisableTimer();
// enable DPMS if it was enabled
enableDPMS(m_dpmsEnabled);
// try xscreensaver
findXScreenSaver();
if (m_xscreensaver != None) {
sendXScreenSaverCommand(m_atomScreenSaverActivate);
return;
}
// try built-in X screen saver
if (m_timeout != 0) {
XForceScreenSaver(m_display, ScreenSaverActive);
}
// try DPMS
activateDPMS(true);
}
void
CXWindowsScreenSaver::deactivate()
{
// reinstall disable job timer
m_suppressDisable = false;
updateDisableTimer();
// try DPMS
activateDPMS(false);
// disable DPMS if screen saver is disabled
if (m_disabled) {
enableDPMS(false);
}
// try xscreensaver
findXScreenSaver();
if (m_xscreensaver != None) {
sendXScreenSaverCommand(m_atomScreenSaverDeactivate);
return;
}
// use built-in X screen saver
XForceScreenSaver(m_display, ScreenSaverReset);
}
bool
CXWindowsScreenSaver::isActive() const
{
// check xscreensaver
if (m_xscreensaver != None) {
return m_xscreensaverActive;
}
// check DPMS
if (isDPMSActivated()) {
return true;
}
// can't check built-in X screen saver activity
return false;
}
bool
CXWindowsScreenSaver::findXScreenSaver()
{
// do nothing if we've already got the xscreensaver window
if (m_xscreensaver == None) {
// find top-level window xscreensaver window
Window root = DefaultRootWindow(m_display);
Window rw, pw, *cw;
unsigned int nc;
if (XQueryTree(m_display, root, &rw, &pw, &cw, &nc)) {
for (unsigned int i = 0; i < nc; ++i) {
if (isXScreenSaver(cw[i])) {
setXScreenSaver(cw[i]);
break;
}
}
XFree(cw);
}
}
return (m_xscreensaver != None);
}
void
CXWindowsScreenSaver::setXScreenSaver(Window window)
{
LOG((CLOG_DEBUG "xscreensaver window: 0x%08x", window));
// save window
m_xscreensaver = window;
if (m_xscreensaver != None) {
// clear old watch list
clearWatchForXScreenSaver();
// see if xscreensaver is active
bool error = false;
XWindowAttributes attr;
{
CXWindowsUtil::CErrorLock lock(m_display, &error);
XGetWindowAttributes(m_display, m_xscreensaver, &attr);
}
setXScreenSaverActive(!error && attr.map_state != IsUnmapped);
// save current DPMS state; xscreensaver may have changed it.
m_dpmsEnabled = isDPMSEnabled();
}
else {
// screen saver can't be active if it doesn't exist
setXScreenSaverActive(false);
// start watching for xscreensaver
watchForXScreenSaver();
}
}
bool
CXWindowsScreenSaver::isXScreenSaver(Window w) const
{
// check for m_atomScreenSaverVersion string property
Atom type;
return (CXWindowsUtil::getWindowProperty(m_display, w,
m_atomScreenSaverVersion,
NULL, &type, NULL, False) &&
type == XA_STRING);
}
void
CXWindowsScreenSaver::setXScreenSaverActive(bool activated)
{
if (m_xscreensaverActive != activated) {
LOG((CLOG_DEBUG "xscreensaver %s on window 0x%08x", activated ? "activated" : "deactivated", m_xscreensaver));
m_xscreensaverActive = activated;
// if screen saver was activated forcefully (i.e. against
// our will) then just accept it. don't try to keep it
// from activating since that'll just pop up the password
// dialog if locking is enabled.
m_suppressDisable = activated;
updateDisableTimer();
if (activated) {
m_eventQueue.addEvent(CEvent(
IPlatformScreen::getScreensaverActivatedEvent(),
m_eventTarget));
}
else {
m_eventQueue.addEvent(CEvent(
IPlatformScreen::getScreensaverDeactivatedEvent(),
m_eventTarget));
}
}
}
void
CXWindowsScreenSaver::sendXScreenSaverCommand(Atom cmd, long arg1, long arg2)
{
XEvent event;
event.xclient.type = ClientMessage;
event.xclient.display = m_display;
event.xclient.window = m_xscreensaverSink;
event.xclient.message_type = m_atomScreenSaver;
event.xclient.format = 32;
event.xclient.data.l[0] = static_cast<long>(cmd);
event.xclient.data.l[1] = arg1;
event.xclient.data.l[2] = arg2;
event.xclient.data.l[3] = 0;
event.xclient.data.l[4] = 0;
LOG((CLOG_DEBUG "send xscreensaver command: %d %d %d", (long)cmd, arg1, arg2));
bool error = false;
{
CXWindowsUtil::CErrorLock lock(m_display, &error);
XSendEvent(m_display, m_xscreensaver, False, 0, &event);
}
if (error) {
findXScreenSaver();
}
}
void
CXWindowsScreenSaver::watchForXScreenSaver()
{
// clear old watch list
clearWatchForXScreenSaver();
// add every child of the root to the list of windows to watch
Window root = DefaultRootWindow(m_display);
Window rw, pw, *cw;
unsigned int nc;
if (XQueryTree(m_display, root, &rw, &pw, &cw, &nc)) {
for (unsigned int i = 0; i < nc; ++i) {
addWatchXScreenSaver(cw[i]);
}
XFree(cw);
}
// now check for xscreensaver window in case it set the property
// before we could request property change events.
if (findXScreenSaver()) {
// found it so clear out our watch list
clearWatchForXScreenSaver();
}
}
void
CXWindowsScreenSaver::clearWatchForXScreenSaver()
{
// stop watching all windows
CXWindowsUtil::CErrorLock lock(m_display);
for (CWatchList::iterator index = m_watchWindows.begin();
index != m_watchWindows.end(); ++index) {
XSelectInput(m_display, index->first, index->second);
}
m_watchWindows.clear();
}
void
CXWindowsScreenSaver::addWatchXScreenSaver(Window window)
{
// get window attributes
bool error = false;
XWindowAttributes attr;
{
CXWindowsUtil::CErrorLock lock(m_display, &error);
XGetWindowAttributes(m_display, window, &attr);
}
// if successful and window uses override_redirect (like xscreensaver
// does) then watch it for property changes.
if (!error && attr.override_redirect == True) {
error = false;
{
CXWindowsUtil::CErrorLock lock(m_display, &error);
XSelectInput(m_display, window,
attr.your_event_mask | PropertyChangeMask);
}
if (!error) {
// if successful then add the window to our list
m_watchWindows.insert(std::make_pair(window, attr.your_event_mask));
}
}
}
void
CXWindowsScreenSaver::updateDisableTimer()
{
if (m_disabled && !m_suppressDisable && m_disableTimer == NULL) {
// 5 seconds should be plenty often to suppress the screen saver
m_disableTimer = m_eventQueue.newTimer(5.0, this);
}
else if ((!m_disabled || m_suppressDisable) && m_disableTimer != NULL) {
m_eventQueue.deleteTimer(m_disableTimer);
m_disableTimer = NULL;
}
}
void
CXWindowsScreenSaver::handleDisableTimer(const CEvent&, void*)
{
// send fake mouse motion directly to xscreensaver
if (m_xscreensaver != None) {
XEvent event;
event.xmotion.type = MotionNotify;
event.xmotion.display = m_display;
event.xmotion.window = m_xscreensaver;
event.xmotion.root = DefaultRootWindow(m_display);
event.xmotion.subwindow = None;
event.xmotion.time = CurrentTime;
event.xmotion.x = m_disablePos;
event.xmotion.y = 0;
event.xmotion.x_root = m_disablePos;
event.xmotion.y_root = 0;
event.xmotion.state = 0;
event.xmotion.is_hint = NotifyNormal;
event.xmotion.same_screen = True;
CXWindowsUtil::CErrorLock lock(m_display);
XSendEvent(m_display, m_xscreensaver, False, 0, &event);
m_disablePos = 20 - m_disablePos;
}
}
void
CXWindowsScreenSaver::activateDPMS(bool activate)
{
#if HAVE_X11_EXTENSIONS_DPMS_H
if (m_dpms) {
// DPMSForceLevel will generate a BadMatch if DPMS is disabled
CXWindowsUtil::CErrorLock lock(m_display);
DPMSForceLevel(m_display, activate ? DPMSModeStandby : DPMSModeOn);
}
#endif
}
void
CXWindowsScreenSaver::enableDPMS(bool enable)
{
#if HAVE_X11_EXTENSIONS_DPMS_H
if (m_dpms) {
if (enable) {
DPMSEnable(m_display);
}
else {
DPMSDisable(m_display);
}
}
#endif
}
bool
CXWindowsScreenSaver::isDPMSEnabled() const
{
#if HAVE_X11_EXTENSIONS_DPMS_H
if (m_dpms) {
CARD16 level;
BOOL state;
DPMSInfo(m_display, &level, &state);
return (state != False);
}
else {
return false;
}
#else
return false;
#endif
}
bool
CXWindowsScreenSaver::isDPMSActivated() const
{
#if HAVE_X11_EXTENSIONS_DPMS_H
if (m_dpms) {
CARD16 level;
BOOL state;
DPMSInfo(m_display, &level, &state);
return (level != DPMSModeOn);
}
else {
return false;
}
#else
return false;
#endif
}

View File

@@ -0,0 +1,170 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CXWINDOWSSCREENSAVER_H
#define CXWINDOWSSCREENSAVER_H
#include "IScreenSaver.h"
#include "stdmap.h"
#if X_DISPLAY_MISSING
# error X11 is required to build synergy
#else
# include <X11/Xlib.h>
#endif
#include "IEventQueue.h"
class CEvent;
class CEventQueueTimer;
//! X11 screen saver implementation
class CXWindowsScreenSaver : public IScreenSaver {
public:
CXWindowsScreenSaver(Display*, Window, void* eventTarget, IEventQueue& eventQueue);
virtual ~CXWindowsScreenSaver();
//! @name manipulators
//@{
//! Event filtering
/*!
Should be called for each system event before event translation and
dispatch. Returns true to skip translation and dispatch.
*/
bool handleXEvent(const XEvent*);
//! Destroy without the display
/*!
Tells this object to delete itself without using the X11 display.
It may leak some resources as a result.
*/
void destroy();
//@}
// IScreenSaver overrides
virtual void enable();
virtual void disable();
virtual void activate();
virtual void deactivate();
virtual bool isActive() const;
private:
// find and set the running xscreensaver's window. returns true iff
// found.
bool findXScreenSaver();
// set the xscreensaver's window, updating the activation state flag
void setXScreenSaver(Window);
// returns true if the window appears to be the xscreensaver window
bool isXScreenSaver(Window) const;
// set xscreensaver's activation state flag. sends notification
// if the state has changed.
void setXScreenSaverActive(bool activated);
// send a command to xscreensaver
void sendXScreenSaverCommand(Atom, long = 0, long = 0);
// watch all windows that could potentially be the xscreensaver for
// the events that will confirm it.
void watchForXScreenSaver();
// stop watching all watched windows
void clearWatchForXScreenSaver();
// add window to the watch list
void addWatchXScreenSaver(Window window);
// install/uninstall the job used to suppress the screensaver
void updateDisableTimer();
// called periodically to prevent the screen saver from starting
void handleDisableTimer(const CEvent&, void*);
// force DPMS to activate or deactivate
void activateDPMS(bool activate);
// enable/disable DPMS screen saver
void enableDPMS(bool);
// check if DPMS is enabled
bool isDPMSEnabled() const;
// check if DPMS is activate
bool isDPMSActivated() const;
private:
typedef std::map<Window, long> CWatchList;
// the X display
Display* m_display;
// window to receive xscreensaver repsonses
Window m_xscreensaverSink;
// the target for the events we generate
void* m_eventTarget;
// xscreensaver's window
Window m_xscreensaver;
// xscreensaver activation state
bool m_xscreensaverActive;
// old event mask on root window
long m_rootEventMask;
// potential xscreensaver windows being watched
CWatchList m_watchWindows;
// atoms used to communicate with xscreensaver's window
Atom m_atomScreenSaver;
Atom m_atomScreenSaverVersion;
Atom m_atomScreenSaverActivate;
Atom m_atomScreenSaverDeactivate;
// built-in screen saver settings
int m_timeout;
int m_interval;
int m_preferBlanking;
int m_allowExposures;
// DPMS screen saver settings
bool m_dpms;
bool m_dpmsEnabled;
// true iff the client wants the screen saver suppressed
bool m_disabled;
// true iff we're ignoring m_disabled. this is true, for example,
// when the client has called activate() and so presumably wants
// to activate the screen saver even if disabled.
bool m_suppressDisable;
// the disable timer (NULL if not installed)
CEventQueueTimer* m_disableTimer;
// fake mouse motion position for suppressing the screen saver.
// xscreensaver since 2.21 requires the mouse to move more than 10
// pixels to be considered significant.
SInt32 m_disablePos;
IEventQueue& m_eventQueue;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,188 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CXWINDOWSUTIL_H
#define CXWINDOWSUTIL_H
#include "CString.h"
#include "BasicTypes.h"
#include "stdmap.h"
#include "stdvector.h"
#if X_DISPLAY_MISSING
# error X11 is required to build synergy
#else
# include <X11/Xlib.h>
#endif
//! X11 utility functions
class CXWindowsUtil {
public:
typedef std::vector<KeySym> KeySyms;
//! Get property
/*!
Gets property \c property on \c window. \b Appends the data to
\c *data if \c data is not NULL, saves the property type in \c *type
if \c type is not NULL, and saves the property format in \c *format
if \c format is not NULL. If \c deleteProperty is true then the
property is deleted after being read.
*/
static bool getWindowProperty(Display*,
Window window, Atom property,
CString* data, Atom* type,
SInt32* format, bool deleteProperty);
//! Set property
/*!
Sets property \c property on \c window to \c size bytes of data from
\c data.
*/
static bool setWindowProperty(Display*,
Window window, Atom property,
const void* data, UInt32 size,
Atom type, SInt32 format);
//! Get X server time
/*!
Returns the current X server time.
*/
static Time getCurrentTime(Display*, Window);
//! Convert KeySym to KeyID
/*!
Converts a KeySym to the equivalent KeyID. Returns kKeyNone if the
KeySym cannot be mapped.
*/
static UInt32 mapKeySymToKeyID(KeySym);
//! Convert KeySym to corresponding KeyModifierMask
/*!
Converts a KeySym to the corresponding KeyModifierMask, or 0 if the
KeySym is not a modifier.
*/
static UInt32 getModifierBitForKeySym(KeySym keysym);
//! Convert Atom to its string
/*!
Converts \p atom to its string representation.
*/
static CString atomToString(Display*, Atom atom);
//! Convert several Atoms to a string
/*!
Converts each atom in \p atoms to its string representation and
concatenates the results.
*/
static CString atomsToString(Display* display,
const Atom* atom, UInt32 num);
//! Prepare a property of atoms for use
/*!
64-bit systems may need to modify a property's data if it's a
list of Atoms before using it.
*/
static void convertAtomProperty(CString& data);
//! Append an Atom to property data
/*!
Converts \p atom to a 32-bit on-the-wire format and appends it to
\p data.
*/
static void appendAtomData(CString& data, Atom atom);
//! Replace an Atom in property data
/*!
Converts \p atom to a 32-bit on-the-wire format and replaces the atom
at index \p index in \p data.
*/
static void replaceAtomData(CString& data,
UInt32 index, Atom atom);
//! Append an Time to property data
/*!
Converts \p time to a 32-bit on-the-wire format and appends it to
\p data.
*/
static void appendTimeData(CString& data, Time time);
//! X11 error handler
/*!
This class sets an X error handler in the c'tor and restores the
previous error handler in the d'tor. A lock should only be
installed while the display is locked by the thread.
CErrorLock() ignores errors
CErrorLock(bool* flag) sets *flag to true if any error occurs
*/
class CErrorLock {
public:
//! Error handler type
typedef void (*ErrorHandler)(Display*, XErrorEvent*, void* userData);
/*!
Ignore X11 errors.
*/
CErrorLock(Display*);
/*!
Set \c *errorFlag if any error occurs.
*/
CErrorLock(Display*, bool* errorFlag);
/*!
Call \c handler on each error.
*/
CErrorLock(Display*, ErrorHandler handler, void* userData);
~CErrorLock();
private:
void install(ErrorHandler, void*);
static int internalHandler(Display*, XErrorEvent*);
static void ignoreHandler(Display*, XErrorEvent*, void*);
static void saveHandler(Display*, XErrorEvent*, void*);
private:
typedef int (*XErrorHandler)(Display*, XErrorEvent*);
Display* m_display;
ErrorHandler m_handler;
void* m_userData;
XErrorHandler m_oldXHandler;
CErrorLock* m_next;
static CErrorLock* s_top;
};
private:
class CPropertyNotifyPredicateInfo {
public:
Window m_window;
Atom m_property;
};
static Bool propertyNotifyPredicate(Display*,
XEvent* xevent, XPointer arg);
static void initKeyMaps();
private:
typedef std::map<KeySym, UInt32> CKeySymMap;
static CKeySymMap s_keySymToUCS4;
};
#endif

View File

@@ -0,0 +1,341 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2011 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*--------------------------------------------------------------------------------------------------------
Original comment:
APIHIJACK.CPP - Based on DelayLoadProfileDLL.CPP, by Matt Pietrek for MSJ February 2000.
http://msdn.microsoft.com/library/periodic/period00/hood0200.htm
Adapted by Wade Brainerd, wadeb@wadeb.com
--------------------------------------------------------------------------------------------------------*/
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include "HookDLL.h"
#include <sstream>
std::stringstream _hookDllLogStream;
#define LOG(s) \
_hookDllLogStream.str(""); \
_hookDllLogStream << "Synergy HookDLL: " << s << std::endl; \
OutputDebugString( _hookDllLogStream.str().c_str() );
//===========================================================================
// Called from the DLPD_IAT_STUB stubs. Increments "count" field of the stub
void __cdecl DefaultHook( PVOID dummy )
{
// asm only supported on 32-bit
#ifdef _M_IX86
__asm pushad // Save all general purpose registers
// Get return address, then subtract 5 (size of a CALL X instruction)
// The result points at a DLPD_IAT_STUB
// pointer math! &dummy-1 really subtracts sizeof(PVOID)
PDWORD pRetAddr = (PDWORD)(&dummy - 1);
DLPD_IAT_STUB * pDLPDStub = (DLPD_IAT_STUB *)(*pRetAddr - 5);
pDLPDStub->count++;
#if 0
// Remove the above conditional to get a cheezy API trace from
// the loader process. It's slow!
if ( !IMAGE_SNAP_BY_ORDINAL( pDLPDStub->pszNameOrOrdinal) )
{
LOG( "Called hooked function: " );
LOG( (PSTR)pDLPDStub->pszNameOrOrdinal );
}
#endif
__asm popad // Restore all general purpose registers
#endif
}
// This function must be __cdecl!!!
void __cdecl DelayLoadProfileDLL_UpdateCount( PVOID dummy );
PIMAGE_IMPORT_DESCRIPTOR g_pFirstImportDesc;
//===========================================================================
// Given an HMODULE, returns a pointer to the PE header
PIMAGE_NT_HEADERS PEHeaderFromHModule(HMODULE hModule)
{
PIMAGE_NT_HEADERS pNTHeader = 0;
__try
{
if ( PIMAGE_DOS_HEADER(hModule)->e_magic != IMAGE_DOS_SIGNATURE )
__leave;
pNTHeader = PIMAGE_NT_HEADERS(PBYTE(hModule)
+ PIMAGE_DOS_HEADER(hModule)->e_lfanew);
if ( pNTHeader->Signature != IMAGE_NT_SIGNATURE )
pNTHeader = 0;
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
}
return pNTHeader;
}
//===========================================================================
// Builds stubs for and redirects the IAT for one DLL (pImportDesc)
bool RedirectIAT( SDLLHook* DLLHook, PIMAGE_IMPORT_DESCRIPTOR pImportDesc, PVOID pBaseLoadAddr )
{
PIMAGE_THUNK_DATA pIAT; // Ptr to import address table
PIMAGE_THUNK_DATA pINT; // Ptr to import names table
PIMAGE_THUNK_DATA pIteratingIAT;
// Figure out which OS platform we're on
OSVERSIONINFO osvi;
osvi.dwOSVersionInfoSize = sizeof(osvi);
GetVersionEx( &osvi );
// If no import names table, we can't redirect this, so bail
if ( pImportDesc->OriginalFirstThunk == 0 )
{
LOG( "no IAT available." );
return false;
}
pIAT = MakePtr( PIMAGE_THUNK_DATA, pBaseLoadAddr, pImportDesc->FirstThunk );
pINT = MakePtr( PIMAGE_THUNK_DATA, pBaseLoadAddr, pImportDesc->OriginalFirstThunk );
// Count how many entries there are in this IAT. Array is 0 terminated
pIteratingIAT = pIAT;
unsigned cFuncs = 0;
while ( pIteratingIAT->u1.Function )
{
cFuncs++;
pIteratingIAT++;
}
if ( cFuncs == 0 ) // If no imported functions, we're done!
{
LOG( "no imported functions" );
return false;
}
// These next few lines ensure that we'll be able to modify the IAT,
// which is often in a read-only section in the EXE.
DWORD flOldProtect, flNewProtect, flDontCare;
MEMORY_BASIC_INFORMATION mbi;
// Get the current protection attributes
VirtualQuery( pIAT, &mbi, sizeof(mbi) );
// remove ReadOnly and ExecuteRead attributes, add on ReadWrite flag
flNewProtect = mbi.Protect;
flNewProtect &= ~(PAGE_READONLY | PAGE_EXECUTE_READ);
flNewProtect |= (PAGE_READWRITE);
if ( !VirtualProtect( pIAT, sizeof(PVOID) * cFuncs,
flNewProtect, &flOldProtect) )
{
LOG( "could not remove ReadOnly and ExecuteRead attributes, or add ReadWrite flag" );
return false;
}
// If the Default hook is enabled, build an array of redirection stubs in the processes memory.
DLPD_IAT_STUB * pStubs = 0;
if ( DLLHook->UseDefault )
{
// Allocate memory for the redirection stubs. Make one extra stub at the
// end to be a sentinel
pStubs = new DLPD_IAT_STUB[ cFuncs + 1];
if ( !pStubs )
{
LOG( "could not allocate memory for redirection stubs" );
return false;
}
}
// Scan through the IAT, completing the stubs and redirecting the IAT
// entries to point to the stubs
pIteratingIAT = pIAT;
while ( pIteratingIAT->u1.Function )
{
void* HookFn = 0; // Set to either the SFunctionHook or pStubs.
if ( !IMAGE_SNAP_BY_ORDINAL( pINT->u1.Ordinal ) ) // import by name
{
PIMAGE_IMPORT_BY_NAME pImportName = MakePtr( PIMAGE_IMPORT_BY_NAME, pBaseLoadAddr, pINT->u1.AddressOfData );
LOG( "checking function with name: " << pImportName->Name );
// Iterate through the hook functions, searching for this import.
SFunctionHook* FHook = DLLHook->Functions;
while ( FHook->Name )
{
if ( lstrcmpi( FHook->Name, (char*)pImportName->Name ) == 0 )
{
// Save the old function in the SFunctionHook structure and get the new one.
FHook->OrigFn = (void*)pIteratingIAT->u1.Function;
HookFn = FHook->HookFn;
LOG( "hooked function: " << pImportName->Name );
break;
}
FHook++;
}
// If the default function is enabled, store the name for the user.
if ( DLLHook->UseDefault )
pStubs->pszNameOrOrdinal = (DWORD)&pImportName->Name;
}
else // added comparison for ordinal
{
LOG( "checking function at ordinal: " << pINT->u1.Ordinal );
SFunctionHook* FHook = DLLHook->Functions;
while ( FHook->Name )
{
if ( (DWORD)FHook->Name == pINT->u1.Ordinal )
{
// Save the old function in the SFunctionHook structure and get the new one.
FHook->OrigFn = (void*)pIteratingIAT->u1.Function;
HookFn = FHook->HookFn;
LOG( "hooked ordinal: " << pINT->u1.Ordinal );
break;
}
FHook++;
}
// If the default function is enabled, store the ordinal for the user.
if ( DLLHook->UseDefault )
pStubs->pszNameOrOrdinal = (DWORD)pINT->u1.Ordinal;
}
// If the default function is enabled, fill in the fields to the stub code.
if ( DLLHook->UseDefault )
{
pStubs->data_call = (DWORD)(PDWORD)DLLHook->DefaultFn
- (DWORD)(PDWORD)&pStubs->instr_JMP;
pStubs->data_JMP = *(PDWORD)pIteratingIAT - (DWORD)(PDWORD)&pStubs->count;
// If it wasn't manually hooked, use the Stub function.
if ( !HookFn )
HookFn = (void*)pStubs;
}
// Replace the IAT function pointer if we have a hook.
if ( HookFn )
{
// Cheez-o hack to see if what we're importing is code or data.
// If it's code, we shouldn't be able to write to it
if ( IsBadWritePtr( (PVOID)pIteratingIAT->u1.Function, 1 ) )
{
pIteratingIAT->u1.Function = (DWORD)(PDWORD)HookFn;
}
else if ( osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS )
{
// Special hack for Win9X, which builds stubs for imported
// functions in system DLLs (Loaded above 2GB). These stubs are
// writeable, so we have to explicitly check for this case
if ( pIteratingIAT->u1.Function > (DWORD)(PDWORD)0x80000000 )
pIteratingIAT->u1.Function = (DWORD)(PDWORD)HookFn;
}
}
if ( DLLHook->UseDefault )
pStubs++; // Advance to next stub
pIteratingIAT++; // Advance to next IAT entry
pINT++; // Advance to next INT entry
}
if ( DLLHook->UseDefault )
pStubs->pszNameOrOrdinal = 0; // Final stub is a sentinel
// Put the page attributes back the way they were.
VirtualProtect( pIAT, sizeof(PVOID) * cFuncs, flOldProtect, &flDontCare);
return true;
}
//===========================================================================
// Top level routine to find the EXE's imports, and redirect them
bool HookAPICalls( SDLLHook* hook )
{
if ( !hook )
{
LOG("no hook");
return false;
}
HMODULE hModEXE = GetModuleHandle( 0 );
PIMAGE_NT_HEADERS pExeNTHdr = PEHeaderFromHModule( hModEXE );
if ( !pExeNTHdr )
{
LOG("no PE header");
return false;
}
DWORD importRVA = pExeNTHdr->OptionalHeader.DataDirectory
[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
if ( !importRVA )
{
LOG("no virtual address for image directory entry import");
return false;
}
// Convert imports RVA to a usable pointer
PIMAGE_IMPORT_DESCRIPTOR pImportDesc = MakePtr( PIMAGE_IMPORT_DESCRIPTOR,
hModEXE, importRVA );
// Save off imports address in a global for later use
g_pFirstImportDesc = pImportDesc;
// Iterate through each import descriptor, and redirect if appropriate
while ( pImportDesc->FirstThunk )
{
PSTR pszImportModuleName = MakePtr( PSTR, hModEXE, pImportDesc->Name);
if ( lstrcmpi( pszImportModuleName, hook->Name ) == 0 )
{
LOG( "found " << hook->Name << " in process" );
if ( RedirectIAT( hook, pImportDesc, (PVOID)hModEXE ) )
{
LOG( "redirected IAT ok" );
return true;
}
else
{
LOG( "failed to redirect IAT" );
}
}
pImportDesc++; // Advance to next import descriptor
}
return false;
}

View File

@@ -0,0 +1,80 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2011 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*--------------------------------------------------------------------------------------------------------
Original comment:
APIHIJACK.H - Based on DelayLoadProfileDLL.CPP, by Matt Pietrek for MSJ February 2000.
http://msdn.microsoft.com/library/periodic/period00/hood0200.htm
Adapted by Wade Brainerd, wadeb@wadeb.com
--------------------------------------------------------------------------------------------------------*/
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#pragma warning(disable:4200)
// Macro for convenient pointer addition.
// Essentially treats the last two parameters as DWORDs. The first
// parameter is used to typecast the result to the appropriate pointer type.
#define MakePtr(cast, ptr, addValue ) (cast)( (DWORD)(ptr)+(DWORD)(addValue))
// Default Hook Stub Structure: Contains data about the original function, Name/Ordinal, Address
// and a Count field. This is actually a block of assembly code.
#pragma pack( push, 1 )
struct DLPD_IAT_STUB
{
BYTE instr_CALL;
DWORD data_call;
BYTE instr_JMP;
DWORD data_JMP;
DWORD count;
DWORD pszNameOrOrdinal;
DLPD_IAT_STUB() : instr_CALL( 0xE8 ), instr_JMP( 0xE9 ), count( 0 ) {}
};
#pragma pack( pop )
// Example DefaultHook procedure, called from the DLPD_IAT_STUB stubs.
// Increments "count" field of the stub.
// See the implementation for more information.
void __cdecl DefaultHook( PVOID dummy );
struct SFunctionHook
{
char* Name; // Function name, e.g. "DirectDrawCreateEx".
void* HookFn; // Address of your function.
void* OrigFn; // Stored by HookAPICalls, the address of the original function.
};
struct SDLLHook
{
// Name of the DLL, e.g. "DDRAW.DLL"
char* Name;
// Set true to call the default for all non-hooked functions before they are executed.
bool UseDefault;
void* DefaultFn;
// Function hook array. Terminated with a NULL Name field.
SFunctionHook Functions[];
};
// Hook functions one or more DLLs.
bool HookAPICalls( SDLLHook* hook );

View File

@@ -0,0 +1,34 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2002 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef IMWINDOWSCLIPBOARDFACADE
#define IMWINDOWSCLIPBOARDFACADE
#include "IInterface.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
class IMSWindowsClipboardConverter;
class IMSWindowsClipboardFacade : public IInterface
{
public:
virtual void write(HANDLE win32Data, UINT win32Format) = 0;
virtual ~IMSWindowsClipboardFacade() { }
};
#endif

View File

@@ -0,0 +1,53 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2009 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// ScreenSaver.framework private API
// Class dumping by Alex Harper http://www.ragingmenace.com/
#import <Foundation/NSObject.h>
@protocol ScreenSaverControl
- (double)screenSaverTimeRemaining;
- (void)restartForUser:fp12;
- (void)screenSaverStopNow;
- (void)screenSaverStartNow;
- (void)setScreenSaverCanRun:(char)fp12;
- (BOOL)screenSaverCanRun;
- (BOOL)screenSaverIsRunning;
@end
@interface ScreenSaverController:NSObject <ScreenSaverControl>
+ controller;
+ monitor;
+ daemonConnectionName;
+ daemonPath;
+ enginePath;
- init;
- (void)dealloc;
- (void)_connectionClosed:fp12;
- (BOOL)screenSaverIsRunning;
- (BOOL)screenSaverCanRun;
- (void)setScreenSaverCanRun:(char)fp12;
- (void)screenSaverStartNow;
- (void)screenSaverStopNow;
- (void)restartForUser:fp12;
- (double)screenSaverTimeRemaining;
@end

228
src/lib/platform/XInput13.h Normal file
View File

@@ -0,0 +1,228 @@
/***************************************************************************
* *
* XInput.h -- This module defines XBOX controller APIs *
* and constansts for the Windows platform. *
* *
* Copyright (c) Microsoft Corp. All rights reserved. *
* *
***************************************************************************/
#ifndef _XINPUT_H_
#define _XINPUT_H_
#include <windef.h>
// Current name of the DLL shipped in the same SDK as this header.
// The name reflects the current version
#ifndef XINPUT_USE_9_1_0
#define XINPUT_DLL_A "xinput1_3.dll"
#define XINPUT_DLL_W L"xinput1_3.dll"
#else
#define XINPUT_DLL_A "xinput9_1_0.dll"
#define XINPUT_DLL_W L"xinput9_1_0.dll"
#endif
#ifdef UNICODE
#define XINPUT_DLL XINPUT_DLL_W
#else
#define XINPUT_DLL XINPUT_DLL_A
#endif
//
// Device types available in XINPUT_CAPABILITIES
//
#define XINPUT_DEVTYPE_GAMEPAD 0x01
//
// Device subtypes available in XINPUT_CAPABILITIES
//
#define XINPUT_DEVSUBTYPE_GAMEPAD 0x01
#ifndef XINPUT_USE_9_1_0
#define XINPUT_DEVSUBTYPE_WHEEL 0x02
#define XINPUT_DEVSUBTYPE_ARCADE_STICK 0x03
#define XINPUT_DEVSUBTYPE_FLIGHT_SICK 0x04
#define XINPUT_DEVSUBTYPE_DANCE_PAD 0x05
#define XINPUT_DEVSUBTYPE_GUITAR 0x06
#define XINPUT_DEVSUBTYPE_DRUM_KIT 0x08
#endif // !XINPUT_USE_9_1_0
//
// Flags for XINPUT_CAPABILITIES
//
#define XINPUT_CAPS_VOICE_SUPPORTED 0x0004
//
// Constants for gamepad buttons
//
#define XINPUT_GAMEPAD_DPAD_UP 0x0001
#define XINPUT_GAMEPAD_DPAD_DOWN 0x0002
#define XINPUT_GAMEPAD_DPAD_LEFT 0x0004
#define XINPUT_GAMEPAD_DPAD_RIGHT 0x0008
#define XINPUT_GAMEPAD_START 0x0010
#define XINPUT_GAMEPAD_BACK 0x0020
#define XINPUT_GAMEPAD_LEFT_THUMB 0x0040
#define XINPUT_GAMEPAD_RIGHT_THUMB 0x0080
#define XINPUT_GAMEPAD_LEFT_SHOULDER 0x0100
#define XINPUT_GAMEPAD_RIGHT_SHOULDER 0x0200
#define XINPUT_GAMEPAD_A 0x1000
#define XINPUT_GAMEPAD_B 0x2000
#define XINPUT_GAMEPAD_X 0x4000
#define XINPUT_GAMEPAD_Y 0x8000
//
// Gamepad thresholds
//
#define XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE 7849
#define XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE 8689
#define XINPUT_GAMEPAD_TRIGGER_THRESHOLD 30
//
// Flags to pass to XInputGetCapabilities
//
#define XINPUT_FLAG_GAMEPAD 0x00000001
#ifndef XINPUT_USE_9_1_0
//
// Devices that support batteries
//
#define BATTERY_DEVTYPE_GAMEPAD 0x00
#define BATTERY_DEVTYPE_HEADSET 0x01
//
// Flags for battery status level
//
#define BATTERY_TYPE_DISCONNECTED 0x00 // This device is not connected
#define BATTERY_TYPE_WIRED 0x01 // Wired device, no battery
#define BATTERY_TYPE_ALKALINE 0x02 // Alkaline battery source
#define BATTERY_TYPE_NIMH 0x03 // Nickel Metal Hydride battery source
#define BATTERY_TYPE_UNKNOWN 0xFF // Cannot determine the battery type
// These are only valid for wireless, connected devices, with known battery types
// The amount of use time remaining depends on the type of device.
#define BATTERY_LEVEL_EMPTY 0x00
#define BATTERY_LEVEL_LOW 0x01
#define BATTERY_LEVEL_MEDIUM 0x02
#define BATTERY_LEVEL_FULL 0x03
// User index definitions
#define XUSER_MAX_COUNT 4
#define XUSER_INDEX_ANY 0x000000FF
//
// Codes returned for the gamepad keystroke
//
#define VK_PAD_A 0x5800
#define VK_PAD_B 0x5801
#define VK_PAD_X 0x5802
#define VK_PAD_Y 0x5803
#define VK_PAD_RSHOULDER 0x5804
#define VK_PAD_LSHOULDER 0x5805
#define VK_PAD_LTRIGGER 0x5806
#define VK_PAD_RTRIGGER 0x5807
#define VK_PAD_DPAD_UP 0x5810
#define VK_PAD_DPAD_DOWN 0x5811
#define VK_PAD_DPAD_LEFT 0x5812
#define VK_PAD_DPAD_RIGHT 0x5813
#define VK_PAD_START 0x5814
#define VK_PAD_BACK 0x5815
#define VK_PAD_LTHUMB_PRESS 0x5816
#define VK_PAD_RTHUMB_PRESS 0x5817
#define VK_PAD_LTHUMB_UP 0x5820
#define VK_PAD_LTHUMB_DOWN 0x5821
#define VK_PAD_LTHUMB_RIGHT 0x5822
#define VK_PAD_LTHUMB_LEFT 0x5823
#define VK_PAD_LTHUMB_UPLEFT 0x5824
#define VK_PAD_LTHUMB_UPRIGHT 0x5825
#define VK_PAD_LTHUMB_DOWNRIGHT 0x5826
#define VK_PAD_LTHUMB_DOWNLEFT 0x5827
#define VK_PAD_RTHUMB_UP 0x5830
#define VK_PAD_RTHUMB_DOWN 0x5831
#define VK_PAD_RTHUMB_RIGHT 0x5832
#define VK_PAD_RTHUMB_LEFT 0x5833
#define VK_PAD_RTHUMB_UPLEFT 0x5834
#define VK_PAD_RTHUMB_UPRIGHT 0x5835
#define VK_PAD_RTHUMB_DOWNRIGHT 0x5836
#define VK_PAD_RTHUMB_DOWNLEFT 0x5837
//
// Flags used in XINPUT_KEYSTROKE
//
#define XINPUT_KEYSTROKE_KEYDOWN 0x0001
#define XINPUT_KEYSTROKE_KEYUP 0x0002
#define XINPUT_KEYSTROKE_REPEAT 0x0004
#endif //!XINPUT_USE_9_1_0
//
// Structures used by XInput APIs
//
typedef struct _XINPUT_GAMEPAD
{
WORD wButtons;
BYTE bLeftTrigger;
BYTE bRightTrigger;
SHORT sThumbLX;
SHORT sThumbLY;
SHORT sThumbRX;
SHORT sThumbRY;
} XINPUT_GAMEPAD, *PXINPUT_GAMEPAD;
typedef struct _XINPUT_STATE
{
DWORD dwPacketNumber;
XINPUT_GAMEPAD Gamepad;
} XINPUT_STATE, *PXINPUT_STATE;
typedef struct _XINPUT_VIBRATION
{
WORD wLeftMotorSpeed;
WORD wRightMotorSpeed;
} XINPUT_VIBRATION, *PXINPUT_VIBRATION;
typedef struct _XINPUT_CAPABILITIES
{
BYTE Type;
BYTE SubType;
WORD Flags;
XINPUT_GAMEPAD Gamepad;
XINPUT_VIBRATION Vibration;
} XINPUT_CAPABILITIES, *PXINPUT_CAPABILITIES;
#ifndef XINPUT_USE_9_1_0
typedef struct _XINPUT_BATTERY_INFORMATION
{
BYTE BatteryType;
BYTE BatteryLevel;
} XINPUT_BATTERY_INFORMATION, *PXINPUT_BATTERY_INFORMATION;
typedef struct _XINPUT_KEYSTROKE
{
WORD VirtualKey;
WCHAR Unicode;
WORD Flags;
BYTE UserIndex;
BYTE HidCode;
} XINPUT_KEYSTROKE, *PXINPUT_KEYSTROKE;
#endif // !XINPUT_USE_9_1_0
//
// XInput APIs
//
// now defined in proxy class.
#endif //_XINPUT_H_

View File

@@ -0,0 +1,295 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2011 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define WIN32_LEAN_AND_MEAN
#define SYNERGY_EXPORT_XINPUT_HOOKS
#define REQUIRED_XINPUT_DLL "xinput1_3.dll"
#define HOOK_TIMEOUT 10000 // 10 sec
#include <Windows.h>
#include <XInput.h>
#include "XInputHook.h"
#include "HookDLL.h"
#include <iostream>
HINSTANCE dll;
char name[256];
#pragma data_seg(".SHARED")
HHOOK s_hook = NULL;
// @todo use a struct for multiple gamepad support
WORD s_buttons = 0;
SHORT s_leftStickX = 0;
SHORT s_leftStickY = 0;
SHORT s_rightStickX = 0;
SHORT s_rightStickY = 0;
BYTE s_leftTrigger = 0;
BYTE s_rightTrigger = 0;
BOOL s_timingReqQueued = FALSE;
BOOL s_timingRespQueued = FALSE;
DWORD s_lastFakeMillis = 0;
WORD s_fakeFreqMillis = 0;
DWORD s_packetNumber = 0;
WORD s_leftMotor = 0;
WORD s_rightMotor = 0;
BOOL s_feedbackQueued = FALSE;
#pragma data_seg()
#pragma comment(linker, "/SECTION:.SHARED,RWS")
#include <sstream>
std::stringstream _xInputHookLogStream;
#define LOG(s) \
_xInputHookLogStream.str(""); \
_xInputHookLogStream << "Synergy XInputHook: " << s << endl; \
OutputDebugString( _xInputHookLogStream.str().c_str())
using namespace std;
SDLLHook s_xInputHook =
{
XINPUT_DLL,
false, NULL,
{
{ (char*)(0x80000002), HookXInputGetState },
{ (char*)(0x80000003), HookXInputSetState },
{ (char*)(0x80000004), HookXInputGetCapabilities },
}
};
BOOL APIENTRY
DllMain(HINSTANCE module, DWORD reason, LPVOID reserved)
{
if (reason == DLL_PROCESS_ATTACH)
{
dll = module;
// disable unwanted thread notifications to reduce overhead
DisableThreadLibraryCalls(dll);
GetModuleFileName(GetModuleHandle(NULL), name, sizeof(name));
// don't hook synergys (this needs to detect real input)
if (string(name).find("synergy") == string::npos)
{
LOG("checking '" << name << "' for " << XINPUT_DLL);
HookAPICalls(&s_xInputHook);
}
}
return TRUE;
}
void
SetXInputButtons(DWORD userIndex, WORD buttons)
{
s_buttons = buttons;
s_packetNumber++;
LOG("SetXInputButtons: idx=" << userIndex << ", btns=" << buttons);
}
void
SetXInputSticks(DWORD userIndex, SHORT lx, SHORT ly, SHORT rx, SHORT ry)
{
s_leftStickX = lx;
s_leftStickY = ly;
s_rightStickX = rx;
s_rightStickY = ry;
s_packetNumber++;
LOG("SetXInputSticks:" <<
" l=" << s_leftStickX << "," << s_leftStickY <<
" r=" << s_rightStickX << "," << s_rightStickY);
}
void
SetXInputTriggers(DWORD userIndex, BYTE left, BYTE right)
{
s_leftTrigger = left;
s_rightTrigger = right;
s_packetNumber++;
LOG("SetXInputTriggers: " <<
"l=" << (int)left << " r=" << (int)right);
}
void
QueueXInputTimingReq()
{
s_timingReqQueued = TRUE;
}
BOOL
DequeueXInputTimingResp()
{
BOOL result = s_timingRespQueued;
s_timingRespQueued = FALSE;
return result;
}
BOOL
DequeueXInputFeedback(WORD* leftMotor, WORD* rightMotor)
{
if (s_feedbackQueued)
{
*leftMotor = s_leftMotor;
*rightMotor = s_rightMotor;
s_feedbackQueued = FALSE;
return TRUE;
}
return FALSE;
}
WORD
GetXInputFakeFreqMillis()
{
return s_fakeFreqMillis;
}
DWORD WINAPI
HookXInputGetState(DWORD userIndex, XINPUT_STATE* state)
{
// @todo multiple device support
if (userIndex != 0)
{
return ERROR_DEVICE_NOT_CONNECTED;
}
DWORD now = GetTickCount();
s_fakeFreqMillis = (WORD)(now - s_lastFakeMillis);
s_lastFakeMillis = now;
state->dwPacketNumber = s_packetNumber;
state->Gamepad.wButtons = s_buttons;
state->Gamepad.bLeftTrigger = s_leftTrigger;
state->Gamepad.bRightTrigger = s_rightTrigger;
state->Gamepad.sThumbLX = s_leftStickX;
state->Gamepad.sThumbLY = s_leftStickY;
state->Gamepad.sThumbRX = s_rightStickX;
state->Gamepad.sThumbRY = s_rightStickY;
LOG("HookXInputGetState"
<< ", idx=" << userIndex
<< ", pkt=" << state->dwPacketNumber
<< ", btn=" << state->Gamepad.wButtons
<< ", t1=" << (int)state->Gamepad.bLeftTrigger
<< ", t2=" << (int)state->Gamepad.bRightTrigger
<< ", s1=" << state->Gamepad.sThumbLX << "," << state->Gamepad.sThumbLY
<< ", s2=" << state->Gamepad.sThumbRX << "," << state->Gamepad.sThumbRY);
if (s_timingReqQueued)
{
s_timingRespQueued = TRUE;
s_timingReqQueued = FALSE;
LOG("timing response queued");
}
return ERROR_SUCCESS;
}
DWORD WINAPI
HookXInputSetState(DWORD userIndex, XINPUT_VIBRATION* vibration)
{
// @todo multiple device support
if (userIndex != 0)
{
return ERROR_DEVICE_NOT_CONNECTED;
}
// only change values and queue feedback change if
// feedback has actually changed.
if ((s_leftMotor != vibration->wLeftMotorSpeed) ||
(s_rightMotor != vibration->wRightMotorSpeed))
{
s_leftMotor = vibration->wLeftMotorSpeed;
s_rightMotor = vibration->wRightMotorSpeed;
s_feedbackQueued = TRUE;
LOG("HookXInputSetState"
", idx=" << userIndex <<
", lm=" << s_leftMotor <<
", rm=" << s_rightMotor);
}
return ERROR_SUCCESS;
}
DWORD WINAPI
HookXInputGetCapabilities(DWORD userIndex, DWORD flags, XINPUT_CAPABILITIES* capabilities)
{
// @todo multiple device support
if (userIndex != 0)
{
return ERROR_DEVICE_NOT_CONNECTED;
}
LOG("HookXInputGetCapabilities"
", idx=" << userIndex <<
", flags=" << flags);
capabilities->Type = 1;
capabilities->SubType = 1;
capabilities->Flags = 4;
capabilities->Gamepad.bLeftTrigger = 1;
capabilities->Gamepad.bRightTrigger = 1;
capabilities->Gamepad.sThumbLX = 1;
capabilities->Gamepad.sThumbLY = 1;
capabilities->Gamepad.sThumbRX = 1;
capabilities->Gamepad.sThumbRY = 1;
capabilities->Gamepad.wButtons = 62463;
capabilities->Vibration.wLeftMotorSpeed = 1;
capabilities->Vibration.wRightMotorSpeed = 1;
return ERROR_SUCCESS;
}
synxinhk_API LRESULT CALLBACK
HookProc(int code, WPARAM wParam, LPARAM lParam)
{
return CallNextHookEx(s_hook, code, wParam, lParam);
}
synxinhk_API BOOL
InstallXInputHook()
{
if (_stricmp(XINPUT_DLL, REQUIRED_XINPUT_DLL) != 0)
{
LOG("DLL not supported: " << XINPUT_DLL);
return FALSE;
}
LOG("installing hook");
s_hook = SetWindowsHookEx(WH_CBT, HookProc, dll, 0);
LOG("hook installed");
return TRUE;
}
synxinhk_API void
RemoveXInputHook()
{
LOG("removing hook");
UnhookWindowsHookEx(s_hook);
LOG("hook removed");
}

View File

@@ -0,0 +1,43 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2011 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef synxinhk_EXPORTS
#define synxinhk_API __declspec(dllexport)
#else
#define synxinhk_API __declspec(dllimport)
#endif
synxinhk_API LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam);
synxinhk_API BOOL InstallXInputHook();
synxinhk_API void RemoveXInputHook();
synxinhk_API void SetXInputButtons(DWORD userIndex, WORD buttons);
synxinhk_API void SetXInputSticks(DWORD userIndex, SHORT lx, SHORT ly, SHORT rx, SHORT ry);
synxinhk_API void SetXInputTriggers(DWORD userIndex, BYTE left, BYTE right);
synxinhk_API void QueueXInputTimingReq();
synxinhk_API BOOL DequeueXInputTimingResp();
synxinhk_API WORD GetXInputFakeFreqMillis();
synxinhk_API BOOL DequeueXInputFeedback(WORD* leftMotor, WORD* rightMotor);
#ifdef SYNERGY_EXPORT_XINPUT_HOOKS
synxinhk_API DWORD WINAPI HookXInputGetState(DWORD dwUserIndex, XINPUT_STATE* pState);
synxinhk_API DWORD WINAPI HookXInputSetState(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration);
synxinhk_API DWORD WINAPI HookXInputGetCapabilities(DWORD userIndex, DWORD flags, XINPUT_CAPABILITIES* capabilities);
#endif

View File

@@ -0,0 +1,72 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2011 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define SYNERGY_EXPORT_XINPUT_HOOKS
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include "XInputProxy13.h"
#include "XInputHook.h"
#pragma comment(linker, "/EXPORT:XInputGetState=_XInputGetState@8,@2")
#pragma comment(linker, "/EXPORT:XInputSetState=_XInputSetState@8,@3")
#pragma comment(linker, "/EXPORT:XInputGetCapabilities=_XInputGetCapabilities@12,@4")
#pragma comment(linker, "/EXPORT:XInputEnable=_XInputEnable@4,@5")
#pragma comment(linker, "/EXPORT:XInputGetDSoundAudioDeviceGuids=_XInputGetDSoundAudioDeviceGuids@12,@6")
#pragma comment(linker, "/EXPORT:XInputGetBatteryInformation=_XInputGetBatteryInformation@12,@7")
#pragma comment(linker, "/EXPORT:XInputGetKeystroke=_XInputGetKeystroke@12,@8")
sxinpx13_API DWORD WINAPI
XInputGetState(DWORD dwUserIndex, XINPUT_STATE* pState)
{
return HookXInputGetState(dwUserIndex, pState);
}
sxinpx13_API DWORD WINAPI
XInputSetState(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration)
{
return HookXInputSetState(dwUserIndex, pVibration);
}
sxinpx13_API DWORD WINAPI
XInputGetCapabilities(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES* pCapabilities)
{
return HookXInputGetCapabilities(dwUserIndex, dwFlags, pCapabilities);
}
sxinpx13_API void WINAPI
XInputEnable(BOOL enable)
{
}
sxinpx13_API DWORD WINAPI
XInputGetDSoundAudioDeviceGuids(DWORD dwUserIndex, GUID* pDSoundRenderGuid, GUID* pDSoundCaptureGuid)
{
return ERROR_DEVICE_NOT_CONNECTED;
}
sxinpx13_API DWORD WINAPI
XInputGetBatteryInformation(DWORD dwUserIndex, BYTE devType, XINPUT_BATTERY_INFORMATION* pBatteryInformation)
{
return ERROR_DEVICE_NOT_CONNECTED;
}
sxinpx13_API DWORD WINAPI
XInputGetKeystroke(DWORD dwUserIndex, DWORD dwReserved, PXINPUT_KEYSTROKE pKeystroke)
{
return ERROR_DEVICE_NOT_CONNECTED;
}

View File

@@ -0,0 +1,79 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2011 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef sxinpx13_EXPORTS
#define sxinpx13_API __declspec(dllexport)
#else
#define sxinpx13_API __declspec(dllimport)
#endif
#include "XInput13.h"
#ifdef __cplusplus
extern "C" {
#endif
sxinpx13_API DWORD WINAPI XInputGetState
(
__in DWORD dwUserIndex, // Index of the gamer associated with the device
__out XINPUT_STATE* pState // Receives the current state
);
sxinpx13_API DWORD WINAPI XInputSetState
(
__in DWORD dwUserIndex, // Index of the gamer associated with the device
__in XINPUT_VIBRATION* pVibration // The vibration information to send to the controller
);
sxinpx13_API DWORD WINAPI XInputGetCapabilities
(
__in DWORD dwUserIndex, // Index of the gamer associated with the device
__in DWORD dwFlags, // Input flags that identify the device type
__out XINPUT_CAPABILITIES* pCapabilities // Receives the capabilities
);
sxinpx13_API void WINAPI XInputEnable
(
__in BOOL enable // [in] Indicates whether xinput is enabled or disabled.
);
sxinpx13_API DWORD WINAPI XInputGetDSoundAudioDeviceGuids
(
__in DWORD dwUserIndex, // Index of the gamer associated with the device
__out GUID* pDSoundRenderGuid, // DSound device ID for render
__out GUID* pDSoundCaptureGuid // DSound device ID for capture
);
sxinpx13_API DWORD WINAPI XInputGetBatteryInformation
(
__in DWORD dwUserIndex, // Index of the gamer associated with the device
__in BYTE devType, // Which device on this user index
__out XINPUT_BATTERY_INFORMATION* pBatteryInformation // Contains the level and types of batteries
);
sxinpx13_API DWORD WINAPI XInputGetKeystroke
(
__in DWORD dwUserIndex, // Index of the gamer associated with the device
__reserved DWORD dwReserved, // Reserved for future use
__out PXINPUT_KEYSTROKE pKeystroke // Pointer to an XINPUT_KEYSTROKE structure that receives an input event.
);
#ifdef __cplusplus
}
#endif