Indentation fix, cleanup.
[AROS.git] / arch / all-native / hidd / vga / vgaclass.c
blob573173e5749f7ac9f9d9d5ba263c85852c245995
1 /*
2 Copyright © 1995-2015, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Class for VGA and compatible cards.
6 Lang: English.
7 */
9 #include <aros/asmcall.h>
10 #include <proto/exec.h>
11 #include <proto/utility.h>
12 #include <proto/oop.h>
13 #include <oop/oop.h>
15 #include <exec/alerts.h>
16 #include <exec/memory.h>
18 #include <hidd/hidd.h>
19 #include <hidd/graphics.h>
21 #include <aros/symbolsets.h>
23 #include <hardware/custom.h>
25 #include <devices/inputevent.h>
26 #include <string.h>
28 #include "vga.h"
29 #include "vgaclass.h"
30 #include "bitmap.h"
32 #include LC_LIBDEFS_FILE
34 #define DEBUG 0
35 #include <aros/debug.h>
37 static AROS_INTH1(ResetHandler, struct vga_staticdata *, xsd)
39 AROS_INTFUNC_INIT
41 struct bitmap_data *data = NULL;
43 /* On my machine this fills the screen with colorful vertical stripes
44 instead of blanking. So for now we use software method.
45 Pavel Fedin.
46 vgaBlankScreen(0); */
48 if (xsd->visible)
49 data = OOP_INST_DATA(xsd->bmclass, xsd->visible);
51 if (data)
53 struct Box box = {0, 0, data->width - 1, data->height - 1};
55 vgaEraseArea(data, &box);
58 return 0;
60 AROS_INTFUNC_EXIT
63 /* Default graphics modes */
65 struct vgaModeDesc
66 vgaDefMode[NUM_MODES]={
67 {"640x480x4 @ 60Hz", // h: 31.5 kHz v: 60Hz
68 640,480,4,0,
70 640,664,760,800,0,
71 480,491,493,525}
72 #ifndef ONLY640
73 ,{"768x576x4 @ 54Hz", // h: 32.5 kHz v: 54Hz
74 768,576,4,1,
76 768,795,805,872,0,
77 576,577,579,600},
78 {"800x600x4 @ 52Hz", // h: 31.5 kHz v: 52Hz
79 800,600,4,1,
81 800,826,838,900,0, // 900
82 600,601,603,617} // 617
83 #endif
86 /*********************
87 ** GfxHidd::New() **
88 *********************/
90 #define NUM_SYNC_TAGS 11
91 #define SET_SYNC_TAG(taglist, idx, tag, val) \
92 taglist[idx].ti_Tag = aHidd_Sync_ ## tag; \
93 taglist[idx].ti_Data = val
95 VOID init_sync_tags(struct TagItem *tags, struct vgaModeDesc *md, STRPTR name)
97 ULONG clock = (md->clock == 1) ? 28322000 : 25175000;
99 SET_SYNC_TAG(tags, 0, PixelClock, clock );
100 SET_SYNC_TAG(tags, 1, HDisp, md->HDisplay );
101 SET_SYNC_TAG(tags, 2, VDisp, md->VDisplay );
102 SET_SYNC_TAG(tags, 3, HSyncStart, md->HSyncStart );
103 SET_SYNC_TAG(tags, 4, HSyncEnd, md->HSyncEnd );
104 SET_SYNC_TAG(tags, 5, HTotal, md->HTotal );
105 SET_SYNC_TAG(tags, 6, VSyncStart, md->VSyncStart );
106 SET_SYNC_TAG(tags, 7, VSyncEnd, md->VSyncEnd );
107 SET_SYNC_TAG(tags, 8, VTotal, md->VTotal );
108 SET_SYNC_TAG(tags, 9, Description, (IPTR)name );
109 tags[10].ti_Tag = TAG_DONE;
112 OOP_Object *PCVGA__Root__New(OOP_Class *cl, OOP_Object *o, struct pRoot_New *msg)
114 struct TagItem pftags[] = {
115 { aHidd_PixFmt_RedShift , 0 }, /* 0 */
116 { aHidd_PixFmt_GreenShift , 0 }, /* 1 */
117 { aHidd_PixFmt_BlueShift , 0 }, /* 2 */
118 { aHidd_PixFmt_AlphaShift , 0 }, /* 3 */
119 { aHidd_PixFmt_RedMask , 0x000000FC }, /* 4 */
120 { aHidd_PixFmt_GreenMask , 0x0000FC00 }, /* 5 */
121 { aHidd_PixFmt_BlueMask , 0x00FC0000 }, /* 6 */
122 { aHidd_PixFmt_AlphaMask , 0x00000000 }, /* 7 */
123 { aHidd_PixFmt_ColorModel , vHidd_ColorModel_Palette}, /* 8 */
124 { aHidd_PixFmt_Depth , 4 }, /* 9 */
125 { aHidd_PixFmt_BytesPerPixel, 1 }, /* 10 */
126 { aHidd_PixFmt_BitsPerPixel , 4 }, /* 11 */
127 { aHidd_PixFmt_StdPixFmt , vHidd_StdPixFmt_LUT8 }, /* 12 */
128 { aHidd_PixFmt_CLUTShift , 0 }, /* 13 */
129 { aHidd_PixFmt_CLUTMask , 0x0f }, /* 14 */
130 { aHidd_PixFmt_BitMapType , vHidd_BitMapType_Chunky }, /* 15 */
131 { TAG_DONE , 0UL }
134 struct TagItem sync_640_480[NUM_SYNC_TAGS];
135 #ifndef ONLY640
136 struct TagItem sync_758_576[NUM_SYNC_TAGS];
137 struct TagItem sync_800_600[NUM_SYNC_TAGS];
138 #endif
140 struct TagItem modetags[] = {
141 { aHidd_Sync_HMax , 16384 },
142 { aHidd_Sync_VMax , 16384 },
143 { aHidd_Gfx_PixFmtTags, (IPTR)pftags },
144 { aHidd_Gfx_SyncTags , (IPTR)sync_640_480 },
145 #ifndef ONLY640
146 { aHidd_Gfx_SyncTags , (IPTR)sync_758_576 },
147 { aHidd_Gfx_SyncTags , (IPTR)sync_800_600 },
148 #endif
149 { TAG_DONE, 0UL }
152 struct TagItem mytags[] = {
153 { aHidd_Gfx_ModeTags, (IPTR)modetags },
154 { TAG_MORE, 0UL }
156 struct pRoot_New mymsg;
158 /* Do not allow to create more than one object */
159 if (XSD(cl)->vgahidd)
160 return NULL;
162 /* First init the sync tags */
163 init_sync_tags(sync_640_480, &vgaDefMode[0], "VGA:640x480");
164 #ifndef ONLY640
165 init_sync_tags(sync_758_576, &vgaDefMode[1], "VGA:758x576");
166 init_sync_tags(sync_800_600, &vgaDefMode[2], "VGA:800x600");
167 #endif
169 /* init mytags. We use TAG_MORE to attach our own tags before we send them
170 to the superclass */
171 mytags[1].ti_Tag = TAG_MORE;
172 mytags[1].ti_Data = (IPTR)msg->attrList;
174 /* Init mymsg. We have to use our own message struct because
175 one should not alter the one passed to this method.
176 message structs passed to a method are always read-only.
177 (The user who called us might want to reuse the same msg struct
178 for several calls, but that will break if some method changes the
179 msg struct contents)
181 mymsg.mID = msg->mID; /* We got New() method and we are sending
182 the same method to the superclass */
183 mymsg.attrList = mytags;
184 msg = &mymsg;
186 EnterFunc(bug("VGAGfx::New()\n"));
188 o = (OOP_Object *)OOP_DoSuperMethod(cl, o, (OOP_Msg)msg);
189 XSD(cl)->vgahidd = o;
190 if (o) {
191 struct Vga_Data *data = OOP_INST_DATA(cl, o);
193 data->ResetInterrupt.is_Node.ln_Name = cl->ClassNode.ln_Name;
194 data->ResetInterrupt.is_Code = (VOID_FUNC)ResetHandler;
195 data->ResetInterrupt.is_Data = XSD(cl);
196 AddResetCallback(&data->ResetInterrupt);
198 ReturnPtr("VGAGfx::New", OOP_Object *, o);
201 VOID PCVGA__Root__Dispose(OOP_Class *cl, OOP_Object *o, OOP_Msg msg)
203 struct Vga_Data *data = OOP_INST_DATA(cl, o);
205 RemResetCallback(&data->ResetInterrupt);
206 OOP_DoSuperMethod(cl, o, (OOP_Msg)msg);
207 XSD(cl)->vgahidd = NULL;
210 VOID PCVGA__Root__Get(OOP_Class *cl, OOP_Object *o, struct pRoot_Get *msg)
212 ULONG idx;
213 BOOL found = FALSE;
214 if (IS_GFX_ATTR(msg->attrID, idx)) {
215 switch (idx) {
216 case aoHidd_Gfx_SupportsHWCursor:
217 case aoHidd_Gfx_NoFrameBuffer:
218 *msg->storage = (IPTR)TRUE;
219 found = TRUE;
220 break;
224 if (!found)
225 OOP_DoSuperMethod(cl, o, (OOP_Msg)msg);
227 return;
230 /********** GfxHidd::CreateObject() ****************************/
231 OOP_Object *PCVGA__Hidd_Gfx__CreateObject(OOP_Class *cl, OOP_Object *o, struct pHidd_Gfx_CreateObject *msg)
233 OOP_Object *object = NULL;
235 EnterFunc(bug("VGAGfx::CreateObject()\n"));
237 if (msg->cl == XSD(cl)->basebm)
239 struct TagItem mytags[] =
241 { TAG_IGNORE, TAG_IGNORE }, /* Placeholder for aHidd_BitMap_ClassPtr */
242 { TAG_MORE, (IPTR)msg->attrList }
245 struct pHidd_Gfx_CreateObject mymsg;
246 HIDDT_ModeID modeid;
248 modeid = (HIDDT_ModeID)GetTagData(aHidd_BitMap_ModeID, vHidd_ModeID_Invalid, msg->attrList);
249 if (vHidd_ModeID_Invalid != modeid) {
250 /* User supplied a valid modeid. We can use our class */
251 mytags[0].ti_Tag = aHidd_BitMap_ClassPtr;
252 mytags[0].ti_Data = (IPTR)XSD(cl)->bmclass;
254 /* Like in Gfx::New() we init a new message struct */
255 mymsg.mID = msg->mID;
256 mymsg.cl = msg->cl;
257 mymsg.attrList = mytags;
259 object = (OOP_Object *)OOP_DoSuperMethod(cl, o, (OOP_Msg)&mymsg);
261 else
262 object = (OOP_Object *)OOP_DoSuperMethod(cl, o, (OOP_Msg)msg);
264 ReturnPtr("VGAGfx::CreateObject", OOP_Object *, object);
267 /********* GfxHidd::Show() ***************************/
269 OOP_Object *PCVGA__Hidd_Gfx__Show(OOP_Class *cl, OOP_Object *o, struct pHidd_Gfx_Show *msg)
271 /* We currently use class static data instead of
272 object data. In addition we directly access
273 bitmap's private data. This is horribly wrong
274 and needs further refactoring */
275 struct vga_staticdata *data = XSD(cl);
276 struct Box box;
278 D(bug("[VGAGfx] Show(0x%p)\n", msg->bitMap));
279 ObtainSemaphore(&data->sema);
281 /* Remove old bitmap from the screen */
282 if (data->visible) {
283 IPTR tags[] = {aHidd_BitMap_Visible, FALSE, TAG_DONE};
285 D(bug("[VGAGfx] Old displayed bitmap: 0x%p\n", data->visible));
286 OOP_SetAttrs(data->visible, (struct TagItem *)tags);
289 if (msg->bitMap) {
290 /* If we have a bitmap to show, set it as visible */
291 IPTR tags[] = {aHidd_BitMap_Visible, TRUE, TAG_DONE};
292 OOP_Object *pixfmt;
293 IPTR depth;
295 OOP_GetAttr(msg->bitMap, aHidd_BitMap_PixFmt, (IPTR *)&pixfmt);
296 OOP_GetAttr(pixfmt, aHidd_PixFmt_Depth, &depth);
297 /* TODO: this should be brought in from SpriteBase of the colormap */
298 data->mouseBase = (depth > 4) ? 16 : (1 << depth) - 8;
300 OOP_SetAttrs(msg->bitMap, (struct TagItem *)tags);
301 data->visible = msg->bitMap;
302 } else {
303 /* Otherwise simply clear the framebuffer */
304 box.x1 = 0;
305 box.y1 = 0;
306 box.x2 = 639;
307 box.y2 = 479;
308 ObtainSemaphore(&data->HW_acc);
309 /* We use old visible bitmap pointer here since this bitmap
310 contains data about the current video mode */
311 vgaEraseArea(OOP_INST_DATA(data->bmclass, data->visible), &box);
312 draw_mouse(data);
313 ReleaseSemaphore(&data->HW_acc);
315 data->visible = NULL;
317 D(bug("[VGAGfx] New displayed bitmap: 0x%p\n", data->visible));
318 D(bug("[VGAGfx] Mouse pointer base color: %u\n", data->mouseBase));
320 ReleaseSemaphore(&data->sema);
321 return msg->bitMap;
324 /********* GfxHidd::CopyBox() ***************************/
326 VOID PCVGA__Hidd_Gfx__CopyBox(OOP_Class *cl, OOP_Object *o, struct pHidd_Gfx_CopyBox *msg)
328 ULONG mode;
329 unsigned char *src = 0, *dest = 0;
331 mode = GC_DRMD(msg->gc);
333 EnterFunc(bug("VGAGfx.BitMap::CopyBox (%d,%d) to (%d,%d) of dim %d,%d\n",
334 msg->srcX, msg->srcY, msg->destX, msg->destY, msg->width, msg->height));
335 D(bug("[VGAGfx] Src: 0x%p, dest: 0x%p\n", msg->src, msg->dest));
336 OOP_GetAttr(msg->src, aHidd_VGABitMap_Drawable, (IPTR *)&src);
337 OOP_GetAttr(msg->dest, aHidd_VGABitMap_Drawable, (IPTR *)&dest);
339 if (!dest || !src ||
340 ((mode != vHidd_GC_DrawMode_Copy) &&
341 (mode != vHidd_GC_DrawMode_And) &&
342 (mode != vHidd_GC_DrawMode_Xor) &&
343 (mode != vHidd_GC_DrawMode_Clear) &&
344 (mode != vHidd_GC_DrawMode_Invert)))
346 /* The source and/or destination object is no VGA bitmap, onscreen nor offscreen.
347 Or drawmode is not one of those we accelerate. Let the superclass do the
348 copying in a more general way
350 OOP_DoSuperMethod(cl, o, (OOP_Msg)msg);
351 return;
356 struct bitmap_data *data = OOP_INST_DATA(OOP_OCLASS(msg->src), msg->src);
357 struct bitmap_data *ddata = OOP_INST_DATA(OOP_OCLASS(msg->dest), msg->dest);
358 int i, width, phase, j;
359 BOOL descending;
361 // start of Source data
362 unsigned char *s_start = data->VideoData +
363 msg->srcX + (msg->srcY * data->bpr);
364 // adder for each line
365 ULONG s_add = data->bpr - msg->width;
366 ULONG cnt = msg->height;
368 unsigned char *d_start = ddata->VideoData +
369 msg->destX + (msg->destY * ddata->bpr);
370 ULONG d_add = ddata->bpr - msg->width;
372 width = msg->width;
374 if ((msg->srcY > msg->destY) || ((msg->srcY == msg->destY) && (msg->srcX >= msg->destX)))
376 if ((phase = ((IPTR)s_start & 3L)))
378 phase = 4 - phase;
379 if (phase > width) phase = width;
380 width -= phase;
382 descending = FALSE;
384 else
386 s_start += (cnt - 1) * data->bpr + width;
387 d_start += (cnt - 1) * ddata->bpr + width;
389 phase = ((IPTR)s_start & 3L);
390 if (phase > width) phase = width;
391 width -= phase;
393 descending = TRUE;
396 switch(mode)
398 case vHidd_GC_DrawMode_Copy:
399 HIDD_BM_CopyMemBox8(msg->dest,
400 data->VideoData,
401 msg->srcX,
402 msg->srcY,
403 ddata->VideoData,
404 msg->destX,
405 msg->destY,
406 msg->width,
407 msg->height,
408 data->bpr,
409 ddata->bpr);
410 break;
412 case vHidd_GC_DrawMode_And:
413 if (!descending)
415 while (cnt--)
417 i = width;
418 j = phase;
419 while (j--)
421 *d_start++ &= *s_start++;
423 while (i >= 4)
425 *((ULONG*)d_start) &= *((ULONG*)s_start);
426 d_start += 4;
427 s_start += 4;
428 i -= 4;
430 while (i--)
432 *d_start++ &= *s_start++;
434 d_start += d_add;
435 s_start += s_add;
438 else
440 while (cnt--)
442 i = width;
443 j = phase;
444 while (j--)
446 *--d_start &= *--s_start;
448 while (i >= 4)
450 d_start -= 4;
451 s_start -= 4;
452 *((ULONG*)d_start) &= *((ULONG*)s_start);
453 i -= 4;
455 while (i--)
457 *--d_start &= *--s_start;
459 d_start -= d_add;
460 s_start -= s_add;
464 break;
466 case vHidd_GC_DrawMode_Xor:
467 if (!descending)
469 while (cnt--)
471 i = width;
472 j = phase;
473 while (j--)
475 *d_start++ ^= *s_start++;
477 while (i >= 4)
479 *((ULONG*)d_start) ^= *((ULONG*)s_start);
480 d_start += 4;
481 s_start += 4;
482 i -= 4;
484 while (i--)
486 *d_start++ ^= *s_start++;
488 d_start += d_add;
489 s_start += s_add;
492 else
494 while (cnt--)
496 i = width;
497 j = phase;
498 while (j--)
500 *--d_start ^= *--s_start;
502 while (i >= 4)
504 d_start -= 4;
505 s_start -= 4;
506 *((ULONG*)d_start) ^= *((ULONG*)s_start);
507 i -= 4;
509 while (i--)
511 *--d_start ^= *--s_start;
513 d_start -= d_add;
514 s_start -= s_add;
517 break;
519 case vHidd_GC_DrawMode_Clear:
520 if (!descending)
522 while (cnt--)
524 i = width;
525 j = phase;
526 while (j--)
528 *d_start++ = 0;
530 while (i >= 4)
532 *((ULONG*)d_start) = 0;
533 d_start += 4;
534 i -= 4;
536 while (i--)
538 *d_start++ = 0;
540 d_start += d_add;
543 else
545 while (cnt--)
547 i = width;
548 j = phase;
549 while (j--)
551 *--d_start = 0;
553 while (i >= 4)
555 d_start -= 4;
556 *((ULONG*)d_start) = 0;
557 i -= 4;
559 while (i--)
561 *--d_start = 0;
563 d_start -= d_add;
566 break;
568 case vHidd_GC_DrawMode_Invert:
569 if (!descending)
571 while (cnt--)
573 i = width;
574 j = phase;
575 while (j--)
577 *d_start = ~*d_start;
578 d_start++;
580 while (i >= 4)
582 *((ULONG*)d_start) = ~*((ULONG*)d_start);
583 d_start += 4;
584 i -= 4;
586 while (i--)
588 *d_start = ~*d_start;
589 d_start++;
591 d_start += d_add;
594 else
596 while (cnt--)
598 i = width;
599 j = phase;
600 while (j--)
602 *d_start = ~*d_start;
603 d_start--;
605 while (i >= 4)
607 d_start -= 4;
608 *((ULONG*)d_start) = ~*((ULONG*)d_start);
609 i -= 4;
611 while (i--)
613 *d_start = ~*d_start;
614 d_start--;
616 d_start -= d_add;
618 break;
620 break;
622 } /* switch(mode) */
624 ReturnVoid("VGAGfx.BitMap::CopyBox");
627 /********** GfxHidd::SetCursorShape() ****************************/
629 BOOL PCVGA__Hidd_Gfx__SetCursorShape(OOP_Class *cl, OOP_Object *o, struct pHidd_Gfx_SetCursorShape *msg)
631 struct vga_staticdata *data = XSD(cl);
632 IPTR curs_width, curs_height;
633 UBYTE *new_curs_pixels;
635 OOP_GetAttr(msg->shape, aHidd_BitMap_Width, &curs_width);
636 OOP_GetAttr(msg->shape, aHidd_BitMap_Height, &curs_height);
638 new_curs_pixels = AllocMem(curs_width * curs_height, MEMF_ANY);
639 if (!new_curs_pixels)
640 return FALSE;
642 HIDD_BM_GetImage(msg->shape, new_curs_pixels, curs_width, 0, 0, curs_width, curs_height, vHidd_StdPixFmt_LUT8);
644 ObtainSemaphore(&data->HW_acc);
645 erase_mouse(data);
646 if (data->mouseShape)
647 FreeMem(data->mouseShape, data->mouseW * data->mouseH);
649 data->mouseW = curs_width;
650 data->mouseH = curs_height;
651 data->mouseShape = new_curs_pixels;
652 draw_mouse(data);
654 ReleaseSemaphore(&data->HW_acc);
655 return TRUE;
658 /********** GfxHidd::SetCursorPos() ****************************/
660 BOOL PCVGA__Hidd_Gfx__SetCursorPos(OOP_Class *cl, OOP_Object *o, struct pHidd_Gfx_SetCursorPos *msg)
662 ObtainSemaphore(&XSD(cl)->HW_acc);
664 erase_mouse(XSD(cl));
666 XSD(cl)->mouseX = (short)msg->x;
667 XSD(cl)->mouseY = (short)msg->y;
669 if (XSD(cl)->visible)
671 struct bitmap_data *bm_data =
672 OOP_INST_DATA(XSD(cl)->bmclass, XSD(cl)->visible);
674 if (XSD(cl)->mouseX < 0) XSD(cl)->mouseX = 0;
675 if (XSD(cl)->mouseY < 0) XSD(cl)->mouseY = 0;
676 if (XSD(cl)->mouseX >= bm_data->width) XSD(cl)->mouseX =
677 bm_data->width - 1;
678 if (XSD(cl)->mouseY >= bm_data->height) XSD(cl)->mouseY =
679 bm_data->height - 1;
682 draw_mouse(XSD(cl));
684 ReleaseSemaphore(&XSD(cl)->HW_acc);
686 return TRUE;
689 /********** GfxHidd::SetCursorVisible() ****************************/
691 VOID PCVGA__Hidd_Gfx__SetCursorVisible(OOP_Class *cl, OOP_Object *o, struct pHidd_Gfx_SetCursorVisible *msg)
693 XSD(cl)->mouseVisible = msg->visible;
695 ObtainSemaphore(&XSD(cl)->HW_acc);
696 erase_mouse(XSD(cl));
697 draw_mouse(XSD(cl));
698 ReleaseSemaphore(&XSD(cl)->HW_acc);
701 /* end of stuff added by stegerg */
702 /*******************************************************************/
704 void draw_mouse(struct vga_staticdata *xsd)
706 int pix;
707 unsigned char *ptr, *data;
708 int x, y, width, fg, x_i, y_i;
710 if (!xsd->mouseShape)
711 return;
713 if (xsd->mouseVisible)
715 if (xsd->visible)
717 struct bitmap_data *bm_data =
718 OOP_INST_DATA(xsd->bmclass, xsd->visible);
720 /* Get display width */
721 width = bm_data->disp_width;
723 /* And pointer data */
724 data = xsd->mouseShape;
726 ObtainSemaphore(&xsd->HW_acc);
728 outw(0x3c4,0x0f02);
729 outw(0x3ce,0x0005);
730 outw(0x3ce,0x0003);
731 outw(0x3ce,0x0f01);
733 for (y_i = 0, y = xsd->mouseY ; y_i < xsd->mouseH; y_i++, y++)
735 for (x_i = 0, x = xsd->mouseX; x_i < xsd->mouseW; x_i++, x++)
737 ptr = (char *)(IPTR)(0xa0000 + (x + (y * width)) / 8);
738 pix = 0x8000 >> (x % 8);
740 fg = (char)*data++;
742 if (fg && (x < width))
744 fg += xsd->mouseBase;
745 outw(0x3ce,pix | 8);
746 outw(0x3ce,(fg << 8));
748 *ptr |= 1; // This or'ed value isn't important
753 ReleaseSemaphore(&xsd->HW_acc);
758 void erase_mouse(struct vga_staticdata *data)
760 if (data->visible) {
761 struct Box box = {0, 0, 0, 0};
763 box.x1 = data->mouseX;
764 box.y1 = data->mouseY;
765 box.x2 = box.x1 + data->mouseW;
766 box.y2 = box.y1 + data->mouseH;
768 vgaRefreshArea(OOP_INST_DATA(data->bmclass, data->visible), &box);