refactored some common platform dependent stuff into a new

library:  platform.  also removed test.cpp.
This commit is contained in:
crs
2002-06-04 12:26:23 +00:00
parent c3649df304
commit 5709d8ddef
29 changed files with 476 additions and 424 deletions

View File

@@ -0,0 +1,199 @@
#include "CMSWindowsClipboard.h"
#include "CString.h"
#include "CLog.h"
//
// CMSWindowsClipboard
//
CMSWindowsClipboard::CMSWindowsClipboard(HWND window) :
m_window(window),
m_time(0)
{
// do nothing
}
CMSWindowsClipboard::~CMSWindowsClipboard()
{
// do nothing
}
bool CMSWindowsClipboard::empty()
{
log((CLOG_DEBUG "empty clipboard"));
if (!EmptyClipboard()) {
log((CLOG_DEBUG "failed to grab clipboard"));
return false;
}
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 required form
const UINT win32Format = convertFormatToWin32(format);
HANDLE win32Data;
switch (win32Format) {
case CF_TEXT:
win32Data = convertTextToWin32(data);
break;
default:
win32Data = NULL;
break;
}
// put the data on the clipboard
if (win32Data != NULL) {
SetClipboardData(win32Format, win32Data);
}
}
bool CMSWindowsClipboard::open(Time time) const
{
log((CLOG_DEBUG "open clipboard"));
if (!OpenClipboard(m_window)) {
log((CLOG_WARN "failed to open clipboard"));
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
{
const UINT win32Format = convertFormatToWin32(format);
return (win32Format != 0 && IsClipboardFormatAvailable(win32Format) != 0);
}
CString CMSWindowsClipboard::get(EFormat format) const
{
// get the win32 format. return empty data if unknown format.
const UINT win32Format = convertFormatToWin32(format);
if (win32Format == 0)
return CString();
// get a handle to the clipboard data and convert it
HANDLE win32Data = GetClipboardData(win32Format);
CString data;
if (win32Data != NULL) {
// convert the data
switch (win32Format) {
case CF_TEXT:
data = convertTextFromWin32(win32Data);
}
}
return data;
}
UINT CMSWindowsClipboard::convertFormatToWin32(
EFormat format) const
{
switch (format) {
case kText:
return CF_TEXT;
default:
return 0;
}
}
HANDLE CMSWindowsClipboard::convertTextToWin32(
const CString& data) const
{
// compute size of converted text
UInt32 dstSize = 1;
const UInt32 srcSize = data.size();
const char* src = data.c_str();
for (UInt32 index = 0; index < srcSize; ++index) {
if (src[index] == '\n') {
// add \r
++dstSize;
}
++dstSize;
}
// allocate
HGLOBAL gData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, dstSize);
if (gData != NULL) {
// get a pointer to the allocated memory
char* dst = (char*)GlobalLock(gData);
if (dst != NULL) {
// convert text. we change LF to CRLF.
dstSize = 0;
for (UInt32 index = 0; index < srcSize; ++index) {
if (src[index] == '\n') {
// add \r
dst[dstSize++] = '\r';
}
dst[dstSize++] = src[index];
}
dst[dstSize] = '\0';
// done converting
GlobalUnlock(gData);
}
}
return gData;
}
CString CMSWindowsClipboard::convertTextFromWin32(
HANDLE handle) const
{
// get source data and it's size
const char* src = (const char*)GlobalLock(handle);
UInt32 srcSize = (SInt32)GlobalSize(handle);
if (src == NULL || srcSize <= 1)
return CString();
// ignore trailing NUL
--srcSize;
// compute size of converted text
UInt32 dstSize = 0;
UInt32 index;
for (index = 0; index < srcSize; ++index) {
if (src[index] == '\r') {
// skip \r
if (index + 1 < srcSize && src[index + 1] == '\n')
++index;
}
++dstSize;
}
// allocate
CString data;
data.reserve(dstSize);
// convert text. we change CRLF to LF.
for (index = 0; index < srcSize; ++index) {
if (src[index] == '\r') {
// skip \r
if (index + 1 < srcSize && src[index + 1] == '\n')
++index;
}
data += src[index];
}
return data;
}

View File

@@ -0,0 +1,31 @@
#ifndef CMSWINDOWSCLIPBOARD_H
#define CMSWINDOWSCLIPBOARD_H
#include "IClipboard.h"
#include <windows.h>
class CMSWindowsClipboard : public IClipboard {
public:
CMSWindowsClipboard(HWND window);
virtual ~CMSWindowsClipboard();
// 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:
UINT convertFormatToWin32(EFormat) const;
HANDLE convertTextToWin32(const CString& data) const;
CString convertTextFromWin32(HANDLE) const;
private:
HWND m_window;
mutable Time m_time;
};
#endif

View File

@@ -0,0 +1,170 @@
#include "CMSWindowsScreen.h"
#include "CThread.h"
#include "CLock.h"
#include "TMethodJob.h"
#include "CLog.h"
#include "CString.h"
#include <string.h>
#include <assert.h>
//
// CMSWindowsScreen
//
HINSTANCE CMSWindowsScreen::s_instance = NULL;
CMSWindowsScreen* CMSWindowsScreen::s_screen = NULL;
CMSWindowsScreen::CMSWindowsScreen() :
m_class(0),
m_cursor(NULL),
m_w(0), m_h(0),
m_thread(0)
{
assert(s_screen == NULL);
s_screen = this;
}
CMSWindowsScreen::~CMSWindowsScreen()
{
assert(m_class == 0);
s_screen = NULL;
}
void CMSWindowsScreen::init(HINSTANCE instance)
{
s_instance = instance;
}
void CMSWindowsScreen::doRun()
{
// save thread id for posting quit message
m_thread = GetCurrentThreadId();
// event loop
for (;;) {
// wait for and get the next event
MSG msg;
getEvent(&msg);
// handle quit message
if (msg.message == WM_QUIT) {
break;
}
// dispatch message
if (!onPreTranslate(&msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
void CMSWindowsScreen::doStop()
{
PostThreadMessage(m_thread, WM_QUIT, 0, 0);
}
void CMSWindowsScreen::openDisplay()
{
assert(s_instance != NULL);
assert(m_class == 0);
// create a transparent cursor
int cw = GetSystemMetrics(SM_CXCURSOR);
int ch = GetSystemMetrics(SM_CYCURSOR);
UInt8* cursorAND = new UInt8[ch * ((cw + 31) >> 2)];
UInt8* cursorXOR = new UInt8[ch * ((cw + 31) >> 2)];
memset(cursorAND, 0xff, ch * ((cw + 31) >> 2));
memset(cursorXOR, 0x00, ch * ((cw + 31) >> 2));
m_cursor = CreateCursor(s_instance, 0, 0, cw, ch, cursorAND, cursorXOR);
delete[] cursorXOR;
delete[] cursorAND;
// register a window class
WNDCLASSEX classInfo;
classInfo.cbSize = sizeof(classInfo);
classInfo.style = CS_DBLCLKS | CS_NOCLOSE;
classInfo.lpfnWndProc = &CMSWindowsScreen::wndProc;
classInfo.cbClsExtra = 0;
classInfo.cbWndExtra = 0;
classInfo.hInstance = s_instance;
classInfo.hIcon = NULL;
classInfo.hCursor = m_cursor;
classInfo.hbrBackground = NULL;
classInfo.lpszMenuName = NULL;
classInfo.lpszClassName = "Synergy";
classInfo.hIconSm = NULL;
m_class = RegisterClassEx(&classInfo);
// get screen size
// FIXME -- should handle multiple screens
m_w = GetSystemMetrics(SM_CXSCREEN);
m_h = GetSystemMetrics(SM_CYSCREEN);
log((CLOG_INFO "display size: %dx%d", m_w, m_h));
// let subclass prep display
onOpenDisplay();
}
void CMSWindowsScreen::closeDisplay()
{
assert(s_instance != NULL);
assert(m_class != 0);
// let subclass close down display
onCloseDisplay();
// unregister the window class
UnregisterClass((LPCTSTR)m_class, s_instance);
m_class = 0;
// delete resources
DestroyCursor(m_cursor);
m_cursor = NULL;
log((CLOG_DEBUG "closed display"));
}
HINSTANCE CMSWindowsScreen::getInstance()
{
return s_instance;
}
ATOM CMSWindowsScreen::getClass() const
{
return m_class;
}
void CMSWindowsScreen::updateScreenSize()
{
m_w = GetSystemMetrics(SM_CXSCREEN);
m_h = GetSystemMetrics(SM_CYSCREEN);
log((CLOG_INFO "display resize: %dx%d", m_w, m_h));
}
void CMSWindowsScreen::getScreenSize(
SInt32* w, SInt32* h) const
{
assert(m_class != 0);
assert(w != NULL && h != NULL);
*w = m_w;
*h = m_h;
}
void CMSWindowsScreen::getEvent(MSG* msg) const
{
// wait for an event in a cancellable way
while (HIWORD(GetQueueStatus(QS_ALLINPUT)) == 0) {
CThread::sleep(0.01);
}
GetMessage(msg, NULL, 0, 0);
}
LRESULT CALLBACK CMSWindowsScreen::wndProc(
HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
assert(s_screen != NULL);
return s_screen->onEvent(hwnd, msg, wParam, lParam);
}

View File

@@ -0,0 +1,78 @@
#ifndef CMSWINDOWSSCREEN_H
#define CMSWINDOWSSCREEN_H
#include "CMutex.h"
#include "IClipboard.h"
#include "BasicTypes.h"
#include <windows.h>
class CString;
class CThread;
class CMSWindowsScreen {
public:
CMSWindowsScreen();
virtual ~CMSWindowsScreen();
// manipulators
static void init(HINSTANCE);
protected:
// runs an event loop and returns when WM_QUIT is received
void doRun();
// sends WM_QUIT to force doRun() to return
void doStop();
// open the X display. calls onOpenDisplay() after opening the display,
// getting the screen, its size, and root window. then it starts the
// event thread.
void openDisplay();
// destroy the window and close the display. calls onCloseDisplay()
// after the event thread has been shut down but before the display
// is closed.
void closeDisplay();
// get the application instance handle and the registered window
// class atom
static HINSTANCE getInstance();
ATOM getClass() const;
// update screen size cache
void updateScreenSize();
// get the size of the screen
void getScreenSize(SInt32* w, SInt32* h) const;
// wait for and get the next message. cancellable.
void getEvent(MSG*) const;
// called by doRun() to handle an event. return true to skip
// event translation and dispatch.
virtual bool onPreTranslate(MSG*) = 0;
// called by window proc. subclass must call DefWindowProc() if necessary
virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM) = 0;
// called by openDisplay() to allow subclasses to prepare the display
virtual void onOpenDisplay() = 0;
// called by closeDisplay() to
virtual void onCloseDisplay() = 0;
private:
static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM);
private:
static HINSTANCE s_instance;
ATOM m_class;
HICON m_icon;
HCURSOR m_cursor;
SInt32 m_w, m_h;
DWORD m_thread;
static CMSWindowsScreen* s_screen;
};
#endif

11
platform/CPlatform.cpp Normal file
View File

@@ -0,0 +1,11 @@
#include "common.h"
#if defined(CONFIG_PLATFORM_WIN32)
#include "CWin32Platform.cpp"
#elif defined(CONFIG_PLATFORM_UNIX)
#include "CUnixPlatform.cpp"
#endif

18
platform/CPlatform.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef CPLATFORM_H
#define CPLATFORM_H
#include "common.h"
#if defined(CONFIG_PLATFORM_WIN32)
#include "CWin32Platform.h"
typedef CWin32Platform CPlatform;
#elif defined(CONFIG_PLATFORM_UNIX)
#include "CUnixPlatform.h"
typedef CUnixPlatform CPlatform;
#endif
#endif

160
platform/CUnixPlatform.cpp Normal file
View File

@@ -0,0 +1,160 @@
#include "CUnixPlatform.h"
#include "CLog.h"
#include <string.h>
#include <unistd.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <syslog.h>
//
// CUnixPlatform
//
CUnixPlatform::CUnixPlatform()
{
// do nothing
}
CUnixPlatform::~CUnixPlatform()
{
// do nothing
}
bool CUnixPlatform::installDaemon(/* FIXME */)
{
// daemons don't require special installation
return true;
}
bool CUnixPlatform::uninstallDaemon(/* FIXME */)
{
// daemons don't require special installation
return true;
}
bool CUnixPlatform::daemonize(const char* name)
{
// fork so shell thinks we're done and so we're not a process
// group leader
switch (fork()) {
case -1:
// failed
return false;
case 0:
// child
break;
default:
// parent exits
exit(0);
}
// become leader of a new session
setsid();
// chdir to root so we don't keep mounted filesystems points busy
chdir("/");
// mask off permissions for any but owner
umask(077);
// close open files. we only expect stdin, stdout, stderr to be open.
close(0);
close(1);
close(2);
// attach file descriptors 0, 1, 2 to /dev/null so inadvertent use
// of standard I/O safely goes in the bit bucket.
open("/dev/null", O_RDONLY);
open("/dev/null", O_RDWR);
dup(1);
// hook up logger
setDaemonLogger(name);
return true;
}
const char* CUnixPlatform::getBasename(const char* pathname) const
{
if (pathname == NULL) {
return NULL;
}
const char* basename = strrchr(pathname, '/');
if (basename != NULL) {
return basename + 1;
}
else {
return pathname;
}
}
CString CUnixPlatform::getUserDirectory() const
{
// FIXME -- use geteuid? shouldn't run this setuid anyway.
struct passwd* pwent = getpwuid(getuid());
if (pwent != NULL && pwent->pw_dir != NULL) {
return pwent->pw_dir;
}
else {
return CString();
}
}
CString CUnixPlatform::getSystemDirectory() const
{
return "/etc";
}
CString CUnixPlatform::addPathComponent(
const CString& prefix,
const CString& suffix) const
{
CString path;
path.reserve(prefix.size() + 1 + suffix.size());
path += prefix;
path += '/';
path += suffix;
return path;
}
void CUnixPlatform::setDaemonLogger(const char* name)
{
openlog(name, 0, LOG_DAEMON);
CLog::setOutputter(&CUnixPlatform::deamonLogger);
}
void CUnixPlatform::deamonLogger(
int priority, const char* msg)
{
// convert priority
switch (priority) {
case CLog::kFATAL:
case CLog::kERROR:
priority = LOG_ERR;
break;
case CLog::kWARNING:
priority = LOG_WARNING;
break;
case CLog::kNOTE:
priority = LOG_NOTICE;
break;
case CLog::kINFO:
priority = LOG_INFO;
break;
default:
priority = LOG_DEBUG;
break;
}
// log it
syslog(priority, "%s", msg);
}

29
platform/CUnixPlatform.h Normal file
View File

@@ -0,0 +1,29 @@
#ifndef CUNIXPLATFORM_H
#define CUNIXPLATFORM_H
#include "IPlatform.h"
class CUnixPlatform : public IPlatform {
public:
CUnixPlatform();
virtual ~CUnixPlatform();
// IPlatform overrides
virtual bool installDaemon(/* FIXME */);
virtual bool uninstallDaemon(/* FIXME */);
virtual bool daemonize(const char* name);
virtual const char* getBasename(const char* pathname) const;
virtual CString getUserDirectory() const;
virtual CString getSystemDirectory() const;
virtual CString addPathComponent(
const CString& prefix,
const CString& suffix) const;
protected:
virtual void setDaemonLogger(const char* name);
private:
static void deamonLogger(int, const char*);
};
#endif

View File

@@ -0,0 +1,90 @@
#include "CWin32Platform.h"
#include "CLog.h"
#include <string.h>
#include <windows.h>
//
// CWin32Platform
//
CWin32Platform::CWin32Platform()
{
// do nothing
}
CWin32Platform::~CWin32Platform()
{
// do nothing
}
bool CWin32Platform::installDaemon(/* FIXME */)
{
// FIXME
return false;
}
bool CWin32Platform::uninstallDaemon(/* FIXME */)
{
// FIXME
return false;
}
bool CWin32Platform::daemonize(const char* name)
{
// FIXME
return false;
}
const char* CWin32Platform::getBasename(const char* pathname) const
{
if (pathname == NULL) {
return NULL;
}
// check for last /
const char* basename = strrchr(pathname, '/');
if (basename != NULL) {
++basename;
}
else {
basename = pathname;
}
// check for last backslash
const char* basename2 = strrchr(pathname, '\\');
if (basename2 != NULL && basename2 > basename) {
basename = basename2 + 1;
}
return basename;
}
CString CWin32Platform::getUserDirectory() const
{
// FIXME
return CString();
}
CString CWin32Platform::getSystemDirectory() const
{
// FIXME
return "";
}
CString CWin32Platform::addPathComponent(
const CString& prefix,
const CString& suffix) const
{
CString path;
path.reserve(prefix.size() + 1 + suffix.size());
path += prefix;
path += '\\';
path += suffix;
return path;
}
void CWin32Platform::serviceLogger(
int priority, const char* msg)
{
// FIXME
}

26
platform/CWin32Platform.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef CWIN32PLATFORM_H
#define CWIN32PLATFORM_H
#include "IPlatform.h"
class CWin32Platform : public IPlatform {
public:
CWin32Platform();
virtual ~CWin32Platform();
// IPlatform overrides
virtual bool installDaemon(/* FIXME */);
virtual bool uninstallDaemon(/* FIXME */);
virtual bool daemonize(const char* name);
virtual const char* getBasename(const char* pathname) const;
virtual CString getUserDirectory() const;
virtual CString getSystemDirectory() const;
virtual CString addPathComponent(
const CString& prefix,
const CString& suffix) const;
private:
static void serviceLogger(int, const char*);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,267 @@
#ifndef CXWINDOWSCLIPBOARD_H
#define CXWINDOWSCLIPBOARD_H
#include "IClipboard.h"
#include "ClipboardTypes.h"
#include "CString.h"
#include "stdmap.h"
#include "stdlist.h"
#include <X11/Xlib.h>
class CXWindowsClipboard : public IClipboard {
public:
CXWindowsClipboard(Display*, Window, ClipboardID);
virtual ~CXWindowsClipboard();
// tell clipboard it lost ownership
void lost(Time);
// add 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);
// continue 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);
// terminate a selection request. returns true iff the request
// was known and handled.
bool destroyRequest(Window requestor);
// get the clipboard's window
Window getWindow() const;
// get the clipboard selection atom
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:
// 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);
// 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();
// 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;
Time motifGetTime() const;
void motifFillCache();
// FIXME
//
// 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 doEventPredicate(Display* display,
XEvent* event);
static Bool eventPredicate(Display* display,
XEvent* event,
XPointer arg);
void timeout(void*);
private:
Window m_requestor;
Time m_time;
Atom m_property;
bool m_incr;
bool m_failed;
bool m_done;
// 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;
// property used in event to wake up event loop
Atom m_timeout;
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];
Window m_selectionOwner;
SInt32 m_pad4[2];
SInt32 m_items[1]; // m_numItems items
};
// Motif clip item structure
class CMotifClipItem {
public:
SInt32 m_id; // kMotifClipItem
SInt32 m_pad1[6];
SInt32 m_numFormats;
SInt32 m_pad2[7];
SInt32 m_formats[1]; // m_numFormats formats
};
// Motif clip format structure
class CMotifClipFormat {
public:
SInt32 m_id; // kMotifClipFormat
SInt32 m_pad1[6];
SInt32 m_length;
SInt32 m_data;
Atom m_type;
SInt32 m_pad2[6];
};
// 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;
// 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;
Atom getStringData(CString&, int* format) const;
private:
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
bool m_cached;
Time m_cacheTime;
bool m_added[kNumFormats];
CString m_data[kNumFormats];
// conversion request replies
CReplyMap m_replies;
CReplyEventMask m_eventMasks;
// atoms we'll need
Atom m_atomTargets;
Atom m_atomMultiple;
Atom m_atomTimestamp;
Atom m_atomAtom;
Atom m_atomAtomPair;
Atom m_atomInteger;
Atom m_atomData;
Atom m_atomINCR;
Atom m_atomString;
Atom m_atomText;
Atom m_atomCompoundText;
Atom m_atomMotifClipLock;
Atom m_atomMotifClipHeader;
Atom m_atomMotifClipAccess;
Atom m_atomGDKSelection;
};
#endif

View File

@@ -0,0 +1,385 @@
#include "CXWindowsScreen.h"
#include "CXWindowsClipboard.h"
#include "CXWindowsUtil.h"
#include "CClipboard.h"
#include "CLock.h"
#include "CLog.h"
#include "CString.h"
#include "CThread.h"
#include "XScreen.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
//
// CXWindowsScreen
//
CXWindowsScreen* CXWindowsScreen::s_screen = NULL;
CXWindowsScreen::CXWindowsScreen() :
m_display(NULL),
m_root(None),
m_w(0), m_h(0),
m_stop(false)
{
assert(s_screen == NULL);
s_screen = this;
}
CXWindowsScreen::~CXWindowsScreen()
{
assert(s_screen != NULL);
assert(m_display == NULL);
s_screen = NULL;
}
void CXWindowsScreen::openDisplay()
{
assert(m_display == NULL);
// set the X I/O error handler so we catch the display disconnecting
XSetIOErrorHandler(&CXWindowsScreen::ioErrorHandler);
// open the display
log((CLOG_DEBUG "XOpenDisplay(%s)", "NULL"));
m_display = XOpenDisplay(NULL); // FIXME -- allow non-default
if (m_display == NULL)
throw XScreenOpenFailure();
// get default screen
m_screen = DefaultScreen(m_display);
Screen* screen = ScreenOfDisplay(m_display, m_screen);
// get screen size
m_w = WidthOfScreen(screen);
m_h = HeightOfScreen(screen);
log((CLOG_INFO "display size: %dx%d", m_w, m_h));
// get the root window
m_root = RootWindow(m_display, m_screen);
// let subclass prep display
onOpenDisplay(m_display);
// initialize clipboards
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
m_clipboard[id] = createClipboard(id);
}
}
void CXWindowsScreen::closeDisplay()
{
CLock lock(&m_mutex);
// let subclass close down display
onCloseDisplay(m_display);
// destroy clipboards
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
delete m_clipboard[id];
}
// close the display
if (m_display != NULL) {
XCloseDisplay(m_display);
m_display = NULL;
log((CLOG_DEBUG "closed display"));
}
XSetIOErrorHandler(NULL);
}
int CXWindowsScreen::getScreen() const
{
assert(m_display != NULL);
return m_screen;
}
Window CXWindowsScreen::getRoot() const
{
assert(m_display != NULL);
return m_root;
}
void CXWindowsScreen::getScreenSize(
SInt32* w, SInt32* h) const
{
assert(m_display != NULL);
assert(w != NULL && h != NULL);
*w = m_w;
*h = m_h;
}
Cursor CXWindowsScreen::createBlankCursor() const
{
// this seems just a bit more complicated than really necessary
// get the closet cursor size to 1x1
unsigned int w, h;
XQueryBestCursor(m_display, m_root, 1, 1, &w, &h);
// make bitmap data for cursor of closet size. since the cursor
// is blank we can use the same bitmap for shape and mask: all
// zeros.
const int size = ((w + 7) >> 3) * h;
char* data = new char[size];
memset(data, 0, size);
// make bitmap
Pixmap bitmap = XCreateBitmapFromData(m_display, m_root, data, w, h);
// need an arbitrary color for the cursor
XColor color;
color.pixel = 0;
color.red = color.green = color.blue = 0;
color.flags = DoRed | DoGreen | DoBlue;
// make cursor from bitmap
Cursor cursor = XCreatePixmapCursor(m_display, bitmap, bitmap,
&color, &color, 0, 0);
// don't need bitmap or the data anymore
delete[] data;
XFreePixmap(m_display, bitmap);
return cursor;
}
bool CXWindowsScreen::getEvent(XEvent* xevent) const
{
// wait for an event in a cancellable way and don't lock the
// display while we're waiting.
m_mutex.lock();
for (;;) {
while (!m_stop && XPending(m_display) == 0) {
m_mutex.unlock();
CThread::sleep(0.01);
m_mutex.lock();
}
if (m_stop) {
m_mutex.unlock();
return false;
}
else {
// get the event
XNextEvent(m_display, xevent);
// process the event. return the event if unhandled.
m_mutex.unlock();
if (!const_cast<CXWindowsScreen*>(this)->processEvent(xevent)) {
return true;
}
m_mutex.lock();
}
}
}
void CXWindowsScreen::doStop()
{
// caller must have locked display
m_stop = true;
}
ClipboardID CXWindowsScreen::getClipboardID(Atom selection) const
{
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
if (m_clipboard[id] != NULL &&
m_clipboard[id]->getSelection() == selection) {
return id;
}
}
return kClipboardEnd;
}
void CXWindowsScreen::onUnexpectedClose()
{
// do nothing
}
bool CXWindowsScreen::processEvent(XEvent* xevent)
{
switch (xevent->type) {
case SelectionClear: {
// we just lost the selection. that means someone else
// grabbed the selection so this screen is now the
// selection owner. report that to the subclass.
ClipboardID id = getClipboardID(xevent->xselectionclear.selection);
if (id != kClipboardEnd) {
log((CLOG_DEBUG "lost clipboard %d ownership at time %d", id, xevent->xselectionclear.time));
m_clipboard[id]->lost(xevent->xselectionclear.time);
onLostClipboard(id);
return true;
}
break;
}
case SelectionNotify:
// notification of selection transferred. we shouldn't
// get this here because we handle them in the selection
// retrieval methods. we'll just delete the property
// with the data (satisfying the usual ICCCM protocol).
if (xevent->xselection.property != None) {
CLock lock(&m_mutex);
XDeleteProperty(m_display,
xevent->xselection.requestor,
xevent->xselection.property);
}
return true;
case SelectionRequest: {
// somebody is asking for clipboard data
ClipboardID id = getClipboardID(xevent->xselectionrequest.selection);
if (id != kClipboardEnd) {
CLock lock(&m_mutex);
m_clipboard[id]->addRequest(
xevent->xselectionrequest.owner,
xevent->xselectionrequest.requestor,
xevent->xselectionrequest.target,
xevent->xselectionrequest.time,
xevent->xselectionrequest.property);
return true;
}
break;
}
case PropertyNotify:
// property delete may be part of a selection conversion
if (xevent->xproperty.state == PropertyDelete) {
processClipboardRequest(xevent->xproperty.window,
xevent->xproperty.time,
xevent->xproperty.atom);
return true;
}
break;
case DestroyNotify:
// looks like one of the windows that requested a clipboard
// transfer has gone bye-bye.
destroyClipboardRequest(xevent->xdestroywindow.window);
return true;
}
return false;
}
bool CXWindowsScreen::setDisplayClipboard(
ClipboardID id,
const IClipboard* clipboard)
{
CLock lock(&m_mutex);
// fail if we don't have the requested clipboard
if (m_clipboard[id] == NULL) {
return false;
}
// get the actual time. ICCCM does not allow CurrentTime.
Time timestamp = CXWindowsUtil::getCurrentTime(
m_display, m_clipboard[id]->getWindow());
if (clipboard != NULL) {
// save clipboard data
return CClipboard::copy(m_clipboard[id], clipboard, timestamp);
}
else {
// assert clipboard ownership
if (!m_clipboard[id]->open(timestamp)) {
return false;
}
m_clipboard[id]->empty();
m_clipboard[id]->close();
return true;
}
}
bool CXWindowsScreen::getDisplayClipboard(
ClipboardID id,
IClipboard* clipboard) const
{
assert(clipboard != NULL);
// block others from using the display while we get the clipboard
CLock lock(&m_mutex);
// fail if we don't have the requested clipboard
if (m_clipboard[id] == NULL) {
return false;
}
// get the actual time. ICCCM does not allow CurrentTime.
Time timestamp = CXWindowsUtil::getCurrentTime(
m_display, m_clipboard[id]->getWindow());
// copy the clipboard
return CClipboard::copy(clipboard, m_clipboard[id], timestamp);
}
void CXWindowsScreen::processClipboardRequest(
Window requestor,
Time time, Atom property)
{
CLock lock(&m_mutex);
// check every clipboard until one returns success
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
if (m_clipboard[id] != NULL &&
m_clipboard[id]->processRequest(requestor, time, property)) {
break;
}
}
}
void CXWindowsScreen::destroyClipboardRequest(
Window requestor)
{
CLock lock(&m_mutex);
// check every clipboard until one returns success
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
if (m_clipboard[id] != NULL &&
m_clipboard[id]->destroyRequest(requestor)) {
break;
}
}
}
int CXWindowsScreen::ioErrorHandler(Display*)
{
// the display has disconnected, probably because X is shutting
// down. X forces us to exit at this point. that's arguably
// a flaw in X but, realistically, it's difficult to gracefully
// handle not having a Display* anymore. we'll simply log the
// error, notify the subclass (which must not use the display
// so we set it to NULL), and exit.
log((CLOG_WARN "X display has unexpectedly disconnected"));
s_screen->m_display = NULL;
s_screen->onUnexpectedClose();
log((CLOG_CRIT "quiting due to X display disconnection"));
exit(17);
}
//
// CXWindowsScreen::CDisplayLock
//
CXWindowsScreen::CDisplayLock::CDisplayLock(const CXWindowsScreen* screen) :
m_mutex(&screen->m_mutex),
m_display(screen->m_display)
{
assert(m_display != NULL);
m_mutex->lock();
}
CXWindowsScreen::CDisplayLock::~CDisplayLock()
{
m_mutex->unlock();
}
CXWindowsScreen::CDisplayLock::operator Display*() const
{
return m_display;
}

