xserver/hw/dmx/dmxselection.c
David Reveman 2f19aa9ab0 Improve selection handling.
Dynamically create proxy windows as needed. Handle multiple
simultaneous selection conversions of the same selection
properly.
2008-11-07 11:24:45 -05:00

1539 lines
34 KiB
C

/*
* Copyright © 2008 Novell, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without
* fee, provided that the above copyright notice appear in all copies
* and that both that copyright notice and this permission notice
* appear in supporting documentation, and that the name of
* Novell, Inc. not be used in advertising or publicity pertaining to
* distribution of the software without specific, written prior permission.
* Novell, Inc. makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
*
* NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
* NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Author: David Reveman <davidr@novell.com>
*/
#ifdef HAVE_DMX_CONFIG_H
#include <dmx-config.h>
#endif
#include "dmx.h"
#include "dmxlog.h"
#include "dmxatom.h"
#include "dmxwindow.h"
#include "dmxscrinit.h"
#include "dmxsync.h"
#include "dmxdnd.h"
#include "dmxselection.h"
#include "selection.h"
#ifdef PANORAMIX
#include "panoramiX.h"
#include "panoramiXsrv.h"
#endif
#define DMX_SELECTION_TIMEOUT (30 * 1000) /* 30 seconds */
typedef struct _DMXSelection {
struct _DMXSelection *next;
XID wid;
XID requestor;
Atom selection;
Atom target;
Atom property;
Time time;
Bool incr;
struct {
unsigned int in;
unsigned int out;
} value[MAXSCREENS];
OsTimerPtr timer;
} DMXSelection;
static DMXSelection *convHead = NULL;
static DMXSelection *propHead = NULL;
static DMXSelection *reqHead = NULL;
static DMXSelection *
dmxUnhookSelection (DMXSelection **head,
DMXSelection *s)
{
DMXSelection *p, *prev = NULL;
for (p = *head; p; p = p->next)
{
if (p == s)
break;
prev = p;
}
assert (p);
if (prev)
prev->next = s->next;
else
*head = s->next;
s->next = NULL;
TimerCancel (s->timer);
return s;
}
static int
dmxSelectionDeleteConv (DMXSelection *s)
{
WindowPtr pWin;
xEvent event;
event.u.u.type = SelectionNotify;
event.u.selectionNotify.time = s->time;
event.u.selectionNotify.requestor = s->wid;
event.u.selectionNotify.selection = s->selection;
event.u.selectionNotify.target = s->target;
event.u.selectionNotify.property = None;
if (dixLookupWindow (&pWin,
s->wid,
serverClient,
DixReadAccess) == Success)
DeliverEventsToWindow (inputInfo.pointer, pWin, &event, 1,
NoEventMask, NullGrab, 0);
if (s->timer)
TimerFree (s->timer);
xfree (s);
return 0;
}
static int
dmxSelectionDeleteProp (DMXSelection *s)
{
int i;
for (i = 0; i < dmxNumScreens; i++)
{
DMXScreenInfo *dmxScreen = &dmxScreens[i];
if (s->incr && s->value[i].out)
{
const uint32_t value = 0;
xcb_change_window_attributes (dmxScreen->connection,
s->value[i].out,
XCB_CW_EVENT_MASK,
&value);
}
}
if (s->timer)
TimerFree (s->timer);
xfree (s);
return 0;
}
static int
dmxSelectionDeleteReq (DMXSelection *s)
{
int i;
for (i = 0; i < dmxNumScreens; i++)
{
if (s->value[i].out)
{
DMXScreenInfo *dmxScreen = &dmxScreens[i];
const uint32_t value = 0;
xcb_change_window_attributes (dmxScreen->connection,
s->value[i].out,
XCB_CW_EVENT_MASK,
&value);
}
}
if (s->wid)
{
#ifdef PANORAMIX
if (!noPanoramiXExtension)
{
PanoramiXRes *win;
win = (PanoramiXRes *) SecurityLookupIDByType (serverClient,
s->wid,
XRT_WINDOW,
DixDestroyAccess);
if (win)
{
int j;
FOR_NSCREENS_BACKWARD(j) {
FreeResource (win->info[j].id, RT_NONE);
}
}
}
else
#endif
FreeResource (s->wid, RT_NONE);
}
if (s->timer)
TimerFree (s->timer);
xfree (s);
return 0;
}
static CARD32
dmxSelectionCallback (OsTimerPtr timer,
CARD32 time,
pointer arg)
{
DMXSelection *r = (DMXSelection *) arg;
DMXSelection *s;
if (time)
dmxLog (dmxWarning,
"selection conversion for %s timed out\n",
NameForAtom (r->selection));
for (s = convHead; s; s = s->next)
if (s == r)
return dmxSelectionDeleteConv (dmxUnhookSelection (&convHead, s));
for (s = propHead; s; s = s->next)
if (s == r)
return dmxSelectionDeleteProp (dmxUnhookSelection (&propHead, s));
for (s = reqHead; s; s = s->next)
if (s == r)
return dmxSelectionDeleteReq (dmxUnhookSelection (&reqHead, s));
return 0;
}
static void
dmxSelectionResetTimer (DMXSelection *s)
{
s->timer = TimerSet (s->timer,
0,
DMX_SELECTION_TIMEOUT,
dmxSelectionCallback,
s);
}
static void
dmxAppendSelection (DMXSelection **head,
DMXSelection *s)
{
DMXSelection *p, *last;
do {
for (last = NULL, p = *head; p; p = p->next)
{
/* avoid duplicates */
if (p->selection == s->selection &&
p->requestor == s->requestor)
{
if (p->timer)
TimerCancel (p->timer);
dmxSelectionCallback (s->timer, 0, p);
break;
}
last = p;
}
} while (p);
dmxSelectionResetTimer (s);
if (last)
last->next = s;
else
*head = s;
}
static Atom
dmxBESelectionAtom (DMXScreenInfo *dmxScreen,
Atom atom)
{
int i;
for (i = 0; i < dmxSelectionMapNum; i++)
if (atom == dmxSelectionMap[i].atom)
return dmxBEAtom (dmxScreen, dmxSelectionMap[i].beAtom);
return dmxBEAtom (dmxScreen, atom);
}
static Atom
dmxSelectionAtom (DMXScreenInfo *dmxScreen,
Atom atom)
{
int i;
for (i = 0; i < dmxSelectionMapNum; i++)
if (atom == dmxSelectionMap[i].beAtom)
return dmxAtom (dmxScreen, dmxSelectionMap[i].atom);
return dmxAtom (dmxScreen, atom);
}
static void
dmxBESetSelectionOwner (ScreenPtr pScreen,
WindowPtr pWin,
Atom selection)
{
DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
Window window = None;
if (!dmxScreen->beDisplay)
return;
if (dmxScreen->selectionOwner != dmxScreen->rootWin)
return;
if (pWin)
window = DMX_GET_WINDOW_PRIV(pWin)->window;
xcb_set_selection_owner (dmxScreen->connection,
window,
dmxBESelectionAtom (dmxScreen, selection),
0);
}
static void
dmxSelectionOwnerReply (ScreenPtr pScreen,
unsigned int sequence,
xcb_generic_reply_t *reply,
xcb_generic_error_t *error,
void *data)
{
DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
if (reply)
{
xcb_get_selection_owner_reply_t *xselection =
(xcb_get_selection_owner_reply_t *) reply;
dmxScreen->getSelectionOwnerResult = xselection->owner;
}
dmxScreen->getSelectionOwner.sequence = 0;
}
static Bool
dmxBEGetSelectionOwner (ScreenPtr pScreen,
Atom selection)
{
DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
/* reset getSelectionOwner fields */
dmxScreen->getSelectionOwner.sequence = 0;
dmxScreen->getSelectionOwnerResult = 0;
if (!dmxScreen->beDisplay)
return FALSE;
if (dmxScreen->selectionOwner != dmxScreen->rootWin)
return FALSE;
dmxScreen->getSelectionOwner =
xcb_get_selection_owner (dmxScreen->connection,
dmxBESelectionAtom (dmxScreen, selection));
dmxAddRequest (&dmxScreen->request,
dmxSelectionOwnerReply,
dmxScreen->getSelectionOwner.sequence,
0);
return TRUE;
}
static Window
dmxBEConvertSelection (WindowPtr pWin,
Atom selection,
Atom target,
Atom property,
Time time)
{
ScreenPtr pScreen = pWin->drawable.pScreen;
DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
dmxWinPrivPtr pWinPriv = DMX_GET_WINDOW_PRIV(pWin);
if (!dmxScreen->beDisplay || !pWinPriv->window)
return 0;
if (dmxScreen->selectionOwner != dmxScreen->rootWin)
return 0;
xcb_convert_selection (dmxScreen->connection,
pWinPriv->window,
dmxBESelectionAtom (dmxScreen, selection),
dmxBEAtom (dmxScreen, target),
property ? dmxBEAtom (dmxScreen, property) : None,
0);
dmxSync (dmxScreen, FALSE);
return pWinPriv->window;
}
Window
dmxBEGetSelectionAdjustedPropertyWindow (WindowPtr pWin)
{
ScreenPtr pScreen = pWin->drawable.pScreen;
dmxWinPrivPtr pWinPriv = DMX_GET_WINDOW_PRIV (pWin);
DMXSelection *s;
if (!pWinPriv->window)
return None;
for (s = reqHead; s; s = s->next)
if (s->value[pScreen->myNum].in == pWinPriv->window)
return s->value[pScreen->myNum].out;
return pWinPriv->window;
}
void
dmxSelectionClear (ScreenPtr pScreen,
Window owner,
Atom xSelection)
{
DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
Atom selection = dmxSelectionAtom (dmxScreen, xSelection);
Selection *pSel;
if (!ValidAtom (selection))
return;
if (dixLookupSelection (&pSel,
selection,
serverClient,
DixGetAttrAccess) == Success &&
pSel->client != NullClient)
{
SelectionInfoRec info = { pSel, NullClient, SelectionSetOwner };
xEvent event;
event.u.u.type = SelectionClear;
event.u.selectionClear.time = currentTime.milliseconds;
event.u.selectionClear.window = pSel->window;
event.u.selectionClear.atom = pSel->selection;
TryClientEvents (pSel->client, NULL, &event, 1, NoEventMask,
NoEventMask /* CantBeFiltered */, NullGrab);
pSel->lastTimeChanged = currentTime;
pSel->window = dmxScreen->inputOverlayWid;
pSel->pWin = NULL;
pSel->client = NullClient;
CallCallbacks (&SelectionCallback, &info);
pSel->window = None;
}
}
static void
dmxSelectionPropertyReply (ScreenPtr pScreen,
unsigned int sequence,
xcb_generic_reply_t *reply,
xcb_generic_error_t *error,
void *data)
{
DMXSelection *s;
for (s = propHead; s; s = s->next)
if (s->value[pScreen->myNum].in == sequence)
break;
if (s)
{
WindowPtr pWin = NullWindow;
xcb_get_property_reply_t *xproperty =
(xcb_get_property_reply_t *) reply;
if (dixLookupWindow (&pWin,
s->wid,
serverClient,
DixReadAccess) == Success)
{
DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
xEvent event;
event.u.selectionNotify.property = None;
if (reply)
{
Atom type = dmxAtom (dmxScreen, xproperty->type);
uint32_t *data = xcb_get_property_value (xproperty);
int length = xcb_get_property_value_length (xproperty);
/* only 32 bit data types can be translated */
if (xproperty->format == 32)
{
int i;
switch (type) {
case XA_ATOM:
for (i = 0; i < length; i++)
data[i] = dmxAtom (dmxScreen, data[i]);
break;
case XA_BITMAP:
case XA_PIXMAP:
case XA_COLORMAP:
case XA_CURSOR:
case XA_DRAWABLE:
case XA_FONT:
case XA_VISUALID:
case XA_WINDOW:
/* XXX: there's no guarantee that properties of these
types can be converted as all back-end resources
don't exist on this server.
*/
type = 0;
default:
if (type == dmxScreen->atomPairAtom)
for (i = 0; i < length; i++)
data[i] = dmxAtom (dmxScreen, data[i]);
break;
}
}
if (ValidAtom (type) &&
ChangeWindowProperty (pWin,
s->property,
type,
xproperty->format,
PropModeReplace,
length,
data,
TRUE) == Success)
event.u.selectionNotify.property = s->property;
}
if (s->target)
{
event.u.u.type = SelectionNotify | 0x80;
event.u.selectionNotify.time = s->time;
event.u.selectionNotify.requestor = s->wid;
event.u.selectionNotify.selection = s->selection;
event.u.selectionNotify.target = s->target;
TryClientEvents (wClient (pWin), NULL, &event, 1, NoEventMask,
NoEventMask /* CantBeFiltered */, NullGrab);
}
if (s->incr)
{
/* end of incremental selection transfer when size is 0 */
if (xproperty->value_len != 0)
{
/* don't send another selection notify event */
s->target = None;
dmxSelectionResetTimer (s);
return;
}
}
}
dmxSelectionDeleteProp (dmxUnhookSelection (&propHead, s));
}
}
void
dmxSelectionNotify (ScreenPtr pScreen,
Window requestor,
Atom xSelection,
Atom xTarget,
Atom xProperty,
Time xTime)
{
DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
Atom selection = dmxSelectionAtom (dmxScreen, xSelection);
DMXSelection *s;
for (s = convHead; s; s = s->next)
if (s->value[pScreen->myNum].out == requestor &&
s->selection == selection)
break;
if (s)
{
xcb_get_property_cookie_t cookie = { 0 };
Atom property = dmxAtom (dmxScreen, xProperty);
Atom target = dmxAtom (dmxScreen, xTarget);
if (ValidAtom (property) && ValidAtom (target))
cookie = xcb_get_property (dmxScreen->connection,
xFalse,
requestor,
xProperty,
XCB_GET_PROPERTY_TYPE_ANY,
0,
0xffffffff);
if (cookie.sequence)
{
const uint32_t value =
XCB_EVENT_MASK_STRUCTURE_NOTIFY |
XCB_EVENT_MASK_PROPERTY_CHANGE;
dmxUnhookSelection (&convHead, s);
memset (s->value, 0, sizeof (s->value));
s->value[pScreen->myNum].out = requestor;
s->value[pScreen->myNum].in = cookie.sequence;
s->property = property;
s->target = target;
if (target == dmxScreen->incrAtom)
s->incr = TRUE;
if (s->incr)
xcb_change_window_attributes (dmxScreen->connection,
requestor,
XCB_CW_EVENT_MASK,
&value);
dmxAppendSelection (&propHead, s);
dmxAddRequest (&dmxScreen->request,
dmxSelectionPropertyReply,
cookie.sequence,
0);
}
else
{
int i;
s->value[pScreen->myNum].out = 0;
for (i = 0; i < dmxNumScreens; i++)
if (s->value[i].out)
break;
if (i == dmxNumScreens)
dmxSelectionDeleteConv (dmxUnhookSelection (&convHead, s));
}
}
}
Bool
dmxSelectionDestroyNotify (ScreenPtr pScreen,
Window window)
{
DMXSelection *s;
for (s = reqHead; s; s = s->next)
if (s->value[pScreen->myNum].out == window)
break;
if (s)
{
dmxSelectionDeleteReq (dmxUnhookSelection (&reqHead, s));
return TRUE;
}
for (s = propHead; s; s = s->next)
if (s->value[pScreen->myNum].out == window)
break;
if (s)
{
s->value[pScreen->myNum].out = None;
return TRUE;
}
return FALSE;
}
Bool
dmxSelectionPropertyNotify (ScreenPtr pScreen,
Window window,
int state,
Atom xProperty,
Time xTime)
{
DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
DMXSelection *s;
if (state == XCB_PROPERTY_NEW_VALUE)
{
for (s = propHead; s; s = s->next)
if (s->value[pScreen->myNum].out == window)
break;
if (s)
{
xcb_get_property_cookie_t cookie = { 0 };
cookie = xcb_get_property (dmxScreen->connection,
xFalse,
window,
xProperty,
XCB_GET_PROPERTY_TYPE_ANY,
0,
0xffffffff);
if (cookie.sequence)
{
s->value[pScreen->myNum].in = cookie.sequence;
dmxAddRequest (&dmxScreen->request,
dmxSelectionPropertyReply,
cookie.sequence,
0);
dmxSelectionResetTimer (s);
}
else
{
dmxSelectionDeleteProp (dmxUnhookSelection (&propHead, s));
}
}
}
else
{
for (s = reqHead; s; s = s->next)
if (s->value[pScreen->myNum].out == window &&
s->property == dmxAtom (dmxScreen, xProperty))
break;
if (s)
{
WindowPtr pWin;
if (dixLookupWindow (&pWin,
s->wid,
serverClient,
DixReadAccess) == Success)
DeleteProperty (serverClient, pWin, s->property);
if (s->incr)
TimerCancel (s->timer);
else
dmxSelectionDeleteReq (dmxUnhookSelection (&reqHead, s));
}
}
return TRUE;
}
static Bool
dmxConvertSelection (ScreenPtr pScreen,
DMXSelection *s,
Window owner,
int multipleLength,
uint32_t *multipleData)
{
DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
WindowPtr pChild0, pChildN;
Selection *pSel = NULL;
pChild0 = WindowTable[0];
pChildN = WindowTable[pScreen->myNum];
for (;;)
{
dmxWinPrivPtr pWinPriv = DMX_GET_WINDOW_PRIV (pChildN);
if (pWinPriv->window == owner)
{
dixLookupSelection (&pSel,
s->selection,
serverClient,
DixReadAccess);
break;
}
if (pChild0->firstChild)
{
pChild0 = pChild0->firstChild;
pChildN = pChildN->firstChild;
continue;
}
while (!pChild0->nextSib && (pChild0 != WindowTable[0]))
{
pChild0 = pChild0->parent;
pChildN = pChildN->parent;
}
if (pChild0 == WindowTable[0])
break;
pChild0 = pChild0->nextSib;
pChildN = pChildN->nextSib;
}
if (pSel)
{
XID overrideRedirect = TRUE;
WindowPtr pProxy = NullWindow;
int result;
#ifdef PANORAMIX
if (!noPanoramiXExtension)
{
PanoramiXRes *newWin;
int j;
if (!(newWin = (PanoramiXRes *) xalloc (sizeof (PanoramiXRes))))
return FALSE;
newWin->type = XRT_WINDOW;
newWin->u.win.visibility = VisibilityNotViewable;
newWin->u.win.class = InputOnly;
newWin->u.win.root = FALSE;
for (j = 0; j < PanoramiXNumScreens; j++)
newWin->info[j].id = FakeClientID (0);
FOR_NSCREENS_BACKWARD(j) {
pProxy = CreateWindow (newWin->info[j].id,
dmxScreens[j].pInputOverlayWin,
0, 0, 1, 1, 0, InputOnly,
CWOverrideRedirect, &overrideRedirect,
0, serverClient, CopyFromParent,
&result);
if (result != Success)
return FALSE;
if (!AddResource (pProxy->drawable.id, RT_WINDOW, pProxy))
return FALSE;
s->value[j].in = DMX_GET_WINDOW_PRIV (pProxy)->window;
}
AddResource (newWin->info[0].id, XRT_WINDOW, newWin);
}
else
#endif
{
pProxy = CreateWindow (FakeClientID (0),
dmxScreens[0].pInputOverlayWin,
0, 0, 1, 1, 0, InputOnly,
CWOverrideRedirect, &overrideRedirect,
0, serverClient, CopyFromParent,
&result);
if (result != Success)
return FALSE;
if (!AddResource (pProxy->drawable.id, RT_WINDOW, pProxy))
return FALSE;
s->value[0].in = DMX_GET_WINDOW_PRIV (pProxy)->window;
}
if (pProxy)
{
xEvent event;
event.u.u.type = SelectionRequest;
event.u.selectionRequest.owner = pSel->window;
event.u.selectionRequest.time = currentTime.milliseconds;
event.u.selectionRequest.requestor = pProxy->drawable.id;
event.u.selectionRequest.selection = s->selection;
event.u.selectionRequest.target = s->target;
event.u.selectionRequest.property = s->property;
s->wid = pProxy->drawable.id;
s->requestor = pProxy->drawable.id;
if (TryClientEvents (pSel->client, NULL, &event, 1,
NoEventMask,
NoEventMask /* CantBeFiltered */,
NullGrab))
{
if (multipleLength)
ChangeWindowProperty (pProxy,
s->property,
dmxScreen->atomPairAtom,
32,
PropModeReplace,
multipleLength,
multipleData,
TRUE);
dmxAppendSelection (&reqHead, s);
return TRUE;
}
}
dmxSelectionDeleteReq (s);
}
return FALSE;
}
static void
dmxMultipleTargetPropertyReply (ScreenPtr pScreen,
unsigned int sequence,
xcb_generic_reply_t *reply,
xcb_generic_error_t *error,
void *data)
{
xcb_selection_notify_event_t xevent;
DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
DMXSelection *s = (DMXSelection *) data;
if (reply)
{
xcb_get_property_reply_t *xproperty =
(xcb_get_property_reply_t *) reply;
if (xproperty->format == 32)
{
uint32_t *data = xcb_get_property_value (xproperty);
int length = xcb_get_property_value_length (xproperty);
int i;
for (i = 0; i < length; i++)
{
data[i] = dmxAtom (dmxScreen, data[i]);
if ((i & 1) == 0 && data[i] == dmxScreen->incrAtom)
s->incr = TRUE;
}
if (dmxConvertSelection (pScreen, s, s->wid, length, data))
return;
}
}
xevent.response_type = XCB_SELECTION_NOTIFY;
xevent.pad0 = 0;
xevent.sequence = 0;
xevent.time = s->time;
xevent.requestor = s->value[pScreen->myNum].out;
xevent.selection = dmxBESelectionAtom (dmxScreen, s->selection);
xevent.target = dmxBEAtom (dmxScreen, s->target);
xevent.property = 0;
xcb_send_event (dmxScreen->connection,
FALSE,
s->value[pScreen->myNum].out,
0,
(const char *) &xevent);
dmxSync (dmxScreen, FALSE);
free (s);
}
void
dmxSelectionRequest (ScreenPtr pScreen,
Window owner,
Window requestor,
Atom xSelection,
Atom xTarget,
Atom xProperty,
Time xTime)
{
xcb_selection_notify_event_t xevent;
DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
Atom selection =
dmxSelectionAtom (dmxScreen, xSelection);
if (ValidAtom (selection))
{
Atom target = dmxAtom (dmxScreen, xTarget);
Atom property = (xProperty) ? dmxAtom (dmxScreen, xProperty) : None;
if (ValidAtom (target) && ((property == None) || ValidAtom (property)))
{
DMXSelection *s;
s = xalloc (sizeof (DMXSelection));
if (s)
{
s->wid = 0;
s->requestor = 0;
s->selection = selection;
s->target = target;
s->property = property;
s->time = xTime;
s->incr = FALSE;
s->next = 0;
s->timer = 0;
memset (s->value, 0, sizeof (s->value));
s->value[pScreen->myNum].out = requestor;
if (target == dmxScreen->multipleAtom)
{
if (ValidAtom (property))
{
xcb_get_property_cookie_t prop;
prop = xcb_get_property (dmxScreen->connection,
xFalse,
requestor,
xProperty,
XCB_GET_PROPERTY_TYPE_ANY,
0,
0xffffffff);
s->wid = owner;
dmxAddRequest (&dmxScreen->request,
dmxMultipleTargetPropertyReply,
prop.sequence,
(void *) s);
return;
}
}
else
{
if (dmxConvertSelection (pScreen, s, owner, 0, 0))
return;
}
}
xfree (s);
}
}
xevent.response_type = XCB_SELECTION_NOTIFY;
xevent.pad0 = 0;
xevent.sequence = 0;
xevent.time = xTime;
xevent.requestor = requestor;
xevent.selection = xSelection;
xevent.target = xTarget;
xevent.property = 0;
xcb_send_event (dmxScreen->connection,
FALSE,
requestor,
0,
(const char *) &xevent);
dmxSync (dmxScreen, FALSE);
}
void
dmxSelectionPropertyChangeCheck (WindowPtr pWin,
Atom property,
int nUnits)
{
DMXSelection *s;
if (nUnits == -1)
{
for (s = reqHead; s; s = s->next)
if (s->requestor == pWin->drawable.id &&
s->property == property)
break;
if (s)
{
if (s->incr)
TimerCancel (s->timer);
else
dmxSelectionDeleteReq (dmxUnhookSelection (&reqHead, s));
return;
}
for (s = propHead; s; s = s->next)
if (s->requestor == pWin->drawable.id &&
s->property == property)
break;
if (s && s->incr)
{
int i;
TimerCancel (s->timer);
for (i = 0; i < dmxNumScreens; i++)
if (s->value[i].out)
break;
/* owner doesn't exist anymore */
if (i == dmxNumScreens)
{
ChangeWindowProperty (pWin,
s->property,
XA_ATOM,
32,
PropModeReplace,
0,
NULL,
TRUE);
dmxSelectionDeleteProp (dmxUnhookSelection (&propHead, s));
}
}
}
else if (nUnits == 0)
{
for (s = reqHead; s; s = s->next)
if (s->requestor == pWin->drawable.id &&
s->property == property)
break;
/* end of incremental selection conversion */
if (s && s->incr)
dmxSelectionDeleteReq (dmxUnhookSelection (&reqHead, s));
}
}
static int (*dmxSaveProcVector[256]) (ClientPtr);
static int
dmxProcSetSelectionOwner (ClientPtr client)
{
WindowPtr pOld = NullWindow;
WindowPtr pWin;
#ifdef PANORAMIX
PanoramiXRes *win = NULL;
#endif
Selection *pSel;
int err;
int i;
REQUEST(xSetSelectionOwnerReq);
if (dixLookupSelection (&pSel,
stuff->selection,
serverClient,
DixReadAccess) == Success)
pOld = pSel->pWin;
err = (*dmxSaveProcVector[X_SetSelectionOwner]) (client);
if (err != Success)
return err;
if (!pSel && dixLookupSelection (&pSel,
stuff->selection,
serverClient,
DixReadAccess) != Success)
return Success;
if (pSel->pWin)
{
#ifdef PANORAMIX
if (!noPanoramiXExtension)
win = (PanoramiXRes *) SecurityLookupIDByType (serverClient,
pSel->window,
XRT_WINDOW,
DixReadAccess);
else
#endif
dixLookupWindow (&pWin, pSel->window, serverClient, DixReadAccess);
}
else if (!pOld)
{
/* avoid setting selection owner to none on back-end servers
when we're not the current owner */
return Success;
}
for (i = 0; i < dmxNumScreens; i++)
{
ScreenPtr pScreen = screenInfo.screens[i];
#ifdef PANORAMIX
if (!noPanoramiXExtension)
{
pWin = NullWindow;
if (win)
dixLookupWindow (&pWin,
win->info[i].id,
serverClient,
DixReadAccess);
dmxBESetSelectionOwner (pScreen, pWin, pSel->selection);
}
else
#endif
{
if (pWin && pWin->drawable.pScreen != pScreen)
continue;
dmxBESetSelectionOwner (pScreen, pWin, pSel->selection);
}
}
return Success;
}
static int
dmxProcGetSelectionOwner (ClientPtr client)
{
xGetSelectionOwnerReply reply;
Selection *pSel;
int rc;
REQUEST(xResourceReq);
REQUEST_SIZE_MATCH(xResourceReq);
if (!ValidAtom (stuff->id))
{
client->errorValue = stuff->id;
return BadAtom;
}
rc = dixLookupSelection (&pSel, stuff->id, client, DixGetAttrAccess);
if (rc == Success)
{
if (pSel->window != None)
return (*dmxSaveProcVector[X_GetSelectionOwner]) (client);
}
else if (rc != BadMatch)
{
return rc;
}
reply.type = X_Reply;
reply.length = 0;
reply.sequenceNumber = client->sequence;
reply.owner = None;
#ifdef PANORAMIX
if (!noPanoramiXExtension)
{
unsigned int sequence;
int j;
FOR_NSCREENS(j) {
dmxBEGetSelectionOwner (screenInfo.screens[j], stuff->id);
}
do {
dmxDispatch ();
sequence = 0;
FOR_NSCREENS_BACKWARD(j) {
DMXScreenInfo *dmxScreen = &dmxScreens[j];
if (dmxScreen->getSelectionOwnerResult)
break;
sequence |= dmxScreen->getSelectionOwner.sequence;
}
} while (sequence && j < 0 && dmxWaitForResponse ());
/* at least one back-end server has an owner for this selection */
if (j >= 0)
reply.owner = dmxScreens[0].inputOverlayWid;
}
#endif
WriteReplyToClient (client, sizeof (xGetSelectionOwnerReply), &reply);
return client->noClientException;
}
static int
dmxProcConvertSelection (ClientPtr client)
{
DMXSelection *s;
Bool paramsOkay;
xEvent event;
WindowPtr pWin;
Selection *pSel;
int rc;
#ifdef PANORAMIX
PanoramiXRes *win = NULL;
int j;
#endif
REQUEST(xConvertSelectionReq);
REQUEST_SIZE_MATCH(xConvertSelectionReq);
rc = dixLookupWindow (&pWin, stuff->requestor, client, DixSetAttrAccess);
if (rc != Success)
return rc;
#ifdef PANORAMIX
if (!noPanoramiXExtension)
{
if (!(win = (PanoramiXRes *) SecurityLookupIDByType (serverClient,
stuff->requestor,
XRT_WINDOW,
DixReadAccess)))
return BadImplementation;
}
#endif
paramsOkay = ValidAtom (stuff->selection) && ValidAtom (stuff->target);
paramsOkay &= (stuff->property == None) || ValidAtom (stuff->property);
if (!paramsOkay)
{
client->errorValue = stuff->property;
return BadAtom;
}
s = xalloc (sizeof (DMXSelection));
if (!s)
return BadAlloc;
s->wid = 0;
s->requestor = stuff->requestor;
s->selection = stuff->selection;
s->target = stuff->target;
s->property = stuff->property;
s->time = stuff->time;
s->incr = FALSE;
s->next = 0;
s->timer = 0;
memset (s->value, 0, sizeof (s->value));
if (stuff->target == dmxScreens[0].multipleAtom)
{
PropertyPtr pProp;
if (dixLookupProperty (&pProp,
pWin,
stuff->property,
serverClient,
DixReadAccess) == Success &&
pProp->format == 32)
{
Atom *data = (Atom *) pProp->data;
int i;
for (i = 0; i < pProp->size; i++)
if ((i & 1) == 0 && data[i] == dmxScreens[0].incrAtom)
s->incr = TRUE;
}
}
rc = dixLookupSelection (&pSel, stuff->selection, client, DixReadAccess);
if (rc == Success)
{
if (pSel->window != None)
{
#ifdef PANORAMIX
if (!noPanoramiXExtension)
{
FOR_NSCREENS_FORWARD(j) {
if (dixLookupWindow (&pWin,
win->info[j].id,
serverClient,
DixReadAccess) == Success)
s->value[j].in = DMX_GET_WINDOW_PRIV (pWin)->window;
}
}
else
#endif
s->value[pWin->drawable.pScreen->myNum].in =
DMX_GET_WINDOW_PRIV (pWin)->window;
dmxAppendSelection (&reqHead, s);
return (*dmxSaveProcVector[X_ConvertSelection]) (client);
}
}
else if (rc != BadMatch)
{
xfree (s);
return rc;
}
#ifdef PANORAMIX
if (!noPanoramiXExtension)
{
FOR_NSCREENS_FORWARD(j) {
if (dixLookupWindow (&pWin,
win->info[j].id,
serverClient,
DixReadAccess) == Success)
s->value[j].out = dmxBEConvertSelection (pWin,
stuff->selection,
stuff->target,
stuff->property,
stuff->time);
}
}
else
#endif
s->value[pWin->drawable.pScreen->myNum].out =
dmxBEConvertSelection (pWin,
stuff->selection,
stuff->target,
stuff->property,
stuff->time);
for (j = 0; j < dmxNumScreens; j++)
if (s->value[j].out)
break;
if (j < dmxNumScreens)
{
s->wid = stuff->requestor;
dmxAppendSelection (&convHead, s);
return client->noClientException;
}
xfree (s);
event.u.u.type = SelectionNotify;
event.u.selectionNotify.time = stuff->time;
event.u.selectionNotify.requestor = stuff->requestor;
event.u.selectionNotify.selection = stuff->selection;
event.u.selectionNotify.target = stuff->target;
event.u.selectionNotify.property = None;
TryClientEvents (client, NULL, &event, 1, NoEventMask,
NoEventMask /* CantBeFiltered */, NullGrab);
return client->noClientException;
}
static int
dmxProcSendEvent (ClientPtr client)
{
REQUEST(xSendEventReq);
REQUEST_SIZE_MATCH(xSendEventReq);
if ((stuff->propagate != xFalse) && (stuff->propagate != xTrue))
{
client->errorValue = stuff->propagate;
return BadValue;
}
switch (stuff->event.u.u.type) {
case SelectionNotify:
if (stuff->eventMask == NoEventMask &&
stuff->destination != PointerWindow &&
stuff->destination != InputFocus)
{
Atom property = stuff->event.u.selectionNotify.property;
Atom target = stuff->event.u.selectionNotify.target;
DMXSelection *s;
for (s = reqHead; s; s = s->next)
if (s->requestor == stuff->destination &&
s->selection == stuff->event.u.selectionNotify.selection)
break;
if (s)
{
int i;
if (target == dmxScreens[0].incrAtom)
s->incr = TRUE;
s->property = property;
for (i = 0; i < dmxNumScreens; i++)
{
xcb_selection_notify_event_t xevent;
DMXScreenInfo *dmxScreen = &dmxScreens[i];
const uint32_t value =
XCB_EVENT_MASK_STRUCTURE_NOTIFY |
XCB_EVENT_MASK_PROPERTY_CHANGE;
if (!s->value[i].out)
continue;
xevent.response_type = XCB_SELECTION_NOTIFY;
xevent.pad0 = 0;
xevent.sequence = 0;
xevent.time = s->time;
xevent.requestor = s->value[i].out;
xevent.selection = dmxBESelectionAtom (dmxScreen,
s->selection);
if (target)
xevent.target = dmxBEAtom (dmxScreen, target);
else
xevent.target = None;
if (property)
xevent.property = dmxBEAtom (dmxScreen, property);
else
xevent.property = None;
xcb_change_window_attributes (dmxScreen->connection,
s->value[i].out,
XCB_CW_EVENT_MASK,
&value);
xcb_send_event (dmxScreen->connection,
FALSE,
s->value[i].out,
0,
(const char *) &xevent);
dmxSync (dmxScreen, FALSE);
dmxSelectionResetTimer (s);
return client->noClientException;
}
dmxSelectionResetTimer (s);
}
}
break;
case ClientMessage:
if (stuff->event.u.u.detail != 8 &&
stuff->event.u.u.detail != 16 &&
stuff->event.u.u.detail != 32)
{
client->errorValue = stuff->event.u.u.detail;
return BadValue;
}
if (stuff->destination == dmxScreens[0].inputOverlayWid)
dmxDnDClientMessageEvent (&stuff->event);
break;
}
return (*dmxSaveProcVector[X_SendEvent]) (client);
}
void
dmxInitSelections (void)
{
int i;
for (i = 0; i < 256; i++)
dmxSaveProcVector[i] = ProcVector[i];
ProcVector[X_GetSelectionOwner] = dmxProcGetSelectionOwner;
ProcVector[X_SetSelectionOwner] = dmxProcSetSelectionOwner;
ProcVector[X_ConvertSelection] = dmxProcConvertSelection;
ProcVector[X_SendEvent] = dmxProcSendEvent;
}
void
dmxResetSelections (void)
{
int i;
for (i = 0; i < 256; i++)
ProcVector[i] = dmxSaveProcVector[i];
}