mirror of
https://gitlab.freedesktop.org/xorg/xserver.git
synced 2025-12-24 04:20:04 +01:00
Selections are automatically shared with back-end servers. This provides seamless communication between back-end server clients and local clients. Some selections are not appropriate to share as they will cause unwanted conflicts. E.g. most manager selections. These selections can still be shared by adding a unique identifier to them. Selections that need this identifier can be specified using a command line option.
1339 lines
30 KiB
C
1339 lines
30 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 "dmxselection.h"
|
|
|
|
#include "selection.h"
|
|
|
|
#ifdef PANORAMIX
|
|
#include "panoramiX.h"
|
|
#include "panoramiXsrv.h"
|
|
#endif
|
|
|
|
#define DMX_SELECTION_TIMEOUT (60 * 1000) /* 60 seconds */
|
|
|
|
typedef struct _DMXSelection {
|
|
struct _DMXSelection *next;
|
|
|
|
XID wid;
|
|
XID requestor;
|
|
Atom selection;
|
|
Atom target;
|
|
Atom property;
|
|
Time time;
|
|
|
|
struct {
|
|
unsigned int in;
|
|
unsigned int out;
|
|
} value[MAXSCREENS];
|
|
|
|
OsTimerPtr timer;
|
|
} DMXSelection;
|
|
|
|
static DMXSelection *convHead = NULL;
|
|
static DMXSelection **convTail = &convHead;
|
|
|
|
static DMXSelection *propHead = NULL;
|
|
static DMXSelection **propTail = &propHead;
|
|
|
|
static DMXSelection *reqHead = NULL;
|
|
static DMXSelection **reqTail = &reqHead;
|
|
|
|
static DMXSelection *
|
|
dmxUnhookSelection (DMXSelection *s,
|
|
DMXSelection **head,
|
|
DMXSelection ***tail)
|
|
{
|
|
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;
|
|
if (!prev->next)
|
|
*tail = &prev->next;
|
|
}
|
|
else
|
|
{
|
|
*head = s->next;
|
|
if (!s->next)
|
|
*tail = head;
|
|
}
|
|
|
|
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->property == dmxScreen->incrAtom && 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)
|
|
{
|
|
WindowPtr pWin;
|
|
int i;
|
|
|
|
for (i = 0; i < dmxNumScreens; i++)
|
|
{
|
|
if (s->value[i].out)
|
|
{
|
|
DMXScreenInfo *dmxScreen = &dmxScreens[i];
|
|
|
|
if (s->property == dmxScreen->incrAtom)
|
|
{
|
|
const uint32_t value = 0;
|
|
|
|
xcb_change_window_attributes (dmxScreen->connection,
|
|
s->value[i].out,
|
|
XCB_CW_EVENT_MASK,
|
|
&value);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (s->wid && dixLookupWindow (&pWin,
|
|
s->wid,
|
|
serverClient,
|
|
DixReadAccess) == Success)
|
|
DeleteProperty (serverClient, pWin, s->property);
|
|
|
|
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;
|
|
|
|
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 (s,
|
|
&convHead,
|
|
&convTail));
|
|
|
|
for (s = propHead; s; s = s->next)
|
|
if (s == r)
|
|
return dmxSelectionDeleteProp (dmxUnhookSelection (s,
|
|
&propHead,
|
|
&propTail));
|
|
|
|
for (s = reqHead; s; s = s->next)
|
|
if (s == r)
|
|
return dmxSelectionDeleteReq (dmxUnhookSelection (s,
|
|
&reqHead,
|
|
&reqTail));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
dmxSelectionResetTimer (DMXSelection *s)
|
|
{
|
|
s->timer = TimerSet (s->timer,
|
|
0,
|
|
DMX_SELECTION_TIMEOUT,
|
|
dmxSelectionCallback,
|
|
s);
|
|
}
|
|
|
|
static void
|
|
dmxAppendSelection (DMXSelection *s,
|
|
DMXSelection ***tail)
|
|
{
|
|
dmxSelectionResetTimer (s);
|
|
|
|
*(*tail) = s;
|
|
*tail = &s->next;
|
|
}
|
|
|
|
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 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));
|
|
|
|
if (!dmxScreen->getSelectionOwner.sequence)
|
|
return FALSE;
|
|
|
|
dmxAddSequence (&dmxScreen->request,
|
|
dmxScreen->getSelectionOwner.sequence);
|
|
|
|
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->selectionProxyWid[0];
|
|
pSel->pWin = NULL;
|
|
pSel->client = NullClient;
|
|
|
|
CallCallbacks (&SelectionCallback, &info);
|
|
|
|
pSel->window = None;
|
|
}
|
|
}
|
|
|
|
void
|
|
dmxSelectionNotify (ScreenPtr pScreen,
|
|
Window requestor,
|
|
Atom xSelection,
|
|
Atom xTarget,
|
|
Atom xProperty,
|
|
Time xTime)
|
|
{
|
|
DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
|
|
Atom selection = dmxSelectionAtom (dmxScreen, xSelection);
|
|
Atom target = dmxAtom (dmxScreen, xTarget);
|
|
DMXSelection *s;
|
|
|
|
for (s = convHead; s; s = s->next)
|
|
if (s->value[pScreen->myNum].out == requestor &&
|
|
s->selection == selection &&
|
|
s->target == target)
|
|
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_PROPERTY_CHANGE;
|
|
|
|
dmxUnhookSelection (s, &convHead, &convTail);
|
|
|
|
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 (property == dmxScreen->incrAtom)
|
|
xcb_change_window_attributes (dmxScreen->connection,
|
|
requestor,
|
|
XCB_CW_EVENT_MASK,
|
|
&value);
|
|
|
|
dmxAppendSelection (s, &propTail);
|
|
|
|
dmxAddSequence (&dmxScreen->request, cookie.sequence);
|
|
}
|
|
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 (s,
|
|
&convHead,
|
|
&convTail));
|
|
}
|
|
}
|
|
}
|
|
|
|
Bool
|
|
dmxSelectionPropertyNotify (ScreenPtr pScreen,
|
|
Window window,
|
|
int state,
|
|
Atom xProperty,
|
|
Time xTime)
|
|
{
|
|
DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
|
|
Atom property = dmxAtom (dmxScreen, xProperty);
|
|
DMXSelection *s;
|
|
|
|
if (property != dmxScreen->incrAtom)
|
|
return FALSE;
|
|
|
|
if (state == XCB_PROPERTY_NEW_VALUE)
|
|
{
|
|
for (s = propHead; s; s = s->next)
|
|
if (s->value[pScreen->myNum].out == window &&
|
|
s->property == dmxScreen->incrAtom)
|
|
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;
|
|
dmxAddSequence (&dmxScreen->request, cookie.sequence);
|
|
dmxSelectionResetTimer (s);
|
|
}
|
|
else
|
|
{
|
|
dmxSelectionDeleteProp (dmxUnhookSelection (s,
|
|
&propHead,
|
|
&propTail));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (s = reqHead; s; s = s->next)
|
|
if (s->value[pScreen->myNum].out == window &&
|
|
s->property == dmxScreen->incrAtom)
|
|
break;
|
|
|
|
if (s)
|
|
{
|
|
WindowPtr pWin;
|
|
|
|
if (dixLookupWindow (&pWin,
|
|
s->wid,
|
|
serverClient,
|
|
DixReadAccess) == Success)
|
|
DeleteProperty (serverClient, pWin, s->property);
|
|
|
|
dmxSelectionResetTimer (s);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
Bool
|
|
dmxSelectionPropertyReplyCheck (ScreenPtr pScreen,
|
|
unsigned int sequence,
|
|
xcb_generic_reply_t *reply)
|
|
{
|
|
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->response_type)
|
|
{
|
|
Atom type = dmxAtom (dmxScreen, xproperty->type);
|
|
|
|
/* only 32 bit data types can be translated */
|
|
if (xproperty->format == 32)
|
|
{
|
|
uint32_t *data = (uint32_t *) (&xproperty[1]);
|
|
int i;
|
|
|
|
switch (type) {
|
|
case XA_ATOM:
|
|
for (i = 0; i < xproperty->value_len; 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:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ValidAtom (type) &&
|
|
ChangeWindowProperty (pWin,
|
|
s->property,
|
|
type,
|
|
xproperty->format,
|
|
PropModeReplace,
|
|
xproperty->value_len,
|
|
&xproperty[1],
|
|
TRUE) == Success)
|
|
event.u.selectionNotify.property = s->property;
|
|
}
|
|
|
|
if (s->selection)
|
|
{
|
|
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 (event.u.selectionNotify.property == dmxScreen->incrAtom)
|
|
{
|
|
/* end of incremental selection transfer when size is 0 */
|
|
if (xproperty->value_len != 0)
|
|
{
|
|
/* don't send another selection notify event */
|
|
s->selection = None;
|
|
dmxSelectionResetTimer (s);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
dmxSelectionDeleteProp (dmxUnhookSelection (s,
|
|
&propHead,
|
|
&propTail));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
dmxSelectionRequest (ScreenPtr pScreen,
|
|
Window owner,
|
|
Window requestor,
|
|
Atom xSelection,
|
|
Atom xTarget,
|
|
Atom xProperty,
|
|
Time xTime)
|
|
{
|
|
DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
|
|
WindowPtr pChild0, pChildN;
|
|
xcb_selection_notify_event_t xevent;
|
|
Selection *pSel = NULL;
|
|
Atom selection = dmxSelectionAtom (dmxScreen,
|
|
xSelection);
|
|
|
|
pChild0 = WindowTable[0];
|
|
pChildN = WindowTable[pScreen->myNum];
|
|
|
|
for (;;)
|
|
{
|
|
dmxWinPrivPtr pWinPriv = DMX_GET_WINDOW_PRIV (pChildN);
|
|
|
|
if (pWinPriv->window == owner)
|
|
{
|
|
if (ValidAtom (selection))
|
|
dixLookupSelection (&pSel,
|
|
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)
|
|
{
|
|
Atom target = dmxAtom (dmxScreen, xTarget);
|
|
Atom property = (xProperty) ? dmxAtom (dmxScreen, xProperty) : None;
|
|
|
|
if (ValidAtom (target) && ((property == None) || ValidAtom (property)))
|
|
{
|
|
WindowPtr pProxy = NullWindow;
|
|
DMXSelection *s;
|
|
int i;
|
|
|
|
for (i = 1; i < DMX_N_SELECTION_PROXY; i++)
|
|
{
|
|
pProxy = dmxScreens[0].pSelectionProxyWin[i];
|
|
|
|
for (s = reqHead; pProxy && s; s = s->next)
|
|
if (s->wid == pProxy->drawable.id)
|
|
pProxy = NullWindow;
|
|
|
|
if (pProxy)
|
|
break;
|
|
}
|
|
|
|
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 = selection;
|
|
event.u.selectionRequest.target = target;
|
|
event.u.selectionRequest.property = property;
|
|
|
|
s = xalloc (sizeof (DMXSelection));
|
|
if (s)
|
|
{
|
|
if (TryClientEvents (pSel->client, NULL, &event, 1,
|
|
NoEventMask,
|
|
NoEventMask /* CantBeFiltered */,
|
|
NullGrab))
|
|
{
|
|
int j;
|
|
|
|
s->wid = pProxy->drawable.id;
|
|
s->requestor = requestor;
|
|
s->selection = selection;
|
|
s->target = target;
|
|
s->property = property;
|
|
s->time = xTime;
|
|
s->next = 0;
|
|
s->timer = 0;
|
|
|
|
memset (s->value, 0, sizeof (s->value));
|
|
|
|
for (j = 0; j < dmxNumScreens; j++)
|
|
{
|
|
WindowPtr pWin =
|
|
dmxScreens[j].pSelectionProxyWin[i];
|
|
dmxWinPrivPtr pWinPriv =
|
|
DMX_GET_WINDOW_PRIV (pWin);
|
|
|
|
s->value[j].in = pWinPriv->window;
|
|
if (j == pScreen->myNum)
|
|
s->value[j].out = requestor;
|
|
}
|
|
|
|
dmxAppendSelection (s, &reqTail);
|
|
return;
|
|
}
|
|
|
|
xfree (s);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* TODO: wait for proxy window to become availble */
|
|
dmxLog (dmxWarning,
|
|
"dmxSelectionRequest: no proxy window available "
|
|
"for conversion of %s selection\n",
|
|
NameForAtom (selection));
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
/* check for end of incremental selection conversion */
|
|
for (s = reqHead; s; s = s->next)
|
|
if (s->wid == pWin->drawable.id &&
|
|
dmxScreens[0].incrAtom == property &&
|
|
nUnits == 0)
|
|
break;
|
|
|
|
if (s)
|
|
dmxSelectionDeleteReq (dmxUnhookSelection (s,
|
|
&reqHead,
|
|
&reqTail));
|
|
}
|
|
|
|
Bool
|
|
dmxCreateSelectionProxies (void)
|
|
{
|
|
WindowPtr pWin;
|
|
WindowPtr pParent;
|
|
XID selectionProxyWid;
|
|
XID overrideRedirect = TRUE;
|
|
int result;
|
|
int i;
|
|
|
|
for (i = 0; i < DMX_N_SELECTION_PROXY; i++)
|
|
{
|
|
if (dmxScreens[0].selectionProxyWid[i])
|
|
continue;
|
|
|
|
selectionProxyWid = FakeClientID (0);
|
|
|
|
#ifdef PANORAMIX
|
|
if (!noPanoramiXExtension)
|
|
{
|
|
PanoramiXRes *newWin;
|
|
int j;
|
|
|
|
if(!(newWin = (PanoramiXRes *) xalloc (sizeof (PanoramiXRes))))
|
|
return BadAlloc;
|
|
|
|
newWin->type = XRT_WINDOW;
|
|
newWin->u.win.visibility = VisibilityNotViewable;
|
|
newWin->u.win.class = InputOnly;
|
|
newWin->u.win.root = FALSE;
|
|
newWin->info[0].id = selectionProxyWid;
|
|
for(j = 1; j < PanoramiXNumScreens; j++)
|
|
newWin->info[j].id = FakeClientID (0);
|
|
|
|
FOR_NSCREENS_BACKWARD(j) {
|
|
if (i)
|
|
pParent = dmxScreens[j].pSelectionProxyWin[0];
|
|
else
|
|
pParent = WindowTable[j];
|
|
|
|
pWin = CreateWindow (newWin->info[j].id, pParent,
|
|
0, 0, 1, 1, 0, InputOnly,
|
|
CWOverrideRedirect, &overrideRedirect,
|
|
0, serverClient, CopyFromParent,
|
|
&result);
|
|
if (result != Success)
|
|
return FALSE;
|
|
if (!AddResource (pWin->drawable.id, RT_WINDOW, pWin))
|
|
return FALSE;
|
|
|
|
dmxScreens[j].selectionProxyWid[i] = selectionProxyWid;
|
|
dmxScreens[j].pSelectionProxyWin[i] = pWin;
|
|
}
|
|
|
|
AddResource(newWin->info[0].id, XRT_WINDOW, newWin);
|
|
}
|
|
else
|
|
#endif
|
|
|
|
{
|
|
if (i)
|
|
pParent = dmxScreens[0].pSelectionProxyWin[0];
|
|
else
|
|
pParent = WindowTable[0];
|
|
|
|
pWin = CreateWindow (selectionProxyWid, pParent,
|
|
0, 0, 1, 1, 0, InputOnly,
|
|
CWOverrideRedirect, &overrideRedirect,
|
|
0, serverClient, CopyFromParent,
|
|
&result);
|
|
if (result != Success)
|
|
return FALSE;
|
|
if (!AddResource (pWin->drawable.id, RT_WINDOW, pWin))
|
|
return FALSE;
|
|
|
|
dmxScreens[0].selectionProxyWid[i] = selectionProxyWid;
|
|
dmxScreens[0].pSelectionProxyWin[i] = pWin;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
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].selectionProxyWid[0];
|
|
}
|
|
#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 = stuff->requestor;
|
|
s->requestor = 0;
|
|
s->selection = stuff->selection;
|
|
s->target = stuff->target;
|
|
s->property = stuff->property;
|
|
s->time = stuff->time;
|
|
s->next = 0;
|
|
s->timer = 0;
|
|
|
|
memset (s->value, 0, sizeof (s->value));
|
|
|
|
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 (s, &reqTail);
|
|
|
|
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)
|
|
{
|
|
dmxAppendSelection (s, &convTail);
|
|
|
|
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->event.u.u.type == SelectionNotify &&
|
|
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;
|
|
|
|
if ((stuff->propagate != xFalse) && (stuff->propagate != xTrue))
|
|
{
|
|
client->errorValue = stuff->propagate;
|
|
return BadValue;
|
|
}
|
|
|
|
for (s = reqHead; s; s = s->next)
|
|
if (s->wid == stuff->destination &&
|
|
s->selection == stuff->event.u.selectionNotify.selection)
|
|
break;
|
|
|
|
if (s)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < dmxNumScreens; i++)
|
|
{
|
|
if (s->value[i].out)
|
|
{
|
|
xcb_selection_notify_event_t xevent;
|
|
DMXScreenInfo *dmxScreen = &dmxScreens[i];
|
|
|
|
xevent.response_type = XCB_SELECTION_NOTIFY;
|
|
xevent.pad0 = 0;
|
|
xevent.sequence = 0;
|
|
xevent.time = s->time;
|
|
xevent.requestor = s->requestor;
|
|
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;
|
|
|
|
if (property == dmxScreen->incrAtom)
|
|
{
|
|
const uint32_t value = XCB_EVENT_MASK_PROPERTY_CHANGE;
|
|
|
|
xcb_change_window_attributes (dmxScreen->connection,
|
|
s->requestor,
|
|
XCB_CW_EVENT_MASK,
|
|
&value);
|
|
|
|
s->property = dmxScreen->incrAtom;
|
|
}
|
|
|
|
xcb_send_event (dmxScreen->connection,
|
|
FALSE,
|
|
s->requestor,
|
|
0,
|
|
(const char *) &xevent);
|
|
|
|
dmxSync (dmxScreen, FALSE);
|
|
|
|
if (property == dmxScreen->incrAtom)
|
|
dmxSelectionResetTimer (s);
|
|
else
|
|
dmxSelectionDeleteReq (dmxUnhookSelection (s,
|
|
&reqHead,
|
|
&reqTail));
|
|
|
|
return Success;
|
|
}
|
|
}
|
|
|
|
s->property = property;
|
|
if (property == dmxScreens[0].incrAtom)
|
|
dmxSelectionResetTimer (s);
|
|
else
|
|
dmxSelectionDeleteReq (dmxUnhookSelection (s,
|
|
&reqHead,
|
|
&reqTail));
|
|
}
|
|
}
|
|
|
|
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];
|
|
}
|