122
platform/CXWindowsScreen.h Normal file
View File

@@ -0,0 +1,122 @@
#ifndef CXWINDOWSSCREEN_H
#define CXWINDOWSSCREEN_H
#include "BasicTypes.h"
#include "ClipboardTypes.h"
#include "CMutex.h"
#include <X11/Xlib.h>
class IClipboard;
class CXWindowsClipboard;
class CXWindowsScreen {
public:
CXWindowsScreen();
virtual ~CXWindowsScreen();
protected:
class CDisplayLock {
public:
CDisplayLock(const CXWindowsScreen*);
~CDisplayLock();
operator Display*() const;
private:
const CMutex* m_mutex;
Display* m_display;
};
friend class CDisplayLock;
// open the X display. calls onOpenDisplay() after opening the display,
// getting the screen, its size, and root window. then it starts the
// event thread.
void openDisplay();
// destroy the window and close the display. calls onCloseDisplay()
// after the event thread has been shut down but before the display
// is closed.
void closeDisplay();
// get the opened screen, its size, its root window. to get the
// display create a CDisplayLock object passing this. while the
// object exists no other threads may access the display. do not
// save the Display* beyond the lifetime of the CDisplayLock.
int getScreen() const;
void getScreenSize(SInt32* w, SInt32* h) const;
Window getRoot() const;
// create a cursor that is transparent everywhere
Cursor createBlankCursor() const;
// wait for and get the next X event. cancellable.
bool getEvent(XEvent*) const;
// cause getEvent() to return false immediately and forever after.
// the caller must have locked the display.
void doStop();
// set the contents of the clipboard (i.e. primary selection)
bool setDisplayClipboard(ClipboardID,
const IClipboard* clipboard);
// copy the clipboard contents to clipboard
bool getDisplayClipboard(ClipboardID,
IClipboard* clipboard) const;
// called by openDisplay() to allow subclasses to prepare the display.
// the display is locked and passed to the subclass.
virtual void onOpenDisplay(Display*) = 0;
// called by openDisplay() after onOpenDisplay() to create each clipboard
virtual CXWindowsClipboard*
createClipboard(ClipboardID) = 0;
// called by closeDisplay() to allow subclasses to clean up the display.
// the display is locked and passed to the subclass. note that the
// display may be NULL if the display has unexpectedly disconnected.
virtual void onCloseDisplay(Display*) = 0;
// called if the display is unexpectedly closing. default does nothing.
virtual void onUnexpectedClose();
// called when a clipboard is lost
virtual void onLostClipboard(ClipboardID) = 0;
private:
// internal event processing
bool processEvent(XEvent*);
// 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
static int ioErrorHandler(Display*);
private:
Display* m_display;
int m_screen;
Window m_root;
SInt32 m_w, m_h;
bool m_stop;
// clipboards
CXWindowsClipboard* m_clipboard[kClipboardEnd];
// X is not thread safe
CMutex m_mutex;
// pointer to (singleton) screen. this is only needed by
// ioErrorHandler().
static CXWindowsScreen* s_screen;
};
#endif

