diff options
author | Yaniv Kamay <ykamay@redhat.com> | 2009-11-04 13:16:11 +0200 |
---|---|---|
committer | Yaniv Kamay <ykamay@redhat.com> | 2009-11-04 13:16:11 +0200 |
commit | 79ab77ee4247858d44674efe38e9c9859a682b94 (patch) | |
tree | 57bfead98b9a2ad43bdec26e92b7309f99213260 |
fresh start
-rw-r--r-- | dirs | 4 | ||||
-rw-r--r-- | display/brush.c | 391 | ||||
-rw-r--r-- | display/driver.c | 1314 | ||||
-rw-r--r-- | display/driver.rc | 29 | ||||
-rw-r--r-- | display/lookup3.c | 18 | ||||
-rw-r--r-- | display/makefile | 2 | ||||
-rw-r--r-- | display/mspace.c | 2435 | ||||
-rw-r--r-- | display/mspace.h | 150 | ||||
-rw-r--r-- | display/pointer.c | 140 | ||||
-rw-r--r-- | display/quic.c | 18 | ||||
-rw-r--r-- | display/qxldd.h | 301 | ||||
-rw-r--r-- | display/res.c | 2433 | ||||
-rw-r--r-- | display/res.h | 56 | ||||
-rw-r--r-- | display/rop.c | 1526 | ||||
-rw-r--r-- | display/rop.h | 35 | ||||
-rw-r--r-- | display/sources | 34 | ||||
-rw-r--r-- | display/text.c | 116 | ||||
-rw-r--r-- | display/utils.h | 113 | ||||
-rw-r--r-- | include/os_dep.h | 37 | ||||
-rw-r--r-- | include/qxl_driver.h | 74 | ||||
-rw-r--r-- | include/wdmhelper.h | 34 | ||||
-rw-r--r-- | miniport/makefile | 17 | ||||
-rw-r--r-- | miniport/qxl.c | 950 | ||||
-rw-r--r-- | miniport/qxl.h | 38 | ||||
-rw-r--r-- | miniport/qxl.inf | 72 | ||||
-rw-r--r-- | miniport/qxl.rc | 29 | ||||
-rw-r--r-- | miniport/sources | 30 | ||||
-rw-r--r-- | miniport/wdmhelper.c | 60 |
28 files changed, 10456 insertions, 0 deletions
@@ -0,0 +1,4 @@ +DIRS= \
+ display \
+ miniport
+
diff --git a/display/brush.c b/display/brush.c new file mode 100644 index 0000000..6105487 --- /dev/null +++ b/display/brush.c @@ -0,0 +1,391 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "os_dep.h" +#include "qxldd.h" +#include "utils.h" +#include "res.h" + +typedef struct InternalBrush { + ULONG format; + SIZEL size; + UINT32 stride; + UINT32 key; + UINT8 data[0]; +} InternalBrush; + +#define COPY1BPP(bits) \ +static _inline void realizeBrush##bits##Copy1bpp(PDev *pdev, InternalBrush *internal, \ + UINT8 *src, LONG src_stride, \ + XLATEOBJ *color_trans) \ +{ \ + UINT8 *end = src + src_stride * internal->size.cy; \ + UINT8 *dest = internal->data; \ + \ + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); \ + ASSERT(pdev, color_trans && color_trans->flXlate & XO_TABLE); \ + for (; src < end; src += src_stride, dest += internal->stride) { \ + UINT##bits *now = (UINT##bits *)dest; \ + int i; \ + \ + for (i = 0; i < internal->size.cx; i++) { \ + *(now++) = (UINT##bits)color_trans->pulXlate[test_bit_be(src, i)]; \ + } \ + } \ + DEBUG_PRINT((pdev, 7, "%s: done\n", __FUNCTION__)); \ +} + +#define COPY4BPP(bits) \ +static _inline void realizeBrush##bits##Copy4bpp(PDev *pdev, InternalBrush *internal, \ + UINT8 *src, LONG src_stride, \ + XLATEOBJ *color_trans) \ +{ \ + UINT8 *dest = internal->data; \ + UINT8 *end = dest + internal->stride * internal->size.cy; \ + \ + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); \ + ASSERT(pdev, color_trans && color_trans->flXlate & XO_TABLE); \ + for (; dest < end; dest += internal->stride, src += src_stride) { \ + UINT8 *src_line = src; \ + UINT8 *src_line_end = src_line + (internal->size.cx >> 1); \ + UINT##bits *dest_line = (UINT##bits *)dest; \ + \ + for (; src_line < src_line_end; src_line++) { \ + ASSERT(pdev, (*src_line & 0x0fU) < color_trans->cEntries); \ + ASSERT(pdev, ((*src_line >> 4) & 0x0fU) < color_trans->cEntries); \ + *(dest_line++) = (UINT##bits)color_trans->pulXlate[(*src_line >> 4) & 0x0f];\ + *(dest_line++) = (UINT##bits)color_trans->pulXlate[*src_line & 0x0f]; \ + } \ + if (internal->size.cx & 1) { \ + *(dest_line) = (UINT##bits)color_trans->pulXlate[(*src_line >> 4) & 0x0f]; \ + } \ + } \ + DEBUG_PRINT((pdev, 7, "%s: done\n", __FUNCTION__)); \ +} + +#define COPY8BPP(bits) \ +static _inline void realizeBrush##bits##Copy8bpp(PDev *pdev, InternalBrush *internal, \ + UINT8 *src, LONG src_stride, \ + XLATEOBJ *color_trans) \ +{ \ + UINT8 *dest = internal->data; \ + UINT8 *end = dest + internal->stride * internal->size.cy; \ + \ + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); \ + ASSERT(pdev, color_trans && color_trans->flXlate & XO_TABLE); \ + for (; dest < end; dest += internal->stride, src += src_stride) { \ + UINT8 *src_line = src; \ + UINT8 *src_line_end = src_line + internal->size.cx; \ + UINT##bits *dest_line = (UINT##bits *)dest; \ + \ + for (; src_line < src_line_end; ++dest_line, src_line++) { \ + ASSERT(pdev, *src_line < color_trans->cEntries); \ + *dest_line = (UINT##bits)color_trans->pulXlate[*src_line]; \ + } \ + } \ + DEBUG_PRINT((pdev, 7, "%s: done\n", __FUNCTION__)); \ +} + +COPY1BPP(16); +COPY1BPP(32); +COPY4BPP(16); +COPY4BPP(32); +COPY8BPP(16); +COPY8BPP(32); + +static _inline void realizeBrush32Copy16bpp(PDev *pdev, InternalBrush *internal, UINT8 *src, + LONG src_stride) +{ + UINT8 *dest = internal->data; + UINT8 *end = dest + internal->stride * internal->size.cy; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + for (; dest < end; dest += internal->stride, src += src_stride) { + UINT32 *dest_line = (UINT32 *)dest; + UINT32 *dest_line_end = dest_line + internal->size.cx; + UINT16 *src_line = (UINT16 *)src; + + for (; dest_line < dest_line_end; ++dest_line, src_line++) { + *dest_line = _16bppTo32bpp(*src_line); + } + } + DEBUG_PRINT((pdev, 7, "%s: done\n", __FUNCTION__)); +} + +static _inline void realizeBrush32Copy24bpp(PDev *pdev, InternalBrush *internal, UINT8 *src, + LONG src_stride) +{ + UINT8 *dest = internal->data; + UINT8 *end = dest + internal->stride * internal->size.cy; + int line_size = internal->size.cx << 2; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + for (; dest < end; dest += internal->stride, src += src_stride) { + UINT8* dest_line = dest; + UINT8* dest_line_end = dest_line + line_size; + UINT8* src_line = src; + + while (dest_line < dest_line_end) { + *(dest_line++) = *(src_line++); + *(dest_line++) = *(src_line++); + *(dest_line++) = *(src_line++); + *(dest_line++) = 0; + } + } + DEBUG_PRINT((pdev, 7, "%s: done\n", __FUNCTION__)); +} + +static _inline void realizeBrush32Copy32bpp(PDev *pdev, InternalBrush *internal, UINT8 *src, + LONG src_stride) +{ + UINT8 *now = internal->data; + UINT8 *end = now + internal->stride * internal->size.cy; + int line_size = internal->size.cx << 2; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + for (; now < end; now += internal->stride, src += src_stride) { + RtlCopyMemory(now, src, line_size); + } + DEBUG_PRINT((pdev, 7, "%s: done\n", __FUNCTION__)); +} + +static _inline void realizeBrush16Copy16bpp(PDev *pdev, InternalBrush *internal, UINT8 *src, + LONG src_stride) +{ + UINT8 *dest = internal->data; + UINT8 *end = dest + internal->stride * internal->size.cy; + int line_size = internal->size.cx << 1; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + for (; dest < end; dest += internal->stride, src += src_stride) { + RtlCopyMemory(dest, src, line_size); + } + DEBUG_PRINT((pdev, 7, "%s: done\n", __FUNCTION__)); +} + +static _inline void realizeBrush16Copy24bpp(PDev *pdev, InternalBrush *internal, UINT8 *src, + LONG src_stride) +{ + UINT8 *dest = internal->data; + UINT8 *end = dest + internal->stride * internal->size.cy; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + for (; dest < end; dest += internal->stride, src += src_stride) { + UINT16 *dest_line = (UINT16 *)dest; + UINT16 *dest_line_end = dest_line + internal->size.cx; + UINT8 *src_line = src; + + while (dest_line < dest_line_end) { + *(dest_line++) = ((UINT16)*(src_line++) >> 3) | + (((UINT16)*(src_line++) & 0xf8) << 2) | + (((UINT16)*(src_line++) & 0xf8) << 7); + } + } + DEBUG_PRINT((pdev, 7, "%s: done\n", __FUNCTION__)); +} + +UINT16 _32bppTo16bpp(UINT32 color) +{ + return ((color & 0xf8) >> 3) | ((color & 0xf800) >> 6) | ((color & 0xf80000) >> 9); +} + +static _inline void realizeBrush16Copy32bpp(PDev *pdev, InternalBrush *internal, UINT8 *src, + LONG src_stride) +{ + UINT8 *now = internal->data; + UINT8 *end = now + internal->stride * internal->size.cy; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + for (; now < end; now += internal->stride, src += src_stride) { + UINT16 *dest_line = (UINT16 *)now; + UINT16 *dest_line_end = dest_line + internal->size.cx; + UINT32 *src_line = (UINT32 *)src; + + while (dest_line < dest_line_end) { + *(dest_line++) = _32bppTo16bpp(*(src_line++)); + } + } + DEBUG_PRINT((pdev, 7, "%s: done\n", __FUNCTION__)); +} + +BOOL APIENTRY DrvRealizeBrush(BRUSHOBJ *brush, SURFOBJ *target, SURFOBJ *pattern, SURFOBJ *mask, + XLATEOBJ *color_trans, ULONG hatch) +{ + PDev *pdev; + InternalBrush *internal; + int stride; + int size; + + if (!(pdev = (PDev *)target->dhpdev)) { + ASSERT(NULL, 0); + return FALSE; + } + + DEBUG_PRINT((pdev, 3, "%s\n", __FUNCTION__)); + + if (mask) { + DEBUG_PRINT((pdev, 0, "%s: ignoring mask\n", __FUNCTION__)); + } + + switch (pattern->iBitmapFormat) { + case BMF_1BPP: + case BMF_32BPP: + case BMF_24BPP: + case BMF_16BPP: + case BMF_8BPP: + case BMF_4BPP: + break; + default: + DEBUG_PRINT((pdev, 0, "%s: bad format\n", __FUNCTION__)); + return FALSE; + } + stride = (pdev->bitmap_format == BMF_32BPP) ? pattern->sizlBitmap.cx << 2 : + ALIGN(pattern->sizlBitmap.cx << 1, 4); + size = stride * pattern->sizlBitmap.cy; + size += sizeof(InternalBrush); + + if (!(internal = (InternalBrush *)BRUSHOBJ_pvAllocRbrush(brush, size))) { + DEBUG_PRINT((pdev, 0, "%s: alloc failed\n", __FUNCTION__)); + return FALSE; + } + internal->size = pattern->sizlBitmap; + internal->key = 0; + internal->stride = stride; + if ((internal->format = pdev->bitmap_format) == BMF_32BPP) { + switch (pattern->iBitmapFormat) { + case BMF_1BPP: + realizeBrush32Copy1bpp(pdev, internal, pattern->pvScan0, pattern->lDelta, color_trans); + break; + case BMF_32BPP: + realizeBrush32Copy32bpp(pdev, internal, pattern->pvScan0, pattern->lDelta); + break; + case BMF_24BPP: + realizeBrush32Copy24bpp(pdev, internal, pattern->pvScan0, pattern->lDelta); + break; + case BMF_16BPP: + realizeBrush32Copy16bpp(pdev, internal, pattern->pvScan0, pattern->lDelta); + break; + case BMF_8BPP: + realizeBrush32Copy8bpp(pdev, internal, pattern->pvScan0, pattern->lDelta, color_trans); + break; + case BMF_4BPP: + realizeBrush32Copy4bpp(pdev, internal, pattern->pvScan0, pattern->lDelta, color_trans); + break; + } + } else { + ASSERT(pdev, pdev->bitmap_format == BMF_16BPP); + switch (pattern->iBitmapFormat) { + case BMF_1BPP: + realizeBrush16Copy1bpp(pdev, internal, pattern->pvScan0, pattern->lDelta, color_trans); + break; + case BMF_32BPP: + realizeBrush16Copy32bpp(pdev, internal, pattern->pvScan0, pattern->lDelta); + break; + case BMF_24BPP: + realizeBrush16Copy24bpp(pdev, internal, pattern->pvScan0, pattern->lDelta); + break; + case BMF_16BPP: + realizeBrush16Copy16bpp(pdev, internal, pattern->pvScan0, pattern->lDelta); + break; + case BMF_8BPP: + realizeBrush16Copy8bpp(pdev, internal, pattern->pvScan0, pattern->lDelta, color_trans); + break; + case BMF_4BPP: + realizeBrush16Copy4bpp(pdev, internal, pattern->pvScan0, pattern->lDelta, color_trans); + break; + } + } + DEBUG_PRINT((pdev, 4, "%s: done\n", __FUNCTION__)); + return TRUE; +} + + +static _inline BOOL GetPattern(PDev *pdev, QXLDrawable *drawable, PHYSICAL *pattern, + InternalBrush *brush) +{ + HSURF hsurf; + SURFOBJ *surf_obj; + Rect area; + UINT32 key; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + if (brush->key && QXLGetBitsFromCache(pdev, drawable, brush->key, pattern)) { + DEBUG_PRINT((pdev, 13, "%s: from cache\n", __FUNCTION__)); + return TRUE; + } + + if (!(hsurf = (HSURF)EngCreateBitmap(brush->size, brush->stride, brush->format, BMF_TOPDOWN, + brush->data))) { + DEBUG_PRINT((pdev, 0, "%s: create bitmap failed\n", __FUNCTION__)); + return FALSE; + } + + if (!(surf_obj = EngLockSurface(hsurf))) { + DEBUG_PRINT((pdev, 0, "%s: lock surf failed\n", __FUNCTION__)); + goto error_1; + } + area.left = area.top = 0; + area.right = brush->size.cx; + area.bottom = brush->size.cy; + + if (!QXLGetBitmap(pdev, drawable, pattern, surf_obj, &area, NULL, &key, TRUE)) { + goto error_2; + } + + brush->key = key; + EngUnlockSurface(surf_obj); + EngDeleteSurface(hsurf); + DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); + return TRUE; + +error_2: + EngUnlockSurface(surf_obj); +error_1: + EngDeleteSurface(hsurf); + return FALSE; +} + + +BOOL QXLGetBrush(PDev *pdev, QXLDrawable *drawable, Brush *qxl_brush, + BRUSHOBJ *brush, POINTL *brush_pos) +{ + DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__)); + ASSERT(pdev, brush); + + if (brush->iSolidColor == ~0) { + ASSERT(pdev, brush_pos); + + DEBUG_PRINT((pdev, 11, "%s: pattern\n", __FUNCTION__)); + if (!brush->pvRbrush && !BRUSHOBJ_pvGetRbrush(brush)) { + DEBUG_PRINT((pdev, 0, "%s: brush realize failed\n", __FUNCTION__)); + return FALSE; + } + qxl_brush->type = BRUSH_TYPE_PATTERN; + qxl_brush->u.pattern.pos.x = brush_pos->x; + qxl_brush->u.pattern.pos.y = brush_pos->y; + if (!GetPattern(pdev, drawable, &qxl_brush->u.pattern.pat, brush->pvRbrush)) { + return FALSE; + } + } else { + qxl_brush->type = BRUSH_TYPE_SOLID; + qxl_brush->u.color = brush->iSolidColor; + DEBUG_PRINT((pdev, 11, "%s: color 0x%x\n", __FUNCTION__, qxl_brush->u.color)); + } + DEBUG_PRINT((pdev, 10, "%s: done\n", __FUNCTION__)); + return TRUE; +} + diff --git a/display/driver.c b/display/driver.c new file mode 100644 index 0000000..c9140af --- /dev/null +++ b/display/driver.c @@ -0,0 +1,1314 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "stddef.h" + +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include "os_dep.h" + +#include "winerror.h" +#include "windef.h" +#include "wingdi.h" +#include "winddi.h" +#include "devioctl.h" +#include "ntddvdeo.h" +#include "ioaccess.h" + +#include "qxldd.h" +#include "utils.h" +#include "mspace.h" +#include "res.h" + +#define DEVICE_NAME L"qxldd" + +#define QXLDD_DEBUG_PREFIX "qxldd: " + +static DRVFN drv_calls[] = { + {INDEX_DrvDisableDriver, (PFN)DrvDisableDriver}, + {INDEX_DrvEnablePDEV, (PFN)DrvEnablePDEV}, + {INDEX_DrvDisablePDEV, (PFN)DrvDisablePDEV}, + {INDEX_DrvCompletePDEV, (PFN)DrvCompletePDEV}, + {INDEX_DrvEnableSurface, (PFN)DrvEnableSurface}, + {INDEX_DrvDisableSurface, (PFN) DrvDisableSurface}, + {INDEX_DrvAssertMode, (PFN)DrvAssertMode}, + {INDEX_DrvGetModes, (PFN)DrvGetModes}, + {INDEX_DrvSynchronize, (PFN)DrvSynchronize}, + {INDEX_DrvCopyBits, (PFN)DrvCopyBits}, + {INDEX_DrvBitBlt, (PFN)DrvBitBlt}, + {INDEX_DrvTextOut, (PFN)DrvTextOut}, + {INDEX_DrvStrokePath, (PFN)DrvStrokePath}, + {INDEX_DrvRealizeBrush, (PFN)DrvRealizeBrush}, + {INDEX_DrvSetPointerShape, (PFN)DrvSetPointerShape}, + {INDEX_DrvMovePointer, (PFN)DrvMovePointer}, + {INDEX_DrvStretchBlt, (PFN)DrvStretchBlt}, + {INDEX_DrvStretchBltROP, (PFN)DrvStretchBltROP}, + {INDEX_DrvTransparentBlt, (PFN)DrvTransparentBlt}, + {INDEX_DrvAlphaBlend, (PFN)DrvAlphaBlend}, + +#ifdef CALL_TEST + {INDEX_DrvFillPath, (PFN)DrvFillPath}, + {INDEX_DrvGradientFill, (PFN)DrvGradientFill}, + {INDEX_DrvLineTo, (PFN)DrvLineTo}, + {INDEX_DrvPlgBlt, (PFN)DrvPlgBlt}, + {INDEX_DrvStrokeAndFillPath, (PFN)DrvStrokeAndFillPath}, +#endif +}; + +#ifdef CALL_TEST + +typedef struct CallCounter { + const char *name; + BOOL effective; +} CallCounterInfo; + +static CallCounterInfo counters_info[NUM_CALL_COUNTERS] = { + { "DrvCopyBits", FALSE}, + { "DrvBitBlt", TRUE}, + { "DrvTextOut", TRUE}, + { "DrvStrokePath", TRUE}, + { "DrvStretchBlt", FALSE}, + { "DrvStretchBltROP", TRUE}, + { "TransparentBlt", FALSE}, + { "DrvAlphaBlend", FALSE}, + + { "DrvFillPath", FALSE}, + { "DrvGradientFill", FALSE}, + { "DrvLineTo", FALSE}, + { "DrvPlgBlt", FALSE}, + { "DrvStrokeAndFillPath", FALSE}, +}; + +#endif + +#define DBG_LEVEL 0 + +void DebugPrintV(PDev *pdev, const char *message, va_list ap) +{ + if (pdev && pdev->log_buf) { + EngAcquireSemaphore(pdev->print_sem); + _snprintf(pdev->log_buf, QXL_LOG_BUF_SIZE, QXLDD_DEBUG_PREFIX); + _vsnprintf(pdev->log_buf + strlen(QXLDD_DEBUG_PREFIX), + QXL_LOG_BUF_SIZE - strlen(QXLDD_DEBUG_PREFIX), message, ap); + WRITE_PORT_UCHAR(pdev->log_port, 0); + EngReleaseSemaphore(pdev->print_sem); + } else { + EngDebugPrint(QXLDD_DEBUG_PREFIX, (PCHAR)message, ap); + } +} + +void DebugPrint(PDev *pdev, int level, const char *message, ...) +{ + va_list ap; + + if (level >= (pdev ? (int)*pdev->log_level : DBG_LEVEL)) { + return; + } + va_start(ap, message); + DebugPrintV(pdev, message, ap); + va_end(ap); +} + +#define DRIVER_VERSION 1 +#define OS_VERSION_MAJOR 5 +#define OS_VERSION_MINOR 0 +#define MK_GDIINFO_VERSION(os_major, os_minor, drv_vers) \ + ((drv_vers) | ((os_minor) << 8) | ((os_major) << 12)) + + +GDIINFO gdi_default = { + MK_GDIINFO_VERSION(OS_VERSION_MAJOR, OS_VERSION_MINOR, DRIVER_VERSION), + DT_RASDISPLAY, + 0, //ulHorzSize + 0, //ulVertSize + 0, //ulHorzRes + 0, //ulVertRes + 0, //cBitsPixel + 0, //cPlanes + 0, //ulNumColors + 0, //flRaster + 0, //ulLogPixelsX + 0, //ulLogPixelsY + TC_RA_ABLE, //flTextCaps + 0, //ulDACRed + 0, //ulDACGreen + 0, //ulDACBlue + 0x0024, //ulAspectX + 0x0024, //ulAspectY + 0x0033, //ulAspectXY + 1, //xStyleStep + 1, //yStyleSte; + 3, //denStyleStep + { 0, 0}, //ptlPhysOffset + { 0, 0}, //szlPhysSize + 0, //ulNumPalReg + + { //ciDevice + { 6700, 3300, 0}, //Red + { 2100, 7100, 0}, //Green + { 1400, 800, 0}, //Blue + { 1750, 3950, 0}, //Cyan + { 4050, 2050, 0}, //Magenta + { 4400, 5200, 0}, //Yellow + { 3127, 3290, 0}, //AlignmentWhite + 20000, //RedGamma + 20000, //GreenGamma + 20000, //BlueGamma + 0, 0, 0, 0, 0, 0 //No dye correction for raster displays + }, + + 0, //ulDevicePelsDPI + PRIMARY_ORDER_CBA, //ulPrimaryOrder + HT_PATSIZE_4x4_M, //ulHTPatternSize + HT_FORMAT_8BPP, //ulHTOutputFormat + HT_FLAG_ADDITIVE_PRIMS, //flHTFlags + 0, //ulVRefresh + 0, //ulPanningHorzRes + 0, //ulPanningVertRes + 0, //ulBltAlignment + //more +}; + +#define SYSTM_LOGFONT {16, 7, 0, 0, 700, 0, 0, 0,ANSI_CHARSET, OUT_DEFAULT_PRECIS,\ + CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,\ + VARIABLE_PITCH | FF_DONTCARE, L"System"} +#define HELVE_LOGFONT {12, 9, 0, 0, 400, 0, 0, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS,\ + CLIP_STROKE_PRECIS, PROOF_QUALITY,\ + VARIABLE_PITCH | FF_DONTCARE, L"MS Sans Serif"} +#define COURI_LOGFONT {12, 9, 0, 0, 400, 0, 0, 0, ANSI_CHARSET,OUT_DEFAULT_PRECIS,\ + CLIP_STROKE_PRECIS,PROOF_QUALITY,\ + FIXED_PITCH | FF_DONTCARE, L"Courier"} + +DEVINFO dev_default = { + GCAPS_ARBRUSHOPAQUE | GCAPS_ARBRUSHTEXT | GCAPS_ASYNCMOVE | GCAPS_BEZIERS | + /*GCAPS_FONT_RASTERIZER |*/ /*for now GCAPS_GEOMETRICWIDE |*/ GCAPS_GRAY16 | GCAPS_OPAQUERECT | + GCAPS_VECTORFONT | GCAPS_WINDINGFILL /*| GCAPS_LAYERED*/, + SYSTM_LOGFONT, //lfDefaultFont + HELVE_LOGFONT, //lfAnsiVarFont + COURI_LOGFONT, //lfAnsiFixFont + 0, //cFonts + 0, //iDitherFormat + 0, //cxDither + 0, //cyDither + 0, //hpalDefault +#if (WINVER >= 0x0501) + GCAPS2_MOUSETRAILS | +#endif + GCAPS2_ALPHACURSOR, +}; + +static void mspace_print(void *user_data, char *format, ...) +{ + PDev *pdev = (PDev *)user_data; + va_list ap; + + va_start(ap, format); + DebugPrintV(pdev, format, ap); + va_end(ap); +} + +static void mspace_abort(void *user_data) +{ + mspace_print(user_data, "mspace abort"); + EngDebugBreak(); +} + +BOOL DrvEnableDriver(ULONG engine_version, ULONG enable_data_size, PDRVENABLEDATA enable_data) +{ + DEBUG_PRINT((NULL, 1, "%s\n", __FUNCTION__)); + enable_data->iDriverVersion = DDI_DRIVER_VERSION_NT5; + enable_data->c = sizeof(drv_calls) / sizeof(DRVFN); + enable_data->pdrvfn = drv_calls; + mspace_set_abort_func(mspace_abort); + mspace_set_print_func(mspace_print); + ResInitGlobals(); + DEBUG_PRINT((NULL, 1, "%s: end\n", __FUNCTION__)); + return TRUE; +} + +VOID DrvDisableDriver(VOID) +{ + DEBUG_PRINT((NULL, 1, "%s\n", __FUNCTION__)); + ResDestroyGlobals(); +} + +DWORD GetAvailableModes(HANDLE driver, PVIDEO_MODE_INFORMATION *mode_info, + DWORD *mode_info_size) +{ + ULONG n; + VIDEO_NUM_MODES modes; + PVIDEO_MODE_INFORMATION info; + + DEBUG_PRINT((NULL, 1, "%s\n", __FUNCTION__)); + + if (EngDeviceIoControl(driver, IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES, NULL, 0, + &modes, sizeof(VIDEO_NUM_MODES), &n)) { + DEBUG_PRINT((NULL, 0, "%s: query num modes failed\n", __FUNCTION__)); + return 0; + } + + info = (PVIDEO_MODE_INFORMATION)EngAllocMem(FL_ZERO_MEMORY, + modes.NumModes * modes.ModeInformationLength, + ALLOC_TAG); + if (!info) { + DEBUG_PRINT((NULL, 0, "%s: memory allocation failed\n", __FUNCTION__)); + return 0; + } + + if (EngDeviceIoControl(driver, IOCTL_VIDEO_QUERY_AVAIL_MODES, NULL, 0, info, + modes.NumModes * modes.ModeInformationLength, &n)) { + DEBUG_PRINT((NULL, 0, "%s: query modes failed\n", __FUNCTION__)); + EngFreeMem(info); + return 0; + } + + *mode_info = info; + *mode_info_size = modes.ModeInformationLength; + + n = modes.NumModes; + while ( n-- ) { + if ( (info->NumberOfPlanes != 1 ) ||!(info->AttributeFlags & VIDEO_MODE_GRAPHICS) + ||((info->BitsPerPlane != 16) && (info->BitsPerPlane != 32)) + || (info->VisScreenWidth > 2000) || (info->VisScreenWidth < 480) + || (info->VisScreenHeight > 2000) || (info->VisScreenHeight < 480) ) { + + DEBUG_PRINT((NULL, 1, "%s: unsuported mode rejecting miniport mode\n", __FUNCTION__)); + DEBUG_PRINT((NULL, 1, " width = %li height = %li\n", + info->VisScreenWidth, info->VisScreenHeight)); + DEBUG_PRINT((NULL, 1, " bpp = %li freq = %li\n", + info->BitsPerPlane * info->NumberOfPlanes, info->Frequency)); + info->Length = 0; + } + + info = (PVIDEO_MODE_INFORMATION) (((PUCHAR)info) + modes.ModeInformationLength); + } + DEBUG_PRINT((NULL, 1, "%s: OK num modes %lu\n", __FUNCTION__, modes.NumModes)); + return modes.NumModes; +} + +BOOL InitializeModeFields(PDev *pdev, GDIINFO *gdi_info, DEVINFO *dev_info, + DEVMODEW *dev_mode) +{ + ULONG n_modes; + PVIDEO_MODE_INFORMATION video_buff; + PVIDEO_MODE_INFORMATION selected_mode; + PVIDEO_MODE_INFORMATION video_mode; + VIDEO_MODE_INFORMATION vmi; + ULONG video_mode_size; + + DEBUG_PRINT((NULL, 1, "%s\n", __FUNCTION__)); + + n_modes = GetAvailableModes(pdev->driver, &video_buff, &video_mode_size); + if ( n_modes == 0 ) { + DEBUG_PRINT((NULL, 0, "%s: get available modes failed\n", __FUNCTION__)); + return FALSE; + } + +#if (WINVER < 0x0501) + DEBUG_PRINT((NULL, 1, "%s: req mode: fields %u bits %u w %u h %u frequency %u\n", + __FUNCTION__, + dev_mode->dmFields, + dev_mode->dmBitsPerPel, + dev_mode->dmPelsWidth, + dev_mode->dmPelsHeight, + dev_mode->dmDisplayFrequency)); +#else + DEBUG_PRINT((NULL, 1, "%s: req mode: fields %u bits %u w %u h %u frequency %u orientation %u\n", + __FUNCTION__, + dev_mode->dmFields, + dev_mode->dmBitsPerPel, + dev_mode->dmPelsWidth, + dev_mode->dmPelsHeight, + dev_mode->dmDisplayFrequency, + dev_mode->dmDisplayOrientation)); +#endif + + + selected_mode = NULL; + video_mode = video_buff; + + while (n_modes--) { + if ( video_mode->Length != 0 ) { + DEBUG_PRINT((NULL, 1, "%s: check width = %li height = %li\n", + __FUNCTION__, + video_mode->VisScreenWidth, + video_mode->VisScreenHeight)); + DEBUG_PRINT((NULL, 1, " bpp = %li freq = %li\n", + video_mode->BitsPerPlane * video_mode->NumberOfPlanes, + video_mode->Frequency)); + + if ( (video_mode->VisScreenWidth == dev_mode->dmPelsWidth) + && (video_mode->VisScreenHeight == dev_mode->dmPelsHeight) + && (video_mode->BitsPerPlane * video_mode->NumberOfPlanes + == dev_mode->dmBitsPerPel) + && (video_mode->Frequency == dev_mode->dmDisplayFrequency) +#if (WINVER >= 0x0501) + && (video_mode->DriverSpecificAttributeFlags + == dev_mode->dmDisplayOrientation) +#endif + ) { + selected_mode = video_mode; + DEBUG_PRINT((NULL, 1, "%s: found\n", __FUNCTION__)); + break; + } + } + video_mode = (PVIDEO_MODE_INFORMATION)(((PUCHAR)video_mode) + video_mode_size); + } + + if (!selected_mode) { + DEBUG_PRINT((NULL, 0, "%s: not found\n")); + EngFreeMem(video_buff); + return FALSE; + } + + vmi = *selected_mode; + EngFreeMem(video_buff); + + pdev->video_mode_index = vmi.ModeIndex; + pdev->resolution.cx = vmi.VisScreenWidth; + pdev->resolution.cy = vmi.VisScreenHeight; + pdev->max_bitmap_size = pdev->resolution.cx * pdev->resolution.cy; + pdev->max_bitmap_size += pdev->max_bitmap_size / 2; + pdev->stride = vmi.ScreenStride; + + *gdi_info = gdi_default; + + gdi_info->ulHorzSize = vmi.XMillimeter; + gdi_info->ulVertSize = vmi.YMillimeter; + gdi_info->ulHorzRes = vmi.VisScreenWidth; + gdi_info->ulVertRes = vmi.VisScreenHeight; + gdi_info->cBitsPixel = vmi.BitsPerPlane; + gdi_info->cPlanes = vmi.NumberOfPlanes; + gdi_info->ulVRefresh = vmi.Frequency; + gdi_info->ulDACRed = vmi.NumberRedBits; + gdi_info->ulDACGreen = vmi.NumberGreenBits; + gdi_info->ulDACBlue = vmi.NumberBlueBits; + gdi_info->ulLogPixelsX = dev_mode->dmLogPixels; + gdi_info->ulLogPixelsY = dev_mode->dmLogPixels; + + *dev_info = dev_default; + + switch ( vmi.BitsPerPlane ) { + case 16: + pdev->bitmap_format = BMF_16BPP; + pdev->red_mask = vmi.RedMask; + pdev->green_mask = vmi.GreenMask; + pdev->blue_mask = vmi.BlueMask; + + gdi_info->ulNumColors = (ULONG)-1; + gdi_info->ulNumPalReg = 0; + gdi_info->ulHTOutputFormat = HT_FORMAT_16BPP; + + dev_info->iDitherFormat = BMF_16BPP; + break; + case 32: + pdev->bitmap_format = BMF_32BPP; + pdev->red_mask = vmi.RedMask; + pdev->green_mask = vmi.GreenMask; + pdev->blue_mask = vmi.BlueMask; + + gdi_info->ulNumColors = (ULONG)-1; + gdi_info->ulNumPalReg = 0; + gdi_info->ulHTOutputFormat = HT_FORMAT_32BPP; + + dev_info->iDitherFormat = BMF_32BPP; + break; + default: + DEBUG_PRINT((NULL, 0, "%s: bit depth not supported\n", __FUNCTION__)); + return FALSE; + } + DEBUG_PRINT((NULL, 1, "%s: exit\n", __FUNCTION__)); + return TRUE; +} + +void DestroyPalette(PDev *pdev) +{ + DEBUG_PRINT((NULL, 1, "%s: 0x%lx\n", __FUNCTION__, pdev)); + if (pdev->palette) { + EngDeletePalette(pdev->palette); + pdev->palette = NULL; + } + DEBUG_PRINT((NULL, 1, "%s: 0x%lx exit\n", __FUNCTION__, pdev)); +} + +BOOL InitPalette(PDev *pdev, DEVINFO *dev_info) +{ + DEBUG_PRINT((NULL, 1, "%s: 0x%lx\n", __FUNCTION__, pdev)); + + if ((pdev->palette = EngCreatePalette(PAL_BITFIELDS, 0, NULL, + pdev->red_mask, + pdev->green_mask, + pdev->blue_mask)) == NULL) { + DEBUG_PRINT((NULL, 0, "%s: create palette failed\n", __FUNCTION__)); + return FALSE; + } + dev_info->hpalDefault = pdev->palette; + + DEBUG_PRINT((NULL, 1, "%s: OK\n", __FUNCTION__)); + return TRUE; +} + +DHPDEV DrvEnablePDEV(DEVMODEW *dev_mode, PWSTR ignore1, ULONG ignore2, HSURF *ignore3, + ULONG dev_caps_size, ULONG *dev_caps, ULONG dev_inf_size, + DEVINFO *in_dev_info, HDEV gdi_dev, PWSTR device_name, HANDLE driver) +{ + PDev *pdev; + GDIINFO gdi_info; + DEVINFO dev_info; + + DEBUG_PRINT((NULL, 1, "%s\n", __FUNCTION__)); + + if (!(pdev = (PDev*)EngAllocMem(FL_ZERO_MEMORY, sizeof(PDev), ALLOC_TAG))) { + DEBUG_PRINT((NULL, 0, "%s: pdev alloc failed\n", __FUNCTION__)); + return NULL; + } + + RtlZeroMemory(&gdi_info, sizeof(GDIINFO)); + RtlCopyMemory(&gdi_info, dev_caps, MIN(dev_caps_size, sizeof(GDIINFO))); + + RtlZeroMemory(&dev_info, sizeof(DEVINFO)); + RtlCopyMemory(&dev_info, in_dev_info, MIN(dev_inf_size, sizeof(DEVINFO))); + + pdev->driver = driver; + + if (!InitializeModeFields(pdev, &gdi_info, &dev_info, dev_mode)) { + DEBUG_PRINT((NULL, 0, "%s: init mode failed\n", __FUNCTION__)); + goto err1; + } + + if (!InitPalette(pdev, &dev_info)) { + DEBUG_PRINT((NULL, 0, "%s: init palet failed\n", __FUNCTION__)); + goto err1; + } + + if (!(pdev->malloc_sem = EngCreateSemaphore())) { + DEBUG_PRINT((NULL, 0, "%s: create malloc sem failed\n", __FUNCTION__)); + goto err2; + } + + if (!(pdev->print_sem = EngCreateSemaphore())) { + DEBUG_PRINT((NULL, 0, "%s: create malloc sem failed\n", __FUNCTION__)); + goto err3; + } + + if (!ResInit(pdev)) { + DEBUG_PRINT((NULL, 0, "%s: init res failed\n", __FUNCTION__)); + goto err4; + } + + RtlCopyMemory(dev_caps, &gdi_info, dev_caps_size); + RtlCopyMemory(in_dev_info, &dev_info, dev_inf_size); + + DEBUG_PRINT((NULL, 1, "%s: 0x%lx\n", __FUNCTION__, pdev)); + return(DHPDEV)pdev; + +err4: + EngDeleteSemaphore(pdev->print_sem); + +err3: + EngDeleteSemaphore(pdev->malloc_sem); + +err2: + DestroyPalette(pdev); + +err1: + EngFreeMem(pdev); + + return NULL; +} + +VOID DrvDisablePDEV(DHPDEV in_pdev) +{ + PDev* pdev = (PDev*)in_pdev; + + DEBUG_PRINT((NULL, 1, "%s: 0x%lx\n", __FUNCTION__, pdev)); + ResDestroy(pdev); + DestroyPalette(pdev); + EngDeleteSemaphore(pdev->malloc_sem); + EngDeleteSemaphore(pdev->print_sem); + EngFreeMem(pdev); + DEBUG_PRINT((NULL, 1, "%s: 0x%lx exit\n", __FUNCTION__, pdev)); +} + +VOID DrvCompletePDEV(DHPDEV in_pdev, HDEV gdi_dev) +{ + PDev* pdev = (PDev*)in_pdev; + + DEBUG_PRINT((NULL, 1, "%s: 0x%lx\n", __FUNCTION__, pdev)); + pdev->eng = gdi_dev; + DEBUG_PRINT((NULL, 1, "%s: 0x%lx exit\n", __FUNCTION__, pdev)); +} + +BOOL SetHardwareMode(PDev *pdev) +{ + VIDEO_MODE_INFORMATION video_info; + DWORD length; + + DEBUG_PRINT((NULL, 1, "%s: 0x%lx mode %lu\n", __FUNCTION__, pdev, pdev->video_mode_index)); + + if (EngDeviceIoControl(pdev->driver, IOCTL_VIDEO_SET_CURRENT_MODE, + &pdev->video_mode_index, sizeof(DWORD), + NULL, 0, &length)) { + DEBUG_PRINT((NULL, 0, "%s: set mode failed, 0x%lx\n", __FUNCTION__, pdev)); + return FALSE; + } + + DEBUG_PRINT((NULL, 1, "%s: 0x%lx OK\n", __FUNCTION__, pdev)); + return TRUE; +} + +static BOOL InitDrawArea(PDev *pdev, UINT8 *base) +{ + HSURF bitmap; + SIZEL size; + SURFOBJ* surf_obj; + + size.cx = pdev->resolution.cx; + size.cy = pdev->resolution.cy; + + if (!(bitmap = (HSURF)EngCreateBitmap(size, size.cx << 2, BMF_32BPP, 0, base))) { + DEBUG_PRINT((pdev, 0, "%s: EngCreateBitmap failed\n", __FUNCTION__)); + return FALSE; + } + + if (!EngAssociateSurface(bitmap, pdev->eng, 0)) { + DEBUG_PRINT((pdev, 0, "%s: EngAssociateSurface failed\n", __FUNCTION__)); + goto error; + + } + + if (!(surf_obj = EngLockSurface(bitmap))) { + DEBUG_PRINT((pdev, 0, "%s: EngLockSurface failed\n", __FUNCTION__)); + goto error; + } + + pdev->draw_bitmap = bitmap; + pdev->draw_surf = surf_obj; + return TRUE; + +error: + EngDeleteSurface(bitmap); + return FALSE; +} + +BOOL PrepareHardware(PDev *pdev) +{ + VIDEO_MEMORY video_mem; + VIDEO_MEMORY_INFORMATION video_mem_Info; + DWORD length; + QXLDriverInfo dev_info; + + DEBUG_PRINT((NULL, 1, "%s: 0x%lx\n", __FUNCTION__, pdev)); + if (!SetHardwareMode(pdev)) { + DEBUG_PRINT((NULL, 0, "%s: set mode failed, 0x%lx\n", __FUNCTION__, pdev)); + return FALSE; + } + + if (EngDeviceIoControl( pdev->driver, IOCTL_QXL_GET_INFO, NULL, + 0, &dev_info, sizeof(QXLDriverInfo), &length) ) { + DEBUG_PRINT((NULL, 0, "%s: get qxl info failed, 0x%lx\n", __FUNCTION__, pdev)); + return FALSE; + } + + if (dev_info.version != QXL_DRIVER_INFO_VERSION) { + DEBUG_PRINT((NULL, 0, "%s: get qxl info mismatch, 0x%lx\n", __FUNCTION__, pdev)); + return FALSE; + } + + pdev->cmd_ring = dev_info.cmd_ring; + pdev->cursor_ring = dev_info.cursor_ring; + pdev->release_ring = dev_info.release_ring; + pdev->notify_cmd_port = dev_info.notify_cmd_port; + pdev->notify_cursor_port = dev_info.notify_cursor_port; + pdev->notify_oom_port = dev_info.notify_oom_port; + pdev->display_event = dev_info.display_event; + pdev->cursor_event = dev_info.cursor_event; + pdev->sleep_event = dev_info.sleep_event; +#if (WINVER < 0x0501) + pdev->WaitForEvent = dev_info.WaitForEvent; +#endif + + pdev->num_io_pages = dev_info.num_io_pages; + pdev->io_pages_virt = dev_info.io_pages_virt; + pdev->io_pages_phys = dev_info.io_pages_phys; + + pdev->dev_update_id = dev_info.update_id; + pdev->update_id = *pdev->dev_update_id; + + pdev->update_area_port = dev_info.update_area_port; + pdev->update_area = dev_info.update_area; + + pdev->mm_clock = dev_info.mm_clock; + + pdev->compression_level = dev_info.compression_level; + + pdev->log_port = dev_info.log_port; + pdev->log_buf = dev_info.log_buf; + pdev->log_level = dev_info.log_level; + + video_mem.RequestedVirtualAddress = NULL; + + if (EngDeviceIoControl( pdev->driver, IOCTL_VIDEO_MAP_VIDEO_MEMORY, &video_mem, + sizeof(VIDEO_MEMORY), &video_mem_Info, + sizeof(video_mem_Info), &length) ) { + DEBUG_PRINT((NULL, 0, "%s: mapping failed, 0x%lx\n", __FUNCTION__, pdev)); + return FALSE; + } + DEBUG_PRINT((NULL, 1, "%s: 0x%lx vals 0x%lx %ul\n", __FUNCTION__, pdev, + video_mem_Info.FrameBufferBase, video_mem_Info.FrameBufferLength)); + pdev->fb = (BYTE*)video_mem_Info.FrameBufferBase; + pdev->fb_size = video_mem_Info.FrameBufferLength; + + if (!InitDrawArea(pdev, dev_info.draw_area)) { + DEBUG_PRINT((NULL, 0, "%s: InitDrawArea failed, 0x%lx\n", __FUNCTION__, pdev)); + return FALSE; + } + + DEBUG_PRINT((NULL, 1, "%s: 0x%lx exit: 0x%lx %ul\n", __FUNCTION__, pdev, + pdev->fb, pdev->fb_size)); + return TRUE; +} + +HSURF DrvEnableSurface(DHPDEV in_pdev) +{ + PDev *pdev; + HSURF surf; + + DEBUG_PRINT((NULL, 1, "%s: 0x%lx\n", __FUNCTION__, in_pdev)); + pdev = (PDev*)in_pdev; + if (!PrepareHardware(pdev)) { + return NULL; + } + + InitResources(pdev); + + DEBUG_PRINT((NULL, 1, "%s: EngCreateDeviceSurface(0x%lx, %ld:%ld, %lu)\n", + __FUNCTION__, + pdev, + pdev->resolution.cx, + pdev->resolution.cy, + pdev->bitmap_format)); + + if (!(surf = (HSURF)EngCreateDeviceSurface((DHSURF)pdev, pdev->resolution, + pdev->bitmap_format))) { + DEBUG_PRINT((NULL, 0, "%s: create device surface failed, 0x%lx\n", + __FUNCTION__, pdev)); + goto err; + } + + DEBUG_PRINT((NULL, 1, "%s: EngModifySurface(0x%lx, 0x%lx, 0, MS_NOTSYSTEMMEMORY, " + "0x%lx, 0x%lx, %lu, NULL)\n", + __FUNCTION__, + surf, + pdev->eng, + pdev, + pdev->fb, + pdev->stride)); + + pdev->surf = surf; + if (!EngModifySurface(surf, pdev->eng, + HOOK_SYNCHRONIZE | HOOK_COPYBITS | HOOK_BITBLT | HOOK_TEXTOUT | + HOOK_STROKEPATH | HOOK_STRETCHBLT | HOOK_STRETCHBLTROP | + HOOK_TRANSPARENTBLT | HOOK_ALPHABLEND +#ifdef CALL_TEST + | HOOK_PLGBLT | HOOK_FILLPATH | HOOK_STROKEANDFILLPATH | HOOK_LINETO | + HOOK_GRADIENTFILL +#endif + , + MS_NOTSYSTEMMEMORY, + (DHSURF)pdev, + NULL, + 0, + NULL)) { + DEBUG_PRINT((NULL, 0, "%s: modify surface failed, 0x%lx\n", + __FUNCTION__, pdev)); + goto err; + } + DEBUG_PRINT((NULL, 1, "%s: 0x%lx exit\n", __FUNCTION__, pdev)); + return surf; + + err: + DrvDisableSurface((DHPDEV)pdev); + DEBUG_PRINT((NULL, 0, "%s: 0x%lx err\n", __FUNCTION__, pdev)); + return NULL; +} + +VOID DrvDisableSurface(DHPDEV in_pdev) +{ + PDev *pdev = (PDev*)in_pdev; + + DEBUG_PRINT((NULL, 1, "%s: 0x%lx\n", __FUNCTION__, pdev)); + + if (pdev->surf) { + EngDeleteSurface(pdev->surf); + pdev->surf = NULL; + } + + if (pdev->draw_surf) { + EngUnlockSurface(pdev->draw_surf); + pdev->draw_surf = NULL; + EngDeleteSurface(pdev->draw_bitmap); + pdev->draw_bitmap = NULL; + } + + if (pdev->fb) { + VIDEO_MEMORY video_mem; + DWORD length; + + video_mem.RequestedVirtualAddress = pdev->fb; + pdev->fb = 0; + pdev->fb_size = 0; + if (EngDeviceIoControl(pdev->driver, + IOCTL_VIDEO_UNMAP_VIDEO_MEMORY, + &video_mem, + sizeof(video_mem), + NULL, + 0, + &length)) { + DEBUG_PRINT((NULL, 0, "%s: unmpap failed, 0x%lx\n", __FUNCTION__, pdev)); + } + } + DEBUG_PRINT((NULL, 1, "%s: 0x%lx exit\n", __FUNCTION__, pdev)); +} + +BOOL DrvAssertMode(DHPDEV in_pdev, BOOL enable) +{ + PDev* pdev = (PDev*)in_pdev; + BOOL ret; + + DEBUG_PRINT((NULL, 1, "%s: 0x%lx\n", __FUNCTION__, pdev)); + if (enable) { + ret = SetHardwareMode(pdev); + InitResources(pdev); + } else { + DWORD length; + if (EngDeviceIoControl(pdev->driver, IOCTL_VIDEO_RESET_DEVICE, + NULL, 0, NULL, 0, &length)) { + DEBUG_PRINT((NULL, 0, "%s: reset failed 0x%lx\n", __FUNCTION__, pdev)); + ret = FALSE; + } else { + ret = TRUE; + } + } + DEBUG_PRINT((NULL, 1, "%s: 0x%lx exit %s\n", __FUNCTION__, pdev, ret ? "TRUE" : "FALSE")); + return ret; +} + +ULONG DrvGetModes(HANDLE driver, ULONG dev_modes_size, DEVMODEW *dev_modes) +{ + PVIDEO_MODE_INFORMATION video_modes; + PVIDEO_MODE_INFORMATION curr_video_mode; + DWORD mode_size; + DWORD output_size; + DWORD n_modes; + + DEBUG_PRINT((NULL, 1, "%s\n", __FUNCTION__)); + + n_modes = GetAvailableModes(driver, &video_modes, &mode_size); + + if (!n_modes) { + DEBUG_PRINT((NULL, 0, "%s: get available modes failed\n", __FUNCTION__)); + return 0; + } + + if (!dev_modes) { + DEBUG_PRINT((NULL, 1, "%s: query size\n", __FUNCTION__)); + output_size = n_modes * sizeof(DEVMODEW); + goto out; + } + + if (dev_modes_size < n_modes * sizeof(DEVMODEW)) { + DEBUG_PRINT((NULL, 0, "%s: buf to small\n", __FUNCTION__)); + output_size = 0; + goto out; + } + + output_size = 0; + curr_video_mode = video_modes; + do { + if (curr_video_mode->Length != 0) { + RtlZeroMemory(dev_modes, sizeof(DEVMODEW)); + ASSERT(NULL, sizeof(DEVICE_NAME) < sizeof(dev_modes->dmDeviceName)); + RtlCopyMemory(dev_modes->dmDeviceName, DEVICE_NAME, sizeof(DEVICE_NAME)); + dev_modes->dmSpecVersion = DM_SPECVERSION; + dev_modes->dmDriverVersion = DM_SPECVERSION; + dev_modes->dmSize = sizeof(DEVMODEW); + dev_modes->dmBitsPerPel = curr_video_mode->NumberOfPlanes * + curr_video_mode->BitsPerPlane; + dev_modes->dmPelsWidth = curr_video_mode->VisScreenWidth; + dev_modes->dmPelsHeight = curr_video_mode->VisScreenHeight; + dev_modes->dmDisplayFrequency = curr_video_mode->Frequency; + dev_modes->dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | + DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS; +#if (WINVER >= 0x0501) + dev_modes->dmDisplayOrientation = curr_video_mode->DriverSpecificAttributeFlags; + dev_modes->dmFields |= DM_DISPLAYORIENTATION; +#endif + + DEBUG_PRINT((NULL, 1, "%s: mode: w %u h %u bits %u frequency %u\n", + __FUNCTION__, + dev_modes->dmPelsWidth, + dev_modes->dmPelsHeight, + dev_modes->dmBitsPerPel, + dev_modes->dmDisplayFrequency)); +#if (WINVER >= 0x0501) + DEBUG_PRINT((NULL, 1, " orientation %u\n", + dev_modes->dmDisplayOrientation)); +#endif + output_size += sizeof(DEVMODEW); + dev_modes++; + } + curr_video_mode = (PVIDEO_MODE_INFORMATION)(((PUCHAR)curr_video_mode) + mode_size); + } while (--n_modes); + + out: + + EngFreeMem(video_modes); + DEBUG_PRINT((NULL, 1, "%s: exit %u\n", __FUNCTION__, output_size)); + return output_size; +} + +VOID DrvSynchronize(DHPDEV in_pdev, RECTL *ignored) +{ + PDev* pdev = (PDev*)in_pdev; + int notify; + + DEBUG_PRINT((pdev, 3, "%s: 0x%lx\n", __FUNCTION__, pdev)); + + DEBUG_PRINT((pdev, 4, "%s: 0x%lx done\n", __FUNCTION__, pdev)); +} + +char *BitmapFormatToStr(int format) +{ + switch (format) { + case BMF_1BPP: + return "BMF_1BPP"; + case BMF_4BPP: + return "BMF_4BPP"; + case BMF_8BPP: + return "BMF_8BPP"; + case BMF_16BPP: + return "BMF_16BPP"; + case BMF_24BPP: + return "BMF_24BPP"; + case BMF_32BPP: + return "BMF_32BPP"; + case BMF_4RLE: + return "BMF_4RLE"; + case BMF_8RLE: + return "BMF_8RLE"; + case BMF_JPEG: + return "BMF_JPEG"; + case BMF_PNG: + return "BMF_PNG"; + default: + return "?"; + } +} + +char *BitmapTypeToStr(int type) +{ + switch (type) { + case STYPE_BITMAP: + return "STYPE_BITMAP"; + case STYPE_DEVICE: + return "STYPE_DEVICE"; + case STYPE_DEVBITMAP: + return "STYPE_DEVBITMAP"; + default: + return "?"; + } +} + +#include "rop.h" +#include "utils.h" +#include "res.h" + +FIX FlotaToFixed(FLOATL val, FLOATL scale) +{ + FLOATOBJ float_obj; + FIX ret; + + FLOATOBJ_SetFloat(&float_obj, val); + FLOATOBJ_MulFloat(&float_obj, scale); + + ret = FLOATOBJ_GetLong(&float_obj) << 4; + FLOATOBJ_MulLong(&float_obj, 16); + ret |= (0x0f & FLOATOBJ_GetLong(&float_obj)); + return ret; +} + +static BOOL GetGeometricAttr(PDev *pdev, QXLDrawable *drawable, LineAttr *q_line_attr, + LINEATTRS *line_attr, XFORMOBJ *width_transform) +{ + ULONG save_buf_size; + FLOATOBJ float_obj; + PVOID fpu_buf; + XFORML xform; + + ASSERT(pdev, width_transform); + ASSERT(pdev, LINE_CAP_ROUND == ENDCAP_ROUND && LINE_CAP_SQUARE == ENDCAP_SQUARE && + LINE_CAP_BUTT == ENDCAP_BUTT && LINE_JOIN_ROUND == JOIN_ROUND && + LINE_JOIN_BEVEL == JOIN_BEVEL && LINE_JOIN_MITER == JOIN_MITER); + + + save_buf_size = EngSaveFloatingPointState(NULL, 0); + if (!(fpu_buf = EngAllocMem( +#if !(WINVER < 0x0501) + FL_NONPAGED_MEMORY | +#endif + FL_ZERO_MEMORY, + save_buf_size, + ALLOC_TAG))) { + DEBUG_PRINT((pdev, 0, "%s: alloc mem failed\n", __FUNCTION__)); + return FALSE; + } + + if (!EngSaveFloatingPointState(fpu_buf, save_buf_size)) { + DEBUG_PRINT((pdev, 0, "%s: save fpu state failed\n", __FUNCTION__)); + goto err1; + } + + if (XFORMOBJ_iGetXform(width_transform ,&xform) == DDI_ERROR) { + DEBUG_PRINT((pdev, 0, "%s: err get xform\n", __FUNCTION__)); + goto err2; + } + + if (xform.eM11 != xform.eM22 || xform.eM12 != 0 || xform.eM21 != 0) { + DEBUG_PRINT((pdev, 0, "%s: complex\n", __FUNCTION__)); + goto err2; + } + + q_line_attr->join_style = (UINT8)line_attr->iJoin; + q_line_attr->end_style = (UINT8)line_attr->iEndCap; + + FLOATOBJ_SetLong(&float_obj, 1); + q_line_attr->miter_limit = FlotaToFixed(line_attr->eMiterLimit, + FLOATOBJ_GetFloat(&float_obj)); + q_line_attr->width = FlotaToFixed(line_attr->elWidth.e, xform.eM11); + + if (line_attr->fl & LA_STYLED) { + FIX *style; + FIX *end; + PFLOAT_LONG src_style = line_attr->pstyle; + UINT32 nseg; + + ASSERT(pdev, LA_STYLED == LINE_STYLED); + ASSERT(pdev, LA_STARTGAP == LINE_START_WITH_GAP); + q_line_attr->flags = (UINT8)(line_attr->fl & (LA_STYLED | LA_STARTGAP)); + nseg = (line_attr->fl & LA_ALTERNATE) ? 2 : line_attr->cstyle; + + if ( nseg > 100) { + goto err2; + } + + if (!(style = (FIX *)QXLGetBuf(pdev, drawable, &q_line_attr->style, + nseg * sizeof(UINT32)))) { + goto err2; + } + + if ((line_attr->fl & LA_ALTERNATE)) { + style[0] = style[1] = FlotaToFixed(FLOATOBJ_GetFloat(&float_obj), xform.eM11); + } else { + for ( end = style + nseg; style < end; style++, src_style++) { + *style = FlotaToFixed(src_style->e, xform.eM11); + } + } + q_line_attr->style_nseg = (UINT8)nseg; + } else { + q_line_attr->flags = 0; + drawable->u.stroke.attr.style_nseg = 0; + drawable->u.stroke.attr.style = 0; + } + EngRestoreFloatingPointState(fpu_buf); + EngFreeMem(fpu_buf); + return TRUE; + +err2: + EngRestoreFloatingPointState(fpu_buf); +err1: + EngFreeMem(fpu_buf); + return FALSE; +} + +static BOOL GetCosmeticAttr(PDev *pdev, QXLDrawable *drawable, LineAttr *q_line_attr, + LINEATTRS *line_attr) +{ + ASSERT(pdev, LINE_CAP_ROUND == ENDCAP_ROUND && LINE_CAP_SQUARE == ENDCAP_SQUARE && + LINE_CAP_BUTT == ENDCAP_BUTT && LINE_JOIN_ROUND == JOIN_ROUND && + LINE_JOIN_BEVEL == JOIN_BEVEL && LINE_JOIN_MITER == JOIN_MITER); + + q_line_attr->join_style = JOIN_MITER; + q_line_attr->end_style = ENDCAP_BUTT; + q_line_attr->width = 1 << 4; + q_line_attr->miter_limit = 0; + + if (line_attr->fl & LA_STYLED) { + PFLOAT_LONG src_style = line_attr->pstyle; + FIX *style; + FIX *end; + UINT32 nseg; + + ASSERT(pdev, LA_STYLED == LINE_STYLED); + ASSERT(pdev, LA_STARTGAP == LINE_START_WITH_GAP); + q_line_attr->flags = (UINT8)(line_attr->fl & (LA_STYLED | LA_STARTGAP)); + nseg = (line_attr->fl & LA_ALTERNATE) ? 2 : line_attr->cstyle; + if ( nseg > 100) { + return FALSE; + } + + if (!(style = (FIX *)QXLGetBuf(pdev, drawable, &q_line_attr->style, + nseg * sizeof(UINT32)))) { + return FALSE; + } + + if (line_attr->fl & LA_ALTERNATE) { + style[0] = style[1] = 1 << 4; + } else { + for ( end = style + nseg; style < end; style++, src_style++) { + *style = (*src_style).l << 4; + } + } + q_line_attr->style_nseg = (UINT8)nseg; + } else { + q_line_attr->flags = 0; + q_line_attr->style_nseg = 0; + q_line_attr->style = 0; + } + return TRUE; +} + +BOOL APIENTRY DrvStrokePath(SURFOBJ *surf, PATHOBJ *path, CLIPOBJ *clip, XFORMOBJ *width_transform, + BRUSHOBJ *brush, POINTL *brush_pos, LINEATTRS *line_attr, + MIX mix /*rop*/) +{ + QXLDrawable *drawable; + RECTFX fx_area; + RECTL area; + PDev *pdev; + ROP3Info *fore_rop; + ROP3Info *back_rop; + BOOL h_or_v_line; + + if (!(pdev = (PDev *)surf->dhpdev)) { + DEBUG_PRINT((NULL, 0, "%s: err no pdev\n", __FUNCTION__)); + return TRUE; + } + + CountCall(pdev, CALL_COUNTER_STROKE_PATH); + + DEBUG_PRINT((pdev, 3, "%s\n", __FUNCTION__)); + ASSERT(pdev, surf && path && line_attr && clip); + + + if (line_attr->fl & (LA_STYLED | LA_ALTERNATE | LA_GEOMETRIC)) { //for now + // punt back to GDI result in infinite recursion + //return EngStrokePath(surf, path, clip, width_transform, brush, brush_pos, line_attr, mix); + } + + if (line_attr->elWidth.l == 0 && (line_attr->fl & LA_GEOMETRIC)) { + DEBUG_PRINT((pdev, 1, "%s: width == 0\n", __FUNCTION__)); + return TRUE; + } + + PATHOBJ_vGetBounds(path, &fx_area); + FXToRect(&area, &fx_area); + + h_or_v_line = area.bottom == area.top + 1 || area.right == area.left + 1; + + if (clip) { + if (clip->iDComplexity == DC_TRIVIAL) { + clip = NULL; + } else { + SectRect(&clip->rclBounds, &area, &area); + if (IsEmptyRect(&area)) { + DEBUG_PRINT((pdev, 1, "%s: empty rect after clip\n", __FUNCTION__)); + return TRUE; + } + } + } + + if (!(drawable = Drawable(pdev, QXL_DRAW_STROKE, &area, clip))) { + return FALSE; + } + + fore_rop = &rops2[(mix - 1) & 0x0f]; + back_rop = &rops2[((mix >> 8) - 1) & 0x0f]; + + if (!((fore_rop->flags | back_rop->flags) & ROP3_BRUSH)) { + drawable->u.stroke.brush.type = BRUSH_TYPE_NONE; + } else if (!QXLGetBrush(pdev, drawable, &drawable->u.stroke.brush, brush, brush_pos)) { + goto err; + } + + if (!QXLGetPath(pdev, drawable, &drawable->u.stroke.path, path)) { + goto err; + } + // DrvStrokePath only draws foreground pixels, unless you support dotted + // lines, so you only care about the low-order byte. + drawable->u.stroke.fore_mode = fore_rop->method_data; + drawable->u.stroke.back_mode = back_rop->method_data; + + drawable->effect = (h_or_v_line) ? QXL_EFFECT_OPAQUE: QXL_EFFECT_BLEND; + + if (((line_attr->fl & LA_GEOMETRIC) ? + !GetGeometricAttr(pdev, drawable, &drawable->u.stroke.attr, line_attr, width_transform) : + !GetCosmeticAttr(pdev, drawable, &drawable->u.stroke.attr, line_attr))){ + goto err; + } + + if (drawable->u.stroke.attr.flags & LA_STYLED) { + drawable->effect = (fore_rop->effect == back_rop->effect) ? fore_rop->effect : + QXL_EFFECT_BLEND; + } else { + drawable->effect = fore_rop->effect; + } + + if (drawable->effect == QXL_EFFECT_OPAQUE && !h_or_v_line) { + drawable->effect = QXL_EFFECT_OPAQUE_BRUSH; + } + + PushDrawable(pdev, drawable); + DEBUG_PRINT((pdev, 4, "%s: done\n", __FUNCTION__)); + return TRUE; + +err: + ReleaseOutput(pdev, drawable->release_info.id); + return FALSE; +} + +#ifdef CALL_TEST + +void CountCall(PDev *pdev, int counter) +{ + if (pdev->count_calls) { + int i; + + pdev->call_counters[counter]++; + if((++pdev->total_calls % 500) == 0) { + DEBUG_PRINT((pdev, 0, "total eng calls is %u\n", pdev->total_calls)); + for (i = 0; i < NUM_CALL_COUNTERS; i++) { + DEBUG_PRINT((pdev, 0, "%s count is %u\n", + counters_info[i].name, pdev->call_counters[i])); + } + } + pdev->count_calls = FALSE; + } else if (counters_info[counter].effective) { + pdev->count_calls = TRUE; + } +} + +BOOL APIENTRY DrvFillPath( + SURFOBJ *pso, + PATHOBJ *ppo, + CLIPOBJ *pco, + BRUSHOBJ *pbo, + POINTL *pptlBrushOrg, + MIX mix, + FLONG flOptions) +{ + PDev *pdev; + + pdev = (PDev *)pso->dhpdev; + CountCall(pdev, CALL_COUNTER_FILL_PATH); + + return EngFillPath(pso, ppo, pco, pbo, pptlBrushOrg, mix, flOptions); +} + +BOOL APIENTRY DrvGradientFill( + SURFOBJ *psoDest, + CLIPOBJ *pco, + XLATEOBJ *pxlo, + TRIVERTEX *pVertex, + ULONG nVertex, + PVOID pMesh, + ULONG nMesh, + RECTL *prclExtents, + POINTL *pptlDitherOrg, + ULONG ulMode) +{ + PDev *pdev; + + pdev = (PDev *)psoDest->dhpdev; + CountCall(pdev, CALL_COUNTER_GRADIENT_FILL); + return EngGradientFill(psoDest, pco, pxlo, pVertex, nVertex, pMesh, nMesh, prclExtents, + pptlDitherOrg, ulMode); +} + +BOOL APIENTRY DrvLineTo( + SURFOBJ *pso, + CLIPOBJ *pco, + BRUSHOBJ *pbo, + LONG x1, + LONG y1, + LONG x2, + LONG y2, + RECTL *prclBounds, + MIX mix) +{ + PDev *pdev; + + pdev = (PDev *)pso->dhpdev; + CountCall(pdev, CALL_COUNTER_LINE_TO); + return EngLineTo(pso, pco, pbo, x1, y1, x2, y2, prclBounds, mix); +} + +BOOL APIENTRY DrvPlgBlt( + SURFOBJ *psoTrg, + SURFOBJ *psoSrc, + SURFOBJ *psoMsk, + CLIPOBJ *pco, + XLATEOBJ *pxlo, + COLORADJUSTMENT *pca, + POINTL *pptlBrushOrg, + POINTFIX *pptfx, + RECTL *prcl, + POINTL *pptl, + ULONG iMode) +{ + if (psoSrc->iType == STYPE_BITMAP) { + PDev *pdev; + + ASSERT(NULL, psoTrg && psoTrg->iType != STYPE_BITMAP && psoTrg->dhpdev); + pdev = (PDev *)psoTrg->dhpdev; + CountCall(pdev, CALL_COUNTER_PLG_BLT); + } + return EngPlgBlt(psoTrg, psoSrc, psoMsk, pco, pxlo, pca, pptlBrushOrg, pptfx, prcl, pptl, + iMode); +} + +BOOL APIENTRY DrvStrokeAndFillPath( + SURFOBJ *pso, + PATHOBJ *ppo, + CLIPOBJ *pco, + XFORMOBJ *pxo, + BRUSHOBJ *pboStroke, + LINEATTRS *plineattrs, + BRUSHOBJ *pboFill, + POINTL *pptlBrushOrg, + MIX mixFill, + FLONG flOptions) +{ + PDev *pdev = (PDev *)pso->dhpdev; + CountCall(pdev, CALL_COUNTER_STROKE_AND_FILL_PATH); + return EngStrokeAndFillPath(pso, ppo, pco, pxo, pboStroke, plineattrs, pboFill, pptlBrushOrg, + mixFill, flOptions); +} + +#endif + diff --git a/display/driver.rc b/display/driver.rc new file mode 100644 index 0000000..1e3821a --- /dev/null +++ b/display/driver.rc @@ -0,0 +1,29 @@ +#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DRV
+#define VER_FILESUBTYPE VFT2_DRV_DISPLAY
+
+#undef VER_COMPANYNAME_STR
+#undef VER_FILEVERSION_STR
+#undef VER_LEGALCOPYRIGHT_STR
+#undef VER_LEGALCOPYRIGHT_YEARS
+#undef VER_PRODUCTNAME_STR
+#undef VER_PRODUCTVERSION_STR
+
+
+#define VER_FILEDESCRIPTION_STR "Red Hat QXL Display Driver"
+#define VER_INTERNALNAME_STR "qxldd.dll"
+#define VER_ORIGINALFILENAME_STR VER_INTERNALNAME_STR
+#define VER_FILEVERSION_STR "1.1.0.0"
+#define VER_PRODUCTNAME_STR "Spice"
+#define VER_PRODUCTVERSION_STR VER_FILEVERSION_STR
+
+#undef VER_PRODUCTVERSION
+#define VER_PRODUCTVERSION 1,1,0,0
+
+#define VER_COMPANYNAME_STR "Red Hat Inc."
+#define VER_LEGALCOPYRIGHT_STR "© Red Hat Inc. All rights reserved."
+
+#include "common.ver"
\ No newline at end of file diff --git a/display/lookup3.c b/display/lookup3.c new file mode 100644 index 0000000..cef7b84 --- /dev/null +++ b/display/lookup3.c @@ -0,0 +1,18 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "..\common\lookup3.c" diff --git a/display/makefile b/display/makefile new file mode 100644 index 0000000..f719f40 --- /dev/null +++ b/display/makefile @@ -0,0 +1,2 @@ +!INCLUDE $(NTMAKEENV)\makefile.def
+
diff --git a/display/mspace.c b/display/mspace.c new file mode 100644 index 0000000..ec29fc6 --- /dev/null +++ b/display/mspace.c @@ -0,0 +1,2435 @@ +// based on dlmalloc from Doug Lea + + +// quote from the Doug Lea original file + /* + This is a version (aka dlmalloc) of malloc/free/realloc written by + Doug Lea and released to the public domain, as explained at + http://creativecommons.org/licenses/publicdomain. Send questions, + comments, complaints, performance data, etc to dl@cs.oswego.edu + + * Version 2.8.3 Thu Sep 22 11:16:15 2005 Doug Lea (dl at gee) + + Note: There may be an updated version of this malloc obtainable at + ftp://gee.cs.oswego.edu/pub/misc/malloc.c + Check before installing! + */ + + +#include <ntddk.h> + +#include "mspace.h" + +#pragma warning( disable : 4146 ) /* no "unsigned" warnings */ + +#define MALLOC_ALIGNMENT ((size_t)8U) +#define USE_LOCKS 0 +#define malloc_getpagesize ((size_t)4096U) +#define DEFAULT_GRANULARITY malloc_getpagesize +#define MAX_SIZE_T (~(size_t)0) +#define MALLOC_FAILURE_ACTION +#define MALLINFO_FIELD_TYPE size_t +#define FOOTERS 0 +#define INSECURE 0 +#define PROCEED_ON_ERROR 0 +#define DEBUG 0 +#define ABORT_ON_ASSERT_FAILURE 1 +#define ABORT(user_data) abort_func(user_data) +#define USE_BUILTIN_FFS 0 +#define USE_DEV_RANDOM 0 +#define PRINT(params) print_func params + + +#define MEMCPY(dest, src, n) RtlCopyMemory(dest, src, n) +#define MEMCLEAR(dest, n) RtlZeroMemory(dest, n) + + +#define M_GRANULARITY (-1) + +void default_abort_func(void *user_data) +{ + for (;;); +} + +void default_print_func(void *user_data, char *format, ...) +{ +} + +static mspace_abort_t abort_func = default_abort_func; +static mspace_print_t print_func = default_print_func; + +void mspace_set_abort_func(mspace_abort_t f) +{ + abort_func = f; +} + +void mspace_set_print_func(mspace_print_t f) +{ + print_func = f; +} + +/* ------------------------ Mallinfo declarations ------------------------ */ + +#if !NO_MALLINFO +/* + This version of malloc supports the standard SVID/XPG mallinfo + routine that returns a struct containing usage properties and + statistics. It should work on any system that has a + /usr/include/malloc.h defining struct mallinfo. The main + declaration needed is the mallinfo struct that is returned (by-copy) + by mallinfo(). The malloinfo struct contains a bunch of fields that + are not even meaningful in this version of malloc. These fields are + are instead filled by mallinfo() with other numbers that might be of + interest. + + HAVE_USR_INCLUDE_MALLOC_H should be set if you have a + /usr/include/malloc.h file that includes a declaration of struct + mallinfo. If so, it is included; else a compliant version is + declared below. These must be precisely the same for mallinfo() to + work. The original SVID version of this struct, defined on most + systems with mallinfo, declares all fields as ints. But some others + define as unsigned long. If your system defines the fields using a + type of different width than listed here, you MUST #include your + system version and #define HAVE_USR_INCLUDE_MALLOC_H. +*/ + +/* #define HAVE_USR_INCLUDE_MALLOC_H */ + + +struct mallinfo { + MALLINFO_FIELD_TYPE arena; /* non-mmapped space allocated from system */ + MALLINFO_FIELD_TYPE ordblks; /* number of free chunks */ + MALLINFO_FIELD_TYPE smblks; /* always 0 */ + MALLINFO_FIELD_TYPE hblks; /* always 0 */ + MALLINFO_FIELD_TYPE hblkhd; /* space in mmapped regions */ + MALLINFO_FIELD_TYPE usmblks; /* maximum total allocated space */ + MALLINFO_FIELD_TYPE fsmblks; /* always 0 */ + MALLINFO_FIELD_TYPE uordblks; /* total allocated space */ + MALLINFO_FIELD_TYPE fordblks; /* total free space */ + MALLINFO_FIELD_TYPE keepcost; /* releasable (via malloc_trim) space */ +}; + +#endif /* NO_MALLINFO */ + + + +#ifdef DEBUG +#if ABORT_ON_ASSERT_FAILURE +#define assert(user_data, x) if(!(x)) ABORT(user_data) +#else /* ABORT_ON_ASSERT_FAILURE */ +#include <assert.h> +#endif /* ABORT_ON_ASSERT_FAILURE */ +#else /* DEBUG */ +#define assert(user_data, x) +#endif /* DEBUG */ + +/* ------------------- size_t and alignment properties -------------------- */ + +/* The byte and bit size of a size_t */ +#define SIZE_T_SIZE (sizeof(size_t)) +#define SIZE_T_BITSIZE (sizeof(size_t) << 3) + +/* Some constants coerced to size_t */ +/* Annoying but necessary to avoid errors on some plaftorms */ +#define SIZE_T_ZERO ((size_t)0) +#define SIZE_T_ONE ((size_t)1) +#define SIZE_T_TWO ((size_t)2) +#define TWO_SIZE_T_SIZES (SIZE_T_SIZE<<1) +#define FOUR_SIZE_T_SIZES (SIZE_T_SIZE<<2) +#define SIX_SIZE_T_SIZES (FOUR_SIZE_T_SIZES+TWO_SIZE_T_SIZES) +#define HALF_MAX_SIZE_T (MAX_SIZE_T / 2U) + +/* The bit mask value corresponding to MALLOC_ALIGNMENT */ +#define CHUNK_ALIGN_MASK (MALLOC_ALIGNMENT - SIZE_T_ONE) + +/* True if address a has acceptable alignment */ +#define is_aligned(A) (((size_t)((A)) & (CHUNK_ALIGN_MASK)) == 0) + +/* the number of bytes to offset an address to align it */ +#define align_offset(A)\ + ((((size_t)(A) & CHUNK_ALIGN_MASK) == 0)? 0 :\ + ((MALLOC_ALIGNMENT - ((size_t)(A) & CHUNK_ALIGN_MASK)) & CHUNK_ALIGN_MASK)) + +/* --------------------------- Lock preliminaries ------------------------ */ + +#if USE_LOCKS + +/* + When locks are defined, there are up to two global locks: + + * If HAVE_MORECORE, morecore_mutex protects sequences of calls to + MORECORE. In many cases sys_alloc requires two calls, that should + not be interleaved with calls by other threads. This does not + protect against direct calls to MORECORE by other threads not + using this lock, so there is still code to cope the best we can on + interference. + + * magic_init_mutex ensures that mparams.magic and other + unique mparams values are initialized only once. +*/ + + +#define USE_LOCK_BIT (2U) +#else /* USE_LOCKS */ +#define USE_LOCK_BIT (0U) +#define INITIAL_LOCK(l) +#endif /* USE_LOCKS */ + +#if USE_LOCKS +#define ACQUIRE_MAGIC_INIT_LOCK() ACQUIRE_LOCK(&magic_init_mutex); +#define RELEASE_MAGIC_INIT_LOCK() RELEASE_LOCK(&magic_init_mutex); +#else /* USE_LOCKS */ +#define ACQUIRE_MAGIC_INIT_LOCK() +#define RELEASE_MAGIC_INIT_LOCK() +#endif /* USE_LOCKS */ + + + +/* ----------------------- Chunk representations ------------------------ */ + +/* + (The following includes lightly edited explanations by Colin Plumb.) + + The malloc_chunk declaration below is misleading (but accurate and + necessary). It declares a "view" into memory allowing access to + necessary fields at known offsets from a given base. + + Chunks of memory are maintained using a `boundary tag' method as + originally described by Knuth. (See the paper by Paul Wilson + ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a survey of such + techniques.) Sizes of free chunks are stored both in the front of + each chunk and at the end. This makes consolidating fragmented + chunks into bigger chunks fast. The head fields also hold bits + representing whether chunks are free or in use. + + Here are some pictures to make it clearer. They are "exploded" to + show that the state of a chunk can be thought of as extending from + the high 31 bits of the head field of its header through the + prev_foot and PINUSE_BIT bit of the following chunk header. + + A chunk that's in use looks like: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk (if P = 1) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| + | Size of this chunk 1| +-+ + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + +- -+ + | | + +- -+ + | : + +- size - sizeof(size_t) available payload bytes -+ + : | + chunk-> +- -+ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |1| + | Size of next chunk (may or may not be in use) | +-+ + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + And if it's free, it looks like this: + + chunk-> +- -+ + | User payload (must be in use, or we would have merged!) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |P| + | Size of this chunk 0| +-+ + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Next pointer | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Prev pointer | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | : + +- size - sizeof(struct chunk) unused bytes -+ + : | + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of this chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0| + | Size of next chunk (must be in use, or we would have merged)| +-+ + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | : + +- User payload -+ + : | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |0| + +-+ + Note that since we always merge adjacent free chunks, the chunks + adjacent to a free chunk must be in use. + + Given a pointer to a chunk (which can be derived trivially from the + payload pointer) we can, in O(1) time, find out whether the adjacent + chunks are free, and if so, unlink them from the lists that they + are on and merge them with the current chunk. + + Chunks always begin on even word boundaries, so the mem portion + (which is returned to the user) is also on an even word boundary, and + thus at least double-word aligned. + + The P (PINUSE_BIT) bit, stored in the unused low-order bit of the + chunk size (which is always a multiple of two words), is an in-use + bit for the *previous* chunk. If that bit is *clear*, then the + word before the current chunk size contains the previous chunk + size, and can be used to find the front of the previous chunk. + The very first chunk allocated always has this bit set, preventing + access to non-existent (or non-owned) memory. If pinuse is set for + any given chunk, then you CANNOT determine the size of the + previous chunk, and might even get a memory addressing fault when + trying to do so. + + The C (CINUSE_BIT) bit, stored in the unused second-lowest bit of + the chunk size redundantly records whether the current chunk is + inuse. This redundancy enables usage checks within free and realloc, + and reduces indirection when freeing and consolidating chunks. + + Each freshly allocated chunk must have both cinuse and pinuse set. + That is, each allocated chunk borders either a previously allocated + and still in-use chunk, or the base of its memory arena. This is + ensured by making all allocations from the the `lowest' part of any + found chunk. Further, no free chunk physically borders another one, + so each free chunk is known to be preceded and followed by either + inuse chunks or the ends of memory. + + Note that the `foot' of the current chunk is actually represented + as the prev_foot of the NEXT chunk. This makes it easier to + deal with alignments etc but can be very confusing when trying + to extend or adapt this code. + + The exceptions to all this are + + 1. The special chunk `top' is the top-most available chunk (i.e., + the one bordering the end of available memory). It is treated + specially. Top is never included in any bin, is used only if + no other chunk is available, and is released back to the + system if it is very large (see M_TRIM_THRESHOLD). In effect, + the top chunk is treated as larger (and thus less well + fitting) than any other available chunk. The top chunk + doesn't update its trailing size field since there is no next + contiguous chunk that would have to index off it. However, + space is still allocated for it (TOP_FOOT_SIZE) to enable + separation or merging when space is extended. + + 3. Chunks allocated via mmap, which have the lowest-order bit + (IS_MMAPPED_BIT) set in their prev_foot fields, and do not set + PINUSE_BIT in their head fields. Because they are allocated + one-by-one, each must carry its own prev_foot field, which is + also used to hold the offset this chunk has within its mmapped + region, which is needed to preserve alignment. Each mmapped + chunk is trailed by the first two fields of a fake next-chunk + for sake of usage checks. + +*/ + +struct malloc_chunk { + size_t prev_foot; /* Size of previous chunk (if free). */ + size_t head; /* Size and inuse bits. */ + struct malloc_chunk* fd; /* double links -- used only if free. */ + struct malloc_chunk* bk; +}; + +typedef struct malloc_chunk mchunk; +typedef struct malloc_chunk* mchunkptr; +typedef struct malloc_chunk* sbinptr; /* The type of bins of chunks */ +typedef unsigned int bindex_t; /* Described below */ +typedef unsigned int binmap_t; /* Described below */ +typedef unsigned int flag_t; /* The type of various bit flag sets */ + + +/* ------------------- Chunks sizes and alignments ----------------------- */ + +#define MCHUNK_SIZE (sizeof(mchunk)) + +#if FOOTERS +#define CHUNK_OVERHEAD (TWO_SIZE_T_SIZES) +#else /* FOOTERS */ +#define CHUNK_OVERHEAD (SIZE_T_SIZE) +#endif /* FOOTERS */ + +/* The smallest size we can malloc is an aligned minimal chunk */ +#define MIN_CHUNK_SIZE\ + ((MCHUNK_SIZE + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) + +/* conversion from malloc headers to user pointers, and back */ +#define chunk2mem(p) ((void*)((char*)(p) + TWO_SIZE_T_SIZES)) +#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - TWO_SIZE_T_SIZES)) +/* chunk associated with aligned address A */ +#define align_as_chunk(A) (mchunkptr)((A) + align_offset(chunk2mem(A))) + +/* Bounds on request (not chunk) sizes. */ +#define MAX_REQUEST ((-MIN_CHUNK_SIZE) << 2) +#define MIN_REQUEST (MIN_CHUNK_SIZE - CHUNK_OVERHEAD - SIZE_T_ONE) + +/* pad request bytes into a usable size */ +#define pad_request(req) \ + (((req) + CHUNK_OVERHEAD + CHUNK_ALIGN_MASK) & ~CHUNK_ALIGN_MASK) + +/* pad request, checking for minimum (but not maximum) */ +#define request2size(req) \ + (((req) < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(req)) + +/* ------------------ Operations on head and foot fields ----------------- */ + +/* + The head field of a chunk is or'ed with PINUSE_BIT when previous + adjacent chunk in use, and or'ed with CINUSE_BIT if this chunk is in + use. If the chunk was obtained with mmap, the prev_foot field has + IS_MMAPPED_BIT set, otherwise holding the offset of the base of the + mmapped region to the base of the chunk. +*/ + +#define PINUSE_BIT (SIZE_T_ONE) +#define CINUSE_BIT (SIZE_T_TWO) +#define INUSE_BITS (PINUSE_BIT|CINUSE_BIT) + +/* Head value for fenceposts */ +#define FENCEPOST_HEAD (INUSE_BITS|SIZE_T_SIZE) + +/* extraction of fields from head words */ +#define cinuse(p) ((p)->head & CINUSE_BIT) +#define pinuse(p) ((p)->head & PINUSE_BIT) +#define chunksize(p) ((p)->head & ~(INUSE_BITS)) + +#define clear_pinuse(p) ((p)->head &= ~PINUSE_BIT) +#define clear_cinuse(p) ((p)->head &= ~CINUSE_BIT) + +/* Treat space at ptr +/- offset as a chunk */ +#define chunk_plus_offset(p, s) ((mchunkptr)(((char*)(p)) + (s))) +#define chunk_minus_offset(p, s) ((mchunkptr)(((char*)(p)) - (s))) + +/* Ptr to next or previous physical malloc_chunk. */ +#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->head & ~INUSE_BITS))) +#define prev_chunk(p) ((mchunkptr)( ((char*)(p)) - ((p)->prev_foot) )) + +/* extract next chunk's pinuse bit */ +#define next_pinuse(p) ((next_chunk(p)->head) & PINUSE_BIT) + +/* Get/set size at footer */ +#define get_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot) +#define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_foot = (s)) + +/* Set size, pinuse bit, and foot */ +#define set_size_and_pinuse_of_free_chunk(p, s)\ + ((p)->head = (s|PINUSE_BIT), set_foot(p, s)) + +/* Set size, pinuse bit, foot, and clear next pinuse */ +#define set_free_with_pinuse(p, s, n)\ + (clear_pinuse(n), set_size_and_pinuse_of_free_chunk(p, s)) + +/* Get the internal overhead associated with chunk p */ +#define overhead_for(p) CHUNK_OVERHEAD + +/* Return true if malloced space is not necessarily cleared */ +#define calloc_must_clear(p) (1) + + +/* ---------------------- Overlaid data structures ----------------------- */ + +/* + When chunks are not in use, they are treated as nodes of either + lists or trees. + + "Small" chunks are stored in circular doubly-linked lists, and look + like this: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `head:' | Size of chunk, in bytes |P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Forward pointer to next chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Back pointer to previous chunk in list | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unused space (may be 0 bytes long) . + . . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `foot:' | Size of chunk, in bytes | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Larger chunks are kept in a form of bitwise digital trees (aka + tries) keyed on chunksizes. Because malloc_tree_chunks are only for + free chunks greater than 256 bytes, their size doesn't impose any + constraints on user chunk sizes. Each node looks like: + + chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Size of previous chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `head:' | Size of chunk, in bytes |P| + mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Forward pointer to next chunk of same size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Back pointer to previous chunk of same size | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer to left child (child[0]) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer to right child (child[1]) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Pointer to parent | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | bin index of this chunk | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Unused space . + . | +nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + `foot:' | Size of chunk, in bytes | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Each tree holding treenodes is a tree of unique chunk sizes. Chunks + of the same size are arranged in a circularly-linked list, with only + the oldest chunk (the next to be used, in our FIFO ordering) + actually in the tree. (Tree members are distinguished by a non-null + parent pointer.) If a chunk with the same size an an existing node + is inserted, it is linked off the existing node using pointers that + work in the same way as fd/bk pointers of small chunks. + + Each tree contains a power of 2 sized range of chunk sizes (the + smallest is 0x100 <= x < 0x180), which is is divided in half at each + tree level, with the chunks in the smaller half of the range (0x100 + <= x < 0x140 for the top nose) in the left subtree and the larger + half (0x140 <= x < 0x180) in the right subtree. This is, of course, + done by inspecting individual bits. + + Using these rules, each node's left subtree contains all smaller + sizes than its right subtree. However, the node at the root of each + subtree has no particular ordering relationship to either. (The + dividing line between the subtree sizes is based on trie relation.) + If we remove the last chunk of a given size from the interior of the + tree, we need to replace it with a leaf node. The tree ordering + rules permit a node to be replaced by any leaf below it. + + The smallest chunk in a tree (a common operation in a best-fit + allocator) can be found by walking a path to the leftmost leaf in + the tree. Unlike a usual binary tree, where we follow left child + pointers until we reach a null, here we follow the right child + pointer any time the left one is null, until we reach a leaf with + both child pointers null. The smallest chunk in the tree will be + somewhere along that path. + + The worst case number of steps to add, find, or remove a node is + bounded by the number of bits differentiating chunks within + bins. Under current bin calculations, this ranges from 6 up to 21 + (for 32 bit sizes) or up to 53 (for 64 bit sizes). The typical case + is of course much better. +*/ + +struct malloc_tree_chunk { + /* The first four fields must be compatible with malloc_chunk */ + size_t prev_foot; + size_t head; + struct malloc_tree_chunk* fd; + struct malloc_tree_chunk* bk; + + struct malloc_tree_chunk* child[2]; + struct malloc_tree_chunk* parent; + bindex_t index; +}; + +typedef struct malloc_tree_chunk tchunk; +typedef struct malloc_tree_chunk* tchunkptr; +typedef struct malloc_tree_chunk* tbinptr; /* The type of bins of trees */ + +/* A little helper macro for trees */ +#define leftmost_child(t) ((t)->child[0] != 0? (t)->child[0] : (t)->child[1]) + +/* ----------------------------- Segments -------------------------------- */ + +/* + Each malloc space may include non-contiguous segments, held in a + list headed by an embedded malloc_segment record representing the + top-most space. Segments also include flags holding properties of + the space. Large chunks that are directly allocated by mmap are not + included in this list. They are instead independently created and + destroyed without otherwise keeping track of them. + + Segment management mainly comes into play for spaces allocated by + MMAP. Any call to MMAP might or might not return memory that is + adjacent to an existing segment. MORECORE normally contiguously + extends the current space, so this space is almost always adjacent, + which is simpler and faster to deal with. (This is why MORECORE is + used preferentially to MMAP when both are available -- see + sys_alloc.) When allocating using MMAP, we don't use any of the + hinting mechanisms (inconsistently) supported in various + implementations of unix mmap, or distinguish reserving from + committing memory. Instead, we just ask for space, and exploit + contiguity when we get it. It is probably possible to do + better than this on some systems, but no general scheme seems + to be significantly better. + + Management entails a simpler variant of the consolidation scheme + used for chunks to reduce fragmentation -- new adjacent memory is + normally prepended or appended to an existing segment. However, + there are limitations compared to chunk consolidation that mostly + reflect the fact that segment processing is relatively infrequent + (occurring only when getting memory from system) and that we + don't expect to have huge numbers of segments: + + * Segments are not indexed, so traversal requires linear scans. (It + would be possible to index these, but is not worth the extra + overhead and complexity for most programs on most platforms.) + * New segments are only appended to old ones when holding top-most + memory; if they cannot be prepended to others, they are held in + different segments. + + Except for the top-most segment of an mstate, each segment record + is kept at the tail of its segment. Segments are added by pushing + segment records onto the list headed by &mstate.seg for the + containing mstate. + + Segment flags control allocation/merge/deallocation policies: + * If EXTERN_BIT set, then we did not allocate this segment, + and so should not try to deallocate or merge with others. + (This currently holds only for the initial segment passed + into create_mspace_with_base.) + * If IS_MMAPPED_BIT set, the segment may be merged with + other surrounding mmapped segments and trimmed/de-allocated + using munmap. + * If neither bit is set, then the segment was obtained using + MORECORE so can be merged with surrounding MORECORE'd segments + and deallocated/trimmed using MORECORE with negative arguments. +*/ + +struct malloc_segment { + char* base; /* base address */ + size_t size; /* allocated size */ + struct malloc_segment* next; /* ptr to next segment */ +}; + +typedef struct malloc_segment msegment; +typedef struct malloc_segment* msegmentptr; + +/* ---------------------------- malloc_state ----------------------------- */ + +/* + A malloc_state holds all of the bookkeeping for a space. + The main fields are: + + Top + The topmost chunk of the currently active segment. Its size is + cached in topsize. The actual size of topmost space is + topsize+TOP_FOOT_SIZE, which includes space reserved for adding + fenceposts and segment records if necessary when getting more + space from the system. The size at which to autotrim top is + cached from mparams in trim_check, except that it is disabled if + an autotrim fails. + + Designated victim (dv) + This is the preferred chunk for servicing small requests that + don't have exact fits. It is normally the chunk split off most + recently to service another small request. Its size is cached in + dvsize. The link fields of this chunk are not maintained since it + is not kept in a bin. + + SmallBins + An array of bin headers for free chunks. These bins hold chunks + with sizes less than MIN_LARGE_SIZE bytes. Each bin contains + chunks of all the same size, spaced 8 bytes apart. To simplify + use in double-linked lists, each bin header acts as a malloc_chunk + pointing to the real first node, if it exists (else pointing to + itself). This avoids special-casing for headers. But to avoid + waste, we allocate only the fd/bk pointers of bins, and then use + repositioning tricks to treat these as the fields of a chunk. + + TreeBins + Treebins are pointers to the roots of trees holding a range of + sizes. There are 2 equally spaced treebins for each power of two + from TREE_SHIFT to TREE_SHIFT+16. The last bin holds anything + larger. + + Bin maps + There is one bit map for small bins ("smallmap") and one for + treebins ("treemap). Each bin sets its bit when non-empty, and + clears the bit when empty. Bit operations are then used to avoid + bin-by-bin searching -- nearly all "search" is done without ever + looking at bins that won't be selected. The bit maps + conservatively use 32 bits per map word, even if on 64bit system. + For a good description of some of the bit-based techniques used + here, see Henry S. Warren Jr's book "Hacker's Delight" (and + supplement at http://hackersdelight.org/). Many of these are + intended to reduce the branchiness of paths through malloc etc, as + well as to reduce the number of memory locations read or written. + + Segments + A list of segments headed by an embedded malloc_segment record + representing the initial space. + + Address check support + The least_addr field is the least address ever obtained from + MORECORE or MMAP. Attempted frees and reallocs of any address less + than this are trapped (unless INSECURE is defined). + + Magic tag + A cross-check field that should always hold same value as mparams.magic. + + Flags + Bits recording whether to use MMAP, locks, or contiguous MORECORE + + Statistics + Each space keeps track of current and maximum system memory + obtained via MORECORE or MMAP. + + Locking + If USE_LOCKS is defined, the "mutex" lock is acquired and released + around every public call using this mspace. +*/ + +/* Bin types, widths and sizes */ +#define NSMALLBINS (32U) +#define NTREEBINS (32U) +#define SMALLBIN_SHIFT (3U) +#define SMALLBIN_WIDTH (SIZE_T_ONE << SMALLBIN_SHIFT) +#define TREEBIN_SHIFT (8U) +#define MIN_LARGE_SIZE (SIZE_T_ONE << TREEBIN_SHIFT) +#define MAX_SMALL_SIZE (MIN_LARGE_SIZE - SIZE_T_ONE) +#define MAX_SMALL_REQUEST (MAX_SMALL_SIZE - CHUNK_ALIGN_MASK - CHUNK_OVERHEAD) + +struct malloc_state { + binmap_t smallmap; + binmap_t treemap; + size_t dvsize; + size_t topsize; + char* least_addr; + mchunkptr dv; + mchunkptr top; + size_t magic; + mchunkptr smallbins[(NSMALLBINS+1)*2]; + tbinptr treebins[NTREEBINS]; + size_t footprint; + size_t max_footprint; + flag_t mflags; + void *user_data; +#if USE_LOCKS + MLOCK_T mutex; /* locate lock among fields that rarely change */ +#endif /* USE_LOCKS */ + msegment seg; +}; + +typedef struct malloc_state* mstate; + +/* ------------- Global malloc_state and malloc_params ------------------- */ + +/* + malloc_params holds global properties, including those that can be + dynamically set using mallopt. There is a single instance, mparams, + initialized in init_mparams. +*/ + +struct malloc_params { + size_t magic; + size_t page_size; + size_t granularity; + flag_t default_mflags; +}; + +static struct malloc_params mparams; + +/* The global malloc_state used for all non-"mspace" calls */ +//static struct malloc_state _gm_; +//#define gm (&_gm_) +//#define is_global(M) ((M) == &_gm_) +#define is_initialized(M) ((M)->top != 0) + +/* -------------------------- system alloc setup ------------------------- */ + +/* Operations on mflags */ + +#define use_lock(M) ((M)->mflags & USE_LOCK_BIT) +#define enable_lock(M) ((M)->mflags |= USE_LOCK_BIT) +#define disable_lock(M) ((M)->mflags &= ~USE_LOCK_BIT) + +#define set_lock(M,L)\ + ((M)->mflags = (L)?\ + ((M)->mflags | USE_LOCK_BIT) :\ + ((M)->mflags & ~USE_LOCK_BIT)) + +/* page-align a size */ +#define page_align(S)\ + (((S) + (mparams.page_size)) & ~(mparams.page_size - SIZE_T_ONE)) + +/* granularity-align a size */ +#define granularity_align(S)\ + (((S) + (mparams.granularity)) & ~(mparams.granularity - SIZE_T_ONE)) + +#define is_page_aligned(S)\ + (((size_t)(S) & (mparams.page_size - SIZE_T_ONE)) == 0) +#define is_granularity_aligned(S)\ + (((size_t)(S) & (mparams.granularity - SIZE_T_ONE)) == 0) + +/* True if segment S holds address A */ +#define segment_holds(S, A)\ + ((char*)(A) >= S->base && (char*)(A) < S->base + S->size) + +/* Return segment holding given address */ +static msegmentptr segment_holding(mstate m, char* addr) { + msegmentptr sp = &m->seg; + for (;;) { + if (addr >= sp->base && addr < sp->base + sp->size) + return sp; + if ((sp = sp->next) == 0) + return 0; + } +} + +/* Return true if segment contains a segment link */ +static int has_segment_link(mstate m, msegmentptr ss) { + msegmentptr sp = &m->seg; + for (;;) { + if ((char*)sp >= ss->base && (char*)sp < ss->base + ss->size) + return 1; + if ((sp = sp->next) == 0) + return 0; + } +} + + + +/* + TOP_FOOT_SIZE is padding at the end of a segment, including space + that may be needed to place segment records and fenceposts when new + noncontiguous segments are added. +*/ +#define TOP_FOOT_SIZE\ + (align_offset(chunk2mem(0))+pad_request(sizeof(struct malloc_segment))+MIN_CHUNK_SIZE) + + +/* ------------------------------- Hooks -------------------------------- */ + +/* + PREACTION should be defined to return 0 on success, and nonzero on + failure. If you are not using locking, you can redefine these to do + anything you like. +*/ + +#if USE_LOCKS + +/* Ensure locks are initialized */ +#define GLOBALLY_INITIALIZE() (mparams.page_size == 0 && init_mparams()) + +#define PREACTION(M) ((GLOBALLY_INITIALIZE() || use_lock(M))? ACQUIRE_LOCK(&(M)->mutex) : 0) +#define POSTACTION(M) { if (use_lock(M)) RELEASE_LOCK(&(M)->mutex); } +#else /* USE_LOCKS */ + +#ifndef PREACTION +#define PREACTION(M) (0) +#endif /* PREACTION */ + +#ifndef POSTACTION +#define POSTACTION(M) +#endif /* POSTACTION */ + +#endif /* USE_LOCKS */ + +/* + CORRUPTION_ERROR_ACTION is triggered upon detected bad addresses. + USAGE_ERROR_ACTION is triggered on detected bad frees and + reallocs. The argument p is an address that might have triggered the + fault. It is ignored by the two predefined actions, but might be + useful in custom actions that try to help diagnose errors. +*/ + +#if PROCEED_ON_ERROR + +/* A count of the number of corruption errors causing resets */ +int malloc_corruption_error_count; + +/* default corruption action */ +static void reset_on_error(mstate m); + +#define CORRUPTION_ERROR_ACTION(m) reset_on_error(m) +#define USAGE_ERROR_ACTION(m, p) + +#else /* PROCEED_ON_ERROR */ + +#ifndef CORRUPTION_ERROR_ACTION +#define CORRUPTION_ERROR_ACTION(m) ABORT(m->user_data) +#endif /* CORRUPTION_ERROR_ACTION */ + +#ifndef USAGE_ERROR_ACTION +#define USAGE_ERROR_ACTION(m,p) ABORT(m->user_data) +#endif /* USAGE_ERROR_ACTION */ + +#endif /* PROCEED_ON_ERROR */ + +/* -------------------------- Debugging setup ---------------------------- */ + +#if ! DEBUG + +#define check_free_chunk(M,P) +#define check_inuse_chunk(M,P) +#define check_malloced_chunk(M,P,N) +#define check_malloc_state(M) +#define check_top_chunk(M,P) + +#else /* DEBUG */ +#define check_free_chunk(M,P) do_check_free_chunk(M,P) +#define check_inuse_chunk(M,P) do_check_inuse_chunk(M,P) +#define check_top_chunk(M,P) do_check_top_chunk(M,P) +#define check_malloced_chunk(M,P,N) do_check_malloced_chunk(M,P,N) +#define check_malloc_state(M) do_check_malloc_state(M) + +static void do_check_any_chunk(mstate m, mchunkptr p); +static void do_check_top_chunk(mstate m, mchunkptr p); +static void do_check_inuse_chunk(mstate m, mchunkptr p); +static void do_check_free_chunk(mstate m, mchunkptr p); +static void do_check_malloced_chunk(mstate m, void* mem, size_t s); +static void do_check_tree(mstate m, tchunkptr t); +static void do_check_treebin(mstate m, bindex_t i); +static void do_check_smallbin(mstate m, bindex_t i); +static void do_check_malloc_state(mstate m); +static int bin_find(mstate m, mchunkptr x); +static size_t traverse_and_check(mstate m); +#endif /* DEBUG */ + +/* ---------------------------- Indexing Bins ---------------------------- */ + +#define is_small(s) (((s) >> SMALLBIN_SHIFT) < NSMALLBINS) +#define small_index(s) ((s) >> SMALLBIN_SHIFT) +#define small_index2size(i) ((i) << SMALLBIN_SHIFT) +#define MIN_SMALL_INDEX (small_index(MIN_CHUNK_SIZE)) + +/* addressing by index. See above about smallbin repositioning */ +#define smallbin_at(M, i) ((sbinptr)((char*)&((M)->smallbins[(i)<<1]))) +#define treebin_at(M,i) (&((M)->treebins[i])) + +/* assign tree index for size S to variable I */ +#if defined(__GNUC__) && defined(i386) +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int K;\ + __asm__("bsrl %1,%0\n\t" : "=r" (K) : "rm" (X));\ + I = (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1)));\ + }\ +} +#else /* GNUC */ +#define compute_tree_index(S, I)\ +{\ + size_t X = S >> TREEBIN_SHIFT;\ + if (X == 0)\ + I = 0;\ + else if (X > 0xFFFF)\ + I = NTREEBINS-1;\ + else {\ + unsigned int Y = (unsigned int)X;\ + unsigned int N = ((Y - 0x100) >> 16) & 8;\ + unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4;\ + N += K;\ + N += K = (((Y <<= K) - 0x4000) >> 16) & 2;\ + K = 14 - N + ((Y <<= K) >> 15);\ + I = (K << 1) + ((S >> (K + (TREEBIN_SHIFT-1)) & 1));\ + }\ +} +#endif /* GNUC */ + +/* Bit representing maximum resolved size in a treebin at i */ +#define bit_for_tree_index(i) \ + (i == NTREEBINS-1)? (SIZE_T_BITSIZE-1) : (((i) >> 1) + TREEBIN_SHIFT - 2) + +/* Shift placing maximum resolved bit in a treebin at i as sign bit */ +#define leftshift_for_tree_index(i) \ + ((i == NTREEBINS-1)? 0 : \ + ((SIZE_T_BITSIZE-SIZE_T_ONE) - (((i) >> 1) + TREEBIN_SHIFT - 2))) + +/* The size of the smallest chunk held in bin with index i */ +#define minsize_for_tree_index(i) \ + ((SIZE_T_ONE << (((i) >> 1) + TREEBIN_SHIFT)) | \ + (((size_t)((i) & SIZE_T_ONE)) << (((i) >> 1) + TREEBIN_SHIFT - 1))) + +/* ------------------------ Operations on bin maps ----------------------- */ + +/* bit corresponding to given index */ +#define idx2bit(i) ((binmap_t)(1) << (i)) + +/* Mark/Clear bits with given index */ +#define mark_smallmap(M,i) ((M)->smallmap |= idx2bit(i)) +#define clear_smallmap(M,i) ((M)->smallmap &= ~idx2bit(i)) +#define smallmap_is_marked(M,i) ((M)->smallmap & idx2bit(i)) + +#define mark_treemap(M,i) ((M)->treemap |= idx2bit(i)) +#define clear_treemap(M,i) ((M)->treemap &= ~idx2bit(i)) +#define treemap_is_marked(M,i) ((M)->treemap & idx2bit(i)) + +/* index corresponding to given bit */ + +#if defined(__GNUC__) && defined(i386) +#define compute_bit2idx(X, I)\ +{\ + unsigned int J;\ + __asm__("bsfl %1,%0\n\t" : "=r" (J) : "rm" (X));\ + I = (bindex_t)J;\ +} + +#else /* GNUC */ +#if USE_BUILTIN_FFS +#define compute_bit2idx(X, I) I = ffs(X)-1 + +#else /* USE_BUILTIN_FFS */ +#define compute_bit2idx(X, I)\ +{\ + unsigned int Y = X - 1;\ + unsigned int K = Y >> (16-4) & 16;\ + unsigned int N = K; Y >>= K;\ + N += K = Y >> (8-3) & 8; Y >>= K;\ + N += K = Y >> (4-2) & 4; Y >>= K;\ + N += K = Y >> (2-1) & 2; Y >>= K;\ + N += K = Y >> (1-0) & 1; Y >>= K;\ + I = (bindex_t)(N + Y);\ +} +#endif /* USE_BUILTIN_FFS */ +#endif /* GNUC */ + +/* isolate the least set bit of a bitmap */ +#define least_bit(x) ((x) & -(x)) + +/* mask with all bits to left of least bit of x on */ +#define left_bits(x) ((x<<1) | -(x<<1)) + +/* mask with all bits to left of or equal to least bit of x on */ +#define same_or_left_bits(x) ((x) | -(x)) + + +/* ----------------------- Runtime Check Support ------------------------- */ + +/* + For security, the main invariant is that malloc/free/etc never + writes to a static address other than malloc_state, unless static + malloc_state itself has been corrupted, which cannot occur via + malloc (because of these checks). In essence this means that we + believe all pointers, sizes, maps etc held in malloc_state, but + check all of those linked or offsetted from other embedded data + structures. These checks are interspersed with main code in a way + that tends to minimize their run-time cost. + + When FOOTERS is defined, in addition to range checking, we also + verify footer fields of inuse chunks, which can be used guarantee + that the mstate controlling malloc/free is intact. This is a + streamlined version of the approach described by William Robertson + et al in "Run-time Detection of Heap-based Overflows" LISA'03 + http://www.usenix.org/events/lisa03/tech/robertson.html The footer + of an inuse chunk holds the xor of its mstate and a random seed, + that is checked upon calls to free() and realloc(). This is + (probablistically) unguessable from outside the program, but can be + computed by any code successfully malloc'ing any chunk, so does not + itself provide protection against code that has already broken + security through some other means. Unlike Robertson et al, we + always dynamically check addresses of all offset chunks (previous, + next, etc). This turns out to be cheaper than relying on hashes. +*/ + +#if !INSECURE +/* Check if address a is at least as high as any from MORECORE or MMAP */ +#define ok_address(M, a) ((char*)(a) >= (M)->least_addr) +/* Check if address of next chunk n is higher than base chunk p */ +#define ok_next(p, n) ((char*)(p) < (char*)(n)) +/* Check if p has its cinuse bit on */ +#define ok_cinuse(p) cinuse(p) +/* Check if p has its pinuse bit on */ +#define ok_pinuse(p) pinuse(p) + +#else /* !INSECURE */ +#define ok_address(M, a) (1) +#define ok_next(b, n) (1) +#define ok_cinuse(p) (1) +#define ok_pinuse(p) (1) +#endif /* !INSECURE */ + +#if (FOOTERS && !INSECURE) +/* Check if (alleged) mstate m has expected magic field */ +#define ok_magic(M) ((M)->magic == mparams.magic) +#else /* (FOOTERS && !INSECURE) */ +#define ok_magic(M) (1) +#endif /* (FOOTERS && !INSECURE) */ + + +/* In gcc, use __builtin_expect to minimize impact of checks */ +#if !INSECURE +#if defined(__GNUC__) && __GNUC__ >= 3 +#define RTCHECK(e) __builtin_expect(e, 1) +#else /* GNUC */ +#define RTCHECK(e) (e) +#endif /* GNUC */ +#else /* !INSECURE */ +#define RTCHECK(e) (1) +#endif /* !INSECURE */ + +/* macros to set up inuse chunks with or without footers */ + +#if !FOOTERS + +#define mark_inuse_foot(M,p,s) + +/* Set cinuse bit and pinuse bit of next chunk */ +#define set_inuse(M,p,s)\ + ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ + ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) + +/* Set cinuse and pinuse of this chunk and pinuse of next chunk */ +#define set_inuse_and_pinuse(M,p,s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + ((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT) + +/* Set size, cinuse and pinuse bit of this chunk */ +#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT)) + +#else /* FOOTERS */ + +/* Set foot of inuse chunk to be xor of mstate and seed */ +#define mark_inuse_foot(M,p,s)\ + (((mchunkptr)((char*)(p) + (s)))->prev_foot = ((size_t)(M) ^ mparams.magic)) + +#define get_mstate_for(p)\ + ((mstate)(((mchunkptr)((char*)(p) +\ + (chunksize(p))))->prev_foot ^ mparams.magic)) + +#define set_inuse(M,p,s)\ + ((p)->head = (((p)->head & PINUSE_BIT)|s|CINUSE_BIT),\ + (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT), \ + mark_inuse_foot(M,p,s)) + +#define set_inuse_and_pinuse(M,p,s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + (((mchunkptr)(((char*)(p)) + (s)))->head |= PINUSE_BIT),\ + mark_inuse_foot(M,p,s)) + +#define set_size_and_pinuse_of_inuse_chunk(M, p, s)\ + ((p)->head = (s|PINUSE_BIT|CINUSE_BIT),\ + mark_inuse_foot(M, p, s)) + +#endif /* !FOOTERS */ + +/* ---------------------------- setting mparams -------------------------- */ + +/* Initialize mparams */ +static int init_mparams(void) { + if (mparams.page_size == 0) { + size_t s; + + mparams.default_mflags = USE_LOCK_BIT; + +#if (FOOTERS && !INSECURE) + { +#if USE_DEV_RANDOM + int fd; + unsigned char buf[sizeof(size_t)]; + /* Try to use /dev/urandom, else fall back on using time */ + if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 && + read(fd, buf, sizeof(buf)) == sizeof(buf)) { + s = *((size_t *) buf); + close(fd); + } + else +#endif /* USE_DEV_RANDOM */ + s = (size_t)(time(0) ^ (size_t)0x55555555U); + + s |= (size_t)8U; /* ensure nonzero */ + s &= ~(size_t)7U; /* improve chances of fault for bad values */ + + } +#else /* (FOOTERS && !INSECURE) */ + s = (size_t)0x58585858U; +#endif /* (FOOTERS && !INSECURE) */ + ACQUIRE_MAGIC_INIT_LOCK(); + if (mparams.magic == 0) { + mparams.magic = s; + /* Set up lock for main malloc area */ + //INITIAL_LOCK(&gm->mutex); + //gm->mflags = mparams.default_mflags; + } + RELEASE_MAGIC_INIT_LOCK(); + + + mparams.page_size = malloc_getpagesize; + mparams.granularity = ((DEFAULT_GRANULARITY != 0)? + DEFAULT_GRANULARITY : mparams.page_size); + + /* Sanity-check configuration: + size_t must be unsigned and as wide as pointer type. + ints must be at least 4 bytes. + alignment must be at least 8. + Alignment, min chunk size, and page size must all be powers of 2. + */ + if ((sizeof(size_t) != sizeof(char*)) || + (MAX_SIZE_T < MIN_CHUNK_SIZE) || + (sizeof(int) < 4) || + (MALLOC_ALIGNMENT < (size_t)8U) || + ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT-SIZE_T_ONE)) != 0) || + ((MCHUNK_SIZE & (MCHUNK_SIZE-SIZE_T_ONE)) != 0) || + ((mparams.granularity & (mparams.granularity-SIZE_T_ONE)) != 0) || + ((mparams.page_size & (mparams.page_size-SIZE_T_ONE)) != 0)) + ABORT(NULL); + } + return 0; +} + +/* support for mallopt */ +static int change_mparam(int param_number, int value) { + size_t val = (size_t)value; + init_mparams(); + switch(param_number) { + case M_GRANULARITY: + if (val >= mparams.page_size && ((val & (val-1)) == 0)) { + mparams.granularity = val; + return 1; + } + else + return 0; + default: + return 0; + } +} + +#if DEBUG +/* ------------------------- Debugging Support --------------------------- */ + +/* Check properties of any chunk, whether free, inuse, mmapped etc */ +static void do_check_any_chunk(mstate m, mchunkptr p) { + assert(m->user_data, (is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); + assert(m->user_data, ok_address(m, p)); +} + +/* Check properties of top chunk */ +static void do_check_top_chunk(mstate m, mchunkptr p) { + msegmentptr sp = segment_holding(m, (char*)p); + size_t sz = chunksize(p); + assert(m->user_data, sp != 0); + assert(m->user_data, (is_aligned(chunk2mem(p))) || (p->head == FENCEPOST_HEAD)); + assert(m->user_data, ok_address(m, p)); + assert(m->user_data, sz == m->topsize); + assert(m->user_data, sz > 0); + assert(m->user_data, sz == ((sp->base + sp->size) - (char*)p) - TOP_FOOT_SIZE); + assert(m->user_data, pinuse(p)); + assert(m->user_data, !next_pinuse(p)); +} + +/* Check properties of inuse chunks */ +static void do_check_inuse_chunk(mstate m, mchunkptr p) { + do_check_any_chunk(m, p); + assert(m->user_data, cinuse(p)); + assert(m->user_data, next_pinuse(p)); + /* If not pinuse, previous chunk has OK offset */ + assert(m->user_data, pinuse(p) || next_chunk(prev_chunk(p)) == p); +} + +/* Check properties of free chunks */ +static void do_check_free_chunk(mstate m, mchunkptr p) { + size_t sz = p->head & ~(PINUSE_BIT|CINUSE_BIT); + mchunkptr next = chunk_plus_offset(p, sz); + do_check_any_chunk(m, p); + assert(m->user_data, !cinuse(p)); + assert(m->user_data, !next_pinuse(p)); + if (p != m->dv && p != m->top) { + if (sz >= MIN_CHUNK_SIZE) { + assert(m->user_data, (sz & CHUNK_ALIGN_MASK) == 0); + assert(m->user_data, is_aligned(chunk2mem(p))); + assert(m->user_data, next->prev_foot == sz); + assert(m->user_data, pinuse(p)); + assert(m->user_data, next == m->top || cinuse(next)); + assert(m->user_data, p->fd->bk == p); + assert(m->user_data, p->bk->fd == p); + } + else /* markers are always of size SIZE_T_SIZE */ + assert(m->user_data, sz == SIZE_T_SIZE); + } +} + +/* Check properties of malloced chunks at the point they are malloced */ +static void do_check_malloced_chunk(mstate m, void* mem, size_t s) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); + size_t sz = p->head & ~(PINUSE_BIT|CINUSE_BIT); + do_check_inuse_chunk(m, p); + assert(m->user_data, (sz & CHUNK_ALIGN_MASK) == 0); + assert(m->user_data, sz >= MIN_CHUNK_SIZE); + assert(m->user_data, sz >= s); + /* size is less than MIN_CHUNK_SIZE more than request */ + assert(m->user_data, sz < (s + MIN_CHUNK_SIZE)); + } +} + +/* Check a tree and its subtrees. */ +static void do_check_tree(mstate m, tchunkptr t) { + tchunkptr head = 0; + tchunkptr u = t; + bindex_t tindex = t->index; + size_t tsize = chunksize(t); + bindex_t idx; + compute_tree_index(tsize, idx); + assert(m->user_data, tindex == idx); + assert(m->user_data, tsize >= MIN_LARGE_SIZE); + assert(m->user_data, tsize >= minsize_for_tree_index(idx)); + assert(m->user_data, (idx == NTREEBINS-1) || (tsize < minsize_for_tree_index((idx+1)))); + + do { /* traverse through chain of same-sized nodes */ + do_check_any_chunk(m, ((mchunkptr)u)); + assert(m->user_data, u->index == tindex); + assert(m->user_data, chunksize(u) == tsize); + assert(m->user_data, !cinuse(u)); + assert(m->user_data, !next_pinuse(u)); + assert(m->user_data, u->fd->bk == u); + assert(m->user_data, u->bk->fd == u); + if (u->parent == 0) { + assert(m->user_data, u->child[0] == 0); + assert(m->user_data, u->child[1] == 0); + } + else { + assert(m->user_data, head == 0); /* only one node on chain has parent */ + head = u; + assert(m->user_data, u->parent != u); + assert(m->user_data, u->parent->child[0] == u || + u->parent->child[1] == u || + *((tbinptr*)(u->parent)) == u); + if (u->child[0] != 0) { + assert(m->user_data, u->child[0]->parent == u); + assert(m->user_data, u->child[0] != u); + do_check_tree(m, u->child[0]); + } + if (u->child[1] != 0) { + assert(m->user_data, u->child[1]->parent == u); + assert(m->user_data, u->child[1] != u); + do_check_tree(m, u->child[1]); + } + if (u->child[0] != 0 && u->child[1] != 0) { + assert(m->user_data, chunksize(u->child[0]) < chunksize(u->child[1])); + } + } + u = u->fd; + } while (u != t); + assert(m->user_data, head != 0); +} + +/* Check all the chunks in a treebin. */ +static void do_check_treebin(mstate m, bindex_t i) { + tbinptr* tb = treebin_at(m, i); + tchunkptr t = *tb; + int empty = (m->treemap & (1U << i)) == 0; + if (t == 0) + assert(m->user_data, empty); + if (!empty) + do_check_tree(m, t); +} + +/* Check all the chunks in a smallbin. */ +static void do_check_smallbin(mstate m, bindex_t i) { + sbinptr b = smallbin_at(m, i); + mchunkptr p = b->bk; + unsigned int empty = (m->smallmap & (1U << i)) == 0; + if (p == b) + assert(m->user_data, empty); + if (!empty) { + for (; p != b; p = p->bk) { + size_t size = chunksize(p); + mchunkptr q; + /* each chunk claims to be free */ + do_check_free_chunk(m, p); + /* chunk belongs in bin */ + assert(m->user_data, small_index(size) == i); + assert(m->user_data, p->bk == b || chunksize(p->bk) == chunksize(p)); + /* chunk is followed by an inuse chunk */ + q = next_chunk(p); + if (q->head != FENCEPOST_HEAD) + do_check_inuse_chunk(m, q); + } + } +} + +/* Find x in a bin. Used in other check functions. */ +static int bin_find(mstate m, mchunkptr x) { + size_t size = chunksize(x); + if (is_small(size)) { + bindex_t sidx = small_index(size); + sbinptr b = smallbin_at(m, sidx); + if (smallmap_is_marked(m, sidx)) { + mchunkptr p = b; + do { + if (p == x) + return 1; + } while ((p = p->fd) != b); + } + } + else { + bindex_t tidx; + compute_tree_index(size, tidx); + if (treemap_is_marked(m, tidx)) { + tchunkptr t = *treebin_at(m, tidx); + size_t sizebits = size << leftshift_for_tree_index(tidx); + while (t != 0 && chunksize(t) != size) { + t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; + sizebits <<= 1; + } + if (t != 0) { + tchunkptr u = t; + do { + if (u == (tchunkptr)x) + return 1; + } while ((u = u->fd) != t); + } + } + } + return 0; +} + +/* Traverse each chunk and check it; return total */ +static size_t traverse_and_check(mstate m) { + size_t sum = 0; + if (is_initialized(m)) { + msegmentptr s = &m->seg; + sum += m->topsize + TOP_FOOT_SIZE; + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + mchunkptr lastq = 0; + assert(m->user_data, pinuse(q)); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + sum += chunksize(q); + if (cinuse(q)) { + assert(m->user_data, !bin_find(m, q)); + do_check_inuse_chunk(m, q); + } + else { + assert(m->user_data, q == m->dv || bin_find(m, q)); + assert(m->user_data, lastq == 0 || cinuse(lastq)); /* Not 2 consecutive free */ + do_check_free_chunk(m, q); + } + lastq = q; + q = next_chunk(q); + } + s = s->next; + } + } + return sum; +} + +/* Check all properties of malloc_state. */ +static void do_check_malloc_state(mstate m) { + bindex_t i; + size_t total; + /* check bins */ + for (i = 0; i < NSMALLBINS; ++i) + do_check_smallbin(m, i); + for (i = 0; i < NTREEBINS; ++i) + do_check_treebin(m, i); + + if (m->dvsize != 0) { /* check dv chunk */ + do_check_any_chunk(m, m->dv); + assert(m->user_data, m->dvsize == chunksize(m->dv)); + assert(m->user_data, m->dvsize >= MIN_CHUNK_SIZE); + assert(m->user_data, bin_find(m, m->dv) == 0); + } + + if (m->top != 0) { /* check top chunk */ + do_check_top_chunk(m, m->top); + assert(m->user_data, m->topsize == chunksize(m->top)); + assert(m->user_data, m->topsize > 0); + assert(m->user_data, bin_find(m, m->top) == 0); + } + + total = traverse_and_check(m); + assert(m->user_data, total <= m->footprint); + assert(m->user_data, m->footprint <= m->max_footprint); +} +#endif /* DEBUG */ + +/* ----------------------------- statistics ------------------------------ */ + +#if !NO_MALLINFO +static struct mallinfo internal_mallinfo(mstate m) { + struct mallinfo nm = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + if (!PREACTION(m)) { + check_malloc_state(m); + if (is_initialized(m)) { + size_t nfree = SIZE_T_ONE; /* top always free */ + size_t mfree = m->topsize + TOP_FOOT_SIZE; + size_t sum = mfree; + msegmentptr s = &m->seg; + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + size_t sz = chunksize(q); + sum += sz; + if (!cinuse(q)) { + mfree += sz; + ++nfree; + } + q = next_chunk(q); + } + s = s->next; + } + + nm.arena = sum; + nm.ordblks = nfree; + nm.hblkhd = m->footprint - sum; + nm.usmblks = m->max_footprint; + nm.uordblks = m->footprint - mfree; + nm.fordblks = mfree; + nm.keepcost = m->topsize; + } + + POSTACTION(m); + } + return nm; +} +#endif /* !NO_MALLINFO */ + +static void internal_malloc_stats(mstate m) { + if (!PREACTION(m)) { + size_t maxfp = 0; + size_t fp = 0; + size_t used = 0; + check_malloc_state(m); + if (is_initialized(m)) { + msegmentptr s = &m->seg; + maxfp = m->max_footprint; + fp = m->footprint; + used = fp - (m->topsize + TOP_FOOT_SIZE); + + while (s != 0) { + mchunkptr q = align_as_chunk(s->base); + while (segment_holds(s, q) && + q != m->top && q->head != FENCEPOST_HEAD) { + if (!cinuse(q)) + used -= chunksize(q); + q = next_chunk(q); + } + s = s->next; + } + } + + PRINT((m->user_data, "max system bytes = %10lu\n", (unsigned long)(maxfp))); + PRINT((m->user_data, "system bytes = %10lu\n", (unsigned long)(fp))); + PRINT((m->user_data, "in use bytes = %10lu\n", (unsigned long)(used))); + + POSTACTION(m); + } +} + +/* ----------------------- Operations on smallbins ----------------------- */ + +/* + Various forms of linking and unlinking are defined as macros. Even + the ones for trees, which are very long but have very short typical + paths. This is ugly but reduces reliance on inlining support of + compilers. +*/ + +/* Link a free chunk into a smallbin */ +#define insert_small_chunk(M, P, S) {\ + bindex_t I = small_index(S);\ + mchunkptr B = smallbin_at(M, I);\ + mchunkptr F = B;\ + assert((M)->user_data, S >= MIN_CHUNK_SIZE);\ + if (!smallmap_is_marked(M, I))\ + mark_smallmap(M, I);\ + else if (RTCHECK(ok_address(M, B->fd)))\ + F = B->fd;\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + B->fd = P;\ + F->bk = P;\ + P->fd = F;\ + P->bk = B;\ +} + +/* Unlink a chunk from a smallbin */ +#define unlink_small_chunk(M, P, S) {\ + mchunkptr F = P->fd;\ + mchunkptr B = P->bk;\ + bindex_t I = small_index(S);\ + assert((M)->user_data, P != B);\ + assert((M)->user_data, P != F);\ + assert((M)->user_data, chunksize(P) == small_index2size(I));\ + if (F == B)\ + clear_smallmap(M, I);\ + else if (RTCHECK((F == smallbin_at(M,I) || ok_address(M, F)) &&\ + (B == smallbin_at(M,I) || ok_address(M, B)))) {\ + F->bk = B;\ + B->fd = F;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ +} + +/* Unlink the first chunk from a smallbin */ +#define unlink_first_small_chunk(M, B, P, I) {\ + mchunkptr F = P->fd;\ + assert((M)->user_data, P != B);\ + assert((M)->user_data, P != F);\ + assert((M)->user_data, chunksize(P) == small_index2size(I));\ + if (B == F)\ + clear_smallmap(M, I);\ + else if (RTCHECK(ok_address(M, F))) {\ + B->fd = F;\ + F->bk = B;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ +} + +/* Replace dv node, binning the old one */ +/* Used only when dvsize known to be small */ +#define replace_dv(M, P, S) {\ + size_t DVS = M->dvsize;\ + if (DVS != 0) {\ + mchunkptr DV = M->dv;\ + assert((M)->user_data, is_small(DVS));\ + insert_small_chunk(M, DV, DVS);\ + }\ + M->dvsize = S;\ + M->dv = P;\ +} + + +/* ------------------------- Operations on trees ------------------------- */ + +/* Insert chunk into tree */ +#define insert_large_chunk(M, X, S) {\ + tbinptr* H;\ + bindex_t I;\ + compute_tree_index(S, I);\ + H = treebin_at(M, I);\ + X->index = I;\ + X->child[0] = X->child[1] = 0;\ + if (!treemap_is_marked(M, I)) {\ + mark_treemap(M, I);\ + *H = X;\ + X->parent = (tchunkptr)H;\ + X->fd = X->bk = X;\ + }\ + else {\ + tchunkptr T = *H;\ + size_t K = S << leftshift_for_tree_index(I);\ + for (;;) {\ + if (chunksize(T) != S) {\ + tchunkptr* C = &(T->child[(K >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]);\ + K <<= 1;\ + if (*C != 0)\ + T = *C;\ + else if (RTCHECK(ok_address(M, C))) {\ + *C = X;\ + X->parent = T;\ + X->fd = X->bk = X;\ + break;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + break;\ + }\ + }\ + else {\ + tchunkptr F = T->fd;\ + if (RTCHECK(ok_address(M, T) && ok_address(M, F))) {\ + T->fd = F->bk = X;\ + X->fd = F;\ + X->bk = T;\ + X->parent = 0;\ + break;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + break;\ + }\ + }\ + }\ + }\ +} + +/* + Unlink steps: + + 1. If x is a chained node, unlink it from its same-sized fd/bk links + and choose its bk node as its replacement. + 2. If x was the last node of its size, but not a leaf node, it must + be replaced with a leaf node (not merely one with an open left or + right), to make sure that lefts and rights of descendents + correspond properly to bit masks. We use the rightmost descendent + of x. We could use any other leaf, but this is easy to locate and + tends to counteract removal of leftmosts elsewhere, and so keeps + paths shorter than minimally guaranteed. This doesn't loop much + because on average a node in a tree is near the bottom. + 3. If x is the base of a chain (i.e., has parent links) relink + x's parent and children to x's replacement (or null if none). +*/ + +#define unlink_large_chunk(M, X) {\ + tchunkptr XP = X->parent;\ + tchunkptr R;\ + if (X->bk != X) {\ + tchunkptr F = X->fd;\ + R = X->bk;\ + if (RTCHECK(ok_address(M, F))) {\ + F->bk = R;\ + R->fd = F;\ + }\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + else {\ + tchunkptr* RP;\ + if (((R = *(RP = &(X->child[1]))) != 0) ||\ + ((R = *(RP = &(X->child[0]))) != 0)) {\ + tchunkptr* CP;\ + while ((*(CP = &(R->child[1])) != 0) ||\ + (*(CP = &(R->child[0])) != 0)) {\ + R = *(RP = CP);\ + }\ + if (RTCHECK(ok_address(M, RP)))\ + *RP = 0;\ + else {\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + }\ + if (XP != 0) {\ + tbinptr* H = treebin_at(M, X->index);\ + if (X == *H) {\ + if ((*H = R) == 0) \ + clear_treemap(M, X->index);\ + }\ + else if (RTCHECK(ok_address(M, XP))) {\ + if (XP->child[0] == X) \ + XP->child[0] = R;\ + else \ + XP->child[1] = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + if (R != 0) {\ + if (RTCHECK(ok_address(M, R))) {\ + tchunkptr C0, C1;\ + R->parent = XP;\ + if ((C0 = X->child[0]) != 0) {\ + if (RTCHECK(ok_address(M, C0))) {\ + R->child[0] = C0;\ + C0->parent = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + if ((C1 = X->child[1]) != 0) {\ + if (RTCHECK(ok_address(M, C1))) {\ + R->child[1] = C1;\ + C1->parent = R;\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ + else\ + CORRUPTION_ERROR_ACTION(M);\ + }\ + }\ +} + +/* Relays to large vs small bin operations */ + +#define insert_chunk(M, P, S)\ + if (is_small(S)) insert_small_chunk(M, P, S)\ + else { tchunkptr TP = (tchunkptr)(P); insert_large_chunk(M, TP, S); } + +#define unlink_chunk(M, P, S)\ + if (is_small(S)) unlink_small_chunk(M, P, S)\ + else { tchunkptr TP = (tchunkptr)(P); unlink_large_chunk(M, TP); } + + +/* Relays to internal calls to malloc/free from realloc, memalign etc */ + +#define internal_malloc(m, b) mspace_malloc(m, b) +#define internal_free(m, mem) mspace_free(m,mem); + + +/* -------------------------- mspace management -------------------------- */ + +/* Initialize top chunk and its size */ +static void init_top(mstate m, mchunkptr p, size_t psize) { + /* Ensure alignment */ + size_t offset = align_offset(chunk2mem(p)); + p = (mchunkptr)((char*)p + offset); + psize -= offset; + + m->top = p; + m->topsize = psize; + p->head = psize | PINUSE_BIT; + /* set size of fake trailing chunk holding overhead space only once */ + chunk_plus_offset(p, psize)->head = TOP_FOOT_SIZE; +} + +/* Initialize bins for a new mstate that is otherwise zeroed out */ +static void init_bins(mstate m) { + /* Establish circular links for smallbins */ + bindex_t i; + for (i = 0; i < NSMALLBINS; ++i) { + sbinptr bin = smallbin_at(m,i); + bin->fd = bin->bk = bin; + } +} + +#if PROCEED_ON_ERROR + +/* default corruption action */ +static void reset_on_error(mstate m) { + int i; + ++malloc_corruption_error_count; + /* Reinitialize fields to forget about all memory */ + m->smallbins = m->treebins = 0; + m->dvsize = m->topsize = 0; + m->seg.base = 0; + m->seg.size = 0; + m->seg.next = 0; + m->top = m->dv = 0; + for (i = 0; i < NTREEBINS; ++i) + *treebin_at(m, i) = 0; + init_bins(m); +} +#endif /* PROCEED_ON_ERROR */ + +/* Allocate chunk and prepend remainder with chunk in successor base. */ +static void* prepend_alloc(mstate m, char* newbase, char* oldbase, + size_t nb) { + mchunkptr p = align_as_chunk(newbase); + mchunkptr oldfirst = align_as_chunk(oldbase); + size_t psize = (char*)oldfirst - (char*)p; + mchunkptr q = chunk_plus_offset(p, nb); + size_t qsize = psize - nb; + set_size_and_pinuse_of_inuse_chunk(m, p, nb); + + assert(m->user_data, (char*)oldfirst > (char*)q); + assert(m->user_data, pinuse(oldfirst)); + assert(m->user_data, qsize >= MIN_CHUNK_SIZE); + + /* consolidate remainder with first chunk of old base */ + if (oldfirst == m->top) { + size_t tsize = m->topsize += qsize; + m->top = q; + q->head = tsize | PINUSE_BIT; + check_top_chunk(m, q); + } + else if (oldfirst == m->dv) { + size_t dsize = m->dvsize += qsize; + m->dv = q; + set_size_and_pinuse_of_free_chunk(q, dsize); + } + else { + if (!cinuse(oldfirst)) { + size_t nsize = chunksize(oldfirst); + unlink_chunk(m, oldfirst, nsize); + oldfirst = chunk_plus_offset(oldfirst, nsize); + qsize += nsize; + } + set_free_with_pinuse(q, qsize, oldfirst); + insert_chunk(m, q, qsize); + check_free_chunk(m, q); + } + + check_malloced_chunk(m, chunk2mem(p), nb); + return chunk2mem(p); +} + +/* -------------------------- System allocation -------------------------- */ + +/* Get memory from system using MORECORE or MMAP */ +static void* sys_alloc(mstate m, size_t nb) { + MALLOC_FAILURE_ACTION; + return 0; +} + +/* ---------------------------- malloc support --------------------------- */ + +/* allocate a large request from the best fitting chunk in a treebin */ +static void* tmalloc_large(mstate m, size_t nb) { + tchunkptr v = 0; + size_t rsize = -nb; /* Unsigned negation */ + tchunkptr t; + bindex_t idx; + compute_tree_index(nb, idx); + + if ((t = *treebin_at(m, idx)) != 0) { + /* Traverse tree for this bin looking for node with size == nb */ + size_t sizebits = nb << leftshift_for_tree_index(idx); + tchunkptr rst = 0; /* The deepest untaken right subtree */ + for (;;) { + tchunkptr rt; + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + v = t; + if ((rsize = trem) == 0) + break; + } + rt = t->child[1]; + t = t->child[(sizebits >> (SIZE_T_BITSIZE-SIZE_T_ONE)) & 1]; + if (rt != 0 && rt != t) + rst = rt; + if (t == 0) { + t = rst; /* set t to least subtree holding sizes > nb */ + break; + } + sizebits <<= 1; + } + } + + if (t == 0 && v == 0) { /* set t to root of next non-empty treebin */ + binmap_t leftbits = left_bits(idx2bit(idx)) & m->treemap; + if (leftbits != 0) { + bindex_t i; + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + t = *treebin_at(m, i); + } + } + + while (t != 0) { /* find smallest of tree or subtree */ + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + rsize = trem; + v = t; + } + t = leftmost_child(t); + } + + /* If dv is a better fit, return 0 so malloc will use it */ + if (v != 0 && rsize < (size_t)(m->dvsize - nb)) { + if (RTCHECK(ok_address(m, v))) { /* split */ + mchunkptr r = chunk_plus_offset(v, nb); + assert(m->user_data, chunksize(v) == rsize + nb); + if (RTCHECK(ok_next(v, r))) { + unlink_large_chunk(m, v); + if (rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(m, v, (rsize + nb)); + else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + insert_chunk(m, r, rsize); + } + return chunk2mem(v); + } + } + CORRUPTION_ERROR_ACTION(m); + } + return 0; +} + +/* allocate a small request from the best fitting chunk in a treebin */ +static void* tmalloc_small(mstate m, size_t nb) { + tchunkptr t, v; + size_t rsize; + bindex_t i; + binmap_t leastbit = least_bit(m->treemap); + compute_bit2idx(leastbit, i); + + v = t = *treebin_at(m, i); + rsize = chunksize(t) - nb; + + while ((t = leftmost_child(t)) != 0) { + size_t trem = chunksize(t) - nb; + if (trem < rsize) { + rsize = trem; + v = t; + } + } + + if (RTCHECK(ok_address(m, v))) { + mchunkptr r = chunk_plus_offset(v, nb); + assert(m->user_data, chunksize(v) == rsize + nb); + if (RTCHECK(ok_next(v, r))) { + unlink_large_chunk(m, v); + if (rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(m, v, (rsize + nb)); + else { + set_size_and_pinuse_of_inuse_chunk(m, v, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(m, r, rsize); + } + return chunk2mem(v); + } + } + + CORRUPTION_ERROR_ACTION(m); + return 0; +} + +/* --------------------------- realloc support --------------------------- */ + +static void* internal_realloc(mstate m, void* oldmem, size_t bytes) { + if (bytes >= MAX_REQUEST) { + MALLOC_FAILURE_ACTION; + return 0; + } + if (!PREACTION(m)) { + mchunkptr oldp = mem2chunk(oldmem); + size_t oldsize = chunksize(oldp); + mchunkptr next = chunk_plus_offset(oldp, oldsize); + mchunkptr newp = 0; + void* extra = 0; + + /* Try to either shrink or extend into top. Else malloc-copy-free */ + + if (RTCHECK(ok_address(m, oldp) && ok_cinuse(oldp) && + ok_next(oldp, next) && ok_pinuse(next))) { + size_t nb = request2size(bytes); + if (oldsize >= nb) { /* already big enough */ + size_t rsize = oldsize - nb; + newp = oldp; + if (rsize >= MIN_CHUNK_SIZE) { + mchunkptr remainder = chunk_plus_offset(newp, nb); + set_inuse(m, newp, nb); + set_inuse(m, remainder, rsize); + extra = chunk2mem(remainder); + } + } + else if (next == m->top && oldsize + m->topsize > nb) { + /* Expand into top */ + size_t newsize = oldsize + m->topsize; + size_t newtopsize = newsize - nb; + mchunkptr newtop = chunk_plus_offset(oldp, nb); + set_inuse(m, oldp, nb); + newtop->head = newtopsize |PINUSE_BIT; + m->top = newtop; + m->topsize = newtopsize; + newp = oldp; + } + } + else { + USAGE_ERROR_ACTION(m, oldmem); + POSTACTION(m); + return 0; + } + + POSTACTION(m); + + if (newp != 0) { + if (extra != 0) { + internal_free(m, extra); + } + check_inuse_chunk(m, newp); + return chunk2mem(newp); + } + else { + void* newmem = internal_malloc(m, bytes); + if (newmem != 0) { + size_t oc = oldsize - overhead_for(oldp); + MEMCPY(newmem, oldmem, (oc < bytes)? oc : bytes); + internal_free(m, oldmem); + } + return newmem; + } + } + return 0; +} + +/* --------------------------- memalign support -------------------------- */ + +static void* internal_memalign(mstate m, size_t alignment, size_t bytes) { + if (alignment <= MALLOC_ALIGNMENT) /* Can just use malloc */ + return internal_malloc(m, bytes); + if (alignment < MIN_CHUNK_SIZE) /* must be at least a minimum chunk size */ + alignment = MIN_CHUNK_SIZE; + if ((alignment & (alignment-SIZE_T_ONE)) != 0) {/* Ensure a power of 2 */ + size_t a = MALLOC_ALIGNMENT << 1; + while (a < alignment) a <<= 1; + alignment = a; + } + + if (bytes >= MAX_REQUEST - alignment) { + if (m != 0) { /* Test isn't needed but avoids compiler warning */ + MALLOC_FAILURE_ACTION; + } + } + else { + size_t nb = request2size(bytes); + size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD; + char* mem = (char*)internal_malloc(m, req); + if (mem != 0) { + void* leader = 0; + void* trailer = 0; + mchunkptr p = mem2chunk(mem); + + if (PREACTION(m)) return 0; + if ((((size_t)(mem)) % alignment) != 0) { /* misaligned */ + /* + Find an aligned spot inside chunk. Since we need to give + back leading space in a chunk of at least MIN_CHUNK_SIZE, if + the first calculation places us at a spot with less than + MIN_CHUNK_SIZE leader, we can move to the next aligned spot. + We've allocated enough total room so that this is always + possible. + */ + char* br = (char*)mem2chunk((size_t)(((size_t)(mem + + alignment - + SIZE_T_ONE)) & + -alignment)); + char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE)? + br : br+alignment; + mchunkptr newp = (mchunkptr)pos; + size_t leadsize = pos - (char*)(p); + size_t newsize = chunksize(p) - leadsize; + + /* Otherwise, give back leader, use the rest */ + set_inuse(m, newp, newsize); + set_inuse(m, p, leadsize); + leader = chunk2mem(p); + + p = newp; + } + + assert(m->user_data, chunksize(p) >= nb); + assert(m->user_data, (((size_t)(chunk2mem(p))) % alignment) == 0); + check_inuse_chunk(m, p); + POSTACTION(m); + if (leader != 0) { + internal_free(m, leader); + } + if (trailer != 0) { + internal_free(m, trailer); + } + return chunk2mem(p); + } + } + return 0; +} + +/* ----------------------------- user mspaces ---------------------------- */ + +static mstate init_user_mstate(char* tbase, size_t tsize, void *user_data) { + size_t msize = pad_request(sizeof(struct malloc_state)); + mchunkptr mn; + mchunkptr msp = align_as_chunk(tbase); + mstate m = (mstate)(chunk2mem(msp)); + MEMCLEAR(m, msize); + INITIAL_LOCK(&m->mutex); + msp->head = (msize|PINUSE_BIT|CINUSE_BIT); + m->seg.base = m->least_addr = tbase; + m->seg.size = m->footprint = m->max_footprint = tsize; + m->magic = mparams.magic; + m->mflags = mparams.default_mflags; + m->user_data = user_data; + init_bins(m); + mn = next_chunk(mem2chunk(m)); + init_top(m, mn, (size_t)((tbase + tsize) - (char*)mn) - TOP_FOOT_SIZE); + check_top_chunk(m, m->top); + return m; +} + +mspace create_mspace_with_base(void* base, size_t capacity, int locked, void *user_data) { + mstate m = 0; + size_t msize = pad_request(sizeof(struct malloc_state)); + init_mparams(); /* Ensure pagesize etc initialized */ + + if (capacity > msize + TOP_FOOT_SIZE && + capacity < (size_t) -(msize + TOP_FOOT_SIZE + mparams.page_size)) { + m = init_user_mstate((char*)base, capacity, user_data); + set_lock(m, locked); + } + return (mspace)m; +} + +/* + mspace versions of routines are near-clones of the global + versions. This is not so nice but better than the alternatives. +*/ + + +void* mspace_malloc(mspace msp, size_t bytes) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + if (!PREACTION(ms)) { + void* mem; + size_t nb; + if (bytes <= MAX_SMALL_REQUEST) { + bindex_t idx; + binmap_t smallbits; + nb = (bytes < MIN_REQUEST)? MIN_CHUNK_SIZE : pad_request(bytes); + idx = small_index(nb); + smallbits = ms->smallmap >> idx; + + if ((smallbits & 0x3U) != 0) { /* Remainderless fit to a smallbin. */ + mchunkptr b, p; + idx += ~smallbits & 1; /* Uses next bin if idx empty */ + b = smallbin_at(ms, idx); + p = b->fd; + assert(ms->user_data, chunksize(p) == small_index2size(idx)); + unlink_first_small_chunk(ms, b, p, idx); + set_inuse_and_pinuse(ms, p, small_index2size(idx)); + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + else if (nb > ms->dvsize) { + if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ + mchunkptr b, p, r; + size_t rsize; + bindex_t i; + binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); + binmap_t leastbit = least_bit(leftbits); + compute_bit2idx(leastbit, i); + b = smallbin_at(ms, i); + p = b->fd; + assert(ms->user_data, chunksize(p) == small_index2size(i)); + unlink_first_small_chunk(ms, b, p, i); + rsize = small_index2size(i) - nb; + /* Fit here cannot be remainderless if 4byte sizes */ + if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) + set_inuse_and_pinuse(ms, p, small_index2size(i)); + else { + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + r = chunk_plus_offset(p, nb); + set_size_and_pinuse_of_free_chunk(r, rsize); + replace_dv(ms, r, rsize); + } + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) { + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + } + } + else if (bytes >= MAX_REQUEST) + nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ + else { + nb = pad_request(bytes); + if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) { + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + } + + if (nb <= ms->dvsize) { + size_t rsize = ms->dvsize - nb; + mchunkptr p = ms->dv; + if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ + mchunkptr r = ms->dv = chunk_plus_offset(p, nb); + ms->dvsize = rsize; + set_size_and_pinuse_of_free_chunk(r, rsize); + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + } + else { /* exhaust dv */ + size_t dvs = ms->dvsize; + ms->dvsize = 0; + ms->dv = 0; + set_inuse_and_pinuse(ms, p, dvs); + } + mem = chunk2mem(p); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + else if (nb < ms->topsize) { /* Split top */ + size_t rsize = ms->topsize -= nb; + mchunkptr p = ms->top; + mchunkptr r = ms->top = chunk_plus_offset(p, nb); + r->head = rsize | PINUSE_BIT; + set_size_and_pinuse_of_inuse_chunk(ms, p, nb); + mem = chunk2mem(p); + check_top_chunk(ms, ms->top); + check_malloced_chunk(ms, mem, nb); + goto postaction; + } + + mem = sys_alloc(ms, nb); + + postaction: + POSTACTION(ms); + return mem; + } + + return 0; +} + +void mspace_free(mspace msp, void* mem) { + if (mem != 0) { + mchunkptr p = mem2chunk(mem); +#if FOOTERS + mstate fm = get_mstate_for(p); +#else /* FOOTERS */ + mstate fm = (mstate)msp; +#endif /* FOOTERS */ + if (!ok_magic(fm)) { + USAGE_ERROR_ACTION(fm, p); + return; + } + if (!PREACTION(fm)) { + check_inuse_chunk(fm, p); + if (RTCHECK(ok_address(fm, p) && ok_cinuse(p))) { + size_t psize = chunksize(p); + mchunkptr next = chunk_plus_offset(p, psize); + if (!pinuse(p)) { + size_t prevsize = p->prev_foot; + + mchunkptr prev = chunk_minus_offset(p, prevsize); + psize += prevsize; + p = prev; + if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ + if (p != fm->dv) { + unlink_chunk(fm, p, prevsize); + } + else if ((next->head & INUSE_BITS) == INUSE_BITS) { + fm->dvsize = psize; + set_free_with_pinuse(p, psize, next); + goto postaction; + } + } + else + goto erroraction; + } + + if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { + if (!cinuse(next)) { /* consolidate forward */ + if (next == fm->top) { + size_t tsize = fm->topsize += psize; + fm->top = p; + p->head = tsize | PINUSE_BIT; + if (p == fm->dv) { + fm->dv = 0; + fm->dvsize = 0; + } + goto postaction; + } + else if (next == fm->dv) { + size_t dsize = fm->dvsize += psize; + fm->dv = p; + set_size_and_pinuse_of_free_chunk(p, dsize); + goto postaction; + } + else { + size_t nsize = chunksize(next); + psize += nsize; + unlink_chunk(fm, next, nsize); + set_size_and_pinuse_of_free_chunk(p, psize); + if (p == fm->dv) { + fm->dvsize = psize; + goto postaction; + } + } + } + else + set_free_with_pinuse(p, psize, next); + insert_chunk(fm, p, psize); + check_free_chunk(fm, p); + goto postaction; + } + } + erroraction: + USAGE_ERROR_ACTION(fm, p); + postaction: + POSTACTION(fm); + } + } +} + +void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size) { + void* mem; + size_t req = 0; + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + if (n_elements != 0) { + req = n_elements * elem_size; + if (((n_elements | elem_size) & ~(size_t)0xffff) && + (req / n_elements != elem_size)) + req = MAX_SIZE_T; /* force downstream failure on overflow */ + } + mem = internal_malloc(ms, req); + if (mem != 0 && calloc_must_clear(mem2chunk(mem))) + MEMCLEAR(mem, req); + return mem; +} + +void* mspace_realloc(mspace msp, void* oldmem, size_t bytes) { + if (oldmem == 0) + return mspace_malloc(msp, bytes); +#ifdef REALLOC_ZERO_BYTES_FREES + if (bytes == 0) { + mspace_free(msp, oldmem); + return 0; + } +#endif /* REALLOC_ZERO_BYTES_FREES */ + else { +#if FOOTERS + mchunkptr p = mem2chunk(oldmem); + mstate ms = get_mstate_for(p); +#else /* FOOTERS */ + mstate ms = (mstate)msp; +#endif /* FOOTERS */ + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + return internal_realloc(ms, oldmem, bytes); + } +} + +void* mspace_memalign(mspace msp, size_t alignment, size_t bytes) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + return 0; + } + return internal_memalign(ms, alignment, bytes); +} + +void mspace_malloc_stats(mspace msp) { + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + internal_malloc_stats(ms); + } + else { + USAGE_ERROR_ACTION(ms,ms); + } +} + +size_t mspace_footprint(mspace msp) { + size_t result; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + result = ms->footprint; + } + USAGE_ERROR_ACTION(ms,ms); + return result; +} + + +size_t mspace_max_footprint(mspace msp) { + size_t result; + mstate ms = (mstate)msp; + if (ok_magic(ms)) { + result = ms->max_footprint; + } + USAGE_ERROR_ACTION(ms,ms); + return result; +} + + +#if !NO_MALLINFO +struct mallinfo mspace_mallinfo(mspace msp) { + mstate ms = (mstate)msp; + if (!ok_magic(ms)) { + USAGE_ERROR_ACTION(ms,ms); + } + return internal_mallinfo(ms); +} +#endif /* NO_MALLINFO */ + +int mspace_mallopt(int param_number, int value) { + return change_mparam(param_number, value); +} + diff --git a/display/mspace.h b/display/mspace.h new file mode 100644 index 0000000..96b0593 --- /dev/null +++ b/display/mspace.h @@ -0,0 +1,150 @@ +#ifndef _H_MSPACE +#define _H_MSPACE + +#define NO_MALLINFO 0 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +//typedef unsigned long size_t; +typedef void (*mspace_abort_t)(void *user_data); +typedef void (*mspace_print_t)(void *user_data, char *format, ...); + +void mspace_set_abort_func(mspace_abort_t f); +void mspace_set_print_func(mspace_print_t f); + +/* + mspace is an opaque type representing an independent + region of space that supports mspace_malloc, etc. +*/ +typedef void* mspace; + +/* + create_mspace creates and returns a new independent space with the + given initial capacity, or, if 0, the default granularity size. It + returns null if there is no system memory available to create the + space. If argument locked is non-zero, the space uses a separate + lock to control access. The capacity of the space will grow + dynamically as needed to service mspace_malloc requests. You can + control the sizes of incremental increases of this space by + compiling with a different DEFAULT_GRANULARITY or dynamically + setting with mallopt(M_GRANULARITY, value). +*/ +//mspace create_mspace(size_t capacity, int locked); + +/* + destroy_mspace destroys the given space, and attempts to return all + of its memory back to the system, returning the total number of + bytes freed. After destruction, the results of access to all memory + used by the space become undefined. +*/ +//size_t destroy_mspace(mspace msp); + +/* + create_mspace_with_base uses the memory supplied as the initial base + of a new mspace. Part (less than 128*sizeof(size_t) bytes) of this + space is used for bookkeeping, so the capacity must be at least this + large. (Otherwise 0 is returned.) When this initial space is + exhausted, additional memory will be obtained from the system. + Destroying this space will deallocate all additionally allocated + space (if possible) but not the initial base. +*/ +mspace create_mspace_with_base(void* base, size_t capacity, int locked, void *user_data); + +/* + mspace_malloc behaves as malloc, but operates within + the given space. +*/ +void* mspace_malloc(mspace msp, size_t bytes); + +/* + mspace_free behaves as free, but operates within + the given space. + + If compiled with FOOTERS==1, mspace_free is not actually needed. + free may be called instead of mspace_free because freed chunks from + any space are handled by their originating spaces. +*/ +void mspace_free(mspace msp, void* mem); + +/* + mspace_realloc behaves as realloc, but operates within + the given space. + + If compiled with FOOTERS==1, mspace_realloc is not actually + needed. realloc may be called instead of mspace_realloc because + realloced chunks from any space are handled by their originating + spaces. +*/ +void* mspace_realloc(mspace msp, void* mem, size_t newsize); + +/* + mspace_calloc behaves as calloc, but operates within + the given space. +*/ +void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size); + +/* + mspace_memalign behaves as memalign, but operates within + the given space. +*/ +void* mspace_memalign(mspace msp, size_t alignment, size_t bytes); + +/* + mspace_independent_calloc behaves as independent_calloc, but + operates within the given space. +*/ +//void** mspace_independent_calloc(mspace msp, size_t n_elements, +// size_t elem_size, void* chunks[]); + +/* + mspace_independent_comalloc behaves as independent_comalloc, but + operates within the given space. +*/ +//void** mspace_independent_comalloc(mspace msp, size_t n_elements, +// size_t sizes[], void* chunks[]); + +/* + mspace_footprint() returns the number of bytes obtained from the + system for this space. +*/ +size_t mspace_footprint(mspace msp); + +/* + mspace_max_footprint() returns the peak number of bytes obtained from the + system for this space. +*/ +size_t mspace_max_footprint(mspace msp); + + +#if !NO_MALLINFO +/* + mspace_mallinfo behaves as mallinfo, but reports properties of + the given space. +*/ +struct mallinfo mspace_mallinfo(mspace msp); +#endif /* NO_MALLINFO */ + +/* + mspace_malloc_stats behaves as malloc_stats, but reports + properties of the given space. +*/ +void mspace_malloc_stats(mspace msp); + +/* + mspace_trim behaves as malloc_trim, but + operates within the given space. +*/ +//int mspace_trim(mspace msp, size_t pad); + +/* + An alias for mallopt. +*/ +int mspace_mallopt(int, int); + +#ifdef __cplusplus +}; /* end of extern "C" */ +#endif /* __cplusplus */ + +#endif diff --git a/display/pointer.c b/display/pointer.c new file mode 100644 index 0000000..270619b --- /dev/null +++ b/display/pointer.c @@ -0,0 +1,140 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "os_dep.h" +#include "qxldd.h" +#include "utils.h" +#include "res.h" + +/* DDK quote + +Calls to the pointer functions are serialized by GDI. This means two different threads in the +driver cannot execute the pointer functions simultaneously. + +*/ + +ULONG APIENTRY DrvSetPointerShape(SURFOBJ *surf, SURFOBJ *mask, SURFOBJ *color_pointer, + XLATEOBJ *color_trans, LONG hot_x, LONG hot_y, + LONG pos_x, LONG pos_y, RECTL *prcl, FLONG flags) +{ + QXLCursorCmd *cursor_cmd; + PDev *pdev; + + if (!(pdev = (PDev *)surf->dhpdev)) { + DEBUG_PRINT((NULL, 0, "%s: err no pdev\n", __FUNCTION__)); + return SPS_ERROR; + } + + DEBUG_PRINT((pdev, 3, "%s\n", __FUNCTION__)); + + if (flags & SPS_CHANGE) { + BOOL ok; + cursor_cmd = CursorCmd(pdev); + cursor_cmd->type = QXL_CURSOR_SET; + if (flags & SPS_ALPHA) { + if (mask) { + DEBUG_PRINT((pdev, 0, "%s: SPS_ALPHA and mask \n", __FUNCTION__)); + } + ASSERT(pdev, color_pointer); + ok = GetAlphaCursor(pdev, cursor_cmd, hot_x, hot_y, color_pointer); + } else if (!mask) { + ok = GetTransparentCursor(pdev, cursor_cmd); + } else if (color_pointer && color_pointer->iBitmapFormat != BMF_1BPP) { + ASSERT(pdev, mask); + ok = GetColorCursor(pdev, cursor_cmd, hot_x, hot_y, color_pointer, mask, color_trans); + } else { + ok = GetMonoCursor(pdev, cursor_cmd, hot_x, hot_y, mask); + } + + if (!ok) { + DEBUG_PRINT((pdev, 0, "%s: get cursor failed\n", __FUNCTION__)); + ReleaseOutput(pdev, cursor_cmd->release_info.id); + return SPS_ERROR; + } + if (pos_x < 0) { + cursor_cmd->u.set.visible = FALSE; + cursor_cmd->u.set.position.x = cursor_cmd->u.set.position.y = 0; + } else { + cursor_cmd->u.set.visible = TRUE; + cursor_cmd->u.set.position.x = (INT16)pos_x; + cursor_cmd->u.set.position.y = (INT16)pos_y; + } + + PushCursorCmd(pdev, cursor_cmd); + + } else { + cursor_cmd = CursorCmd(pdev); + if (pos_x < 0) { +#ifdef DBG + DEBUG_PRINT((pdev, 0, "%s: no SPS_CHANGE and pos_x < 0\n", __FUNCTION__)); +#endif + cursor_cmd->type = QXL_CURSOR_HIDE; + } else { +#ifdef DBG + DEBUG_PRINT((pdev, 0, "%s: no SPS_CHANGE\n", __FUNCTION__)); +#endif + cursor_cmd->type = QXL_CURSOR_MOVE; + cursor_cmd->u.position.x = (INT16)pos_x; + cursor_cmd->u.position.y = (INT16)pos_y; + } + PushCursorCmd(pdev, cursor_cmd); + } +#if (WINVER >= 0x0501) + if ((flags & (SPS_LENGTHMASK | SPS_FREQMASK)) != pdev->cursor_trail){ + pdev->cursor_trail = (flags & (SPS_LENGTHMASK | SPS_FREQMASK)); + cursor_cmd = CursorCmd(pdev); + cursor_cmd->type = QXL_CURSOR_TRAIL; + cursor_cmd->u.trail.length = (UINT16)((flags & SPS_LENGTHMASK) >> 8); + cursor_cmd->u.trail.frequency = (UINT16)((flags & SPS_FREQMASK) >> 12); + PushCursorCmd(pdev, cursor_cmd); + } +#endif + + DEBUG_PRINT((pdev, 4, "%s: done\n", __FUNCTION__)); + return SPS_ACCEPT_NOEXCLUDE; +} + +VOID APIENTRY DrvMovePointer(SURFOBJ *surf, LONG pos_x, LONG pos_y, RECTL *area) +{ + QXLCursorCmd *cursor_cmd; + PDev *pdev; + + if (!(pdev = (PDev *)surf->dhpdev)) { + DEBUG_PRINT((NULL, 0, "%s: err no pdev\n", __FUNCTION__)); + return; + } + + DEBUG_PRINT((pdev, 3, "%s\n", __FUNCTION__)); + + if (pos_y < 0 && pos_x >= 0) { + DEBUG_PRINT((pdev, 0, "%s: unexpected negative y pos\n", __FUNCTION__)); + return; + } + + cursor_cmd = CursorCmd(pdev); + if (pos_x < 0) { + cursor_cmd->type = QXL_CURSOR_HIDE; + } else { + cursor_cmd->type = QXL_CURSOR_MOVE; + cursor_cmd->u.position.x = (INT16)pos_x; + cursor_cmd->u.position.y = (INT16)pos_y; + } + PushCursorCmd(pdev, cursor_cmd); + + DEBUG_PRINT((pdev, 4, "%s: done\n", __FUNCTION__)); +} + diff --git a/display/quic.c b/display/quic.c new file mode 100644 index 0000000..848d6d9 --- /dev/null +++ b/display/quic.c @@ -0,0 +1,18 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "..\common\quic.c" diff --git a/display/qxldd.h b/display/qxldd.h new file mode 100644 index 0000000..bcdcff2 --- /dev/null +++ b/display/qxldd.h @@ -0,0 +1,301 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _H_QXLDD +#define _H_QXLDD + + +#include "stddef.h" + +#include <stdarg.h> +#include <stdlib.h> +#include "windef.h" +#include "wingdi.h" +#include "winddi.h" +#include "qxl_driver.h" +#include "mspace.h" +#if (WINVER < 0x0501) +#include "wdmhelper.h" +#endif + +#define ALLOC_TAG 'dlxq' + +#define PAGE_SHIFT 12 +#define PAGE_SIZE (1 << PAGE_SHIFT) +#define PAGE_MASK (~(PAGE_SIZE - 1)) + +#define DEBUG_PRINT(arg) DebugPrint arg + +#ifdef DBG +#define ASSERT(pdev, x) if (!(x)) { \ + DebugPrint(pdev, 0, "ASSERT(%s) failed @ %s\n", #x, __FUNCTION__); \ + EngDebugBreak();\ +} +#define ONDBG(x) x +#else +#define ASSERT(pdev, x) +#define ONDBG(x) +#endif + +typedef enum { + QXL_SUCCESS, + QXL_FAILED, + QXL_UNSUPPORTED, +} QXLRESULT; + +typedef struct Ring RingItem; +typedef struct Ring { + RingItem *prev; + RingItem *next; +} Ring; + +#define IMAGE_HASH_SHIFT 15 +#define IMAGE_HASH_SIZE (1 << IMAGE_HASH_SHIFT) +#define IMAGE_HASH_MASK (IMAGE_HASH_SIZE - 1) + +#define IMAGE_POOL_SIZE (1 << 15) + +#define CURSOR_CACHE_SIZE (1 << 6) +#define CURSOR_HASH_SIZE (CURSOR_CACHE_SIZE << 1) +#define CURSOR_HASH_NASKE (CURSOR_HASH_SIZE - 1) + +#define PALETTE_CACHE_SIZE (1 << 6) +#define PALETTE_HASH_SIZE (PALETTE_CACHE_SIZE << 1) +#define PALETTE_HASH_NASKE (PALETTE_HASH_SIZE - 1) + +//#define CALL_TEST + +#ifdef CALL_TEST +enum { + CALL_COUNTER_COPY_BITS, + CALL_COUNTER_BIT_BLT, + CALL_COUNTER_TEXT_OUT, + CALL_COUNTER_STROKE_PATH, + CALL_COUNTER_STRETCH_BLT, + CALL_COUNTER_STRETCH_BLT_ROP, + CALL_COUNTER_TRANSPARENT_BLT, + CALL_COUNTER_ALPHA_BLEND, + + CALL_COUNTER_FILL_PATH, + CALL_COUNTER_GRADIENT_FILL, + CALL_COUNTER_LINE_TO, + CALL_COUNTER_PLG_BLT, + CALL_COUNTER_STROKE_AND_FILL_PATH, + + NUM_CALL_COUNTERS, +}; +#endif + +typedef struct QuicData QuicData; + +#define IMAGE_KEY_HASH_SIZE (1 << 15) +#define IMAGE_KEY_HASH_MASK (IMAGE_KEY_HASH_SIZE - 1) + +typedef struct ImageKey { + HSURF hsurf; + UINT64 unique; + UINT32 key; +} ImageKey; + +typedef struct CacheImage { + struct CacheImage *next; + RingItem lru_link; + UINT32 key; + UINT32 hits; + UINT8 format; + UINT32 width; + UINT32 height; + struct InternalImage *image; +} CacheImage; + +#define NUM_UPDATE_TRACE_ITEMS 10 +typedef struct UpdateTrace { + RingItem link; + UINT32 last_time; + RECTL area; + HSURF hsurf; +} UpdateTrace; + +typedef struct PDev { + HANDLE driver; + HDEV eng; + HPALETTE palette; + HSURF surf; + DWORD video_mode_index; + SIZEL resolution; + UINT32 max_bitmap_size; + ULONG bitmap_format; + ULONG fb_size; + BYTE* fb; + ULONG stride; + FLONG red_mask; + FLONG green_mask; + FLONG blue_mask; + ULONG fp_state_size; + + QuicData *quic_data; + + QXLCommandRing *cmd_ring; + QXLCursorRing *cursor_ring; + QXLReleaseRing *release_ring; + UINT32 notify_cmd_port; + UINT32 notify_cursor_port; + UINT32 notify_oom_port; + PEVENT display_event; + PEVENT cursor_event; + PEVENT sleep_event; + + HSURF draw_bitmap; + SURFOBJ *draw_surf; + + UINT32 log_port; + UINT8 *log_buf; + UINT32 *log_level; + + HSEMAPHORE malloc_sem; + HSEMAPHORE print_sem; + + UINT32 num_io_pages; + UINT8 *io_pages_virt; + UINT64 io_pages_phys; + + mspace mspace; + UINT8 *mspace_start; + UINT8 *mspace_end; + + UINT64 free_outputs; + + UINT32 update_id; + UINT32 *dev_update_id; + + UINT32 update_area_port; + Rect *update_area; + + UINT32 *mm_clock; + + UINT32 *compression_level; + + ImageKey image_key_lookup[IMAGE_KEY_HASH_SIZE]; + CacheImage cache_image_pool[IMAGE_POOL_SIZE]; + Ring cache_image_lru; + struct CacheImage *image_cache[IMAGE_HASH_SIZE]; + + struct InternalCursor *cursor_cache[CURSOR_HASH_SIZE]; + Ring cursors_lru; + UINT32 num_cursors; + UINT32 last_cursor_id; + FLONG cursor_trail; + + struct InternalPalette *palette_cache[PALETTE_HASH_SIZE]; + Ring palette_lru; + UINT32 num_palettes; + + Ring update_trace; + UpdateTrace update_trace_items[NUM_UPDATE_TRACE_ITEMS]; + +#ifdef DBG + int num_free_pages; + int num_outputs; + int num_path_pages; + int num_rects_pages; + int num_bits_pages; + int num_buf_pages; + int num_glyphs_pages; + int num_cursor_pages; +#endif + +#ifdef CALL_TEST + BOOL count_calls; + UINT32 total_calls; + UINT32 call_counters[NUM_CALL_COUNTERS]; +#endif + +#if (WINVER < 0x0501) + PQXLWaitForEvent WaitForEvent; +#endif +} PDev; + + +void DebugPrintV(PDev *pdev, const char *message, va_list ap); +void DebugPrint(PDev *pdev, int level, const char *message, ...); + +void InitResources(PDev *pdev); + +#ifdef CALL_TEST +void CountCall(PDev *pdev, int counter); +#else +#define CountCall(a, b) +#endif + +char *BitmapFormatToStr(int format); +char *BitmapTypeToStr(int type); + +static _inline void RingInit(Ring *ring) +{ + ring->next = ring->prev = ring; +} + +static _inline void RingItemInit(RingItem *item) +{ + item->next = item->prev = NULL; +} + +static _inline BOOL RingItemIsLinked(RingItem *item) +{ + return !!item->next; +} + +static _inline BOOL RingIsEmpty(PDev *pdev, Ring *ring) +{ + ASSERT(pdev, ring->next != NULL && ring->prev != NULL); + return ring == ring->next; +} + +static _inline void RingAdd(PDev *pdev, Ring *ring, RingItem *item) +{ + ASSERT(pdev, ring->next != NULL && ring->prev != NULL); + ASSERT(pdev, item->next == NULL && item->prev == NULL); + + item->next = ring->next; + item->prev = ring; + ring->next = item->next->prev = item; +} + +static _inline void RingRemove(PDev *pdev, RingItem *item) +{ + ASSERT(pdev, item->next != NULL && item->prev != NULL); + ASSERT(pdev, item->next != item); + + item->next->prev = item->prev; + item->prev->next = item->next; + item->prev = item->next = 0; +} + +static _inline RingItem *RingGetTail(PDev *pdev, Ring *ring) +{ + RingItem *ret; + + ASSERT(pdev, ring->next != NULL && ring->prev != NULL); + + if (RingIsEmpty(pdev, ring)) { + return NULL; + } + ret = ring->prev; + return ret; +} + +#endif diff --git a/display/res.c b/display/res.c new file mode 100644 index 0000000..13189be --- /dev/null +++ b/display/res.c @@ -0,0 +1,2433 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "os_dep.h" +#include "res.h" +#include "ioaccess.h" +#include "utils.h" +#include "mspace.h" +#include "quic.h" +#include "lookup3.h" + +#if (WINVER < 0x0501) +#define WAIT_FOR_EVENT(pdev, event, timeout) (pdev)->WaitForEvent(event, timeout) +#else +#define WAIT_FOR_EVENT(pdev, event, timeout) EngWaitForSingleObject(event, timeout) +#endif + +#define PA(pdev, vaddr)\ + ((pdev)->io_pages_phys + ((UINT8*)(vaddr) - (pdev)->io_pages_virt)) + +#define VA(pdev, paddr)\ + ((pdev)->io_pages_virt + ((paddr) - (pdev)->io_pages_phys)) + +#define RELEASE_RES(pdev, res) if (!--(res)->refs) (res)->free(pdev, res); +#define GET_RES(res) (++(res)->refs) + +typedef struct Resource Resource; +struct Resource { + UINT32 refs; + void (*free)(PDev *pdev, Resource *res); + UINT8 res[0]; +}; + +static void FreeMem(PDev* pdev, void *ptr); +static BOOL SetClip(PDev *pdev, CLIPOBJ *clip, QXLDrawable *drawable); + + +#define PUSH_CMD(pdev) do { \ + int notify; \ + RING_PUSH(pdev->cmd_ring, notify); \ + if (notify) { \ + WRITE_PORT_UCHAR(pdev->notify_cmd_port, 0); \ + } \ +} while (0); + +#define PUSH_CURSOR_CMD(pdev) do { \ + int notify; \ + RING_PUSH(pdev->cursor_ring, notify); \ + if (notify) { \ + WRITE_PORT_UCHAR(pdev->notify_cursor_port, 0); \ + } \ +} while (0); + + +#define MAX_OUTPUT_RES 6 + +typedef struct QXLOutput { + UINT32 num_res; + Resource *resources[MAX_OUTPUT_RES]; + UINT8 data[0]; +} QXLOutput; + + +UINT64 ReleaseOutput(PDev *pdev, UINT64 output_id) +{ + QXLOutput *output = (QXLOutput *)output_id; + Resource **now; + Resource **end; + UINT64 next; + + ASSERT(pdev, output_id); + DEBUG_PRINT((pdev, 9, "%s 0x%x\n", __FUNCTION__, output)); + + for (now = output->resources, end = now + output->num_res; now < end; now++) { + RELEASE_RES(pdev, *now); + } + next = *(UINT64*)output->data; + FreeMem(pdev, output); + DEBUG_PRINT((pdev, 10, "%s done\n", __FUNCTION__)); + ONDBG(pdev->num_outputs--); //todo: atomic + return next; +} + +static void AddRes(PDev *pdev, QXLOutput *output, Resource *res) +{ + DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__)); + ASSERT(pdev, output->num_res < MAX_OUTPUT_RES); + res->refs++; + output->resources[output->num_res++] = res; + DEBUG_PRINT((pdev, 10, "%s: done\n", __FUNCTION__)); +} + +static _inline void DrawableAddRes(PDev *pdev, QXLDrawable *drawable, Resource *res) +{ + QXLOutput *output; + + output = (QXLOutput *)((UINT8 *)drawable - sizeof(QXLOutput)); + AddRes(pdev, output, res); +} + +static _inline void CursorCmdAddRes(PDev *pdev, QXLCursorCmd *cmd, Resource *res) +{ + QXLOutput *output; + + output = (QXLOutput *)((UINT8 *)cmd - sizeof(QXLOutput)); + AddRes(pdev, output, res); +} + +static void WaitForCursorRing(PDev* pdev) +{ + int wait; + + DEBUG_PRINT((pdev, 9, "%s: 0x%lx\n", __FUNCTION__, pdev)); + + for (;;) { + RING_PROD_WAIT(pdev->cursor_ring, wait); + + if (!wait) { + break; + } +#ifdef DBG + { + LARGE_INTEGER timeout; // 1 => 100 nanoseconds + timeout.QuadPart = -1 * (1000 * 1000 * 10); //negative => relative // 1s +#if (WINVER < 0x0501) + pdev->WaitForEvent(pdev->cursor_event, &timeout); +#else + EngWaitForSingleObject(pdev->cursor_event, &timeout); +#endif // (WINVER < 0x0501) + + if (RING_IS_FULL(pdev->cursor_ring)) { + DEBUG_PRINT((pdev, 0, "%s: 0x%lx: timeout\n", __FUNCTION__, pdev)); + } + } +#else +#if (WINVER < 0x0501) + pdev->WaitForEvent(pdev->cursor_event, NULL); +#else + EngWaitForSingleObject(pdev->cursor_event, NULL); +#endif // (WINVER < 0x0501) +#endif //DBG + } +} + +static void WaitForCmdRing(PDev* pdev) +{ + int wait; + + DEBUG_PRINT((pdev, 9, "%s: 0x%lx\n", __FUNCTION__, pdev)); + + for (;;) { + RING_PROD_WAIT(pdev->cmd_ring, wait); + + if (!wait) { + break; + } +#ifdef DBG + { + LARGE_INTEGER timeout; // 1 => 100 nanoseconds + timeout.QuadPart = -1 * (1000 * 1000 * 10); //negative => relative // 1s +#if (WINVER < 0x0501) + pdev->WaitForEvent(pdev->display_event, &timeout); +#else + EngWaitForSingleObject(pdev->display_event, &timeout); +#endif // (WINVER < 0x0501) + + if (RING_IS_FULL(pdev->cmd_ring)) { + DEBUG_PRINT((pdev, 0, "%s: 0x%lx: timeout\n", __FUNCTION__, pdev)); + } + } +#else +#if (WINVER < 0x0501) + pdev->WaitForEvent(pdev->display_event, NULL); +#else + EngWaitForSingleObject(pdev->display_event, NULL); +#endif // (WINVER < 0x0501) +#endif //DBG + } +} + +static void QXLSleep(PDev* pdev, int msec) +{ + LARGE_INTEGER timeout; + + DEBUG_PRINT((pdev, 18, "%s: 0x%lx msec %u\n", __FUNCTION__, pdev, msec)); + timeout.QuadPart = -msec * 1000 * 10; + WAIT_FOR_EVENT(pdev, pdev->sleep_event, &timeout); + DEBUG_PRINT((pdev, 19, "%s: 0x%lx exit\n", __FUNCTION__, pdev)); +} + +static void WaitForReleaseRing(PDev* pdev) +{ + int wait; + + DEBUG_PRINT((pdev, 15, "%s: 0x%lx\n", __FUNCTION__, pdev)); + + for (;;) { + LARGE_INTEGER timeout; + + if (RING_IS_EMPTY(pdev->release_ring)) { + QXLSleep(pdev, 10); + if (!RING_IS_EMPTY(pdev->release_ring)) { + break; + } + WRITE_PORT_UCHAR(pdev->notify_oom_port, 0); + } + RING_CONS_WAIT(pdev->release_ring, wait); + + if (!wait) { + break; + } + + timeout.QuadPart = -30 * 1000 * 10; //30ms + WAIT_FOR_EVENT(pdev, pdev->display_event, &timeout); + + if (RING_IS_EMPTY(pdev->release_ring)) { +#ifdef DBG + DEBUG_PRINT((pdev, 0, "%s: 0x%lx: timeout\n", __FUNCTION__, pdev)); + DEBUG_PRINT((pdev, 0, "\tfree %d out %d path %d rect %d bits %d\n", + pdev->num_free_pages, + pdev->num_outputs, + pdev->num_path_pages, + pdev->num_rects_pages, + pdev->num_bits_pages, + pdev->num_buf_pages, + pdev->num_glyphs_pages, + pdev->num_cursor_pages)); +#endif + //oom + WRITE_PORT_UCHAR(pdev->notify_oom_port, 0); + } + } + DEBUG_PRINT((pdev, 16, "%s: 0x%lx, done\n", __FUNCTION__, pdev)); +} + +static void InitMspace(PDev *pdev) +{ + size_t capacity = pdev->num_io_pages * PAGE_SIZE; + pdev->mspace = create_mspace_with_base(pdev->io_pages_virt, capacity, 0, pdev); + pdev->mspace_start = pdev->io_pages_virt; + pdev->mspace_end = pdev->io_pages_virt + capacity; +} + +static void *AllocMem(PDev* pdev, size_t size) +{ + UINT8 *ptr; + + ASSERT(pdev, pdev && pdev->mspace); + DEBUG_PRINT((pdev, 12, "%s: 0x%lx size %u\n", __FUNCTION__, pdev, size)); + + EngAcquireSemaphore(pdev->malloc_sem); + while (!(ptr = mspace_malloc(pdev->mspace, size))) { + int notify; + if (pdev->free_outputs) { + pdev->free_outputs = ReleaseOutput(pdev, pdev->free_outputs); + continue; + } + WaitForReleaseRing(pdev); + pdev->free_outputs = *RING_CONS_ITEM(pdev->release_ring); + RING_POP(pdev->release_ring, notify); + } + EngReleaseSemaphore(pdev->malloc_sem); + ASSERT(pdev, ptr >= pdev->mspace_start && ptr < pdev->mspace_end); + DEBUG_PRINT((pdev, 13, "%s: 0x%lx done 0x%x\n", __FUNCTION__, pdev, ptr)); + return ptr; +} + +static void FreeMem(PDev* pdev, void *ptr) +{ + ASSERT(pdev, pdev && pdev->mspace); + ASSERT(pdev, (UINT8 *)ptr >= pdev->mspace_start && (UINT8 *)ptr < pdev->mspace_end); + mspace_free(pdev->mspace, ptr); +} + +void InitResources(PDev *pdev) +{ + int i; + + pdev->free_outputs = 0; + InitMspace(pdev); + pdev->update_id = *pdev->dev_update_id; + + RtlZeroMemory(pdev->image_key_lookup, sizeof(pdev->image_key_lookup)); + RtlZeroMemory(pdev->cache_image_pool, sizeof(pdev->cache_image_pool)); + RingInit(&pdev->cache_image_lru); + for (i = 0; i < IMAGE_POOL_SIZE; i++) { + RingAdd(pdev, &pdev->cache_image_lru, &pdev->cache_image_pool[i].lru_link); + } + RtlZeroMemory(pdev->image_cache, sizeof(pdev->image_cache)); + + RtlZeroMemory(pdev->cursor_cache, sizeof(pdev->cursor_cache)); + RingInit(&pdev->cursors_lru); + pdev->num_cursors = 0; + pdev->last_cursor_id = 0; + + RtlZeroMemory(pdev->palette_cache, sizeof(pdev->palette_cache)); + RingInit(&pdev->palette_lru); + pdev->num_palettes = 0; + + RtlZeroMemory(pdev->update_trace_items, sizeof(pdev->update_trace_items)); + RingInit(&pdev->update_trace); + for (i = 0; i < NUM_UPDATE_TRACE_ITEMS; i++) { + RingAdd(pdev, &pdev->update_trace, &pdev->update_trace_items[i].link); + } + + ONDBG(pdev->num_outputs = 0); + ONDBG(pdev->num_path_pages = 0); + ONDBG(pdev->num_rects_pages = 0); + ONDBG(pdev->num_bits_pages = 0); + ONDBG(pdev->num_buf_pages = 0); + ONDBG(pdev->num_glyphs_pages = 0); + ONDBG(pdev->num_cursor_pages = 0); + +#ifdef CALL_TEST + pdev->count_calls = TRUE; + pdev->total_calls = 0; + for (i = 0; i < NUM_CALL_COUNTERS; i++) { + pdev->call_counters[i] = 0; + } +#endif +} + +static QXLDrawable *GetDrawable(PDev *pdev) +{ + QXLOutput *output; + + output = (QXLOutput *)AllocMem(pdev, sizeof(QXLOutput) + sizeof(QXLDrawable)); + output->num_res = 0; + ((QXLDrawable *)output->data)->release_info.id = (UINT64)output; + DEBUG_PRINT((pdev, 9, "%s 0x%x\n", __FUNCTION__, output)); + ONDBG(pdev->num_outputs++); //todo: atomic + return (QXLDrawable *)output->data; +} + +QXLDrawable *Drawable(PDev *pdev, UINT8 type, RECTL *area, CLIPOBJ *clip) +{ + QXLDrawable *drawable; + + ASSERT(pdev, pdev && area); + + drawable = GetDrawable(pdev); + drawable->type = type; + drawable->effect = QXL_EFFECT_BLEND; + drawable->bitmap_offset = 0; + drawable->mm_time = *pdev->mm_clock; + CopyRect(&drawable->bbox, area); + + if (!SetClip(pdev, clip, drawable)) { + DEBUG_PRINT((pdev, 0, "%s: set clip filed\n", __FUNCTION__)); + ReleaseOutput(pdev, drawable->release_info.id); + drawable = NULL; + } + return drawable; +} + +void PushDrawable(PDev *pdev, QXLDrawable *drawable) +{ + QXLCommand *cmd; + + WaitForCmdRing(pdev); + cmd = RING_PROD_ITEM(pdev->cmd_ring); + cmd->type = QXL_CMD_DRAW; + cmd->data = PA(pdev, drawable); + PUSH_CMD(pdev); +} + +static void FreePath(PDev *pdev, Resource *res) +{ + PHYSICAL chunk_phys; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + + chunk_phys = ((QXLPath *)res->res)->chunk.next_chunk; + while (chunk_phys) { + QXLDataChunk *chunk = (QXLDataChunk *)VA(pdev, chunk_phys); + chunk_phys = chunk->next_chunk; + FreeMem(pdev, chunk); + ONDBG(pdev->num_path_pages--); + } + FreeMem(pdev, res); + ONDBG(pdev->num_path_pages--); + + DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); +} + +#define NEW_DATA_CHUNK(page_counter, size) { \ + void *ptr = AllocMem(pdev, size + sizeof(QXLDataChunk)); \ + ONDBG((*(page_counter))++); \ + chunk->next_chunk = PA(pdev, ptr); \ + ((QXLDataChunk *)ptr)->prev_chunk = PA(pdev, chunk); \ + chunk = (QXLDataChunk *)ptr; \ + chunk->data_size = 0; \ + chunk->next_chunk = 0; \ + now = chunk->data; \ + end = now + size; \ +} + +#ifdef DBG +#define GetPathCommon __GetPathCommon +#else +#define GetPathCommon(pdev, path, chunk_ptr, now_ptr, end_ptr, data_size, page_counter)\ + __GetPathCommon(pdev, path, chunk_ptr, now_ptr, end_ptr, data_size, NULL) +#endif + +#define PATH_PREALLOC_PONTS 20 +#define PATH_MAX_ALLOC_PONTS 128 +#define PATH_ALLOC_SIZE (sizeof(Resource) + sizeof(QXLPath) + sizeof(PathSeg) +\ + sizeof(POINTFIX) * PATH_PREALLOC_PONTS) + + +static void __GetPathCommon(PDev *pdev, PATHOBJ *path, QXLDataChunk **chunk_ptr, UINT8 **now_ptr, + UINT8 **end_ptr, UINT32 *data_size, int *page_counter) +{ + QXLDataChunk *chunk = *chunk_ptr; + UINT8 *now = *now_ptr; + UINT8 *end = *end_ptr; + PATHDATA data; + int more; + + DEBUG_PRINT((pdev, 15, "%s\n", __FUNCTION__)); + PATHOBJ_vEnumStart(path); + + do { + int pt_buf_size; + UINT8 *pt_buf; + PathSeg *seg; + + more = PATHOBJ_bEnum(path, &data); + if (data.count == 0) { + break; + } + + if (end - now < sizeof(PathSeg)) { + size_t alloc_size = MIN(data.count << 3, sizeof(POINTFIX) * PATH_MAX_ALLOC_PONTS); + alloc_size += sizeof(PathSeg); + NEW_DATA_CHUNK(page_counter, alloc_size); + } + seg = (PathSeg*)now; + seg->flags = data.flags; + seg->count = data.count; + now = seg->data; + chunk->data_size += sizeof(*seg); + *data_size += sizeof(*seg); + pt_buf_size = data.count << 3; + pt_buf = (UINT8 *)data.pptfx; + + do { + int cp_size; + if (end == now ) { + size_t alloc_size = MIN(pt_buf_size, sizeof(POINTFIX) * PATH_MAX_ALLOC_PONTS); + NEW_DATA_CHUNK(page_counter, alloc_size); + } + + cp_size = MIN(end - now, pt_buf_size); + memcpy(now, pt_buf, cp_size); + chunk->data_size += cp_size; + *data_size += cp_size; + now += cp_size; + pt_buf += cp_size; + pt_buf_size -= cp_size; + } while (pt_buf_size); + } while (more); + + *chunk_ptr = chunk; + *now_ptr = now; + *end_ptr = end; + DEBUG_PRINT((pdev, 17, "%s: done\n", __FUNCTION__)); +} + +static Resource *__GetPath(PDev *pdev, PATHOBJ *path) +{ + Resource *res; + QXLPath *qxl_path; + QXLDataChunk *chunk; + PATHDATA data; + UINT8 *now; + UINT8 *end; + int more; + + ASSERT(pdev, QXL_PATH_BEGIN == PD_BEGINSUBPATH && QXL_PATH_END == PD_ENDSUBPATH && + QXL_PATH_CLOSE == PD_CLOSEFIGURE && QXL_PATH_BEZIER == PD_BEZIERS); + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + res = AllocMem(pdev, PATH_ALLOC_SIZE); + ONDBG(pdev->num_path_pages++); + res->refs = 1; + res->free = FreePath; + + qxl_path = (QXLPath *)res->res; + qxl_path->data_size = 0; + chunk = &qxl_path->chunk; + chunk->data_size = 0; + chunk->prev_chunk = 0; + chunk->next_chunk = 0; + + now = chunk->data; + end = (UINT8 *)res + PATH_ALLOC_SIZE; + GetPathCommon(pdev, path, &chunk, &now, &end, &qxl_path->data_size, &pdev->num_path_pages); + + DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); + return res; +} + +BOOL QXLGetPath(PDev *pdev, QXLDrawable *drawable, PHYSICAL *path_phys, PATHOBJ *path) +{ + Resource *path_res; + ASSERT(pdev, pdev && drawable && path_phys && path); + + DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__)); + + path_res = __GetPath(pdev, path); + *path_phys = PA(pdev, path_res->res); + DrawableAddRes(pdev, drawable, path_res); + RELEASE_RES(pdev, path_res); + return TRUE; +} + + +static void FreeClipRects(PDev *pdev, Resource *res) +{ + PHYSICAL chunk_phys; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + + chunk_phys = ((QXLClipRects *)res->res)->chunk.next_chunk; + while (chunk_phys) { + QXLDataChunk *chunk = (QXLDataChunk *)VA(pdev, chunk_phys); + chunk_phys = chunk->next_chunk; + FreeMem(pdev, chunk); + ONDBG(pdev->num_rects_pages--); + } + FreeMem(pdev, res); + ONDBG(pdev->num_rects_pages--); + + DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); +} + + +#define RECTS_NUM_PREALLOC 8 +#define RECTS_ALLOC_SIZE (sizeof(Resource) + sizeof(QXLClipRects) + \ + sizeof(Rect) * RECTS_NUM_PREALLOC) +#define RECTS_NUM_ALLOC 20 +#define RECTS_CHUNK_ALLOC_SIZE (sizeof(QXLDataChunk) + sizeof(Rect) * RECTS_NUM_ALLOC) + +static Resource *GetClipRects(PDev *pdev, CLIPOBJ *clip) +{ + Resource *res; + QXLClipRects *rects; + QXLDataChunk *chunk; + Rect *dest; + Rect *dest_end; + int more; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + res = (Resource *)AllocMem(pdev, RECTS_ALLOC_SIZE); + ONDBG(pdev->num_rects_pages++); + res->refs = 1; + res->free = FreeClipRects; + rects = (QXLClipRects *)res->res; + rects->num_rects = 0; + + chunk = &rects->chunk; + chunk->data_size = 0; + chunk->prev_chunk = 0; + chunk->next_chunk = 0; + + dest = (Rect *)chunk->data; + dest_end = dest + ((RECTS_ALLOC_SIZE - sizeof(Resource) - sizeof(QXLClipRects)) >> 4); + + CLIPOBJ_cEnumStart(clip, TRUE, CT_RECTANGLES, CD_RIGHTDOWN, 0); + do { + RECTL *now; + RECTL *end; + struct { + ULONG count; + RECTL rects[20]; + } buf; + + more = CLIPOBJ_bEnum(clip, sizeof(buf), (ULONG *)&buf); + rects->num_rects += buf.count; + for(now = buf.rects, end = now + buf.count; now < end; now++, dest++) { + if (dest == dest_end) { + void *page = AllocMem(pdev, RECTS_CHUNK_ALLOC_SIZE); + ONDBG(pdev->num_rects_pages++); + chunk->next_chunk = PA(pdev, page); + ((QXLDataChunk *)page)->prev_chunk = PA(pdev, chunk); + chunk = (QXLDataChunk *)page; + chunk->data_size = 0; + chunk->next_chunk = 0; + dest = (Rect *)chunk->data; + dest_end = dest + RECTS_NUM_ALLOC; + } + CopyRect(dest, now); + chunk->data_size += sizeof(Rect); + } + } while (more); + DEBUG_PRINT((pdev, 13, "%s: done, num_rects %d\n", __FUNCTION__, rects->num_rects)); + return res; +} + +static BOOL SetClip(PDev *pdev, CLIPOBJ *clip, QXLDrawable *drawable) +{ + Resource *rects_res; + + DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__)); + + if (clip == NULL) { + drawable->clip.type = CLIP_TYPE_NONE; + DEBUG_PRINT((pdev, 10, "%s: QXL_CLIP_TYPE_NONE\n", __FUNCTION__)); + return TRUE; + } + + if (clip->iDComplexity == DC_RECT) { + QXLClipRects *rects; + rects_res = (Resource *)AllocMem(pdev, sizeof(Resource) + sizeof(QXLClipRects) + + sizeof(Rect)); + rects_res->refs = 1; + rects_res->free = FreeClipRects; + rects = (QXLClipRects *)rects_res->res; + rects->num_rects = 1; + rects->chunk.data_size = sizeof(Rect); + rects->chunk.prev_chunk = 0; + rects->chunk.next_chunk = 0; + CopyRect((Rect *)rects->chunk.data, &clip->rclBounds); + } else { + + ASSERT(pdev, clip->iDComplexity == DC_COMPLEX); + if (clip->iMode == TC_PATHOBJ) { + Resource *path_res; + PATHOBJ *path = CLIPOBJ_ppoGetPath(clip); + if (!path) { + DEBUG_PRINT((pdev, 0, "%s: get path failed\n", __FUNCTION__)); + return FALSE; + } + + path_res = __GetPath(pdev, path); + EngDeletePath(path); + DrawableAddRes(pdev, drawable, path_res); + RELEASE_RES(pdev, path_res); + drawable->clip.type = CLIP_TYPE_PATH; + drawable->clip.data = PA(pdev, path_res->res); + DEBUG_PRINT((pdev, 10, "%s: done\n", __FUNCTION__)); + return TRUE; + } else { + ASSERT(pdev, clip->iMode == TC_RECTANGLES); + rects_res = GetClipRects(pdev, clip); + } + } + + DrawableAddRes(pdev, drawable, rects_res); + RELEASE_RES(pdev, rects_res); + drawable->clip.type = CLIP_TYPE_RECTS; + drawable->clip.data = PA(pdev, rects_res->res); + DEBUG_PRINT((pdev, 10, "%s: done\n", __FUNCTION__)); + return TRUE; +} + +#ifdef DBG +#define PutBytesAlign __PutBytesAlign +#define PutBytes(pdev, chunk, now, end, src, size, page_counter, alloc_size)\ + __PutBytesAlign(pdev, chunk, now, end, src, size, page_counter, alloc_size, 1) +#else +#define PutBytesAlign(pdev, chunk, now, end, src, size, page_counter, alloc_size, alignment)\ + __PutBytesAlign(pdev, chunk, now, end, src, size, NULL, alloc_size, alignment) +#define PutBytes(pdev, chunk, now, end, src, size, page_counter, alloc_size)\ + __PutBytesAlign(pdev, chunk, now, end, src, size, NULL, alloc_size, 1) +#endif + +#define BITS_BUF_MAX (64 * 1024) + +static void __PutBytesAlign(PDev *pdev, QXLDataChunk **chunk_ptr, UINT8 **now_ptr, + UINT8 **end_ptr, UINT8 *src, int size, int *page_counter, + size_t alloc_size, uint32_t alignment) +{ + QXLDataChunk *chunk = *chunk_ptr; + UINT8 *now = *now_ptr; + UINT8 *end = *end_ptr; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + while (size) { + int cp_size = MIN(end - now, size); + if (!cp_size) { + size_t aligned_size; + ASSERT(pdev, alloc_size > 0); + ASSERT(pdev, BITS_BUF_MAX > alignment); + aligned_size = MIN(alloc_size + alignment - 1, BITS_BUF_MAX); + aligned_size -= aligned_size % alignment; + NEW_DATA_CHUNK(page_counter, aligned_size); + cp_size = MIN(end - now, size); + } + RtlCopyMemory(now, src, cp_size); + src += cp_size; + now += cp_size; + chunk->data_size += cp_size; + size -= cp_size; + } + *chunk_ptr = chunk; + *now_ptr = now; + *end_ptr = end; + DEBUG_PRINT((pdev, 14, "%s: done\n", __FUNCTION__)); +} + +typedef struct InternalImage { + CacheImage *cache; + QXLImage image; +} InternalImage; + +#define HSURF_HASH_VAL(h) (((unsigned long)h >> 4) ^ ((unsigned long)(h) >> 8) ^ \ + ((unsigned long)(h) >> 16) ^ ((unsigned long)(h) >> 24)) + +#define IMAGE_KEY_HASH_VAL(hsurf) (HSURF_HASH_VAL(hsurf) & IMAGE_KEY_HASH_MASK) + +void ImageKeyPut(PDev *pdev, HSURF hsurf, UINT64 unique, UINT32 key) +{ + ImageKey *image_key = &pdev->image_key_lookup[IMAGE_KEY_HASH_VAL(hsurf)]; + + if (!unique) { + return; + } + image_key->hsurf = hsurf; + image_key->unique = unique; + image_key->key = key; +} + +BOOL ImageKeyGet(PDev *pdev, HSURF hsurf, UINT64 unique, UINT32 *key) +{ + ImageKey *image_key; + + if (!unique) { + return FALSE; + } + image_key = &pdev->image_key_lookup[IMAGE_KEY_HASH_VAL(hsurf)]; + if (image_key->hsurf == hsurf && image_key->unique == unique) { + *key = image_key->key; + return TRUE; + } + return FALSE; +} + +#define IMAGE_HASH_VAL(hsurf) (HSURF_HASH_VAL(hsurf) & IMAGE_HASH_MASK) + +static CacheImage *ImageCacheGetByKey(PDev *pdev, UINT32 key, BOOL check_rest, + UINT8 format, UINT32 width, UINT32 height) +{ + CacheImage *cache_image = pdev->image_cache[IMAGE_HASH_VAL(key)]; + + while (cache_image) { + if (cache_image->key == key && (!check_rest || (cache_image->format == format && + cache_image->width == width && cache_image->height == height))) { + cache_image->hits++; + return cache_image; + } + cache_image = cache_image->next; + } + return NULL; +} + +static void ImageCacheAdd(PDev *pdev, CacheImage *cache_image) +{ + int key = IMAGE_HASH_VAL(cache_image->key); + cache_image->next = pdev->image_cache[key]; + cache_image->hits = 1; + pdev->image_cache[key] = cache_image; +} + +static void ImageCacheRemove(PDev *pdev, CacheImage *cache_image) +{ + CacheImage **cache_img; + + if (!cache_image->hits) { + return; + } + cache_img = &pdev->image_cache[IMAGE_HASH_VAL(cache_image->key)]; + while (*cache_img) { + if ((*cache_img)->key == cache_image->key) { + *cache_img = cache_image->next; + return; + } + cache_img = &(*cache_img)->next; + } +} + +static CacheImage *AllocCacheImage(PDev* pdev) +{ + RingItem *item; + + while (!(item = RingGetTail(pdev, &pdev->cache_image_lru))) { + int notify; + if (pdev->free_outputs) { + pdev->free_outputs = ReleaseOutput(pdev, pdev->free_outputs); + continue; + } + WaitForReleaseRing(pdev); + pdev->free_outputs = *RING_CONS_ITEM(pdev->release_ring); + RING_POP(pdev->release_ring, notify); + } + RingRemove(pdev, item); + return CONTAINEROF(item, CacheImage, lru_link); +} + +#define IMAGE_HASH_INIT_VAL(width, height, format) \ + ((UINT32)((width) & 0x1FFF) | ((UINT32)((height) & 0x1FFF) << 13) |\ + ((UINT32)(format) << 26)) + +static _inline void SetImageId(InternalImage *internal, BOOL cache_me, LONG width, LONG height, + UINT8 format, UINT32 key) +{ + UINT32 image_info = IMAGE_HASH_INIT_VAL(width, height, format); + + if (cache_me) { + QXL_SET_IMAGE_ID(&internal->image, ((UINT32)QXL_IMAGE_GROUP_DRIVER << 30) | + image_info, key); + internal->image.descriptor.flags = QXL_IMAGE_CACHE; + } else { + QXL_SET_IMAGE_ID(&internal->image, ((UINT32)QXL_IMAGE_GROUP_DRIVER_DONT_CACHE << 30) | + image_info, key); + internal->image.descriptor.flags = 0; + } +} + +typedef struct InternalPalette { + UINT32 refs; + struct InternalPalette *next; + RingItem lru_link; + Palette palette; +} InternalPalette; + +#define PALETTE_HASH_VAL(unique) ((int)(unique) & PALETTE_HASH_NASKE) + +static _inline void ReleasePalette(PDev *pdev, InternalPalette *palette) +{ + ASSERT(pdev, palette); + DEBUG_PRINT((pdev, 15, "%s\n", __FUNCTION__)); + if (--palette->refs == 0) { + FreeMem(pdev, palette); + } +} + +static _inline void PaletteCacheRemove(PDev *pdev, InternalPalette *palette) +{ + InternalPalette **internal; + + DEBUG_PRINT((pdev, 15, "%s\n", __FUNCTION__)); + + ASSERT(pdev, palette->palette.unique); + internal = &pdev->palette_cache[PALETTE_HASH_VAL(palette->palette.unique)]; + + while (*internal) { + if ((*internal)->palette.unique == palette->palette.unique) { + *internal = palette->next; + RingRemove(pdev, &palette->lru_link); + ReleasePalette(pdev, palette); + pdev->num_palettes--; + DEBUG_PRINT((pdev, 16, "%s: done\n", __FUNCTION__)); + return; + } + internal = &(*internal)->next; + } + ASSERT(pdev, FALSE); +} + +static _inline InternalPalette *PaletteCacheGet(PDev *pdev, UINT32 unique) +{ + InternalPalette *now; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + if (!unique) { + return NULL; + } + + now = pdev->palette_cache[PALETTE_HASH_VAL(unique)]; + while (now) { + if (now->palette.unique == unique) { + RingRemove(pdev, &now->lru_link); + RingAdd(pdev, &pdev->palette_lru, &now->lru_link); + now->refs++; + DEBUG_PRINT((pdev, 13, "%s: found\n", __FUNCTION__)); + return now; + } + now = now->next; + } + DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); + return NULL; +} + +static _inline void PaletteCacheAdd(PDev *pdev, InternalPalette *palette) +{ + int key; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + + if (!palette->palette.unique) { + DEBUG_PRINT((pdev, 13, "%s: not unique\n", __FUNCTION__)); + return; + } + + if (pdev->num_palettes == PALETTE_CACHE_SIZE) { + ASSERT(pdev, RingGetTail(pdev, &pdev->palette_lru)); + PaletteCacheRemove(pdev, CONTAINEROF(RingGetTail(pdev, &pdev->palette_lru), + InternalPalette, lru_link)); + } + + key = PALETTE_HASH_VAL(palette->palette.unique); + palette->next = pdev->palette_cache[key]; + pdev->palette_cache[key] = palette; + + RingAdd(pdev, &pdev->palette_lru, &palette->lru_link); + palette->refs++; + pdev->num_palettes++; + DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); +} + + +static _inline void GetPallette(PDev *pdev, Bitmap *bitmap, XLATEOBJ *color_trans) +{ + InternalPalette *internal; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + if (!color_trans || !(color_trans->flXlate & XO_TABLE)) { + bitmap->palette = 0; + return; + } + + if ((internal = PaletteCacheGet(pdev, color_trans->iUniq))) { + DEBUG_PRINT((pdev, 12, "%s: from cache\n", __FUNCTION__)); + bitmap->palette = PA(pdev, &internal->palette); + return; + } + + internal = (InternalPalette *)AllocMem(pdev, sizeof(InternalPalette) + + (color_trans->cEntries << 2)); + internal->refs = 1; + RingItemInit(&internal->lru_link); + bitmap->palette = PA(pdev, &internal->palette); + internal->palette.unique = color_trans->iUniq; + internal->palette.num_ents = (UINT16)color_trans->cEntries; + + RtlCopyMemory(internal->palette.ents, color_trans->pulXlate, color_trans->cEntries << 2); + PaletteCacheAdd(pdev, internal); + DEBUG_PRINT((pdev, 12, "%s: done\n", __FUNCTION__)); +} + +static void FreeQuicImage(PDev *pdev, Resource *res) // todo: defer +{ + InternalImage *internal; + PHYSICAL chunk_phys; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + + internal = (InternalImage *)res->res; + if (internal->cache) { + RingAdd(pdev, &pdev->cache_image_lru, &internal->cache->lru_link); + internal->cache->image = NULL; + } + + chunk_phys = ((QXLDataChunk *)internal->image.quic.data)->next_chunk; + while (chunk_phys) { + QXLDataChunk *chunk = (QXLDataChunk *)VA(pdev, chunk_phys); + chunk_phys = chunk->next_chunk; + FreeMem(pdev, chunk); + ONDBG(pdev->num_bits_pages--); + + } + FreeMem(pdev, res); + ONDBG(pdev->num_bits_pages--); + DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); +} + +static _inline QuicImageType GetQuicImageType(UINT8 format) +{ + switch (format) { + case BITMAP_FMT_32BIT: + return QUIC_IMAGE_TYPE_RGB32; + case BITMAP_FMT_16BIT: + return QUIC_IMAGE_TYPE_RGB16; + case BITMAP_FMT_RGBA: + return QUIC_IMAGE_TYPE_RGBA; + case BITMAP_FMT_24BIT: + return QUIC_IMAGE_TYPE_RGB24; + default: + return QUIC_IMAGE_TYPE_INVALID; + }; +} + +#define QUIC_ALLOC_BASE (sizeof(Resource) + sizeof(InternalImage) + sizeof(QXLDataChunk)) +#define QUIC_BUF_MAX (64 * 1024) +#define QUIC_BUF_MIN 1024 + +struct QuicData { + QuicUsrContext user; + PDev *pdev; + QuicContext *quic; + QXLDataChunk *chunk; + int chunk_io_words; + int prev_chunks_io_words; + int rows; + int raw_row_size; +}; + +static int quic_usr_more_space(QuicUsrContext *usr, uint32_t **io_ptr, int rows_completed) +{ + QuicData *usr_data = (QuicData *)usr; + PDev *pdev = usr_data->pdev; + QXLDataChunk *new_chank; + int alloc_size; + int more; + + ASSERT(pdev, usr_data->rows >= rows_completed); + more = (rows_completed - usr_data->rows) * usr_data->raw_row_size; + + alloc_size = MIN(MAX(more >> 4, QUIC_BUF_MIN), QUIC_BUF_MAX); + new_chank = AllocMem(pdev, sizeof(QXLDataChunk) + alloc_size); + new_chank->data_size = 0; + new_chank->prev_chunk = PA(pdev, usr_data->chunk); + new_chank->next_chunk = 0; + + usr_data->prev_chunks_io_words += usr_data->chunk_io_words; + usr_data->chunk->data_size = usr_data->chunk_io_words << 2; + usr_data->chunk->next_chunk = PA(pdev, new_chank); + usr_data->chunk = new_chank; + + usr_data->chunk_io_words = alloc_size >> 2; + + ONDBG(pdev->num_bits_pages++); + + *io_ptr = (UINT32 *)new_chank->data; + return usr_data->chunk_io_words; +} + +static int quic_usr_more_lines(QuicUsrContext *usr, uint8_t **lines) +{ + return 0; +} + +static _inline Resource *GetQuicImage(PDev *pdev, SURFOBJ *surf, XLATEOBJ *color_trans, + BOOL cache_me, LONG width, LONG height, UINT8 format, + UINT8 *src, UINT32 line_size, UINT32 key) +{ + Resource *image_res; + InternalImage *internal; + QuicImageType type; + size_t alloc_size; + int data_size; + QuicData *quic_data; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + ASSERT(pdev, pdev->quic_data); + + if (!*pdev->compression_level) { + return NULL; + } + + if ((type = GetQuicImageType(format)) == QUIC_IMAGE_TYPE_INVALID) { + DEBUG_PRINT((pdev, 13, "%s: unsupported\n", __FUNCTION__)); + return NULL; + } + + quic_data = pdev->quic_data; + + alloc_size = MIN(QUIC_ALLOC_BASE + (height * line_size >> 4), QUIC_ALLOC_BASE + QUIC_BUF_MAX); + alloc_size = MAX(alloc_size, QUIC_ALLOC_BASE + QUIC_BUF_MIN); + + image_res = AllocMem(pdev, alloc_size); + ONDBG(pdev->num_bits_pages++); + image_res->refs = 1; + image_res->free = FreeQuicImage; + + internal = (InternalImage *)image_res->res; + SetImageId(internal, cache_me, width, height, format, key); + internal->image.descriptor.type = IMAGE_TYPE_QUIC; + internal->image.descriptor.width = width; + internal->image.descriptor.height = height; + + quic_data->chunk = (QXLDataChunk *)internal->image.quic.data; + quic_data->chunk->data_size = 0; + quic_data->chunk->prev_chunk = 0; + quic_data->chunk->next_chunk = 0; + quic_data->prev_chunks_io_words = 0; + quic_data->chunk_io_words = ((UINT8 *)image_res + alloc_size - quic_data->chunk->data) >> 2; + quic_data->rows = height; + quic_data->raw_row_size = line_size; + + ASSERT(pdev, quic_data->chunk_io_words > 0); + data_size = quic_encode(quic_data->quic, type, width, height, src, height, surf->lDelta, + (UINT32 *)quic_data->chunk->data, quic_data->chunk_io_words); + if (data_size == QUIC_ERROR) { + FreeQuicImage(pdev, image_res); + DEBUG_PRINT((pdev, 13, "%s: error\n", __FUNCTION__)); + return NULL; + } + + quic_data->chunk->data_size = (data_size - quic_data->prev_chunks_io_words) << 2; + internal->image.quic.data_size = data_size << 2; + DEBUG_PRINT((pdev, 13, "%s: done. row size %u quic size %u \n", __FUNCTION__, + line_size * height, data_size << 2)); + return image_res; +} + +static void FreeBitmapImage(PDev *pdev, Resource *res) // todo: defer +{ + InternalImage *internal; + PHYSICAL chunk_phys; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + + internal = (InternalImage *)res->res; + if (internal->cache) { + RingAdd(pdev, &pdev->cache_image_lru, &internal->cache->lru_link); + internal->cache->image = NULL; + } + + if (internal->image.bitmap.palette) { + Palette *palette = (Palette *)VA(pdev, internal->image.bitmap.palette); + ReleasePalette(pdev, CONTAINEROF(palette, InternalPalette, palette)); + } + + chunk_phys = ((QXLDataChunk *)(&internal->image.bitmap + 1))->next_chunk; + while (chunk_phys) { + QXLDataChunk *chunk = (QXLDataChunk *)VA(pdev, chunk_phys); + chunk_phys = chunk->next_chunk; + FreeMem(pdev, chunk); + ONDBG(pdev->num_bits_pages--); + + } + + FreeMem(pdev, res); + ONDBG(pdev->num_bits_pages--); + DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); +} + +#define BITMAP_ALLOC_BASE (sizeof(Resource) + sizeof(InternalImage) + sizeof(QXLDataChunk)) + +static _inline Resource *GetBitmapImage(PDev *pdev, SURFOBJ *surf, XLATEOBJ *color_trans, + BOOL cache_me, LONG width, LONG height, UINT8 format, + UINT8 *src, UINT32 line_size, UINT32 key) +{ + Resource *image_res; + InternalImage *internal; + size_t alloc_size; + QXLDataChunk *chunk; + UINT8 *src_end; + UINT8 *dest; + UINT8 *dest_end; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + ASSERT(pdev, width > 0 && height > 0); + + ASSERT(pdev, BITS_BUF_MAX > line_size); + alloc_size = BITMAP_ALLOC_BASE + BITS_BUF_MAX - BITS_BUF_MAX % line_size; + alloc_size = MIN(BITMAP_ALLOC_BASE + height * line_size, alloc_size); + image_res = AllocMem(pdev, alloc_size); + ONDBG(pdev->num_bits_pages++); + + image_res->refs = 1; + image_res->free = FreeBitmapImage; + + internal = (InternalImage *)image_res->res; + SetImageId(internal, cache_me, width, height, format, key); + internal->image.descriptor.type = IMAGE_TYPE_BITMAP; + chunk = (QXLDataChunk *)(&internal->image.bitmap + 1); + chunk->data_size = 0; + chunk->prev_chunk = 0; + chunk->next_chunk = 0; + internal->image.bitmap.data = PA(pdev, chunk); + internal->image.bitmap.flags = 0; + internal->image.descriptor.width = internal->image.bitmap.x = width; + internal->image.descriptor.height = internal->image.bitmap.y = height; + internal->image.bitmap.format = format; + internal->image.bitmap.stride = line_size; + src_end = src - surf->lDelta; + src += surf->lDelta * (height - 1); + dest = chunk->data; + dest_end = (UINT8 *)image_res + alloc_size; + alloc_size = height * line_size; + for (; src != src_end; src -= surf->lDelta, alloc_size -= line_size) { + PutBytesAlign(pdev, &chunk, &dest, &dest_end, src, line_size, &pdev->num_bits_pages, + alloc_size, line_size); + } + + GetPallette(pdev, &internal->image.bitmap, color_trans); + DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); + return image_res; +} + +#define ADAPTIVE_HASH + +static _inline UINT32 GetHash(UINT8 *src, INT32 width, INT32 height, UINT8 format, + UINT32 line_size, LONG stride, XLATEOBJ *color_trans) +{ + UINT32 hash_value = IMAGE_HASH_INIT_VAL(width, height, format); + UINT8 *row_buf = src; + UINT8 last_byte = 0; + UINT8 reminder; + UINT32 i; + int row; + + if (color_trans && color_trans->flXlate == XO_TABLE) { + hash_value = hashlittle(color_trans->pulXlate, + sizeof(*color_trans->pulXlate) * color_trans->cEntries, hash_value); + } + for (row = 0; row < height; row++) { +#ifdef ADAPTIVE_HASH + if (format == BITMAP_FMT_32BIT) { + for (i = 0; i < line_size; i += 4) { + hash_value = hashlittle(row_buf + i, 3, hash_value); + } + } else { + if (format == BITMAP_FMT_4BIT_BE && (width & 0x1)) { + last_byte = row_buf[line_size - 1] & 0xF0; + } else if (format == BITMAP_FMT_1BIT_BE && (reminder = width & 0x7)) { + last_byte = row_buf[line_size - 1] & ~((1 << (8 - reminder)) - 1); + } + if (last_byte) { + hash_value = hashlittle(row_buf, line_size - 1, hash_value); + hash_value = hashlittle(&last_byte, 1, hash_value); + } else { + hash_value = hashlittle(row_buf, line_size, hash_value); + } + } +#else + hash_value = hashlittle(row_buf, line_size, hash_value); +#endif + row_buf += stride; + } + return hash_value; +} + +static _inline UINT32 GetFormatLineSize(INT32 width, ULONG bitmap_format, UINT8 *format) +{ + switch (bitmap_format) { + case BMF_32BPP: + *format = BITMAP_FMT_32BIT; + return width << 2; + case BMF_24BPP: + *format = BITMAP_FMT_24BIT; + return width * 3; + case BMF_16BPP: + *format = BITMAP_FMT_16BIT; + return width << 1; + case BMF_8BPP: + *format = BITMAP_FMT_8BIT; + return width; + case BMF_4BPP: + *format = BITMAP_FMT_4BIT_BE; + return ALIGN(width, 2) >> 1; + case BMF_1BPP: + *format = BITMAP_FMT_1BIT_BE; + return ALIGN(width, 8) >> 3; + default: + return 0; + } +} + +static BOOL ChachSizeTest(PDev *pdev, SURFOBJ *surf) +{ + BOOL ret = (UINT32)surf->sizlBitmap.cx * surf->sizlBitmap.cy <= pdev->max_bitmap_size; + if (!ret) { + DEBUG_PRINT((pdev, 1, "%s: cache size test failed x %d y %d max\n", + __FUNCTION__, + surf->sizlBitmap.cx, + surf->sizlBitmap.cy, + pdev->max_bitmap_size)); + } + return ret; +} + + +static CacheImage *GetChachImage(PDev *pdev, SURFOBJ *surf, XLATEOBJ *color_trans, UINT32 *hash_key) +{ + CacheImage *cache_image; + ULONG pallette_unique; + UINT64 gdi_unique; + int pallette; + UINT32 key; + UINT8 format; + UINT32 line_size; + + pallette = color_trans && (color_trans->flXlate & XO_TABLE); + pallette_unique = pallette ? color_trans->iUniq : 0; + + // NOTE: GDI sometimes gives many instances of the exactly same SURFOBJ (hsurf & iUniq), + // but with (fjBitmap & BMF_DONTCACHE). This opposed to what documented in the MSDN. + if (!surf->iUniq || (surf->fjBitmap & BMF_DONTCACHE) || (pallette && !pallette_unique)) { + gdi_unique = 0; + } else { + gdi_unique = surf->iUniq | ((UINT64)pallette_unique << 32); + } + + if (!(line_size = GetFormatLineSize(surf->sizlBitmap.cx, surf->iBitmapFormat, &format))) { + DEBUG_PRINT((pdev, 0, "%s: bitmap format err\n", __FUNCTION__)); + return FALSE; + } + + if (!ImageKeyGet(pdev, surf->hsurf, gdi_unique, &key)) { + key = GetHash(surf->pvScan0, surf->sizlBitmap.cx, surf->sizlBitmap.cy, format, + line_size, surf->lDelta, color_trans); + ImageKeyPut(pdev, surf->hsurf, gdi_unique, key); + DEBUG_PRINT((pdev, 11, "%s: ImageKeyPut %u\n", __FUNCTION__, key)); + } else { + DEBUG_PRINT((pdev, 11, "%s: ImageKeyGet %u\n", __FUNCTION__, key)); + } + + if (hash_key) { + *hash_key = key; + } + + if ((cache_image = ImageCacheGetByKey(pdev, key, TRUE, format, + surf->sizlBitmap.cx, + surf->sizlBitmap.cy))) { + DEBUG_PRINT((pdev, 11, "%s: ImageCacheGetByKey %u hits %u\n", __FUNCTION__, + key, cache_image->hits)); + return cache_image; + } + + if (ChachSizeTest(pdev, surf)) { + CacheImage *cache_image = AllocCacheImage(pdev); + ImageCacheRemove(pdev, cache_image); + cache_image->key = key; + cache_image->image = NULL; + cache_image->format = format; + cache_image->width = surf->sizlBitmap.cx; + cache_image->height = surf->sizlBitmap.cy; + ImageCacheAdd(pdev, cache_image); + RingAdd(pdev, &pdev->cache_image_lru, &cache_image->lru_link); + DEBUG_PRINT((pdev, 11, "%s: ImageCacheAdd %u\n", __FUNCTION__, key)); + } + return NULL; +} + +static HSEMAPHORE image_id_sem = NULL; + +static _inline UINT32 get_image_serial() +{ + static UINT32 image_id = 0; // move to dev mem and use InterlockedIncrement + UINT32 ret = 0; + + EngAcquireSemaphore(image_id_sem); + ret = ++image_id; + EngReleaseSemaphore(image_id_sem); + return ret; +} + +BOOL QXLGetBitmap(PDev *pdev, QXLDrawable *drawable, PHYSICAL *image_phys, SURFOBJ *surf, + Rect *area, XLATEOBJ *color_trans, UINT32 *hash_key, BOOL use_cache) +{ + Resource *image_res; + InternalImage *internal; + CacheImage *cache_image; + UINT32 key; + UINT8 format; + UINT32 line_size; + UINT8 *src; + INT32 width = area->right - area->left; + INT32 height = area->bottom - area->top; + + ASSERT(pdev, !hash_key || use_cache); + DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__)); + if (surf->iType != STYPE_BITMAP) { + DEBUG_PRINT((pdev, 0, "%s: copy from device doing nothing!!!\n", __FUNCTION__)); + return FALSE; + } + + if (area->left < 0 || area->right > surf->sizlBitmap.cx || + area->top < 0 || area->bottom > surf->sizlBitmap.cy) { + DEBUG_PRINT((pdev, 0, "%s: bad dimensions\n", __FUNCTION__)); + return FALSE; + } + + DEBUG_PRINT((pdev, 11, "%s: iUniq=%x DONTCACHE=%x w=%d h=%d cx=%d cy=%d " + "hsurf=%x ctiUniq=%x XO_TABLE=%u format=%u\n", __FUNCTION__, + surf->iUniq, surf->fjBitmap & BMF_DONTCACHE, width, height, + surf->sizlBitmap.cx, surf->sizlBitmap.cy, surf->hsurf, + color_trans ? color_trans->iUniq : 0, + color_trans ? !!(color_trans->flXlate & XO_TABLE) : 0, + surf->iBitmapFormat)); + + if (use_cache) { + cache_image = GetChachImage(pdev, surf, color_trans, hash_key); + if (cache_image && cache_image->image) { + DEBUG_PRINT((pdev, 11, "%s: cached image found %u\n", __FUNCTION__, cache_image->key)); + internal = cache_image->image; + *image_phys = PA(pdev, &internal->image); + image_res = (Resource *)((UINT8 *)internal - sizeof(Resource)); + DrawableAddRes(pdev, drawable, image_res); + return TRUE; + } + } else { + cache_image = NULL; + } + + if (cache_image) { + key = cache_image->key; + width = surf->sizlBitmap.cx; + height = surf->sizlBitmap.cy; + src = surf->pvScan0; + } else { + int scan0_offset; + int dx; + + key = get_image_serial(); + switch (surf->iBitmapFormat) { + case BMF_32BPP: + dx = 0; + scan0_offset = area->left << 2; + break; + case BMF_24BPP: + dx = 0; + scan0_offset = area->left * 3; + break; + case BMF_16BPP: + dx = 0; + scan0_offset = area->left << 1; + break; + case BMF_8BPP: + dx = 0; + scan0_offset = area->left; + break; + case BMF_4BPP: + dx = area->left & 0x01; + scan0_offset = (area->left & ~0x01) >> 1; + break; + case BMF_1BPP: + dx = area->left & 0x07; + scan0_offset = (area->left & ~0x07) >> 3; + break; + default: + DEBUG_PRINT((pdev, 0, "%s: bitmap format err\n", __FUNCTION__)); + return FALSE; + } + width = width + dx; + src = (UINT8 *)surf->pvScan0 + area->top * surf->lDelta + scan0_offset; + + area->left = dx; + area->right = width; + + area->top = 0; + area->bottom = height; + } + + if (!(line_size = GetFormatLineSize(width, surf->iBitmapFormat, &format))) { + DEBUG_PRINT((pdev, 0, "%s: bitmap format err\n", __FUNCTION__)); + return FALSE; + } + + if (!(image_res = GetQuicImage(pdev, surf, color_trans, !!cache_image, width, height, format, + src, line_size, key))) { + image_res = GetBitmapImage(pdev, surf, color_trans, !!cache_image, width, height, format, + src, line_size, key); + } + internal = (InternalImage *)image_res->res; + if ((internal->cache = cache_image)) { + DEBUG_PRINT((pdev, 11, "%s: cache_me %u\n", __FUNCTION__, key)); + cache_image->image = internal; + if (RingItemIsLinked(&cache_image->lru_link)) { + RingRemove(pdev, &cache_image->lru_link); + } + } + *image_phys = PA(pdev, &internal->image); + DrawableAddRes(pdev, drawable, image_res); + RELEASE_RES(pdev, image_res); + return TRUE; +} + +BOOL QXLGetAlphaBitmap(PDev *pdev, QXLDrawable *drawable, PHYSICAL *image_phys, + SURFOBJ *surf, Rect *area) +{ + Resource *image_res; + InternalImage *internal; + CacheImage *cache_image; + UINT64 gdi_unique; + UINT32 key; + UINT8 *src; + INT32 width = area->right - area->left; + INT32 height = area->bottom - area->top; + + DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__)); + ASSERT(pdev, surf->iBitmapFormat == BMF_32BPP && surf->iType == STYPE_BITMAP); + ASSERT(pdev, area->left >= 0 && area->right <= surf->sizlBitmap.cx && + area->top >= 0 && area->bottom <= surf->sizlBitmap.cy); + + DEBUG_PRINT((pdev, 11, "%s: iUniq=%x DONTCACHE=%x w=%d h=%d cx=%d cy=%d " + "hsurf=%x format=%u\n", __FUNCTION__, surf->iUniq, + surf->fjBitmap & BMF_DONTCACHE, width, height, + surf->sizlBitmap.cx, surf->sizlBitmap.cy, surf->hsurf, + surf->iBitmapFormat)); + + //todo: use GetChachImage + + // NOTE: Same BMF_DONTCACHE issue as in QXLGetBitmap + if (!surf->iUniq || (surf->fjBitmap & BMF_DONTCACHE)) { + gdi_unique = 0; + } else { + gdi_unique = surf->iUniq; + } + + if (!ImageKeyGet(pdev, surf->hsurf, gdi_unique, &key)) { + key = GetHash(surf->pvScan0, surf->sizlBitmap.cx, surf->sizlBitmap.cy, BITMAP_FMT_RGBA, + surf->sizlBitmap.cx << 2, surf->lDelta, NULL); + ImageKeyPut(pdev, surf->hsurf, gdi_unique, key); + DEBUG_PRINT((pdev, 11, "%s: ImageKeyPut %u\n", __FUNCTION__, key)); + } else { + DEBUG_PRINT((pdev, 11, "%s: ImageKeyGet %u\n", __FUNCTION__, key)); + } + + if (cache_image = ImageCacheGetByKey(pdev, key, TRUE, BITMAP_FMT_RGBA, + surf->sizlBitmap.cx, surf->sizlBitmap.cy)) { + DEBUG_PRINT((pdev, 11, "%s: ImageCacheGetByKey %u hits %u\n", __FUNCTION__, + key, cache_image->hits)); + if (internal = cache_image->image) { + DEBUG_PRINT((pdev, 11, "%s: cached image found %u\n", __FUNCTION__, key)); + *image_phys = PA(pdev, &internal->image); + image_res = (Resource *)((UINT8 *)internal - sizeof(Resource)); + DrawableAddRes(pdev, drawable, image_res); + return TRUE; + } + } else if (ChachSizeTest(pdev, surf)) { + CacheImage *cache_image = AllocCacheImage(pdev); + ImageCacheRemove(pdev, cache_image); + cache_image->key = key; + cache_image->image = NULL; + cache_image->format = BITMAP_FMT_RGBA; + cache_image->width = surf->sizlBitmap.cx; + cache_image->height = surf->sizlBitmap.cy; + ImageCacheAdd(pdev, cache_image); + RingAdd(pdev, &pdev->cache_image_lru, &cache_image->lru_link); + DEBUG_PRINT((pdev, 11, "%s: ImageCacheAdd %u\n", __FUNCTION__, key)); + } + + if (cache_image) { + width = surf->sizlBitmap.cx; + height = surf->sizlBitmap.cy; + src = surf->pvScan0; + } else { + src = (UINT8 *)surf->pvScan0 + area->top * surf->lDelta + (area->left << 2); + area->left = 0; + area->right = width; + area->top = 0; + area->bottom = height; + } + + if (!(image_res = GetQuicImage(pdev, surf, NULL, !!cache_image, width, height, + BITMAP_FMT_RGBA, src, width << 2, key))) { + image_res = GetBitmapImage(pdev, surf, NULL, !!cache_image, width, height, + BITMAP_FMT_RGBA, src, width << 2, key); + } + internal = (InternalImage *)image_res->res; + if ((internal->cache = cache_image)) { + DEBUG_PRINT((pdev, 11, "%s: cache_me %u\n", __FUNCTION__, key)); + cache_image->image = internal; + if (RingItemIsLinked(&cache_image->lru_link)) { + RingRemove(pdev, &cache_image->lru_link); + } + } + *image_phys = PA(pdev, &internal->image); + DrawableAddRes(pdev, drawable, image_res); + RELEASE_RES(pdev, image_res); + return TRUE; +} + +BOOL QXLGetBitsFromCache(PDev *pdev, QXLDrawable *drawable, UINT32 hash_key, PHYSICAL *image_phys) +{ + InternalImage *internal; + CacheImage *cache_image; + Resource *image_res; + + if ((cache_image = ImageCacheGetByKey(pdev, hash_key, FALSE, 0, 0, 0)) && + (internal = cache_image->image)) { + *image_phys = PA(pdev, &internal->image); + image_res = (Resource *)((UINT8 *)internal - sizeof(Resource)); + DrawableAddRes(pdev, drawable, image_res); + return TRUE; + } + return FALSE; +} + +BOOL QXLGetMask(PDev *pdev, QXLDrawable *drawable, QMask *qxl_mask, SURFOBJ *mask, POINTL *pos, + BOOL invers, LONG width, LONG height) +{ + Rect area; + + if (!mask) { + qxl_mask->bitmap = 0; + return TRUE; + } + + ASSERT(pdev, pos && qxl_mask && drawable); + if (mask->iBitmapFormat != BMF_1BPP) { + DEBUG_PRINT((pdev, 0, "%s: bitmap format err\n", __FUNCTION__)); + return FALSE; + } + + qxl_mask->flags = invers ? MASK_INVERS : 0; + + area.left = pos->x; + area.right = area.left + width; + area.top = pos->y; + area.bottom = area.top + height; + + if (QXLGetBitmap(pdev, drawable, &qxl_mask->bitmap, mask, &area, NULL, NULL, TRUE)) { + qxl_mask->pos.x = area.left; + qxl_mask->pos.y = area.top; + return TRUE; + } + return FALSE; +} + +static void FreeBuf(PDev *pdev, Resource *res) +{ + ONDBG(pdev->num_buf_pages--); + FreeMem(pdev, res); +} + +UINT8 *QXLGetBuf(PDev *pdev, QXLDrawable *drawable, PHYSICAL *buf_phys, UINT32 size) +{ + Resource *buf_res; + + DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__)); + if (size > PAGE_SIZE - sizeof(Resource)) { + DEBUG_PRINT((pdev, 0, "%s: size err\n", __FUNCTION__)); + return NULL; + } + + buf_res = (Resource *)AllocMem(pdev, sizeof(Resource) + size); + ONDBG(pdev->num_buf_pages++); + buf_res->refs = 1; + buf_res->free = FreeBuf; + + *buf_phys = PA(pdev, buf_res->res); + DrawableAddRes(pdev, drawable, buf_res); + RELEASE_RES(pdev, buf_res); + return buf_res->res; +} + +#ifdef UPDATE_CMD +void UpdateArea(PDev *pdev, RECTL *area) +{ + QXLCommand *cmd; + QXLOutput *output; + QXLUpdateCmd *updat_cmd; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + + output = (QXLOutput *)AllocMem(pdev, sizeof(QXLOutput) + sizeof(QXLUpdateCmd)); + output->num_res = 0; + updat_cmd = (QXLUpdateCmd *)output->data; + updat_cmd->release_info.id = (UINT64)output; + ONDBG(pdev->num_outputs++); //todo: atomic + + CopyRect(&updat_cmd->area, area); + updat_cmd->update_id = ++pdev->update_id; + + WaitForCmdRing(pdev); + cmd = RING_PROD_ITEM(pdev->cmd_ring); + cmd->type = QXL_CMD_UPDATE; + cmd->data = PA(pdev, updat_cmd); + PUSH_CMD(pdev); + do { +#ifdef DBG + { + LARGE_INTEGER timeout; // 1 => 100 nanoseconds + timeout.QuadPart = -1 * (1000 * 1000 * 10); //negative => relative // 1s +#if (WINVER < 0x0501) + pdev->WaitForEvent(pdev->display_event, &timeout); +#else + EngWaitForSingleObject(pdev->display_event, &timeout); +#endif //(WINVER < 0x0501) + if (*pdev->dev_update_id != pdev->update_id) { + DEBUG_PRINT((pdev, 0, "%s: 0x%lx: timeout\n", __FUNCTION__, pdev)); + } + } +#else +#if (WINVER < 0x0501) + pdev->WaitForEvent(pdev->display_event, NULL); +#else + EngWaitForSingleObject(pdev->display_event, NULL); +#endif //(WINVER < 0x0501) +#endif // DEBUG + mb(); + } while (*pdev->dev_update_id != pdev->update_id); +} + +#else + +void UpdateArea(PDev *pdev, RECTL *area) +{ + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + CopyRect(pdev->update_area, area); + WRITE_PORT_UCHAR(pdev->update_area_port, 0); +} + +#endif + +static _inline void add_rast_glyphs(PDev *pdev, QXLString *str, ULONG count, GLYPHPOS *glyps, + QXLDataChunk **chunk_ptr, UINT8 **now_ptr, + UINT8 **end_ptr, int bpp, POINTL *delta, Point **str_pos) +{ + GLYPHPOS *glyps_end = glyps + count; + QXLDataChunk *chunk = *chunk_ptr; + UINT8 *now = *now_ptr; + UINT8 *end = *end_ptr; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + for (; glyps < glyps_end; glyps++) { + RasterGlyph *glyph; + UINT8 *line; + UINT8 *end_line; + UINT32 stride; + + if (end - now < sizeof(*glyph)) { + NEW_DATA_CHUNK(&pdev->num_glyphs_pages, PAGE_SIZE); + } + + glyph = (RasterGlyph *)now; + if (delta) { + if (*str_pos) { + glyph->render_pos.x = (*str_pos)->x + delta->x; + glyph->render_pos.y = (*str_pos)->y + delta->y; + } else { + glyph->render_pos.x = glyps->ptl.x; + glyph->render_pos.y = glyps->ptl.y; + } + *str_pos = &glyph->render_pos; + } else { + glyph->render_pos.x = glyps->ptl.x; + glyph->render_pos.y = glyps->ptl.y; + } + glyph->glyph_origin.x = glyps->pgdf->pgb->ptlOrigin.x; + glyph->glyph_origin.y = glyps->pgdf->pgb->ptlOrigin.y; + glyph->width = (UINT16)glyps->pgdf->pgb->sizlBitmap.cx; + glyph->height = (UINT16)glyps->pgdf->pgb->sizlBitmap.cy; + now += sizeof(*glyph); + chunk->data_size += sizeof(*glyph); + str->data_size += sizeof(*glyph); + if (!glyph->height) { + continue; + } + + stride = ALIGN(glyph->width * bpp, 8) >> 3; + end_line = (UINT8 *)glyps->pgdf->pgb->aj - stride; + line = (UINT8 *)glyps->pgdf->pgb->aj + stride * (glyph->height - 1); + + for (; line != end_line; line -= stride) { + UINT8 *bits_pos = line; + UINT8 *bits_end = bits_pos + stride; + + for (; bits_pos != bits_end; bits_pos++) { + UINT8 val; + int i; + if (end - now < sizeof(*bits_pos)) { + NEW_DATA_CHUNK(&pdev->num_glyphs_pages, PAGE_SIZE); + } + *(UINT8 *)now = *bits_pos; + now += sizeof(*bits_pos); + chunk->data_size += sizeof(*bits_pos); + str->data_size += sizeof(*bits_pos); + } + } + } + *chunk_ptr = chunk; + *now_ptr = now; + *end_ptr = end; + DEBUG_PRINT((pdev, 14, "%s: done\n", __FUNCTION__)); +} + +static _inline void add_vec_glyphs(PDev *pdev, QXLString *str, ULONG count, GLYPHPOS *glyps, + QXLDataChunk **chunk_ptr, UINT8 **now_ptr, UINT8 **end_ptr, + POINTL *delta, Point **str_pos) +{ + GLYPHPOS *glyps_end = glyps + count; + QXLDataChunk *chunk = *chunk_ptr; + UINT8 *now = *now_ptr; + UINT8 *end = *end_ptr; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + + for (; glyps < glyps_end; glyps++) { + VectotGlyph *glyph; + + if (end - now < sizeof(*glyph)) { + NEW_DATA_CHUNK(&pdev->num_glyphs_pages, PAGE_SIZE); + } + chunk->data_size += sizeof(*glyph); + str->data_size += sizeof(*glyph); + glyph = (VectotGlyph *)now; + now += sizeof(*glyph); + + if (delta) { + if (*str_pos) { + glyph->render_pos.x = (*str_pos)->x + delta->x; + glyph->render_pos.y = (*str_pos)->y + delta->y; + } else { + glyph->render_pos.x = glyps->ptl.x; + glyph->render_pos.y = glyps->ptl.y; + } + *str_pos = &glyph->render_pos; + } else { + glyph->render_pos.x = glyps->ptl.x; + glyph->render_pos.y = glyps->ptl.y; + } + glyph->data_size = 0; + GetPathCommon(pdev, glyps->pgdf->ppo, &chunk, &now, &end, &glyph->data_size, + &pdev->num_glyphs_pages); + str->data_size += glyph->data_size; + } + *chunk_ptr = chunk; + *now_ptr = now; + *end_ptr = end; + + DEBUG_PRINT((pdev, 14, "%s: done\n", __FUNCTION__)); +} + +static _inline BOOL add_glyphs(PDev *pdev, QXLString *str, ULONG count, GLYPHPOS *glyps, + QXLDataChunk **chunk, UINT8 **now, UINT8 **end, POINTL *delta, + Point **str_pos) +{ + if (str->flags & STRING_RASTER_A1) { + add_rast_glyphs(pdev, str, count, glyps, chunk, now, end, 1, delta, str_pos); + } else if (str->flags & STRING_RASTER_A4) { + add_rast_glyphs(pdev, str, count, glyps, chunk, now, end, 4, delta, str_pos); + } else { + DEBUG_PRINT((pdev, 0, "%s: vector: untested path, doing nothing!!!\n", __FUNCTION__)); + return FALSE; + add_vec_glyphs(pdev, str, count, glyps, chunk, now, end, delta, str_pos); + } + return TRUE; +} + +static void FreeSring(PDev *pdev, Resource *res) +{ + PHYSICAL chunk_phys; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + chunk_phys = ((QXLString *)res->res)->chunk.next_chunk; + while (chunk_phys) { + QXLDataChunk *chunk = (QXLDataChunk *)VA(pdev, chunk_phys); + chunk_phys = chunk->next_chunk; + FreeMem(pdev, chunk); + ONDBG(pdev->num_glyphs_pages--); + } + + FreeMem(pdev, res); + ONDBG(pdev->num_glyphs_pages--); + + DEBUG_PRINT((pdev, 14, "%s: done\n", __FUNCTION__)); +} + + +#define TEXT_ALLOC_SIZE sizeof(Resource) + sizeof(QXLString) + 512 + +BOOL QXLGetStr(PDev *pdev, QXLDrawable *drawable, PHYSICAL *str_phys, FONTOBJ *font, STROBJ *str) +{ + Resource *str_res; + QXLString *qxl_str; + QXLDataChunk *chunk; + UINT8 *now; + UINT8 *end; + BOOL more; + static int id_QXLGetStr = 0; + POINTL delta; + POINTL *delta_ptr; + Point *str_pos; + + DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__)); + + str_res = (Resource *)AllocMem(pdev, TEXT_ALLOC_SIZE); + ONDBG(pdev->num_glyphs_pages++); + str_res->refs = 1; + str_res->free = FreeSring; + + qxl_str = (QXLString *)str_res->res; + qxl_str->data_size = 0; + qxl_str->length = (UINT16)str->cGlyphs; + qxl_str->flags = 0; + + if (font->flFontType & FO_TYPE_RASTER) { + qxl_str->flags = (font->flFontType & FO_GRAY16) ? STRING_RASTER_A4 : + STRING_RASTER_A1; + } + + chunk = &qxl_str->chunk; + chunk->data_size = 0; + chunk->prev_chunk = 0; + chunk->next_chunk = 0; + + now = chunk->data; + end = (UINT8 *)str_res + TEXT_ALLOC_SIZE; + + if (str->ulCharInc) { + str_pos = NULL; + if (str->flAccel & SO_VERTICAL) { + delta.x = 0; + delta.y = (str->flAccel & SO_REVERSED) ? -(LONG)str->ulCharInc : str->ulCharInc; + } else { + delta.x = (str->flAccel & SO_REVERSED) ? -(LONG)str->ulCharInc : str->ulCharInc; + delta.y = 0; + } + delta_ptr = δ + } else { + delta_ptr = NULL; + } + + STROBJ_vEnumStart(str); + + do { + ULONG count; + GLYPHPOS *glyps; + + if (str->pgp) { + count = str->cGlyphs; + glyps = str->pgp; + more = FALSE; + } else { + more = STROBJ_bEnum(str, &count, &glyps); + + if (more == DDI_ERROR) { + goto error; + } + } + if (!add_glyphs(pdev, qxl_str, count, glyps, &chunk, &now, &end, delta_ptr, &str_pos)) { + goto error; + } + + } while (more); + + *str_phys = PA(pdev, str_res->res); + DrawableAddRes(pdev, drawable, str_res); + RELEASE_RES(pdev, str_res); + + DEBUG_PRINT((pdev, 10, "%s: done size %u\n", __FUNCTION__, qxl_str->data_size)); + return TRUE; + +error: + FreeSring(pdev, str_res); + DEBUG_PRINT((pdev, 10, "%s: error\n", __FUNCTION__)); + return FALSE; +} + +QXLCursorCmd *CursorCmd(PDev *pdev) +{ + QXLCursorCmd *cursor_cmd; + QXLOutput *output; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + + output = (QXLOutput *)AllocMem(pdev, sizeof(QXLOutput) + sizeof(QXLCursorCmd)); + output->num_res = 0; + cursor_cmd = (QXLCursorCmd *)output->data; + cursor_cmd->release_info.id = (UINT64)output; + ONDBG(pdev->num_outputs++); //todo: atomic + DEBUG_PRINT((pdev, 8, "%s: done\n", __FUNCTION__)); + return cursor_cmd; +} + +void PushCursorCmd(PDev *pdev, QXLCursorCmd *cursor_cmd) +{ + QXLCommand *cmd; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + WaitForCursorRing(pdev); + cmd = RING_PROD_ITEM(pdev->cursor_ring); + cmd->type = QXL_CMD_CURSOR; + cmd->data = PA(pdev, cursor_cmd); + PUSH_CURSOR_CMD(pdev); + DEBUG_PRINT((pdev, 8, "%s: done\n", __FUNCTION__)); +} + +typedef struct InternalCursor { + struct InternalCursor *next; + RingItem lru_link; + HSURF hsurf; + ULONG unique; + QXLCursor cursor; +} InternalCursor; + + +#define CURSOR_HASH_VAL(hsurf) (HSURF_HASH_VAL(hsurf) & CURSOR_HASH_NASKE) + +static void CursorCacheRemove(PDev *pdev, InternalCursor *cursor) +{ + InternalCursor **internal; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + + ASSERT(pdev, cursor->unique); + internal = &pdev->cursor_cache[CURSOR_HASH_VAL(cursor->hsurf)]; + + while (*internal) { + if ((*internal)->hsurf == cursor->hsurf) { + if ((*internal) == cursor) { + *internal = cursor->next; + RingRemove(pdev, &cursor->lru_link); + RELEASE_RES(pdev, (Resource *)((UINT8 *)cursor - sizeof(Resource))); + pdev->num_cursors--; + return; + } + DEBUG_PRINT((pdev, 0, "%s: unexpected\n", __FUNCTION__)); + } + internal = &(*internal)->next; + } + ASSERT(pdev, FALSE); +} + +static void CursorCacheAdd(PDev *pdev, InternalCursor *cursor) +{ + int key; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + + if (!cursor->unique) { + return; + } + + if (pdev->num_cursors == CURSOR_CACHE_SIZE) { + ASSERT(pdev, RingGetTail(pdev, &pdev->cursors_lru)); + CursorCacheRemove(pdev, CONTAINEROF(RingGetTail(pdev, &pdev->cursors_lru), + InternalCursor, lru_link)); + } + + key = CURSOR_HASH_VAL(cursor->hsurf); + cursor->next = pdev->cursor_cache[key]; + pdev->cursor_cache[key] = cursor; + + RingAdd(pdev, &pdev->cursors_lru, &cursor->lru_link); + GET_RES((Resource *)((UINT8 *)cursor - sizeof(Resource))); + pdev->num_cursors++; +} + +static InternalCursor *CursorCacheGet(PDev *pdev, HSURF hsurf, UINT32 unique) +{ + InternalCursor **internal; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + if (!unique) { + return NULL; + } + + internal = &pdev->cursor_cache[CURSOR_HASH_VAL(hsurf)]; + while (*internal) { + InternalCursor *now = *internal; + if (now->hsurf == hsurf) { + if (now->unique == unique) { + RingRemove(pdev, &now->lru_link); + RingAdd(pdev, &pdev->cursors_lru, &now->lru_link); + return now; + } + CursorCacheRemove(pdev, now); + break; + } + internal = &now->next; + } + return NULL; +} + +static void FreeCursor(PDev *pdev, Resource *res) +{ + PHYSICAL chunk_phys; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + chunk_phys = ((InternalCursor *)res->res)->cursor.chunk.next_chunk; + while (chunk_phys) { + QXLDataChunk *chunk = (QXLDataChunk *)VA(pdev, chunk_phys); + chunk_phys = chunk->next_chunk; + FreeMem(pdev, res); + ONDBG(pdev->num_cursor_pages--); + } + + FreeMem(pdev, res); + ONDBG(pdev->num_cursor_pages--); + + DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); +} + + +typedef struct NewCursorInfo { + QXLCursor *cursor; + QXLDataChunk *chunk; + UINT8 *now; + UINT8 *end; +} NewCursorInfo; + +#define CURSOR_ALLOC_SIZE (PAGE_SIZE << 1) + +static BOOL GetCursorCommon(PDev *pdev, QXLCursorCmd *cmd, LONG hot_x, LONG hot_y, SURFOBJ *surf, + UINT16 type, NewCursorInfo *info) +{ + InternalCursor *internal; + QXLCursor *cursor; + Resource *res; + ULONG unique; + UINT8 *src; + UINT8 *src_end; + int line_size; + + + DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__)); + + unique = (surf->fjBitmap & BMF_DONTCACHE) ? 0 : surf->iUniq; + + if ((internal = CursorCacheGet(pdev, surf->hsurf, unique))) { + res = (Resource *)((UINT8 *)internal - sizeof(Resource)); + CursorCmdAddRes(pdev, cmd, res); + cmd->u.set.shape = PA(pdev, &internal->cursor); + return TRUE; + } + + ASSERT(pdev, sizeof(Resource) + sizeof(InternalCursor) < CURSOR_ALLOC_SIZE); + res = (Resource *)AllocMem(pdev, CURSOR_ALLOC_SIZE); + ONDBG(pdev->num_cursor_pages++); + res->refs = 1; + res->free = FreeCursor; + + internal = (InternalCursor *)res->res; + internal->hsurf = surf->hsurf; + internal->unique = unique; + RingItemInit(&internal->lru_link); + + cursor = info->cursor = &internal->cursor; + cursor->header.type = type; + cursor->header.unique = unique ? ++pdev->last_cursor_id : 0; + cursor->header.width = (UINT16)surf->sizlBitmap.cx; + cursor->header.height = (type == CURSOR_TYPE_MONO) ? (UINT16)surf->sizlBitmap.cy >> 1 : + (UINT16)surf->sizlBitmap.cy; + cursor->header.hot_spot_x = (UINT16)hot_x; + cursor->header.hot_spot_y = (UINT16)hot_y; + + cursor->data_size = 0; + + info->chunk = &cursor->chunk; + info->chunk->data_size = 0; + info->chunk->prev_chunk = 0; + info->chunk->next_chunk = 0; + + info->now = info->chunk->data; + info->end = (UINT8 *)res + CURSOR_ALLOC_SIZE; + + switch (type) { + case CURSOR_TYPE_ALPHA: + case CURSOR_TYPE_COLOR32: + line_size = cursor->header.width << 2; + break; + case CURSOR_TYPE_MONO: + line_size = ALIGN(cursor->header.width, 8) >> 3; + break; + case CURSOR_TYPE_COLOR4: + line_size = ALIGN(cursor->header.width, 2) >> 1; + break; + case CURSOR_TYPE_COLOR8: + line_size = cursor->header.width; + break; + case CURSOR_TYPE_COLOR16: + line_size = cursor->header.width << 1; + break; + case CURSOR_TYPE_COLOR24: + line_size = cursor->header.width * 3; + break; + } + + cursor->data_size = line_size * surf->sizlBitmap.cy; + src = surf->pvScan0; + src_end = src + (surf->lDelta * surf->sizlBitmap.cy); + for (; src != src_end; src += surf->lDelta) { + PutBytes(pdev, &info->chunk, &info->now, &info->end, src, line_size, + &pdev->num_cursor_pages, PAGE_SIZE); + } + + CursorCacheAdd(pdev, internal); + CursorCmdAddRes(pdev, cmd, res); + RELEASE_RES(pdev, res); + cmd->u.set.shape = PA(pdev, &internal->cursor); + DEBUG_PRINT((pdev, 11, "%s: done, data_size %u\n", __FUNCTION__, cursor->data_size)); + return FALSE; +} + +BOOL GetAlphaCursor(PDev *pdev, QXLCursorCmd *cmd, LONG hot_x, LONG hot_y, SURFOBJ *surf) +{ + NewCursorInfo info; + + ASSERT(pdev, surf->iBitmapFormat == BMF_32BPP); + ASSERT(pdev, surf->sizlBitmap.cx > 0 && surf->sizlBitmap.cy > 0); + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + GetCursorCommon(pdev, cmd, hot_x, hot_y, surf, CURSOR_TYPE_ALPHA, &info); + DEBUG_PRINT((pdev, 8, "%s: done\n", __FUNCTION__)); + return TRUE; +} + +BOOL GetMonoCursor(PDev *pdev, QXLCursorCmd *cmd, LONG hot_x, LONG hot_y, SURFOBJ *surf) +{ + NewCursorInfo info; + + ASSERT(pdev, surf->iBitmapFormat == BMF_1BPP); + ASSERT(pdev, surf->sizlBitmap.cy > 0 && (surf->sizlBitmap.cy & 1) == 0); + ASSERT(pdev, surf->sizlBitmap.cx > 0); + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + + GetCursorCommon(pdev, cmd, hot_x, hot_y, surf, CURSOR_TYPE_MONO, &info); + DEBUG_PRINT((pdev, 8, "%s: done\n", __FUNCTION__)); + return TRUE; +} + +BOOL GetColorCursor(PDev *pdev, QXLCursorCmd *cmd, LONG hot_x, LONG hot_y, SURFOBJ *surf, + SURFOBJ *mask, XLATEOBJ *color_trans) +{ + NewCursorInfo info; + UINT16 type; + + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + + ASSERT(pdev, surf && mask); + ASSERT(pdev, surf->sizlBitmap.cx > 0 && surf->sizlBitmap.cy); + + if ( mask->sizlBitmap.cx != surf->sizlBitmap.cx || + mask->sizlBitmap.cy != surf->sizlBitmap.cy * 2 ) { + DEBUG_PRINT((pdev, 0, "%s: err mask size, surf(%d, %d) mask(%d, %d)\n", + __FUNCTION__, + surf->sizlBitmap.cx, + surf->sizlBitmap.cy, + mask->sizlBitmap.cx, + mask->sizlBitmap.cy)); + return FALSE; + } + + switch (surf->iBitmapFormat) { + case BMF_32BPP: + type = CURSOR_TYPE_COLOR32; + break; + case BMF_24BPP: + type = CURSOR_TYPE_COLOR24; + break; + case BMF_16BPP: + type = CURSOR_TYPE_COLOR16; + break; + case BMF_8BPP: + type = CURSOR_TYPE_COLOR8; + break; + case BMF_4BPP: + type = CURSOR_TYPE_COLOR4; + break; + default: + DEBUG_PRINT((pdev, 0, "%s: unexpected format\n", __FUNCTION__)); + return FALSE; + } + if (!GetCursorCommon(pdev, cmd, hot_x, hot_y, surf, type, &info)) { + int line_size; + UINT8 *src; + UINT8 *src_end; + + if (type == CURSOR_TYPE_COLOR8) { + + DEBUG_PRINT((pdev, 8, "%s: CURSOR_TYPE_COLOR8\n", __FUNCTION__)); + ASSERT(pdev, color_trans); + ASSERT(pdev, color_trans->pulXlate); + ASSERT(pdev, color_trans->flXlate & XO_TABLE); + ASSERT(pdev, color_trans->cEntries == 256); + + if (pdev->bitmap_format == BMF_32BPP) { + PutBytes(pdev, &info.chunk, &info.now, &info.end, (UINT8 *)color_trans->pulXlate, + 256 << 2, &pdev->num_cursor_pages, PAGE_SIZE); + } else { + int i; + + for (i = 0; i < 256; i++) { + UINT32 ent = _16bppTo32bpp(color_trans->pulXlate[i]); + PutBytes(pdev, &info.chunk, &info.now, &info.end, (UINT8 *)&ent, + 4, &pdev->num_cursor_pages, PAGE_SIZE); + } + } + info.cursor->data_size += 256 << 2; + } else if (type == CURSOR_TYPE_COLOR4) { + + ASSERT(pdev, color_trans); + ASSERT(pdev, color_trans->pulXlate); + ASSERT(pdev, color_trans->flXlate & XO_TABLE); + ASSERT(pdev, color_trans->cEntries == 16); + + if (pdev->bitmap_format == BMF_32BPP) { + PutBytes(pdev, &info.chunk, &info.now, &info.end, (UINT8 *)color_trans->pulXlate, + 16 << 2, &pdev->num_cursor_pages, PAGE_SIZE); + } else { + int i; + + for (i = 0; i < 16; i++) { + UINT32 ent = _16bppTo32bpp(color_trans->pulXlate[i]); + PutBytes(pdev, &info.chunk, &info.now, &info.end, (UINT8 *)&ent, + 4, &pdev->num_cursor_pages, PAGE_SIZE); + } + } + info.cursor->data_size += 16 << 2; + } + line_size = ALIGN(mask->sizlBitmap.cx, 8) >> 3; + info.cursor->data_size += line_size * surf->sizlBitmap.cy; + src = mask->pvScan0; + src_end = src + (mask->lDelta * surf->sizlBitmap.cy); + + for (; src != src_end; src += mask->lDelta) { + PutBytes(pdev, &info.chunk, &info.now, &info.end, src, line_size, + &pdev->num_cursor_pages, PAGE_SIZE); + } + } + + DEBUG_PRINT((pdev, 8, "%s: done\n", __FUNCTION__)); + return TRUE; +} + +BOOL GetTransparentCursor(PDev *pdev, QXLCursorCmd *cmd) +{ + Resource *res; + InternalCursor *internal; + QXLCursor *cursor; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + ASSERT(pdev, sizeof(Resource) + sizeof(InternalCursor) < PAGE_SIZE); + + res = (Resource *)AllocMem(pdev, sizeof(Resource) + sizeof(InternalCursor)); + ONDBG(pdev->num_cursor_pages++); + res->refs = 1; + res->free = FreeCursor; + + internal = (InternalCursor *)res->res; + internal->hsurf = NULL; + internal->unique = 0; + RingItemInit(&internal->lru_link); + + cursor = &internal->cursor; + cursor->header.type = CURSOR_TYPE_MONO; + cursor->header.unique = 0; + cursor->header.width = 0; + cursor->header.height = 0; + cursor->header.hot_spot_x = 0; + cursor->header.hot_spot_y = 0; + cursor->data_size = 0; + cursor->chunk.data_size = 0; + + CursorCmdAddRes(pdev, cmd, res); + RELEASE_RES(pdev, res); + cmd->u.set.shape = PA(pdev, &internal->cursor); + + DEBUG_PRINT((pdev, 8, "%s: done\n", __FUNCTION__)); + return TRUE; +} + +static void quic_usr_error(QuicUsrContext *usr, const char *format, ...) +{ + QuicData *quic_data = (QuicData *)usr; + va_list ap; + + va_start(ap, format); + DebugPrintV(quic_data->pdev, format, ap); + va_end(ap); + EngDebugBreak(); +} + +static void quic_usr_warn(QuicUsrContext *usr, const char *format, ...) +{ + QuicData *quic_data = (QuicData *)usr; + va_list ap; + + va_start(ap, format); + DebugPrintV(quic_data->pdev, format, ap); + va_end(ap); +} + +static void *quic_usr_malloc(QuicUsrContext *usr, int size) +{ + return EngAllocMem(0, size, ALLOC_TAG); +} + +static void quic_usr_free(QuicUsrContext *usr, void *ptr) +{ + EngFreeMem(ptr); +} + +BOOL ResInit(PDev *pdev) +{ + QuicData *usr_data; + + if (!(usr_data = EngAllocMem(FL_ZERO_MEMORY, sizeof(QuicData), ALLOC_TAG))) { + return FALSE; + } + usr_data->user.error = quic_usr_error; + usr_data->user.warn = quic_usr_warn; + usr_data->user.info = quic_usr_warn; + usr_data->user.malloc = quic_usr_malloc; + usr_data->user.free = quic_usr_free; + usr_data->user.more_space = quic_usr_more_space; + usr_data->user.more_lines = quic_usr_more_lines; + usr_data->pdev = pdev; + if (!(usr_data->quic = quic_create(&usr_data->user))) { + EngFreeMem(usr_data); + return FALSE; + } + pdev->quic_data = usr_data; + return TRUE; +} + +void ResDestroy(PDev *pdev) +{ + QuicData *usr_data = pdev->quic_data; + quic_destroy(usr_data->quic); + EngFreeMem(usr_data); +} + +void ResInitGlobals() +{ + image_id_sem = EngCreateSemaphore(); + if (!image_id_sem) { + EngDebugBreak(); + } + quic_init(); +} + +void ResDestroyGlobals() +{ + EngDeleteSemaphore(image_id_sem); + image_id_sem = NULL; +} + diff --git a/display/res.h b/display/res.h new file mode 100644 index 0000000..9e2b261 --- /dev/null +++ b/display/res.h @@ -0,0 +1,56 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _H_RES +#define _H_RES + +#include "qxldd.h" + +UINT64 ReleaseOutput(PDev *pdev, UINT64 output_id); + +QXLDrawable *Drawable(PDev *pdev, UINT8 type, RECTL *area, CLIPOBJ *clip); +void PushDrawable(PDev *pdev, QXLDrawable *drawable); + +BOOL QXLGetPath(PDev *pdev, QXLDrawable *drawable, PHYSICAL *path_phys, PATHOBJ *path); +BOOL QXLGetMask(PDev *pdev, QXLDrawable *drawable, QMask *qxl_mask, SURFOBJ *mask, POINTL *pos, + BOOL invers, LONG width, LONG height); +BOOL QXLGetBrush(PDev *pdev, QXLDrawable *drawable, Brush *qxl_brush, + BRUSHOBJ *brush, POINTL *brush_pos); +BOOL QXLGetBitmap(PDev *pdev, QXLDrawable *drawable, PHYSICAL *image_phys, SURFOBJ *surf, + Rect *area, XLATEOBJ *color_trans, UINT32 *hash_key, BOOL use_cache); +BOOL QXLGetBitsFromCache(PDev *pdev, QXLDrawable *drawable, UINT32 hash_key, PHYSICAL *image_phys); +BOOL QXLGetAlphaBitmap(PDev *pdev, QXLDrawable *drawable, PHYSICAL *image_phys, SURFOBJ *surf, + Rect *area); +UINT8 *QXLGetBuf(PDev *pdev, QXLDrawable *drawable, PHYSICAL *buf_phys, UINT32 size); +BOOL QXLGetStr(PDev *pdev, QXLDrawable *drawable, PHYSICAL *str_phys, FONTOBJ *font, STROBJ *str); + +void UpdateArea(PDev *pdev, RECTL *area); + +QXLCursorCmd *CursorCmd(PDev *pdev); +void PushCursorCmd(PDev *pdev, QXLCursorCmd *cursor_cmd); + +BOOL GetAlphaCursor(PDev *pdev, QXLCursorCmd *cmd, LONG hot_x, LONG hot_y, SURFOBJ *surf); +BOOL GetColorCursor(PDev *pdev, QXLCursorCmd *cmd, LONG hot_x, LONG hot_y, SURFOBJ *surf, + SURFOBJ *mask, XLATEOBJ *color_trans); +BOOL GetMonoCursor(PDev *pdev, QXLCursorCmd *cmd, LONG hot_x, LONG hot_y, SURFOBJ *surf); +BOOL GetTransparentCursor(PDev *pdev, QXLCursorCmd *cmd); + +BOOL ResInit(PDev *pdev); +void ResDestroy(PDev *pdev); +void ResInitGlobals(); +void ResDestroyGlobals(); +#endif diff --git a/display/rop.c b/display/rop.c new file mode 100644 index 0000000..56ba3df --- /dev/null +++ b/display/rop.c @@ -0,0 +1,1526 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "os_dep.h" +#include "qxldd.h" +#include "utils.h" +#include "res.h" +#include "rop.h" + + +enum ROP3type { + ROP3_TYPE_ERR, + ROP3_TYPE_FILL, + ROP3_TYPE_OPAQUE, + ROP3_TYPE_COPY, + ROP3_TYPE_BLEND, + ROP3_TYPE_BLACKNESS, + ROP3_TYPE_WHITENESS, + ROP3_TYPE_INVERS, + ROP3_TYPE_ROP3, + ROP3_TYPE_NOP, +}; + + +ROP3Info rops2[] = { + {QXL_EFFECT_OPAQUE, 0, ROP3_TYPE_BLACKNESS, ROPD_OP_BLACKNESS}, //0 + {QXL_EFFECT_BLEND, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, ROPD_OP_OR | ROPD_INVERS_RES}, //DPon + {QXL_EFFECT_NOP_ON_DUP, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, + ROPD_INVERS_BRUSH | ROPD_OP_AND}, //DPna + {QXL_EFFECT_OPAQUE, ROP3_BRUSH, ROP3_TYPE_FILL, ROPD_INVERS_BRUSH | ROPD_OP_PUT}, //Pn + {QXL_EFFECT_BLACKNESS_ON_DUP, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, + ROPD_INVERS_DEST | ROPD_OP_AND}, //PDna + {QXL_EFFECT_REVERT_ON_DUP, ROP3_DEST, ROP3_TYPE_INVERS, ROPD_OP_INVERS}, //Dn + {QXL_EFFECT_REVERT_ON_DUP, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, ROPD_OP_XOR}, //DPx + {QXL_EFFECT_BLACKNESS_ON_DUP, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, + ROPD_OP_AND | ROPD_INVERS_RES}, //DPan + {QXL_EFFECT_NOP_ON_DUP, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, ROPD_OP_AND}, //DPa + {QXL_EFFECT_BLEND, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, ROPD_OP_XOR | ROPD_INVERS_RES},//DPxn + {QXL_EFFECT_NOP, ROP3_DEST, ROP3_TYPE_NOP, 0}, //D + {QXL_EFFECT_NOP_ON_DUP, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, + ROPD_INVERS_BRUSH | ROPD_OP_OR}, //DPno + {QXL_EFFECT_OPAQUE, ROP3_BRUSH, ROP3_TYPE_FILL, ROPD_OP_PUT}, //P + {QXL_EFFECT_BLEND, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, ROPD_INVERS_DEST | ROPD_OP_OR},//PDno + {QXL_EFFECT_NOP_ON_DUP, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, ROPD_OP_OR}, //DPo + {QXL_EFFECT_OPAQUE, 0, ROP3_TYPE_WHITENESS, ROPD_OP_WHITENESS}, //1 +}; + + +ROP3Info rops3[] = { + + //todo: update rop3 effect + + {QXL_EFFECT_OPAQUE, 0, ROP3_TYPE_BLACKNESS, 0}, //0 + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x01}, //DPSoon + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x02}, //DPSona + //PSon + {QXL_EFFECT_OPAQUE, ROP3_SRC | ROP3_BRUSH, ROP3_TYPE_OPAQUE, ROPD_OP_OR | ROPD_INVERS_RES}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x04}, //SDPona + //DPon + {QXL_EFFECT_BLEND, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, ROPD_OP_OR | ROPD_INVERS_RES}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x06}, //PDSxnon + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x07}, //PDSaon + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x08}, //SDPnaa + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x09}, //PDSxon + //DPna + {QXL_EFFECT_NOP_ON_DUP, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, + ROPD_INVERS_BRUSH | ROPD_OP_AND}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x0b}, //PSDnaon + //SPna + {QXL_EFFECT_OPAQUE, ROP3_SRC | ROP3_BRUSH, ROP3_TYPE_OPAQUE, ROPD_INVERS_BRUSH | ROPD_OP_AND }, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x0d}, //PDSnaon + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x0e}, //PDSonon + //Pn + {QXL_EFFECT_OPAQUE, ROP3_BRUSH, ROP3_TYPE_FILL, ROPD_INVERS_BRUSH | ROPD_OP_PUT}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x10}, //PDSona + //DSon + {QXL_EFFECT_BLEND, ROP3_SRC | ROP3_DEST, ROP3_TYPE_BLEND, ROPD_OP_OR | ROPD_INVERS_RES}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x12}, //SDPxnon + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x13}, //SDPaon + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x14}, //DPSxnon + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x15}, //DPSaon + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x16}, //PSDPSanaxx + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x17}, //SSPxDSxaxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x18}, //SPxPDxa + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x19}, //SDPSanaxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x1a}, //PDSPaox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x1b}, //SDPSxaxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x1c}, //PSDPaox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x1d}, //DSPDxaxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x1e}, //PDSox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x1f}, //PDSoan + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x20}, //DPSnaa + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x21}, //SDPxon + //DSna + {QXL_EFFECT_NOP_ON_DUP, ROP3_SRC | ROP3_DEST, ROP3_TYPE_BLEND, ROPD_INVERS_SRC | ROPD_OP_AND}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x23}, //SPDnaon + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x24}, //SPxDSxa + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x25}, //PDSPanaxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x26}, //SDPSaox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x27}, //SDPSxnox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x28}, //DPSxa + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x29}, //PSDPSaoxxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x2a}, //DPSana + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x2b}, //SSPxPDxaxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x2c}, //SPDSoax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x2d}, //PSDnox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x2e}, //PSDPxox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x2f}, //PSDnoan + //PSna + {QXL_EFFECT_OPAQUE, ROP3_SRC | ROP3_BRUSH, ROP3_TYPE_OPAQUE, ROPD_INVERS_SRC | ROPD_OP_AND}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x31}, //SDPnaon + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x32}, //SDPSoox + {QXL_EFFECT_OPAQUE, ROP3_SRC, ROP3_TYPE_COPY, ROPD_INVERS_SRC | ROPD_OP_PUT}, //Sn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x34}, //SPDSaox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x35}, //SPDSxnox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x36}, //SDPox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x37}, //SDPoan + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x38}, //PSDPoax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x39}, //SPDnox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x3a}, //SPDSxox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x3b}, //SPDnoan + {QXL_EFFECT_OPAQUE, ROP3_SRC | ROP3_BRUSH, ROP3_TYPE_OPAQUE, ROPD_OP_XOR}, //PSx + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x3d}, //SPDSonox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x3e}, //SPDSnaox + //PSan + {QXL_EFFECT_OPAQUE, ROP3_SRC | ROP3_BRUSH, ROP3_TYPE_OPAQUE, ROPD_OP_AND | ROPD_INVERS_RES}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x40}, //PSDnaa + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x41}, //DPSxon + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x42}, //SDxPDxa + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x43}, //SPDSanaxn + //SDna + {QXL_EFFECT_BLEND, ROP3_SRC | ROP3_DEST, ROP3_TYPE_BLEND, ROPD_INVERS_DEST | ROPD_OP_AND}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x45}, //DPSnaon + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x46}, //DSPDaox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x47}, //PSDPxaxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x48}, //SDPxa + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x49}, //PDSPDaoxxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x4a}, //DPSDoax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x4b}, //PDSnox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x4c}, //SDPana + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x4d}, //SSPxDSxoxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x4e}, //PDSPxox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x4f}, //PDSnoan + //PDna + {QXL_EFFECT_BLEND, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, ROPD_INVERS_DEST | ROPD_OP_AND}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x51}, //DSPnaon + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x52}, //DPSDaox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x53}, //SPDSxaxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x54}, //DPSonon + {QXL_EFFECT_NOP_ON_DUP, ROP3_DEST, ROP3_TYPE_INVERS, 0}, //Dn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x56}, //DPSox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x57}, //DPSoan + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x58}, //PDSPoax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x59}, //DPSnox + {QXL_EFFECT_REVERT_ON_DUP, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, ROPD_OP_XOR},//DPx + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x5b}, //DPSDonox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x5c}, //DPSDxox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x5d}, //DPSnoan + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x5e}, //DPSDnaox + //DPan + {QXL_EFFECT_BLEND, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, ROPD_OP_AND | ROPD_INVERS_RES}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x60}, //PDSxa + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x61}, //DSPDSaoxxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x62}, //DSPDoax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x63}, //SDPnox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x64}, //SDPSoax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x65}, //DSPnox + {QXL_EFFECT_REVERT_ON_DUP, ROP3_SRC | ROP3_DEST, ROP3_TYPE_BLEND, ROPD_OP_XOR}, //DSx + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x67}, //SDPSonox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x68}, //DSPDSonoxxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x69}, //PDSxxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x6a}, //DPSax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x6b}, //PSDPSoaxxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x6c}, //SDPax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x6d}, //PDSPDoaxxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x6e}, //SDPSnoax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x6f}, //PDSxnan + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x70}, //PDSana + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x71}, //SSDxPDxaxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x72}, //SDPSxox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x73}, //SDPnoan + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x74}, //DSPDxox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x75}, //DSPnoan + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x76}, //SDPSnaox + //DSan + {QXL_EFFECT_BLEND, ROP3_SRC | ROP3_DEST, ROP3_TYPE_BLEND, ROPD_OP_AND | ROPD_INVERS_RES}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x78}, //PDSax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x79}, //DSPDSoaxxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x7a}, //DPSDnoax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x7b}, //SDPxnan + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x7c}, //SPDSnoax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x7d}, //DPSxnan + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x7e}, //SPxDSxo + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x7f}, //DPSaan + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x80}, //DPSaa + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x81}, //SPxDSxon + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x82}, //DPSxna + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x83}, //SPDSnoaxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x84}, //SDPxna + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x85}, //PDSPnoaxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x86}, //DSPDSoaxx + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x87}, //PDSaxn + {QXL_EFFECT_NOP_ON_DUP, ROP3_SRC | ROP3_DEST, ROP3_TYPE_BLEND, ROPD_OP_AND}, //DSa + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x89}, //SDPSnaoxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x8a}, //DSPnoa + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x8b}, //DSPDxoxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x8c}, //SDPnoa + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x8d}, //SDPSxoxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x8e}, //SSDxPDxax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x8f}, //PDSanan + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x90}, //PDSxna + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x91}, //SDPSnoaxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x92}, //DPSDPoaxx + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x93}, //SPDaxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x94}, //PSDPSoaxx + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x95}, //DPSaxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x96}, //DPSxx + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x97}, //PSDPSonoxx + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x98}, //SDPSonoxn + //DSxn + {QXL_EFFECT_BLEND, ROP3_SRC | ROP3_DEST, ROP3_TYPE_BLEND, ROPD_OP_XOR | ROPD_INVERS_RES}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x9a}, //DPSnax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x9b}, //SDPSoaxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x9c}, //SPDnax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x9d}, //DSPDoaxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x9e}, //DSPDSaoxx + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x9f}, //PDSxan + {QXL_EFFECT_NOP_ON_DUP, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, ROPD_OP_AND}, //DPa + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xa1}, //PDSPnaoxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xa2}, //DPSnoa + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xa3}, //DPSDxoxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xa4}, //PDSPonoxn + //PDxn + {QXL_EFFECT_BLEND, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, ROPD_OP_XOR | ROPD_INVERS_RES}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xa6}, //DSPnax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xa7}, //PDSPoaxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xa8}, //DPSoa + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xa9}, //DPSoxn + {QXL_EFFECT_NOP, ROP3_DEST, ROP3_TYPE_NOP, 0}, //D + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xab}, //DPSono + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xac}, //SPDSxax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xad}, //DPSDaoxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xae}, //DSPnao + //DPno + {QXL_EFFECT_NOP_ON_DUP, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, ROPD_INVERS_BRUSH | ROPD_OP_OR}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xb0}, //PDSnoa + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xb1}, //PDSPxoxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xb2}, //SSPxDSxox + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xb3}, //SDPanan + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xb4}, //PSDnax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xb5}, //DPSDoaxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xb6}, //DPSDPaoxx + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xb7}, //SDPxan + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xb8}, //PSDPxax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xb9}, //DSPDaoxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xba}, //DPSnao + //DSno + {QXL_EFFECT_NOP_ON_DUP, ROP3_SRC | ROP3_DEST, ROP3_TYPE_BLEND, ROPD_INVERS_SRC | ROPD_OP_OR}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xbc}, //SPDSanax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xbd}, //SDxPDxan + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xbe}, //DPSxo + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xbf}, //DPSano + {QXL_EFFECT_OPAQUE, ROP3_SRC | ROP3_BRUSH, ROP3_TYPE_OPAQUE, ROPD_OP_AND}, //PSa + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xc1}, //SPDSnaoxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xc2}, //SPDSonoxn + //PSxn + {QXL_EFFECT_OPAQUE, ROP3_SRC | ROP3_BRUSH, ROP3_TYPE_OPAQUE, ROPD_OP_XOR | ROPD_INVERS_RES}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xc4}, //SPDnoa + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xc5}, //SPDSxoxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xc6}, //SDPnax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xc7}, //PSDPoaxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xc8}, //SDPoa + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xc9}, //SPDoxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xca}, //DPSDxax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xcb}, //SPDSaoxn + {QXL_EFFECT_OPAQUE, ROP3_SRC, ROP3_TYPE_COPY, ROPD_OP_PUT}, //S + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xcd}, //SDPono + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xce}, //SDPnao + //SPno + {QXL_EFFECT_OPAQUE, ROP3_SRC | ROP3_BRUSH, ROP3_TYPE_OPAQUE, ROPD_INVERS_BRUSH | ROPD_OP_OR}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xd0}, //PSDnoa + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xd1}, //PSDPxoxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xd2}, //PDSnax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xd3}, //SPDSoaxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xd4}, //SSPxPDxax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xd5}, //DPSanan + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xd6}, //PSDPSaoxx + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xd7}, //DPSxan + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xd8}, //PDSPxax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xd9}, //SDPSaoxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xda}, //DPSDanax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xdb}, //SPxDSxan + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xdc}, //SPDnao + //SDno + {QXL_EFFECT_BLEND, ROP3_SRC | ROP3_DEST, ROP3_TYPE_BLEND, ROPD_INVERS_DEST | ROPD_OP_OR}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xde}, //SDPxo + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xdf}, //SDPano + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xe0}, //PDSoa + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xe1}, //PDSoxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xe2}, //DSPDxax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xe3}, //PSDPaoxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xe4}, //SDPSxax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xe5}, //PDSPaoxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xe6}, //SDPSanax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xe7}, //SPxPDxan + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xe8}, //SSPxDSxax + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xe9}, //DSPDSanaxxn + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xea}, //DPSao + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xeb}, //DPSxno + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xec}, //SDPao + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xed}, //SDPxno + {QXL_EFFECT_NOP_ON_DUP, ROP3_SRC | ROP3_DEST, ROP3_TYPE_BLEND, ROPD_OP_OR}, //DSo + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xef}, //SDPnoo + {QXL_EFFECT_OPAQUE, ROP3_BRUSH, ROP3_TYPE_FILL, ROPD_OP_PUT}, //P + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xf1}, //PDSono + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xf2}, //PDSnao + //PSno + {QXL_EFFECT_OPAQUE, ROP3_SRC | ROP3_BRUSH, ROP3_TYPE_OPAQUE, ROPD_INVERS_SRC | ROPD_OP_OR}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xf4}, //PSDnao + //PDno + {QXL_EFFECT_BLEND, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, ROPD_INVERS_DEST | ROPD_OP_OR}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xf6}, //PDSxo + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xf7}, //PDSano + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xf8}, //PDSao + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xf9}, //PDSxno + {QXL_EFFECT_NOP_ON_DUP, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, ROPD_OP_OR}, //DPo + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xfb}, //DPSnoo + {QXL_EFFECT_OPAQUE, ROP3_SRC | ROP3_BRUSH, ROP3_TYPE_OPAQUE, ROPD_OP_OR}, //PSo + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xfd}, //PSDnoo + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xfe}, //DPSoo + {QXL_EFFECT_OPAQUE, 0, ROP3_TYPE_WHITENESS, 1}, //1 +}; + + +static BOOL DoFill(PDev *pdev, RECTL *area, CLIPOBJ *clip, BRUSHOBJ *brush, POINTL *brush_pos, + ROP3Info *rop_info, SURFOBJ *mask, POINTL *mask_pos, BOOL invers_mask) +{ + QXLDrawable *drawable; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + ASSERT(pdev, pdev && area && brush); + + if (!(drawable = Drawable(pdev, QXL_DRAW_FILL, area, clip))) { + return FALSE; + } + + if (!QXLGetBrush(pdev, drawable, &drawable->u.fill.brush, brush, brush_pos) || + !QXLGetMask(pdev, drawable, &drawable->u.fill.mask, mask, mask_pos, invers_mask, + area->right - area->left, area->bottom - area->top)) { + ReleaseOutput(pdev, drawable->release_info.id); + return FALSE; + } + + drawable->u.fill.rop_decriptor = rop_info->method_data; + + drawable->effect = mask ? QXL_EFFECT_BLEND : rop_info->effect; + + PushDrawable(pdev, drawable); + return TRUE; +} + +static BOOL GetBitmap(PDev *pdev, QXLDrawable *drawable, PHYSICAL *bitmap_phys, SURFOBJ *surf, + Rect *area, XLATEOBJ *color_trans, BOOL use_cache) +{ + DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__)); + if (surf->iType != STYPE_BITMAP) { + ASSERT(pdev, (PDev *)surf->dhpdev == pdev); + DEBUG_PRINT((pdev, 9, "%s copy from self\n", __FUNCTION__)); + *bitmap_phys = 0; + drawable->bitmap_offset = (UINT16)((UINT8 *)bitmap_phys - (UINT8 *)drawable); + drawable->bitmap_area = *area; + area->right = area->right - area->left; + area->left = 0; + area->bottom = area->bottom - area->top; + area->top = 0; + return TRUE; + } + return QXLGetBitmap(pdev, drawable, &drawable->u.opaque.src_bitmap, surf, + area, color_trans, NULL, use_cache); +} + +static _inline UINT8 GdiScaleModeToQxl(ULONG scale_mode) +{ + return (scale_mode == HALFTONE) ? IMAGE_SCALE_INTERPOLATE : IMAGE_SCALE_NEAREST; +} + +static BOOL DoOpaque(PDev *pdev, RECTL *area, CLIPOBJ *clip, SURFOBJ *src, RECTL *src_rect, + XLATEOBJ *color_trans, BRUSHOBJ *brush, POINTL *brush_pos, + UINT16 rop_decriptor, SURFOBJ *mask, POINTL *mask_pos, BOOL invers_mask, + ULONG scale_mode) +{ + QXLDrawable *drawable; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + ASSERT(pdev, pdev && area && brush && src_rect && src); + + if (!(drawable = Drawable(pdev, QXL_DRAW_OPAQUE, area, clip))) { + return FALSE; + } + + drawable->u.opaque.scale_mode = GdiScaleModeToQxl(scale_mode); + CopyRect(&drawable->u.opaque.src_area, src_rect); + if (!QXLGetBrush(pdev, drawable, &drawable->u.opaque.brush, brush, brush_pos) || + !QXLGetMask(pdev, drawable, &drawable->u.opaque.mask, mask, mask_pos, invers_mask, + area->right - area->left, area->bottom - area->top) || + !GetBitmap(pdev, drawable, &drawable->u.opaque.src_bitmap, src, + &drawable->u.opaque.src_area, color_trans, TRUE)) { + ReleaseOutput(pdev, drawable->release_info.id); + return FALSE; + } + + drawable->u.opaque.rop_decriptor = rop_decriptor; + drawable->effect = mask ? QXL_EFFECT_BLEND : QXL_EFFECT_OPAQUE; + PushDrawable(pdev, drawable); + return TRUE; +} + +static BOOL StreamTest(PDev *pdev, SURFOBJ *src_surf, XLATEOBJ *color_trans, RECTL *src_rect, + RECTL *dest) +{ + Ring *ring = &pdev->update_trace; + UpdateTrace *trace = (UpdateTrace *)ring->next; + LONG src_pixmap_pixels = src_surf->sizlBitmap.cx * src_surf->sizlBitmap.cy; + + if (src_pixmap_pixels <= 128 * 128) { + return TRUE; + } + + for (;;) { + if (SameRect(dest, &trace->area) || (trace->hsurf == src_surf->hsurf && + src_pixmap_pixels / RectSize(src_rect) > 100)) { + UINT32 now = *pdev->mm_clock; + BOOL ret; + + if (now != trace->last_time && now - trace->last_time < 1000 / 5) { + ret = FALSE; + trace->last_time = now - 1; // asumong mm clock is active so delta t == 0 is + // imposibole. frocing delata t to be at least 1. + } else { + trace->last_time = now; + ret = TRUE; + } + RingRemove(pdev, (RingItem *)trace); + RingAdd(pdev, ring, (RingItem *)trace); + return ret; + } + if (trace->link.next == ring) { + break; + } + trace = (UpdateTrace *)trace->link.next; + } + RingRemove(pdev, (RingItem *)trace); + trace->area = *dest; + trace->last_time = *pdev->mm_clock; + + if (IsUniqueSurf(src_surf, color_trans)) { + trace->hsurf = src_surf->hsurf; + } else { + trace->hsurf = NULL; + } + RingAdd(pdev, ring, (RingItem *)trace); + + return TRUE; +} + +static BOOL DoCopy(PDev *pdev, RECTL *area, CLIPOBJ *clip, SURFOBJ *src, RECTL *src_rect, + XLATEOBJ *color_trans, UINT16 rop_decriptor, SURFOBJ *mask, POINTL *mask_pos, + BOOL invers_mask, ULONG scale_mode) +{ + QXLDrawable *drawable; + BOOL use_cache; + + ASSERT(pdev, pdev && area && src_rect && src); + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + + if (!(drawable = Drawable(pdev, QXL_DRAW_COPY, area, clip))) { + return FALSE; + } + + if (mask) { + drawable->effect = QXL_EFFECT_BLEND; + use_cache = TRUE; + } else { + drawable->effect = QXL_EFFECT_OPAQUE; + use_cache = StreamTest(pdev, src, color_trans, src_rect, area); + } + + drawable->u.copy.scale_mode = GdiScaleModeToQxl(scale_mode); + CopyRect(&drawable->u.copy.src_area, src_rect); + if (!QXLGetMask(pdev, drawable, &drawable->u.copy.mask, mask, mask_pos, invers_mask, + area->right - area->left, area->bottom - area->top) || + !GetBitmap(pdev, drawable, &drawable->u.copy.src_bitmap, src, &drawable->u.copy.src_area, + color_trans, use_cache)) { + ReleaseOutput(pdev, drawable->release_info.id); + return FALSE; + } + + drawable->u.copy.rop_decriptor = rop_decriptor; + PushDrawable(pdev, drawable); + DEBUG_PRINT((pdev, 7, "%s: done\n", __FUNCTION__)); + return TRUE; +} + +static BOOL DoCopyBits(PDev *pdev, CLIPOBJ *clip, RECTL *area, POINTL *src_pos) +{ + QXLDrawable *drawable; + + ASSERT(pdev, pdev && area && src_pos); + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + + if (area->left == src_pos->x && area->top == src_pos->y) { + DEBUG_PRINT((pdev, 0, "%s: NOP\n", __FUNCTION__)); + return TRUE; + } + + if (!(drawable = Drawable(pdev, QXL_COPY_BITS, area, clip))) { + return FALSE; + } + CopyPoint(&drawable->u.copy_bits.src_pos, src_pos); + drawable->effect = QXL_EFFECT_OPAQUE; + PushDrawable(pdev, drawable); + return TRUE; +} + +static BOOL DoBlend(PDev *pdev, RECTL *area, CLIPOBJ *clip, SURFOBJ *src, RECTL *src_rect, + XLATEOBJ *color_trans, ROP3Info *rop_info, SURFOBJ *mask, POINTL *mask_pos, + BOOL invers_mask, ULONG scale_mode) +{ + QXLDrawable *drawable; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + ASSERT(pdev, pdev && area && src_rect && src); + + if (!(drawable = Drawable(pdev, QXL_DRAW_BLEND, area, clip))) { + return FALSE; + } + + drawable->u.blend.scale_mode = GdiScaleModeToQxl(scale_mode); + CopyRect(&drawable->u.blend.src_area, src_rect); + if (!QXLGetMask(pdev, drawable, &drawable->u.blend.mask, mask, mask_pos, invers_mask, + area->right - area->left, area->bottom - area->top) || + !GetBitmap(pdev, drawable, &drawable->u.blend.src_bitmap, src, &drawable->u.blend.src_area, + color_trans, TRUE)) { + ReleaseOutput(pdev, drawable->release_info.id); + return FALSE; + } + + drawable->u.blend.rop_decriptor = rop_info->method_data; + drawable->effect = mask ? QXL_EFFECT_BLEND : rop_info->effect; + PushDrawable(pdev, drawable); + return TRUE; +} + +static BOOL DoBlackness(PDev *pdev, RECTL *area, CLIPOBJ *clip, SURFOBJ *mask, POINTL *mask_pos, + BOOL invers_mask) +{ + QXLDrawable *drawable; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + ASSERT(pdev, pdev && area); + + if (!(drawable = Drawable(pdev, QXL_DRAW_BLACKNESS, area, clip))) { + return FALSE; + } + + if (!QXLGetMask(pdev, drawable, &drawable->u.blackness.mask, mask, mask_pos, invers_mask, + area->right - area->left, area->bottom - area->top)) { + ReleaseOutput(pdev, drawable->release_info.id); + return FALSE; + } + + drawable->effect = mask ? QXL_EFFECT_BLEND : QXL_EFFECT_OPAQUE; + PushDrawable(pdev, drawable); + return TRUE; +} + +static BOOL DoWhiteness(PDev *pdev, RECTL *area, CLIPOBJ *clip, SURFOBJ *mask, POINTL *mask_pos, + BOOL invers_mask) +{ + QXLDrawable *drawable; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + ASSERT(pdev, pdev && area); + + if (!(drawable = Drawable(pdev, QXL_DRAW_WHITENESS, area, clip))) { + return FALSE; + } + + if (!QXLGetMask(pdev, drawable, &drawable->u.whiteness.mask, mask, mask_pos, invers_mask, + area->right - area->left, area->bottom - area->top)) { + ReleaseOutput(pdev, drawable->release_info.id); + return FALSE; + } + + drawable->effect = mask ? QXL_EFFECT_BLEND : QXL_EFFECT_OPAQUE; + PushDrawable(pdev, drawable); + return TRUE; +} + +static BOOL DoInvers(PDev *pdev, RECTL *area, CLIPOBJ *clip, SURFOBJ *mask, POINTL *mask_pos, + BOOL invers_mask) +{ + QXLDrawable *drawable; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + ASSERT(pdev, pdev && area); + + if (!(drawable = Drawable(pdev, QXL_DRAW_INVERS, area, clip))) { + return FALSE; + } + + if (!QXLGetMask(pdev, drawable, &drawable->u.invers.mask, mask, mask_pos, invers_mask, + area->right - area->left, area->bottom - area->top)) { + ReleaseOutput(pdev, drawable->release_info.id); + return FALSE; + } + + drawable->effect = mask ? QXL_EFFECT_BLEND : QXL_EFFECT_REVERT_ON_DUP; + PushDrawable(pdev, drawable); + return TRUE; +} + +static BOOL DoROP3(PDev *pdev, RECTL *area, CLIPOBJ *clip, SURFOBJ *src, RECTL *src_rect, + XLATEOBJ *color_trans, BRUSHOBJ *brush, POINTL *brush_pos, UINT8 rop3, + SURFOBJ *mask, POINTL *mask_pos, BOOL invers_mask, ULONG scale_mode) +{ + QXLDrawable *drawable; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + ASSERT(pdev, pdev && area && brush && src_rect && src); + + if (!(drawable = Drawable(pdev, QXL_DRAW_ROP3, area, clip))) { + return FALSE; + } + + drawable->u.rop3.scale_mode = GdiScaleModeToQxl(scale_mode); + CopyRect(&drawable->u.rop3.src_area, src_rect); + if (!QXLGetBrush(pdev, drawable, &drawable->u.rop3.brush, brush, brush_pos) || + !QXLGetMask(pdev, drawable, &drawable->u.rop3.mask, mask, mask_pos, invers_mask, + area->right - area->left, area->bottom - area->top) || + !GetBitmap(pdev, drawable, &drawable->u.rop3.src_bitmap, src, &drawable->u.rop3.src_area, + color_trans, TRUE)) { + ReleaseOutput(pdev, drawable->release_info.id); + return FALSE; + } + + drawable->u.rop3.rop3 = rop3; + drawable->effect = mask ? QXL_EFFECT_BLEND : QXL_EFFECT_BLEND; //for now + PushDrawable(pdev, drawable); + return TRUE; +} + +static SURFOBJ *Copy16bppArea(PDev *pdev, RECTL *area) +{ + SIZEL size; + HSURF bitmap; + SURFOBJ *surf_obj; + UINT8 *dest_line; + UINT8 *dest_end_line; + LONG src_stride; + UINT8 *src_line; + + size.cx = area->right - area->left; + size.cy = area->bottom - area->top; + + if (!(bitmap = (HSURF)EngCreateBitmap(size, 0, BMF_16BPP, 0, NULL))) { + DEBUG_PRINT((pdev, 0, "%s: EngCreateBitmap failed\n", __FUNCTION__)); + return NULL; + } + + if (!EngAssociateSurface(bitmap, pdev->eng, 0)) { + DEBUG_PRINT((pdev, 0, "%s: EngAssociateSurface failed\n", __FUNCTION__)); + goto error; + } + + if (!(surf_obj = EngLockSurface(bitmap))) { + DEBUG_PRINT((pdev, 0, "%s: EngLockSurface failed\n", __FUNCTION__)); + goto error; + } + + dest_line = surf_obj->pvScan0; + dest_end_line = dest_line + surf_obj->lDelta * surf_obj->sizlBitmap.cy; + src_stride = pdev->draw_surf->lDelta; + src_line = (UINT8 *)pdev->draw_surf->pvScan0 + area->top * src_stride + (area->left << 2); + + for (; dest_line != dest_end_line; dest_line += surf_obj->lDelta, src_line += src_stride) { + UINT16 *dest = (UINT16 *)dest_line; + UINT16 *end = dest + surf_obj->sizlBitmap.cx; + UINT32 *src = (UINT32 *)src_line; + for (; dest < end; dest++, src++) { + *dest = ((*src & 0x00f80000) >> 9) | + ((*src & 0x0000f800) >> 6) | + ((*src & 0x000000f8) >> 3); + } + } + return surf_obj; + +error: + EngDeleteSurface(bitmap); + return NULL; + +} + +static BOOL BitBltFromDev(PDev *pdev, SURFOBJ *dest, SURFOBJ *mask, CLIPOBJ *clip, + XLATEOBJ *color_trans, RECTL *dest_rect, POINTL src_pos, + POINTL *mask_pos, BRUSHOBJ *brush, POINTL *brush_pos, ROP4 rop4) +{ + RECTL area; + SURFOBJ* surf_obj; + BOOL ret; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + + area.top = MAX(0, src_pos.y); + area.bottom = MIN(src_pos.y + dest_rect->bottom - dest_rect->top, pdev->resolution.cy); + area.left = MAX(0, src_pos.x); + area.right = MIN(src_pos.x + dest_rect->right - dest_rect->left, pdev->resolution.cx); + + UpdateArea(pdev, &area); + + if (pdev->bitmap_format == BMF_16BPP) { + surf_obj = Copy16bppArea(pdev, &area); + if (!surf_obj) { + return FALSE; + } + src_pos.y = src_pos.y - area.top; + src_pos.x = src_pos.x - area.left; + } else { + surf_obj = pdev->draw_surf; + } + + if (rop4 == 0xcccc) { + ret = EngCopyBits(dest, surf_obj, clip, color_trans, dest_rect, &src_pos); + } else { + ret = EngBitBlt(dest, surf_obj, mask, clip, color_trans, dest_rect, &src_pos, + mask_pos, brush, brush_pos, rop4); + } + + if (pdev->bitmap_format == BMF_16BPP) { + HSURF surf = surf_obj->hsurf; + + EngUnlockSurface(surf_obj); + EngDeleteSurface(surf); + } + + return ret; +} + +BOOL _inline __DrvBitBlt(PDev *pdev, RECTL *dest_rect, CLIPOBJ *clip, SURFOBJ *src, RECTL *src_rect, + XLATEOBJ *color_trans, BRUSHOBJ *brush, POINTL *brush_pos, ULONG rop3, + SURFOBJ *mask, POINTL *mask_pos, BOOL invers_mask, ULONG scale_mode) +{ + ROP3Info *rop_info = &rops3[rop3]; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + + switch (rop_info->method_type) { + case ROP3_TYPE_FILL: + return DoFill(pdev, dest_rect, clip, brush, brush_pos, rop_info, mask, mask_pos, + invers_mask); + case ROP3_TYPE_OPAQUE: + return DoOpaque(pdev, dest_rect, clip, src, src_rect, color_trans, brush, brush_pos, + rop_info->method_data, mask, mask_pos, invers_mask, scale_mode); + case ROP3_TYPE_COPY: + return DoCopy(pdev, dest_rect, clip, src, src_rect, color_trans, rop_info->method_data, + mask, mask_pos, invers_mask, scale_mode); + case ROP3_TYPE_BLEND: + return DoBlend(pdev, dest_rect, clip, src, src_rect, color_trans, rop_info, mask, mask_pos, + invers_mask, scale_mode); + case ROP3_TYPE_BLACKNESS: + return DoBlackness(pdev, dest_rect, clip, mask, mask_pos, invers_mask); + case ROP3_TYPE_WHITENESS: + return DoWhiteness(pdev, dest_rect, clip, mask, mask_pos, invers_mask); + case ROP3_TYPE_INVERS: + return DoInvers(pdev, dest_rect, clip, mask, mask_pos, invers_mask); + case ROP3_TYPE_ROP3: + return DoROP3(pdev, dest_rect, clip, src, src_rect, color_trans, brush, brush_pos, + (UINT8)rop_info->method_data, mask, mask_pos, invers_mask, scale_mode); + case ROP3_TYPE_NOP: + return TRUE; + default: + DEBUG_PRINT((pdev, 0, "%s: Error\n", __FUNCTION__)); + //EngSetError + return FALSE; + } +} + +#ifdef SUPPORT_BRUSH_AS_MASK +SURFOBJ *BrushToMask(PDev *pdev, BRUSHOBJ *brush) +{ + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + + if (!brush || brush->iSolidColor != ~0) { + DEBUG_PRINT((pdev, 8, "%s: no mask, brush 0x%x color 0x%x\n", + __FUNCTION__, brush, brush ? brush->iSolidColor : 0)); + return NULL; + } + + if (!brush->pvRbrush && !BRUSHOBJ_pvGetRbrush(brush)) { + DEBUG_PRINT((pdev, 0, "%s: realize failed\n", __FUNCTION__)); + return NULL; + } + DEBUG_PRINT((pdev, 7, "%s: done 0x%x\n", __FUNCTION__, brush->pvRbrush)); + return NULL; +} +#endif + + +static _inline BOOL TestSrcBits(PDev *pdev, SURFOBJ *src, XLATEOBJ *color_trans) +{ + if (src) { + switch (src->iBitmapFormat) { + case BMF_32BPP: + case BMF_24BPP: + case BMF_16BPP: { + ULONG bit_fields[3]; + ULONG ents; + + if (!color_trans || (color_trans->flXlate & XO_TRIVIAL)) { + return TRUE; + } + + ents = XLATEOBJ_cGetPalette(color_trans, XO_SRCBITFIELDS, 3, bit_fields); + ASSERT(pdev, ents == 3); + switch (src->iBitmapFormat) { + case BMF_32BPP: + case BMF_24BPP: + if (bit_fields[0] != 0x00ff0000 || bit_fields[1] != 0x0000ff00 || + bit_fields[2] != 0x000000ff) { + DEBUG_PRINT((pdev, 11, "%s: BMF_32BPP/24BPP r 0x%x g 0x%x b 0x%x\n", + __FUNCTION__, + bit_fields[0], + bit_fields[1], + bit_fields[2])); + return FALSE; + } + break; + case BMF_16BPP: + if (bit_fields[0] != 0x7c00 || bit_fields[1] != 0x03e0 || + bit_fields[2] != 0x001f) { + DEBUG_PRINT((pdev, 11, "%s: BMF_16BPP r 0x%x g 0x%x b 0x%x\n", + __FUNCTION__, + bit_fields[0], + bit_fields[1], + bit_fields[2])); + return FALSE; + } + break; + } + return TRUE; + } + case BMF_8BPP: + case BMF_4BPP: + case BMF_1BPP: + return color_trans && (color_trans->flXlate & XO_TABLE); + default: + return FALSE; + } + } + return TRUE; +} + +static QXLRESULT BitBltCommon(PDev *pdev, SURFOBJ *dest, SURFOBJ *src, SURFOBJ *mask, CLIPOBJ *clip, + XLATEOBJ *color_trans, RECTL *dest_rect, RECTL *src_rect, + POINTL *mask_pos, BRUSHOBJ *brush, POINTL *brush_pos, ROP4 rop4, + ULONG scale_mode, COLORADJUSTMENT *color_adjust) +{ + ULONG rop3; + ULONG second_rop3; +#ifdef SUPPORT_BRUSH_AS_MASK + SURFOBJ *brush_mask = NULL; +#endif + QXLRESULT res; + + if (!PrepareBrush(brush)) { + return QXL_FAILED; + } + + if ((rop3 = rop4 & 0xff) == (second_rop3 = ((rop4 >> 8) & 0xff))) { + return __DrvBitBlt(pdev, dest_rect, clip, src, src_rect, color_trans, brush, brush_pos, + rop3, NULL, NULL, FALSE, scale_mode) ? QXL_SUCCESS : QXL_FAILED; + } + + if (!mask) { + DEBUG_PRINT((pdev, 5, "%s: no mask. rop4 is 0x%x\n", __FUNCTION__, rop4)); + return QXL_UNSUPPORTED; +#ifdef SUPPORT_BRUSH_AS_MASK + brush_mask = BrushToMask(pdev, brush); + if (!brush_mask) { + DEBUG_PRINT((pdev, 5, "%s: no mask. rop4 is 0x%x\n", __FUNCTION__, rop4)); + return QXL_UNSUPPORTED; + } + mask = brush_mask; + ASSERT(pdev, mask_pos); +#endif + } + DEBUG_PRINT((pdev, 5, "%s: mask, rop4 is 0x%x\n", __FUNCTION__, rop4)); + ASSERT(pdev, mask_pos); + res = (__DrvBitBlt(pdev, dest_rect, clip, src, src_rect, color_trans, brush, brush_pos, rop3, + mask, mask_pos, FALSE, scale_mode) && + __DrvBitBlt(pdev, dest_rect, clip, src, src_rect, color_trans, brush, brush_pos, + second_rop3, mask, mask_pos, TRUE, scale_mode)) ? QXL_SUCCESS : QXL_FAILED; +#ifdef SUPPORT_BRUSH_AS_MASK + if (brush_mask) { + //free brush_mask; + } +#endif + return res; +} + +static _inline void FixDestParams(PDev *pdev, SURFOBJ *dest, CLIPOBJ **in_clip, + RECTL *dest_rect, RECTL *area, POINTL **in_mask_pos, + POINTL *local_mask_pos) +{ + CLIPOBJ *clip; + + area->top = MAX(dest_rect->top, 0); + area->left = MAX(dest_rect->left, 0); + area->bottom = MIN(dest->sizlBitmap.cy, dest_rect->bottom); + area->right = MIN(dest->sizlBitmap.cx, dest_rect->right); + + clip = *in_clip; + if (clip) { + if (clip->iDComplexity == DC_TRIVIAL) { + clip = NULL; + } else { + SectRect(&clip->rclBounds, area, area); + if (clip->iDComplexity == DC_RECT) { + clip = NULL; + } + } + *in_clip = clip; + } + + if (in_mask_pos && *in_mask_pos) { + POINTL *mask_pos; + ASSERT(pdev, local_mask_pos); + mask_pos = *in_mask_pos; + local_mask_pos->x = mask_pos->x + (area->left - dest_rect->left); + local_mask_pos->y = mask_pos->y + (area->top - dest_rect->top); + *in_mask_pos = local_mask_pos; + } +} + +static QXLRESULT _BitBlt(PDev *pdev, SURFOBJ *dest, SURFOBJ *src, SURFOBJ *mask, CLIPOBJ *clip, + XLATEOBJ *color_trans, RECTL *dest_rect, POINTL *src_pos, + POINTL *mask_pos, BRUSHOBJ *brush, POINTL *brush_pos, ROP4 rop4) +{ + RECTL area; + POINTL local_mask_pos; + RECTL src_rect; + RECTL *src_rect_ptr; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + + if (!TestSrcBits(pdev, src, color_trans)) { + DEBUG_PRINT((pdev, 1, "%s: test src failed\n", __FUNCTION__)); + + return EngBitBlt(dest, src, mask, clip, color_trans, dest_rect, src_pos, mask_pos, brush, + brush_pos, rop4) ? QXL_SUCCESS : QXL_FAILED; + } + +#if 0 + if (rop4 == 0xccaa) { + DEBUG_PRINT((pdev, 7, "%s: rop4 is 0xccaa, call EngBitBlt\n", __FUNCTION__)); + return QXL_UNSUPPORTED; + } +#endif + + ASSERT(pdev, dest->iType == STYPE_BITMAP || dest->hsurf == pdev->surf); + ASSERT(pdev, !src || src->iType == STYPE_BITMAP || src->hsurf == pdev->surf); + ASSERT(pdev, dest_rect && dest_rect->left < dest_rect->right && + dest_rect->top < dest_rect->bottom); + + FixDestParams(pdev, dest, &clip, dest_rect, &area, &mask_pos, &local_mask_pos); + if (IsEmptyRect(&area)) { + DEBUG_PRINT((pdev, 0, "%s: empty rect\n", __FUNCTION__)); + return QXL_SUCCESS; + } + + if (src && src_pos) { + POINTL local_pos; + + local_pos.x = src_pos->x + (area.left - dest_rect->left); + local_pos.y = src_pos->y + (area.top - dest_rect->top); + + if (dest->iType == STYPE_BITMAP) { + return BitBltFromDev(pdev, dest, mask, clip, color_trans, &area, local_pos, mask_pos, + brush, brush_pos, rop4) ? QXL_SUCCESS : QXL_FAILED; + } + + if (src->iType != STYPE_BITMAP && rop4 == 0xcccc) { //SRCCOPY no mask + return DoCopyBits(pdev, clip, &area, &local_pos) ? QXL_SUCCESS : QXL_FAILED; + } + + src_rect.left = local_pos.x; + src_rect.right = src_rect.left + (area.right - area.left); + src_rect.top = local_pos.y; + src_rect.bottom = src_rect.top + (area.bottom - area.top); + src_rect_ptr = &src_rect; + } else { + src_rect_ptr = NULL; + } + return BitBltCommon(pdev, dest, src, mask, clip, color_trans, &area, src_rect_ptr, + mask_pos, brush, brush_pos, rop4, COLORONCOLOR, NULL); +} + +BOOL APIENTRY DrvBitBlt(SURFOBJ *dest, SURFOBJ *src, SURFOBJ *mask, CLIPOBJ *clip, + XLATEOBJ *color_trans, RECTL *dest_rect, POINTL *src_pos, + POINTL *mask_pos, BRUSHOBJ *brush, POINTL *brush_pos, ROP4 rop4) +{ + PDev *pdev; + QXLRESULT res; + + if (dest->iType == STYPE_BITMAP) { + ASSERT(NULL, src && src->iType != STYPE_BITMAP && src->dhpdev && src_pos); + pdev = (PDev *)src->dhpdev; + } else { + ASSERT(NULL, dest->dhpdev); + pdev = (PDev *)dest->dhpdev; + CountCall(pdev, CALL_COUNTER_BIT_BLT); + } + + DEBUG_PRINT((pdev, 3, "%s\n", __FUNCTION__)); + if ((res = _BitBlt(pdev, dest, src, mask, clip, color_trans, dest_rect, src_pos, mask_pos, + brush, brush_pos, rop4))) { + if (res == QXL_UNSUPPORTED) { + DEBUG_PRINT((pdev, 4, "%s: call EngBitBlt\n", __FUNCTION__)); + return EngBitBlt(dest, src, mask, clip, color_trans, dest_rect, src_pos, mask_pos, + brush, brush_pos, rop4); + } + return FALSE; + + } + DEBUG_PRINT((pdev, 4, "%s: done\n", __FUNCTION__)); + return TRUE; +} + +BOOL APIENTRY DrvCopyBits(SURFOBJ *dest, SURFOBJ *src, CLIPOBJ *clip, + XLATEOBJ *color_trans, RECTL *dest_rect, POINTL *src_pos) +{ + PDev *pdev; + + if (dest->iType == STYPE_BITMAP) { + ASSERT(NULL, src && src->iType != STYPE_BITMAP && src->dhpdev && src_pos); + pdev = (PDev *)src->dhpdev; + } else { + ASSERT(NULL, dest->dhpdev); + pdev = (PDev *)dest->dhpdev; + CountCall(pdev, CALL_COUNTER_BIT_BLT); + } + + DEBUG_PRINT((pdev, 3, "%s\n", __FUNCTION__)); + + return _BitBlt(pdev, dest, src, NULL, clip, color_trans, dest_rect, src_pos, NULL, NULL, + NULL, /*SRCCOPY*/ 0xcccc) == QXL_SUCCESS ? TRUE : FALSE; +} + +static _inline BOOL TestStretchCondition(PDev *pdev, SURFOBJ *src, XLATEOBJ *color_trans, + COLORADJUSTMENT *color_adjust, + RECTL *dest_rect, RECTL *src_rect) +{ + int src_size; + int dest_size; + + if (color_adjust && (color_adjust->caFlags & CA_NEGATIVE)) { + return FALSE; + } + + if (IsCacheableSurf(src, color_trans)) { + return TRUE; + } + + src_size = (src_rect->right - src_rect->left) * (src_rect->bottom - src_rect->top); + dest_size = (dest_rect->right - dest_rect->left) * (dest_rect->bottom - dest_rect->top); + + return dest_size - src_size >= -(src_size >> 2); + +} + +static _inline unsigned int Scale(unsigned int val, unsigned int base_unit, unsigned int dest_unit) +{ + unsigned int div; + unsigned int mod; + + div = dest_unit * val / base_unit; + mod = dest_unit * val % base_unit; + return (mod >= (base_unit >> 1)) ? div + 1: div; +} + +static QXLRESULT _StretchBlt(PDev *pdev, SURFOBJ *dest, SURFOBJ *src, SURFOBJ *mask, CLIPOBJ *clip, + XLATEOBJ *color_trans, COLORADJUSTMENT *color_adjust, + POINTL *brush_pos, RECTL *dest_rect, RECTL *src_rect, + POINTL *mask_pos, ULONG mode, BRUSHOBJ *brush, DWORD rop4) +{ + RECTL area; + POINTL local_mask_pos; + RECTL local_dest_rect; + RECTL local_src_rect; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + + ASSERT(pdev, src_rect && src_rect->left < src_rect->right && + src_rect->top < src_rect->bottom); + ASSERT(pdev, dest_rect); + + + if (dest_rect->left > dest_rect->right) { + local_dest_rect.left = dest_rect->right; + local_dest_rect.right = dest_rect->left; + } else { + local_dest_rect.left = dest_rect->left; + local_dest_rect.right = dest_rect->right; + } + + if (dest_rect->top > dest_rect->bottom) { + local_dest_rect.top = dest_rect->bottom; + local_dest_rect.bottom = dest_rect->top; + } else { + local_dest_rect.top = dest_rect->top; + local_dest_rect.bottom = dest_rect->bottom; + } + + if (!TestSrcBits(pdev, src, color_trans)) { + DEBUG_PRINT((pdev, 1, "%s: test src failed\n", __FUNCTION__)); + return QXL_UNSUPPORTED; + } + + if (!TestStretchCondition(pdev, src, color_trans, color_adjust, &local_dest_rect, src_rect)) { + DEBUG_PRINT((pdev, 1, "%s: stretch test failed\n", __FUNCTION__)); + return QXL_UNSUPPORTED; + } + + FixDestParams(pdev, dest, &clip, &local_dest_rect, &area, &mask_pos, &local_mask_pos); + if (IsEmptyRect(&area)) { + DEBUG_PRINT((pdev, 0, "%s: empty dest\n", __FUNCTION__)); + return QXL_SUCCESS; + } + //todo: use FixStreatchSrcArea + if (!SameRect(&local_dest_rect, &area)) { // possibly generate incosistent rendering on dest + // edges + unsigned int w_dest; + unsigned int h_dest; + + if ((w_dest = local_dest_rect.right - local_dest_rect.left) != area.right - area.left) { + unsigned int w_src = src_rect->right - src_rect->left; + unsigned int delta; + + if ((delta = area.left - local_dest_rect.left)) { + local_src_rect.left = src_rect->left + Scale(delta, w_dest, w_src); + } else { + local_src_rect.left = src_rect->left; + } + + if ((delta = local_dest_rect.right - area.right)) { + local_src_rect.right = src_rect->right - Scale(delta, w_dest, w_src); + } else { + local_src_rect.right = src_rect->right; + } + + local_src_rect.left = MIN(local_src_rect.left, src->sizlBitmap.cx - 1); + local_src_rect.right = MAX(local_src_rect.right, local_src_rect.left + 1); + + } else { + local_src_rect.left = src_rect->left; + local_src_rect.right = src_rect->right; + } + + if ((h_dest = local_dest_rect.bottom - local_dest_rect.top) != area.bottom - area.top) { + unsigned int h_src = src_rect->bottom - src_rect->top; + unsigned int delta; + + if ((delta = area.top - local_dest_rect.top)) { + local_src_rect.top = src_rect->top + Scale(delta, h_dest, h_src); + } else { + local_src_rect.top = src_rect->top; + } + + if ((delta = local_dest_rect.bottom - area.bottom)) { + local_src_rect.bottom = src_rect->bottom - Scale(delta, h_dest, h_src); + } else { + local_src_rect.bottom = src_rect->bottom; + } + + local_src_rect.top = MIN(local_src_rect.top, src->sizlBitmap.cy - 1); + local_src_rect.bottom = MAX(local_src_rect.bottom, local_src_rect.top + 1); + + } else { + local_src_rect.top = src_rect->top; + local_src_rect.bottom = src_rect->bottom; + } + + src_rect = &local_src_rect; + } + + return BitBltCommon(pdev, dest, src, mask, clip, color_trans, &area, src_rect, mask_pos, + brush, brush_pos, rop4, mode, color_adjust); +} + +BOOL APIENTRY DrvStretchBltROP(SURFOBJ *dest, SURFOBJ *src, SURFOBJ *mask, CLIPOBJ *clip, + XLATEOBJ *color_trans, COLORADJUSTMENT *color_adjust, + POINTL *brush_pos, RECTL *dest_rect, RECTL *src_rect, + POINTL *mask_pos, ULONG mode, BRUSHOBJ *brush, DWORD rop4) +{ + PDev *pdev; + QXLRESULT res; + + if (!src || src->iType != STYPE_BITMAP) { + ASSERT(NULL, src->dhpdev); + pdev = (PDev *)src->dhpdev; + goto punt; + } + + ASSERT(NULL, dest && dest->iType != STYPE_BITMAP && dest->dhpdev); + pdev = (PDev *)dest->dhpdev; + DEBUG_PRINT((pdev, 3, "%s\n", __FUNCTION__)); + CountCall(pdev, CALL_COUNTER_STRETCH_BLT_ROP); + + if ((res = _StretchBlt(pdev, dest, src, mask, clip, color_trans, + mode == HALFTONE ? color_adjust: NULL, brush_pos, + dest_rect, src_rect, mask_pos, mode, brush,rop4))) { + if (res == QXL_UNSUPPORTED) { + goto punt; + } + return FALSE; + } + return TRUE; + +punt: + return EngStretchBltROP(dest, src, mask, clip, color_trans, color_adjust, brush_pos, + dest_rect, src_rect, mask_pos, mode, brush, rop4); +} + +BOOL APIENTRY DrvStretchBlt(SURFOBJ *dest, SURFOBJ *src, SURFOBJ *mask, CLIPOBJ *clip, + XLATEOBJ *color_trans, COLORADJUSTMENT *color_adjust, + POINTL *halftone_brush_pos, RECTL *dest_rect, RECTL *src_rect, + POINTL *mask_pos, ULONG mode) +{ + PDev *pdev; + QXLRESULT res; + + ASSERT(NULL, src); + if (src->iType != STYPE_BITMAP) { + ASSERT(NULL, src->dhpdev); + pdev = (PDev *)src->dhpdev; + goto punt; + } + ASSERT(NULL, dest && dest->iType != STYPE_BITMAP && dest->dhpdev); + pdev = (PDev *)dest->dhpdev; + + DEBUG_PRINT((pdev, 3, "%s\n", __FUNCTION__)); + CountCall(pdev, CALL_COUNTER_STRETCH_BLT); + if ((res = _StretchBlt(pdev, dest, src, mask, clip, color_trans, + mode == HALFTONE ? color_adjust: NULL, NULL, dest_rect, + src_rect, mask_pos, mode, NULL, (mask) ? 0xccaa: 0xcccc))) { + if (res == QXL_UNSUPPORTED) { + goto punt; + } + return FALSE; + } + return TRUE; + +punt: + return EngStretchBlt(dest, src, mask, clip, color_trans, color_adjust, halftone_brush_pos, + dest_rect, src_rect, mask_pos, mode); +} + +static BOOL FixStreatchSrcArea(const RECTL *orig_dest, const RECTL *dest, const RECTL *orig_src, + const SIZEL *bitmap_size, RECTL *src) +{ + unsigned int w_dest; + unsigned int h_dest; + + if (SameRect(orig_dest, dest)) { + return FALSE; + } + + // possibly generate incosistent rendering on dest edges + + if ((w_dest = orig_dest->right - orig_dest->left) != dest->right - dest->left) { + unsigned int w_src = orig_src->right - orig_src->left; + unsigned int delta; + + if ((delta = dest->left - orig_dest->left)) { + src->left = orig_src->left + Scale(delta, w_dest, w_src); + } else { + src->left = orig_src->left; + } + + if ((delta = orig_dest->right - dest->right)) { + src->right = orig_src->right - Scale(delta, w_dest, w_src); + } else { + src->right = orig_src->right; + } + + src->left = MIN(src->left, bitmap_size->cx - 1); + src->right = MAX(src->right, src->left + 1); + + } else { + src->left = orig_src->left; + src->right = orig_src->right; + } + + if ((h_dest = orig_dest->bottom - orig_dest->top) != dest->bottom - dest->top) { + unsigned int h_src = orig_src->bottom - orig_src->top; + unsigned int delta; + + if ((delta = dest->top - orig_dest->top)) { + src->top = orig_src->top + Scale(delta, h_dest, h_src); + } else { + src->top = orig_src->top; + } + + if ((delta = orig_dest->bottom - dest->bottom)) { + src->bottom = orig_src->bottom - Scale(delta, h_dest, h_src); + } else { + src->bottom = orig_src->bottom; + } + + src->top = MIN(src->top, bitmap_size->cy - 1); + src->bottom = MAX(src->bottom, src->top + 1); + + } else { + src->top = orig_src->top; + src->bottom = orig_src->bottom; + } + + return TRUE; +} + +BOOL APIENTRY DrvAlphaBlend(SURFOBJ *dest, SURFOBJ *src, CLIPOBJ *clip, XLATEOBJ *color_trans, + RECTL *dest_rect, RECTL *src_rect, BLENDOBJ *bland) +{ + QXLDrawable *drawable; + PDev *pdev; + RECTL area; + RECTL local_src; + + ASSERT(NULL, src && dest); + if (src->iType != STYPE_BITMAP) { + ASSERT(NULL, src->dhpdev); + pdev = (PDev *)src->dhpdev; + goto punt; + } + ASSERT(NULL, dest->iType != STYPE_BITMAP && dest->dhpdev); + pdev = (PDev *)dest->dhpdev; + DEBUG_PRINT((pdev, 3, "%s\n", __FUNCTION__)); + + ASSERT(pdev, src_rect && src_rect->left < src_rect->right && + src_rect->top < src_rect->bottom); + ASSERT(pdev, dest_rect && dest_rect->left < dest_rect->right && + dest_rect->top < dest_rect->bottom); + + CountCall(pdev, CALL_COUNTER_ALPHA_BLEND); + + if (bland->BlendFunction.BlendOp != AC_SRC_OVER) { + DEBUG_PRINT((pdev, 0, "%s: unexpected BlendOp\n", __FUNCTION__)); + goto punt; + } + + if (bland->BlendFunction.SourceConstantAlpha == 0) { + return TRUE; + } + + if (!TestSrcBits(pdev, src, color_trans)) { + DEBUG_PRINT((pdev, 1, "%s: test src failed\n", __FUNCTION__)); + goto punt; + } + + if (!TestStretchCondition(pdev, src, color_trans, NULL, dest_rect, src_rect)) { + DEBUG_PRINT((pdev, 1, "%s: stretch test failed\n", __FUNCTION__)); + goto punt; + } + + FixDestParams(pdev, dest, &clip, dest_rect, &area, NULL, NULL); + if (IsEmptyRect(&area)) { + DEBUG_PRINT((pdev, 0, "%s: empty dest\n", __FUNCTION__)); + return TRUE; + } + + if (!(drawable = Drawable(pdev, QXL_DRAW_ALPHA_BLEND, &area, clip))) { + DEBUG_PRINT((pdev, 0, "%s: Drawable failed\n", __FUNCTION__)); + return FALSE; + } + + if (FixStreatchSrcArea(dest_rect, &area, src_rect, &src->sizlBitmap, &local_src)) { + src_rect = &local_src; + } + + CopyRect(&drawable->u.alpha_blend.src_area, src_rect); + if (bland->BlendFunction.AlphaFormat == AC_SRC_ALPHA) { + ASSERT(pdev, src->iBitmapFormat == BMF_32BPP); + if (!QXLGetAlphaBitmap(pdev, drawable, &drawable->u.alpha_blend.src_bitmap, src, + &drawable->u.alpha_blend.src_area)) { + DEBUG_PRINT((pdev, 0, "%s: QXLGetAlphaBitmap failed\n", __FUNCTION__)); + ReleaseOutput(pdev, drawable->release_info.id); + return FALSE; + } + } else { + if (!QXLGetBitmap(pdev, drawable, &drawable->u.alpha_blend.src_bitmap, src, + &drawable->u.alpha_blend.src_area, color_trans, NULL, TRUE)) { + DEBUG_PRINT((pdev, 0, "%s: QXLGetBitmap failed\n", __FUNCTION__)); + ReleaseOutput(pdev, drawable->release_info.id); + return FALSE; + } + } + drawable->u.alpha_blend.alpha = bland->BlendFunction.SourceConstantAlpha; + drawable->effect = QXL_EFFECT_BLEND; + + PushDrawable(pdev, drawable); + DEBUG_PRINT((pdev, 4, "%s: done\n", __FUNCTION__)); + + return TRUE; + +punt: + return EngAlphaBlend(dest, src, clip, color_trans, dest_rect, src_rect, bland); +} + +BOOL APIENTRY DrvTransparentBlt(SURFOBJ *dest, SURFOBJ *src, CLIPOBJ *clip, XLATEOBJ *color_trans, + RECTL *dest_rect, RECTL *src_rect, ULONG trans_color, + ULONG reserved) +{ + QXLDrawable *drawable; + PDev *pdev; + RECTL area; + RECTL local_src; + + ASSERT(NULL, src && dest); + if (src->iType != STYPE_BITMAP) { + ASSERT(NULL, src->dhpdev); + pdev = (PDev *)src->dhpdev; + goto punt; + } + + ASSERT(NULL, dest->iType != STYPE_BITMAP && dest->dhpdev); + pdev = (PDev *)dest->dhpdev; + DEBUG_PRINT((pdev, 3, "%s\n", __FUNCTION__)); + + ASSERT(pdev, src_rect && src_rect->left < src_rect->right && + src_rect->top < src_rect->bottom); + ASSERT(pdev, dest_rect && dest_rect->left < dest_rect->right && + dest_rect->top < dest_rect->bottom); + + CountCall(pdev, CALL_COUNTER_TRANSPARENT_BLT); + + if (!TestSrcBits(pdev, src, color_trans)) { + DEBUG_PRINT((pdev, 1, "%s: test src failed\n", __FUNCTION__)); + goto punt; + } + + if (!TestStretchCondition(pdev, src, color_trans, NULL, dest_rect, src_rect)) { + DEBUG_PRINT((pdev, 1, "%s: stretch test failed\n", __FUNCTION__)); + goto punt; + } + + FixDestParams(pdev, dest, &clip, dest_rect, &area, NULL, NULL); + if (IsEmptyRect(&area)) { + DEBUG_PRINT((pdev, 0, "%s: empty dest\n", __FUNCTION__)); + return TRUE; + } + + if (!(drawable = Drawable(pdev, QXL_DRAW_TRANSPARENT, &area, clip))) { + DEBUG_PRINT((pdev, 0, "%s: Drawable failed\n", __FUNCTION__)); + return FALSE; + } + + if (FixStreatchSrcArea(dest_rect, &area, src_rect, &src->sizlBitmap, &local_src)) { + src_rect = &local_src; + } + + CopyRect(&drawable->u.transparent.src_area, src_rect); + if (!QXLGetBitmap(pdev, drawable, &drawable->u.transparent.src_bitmap, src, + &drawable->u.transparent.src_area, color_trans, NULL, TRUE)) { + DEBUG_PRINT((pdev, 0, "%s: QXLGetBitmap failed\n", __FUNCTION__)); + ReleaseOutput(pdev, drawable->release_info.id); + return FALSE; + } + + drawable->u.transparent.src_color = trans_color; + switch (src->iBitmapFormat) { + case BMF_32BPP: + case BMF_24BPP: + drawable->u.transparent.true_color = trans_color; + break; + case BMF_16BPP: + drawable->u.transparent.true_color = _16bppTo32bpp(trans_color); + break; + case BMF_8BPP: + case BMF_4BPP: + case BMF_1BPP: + ASSERT(pdev, trans_color < color_trans->cEntries); + if (pdev->bitmap_format == BMF_32BPP) { + drawable->u.transparent.true_color = color_trans->pulXlate[trans_color]; + } else { + ASSERT(pdev, pdev->bitmap_format == BMF_16BPP); + drawable->u.transparent.true_color = _16bppTo32bpp(color_trans->pulXlate[trans_color]); + } + break; + return color_trans && (color_trans->flXlate & XO_TABLE); + } + + drawable->effect = QXL_EFFECT_BLEND; + PushDrawable(pdev, drawable); + DEBUG_PRINT((pdev, 4, "%s: done\n", __FUNCTION__)); + + return TRUE; + +punt: + return EngTransparentBlt(dest, src, clip, color_trans, dest_rect, src_rect, trans_color, + reserved); +} + diff --git a/display/rop.h b/display/rop.h new file mode 100644 index 0000000..7519e63 --- /dev/null +++ b/display/rop.h @@ -0,0 +1,35 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _H_ROP +#define _H_ROP + +#define ROP3_DEST (1 << 0) +#define ROP3_SRC (1 << 1) +#define ROP3_BRUSH (1 << 2) +#define ROP3_ALL (ROP3_DEST | ROP3_SRC | ROP3_BRUSH) + +typedef struct ROP3Info { + UINT8 effect; + UINT8 flags; + UINT32 method_type; + UINT16 method_data; +} ROP3Info; + +extern ROP3Info rops2[]; + +#endif diff --git a/display/sources b/display/sources new file mode 100644 index 0000000..f6c339b --- /dev/null +++ b/display/sources @@ -0,0 +1,34 @@ +TARGETNAME=qxldd
+TARGETPATH=obj
+TARGETTYPE=GDI_DRIVER
+
+!IFNDEF MSC_WARNING_LEVEL
+MSC_WARNING_LEVEL=/W3
+!ENDIF
+
+MSC_WARNING_LEVEL=$(MSC_WARNING_LEVEL) /WX
+
+INCLUDES=$(DDK_INC_PATH); ..\include; $(SPICE_COMMON_DIR);
+
+# todo: add ntoskrnl.lib for 2008 build
+
+TARGETLIBS = $(DDK_LIB_PATH)\ntstrsafe.lib
+
+!IFNDEF DEBUG
+MSC_OPTIMIZATION = /Ox
+!ENDIF
+
+C_DEFINES = $(C_DEFINES) /DQXLDD
+
+
+SOURCES=driver.c \
+ rop.c \
+ res.c \
+ text.c \
+ pointer.c \
+ brush.c \
+ mspace.c \
+ quic.c \
+ lookup3.c \
+ driver.rc
+
diff --git a/display/text.c b/display/text.c new file mode 100644 index 0000000..261c0c7 --- /dev/null +++ b/display/text.c @@ -0,0 +1,116 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "os_dep.h" +#include "qxldd.h" +#include "utils.h" +#include "res.h" +#include "rop.h" + +BOOL APIENTRY DrvTextOut(SURFOBJ *surf, STROBJ *str, FONTOBJ *font, CLIPOBJ *clip, + RECTL *ignored, RECTL *opaque_rect, + BRUSHOBJ *fore_brush, BRUSHOBJ *back_brash, + POINTL *brushs_origin, MIX mix) +{ + QXLDrawable *drawable; + ROP3Info *fore_rop; + ROP3Info *back_rop; + PDev* pdev; + RECTL area; + + if (!(pdev = (PDev *)surf->dhpdev)) { + DEBUG_PRINT((NULL, 0, "%s: err no pdev\n", __FUNCTION__)); + return FALSE; + } + + CountCall(pdev, CALL_COUNTER_TEXT_OUT); + + DEBUG_PRINT((pdev, 3, "%s\n", __FUNCTION__)); + ASSERT(pdev, opaque_rect == NULL || + (opaque_rect->left < opaque_rect->right && opaque_rect->top < opaque_rect->bottom)); + ASSERT(pdev, surf && str && font && clip); + + if (opaque_rect) { + CopyRect(&area, opaque_rect); + } else { + CopyRect(&area, &str->rclBkGround); + } + + if (clip) { + if (clip->iDComplexity == DC_TRIVIAL) { + clip = NULL; + } else { + SectRect(&clip->rclBounds, &area, &area); + if (IsEmptyRect(&area)) { + DEBUG_PRINT((pdev, 1, "%s: empty rect after clip\n", __FUNCTION__)); + return TRUE; + } + } + } + + if (!(drawable = Drawable(pdev, QXL_DRAW_TEXT, &area, clip))) { + return FALSE; + } + + if (opaque_rect) { + ASSERT(pdev, back_brash && brushs_origin); + if (!QXLGetBrush(pdev, drawable, &drawable->u.text.back_brush, back_brash, brushs_origin)) { + goto error; + } + CopyRect(&drawable->u.text.back_area, &area); + drawable->u.text.back_mode = ROPD_OP_PUT; + drawable->effect = QXL_EFFECT_OPAQUE; + } else { + drawable->u.text.back_brush.type = BRUSH_TYPE_NONE; + RtlZeroMemory(&drawable->u.text.back_area, sizeof(drawable->u.text.back_area)); + drawable->u.text.back_mode = 0; + drawable->effect = QXL_EFFECT_BLEND; + } + + fore_rop = &rops2[(mix - 1) & 0x0f]; + back_rop = &rops2[((mix >> 8) - 1) & 0x0f]; + + if (!((fore_rop->flags | back_rop->flags) & ROP3_BRUSH)) { + drawable->u.stroke.brush.type = BRUSH_TYPE_NONE; + } else if (!QXLGetBrush(pdev, drawable, &drawable->u.text.fore_brush, fore_brush, + brushs_origin)) { + DEBUG_PRINT((pdev, 0, "%s: get brush failed\n", __FUNCTION__)); + goto error; + } + + if (fore_rop->method_data != back_rop->method_data && back_rop->method_data) { + DEBUG_PRINT((pdev, 0, "%s: ignoring back rop, fore %u back %u\n", + __FUNCTION__, + (UINT32)fore_rop->method_data, + (UINT32)back_rop->method_data)); + } + drawable->u.text.fore_mode = fore_rop->method_data; + + if (!QXLGetStr(pdev, drawable, &drawable->u.text.str, font, str)) { + DEBUG_PRINT((pdev, 0, "%s: get str failed\n", __FUNCTION__)); + goto error; + } + + PushDrawable(pdev, drawable); + DEBUG_PRINT((pdev, 4, "%s: done\n", __FUNCTION__)); + return TRUE; + +error: + ReleaseOutput(pdev, drawable->release_info.id); + DEBUG_PRINT((pdev, 4, "%s: error\n", __FUNCTION__)); + return FALSE; +} diff --git a/display/utils.h b/display/utils.h new file mode 100644 index 0000000..74f3476 --- /dev/null +++ b/display/utils.h @@ -0,0 +1,113 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _H_UTILS +#define _H_UTILS + +#define MIN(x, y) (((x) <= (y)) ? (x) : (y)) +#define MAX(x, y) (((x) >= (y)) ? (x) : (y)) +#define ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1)) + + +#define OFFSETOF(type, member) ((unsigned)&((type *)0)->member) +#define CONTAINEROF(ptr, type, member) \ + ((type *) ((UINT8 *)(ptr) - OFFSETOF(type, member))) + +static __inline BOOL IsEmptyRect(RECTL *r) +{ + return r->left >= r->right || r->top >= r->bottom; +} + +static __inline void SectRect(RECTL *r1, RECTL *r2, RECTL *dest) +{ + dest->top = MAX(r1->top, r2->top); + dest->bottom = MAX(MIN(r1->bottom, r2->bottom), dest->top); + + dest->left = MAX(r1->left, r2->left); + dest->right = MAX(MIN(r1->right, r2->right), dest->left); +} + +static _inline LONG RectSize(RECTL *rect) +{ + return (rect->right - rect->left) * (rect->bottom - rect->top); +} + +#define SameRect(r1, r2) ((r1)->left == (r2)->left && (r1)->right == (r2)->right && \ + (r1)->top == (r2)->top && (r1)->bottom == (r2)->bottom) + +#define CopyRect(dest, src) \ + (dest)->top = (src)->top; \ + (dest)->left = (src)->left; \ + (dest)->bottom = (src)->bottom; \ + (dest)->right = (src)->right; + +#define CopyPoint(dest, src) \ + (dest)->x = (src)->x; \ + (dest)->y = (src)->y; + +static __inline void FXToRect(RECTL *dest, RECTFX *src) +{ + dest->left = src->xLeft >> 4; + dest->top = src->yTop >> 4; + dest->right = ALIGN(src->xRight, 16) >> 4; + dest->bottom = ALIGN(src->yBottom, 16) >> 4; + +} + +static _inline int test_bit(void* addr, int bit) +{ + return !!(((UINT32 *)addr)[bit >> 5] & (1 << (bit & 0x1f))); +} + +static _inline int test_bit_be(void* addr, int bit) +{ + return !!(((UINT8 *)addr)[bit >> 3] & (0x80 >> (bit & 0x07))); +} + +static _inline BOOL PrepareBrush(BRUSHOBJ *brush) +{ + if (!brush || brush->iSolidColor != ~0 || brush->pvRbrush) { + return TRUE; + } + return BRUSHOBJ_pvGetRbrush(brush) != NULL; +} + +static _inline BOOL IsCacheableSurf(SURFOBJ *surf, XLATEOBJ *color_trans) +{ + return surf->iUniq && !(surf->fjBitmap & BMF_DONTCACHE) && + (!color_trans || color_trans->iUniq); +} + +static _inline UINT32 _16bppTo32bpp(UINT32 color) +{ + UINT32 ret; + + ret = ((color & 0x001f) << 3) | ((color & 0x001c) >> 2); + ret |= ((color & 0x03e0) << 6) | ((color & 0x0380) << 1); + ret |= ((color & 0x7c00) << 9) | ((color & 0x7000) << 4); + + return ret; +} + +static _inline BOOL IsUniqueSurf(SURFOBJ *surf, XLATEOBJ *color_trans) +{ + int pallette = color_trans && (color_trans->flXlate & XO_TABLE); + return surf->iUniq && (surf->fjBitmap & BMF_DONTCACHE) && (!pallette || color_trans->iUniq); +} + +#endif + diff --git a/include/os_dep.h b/include/os_dep.h new file mode 100644 index 0000000..24a13e9 --- /dev/null +++ b/include/os_dep.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef OS_DEP_H +#define OS_DEP_H + +#if (WINVER < 0x0501) //Definitions for Win2K +typedef signed char INT8, *PINT8; +typedef signed short INT16, *PINT16; +typedef signed int INT32, *PINT32; +typedef signed __int64 INT64, *PINT64; +typedef unsigned char UINT8, *PUINT8; +typedef unsigned short UINT16, *PUINT16; +typedef unsigned int UINT32, *PUINT32; +typedef unsigned __int64 UINT64, *PUINT64; + +#define SIZE_OF_W2K_VIDEO_HW_INITIALIZATION_DATA 0x50 + +#define VideoPortFreePool VideoPortReleaseBuffer + +#endif + +#endif diff --git a/include/qxl_driver.h b/include/qxl_driver.h new file mode 100644 index 0000000..b94b749 --- /dev/null +++ b/include/qxl_driver.h @@ -0,0 +1,74 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _H_QXL_DRIVER +#define _H_QXL_DRIVER + +#include "qxl_dev.h" + +#if (WINVER < 0x0501) +#include "wdmhelper.h" +#endif + +enum { + FIRST_AVIL_IOCTL_FUNC = 0x800, + QXL_GET_INFO_FUNC = FIRST_AVIL_IOCTL_FUNC +}; + +#define IOCTL_QXL_GET_INFO \ + CTL_CODE(FILE_DEVICE_VIDEO, QXL_GET_INFO_FUNC, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define QXL_DRIVER_INFO_VERSION 2 + +typedef struct QXLDriverInfo { + UINT32 version; + QXLCommandRing *cmd_ring; + QXLCursorRing *cursor_ring; + QXLReleaseRing *release_ring; + UINT32 notify_cmd_port; + UINT32 notify_cursor_port; + UINT32 notify_oom_port; + PEVENT display_event; + PEVENT cursor_event; + PEVENT sleep_event; + + UINT32 num_io_pages; + void *io_pages_virt; + UINT64 io_pages_phys; + + UINT8 *draw_area; + UINT32 draw_area_size; + + UINT32 *update_id; + UINT32 *compression_level; + + UINT32 update_area_port; + Rect *update_area; + + UINT32 *mm_clock; + + UINT32 log_port; + UINT8 *log_buf; + UINT32 *log_level; +#if (WINVER < 0x0501) + PQXLWaitForEvent WaitForEvent; +#endif +} QXLDriverInfo; + + +#endif + diff --git a/include/wdmhelper.h b/include/wdmhelper.h new file mode 100644 index 0000000..ac0d6ba --- /dev/null +++ b/include/wdmhelper.h @@ -0,0 +1,34 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef WDM_HELPER_H +#define WDM_HELPER_H + +#include "os_dep.h" + +typedef ULONG (*PQXLWaitForEvent)(PVOID,PLARGE_INTEGER); + +LONG QXLInitializeEvent(PVOID * pEvent); +void QXLSetEvent(PVOID pEvent); +void QXLDeleteEvent(PVOID pEvent); +ULONG QXLWaitForEvent(PVOID pEvent,PLARGE_INTEGER Timeout); + +#define VideoPortDeleteEvent(dev,pEvent) QXLDeleteEvent(pEvent) +#define VideoPortCreateEvent(dev,flag,reserved,ppEvent) QXLInitializeEvent(ppEvent) +#define VideoPortSetEvent(dev,pEvent) QXLSetEvent(pEvent) + +#endif diff --git a/miniport/makefile b/miniport/makefile new file mode 100644 index 0000000..bc5d75a --- /dev/null +++ b/miniport/makefile @@ -0,0 +1,17 @@ +#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of Windows NT
+#
+
+!IF DEFINED(_NT_TARGET_VERSION)
+! IFNDEF AMD64
+! INCLUDE $(NTMAKEENV)\makefile.def
+! ELSE
+! message BUILDMSG: Warning : miniport is not supported on AMD64.
+! ENDIF
+!ELSE
+! INCLUDE $(NTMAKEENV)\makefile.def
+!ENDIF
+
+
diff --git a/miniport/qxl.c b/miniport/qxl.c new file mode 100644 index 0000000..c4d2888 --- /dev/null +++ b/miniport/qxl.c @@ -0,0 +1,950 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "os_dep.h" +#include "qxl.h" +#if (WINVER < 0x0501) +#include "wdmhelper.h" +#endif + +VP_STATUS FindAdapter(PVOID dev_extension, + PVOID reserved, + PWSTR arg_str, + PVIDEO_PORT_CONFIG_INFO conf_info, + PUCHAR again); + +BOOLEAN Initialize(PVOID dev_extension); + +VP_STATUS GetPowerState(PVOID dev_extension, + ULONG hw_id, + PVIDEO_POWER_MANAGEMENT state); + +VP_STATUS SetPowerState(PVOID dev_extension, + ULONG hw_wId, + PVIDEO_POWER_MANAGEMENT state); + +VP_STATUS GetChildDescriptor(IN PVOID dev_extension, + IN PVIDEO_CHILD_ENUM_INFO enum_info, + OUT PVIDEO_CHILD_TYPE type, + OUT PUCHAR descriptor, + OUT PULONG uid, + OUT PULONG unused); + +BOOLEAN StartIO(PVOID dev_extension, PVIDEO_REQUEST_PACKET packet); + +BOOLEAN Interrupt(PVOID HwDeviceExtension); + +#if defined(ALLOC_PRAGMA) +#pragma alloc_text(PAGE, DriverEntry) +#pragma alloc_text(PAGE, FindAdapter) +#pragma alloc_text(PAGE, Initialize) +#pragma alloc_text(PAGE, GetPowerState) +#pragma alloc_text(PAGE, SetPowerState) +#pragma alloc_text(PAGE, GetChildDescriptor) +#pragma alloc_text(PAGE, StartIO) +#endif + +typedef struct QXLExtension { + PVOID io_base; + ULONG io_port; + + QXLRom *rom; + ULONG rom_size; + + PHYSICAL_ADDRESS ram_physical; + UINT8 *ram_start; + QXLRam *ram_header; + ULONG ram_size; + + PHYSICAL_ADDRESS vram_physical; + ULONG vram_size; + + ULONG n_modes; + PVIDEO_MODE_INFORMATION modes; + + PEVENT display_event; + PEVENT cursor_event; + PEVENT sleep_event; + +} QXLExtension; + +#define QXL_ALLOC_TAG '_lxq' + + +ULONG DriverEntry(PVOID context1, PVOID context2) +{ + VIDEO_HW_INITIALIZATION_DATA init_data; + ULONG ret; + + PAGED_CODE(); + + DEBUG_PRINT((0, "%s: enter\n", __FUNCTION__)); + + VideoPortZeroMemory(&init_data, sizeof(VIDEO_HW_INITIALIZATION_DATA)); + init_data.HwInitDataSize = sizeof(VIDEO_HW_INITIALIZATION_DATA); + init_data.HwDeviceExtensionSize = sizeof(QXLExtension); + + init_data.HwFindAdapter = FindAdapter; + init_data.HwInitialize = Initialize; + init_data.HwGetPowerState = GetPowerState; + init_data.HwSetPowerState = SetPowerState; + init_data.HwGetVideoChildDescriptor = GetChildDescriptor; + init_data.HwStartIO = StartIO; + init_data.HwInterrupt = Interrupt; + + ret = VideoPortInitialize(context1, context2, &init_data, NULL); + + if (ret != NO_ERROR) { + DEBUG_PRINT((0, "%s: try W2K %u\n", __FUNCTION__, ret)); + init_data.HwInitDataSize = SIZE_OF_W2K_VIDEO_HW_INITIALIZATION_DATA; + ret = VideoPortInitialize(context1, context2, &init_data, NULL); + } + DEBUG_PRINT((0, "%s: exit %u\n", __FUNCTION__, ret)); + return ret; +} + + +#if defined(ALLOC_PRAGMA) +VP_STATUS InitIO(QXLExtension *dev, PVIDEO_ACCESS_RANGE range); +#pragma alloc_text(PAGE, InitIO) +#endif + +VP_STATUS InitIO(QXLExtension *dev, PVIDEO_ACCESS_RANGE range) +{ + PVOID io_base; + + PAGED_CODE(); + DEBUG_PRINT((0, "%s\n", __FUNCTION__)); + + if (range->RangeLength < QXL_IO_RANGE_SIZE + || !range->RangeInIoSpace) { + DEBUG_PRINT((0, "%s: bad io range\n", __FUNCTION__)); + return ERROR_INVALID_DATA; + } + + io_base = VideoPortGetDeviceBase(dev, range->RangeStart, range->RangeLength, + range->RangeInIoSpace); + + if (!io_base) { + DEBUG_PRINT((0, "%s: get io base failed\n", __FUNCTION__)); + return ERROR_NOT_ENOUGH_MEMORY; + } + + dev->io_base = io_base; + dev->io_port = range->RangeStart.LowPart; + + DEBUG_PRINT((0, "%s: OK, io 0x%x size %lu\n", __FUNCTION__, + (ULONG)range->RangeStart.LowPart, range->RangeLength)); + + return NO_ERROR; +} + +#if defined(ALLOC_PRAGMA) +VP_STATUS InitRom(QXLExtension *dev, PVIDEO_ACCESS_RANGE range); +#pragma alloc_text(PAGE, InitRom) +#endif + +VP_STATUS InitRom(QXLExtension *dev, PVIDEO_ACCESS_RANGE range) +{ + PVOID rom = NULL; + ULONG rom_size = range->RangeLength; + ULONG io_space = VIDEO_MEMORY_SPACE_MEMORY; + VP_STATUS error; + + PAGED_CODE(); + DEBUG_PRINT((0, "%s\n", __FUNCTION__)); + + if (rom_size < sizeof(QXLRom) || range->RangeInIoSpace) { + DEBUG_PRINT((0, "%s: bad rom range\n", __FUNCTION__)); + return ERROR_INVALID_DATA; + } + if ((error = VideoPortMapMemory(dev, range->RangeStart, + &rom_size, &io_space, + &rom)) != NO_ERROR ) { + DEBUG_PRINT((0, "%s: map rom filed\n", __FUNCTION__)); + return error; + } + + if (rom_size < range->RangeLength) { + DEBUG_PRINT((0, "%s: short rom map\n", __FUNCTION__)); + error = ERROR_NOT_ENOUGH_MEMORY; + goto err; + } + + if (((QXLRom*)rom)->magic != QXL_ROM_MAGIC) { + DEBUG_PRINT((0, "%s: bad rom magic\n", __FUNCTION__)); + error = ERROR_INVALID_DATA; + goto err; + } + + dev->rom = rom; + dev->rom_size = range->RangeLength; + DEBUG_PRINT((0, "%s OK: rom 0x%lx size %lu\n", + __FUNCTION__, (ULONG)range->RangeStart.QuadPart, range->RangeLength)); + return NO_ERROR; + + err: + VideoPortUnmapMemory(dev, rom, NULL); + DEBUG_PRINT((0, "%s ERR\n", __FUNCTION__)); + return error; +} + +#if defined(ALLOC_PRAGMA) +VP_STATUS InitRam(QXLExtension *dev, PVIDEO_ACCESS_RANGE range); +#pragma alloc_text(PAGE, InitRam) +#endif + +VP_STATUS InitRam(QXLExtension *dev, PVIDEO_ACCESS_RANGE range) +{ + UINT8 *ram = NULL; + QXLRam *ram_header; + ULONG ram_size = range->RangeLength; + ULONG io_space = VIDEO_MEMORY_SPACE_MEMORY; + VP_STATUS error; + + PAGED_CODE(); + DEBUG_PRINT((0, "%s\n", __FUNCTION__)); + + if (ram_size < sizeof(QXLRam) + dev->rom->ram_header_offset || range->RangeInIoSpace) { + DEBUG_PRINT((0, "%s: bad ram range\n", __FUNCTION__)); + return ERROR_INVALID_DATA; + } + + if (ram_size < dev->rom->pages_offset + (dev->rom->num_io_pages << PAGE_SHIFT) ) { + DEBUG_PRINT((0, "%s: bad ram size\n", __FUNCTION__)); + return ERROR_INVALID_DATA; + } + + if ((error = VideoPortMapMemory(dev, range->RangeStart, + &ram_size, &io_space, + &ram)) != NO_ERROR ) { + DEBUG_PRINT((0, "%s: map ram filed\n", __FUNCTION__)); + return error; + } + + if (ram_size < range->RangeLength) { + DEBUG_PRINT((0, "%s: short ram map\n", __FUNCTION__)); + error = ERROR_NOT_ENOUGH_MEMORY; + goto err; + } + ram_header = (QXLRam *)(ram + dev->rom->ram_header_offset); + if (ram_header->magic != QXL_RAM_MAGIC) { + DEBUG_PRINT((0, "%s: bad ram magic\n", __FUNCTION__)); + error = ERROR_INVALID_DATA; + goto err; + } + + dev->ram_physical = range->RangeStart; + dev->ram_start = ram; + dev->ram_header = ram_header; + dev->ram_size = range->RangeLength; + DEBUG_PRINT((0, "%s OK: ram 0x%lx size %lu\n", + __FUNCTION__, (ULONG)range->RangeStart.QuadPart, range->RangeLength)); + return NO_ERROR; + + err: + VideoPortUnmapMemory(dev, ram, NULL); + DEBUG_PRINT((0, "%s ERR\n", __FUNCTION__)); + return error; +} + + +#if defined(ALLOC_PRAGMA) +VP_STATUS InitVRAM(QXLExtension *dev, PVIDEO_ACCESS_RANGE range); +#pragma alloc_text(PAGE, InitVRAM) +#endif + +VP_STATUS InitVRAM(QXLExtension *dev, PVIDEO_ACCESS_RANGE range) +{ + PAGED_CODE(); + DEBUG_PRINT((0, "%s\n", __FUNCTION__)); + + if (range->RangeLength == 0 || range->RangeInIoSpace) { + DEBUG_PRINT((0, "%s: bad mem range\n", __FUNCTION__)); + return ERROR_INVALID_DATA; + } + + dev->vram_physical = range->RangeStart; + dev->vram_size = range->RangeLength; + DEBUG_PRINT((0, "%s: OK, vram 0x%lx size %lu\n", + __FUNCTION__, (ULONG)range->RangeStart.QuadPart, range->RangeLength)); + return NO_ERROR; +} + +#if defined(ALLOC_PRAGMA) +VP_STATUS Prob(QXLExtension *dev, VIDEO_PORT_CONFIG_INFO *conf_info, + PVIDEO_ACCESS_RANGE ranges, int n_ranges); +#pragma alloc_text(PAGE, Prob) +#endif + +VP_STATUS Prob(QXLExtension *dev, VIDEO_PORT_CONFIG_INFO *conf_info, + PVIDEO_ACCESS_RANGE ranges, int n_ranges) +{ + PCI_COMMON_CONFIG pci_conf; + ULONG bus_data_size; + VP_STATUS error; + + PAGED_CODE(); + DEBUG_PRINT((0, "%s\n", __FUNCTION__)); + + bus_data_size = VideoPortGetBusData(dev, + PCIConfiguration, + 0, + &pci_conf, + 0, + sizeof(PCI_COMMON_CONFIG)); + + if (bus_data_size != sizeof(PCI_COMMON_CONFIG)) { + DEBUG_PRINT((0, "%s: GetBusData size %d expectes %d\n", + __FUNCTION__, bus_data_size, sizeof(PCI_COMMON_CONFIG))); + return ERROR_INVALID_PARAMETER; + } + + if (pci_conf.VendorID != REDHAT_PCI_VENDOR_ID) { + DEBUG_PRINT((0, "%s: bad vendor id 0x%x expectes 0x%x\n", + __FUNCTION__, pci_conf.VendorID, REDHAT_PCI_VENDOR_ID)); + return ERROR_INVALID_PARAMETER; + } + + if (pci_conf.DeviceID != QXL_DEVICE_ID) { + DEBUG_PRINT((0, "%s: bad vendor id 0x%x expectes 0x%x\n", + __FUNCTION__, pci_conf.DeviceID, QXL_DEVICE_ID)); + return ERROR_INVALID_PARAMETER; + } + + if (pci_conf.RevisionID != QXL_REVISION) { + DEBUG_PRINT((0, "%s: bad revision 0x%x expectes 0x%x\n", + __FUNCTION__, pci_conf.RevisionID, QXL_REVISION)); + return ERROR_INVALID_PARAMETER; + } + + VideoPortZeroMemory(ranges, sizeof(VIDEO_ACCESS_RANGE) * n_ranges); + if ((error = VideoPortGetAccessRanges(dev, 0, NULL, n_ranges, + ranges, NULL, NULL, + NULL)) != NO_ERROR ) { + DEBUG_PRINT((0, "%s: get access ranges failed status %u\n", __FUNCTION__, error)); + } + + if (conf_info->BusInterruptLevel == 0 && conf_info->BusInterruptVector == 0) { + DEBUG_PRINT((0, "%s: no interrupt\n", __FUNCTION__)); + error = ERROR_INVALID_DATA; + } + +#ifdef DBG + if (error == NO_ERROR) { + int i; + + DEBUG_PRINT((0, "%s: interrupt: vector %lu level %lu mode %s\n", + __FUNCTION__, + conf_info->BusInterruptVector, + conf_info->BusInterruptLevel, + (conf_info->InterruptMode == LevelSensitive) ? "LevelSensitive" : "Latched")); + + for (i = 0; i < n_ranges; i++) { + DEBUG_PRINT((0, "%s: range %d start 0x%lx length %lu space %lu\n", __FUNCTION__, i, + (ULONG)ranges[i].RangeStart.QuadPart, + ranges[i].RangeLength, + (ULONG)ranges[i].RangeInIoSpace)); + } + } +#endif + + DEBUG_PRINT((0, "%s exit %lu\n", __FUNCTION__, error)); + return error; +} + +#if defined(ALLOC_PRAGMA) +VP_STATUS SetVideoModeInfo(PVIDEO_MODE_INFORMATION video_mode, QXLMode *qxl_mode); +#pragma alloc_text(PAGE, SetVideoModeInfo) +#endif + +VP_STATUS SetVideoModeInfo(PVIDEO_MODE_INFORMATION video_mode, QXLMode *qxl_mode) +{ + ULONG color_bits; + PAGED_CODE(); + DEBUG_PRINT((0, "%s: x %u y %u bits %u stride %u orientation %u\n", + __FUNCTION__, qxl_mode->x_res, qxl_mode->y_res, + qxl_mode->bits, qxl_mode->stride, qxl_mode->orientation)); + + video_mode->Length = sizeof(VIDEO_MODE_INFORMATION); + video_mode->ModeIndex = qxl_mode->id; + video_mode->VisScreenWidth = qxl_mode->x_res; + video_mode->VisScreenHeight = qxl_mode->y_res; + video_mode->ScreenStride = qxl_mode->stride; + video_mode->NumberOfPlanes = 1; + video_mode->BitsPerPlane = qxl_mode->bits; + video_mode->Frequency = 100; + video_mode->XMillimeter = qxl_mode->x_mili; + video_mode->YMillimeter = qxl_mode->y_mili; + color_bits = (qxl_mode->bits == 16) ? 5 : 8; + video_mode->NumberRedBits = color_bits; + video_mode->NumberGreenBits = color_bits; + video_mode->NumberBlueBits = color_bits; + + video_mode->BlueMask = (1 << color_bits) - 1; + video_mode->GreenMask = video_mode->BlueMask << color_bits; + video_mode->RedMask = video_mode->GreenMask << color_bits; + + video_mode->AttributeFlags = VIDEO_MODE_COLOR | VIDEO_MODE_GRAPHICS; + video_mode->VideoMemoryBitmapWidth = qxl_mode->x_res; + video_mode->VideoMemoryBitmapHeight = qxl_mode->y_res; + video_mode->DriverSpecificAttributeFlags = qxl_mode->orientation; + DEBUG_PRINT((0, "%s OK\n", __FUNCTION__)); + return NO_ERROR; +} + + +#if defined(ALLOC_PRAGMA) +VP_STATUS InitModes(QXLExtension *dev); +#pragma alloc_text(PAGE, InitModes) +#endif + +VP_STATUS InitModes(QXLExtension *dev) +{ + QXLRom *rom; + QXLModes *modes; + PVIDEO_MODE_INFORMATION modes_info; + ULONG n_modes; + ULONG i; + VP_STATUS error; + + PAGED_CODE(); + DEBUG_PRINT((0, "%s\n", __FUNCTION__)); + rom = dev->rom; + modes = (QXLModes *)((UCHAR*)rom + rom->modes_offset); + if (dev->rom_size < rom->modes_offset + sizeof(QXLModes) || + (n_modes = modes->n_modes) == 0 || dev->rom_size < + rom->modes_offset + sizeof(QXLModes) + n_modes * sizeof(QXLMode)) { + DEBUG_PRINT((0, "%s: bad rom size\n", __FUNCTION__)); + return ERROR_INVALID_DATA; + } + +#if (WINVER < 0x0501) //Win2K + error = VideoPortAllocateBuffer(dev, n_modes * sizeof(VIDEO_MODE_INFORMATION), &modes_info); + + if(!modes_info || error != NO_ERROR) { + return ERROR_NOT_ENOUGH_MEMORY; + } +#else + if (!(modes_info = VideoPortAllocatePool(dev, VpPagedPool, + n_modes * sizeof(VIDEO_MODE_INFORMATION), + QXL_ALLOC_TAG))) { + DEBUG_PRINT((0, "%s: alloc mem failed\n", __FUNCTION__)); + return ERROR_NOT_ENOUGH_MEMORY; + } +#endif + VideoPortZeroMemory(modes_info, sizeof(VIDEO_MODE_INFORMATION) * n_modes); + for (i = 0; i < n_modes; i++) { + error = SetVideoModeInfo(&modes_info[i], &modes->modes[i]); + if (error != NO_ERROR) { + VideoPortFreePool(dev, modes_info); + DEBUG_PRINT((0, "%s: set video mode failed\n", __FUNCTION__)); + return error; + } + } + dev->n_modes = n_modes; + dev->modes = modes_info; + DEBUG_PRINT((0, "%s OK\n", __FUNCTION__)); + return NO_ERROR; +} + +#if defined(ALLOC_PRAGMA) +void DevExternsionCleanup(QXLExtension *dev); +#pragma alloc_text(PAGE, DevExternsionCleanup) +#endif + +void DevExternsionCleanup(QXLExtension *dev) +{ + if (dev->sleep_event) { + VideoPortDeleteEvent(dev, dev->sleep_event); + } + + if (dev->cursor_event) { + VideoPortDeleteEvent(dev, dev->cursor_event); + } + + if (dev->display_event) { + VideoPortDeleteEvent(dev, dev->display_event); + } + + if (dev->rom) { + VideoPortUnmapMemory(dev, dev->rom, NULL); + } + + if (dev->ram_start) { + VideoPortUnmapMemory(dev, dev->ram_start, NULL); + } + + if (dev->modes) { + VideoPortFreePool(dev, dev->modes); + } + + VideoPortZeroMemory(dev, sizeof(QXLExtension)); +} + +VP_STATUS FindAdapter(PVOID dev_extension, + PVOID reserved, + PWSTR arg_str, + PVIDEO_PORT_CONFIG_INFO conf_info, + PUCHAR again) +{ + QXLExtension *dev_ext = dev_extension; + VP_STATUS status; + VIDEO_ACCESS_RANGE ranges[QXL_PCI_RANGES]; + PEVENT display_event = NULL; + PEVENT cursor_event = NULL; + PEVENT sleep_event = NULL; +#if (WINVER >= 0x0501) + VPOSVERSIONINFO sys_info; +#endif + + PAGED_CODE(); + + DEBUG_PRINT((0, "%s: enter\n", __FUNCTION__)); + +#if (WINVER >= 0x0501) + VideoPortZeroMemory(&sys_info, sizeof(VPOSVERSIONINFO)); + sys_info.Size = sizeof(VPOSVERSIONINFO); + if ((status = VideoPortGetVersion(dev_ext, &sys_info)) != NO_ERROR || + sys_info.MajorVersion < 5 || (sys_info.MajorVersion == 5 && sys_info.MinorVersion < 1) ) { + DEBUG_PRINT((0, "%s: err not supported, status %lu major %lu minor %lu\n", + __FUNCTION__, status, sys_info.MajorVersion, sys_info.MinorVersion)); + return ERROR_NOT_SUPPORTED; + } +#endif + + if (conf_info->Length < sizeof(VIDEO_PORT_CONFIG_INFO)) { + DEBUG_PRINT((0, "%s: err configInfo size\n", __FUNCTION__)); + return ERROR_INVALID_PARAMETER; + } + + if (conf_info->AdapterInterfaceType != PCIBus) { + DEBUG_PRINT((0, "%s: not a PCI device %d\n", + __FUNCTION__, conf_info->AdapterInterfaceType)); + return ERROR_DEV_NOT_EXIST; + } + + if ((status = VideoPortCreateEvent(dev_ext, 0, NULL, &display_event)) != NO_ERROR) { + DEBUG_PRINT((0, "%s: create display event failed %lu\n", + __FUNCTION__, status)); + return status; + } + + if ((status = VideoPortCreateEvent(dev_ext, 0, NULL, &cursor_event)) != NO_ERROR) { + DEBUG_PRINT((0, "%s: create cursor event failed %lu\n", + __FUNCTION__, status)); + VideoPortDeleteEvent(dev_ext, display_event); + return status; + } + + if ((status = VideoPortCreateEvent(dev_ext, 0, NULL, &sleep_event)) != NO_ERROR) { + DEBUG_PRINT((0, "%s: create sleep event failed %lu\n", + __FUNCTION__, status)); + VideoPortDeleteEvent(dev_ext, display_event); + VideoPortDeleteEvent(dev_ext, cursor_event); + return status; + } + + dev_ext->display_event = display_event; + dev_ext->cursor_event = cursor_event; + dev_ext->sleep_event = sleep_event; + + if ((status = Prob(dev_ext, conf_info, ranges, QXL_PCI_RANGES)) != NO_ERROR || + (status = InitIO(dev_ext, &ranges[QXL_IO_RANGE_INDEX])) != NO_ERROR || + (status = InitRom(dev_ext, &ranges[QXL_ROM_RANGE_INDEX])) != NO_ERROR || + (status = InitRam(dev_ext, &ranges[QXL_RAM_RANGE_INDEX])) != NO_ERROR || + (status = InitVRAM(dev_ext, &ranges[QXL_VRAM_RANGE_INDEX])) != NO_ERROR || + (status = InitModes(dev_ext)) != NO_ERROR ) { + DEBUG_PRINT((0, "%s: findAdapter failed\n", __FUNCTION__)); + DevExternsionCleanup(dev_ext); + } + + if (VideoPortSetRegistryParameters(dev_extension, L"QxlDeviceID", + &dev_ext->rom->id, sizeof(UINT32)) != NO_ERROR) { + DEBUG_PRINT((0, "%s: write QXL ID failed\n", __FUNCTION__)); + } + + DEBUG_PRINT((0, "%s: exit %lu\n", __FUNCTION__, status)); + return status; +} + + +#if defined(ALLOC_PRAGMA) +void HWReset(QXLExtension *dev_ext); +#pragma alloc_text(PAGE, HWReset) +#endif + +void HWReset(QXLExtension *dev_ext) +{ + PAGED_CODE(); + DEBUG_PRINT((0, "%s\n", __FUNCTION__)); + VideoPortWritePortUchar((PUCHAR)dev_ext->io_base + QXL_IO_RESET, 0); + DEBUG_PRINT((0, "%s: done\n", __FUNCTION__)); +} + +BOOLEAN Initialize(PVOID dev_ext) +{ + PAGED_CODE(); + DEBUG_PRINT((0, "%s: enter\n", __FUNCTION__)); + HWReset(dev_ext); + DEBUG_PRINT((0, "%s: done\n", __FUNCTION__)); + return TRUE; +} + +VP_STATUS GetPowerState(PVOID dev_extension, + ULONG hw_id, + PVIDEO_POWER_MANAGEMENT pm_stat) +{ + PAGED_CODE(); + DEBUG_PRINT((0, "%s: %lu\n", __FUNCTION__, pm_stat->PowerState)); + + switch (hw_id) { + case DISPLAY_ADAPTER_HW_ID: + switch (pm_stat->PowerState) { + case VideoPowerOn: + case VideoPowerStandBy: + case VideoPowerSuspend: + case VideoPowerOff: + case VideoPowerShutdown: + case VideoPowerHibernate: + DEBUG_PRINT((0, "%s: OK\n", __FUNCTION__)); + return NO_ERROR; + } + break; + default: + DEBUG_PRINT((0, "%s: unexpected hw_id %lu\n", __FUNCTION__, hw_id)); + } + DEBUG_PRINT((0, "%s: ERROR_DEVICE_REINITIALIZATION_NEEDED\n", __FUNCTION__)); + return ERROR_DEVICE_REINITIALIZATION_NEEDED; +} + +VP_STATUS SetPowerState(PVOID dev_extension, + ULONG hw_id, + PVIDEO_POWER_MANAGEMENT pm_stat) +{ + PAGED_CODE(); + DEBUG_PRINT((0, "%s: %lu\n", __FUNCTION__, pm_stat->PowerState)); + + switch (hw_id) { + case DISPLAY_ADAPTER_HW_ID: + switch (pm_stat->PowerState) { + case VideoPowerOn: + case VideoPowerStandBy: + case VideoPowerSuspend: + case VideoPowerOff: + case VideoPowerShutdown: + case VideoPowerHibernate: + DEBUG_PRINT((0, "%s: OK\n", __FUNCTION__)); + return NO_ERROR; + } + break; + default: + DEBUG_PRINT((0, "%s: unexpected hw_id %lu\n", __FUNCTION__, hw_id)); + } + DEBUG_PRINT((0, "%s: ERROR_DEVICE_REINITIALIZATION_NEEDED\n", __FUNCTION__)); + return ERROR_DEVICE_REINITIALIZATION_NEEDED; +} + +VP_STATUS GetChildDescriptor(IN PVOID dev_extension, + IN PVIDEO_CHILD_ENUM_INFO enum_info, + OUT PVIDEO_CHILD_TYPE type, + OUT PUCHAR descriptor, + OUT PULONG uid, + OUT PULONG unused) +{ + PAGED_CODE(); + DEBUG_PRINT((0, "%s: enter\n", __FUNCTION__)); + + switch (enum_info->ChildIndex) { + case 0: + DEBUG_PRINT((0, "%s: ACPI id %u\n", __FUNCTION__, enum_info->ACPIHwId)); + return ERROR_NO_MORE_DEVICES; + case 1: + DEBUG_PRINT((0, "%s: Monitor\n", __FUNCTION__)); + /* + *pChildType = Monitor; + todo: handle EDID + return ERROR_MORE_DATA; + */ + return ERROR_NO_MORE_DEVICES; + } + DEBUG_PRINT((0, "%s: ERROR_NO_MORE_DEVICES\n", __FUNCTION__)); + return ERROR_NO_MORE_DEVICES; +} + +#if defined(ALLOC_PRAGMA) +PVIDEO_MODE_INFORMATION FindMode(QXLExtension *dev_ext, ULONG mode); +#pragma alloc_text(PAGE, FindMode) +#endif + +#define IsValidMode(dev, mode) (FindMode(dev, mode) != NULL) + +PVIDEO_MODE_INFORMATION FindMode(QXLExtension *dev_ext, ULONG mode) +{ + VIDEO_MODE_INFORMATION *inf; + VIDEO_MODE_INFORMATION *end; + + PAGED_CODE(); + DEBUG_PRINT((0, "%s\n", __FUNCTION__)); + + inf = dev_ext->modes; + end = inf + dev_ext->n_modes; + for (; inf < end; inf++) { + if (inf->ModeIndex == mode) { + DEBUG_PRINT((0, "%s: OK mode %lu res %lu-%lu orientation %lu\n", __FUNCTION__, + mode, inf->VisScreenWidth, inf->VisScreenHeight, + inf->DriverSpecificAttributeFlags )); + return inf; + } + } + DEBUG_PRINT((0, "%s: mod info not found\n", __FUNCTION__)); + return NULL; +} + +BOOLEAN StartIO(PVOID dev_extension, PVIDEO_REQUEST_PACKET packet) +{ + QXLExtension *dev_ext = dev_extension; + VP_STATUS error; + + PAGED_CODE(); + DEBUG_PRINT((0, "%s\n", __FUNCTION__)); + + switch (packet->IoControlCode) { + case IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES: + DEBUG_PRINT((0, "%s: IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES\n", __FUNCTION__)); + if (packet->OutputBufferLength < (packet->StatusBlock->Information = + sizeof(VIDEO_NUM_MODES))) { + error = ERROR_INSUFFICIENT_BUFFER; + goto err; + } + ((PVIDEO_NUM_MODES)packet->OutputBuffer)->NumModes = dev_ext->n_modes; + ((PVIDEO_NUM_MODES)packet->OutputBuffer)->ModeInformationLength = + sizeof(VIDEO_MODE_INFORMATION); + break; + case IOCTL_VIDEO_QUERY_AVAIL_MODES: { + VIDEO_MODE_INFORMATION *inf; + VIDEO_MODE_INFORMATION *end; + VIDEO_MODE_INFORMATION *out; + + DEBUG_PRINT((0, "%s: IOCTL_VIDEO_QUERY_AVAIL_MODES\n", __FUNCTION__)); + if (packet->OutputBufferLength < (packet->StatusBlock->Information = + dev_ext->n_modes * sizeof(VIDEO_MODE_INFORMATION))) { + error = ERROR_INSUFFICIENT_BUFFER; + goto err; + } + out = packet->OutputBuffer; + inf = dev_ext->modes; + end = inf + dev_ext->n_modes; + for ( ;inf < end; out++, inf++) { + *out = *inf; + } + } + break; + case IOCTL_VIDEO_SET_CURRENT_MODE: { + ULONG request_mode; + DEBUG_PRINT((0, "%s: IOCTL_VIDEO_SET_CURRENT_MODE\n", __FUNCTION__)); + if (packet->InputBufferLength < sizeof(VIDEO_MODE)) { + error = ERROR_INSUFFICIENT_BUFFER; + goto err; + } + request_mode = ((PVIDEO_MODE)packet->InputBuffer)->RequestedMode; + DEBUG_PRINT((0, "%s: mode %u\n", __FUNCTION__, request_mode)); + if (!IsValidMode(dev_ext, request_mode)) { + error = ERROR_INVALID_PARAMETER; + goto err; + } + VideoPortWritePortUchar((PUCHAR)dev_ext->io_base + QXL_IO_SET_MODE, + (UCHAR)request_mode); + dev_ext->ram_header->int_mask = ~0; + VideoPortWritePortUchar((PUCHAR)dev_ext->io_base + QXL_IO_UPDATE_IRQ, 0); + } + break; + case IOCTL_VIDEO_QUERY_CURRENT_MODE: { + PVIDEO_MODE_INFORMATION inf; + + DEBUG_PRINT((0, "%s: IOCTL_VIDEO_QUERY_CURRENT_MODE\n", __FUNCTION__)); + + if (packet->OutputBufferLength < (packet->StatusBlock->Information = + sizeof(VIDEO_MODE_INFORMATION))) { + error = ERROR_INSUFFICIENT_BUFFER; + goto err; + } + + if ((inf = FindMode(dev_ext, dev_ext->rom->mode)) == NULL) { + DEBUG_PRINT((0, "%s: mod info not found\n", __FUNCTION__)); + error = ERROR_INVALID_DATA; + goto err; + } + *(PVIDEO_MODE_INFORMATION)packet->OutputBuffer = *inf; + } + break; + case IOCTL_VIDEO_MAP_VIDEO_MEMORY: { + PVIDEO_MEMORY_INFORMATION mem_info; + ULONG fb_io_space; + + DEBUG_PRINT((0, "%s: IOCTL_VIDEO_MAP_VIDEO_MEMORY\n", __FUNCTION__)); + + if (packet->OutputBufferLength < (packet->StatusBlock->Information = + sizeof(VIDEO_MEMORY_INFORMATION)) || + ( packet->InputBufferLength < sizeof(VIDEO_MEMORY) ) ) { + error = ERROR_INSUFFICIENT_BUFFER; + goto err; + } + mem_info = packet->OutputBuffer; + + mem_info->VideoRamBase = + ((PVIDEO_MEMORY)(packet->InputBuffer))->RequestedVirtualAddress; + ASSERT(mem_info->VideoRamBase == NULL); + mem_info->VideoRamLength = dev_ext->vram_size; + fb_io_space = VIDEO_MEMORY_SPACE_MEMORY; + + if ((error = VideoPortMapMemory(dev_ext, dev_ext->vram_physical, + &mem_info->VideoRamLength, + &fb_io_space, &mem_info->VideoRamBase)) != NO_ERROR) { + DEBUG_PRINT((0, "%s: map filed\n", __FUNCTION__)); + goto err; + } + DEBUG_PRINT((0, "%s: vram size %lu ret size %lu fb vaddr 0x%lx\n", + __FUNCTION__, + dev_ext->vram_size, + mem_info->VideoRamLength, + mem_info->VideoRamBase)); + if (mem_info->VideoRamLength < dev_ext->vram_size) { + DEBUG_PRINT((0, "%s: fb shrink\n", __FUNCTION__)); + VideoPortUnmapMemory(dev_ext, mem_info->VideoRamBase, NULL); + mem_info->VideoRamBase = NULL; + mem_info->VideoRamLength = 0; + error = ERROR_NOT_ENOUGH_MEMORY; + goto err; + } + mem_info->FrameBufferBase = mem_info->VideoRamBase; + mem_info->FrameBufferLength = mem_info->VideoRamLength; +#ifdef DBG + DEBUG_PRINT((0, "%s: zap\n", __FUNCTION__)); + VideoPortZeroMemory(mem_info->VideoRamBase, mem_info->VideoRamLength); + DEBUG_PRINT((0, "%s: zap done\n", __FUNCTION__)); +#endif + } + break; + case IOCTL_VIDEO_UNMAP_VIDEO_MEMORY: { + PVOID addr; + + DEBUG_PRINT((0, "%s: IOCTL_VIDEO_UNMAP_VIDEO_MEMORY\n", __FUNCTION__)); + + if (packet->InputBufferLength < sizeof(VIDEO_MEMORY)) { + error = ERROR_INSUFFICIENT_BUFFER; + goto err; + } + addr = ((PVIDEO_MEMORY)(packet->InputBuffer))->RequestedVirtualAddress; + if ((error = VideoPortUnmapMemory(dev_ext, addr, NULL)) != NO_ERROR) { + DEBUG_PRINT((0, "%s: unmap failed\n", __FUNCTION__)); + } + } + break; + case IOCTL_VIDEO_RESET_DEVICE: + DEBUG_PRINT((0, "%s: IOCTL_VIDEO_RESET_DEVICE\n", __FUNCTION__)); + HWReset(dev_ext); + break; + case IOCTL_QXL_GET_INFO: { + QXLDriverInfo *driver_info; + DEBUG_PRINT((0, "%s: IOCTL_QXL_GET_INFO\n", __FUNCTION__)); + + if (packet->OutputBufferLength < (packet->StatusBlock->Information = + sizeof(QXLDriverInfo))) { + error = ERROR_INSUFFICIENT_BUFFER; + goto err; + } + + driver_info = packet->OutputBuffer; + driver_info->version = QXL_DRIVER_INFO_VERSION; + driver_info->display_event = dev_ext->display_event; + driver_info->cursor_event = dev_ext->cursor_event; + driver_info->sleep_event = dev_ext->sleep_event; + driver_info->cmd_ring = &dev_ext->ram_header->cmd_ring; + driver_info->cursor_ring = &dev_ext->ram_header->cursor_ring; + driver_info->release_ring = &dev_ext->ram_header->release_ring; + driver_info->notify_cmd_port = dev_ext->io_port + QXL_IO_NOTIFY_CMD; + driver_info->notify_cursor_port = dev_ext->io_port + QXL_IO_NOTIFY_CURSOR; + driver_info->notify_oom_port = dev_ext->io_port + QXL_IO_NOTIFY_OOM; + driver_info->log_port = dev_ext->io_port + QXL_IO_LOG; + driver_info->log_buf = dev_ext->ram_header->log_buf; + + driver_info->draw_area = dev_ext->ram_start + dev_ext->rom->draw_area_offset; + driver_info->draw_area_size = dev_ext->rom->draw_area_size; + driver_info->update_id = &dev_ext->rom->update_id; + driver_info->mm_clock = &dev_ext->rom->mm_clock; + driver_info->compression_level = &dev_ext->rom->compression_level; + driver_info->log_level = &dev_ext->rom->log_level; + driver_info->update_area_port = dev_ext->io_port + QXL_IO_UPDATE_AREA; + driver_info->update_area = &dev_ext->ram_header->update_area; + + driver_info->num_io_pages = dev_ext->rom->num_io_pages; + driver_info->io_pages_virt = dev_ext->ram_start + dev_ext->rom->pages_offset; + driver_info->io_pages_phys = dev_ext->ram_physical.QuadPart + + dev_ext->rom->pages_offset; +#if (WINVER < 0x0501) + driver_info->WaitForEvent = QXLWaitForEvent; +#endif + } + break; + default: + DEBUG_PRINT((0, "%s: invalid command 0x%lx\n", __FUNCTION__, packet->IoControlCode)); + error = ERROR_INVALID_FUNCTION; + goto err; + } + packet->StatusBlock->Status = NO_ERROR; + DEBUG_PRINT((0, "%s: OK\n", __FUNCTION__)); + return TRUE; + err: + packet->StatusBlock->Information = 0; + packet->StatusBlock->Status = error; + DEBUG_PRINT((0, "%s: ERR\n", __FUNCTION__)); + return TRUE; +} + +VOID InterruptCallback(PVOID dev_extension, PVOID Context) +{ + QXLExtension *dev_ext = dev_extension; + UINT32 pending = VideoPortInterlockedExchange(&dev_ext->ram_header->int_pending, 0); + + if (pending & QXL_INTERRUPT_DISPLAY) { + VideoPortSetEvent(dev_ext, dev_ext->display_event); + } if (pending & QXL_INTERRUPT_CURSOR) { + VideoPortSetEvent(dev_ext, dev_ext->cursor_event); + } + + dev_ext->ram_header->int_mask = ~0; + VideoPortWritePortUchar((PUCHAR)dev_ext->io_base + QXL_IO_UPDATE_IRQ, 0); +} + +BOOLEAN Interrupt(PVOID dev_extension) +{ + QXLExtension *dev_ext = dev_extension; + + if (!(dev_ext->ram_header->int_pending & dev_ext->ram_header->int_mask)) { + return FALSE; + } + dev_ext->ram_header->int_mask = 0; + VideoPortWritePortUchar((PUCHAR)dev_ext->io_base + QXL_IO_UPDATE_IRQ, 0); + + if (!VideoPortQueueDpc(dev_extension, InterruptCallback, NULL)) { + VideoPortLogError(dev_extension, NULL, E_UNEXPECTED, QXLERR_INT_DELIVERY); + dev_ext->ram_header->int_mask = ~0; + VideoPortWritePortUchar((PUCHAR)dev_ext->io_base + QXL_IO_UPDATE_IRQ, 0); + } + return TRUE; +} diff --git a/miniport/qxl.h b/miniport/qxl.h new file mode 100644 index 0000000..36d3a07 --- /dev/null +++ b/miniport/qxl.h @@ -0,0 +1,38 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "winerror.h" +#include "devioctl.h" +#include "miniport.h" +#include "ntddvdeo.h" +#include "video.h" + +#include "qxl_driver.h" + +enum { + QXLERR_INT_DELIVERY = 1, +}; + +#define PAGE_SHIFT 12 +#define PAGE_SIZE (1 << PAGE_SHIFT) + +#if DBG +#define DEBUG_PRINT(arg) VideoDebugPrint(arg) +#else +#define DEBUG_PRINT(arg) +#endif + diff --git a/miniport/qxl.inf b/miniport/qxl.inf new file mode 100644 index 0000000..ccb8524 --- /dev/null +++ b/miniport/qxl.inf @@ -0,0 +1,72 @@ +
+; Installation inf for qxl driver
+
+[Version]
+Signature = "$CHICAGO$"
+DriverVer = 10/19/2009,1.1.0.0
+Provider = %RHAT%
+Class = Display
+ClassGUID = {4d36e968-e325-11ce-bfc1-08002be10318}
+
+[DestinationDirs]
+DefaultDestDir = 11 ; system32
+qxl.Miniport = 12 ; drivers
+qxl.Display = 11 ; system32
+
+[Manufacturer]
+%RHAT% = q.Mfg
+
+[q.Mfg]
+%RHAT% %QXL% = qxl, PCI\VEN_1b36&DEV_0100
+
+[ControlFlags]
+ExcludeFromSelect = *
+
+[qxl]
+CopyFiles = qxl.Miniport, qxl.Display
+
+[qxl.Miniport]
+qxl.sys
+
+[qxl.Display]
+qxldd.dll
+
+[SourceDisksNames]
+1 = %DiskId%
+
+[SourceDisksFiles]
+qxl.sys = 1
+qxldd.dll = 1
+
+[qxl.SoftwareSettings]
+AddReg = qxl_SoftwareDeviceSettings
+
+[qxl_SoftwareDeviceSettings]
+HKR,, InstalledDisplayDrivers, %REG_MULTI_SZ%, qxldd
+HKR,, VgaCompatible, %REG_DWORD%, 0
+HKR,, DefaultSettings.BitsPerPel, %REG_DWORD%, 32
+HKR,, DefaultSettings.XResolution, %REG_DWORD%, 800
+HKR,, DefaultSettings.YResolution, %REG_DWORD%, 600
+HKR,, Acceleration.Level, %REG_DWORD%, 0
+
+[qxl.Services]
+AddService = qxl, 0x00000002, qxl_Service_Inst ; Assign the named service as the PnP function driver
+
+[qxl_Service_Inst]
+ServiceType = 1 ; SERVICE_KERNEL_DRIVER
+StartType = 3 ; SERVICE_DEMAND_START
+ErrorControl = 0 ; SERVICE_ERROR_IGNORE
+LoadOrderGroup = Video
+ServiceBinary = %12%\qxl.sys
+
+[Strings]
+RHAT = "Red Hat"
+QXL = "QXL GPU"
+DiskId = "Windows 2000 Driver Installation Disk"
+
+REG_SZ = 0x00000000
+REG_MULTI_SZ = 0x00010000
+REG_EXPAND_SZ = 0x00020000
+REG_BINARY = 0x00000001
+REG_DWORD = 0x00010001
+FLG_ADDREG_DELVAL = 0x00000004
diff --git a/miniport/qxl.rc b/miniport/qxl.rc new file mode 100644 index 0000000..a73c6c1 --- /dev/null +++ b/miniport/qxl.rc @@ -0,0 +1,29 @@ +#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DRV
+#define VER_FILESUBTYPE VFT2_DRV_DISPLAY
+
+#undef VER_COMPANYNAME_STR
+#undef VER_FILEVERSION_STR
+#undef VER_LEGALCOPYRIGHT_STR
+#undef VER_LEGALCOPYRIGHT_YEARS
+#undef VER_PRODUCTNAME_STR
+#undef VER_PRODUCTVERSION_STR
+
+
+#define VER_FILEDESCRIPTION_STR "Red Hat QXL Display Driver"
+#define VER_INTERNALNAME_STR "qxl.sys"
+#define VER_ORIGINALFILENAME_STR VER_INTERNALNAME_STR
+#define VER_FILEVERSION_STR "1.1.0.0"
+#define VER_PRODUCTNAME_STR "Spice"
+#define VER_PRODUCTVERSION_STR "1.1.0.0"
+
+#undef VER_PRODUCTVERSION
+#define VER_PRODUCTVERSION 1,1,0,0
+
+#define VER_COMPANYNAME_STR "Red Hat Inc."
+#define VER_LEGALCOPYRIGHT_STR "© Red Hat Inc. All rights reserved."
+
+#include "common.ver"
diff --git a/miniport/sources b/miniport/sources new file mode 100644 index 0000000..15b1c76 --- /dev/null +++ b/miniport/sources @@ -0,0 +1,30 @@ +TARGETNAME=qxl
+TARGETPATH=obj
+TARGETTYPE=MINIPORT
+
+TARGETLIBS=$(DDK_LIB_PATH)\videoprt.lib
+
+!if !defined(DDK_TARGET_OS) || "$(DDK_TARGET_OS)"=="Win2K"
+#
+# The driver is built in the Win2K build environment
+#
+TARGETLIBS=$(TARGETLIBS) $(DDK_LIB_PATH)\ntoskrnl.lib
+!endif
+
+
+AXP64_FLAGS=/QA21164
+
+!IFNDEF MSC_WARNING_LEVEL
+MSC_WARNING_LEVEL=/W3
+!ENDIF
+MSC_WARNING_LEVEL=$(MSC_WARNING_LEVEL) /WX
+
+INCLUDES=$(SPICE_COMMON_DIR); ..\include
+
+SOURCES=qxl.c \
+ wdmhelper.c \
+ qxl.rc
+
+MISCFILES=qxl.inf
+
+
diff --git a/miniport/wdmhelper.c b/miniport/wdmhelper.c new file mode 100644 index 0000000..344a49b --- /dev/null +++ b/miniport/wdmhelper.c @@ -0,0 +1,60 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if (WINVER < 0x0501) +#include <ntddk.h> +#include "wdmhelper.h" + +void QXLDeleteEvent(PVOID pEvent) +{ + if(pEvent) { + ExFreePool(pEvent); + } +} + +LONG QXLInitializeEvent(PVOID * pEvent) +{ + if(pEvent) { + *pEvent = ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), '_lxq'); + + if(*pEvent) { + KeInitializeEvent((PRKEVENT)*pEvent, SynchronizationEvent, FALSE); + } + } + + return 0; +} + +void QXLSetEvent(PVOID pEvent) +{ + //Pririty boost can be switched to IO_NO_INCREMENT + if(pEvent) { + KeSetEvent((PRKEVENT)pEvent, IO_VIDEO_INCREMENT, FALSE); + } +} + +ULONG QXLWaitForEvent(PVOID pEvent, PLARGE_INTEGER Timeout) +{ + if(pEvent) { + return NT_SUCCESS(KeWaitForSingleObject(pEvent, Executive, KernelMode, TRUE, Timeout)); + } + + return FALSE; +} + +#endif + |