mirror of
https://github.com/debauchee/barrier.git
synced 2026-05-09 16:02:46 +08:00
moving 1.4 to trunk
This commit is contained in:
226
src/lib/platform/CMSWindowsClipboard.cpp
Normal file
226
src/lib/platform/CMSWindowsClipboard.cpp
Normal 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;
|
||||
}
|
||||
114
src/lib/platform/CMSWindowsClipboard.h
Normal file
114
src/lib/platform/CMSWindowsClipboard.h
Normal 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
|
||||
148
src/lib/platform/CMSWindowsClipboardAnyTextConverter.cpp
Normal file
148
src/lib/platform/CMSWindowsClipboardAnyTextConverter.cpp
Normal 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;
|
||||
}
|
||||
59
src/lib/platform/CMSWindowsClipboardAnyTextConverter.h
Normal file
59
src/lib/platform/CMSWindowsClipboardAnyTextConverter.h
Normal 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
|
||||
150
src/lib/platform/CMSWindowsClipboardBitmapConverter.cpp
Normal file
150
src/lib/platform/CMSWindowsClipboardBitmapConverter.cpp
Normal 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;
|
||||
}
|
||||
38
src/lib/platform/CMSWindowsClipboardBitmapConverter.h
Normal file
38
src/lib/platform/CMSWindowsClipboardBitmapConverter.h
Normal 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
|
||||
29
src/lib/platform/CMSWindowsClipboardFacade.cpp
Normal file
29
src/lib/platform/CMSWindowsClipboardFacade.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
30
src/lib/platform/CMSWindowsClipboardFacade.h
Normal file
30
src/lib/platform/CMSWindowsClipboardFacade.h
Normal 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
|
||||
118
src/lib/platform/CMSWindowsClipboardHTMLConverter.cpp
Normal file
118
src/lib/platform/CMSWindowsClipboardHTMLConverter.cpp
Normal 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);
|
||||
}
|
||||
47
src/lib/platform/CMSWindowsClipboardHTMLConverter.h
Normal file
47
src/lib/platform/CMSWindowsClipboardHTMLConverter.h
Normal 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
|
||||
58
src/lib/platform/CMSWindowsClipboardTextConverter.cpp
Normal file
58
src/lib/platform/CMSWindowsClipboardTextConverter.cpp
Normal 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;
|
||||
}
|
||||
39
src/lib/platform/CMSWindowsClipboardTextConverter.h
Normal file
39
src/lib/platform/CMSWindowsClipboardTextConverter.h
Normal 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
|
||||
58
src/lib/platform/CMSWindowsClipboardUTF16Converter.cpp
Normal file
58
src/lib/platform/CMSWindowsClipboardUTF16Converter.cpp
Normal 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;
|
||||
}
|
||||
39
src/lib/platform/CMSWindowsClipboardUTF16Converter.h
Normal file
39
src/lib/platform/CMSWindowsClipboardUTF16Converter.h
Normal 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
|
||||
58
src/lib/platform/CMSWindowsDebugOutputter.cpp
Normal file
58
src/lib/platform/CMSWindowsDebugOutputter.cpp
Normal 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()
|
||||
{
|
||||
}
|
||||
38
src/lib/platform/CMSWindowsDebugOutputter.h
Normal file
38
src/lib/platform/CMSWindowsDebugOutputter.h
Normal 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();
|
||||
};
|
||||
1043
src/lib/platform/CMSWindowsDesks.cpp
Normal file
1043
src/lib/platform/CMSWindowsDesks.cpp
Normal file
File diff suppressed because it is too large
Load Diff
303
src/lib/platform/CMSWindowsDesks.h
Normal file
303
src/lib/platform/CMSWindowsDesks.h
Normal 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
|
||||
141
src/lib/platform/CMSWindowsEventQueueBuffer.cpp
Normal file
141
src/lib/platform/CMSWindowsEventQueueBuffer.cpp
Normal 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;
|
||||
}
|
||||
47
src/lib/platform/CMSWindowsEventQueueBuffer.h
Normal file
47
src/lib/platform/CMSWindowsEventQueueBuffer.h
Normal 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
|
||||
69
src/lib/platform/CMSWindowsHookLibraryLoader.cpp
Normal file
69
src/lib/platform/CMSWindowsHookLibraryLoader.cpp
Normal 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;
|
||||
}
|
||||
45
src/lib/platform/CMSWindowsHookLibraryLoader.h
Normal file
45
src/lib/platform/CMSWindowsHookLibraryLoader.h
Normal 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
|
||||
1467
src/lib/platform/CMSWindowsKeyState.cpp
Normal file
1467
src/lib/platform/CMSWindowsKeyState.cpp
Normal file
File diff suppressed because it is too large
Load Diff
230
src/lib/platform/CMSWindowsKeyState.h
Normal file
230
src/lib/platform/CMSWindowsKeyState.h
Normal 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
|
||||
488
src/lib/platform/CMSWindowsRelauncher.cpp
Normal file
488
src/lib/platform/CMSWindowsRelauncher.cpp
Normal 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,
|
||||
¤tToken);
|
||||
|
||||
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);
|
||||
}
|
||||
54
src/lib/platform/CMSWindowsRelauncher.h
Normal file
54
src/lib/platform/CMSWindowsRelauncher.h
Normal 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;
|
||||
};
|
||||
1860
src/lib/platform/CMSWindowsScreen.cpp
Normal file
1860
src/lib/platform/CMSWindowsScreen.cpp
Normal file
File diff suppressed because it is too large
Load Diff
332
src/lib/platform/CMSWindowsScreen.h
Normal file
332
src/lib/platform/CMSWindowsScreen.h
Normal 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
|
||||
501
src/lib/platform/CMSWindowsScreenSaver.cpp
Normal file
501
src/lib/platform/CMSWindowsScreenSaver.cpp
Normal 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;
|
||||
}
|
||||
95
src/lib/platform/CMSWindowsScreenSaver.h
Normal file
95
src/lib/platform/CMSWindowsScreenSaver.h
Normal 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
|
||||
78
src/lib/platform/CMSWindowsUtil.cpp
Normal file
78
src/lib/platform/CMSWindowsUtil.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
41
src/lib/platform/CMSWindowsUtil.h
Normal file
41
src/lib/platform/CMSWindowsUtil.h
Normal 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
|
||||
362
src/lib/platform/CMSWindowsXInput.cpp
Normal file
362
src/lib/platform/CMSWindowsXInput.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
91
src/lib/platform/CMSWindowsXInput.h
Normal file
91
src/lib/platform/CMSWindowsXInput.h
Normal 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;
|
||||
};
|
||||
195
src/lib/platform/CMakeLists.txt
Normal file
195
src/lib/platform/CMakeLists.txt
Normal 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()
|
||||
250
src/lib/platform/COSXClipboard.cpp
Normal file
250
src/lib/platform/COSXClipboard.cpp
Normal 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();
|
||||
}
|
||||
96
src/lib/platform/COSXClipboard.h
Normal file
96
src/lib/platform/COSXClipboard.h
Normal 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
|
||||
88
src/lib/platform/COSXClipboardAnyTextConverter.cpp
Normal file
88
src/lib/platform/COSXClipboardAnyTextConverter.cpp
Normal 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;
|
||||
}
|
||||
55
src/lib/platform/COSXClipboardAnyTextConverter.h
Normal file
55
src/lib/platform/COSXClipboardAnyTextConverter.h
Normal 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
|
||||
91
src/lib/platform/COSXClipboardTextConverter.cpp
Normal file
91
src/lib/platform/COSXClipboardTextConverter.cpp
Normal 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);
|
||||
}
|
||||
44
src/lib/platform/COSXClipboardTextConverter.h
Normal file
44
src/lib/platform/COSXClipboardTextConverter.h
Normal 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
|
||||
53
src/lib/platform/COSXClipboardUTF16Converter.cpp
Normal file
53
src/lib/platform/COSXClipboardUTF16Converter.cpp
Normal 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);
|
||||
}
|
||||
39
src/lib/platform/COSXClipboardUTF16Converter.h
Normal file
39
src/lib/platform/COSXClipboardUTF16Converter.h
Normal 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
|
||||
127
src/lib/platform/COSXEventQueueBuffer.cpp
Normal file
127
src/lib/platform/COSXEventQueueBuffer.cpp
Normal 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;
|
||||
}
|
||||
43
src/lib/platform/COSXEventQueueBuffer.h
Normal file
43
src/lib/platform/COSXEventQueueBuffer.h
Normal 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
|
||||
1317
src/lib/platform/COSXKeyState.cpp
Normal file
1317
src/lib/platform/COSXKeyState.cpp
Normal file
File diff suppressed because it is too large
Load Diff
225
src/lib/platform/COSXKeyState.h
Normal file
225
src/lib/platform/COSXKeyState.h
Normal 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
|
||||
1913
src/lib/platform/COSXScreen.cpp
Normal file
1913
src/lib/platform/COSXScreen.cpp
Normal file
File diff suppressed because it is too large
Load Diff
339
src/lib/platform/COSXScreen.h
Normal file
339
src/lib/platform/COSXScreen.h
Normal 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
|
||||
178
src/lib/platform/COSXScreenSaver.cpp
Normal file
178
src/lib/platform/COSXScreenSaver.cpp
Normal 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));
|
||||
}
|
||||
57
src/lib/platform/COSXScreenSaver.h
Normal file
57
src/lib/platform/COSXScreenSaver.h
Normal 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
|
||||
42
src/lib/platform/COSXScreenSaverUtil.h
Normal file
42
src/lib/platform/COSXScreenSaverUtil.h
Normal 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
|
||||
81
src/lib/platform/COSXScreenSaverUtil.m
Normal file
81
src/lib/platform/COSXScreenSaverUtil.m
Normal 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];
|
||||
}
|
||||
1146
src/lib/platform/CSynergyHook.cpp
Normal file
1146
src/lib/platform/CSynergyHook.cpp
Normal file
File diff suppressed because it is too large
Load Diff
95
src/lib/platform/CSynergyHook.h
Normal file
95
src/lib/platform/CSynergyHook.h
Normal 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
|
||||
1510
src/lib/platform/CXWindowsClipboard.cpp
Normal file
1510
src/lib/platform/CXWindowsClipboard.cpp
Normal file
File diff suppressed because it is too large
Load Diff
379
src/lib/platform/CXWindowsClipboard.h
Normal file
379
src/lib/platform/CXWindowsClipboard.h
Normal 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
|
||||
190
src/lib/platform/CXWindowsClipboardAnyBitmapConverter.cpp
Normal file
190
src/lib/platform/CXWindowsClipboardAnyBitmapConverter.cpp
Normal 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;
|
||||
}
|
||||
62
src/lib/platform/CXWindowsClipboardAnyBitmapConverter.h
Normal file
62
src/lib/platform/CXWindowsClipboardAnyBitmapConverter.h
Normal 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
|
||||
142
src/lib/platform/CXWindowsClipboardBMPConverter.cpp
Normal file
142
src/lib/platform/CXWindowsClipboardBMPConverter.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
42
src/lib/platform/CXWindowsClipboardBMPConverter.h
Normal file
42
src/lib/platform/CXWindowsClipboardBMPConverter.h
Normal 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
|
||||
65
src/lib/platform/CXWindowsClipboardHTMLConverter.cpp
Normal file
65
src/lib/platform/CXWindowsClipboardHTMLConverter.cpp
Normal 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);
|
||||
}
|
||||
44
src/lib/platform/CXWindowsClipboardHTMLConverter.h
Normal file
44
src/lib/platform/CXWindowsClipboardHTMLConverter.h
Normal 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
|
||||
77
src/lib/platform/CXWindowsClipboardTextConverter.cpp
Normal file
77
src/lib/platform/CXWindowsClipboardTextConverter.cpp
Normal 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;
|
||||
}
|
||||
44
src/lib/platform/CXWindowsClipboardTextConverter.h
Normal file
44
src/lib/platform/CXWindowsClipboardTextConverter.h
Normal 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
|
||||
65
src/lib/platform/CXWindowsClipboardUCS2Converter.cpp
Normal file
65
src/lib/platform/CXWindowsClipboardUCS2Converter.cpp
Normal 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);
|
||||
}
|
||||
44
src/lib/platform/CXWindowsClipboardUCS2Converter.h
Normal file
44
src/lib/platform/CXWindowsClipboardUCS2Converter.h
Normal 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
|
||||
64
src/lib/platform/CXWindowsClipboardUTF8Converter.cpp
Normal file
64
src/lib/platform/CXWindowsClipboardUTF8Converter.cpp
Normal 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;
|
||||
}
|
||||
44
src/lib/platform/CXWindowsClipboardUTF8Converter.h
Normal file
44
src/lib/platform/CXWindowsClipboardUTF8Converter.h
Normal 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
|
||||
287
src/lib/platform/CXWindowsEventQueueBuffer.cpp
Normal file
287
src/lib/platform/CXWindowsEventQueueBuffer.cpp
Normal 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();
|
||||
}
|
||||
61
src/lib/platform/CXWindowsEventQueueBuffer.h
Normal file
61
src/lib/platform/CXWindowsEventQueueBuffer.h
Normal 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
|
||||
861
src/lib/platform/CXWindowsKeyState.cpp
Normal file
861
src/lib/platform/CXWindowsKeyState.cpp
Normal 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;
|
||||
}
|
||||
161
src/lib/platform/CXWindowsKeyState.h
Normal file
161
src/lib/platform/CXWindowsKeyState.h
Normal 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
|
||||
2085
src/lib/platform/CXWindowsScreen.cpp
Normal file
2085
src/lib/platform/CXWindowsScreen.cpp
Normal file
File diff suppressed because it is too large
Load Diff
255
src/lib/platform/CXWindowsScreen.h
Normal file
255
src/lib/platform/CXWindowsScreen.h
Normal 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
|
||||
602
src/lib/platform/CXWindowsScreenSaver.cpp
Normal file
602
src/lib/platform/CXWindowsScreenSaver.cpp
Normal 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
|
||||
}
|
||||
170
src/lib/platform/CXWindowsScreenSaver.h
Normal file
170
src/lib/platform/CXWindowsScreenSaver.h
Normal 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
|
||||
1779
src/lib/platform/CXWindowsUtil.cpp
Normal file
1779
src/lib/platform/CXWindowsUtil.cpp
Normal file
File diff suppressed because it is too large
Load Diff
188
src/lib/platform/CXWindowsUtil.h
Normal file
188
src/lib/platform/CXWindowsUtil.h
Normal 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
|
||||
341
src/lib/platform/HookDLL.cpp
Normal file
341
src/lib/platform/HookDLL.cpp
Normal 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;
|
||||
}
|
||||
|
||||
80
src/lib/platform/HookDLL.h
Normal file
80
src/lib/platform/HookDLL.h
Normal 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 );
|
||||
34
src/lib/platform/IMSWindowsClipboardFacade.h
Normal file
34
src/lib/platform/IMSWindowsClipboardFacade.h
Normal 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
|
||||
53
src/lib/platform/OSXScreenSaverControl.h
Normal file
53
src/lib/platform/OSXScreenSaverControl.h
Normal 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
228
src/lib/platform/XInput13.h
Normal 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_
|
||||
295
src/lib/platform/XInputHook.cpp
Normal file
295
src/lib/platform/XInputHook.cpp
Normal 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");
|
||||
}
|
||||
43
src/lib/platform/XInputHook.h
Normal file
43
src/lib/platform/XInputHook.h
Normal 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
|
||||
72
src/lib/platform/XInputProxy13.cpp
Normal file
72
src/lib/platform/XInputProxy13.cpp
Normal 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;
|
||||
}
|
||||
79
src/lib/platform/XInputProxy13.h
Normal file
79
src/lib/platform/XInputProxy13.h
Normal 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
|
||||
Reference in New Issue
Block a user