234
platform/CXWindowsUtil.cpp Normal file
View File

@@ -0,0 +1,234 @@
#include "CXWindowsUtil.h"
#include "CLog.h"
#include "CThread.h"
#include <assert.h>
#include <X11/Xatom.h>
//
// CXWindowsUtil
//
bool CXWindowsUtil::getWindowProperty(
Display* display,
Window window, Atom property,
CString* data, Atom* type,
int* format, bool deleteProperty)
{
assert(display != NULL);
assert(data != NULL);
Atom actualType;
int actualDatumSize;
// ignore errors. XGetWindowProperty() will report failure.
CXWindowsUtil::CErrorLock lock;
// read the property
const long length = XMaxRequestSize(display);
long offset = 0;
unsigned long bytesLeft = 1;
while (bytesLeft != 0) {
// get more data
unsigned long numItems;
unsigned char* rawData;
const int result = XGetWindowProperty(display, window, property,
offset, length, False, AnyPropertyType,
&actualType, &actualDatumSize,
&numItems, &bytesLeft, &rawData);
if (result != Success || actualType == None || actualDatumSize == 0) {
// failed
return false;
}
// compute bytes read and advance offset
unsigned long numBytes;
switch (actualDatumSize) {
case 8:
default:
numBytes = numItems;
offset += numItems / 4;
break;
case 16:
numBytes = 2 * numItems;
offset += numItems / 2;
break;
case 32:
numBytes = 4 * numItems;
offset += numItems;
break;
}
// append data
data->append((char*)rawData, numBytes);
// done with returned data
XFree(rawData);
}
// delete the property if requested
if (deleteProperty) {
XDeleteProperty(display, window, property);
}
// save property info
if (type != NULL) {
*type = actualType;
}
if (format != NULL) {
*format = static_cast<SInt32>(actualDatumSize);
}
log((CLOG_DEBUG1 "read property %d on window 0x%08x: bytes=%d", property, window, data->size()));
return true;
}
bool CXWindowsUtil::setWindowProperty(
Display* display,
Window window, Atom property,
const void* vdata, UInt32 size,
Atom type, SInt32 format)
{
const UInt32 length = 4 * XMaxRequestSize(display);
const unsigned char* data = reinterpret_cast<const unsigned char*>(vdata);
const UInt32 datumSize = static_cast<UInt32>(format / 8);
// save errors
bool error = false;
CXWindowsUtil::CErrorLock lock(&error);
// how much data to send in first chunk?
UInt32 chunkSize = size;
if (chunkSize > length)
chunkSize = length;
// send first chunk
XChangeProperty(display, window, property,
type, format, PropModeReplace,
data, chunkSize / datumSize);
// append remaining chunks
data += chunkSize;
size -= chunkSize;
while (!error && size > 0) {
chunkSize = size;
if (chunkSize > length)
chunkSize = length;
XChangeProperty(display, window, property,
type, format, PropModeAppend,
data, chunkSize / datumSize);
data += chunkSize;
size -= chunkSize;
}
return !error;
}
Time CXWindowsUtil::getCurrentTime(
Display* display, Window window)
{
// select property events on window
XWindowAttributes attr;
XGetWindowAttributes(display, window, &attr);
XSelectInput(display, window, attr.your_event_mask | PropertyChangeMask);
// make a property name to receive dummy change
Atom atom = XInternAtom(display, "TIMESTAMP", False);
// do a zero-length append to get the current time
unsigned char dummy;
XChangeProperty(display, window, atom,
XA_INTEGER, 8,
PropModeAppend,
&dummy, 0);
// look for property notify events with the following
CPropertyNotifyPredicateInfo filter;
filter.m_window = window;
filter.m_property = atom;
// wait for reply
XEvent xevent;
XIfEvent(display, &xevent, &CXWindowsUtil::propertyNotifyPredicate,
(XPointer)&filter);
assert(xevent.type == PropertyNotify);
assert(xevent.xproperty.window == window);
assert(xevent.xproperty.atom == atom);
// restore event mask
XSelectInput(display, window, attr.your_event_mask);
return xevent.xproperty.time;
}
Bool CXWindowsUtil::propertyNotifyPredicate(
Display*, XEvent* xevent, XPointer arg)
{
CPropertyNotifyPredicateInfo* filter =
reinterpret_cast<CPropertyNotifyPredicateInfo*>(arg);
return (xevent->type == PropertyNotify &&
xevent->xproperty.window == filter->m_window &&
xevent->xproperty.atom == filter->m_property &&
xevent->xproperty.state == PropertyNewValue) ? True : False;
}
//
// CXWindowsUtil::CErrorLock
//
CXWindowsUtil::CErrorLock* CXWindowsUtil::CErrorLock::s_top = NULL;
CXWindowsUtil::CErrorLock::CErrorLock()
{
install(&CXWindowsUtil::CErrorLock::ignoreHandler, NULL);
}
CXWindowsUtil::CErrorLock::CErrorLock(bool* flag)
{
install(&CXWindowsUtil::CErrorLock::saveHandler, flag);
}
CXWindowsUtil::CErrorLock::CErrorLock(ErrorHandler handler, void* data)
{
install(handler, data);
}
CXWindowsUtil::CErrorLock::~CErrorLock()
{
XSetErrorHandler(m_oldXHandler);
s_top = m_next;
}
void CXWindowsUtil::CErrorLock::install(
ErrorHandler handler, void* data)
{
m_handler = handler;
m_userData = data;
m_oldXHandler = XSetErrorHandler(
&CXWindowsUtil::CErrorLock::internalHandler);
m_next = s_top;
s_top = this;
}
int CXWindowsUtil::CErrorLock::internalHandler(
Display* display, XErrorEvent* event)
{
if (s_top != NULL && s_top->m_handler != NULL) {
s_top->m_handler(display, event, s_top->m_userData);
}
return 0;
}
void CXWindowsUtil::CErrorLock::ignoreHandler(
Display*, XErrorEvent*, void*)
{
// do nothing
}
void CXWindowsUtil::CErrorLock::saveHandler(
Display*, XErrorEvent*, void* flag)
{
*reinterpret_cast<bool*>(flag) = true;
}

