1 /*
2  * Copyright © 2001 Keith Packard
3  *
4  * Partly based on code that is Copyright © The XFree86 Project Inc.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of Keith Packard not be used in
11  * advertising or publicity pertaining to distribution of the software without
12  * specific, written prior permission.  Keith Packard makes no
13  * representations about the suitability of this software for any purpose.  It
14  * is provided "as is" without express or implied warranty.
15  *
16  * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18  * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22  * PERFORMANCE OF THIS SOFTWARE.
23  */
24 
25 /** @file
26  * This file covers the initialization and teardown of UXA, and has various
27  * functions not responsible for performing rendering, pixmap migration, or
28  * memory management.
29  */
30 
31 #ifdef HAVE_DIX_CONFIG_H
32 #include <dix-config.h>
33 #endif
34 
35 #include <stdlib.h>
36 
37 #include "uxa-priv.h"
38 #include <X11/fonts/fontstruct.h>
39 #include "dixfontstr.h"
40 #include "uxa.h"
41 
42 #if HAS_DEVPRIVATEKEYREC
43 DevPrivateKeyRec uxa_screen_index;
44 #else
45 int uxa_screen_index;
46 #endif
47 
48 /**
49  * uxa_get_drawable_pixmap() returns a backing pixmap for a given drawable.
50  *
51  * @param pDrawable the drawable being requested.
52  *
53  * This function returns the backing pixmap for a drawable, whether it is a
54  * redirected window, unredirected window, or already a pixmap.  Note that
55  * coordinate translation is needed when drawing to the backing pixmap of a
56  * redirected window, and the translation coordinates are provided by calling
57  * uxa_get_drawable_pixmap() on the drawable.
58  */
uxa_get_drawable_pixmap(DrawablePtr pDrawable)59 PixmapPtr uxa_get_drawable_pixmap(DrawablePtr pDrawable)
60 {
61 	if (pDrawable->type == DRAWABLE_WINDOW)
62 		return pDrawable->pScreen->
63 		    GetWindowPixmap((WindowPtr) pDrawable);
64 	else
65 		return (PixmapPtr) pDrawable;
66 }
67 
68 /**
69  * Sets the offsets to add to coordinates to make them address the same bits in
70  * the backing drawable. These coordinates are nonzero only for redirected
71  * windows.
72  */
73 void
uxa_get_drawable_deltas(DrawablePtr pDrawable,PixmapPtr pPixmap,int * xp,int * yp)74 uxa_get_drawable_deltas(DrawablePtr pDrawable, PixmapPtr pPixmap,
75 			int *xp, int *yp)
76 {
77 #ifdef COMPOSITE
78 	if (pDrawable->type == DRAWABLE_WINDOW) {
79 		*xp = -pPixmap->screen_x;
80 		*yp = -pPixmap->screen_y;
81 		return;
82 	}
83 #endif
84 
85 	*xp = 0;
86 	*yp = 0;
87 }
88 
89 /**
90  * uxa_pixmap_is_offscreen() is used to determine if a pixmap is in offscreen
91  * memory, meaning that acceleration could probably be done to it, and that it
92  * will need to be wrapped by PrepareAccess()/FinishAccess() when accessing it
93  * with the CPU.
94  *
95  * Note that except for UploadToScreen()/DownloadFromScreen() (which explicitly
96  * deal with moving pixmaps in and out of system memory), UXA will give drivers
97  * pixmaps as arguments for which uxa_pixmap_is_offscreen() is TRUE.
98  *
99  * @return TRUE if the given drawable is in framebuffer memory.
100  */
uxa_pixmap_is_offscreen(PixmapPtr p)101 Bool uxa_pixmap_is_offscreen(PixmapPtr p)
102 {
103 	ScreenPtr pScreen = p->drawable.pScreen;
104 	uxa_screen_t *uxa_screen = uxa_get_screen(pScreen);
105 
106 	if (uxa_screen->info->pixmap_is_offscreen)
107 		return uxa_screen->info->pixmap_is_offscreen(p);
108 
109 	return FALSE;
110 }
111 
112 /**
113  * uxa_drawable_is_offscreen() is a convenience wrapper for
114  * uxa_pixmap_is_offscreen().
115  */
uxa_drawable_is_offscreen(DrawablePtr pDrawable)116 Bool uxa_drawable_is_offscreen(DrawablePtr pDrawable)
117 {
118 	return uxa_pixmap_is_offscreen(uxa_get_drawable_pixmap(pDrawable));
119 }
120 
121 /**
122   * Returns the pixmap which backs a drawable, and the offsets to add to
123   * coordinates to make them address the same bits in the backing drawable.
124   */
uxa_get_offscreen_pixmap(DrawablePtr drawable,int * xp,int * yp)125 PixmapPtr uxa_get_offscreen_pixmap(DrawablePtr drawable, int *xp, int *yp)
126 {
127 	PixmapPtr pixmap = uxa_get_drawable_pixmap(drawable);
128 
129 	uxa_get_drawable_deltas(drawable, pixmap, xp, yp);
130 
131 	if (uxa_pixmap_is_offscreen(pixmap))
132 		return pixmap;
133 	else
134 		return NULL;
135 }
136 
137 /**
138  * uxa_prepare_access() is UXA's wrapper for the driver's PrepareAccess() handler.
139  *
140  * It deals with waiting for synchronization with the card, determining if
141  * PrepareAccess() is necessary, and working around PrepareAccess() failure.
142  */
uxa_prepare_access(DrawablePtr pDrawable,uxa_access_t access)143 Bool uxa_prepare_access(DrawablePtr pDrawable, uxa_access_t access)
144 {
145 	ScreenPtr pScreen = pDrawable->pScreen;
146 	uxa_screen_t *uxa_screen = uxa_get_screen(pScreen);
147 	PixmapPtr pPixmap = uxa_get_drawable_pixmap(pDrawable);
148 	Bool offscreen = uxa_pixmap_is_offscreen(pPixmap);
149 
150 	if (!offscreen)
151 		return TRUE;
152 
153 	if (uxa_screen->info->prepare_access)
154 		return (*uxa_screen->info->prepare_access) (pPixmap, access);
155 	return TRUE;
156 }
157 
158 /**
159  * uxa_finish_access() is UXA's wrapper for the driver's finish_access() handler.
160  *
161  * It deals with calling the driver's finish_access() only if necessary.
162  */
uxa_finish_access(DrawablePtr pDrawable,uxa_access_t access)163 void uxa_finish_access(DrawablePtr pDrawable, uxa_access_t access)
164 {
165 	ScreenPtr pScreen = pDrawable->pScreen;
166 	uxa_screen_t *uxa_screen = uxa_get_screen(pScreen);
167 	PixmapPtr pPixmap;
168 
169 	if (uxa_screen->info->finish_access == NULL)
170 		return;
171 
172 	pPixmap = uxa_get_drawable_pixmap(pDrawable);
173 	if (!uxa_pixmap_is_offscreen(pPixmap))
174 		return;
175 
176 	(*uxa_screen->info->finish_access) (pPixmap, access);
177 }
178 
179 /**
180  * uxa_validate_gc() sets the ops to UXA's implementations, which may be
181  * accelerated or may sync the card and fall back to fb.
182  */
183 static void
uxa_validate_gc(GCPtr pGC,unsigned long changes,DrawablePtr pDrawable)184 uxa_validate_gc(GCPtr pGC, unsigned long changes, DrawablePtr pDrawable)
185 {
186 	/* fbValidateGC will do direct access to pixmaps if the tiling has
187 	 * changed.
188 	 * Preempt fbValidateGC by doing its work and masking the change out, so
189 	 * that we can do the Prepare/finish_access.
190 	 */
191 
192 #ifdef FB_24_32BIT
193 	if ((changes & GCTile) && fbGetRotatedPixmap(pGC)) {
194 		(*pGC->pScreen->DestroyPixmap) (fbGetRotatedPixmap(pGC));
195 		fbGetRotatedPixmap(pGC) = 0;
196 	}
197 
198 	if (pGC->fillStyle == FillTiled) {
199 		PixmapPtr pOldTile, pNewTile;
200 
201 		pOldTile = pGC->tile.pixmap;
202 		if (pOldTile->drawable.bitsPerPixel != pDrawable->bitsPerPixel) {
203 			pNewTile = fbGetRotatedPixmap(pGC);
204 			if (!pNewTile ||
205 			    pNewTile->drawable.bitsPerPixel !=
206 			    pDrawable->bitsPerPixel) {
207 				if (pNewTile)
208 					(*pGC->pScreen->
209 					 DestroyPixmap) (pNewTile);
210 				/* fb24_32ReformatTile will do direct access
211 				 * of a newly-allocated pixmap.  This isn't a
212 				 * problem yet, since we don't put pixmaps in
213 				 * FB until at least one accelerated UXA op.
214 				 */
215 				if (uxa_prepare_access
216 				    (&pOldTile->drawable, UXA_ACCESS_RO)) {
217 					pNewTile =
218 					    fb24_32ReformatTile(pOldTile,
219 								pDrawable->
220 								bitsPerPixel);
221 					uxa_finish_access(&pOldTile->drawable, UXA_ACCESS_RO);
222 				}
223 			}
224 			if (pNewTile) {
225 				fbGetRotatedPixmap(pGC) = pOldTile;
226 				pGC->tile.pixmap = pNewTile;
227 				changes |= GCTile;
228 			}
229 		}
230 	}
231 #endif
232 	if (changes & GCTile) {
233 		if (!pGC->tileIsPixel
234 		    && FbEvenTile(pGC->tile.pixmap->drawable.width *
235 				  pDrawable->bitsPerPixel)) {
236 			if (uxa_prepare_access
237 			    (&pGC->tile.pixmap->drawable, UXA_ACCESS_RW)) {
238 				fbPadPixmap(pGC->tile.pixmap);
239 				uxa_finish_access(&pGC->tile.pixmap->drawable, UXA_ACCESS_RW);
240 			}
241 		}
242 		/* Mask out the GCTile change notification, now that we've
243 		 * done FB's job for it.
244 		 */
245 		changes &= ~GCTile;
246 	}
247 
248 	if (changes & GCStipple && pGC->stipple) {
249 		/* We can't inline stipple handling like we do for GCTile
250 		 * because it sets fbgc privates.
251 		 */
252 		if (uxa_prepare_access(&pGC->stipple->drawable, UXA_ACCESS_RW)) {
253 			fbValidateGC(pGC, changes, pDrawable);
254 			uxa_finish_access(&pGC->stipple->drawable, UXA_ACCESS_RW);
255 		}
256 	} else {
257 		fbValidateGC(pGC, changes, pDrawable);
258 	}
259 
260 	pGC->ops = (GCOps *) & uxa_ops;
261 }
262 
263 static GCFuncs uxaGCFuncs = {
264 	uxa_validate_gc,
265 	miChangeGC,
266 	miCopyGC,
267 	miDestroyGC,
268 	miChangeClip,
269 	miDestroyClip,
270 	miCopyClip
271 };
272 
273 /**
274  * uxa_create_gc makes a new GC and hooks up its funcs handler, so that
275  * uxa_validate_gc() will get called.
276  */
uxa_create_gc(GCPtr pGC)277 static int uxa_create_gc(GCPtr pGC)
278 {
279 	if (!fbCreateGC(pGC))
280 		return FALSE;
281 
282 	pGC->funcs = &uxaGCFuncs;
283 
284 	return TRUE;
285 }
286 
uxa_prepare_access_window(WindowPtr pWin)287 Bool uxa_prepare_access_window(WindowPtr pWin)
288 {
289 	if (pWin->backgroundState == BackgroundPixmap) {
290 		if (!uxa_prepare_access
291 		    (&pWin->background.pixmap->drawable, UXA_ACCESS_RO))
292 			return FALSE;
293 	}
294 
295 	if (pWin->borderIsPixel == FALSE) {
296 		if (!uxa_prepare_access
297 		    (&pWin->border.pixmap->drawable, UXA_ACCESS_RO)) {
298 			if (pWin->backgroundState == BackgroundPixmap)
299 				uxa_finish_access(&pWin->background.pixmap->
300 						  drawable, UXA_ACCESS_RO);
301 			return FALSE;
302 		}
303 	}
304 	return TRUE;
305 }
306 
uxa_finish_access_window(WindowPtr pWin)307 void uxa_finish_access_window(WindowPtr pWin)
308 {
309 	if (pWin->backgroundState == BackgroundPixmap)
310 		uxa_finish_access(&pWin->background.pixmap->drawable, UXA_ACCESS_RO);
311 
312 	if (pWin->borderIsPixel == FALSE)
313 		uxa_finish_access(&pWin->border.pixmap->drawable, UXA_ACCESS_RO);
314 }
315 
uxa_change_window_attributes(WindowPtr pWin,unsigned long mask)316 static Bool uxa_change_window_attributes(WindowPtr pWin, unsigned long mask)
317 {
318 	Bool ret;
319 
320 	if (!uxa_prepare_access_window(pWin))
321 		return FALSE;
322 	ret = fbChangeWindowAttributes(pWin, mask);
323 	uxa_finish_access_window(pWin);
324 	return ret;
325 }
326 
uxa_bitmap_to_region(PixmapPtr pPix)327 static RegionPtr uxa_bitmap_to_region(PixmapPtr pPix)
328 {
329 	RegionPtr ret;
330 	if (!uxa_prepare_access(&pPix->drawable, UXA_ACCESS_RO))
331 		return NULL;
332 	ret = fbPixmapToRegion(pPix);
333 	uxa_finish_access(&pPix->drawable, UXA_ACCESS_RO);
334 	return ret;
335 }
336 
uxa_set_fallback_debug(ScreenPtr screen,Bool enable)337 void uxa_set_fallback_debug(ScreenPtr screen, Bool enable)
338 {
339 	uxa_screen_t *uxa_screen = uxa_get_screen(screen);
340 
341 	uxa_screen->fallback_debug = enable;
342 }
343 
uxa_set_force_fallback(ScreenPtr screen,Bool value)344 void uxa_set_force_fallback(ScreenPtr screen, Bool value)
345 {
346 	uxa_screen_t *uxa_screen = uxa_get_screen(screen);
347 
348 	uxa_screen->force_fallback = value;
349 }
350 
351 /**
352  * uxa_close_screen() unwraps its wrapped screen functions and tears down UXA's
353  * screen private, before calling down to the next CloseSccreen.
354  */
uxa_close_screen(CLOSE_SCREEN_ARGS_DECL)355 static Bool uxa_close_screen(CLOSE_SCREEN_ARGS_DECL)
356 {
357 	uxa_screen_t *uxa_screen = uxa_get_screen(screen);
358 #ifdef RENDER
359 	PictureScreenPtr ps = GetPictureScreenIfSet(screen);
360 #endif
361 	int n;
362 
363 	if (uxa_screen->solid_clear)
364 		FreePicture(uxa_screen->solid_clear, 0);
365 	if (uxa_screen->solid_black)
366 		FreePicture(uxa_screen->solid_black, 0);
367 	if (uxa_screen->solid_white)
368 		FreePicture(uxa_screen->solid_white, 0);
369 	for (n = 0; n < uxa_screen->solid_cache_size; n++)
370 		FreePicture(uxa_screen->solid_cache[n].picture, 0);
371 
372 	uxa_glyphs_fini(screen);
373 
374 #if XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(1,15,99,903,0)
375 	if (screen->devPrivate) {
376 		/* Destroy the pixmap created by miScreenInit() *before*
377 		 * chaining up as we finalize ourselves here and so this
378 		 * is the last chance we have of releasing our resources
379 		 * associated with the Pixmap. So do it first.
380 		 */
381 		(void) (*screen->DestroyPixmap) (screen->devPrivate);
382 		screen->devPrivate = NULL;
383 	}
384 #endif
385 
386 	screen->CreateGC = uxa_screen->SavedCreateGC;
387 	screen->CloseScreen = uxa_screen->SavedCloseScreen;
388 	screen->GetImage = uxa_screen->SavedGetImage;
389 	screen->GetSpans = uxa_screen->SavedGetSpans;
390 	screen->CreatePixmap = uxa_screen->SavedCreatePixmap;
391 	screen->DestroyPixmap = uxa_screen->SavedDestroyPixmap;
392 	screen->CopyWindow = uxa_screen->SavedCopyWindow;
393 	screen->ChangeWindowAttributes =
394 	    uxa_screen->SavedChangeWindowAttributes;
395 	screen->BitmapToRegion = uxa_screen->SavedBitmapToRegion;
396 #ifdef RENDER
397 	if (ps) {
398 		ps->Composite = uxa_screen->SavedComposite;
399 		ps->Glyphs = uxa_screen->SavedGlyphs;
400 		ps->Trapezoids = uxa_screen->SavedTrapezoids;
401 		ps->AddTraps = uxa_screen->SavedAddTraps;
402 		ps->Triangles = uxa_screen->SavedTriangles;
403 
404 		ps->UnrealizeGlyph = uxa_screen->SavedUnrealizeGlyph;
405 	}
406 #endif
407 
408 	free(uxa_screen);
409 
410 	return (*screen->CloseScreen) (CLOSE_SCREEN_ARGS);
411 }
412 
413 /**
414  * This function allocates a driver structure for UXA drivers to fill in.  By
415  * having UXA allocate the structure, the driver structure can be extended
416  * without breaking ABI between UXA and the drivers.  The driver's
417  * responsibility is to check beforehand that the UXA module has a matching
418  * major number and sufficient minor.  Drivers are responsible for freeing the
419  * driver structure using free().
420  *
421  * @return a newly allocated, zero-filled driver structure
422  */
uxa_driver_alloc(void)423 uxa_driver_t *uxa_driver_alloc(void)
424 {
425 	return calloc(1, sizeof(uxa_driver_t));
426 }
427 
428 /**
429  * @param screen screen being initialized
430  * @param pScreenInfo UXA driver record
431  *
432  * uxa_driver_init sets up UXA given a driver record filled in by the driver.
433  * pScreenInfo should have been allocated by uxa_driver_alloc().  See the
434  * comments in _UxaDriver for what must be filled in and what is optional.
435  *
436  * @return TRUE if UXA was successfully initialized.
437  */
uxa_driver_init(ScreenPtr screen,uxa_driver_t * uxa_driver)438 Bool uxa_driver_init(ScreenPtr screen, uxa_driver_t * uxa_driver)
439 {
440 	uxa_screen_t *uxa_screen;
441 
442 	if (!uxa_driver)
443 		return FALSE;
444 
445 	if (uxa_driver->uxa_major != UXA_VERSION_MAJOR ||
446 	    uxa_driver->uxa_minor > UXA_VERSION_MINOR) {
447 		LogMessage(X_ERROR,
448 			   "UXA(%d): driver's UXA version requirements "
449 			   "(%d.%d) are incompatible with UXA version (%d.%d)\n",
450 			   screen->myNum, uxa_driver->uxa_major,
451 			   uxa_driver->uxa_minor, UXA_VERSION_MAJOR,
452 			   UXA_VERSION_MINOR);
453 		return FALSE;
454 	}
455 
456 	if (!uxa_driver->prepare_solid) {
457 		LogMessage(X_ERROR,
458 			   "UXA(%d): uxa_driver_t::prepare_solid must be "
459 			   "non-NULL\n", screen->myNum);
460 		return FALSE;
461 	}
462 
463 	if (!uxa_driver->prepare_copy) {
464 		LogMessage(X_ERROR,
465 			   "UXA(%d): uxa_driver_t::prepare_copy must be "
466 			   "non-NULL\n", screen->myNum);
467 		return FALSE;
468 	}
469 #if HAS_DIXREGISTERPRIVATEKEY
470 	if (!dixRegisterPrivateKey(&uxa_screen_index, PRIVATE_SCREEN, 0))
471 	    return FALSE;
472 #endif
473 	uxa_screen = calloc(sizeof(uxa_screen_t), 1);
474 
475 	if (!uxa_screen) {
476 		LogMessage(X_WARNING,
477 			   "UXA(%d): Failed to allocate screen private\n",
478 			   screen->myNum);
479 		return FALSE;
480 	}
481 
482 	uxa_screen->info = uxa_driver;
483 
484 	dixSetPrivate(&screen->devPrivates, &uxa_screen_index, uxa_screen);
485 
486 	uxa_screen->force_fallback = FALSE;
487 
488 	uxa_screen->solid_cache_size = 0;
489 	uxa_screen->solid_clear = 0;
490 	uxa_screen->solid_black = 0;
491 	uxa_screen->solid_white = 0;
492 
493 //    exaDDXDriverInit(screen);
494 
495 	/*
496 	 * Replace various fb screen functions
497 	 */
498 	uxa_screen->SavedCloseScreen = screen->CloseScreen;
499 	screen->CloseScreen = uxa_close_screen;
500 
501 	uxa_screen->SavedCreateGC = screen->CreateGC;
502 	screen->CreateGC = uxa_create_gc;
503 
504 	uxa_screen->SavedGetImage = screen->GetImage;
505 	screen->GetImage = uxa_get_image;
506 
507 	uxa_screen->SavedGetSpans = screen->GetSpans;
508 	screen->GetSpans = uxa_get_spans;
509 
510 	uxa_screen->SavedCopyWindow = screen->CopyWindow;
511 	screen->CopyWindow = uxa_copy_window;
512 
513 	uxa_screen->SavedChangeWindowAttributes =
514 	    screen->ChangeWindowAttributes;
515 	screen->ChangeWindowAttributes = uxa_change_window_attributes;
516 
517 	uxa_screen->SavedBitmapToRegion = screen->BitmapToRegion;
518 	screen->BitmapToRegion = uxa_bitmap_to_region;
519 
520 #ifdef RENDER
521 	{
522 		PictureScreenPtr ps = GetPictureScreenIfSet(screen);
523 		if (ps) {
524 			uxa_screen->SavedComposite = ps->Composite;
525 			ps->Composite = uxa_composite;
526 
527 			uxa_screen->SavedGlyphs = ps->Glyphs;
528 			ps->Glyphs = uxa_glyphs;
529 
530 			uxa_screen->SavedUnrealizeGlyph = ps->UnrealizeGlyph;
531 			ps->UnrealizeGlyph = uxa_glyph_unrealize;
532 
533 			uxa_screen->SavedTriangles = ps->Triangles;
534 			ps->Triangles = uxa_triangles;
535 
536 			uxa_screen->SavedTrapezoids = ps->Trapezoids;
537 			ps->Trapezoids = uxa_trapezoids;
538 
539 			uxa_screen->SavedAddTraps = ps->AddTraps;
540 			ps->AddTraps = uxa_add_traps;
541 		}
542 	}
543 #endif
544 
545 	LogMessage(X_INFO,
546 		   "UXA(%d): Driver registered support for the following"
547 		   " operations:\n", screen->myNum);
548 	assert(uxa_driver->prepare_solid != NULL);
549 	LogMessage(X_INFO, "        solid\n");
550 	assert(uxa_driver->prepare_copy != NULL);
551 	LogMessage(X_INFO, "        copy\n");
552 	if (uxa_driver->prepare_composite != NULL) {
553 		LogMessage(X_INFO, "        composite (RENDER acceleration)\n");
554 	}
555 	if (uxa_driver->put_image != NULL) {
556 		LogMessage(X_INFO, "        put_image\n");
557 	}
558 	if (uxa_driver->get_image != NULL) {
559 		LogMessage(X_INFO, "        get_image\n");
560 	}
561 
562 	return TRUE;
563 }
564 
uxa_resources_init(ScreenPtr screen)565 Bool uxa_resources_init(ScreenPtr screen)
566 {
567 	if (!uxa_glyphs_init(screen))
568 		return FALSE;
569 
570 	return TRUE;
571 }
572 
573 /**
574  * uxa_driver_fini tears down UXA on a given screen.
575  *
576  * @param pScreen screen being torn down.
577  */
uxa_driver_fini(ScreenPtr pScreen)578 void uxa_driver_fini(ScreenPtr pScreen)
579 {
580 	/*right now does nothing */
581 }
582