Hands-on Projects for the Linux Graphics Subsystem
|
In this sections we examine some of the last routines called by main() related to the screen operations, called after InitOutput(). We actually continue from section 3.2.2.12, where CreateScreenResources(), the first of those routines, was covered.
if (!CreateRootWindow(pScreen)) FatalError("failed to create root window"); |
CreateRootWindow() creates the initial window of the screen. It is implemented as:
/***** * CreateRootWindow * Makes a window at initialization time for specified screen *****/ Bool CreateRootWindow(ScreenPtr pScreen) { WindowPtr pWin; BoxRec box; PixmapFormatRec *format; pWin = AllocateWindow(pScreen); if (!pWin) return FALSE; savedScreenInfo[pScreen->myNum].pWindow = NULL; savedScreenInfo[pScreen->myNum].wid = FakeClientID(0); savedScreenInfo[pScreen->myNum].ExternalScreenSaver = NULL; screenIsSaved = SCREEN_SAVER_OFF; WindowTable[pScreen->myNum] = pWin; pWin->drawable.pScreen = pScreen; pWin->drawable.type = DRAWABLE_WINDOW; pWin->drawable.depth = pScreen->rootDepth; for (format = screenInfo.formats; format->depth != pScreen->rootDepth; format++) ; pWin->drawable.bitsPerPixel = format->bitsPerPixel; pWin->drawable.serialNumber = NEXT_SERIAL_NUMBER; pWin->parent = NullWindow; SetWindowToDefaults(pWin); pWin->optional = (WindowOptRec *) xalloc (sizeof (WindowOptRec)); if (!pWin->optional) return FALSE; pWin->optional->dontPropagateMask = 0; pWin->optional->otherEventMasks = 0; pWin->optional->otherClients = NULL; pWin->optional->passiveGrabs = NULL; pWin->optional->userProps = NULL; pWin->optional->backingBitPlanes = ~0L; pWin->optional->backingPixel = 0; #ifdef SHAPE pWin->optional->boundingShape = NULL; pWin->optional->clipShape = NULL; pWin->optional->inputShape = NULL; #endif #ifdef XINPUT pWin->optional->inputMasks = NULL; #endif pWin->optional->colormap = pScreen->defColormap; pWin->optional->visual = pScreen->rootVisual; pWin->nextSib = NullWindow; pWin->drawable.id = FakeClientID(0); pWin->origin.x = pWin->origin.y = 0; pWin->drawable.height = pScreen->height; pWin->drawable.width = pScreen->width; pWin->drawable.x = pWin->drawable.y = 0; box.x1 = 0; box.y1 = 0; box.x2 = pScreen->width; box.y2 = pScreen->height; REGION_INIT(pScreen, &pWin->clipList, &box, 1); REGION_INIT(pScreen, &pWin->winSize, &box, 1); REGION_INIT(pScreen, &pWin->borderSize, &box, 1); REGION_INIT(pScreen, &pWin->borderClip, &box, 1); pWin->drawable.class = InputOutput; pWin->optional->visual = pScreen->rootVisual; pWin->backgroundState = BackgroundPixel; pWin->background.pixel = pScreen->whitePixel; pWin->borderIsPixel = TRUE; pWin->border.pixel = pScreen->blackPixel; pWin->borderWidth = 0; if (!AddResource(pWin->drawable.id, RT_WINDOW, (pointer)pWin)) return FALSE; if (disableBackingStore) pScreen->backingStoreSupport = NotUseful; if (enableBackingStore) pScreen->backingStoreSupport = Always; #ifdef DO_SAVE_UNDERS if ((pScreen->backingStoreSupport != NotUseful) && (pScreen->saveUnderSupport == NotUseful)) { /* * If the screen has backing-store but no save-unders, let the * clients know we can support save-unders using backing-store. */ pScreen->saveUnderSupport = USE_DIX_SAVE_UNDERS; } #endif /* DO_SAVE_UNDERS */ if (disableSaveUnders) pScreen->saveUnderSupport = NotUseful; return TRUE; } |
For DO_SAVE_UNDERS we read in this link:
"If save-under is True, the X server is advised that, when this window is mapped, saving the contents of windows it obscures would be beneficial".
for (i = 0; i < screenInfo.numScreens; i++) InitRootWindow(WindowTable[i]); DefineInitialRootWindow(WindowTable[0]); |
The Root window is initialised with InitRootWindow, which is implemented as:
void InitRootWindow(WindowPtr pWin) { ScreenPtr pScreen = pWin->drawable.pScreen; if (!(*pScreen->CreateWindow)(pWin)) return; /* XXX */ (*pScreen->PositionWindow)(pWin, 0, 0); pWin->cursorIsNone = FALSE; pWin->optional->cursor = rootCursor; rootCursor->refcnt++; MakeRootTile(pWin); pWin->backingStore = defaultBackingStore; pWin->forcedBS = (defaultBackingStore != NotUseful); /* We SHOULD check for an error value here XXX */ (*pScreen->ChangeWindowAttributes)(pWin, CWBackPixmap|CWBorderPixel|CWCursor|CWBackingStore); MapWindow(pWin, serverClient); } |
As we saw in section 3.2.2.12 as pScreen->PositionWindow is assigned fbCreateWindow(), implemented as:
Bool fbCreateWindow(WindowPtr pWin) { #ifndef FB_NO_WINDOW_PIXMAPS pWin->devPrivates[fbWinPrivateIndex].ptr = (pointer) fbGetScreenPixmap(pWin->drawable.pScreen); #endif #ifdef FB_SCREEN_PRIVATE if (pWin->drawable.bitsPerPixel == 32) pWin->drawable.bitsPerPixel = fbGetScreenPrivate(pWin->drawable.pScreen)->win32bpp; #endif return TRUE; } |
Macro fbGetScreenPixmap is defined as:
#define fbGetScreenPixmap(s) ((PixmapPtr) (s)->devPrivate) |
The devPrivate value was set at miCreateScreenResources() as seen in section 3.2.2.12. As this comment explains the value: ((PixmapPtr) (s)->devPrivate) is either the pointer pbits or pPixmap, according to the existence of a 'stride'. This value fills pWin->devPrivates[fbWinPrivateIndex].ptr. Notice the similarity in the names of two different fileds from two different structs: devPrivate and devPrivates. |
The ChangeWindowAttributes implementation is fbChangeWindowAttributes(). Some of the flags that are passed as the argument are defined in X.h. The attributes assigned by those flags are explained in numerous sites, for instance this wiki, this man page and the Xlib - C Language X Interface.
Next MapWindow() is called, implemented as:
/***** * MapWindow * If some other client has selected SubStructureReDirect on the parent * and override-redirect is xFalse, then a MapRequest event is generated, * but the window remains unmapped. Otherwise, the window is mapped and a * MapNotify event is generated. *****/ int MapWindow(register WindowPtr pWin, ClientPtr client) { register ScreenPtr pScreen; register WindowPtr pParent; #ifdef DO_SAVE_UNDERS Bool dosave = FALSE; #endif WindowPtr pLayerWin; if (pWin->mapped) return(Success); #ifdef XCSECURITY /* don't let an untrusted client map a child-of-trusted-window, InputOnly * window; too easy to steal device input */ if ( (client->trustLevel != XSecurityClientTrusted) && (pWin->drawable.class == InputOnly) && (wClient(pWin->parent)->trustLevel == XSecurityClientTrusted) ) return Success; #endif pScreen = pWin->drawable.pScreen; if ( (pParent = pWin->parent) ) { xEvent event; Bool anyMarked; #ifdef XAPPGROUP ClientPtr win_owner = clients[CLIENT_ID(pWin->drawable.id)]; ClientPtr ag_leader = XagLeader (win_owner); #endif if ((!pWin->overrideRedirect) && (RedirectSend(pParent) #ifdef XAPPGROUP || (win_owner->appgroup && ag_leader && XagIsControlledRoot (client, pParent)) #endif )) { event.u.u.type = MapRequest; event.u.mapRequest.window = pWin->drawable.id; #ifdef XAPPGROUP /* make sure if the ag_leader maps the window it goes to the wm */ if (ag_leader && ag_leader != client && XagIsControlledRoot (client, pParent)) { event.u.mapRequest.parent = XagId (win_owner); (void) TryClientEvents (ag_leader, &event, 1, NoEventMask, NoEventMask, NullGrab); return Success; } #endif event.u.mapRequest.parent = pParent->drawable.id; if (MaybeDeliverEventsToClient(pParent, &event, 1, SubstructureRedirectMask, client) == 1) return(Success); } pWin->mapped = TRUE; if (SubStrSend(pWin, pParent)) { event.u.u.type = MapNotify; event.u.mapNotify.window = pWin->drawable.id; event.u.mapNotify.override = pWin->overrideRedirect; DeliverEvents(pWin, &event, 1, NullWindow); } if (!pParent->realized) return(Success); RealizeTree(pWin); if (pWin->viewable) { anyMarked = (*pScreen->MarkOverlappedWindows)(pWin, pWin, &pLayerWin); #ifdef DO_SAVE_UNDERS if (DO_SAVE_UNDERS(pWin)) { dosave = (*pScreen->ChangeSaveUnder)(pLayerWin, pWin->nextSib); } #endif /* DO_SAVE_UNDERS */ if (anyMarked) { (*pScreen->ValidateTree)(pLayerWin->parent, pLayerWin, VTMap); (*pScreen->HandleExposures)(pLayerWin->parent); } #ifdef DO_SAVE_UNDERS if (dosave) (*pScreen->PostChangeSaveUnder)(pLayerWin, pWin->nextSib); #endif /* DO_SAVE_UNDERS */ if (anyMarked && pScreen->PostValidateTree) (*pScreen->PostValidateTree)(pLayerWin->parent, pLayerWin, VTMap); } WindowsRestructured (); } else { RegionRec temp; pWin->mapped = TRUE; pWin->realized = TRUE; /* for roots */ pWin->viewable = pWin->drawable.class == InputOutput; /* We SHOULD check for an error value here XXX */ (*pScreen->RealizeWindow)(pWin); if (pScreen->ClipNotify) (*pScreen->ClipNotify) (pWin, 0, 0); if (pScreen->PostValidateTree) (*pScreen->PostValidateTree)(NullWindow, pWin, VTMap); REGION_NULL(pScreen, &temp); REGION_COPY(pScreen, &temp, &pWin->clipList); (*pScreen->WindowExposures) (pWin, &temp, NullRegion); REGION_UNINIT(pScreen, &temp); } return(Success); } |
MapWindow() mainly examines two cases, according to the condition:
if ( (pParent = pWin->parent) ) |
This is an if condition with the assignment operation ('=') instead the equality testing ('=='). The condition holds true when the first part is not NULL after the assignment. On this see this wiki. Recall also that previously in CreateRootWindow() as parent of the Root window (pWin->parent) was assigned the NullWindow. Therefore the 'else' part of the condition holds true.
The macros we meet here are defined in regionstr.h. REGION_COPY is defined as:
#define REGION_COPY(_pScreen, dst, src) \ (*(REG_pScreen)->RegionCopy)(dst, src) |
where REG_pScreen is defined as:
#define REG_pScreen screenInfo.screens[0] |
As RegionCopy miRegionCopy() was assigned by miScreenInit() in section 3.2.2.12. miRegionCopy() is implemented as:
Bool miRegionCopy(dst, src) register RegionPtr dst; register RegionPtr src; { good(dst); good(src); if (dst == src) return TRUE; dst->extents = src->extents; if (!src->data || !src->data->size) { xfreeData(dst); dst->data = src->data; return TRUE; } if (!dst->data || (dst->data->size < src->data->numRects)) { xfreeData(dst); dst->data = xallocData(src->data->numRects); if (!dst->data) return miRegionBreak (dst); dst->data->size = src->data->numRects; } dst->data->numRects = src->data->numRects; memmove((char *)REGION_BOXPTR(dst),(char *)REGION_BOXPTR(src), dst->data->numRects * sizeof(BoxRec)); return TRUE; } |
As we read from the memmove() man page:
void * memmove(void *s1, const void *s2, size_t n); The memmove() function copies n bytes from string s2 to string s1. The two strings may overlap; the copy is always done in a non-destructive manner. |
Struct BoxRec is defined as:
typedef struct _Box { short x1, y1, x2, y2; } BoxRec; |
BoxPtr is defined as:
typedef struct _Box *BoxPtr; |
As dst and src the &temp and &pWin->clipList values were previously passed to REGION_COPY. Both are of type RegionRec, which is defined as:
typedef struct _Region RegionRec, *RegionPtr; . . . typedef struct _RegData { long size; long numRects; /* BoxRec rects[size]; in memory but not explicitly declared */ } RegDataRec, *RegDataPtr; struct _Region { BoxRec extents; RegDataPtr data; }; |
Since rects of type BoxRec are not explicitly declared, however there are in memory REGION_BOXPTR makes a hack by accessing the next pointer ((reg)->data + 1)) at the end of the struct (RegDataRec). However the point the struct ends starts the 'hidden' third field of the struct, rects[]. |
REGION_BOXPTR is defined as:
#define REGION_BOXPTR(reg) ((BoxPtr)((reg)->data + 1)) |
pWin is of type WindowPtr, which is defined as:
typedef struct _Window *WindowPtr; |
where _Window is defined as:
typedef struct _Window { DrawableRec drawable; WindowPtr parent; /* ancestor chain */ WindowPtr nextSib; /* next lower sibling */ WindowPtr prevSib; /* next higher sibling */ WindowPtr firstChild; /* top-most child */ WindowPtr lastChild; /* bottom-most child */ RegionRec clipList; /* clipping rectangle for output */ RegionRec borderClip; /* NotClippedByChildren + border */ union _Validate *valdata; RegionRec winSize; RegionRec borderSize; DDXPointRec origin; /* position relative to parent */ unsigned short borderWidth; unsigned short deliverableEvents; Mask eventMask; PixUnion background; PixUnion border; pointer backStorage; /* null when BS disabled */ WindowOptPtr optional; unsigned backgroundState:2; /* None, Relative, Pixel, Pixmap */ unsigned borderIsPixel:1; unsigned cursorIsNone:1; /* else real cursor (might inherit) */ unsigned backingStore:2; unsigned saveUnder:1; unsigned DIXsaveUnder:1; unsigned bitGravity:4; unsigned winGravity:4; unsigned overrideRedirect:1; unsigned visibility:2; unsigned mapped:1; unsigned realized:1; /* ancestors are all mapped */ unsigned viewable:1; /* realized && InputOutput */ unsigned dontPropagate:3;/* index into DontPropagateMasks */ unsigned forcedBS:1; /* system-supplied backingStore */ #ifdef NEED_DBE_BUF_BITS #define DBE_FRONT_BUFFER 1 #define DBE_BACK_BUFFER 0 unsigned dstBuffer:1; /* destination buffer for rendering */ unsigned srcBuffer:1; /* source buffer for rendering */ #endif #ifdef COMPOSITE unsigned redirectDraw:1; /* rendering is redirected from here */ #endif DevUnion *devPrivates; } WindowRec; |
The previous show that REGION_COPY called as:
REGION_COPY(pScreen, &temp, &pWin->clipList);resolves to a miRegionCopy() call. We also show the types of the dst and src arguments, the &temp and &pWin->clipList respectivelly. The source, clipList, was initialised in CreateRootWindow() as:
box.x1 = 0; box.y1 = 0; box.x2 = pScreen->width; box.y2 = pScreen->height; REGION_INIT(pScreen, &pWin->clipList, &box, 1); |
REGION_INIT is an encapsulation of pScreen->RegionInit, which is miRegionInit(). This function initialises the region with the box of the third argument. This box starts at coordination (0,0) and spans the whole screen.
A second empty region called temp is initialised with REGION_NULL in MapWindow() as:
REGION_NULL(pScreen, &temp); |
Then the REGION_COPY copies the contents of the clipList to temp.
temp is passed then as an argument to (*pScreen->WindowExposures), which is actually miWindowExposures().
(*pScreen->WindowExposures) (pWin, &temp, NullRegion); |
As the second argument of miWindowExposures(), prgn, is passed the value temp, a copy of clipList, which as seen previously represents the whole screen region.
miWindowExposures() is implemented as:
void miWindowExposures(pWin, prgn, other_exposed) WindowPtr pWin; register RegionPtr prgn, other_exposed; { RegionPtr exposures = prgn; if (pWin->backStorage && prgn) /* * in some cases, backing store will cause a different * region to be exposed than needs to be repainted * (like when a window is mapped). RestoreAreas is * allowed to return a region other than prgn, * in which case this routine will free the resultant * region. If exposures is null, then no events will * be sent to the client; if prgn is empty * no areas will be repainted. */ exposures = (*pWin->drawable.pScreen->RestoreAreas)(pWin, prgn); if ((prgn && !REGION_NIL(prgn)) || (exposures && !REGION_NIL(exposures)) || other_exposed) { RegionRec expRec; int clientInterested; /* * Restore from backing-store FIRST. */ clientInterested = (pWin->eventMask|wOtherEventMasks(pWin)) & ExposureMask; if (other_exposed) { if (exposures) { REGION_UNION(pWin->drawable.pScreen, other_exposed, exposures, other_exposed); if (exposures != prgn) REGION_DESTROY(pWin->drawable.pScreen, exposures); } exposures = other_exposed; } if (clientInterested && exposures && (REGION_NUM_RECTS(exposures) > RECTLIMIT)) { /* * If we have LOTS of rectangles, we decide to take the extents * and force an exposure on that. This should require much less * work overall, on both client and server. This is cheating, but * isn't prohibited by the protocol ("spontaneous combustion" :-). */ BoxRec box; box = *REGION_EXTENTS( pWin->drawable.pScreen, exposures); if (exposures == prgn) { exposures = &expRec; REGION_INIT( pWin->drawable.pScreen, exposures, &box, 1); REGION_RESET( pWin->drawable.pScreen, prgn, &box); } else { REGION_RESET( pWin->drawable.pScreen, exposures, &box); REGION_UNION( pWin->drawable.pScreen, prgn, prgn, exposures); } /* PaintWindowBackground doesn't clip, so we have to */ REGION_INTERSECT( pWin->drawable.pScreen, prgn, prgn, &pWin->clipList); /* need to clear out new areas of backing store, too */ if (pWin->backStorage) (void) (* pWin->drawable.pScreen->ClearBackingStore)( pWin, box.x1 - pWin->drawable.x, box.y1 - pWin->drawable.y, box.x2 - box.x1, box.y2 - box.y1, FALSE); } if (prgn && !REGION_NIL(prgn)) (*pWin->drawable.pScreen->PaintWindowBackground)(pWin, prgn, PW_BACKGROUND); if (clientInterested && exposures && !REGION_NIL(exposures)) miSendExposures(pWin, exposures, pWin->drawable.x, pWin->drawable.y); if (exposures == &expRec) { REGION_UNINIT( pWin->drawable.pScreen, exposures); } else if (exposures && exposures != prgn && exposures != other_exposed) REGION_DESTROY( pWin->drawable.pScreen, exposures); if (prgn) REGION_EMPTY( pWin->drawable.pScreen, prgn); } else if (exposures && exposures != prgn) REGION_DESTROY( pWin->drawable.pScreen, exposures); } |
InitRootWindow() set backingStore to defaultBackingStore as:
pWin->backingStore = defaultBackingStore; |
defaultBackingStore is defined in window.c as:
int defaultBackingStore = NotUseful; |
For BackingStore read this text. |
NotUseful is defined in X.h as:
#define NotUseful 0 |
Since pWin->backStorage is zero miWindowExposures() executes the following instruction:
if (prgn && !REGION_NIL(prgn)) (*pWin->drawable.pScreen->PaintWindowBackground)(pWin, prgn, PW_BACKGROUND); |
PW_BACKGROUND is defined in pixmap.h as:
/* flags to PaintWindow() */ #define PW_BACKGROUND 0 |
The current PaintWindowBackground implementation as seen in section 3.2.2.12 is fbPaintWindow(), implemented as:
void fbPaintWindow(WindowPtr pWin, RegionPtr pRegion, int what) { WindowPtr pBgWin; switch (what) { case PW_BACKGROUND: switch (pWin->backgroundState) { case None: break; case ParentRelative: do { pWin = pWin->parent; } while (pWin->backgroundState == ParentRelative); (*pWin->drawable.pScreen->PaintWindowBackground)(pWin, pRegion, what); break; case BackgroundPixmap: fbFillRegionTiled (&pWin->drawable, pRegion, pWin->background.pixmap); break; case BackgroundPixel: fbFillRegionSolid (&pWin->drawable, pRegion, 0, fbReplicatePixel (pWin->background.pixel, pWin->drawable.bitsPerPixel)); break; } break; case PW_BORDER: if (pWin->borderIsPixel) { fbFillRegionSolid (&pWin->drawable, pRegion, 0, fbReplicatePixel (pWin->border.pixel, pWin->drawable.bitsPerPixel)); } else { for (pBgWin = pWin; pBgWin->backgroundState == ParentRelative; pBgWin = pBgWin->parent); fbFillRegionTiled (&pBgWin->drawable, pRegion, pWin->border.pixmap); } break; } fbValidateDrawable (&pWin->drawable); } |
CreateRootWindow() set backgroundState previously as:
pWin->backgroundState = BackgroundPixel; |
Therefore the following lines execute:
case BackgroundPixel: fbFillRegionSolid (&pWin->drawable, pRegion, 0, fbReplicatePixel (pWin->background.pixel, pWin->drawable.bitsPerPixel)); break; |
background.pixel was previously set by CreateRootWindow() as:
pWin->background.pixel = pScreen->whitePixel; |
pScreen->whitePixel was set fbSetupScreen(), in section 3.2.2.12 as:
pScreen->blackPixel = pScreen->whitePixel = (Pixel) 0; |
fbReplicatePixel is implemented as:
FbBits fbReplicatePixel (Pixel p, int bpp) { FbBits b = p; b &= FbFullMask (bpp); while (bpp < FB_UNIT) { b |= b << bpp; bpp <<= 1; } return b; } |
Macro FbFullMask is defined in fb.h as:
#define FbFullMask(n) ((n) == FB_UNIT ? FB_ALLONES : ((((FbBits) 1) << n) - 1)) |
Macro FbFullMask masks off the number of bits determined by bpp, for instance 8 or 24. In the case of 8 it masks 1111111, that is 8 ones, or in the case of 24 it masks 24 bits. In the case of 32-bits it macro FB_ALLONES is used which does the same for 32 bits, it just uses -1, which is binary 11111111111111111111111111111111. This is 32 ones. Considering that we see that fbReplicatePixel() actually replicates a byte 4 times or a word 2 times (to a the 32-bit region of FbBits). Using a 32-bit quantity it improves the performance of the 'xor', 'and' operations.
Pixel is defined in colormap.h as:
typedef CARD32 Pixel; |
fbFillRegionSolid() is implemented as:
void fbFillRegionSolid (DrawablePtr pDrawable, RegionPtr pRegion, FbBits and, FbBits xor) { FbBits *dst; FbStride dstStride; int dstBpp; int dstXoff, dstYoff; int n = REGION_NUM_RECTS(pRegion); BoxPtr pbox = REGION_RECTS(pRegion); fbGetDrawable (pDrawable, dst, dstStride, dstBpp, dstXoff, dstYoff); while (n--) { fbSolid (dst + (pbox->y1 + dstYoff) * dstStride, dstStride, (pbox->x1 + dstXoff) * dstBpp, dstBpp, (pbox->x2 - pbox->x1) * dstBpp, pbox->y2 - pbox->y1, and, xor); fbValidateDrawable (pDrawable); pbox++; } } |
fbSolid fills dst with the pRegion contents.
pRegion is actually the argument prgn, passed to PaintWindowBackground. Previously prgn was substituted by temp, the &pWin->clipList which represents the whole screen region, the one the Root window that we currently build occupies: box.x1 = 0; box.y1 = 0; box.x2 = pScreen->width; box.y2 = pScreen->height; REGION_INIT(pScreen, &pWin->clipList, &box, 1);dst is obtained from macro fbGetDrawable and it is the pPixmap->devPrivate.ptr, which was set in section 3.2.2.12 to point at the pixmap data. This is the pixmap that represends the screen memory. Recall the commnet of miCreateScreenResources() at section 3.2.2.12 "create a pixmap with no data, then redirect it to point to the screen".
At this comment (*pScreen->CreatePixmap), the fbCreatePixmap(), created the empty pixmap and See also this comment. |
Macros REGION_NUM_RECTS and REGION_RECTS are defined in regionstr.h as:
#define REGION_NUM_RECTS(reg) ((reg)->data ? (reg)->data->numRects : 1) #define REGION_RECTS(reg) ((reg)->data ? (BoxPtr)((reg)->data + 1) \ : &(reg)->extents) |
Macro fbGetDrawable is defined as:
#define fbGetDrawable(pDrawable, pointer, stride, bpp, xoff, yoff) { \ PixmapPtr _pPix; \ if ((pDrawable)->type != DRAWABLE_PIXMAP) { \ _pPix = fbGetWindowPixmap(pDrawable); \ (xoff) = __fbPixOffXWin(_pPix); \ (yoff) = __fbPixOffYWin(_pPix); \ } else { \ _pPix = (PixmapPtr) (pDrawable); \ (xoff) = __fbPixOffXPix(_pPix); \ (yoff) = __fbPixOffYPix(_pPix); \ } \ (pointer) = (FbBits *) _pPix->devPrivate.ptr; \ (stride) = ((int) _pPix->devKind) / sizeof (FbBits); (void)(stride); \ (bpp) = _pPix->drawable.bitsPerPixel; (void)(bpp); \ } |
Since fbCreatePixmapBpp () set at section 3.2.2.12 pPixmap->drawable.type to DRAWABLE_PIXMAP the following lines execute:
_pPix = (PixmapPtr) (pDrawable); \ (xoff) = __fbPixOffXPix(_pPix); \ (yoff) = __fbPixOffYPix(_pPix); \ |
stride is calculated in fbGetDrawable as:
(stride) = ((int) _pPix->devKind) / sizeof (FbBits);pPixmap->devKind is filled in fbCreatePixmapBpp() at section 3.2.2.12 as:
pPixmap->devKind = paddedWidth;where paddedWidth is filled in fbCreatePixmapBpp() as:
paddedWidth = ((width * bpp + FB_MASK) >> FB_SHIFT) * sizeof (FbBits);This pads width to a multiple of 32. The simplified rule for stride is width * bpp.
The definitions for FB_MASK, FB_SHIFT and FbBits follow bellow.
The following macros are defined in fb.h as:
#define __fbPixOffXPix(pPix) (__fbPixDrawableX(pPix)) #define __fbPixOffYPix(pPix) (__fbPixDrawableY(pPix)) . . . #define __fbPixDrawableX(pPix) 0 #define __fbPixDrawableY(pPix) 0 |
fbSolid() is implemented as:
void fbSolid (FbBits *dst, FbStride dstStride, int dstX, int bpp, int width, int height, FbBits and, FbBits xor) { FbBits startmask, endmask; int n, nmiddle; int startbyte, endbyte; #ifdef FB_24BIT if (bpp == 24 && (!FbCheck24Pix(and) || !FbCheck24Pix(xor))) { fbSolid24 (dst, dstStride, dstX, width, height, and, xor); return; } #endif dst += dstX >> FB_SHIFT; dstX &= FB_MASK; FbMaskBitsBytes(dstX, width, and == 0, startmask, startbyte, nmiddle, endmask, endbyte); if (startmask) dstStride--; dstStride -= nmiddle; while (height--) { if (startmask) { FbDoLeftMaskByteRRop(dst,startbyte,startmask,and,xor); dst++; } n = nmiddle; if (!and) while (n--) *dst++ = xor; else while (n--) { *dst = FbDoRRop (*dst, and, xor); dst++; } if (endmask) FbDoRightMaskByteRRop(dst,endbyte,endmask,and,xor); dst += dstStride; } } |
fbsolid() is called from fbFillRegionSolid() to fill the specified number or ranges (in our case, where the initial screen window, the Root window is created there is one range that fills the whole screen range). It copies the box that represents the screen dimensions at the memory location dst, which as we previously saw is the starting memory location of the video memory. It is called as: fbSolid (dst + (pbox->y1 + dstYoff) * dstStride, dstStride, (pbox->x1 + dstXoff) * dstBpp, dstBpp, (pbox->x2 - pbox->x1) * dstBpp, pbox->y2 - pbox->y1, and, xor);The first argument represents the whole screen bits, starting from location dst. It multiplies the y dimension with the stride of the x axis. Stride is the number of bits per pixel, therefore the product is the whole screen bits. The second argument is the stride of the screen The third argument is the number of bits in the x dimension The fourth argument is the screen Bpp (bits per pixel) The fifth argument is the width of the box The sixth argument is the height of the box The last two arguments are the FbBits pattern that will be used in the 'and' and 'xor' raster operations. Both were set to zero when fbFillRegionSolid() was called.
The blue arrow point at dst, the start of the video memory.
The green arrow point at the actual dst as this is formed at the first argument. The first argument is The red arrow point at the upper-left corner of the box and we proceed there with the instruction dst += dstX >> FB_SHIFT;
The next instruction masks dst:
FbMaskBitsBytes finds the left and right mask than need to apply left and right in order to determnine those areas. Then the while() loop starting from the top and processing each line paints the left, right parts (if exist) and the main part. Recall that each line is actually a stride, consists therefore from the number of pixels multiplied by the bits a pixel needs for its color. The painting consists of the 'and' and 'xor' logical operations.
The previous figure examines a case where the left side starts from a non 32-bit boundary and has to be masked. The red line indicates the masked area after the fbSolid() instruction:
|
NOTE: We end this section with fbsolid(). The text that follows describe macros and functions that are called from fbsolid(). |
Macros FB_SHIFT and FB_MASK are defined in fb.h as:
/* * This single define controls the basic size of data manipulated * by this software; it must be log2(sizeof (FbBits) * 8) */ #ifndef FB_SHIFT #define FB_SHIFT LOG2_BITMAP_PAD #endif #define FB_UNIT (1 << FB_SHIFT) #define FB_HALFUNIT (1 << (FB_SHIFT-1)) #define FB_MASK (FB_UNIT - 1) #define FB_ALLONES ((FbBits) -1) |
where LOG2_BITMAP_PAD is defined as:
#define LOG2_BITMAP_PAD 5 |
Macro FbMaskBitsBytes is defined as:
#define FbMaskBitsBytes(x,w,copy,l,lb,n,r,rb) { \ n = (w); \ lb = 0; \ rb = 0; \ r = FbRightMask((x)+n); \ if (r) { \ /* compute right byte length */ \ if ((copy) && (((x) + n) & 7) == 0) { \ rb = (((x) + n) & FB_MASK) >> 3; \ } else { \ rb = FbByteMaskInvalid; \ } \ } \ l = FbLeftMask(x); \ if (l) { \ /* compute left byte length */ \ if ((copy) && ((x) & 7) == 0) { \ lb = ((x) & FB_MASK) >> 3; \ } else { \ lb = FbByteMaskInvalid; \ } \ /* subtract out the portion painted by leftMask */ \ n -= FB_UNIT - ((x) & FB_MASK); \ if (n < 0) { \ if (lb != FbByteMaskInvalid) { \ if (rb == FbByteMaskInvalid) { \ lb = FbByteMaskInvalid; \ } else if (rb) { \ lb |= (rb - lb) << (FB_SHIFT - 3); \ rb = 0; \ } \ } \ n = 0; \ l &= r; \ r = 0; \ }\ } \ n >>= FB_SHIFT; \ } |
FbMaskBitsBytesFbMaskBitsBytes() finds for the range the left mask (l) and the right mask (r) and then computes the left (lb) and the right (rb) byte length, of the distance from a 32-bit boundary. Seeing the range as 4-byte quantities improves the performance when painting the range: l = FbLeftMask(x);FbLeftMask is defined as: #define FbLeftMask(x) ( ((x) & FB_MASK) ? \ FbScrRight(FB_ALLONES,(x) & FB_MASK) : 0)and FB_ALLONES is 32 "all ones" bits, 11111111111111111111111111111111. It is defined as: #define FB_ALLONES ((FbBits) -1)
For the left side of the region, FbLeftMask is used to mask the bytes and then the following instruction is used to find the distance, in bytes, from a 32-bit boundary: /* compute left byte length */ \ if ((copy) && ((x) & 7) == 0) { \ lb = ((x) & FB_MASK) >> 3; \ } else { \ lb = FbByteMaskInvalid; \ } \In the current example x is not a multiple of 8 (a byte) and the lb takes the FbByteMaskInvalid value. Similarly for the right side of the region FbRightMask is used to mask the bytes and this time as argument x+n is used, where x is distance of the region from the left side of the screen and n the region's width, so that we proceed to the region's right side: r = FbRightMask((x)+n);and the distance, in bytes, from a 32-bit boundary is also: /* compute right byte length */ \ if ((copy) && (((x) + n) & 7) == 0) { \ rb = (((x) + n) & FB_MASK) >> 3; \ } else { \ rb = FbByteMaskInvalid; \ } \where also the FbByteMaskInvalid value is used for rb. Finally the width is turned to 32-bit quantities: n >>= FB_SHIFT; |
FbByteMaskInvalid is defined as:
#define FbByteMaskInvalid 0x10 |
Also the following are defined in fb.h:
#define FbLeftMask(x) ( ((x) & FB_MASK) ? \ FbScrRight(FB_ALLONES,(x) & FB_MASK) : 0) #define FbRightMask(x) ( ((FB_UNIT - (x)) & FB_MASK) ? \ FbScrLeft(FB_ALLONES,(FB_UNIT - (x)) & FB_MASK) : 0) |
and
#if BITMAP_BIT_ORDER == LSBFirst #define FbScrLeft(x,n) ((x) >> (n)) #define FbScrRight(x,n) ((x) << (n)) . . . #else #define FbScrLeft(x,n) ((x) << (n)) #define FbScrRight(x,n) ((x) >> (n)) |
BITMAP_BIT_ORDER is defined in servermd.h for various computer architectures. For instance for i386 it is defined as:
#ifdef i386 # define IMAGE_BYTE_ORDER LSBFirst /* Value for PS/2 only */ |
FbBits is defined in fb.h as:
#if FB_SHIFT == 5 typedef CARD32 FbBits; #endif |
FbDoRRop is defined as:
#define FbDoRRop(dst, and, xor) (((dst) & (and)) ^ (xor)) |
Rop in FbDoRRop() and at the end of some other routine names stand for Raster Operation. From How Video Cards Work we read:
The 2D engine (often called a blitter) basically moves data around in video ram. There are generally 4 operations done by the 2D engine: blits (copying data from one place to another), fills (draw a solid color), lines (draws lines), and color expansion (convert mono data to color data; e.g. convert monochrome font glyphs to the depth of your screen: usually 16 or 24 bit color). Logical operations (rops -- raster operations) can also be performed on the data. |
Macros FbDoLeftMaskByteRRop and FbDoRightMaskByteRRop are defined in fb.h as:
#define FbDoLeftMaskByteRRop(dst,lb,l,and,xor) { \ switch (lb) { \ FbDoLeftMaskByteRRop6Cases(dst,xor) \ case (sizeof (FbBits) - 3) | (1 << (FB_SHIFT - 3)): \ FbStorePart(dst,sizeof (FbBits) - 3,CARD8,xor); \ break; \ case (sizeof (FbBits) - 3) | (2 << (FB_SHIFT - 3)): \ FbStorePart(dst,sizeof (FbBits) - 3,CARD8,xor); \ FbStorePart(dst,sizeof (FbBits) - 2,CARD8,xor); \ break; \ case (sizeof (FbBits) - 2) | (1 << (FB_SHIFT - 3)): \ FbStorePart(dst,sizeof (FbBits) - 2,CARD8,xor); \ break; \ case sizeof (FbBits) - 3: \ FbStorePart(dst,sizeof (FbBits) - 3,CARD8,xor); \ case sizeof (FbBits) - 2: \ FbStorePart(dst,sizeof (FbBits) - 2,CARD16,xor); \ break; \ case sizeof (FbBits) - 1: \ FbStorePart(dst,sizeof (FbBits) - 1,CARD8,xor); \ break; \ default: \ *dst = FbDoMaskRRop(*dst, and, xor, l); \ break; \ } \ } #define FbDoRightMaskByteRRop(dst,rb,r,and,xor) { \ switch (rb) { \ case 1: \ FbStorePart(dst,0,CARD8,xor); \ break; \ case 2: \ FbStorePart(dst,0,CARD16,xor); \ break; \ case 3: \ FbStorePart(dst,0,CARD16,xor); \ FbStorePart(dst,2,CARD8,xor); \ break; \ FbDoRightMaskByteRRop6Cases(dst,xor) \ default: \ *dst = FbDoMaskRRop (*dst, and, xor, r); \ } \ } |
Because lb and rb were set to FbByteMaskInvalid previously in FbMaskBitsBytes the default cases apply in both bDoLeftMaskByteRRop and FbDoRightMaskByteRRop.
The following macros are empty:
#define FbDoLeftMaskByteRRop6Cases(dst,xor) #define FbDoRightMaskByteRRop6Cases(dst,xor) |
Macro FbStorePart is defined with other relevant macros in fb.h as:
#define FbSelectPatternPart(xor,o,t) ((xor) >> (FbPatternOffset (o,t) << 3)) #define FbStorePart(dst,off,t,xor) (*FbPtrOffset(dst,off,t) = \ FbSelectPart(xor,off,t)) #ifndef FbSelectPart #define FbSelectPart(x,o,t) FbSelectPatternPart(x,o,t) #endif |
FbDoMaskRRop is defined as:
#define FbDoMaskRRop(dst, and, xor, mask) \ (((dst) & ((and) | ~(mask))) ^ (xor & mask)) |
FbDoMaskRRop is similar to FbDoRRop, which applies the 'and' and 'xor' operations to dst, however this time the masked area is only painted. The 'and' operator applies first and then 'xor'. At the first case the mask is negated and ORed:
((and) | ~(mask))
and at the second the normal mask is used:
(xor & mask)
If we use the previously example we see that in order to apply 'and and 'xor' to the area 170 to 191 the area from 192 and up should be preserved untouched until the next FbDoRRop. With the mask negation the green zeroes as appear bellow are turned to ones. The formula
((and) | ~(mask))
also produce ones at the 'green' area and and as a result only the first 'blue' part is logically added to dst. The 'xor' part that follows:
^ (xor & mask)
does not alter the 192 and up area, since the 0 at the one side of an XOR operation preserves the other operant (see this wiki).
x * - - - - - - - - - * - - - - - - - - - - - - - - - - - - - - - * 160= 170 +22 to the next boundary 192= 5x32 6x32 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 |
FbDoRRop and FbDoMaskRRop are the macros that fbSolid() actually uses since
FbDoLeftMaskByteRRop and FbDoRightMaskByteRRop are replaced by FbDoMaskRRop.
Actually FbDoRRop is not used at all in the current case since the following condition in fbsolid() do not apply:
if (!and) |
REFERENCES:
Wiki about Blit
How Video Cards Work