1/*
2 * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Copyright © 2003 Keith Packard
24 *
25 * Permission to use, copy, modify, distribute, and sell this software and its
26 * documentation for any purpose is hereby granted without fee, provided that
27 * the above copyright notice appear in all copies and that both that
28 * copyright notice and this permission notice appear in supporting
29 * documentation, and that the name of Keith Packard not be used in
30 * advertising or publicity pertaining to distribution of the software without
31 * specific, written prior permission.  Keith Packard makes no
32 * representations about the suitability of this software for any purpose.  It
33 * is provided "as is" without express or implied warranty.
34 *
35 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
36 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
37 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
38 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
39 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
40 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
41 * PERFORMANCE OF THIS SOFTWARE.
42 */
43
44#ifdef HAVE_DIX_CONFIG_H
45#include <dix-config.h>
46#endif
47
48#include "compint.h"
49
50static void
51compScreenUpdate (ScreenPtr pScreen)
52{
53    compCheckTree (pScreen);
54    compPaintChildrenToWindow (pScreen->root);
55}
56
57static void
58compBlockHandler (int	    i,
59		  pointer   blockData,
60		  pointer   pTimeout,
61		  pointer   pReadmask)
62{
63    ScreenPtr	    pScreen = screenInfo.screens[i];
64    CompScreenPtr   cs = GetCompScreen (pScreen);
65
66    pScreen->BlockHandler = cs->BlockHandler;
67    compScreenUpdate (pScreen);
68    (*pScreen->BlockHandler) (i, blockData, pTimeout, pReadmask);
69
70    /* Next damage will restore the block handler */
71    cs->BlockHandler = NULL;
72}
73
74static void
75compReportDamage (DamagePtr pDamage, RegionPtr pRegion, void *closure)
76{
77    WindowPtr	    pWin = (WindowPtr) closure;
78    ScreenPtr	    pScreen = pWin->drawable.pScreen;
79    CompScreenPtr   cs = GetCompScreen (pScreen);
80    CompWindowPtr   cw = GetCompWindow (pWin);
81
82    if (!cs->BlockHandler) {
83        cs->BlockHandler = pScreen->BlockHandler;
84        pScreen->BlockHandler = compBlockHandler;
85    }
86    cw->damaged = TRUE;
87
88    /* Mark the ancestors */
89    pWin = pWin->parent;
90    while (pWin) {
91	if (pWin->damagedDescendants)
92	    break;
93	pWin->damagedDescendants = TRUE;
94	pWin = pWin->parent;
95    }
96}
97
98static void
99compDestroyDamage (DamagePtr pDamage, void *closure)
100{
101    WindowPtr	    pWin = (WindowPtr) closure;
102    CompWindowPtr   cw = GetCompWindow (pWin);
103
104    cw->damage = 0;
105}
106
107/*
108 * Redirect one window for one client
109 */
110int
111compRedirectWindow (ClientPtr pClient, WindowPtr pWin, int update)
112{
113    CompWindowPtr	cw = GetCompWindow (pWin);
114    CompClientWindowPtr	ccw;
115    Bool		wasMapped = pWin->mapped;
116    CompScreenPtr       cs = GetCompScreen(pWin->drawable.pScreen);
117
118    if (pWin == cs->pOverlayWin) {
119	return Success;
120    }
121
122    if (!pWin->parent)
123	return BadMatch;
124
125    /*
126     * Only one Manual update is allowed
127     */
128    if (cw && update == CompositeRedirectManual)
129	for (ccw = cw->clients; ccw; ccw = ccw->next)
130	    if (ccw->update == CompositeRedirectManual)
131		return BadAccess;
132
133    /*
134     * Allocate per-client per-window structure
135     * The client *could* allocate multiple, but while supported,
136     * it is not expected to be common
137     */
138    ccw = malloc(sizeof (CompClientWindowRec));
139    if (!ccw)
140	return BadAlloc;
141    ccw->id = FakeClientID (pClient->index);
142    ccw->update = update;
143    /*
144     * Now make sure there's a per-window structure to hang this from
145     */
146    if (!cw)
147    {
148	cw = malloc(sizeof (CompWindowRec));
149	if (!cw)
150	{
151	    free(ccw);
152	    return BadAlloc;
153	}
154	cw->damage = DamageCreate (compReportDamage,
155				   compDestroyDamage,
156				   DamageReportNonEmpty,
157				   FALSE,
158				   pWin->drawable.pScreen,
159				   pWin);
160	if (!cw->damage)
161	{
162	    free(ccw);
163	    free(cw);
164	    return BadAlloc;
165	}
166	if (wasMapped)
167	{
168	    DisableMapUnmapEvents (pWin);
169	    UnmapWindow (pWin, FALSE);
170	    EnableMapUnmapEvents (pWin);
171	}
172
173	RegionNull(&cw->borderClip);
174	cw->borderClipX = 0;
175	cw->borderClipY = 0;
176	cw->update = CompositeRedirectAutomatic;
177	cw->clients = 0;
178	cw->oldx = COMP_ORIGIN_INVALID;
179	cw->oldy = COMP_ORIGIN_INVALID;
180	cw->damageRegistered = FALSE;
181	cw->damaged = FALSE;
182	cw->pOldPixmap = NullPixmap;
183	dixSetPrivate(&pWin->devPrivates, CompWindowPrivateKey, cw);
184    }
185    ccw->next = cw->clients;
186    cw->clients = ccw;
187    if (!AddResource (ccw->id, CompositeClientWindowType, pWin))
188	return BadAlloc;
189    if (ccw->update == CompositeRedirectManual)
190    {
191	/* If the window was CompositeRedirectAutomatic, then
192	 * unmap the window so that the parent clip list will
193	 * be correctly recomputed.
194	 */
195	if (pWin->mapped)
196	{
197	    DisableMapUnmapEvents (pWin);
198	    UnmapWindow (pWin, FALSE);
199	    EnableMapUnmapEvents (pWin);
200	}
201	if (cw->damageRegistered)
202	{
203	    DamageUnregister (&pWin->drawable, cw->damage);
204	    cw->damageRegistered = FALSE;
205	}
206	cw->update = CompositeRedirectManual;
207    }
208
209    if (!compCheckRedirect (pWin))
210    {
211	FreeResource (ccw->id, RT_NONE);
212	return BadAlloc;
213    }
214    if (wasMapped && !pWin->mapped)
215    {
216	Bool	overrideRedirect = pWin->overrideRedirect;
217	pWin->overrideRedirect = TRUE;
218	DisableMapUnmapEvents (pWin);
219	MapWindow (pWin, pClient);
220	EnableMapUnmapEvents (pWin);
221	pWin->overrideRedirect = overrideRedirect;
222    }
223
224    return Success;
225}
226
227/*
228 * Free one of the per-client per-window resources, clearing
229 * redirect and the per-window pointer as appropriate
230 */
231void
232compFreeClientWindow (WindowPtr pWin, XID id)
233{
234    CompWindowPtr	cw = GetCompWindow (pWin);
235    CompClientWindowPtr	ccw, *prev;
236    Bool		wasMapped = pWin->mapped;
237
238    if (!cw)
239	return;
240    for (prev = &cw->clients; (ccw = *prev); prev = &ccw->next)
241    {
242	if (ccw->id == id)
243	{
244	    *prev = ccw->next;
245	    if (ccw->update == CompositeRedirectManual)
246		cw->update = CompositeRedirectAutomatic;
247	    free(ccw);
248	    break;
249	}
250    }
251    if (!cw->clients)
252    {
253	if (wasMapped)
254	{
255	    DisableMapUnmapEvents (pWin);
256	    UnmapWindow (pWin, FALSE);
257	    EnableMapUnmapEvents (pWin);
258	}
259
260	if (pWin->redirectDraw != RedirectDrawNone)
261	    compFreePixmap (pWin);
262
263	if (cw->damage)
264	    DamageDestroy (cw->damage);
265
266	RegionUninit(&cw->borderClip);
267
268	dixSetPrivate(&pWin->devPrivates, CompWindowPrivateKey, NULL);
269	free(cw);
270    }
271    else if (cw->update == CompositeRedirectAutomatic &&
272	     !cw->damageRegistered && pWin->redirectDraw != RedirectDrawNone)
273    {
274	DamageRegister (&pWin->drawable, cw->damage);
275	cw->damageRegistered = TRUE;
276	pWin->redirectDraw = RedirectDrawAutomatic;
277	DamageDamageRegion(&pWin->drawable, &pWin->borderSize);
278    }
279    if (wasMapped && !pWin->mapped)
280    {
281	Bool	overrideRedirect = pWin->overrideRedirect;
282	pWin->overrideRedirect = TRUE;
283	DisableMapUnmapEvents (pWin);
284	MapWindow (pWin, clients[CLIENT_ID(id)]);
285	EnableMapUnmapEvents (pWin);
286	pWin->overrideRedirect = overrideRedirect;
287    }
288}
289
290/*
291 * This is easy, just free the appropriate resource.
292 */
293
294int
295compUnredirectWindow (ClientPtr pClient, WindowPtr pWin, int update)
296{
297    CompWindowPtr	cw = GetCompWindow (pWin);
298    CompClientWindowPtr	ccw;
299
300    if (!cw)
301	return BadValue;
302
303    for (ccw = cw->clients; ccw; ccw = ccw->next)
304	if (ccw->update == update && CLIENT_ID(ccw->id) == pClient->index)
305	{
306	    FreeResource (ccw->id, RT_NONE);
307	    return Success;
308	}
309    return BadValue;
310}
311
312/*
313 * Redirect all subwindows for one client
314 */
315
316int
317compRedirectSubwindows (ClientPtr pClient, WindowPtr pWin, int update)
318{
319    CompSubwindowsPtr	csw = GetCompSubwindows (pWin);
320    CompClientWindowPtr	ccw;
321    WindowPtr		pChild;
322
323    /*
324     * Only one Manual update is allowed
325     */
326    if (csw && update == CompositeRedirectManual)
327	for (ccw = csw->clients; ccw; ccw = ccw->next)
328	    if (ccw->update == CompositeRedirectManual)
329		return BadAccess;
330    /*
331     * Allocate per-client per-window structure
332     * The client *could* allocate multiple, but while supported,
333     * it is not expected to be common
334     */
335    ccw = malloc(sizeof (CompClientWindowRec));
336    if (!ccw)
337	return BadAlloc;
338    ccw->id = FakeClientID (pClient->index);
339    ccw->update = update;
340    /*
341     * Now make sure there's a per-window structure to hang this from
342     */
343    if (!csw)
344    {
345	csw = malloc(sizeof (CompSubwindowsRec));
346	if (!csw)
347	{
348	    free(ccw);
349	    return BadAlloc;
350	}
351	csw->update = CompositeRedirectAutomatic;
352	csw->clients = 0;
353	dixSetPrivate(&pWin->devPrivates, CompSubwindowsPrivateKey, csw);
354    }
355    /*
356     * Redirect all existing windows
357     */
358    for (pChild = pWin->lastChild; pChild; pChild = pChild->prevSib)
359    {
360	int ret = compRedirectWindow (pClient, pChild, update);
361	if (ret != Success)
362	{
363	    for (pChild = pChild->nextSib; pChild; pChild = pChild->nextSib)
364		(void) compUnredirectWindow (pClient, pChild, update);
365	    if (!csw->clients)
366	    {
367		free(csw);
368		dixSetPrivate(&pWin->devPrivates, CompSubwindowsPrivateKey, 0);
369	    }
370	    free(ccw);
371	    return ret;
372	}
373    }
374    /*
375     * Hook into subwindows list
376     */
377    ccw->next = csw->clients;
378    csw->clients = ccw;
379    if (!AddResource (ccw->id, CompositeClientSubwindowsType, pWin))
380	return BadAlloc;
381    if (ccw->update == CompositeRedirectManual)
382    {
383	csw->update = CompositeRedirectManual;
384	/*
385	 * tell damage extension that damage events for this client are
386	 * critical output
387	 */
388	DamageExtSetCritical (pClient, TRUE);
389    }
390    return Success;
391}
392
393/*
394 * Free one of the per-client per-subwindows resources,
395 * which frees one redirect per subwindow
396 */
397void
398compFreeClientSubwindows (WindowPtr pWin, XID id)
399{
400    CompSubwindowsPtr	csw = GetCompSubwindows (pWin);
401    CompClientWindowPtr	ccw, *prev;
402    WindowPtr		pChild;
403
404    if (!csw)
405	return;
406    for (prev = &csw->clients; (ccw = *prev); prev = &ccw->next)
407    {
408	if (ccw->id == id)
409	{
410	    ClientPtr	pClient = clients[CLIENT_ID(id)];
411
412	    *prev = ccw->next;
413	    if (ccw->update == CompositeRedirectManual)
414	    {
415		/*
416		 * tell damage extension that damage events for this client are
417		 * critical output
418		 */
419		DamageExtSetCritical (pClient, FALSE);
420		csw->update = CompositeRedirectAutomatic;
421		if (pWin->mapped)
422		    (*pWin->drawable.pScreen->ClearToBackground)(pWin, 0, 0, 0, 0, TRUE);
423	    }
424
425	    /*
426	     * Unredirect all existing subwindows
427	     */
428	    for (pChild = pWin->lastChild; pChild; pChild = pChild->prevSib)
429		(void) compUnredirectWindow (pClient, pChild, ccw->update);
430
431	    free(ccw);
432	    break;
433	}
434    }
435
436    /*
437     * Check if all of the per-client records are gone
438     */
439    if (!csw->clients)
440    {
441	dixSetPrivate(&pWin->devPrivates, CompSubwindowsPrivateKey, NULL);
442	free(csw);
443    }
444}
445
446/*
447 * This is easy, just free the appropriate resource.
448 */
449
450int
451compUnredirectSubwindows (ClientPtr pClient, WindowPtr pWin, int update)
452{
453    CompSubwindowsPtr	csw = GetCompSubwindows (pWin);
454    CompClientWindowPtr	ccw;
455
456    if (!csw)
457	return BadValue;
458    for (ccw = csw->clients; ccw; ccw = ccw->next)
459	if (ccw->update == update && CLIENT_ID(ccw->id) == pClient->index)
460	{
461	    FreeResource (ccw->id, RT_NONE);
462	    return Success;
463	}
464    return BadValue;
465}
466
467/*
468 * Add redirection information for one subwindow (during reparent)
469 */
470
471int
472compRedirectOneSubwindow (WindowPtr pParent, WindowPtr pWin)
473{
474    CompSubwindowsPtr	csw = GetCompSubwindows (pParent);
475    CompClientWindowPtr	ccw;
476
477    if (!csw)
478	return Success;
479    for (ccw = csw->clients; ccw; ccw = ccw->next)
480    {
481	int ret = compRedirectWindow (clients[CLIENT_ID(ccw->id)],
482				      pWin, ccw->update);
483	if (ret != Success)
484	    return ret;
485    }
486    return Success;
487}
488
489/*
490 * Remove redirection information for one subwindow (during reparent)
491 */
492
493int
494compUnredirectOneSubwindow (WindowPtr pParent, WindowPtr pWin)
495{
496    CompSubwindowsPtr	csw = GetCompSubwindows (pParent);
497    CompClientWindowPtr	ccw;
498
499    if (!csw)
500	return Success;
501    for (ccw = csw->clients; ccw; ccw = ccw->next)
502    {
503	int ret = compUnredirectWindow (clients[CLIENT_ID(ccw->id)],
504					pWin, ccw->update);
505	if (ret != Success)
506	    return ret;
507    }
508    return Success;
509}
510
511static int
512bgNoneVisitWindow(WindowPtr pWin, void *null)
513{
514    if (pWin->backgroundState != BackgroundPixmap)
515	return WT_WALKCHILDREN;
516    if (pWin->background.pixmap != None)
517	return WT_WALKCHILDREN;
518
519    return WT_STOPWALKING;
520}
521
522static PixmapPtr
523compNewPixmap (WindowPtr pWin, int x, int y, int w, int h, Bool map)
524{
525    ScreenPtr	    pScreen = pWin->drawable.pScreen;
526    WindowPtr	    pParent = pWin->parent;
527    PixmapPtr	    pPixmap;
528
529    pPixmap = (*pScreen->CreatePixmap) (pScreen, w, h, pWin->drawable.depth,
530					CREATE_PIXMAP_USAGE_BACKING_PIXMAP);
531
532    if (!pPixmap)
533	return 0;
534
535    pPixmap->screen_x = x;
536    pPixmap->screen_y = y;
537
538    /* resize allocations will update later in compCopyWindow, not here */
539    if (!map)
540	return pPixmap;
541
542    /*
543     * If there's no bg=None in the tree, we're done.
544     *
545     * We could optimize this more by collection the regions of all the
546     * bg=None subwindows and feeding that in as the clip for the
547     * CopyArea below, but since window trees are shallow these days it
548     * might not be worth the effort.
549     */
550    if (TraverseTree(pWin, bgNoneVisitWindow, NULL) == WT_NOMATCH)
551	return pPixmap;
552
553    /*
554     * Copy bits from the parent into the new pixmap so that it will
555     * have "reasonable" contents in case for background None areas.
556     */
557    if (pParent->drawable.depth == pWin->drawable.depth)
558    {
559	GCPtr	pGC = GetScratchGC (pWin->drawable.depth, pScreen);
560
561	if (pGC)
562	{
563	    ChangeGCVal val;
564	    val.val = IncludeInferiors;
565
566	    ValidateGC(&pPixmap->drawable, pGC);
567	    ChangeGC (serverClient, pGC, GCSubwindowMode, &val);
568	    (*pGC->ops->CopyArea) (&pParent->drawable,
569				   &pPixmap->drawable,
570				   pGC,
571				   x - pParent->drawable.x,
572				   y - pParent->drawable.y,
573				   w, h, 0, 0);
574	    FreeScratchGC (pGC);
575	}
576    }
577    else
578    {
579	PictFormatPtr	pSrcFormat = compWindowFormat (pParent);
580	PictFormatPtr	pDstFormat = compWindowFormat (pWin);
581	XID		inferiors = IncludeInferiors;
582	int		error;
583
584	PicturePtr	pSrcPicture = CreatePicture (None,
585						     &pParent->drawable,
586						     pSrcFormat,
587						     CPSubwindowMode,
588						     &inferiors,
589						     serverClient, &error);
590
591	PicturePtr	pDstPicture = CreatePicture (None,
592						     &pPixmap->drawable,
593						     pDstFormat,
594						     0, 0,
595						     serverClient, &error);
596
597	if (pSrcPicture && pDstPicture)
598	{
599	    CompositePicture (PictOpSrc,
600			      pSrcPicture,
601			      NULL,
602			      pDstPicture,
603			      x - pParent->drawable.x,
604			      y - pParent->drawable.y,
605			      0, 0, 0, 0, w, h);
606	}
607	if (pSrcPicture)
608	    FreePicture (pSrcPicture, 0);
609	if (pDstPicture)
610	    FreePicture (pDstPicture, 0);
611    }
612    return pPixmap;
613}
614
615Bool
616compAllocPixmap (WindowPtr pWin)
617{
618    int		    bw = (int) pWin->borderWidth;
619    int		    x = pWin->drawable.x - bw;
620    int		    y = pWin->drawable.y - bw;
621    int		    w = pWin->drawable.width + (bw << 1);
622    int		    h = pWin->drawable.height + (bw << 1);
623    PixmapPtr	    pPixmap = compNewPixmap (pWin, x, y, w, h, TRUE);
624    CompWindowPtr   cw = GetCompWindow (pWin);
625
626    if (!pPixmap)
627	return FALSE;
628    if (cw->update == CompositeRedirectAutomatic)
629	pWin->redirectDraw = RedirectDrawAutomatic;
630    else
631	pWin->redirectDraw = RedirectDrawManual;
632
633    compSetPixmap (pWin, pPixmap);
634    cw->oldx = COMP_ORIGIN_INVALID;
635    cw->oldy = COMP_ORIGIN_INVALID;
636    cw->damageRegistered = FALSE;
637    if (cw->update == CompositeRedirectAutomatic)
638    {
639	DamageRegister (&pWin->drawable, cw->damage);
640	cw->damageRegistered = TRUE;
641    }
642    return TRUE;
643}
644
645void
646compFreePixmap (WindowPtr pWin)
647{
648    ScreenPtr	    pScreen = pWin->drawable.pScreen;
649    PixmapPtr	    pRedirectPixmap, pParentPixmap;
650    CompWindowPtr   cw = GetCompWindow (pWin);
651
652    if (cw->damageRegistered)
653    {
654	DamageUnregister (&pWin->drawable, cw->damage);
655	cw->damageRegistered = FALSE;
656	DamageEmpty (cw->damage);
657    }
658    /*
659     * Move the parent-constrained border clip region back into
660     * the window so that ValidateTree will handle the unmap
661     * case correctly.  Unmap adds the window borderClip to the
662     * parent exposed area; regions beyond the parent cause crashes
663     */
664    RegionCopy(&pWin->borderClip, &cw->borderClip);
665    pRedirectPixmap = (*pScreen->GetWindowPixmap) (pWin);
666    pParentPixmap = (*pScreen->GetWindowPixmap) (pWin->parent);
667    pWin->redirectDraw = RedirectDrawNone;
668    compSetPixmap (pWin, pParentPixmap);
669    (*pScreen->DestroyPixmap) (pRedirectPixmap);
670}
671
672/*
673 * Make sure the pixmap is the right size and offset.  Allocate a new
674 * pixmap to change size, adjust origin to change offset, leaving the
675 * old pixmap in cw->pOldPixmap so bits can be recovered
676 */
677Bool
678compReallocPixmap (WindowPtr pWin, int draw_x, int draw_y,
679		   unsigned int w, unsigned int h, int bw)
680{
681    ScreenPtr	    pScreen = pWin->drawable.pScreen;
682    PixmapPtr	    pOld = (*pScreen->GetWindowPixmap) (pWin);
683    PixmapPtr	    pNew;
684    CompWindowPtr   cw = GetCompWindow (pWin);
685    int		    pix_x, pix_y;
686    int		    pix_w, pix_h;
687
688    assert (cw && pWin->redirectDraw != RedirectDrawNone);
689    cw->oldx = pOld->screen_x;
690    cw->oldy = pOld->screen_y;
691    pix_x = draw_x - bw;
692    pix_y = draw_y - bw;
693    pix_w = w + (bw << 1);
694    pix_h = h + (bw << 1);
695    if (pix_w != pOld->drawable.width || pix_h != pOld->drawable.height)
696    {
697	pNew = compNewPixmap (pWin, pix_x, pix_y, pix_w, pix_h, FALSE);
698	if (!pNew)
699	    return FALSE;
700	cw->pOldPixmap = pOld;
701	compSetPixmap (pWin, pNew);
702    }
703    else
704    {
705	pNew = pOld;
706	cw->pOldPixmap = 0;
707    }
708    pNew->screen_x = pix_x;
709    pNew->screen_y = pix_y;
710    return TRUE;
711}
712