61
platform/CXWindowsUtil.h Normal file
View File

@@ -0,0 +1,61 @@
#ifndef CXWINDOWSUTIL_H
#define CXWINDOWSUTIL_H
#include "BasicTypes.h"
#include "CString.h"
#include <X11/Xlib.h>
class CXWindowsUtil {
public:
static bool getWindowProperty(Display*,
Window window, Atom property,
CString* data, Atom* type,
SInt32* format, bool deleteProperty);
static bool setWindowProperty(Display*,
Window window, Atom property,
const void* data, UInt32 size,
Atom type, SInt32 format);
static Time getCurrentTime(Display*, Window);
// class to set an X error handler in the c'tor and restore 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:
typedef void (*ErrorHandler)(Display*, XErrorEvent*, void* userData);
CErrorLock();
CErrorLock(bool* errorFlag);
CErrorLock(ErrorHandler, 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*);
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);
};
#endif

45
platform/IPlatform.h Normal file
View File

@@ -0,0 +1,45 @@
#ifndef IPLATFORM_H
#define IPLATFORM_H
#include "BasicTypes.h"
#include "CString.h"
#include "IInterface.h"
class IPlatform : public IInterface {
public:
// manipulators
// install/uninstall a daemon.
// FIXME -- throw on error? will get better error messages that way.
virtual bool installDaemon(/* FIXME */) = 0;
virtual bool uninstallDaemon(/* FIXME */) = 0;
// daemonize. this should have the side effect of sending log
// messages to a system message logger since messages can no
// longer go to the console. returns true iff successful.
// the name is the name of the daemon.
// FIXME -- win32 services will require a more complex interface
virtual bool daemonize(const char* name) = 0;
// accessors
// find the basename in the given pathname
virtual const char* getBasename(const char* pathname) const = 0;
// get the user's home directory. returns the empty string if
// this cannot be determined.
virtual CString getUserDirectory() const = 0;
// get the system configuration file directory
virtual CString getSystemDirectory() const = 0;
// concatenate pathname components with a directory separator
// between them. this should not check if the resulting path
// is longer than allowed by the system. we'll rely on the
// system calls to tell us that.
virtual CString addPathComponent(
const CString& prefix,
const CString& suffix) const = 0;
};
#endif

28
platform/Makefile Normal file
View File

@@ -0,0 +1,28 @@
DEPTH=..
include $(DEPTH)/Makecommon
#
# target file
#
TARGET = platform
#
# source files
#
LCXXINCS = \
-I$(DEPTH)/base \
-I$(DEPTH)/mt \
-I$(DEPTH)/synergy \
$(NULL)
CXXFILES = \
CPlatform.cpp \
CXWindowsClipboard.cpp \
CXWindowsScreen.cpp \
CXWindowsUtil.cpp \
$(NULL)
targets: $(LIBTARGET)
$(LIBTARGET): $(OBJECTS) $(DEPLIBS)
if test ! -d $(LIBDIR); then $(MKDIR) $(LIBDIR); fi
$(ARF) $(LIBTARGET) $(OBJECTS)