mirror of
https://github.com/debauchee/barrier.git
synced 2026-05-10 00:11:43 +08:00
refactored some common platform dependent stuff into a new
library: platform. also removed test.cpp.
This commit is contained in:
199
platform/CMSWindowsClipboard.cpp
Normal file
199
platform/CMSWindowsClipboard.cpp
Normal 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;
|
||||
}
|
||||
31
platform/CMSWindowsClipboard.h
Normal file
31
platform/CMSWindowsClipboard.h
Normal 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
|
||||
170
platform/CMSWindowsScreen.cpp
Normal file
170
platform/CMSWindowsScreen.cpp
Normal 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);
|
||||
}
|
||||
78
platform/CMSWindowsScreen.h
Normal file
78
platform/CMSWindowsScreen.h
Normal 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
11
platform/CPlatform.cpp
Normal 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
18
platform/CPlatform.h
Normal 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
160
platform/CUnixPlatform.cpp
Normal 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
29
platform/CUnixPlatform.h
Normal 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
|
||||
90
platform/CWin32Platform.cpp
Normal file
90
platform/CWin32Platform.cpp
Normal 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
26
platform/CWin32Platform.h
Normal 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
|
||||
1342
platform/CXWindowsClipboard.cpp
Normal file
1342
platform/CXWindowsClipboard.cpp
Normal file
File diff suppressed because it is too large
Load Diff
267
platform/CXWindowsClipboard.h
Normal file
267
platform/CXWindowsClipboard.h
Normal 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
|
||||
385
platform/CXWindowsScreen.cpp
Normal file
385
platform/CXWindowsScreen.cpp
Normal 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
122
platform/CXWindowsScreen.h
Normal 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
234
platform/CXWindowsUtil.cpp
Normal 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
61
platform/CXWindowsUtil.h
Normal 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
45
platform/IPlatform.h
Normal 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
28
platform/Makefile
Normal 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)
|
||||
Reference in New Issue
Block a user