From a1dd3fe5519e366fd10420455b6e7994232fa454 Mon Sep 17 00:00:00 2001 From: David Reveman Date: Sat, 4 Oct 2008 01:00:40 -0400 Subject: [PATCH] Add selection support. 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. --- hw/dmx/dmx.h | 25 +- hw/dmx/dmxcb.c | 3 + hw/dmx/dmxinit.c | 77 +++ hw/dmx/dmxprop.c | 93 ++- hw/dmx/dmxscrinit.c | 229 +++++++- hw/dmx/dmxselection.c | 1294 +++++++++++++++++++++++++++++++++++++++-- hw/dmx/dmxselection.h | 53 +- hw/dmx/dmxshm.c | 11 +- hw/dmx/dmxsync.c | 5 +- hw/dmx/dmxwindow.c | 10 +- 10 files changed, 1696 insertions(+), 104 deletions(-) diff --git a/hw/dmx/dmx.h b/hw/dmx/dmx.h index d254b13ad..9f3bcb86f 100644 --- a/hw/dmx/dmx.h +++ b/hw/dmx/dmx.h @@ -104,9 +104,18 @@ typedef struct _DMXPropTrans { Atom type; } DMXPropTrans; -/** Opcode for xcb_implementation. */ +typedef struct _DMXSelectionMap { + const char *name; + Atom atom; + Atom beAtom; +} DMXSelectionMap; + #define DMX_DETACHED 0xff +/** Number of backend selection conversion requests that can be + processed simultaneously . */ +#define DMX_N_SELECTION_PROXY 10 + /** Provide the typedef globally, but keep the contents opaque outside * of the XSync statistic routines. \see dmxstat.c */ typedef struct _DMXStatInfo DMXStatInfo; @@ -203,6 +212,15 @@ typedef struct _DMXScreenInfo { int rootY; /**< Y offset of "root" window WRT "screen"*/ int rootEventMask; + /*---------- Selection information ----------*/ + Atom selectionAtom; + Window selectionOwner; + xcb_get_selection_owner_cookie_t getSelectionOwner; + Window getSelectionOwnerResult; + XID selectionProxyWid[DMX_N_SELECTION_PROXY]; + WindowPtr pSelectionProxyWin[DMX_N_SELECTION_PROXY]; + Atom incrAtom; + /*---------- Other related information ----------*/ int dpmsCapable; /**< Non-zero if backend is DPMS capable */ @@ -364,11 +382,16 @@ extern int xRRCrtcsPerScreen; extern DMXPropTrans *dmxPropTrans; extern int dmxPropTransNum; +extern DMXSelectionMap *dmxSelectionMap; +extern int dmxSelectionMapNum; + #ifdef XV extern char **dmxXvImageFormats; extern int dmxXvImageFormatsNum; #endif +extern char dmxDigest[64]; + /** Wrap screen or GC function pointer */ #define DMX_WRAP(_entry, _newfunc, _saved, _actual) \ do { \ diff --git a/hw/dmx/dmxcb.c b/hw/dmx/dmxcb.c index 9cf9b9423..996eee755 100644 --- a/hw/dmx/dmxcb.c +++ b/hw/dmx/dmxcb.c @@ -42,6 +42,7 @@ #include "dmxcb.h" #include "dmxinput.h" #include "dmxlog.h" +#include "dmxselection.h" extern int connBlockScreenStart; @@ -194,4 +195,6 @@ void dmxConnectionBlockCallback(void) } #endif MAXSCREENSFREE(found); + + dmxCreateSelectionProxies (); } diff --git a/hw/dmx/dmxinit.c b/hw/dmx/dmxinit.c index 3ec38f461..56af57574 100644 --- a/hw/dmx/dmxinit.c +++ b/hw/dmx/dmxinit.c @@ -141,6 +141,9 @@ int xRRCrtcsPerScreen = 1; DMXPropTrans *dmxPropTrans = NULL; int dmxPropTransNum = 0; +DMXSelectionMap *dmxSelectionMap = NULL; +int dmxSelectionMapNum = 0; + #ifdef XV char **dmxXvImageFormats = NULL; int dmxXvImageFormatsNum = 0; @@ -839,6 +842,26 @@ void InitOutput(ScreenInfo *pScreenInfo, int argc, char *argv[]) strlen (dmxPropTrans[i].name), TRUE); + for (i = 0; i < dmxSelectionMapNum; i++) + { + char *beName; + + dmxSelectionMap[i].atom = MakeAtom ((char *) dmxSelectionMap[i].name, + strlen (dmxSelectionMap[i].name), + TRUE); + + beName = xalloc (strlen (dmxSelectionMap[i].name) + + strlen (dmxDigest) + 2); + if (!beName) + dmxLog (dmxFatal, "InitOutput: not enough memory\n"); + + sprintf (beName, "%s_%s", dmxSelectionMap[i].name, dmxDigest); + dmxSelectionMap[i].beAtom = MakeAtom ((char *) beName, + strlen (beName), + TRUE); + xfree (beName); + } + if (!dmxNumScreens) { dmxLaunchDisplay (argc, argv, dmxLaunchIndex, dmxLaunchVT); @@ -1093,6 +1116,39 @@ void OsVendorInit(void) dmxPropTransNum = 1; } + if (!dmxSelectionMap) + { + dmxSelectionMap = xalloc (sizeof (DMXSelectionMap) * 9); + dmxSelectionMap[0].name = "WM_S0"; + dmxSelectionMap[0].atom = 0; + dmxSelectionMap[0].beAtom = 0; + dmxSelectionMap[1].name = "_NET_WM_CM_S0"; + dmxSelectionMap[1].atom = 0; + dmxSelectionMap[1].beAtom = 0; + dmxSelectionMap[2].name = "_NET_SYSTEM_TRAY_S0"; + dmxSelectionMap[2].atom = 0; + dmxSelectionMap[2].beAtom = 0; + dmxSelectionMap[3].name = "_NET_DESKTOP_LAYOUT_S0"; + dmxSelectionMap[3].atom = 0; + dmxSelectionMap[3].beAtom = 0; + dmxSelectionMap[4].name = "_NET_DESKTOP_MANAGER_S0"; + dmxSelectionMap[4].atom = 0; + dmxSelectionMap[4].beAtom = 0; + dmxSelectionMap[5].name = "_XSETTINGS_S0"; + dmxSelectionMap[5].atom = 0; + dmxSelectionMap[5].beAtom = 0; + dmxSelectionMap[6].name = "CLIPBOARD_MANAGER"; + dmxSelectionMap[6].atom = 0; + dmxSelectionMap[6].beAtom = 0; + dmxSelectionMap[7].name = "GVM_SELECTION"; + dmxSelectionMap[7].atom = 0; + dmxSelectionMap[7].beAtom = 0; + dmxSelectionMap[8].name = "_COMPIZ_DM_S0"; + dmxSelectionMap[8].atom = 0; + dmxSelectionMap[8].beAtom = 0; + dmxSelectionMapNum = 9; + } + #ifdef PANORAMIX noPanoramiXExtension = dmxNoPanoramiXExtension; PanoramiXExtensionDisabledHack = TRUE; @@ -1211,6 +1267,26 @@ int ddxProcessArgument(int argc, char *argv[], int i) } retval = 3; } + else if (!strcmp (argv[i], "-selection")) + { + if (++i < argc) + { + DMXSelectionMap *selection; + + selection = xrealloc (dmxSelectionMap, sizeof (DMXSelectionMap) * + (dmxSelectionMapNum + 1)); + if (selection) + { + selection[dmxSelectionMapNum].name = argv[i]; + selection[dmxSelectionMapNum].atom = 0; + selection[dmxSelectionMapNum].beAtom = 0; + + dmxSelectionMapNum++; + dmxSelectionMap = selection; + } + } + retval = 2; + } #ifdef XV else if (!strcmp (argv[i], "-xvimage")) { @@ -1277,6 +1353,7 @@ void ddxUseMsg(void) ErrorF("-crtcs num RANDR crtcs for each back-end display\n"); #endif ErrorF("-prop name format Specify property translation\n"); + ErrorF("-selection name Specify selection that needs unique prefix\n"); #ifdef XV ErrorF("-xvimage fourcc Enable XVideo image format\n"); #endif diff --git a/hw/dmx/dmxprop.c b/hw/dmx/dmxprop.c index ed1776179..e37631e62 100644 --- a/hw/dmx/dmxprop.c +++ b/hw/dmx/dmxprop.c @@ -64,6 +64,7 @@ #include "dmxwindow.h" #include "dmxlog.h" #include "dmxatom.h" +#include "dmxselection.h" #ifdef PANORAMIX #include "panoramiX.h" @@ -146,14 +147,20 @@ dmxProcChangeProperty (ClientPtr client) XRT_WINDOW, DixReadAccess))) { - FOR_NSCREENS_FORWARD(j) { - if (dixLookupWindow (&pWin, + FOR_NSCREENS_BACKWARD(j) { + WindowPtr pScrWin; + + if (dixLookupWindow (&pScrWin, win->info[j].id, serverClient, DixReadAccess) == Success) - dmxBESetWindowProperty (pWin, pProp); + dmxBESetWindowProperty (pScrWin, pProp); } } + + dmxSelectionPropertyChangeCheck (pWin, + stuff->property, + stuff->nUnits); return Success; } @@ -161,6 +168,10 @@ dmxProcChangeProperty (ClientPtr client) dmxBESetWindowProperty (pWin, pProp); + dmxSelectionPropertyChangeCheck (pWin, + stuff->property, + stuff->nUnits); + return Success; } @@ -168,16 +179,17 @@ static void dmxDeleteProperty (WindowPtr pWin, Atom property) { - ScreenPtr pScreen = pWin->drawable.pScreen; - DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; - dmxWinPrivPtr pWinPriv = DMX_GET_WINDOW_PRIV (pWin); + ScreenPtr pScreen = pWin->drawable.pScreen; + DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; + Window window; - if (!pWinPriv->window) + window = dmxBEGetSelectionAdjustedPropertyWindow (pWin); + if (!window) return; XLIB_PROLOGUE (dmxScreen); XDeleteProperty (dmxScreen->beDisplay, - pWinPriv->window, + window, dmxBEAtom (dmxScreen, property)); XLIB_EPILOGUE (dmxScreen); } @@ -226,6 +238,60 @@ dmxProcDeleteProperty (ClientPtr client) return Success; } +static int +dmxProcGetProperty (ClientPtr client) +{ + WindowPtr pWin; + PropertyPtr pProp; + int err; + REQUEST(xGetPropertyReq); + + err = (*dmxSaveProcVector[X_GetProperty]) (client); + if (err != Success || !stuff->delete) + return err; + + if (dixLookupWindow (&pWin, + stuff->window, + serverClient, + DixReadAccess) != Success || + dixLookupProperty (&pProp, + pWin, + stuff->property, + serverClient, + DixReadAccess) != BadMatch) + return Success; + +#ifdef PANORAMIX + if (!noPanoramiXExtension) + { + PanoramiXRes *win; + int j; + + if ((win = (PanoramiXRes *) SecurityLookupIDByType (serverClient, + stuff->window, + XRT_WINDOW, + DixReadAccess))) + { + FOR_NSCREENS_FORWARD(j) { + if (dixLookupWindow (&pWin, + win->info[j].id, + serverClient, + DixReadAccess) == Success) + { + dmxDeleteProperty (pWin, stuff->property); + } + } + } + + return Success; + } +#endif + + dmxDeleteProperty (pWin, stuff->property); + + return Success; +} + static void dmxRotateProperties (WindowPtr pWin, Atom *atoms, @@ -235,22 +301,22 @@ dmxRotateProperties (WindowPtr pWin, { ScreenPtr pScreen = pWin->drawable.pScreen; DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; - dmxWinPrivPtr pWinPriv = DMX_GET_WINDOW_PRIV (pWin); + Window window; int i; - if (!pWinPriv->window) + window = dmxBEGetSelectionAdjustedPropertyWindow (pWin); + if (!window) return; - XLIB_PROLOGUE (dmxScreen); for (i = 0; i < nAtoms; i++) buf[i] = dmxBEAtom (dmxScreen, atoms[i]); + XLIB_PROLOGUE (dmxScreen); XRotateWindowProperties (dmxScreen->beDisplay, - pWinPriv->window, + window, buf, nAtoms, nPositions); - XLIB_EPILOGUE (dmxScreen); } @@ -323,6 +389,7 @@ void dmxInitProps (void) ProcVector[X_ChangeProperty] = dmxProcChangeProperty; ProcVector[X_DeleteProperty] = dmxProcDeleteProperty; + ProcVector[X_GetProperty] = dmxProcGetProperty; ProcVector[X_RotateProperties] = dmxProcRotateProperties; } diff --git a/hw/dmx/dmxscrinit.c b/hw/dmx/dmxscrinit.c index 23483adb9..3550e71e4 100644 --- a/hw/dmx/dmxscrinit.c +++ b/hw/dmx/dmxscrinit.c @@ -119,6 +119,7 @@ DevPrivateKey dmxGlyphPrivateKey = &dmxGlyphPrivateKeyIndex; /**< Private index void dmxBEScreenInit(int idx, ScreenPtr pScreen) { DMXScreenInfo *dmxScreen = &dmxScreens[idx]; + char buf[256]; int i, j; /* FIXME: The dmxScreenInit() code currently assumes that it will @@ -138,6 +139,12 @@ void dmxBEScreenInit(int idx, ScreenPtr pScreen) pScreen->whitePixel = dmxScreen->beWhitePixel; pScreen->blackPixel = dmxScreen->beBlackPixel; + dmxScreen->selectionAtom = None; + dmxScreen->selectionOwner = None; + + sprintf(buf, "DMX_%s", dmxDigest); + dmxScreen->selectionAtom = XInternAtom (dmxScreen->beDisplay, buf, 0); + /* Handle screen savers and DPMS on the backend */ dmxDPMSInit(dmxScreen); @@ -208,6 +215,168 @@ dmxScreenReplyCheckInput (ScreenPtr pScreen, return dmxInputReplyCheck (&dmxScreen->input, sequence, reply); } +static void +dmxScreenGetSelectionOwner (ScreenPtr pScreen) +{ + DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; + + dmxScreen->selectionOwner = None; + while (dmxScreen->selectionOwner == None) + { + xcb_connection_t *c = dmxScreen->connection; + xcb_atom_t a = dmxScreen->selectionAtom; + xcb_get_selection_owner_reply_t *reply; + + reply = xcb_get_selection_owner_reply (c, + xcb_get_selection_owner (c, a), + NULL); + if (!reply) + break; + + if (reply->owner) + { + if (reply->owner != dmxScreen->rootWin) + { + const uint32_t value = XCB_EVENT_MASK_STRUCTURE_NOTIFY; + xcb_void_cookie_t r; + xcb_generic_error_t *error; + + r = xcb_change_window_attributes_checked (c, + reply->owner, + XCB_CW_EVENT_MASK, + &value); + error = xcb_request_check (c, r); + if (error) + { + if (error->error_code != BadWindow) + dmxScreen->selectionOwner = reply->owner; + + free (error); + } + else + { + dmxScreen->selectionOwner = reply->owner; + } + } + else + { + dmxScreen->selectionOwner = dmxScreen->rootWin; + } + } + else + { + xcb_set_selection_owner (dmxScreen->connection, + dmxScreen->rootWin, + dmxScreen->selectionAtom, + 0); + } + + free (reply); + } +} + +static Bool +dmxScreenEventCheckSelection (ScreenPtr pScreen, + xcb_generic_event_t *event) +{ + DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; + + switch (event->response_type & ~0x80) { + case XCB_DESTROY_NOTIFY: { + xcb_destroy_notify_event_t *xdestroy = + (xcb_destroy_notify_event_t *) event; + + if (xdestroy->window != dmxScreen->selectionOwner) + return FALSE; + + if (dmxScreen->selectionOwner == dmxScreen->rootWin) + return FALSE; + + dmxScreenGetSelectionOwner (pScreen); + } break; + case XCB_PROPERTY_NOTIFY: { + xcb_property_notify_event_t *xproperty = + (xcb_property_notify_event_t *) event; + + if (!dmxSelectionPropertyNotify (pScreen, + xproperty->window, + xproperty->state, + xproperty->atom, + xproperty->time)) + return FALSE; + } break; + case XCB_SELECTION_CLEAR: { + xcb_selection_clear_event_t *xclear = + (xcb_selection_clear_event_t *) event; + + if (xclear->selection == dmxScreen->selectionAtom) + { + dmxScreenGetSelectionOwner (pScreen); + } + else + { + dmxSelectionClear (pScreen, + xclear->owner, + xclear->selection); + } + } break; + case XCB_SELECTION_NOTIFY: { + xcb_selection_notify_event_t *xnotify = + (xcb_selection_notify_event_t *) event; + + dmxSelectionNotify (pScreen, + xnotify->requestor, + xnotify->selection, + xnotify->target, + xnotify->property, + xnotify->time); + } break; + case XCB_SELECTION_REQUEST: { + xcb_selection_request_event_t *xrequest = + (xcb_selection_request_event_t *) event; + + dmxSelectionRequest (pScreen, + xrequest->owner, + xrequest->requestor, + xrequest->selection, + xrequest->target, + xrequest->property, + xrequest->time); + } break; + default: + return FALSE; + } + + return TRUE; +} + +static Bool +dmxScreenReplyCheckSelection (ScreenPtr pScreen, + unsigned int sequence, + xcb_generic_reply_t *reply) +{ + DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; + + if (sequence == dmxScreen->getSelectionOwner.sequence) + { + dmxScreen->getSelectionOwner.sequence = 0; + + if (reply->response_type) + { + xcb_get_selection_owner_reply_t *xselection = + (xcb_get_selection_owner_reply_t *) reply; + + dmxScreen->getSelectionOwnerResult = xselection->owner; + } + + return TRUE; + } + else + { + return dmxSelectionPropertyReplyCheck (pScreen, sequence, reply); + } +} + static void dmxDiscardIgnore (DMXScreenInfo *dmxScreen, unsigned long sequence) @@ -326,6 +495,7 @@ dmxScreenEventCheckOutputWindow (ScreenPtr pScreen, /* output window has been destroyed, detach screen when we reach the block handler */ dmxScreen->scrnWin = None; + return TRUE; } break; case XCB_MAP_NOTIFY: { xcb_map_notify_event_t *xmap = (xcb_map_notify_event_t *) event; @@ -334,10 +504,10 @@ dmxScreenEventCheckOutputWindow (ScreenPtr pScreen, return TRUE; } break; default: - return FALSE; + break; } - return TRUE; + return FALSE; } static Bool @@ -361,7 +531,7 @@ dmxScreenEventCheckManageRoot (ScreenPtr pScreen, (xcb_map_request_event_t *) event; xcb_client_message_event_t *xclient = (xcb_client_message_event_t *) event; - xcb_map_notify_event_t * xmap = + xcb_map_notify_event_t *xmap = (xcb_map_notify_event_t *) event; switch (event->response_type & ~0x80) { @@ -379,7 +549,10 @@ dmxScreenEventCheckManageRoot (ScreenPtr pScreen, break; case XCB_MAP_NOTIFY: if (xmap->window == dmxScreen->rootWin) + { + dmxScreenGetSelectionOwner (pScreen); return TRUE; + } /* fall-through */ default: @@ -680,6 +853,7 @@ dmxBEDispatch (ScreenPtr pScreen) while ((event = xcb_poll_for_event (dmxScreen->connection))) { if (!dmxScreenEventCheckInput (pScreen, event) && + !dmxScreenEventCheckSelection (pScreen, event) && !dmxScreenEventCheckOutputWindow (pScreen, event) && !dmxScreenEventCheckManageRoot (pScreen, event) && !dmxScreenEventCheckExpose (pScreen, event) && @@ -731,7 +905,8 @@ dmxBEDispatch (ScreenPtr pScreen) dmxScreen->request.tail = &dmxScreen->request.head; if (!dmxScreenReplyCheckSync (pScreen, head->sequence, rep) && - !dmxScreenReplyCheckInput (pScreen, head->sequence, rep)) + !dmxScreenReplyCheckInput (pScreen, head->sequence, rep) && + !dmxScreenReplyCheckSelection (pScreen, head->sequence, rep)) { /* error response */ if (rep->response_type == 0) @@ -751,36 +926,27 @@ dmxBEDispatch (ScreenPtr pScreen) if (!dmxScreen->scrnWin || xcb_connection_has_error (dmxScreen->connection)) { - if (!dmxScreen->broken) + while (dmxScreen->request.head) { + DMXSequence *head = dmxScreen->request.head; static xcb_generic_error_t detached_error = { 0, DMX_DETACHED }; - dmxScreenEventCheckInput (pScreen, (xcb_generic_event_t *) - &detached_error); - dmxScreenEventCheckOutputWindow (pScreen, (xcb_generic_event_t *) - &detached_error); - dmxScreenEventCheckManageRoot (pScreen, (xcb_generic_event_t *) - &detached_error); - dmxScreenEventCheckExpose (pScreen, (xcb_generic_event_t *) - &detached_error); + dmxScreen->request.head = head->next; + if (!dmxScreen->request.head) + dmxScreen->request.tail = &dmxScreen->request.head; -#ifdef MITSHM - dmxScreenEventCheckShm (pScreen, (xcb_generic_event_t *) - &detached_error); -#endif - -#ifdef RANDR - dmxScreenEventCheckRR (pScreen, (xcb_generic_event_t *) - &detached_error); -#endif - - dmxScreenReplyCheckSync (pScreen, 0, (xcb_generic_reply_t *) + dmxScreenReplyCheckSync (pScreen, head->sequence, + (xcb_generic_reply_t *) &detached_error); - dmxScreenReplyCheckInput (pScreen, 0, (xcb_generic_reply_t *) + dmxScreenReplyCheckInput (pScreen, head->sequence, + (xcb_generic_reply_t *) &detached_error); - - dmxScreen->broken = TRUE; + dmxScreenReplyCheckSelection (pScreen, head->sequence, + (xcb_generic_reply_t *) + &detached_error); } + + dmxScreen->broken = TRUE; } dmxScreen->inDispatch--; @@ -902,7 +1068,10 @@ Bool dmxScreenInit(int idx, ScreenPtr pScreen, int argc, char *argv[]) dmxScreen->request.head = NULL; dmxScreen->request.tail = &dmxScreen->request.head; - dmxScreen->rootEventMask = ExposureMask | SubstructureRedirectMask; + dmxScreen->rootEventMask = ExposureMask | StructureNotifyMask | + SubstructureRedirectMask; + + dmxScreen->incrAtom = MakeAtom ("INCR", strlen ("INCR"), TRUE); #ifdef MITSHM dmxScreen->beShm = FALSE; @@ -1207,6 +1376,10 @@ Bool dmxCloseScreen(int idx, ScreenPtr pScreen) { DMXScreenInfo *dmxScreen = &dmxScreens[idx]; + /* Free selection proxy window tree */ + if (dmxScreen->selectionProxyWid[0]) + FreeResource (dmxScreen->selectionProxyWid[0], RT_NONE); + /* Reset the proc vectors */ if (idx == 0) { #ifdef COMPOSITE diff --git a/hw/dmx/dmxselection.c b/hw/dmx/dmxselection.c index 0280e93bf..964f84138 100644 --- a/hw/dmx/dmxselection.c +++ b/hw/dmx/dmxselection.c @@ -29,6 +29,10 @@ #include "dmx.h" #include "dmxlog.h" +#include "dmxatom.h" +#include "dmxwindow.h" +#include "dmxscrinit.h" +#include "dmxsync.h" #include "dmxselection.h" #include "selection.h" @@ -38,10 +42,893 @@ #include "panoramiXsrv.h" #endif -static void -dmxBESetSelectionOwner (WindowPtr pWin, - Selection *pSel) +#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); @@ -49,50 +936,78 @@ static int (*dmxSaveProcVector[256]) (ClientPtr); static int dmxProcSetSelectionOwner (ClientPtr client) { - WindowPtr pWin; - Selection *pSel; - int err; + 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 (dixLookupWindow (&pWin, - stuff->window, - serverClient, - DixReadAccess) != Success || - dixLookupSelection (&pSel, - stuff->selection, - serverClient, - DixReadAccess) != Success) + if (!pSel && dixLookupSelection (&pSel, + stuff->selection, + serverClient, + DixReadAccess) != Success) return Success; + if (pSel->pWin) + { + #ifdef PANORAMIX - if (!noPanoramiXExtension) + if (!noPanoramiXExtension) + win = (PanoramiXRes *) SecurityLookupIDByType (serverClient, + pSel->window, + XRT_WINDOW, + DixReadAccess); + else +#endif + dixLookupWindow (&pWin, pSel->window, serverClient, DixReadAccess); + } + else if (!pOld) { - PanoramiXRes *win; - int j; - - if ((win = (PanoramiXRes *) SecurityLookupIDByType (serverClient, - stuff->window, - XRT_WINDOW, - DixReadAccess))) - { - FOR_NSCREENS_FORWARD(j) { - if (dixLookupWindow (&pWin, - win->info[j].id, - serverClient, - DixReadAccess) == Success) - dmxBESetSelectionOwner (pWin, pSel); - } - } - + /* avoid setting selection owner to none on back-end servers + when we're not the current owner */ return Success; } -#endif - dmxBESetSelectionOwner (pWin, pSel); + 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; } @@ -100,41 +1015,324 @@ dmxProcSetSelectionOwner (ClientPtr client) static int dmxProcGetSelectionOwner (ClientPtr client) { - int err; + xGetSelectionOwnerReply reply; + Selection *pSel; + int rc; - err = (*dmxSaveProcVector[X_GetSelectionOwner]) (client); - if (err != Success) - return err; + REQUEST(xResourceReq); + REQUEST_SIZE_MATCH(xResourceReq); - return Success; + 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) { - int err; + DMXSelection *s; + Bool paramsOkay; + xEvent event; + WindowPtr pWin; + Selection *pSel; + int rc; +#ifdef PANORAMIX + PanoramiXRes *win = NULL; + int j; +#endif - err = (*dmxSaveProcVector[X_ConvertSelection]) (client); - if (err != Success) - return err; + REQUEST(xConvertSelectionReq); + REQUEST_SIZE_MATCH(xConvertSelectionReq); - return Success; + 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; } -void dmxInitSelections (void) +static int +dmxProcSendEvent (ClientPtr client) { - int i; + 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) +void +dmxResetSelections (void) { - int i; + int i; + for (i = 0; i < 256; i++) ProcVector[i] = dmxSaveProcVector[i]; diff --git a/hw/dmx/dmxselection.h b/hw/dmx/dmxselection.h index a75c575be..ac807adf0 100644 --- a/hw/dmx/dmxselection.h +++ b/hw/dmx/dmxselection.h @@ -27,8 +27,57 @@ #define DMXSELECTION_H #include "dmx.h" +#include "propertyst.h" -extern void dmxInitSelections (void); -extern void dmxResetSelections (void); +Window +dmxBEGetSelectionAdjustedPropertyWindow (WindowPtr pWin); + +void +dmxSelectionClear (ScreenPtr pScreen, + Window owner, + Atom xSelection); + +void +dmxSelectionNotify (ScreenPtr pScreen, + Window requestor, + Atom xSelection, + Atom xTarget, + Atom xProperty, + Time xTime); + +Bool +dmxSelectionPropertyNotify (ScreenPtr pScreen, + Window requestor, + int state, + Atom xProperty, + Time xTime); + +Bool +dmxSelectionPropertyReplyCheck (ScreenPtr pScreen, + unsigned int sequence, + xcb_generic_reply_t *reply); + +void +dmxSelectionRequest (ScreenPtr pScreen, + Window owner, + Window requestor, + Atom xSelection, + Atom xTarget, + Atom xProperty, + Time xTime); + +void +dmxSelectionPropertyChangeCheck (WindowPtr pWin, + Atom property, + int nUnits); + +Bool +dmxCreateSelectionProxies (void); + +void +dmxInitSelections (void); + +void +dmxResetSelections (void); #endif /* DMXSELECTION_H */ diff --git a/hw/dmx/dmxshm.c b/hw/dmx/dmxshm.c index 8a5969128..f16ec7f51 100644 --- a/hw/dmx/dmxshm.c +++ b/hw/dmx/dmxshm.c @@ -650,12 +650,13 @@ dmxShmInit (ScreenPtr pScreen) return FALSE; } - mask = (GCFunction | GCPlaneMask | GCClipMask); + mask = (GCFunction | GCPlaneMask | GCClipMask | GCGraphicsExposures); - gcvals.function = GXcopy; - gcvals.plane_mask = AllPlanes; - gcvals.clip_mask = None; - gcvals.foreground = 0; + gcvals.function = GXcopy; + gcvals.plane_mask = AllPlanes; + gcvals.clip_mask = None; + gcvals.foreground = 0; + gcvals.graphics_exposures = FALSE; pixmap = xcb_generate_id (dmxScreen->connection); xcb_create_pixmap (dmxScreen->connection, diff --git a/hw/dmx/dmxsync.c b/hw/dmx/dmxsync.c index e2c94c8e3..230bc79ba 100644 --- a/hw/dmx/dmxsync.c +++ b/hw/dmx/dmxsync.c @@ -283,9 +283,8 @@ dmxScreenReplyCheckSync (ScreenPtr pScreen, { DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; - if (reply->response_type || reply->pad0 != DMX_DETACHED) - if (sequence != dmxScreen->sync.sequence) - return FALSE; + if (sequence != dmxScreen->sync.sequence) + return FALSE; if (dmxScreen->sync.sequence) { diff --git a/hw/dmx/dmxwindow.c b/hw/dmx/dmxwindow.c index a0853f53c..1dcbd4a30 100644 --- a/hw/dmx/dmxwindow.c +++ b/hw/dmx/dmxwindow.c @@ -51,6 +51,7 @@ #include "dmxfont.h" #include "dmxatom.h" #include "dmxprop.h" +#include "dmxselection.h" #ifdef RENDER #include "dmxpict.h" #endif @@ -152,7 +153,7 @@ Window dmxCreateRootWindow(WindowPtr pWindow) XLIB_EPILOGUE (dmxScreen); dmxPropertyWindow (dmxScreen, win); - + return win; } @@ -1347,12 +1348,13 @@ dmxBESetWindowProperty (WindowPtr pWindow, { ScreenPtr pScreen = pWindow->drawable.pScreen; DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; - dmxWinPrivPtr pWinPriv = DMX_GET_WINDOW_PRIV (pWindow); unsigned char *data = pProp->data; const char *format = NULL; + Window window; int i; - if (!pWinPriv->window) + window = dmxBEGetSelectionAdjustedPropertyWindow (pWindow); + if (!window) return; /* only 32 bit data types can be translated */ @@ -1429,7 +1431,7 @@ dmxBESetWindowProperty (WindowPtr pWindow, XLIB_PROLOGUE (dmxScreen); XChangeProperty (dmxScreen->beDisplay, - pWinPriv->window, + window, dmxBEAtom (dmxScreen, pProp->propertyName), dmxBEAtom (dmxScreen, pProp->type), pProp->format,