mirror of
https://github.com/debauchee/barrier.git
synced 2026-02-07 20:34:28 +08:00
be accessed now between open()/close(). ownership of the clipboard is asserted via the empty() method. this parallels the win32 model (but the win32 code hasn't been updated yet). refactored X11 clipboard code. moved the bulk of it into CXWindowsClipboard and moved some comment event handling into CXWindowsScreen. changed how requests are processed into a hopefully easier to understand model. added support for getting clipboard from and sending clipboard to motif (or at least lesstif) clients. sending to lesstif required a hack to work around an apparent bug in lesstif.
352 lines
8.0 KiB
C++
352 lines
8.0 KiB
C++
#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 <string.h>
|
|
#include <assert.h>
|
|
|
|
//
|
|
// CXWindowsScreen
|
|
//
|
|
|
|
CXWindowsScreen::CXWindowsScreen() :
|
|
m_display(NULL),
|
|
m_root(None),
|
|
m_w(0), m_h(0),
|
|
m_stop(false)
|
|
{
|
|
// do nothing
|
|
}
|
|
|
|
CXWindowsScreen::~CXWindowsScreen()
|
|
{
|
|
assert(m_display == NULL);
|
|
}
|
|
|
|
void CXWindowsScreen::openDisplay()
|
|
{
|
|
assert(m_display == NULL);
|
|
|
|
// open the display
|
|
log((CLOG_DEBUG "XOpenDisplay(%s)", "NULL"));
|
|
m_display = XOpenDisplay(NULL); // FIXME -- allow non-default
|
|
if (m_display == NULL)
|
|
throw int(5); // FIXME -- make exception for this
|
|
|
|
// 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();
|
|
|
|
// initialize clipboards
|
|
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
|
|
m_clipboard[id] = createClipboard(id);
|
|
}
|
|
}
|
|
|
|
void CXWindowsScreen::closeDisplay()
|
|
{
|
|
assert(m_display != NULL);
|
|
|
|
// let subclass close down display
|
|
onCloseDisplay();
|
|
|
|
// destroy clipboards
|
|
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
|
|
delete m_clipboard[id];
|
|
}
|
|
|
|
// close the display
|
|
XCloseDisplay(m_display);
|
|
m_display = NULL;
|
|
log((CLOG_DEBUG "closed display"));
|
|
}
|
|
|
|
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.05);
|
|
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()
|
|
{
|
|
CLock lock(&m_mutex);
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// 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;
|
|
}
|