mirror of
https://github.com/kanjitalk755/macemu.git
synced 2025-01-12 16:30:44 +00:00
Handle copy-paste between X11 and MacOS. X11 events handling code has to
be improved in copy mode (when we own the selection to service other clients). Also note that older klipper has a tendency to request clipboard data several times per second.
This commit is contained in:
parent
70c1d04b2a
commit
ea33a5c8b0
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* clip_unix.cpp - Clipboard handling, Unix implementation
|
||||
*
|
||||
* SheepShaver (C) 1997-2002 Christian Bauer and Marc Hellwig
|
||||
* SheepShaver (C) 1997-2003 Christian Bauer and Marc Hellwig
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -21,13 +21,42 @@
|
||||
#include "sysdeps.h"
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <pthread.h>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "macos_util.h"
|
||||
#include "clip.h"
|
||||
#include "prefs.h"
|
||||
#include "cpu_emulation.h"
|
||||
#include "main.h"
|
||||
#include "emul_op.h"
|
||||
|
||||
#define DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
#ifndef NO_STD_NAMESPACE
|
||||
using std::set;
|
||||
using std::vector;
|
||||
#endif
|
||||
|
||||
|
||||
// Do we replace PutScrap()?
|
||||
#define REPLACE_PUTSCRAP 1
|
||||
|
||||
// Do we replace GetScrap()?
|
||||
#define REPLACE_GETSCRAP 1
|
||||
|
||||
// Do we want PutScrap() to ignore requests from klipper?
|
||||
#define PUTSCRAP_IGNORES_KLIPPER 0
|
||||
|
||||
// Do we want GetScrap() to check for TIMESTAMP and optimize out clipboard syncs?
|
||||
#define GETSCRAP_REQUESTS_TIMESTAMP 0
|
||||
|
||||
// Do we want GetScrap() to first check for TARGETS available from the clipboard?
|
||||
#define GETSCRAP_REQUESTS_TARGETS 0
|
||||
|
||||
|
||||
// From main_linux.cpp
|
||||
extern Display *x_display;
|
||||
@ -53,6 +82,224 @@ static const uint8 mac2iso[0x80] = {
|
||||
0xaf, 0x20, 0xb7, 0xb0, 0xb8, 0x22, 0xb8, 0x20
|
||||
};
|
||||
|
||||
static const uint8 iso2mac[0x80] = {
|
||||
0xad, 0xb0, 0xe2, 0xc4, 0xe3, 0xc9, 0xa0, 0xe0,
|
||||
0xf6, 0xe4, 0xde, 0xdc, 0xce, 0xb2, 0xb3, 0xb6,
|
||||
0xb7, 0xd4, 0xd5, 0xd2, 0xd3, 0xa5, 0xd0, 0xd1,
|
||||
0xf7, 0xaa, 0xdf, 0xdd, 0xcf, 0xba, 0xfd, 0xd9,
|
||||
0xca, 0xc1, 0xa2, 0xa3, 0xdb, 0xb4, 0xbd, 0xa4,
|
||||
0xac, 0xa9, 0xbb, 0xc7, 0xc2, 0xf0, 0xa8, 0xf8,
|
||||
0xa1, 0xb1, 0xc3, 0xc5, 0xab, 0xb5, 0xa6, 0xe1,
|
||||
0xfc, 0xc6, 0xbc, 0xc8, 0xf9, 0xda, 0xd7, 0xc0,
|
||||
0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xae, 0x82,
|
||||
0xe9, 0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec,
|
||||
0xf5, 0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0xfb,
|
||||
0xaf, 0xf4, 0xf2, 0xf3, 0x86, 0xfa, 0xb8, 0xa7,
|
||||
0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c, 0xbe, 0x8d,
|
||||
0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95,
|
||||
0xfe, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6,
|
||||
0xbf, 0x9d, 0x9c, 0x9e, 0x9f, 0xff, 0xb9, 0xd8
|
||||
};
|
||||
|
||||
// Flag: Don't convert clipboard text
|
||||
static bool no_clip_conversion;
|
||||
|
||||
// Flag for PutScrap(): the data was put by GetScrap(), don't bounce it back to the Be side
|
||||
static bool we_put_this_data = false;
|
||||
|
||||
// X11 variables
|
||||
static int screen; // Screen number
|
||||
static Window rootwin, clip_win; // Root window and the clipboard window
|
||||
static Atom xa_clipboard;
|
||||
static Atom xa_targets;
|
||||
static Atom xa_multiple;
|
||||
static Atom xa_timestamp;
|
||||
static Atom xa_atom_pair;
|
||||
|
||||
// Define a byte array (rewrite if it's a bottleneck)
|
||||
struct ByteArray : public vector<uint8> {
|
||||
void resize(int size) { reserve(size); vector<uint8>::resize(size); }
|
||||
uint8 *data() { return &at(0); }
|
||||
};
|
||||
|
||||
// Clipboard locks
|
||||
#ifdef HAVE_PTHREADS
|
||||
static pthread_mutex_t clip_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
#define LOCK_CLIPBOARD pthread_mutex_lock(&clip_lock);
|
||||
#define UNLOCK_CLIPBOARD pthread_mutex_unlock(&clip_lock);
|
||||
#elif defined(HAVE_SPINLOCKS)
|
||||
static spinlock_t clip_lock = SPIN_LOCK_UNLOCKED;
|
||||
#define LOCK_CLIPBOARD spin_lock(&clip_lock)
|
||||
#define UNLOCK_CLIPBOARD spin_unlock(&clip_lock)
|
||||
#else
|
||||
#define LOCK_CLIPBOARD
|
||||
#define UNLOCK_CLIPBOARD
|
||||
#endif
|
||||
|
||||
// Clipboard data
|
||||
struct ClipboardData {
|
||||
Time time;
|
||||
uint32 type;
|
||||
ByteArray data;
|
||||
};
|
||||
static ClipboardData clip_data;
|
||||
|
||||
|
||||
/*
|
||||
* Read an X11 property (hack from QT 3.1.2)
|
||||
*/
|
||||
|
||||
static inline int max_selection_incr(Display *dpy)
|
||||
{
|
||||
int max_request_size = 4 * XMaxRequestSize(dpy);
|
||||
if (max_request_size > 4 * 65536)
|
||||
max_request_size = 4 * 65536;
|
||||
else if ((max_request_size -= 100) < 0)
|
||||
max_request_size = 100;
|
||||
return max_request_size;
|
||||
}
|
||||
|
||||
static bool read_property(Display *dpy, Window win,
|
||||
Atom property, bool deleteProperty,
|
||||
ByteArray & buffer, int *size, Atom *type,
|
||||
int *format, bool nullterm)
|
||||
{
|
||||
int maxsize = max_selection_incr(dpy);
|
||||
unsigned long bytes_left;
|
||||
unsigned long length;
|
||||
unsigned char *data;
|
||||
Atom dummy_type;
|
||||
int dummy_format;
|
||||
|
||||
if (!type)
|
||||
type = &dummy_type;
|
||||
if (!format)
|
||||
format = &dummy_format;
|
||||
|
||||
// Don't read anything but get the size of the property data
|
||||
if (XGetWindowProperty(dpy, win, property, 0, 0, False,
|
||||
AnyPropertyType, type, format, &length, &bytes_left, &data) != Success) {
|
||||
buffer.clear();
|
||||
return false;
|
||||
}
|
||||
XFree(data);
|
||||
|
||||
int offset = 0, buffer_offset = 0, format_inc = 1, proplen = bytes_left;
|
||||
|
||||
switch (*format) {
|
||||
case 16:
|
||||
format_inc = sizeof(short) / 2;
|
||||
proplen *= format_inc;
|
||||
break;
|
||||
case 32:
|
||||
format_inc = sizeof(long) / 4;
|
||||
proplen *= format_inc;
|
||||
break;
|
||||
}
|
||||
|
||||
buffer.resize(proplen + (nullterm ? 1 : 0));
|
||||
while (bytes_left) {
|
||||
if (XGetWindowProperty(dpy, win, property, offset, maxsize / 4,
|
||||
False, AnyPropertyType, type, format,
|
||||
&length, &bytes_left, &data) != Success)
|
||||
break;
|
||||
|
||||
offset += length / (32 / *format);
|
||||
length *= format_inc * (*format / 8);
|
||||
|
||||
memcpy(buffer.data() + buffer_offset, data, length);
|
||||
buffer_offset += length;
|
||||
XFree(data);
|
||||
}
|
||||
|
||||
if (nullterm)
|
||||
buffer[buffer_offset] = '\0';
|
||||
|
||||
if (size)
|
||||
*size = buffer_offset;
|
||||
|
||||
if (deleteProperty)
|
||||
XDeleteProperty(dpy, win, property);
|
||||
|
||||
XFlush(dpy);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Timed wait for an SelectionNotify event
|
||||
*/
|
||||
|
||||
static const uint64 SELECTION_MAX_WAIT = 500000; // 500 ms
|
||||
static volatile bool xselection_event_avail;
|
||||
static XSelectionEvent xselection_event;
|
||||
|
||||
static bool wait_for_selection_notify_event(Display *dpy, Window win, int timeout)
|
||||
{
|
||||
uint64 start = GetTicks_usec();
|
||||
|
||||
LOCK_CLIPBOARD;
|
||||
xselection_event_avail = false;
|
||||
UNLOCK_CLIPBOARD;
|
||||
|
||||
// First wait a very short period of time < the VideoRefresh() resolution
|
||||
struct timespec req = {0, 5000000};
|
||||
nanosleep(&req, NULL);
|
||||
|
||||
// FIXME: Since handle_events() is in a separate thread, wait for
|
||||
// a SelectionNotify event to occur and reported here. The
|
||||
// resolution of this wait action should match that of VideoRefresh()
|
||||
if (xselection_event_avail)
|
||||
return true;
|
||||
|
||||
do {
|
||||
// Wait
|
||||
struct timespec req = {0, 16666667};
|
||||
nanosleep(&req, NULL);
|
||||
|
||||
// Return immediately if the event is now available
|
||||
if (xselection_event_avail)
|
||||
return true;
|
||||
|
||||
} while ((GetTicks_usec() - start) < timeout);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Check for a "klipper" window which, in older versions (3.1), was
|
||||
* polling and retrieving clipboard data several times per second.
|
||||
*/
|
||||
|
||||
static bool is_klipper_window_probe(Window w);
|
||||
static bool is_klipper_window_check(Window w);
|
||||
static bool (*is_klipper_window)(Window w) = is_klipper_window_probe;
|
||||
static Window klipper_win = None;
|
||||
|
||||
static bool is_klipper_window_probe(Window w)
|
||||
{
|
||||
// We expect "klipper" to be within the first clients to request
|
||||
// data from our clipboard
|
||||
static int clients_countdown = 2;
|
||||
|
||||
bool found = false;
|
||||
char *window_name;
|
||||
if (XFetchName(x_display, w, &window_name) && !strcmp(window_name, "klipper")) {
|
||||
D(bug(" found and ignore clipboard requests from klipper window id: 0x%08x\n", w));
|
||||
klipper_win = w;
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (found || --clients_countdown <= 0)
|
||||
is_klipper_window = is_klipper_window_check;
|
||||
}
|
||||
|
||||
static bool is_klipper_window_check(Window w)
|
||||
{
|
||||
return w == klipper_win;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialization
|
||||
@ -60,6 +307,21 @@ static const uint8 mac2iso[0x80] = {
|
||||
|
||||
void ClipInit(void)
|
||||
{
|
||||
no_clip_conversion = PrefsFindBool("noclipconversion");
|
||||
|
||||
// Find screen and root window
|
||||
screen = XDefaultScreen(x_display);
|
||||
rootwin = XRootWindow(x_display, screen);
|
||||
|
||||
// Create fake window to receive selection events
|
||||
clip_win = XCreateSimpleWindow(x_display, rootwin, 0, 0, 1, 1, 0, 0, 0);
|
||||
|
||||
// Initialize X11 atoms
|
||||
xa_clipboard = XInternAtom(x_display, "CLIPBOARD", False);
|
||||
xa_targets = XInternAtom(x_display, "TARGETS", False);
|
||||
xa_multiple = XInternAtom(x_display, "MULTIPLE", False);
|
||||
xa_timestamp = XInternAtom(x_display, "TIMESTAMP", False);
|
||||
xa_atom_pair = XInternAtom(x_display, "ATOM_PAIR", False);
|
||||
}
|
||||
|
||||
|
||||
@ -69,6 +331,8 @@ void ClipInit(void)
|
||||
|
||||
void ClipExit(void)
|
||||
{
|
||||
// Close window
|
||||
XDestroyWindow(x_display, clip_win);
|
||||
}
|
||||
|
||||
|
||||
@ -79,32 +343,44 @@ void ClipExit(void)
|
||||
void PutScrap(uint32 type, void *scrap, int32 length)
|
||||
{
|
||||
D(bug("PutScrap type %08lx, data %p, length %ld\n", type, scrap, length));
|
||||
if (!REPLACE_PUTSCRAP)
|
||||
return;
|
||||
if (we_put_this_data) {
|
||||
we_put_this_data = false;
|
||||
return;
|
||||
}
|
||||
if (length <= 0)
|
||||
return;
|
||||
|
||||
bool did_putscrap = false;
|
||||
switch (type) {
|
||||
case FOURCC('T','E','X','T'):
|
||||
D(bug(" clipping TEXT\n"));
|
||||
clip_data.type = type;
|
||||
clip_data.data.clear();
|
||||
clip_data.data.reserve(length);
|
||||
|
||||
// Convert text from Mac charset to ISO-Latin1
|
||||
uint8 *buf = new uint8[length];
|
||||
uint8 *p = (uint8 *)scrap;
|
||||
uint8 *q = buf;
|
||||
for (int i=0; i<length; i++) {
|
||||
uint8 c = *p++;
|
||||
if (c < 0x80) {
|
||||
if (c == 13) // CR -> LF
|
||||
c = 10;
|
||||
} else
|
||||
} else if (!no_clip_conversion)
|
||||
c = mac2iso[c & 0x7f];
|
||||
*q++ = c;
|
||||
clip_data.data.push_back(c);
|
||||
}
|
||||
|
||||
// Put text into cut buffer
|
||||
//!! XStoreBytes(x_display, buf, length);
|
||||
delete[] buf;
|
||||
did_putscrap = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Acquire selection ownership
|
||||
if (did_putscrap) {
|
||||
clip_data.time = CurrentTime;
|
||||
while (XGetSelectionOwner(x_display, xa_clipboard) != clip_win)
|
||||
XSetSelectionOwner(x_display, xa_clipboard, clip_win, clip_data.time);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -115,10 +391,305 @@ void PutScrap(uint32 type, void *scrap, int32 length)
|
||||
void GetScrap(void **handle, uint32 type, int32 offset)
|
||||
{
|
||||
D(bug("GetScrap handle %p, type %08x, offset %d\n", handle, type, offset));
|
||||
switch (type) {
|
||||
if (!REPLACE_GETSCRAP)
|
||||
return;
|
||||
|
||||
// Check TIMESTAMP
|
||||
#if GETSCRAP_REQUESTS_TIMESTAMP
|
||||
static Time last_timestamp = 0;
|
||||
XConvertSelection(x_display, xa_clipboard, xa_timestamp, xa_clipboard, clip_win, CurrentTime);
|
||||
if (wait_for_selection_notify_event(x_display, clip_win, SELECTION_MAX_WAIT) &&
|
||||
xselection_event.property != None) {
|
||||
ByteArray data;
|
||||
if (read_property(x_display,
|
||||
xselection_event.requestor, xselection_event.property,
|
||||
true, data, 0, 0, 0, false)) {
|
||||
Time timestamp = ((long *)data.data())[0];
|
||||
if (timestamp == last_timestamp)
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Get TARGETS available
|
||||
#if GETSCRAP_REQUESTS_TARGETS
|
||||
XConvertSelection(x_display, xa_clipboard, xa_targets, xa_clipboard, clip_win, CurrentTime);
|
||||
if (!wait_for_selection_notify_event(x_display, clip_win, SELECTION_MAX_WAIT) ||
|
||||
xselection_event.property == None)
|
||||
return;
|
||||
|
||||
ByteArray data;
|
||||
if (!read_property(x_display,
|
||||
xselection_event.requestor, xselection_event.property,
|
||||
true, data, 0, 0, 0, false))
|
||||
return;
|
||||
#endif
|
||||
|
||||
// Get appropriate format for requested data
|
||||
Atom format = None;
|
||||
#if GETSCRAP_REQUESTS_TARGETS
|
||||
int n_atoms = data.size() / sizeof(long);
|
||||
long *atoms = (long *)data.data();
|
||||
for (int i = 0; i < n_atoms; i++) {
|
||||
Atom target = atoms[i];
|
||||
switch (type) {
|
||||
case FOURCC('T','E','X','T'):
|
||||
D(bug(" clipping TEXT\n"));
|
||||
//!!
|
||||
if (target == XA_STRING)
|
||||
format = target;
|
||||
break;
|
||||
case FOURCC('P','I','C','T'):
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
switch (type) {
|
||||
case FOURCC('T','E','X','T'):
|
||||
D(bug(" clipping TEXT\n"));
|
||||
format = XA_STRING;
|
||||
break;
|
||||
case FOURCC('P','I','C','T'):
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
if (format == None)
|
||||
return;
|
||||
|
||||
// Get the native clipboard data
|
||||
XConvertSelection(x_display, xa_clipboard, format, xa_clipboard, clip_win, CurrentTime);
|
||||
if (!wait_for_selection_notify_event(x_display, clip_win, SELECTION_MAX_WAIT) ||
|
||||
xselection_event.property == None ||
|
||||
!read_property(x_display,
|
||||
xselection_event.requestor, xselection_event.property,
|
||||
false, clip_data.data, 0, 0, 0, format == XA_STRING))
|
||||
return;
|
||||
|
||||
clip_data.type = type;
|
||||
clip_data.time = xselection_event.time;
|
||||
|
||||
// Allocate space for new scrap in MacOS side
|
||||
M68kRegisters r;
|
||||
r.d[0] = clip_data.data.size();
|
||||
Execute68kTrap(0xa71e, &r); // NewPtrSysClear()
|
||||
uint32 scrap_area = r.a[0];
|
||||
|
||||
if (scrap_area) {
|
||||
switch (type) {
|
||||
case FOURCC('T','E','X','T'):
|
||||
// Convert text from ISO-Latin1 to Mac charset
|
||||
uint8 *p = Mac2HostAddr(scrap_area);
|
||||
for (int i = 0; i < clip_data.data.size(); i++) {
|
||||
uint8 c = clip_data.data[i];
|
||||
if (c < 0x80) {
|
||||
if (c == 10) // LF -> CR
|
||||
c = 13;
|
||||
} else if (!no_clip_conversion)
|
||||
c = iso2mac[c & 0x7f];
|
||||
*p++ = c;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Add new data to clipboard
|
||||
static uint8 proc[] = {
|
||||
0x59, 0x8f, // subq.l #4,sp
|
||||
0xa9, 0xfc, // ZeroScrap()
|
||||
0x2f, 0x3c, 0, 0, 0, 0, // move.l #length,-(sp)
|
||||
0x2f, 0x3c, 0, 0, 0, 0, // move.l #type,-(sp)
|
||||
0x2f, 0x3c, 0, 0, 0, 0, // move.l #outbuf,-(sp)
|
||||
0xa9, 0xfe, // PutScrap()
|
||||
0x58, 0x8f, // addq.l #4,sp
|
||||
M68K_RTS >> 8, M68K_RTS
|
||||
};
|
||||
uint32 proc_area = (uint32)proc; // FIXME: make sure 32-bit relocs are used
|
||||
WriteMacInt32(proc_area + 6, clip_data.data.size());
|
||||
WriteMacInt32(proc_area + 12, type);
|
||||
WriteMacInt32(proc_area + 18, scrap_area);
|
||||
we_put_this_data = true;
|
||||
Execute68k(proc_area, &r);
|
||||
|
||||
// We are done with scratch memory
|
||||
r.a[0] = scrap_area;
|
||||
Execute68kTrap(0xa01f, &r); // DisposePtr
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Handle X11 selection events
|
||||
*/
|
||||
|
||||
void ClipboardSelectionClear(XSelectionClearEvent *xev)
|
||||
{
|
||||
if (xev->selection != xa_clipboard)
|
||||
return;
|
||||
|
||||
D(bug("Selection cleared, lost clipboard ownership\n"));
|
||||
clip_data.type = 0;
|
||||
clip_data.data.clear();
|
||||
}
|
||||
|
||||
// Top level selection handler
|
||||
static bool handle_selection(XSelectionRequestEvent *req, bool is_multiple);
|
||||
|
||||
static bool handle_selection_TIMESTAMP(XSelectionRequestEvent *req)
|
||||
{
|
||||
// 32-bit integer values are always passed as a long whatever is its size
|
||||
long timestamp = clip_data.time;
|
||||
|
||||
// Change requestor property
|
||||
XChangeProperty(x_display, req->requestor, req->property,
|
||||
XA_INTEGER, 32,
|
||||
PropModeReplace, (uint8 *)×tamp, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool handle_selection_TARGETS(XSelectionRequestEvent *req)
|
||||
{
|
||||
// Default supported targets
|
||||
vector<long> targets;
|
||||
targets.push_back(xa_targets);
|
||||
targets.push_back(xa_multiple);
|
||||
targets.push_back(xa_timestamp);
|
||||
|
||||
// Extra targets matchin current clipboard data
|
||||
switch (clip_data.type) {
|
||||
case FOURCC('T','E','X','T'):
|
||||
targets.push_back(XA_STRING);
|
||||
break;
|
||||
case FOURCC('P','I','C','T'):
|
||||
break;
|
||||
}
|
||||
|
||||
// Change requestor property
|
||||
XChangeProperty(x_display, req->requestor, req->property,
|
||||
xa_targets, 32,
|
||||
PropModeReplace, (uint8 *)&targets.at(0), targets.size());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool handle_selection_STRING(XSelectionRequestEvent *req)
|
||||
{
|
||||
// Make sure we have valid data to send though ICCCM compliant
|
||||
// clients should have first requested TARGETS to identify
|
||||
// this possibility.
|
||||
if (clip_data.type != FOURCC('T','E','X','T'))
|
||||
return false;
|
||||
|
||||
// Send the string, it's already encoded as ISO-8859-1
|
||||
XChangeProperty(x_display, req->requestor, req->property,
|
||||
XA_STRING, 8,
|
||||
PropModeReplace, (uint8 *)clip_data.data.data(), clip_data.data.size());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool handle_selection_MULTIPLE(XSelectionRequestEvent *req)
|
||||
{
|
||||
Atom rtype;
|
||||
int rformat;
|
||||
ByteArray data;
|
||||
|
||||
if (!read_property(x_display, req->requestor, req->property,
|
||||
false, data, 0, &rtype, &rformat, 0))
|
||||
return false;
|
||||
|
||||
// rtype should be ATOM_PAIR but some clients don't honour that
|
||||
if (rformat != 32)
|
||||
return false;
|
||||
|
||||
struct AtomPair { long target; long property; };
|
||||
AtomPair *atom_pairs = (AtomPair *)data.data();
|
||||
int n_atom_pairs = data.size() / sizeof(AtomPair);
|
||||
|
||||
bool handled = true;
|
||||
if (n_atom_pairs) {
|
||||
// Setup a new XSelectionRequestEvent when servicing individual requests
|
||||
XSelectionRequestEvent event;
|
||||
memcpy(&event, req, sizeof(event));
|
||||
|
||||
for (int i = 0; i < n_atom_pairs; i++) {
|
||||
Atom target = atom_pairs[i].target;
|
||||
Atom property = atom_pairs[i].property;
|
||||
|
||||
// None is not a valid property
|
||||
if (property == None)
|
||||
continue;
|
||||
|
||||
// Service this request
|
||||
event.target = target;
|
||||
event.property = property;
|
||||
if (!handle_selection(&event, true)) {
|
||||
/* FIXME: ICCCM 2.6.2:
|
||||
|
||||
If the owner fails to convert the target named by an
|
||||
atom in the MULTIPLE property, it should replace that
|
||||
atom in the property with None.
|
||||
*/
|
||||
handled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
static bool handle_selection(XSelectionRequestEvent *req, bool is_multiple)
|
||||
{
|
||||
bool handled =false;
|
||||
|
||||
if (req->target == xa_timestamp)
|
||||
handled = handle_selection_TIMESTAMP(req);
|
||||
else if (req->target == xa_targets)
|
||||
handled = handle_selection_TARGETS(req);
|
||||
else if (req->target == XA_STRING)
|
||||
handled = handle_selection_STRING(req);
|
||||
else if (req->target == xa_multiple)
|
||||
handled = handle_selection_MULTIPLE(req);
|
||||
|
||||
// Notify requestor only when we are done with his request
|
||||
if (handled && !is_multiple) {
|
||||
XEvent out_event;
|
||||
out_event.xselection.type = SelectionNotify;
|
||||
out_event.xselection.requestor = req->requestor;
|
||||
out_event.xselection.selection = req->selection;
|
||||
out_event.xselection.target = req->target;
|
||||
out_event.xselection.property = handled ? req->property : None;
|
||||
out_event.xselection.time = req->time;
|
||||
XSendEvent(x_display, req->requestor, False, 0, &out_event);
|
||||
}
|
||||
}
|
||||
|
||||
void ClipboardSelectionRequest(XSelectionRequestEvent *req)
|
||||
{
|
||||
if (req->requestor == clip_win || req->selection != xa_clipboard)
|
||||
return;
|
||||
|
||||
#if PUTSCRAP_IGNORES_KLIPPER
|
||||
if (is_klipper_window(req->requestor))
|
||||
return;
|
||||
#endif
|
||||
|
||||
D(bug("Selection requested from 0x%lx to 0x%lx (%s) 0x%lx (%s)\n",
|
||||
req->requestor,
|
||||
req->selection,
|
||||
XGetAtomName(req->display, req->selection),
|
||||
req->target,
|
||||
XGetAtomName(req->display, req->target)));
|
||||
|
||||
handle_selection(req, false);
|
||||
}
|
||||
|
||||
void ClipboardSelectionNotify(XSelectionEvent *req)
|
||||
{
|
||||
if (req->requestor != clip_win || req->selection != xa_clipboard)
|
||||
return;
|
||||
|
||||
D(bug("Selection is now available\n"));
|
||||
memcpy(&xselection_event, req, sizeof(xselection_event));
|
||||
LOCK_CLIPBOARD;
|
||||
xselection_event_avail = true;
|
||||
UNLOCK_CLIPBOARD;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* video_x.cpp - Video/graphics emulation, X11 specific stuff
|
||||
*
|
||||
* SheepShaver (C) 1997-2002 Marc Hellwig and Christian Bauer
|
||||
* SheepShaver (C) 1997-2003 Marc Hellwig and Christian Bauer
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -135,6 +135,11 @@ extern Display *x_display;
|
||||
// From sys_unix.cpp
|
||||
extern void SysMountFirstFloppy(void);
|
||||
|
||||
// From clip_unix.cpp
|
||||
extern void ClipboardSelectionClear(XSelectionClearEvent *);
|
||||
extern void ClipboardSelectionRequest(XSelectionRequestEvent *);
|
||||
extern void ClipboardSelectionNotify(XSelectionEvent *req);
|
||||
|
||||
|
||||
// Video acceleration through SIGSEGV
|
||||
#ifdef ENABLE_VOSF
|
||||
@ -1181,8 +1186,16 @@ static void handle_events(void)
|
||||
for (;;) {
|
||||
XEvent event;
|
||||
|
||||
if (!XCheckMaskEvent(x_display, eventmask, &event))
|
||||
if (!XCheckMaskEvent(x_display, eventmask, &event)) {
|
||||
// Handle clipboard events
|
||||
if (XCheckTypedEvent(x_display, SelectionRequest, &event))
|
||||
ClipboardSelectionRequest(&event.xselectionrequest);
|
||||
else if (XCheckTypedEvent(x_display, SelectionClear, &event))
|
||||
ClipboardSelectionClear(&event.xselectionclear);
|
||||
else if (XCheckTypedEvent(x_display, SelectionNotify, &event))
|
||||
ClipboardSelectionNotify(&event.xselection);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
// Mouse button
|
||||
|
@ -51,6 +51,7 @@ prefs_desc common_prefs_items[] = {
|
||||
{"nonet", TYPE_BOOLEAN, false, "don't use Ethernet"},
|
||||
{"nosound", TYPE_BOOLEAN, false, "don't enable sound output"},
|
||||
{"nogui", TYPE_BOOLEAN, false, "disable GUI"},
|
||||
{"noclipconversion", TYPE_BOOLEAN, false, "don't convert clipboard contents"},
|
||||
{"ignoresegv", TYPE_BOOLEAN, false, "ignore illegal memory accesses"},
|
||||
{"jit", TYPE_BOOLEAN, false, "enable JIT compiler"},
|
||||
{NULL, TYPE_END, false, NULL} // End of list
|
||||
@ -72,6 +73,7 @@ void AddPrefsDefaults(void)
|
||||
PrefsAddBool("nonet", false);
|
||||
PrefsAddBool("nosound", false);
|
||||
PrefsAddBool("nogui", false);
|
||||
PrefsAddBool("noclipconversion", false);
|
||||
PrefsAddBool("ignoresegv", true);
|
||||
|
||||
#if USE_JIT
|
||||
|
Loading…
x
Reference in New Issue
Block a user