diff options
Diffstat (limited to 'xddm')
42 files changed, 18271 insertions, 0 deletions
diff --git a/xddm/COPYING b/xddm/COPYING new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/xddm/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/xddm/build.bat b/xddm/build.bat new file mode 100755 index 0000000..1d19875 --- /dev/null +++ b/xddm/build.bat @@ -0,0 +1,45 @@ +@echo off
+
+Rem
+Rem Build and copy to target based on environment variables set by WinDDK\bin\setenv.
+Rem if an argument is supplied the build products are copied there too under a subdirectory.
+Rem
+
+Rem Just use %BUILD_ALT_DIR%, it is equivalent
+Rem set TARGET=%DDK_TARGET_OS%_%_BUILDARCH%_%BUILD_TYPE%
+
+if not DEFINED BUILD_ALT_DIR (
+ echo BUILD_ALT_DIR not defined. Please run in a WinDDK Build Environment.
+ goto exit
+)
+if not DEFINED SPICE_COMMON_DIR (
+ set SPICE_COMMON_DIR=%CD%\..\spice-protocol
+)
+
+set TARGET=install_%BUILD_ALT_DIR%
+echo TARGET=%TARGET%
+if not exist %TARGET% mkdir %TARGET%
+
+if not x%1 == x set DEST=%1
+
+:build
+cd miniport
+build -cZg
+cd ../display
+build -cZg
+cd ../
+
+:copy_local
+copy display\obj%BUILD_ALT_DIR%\i386\qxldd.dll %TARGET%
+copy miniport\obj%BUILD_ALT_DIR%\i386\qxl.sys %TARGET%
+copy miniport\qxl.inf %TARGET%
+copy display\obj%BUILD_ALT_DIR%\i386\qxldd.pdb %TARGET%
+copy miniport\obj%BUILD_ALT_DIR%\i386\qxl.pdb %TARGET%
+if not defined DEST goto exit
+if exist %DEST% (
+ echo copying to %DEST%
+ if not exist %DEST%\%TARGET% ( mkdir "%DEST%\%TARGET%" )
+ xcopy /s /y %TARGET% %DEST%\%TARGET%
+)
+
+:exit
diff --git a/xddm/dirs b/xddm/dirs new file mode 100644 index 0000000..94db1d0 --- /dev/null +++ b/xddm/dirs @@ -0,0 +1,4 @@ +DIRS= \
+ display \
+ miniport
+
diff --git a/xddm/display/brush.c b/xddm/display/brush.c new file mode 100644 index 0000000..0b9400d --- /dev/null +++ b/xddm/display/brush.c @@ -0,0 +1,400 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This software is licensed under the GNU General Public License, + version 2 (GPLv2) (see COPYING for details), subject to the + following clarification. + + With respect to binaries built using the Microsoft(R) Windows + Driver Kit (WDK), GPLv2 does not extend to any code contained in or + derived from the WDK ("WDK Code"). As to WDK Code, by using or + distributing such binaries you agree to be bound by the Microsoft + Software License Terms for the WDK. All WDK Code is considered by + the GPLv2 licensors to qualify for the special exception stated in + section 3 of GPLv2 (commonly known as the system library + exception). + + There is NO WARRANTY for this software, express or implied, + including the implied warranties of NON-INFRINGEMENT, TITLE, + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#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, QXLPHYSICAL *pattern, + InternalBrush *brush, INT32 *surface_dest, + QXLRect *surface_rect) +{ + HSURF hsurf; + SURFOBJ *surf_obj; + QXLRect 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; + + CopyRect(surface_rect, &area); + + if (!QXLGetBitmap(pdev, drawable, pattern, surf_obj, &area, NULL, &key, TRUE, surface_dest)) { + 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, QXLBrush *qxl_brush, + BRUSHOBJ *brush, POINTL *brush_pos, INT32 *surface_dest, + QXLRect *surface_rect) +{ + 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 = SPICE_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, + surface_dest, surface_rect)) { + return FALSE; + } + } else { + qxl_brush->type = SPICE_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/xddm/display/driver.c b/xddm/display/driver.c new file mode 100644 index 0000000..d7fdbf7 --- /dev/null +++ b/xddm/display/driver.c @@ -0,0 +1,1590 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This software is licensed under the GNU General Public License, + version 2 (GPLv2) (see COPYING for details), subject to the + following clarification. + + With respect to binaries built using the Microsoft(R) Windows + Driver Kit (WDK), GPLv2 does not extend to any code contained in or + derived from the WDK ("WDK Code"). As to WDK Code, by using or + distributing such binaries you agree to be bound by the Microsoft + Software License Terms for the WDK. All WDK Code is considered by + the GPLv2 licensors to qualify for the special exception stated in + section 3 of GPLv2 (commonly known as the system library + exception). + + There is NO WARRANTY for this software, express or implied, + including the implied warranties of NON-INFRINGEMENT, TITLE, + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#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 "qxldd.h" +#include "utils.h" +#include "mspace.h" +#include "res.h" +#include "surface.h" + +#define DEVICE_NAME L"qxldd" + +#define QXLDD_DEBUG_PREFIX "qxldd: " + +static DRVFN drv_calls[] = { + {INDEX_DrvDisableDriver, (PFN)DrvDisableDriver}, + {INDEX_DrvEscape, (PFN)DrvEscape}, + {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}, + {INDEX_DrvCreateDeviceBitmap, (PFN)DrvCreateDeviceBitmap}, + {INDEX_DrvDeleteDeviceBitmap, (PFN)DrvDeleteDeviceBitmap}, + +#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); + sync_io(pdev, 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 && pdev->log_level ? (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_GRAY16 | GCAPS_OPAQUERECT | + 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 BOOL PrepareHardware(PDev *pdev); + +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(); +#ifndef _WIN64 + CheckAndSetSSE2(); +#endif + DEBUG_PRINT((NULL, 1, "%s: end\n", __FUNCTION__)); + return TRUE; +} + +ULONG DrvEscape(SURFOBJ *pso, ULONG iEsc, ULONG cjIn, PVOID pvIn, + ULONG cjOut, PVOID pvOut) +{ + PDev* pdev = pso ? (PDev*)pso->dhpdev : NULL; + int RetVal = -1; + + switch (iEsc) { + case QXL_ESCAPE_SET_CUSTOM_DISPLAY: { + ULONG length; + + DEBUG_PRINT((pdev, 1, "set custom display %p\n", pdev)); + if (pdev == NULL) + break; + + if (EngDeviceIoControl(pdev->driver, IOCTL_QXL_SET_CUSTOM_DISPLAY, + pvIn, cjIn, NULL, 0, &length)) { + DEBUG_PRINT((pdev, 0, "%s: IOCTL_QXL_SET_CUSTOM_DISPLAY failed\n", __FUNCTION__)); + break; + } + RetVal = 1; + break; + } + default: + DEBUG_PRINT((NULL, 1, "%s: unhandled escape code %d\n", __FUNCTION__, iEsc)); + RetVal = 0; + } + + DEBUG_PRINT((NULL, 1, "%s: end\n", __FUNCTION__)); + return RetVal; +} + +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))) { + + 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 (!ResInit(pdev)) { + DEBUG_PRINT((NULL, 0, "%s: init res failed\n", __FUNCTION__)); + goto err2; + } + + RtlCopyMemory(dev_caps, &gdi_info, dev_caps_size); + RtlCopyMemory(in_dev_info, &dev_info, dev_inf_size); + + pdev->enabled = TRUE; /* assume no operations before a DrvEnablePDEV. */ + DEBUG_PRINT((NULL, 1, "%s: 0x%lx\n", __FUNCTION__, pdev)); + return(DHPDEV)pdev; + +err2: + DestroyPalette(pdev); + +err1: + EngFreeMem(pdev); + + return NULL; +} + +#ifdef DBG +static void DebugCountAliveSurfaces(PDev *pdev) +{ + UINT32 i; + SurfaceInfo *surface_info; + int total = 0; + int of_pdev = 0; + int no_surf_obj = 0; + + for (i = 0 ; i < pdev->n_surfaces; ++i) { + surface_info = GetSurfaceInfo(pdev, i); + if (surface_info->draw_area.base_mem != NULL) { + total++; + // all should belong to the same pdev + if (surface_info->u.pdev == pdev) { + of_pdev++; + if (surface_info->draw_area.surf_obj == NULL) { + no_surf_obj++; + } + } + } + } + DEBUG_PRINT((pdev, 1, "%s: %p: %d / %d / %d (total,pdev,no_surf_obj)\n", __FUNCTION__, pdev, + total, of_pdev, no_surf_obj)); +} +#else +static void DebugCountAliveSurfaces(PDev *pdev) +{ +} +#endif + +VOID DrvDisablePDEV(DHPDEV in_pdev) +{ + PDev* pdev = (PDev*)in_pdev; + + DEBUG_PRINT((pdev, 1, "%s: 0x%lx\n", __FUNCTION__, pdev)); + ResDestroy(pdev); + DestroyPalette(pdev); + 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)); +} + +static VOID HideMouse(PDev *pdev) +{ + QXLCursorCmd *cursor_cmd; + + cursor_cmd = CursorCmd(pdev); + cursor_cmd->type = QXL_CURSOR_HIDE; + + PushCursorCmd(pdev, cursor_cmd); +} + +static VOID CreatePrimarySurface(PDev *pdev, UINT32 depth, UINT32 format, + UINT32 width, UINT32 height, INT32 stride, + QXLPHYSICAL phys_mem) +{ + pdev->primary_surface_create->format = format; + pdev->primary_surface_create->width = width; + pdev->primary_surface_create->height = height; + pdev->primary_surface_create->stride = -stride; + pdev->primary_surface_create->mem = phys_mem; + + pdev->primary_surface_create->flags = 0; + pdev->primary_surface_create->type = QXL_SURF_TYPE_PRIMARY; + + async_io(pdev, ASYNCABLE_CREATE_PRIMARY, 0); +} + +static void DestroyPrimarySurface(PDev *pdev, int hide_mouse) +{ + if (hide_mouse) { + HideMouse(pdev); + } + async_io(pdev, ASYNCABLE_DESTROY_PRIMARY, 0); +} + +static void DestroyAllSurfaces(PDev *pdev) +{ + HideMouse(pdev); + async_io(pdev, ASYNCABLE_DESTROY_ALL_SURFACES, 0); +} + +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 VOID UpdateMainSlot(PDev *pdev, MemSlot *slot) +{ + QXLPHYSICAL high_bits; + + + pdev->mem_slots[pdev->main_mem_slot].slot = *slot; + + high_bits = pdev->main_mem_slot << pdev->slot_gen_bits; + high_bits |= slot->generation; + high_bits <<= (64 - (pdev->slot_gen_bits + pdev->slot_id_bits)); + pdev->mem_slots[pdev->main_mem_slot].high_bits = high_bits; + + pdev->va_slot_mask = (~(QXLPHYSICAL)0) >> (pdev->slot_id_bits + pdev->slot_gen_bits); +} + +static void RemoveVRamSlot(PDev *pdev) +{ + sync_io(pdev, pdev->memslot_del_port, pdev->vram_mem_slot); + pdev->vram_slot_initialized = FALSE; +} + +static BOOLEAN CreateVRamSlot(PDev *pdev) +{ + QXLMemSlot *slot; + UINT64 high_bits; + UINT8 slot_id = pdev->main_mem_slot + 1; + + if (slot_id >= pdev->num_mem_slot) { + return FALSE; + } + + pdev->va_slot_mask = (~(QXLPHYSICAL)0) >> (pdev->slot_id_bits + pdev->slot_gen_bits); + + + *pdev->ram_slot_start = pdev->fb_phys; + *pdev->ram_slot_end = pdev->fb_phys + pdev->fb_size; + + async_io(pdev, ASYNCABLE_MEMSLOT_ADD, slot_id); + + pdev->vram_mem_slot = slot_id; + + pdev->mem_slots[slot_id].slot.generation = *pdev->slots_generation; + pdev->mem_slots[slot_id].slot.start_phys_addr = pdev->fb_phys; + pdev->mem_slots[slot_id].slot.end_phys_addr = pdev->fb_phys + pdev->fb_size; + pdev->mem_slots[slot_id].slot.start_virt_addr = (UINT64)pdev->fb; + pdev->mem_slots[slot_id].slot.end_virt_addr = (UINT64)pdev->fb + pdev->fb_size; + + high_bits = slot_id << pdev->slot_gen_bits; + high_bits |= pdev->mem_slots[slot_id].slot.generation; + high_bits <<= (64 - (pdev->slot_gen_bits + pdev->slot_id_bits)); + pdev->mem_slots[slot_id].high_bits = high_bits; + + pdev->vram_slot_initialized = TRUE; + + return TRUE; +} + +static BOOL PrepareHardware(PDev *pdev) +{ + VIDEO_MEMORY video_mem; + VIDEO_MEMORY_INFORMATION video_mem_Info; + DWORD length; + QXLDriverInfo dev_info; + QXLPHYSICAL high_bits; + + DEBUG_PRINT((pdev, 1, "%s: 0x%lx\n", __FUNCTION__, pdev)); + + if (!SetHardwareMode(pdev)) { + DEBUG_PRINT((pdev, 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((pdev, 0, "%s: get qxl info failed, 0x%lx\n", __FUNCTION__, pdev)); + return FALSE; + } + + if (dev_info.version != QXL_DRIVER_INFO_VERSION) { + DEBUG_PRINT((pdev, 0, "%s: get qxl info mismatch, 0x%lx\n", __FUNCTION__, pdev)); + return FALSE; + } + + pdev->pci_revision = dev_info.pci_revision; + pdev->use_async = (pdev->pci_revision >= QXL_REVISION_STABLE_V10); + 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->asyncable[ASYNCABLE_UPDATE_AREA][ASYNC] = dev_info.update_area_async_port; + pdev->asyncable[ASYNCABLE_UPDATE_AREA][SYNC] = dev_info.update_area_port; + pdev->asyncable[ASYNCABLE_MEMSLOT_ADD][ASYNC] = dev_info.memslot_add_async_port; + pdev->asyncable[ASYNCABLE_MEMSLOT_ADD][SYNC] = dev_info.memslot_add_port; + pdev->asyncable[ASYNCABLE_CREATE_PRIMARY][ASYNC] = dev_info.create_primary_async_port; + pdev->asyncable[ASYNCABLE_CREATE_PRIMARY][SYNC] = dev_info.create_primary_port; + pdev->asyncable[ASYNCABLE_DESTROY_PRIMARY][ASYNC] = dev_info.destroy_primary_async_port; + pdev->asyncable[ASYNCABLE_DESTROY_PRIMARY][SYNC] = dev_info.destroy_primary_port; + pdev->asyncable[ASYNCABLE_DESTROY_SURFACE][ASYNC] = dev_info.destroy_surface_async_port; + pdev->asyncable[ASYNCABLE_DESTROY_SURFACE][SYNC] = dev_info.destroy_surface_wait_port; + pdev->asyncable[ASYNCABLE_DESTROY_ALL_SURFACES][ASYNC] = dev_info.destroy_all_surfaces_async_port; + pdev->asyncable[ASYNCABLE_DESTROY_ALL_SURFACES][SYNC] = dev_info.destroy_all_surfaces_port; + pdev->asyncable[ASYNCABLE_FLUSH_SURFACES][ASYNC] = dev_info.flush_surfaces_async_port; + pdev->asyncable[ASYNCABLE_FLUSH_SURFACES][SYNC] = NULL; + + pdev->display_event = dev_info.display_event; + pdev->cursor_event = dev_info.cursor_event; + pdev->sleep_event = dev_info.sleep_event; + pdev->io_cmd_event = dev_info.io_cmd_event; +#if (WINVER < 0x0501) + pdev->WaitForEvent = dev_info.WaitForEvent; +#endif + + pdev->num_io_pages = dev_info.num_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_area = dev_info.update_area; + pdev->update_surface = dev_info.update_surface; + + 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; + + pdev->n_surfaces = dev_info.n_surfaces; + + pdev->mem_slots = EngAllocMem(FL_ZERO_MEMORY, sizeof(PMemSlot) * dev_info.num_mem_slot, + ALLOC_TAG); + if (!pdev->mem_slots) { + DEBUG_PRINT((pdev, 0, "%s: mem slots alloc failed, 0x%lx\n", __FUNCTION__, pdev)); + return FALSE; + } + + pdev->slots_generation = dev_info.slots_generation; + pdev->ram_slot_start = dev_info.ram_slot_start; + pdev->ram_slot_end = dev_info.ram_slot_end; + pdev->slot_id_bits = dev_info.slot_id_bits; + pdev->slot_gen_bits = dev_info.slot_gen_bits; + pdev->main_mem_slot = dev_info.main_mem_slot_id; + pdev->num_mem_slot = dev_info.num_mem_slot; + + UpdateMainSlot(pdev, &dev_info.main_mem_slot); + + 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((pdev, 0, "%s: mapping failed, 0x%lx\n", __FUNCTION__, pdev)); + return FALSE; + } + DEBUG_PRINT((pdev, 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; + pdev->fb_phys = dev_info.fb_phys; + + pdev->memslot_del_port = dev_info.memslot_del_port; + + pdev->flush_release_port = dev_info.flush_release_port; + + pdev->primary_memory_start = dev_info.surface0_area; + pdev->primary_memory_size = dev_info.surface0_area_size; + + pdev->primary_surface_create = dev_info.primary_surface_create; + + pdev->dev_id = dev_info.dev_id; + + pdev->create_non_primary_surfaces = dev_info.create_non_primary_surfaces; + DEBUG_PRINT((pdev, 1, "%s: create_non_primary_surfaces = %d\n", __FUNCTION__, + pdev->create_non_primary_surfaces)); + + CreateVRamSlot(pdev); + + DEBUG_PRINT((NULL, 1, "%s: 0x%lx exit: 0x%lx %ul\n", __FUNCTION__, pdev, + pdev->fb, pdev->fb_size)); + return TRUE; +} + +static VOID UnmapFB(PDev *pdev) +{ + VIDEO_MEMORY video_mem; + DWORD length; + + if (!pdev->fb) { + return; + } + + 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)); + } +} + +VOID EnableQXLPrimarySurface(PDev *pdev) +{ + UINT32 depth, format; + + switch (pdev->bitmap_format) { + case BMF_8BPP: + PANIC(pdev, "bad formart type 8bpp\n"); + case BMF_16BPP: + depth = 16; + format = SPICE_SURFACE_FMT_16_555; + break; + case BMF_24BPP: + case BMF_32BPP: + depth = 32; + format = SPICE_SURFACE_FMT_32_xRGB; + break; + default: + PANIC(pdev, "bad formart type\n"); + }; + + CreatePrimarySurface(pdev, depth, format, + pdev->resolution.cx, pdev->resolution.cy, + pdev->stride, pdev->surf_phys); + pdev->surf_enable = TRUE; +} + +HSURF DrvEnableSurface(DHPDEV in_pdev) +{ + PDev *pdev; + HSURF surf; + DWORD length; + QXLPHYSICAL phys_mem; + UINT8 *base_mem; + + pdev = (PDev*)in_pdev; + DEBUG_PRINT((pdev, 1, "%s: 0x%lx\n", __FUNCTION__, in_pdev)); + + if (!PrepareHardware(pdev)) { + return FALSE; + } + InitResources(pdev); + + if (!(surf = (HSURF)CreateDeviceBitmap(pdev, pdev->resolution, pdev->bitmap_format, &phys_mem, + &base_mem, 0, DEVICE_BITMAP_ALLOCATION_TYPE_SURF0))) { + DEBUG_PRINT((pdev, 0, "%s: create device surface failed, 0x%lx\n", + __FUNCTION__, pdev)); + goto err; + } + + DEBUG_PRINT((pdev, 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; + pdev->surf_phys = phys_mem; + pdev->surf_base = base_mem; + + EnableQXLPrimarySurface(pdev); + + DEBUG_PRINT((pdev, 1, "%s: 0x%lx exit\n", __FUNCTION__, pdev)); + return surf; + +err: + DrvDisableSurface((DHPDEV)pdev); + DEBUG_PRINT((pdev, 0, "%s: 0x%lx err\n", __FUNCTION__, pdev)); + return NULL; +} + +VOID DisableQXLPrimarySurface(PDev *pdev, int hide_mouse) +{ + DrawArea drawarea; + + if (pdev->surf_enable) { + DestroyPrimarySurface(pdev, hide_mouse); + pdev->surf_enable = FALSE; + } +} + +VOID DisableQXLAllSurfaces(PDev *pdev) +{ + DestroyAllSurfaces(pdev); +} + +VOID DrvDisableSurface(DHPDEV in_pdev) +{ + PDev *pdev = (PDev*)in_pdev; + DrawArea drawarea; + + DEBUG_PRINT((pdev, 1, "%s: 0x%lx\n", __FUNCTION__, pdev)); + + // Don't destroy the primary - it's destroyed by destroy_all_surfaces + // at AssertModeDisable. Also, msdn specifically mentions DrvDisableSurface + // should not touch the hardware, that should be done just via DrvAssertMode + // (http://msdn.microsoft.com/en-us/library/ff556200%28VS.85%29.aspx) + pdev->surf_enable = FALSE; + UnmapFB(pdev); + + if (pdev->surf) { + DeleteDeviceBitmap(pdev, 0, DEVICE_BITMAP_ALLOCATION_TYPE_SURF0); + EngDeleteSurface(pdev->surf); + pdev->surf = NULL; + } + + if (pdev->mem_slots) { + EngFreeMem(pdev->mem_slots); + pdev->mem_slots = NULL; + } + + DebugCountAliveSurfaces(pdev); + ClearResources(pdev); + DEBUG_PRINT((pdev, 1, "%s: 0x%lx exit\n", __FUNCTION__, pdev)); +} + +static void FlushSurfaces(PDev *pdev) +{ + UINT32 surface_id; + SurfaceInfo *surface_info; + SURFOBJ *surf_obj; + RECTL area = {0, 0, 0, 0}; + + if (pdev->pci_revision < QXL_REVISION_STABLE_V10) { + DEBUG_PRINT((pdev, 1, "%s: revision too old for QXL_IO_FLUSH_SURFACES\n", __FUNCTION__)); + for (surface_id = pdev->n_surfaces - 1; surface_id > 0 ; --surface_id) { + surface_info = GetSurfaceInfo(pdev, surface_id); + + if (!surface_info->draw_area.base_mem) { + continue; + } + surf_obj = surface_info->draw_area.surf_obj; + if (!surf_obj) { + continue; + } + area.right = surf_obj->sizlBitmap.cx; + area.bottom = surf_obj->sizlBitmap.cy; + UpdateArea(pdev,&area, surface_id); + } + } else { + async_io(pdev, ASYNCABLE_FLUSH_SURFACES, 0); + } +} + +static BOOL FlushRelease(PDev *pdev) +{ + if (pdev->pci_revision< QXL_REVISION_STABLE_V10) { + DWORD length; + + DEBUG_PRINT((pdev, 1, "%s: revision too old for QXL_IO_FLUSH_RELEASE\n", __FUNCTION__)); + 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)); + return FALSE; + } + } else { + /* Free release ring contents */ + ReleaseCacheDeviceMemoryResources(pdev); + EmptyReleaseRing(pdev); + /* Get the last free list onto the release ring */ + sync_io(pdev, pdev->flush_release_port, 0); + DEBUG_PRINT((pdev, 4, "%s after FLUSH_RELEASE\n", __FUNCTION__)); + /* And release that. mspace allocators should be clean after. */ + EmptyReleaseRing(pdev); + } + return TRUE; +} + +static BOOL AssertModeDisable(PDev *pdev) +{ + DEBUG_PRINT((pdev, 3, "%s entry\n", __FUNCTION__)); + /* flush command ring and update all surfaces */ + FlushSurfaces(pdev); + DebugCountAliveSurfaces(pdev); + /* + * this call is redundant for + * pci_revision < QXL_REVISION_STABLE_V10, due to the + * IOCTL_VIDEO_RESET_DEVICE in FlushRelease. However, + * MoveAllSurfacesToRam depends on destroy_all_surfaces + * in case of failure. + * TODO: make MoveAllSurfacesToRam send destroy_surface + * commands instead of create_surface commands in case + * of failure + */ + async_io(pdev, ASYNCABLE_DESTROY_ALL_SURFACES, 0); + /* move all surfaces from device to system memory */ + if (!MoveAllSurfacesToRam(pdev)) { + EnableQXLPrimarySurface(pdev); + return FALSE; + } + if (!FlushRelease(pdev)) { + return FALSE; + } + RemoveVRamSlot(pdev); + DebugCountAliveSurfaces(pdev); + DEBUG_PRINT((pdev, 4, "%s: [%d,%d] [%d,%d] [%d,%d] %lx\n", __FUNCTION__, + pdev->cmd_ring->prod, pdev->cmd_ring->cons, + pdev->cursor_ring->prod, pdev->cursor_ring->cons, + pdev->release_ring->prod, pdev->release_ring->cons, + pdev->free_outputs)); + DEBUG_PRINT((pdev, 3, "%s exit\n", __FUNCTION__)); + return TRUE; +} + +static void AssertModeEnable(PDev *pdev) +{ + InitDeviceMemoryResources(pdev); + DEBUG_PRINT((pdev, 3, "%s: [%d,%d] [%d,%d] [%d,%d] %lx\n", __FUNCTION__, + pdev->cmd_ring->prod, pdev->cmd_ring->cons, + pdev->cursor_ring->prod, pdev->cursor_ring->cons, + pdev->release_ring->prod, pdev->release_ring->cons, + pdev->free_outputs)); + EnableQXLPrimarySurface(pdev); + CreateVRamSlot(pdev); + DebugCountAliveSurfaces(pdev); + MoveAllSurfacesToVideoRam(pdev); + DebugCountAliveSurfaces(pdev); +} + +BOOL DrvAssertMode(DHPDEV in_pdev, BOOL enable) +{ + PDev* pdev = (PDev*)in_pdev; + BOOL ret = TRUE; + + DEBUG_PRINT((pdev, 1, "%s: 0x%lx revision %d enable %d\n", __FUNCTION__, pdev, pdev->pci_revision, enable)); + if (pdev->enabled == enable) { + DEBUG_PRINT((pdev, 1, "%s: called twice with same argument (%d)\n", __FUNCTION__, + enable)); + return TRUE; + } + pdev->enabled = enable; + if (enable) { + AssertModeEnable(pdev); + } else { + ret = AssertModeDisable(pdev); + if (!ret) { + pdev->enabled = !enable; + } + } + DEBUG_PRINT((pdev, 1, "%s: 0x%lx exit %d\n", __FUNCTION__, pdev, enable)); + 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 GetCosmeticAttr(PDev *pdev, QXLDrawable *drawable, QXLLineAttr *q_line_attr, + LINEATTRS *line_attr) +{ + 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; + + 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; + } + + PUNT_IF_DISABLED(pdev); + + 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); + } + + ASSERT(pdev, (line_attr->fl & LA_GEOMETRIC) == 0); /* We should not get these */ + + 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, GetSurfaceId(surf)))) { + 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 = SPICE_BRUSH_TYPE_NONE; + } else if (!QXLGetBrush(pdev, drawable, &drawable->u.stroke.brush, brush, brush_pos, + &drawable->surfaces_dest[0], &drawable->surfaces_rects[0])) { + 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 (!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; +} + +HBITMAP APIENTRY DrvCreateDeviceBitmap(DHPDEV dhpdev, SIZEL size, ULONG format) +{ + PDev *pdev; + UINT8 *base_mem; + UINT32 surface_id; + QXLPHYSICAL phys_mem; + HBITMAP hbitmap; + + pdev = (PDev *)dhpdev; + + if (!pdev->create_non_primary_surfaces) { + return FALSE; + } + + if (!pdev->vram_slot_initialized || pdev->bitmap_format != format || pdev->fb == 0) { + DEBUG_PRINT((pdev, 3, "%s failed: %p: slot_initialized %d, format(%d,%d), fb %p\n", + __FUNCTION__, pdev, pdev->vram_slot_initialized, + pdev->bitmap_format, format, pdev->fb)); + return 0; + } + + PUNT_IF_DISABLED(pdev); + + surface_id = GetFreeSurface(pdev); + if (!surface_id) { + DEBUG_PRINT((pdev, 3, "%s:%p GetFreeSurface failed\n", __FUNCTION__, pdev)); + goto out_error; + } + DEBUG_PRINT((pdev, 3, "%s: %p: %d\n", __FUNCTION__, pdev, surface_id)); + + hbitmap = CreateDeviceBitmap(pdev, size, pdev->bitmap_format, &phys_mem, &base_mem, surface_id, + DEVICE_BITMAP_ALLOCATION_TYPE_VRAM); + if (!hbitmap) { + DEBUG_PRINT((pdev, 3, "%s:%p CreateDeviceBitmap failed\n", __FUNCTION__, pdev)); + goto out_error2; + } + + return hbitmap; + + // to optimize the failure case +out_error2: + FreeSurfaceInfo(pdev, surface_id); +out_error: + return 0; +} + +VOID APIENTRY DrvDeleteDeviceBitmap(DHSURF dhsurf) +{ + UINT32 surface_id; + SurfaceInfo *surface; + PDev *pdev; + + surface = (SurfaceInfo *)dhsurf; + surface_id = GetSurfaceIdFromInfo(surface); + pdev = surface->u.pdev; + + DEBUG_PRINT((pdev, 3, "%s: %p: %d\n", __FUNCTION__, pdev, surface_id)); + + ASSERT(pdev, surface_id < pdev->n_surfaces); + + DeleteDeviceBitmap(surface->u.pdev, surface_id, + surface->copy ? DEVICE_BITMAP_ALLOCATION_TYPE_RAM + : DEVICE_BITMAP_ALLOCATION_TYPE_VRAM); +} + +#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/xddm/display/driver.rc b/xddm/display/driver.rc new file mode 100644 index 0000000..f11c9db --- /dev/null +++ b/xddm/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.4.2.3" +#define VER_PRODUCTNAME_STR "Spice"
+#define VER_PRODUCTVERSION_STR VER_FILEVERSION_STR
+
+#undef VER_PRODUCTVERSION
+#define VER_PRODUCTVERSION 1,4,2,3 +
+#define VER_COMPANYNAME_STR "Red Hat Inc."
+#define VER_LEGALCOPYRIGHT_STR "© Red Hat Inc. All rights reserved."
+
+#include "common.ver"
diff --git a/xddm/display/makefile b/xddm/display/makefile new file mode 100644 index 0000000..53b9a3d --- /dev/null +++ b/xddm/display/makefile @@ -0,0 +1 @@ +!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/xddm/display/mspace.c b/xddm/display/mspace.c new file mode 100644 index 0000000..d0ba123 --- /dev/null +++ b/xddm/display/mspace.c @@ -0,0 +1,2437 @@ +// 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; + } else { + 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; + } else { + 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/xddm/display/mspace.h b/xddm/display/mspace.h new file mode 100644 index 0000000..96b0593 --- /dev/null +++ b/xddm/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/xddm/display/pointer.c b/xddm/display/pointer.c new file mode 100644 index 0000000..d38a207 --- /dev/null +++ b/xddm/display/pointer.c @@ -0,0 +1,144 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This software is licensed under the GNU General Public License, + version 2 (GPLv2) (see COPYING for details), subject to the + following clarification. + + With respect to binaries built using the Microsoft(R) Windows + Driver Kit (WDK), GPLv2 does not extend to any code contained in or + derived from the WDK ("WDK Code"). As to WDK Code, by using or + distributing such binaries you agree to be bound by the Microsoft + Software License Terms for the WDK. All WDK Code is considered by + the GPLv2 licensors to qualify for the special exception stated in + section 3 of GPLv2 (commonly known as the system library + exception). + + There is NO WARRANTY for this software, express or implied, + including the implied warranties of NON-INFRINGEMENT, TITLE, + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#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/xddm/display/quic.c b/xddm/display/quic.c new file mode 100644 index 0000000..ee12fab --- /dev/null +++ b/xddm/display/quic.c @@ -0,0 +1,1738 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This software is licensed under the GNU General Public License, + version 2 (GPLv2) (see COPYING for details), subject to the + following clarification. + + With respect to binaries built using the Microsoft(R) Windows + Driver Kit (WDK), GPLv2 does not extend to any code contained in or + derived from the WDK ("WDK Code"). As to WDK Code, by using or + distributing such binaries you agree to be bound by the Microsoft + Software License Terms for the WDK. All WDK Code is considered by + the GPLv2 licensors to qualify for the special exception stated in + section 3 of GPLv2 (commonly known as the system library + exception). + + There is NO WARRANTY for this software, express or implied, + including the implied warranties of NON-INFRINGEMENT, TITLE, + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +// Red Hat image compression based on SFALIC by Roman Starosolski +// http://sun.iinf.polsl.gliwice.pl/~rstaros/sfalic/index.html + +#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 "qxldd.h" +#include "utils.h" +#include "mspace.h" +#include "res.h" +#include "surface.h" + + +#include "quic.h" + +//#define DEBUG + +#define RLE +#define RLE_STAT +#define PRED_1 +//#define RLE_PRED_1 +#define RLE_PRED_2 +//#define RLE_PRED_3 +#define QUIC_RGB + +#define QUIC_MAGIC (*(uint32_t *)"QUIC") +#define QUIC_VERSION_MAJOR 0U +#define QUIC_VERSION_MINOR 1U +#define QUIC_VERSION ((QUIC_VERSION_MAJOR << 16) | (QUIC_VERSION_MAJOR & 0xffff)) + +#define ABS(a) ((a) >= 0 ? (a) : -(a)) + +#ifdef ASSERT +#undef ASSERT +#endif + +#ifdef DEBUG + +#define ASSERT(usr, x) \ + if (!(x)) (usr)->error(usr, "%s: ASSERT %s failed\n", __FUNCTION__, #x); + +#else + +#define ASSERT(usr, x) + +#endif + +#define FALSE 0 +#define TRUE 1 + +typedef uint8_t BYTE; + +/* maximum number of codes in family */ +#define MAXNUMCODES 8 + +/* model evolution, warning: only 1,3 and 5 allowed */ +#define DEFevol 3 +#define MINevol 0 +#define MAXevol 5 + +/* starting wait mask index */ +#define DEFwmistart 0 +#define MINwmistart 0 + +/* codeword length limit */ +#define DEFmaxclen 26 + +/* target wait mask index */ +#define DEFwmimax 6 + +/* number of symbols to encode before increasing wait mask index */ +#define DEFwminext 2048 +#define MINwminext 1 +#define MAXwminext 100000000 + +typedef struct QuicFamily { + unsigned int nGRcodewords[MAXNUMCODES]; /* indexed by code number, contains number of + unmodofied GR codewords in the code */ + unsigned int notGRcwlen[MAXNUMCODES]; /* indexed by code number, contains codeword + length of the not-GR codeword */ + unsigned int notGRprefixmask[MAXNUMCODES]; /* indexed by code number, contains mask to + determine if the codeword is GR or not-GR */ + unsigned int notGRsuffixlen[MAXNUMCODES]; /* indexed by code number, contains suffix + length of the not-GR codeword */ + + /* array for translating distribution U to L for depths up to 8 bpp, + initialized by decorelateinit() */ + BYTE xlatU2L[256]; + + /* array for translating distribution L to U for depths up to 8 bpp, + initialized by corelateinit() */ + unsigned int xlatL2U[256]; +} QuicFamily; + +static QuicFamily family_8bpc; +static QuicFamily family_5bpc; + +typedef unsigned COUNTER; /* counter in the array of counters in bucket of the data model */ + +typedef struct s_bucket { + COUNTER *pcounters; /* pointer to array of counters */ + unsigned int bestcode; /* best code so far */ +} s_bucket; + +typedef struct Encoder Encoder; + +typedef struct CommonState { + Encoder *encoder; + + unsigned int waitcnt; + unsigned int tabrand_seed; + unsigned int wm_trigger; + unsigned int wmidx; + unsigned int wmileft; + +#ifdef RLE_STAT + int melcstate; /* index to the state array */ + + int melclen; /* contents of the state array location + indexed by melcstate: the "expected" + run length is 2^melclen, shorter runs are + encoded by a 1 followed by the run length + in binary representation, wit a fixed length + of melclen bits */ + + unsigned long melcorder; /* 2^ melclen */ +#endif +} CommonState; + + +#define MAX_CHANNELS 4 + +typedef struct FamilyStat { + s_bucket **buckets_ptrs; + s_bucket *buckets_buf; + COUNTER *counters; +} FamilyStat; + +typedef struct Channel { + Encoder *encoder; + + int correlate_row_width; + BYTE *correlate_row; + + s_bucket **_buckets_ptrs; + + FamilyStat family_stat_8bpc; + FamilyStat family_stat_5bpc; + + CommonState state; +} Channel; + +struct Encoder { + QuicUsrContext *usr; + QuicImageType type; + unsigned int width; + unsigned int height; + unsigned int num_channels; + + unsigned int n_buckets_8bpc; + unsigned int n_buckets_5bpc; + + unsigned int io_available_bits; + uint32_t io_word; + uint32_t io_next_word; + uint32_t *io_now; + uint32_t *io_end; + uint32_t io_words_count; + + int rows_completed; + + Channel channels[MAX_CHANNELS]; + + CommonState rgb_state; +}; + +/* target wait mask index */ +static int wmimax = DEFwmimax; + +/* number of symbols to encode before increasing wait mask index */ +static int wminext = DEFwminext; + +/* model evolution mode */ +static int evol = DEFevol; + +/* bppmask[i] contains i ones as lsb-s */ +static const unsigned long int bppmask[33] = { + 0x00000000, /* [0] */ + 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, + 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, + 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, + 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff, + 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff, + 0x01ffffff, 0x03ffffff, 0x07ffffff, 0x0fffffff, + 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff /* [32] */ +}; + +static const unsigned int bitat[32] = { + 0x00000001, 0x00000002, 0x00000004, 0x00000008, + 0x00000010, 0x00000020, 0x00000040, 0x00000080, + 0x00000100, 0x00000200, 0x00000400, 0x00000800, + 0x00001000, 0x00002000, 0x00004000, 0x00008000, + 0x00010000, 0x00020000, 0x00040000, 0x00080000, + 0x00100000, 0x00200000, 0x00400000, 0x00800000, + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000 /* [31]*/ +}; + + +#define TABRAND_TABSIZE 256 +#define TABRAND_SEEDMASK 0x0ff + +static const unsigned int tabrand_chaos[TABRAND_TABSIZE] = { + 0x02c57542, 0x35427717, 0x2f5a2153, 0x9244f155, 0x7bd26d07, 0x354c6052, 0x57329b28, 0x2993868e, + 0x6cd8808c, 0x147b46e0, 0x99db66af, 0xe32b4cac, 0x1b671264, 0x9d433486, 0x62a4c192, 0x06089a4b, + 0x9e3dce44, 0xdaabee13, 0x222425ea, 0xa46f331d, 0xcd589250, 0x8bb81d7f, 0xc8b736b9, 0x35948d33, + 0xd7ac7fd0, 0x5fbe2803, 0x2cfbc105, 0x013dbc4e, 0x7a37820f, 0x39f88e9e, 0xedd58794, 0xc5076689, + 0xfcada5a4, 0x64c2f46d, 0xb3ba3243, 0x8974b4f9, 0x5a05aebd, 0x20afcd00, 0x39e2b008, 0x88a18a45, + 0x600bde29, 0xf3971ace, 0xf37b0a6b, 0x7041495b, 0x70b707ab, 0x06beffbb, 0x4206051f, 0xe13c4ee3, + 0xc1a78327, 0x91aa067c, 0x8295f72a, 0x732917a6, 0x1d871b4d, 0x4048f136, 0xf1840e7e, 0x6a6048c1, + 0x696cb71a, 0x7ff501c3, 0x0fc6310b, 0x57e0f83d, 0x8cc26e74, 0x11a525a2, 0x946934c7, 0x7cd888f0, + 0x8f9d8604, 0x4f86e73b, 0x04520316, 0xdeeea20c, 0xf1def496, 0x67687288, 0xf540c5b2, 0x22401484, + 0x3478658a, 0xc2385746, 0x01979c2c, 0x5dad73c8, 0x0321f58b, 0xf0fedbee, 0x92826ddf, 0x284bec73, + 0x5b1a1975, 0x03df1e11, 0x20963e01, 0xa17cf12b, 0x740d776e, 0xa7a6bf3c, 0x01b5cce4, 0x1118aa76, + 0xfc6fac0a, 0xce927e9b, 0x00bf2567, 0x806f216c, 0xbca69056, 0x795bd3e9, 0xc9dc4557, 0x8929b6c2, + 0x789d52ec, 0x3f3fbf40, 0xb9197368, 0xa38c15b5, 0xc3b44fa8, 0xca8333b0, 0xb7e8d590, 0xbe807feb, + 0xbf5f8360, 0xd99e2f5c, 0x372928e1, 0x7c757c4c, 0x0db5b154, 0xc01ede02, 0x1fc86e78, 0x1f3985be, + 0xb4805c77, 0x00c880fa, 0x974c1b12, 0x35ab0214, 0xb2dc840d, 0x5b00ae37, 0xd313b026, 0xb260969d, + 0x7f4c8879, 0x1734c4d3, 0x49068631, 0xb9f6a021, 0x6b863e6f, 0xcee5debf, 0x29f8c9fb, 0x53dd6880, + 0x72b61223, 0x1f67a9fd, 0x0a0f6993, 0x13e59119, 0x11cca12e, 0xfe6b6766, 0x16b6effc, 0x97918fc4, + 0xc2b8a563, 0x94f2f741, 0x0bfa8c9a, 0xd1537ae8, 0xc1da349c, 0x873c60ca, 0x95005b85, 0x9b5c080e, + 0xbc8abbd9, 0xe1eab1d2, 0x6dac9070, 0x4ea9ebf1, 0xe0cf30d4, 0x1ef5bd7b, 0xd161043e, 0x5d2fa2e2, + 0xff5d3cae, 0x86ed9f87, 0x2aa1daa1, 0xbd731a34, 0x9e8f4b22, 0xb1c2c67a, 0xc21758c9, 0xa182215d, + 0xccb01948, 0x8d168df7, 0x04238cfe, 0x368c3dbc, 0x0aeadca5, 0xbad21c24, 0x0a71fee5, 0x9fc5d872, + 0x54c152c6, 0xfc329483, 0x6783384a, 0xeddb3e1c, 0x65f90e30, 0x884ad098, 0xce81675a, 0x4b372f7d, + 0x68bf9a39, 0x43445f1e, 0x40f8d8cb, 0x90d5acb6, 0x4cd07282, 0x349eeb06, 0x0c9d5332, 0x520b24ef, + 0x80020447, 0x67976491, 0x2f931ca3, 0xfe9b0535, 0xfcd30220, 0x61a9e6cc, 0xa487d8d7, 0x3f7c5dd1, + 0x7d0127c5, 0x48f51d15, 0x60dea871, 0xc9a91cb7, 0x58b53bb3, 0x9d5e0b2d, 0x624a78b4, 0x30dbee1b, + 0x9bdf22e7, 0x1df5c299, 0x2d5643a7, 0xf4dd35ff, 0x03ca8fd6, 0x53b47ed8, 0x6f2c19aa, 0xfeb0c1f4, + 0x49e54438, 0x2f2577e6, 0xbf876969, 0x72440ea9, 0xfa0bafb8, 0x74f5b3a0, 0x7dd357cd, 0x89ce1358, + 0x6ef2cdda, 0x1e7767f3, 0xa6be9fdb, 0x4f5f88f8, 0xba994a3a, 0x08ca6b65, 0xe0893818, 0x9e00a16a, + 0xf42bfc8f, 0x9972eedc, 0x749c8b51, 0x32c05f5e, 0xd706805f, 0x6bfbb7cf, 0xd9210a10, 0x31a1db97, + 0x923a9559, 0x37a7a1f6, 0x059f8861, 0xca493e62, 0x65157e81, 0x8f6467dd, 0xab85ff9f, 0x9331aff2, + 0x8616b9f5, 0xedbd5695, 0xee7e29b1, 0x313ac44f, 0xb903112f, 0x432ef649, 0xdc0a36c0, 0x61cf2bba, + 0x81474925, 0xa8b6c7ad, 0xee5931de, 0xb2f8158d, 0x59fb7409, 0x2e3dfaed, 0x9af25a3f, 0xe1fed4d5, +}; + +static unsigned int stabrand() +{ + //ASSERT( !(TABRAND_SEEDMASK & TABRAND_TABSIZE)); + //ASSERT( TABRAND_SEEDMASK + 1 == TABRAND_TABSIZE ); + + return TABRAND_SEEDMASK; +} + +static unsigned int tabrand(unsigned int *tabrand_seed) +{ + return tabrand_chaos[++*tabrand_seed & TABRAND_SEEDMASK]; +} + +static const unsigned short besttrigtab[3][11] = { /* array of wm_trigger for waitmask and evol, + used by set_wm_trigger() */ + /* 1 */ { 550, 900, 800, 700, 500, 350, 300, 200, 180, 180, 160}, + /* 3 */ { 110, 550, 900, 800, 550, 400, 350, 250, 140, 160, 140}, + /* 5 */ { 100, 120, 550, 900, 700, 500, 400, 300, 220, 250, 160} +}; + +/* set wm_trigger knowing waitmask (param) and evol (glob)*/ +static void set_wm_trigger(CommonState *state) +{ + unsigned int wm = state->wmidx; + if (wm > 10) { + wm = 10; + } + + ASSERT(state->encoder->usr, evol < 6); + + state->wm_trigger = besttrigtab[evol / 2][wm]; + + ASSERT(state->encoder->usr, state->wm_trigger <= 2000); + ASSERT(state->encoder->usr, state->wm_trigger >= 1); +} + +static int ceil_log_2(int val) /* ceil(log_2(val)) */ +{ + int result; + + //ASSERT(val>0); + + if (val == 1) { + return 0; + } + + result = 1; + val -= 1; + while (val >>= 1) { + result++; + } + + return result; +} + +/* number of leading zeroes in the byte, used by cntlzeroes(uint)*/ +static const BYTE lzeroes[256] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* count leading zeroes */ +static unsigned int cnt_l_zeroes(const unsigned int bits) +{ + if (bits & 0xff800000) { + return lzeroes[bits >> 24]; + } else if (bits & 0xffff8000) { + return 8 + lzeroes[(bits >> 16) & 0x000000ff]; + } else if (bits & 0xffffff80) { + return 16 + lzeroes[(bits >> 8) & 0x000000ff]; + } else { + return 24 + lzeroes[bits & 0x000000ff]; + } +} + +#define QUIC_FAMILY_8BPC +#include "quic_family_tmpl.c" + +#ifdef QUIC_RGB +#define QUIC_FAMILY_5BPC +#include "quic_family_tmpl.c" +#endif + +static void decorelate_init(QuicFamily *family, int bpc) +{ + const unsigned int pixelbitmask = bppmask[bpc]; + const unsigned int pixelbitmaskshr = pixelbitmask >> 1; + unsigned int s; + + //ASSERT(bpc <= 8); + + for (s = 0; s <= pixelbitmask; s++) { + if (s <= pixelbitmaskshr) { + family->xlatU2L[s] = s << 1; + } else { + family->xlatU2L[s] = ((pixelbitmask - s) << 1) + 1; + } + } +} + +static void corelate_init(QuicFamily *family, int bpc) +{ + const unsigned long int pixelbitmask = bppmask[bpc]; + unsigned long int s; + + //ASSERT(bpc <= 8); + + for (s = 0; s <= pixelbitmask; s++) { + if (s & 0x01) { + family->xlatL2U[s] = pixelbitmask - (s >> 1); + } else { + family->xlatL2U[s] = (s >> 1); + } + } +} + +static void family_init(QuicFamily *family, int bpc, int limit) +{ + int l; + + for (l = 0; l < bpc; l++) { /* fill arrays indexed by code number */ + int altprefixlen, altcodewords; + + altprefixlen = limit - bpc; + if (altprefixlen > (int)(bppmask[bpc - l])) { + altprefixlen = bppmask[bpc - l]; + } + + altcodewords = bppmask[bpc] + 1 - (altprefixlen << l); + + family->nGRcodewords[l] = (altprefixlen << l); + family->notGRcwlen[l] = altprefixlen + ceil_log_2(altcodewords); + family->notGRprefixmask[l] = bppmask[32 - altprefixlen]; /* needed for decoding only */ + family->notGRsuffixlen[l] = ceil_log_2(altcodewords); /* needed for decoding only */ + } + + decorelate_init(family, bpc); + corelate_init(family, bpc); +} + +static void more_io_words(Encoder *encoder) +{ + uint32_t *io_ptr; + int num_io_words = encoder->usr->more_space(encoder->usr, &io_ptr, encoder->rows_completed); + if (num_io_words <= 0) { + encoder->usr->error(encoder->usr, "%s: no more words\n", __FUNCTION__); + } + ASSERT(encoder->usr, io_ptr); + encoder->io_words_count += num_io_words; + encoder->io_now = io_ptr; + encoder->io_end = encoder->io_now + num_io_words; +} + +static void __write_io_word(Encoder *encoder) +{ + more_io_words(encoder); + *(encoder->io_now++) = encoder->io_word; +} + +static void (*__write_io_word_ptr)(Encoder *encoder) = __write_io_word; + +static INLINE void write_io_word(Encoder *encoder) +{ + if (encoder->io_now == encoder->io_end) { + __write_io_word_ptr(encoder); //disable inline optimizations + return; + } + *(encoder->io_now++) = encoder->io_word; +} + +static INLINE void encode(Encoder *encoder, unsigned int word, unsigned int len) +{ + int delta; + + ASSERT(encoder->usr, len > 0 && len < 32); + ASSERT(encoder->usr, !(word & ~bppmask[len])); + if ((delta = ((int)encoder->io_available_bits - len)) >= 0) { + encoder->io_available_bits = delta; + encoder->io_word |= word << encoder->io_available_bits; + return; + } + delta = -delta; + encoder->io_word |= word >> delta; + write_io_word(encoder); + encoder->io_available_bits = 32 - delta; + encoder->io_word = word << encoder->io_available_bits; + + ASSERT(encoder->usr, encoder->io_available_bits < 32); + ASSERT(encoder->usr, (encoder->io_word & bppmask[encoder->io_available_bits]) == 0); +} + +static INLINE void encode_32(Encoder *encoder, unsigned int word) +{ + encode(encoder, word >> 16, 16); + encode(encoder, word & 0x0000ffff, 16); +} + +static INLINE void flush(Encoder *encoder) +{ + if (encoder->io_available_bits > 0 && encoder->io_available_bits != 32) { + encode(encoder, 0, encoder->io_available_bits); + } + encode_32(encoder, 0); + encode(encoder, 0, 1); +} + +static void __read_io_word(Encoder *encoder) +{ + more_io_words(encoder); + encoder->io_next_word = *(encoder->io_now++); +} + +static void (*__read_io_word_ptr)(Encoder *encoder) = __read_io_word; + + +static INLINE void read_io_word(Encoder *encoder) +{ + if (encoder->io_now == encoder->io_end) { + __read_io_word_ptr(encoder); //disable inline optimizations + return; + } + ASSERT(encoder->usr, encoder->io_now < encoder->io_end); + encoder->io_next_word = *(encoder->io_now++); +} + +static INLINE void decode_eatbits(Encoder *encoder, int len) +{ + int delta; + + ASSERT(encoder->usr, len > 0 && len < 32); + encoder->io_word <<= len; + + if ((delta = ((int)encoder->io_available_bits - len)) >= 0) { + encoder->io_available_bits = delta; + encoder->io_word |= encoder->io_next_word >> encoder->io_available_bits; + return; + } + + delta = -delta; + encoder->io_word |= encoder->io_next_word << delta; + read_io_word(encoder); + encoder->io_available_bits = 32 - delta; + encoder->io_word |= (encoder->io_next_word >> encoder->io_available_bits); +} + +static INLINE void decode_eat32bits(Encoder *encoder) +{ + decode_eatbits(encoder, 16); + decode_eatbits(encoder, 16); +} + +#ifdef RLE + +#ifdef RLE_STAT + +static INLINE void encode_ones(Encoder *encoder, unsigned int n) +{ + unsigned int count; + + for (count = n >> 5; count; count--) { + encode(encoder, ~0U, 32); + } + + if ((n &= 0x1f)) { + encode(encoder, (1U << n) - 1, n); + } +} + +#define MELCSTATES 32 /* number of melcode states */ + +static int zeroLUT[256]; /* table to find out number of leading zeros */ + +static int J[MELCSTATES] = { + 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 5, 5, 6, 6, 7, + 7, 8, 9, 10, 11, 12, 13, 14, 15 +}; + +/* creates the bit counting look-up table. */ +static void init_zeroLUT() +{ + int i, j, k, l; + + j = k = 1; + l = 8; + for (i = 0; i < 256; ++i) { + zeroLUT[i] = l; + --k; + if (k == 0) { + k = j; + --l; + j *= 2; + } + } +} + +static void encoder_init_rle(CommonState *state) +{ + state->melcstate = 0; + state->melclen = J[0]; + state->melcorder = 1 << state->melclen; +} + +#ifdef QUIC_RGB + +static void encode_run(Encoder *encoder, unsigned int runlen) //todo: try use end of line +{ + int hits = 0; + + while (runlen >= encoder->rgb_state.melcorder) { + hits++; + runlen -= encoder->rgb_state.melcorder; + if (encoder->rgb_state.melcstate < MELCSTATES) { + encoder->rgb_state.melclen = J[++encoder->rgb_state.melcstate]; + encoder->rgb_state.melcorder = (1L << encoder->rgb_state.melclen); + } + } + + /* send the required number of "hit" bits (one per occurrence + of a run of length melcorder). This number is never too big: + after 31 such "hit" bits, each "hit" would represent a run of 32K + pixels. + */ + encode_ones(encoder, hits); + + encode(encoder, runlen, encoder->rgb_state.melclen + 1); + + /* adjust melcoder parameters */ + if (encoder->rgb_state.melcstate) { + encoder->rgb_state.melclen = J[--encoder->rgb_state.melcstate]; + encoder->rgb_state.melcorder = (1L << encoder->rgb_state.melclen); + } +} + +#endif + +static void encode_channel_run(Encoder *encoder, Channel *channel, unsigned int runlen) +{ + //todo: try use end of line + int hits = 0; + + while (runlen >= channel->state.melcorder) { + hits++; + runlen -= channel->state.melcorder; + if (channel->state.melcstate < MELCSTATES) { + channel->state.melclen = J[++channel->state.melcstate]; + channel->state.melcorder = (1L << channel->state.melclen); + } + } + + /* send the required number of "hit" bits (one per occurrence + of a run of length melcorder). This number is never too big: + after 31 such "hit" bits, each "hit" would represent a run of 32K + pixels. + */ + encode_ones(encoder, hits); + + encode(encoder, runlen, channel->state.melclen + 1); + + /* adjust melcoder parameters */ + if (channel->state.melcstate) { + channel->state.melclen = J[--channel->state.melcstate]; + channel->state.melcorder = (1L << channel->state.melclen); + } +} + +/* decoding routine: reads bits from the input and returns a run length. */ +/* argument is the number of pixels left to end-of-line (bound on run length) */ + +#ifdef QUIC_RGB +static int decode_run(Encoder *encoder) +{ + int runlen = 0; + + do { + register int temp, hits; + temp = zeroLUT[(BYTE)(~(encoder->io_word >> 24))];/* number of leading ones in the + input stream, up to 8 */ + for (hits = 1; hits <= temp; hits++) { + runlen += encoder->rgb_state.melcorder; + + if (encoder->rgb_state.melcstate < MELCSTATES) { + encoder->rgb_state.melclen = J[++encoder->rgb_state.melcstate]; + encoder->rgb_state.melcorder = (1U << encoder->rgb_state.melclen); + } + } + if (temp != 8) { + decode_eatbits(encoder, temp + 1); /* consume the leading + 0 of the remainder encoding */ + break; + } + decode_eatbits(encoder, 8); + } while (1); + + /* read the length of the remainder */ + if (encoder->rgb_state.melclen) { + runlen += encoder->io_word >> (32 - encoder->rgb_state.melclen); + decode_eatbits(encoder, encoder->rgb_state.melclen); + } + + /* adjust melcoder parameters */ + if (encoder->rgb_state.melcstate) { + encoder->rgb_state.melclen = J[--encoder->rgb_state.melcstate]; + encoder->rgb_state.melcorder = (1U << encoder->rgb_state.melclen); + } + + return runlen; +} + +#endif + +static int decode_channel_run(Encoder *encoder, Channel *channel) +{ + int runlen = 0; + + do { + register int temp, hits; + temp = zeroLUT[(BYTE)(~(encoder->io_word >> 24))];/* number of leading ones in the + input stream, up to 8 */ + for (hits = 1; hits <= temp; hits++) { + runlen += channel->state.melcorder; + + if (channel->state.melcstate < MELCSTATES) { + channel->state.melclen = J[++channel->state.melcstate]; + channel->state.melcorder = (1U << channel->state.melclen); + } + } + if (temp != 8) { + decode_eatbits(encoder, temp + 1); /* consume the leading + 0 of the remainder encoding */ + break; + } + decode_eatbits(encoder, 8); + } while (1); + + /* read the length of the remainder */ + if (channel->state.melclen) { + runlen += encoder->io_word >> (32 - channel->state.melclen); + decode_eatbits(encoder, channel->state.melclen); + } + + /* adjust melcoder parameters */ + if (channel->state.melcstate) { + channel->state.melclen = J[--channel->state.melcstate]; + channel->state.melcorder = (1U << channel->state.melclen); + } + + return runlen; +} + +#else + +static INLINE int find_msb(int x) +{ + int r; + + __asm__("bsrl %1,%0\n\t" + "jnz 1f\n\t" + "movl $-1,%0\n" + "1:" : "=r" (r) : "rm" (x)); + return r + 1; +} + +static INLINE void encode_run(Encoder *encoder, unsigned int len) +{ + int odd = len & 1U; + int msb; + + len &= ~1U; + + while ((msb = find_msb(len))) { + len &= ~(1 << (msb - 1)); + ASSERT(encoder->usr, msb < 32); + encode(encoder, (1 << (msb)) - 1, msb); + encode(encoder, 0, 1); + } + + if (odd) { + encode(encoder, 2, 2); + } else { + encode(encoder, 0, 1); + } +} + +static INLINE unsigned int decode_run(Encoder *encoder) +{ + unsigned int len = 0; + int count; + + do { + count = 0; + while (encoder->io_word & (1U << 31)) { + decode_eatbits(encoder, 1); + count++; + ASSERT(encoder->usr, count < 32); + } + decode_eatbits(encoder, 1); + len += (1U << count) >> 1; + } while (count > 1); + + return len; +} + +#endif +#endif + +static INLINE void init_decode_io(Encoder *encoder) +{ + encoder->io_next_word = encoder->io_word = *(encoder->io_now++); + encoder->io_available_bits = 0; +} + +#ifdef __GNUC__ +#define ATTR_PACKED __attribute__ ((__packed__)) +#else +#define ATTR_PACKED +#pragma pack(push) +#pragma pack(1) +#endif + +typedef struct ATTR_PACKED one_byte_pixel_t { + BYTE a; +} one_byte_t; + +typedef struct ATTR_PACKED three_bytes_pixel_t { + BYTE a; + BYTE b; + BYTE c; +} three_bytes_t; + +typedef struct ATTR_PACKED four_bytes_pixel_t { + BYTE a; + BYTE b; + BYTE c; + BYTE d; +} four_bytes_t; + +typedef struct ATTR_PACKED rgb32_pixel_t { + BYTE b; + BYTE g; + BYTE r; + BYTE pad; +} rgb32_pixel_t; + +typedef struct ATTR_PACKED rgb24_pixel_t { + BYTE b; + BYTE g; + BYTE r; +} rgb24_pixel_t; + +typedef uint16_t rgb16_pixel_t; + +#ifndef __GNUC__ +#pragma pack(pop) +#endif + +#undef ATTR_PACKED + +#define ONE_BYTE +#include "quic_tmpl.c" + +#define FOUR_BYTE +#include "quic_tmpl.c" + +#ifdef QUIC_RGB + +#define QUIC_RGB32 +#include "quic_rgb_tmpl.c" + +#define QUIC_RGB24 +#include "quic_rgb_tmpl.c" + +#define QUIC_RGB16 +#include "quic_rgb_tmpl.c" + +#define QUIC_RGB16_TO_32 +#include "quic_rgb_tmpl.c" + +#else + +#define THREE_BYTE +#include "quic_tmpl.c" + +#endif + +static void fill_model_structures(Encoder *encoder, FamilyStat *family_stat, + unsigned int rep_first, unsigned int first_size, + unsigned int rep_next, unsigned int mul_size, + unsigned int levels, unsigned int ncounters, + unsigned int nbuckets, unsigned int n_buckets_ptrs) +{ + unsigned int + bsize, + bstart, + bend = 0, + repcntr, + bnumber; + + COUNTER * free_counter = family_stat->counters;/* first free location in the array of + counters */ + + bnumber = 0; + + repcntr = rep_first + 1; /* first bucket */ + bsize = first_size; + + do { /* others */ + if (bnumber) { + bstart = bend + 1; + } else { + bstart = 0; + } + + if (!--repcntr) { + repcntr = rep_next; + bsize *= mul_size; + } + + bend = bstart + bsize - 1; + if (bend + bsize >= levels) { + bend = levels - 1; + } + + family_stat->buckets_buf[bnumber].pcounters = free_counter; + free_counter += ncounters; + + ASSERT(encoder->usr, bstart < n_buckets_ptrs); + { + unsigned int i; + ASSERT(encoder->usr, bend < n_buckets_ptrs); + for (i = bstart; i <= bend; i++) { + family_stat->buckets_ptrs[i] = family_stat->buckets_buf + bnumber; + } + } + + bnumber++; + } while (bend < levels - 1); + + ASSERT(encoder->usr, free_counter - family_stat->counters == nbuckets * ncounters); +} + +static void find_model_params(Encoder *encoder, + const int bpc, + unsigned int *ncounters, + unsigned int *levels, + unsigned int *n_buckets_ptrs, + unsigned int *repfirst, + unsigned int *firstsize, + unsigned int *repnext, + unsigned int *mulsize, + unsigned int *nbuckets) +{ + unsigned int bsize; /* bucket size */ + unsigned int bstart, bend = 0; /* bucket start and end, range : 0 to levels-1*/ + unsigned int repcntr; /* helper */ + + ASSERT(encoder->usr, bpc <= 8 && bpc > 0); + + + *ncounters = 8; + + *levels = 0x1 << bpc; + + *n_buckets_ptrs = 0; /* ==0 means: not set yet */ + + switch (evol) { /* set repfirst firstsize repnext mulsize */ + case 1: /* buckets contain following numbers of contexts: 1 1 1 2 2 4 4 8 8 ... */ + *repfirst = 3; + *firstsize = 1; + *repnext = 2; + *mulsize = 2; + break; + case 3: /* 1 2 4 8 16 32 64 ... */ + *repfirst = 1; + *firstsize = 1; + *repnext = 1; + *mulsize = 2; + break; + case 5: /* 1 4 16 64 256 1024 4096 16384 65536 */ + *repfirst = 1; + *firstsize = 1; + *repnext = 1; + *mulsize = 4; + break; + case 0: /* obsolete */ + case 2: /* obsolete */ + case 4: /* obsolete */ + encoder->usr->error(encoder->usr, "findmodelparams(): evol value obsolete!!!\n"); + default: + encoder->usr->error(encoder->usr, "findmodelparams(): evol out of range!!!\n"); + } + + *nbuckets = 0; + repcntr = *repfirst + 1; /* first bucket */ + bsize = *firstsize; + + do { /* other buckets */ + if (nbuckets) { /* bucket start */ + bstart = bend + 1; + } else { + bstart = 0; + } + + if (!--repcntr) { /* bucket size */ + repcntr = *repnext; + bsize *= *mulsize; + } + + bend = bstart + bsize - 1; /* bucket end */ + if (bend + bsize >= *levels) { /* if following bucked was bigger than current one */ + bend = *levels - 1; /* concatenate them */ + } + + if (!*n_buckets_ptrs) { /* array size not set yet? */ + *n_buckets_ptrs = *levels; + #if 0 + if (bend == *levels - 1) { /* this bucket is last - all in the first array */ + *n_buckets_ptrs = *levels; + } else if (bsize >= 256) { /* this bucket is allowed to reside in the 2nd table */ + b_lo_ptrs = bstart; + assert(bstart); /* previous bucket exists */ + } + #endif + } + + (*nbuckets)++; + } while (bend < *levels - 1); +} + +static int init_model_structures(Encoder *encoder, FamilyStat *family_stat, + unsigned int rep_first, unsigned int first_size, + unsigned int rep_next, unsigned int mul_size, + unsigned int levels, unsigned int ncounters, + unsigned int n_buckets_ptrs, unsigned int n_buckets) +{ + family_stat->buckets_ptrs = (s_bucket **)encoder->usr->malloc(encoder->usr, + n_buckets_ptrs * + sizeof(s_bucket *)); + if (!family_stat->buckets_ptrs) { + return FALSE; + } + + family_stat->counters = (COUNTER *)encoder->usr->malloc(encoder->usr, + n_buckets * sizeof(COUNTER) * + MAXNUMCODES); + if (!family_stat->counters) { + goto error_1; + } + + family_stat->buckets_buf = (s_bucket *)encoder->usr->malloc(encoder->usr, + n_buckets * sizeof(s_bucket)); + if (!family_stat->buckets_buf) { + goto error_2; + } + + fill_model_structures(encoder, family_stat, rep_first, first_size, rep_next, mul_size, levels, + ncounters, n_buckets, n_buckets_ptrs); + + return TRUE; + +error_2: + encoder->usr->free(encoder->usr, family_stat->counters); + +error_1: + encoder->usr->free(encoder->usr, family_stat->buckets_ptrs); + + return FALSE; +} + +static void free_family_stat(QuicUsrContext *usr, FamilyStat *family_stat) +{ + usr->free(usr, family_stat->buckets_ptrs); + usr->free(usr, family_stat->counters); + usr->free(usr, family_stat->buckets_buf); +} + +static int init_channel(Encoder *encoder, Channel *channel) +{ + unsigned int ncounters; + unsigned int levels; + unsigned int rep_first; + unsigned int first_size; + unsigned int rep_next; + unsigned int mul_size; + unsigned int n_buckets; + unsigned int n_buckets_ptrs; + + channel->encoder = encoder; + channel->state.encoder = encoder; + channel->correlate_row_width = 0; + channel->correlate_row = NULL; + + find_model_params(encoder, 8, &ncounters, &levels, &n_buckets_ptrs, &rep_first, + &first_size, &rep_next, &mul_size, &n_buckets); + encoder->n_buckets_8bpc = n_buckets; + if (!init_model_structures(encoder, &channel->family_stat_8bpc, rep_first, first_size, + rep_next, mul_size, levels, ncounters, n_buckets_ptrs, + n_buckets)) { + return FALSE; + } + + find_model_params(encoder, 5, &ncounters, &levels, &n_buckets_ptrs, &rep_first, + &first_size, &rep_next, &mul_size, &n_buckets); + encoder->n_buckets_5bpc = n_buckets; + if (!init_model_structures(encoder, &channel->family_stat_5bpc, rep_first, first_size, + rep_next, mul_size, levels, ncounters, n_buckets_ptrs, + n_buckets)) { + free_family_stat(encoder->usr, &channel->family_stat_8bpc); + return FALSE; + } + + return TRUE; +} + +static void destroy_channel(Channel *channel) +{ + QuicUsrContext *usr = channel->encoder->usr; + if (channel->correlate_row) { + usr->free(usr, channel->correlate_row - 1); + } + free_family_stat(usr, &channel->family_stat_8bpc); + free_family_stat(usr, &channel->family_stat_5bpc); +} + +static int init_encoder(Encoder *encoder, QuicUsrContext *usr) +{ + int i; + + encoder->usr = usr; + encoder->rgb_state.encoder = encoder; + + for (i = 0; i < MAX_CHANNELS; i++) { + if (!init_channel(encoder, &encoder->channels[i])) { + for (--i; i >= 0; i--) { + destroy_channel(&encoder->channels[i]); + } + return FALSE; + } + } + return TRUE; +} + +static int encoder_reste(Encoder *encoder, uint32_t *io_ptr, uint32_t *io_ptr_end) +{ + ASSERT(encoder->usr, ((unsigned long)io_ptr % 4) == ((unsigned long)io_ptr_end % 4)); + ASSERT(encoder->usr, io_ptr <= io_ptr_end); + + encoder->rgb_state.waitcnt = 0; + encoder->rgb_state.tabrand_seed = stabrand(); + encoder->rgb_state.wmidx = DEFwmistart; + encoder->rgb_state.wmileft = wminext; + set_wm_trigger(&encoder->rgb_state); + +#if defined(RLE) && defined(RLE_STAT) + encoder_init_rle(&encoder->rgb_state); +#endif + + encoder->io_words_count = (uint32_t)(io_ptr_end - io_ptr); + encoder->io_now = io_ptr; + encoder->io_end = io_ptr_end; + encoder->rows_completed = 0; + + return TRUE; +} + +static int encoder_reste_channels(Encoder *encoder, int channels, int width, int bpc) +{ + int i; + + encoder->num_channels = channels; + + for (i = 0; i < channels; i++) { + s_bucket *bucket; + s_bucket *end_bucket; + + if (encoder->channels[i].correlate_row_width < width) { + encoder->channels[i].correlate_row_width = 0; + if (encoder->channels[i].correlate_row) { + encoder->usr->free(encoder->usr, encoder->channels[i].correlate_row - 1); + } + if (!(encoder->channels[i].correlate_row = (BYTE *)encoder->usr->malloc(encoder->usr, + width + 1))) { + return FALSE; + } + encoder->channels[i].correlate_row++; + encoder->channels[i].correlate_row_width = width; + } + + if (bpc == 8) { + MEMCLEAR(encoder->channels[i].family_stat_8bpc.counters, + encoder->n_buckets_8bpc * sizeof(COUNTER) * MAXNUMCODES); + bucket = encoder->channels[i].family_stat_8bpc.buckets_buf; + end_bucket = bucket + encoder->n_buckets_8bpc; + for (; bucket < end_bucket; bucket++) { + bucket->bestcode = /*BPC*/ 8 - 1; + } + encoder->channels[i]._buckets_ptrs = encoder->channels[i].family_stat_8bpc.buckets_ptrs; + } else if (bpc == 5) { + MEMCLEAR(encoder->channels[i].family_stat_5bpc.counters, + encoder->n_buckets_5bpc * sizeof(COUNTER) * MAXNUMCODES); + bucket = encoder->channels[i].family_stat_5bpc.buckets_buf; + end_bucket = bucket + encoder->n_buckets_5bpc; + for (; bucket < end_bucket; bucket++) { + bucket->bestcode = /*BPC*/ 5 - 1; + } + encoder->channels[i]._buckets_ptrs = encoder->channels[i].family_stat_5bpc.buckets_ptrs; + } else { + encoder->usr->warn(encoder->usr, "%s: bad bpc %d\n", __FUNCTION__, bpc); + return FALSE; + } + + encoder->channels[i].state.waitcnt = 0; + encoder->channels[i].state.tabrand_seed = stabrand(); + encoder->channels[i].state.wmidx = DEFwmistart; + encoder->channels[i].state.wmileft = wminext; + set_wm_trigger(&encoder->channels[i].state); + +#if defined(RLE) && defined(RLE_STAT) + encoder_init_rle(&encoder->channels[i].state); +#endif + } + return TRUE; +} + +static void quic_image_params(Encoder *encoder, QuicImageType type, int *channels, int *bpc) +{ + ASSERT(encoder->usr, channels && bpc); + switch (type) { + case QUIC_IMAGE_TYPE_GRAY: + *channels = 1; + *bpc = 8; + break; + case QUIC_IMAGE_TYPE_RGB16: + *channels = 3; + *bpc = 5; +#ifndef QUIC_RGB + encoder->usr->error(encoder->usr, "not implemented\n"); +#endif + break; + case QUIC_IMAGE_TYPE_RGB24: + *channels = 3; + *bpc = 8; + break; + case QUIC_IMAGE_TYPE_RGB32: + *channels = 3; + *bpc = 8; + break; + case QUIC_IMAGE_TYPE_RGBA: + *channels = 4; + *bpc = 8; + break; + case QUIC_IMAGE_TYPE_INVALID: + default: + *channels = 0; + *bpc = 0; + encoder->usr->error(encoder->usr, "bad image type\n"); + } +} + +#define FILL_LINES() { \ + if (line == lines_end) { \ + int n = encoder->usr->more_lines(encoder->usr, &line); \ + if (n <= 0) { \ + encoder->usr->error(encoder->usr, "more lines failed\n"); \ + } \ + lines_end = line + n * stride; \ + } \ +} + +#define NEXT_LINE() { \ + line += stride; \ + FILL_LINES(); \ +} + +#define QUIC_COMPRESS_RGB(bits) \ + encoder->channels[0].correlate_row[-1] = 0; \ + encoder->channels[1].correlate_row[-1] = 0; \ + encoder->channels[2].correlate_row[-1] = 0; \ + quic_rgb##bits##_compress_row0(encoder, (rgb##bits##_pixel_t *)(line), width); \ + encoder->rows_completed++; \ + for (row = 1; row < height; row++) { \ + prev = line; \ + NEXT_LINE(); \ + encoder->channels[0].correlate_row[-1] = encoder->channels[0].correlate_row[0]; \ + encoder->channels[1].correlate_row[-1] = encoder->channels[1].correlate_row[0]; \ + encoder->channels[2].correlate_row[-1] = encoder->channels[2].correlate_row[0]; \ + quic_rgb##bits##_compress_row(encoder, (rgb##bits##_pixel_t *)prev, \ + (rgb##bits##_pixel_t *)line, width); \ + encoder->rows_completed++; \ + } + +int quic_encode(QuicContext *quic, QuicImageType type, int width, int height, + uint8_t *line, unsigned int num_lines, int stride, + uint32_t *io_ptr, unsigned int num_io_words) +{ + Encoder *encoder = (Encoder *)quic; + uint32_t *io_ptr_end = io_ptr + num_io_words; + uint8_t *lines_end; + int row; + uint8_t *prev; + int channels; + int bpc; +#ifndef QUIC_RGB + int i; +#endif + + ASSERT(encoder->usr, line); + lines_end = line + num_lines * stride; + + quic_image_params(encoder, type, &channels, &bpc); + + if (!encoder_reste(encoder, io_ptr, io_ptr_end) || + !encoder_reste_channels(encoder, channels, width, bpc)) { + return QUIC_ERROR; + } + + encoder->io_word = 0; + encoder->io_available_bits = 32; + + encode_32(encoder, QUIC_MAGIC); + encode_32(encoder, QUIC_VERSION); + encode_32(encoder, type); + encode_32(encoder, width); + encode_32(encoder, height); + + FILL_LINES(); + + switch (type) { +#ifdef QUIC_RGB + case QUIC_IMAGE_TYPE_RGB32: + ASSERT(encoder->usr, ABS(stride) >= width * 4); + QUIC_COMPRESS_RGB(32); + break; + case QUIC_IMAGE_TYPE_RGB24: + ASSERT(encoder->usr, ABS(stride) >= width * 3); + QUIC_COMPRESS_RGB(24); + break; + case QUIC_IMAGE_TYPE_RGB16: + ASSERT(encoder->usr, ABS(stride) >= width * 2); + QUIC_COMPRESS_RGB(16); + break; + case QUIC_IMAGE_TYPE_RGBA: + ASSERT(encoder->usr, ABS(stride) >= width * 4); + + encoder->channels[0].correlate_row[-1] = 0; + encoder->channels[1].correlate_row[-1] = 0; + encoder->channels[2].correlate_row[-1] = 0; + quic_rgb32_compress_row0(encoder, (rgb32_pixel_t *)(line), width); + + encoder->channels[3].correlate_row[-1] = 0; + quic_four_compress_row0(encoder, &encoder->channels[3], (four_bytes_t *)(line + 3), width); + + encoder->rows_completed++; + + for (row = 1; row < height; row++) { + prev = line; + NEXT_LINE(); + encoder->channels[0].correlate_row[-1] = encoder->channels[0].correlate_row[0]; + encoder->channels[1].correlate_row[-1] = encoder->channels[1].correlate_row[0]; + encoder->channels[2].correlate_row[-1] = encoder->channels[2].correlate_row[0]; + quic_rgb32_compress_row(encoder, (rgb32_pixel_t *)prev, (rgb32_pixel_t *)line, width); + + encoder->channels[3].correlate_row[-1] = encoder->channels[3].correlate_row[0]; + quic_four_compress_row(encoder, &encoder->channels[3], (four_bytes_t *)(prev + 3), + (four_bytes_t *)(line + 3), width); + encoder->rows_completed++; + } + break; +#else + case QUIC_IMAGE_TYPE_RGB24: + ASSERT(encoder->usr, ABS(stride) >= width * 3); + for (i = 0; i < 3; i++) { + encoder->channels[i].correlate_row[-1] = 0; + quic_three_compress_row0(encoder, &encoder->channels[i], (three_bytes_t *)(line + i), + width); + } + encoder->rows_completed++; + for (row = 1; row < height; row++) { + prev = line; + NEXT_LINE(); + for (i = 0; i < 3; i++) { + encoder->channels[i].correlate_row[-1] = encoder->channels[i].correlate_row[0]; + quic_three_compress_row(encoder, &encoder->channels[i], (three_bytes_t *)(prev + i), + (three_bytes_t *)(line + i), width); + } + encoder->rows_completed++; + } + break; + case QUIC_IMAGE_TYPE_RGB32: + case QUIC_IMAGE_TYPE_RGBA: + ASSERT(encoder->usr, ABS(stride) >= width * 4); + for (i = 0; i < channels; i++) { + encoder->channels[i].correlate_row[-1] = 0; + quic_four_compress_row0(encoder, &encoder->channels[i], (four_bytes_t *)(line + i), + width); + } + encoder->rows_completed++; + for (row = 1; row < height; row++) { + prev = line; + NEXT_LINE(); + for (i = 0; i < channels; i++) { + encoder->channels[i].correlate_row[-1] = encoder->channels[i].correlate_row[0]; + quic_four_compress_row(encoder, &encoder->channels[i], (four_bytes_t *)(prev + i), + (four_bytes_t *)(line + i), width); + } + encoder->rows_completed++; + } + break; +#endif + case QUIC_IMAGE_TYPE_GRAY: + ASSERT(encoder->usr, ABS(stride) >= width); + encoder->channels[0].correlate_row[-1] = 0; + quic_one_compress_row0(encoder, &encoder->channels[0], (one_byte_t *)line, width); + encoder->rows_completed++; + for (row = 1; row < height; row++) { + prev = line; + NEXT_LINE(); + encoder->channels[0].correlate_row[-1] = encoder->channels[0].correlate_row[0]; + quic_one_compress_row(encoder, &encoder->channels[0], (one_byte_t *)prev, + (one_byte_t *)line, width); + encoder->rows_completed++; + } + break; + case QUIC_IMAGE_TYPE_INVALID: + default: + encoder->usr->error(encoder->usr, "bad image type\n"); + } + + flush(encoder); + encoder->io_words_count -= (uint32_t)(encoder->io_end - encoder->io_now); + + return encoder->io_words_count; +} + +int quic_decode_begin(QuicContext *quic, uint32_t *io_ptr, unsigned int num_io_words, + QuicImageType *out_type, int *out_width, int *out_height) +{ + Encoder *encoder = (Encoder *)quic; + uint32_t *io_ptr_end = io_ptr + num_io_words; + QuicImageType type; + int width; + int height; + uint32_t magic; + uint32_t version; + int channels; + int bpc; + + if (!encoder_reste(encoder, io_ptr, io_ptr_end)) { + return QUIC_ERROR; + } + + init_decode_io(encoder); + + magic = encoder->io_word; + decode_eat32bits(encoder); + if (magic != QUIC_MAGIC) { + encoder->usr->warn(encoder->usr, "bad magic\n"); + return QUIC_ERROR; + } + + version = encoder->io_word; + decode_eat32bits(encoder); + if (version != QUIC_VERSION) { + encoder->usr->warn(encoder->usr, "bad version\n"); + return QUIC_ERROR; + } + + type = (QuicImageType)encoder->io_word; + decode_eat32bits(encoder); + + width = encoder->io_word; + decode_eat32bits(encoder); + + height = encoder->io_word; + decode_eat32bits(encoder); + + quic_image_params(encoder, type, &channels, &bpc); + + if (!encoder_reste_channels(encoder, channels, width, bpc)) { + return QUIC_ERROR; + } + + *out_width = encoder->width = width; + *out_height = encoder->height = height; + *out_type = encoder->type = type; + return QUIC_OK; +} + +#ifndef QUIC_RGB +static void clear_row(four_bytes_t *row, int width) +{ + four_bytes_t *end; + for (end = row + width; row < end; row++) { + row->a = 0; + } +} + +#endif + +#ifdef QUIC_RGB + +static void uncompress_rgba(Encoder *encoder, uint8_t *buf, int stride) +{ + unsigned int row; + uint8_t *prev; + + encoder->channels[0].correlate_row[-1] = 0; + encoder->channels[1].correlate_row[-1] = 0; + encoder->channels[2].correlate_row[-1] = 0; + quic_rgb32_uncompress_row0(encoder, (rgb32_pixel_t *)buf, encoder->width); + + encoder->channels[3].correlate_row[-1] = 0; + quic_four_uncompress_row0(encoder, &encoder->channels[3], (four_bytes_t *)(buf + 3), + encoder->width); + + encoder->rows_completed++; + for (row = 1; row < encoder->height; row++) { + prev = buf; + buf += stride; + + encoder->channels[0].correlate_row[-1] = encoder->channels[0].correlate_row[0]; + encoder->channels[1].correlate_row[-1] = encoder->channels[1].correlate_row[0]; + encoder->channels[2].correlate_row[-1] = encoder->channels[2].correlate_row[0]; + quic_rgb32_uncompress_row(encoder, (rgb32_pixel_t *)prev, (rgb32_pixel_t *)buf, + encoder->width); + + encoder->channels[3].correlate_row[-1] = encoder->channels[3].correlate_row[0]; + quic_four_uncompress_row(encoder, &encoder->channels[3], (four_bytes_t *)(prev + 3), + (four_bytes_t *)(buf + 3), encoder->width); + + encoder->rows_completed++; + } +} + +#endif + +static void uncompress_gray(Encoder *encoder, uint8_t *buf, int stride) +{ + unsigned int row; + uint8_t *prev; + + encoder->channels[0].correlate_row[-1] = 0; + quic_one_uncompress_row0(encoder, &encoder->channels[0], (one_byte_t *)buf, encoder->width); + encoder->rows_completed++; + for (row = 1; row < encoder->height; row++) { + prev = buf; + buf += stride; + encoder->channels[0].correlate_row[-1] = encoder->channels[0].correlate_row[0]; + quic_one_uncompress_row(encoder, &encoder->channels[0], (one_byte_t *)prev, + (one_byte_t *)buf, encoder->width); + encoder->rows_completed++; + } +} + +#define QUIC_UNCOMPRESS_RGB(prefix, type) \ + encoder->channels[0].correlate_row[-1] = 0; \ + encoder->channels[1].correlate_row[-1] = 0; \ + encoder->channels[2].correlate_row[-1] = 0; \ + quic_rgb##prefix##_uncompress_row0(encoder, (type *)buf, encoder->width); \ + encoder->rows_completed++; \ + for (row = 1; row < encoder->height; row++) { \ + prev = buf; \ + buf += stride; \ + encoder->channels[0].correlate_row[-1] = encoder->channels[0].correlate_row[0]; \ + encoder->channels[1].correlate_row[-1] = encoder->channels[1].correlate_row[0]; \ + encoder->channels[2].correlate_row[-1] = encoder->channels[2].correlate_row[0]; \ + quic_rgb##prefix##_uncompress_row(encoder, (type *)prev, (type *)buf, \ + encoder->width); \ + encoder->rows_completed++; \ + } + +int quic_decode(QuicContext *quic, QuicImageType type, uint8_t *buf, int stride) +{ + Encoder *encoder = (Encoder *)quic; + unsigned int row; + uint8_t *prev; +#ifndef QUIC_RGB + int i; +#endif + + ASSERT(encoder->usr, buf); + + switch (encoder->type) { +#ifdef QUIC_RGB + case QUIC_IMAGE_TYPE_RGB32: + case QUIC_IMAGE_TYPE_RGB24: + if (type == QUIC_IMAGE_TYPE_RGB32) { + ASSERT(encoder->usr, ABS(stride) >= (int)encoder->width * 4); + QUIC_UNCOMPRESS_RGB(32, rgb32_pixel_t); + break; + } else if (type == QUIC_IMAGE_TYPE_RGB24) { + ASSERT(encoder->usr, ABS(stride) >= (int)encoder->width * 3); + QUIC_UNCOMPRESS_RGB(24, rgb24_pixel_t); + break; + } + encoder->usr->warn(encoder->usr, "unsupported output format\n"); + return QUIC_ERROR; + case QUIC_IMAGE_TYPE_RGB16: + if (type == QUIC_IMAGE_TYPE_RGB16) { + ASSERT(encoder->usr, ABS(stride) >= (int)encoder->width * 2); + QUIC_UNCOMPRESS_RGB(16, rgb16_pixel_t); + } else if (type == QUIC_IMAGE_TYPE_RGB32) { + ASSERT(encoder->usr, ABS(stride) >= (int)encoder->width * 4); + QUIC_UNCOMPRESS_RGB(16_to_32, rgb32_pixel_t); + } else { + encoder->usr->warn(encoder->usr, "unsupported output format\n"); + return QUIC_ERROR; + } + + break; + case QUIC_IMAGE_TYPE_RGBA: + + if (type != QUIC_IMAGE_TYPE_RGBA) { + encoder->usr->warn(encoder->usr, "unsupported output format\n"); + return QUIC_ERROR; + } + ASSERT(encoder->usr, ABS(stride) >= (int)encoder->width * 4); + uncompress_rgba(encoder, buf, stride); + break; +#else + case QUIC_IMAGE_TYPE_RGB24: + ASSERT(encoder->usr, ABS(stride) >= (int)encoder->width * 3); + for (i = 0; i < 3; i++) { + encoder->channels[i].correlate_row[-1] = 0; + quic_three_uncompress_row0(encoder, &encoder->channels[i], (three_bytes_t *)(buf + i), + encoder->width); + } + encoder->rows_completed++; + for (row = 1; row < encoder->height; row++) { + prev = buf; + buf += stride; + for (i = 0; i < 3; i++) { + encoder->channels[i].correlate_row[-1] = encoder->channels[i].correlate_row[0]; + quic_three_uncompress_row(encoder, &encoder->channels[i], + (three_bytes_t *)(prev + i), + (three_bytes_t *)(buf + i), + encoder->width); + } + encoder->rows_completed++; + } + break; + case QUIC_IMAGE_TYPE_RGB32: + ASSERT(encoder->usr, ABS(stride) >= encoder->width * 4); + for (i = 0; i < 3; i++) { + encoder->channels[i].correlate_row[-1] = 0; + quic_four_uncompress_row0(encoder, &encoder->channels[i], (four_bytes_t *)(buf + i), + encoder->width); + } + clear_row((four_bytes_t *)(buf + 3), encoder->width); + encoder->rows_completed++; + for (row = 1; row < encoder->height; row++) { + prev = buf; + buf += stride; + for (i = 0; i < 3; i++) { + encoder->channels[i].correlate_row[-1] = encoder->channels[i].correlate_row[0]; + quic_four_uncompress_row(encoder, &encoder->channels[i], + (four_bytes_t *)(prev + i), + (four_bytes_t *)(buf + i), + encoder->width); + } + clear_row((four_bytes_t *)(buf + 3), encoder->width); + encoder->rows_completed++; + } + break; + case QUIC_IMAGE_TYPE_RGBA: + ASSERT(encoder->usr, ABS(stride) >= encoder->width * 4); + for (i = 0; i < 4; i++) { + encoder->channels[i].correlate_row[-1] = 0; + quic_four_uncompress_row0(encoder, &encoder->channels[i], (four_bytes_t *)(buf + i), + encoder->width); + } + encoder->rows_completed++; + for (row = 1; row < encoder->height; row++) { + prev = buf; + buf += stride; + for (i = 0; i < 4; i++) { + encoder->channels[i].correlate_row[-1] = encoder->channels[i].correlate_row[0]; + quic_four_uncompress_row(encoder, &encoder->channels[i], + (four_bytes_t *)(prev + i), + (four_bytes_t *)(buf + i), + encoder->width); + } + encoder->rows_completed++; + } + break; +#endif + case QUIC_IMAGE_TYPE_GRAY: + + if (type != QUIC_IMAGE_TYPE_GRAY) { + encoder->usr->warn(encoder->usr, "unsupported output format\n"); + return QUIC_ERROR; + } + ASSERT(encoder->usr, ABS(stride) >= (int)encoder->width); + uncompress_gray(encoder, buf, stride); + break; + case QUIC_IMAGE_TYPE_INVALID: + default: + encoder->usr->error(encoder->usr, "bad image type\n"); + } + return QUIC_OK; +} + +static int need_init = TRUE; + +QuicContext *quic_create(QuicUsrContext *usr) +{ + Encoder *encoder; + + if (!usr || need_init || !usr->error || !usr->warn || !usr->info || !usr->malloc || + !usr->free || !usr->more_space || !usr->more_lines) { + return NULL; + } + + if (!(encoder = (Encoder *)usr->malloc(usr, sizeof(Encoder)))) { + return NULL; + } + + if (!init_encoder(encoder, usr)) { + usr->free(usr, encoder); + return NULL; + } + return (QuicContext *)encoder; +} + +void quic_destroy(QuicContext *quic) +{ + Encoder *encoder = (Encoder *)quic; + int i; + + if (!quic) { + return; + } + + for (i = 0; i < MAX_CHANNELS; i++) { + destroy_channel(&encoder->channels[i]); + } + encoder->usr->free(encoder->usr, encoder); +} + +void quic_init() +{ + if (!need_init) { + return; + } + need_init = FALSE; + + family_init(&family_8bpc, 8, DEFmaxclen); + family_init(&family_5bpc, 5, DEFmaxclen); +#if defined(RLE) && defined(RLE_STAT) + init_zeroLUT(); +#endif +} + diff --git a/xddm/display/quic.h b/xddm/display/quic.h new file mode 100644 index 0000000..9463760 --- /dev/null +++ b/xddm/display/quic.h @@ -0,0 +1,68 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This software is licensed under the GNU General Public License, + version 2 (GPLv2) (see COPYING for details), subject to the + following clarification. + + With respect to binaries built using the Microsoft(R) Windows + Driver Kit (WDK), GPLv2 does not extend to any code contained in or + derived from the WDK ("WDK Code"). As to WDK Code, by using or + distributing such binaries you agree to be bound by the Microsoft + Software License Terms for the WDK. All WDK Code is considered by + the GPLv2 licensors to qualify for the special exception stated in + section 3 of GPLv2 (commonly known as the system library + exception). + + There is NO WARRANTY for this software, express or implied, + including the implied warranties of NON-INFRINGEMENT, TITLE, + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#ifndef __QUIC_H +#define __QUIC_H + +#include "quic_config.h" + +typedef enum { + QUIC_IMAGE_TYPE_INVALID, + QUIC_IMAGE_TYPE_GRAY, + QUIC_IMAGE_TYPE_RGB16, + QUIC_IMAGE_TYPE_RGB24, + QUIC_IMAGE_TYPE_RGB32, + QUIC_IMAGE_TYPE_RGBA +} QuicImageType; + +#define QUIC_ERROR -1 +#define QUIC_OK 0 + +typedef void *QuicContext; + +typedef struct QuicUsrContext QuicUsrContext; +struct QuicUsrContext { + void (*error)(QuicUsrContext *usr, const char *fmt, ...); + void (*warn)(QuicUsrContext *usr, const char *fmt, ...); + void (*info)(QuicUsrContext *usr, const char *fmt, ...); + void *(*malloc)(QuicUsrContext *usr, int size); + void (*free)(QuicUsrContext *usr, void *ptr); + int (*more_space)(QuicUsrContext *usr, uint32_t **io_ptr, int rows_completed); + int (*more_lines)(QuicUsrContext *usr, uint8_t **lines); // on return the last line of previous + // lines bunch must stil be valid +}; + +int quic_encode(QuicContext *quic, QuicImageType type, int width, int height, + uint8_t *lines, unsigned int num_lines, int stride, + uint32_t *io_ptr, unsigned int num_io_words); + +int quic_decode_begin(QuicContext *quic, uint32_t *io_ptr, unsigned int num_io_words, + QuicImageType *type, int *width, int *height); +int quic_decode(QuicContext *quic, QuicImageType type, uint8_t *buf, int stride); + + +QuicContext *quic_create(QuicUsrContext *usr); +void quic_destroy(QuicContext *quic); + +void quic_init(); + +#endif + diff --git a/xddm/display/quic_config.h b/xddm/display/quic_config.h new file mode 100644 index 0000000..2c41ecb --- /dev/null +++ b/xddm/display/quic_config.h @@ -0,0 +1,54 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This software is licensed under the GNU General Public License, + version 2 (GPLv2) (see COPYING for details), subject to the + following clarification. + + With respect to binaries built using the Microsoft(R) Windows + Driver Kit (WDK), GPLv2 does not extend to any code contained in or + derived from the WDK ("WDK Code"). As to WDK Code, by using or + distributing such binaries you agree to be bound by the Microsoft + Software License Terms for the WDK. All WDK Code is considered by + the GPLv2 licensors to qualify for the special exception stated in + section 3 of GPLv2 (commonly known as the system library + exception). + + There is NO WARRANTY for this software, express or implied, + including the implied warranties of NON-INFRINGEMENT, TITLE, + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#ifndef __QUIC_CONFIG_H +#define __QUIC_CONFIG_H + +#include <spice/types.h> + +#ifdef __GNUC__ + +#include <string.h> + +#define INLINE inline + +#define MEMCLEAR(ptr, size) memset(ptr, 0, size) + +#else + +#ifdef QXLDD +#include <windef.h> +#include "os_dep.h" +#define INLINE _inline +#define MEMCLEAR(ptr, size) RtlZeroMemory(ptr, size) +#else +#include <stddef.h> +#include <string.h> + +#define INLINE inline +#define MEMCLEAR(ptr, size) memset(ptr, 0, size) +#endif + + +#endif + +#endif + diff --git a/xddm/display/quic_family_tmpl.c b/xddm/display/quic_family_tmpl.c new file mode 100644 index 0000000..695c482 --- /dev/null +++ b/xddm/display/quic_family_tmpl.c @@ -0,0 +1,118 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This software is licensed under the GNU General Public License, + version 2 (GPLv2) (see COPYING for details), subject to the + following clarification. + + With respect to binaries built using the Microsoft(R) Windows + Driver Kit (WDK), GPLv2 does not extend to any code contained in or + derived from the WDK ("WDK Code"). As to WDK Code, by using or + distributing such binaries you agree to be bound by the Microsoft + Software License Terms for the WDK. All WDK Code is considered by + the GPLv2 licensors to qualify for the special exception stated in + section 3 of GPLv2 (commonly known as the system library + exception). + + There is NO WARRANTY for this software, express or implied, + including the implied warranties of NON-INFRINGEMENT, TITLE, + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#ifdef QUIC_FAMILY_8BPC +#undef QUIC_FAMILY_8BPC +#define FNAME(name) name##_8bpc +#define VNAME(name) name##_8bpc +#define BPC 8 +#endif + + +#ifdef QUIC_FAMILY_5BPC +#undef QUIC_FAMILY_5BPC +#define FNAME(name) name##_5bpc +#define VNAME(name) name##_5bpc +#define BPC 5 +#endif + + +static unsigned int FNAME(golomb_code_len)(const BYTE n, const unsigned int l) +{ + if (n < VNAME(family).nGRcodewords[l]) { + return (n >> l) + 1 + l; + } else { + return VNAME(family).notGRcwlen[l]; + } +} + +static void FNAME(golomb_coding)(const BYTE n, const unsigned int l, unsigned int * const codeword, + unsigned int * const codewordlen) +{ + if (n < VNAME(family).nGRcodewords[l]) { + (*codeword) = bitat[l] | (n & bppmask[l]); + (*codewordlen) = (n >> l) + l + 1; + } else { + (*codeword) = n - VNAME(family).nGRcodewords[l]; + (*codewordlen) = VNAME(family).notGRcwlen[l]; + } +} + +unsigned int FNAME(golomb_decoding)(const unsigned int l, const unsigned int bits, + unsigned int * const codewordlen) +{ + if (bits > VNAME(family).notGRprefixmask[l]) { /*GR*/ + const unsigned int zeroprefix = cnt_l_zeroes(bits); /* leading zeroes in codeword */ + const unsigned int cwlen = zeroprefix + 1 + l; /* codeword length */ + (*codewordlen) = cwlen; + return (zeroprefix << l) | ((bits >> (32 - cwlen)) & bppmask[l]); + } else { /* not-GR */ + const unsigned int cwlen = VNAME(family).notGRcwlen[l]; + (*codewordlen) = cwlen; + return VNAME(family).nGRcodewords[l] + ((bits) >> (32 - cwlen) & + bppmask[VNAME(family).notGRsuffixlen[l]]); + } +} + +/* update the bucket using just encoded curval */ +static void FNAME(update_model)(CommonState *state, s_bucket * const bucket, + const BYTE curval, unsigned int bpp) +{ + COUNTER * const pcounters = bucket->pcounters; + unsigned int i; + unsigned int bestcode; + unsigned int bestcodelen; + //unsigned int bpp = encoder->bpp; + + /* update counters, find minimum */ + + bestcode = bpp - 1; + bestcodelen = (pcounters[bestcode] += FNAME(golomb_code_len)(curval, bestcode)); + + for (i = bpp - 2; i < bpp; i--) { /* NOTE: expression i<bpp for signed int i would be: i>=0 */ + const unsigned int ithcodelen = (pcounters[i] += FNAME(golomb_code_len)(curval, i)); + + if (ithcodelen < bestcodelen) { + bestcode = i; + bestcodelen = ithcodelen; + } + } + + bucket->bestcode = bestcode; /* store the found minimum */ + + if (bestcodelen > state->wm_trigger) { /* halving counters? */ + for (i = 0; i < bpp; i++) { + pcounters[i] >>= 1; + } + } +} + +static s_bucket *FNAME(find_bucket)(Channel *channel, const unsigned int val) +{ + ASSERT(channel->encoder->usr, val < (0x1U << BPC)); + + return channel->_buckets_ptrs[val]; +} + +#undef FNAME +#undef VNAME +#undef BPC + diff --git a/xddm/display/quic_rgb_tmpl.c b/xddm/display/quic_rgb_tmpl.c new file mode 100644 index 0000000..b256d18 --- /dev/null +++ b/xddm/display/quic_rgb_tmpl.c @@ -0,0 +1,766 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This software is licensed under the GNU General Public License, + version 2 (GPLv2) (see COPYING for details), subject to the + following clarification. + + With respect to binaries built using the Microsoft(R) Windows + Driver Kit (WDK), GPLv2 does not extend to any code contained in or + derived from the WDK ("WDK Code"). As to WDK Code, by using or + distributing such binaries you agree to be bound by the Microsoft + Software License Terms for the WDK. All WDK Code is considered by + the GPLv2 licensors to qualify for the special exception stated in + section 3 of GPLv2 (commonly known as the system library + exception). + + There is NO WARRANTY for this software, express or implied, + including the implied warranties of NON-INFRINGEMENT, TITLE, + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#ifdef QUIC_RGB32 +#undef QUIC_RGB32 +#define PIXEL rgb32_pixel_t +#define FNAME(name) quic_rgb32_##name +#define golomb_coding golomb_coding_8bpc +#define golomb_decoding golomb_decoding_8bpc +#define update_model update_model_8bpc +#define find_bucket find_bucket_8bpc +#define family family_8bpc +#define BPC 8 +#define BPC_MASK 0xffU +#define COMPRESS_IMP +#define SET_r(pix, val) ((pix)->r = val) +#define GET_r(pix) ((pix)->r) +#define SET_g(pix, val) ((pix)->g = val) +#define GET_g(pix) ((pix)->g) +#define SET_b(pix, val) ((pix)->b = val) +#define GET_b(pix) ((pix)->b) +#define UNCOMPRESS_PIX_START(pix) ((pix)->pad = 0) +#endif + +#ifdef QUIC_RGB24 +#undef QUIC_RGB24 +#define PIXEL rgb24_pixel_t +#define FNAME(name) quic_rgb24_##name +#define golomb_coding golomb_coding_8bpc +#define golomb_decoding golomb_decoding_8bpc +#define update_model update_model_8bpc +#define find_bucket find_bucket_8bpc +#define family family_8bpc +#define BPC 8 +#define BPC_MASK 0xffU +#define COMPRESS_IMP +#define SET_r(pix, val) ((pix)->r = val) +#define GET_r(pix) ((pix)->r) +#define SET_g(pix, val) ((pix)->g = val) +#define GET_g(pix) ((pix)->g) +#define SET_b(pix, val) ((pix)->b = val) +#define GET_b(pix) ((pix)->b) +#define UNCOMPRESS_PIX_START(pix) +#endif + +#ifdef QUIC_RGB16 +#undef QUIC_RGB16 +#define PIXEL rgb16_pixel_t +#define FNAME(name) quic_rgb16_##name +#define golomb_coding golomb_coding_5bpc +#define golomb_decoding golomb_decoding_5bpc +#define update_model update_model_5bpc +#define find_bucket find_bucket_5bpc +#define family family_5bpc +#define BPC 5 +#define BPC_MASK 0x1fU +#define COMPRESS_IMP +#define SET_r(pix, val) (*(pix) = (*(pix) & ~(0x1f << 10)) | ((val) << 10)) +#define GET_r(pix) ((*(pix) >> 10) & 0x1f) +#define SET_g(pix, val) (*(pix) = (*(pix) & ~(0x1f << 5)) | ((val) << 5)) +#define GET_g(pix) ((*(pix) >> 5) & 0x1f) +#define SET_b(pix, val) (*(pix) = (*(pix) & ~0x1f) | (val)) +#define GET_b(pix) (*(pix) & 0x1f) +#define UNCOMPRESS_PIX_START(pix) (*(pix) = 0) +#endif + +#ifdef QUIC_RGB16_TO_32 +#undef QUIC_RGB16_TO_32 +#define PIXEL rgb32_pixel_t +#define FNAME(name) quic_rgb16_to_32_##name +#define golomb_coding golomb_coding_5bpc +#define golomb_decoding golomb_decoding_5bpc +#define update_model update_model_5bpc +#define find_bucket find_bucket_5bpc +#define family family_5bpc +#define BPC 5 +#define BPC_MASK 0x1fU + +#define SET_r(pix, val) ((pix)->r = ((val) << 3) | (((val) & 0x1f) >> 2)) +#define GET_r(pix) ((pix)->r >> 3) +#define SET_g(pix, val) ((pix)->g = ((val) << 3) | (((val) & 0x1f) >> 2)) +#define GET_g(pix) ((pix)->g >> 3) +#define SET_b(pix, val) ((pix)->b = ((val) << 3) | (((val) & 0x1f) >> 2)) +#define GET_b(pix) ((pix)->b >> 3) +#define UNCOMPRESS_PIX_START(pix) ((pix)->pad = 0) +#endif + +#define SAME_PIXEL(p1, p2) \ + (GET_r(p1) == GET_r(p2) && GET_g(p1) == GET_g(p2) && \ + GET_b(p1) == GET_b(p2)) + + +#define _PIXEL_A(channel, curr) ((unsigned int)GET_##channel((curr) - 1)) +#define _PIXEL_B(channel, prev) ((unsigned int)GET_##channel(prev)) +#define _PIXEL_C(channel, prev) ((unsigned int)GET_##channel((prev) - 1)) + +/* a */ + +#define DECORELATE_0(channel, curr, bpc_mask)\ + family.xlatU2L[(unsigned)((int)GET_##channel(curr) - (int)_PIXEL_A(channel, curr)) & bpc_mask] + +#define CORELATE_0(channel, curr, correlate, bpc_mask)\ + ((family.xlatL2U[correlate] + _PIXEL_A(channel, curr)) & bpc_mask) + +#ifdef PRED_1 + +/* (a+b)/2 */ +#define DECORELATE(channel, prev, curr, bpc_mask, r) \ + r = family.xlatU2L[(unsigned)((int)GET_##channel(curr) - (int)((_PIXEL_A(channel, curr) + \ + _PIXEL_B(channel, prev)) >> 1)) & bpc_mask] + +#define CORELATE(channel, prev, curr, correlate, bpc_mask, r) \ + SET_##channel(r, ((family.xlatL2U[correlate] + \ + (int)((_PIXEL_A(channel, curr) + _PIXEL_B(channel, prev)) >> 1)) & bpc_mask)) +#endif + +#ifdef PRED_2 + +/* .75a+.75b-.5c */ +#define DECORELATE(channel, prev, curr, bpc_mask, r) { \ + int p = ((int)(3 * (_PIXEL_A(channel, curr) + _PIXEL_B(channel, prev))) - \ + (int)(_PIXEL_C(channel, prev) << 1)) >> 2; \ + if (p < 0) { \ + p = 0; \ + } else if ((unsigned)p > bpc_mask) { \ + p = bpc_mask; \ + } \ + r = family.xlatU2L[(unsigned)((int)GET_##channel(curr) - p) & bpc_mask]; \ +} + +#define CORELATE(channel, prev, curr, correlate, bpc_mask, r) { \ + const int p = ((int)(3 * (_PIXEL_A(channel, curr) + _PIXEL_B(channel, prev))) - \ + (int)(_PIXEL_C(channel, prev) << 1) ) >> 2; \ + const unsigned int s = family.xlatL2U[correlate]; \ + if (!(p & ~bpc_mask)) { \ + SET_##channel(r, (s + (unsigned)p) & bpc_mask); \ + } else if (p < 0) { \ + SET_##channel(r, s); \ + } else { \ + SET_##channel(r, (s + bpc_mask) & bpc_mask); \ + } \ +} + +#endif + + +#define COMPRESS_ONE_ROW0_0(channel) \ + correlate_row_##channel[0] = family.xlatU2L[GET_##channel(cur_row)]; \ + golomb_coding(correlate_row_##channel[0], find_bucket(channel_##channel, \ + correlate_row_##channel[-1])->bestcode, \ + &codeword, &codewordlen); \ + encode(encoder, codeword, codewordlen); + +#define COMPRESS_ONE_ROW0(channel, index) \ + correlate_row_##channel[index] = DECORELATE_0(channel, &cur_row[index], bpc_mask); \ + golomb_coding(correlate_row_##channel[index], find_bucket(channel_##channel, \ + correlate_row_##channel[index -1])->bestcode, \ + &codeword, &codewordlen); \ + encode(encoder, codeword, codewordlen); + +#define UPDATE_MODEL(index) \ + update_model(&encoder->rgb_state, find_bucket(channel_r, correlate_row_r[index - 1]), \ + correlate_row_r[index], bpc); \ + update_model(&encoder->rgb_state, find_bucket(channel_g, correlate_row_g[index - 1]), \ + correlate_row_g[index], bpc); \ + update_model(&encoder->rgb_state, find_bucket(channel_b, correlate_row_b[index - 1]), \ + correlate_row_b[index], bpc); + + +#ifdef RLE_PRED_1 +#define RLE_PRED_1_IMP \ +if (SAME_PIXEL(&cur_row[i - 1], &prev_row[i])) { \ + if (run_index != i && SAME_PIXEL(&prev_row[i - 1], &prev_row[i]) && \ + i + 1 < end && SAME_PIXEL(&prev_row[i], &prev_row[i + 1])) { \ + goto do_run; \ + } \ +} +#else +#define RLE_PRED_1_IMP +#endif + +#ifdef RLE_PRED_2 +#define RLE_PRED_2_IMP \ +if (SAME_PIXEL(&prev_row[i - 1], &prev_row[i])) { \ + if (run_index != i && i > 2 && SAME_PIXEL(&cur_row[i - 1], &cur_row[i - 2])) { \ + goto do_run; \ + } \ +} +#else +#define RLE_PRED_2_IMP +#endif + +#ifdef RLE_PRED_3 +#define RLE_PRED_3_IMP \ +if (i > 1 && SAME_PIXEL(&cur_row[i - 1], &cur_row[i - 2]) && i != run_index) { \ + goto do_run; \ +} +#else +#define RLE_PRED_3_IMP +#endif + +#ifdef COMPRESS_IMP + +static void FNAME(compress_row0_seg)(Encoder *encoder, int i, + const PIXEL * const cur_row, + const int end, + const unsigned int waitmask, + const unsigned int bpc, + const unsigned int bpc_mask) +{ + Channel * const channel_r = encoder->channels; + Channel * const channel_g = channel_r + 1; + Channel * const channel_b = channel_g + 1; + + BYTE * const correlate_row_r = channel_r->correlate_row; + BYTE * const correlate_row_g = channel_g->correlate_row; + BYTE * const correlate_row_b = channel_b->correlate_row; + int stopidx; + + ASSERT(encoder->usr, end - i > 0); + + if (!i) { + unsigned int codeword, codewordlen; + + COMPRESS_ONE_ROW0_0(r); + COMPRESS_ONE_ROW0_0(g); + COMPRESS_ONE_ROW0_0(b); + + if (encoder->rgb_state.waitcnt) { + encoder->rgb_state.waitcnt--; + } else { + encoder->rgb_state.waitcnt = (tabrand(&encoder->rgb_state.tabrand_seed) & waitmask); + UPDATE_MODEL(0); + } + stopidx = ++i + encoder->rgb_state.waitcnt; + } else { + stopidx = i + encoder->rgb_state.waitcnt; + } + + while (stopidx < end) { + for (; i <= stopidx; i++) { + unsigned int codeword, codewordlen; + COMPRESS_ONE_ROW0(r, i); + COMPRESS_ONE_ROW0(g, i); + COMPRESS_ONE_ROW0(b, i); + } + + UPDATE_MODEL(stopidx); + stopidx = i + (tabrand(&encoder->rgb_state.tabrand_seed) & waitmask); + } + + for (; i < end; i++) { + unsigned int codeword, codewordlen; + + COMPRESS_ONE_ROW0(r, i); + COMPRESS_ONE_ROW0(g, i); + COMPRESS_ONE_ROW0(b, i); + } + encoder->rgb_state.waitcnt = stopidx - end; +} + +static void FNAME(compress_row0)(Encoder *encoder, const PIXEL *cur_row, + unsigned int width) +{ + const unsigned int bpc = BPC; + const unsigned int bpc_mask = BPC_MASK; + int pos = 0; + + while ((wmimax > (int)encoder->rgb_state.wmidx) && (encoder->rgb_state.wmileft <= width)) { + if (encoder->rgb_state.wmileft) { + FNAME(compress_row0_seg)(encoder, pos, cur_row, pos + encoder->rgb_state.wmileft, + bppmask[encoder->rgb_state.wmidx], bpc, bpc_mask); + width -= encoder->rgb_state.wmileft; + pos += encoder->rgb_state.wmileft; + } + + encoder->rgb_state.wmidx++; + set_wm_trigger(&encoder->rgb_state); + encoder->rgb_state.wmileft = wminext; + } + + if (width) { + FNAME(compress_row0_seg)(encoder, pos, cur_row, pos + width, + bppmask[encoder->rgb_state.wmidx], bpc, bpc_mask); + if (wmimax > (int)encoder->rgb_state.wmidx) { + encoder->rgb_state.wmileft -= width; + } + } + + ASSERT(encoder->usr, (int)encoder->rgb_state.wmidx <= wmimax); + ASSERT(encoder->usr, encoder->rgb_state.wmidx <= 32); + ASSERT(encoder->usr, wminext > 0); +} + +#define COMPRESS_ONE_0(channel) \ + correlate_row_##channel[0] = family.xlatU2L[(unsigned)((int)GET_##channel(cur_row) - \ + (int)GET_##channel(prev_row) ) & bpc_mask]; \ + golomb_coding(correlate_row_##channel[0], \ + find_bucket(channel_##channel, correlate_row_##channel[-1])->bestcode, \ + &codeword, &codewordlen); \ + encode(encoder, codeword, codewordlen); + +#define COMPRESS_ONE(channel, index) \ + DECORELATE(channel, &prev_row[index], &cur_row[index],bpc_mask, \ + correlate_row_##channel[index]); \ + golomb_coding(correlate_row_##channel[index], \ + find_bucket(channel_##channel, correlate_row_##channel[index - 1])->bestcode, \ + &codeword, &codewordlen); \ + encode(encoder, codeword, codewordlen); + +static void FNAME(compress_row_seg)(Encoder *encoder, int i, + const PIXEL * const prev_row, + const PIXEL * const cur_row, + const int end, + const unsigned int waitmask, + const unsigned int bpc, + const unsigned int bpc_mask) +{ + Channel * const channel_r = encoder->channels; + Channel * const channel_g = channel_r + 1; + Channel * const channel_b = channel_g + 1; + + BYTE * const correlate_row_r = channel_r->correlate_row; + BYTE * const correlate_row_g = channel_g->correlate_row; + BYTE * const correlate_row_b = channel_b->correlate_row; + int stopidx; +#ifdef RLE + int run_index = 0; + int run_size; +#endif + + ASSERT(encoder->usr, end - i > 0); + + if (!i) { + unsigned int codeword, codewordlen; + + COMPRESS_ONE_0(r); + COMPRESS_ONE_0(g); + COMPRESS_ONE_0(b); + + if (encoder->rgb_state.waitcnt) { + encoder->rgb_state.waitcnt--; + } else { + encoder->rgb_state.waitcnt = (tabrand(&encoder->rgb_state.tabrand_seed) & waitmask); + UPDATE_MODEL(0); + } + stopidx = ++i + encoder->rgb_state.waitcnt; + } else { + stopidx = i + encoder->rgb_state.waitcnt; + } + for (;;) { + while (stopidx < end) { + for (; i <= stopidx; i++) { + unsigned int codeword, codewordlen; +#ifdef RLE + RLE_PRED_1_IMP; + RLE_PRED_2_IMP; + RLE_PRED_3_IMP; +#endif + COMPRESS_ONE(r, i); + COMPRESS_ONE(g, i); + COMPRESS_ONE(b, i); + } + + UPDATE_MODEL(stopidx); + stopidx = i + (tabrand(&encoder->rgb_state.tabrand_seed) & waitmask); + } + + for (; i < end; i++) { + unsigned int codeword, codewordlen; +#ifdef RLE + RLE_PRED_1_IMP; + RLE_PRED_2_IMP; + RLE_PRED_3_IMP; +#endif + COMPRESS_ONE(r, i); + COMPRESS_ONE(g, i); + COMPRESS_ONE(b, i); + } + encoder->rgb_state.waitcnt = stopidx - end; + + return; + +#ifdef RLE +do_run: + run_index = i; + encoder->rgb_state.waitcnt = stopidx - i; + run_size = 0; + + while (SAME_PIXEL(&cur_row[i], &cur_row[i - 1])) { + run_size++; + if (++i == end) { + encode_run(encoder, run_size); + return; + } + } + encode_run(encoder, run_size); + stopidx = i + encoder->rgb_state.waitcnt; +#endif + } +} + +static void FNAME(compress_row)(Encoder *encoder, + const PIXEL * const prev_row, + const PIXEL * const cur_row, + unsigned int width) + +{ + const unsigned int bpc = BPC; + const unsigned int bpc_mask = BPC_MASK; + unsigned int pos = 0; + + while ((wmimax > (int)encoder->rgb_state.wmidx) && (encoder->rgb_state.wmileft <= width)) { + if (encoder->rgb_state.wmileft) { + FNAME(compress_row_seg)(encoder, pos, prev_row, cur_row, + pos + encoder->rgb_state.wmileft, + bppmask[encoder->rgb_state.wmidx], + bpc, bpc_mask); + width -= encoder->rgb_state.wmileft; + pos += encoder->rgb_state.wmileft; + } + + encoder->rgb_state.wmidx++; + set_wm_trigger(&encoder->rgb_state); + encoder->rgb_state.wmileft = wminext; + } + + if (width) { + FNAME(compress_row_seg)(encoder, pos, prev_row, cur_row, pos + width, + bppmask[encoder->rgb_state.wmidx], bpc, bpc_mask); + if (wmimax > (int)encoder->rgb_state.wmidx) { + encoder->rgb_state.wmileft -= width; + } + } + + ASSERT(encoder->usr, (int)encoder->rgb_state.wmidx <= wmimax); + ASSERT(encoder->usr, encoder->rgb_state.wmidx <= 32); + ASSERT(encoder->usr, wminext > 0); +} + +#endif + +#define UNCOMPRESS_ONE_ROW0_0(channel) \ + correlate_row_##channel[0] = (BYTE)golomb_decoding(find_bucket(channel_##channel, \ + correlate_row_##channel[-1])->bestcode, \ + encoder->io_word, &codewordlen); \ + SET_##channel(&cur_row[0], (BYTE)family.xlatL2U[correlate_row_##channel[0]]); \ + decode_eatbits(encoder, codewordlen); + +#define UNCOMPRESS_ONE_ROW0(channel) \ + correlate_row_##channel[i] = (BYTE)golomb_decoding(find_bucket(channel_##channel, \ + correlate_row_##channel[i - 1])->bestcode, \ + encoder->io_word, \ + &codewordlen); \ + SET_##channel(&cur_row[i], CORELATE_0(channel, &cur_row[i], correlate_row_##channel[i], \ + bpc_mask)); \ + decode_eatbits(encoder, codewordlen); + +static void FNAME(uncompress_row0_seg)(Encoder *encoder, int i, + PIXEL * const cur_row, + const int end, + const unsigned int waitmask, + const unsigned int bpc, + const unsigned int bpc_mask) +{ + Channel * const channel_r = encoder->channels; + Channel * const channel_g = channel_r + 1; + Channel * const channel_b = channel_g + 1; + + BYTE * const correlate_row_r = channel_r->correlate_row; + BYTE * const correlate_row_g = channel_g->correlate_row; + BYTE * const correlate_row_b = channel_b->correlate_row; + int stopidx; + + ASSERT(encoder->usr, end - i > 0); + + if (!i) { + unsigned int codewordlen; + + UNCOMPRESS_PIX_START(&cur_row[i]); + UNCOMPRESS_ONE_ROW0_0(r); + UNCOMPRESS_ONE_ROW0_0(g); + UNCOMPRESS_ONE_ROW0_0(b); + + if (encoder->rgb_state.waitcnt) { + --encoder->rgb_state.waitcnt; + } else { + encoder->rgb_state.waitcnt = (tabrand(&encoder->rgb_state.tabrand_seed) & waitmask); + UPDATE_MODEL(0); + } + stopidx = ++i + encoder->rgb_state.waitcnt; + } else { + stopidx = i + encoder->rgb_state.waitcnt; + } + + while (stopidx < end) { + for (; i <= stopidx; i++) { + unsigned int codewordlen; + + UNCOMPRESS_PIX_START(&cur_row[i]); + UNCOMPRESS_ONE_ROW0(r); + UNCOMPRESS_ONE_ROW0(g); + UNCOMPRESS_ONE_ROW0(b); + } + UPDATE_MODEL(stopidx); + stopidx = i + (tabrand(&encoder->rgb_state.tabrand_seed) & waitmask); + } + + for (; i < end; i++) { + unsigned int codewordlen; + + UNCOMPRESS_PIX_START(&cur_row[i]); + UNCOMPRESS_ONE_ROW0(r); + UNCOMPRESS_ONE_ROW0(g); + UNCOMPRESS_ONE_ROW0(b); + } + encoder->rgb_state.waitcnt = stopidx - end; +} + +static void FNAME(uncompress_row0)(Encoder *encoder, + PIXEL * const cur_row, + unsigned int width) + +{ + const unsigned int bpc = BPC; + const unsigned int bpc_mask = BPC_MASK; + unsigned int pos = 0; + + while ((wmimax > (int)encoder->rgb_state.wmidx) && (encoder->rgb_state.wmileft <= width)) { + if (encoder->rgb_state.wmileft) { + FNAME(uncompress_row0_seg)(encoder, pos, cur_row, + pos + encoder->rgb_state.wmileft, + bppmask[encoder->rgb_state.wmidx], + bpc, bpc_mask); + pos += encoder->rgb_state.wmileft; + width -= encoder->rgb_state.wmileft; + } + + encoder->rgb_state.wmidx++; + set_wm_trigger(&encoder->rgb_state); + encoder->rgb_state.wmileft = wminext; + } + + if (width) { + FNAME(uncompress_row0_seg)(encoder, pos, cur_row, pos + width, + bppmask[encoder->rgb_state.wmidx], bpc, bpc_mask); + if (wmimax > (int)encoder->rgb_state.wmidx) { + encoder->rgb_state.wmileft -= width; + } + } + + ASSERT(encoder->usr, (int)encoder->rgb_state.wmidx <= wmimax); + ASSERT(encoder->usr, encoder->rgb_state.wmidx <= 32); + ASSERT(encoder->usr, wminext > 0); +} + +#define UNCOMPRESS_ONE_0(channel) \ + correlate_row_##channel[0] = (BYTE)golomb_decoding(find_bucket(channel_##channel, \ + correlate_row_##channel[-1])->bestcode, \ + encoder->io_word, &codewordlen); \ + SET_##channel(&cur_row[0], (family.xlatL2U[correlate_row_##channel[0]] + \ + GET_##channel(prev_row)) & bpc_mask); \ + decode_eatbits(encoder, codewordlen); + +#define UNCOMPRESS_ONE(channel) \ + correlate_row_##channel[i] = (BYTE)golomb_decoding(find_bucket(channel_##channel, \ + correlate_row_##channel[i - 1])->bestcode, \ + encoder->io_word, \ + &codewordlen); \ + CORELATE(channel, &prev_row[i], &cur_row[i], correlate_row_##channel[i], bpc_mask, \ + &cur_row[i]); \ + decode_eatbits(encoder, codewordlen); + +static void FNAME(uncompress_row_seg)(Encoder *encoder, + const PIXEL * const prev_row, + PIXEL * const cur_row, + int i, + const int end, + const unsigned int bpc, + const unsigned int bpc_mask) +{ + Channel * const channel_r = encoder->channels; + Channel * const channel_g = channel_r + 1; + Channel * const channel_b = channel_g + 1; + + BYTE * const correlate_row_r = channel_r->correlate_row; + BYTE * const correlate_row_g = channel_g->correlate_row; + BYTE * const correlate_row_b = channel_b->correlate_row; + const unsigned int waitmask = bppmask[encoder->rgb_state.wmidx]; + int stopidx; +#ifdef RLE + int run_index = 0; + int run_end; +#endif + + ASSERT(encoder->usr, end - i > 0); + + if (!i) { + unsigned int codewordlen; + + UNCOMPRESS_PIX_START(&cur_row[i]); + UNCOMPRESS_ONE_0(r); + UNCOMPRESS_ONE_0(g); + UNCOMPRESS_ONE_0(b); + + if (encoder->rgb_state.waitcnt) { + --encoder->rgb_state.waitcnt; + } else { + encoder->rgb_state.waitcnt = (tabrand(&encoder->rgb_state.tabrand_seed) & waitmask); + UPDATE_MODEL(0); + } + stopidx = ++i + encoder->rgb_state.waitcnt; + } else { + stopidx = i + encoder->rgb_state.waitcnt; + } + for (;;) { + while (stopidx < end) { + for (; i <= stopidx; i++) { + unsigned int codewordlen; +#ifdef RLE + RLE_PRED_1_IMP; + RLE_PRED_2_IMP; + RLE_PRED_3_IMP; +#endif + UNCOMPRESS_PIX_START(&cur_row[i]); + UNCOMPRESS_ONE(r); + UNCOMPRESS_ONE(g); + UNCOMPRESS_ONE(b); + } + + UPDATE_MODEL(stopidx); + + stopidx = i + (tabrand(&encoder->rgb_state.tabrand_seed) & waitmask); + } + + for (; i < end; i++) { + unsigned int codewordlen; +#ifdef RLE + RLE_PRED_1_IMP; + RLE_PRED_2_IMP; + RLE_PRED_3_IMP; +#endif + UNCOMPRESS_PIX_START(&cur_row[i]); + UNCOMPRESS_ONE(r); + UNCOMPRESS_ONE(g); + UNCOMPRESS_ONE(b); + } + + encoder->rgb_state.waitcnt = stopidx - end; + + return; + +#ifdef RLE +do_run: + encoder->rgb_state.waitcnt = stopidx - i; + run_index = i; + run_end = i + decode_run(encoder); + + for (; i < run_end; i++) { + UNCOMPRESS_PIX_START(&cur_row[i]); + SET_r(&cur_row[i], GET_r(&cur_row[i - 1])); + SET_g(&cur_row[i], GET_g(&cur_row[i - 1])); + SET_b(&cur_row[i], GET_b(&cur_row[i - 1])); + } + + if (i == end) { + return; + } + + stopidx = i + encoder->rgb_state.waitcnt; +#endif + } +} + +static void FNAME(uncompress_row)(Encoder *encoder, + const PIXEL * const prev_row, + PIXEL * const cur_row, + unsigned int width) + +{ + const unsigned int bpc = BPC; + const unsigned int bpc_mask = BPC_MASK; + unsigned int pos = 0; + + while ((wmimax > (int)encoder->rgb_state.wmidx) && (encoder->rgb_state.wmileft <= width)) { + if (encoder->rgb_state.wmileft) { + FNAME(uncompress_row_seg)(encoder, prev_row, cur_row, pos, + pos + encoder->rgb_state.wmileft, bpc, bpc_mask); + pos += encoder->rgb_state.wmileft; + width -= encoder->rgb_state.wmileft; + } + + encoder->rgb_state.wmidx++; + set_wm_trigger(&encoder->rgb_state); + encoder->rgb_state.wmileft = wminext; + } + + if (width) { + FNAME(uncompress_row_seg)(encoder, prev_row, cur_row, pos, + pos + width, bpc, bpc_mask); + if (wmimax > (int)encoder->rgb_state.wmidx) { + encoder->rgb_state.wmileft -= width; + } + } + + ASSERT(encoder->usr, (int)encoder->rgb_state.wmidx <= wmimax); + ASSERT(encoder->usr, encoder->rgb_state.wmidx <= 32); + ASSERT(encoder->usr, wminext > 0); +} + +#undef PIXEL +#undef FNAME +#undef _PIXEL_A +#undef _PIXEL_B +#undef _PIXEL_C +#undef SAME_PIXEL +#undef RLE_PRED_1_IMP +#undef RLE_PRED_2_IMP +#undef RLE_PRED_3_IMP +#undef UPDATE_MODEL +#undef DECORELATE_0 +#undef DECORELATE +#undef COMPRESS_ONE_ROW0_0 +#undef COMPRESS_ONE_ROW0 +#undef COMPRESS_ONE_0 +#undef COMPRESS_ONE +#undef CORELATE_0 +#undef CORELATE +#undef UNCOMPRESS_ONE_ROW0_0 +#undef UNCOMPRESS_ONE_ROW0 +#undef UNCOMPRESS_ONE_0 +#undef UNCOMPRESS_ONE +#undef golomb_coding +#undef golomb_decoding +#undef update_model +#undef find_bucket +#undef family +#undef BPC +#undef BPC_MASK +#undef COMPRESS_IMP +#undef SET_r +#undef GET_r +#undef SET_g +#undef GET_g +#undef SET_b +#undef GET_b +#undef UNCOMPRESS_PIX_START + diff --git a/xddm/display/quic_tmpl.c b/xddm/display/quic_tmpl.c new file mode 100644 index 0000000..6591d02 --- /dev/null +++ b/xddm/display/quic_tmpl.c @@ -0,0 +1,636 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This software is licensed under the GNU General Public License, + version 2 (GPLv2) (see COPYING for details), subject to the + following clarification. + + With respect to binaries built using the Microsoft(R) Windows + Driver Kit (WDK), GPLv2 does not extend to any code contained in or + derived from the WDK ("WDK Code"). As to WDK Code, by using or + distributing such binaries you agree to be bound by the Microsoft + Software License Terms for the WDK. All WDK Code is considered by + the GPLv2 licensors to qualify for the special exception stated in + section 3 of GPLv2 (commonly known as the system library + exception). + + There is NO WARRANTY for this software, express or implied, + including the implied warranties of NON-INFRINGEMENT, TITLE, + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#ifdef ONE_BYTE +#undef ONE_BYTE +#define FNAME(name) quic_one_##name +#define PIXEL one_byte_t +#endif + +#ifdef THREE_BYTE +#undef THREE_BYTE +#define FNAME(name) quic_three_##name +#define PIXEL three_bytes_t +#endif + +#ifdef FOUR_BYTE +#undef FOUR_BYTE +#define FNAME(name) quic_four_##name +#define PIXEL four_bytes_t +#endif + +#define golomb_coding golomb_coding_8bpc +#define golomb_decoding golomb_decoding_8bpc +#define update_model update_model_8bpc +#define find_bucket find_bucket_8bpc +#define family family_8bpc + +#define BPC 8 +#define BPC_MASK 0xffU + +#define _PIXEL_A ((unsigned int)curr[-1].a) +#define _PIXEL_B ((unsigned int)prev[0].a) +#define _PIXEL_C ((unsigned int)prev[-1].a) + +#ifdef RLE_PRED_1 +#define RLE_PRED_1_IMP \ +if (cur_row[i - 1].a == prev_row[i].a) { \ + if (run_index != i && prev_row[i - 1].a == prev_row[i].a && \ + i + 1 < end && prev_row[i].a == prev_row[i + 1].a) { \ + goto do_run; \ + } \ +} +#else +#define RLE_PRED_1_IMP +#endif + +#ifdef RLE_PRED_2 +#define RLE_PRED_2_IMP \ +if (prev_row[i - 1].a == prev_row[i].a) { \ + if (run_index != i && i > 2 && cur_row[i - 1].a == cur_row[i - 2].a) { \ + goto do_run; \ + } \ +} +#else +#define RLE_PRED_2_IMP +#endif + +#ifdef RLE_PRED_3 +#define RLE_PRED_3_IMP \ +if (i > 1 && cur_row[i - 1].a == cur_row[i - 2].a && i != run_index) { \ + goto do_run; \ +} +#else +#define RLE_PRED_3_IMP +#endif + +/* a */ +static INLINE BYTE FNAME(decorelate_0)(const PIXEL * const curr, const unsigned int bpc_mask) +{ + return family.xlatU2L[(unsigned)((int)curr[0].a - (int)_PIXEL_A) & bpc_mask]; +} + +static INLINE void FNAME(corelate_0)(PIXEL *curr, const BYTE corelate, + const unsigned int bpc_mask) +{ + curr->a = (family.xlatL2U[corelate] + _PIXEL_A) & bpc_mask; +} + +#ifdef PRED_1 + +/* (a+b)/2 */ +static INLINE BYTE FNAME(decorelate)(const PIXEL *const prev, const PIXEL * const curr, + const unsigned int bpc_mask) +{ + return family.xlatU2L[(unsigned)((int)curr->a - (int)((_PIXEL_A + _PIXEL_B) >> 1)) & bpc_mask]; +} + + +static INLINE void FNAME(corelate)(const PIXEL *prev, PIXEL *curr, const BYTE corelate, + const unsigned int bpc_mask) +{ + curr->a = (family.xlatL2U[corelate] + (int)((_PIXEL_A + _PIXEL_B) >> 1)) & bpc_mask; +} + +#endif + +#ifdef PRED_2 + +/* .75a+.75b-.5c */ +static INLINE BYTE FNAME(decorelate)(const PIXEL *const prev, const PIXEL * const curr, + const unsigned int bpc_mask) +{ + int p = ((int)(3 * (_PIXEL_A + _PIXEL_B)) - (int)(_PIXEL_C << 1)) >> 2; + + if (p < 0) { + p = 0; + } else if ((unsigned)p > bpc_mask) { + p = bpc_mask; + } + + { + return family.xlatU2L[(unsigned)((int)curr->a - p) & bpc_mask]; + } +} + +static INLINE void FNAME(corelate)(const PIXEL *prev, PIXEL *curr, const BYTE corelate, + const unsigned int bpc_mask) +{ + const int p = ((int)(3 * (_PIXEL_A + _PIXEL_B)) - (int)(_PIXEL_C << 1)) >> 2; + const unsigned int s = family.xlatL2U[corelate]; + + if (!(p & ~bpc_mask)) { + curr->a = (s + (unsigned)p) & bpc_mask; + } else if (p < 0) { + curr->a = s; + } else { + curr->a = (s + bpc_mask) & bpc_mask; + } +} + +#endif + +static void FNAME(compress_row0_seg)(Encoder *encoder, Channel *channel, int i, + const PIXEL * const cur_row, + const int end, + const unsigned int waitmask, + const unsigned int bpc, + const unsigned int bpc_mask) +{ + BYTE * const decorelate_drow = channel->correlate_row; + int stopidx; + + ASSERT(encoder->usr, end - i > 0); + + if (i == 0) { + unsigned int codeword, codewordlen; + + decorelate_drow[0] = family.xlatU2L[cur_row->a]; + golomb_coding(decorelate_drow[0], find_bucket(channel, decorelate_drow[-1])->bestcode, + &codeword, &codewordlen); + encode(encoder, codeword, codewordlen); + + if (channel->state.waitcnt) { + channel->state.waitcnt--; + } else { + channel->state.waitcnt = (tabrand(&channel->state.tabrand_seed) & waitmask); + update_model(&channel->state, find_bucket(channel, decorelate_drow[-1]), + decorelate_drow[i], bpc); + } + stopidx = ++i + channel->state.waitcnt; + } else { + stopidx = i + channel->state.waitcnt; + } + + while (stopidx < end) { + for (; i <= stopidx; i++) { + unsigned int codeword, codewordlen; + decorelate_drow[i] = FNAME(decorelate_0)(&cur_row[i], bpc_mask); + golomb_coding(decorelate_drow[i], + find_bucket(channel, decorelate_drow[i - 1])->bestcode, &codeword, + &codewordlen); + encode(encoder, codeword, codewordlen); + } + + update_model(&channel->state, find_bucket(channel, decorelate_drow[stopidx - 1]), + decorelate_drow[stopidx], bpc); + stopidx = i + (tabrand(&channel->state.tabrand_seed) & waitmask); + } + + for (; i < end; i++) { + unsigned int codeword, codewordlen; + decorelate_drow[i] = FNAME(decorelate_0)(&cur_row[i], bpc_mask); + golomb_coding(decorelate_drow[i], find_bucket(channel, decorelate_drow[i - 1])->bestcode, + &codeword, &codewordlen); + encode(encoder, codeword, codewordlen); + } + channel->state.waitcnt = stopidx - end; +} + +static void FNAME(compress_row0)(Encoder *encoder, Channel *channel, const PIXEL *cur_row, + unsigned int width) +{ + const unsigned int bpc = BPC; + const unsigned int bpc_mask = BPC_MASK; + int pos = 0; + + while ((wmimax > (int)channel->state.wmidx) && (channel->state.wmileft <= width)) { + if (channel->state.wmileft) { + FNAME(compress_row0_seg)(encoder, channel, pos, cur_row, pos + channel->state.wmileft, + bppmask[channel->state.wmidx], bpc, bpc_mask); + width -= channel->state.wmileft; + pos += channel->state.wmileft; + } + + channel->state.wmidx++; + set_wm_trigger(&channel->state); + channel->state.wmileft = wminext; + } + + if (width) { + FNAME(compress_row0_seg)(encoder, channel, pos, cur_row, pos + width, + bppmask[channel->state.wmidx], bpc, bpc_mask); + if (wmimax > (int)channel->state.wmidx) { + channel->state.wmileft -= width; + } + } + + ASSERT(encoder->usr, (int)channel->state.wmidx <= wmimax); + ASSERT(encoder->usr, channel->state.wmidx <= 32); + ASSERT(encoder->usr, wminext > 0); +} + +static void FNAME(compress_row_seg)(Encoder *encoder, Channel *channel, int i, + const PIXEL * const prev_row, + const PIXEL * const cur_row, + const int end, + const unsigned int waitmask, + const unsigned int bpc, + const unsigned int bpc_mask) +{ + BYTE * const decorelate_drow = channel->correlate_row; + int stopidx; +#ifdef RLE + int run_index = 0; + int run_size; +#endif + + ASSERT(encoder->usr, end - i > 0); + + if (!i) { + unsigned int codeword, codewordlen; + + decorelate_drow[0] = family.xlatU2L[(unsigned)((int)cur_row->a - + (int)prev_row->a) & bpc_mask]; + + golomb_coding(decorelate_drow[0], + find_bucket(channel, decorelate_drow[-1])->bestcode, + &codeword, + &codewordlen); + encode(encoder, codeword, codewordlen); + + if (channel->state.waitcnt) { + channel->state.waitcnt--; + } else { + channel->state.waitcnt = (tabrand(&channel->state.tabrand_seed) & waitmask); + update_model(&channel->state, find_bucket(channel, decorelate_drow[-1]), + decorelate_drow[0], bpc); + } + stopidx = ++i + channel->state.waitcnt; + } else { + stopidx = i + channel->state.waitcnt; + } + for (;;) { + while (stopidx < end) { + for (; i <= stopidx; i++) { + unsigned int codeword, codewordlen; +#ifdef RLE + RLE_PRED_1_IMP; + RLE_PRED_2_IMP; + RLE_PRED_3_IMP; +#endif + decorelate_drow[i] = FNAME(decorelate)(&prev_row[i], &cur_row[i], bpc_mask); + golomb_coding(decorelate_drow[i], + find_bucket(channel, decorelate_drow[i - 1])->bestcode, &codeword, + &codewordlen); + encode(encoder, codeword, codewordlen); + } + + update_model(&channel->state, find_bucket(channel, decorelate_drow[stopidx - 1]), + decorelate_drow[stopidx], bpc); + stopidx = i + (tabrand(&channel->state.tabrand_seed) & waitmask); + } + + for (; i < end; i++) { + unsigned int codeword, codewordlen; +#ifdef RLE + RLE_PRED_1_IMP; + RLE_PRED_2_IMP; + RLE_PRED_3_IMP; +#endif + decorelate_drow[i] = FNAME(decorelate)(&prev_row[i], &cur_row[i], bpc_mask); + golomb_coding(decorelate_drow[i], find_bucket(channel, + decorelate_drow[i - 1])->bestcode, + &codeword, &codewordlen); + encode(encoder, codeword, codewordlen); + } + channel->state.waitcnt = stopidx - end; + + return; + +#ifdef RLE +do_run: + run_index = i; + channel->state.waitcnt = stopidx - i; + run_size = 0; + + while (cur_row[i].a == cur_row[i - 1].a) { + run_size++; + if (++i == end) { +#ifdef RLE_STAT + encode_channel_run(encoder, channel, run_size); +#else + encode_run(encoder, run_size); +#endif + return; + } + } +#ifdef RLE_STAT + encode_channel_run(encoder, channel, run_size); +#else + encode_run(encoder, run_size); +#endif + stopidx = i + channel->state.waitcnt; +#endif + } +} + +static void FNAME(compress_row)(Encoder *encoder, Channel *channel, + const PIXEL * const prev_row, + const PIXEL * const cur_row, + unsigned int width) + +{ + const unsigned int bpc = BPC; + const unsigned int bpc_mask = BPC_MASK; + unsigned int pos = 0; + + while ((wmimax > (int)channel->state.wmidx) && (channel->state.wmileft <= width)) { + if (channel->state.wmileft) { + FNAME(compress_row_seg)(encoder, channel, pos, prev_row, cur_row, + pos + channel->state.wmileft, bppmask[channel->state.wmidx], + bpc, bpc_mask); + width -= channel->state.wmileft; + pos += channel->state.wmileft; + } + + channel->state.wmidx++; + set_wm_trigger(&channel->state); + channel->state.wmileft = wminext; + } + + if (width) { + FNAME(compress_row_seg)(encoder, channel, pos, prev_row, cur_row, pos + width, + bppmask[channel->state.wmidx], bpc, bpc_mask); + if (wmimax > (int)channel->state.wmidx) { + channel->state.wmileft -= width; + } + } + + ASSERT(encoder->usr, (int)channel->state.wmidx <= wmimax); + ASSERT(encoder->usr, channel->state.wmidx <= 32); + ASSERT(encoder->usr, wminext > 0); +} + +static void FNAME(uncompress_row0_seg)(Encoder *encoder, Channel *channel, int i, + BYTE * const correlate_row, + PIXEL * const cur_row, + const int end, + const unsigned int waitmask, + const unsigned int bpc, + const unsigned int bpc_mask) +{ + int stopidx; + + ASSERT(encoder->usr, end - i > 0); + + if (i == 0) { + unsigned int codewordlen; + + correlate_row[0] = (BYTE)golomb_decoding(find_bucket(channel, + correlate_row[-1])->bestcode, + encoder->io_word, &codewordlen); + cur_row[0].a = (BYTE)family.xlatL2U[correlate_row[0]]; + decode_eatbits(encoder, codewordlen); + + if (channel->state.waitcnt) { + --channel->state.waitcnt; + } else { + channel->state.waitcnt = (tabrand(&channel->state.tabrand_seed) & waitmask); + update_model(&channel->state, find_bucket(channel, correlate_row[-1]), + correlate_row[0], bpc); + } + stopidx = ++i + channel->state.waitcnt; + } else { + stopidx = i + channel->state.waitcnt; + } + + while (stopidx < end) { + struct s_bucket * pbucket = NULL; + + for (; i <= stopidx; i++) { + unsigned int codewordlen; + + pbucket = find_bucket(channel, correlate_row[i - 1]); + correlate_row[i] = (BYTE)golomb_decoding(pbucket->bestcode, encoder->io_word, + &codewordlen); + FNAME(corelate_0)(&cur_row[i], correlate_row[i], bpc_mask); + decode_eatbits(encoder, codewordlen); + } + + update_model(&channel->state, pbucket, correlate_row[stopidx], bpc); + + stopidx = i + (tabrand(&channel->state.tabrand_seed) & waitmask); + } + + for (; i < end; i++) { + unsigned int codewordlen; + + correlate_row[i] = (BYTE)golomb_decoding(find_bucket(channel, + correlate_row[i - 1])->bestcode, + encoder->io_word, &codewordlen); + FNAME(corelate_0)(&cur_row[i], correlate_row[i], bpc_mask); + decode_eatbits(encoder, codewordlen); + } + channel->state.waitcnt = stopidx - end; +} + +static void FNAME(uncompress_row0)(Encoder *encoder, Channel *channel, + PIXEL * const cur_row, + unsigned int width) + +{ + const unsigned int bpc = BPC; + const unsigned int bpc_mask = BPC_MASK; + BYTE * const correlate_row = channel->correlate_row; + unsigned int pos = 0; + + while ((wmimax > (int)channel->state.wmidx) && (channel->state.wmileft <= width)) { + if (channel->state.wmileft) { + FNAME(uncompress_row0_seg)(encoder, channel, pos, correlate_row, cur_row, + pos + channel->state.wmileft, bppmask[channel->state.wmidx], + bpc, bpc_mask); + pos += channel->state.wmileft; + width -= channel->state.wmileft; + } + + channel->state.wmidx++; + set_wm_trigger(&channel->state); + channel->state.wmileft = wminext; + } + + if (width) { + FNAME(uncompress_row0_seg)(encoder, channel, pos, correlate_row, cur_row, pos + width, + bppmask[channel->state.wmidx], bpc, bpc_mask); + if (wmimax > (int)channel->state.wmidx) { + channel->state.wmileft -= width; + } + } + + ASSERT(encoder->usr, (int)channel->state.wmidx <= wmimax); + ASSERT(encoder->usr, channel->state.wmidx <= 32); + ASSERT(encoder->usr, wminext > 0); +} + +static void FNAME(uncompress_row_seg)(Encoder *encoder, Channel *channel, + BYTE *correlate_row, + const PIXEL * const prev_row, + PIXEL * const cur_row, + int i, + const int end, + const unsigned int bpc, + const unsigned int bpc_mask) +{ + const unsigned int waitmask = bppmask[channel->state.wmidx]; + int stopidx; +#ifdef RLE + int run_index = 0; + int run_end; +#endif + + ASSERT(encoder->usr, end - i > 0); + + if (i == 0) { + unsigned int codewordlen; + + correlate_row[0] = (BYTE)golomb_decoding(find_bucket(channel, correlate_row[-1])->bestcode, + encoder->io_word, &codewordlen); + cur_row[0].a = (family.xlatL2U[correlate_row[0]] + prev_row[0].a) & bpc_mask; + decode_eatbits(encoder, codewordlen); + + if (channel->state.waitcnt) { + --channel->state.waitcnt; + } else { + channel->state.waitcnt = (tabrand(&channel->state.tabrand_seed) & waitmask); + update_model(&channel->state, find_bucket(channel, correlate_row[-1]), + correlate_row[0], bpc); + } + stopidx = ++i + channel->state.waitcnt; + } else { + stopidx = i + channel->state.waitcnt; + } + for (;;) { + while (stopidx < end) { + struct s_bucket * pbucket = NULL; + + for (; i <= stopidx; i++) { + unsigned int codewordlen; +#ifdef RLE + RLE_PRED_1_IMP; + RLE_PRED_2_IMP; + RLE_PRED_3_IMP; +#endif + pbucket = find_bucket(channel, correlate_row[i - 1]); + correlate_row[i] = (BYTE)golomb_decoding(pbucket->bestcode, encoder->io_word, + &codewordlen); + FNAME(corelate)(&prev_row[i], &cur_row[i], correlate_row[i], bpc_mask); + decode_eatbits(encoder, codewordlen); + } + + update_model(&channel->state, pbucket, correlate_row[stopidx], bpc); + + stopidx = i + (tabrand(&channel->state.tabrand_seed) & waitmask); + } + + for (; i < end; i++) { + unsigned int codewordlen; +#ifdef RLE + RLE_PRED_1_IMP; + RLE_PRED_2_IMP; + RLE_PRED_3_IMP; +#endif + correlate_row[i] = (BYTE)golomb_decoding(find_bucket(channel, + correlate_row[i - 1])->bestcode, + encoder->io_word, &codewordlen); + FNAME(corelate)(&prev_row[i], &cur_row[i], correlate_row[i], bpc_mask); + decode_eatbits(encoder, codewordlen); + } + + channel->state.waitcnt = stopidx - end; + + return; + +#ifdef RLE +do_run: + channel->state.waitcnt = stopidx - i; + run_index = i; +#ifdef RLE_STAT + run_end = i + decode_channel_run(encoder, channel); +#else + run_end = i + decode_run(encoder); +#endif + + for (; i < run_end; i++) { + cur_row[i].a = cur_row[i - 1].a; + } + + if (i == end) { + return; + } + + stopidx = i + channel->state.waitcnt; +#endif + } +} + +static void FNAME(uncompress_row)(Encoder *encoder, Channel *channel, + const PIXEL * const prev_row, + PIXEL * const cur_row, + unsigned int width) + +{ + const unsigned int bpc = BPC; + const unsigned int bpc_mask = BPC_MASK; + BYTE * const correlate_row = channel->correlate_row; + unsigned int pos = 0; + + while ((wmimax > (int)channel->state.wmidx) && (channel->state.wmileft <= width)) { + if (channel->state.wmileft) { + FNAME(uncompress_row_seg)(encoder, channel, correlate_row, prev_row, cur_row, pos, + pos + channel->state.wmileft, bpc, bpc_mask); + pos += channel->state.wmileft; + width -= channel->state.wmileft; + } + + channel->state.wmidx++; + set_wm_trigger(&channel->state); + channel->state.wmileft = wminext; + } + + if (width) { + FNAME(uncompress_row_seg)(encoder, channel, correlate_row, prev_row, cur_row, pos, + pos + width, bpc, bpc_mask); + if (wmimax > (int)channel->state.wmidx) { + channel->state.wmileft -= width; + } + } + + ASSERT(encoder->usr, (int)channel->state.wmidx <= wmimax); + ASSERT(encoder->usr, channel->state.wmidx <= 32); + ASSERT(encoder->usr, wminext > 0); +} + +#undef PIXEL +#undef FNAME +#undef _PIXEL_A +#undef _PIXEL_B +#undef _PIXEL_C +#undef RLE_PRED_1_IMP +#undef RLE_PRED_2_IMP +#undef RLE_PRED_3_IMP +#undef golomb_coding +#undef golomb_deoding +#undef update_model +#undef find_bucket +#undef family +#undef BPC +#undef BPC_MASK + diff --git a/xddm/display/qxldd.h b/xddm/display/qxldd.h new file mode 100644 index 0000000..1a1b5d5 --- /dev/null +++ b/xddm/display/qxldd.h @@ -0,0 +1,548 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This software is licensed under the GNU General Public License, + version 2 (GPLv2) (see COPYING for details), subject to the + following clarification. + + With respect to binaries built using the Microsoft(R) Windows + Driver Kit (WDK), GPLv2 does not extend to any code contained in or + derived from the WDK ("WDK Code"). As to WDK Code, by using or + distributing such binaries you agree to be bound by the Microsoft + Software License Terms for the WDK. All WDK Code is considered by + the GPLv2 licensors to qualify for the special exception stated in + section 3 of GPLv2 (commonly known as the system library + exception). + + There is NO WARRANTY for this software, express or implied, + including the implied warranties of NON-INFRINGEMENT, TITLE, + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#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 "ioaccess.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 + +#define PANIC(pdev, str) { \ + DebugPrint(pdev, 0, "PANIC: %s @ %s\n", str, __FUNCTION__); \ + EngDebugBreak(); \ +} + +#define PUNT_IF_DISABLED(pdev) \ + do { \ + if (!pdev->enabled) { \ + DEBUG_PRINT((pdev, 0, "%s: punting\n", __FUNCTION__)); \ + return FALSE; \ + } \ + } while (0) + +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; + UINT8 count; +} UpdateTrace; + +typedef struct PMemSlot { + MemSlot slot; + QXLPHYSICAL high_bits; +} PMemSlot; + +typedef struct MspaceInfo { + mspace _mspace; + UINT8 *mspace_start; + UINT8 *mspace_end; +} MspaceInfo; + +enum { + MSPACE_TYPE_DEVRAM, + MSPACE_TYPE_VRAM, + + NUM_MSPACES, +}; + +enum { + SYNC = 0, + ASYNC = 1 +}; + +typedef enum { + ASYNCABLE_UPDATE_AREA = 0, + ASYNCABLE_MEMSLOT_ADD, + ASYNCABLE_CREATE_PRIMARY, + ASYNCABLE_DESTROY_PRIMARY, + ASYNCABLE_DESTROY_SURFACE, + ASYNCABLE_DESTROY_ALL_SURFACES, + ASYNCABLE_FLUSH_SURFACES, + + ASYNCABLE_COUNT +} asyncable_t; + + +typedef struct PDev PDev; + +typedef struct DrawArea { + HSURF bitmap; + SURFOBJ* surf_obj; + UINT8 *base_mem; +} DrawArea; + +typedef struct SurfaceInfo SurfaceInfo; +struct SurfaceInfo { + DrawArea draw_area; + HBITMAP hbitmap; + SIZEL size; + UINT8 *copy; + ULONG bitmap_format; + INT32 stride; + union { + PDev *pdev; + SurfaceInfo *next_free; + } u; +}; + +#define SSE_MASK 15 +#define SSE_ALIGN 16 + +typedef struct PDev { + HANDLE driver; + HDEV eng; + HPALETTE palette; + HSURF surf; + UINT8 surf_enable; + DWORD video_mode_index; + SIZEL resolution; + UINT32 max_bitmap_size; + ULONG bitmap_format; + UINT8 create_non_primary_surfaces; + + ULONG fb_size; + BYTE* fb; + UINT64 fb_phys; + UINT8 vram_slot_initialized; + UINT8 vram_mem_slot; + + ULONG stride; + FLONG red_mask; + FLONG green_mask; + FLONG blue_mask; + ULONG fp_state_size; + + QXLPHYSICAL surf_phys; + UINT8 *surf_base; + + QuicData *quic_data; + HSEMAPHORE quic_data_sem; + + QXLCommandRing *cmd_ring; + QXLCursorRing *cursor_ring; + QXLReleaseRing *release_ring; + PUCHAR notify_cmd_port; + PUCHAR notify_cursor_port; + PUCHAR notify_oom_port; + PEVENT display_event; + PEVENT cursor_event; + PEVENT sleep_event; + PEVENT io_cmd_event; + + PUCHAR log_port; + UINT8 *log_buf; + UINT32 *log_level; + + PMemSlot *mem_slots; + UINT8 num_mem_slot; + UINT8 main_mem_slot; + UINT8 slot_id_bits; + UINT8 slot_gen_bits; + UINT8 *slots_generation; + UINT64 *ram_slot_start; + UINT64 *ram_slot_end; + QXLPHYSICAL va_slot_mask; + + UINT32 num_io_pages; + UINT8 *io_pages_virt; + UINT64 io_pages_phys; + + UINT32 *dev_update_id; + + QXLRect *update_area; + UINT32 *update_surface; + + UINT32 *mm_clock; + + UINT32 *compression_level; + + FLONG cursor_trail; + +#if (WINVER < 0x0501) + PQXLWaitForEvent WaitForEvent; +#endif + + PUCHAR asyncable[ASYNCABLE_COUNT][2]; + HSEMAPHORE io_sem; + PUCHAR memslot_del_port; + PUCHAR flush_release_port; + UINT32 use_async; + + UINT8* primary_memory_start; + UINT32 primary_memory_size; + + QXLSurfaceCreate *primary_surface_create; + + UINT32 dev_id; + + Ring update_trace; + UpdateTrace update_trace_items[NUM_UPDATE_TRACE_ITEMS]; + + UINT64 free_outputs; + + MspaceInfo mspaces[NUM_MSPACES]; + + /* + * TODO: reconsider semaphores according to + * http://msdn.microsoft.com/en-us/library/ff568281%28v=vs.85%29.aspx + * 1) In order to protect the device log buffer, + * the print_sem must be shared between different pdevs and + * different display sessions. + * 2) malloc_sem: not sure what it protects. Maybe globals in mspace? + * since only the enabled pdev is allocating memory, I don't + * think it is required (unless it is possible to have + * AssertMode(x, enable) before AssertMode(y, disable). + * 3) cmd_sem, cursor_sem: again, since only the enabled pdev touches the cmd rings + * I don't think it is required. + * 4) io_sem - same as print sem. Note that we should prevent starvation between + * print_sem and io_sem in DebugPrintV. + * + */ + HSEMAPHORE malloc_sem; /* Also protects release ring */ + HSEMAPHORE print_sem; + HSEMAPHORE cmd_sem; + HSEMAPHORE cursor_sem; /* Protects cursor_ring */ + + CacheImage cache_image_pool[IMAGE_POOL_SIZE]; + Ring cache_image_lru; + Ring cursors_lru; + Ring palette_lru; + ImageKey image_key_lookup[IMAGE_KEY_HASH_SIZE]; + struct CacheImage *image_cache[IMAGE_HASH_SIZE]; + struct InternalCursor *cursor_cache[CURSOR_HASH_SIZE]; + UINT32 num_cursors; + UINT32 last_cursor_id; + struct InternalPalette *palette_cache[PALETTE_HASH_SIZE]; + UINT32 num_palettes; + + UINT32 n_surfaces; + SurfaceInfo surface0_info; + SurfaceInfo *surfaces_info; + SurfaceInfo *free_surfaces; + + UINT32 update_id; + + UINT32 enabled; /* 1 between DrvAssertMode(TRUE) and DrvAssertMode(FALSE) */ + + + UCHAR pci_revision; + +#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 +} PDev; + + +void DebugPrintV(PDev *pdev, const char *message, va_list ap); +void DebugPrint(PDev *pdev, int level, const char *message, ...); + +void InitResources(PDev *pdev); +void ClearResources(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; +} + +#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 + +/* Helpers for dealing with ENG_TIME_FIELDS */ +static _inline ULONG64 eng_time_diff_ms(ENG_TIME_FIELDS *b, ENG_TIME_FIELDS *a) +{ + ULONG64 ret = 0; + + ret += b->usMilliseconds - a->usMilliseconds; + ret += 1000 * (b->usSecond - a->usSecond); + ret += 60000 * (b->usMinute - a->usMinute); + ret += 3600000L * (b->usHour - a->usHour); + // don't get into gregorian calendar, just ignore more then a single day difference + if (b->usDay != a->usDay) { + ret += (3600L * 24L * 1000L); + } + return ret; +} + +#define INTERRUPT_NOT_PRESENT_TIMEOUT_MS 60000L +#define INTERRUPT_NOT_PRESENT_TIMEOUT_100NS (INTERRUPT_NOT_PRESENT_TIMEOUT_MS * 10000L) + +/* Write to an ioport. For some operations we support a new port that returns + * immediatly, and completion is signaled by an interrupt that sets io_cmd_event. + * If the pci_revision is >= QXL_REVISION_STABLE_V10, we support it, else do + * a regular ioport write. + */ +static _inline void async_io(PDev *pdev, asyncable_t op, UCHAR val) +{ + ENG_TIME_FIELDS start, finish; + LARGE_INTEGER timeout; // 1 => 100 nanoseconds + ULONG64 millis; + + if (pdev->use_async) { + EngAcquireSemaphore(pdev->io_sem); + WRITE_PORT_UCHAR(pdev->asyncable[op][ASYNC], val); + /* Our Interrupt may be taken from us unexpectedly, by a surprise removal. + * in which case this event will never be set. This happens only during WHQL + * tests (pnpdtest /surprise). So instead: Wait on a timer, if we fail, stop waiting, until + * we get reset. We use EngQueryLocalTime because there is no way to differentiate a return on + * timeout from a return on event set otherwise. */ + timeout.QuadPart = -INTERRUPT_NOT_PRESENT_TIMEOUT_100NS; // negative => relative + DEBUG_PRINT((pdev, 15, "WAIT_FOR_EVENT %d\n", (int)op)); + EngQueryLocalTime(&start); + WAIT_FOR_EVENT(pdev, pdev->io_cmd_event, &timeout); + EngQueryLocalTime(&finish); + millis = eng_time_diff_ms(&finish, &start); + if (millis >= INTERRUPT_NOT_PRESENT_TIMEOUT_MS) { + pdev->use_async = 0; + DEBUG_PRINT((pdev, 0, "%s: timeout reached, disabling async io!\n", __FUNCTION__)); + } + EngReleaseSemaphore(pdev->io_sem); + DEBUG_PRINT((pdev, 3, "finished async %d\n", (int)op)); + } else { + if (pdev->asyncable[op][SYNC] == NULL) { + DEBUG_PRINT((pdev, 0, "ERROR: trying calling sync io on NULL port %d\n", op)); + } else { + EngAcquireSemaphore(pdev->io_sem); + WRITE_PORT_UCHAR(pdev->asyncable[op][SYNC], val); + EngReleaseSemaphore(pdev->io_sem); + } + } +} + +/* + * Before the introduction of QXL_IO_*_ASYNC all io writes would return + * only when their function was complete. Since qemu would only allow + * a single outstanding io operation between all vcpu threads, they were + * also protected from simultaneous calls between different vcpus. + * + * With the introduction of _ASYNC we need to explicitly lock between different + * threads running on different vcpus, this is what this helper accomplishes. + */ +static _inline void sync_io(PDev *pdev, PUCHAR port, UCHAR val) +{ + EngAcquireSemaphore(pdev->io_sem); + WRITE_PORT_UCHAR(port, val); + EngReleaseSemaphore(pdev->io_sem); +} + +#ifdef DBG +#define DUMP_VRAM_MSPACE(pdev) \ + do { \ + DEBUG_PRINT((pdev, 0, "%s: dumping mspace vram (%p)\n", __FUNCTION__, pdev)); \ + if (pdev) { \ + mspace_malloc_stats(pdev->mspaces[MSPACE_TYPE_VRAM]._mspace); \ + } else { \ + DEBUG_PRINT((pdev, 0, "nothing\n")); \ + }\ + } while (0) + +#define DUMP_DEVRAM_MSPACE(pdev) \ + do { \ + DEBUG_PRINT((pdev, 0, "%s: dumping mspace devram (%p)\n", __FUNCTION__, pdev)); \ + if (pdev) { \ + mspace_malloc_stats(pdev->mspaces[MSPACE_TYPE_DEVRAM]._mspace); \ + } else { \ + DEBUG_PRINT((pdev, 0, "nothing\n")); \ + }\ + } while (0) +#else +#define DUMP_VRAM_MSPACE +#define DUMP_DEVRAM_MSPACE +#endif + +#endif diff --git a/xddm/display/res.c b/xddm/display/res.c new file mode 100644 index 0000000..e494271 --- /dev/null +++ b/xddm/display/res.c @@ -0,0 +1,3387 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This software is licensed under the GNU General Public License, + version 2 (GPLv2) (see COPYING for details), subject to the + following clarification. + + With respect to binaries built using the Microsoft(R) Windows + Driver Kit (WDK), GPLv2 does not extend to any code contained in or + derived from the WDK ("WDK Code"). As to WDK Code, by using or + distributing such binaries you agree to be bound by the Microsoft + Software License Terms for the WDK. All WDK Code is considered by + the GPLv2 licensors to qualify for the special exception stated in + section 3 of GPLv2 (commonly known as the system library + exception). + + There is NO WARRANTY for this software, express or implied, + including the implied warranties of NON-INFRINGEMENT, TITLE, + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#ifdef DBG +#include <stdio.h> +#endif +#include <ddraw.h> +#include <dxmini.h> +#include "qxldd.h" +#include "os_dep.h" +#include "res.h" +#include "utils.h" +#include "mspace.h" +#include "quic.h" +#include "murmur_hash2a.h" +#include "surface.h" +#include "rop.h" +#include "devioctl.h" +#include "ntddvdeo.h" + +static _inline QXLPHYSICAL PA(PDev *pdev, PVOID virt, UINT8 slot_id) +{ + PMemSlot *p_slot = &pdev->mem_slots[slot_id]; + + return p_slot->high_bits | ((UINT64)virt - p_slot->slot.start_virt_addr); +} + +static _inline UINT64 VA(PDev *pdev, QXLPHYSICAL paddr, UINT8 slot_id) +{ + UINT64 virt; + PMemSlot *p_slot = &pdev->mem_slots[slot_id]; + + ASSERT(pdev, (paddr >> (64 - pdev->slot_id_bits)) == slot_id); + ASSERT(pdev, ((paddr << pdev->slot_id_bits) >> (64 - pdev->slot_gen_bits)) == + p_slot->slot.generation); + + virt = paddr & pdev->va_slot_mask; + virt += p_slot->slot.start_virt_addr;; + + return virt; +} + +#define RELEASE_RES(pdev, res) if (!--(res)->refs) (res)->free(pdev, res); +#define GET_RES(res) (++(res)->refs) + +/* Debug helpers - tag each resource with this enum */ +enum { + RESOURCE_TYPE_DRAWABLE = 1, + RESOURCE_TYPE_SURFACE, + RESOURCE_TYPE_PATH, + RESOURCE_TYPE_CLIP_RECTS, + RESOURCE_TYPE_QUIC_IMAGE, + RESOURCE_TYPE_BITMAP_IMAGE, + RESOURCE_TYPE_SURFACE_IMAGE, + RESOURCE_TYPE_SRING, + RESOURCE_TYPE_CURSOR, + RESOURCE_TYPE_BUF, + RESOURCE_TYPE_UPDATE, +}; + +#ifdef DBG +#define RESOURCE_TYPE(res, val) do { res->type = val; } while (0) +#else +#define RESOURCE_TYPE(res, val) +#endif + +typedef struct Resource Resource; +struct Resource { + UINT32 refs; +#ifdef DBG + UINT32 type; +#endif + void (*free)(PDev *pdev, Resource *res); + UINT8 res[0]; +}; + +static void FreeMem(PDev* pdev, UINT32 mspace_type, void *ptr); +static BOOL SetClip(PDev *pdev, CLIPOBJ *clip, QXLDrawable *drawable); + + +#define PUSH_CMD(pdev) do { \ + int notify; \ + SPICE_RING_PUSH(pdev->cmd_ring, notify); \ + if (notify) { \ + sync_io(pdev, pdev->notify_cmd_port, 0); \ + } \ +} while (0); + +#define PUSH_CURSOR_CMD(pdev) do { \ + int notify; \ + SPICE_RING_PUSH(pdev->cursor_ring, notify); \ + if (notify) { \ + sync_io(pdev, pdev->notify_cursor_port, 0); \ + } \ +} while (0); + + +#define MAX_OUTPUT_RES 6 + +typedef struct QXLOutput { + UINT32 num_res; +#ifdef DBG + UINT32 type; +#endif + Resource *resources[MAX_OUTPUT_RES]; + UINT8 data[0]; +} QXLOutput; + +static int have_sse2 = FALSE; + +#ifndef DBG +static _inline void DebugShowOutput(PDev *pdev, QXLOutput* output) +{ +} +#else +const char* resource_type_to_string(QXLOutput *output, UINT32 type) +{ + static char buf[1024]; + + switch (type) { + case 0: return "UNSET"; + case RESOURCE_TYPE_DRAWABLE: return "drawable"; + case RESOURCE_TYPE_SURFACE: { + QXLSurfaceCmd *surface_cmd = (QXLSurfaceCmd*)output->data; + _snprintf(buf, sizeof(buf) - 1, "surface %u", surface_cmd->surface_id); + return buf; + } + case RESOURCE_TYPE_PATH: return "path"; + case RESOURCE_TYPE_CLIP_RECTS: return "clip_rects"; + case RESOURCE_TYPE_QUIC_IMAGE: return "quic_image"; + case RESOURCE_TYPE_BITMAP_IMAGE: return "bitmap_image"; + case RESOURCE_TYPE_SURFACE_IMAGE: return "surface_image"; + case RESOURCE_TYPE_SRING: return "sring"; + case RESOURCE_TYPE_CURSOR: return "cursor"; + case RESOURCE_TYPE_BUF: return "buf"; + case RESOURCE_TYPE_UPDATE: return "update"; + } + return "UNDEFINED"; +} + +static void DebugShowOutput(PDev *pdev, QXLOutput* output) +{ + UINT32 i; + + DEBUG_PRINT((pdev, 11, "output: %s res %d\n", resource_type_to_string(output, output->type), + output->num_res)); + for (i = 0 ; i < output->num_res ; ++i) { + DEBUG_PRINT((pdev, 11, "type %s\n", resource_type_to_string(output, + output->resources[i]->type))); + } +} +#endif + +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)); + DebugShowOutput(pdev, output); + + for (now = output->resources, end = now + output->num_res; now < end; now++) { + RELEASE_RES(pdev, *now); + } + next = *(UINT64*)output->data; + FreeMem(pdev, MSPACE_TYPE_DEVRAM, 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 SurfaceAddRes(PDev *pdev, QXLSurfaceCmd *surface, Resource *res) +{ + QXLOutput *output; + + output = (QXLOutput *)((UINT8 *)surface - 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); +} + +#define SUPPORT_SURPRISE_REMOVE + + +/* Called with cursor_sem held */ +static void WaitForCursorRing(PDev* pdev) +{ + int wait; + + DEBUG_PRINT((pdev, 9, "%s: 0x%lx\n", __FUNCTION__, pdev)); + + for (;;) { + SPICE_RING_PROD_WAIT(pdev->cursor_ring, wait); + + if (!wait) { + break; + } +#ifdef SUPPORT_SURPRISE_REMOVE + { + LARGE_INTEGER timeout; // 1 => 100 nanoseconds + timeout.QuadPart = -1 * (1000 * 1000 * 10); //negative => relative // 1s + WAIT_FOR_EVENT(pdev, pdev->cursor_event, &timeout); + if (SPICE_RING_IS_FULL(pdev->cursor_ring)) { + DEBUG_PRINT((pdev, 0, "%s: 0x%lx: timeout\n", __FUNCTION__, pdev)); + } + } +#else + WAIT_FOR_EVENT(pdev, pdev->cursor_event, NULL); +#endif //SUPPORT_SURPRISE_REMOVE + } +} + +/* Called with cmd_sem held */ +static void WaitForCmdRing(PDev* pdev) +{ + int wait; + + DEBUG_PRINT((pdev, 9, "%s: 0x%lx\n", __FUNCTION__, pdev)); + + for (;;) { + SPICE_RING_PROD_WAIT(pdev->cmd_ring, wait); + + if (!wait) { + break; + } + DEBUG_PRINT((pdev, 9, "%s: 0x%lx\n", __FUNCTION__, pdev)); + +#ifdef SUPPORT_SURPRISE_REMOVE + { + LARGE_INTEGER timeout; // 1 => 100 nanoseconds + timeout.QuadPart = -1 * (1000 * 1000 * 10); //negative => relative // 1s + WAIT_FOR_EVENT(pdev, pdev->display_event, &timeout); + if (SPICE_RING_IS_FULL(pdev->cmd_ring)) { + DEBUG_PRINT((pdev, 0, "%s: 0x%lx: timeout\n", __FUNCTION__, pdev)); + } + } +#else + WAIT_FOR_EVENT(pdev, pdev->display_event, NULL); +#endif //SUPPORT_SURPRISE_REMOVE + } +} + +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)); +} + +/* Called with malloc_sem held */ +static void WaitForReleaseRing(PDev* pdev) +{ + int wait; + + DEBUG_PRINT((pdev, 15, "%s: 0x%lx\n", __FUNCTION__, pdev)); + + for (;;) { + LARGE_INTEGER timeout; + + if (SPICE_RING_IS_EMPTY(pdev->release_ring)) { + QXLSleep(pdev, 10); + if (!SPICE_RING_IS_EMPTY(pdev->release_ring)) { + break; + } + sync_io(pdev, pdev->notify_oom_port, 0); + } + SPICE_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 (SPICE_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 buf %d glyph %d cursor %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 + sync_io(pdev, pdev->notify_oom_port, 0); + } + } + DEBUG_PRINT((pdev, 16, "%s: 0x%lx, done\n", __FUNCTION__, pdev)); +} + +/* Called with malloc_sem held */ +static void FlushReleaseRing(PDev *pdev) +{ + UINT64 output; + int notify; + int num_to_release = 50; + + output = pdev->free_outputs; + + while (1) { + while (output != 0) { + output = ReleaseOutput(pdev, output); + if (--num_to_release == 0) { + break; + } + } + + if (output != 0 || + SPICE_RING_IS_EMPTY(pdev->release_ring)) { + break; + } + + output = *SPICE_RING_CONS_ITEM(pdev->release_ring); + SPICE_RING_POP(pdev->release_ring, notify); + } + + pdev->free_outputs = output; +} + +void EmptyReleaseRing(PDev *pdev) +{ + int count = 0; + + EngAcquireSemaphore(pdev->malloc_sem); + while (pdev->free_outputs || !SPICE_RING_IS_EMPTY(pdev->release_ring)) { + FlushReleaseRing(pdev); + count++; + } + EngReleaseSemaphore(pdev->malloc_sem); + DEBUG_PRINT((pdev, 3, "%s: complete after %d rounds\n", __FUNCTION__, count)); +} + +// todo: separate VRAM releases from DEVRAM releases +#define AllocMem(pdev, mspace_type, size) __AllocMem(pdev, mspace_type, size, TRUE) +static void *__AllocMem(PDev* pdev, UINT32 mspace_type, size_t size, BOOL force) +{ + UINT8 *ptr; + + ASSERT(pdev, pdev && pdev->mspaces[mspace_type]._mspace); + DEBUG_PRINT((pdev, 12, "%s: 0x%lx %p(%d) size %u\n", __FUNCTION__, pdev, + pdev->mspaces[mspace_type]._mspace, + mspace_footprint(pdev->mspaces[mspace_type]._mspace), + size)); +#ifdef DBG + if (pdev && pdev->log_level && *pdev->log_level > 11) { + mspace_malloc_stats(pdev->mspaces[mspace_type]._mspace); + } +#endif + EngAcquireSemaphore(pdev->malloc_sem); + + while (1) { + /* Release lots of queued resources, before allocating, as we + want to release early to minimize fragmentation risks. */ + FlushReleaseRing(pdev); + + ptr = mspace_malloc(pdev->mspaces[mspace_type]._mspace, size); + if (ptr) { + break; + } + + if (pdev->free_outputs != 0 || + !SPICE_RING_IS_EMPTY(pdev->release_ring)) { + /* We have more things to free, try that */ + continue; + } + + if (force) { + /* Ask spice to free some stuff */ + WaitForReleaseRing(pdev); + } else { + /* Fail */ + break; + } + } + + EngReleaseSemaphore(pdev->malloc_sem); + ASSERT(pdev, (!ptr && !force) || (ptr >= pdev->mspaces[mspace_type].mspace_start && + ptr < pdev->mspaces[mspace_type].mspace_end)); + DEBUG_PRINT((pdev, 13, "%s: 0x%lx done 0x%x\n", __FUNCTION__, pdev, ptr)); + return ptr; +} + +static void FreeMem(PDev* pdev, UINT32 mspace_type, void *ptr) +{ + ASSERT(pdev, pdev && pdev->mspaces[mspace_type]._mspace); +#ifdef DBG + if (!((UINT8 *)ptr >= pdev->mspaces[mspace_type].mspace_start && + (UINT8 *)ptr < pdev->mspaces[mspace_type].mspace_end)) { + DebugPrint(pdev, 0, "ASSERT failed @ %s, %p not in [%p, %p) (%d)\n", __FUNCTION__, + ptr, pdev->mspaces[mspace_type].mspace_start, + pdev->mspaces[mspace_type].mspace_end, mspace_type); + EngDebugBreak(); + } +#endif + EngAcquireSemaphore(pdev->malloc_sem); + mspace_free(pdev->mspaces[mspace_type]._mspace, ptr); + EngReleaseSemaphore(pdev->malloc_sem); +} + +static void InitMspace(PDev *pdev, UINT32 mspace_type, UINT8 *start, size_t capacity) +{ + pdev->mspaces[mspace_type]._mspace = create_mspace_with_base(start, capacity, 0, pdev); + pdev->mspaces[mspace_type].mspace_start = start; + pdev->mspaces[mspace_type].mspace_end = start + capacity; +} + +static void ResetCache(PDev *pdev) +{ + int i; + + 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; +} + +/* Init anything that resides on the device memory (pci vram and devram bars). + * NOTE: TODO better documentation of what is on the guest ram (saved during sleep) + * and what is on the pci device bars (bar 0 and 1, devram and vram) + */ +void InitDeviceMemoryResources(PDev *pdev) +{ + UINT32 i; + + DEBUG_PRINT((pdev, 0, "%s: %d, %d\n", __FUNCTION__, pdev->num_io_pages * PAGE_SIZE, + pdev->fb_size)); + 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); + } + InitMspace(pdev, MSPACE_TYPE_DEVRAM, pdev->io_pages_virt, pdev->num_io_pages * PAGE_SIZE); + InitMspace(pdev, MSPACE_TYPE_VRAM, pdev->fb, pdev->fb_size); + ResetCache(pdev); + pdev->free_outputs = 0; +} + +void InitSurfaces(PDev *pdev) +{ + UINT32 i; + pdev->surfaces_info = (SurfaceInfo *)EngAllocMem(FL_ZERO_MEMORY, + sizeof(SurfaceInfo) * pdev->n_surfaces, + ALLOC_TAG); + if (!pdev->surfaces_info) { + PANIC(pdev, "surfaces_info allocation failed\n"); + } + + pdev->free_surfaces = &pdev->surfaces_info[1]; + for (i = 0; i < pdev->n_surfaces - 1; i++) { + pdev->surfaces_info[i].u.next_free = &pdev->surfaces_info[i+1]; + } +} + +void ClearResources(PDev *pdev) +{ + if (pdev->surfaces_info) { + EngFreeMem(pdev->surfaces_info); + pdev->surfaces_info = NULL; + } + + if (pdev->malloc_sem) { + EngDeleteSemaphore(pdev->malloc_sem); + pdev->malloc_sem = NULL; + } + + if (pdev->cmd_sem) { + EngDeleteSemaphore(pdev->cmd_sem); + pdev->cmd_sem = NULL; + } + + if (pdev->cursor_sem) { + EngDeleteSemaphore(pdev->cursor_sem); + pdev->cursor_sem = NULL; + } + + if (pdev->print_sem) { + EngDeleteSemaphore(pdev->print_sem); + pdev->print_sem = NULL; + } +} + +void InitResources(PDev *pdev) +{ + DEBUG_PRINT((pdev, 3, "%s: entry\n", __FUNCTION__)); + + InitSurfaces(pdev); + InitDeviceMemoryResources(pdev); + + pdev->update_id = *pdev->dev_update_id; + + pdev->malloc_sem = EngCreateSemaphore(); + if (!pdev->malloc_sem) { + PANIC(pdev, "malloc sem creation failed\n"); + } + pdev->cmd_sem = EngCreateSemaphore(); + if (!pdev->cmd_sem) { + PANIC(pdev, "cmd sem creation failed\n"); + } + pdev->cursor_sem = EngCreateSemaphore(); + if (!pdev->cursor_sem) { + PANIC(pdev, "cursor sem creation failed\n"); + } + pdev->print_sem = EngCreateSemaphore(); + if (!pdev->print_sem) { + PANIC(pdev, "print sem creation failed\n"); + } + + 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 + DEBUG_PRINT((pdev, 1, "%s: exit\n", __FUNCTION__)); +} + +static QXLDrawable *GetDrawable(PDev *pdev) +{ + QXLOutput *output; + + output = (QXLOutput *)AllocMem(pdev, MSPACE_TYPE_DEVRAM, sizeof(QXLOutput) + sizeof(QXLDrawable)); + output->num_res = 0; + RESOURCE_TYPE(output, RESOURCE_TYPE_DRAWABLE); + ((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, UINT32 surface_id) +{ + QXLDrawable *drawable; + + ASSERT(pdev, pdev && area); + + drawable = GetDrawable(pdev); + drawable->surface_id = surface_id; + drawable->type = type; + drawable->effect = QXL_EFFECT_BLEND; + drawable->self_bitmap = 0; + drawable->mm_time = *pdev->mm_clock; + drawable->surfaces_dest[0] = -1; + drawable->surfaces_dest[1] = - 1; + drawable->surfaces_dest[2] = -1; + CopyRect(&drawable->bbox, area); + + if (!SetClip(pdev, clip, drawable)) { + DEBUG_PRINT((pdev, 0, "%s: set clip failed\n", __FUNCTION__)); + ReleaseOutput(pdev, drawable->release_info.id); + drawable = NULL; + } + return drawable; +} + +void PushDrawable(PDev *pdev, QXLDrawable *drawable) +{ + QXLCommand *cmd; + + EngAcquireSemaphore(pdev->cmd_sem); + WaitForCmdRing(pdev); + cmd = SPICE_RING_PROD_ITEM(pdev->cmd_ring); + cmd->type = QXL_CMD_DRAW; + cmd->data = PA(pdev, drawable, pdev->main_mem_slot); + PUSH_CMD(pdev); + EngReleaseSemaphore(pdev->cmd_sem); +} + +static QXLSurfaceCmd *GetSurfaceCmd(PDev *pdev) +{ + QXLOutput *output; + + output = (QXLOutput *)AllocMem(pdev, MSPACE_TYPE_DEVRAM, sizeof(QXLOutput) + sizeof(QXLSurfaceCmd)); + output->num_res = 0; + RESOURCE_TYPE(output, RESOURCE_TYPE_SURFACE); + ((QXLSurfaceCmd *)output->data)->release_info.id = (UINT64)output; + DEBUG_PRINT((pdev, 9, "%s 0x%x\n", __FUNCTION__, output)); + ONDBG(pdev->num_outputs++); //todo: atomic + return(QXLSurfaceCmd *)output->data; +} + +QXLSurfaceCmd *SurfaceCmd(PDev *pdev, UINT8 type, UINT32 surface_id) +{ + QXLSurfaceCmd *surface_cmd; + + ASSERT(pdev, pdev); + + surface_cmd = GetSurfaceCmd(pdev); + surface_cmd->surface_id = surface_id; + surface_cmd->type = type; + surface_cmd->flags = 0; + + return surface_cmd; +} + +void PushSurfaceCmd(PDev *pdev, QXLSurfaceCmd *surface_cmd) +{ + QXLCommand *cmd; + + EngAcquireSemaphore(pdev->cmd_sem); + WaitForCmdRing(pdev); + cmd = SPICE_RING_PROD_ITEM(pdev->cmd_ring); + cmd->type = QXL_CMD_SURFACE; + cmd->data = PA(pdev, surface_cmd, pdev->main_mem_slot); + PUSH_CMD(pdev); + EngReleaseSemaphore(pdev->cmd_sem); +} + +QXLPHYSICAL SurfaceToPhysical(PDev *pdev, UINT8 *base_mem) +{ + return PA(pdev, base_mem, pdev->vram_mem_slot); +} + +_inline void GetSurfaceMemory(PDev *pdev, UINT32 x, UINT32 y, UINT32 depth, INT32 *stride, + UINT8 **base_mem, QXLPHYSICAL *phys_mem, UINT8 allocation_type) +{ + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + + switch (allocation_type) { + case DEVICE_BITMAP_ALLOCATION_TYPE_SURF0: + ASSERT(pdev, x * y * depth /8 <= pdev->primary_memory_size); + *base_mem = pdev->primary_memory_start; + *phys_mem = PA(pdev, *base_mem, pdev->main_mem_slot); + *stride = (x * depth / 8 + 3) & ~0x3; /* Pixman requires 4 byte aligned stride */ + break; + case DEVICE_BITMAP_ALLOCATION_TYPE_DEVRAM: + *stride = x * depth / 8; + *stride = ALIGN(*stride, 4); + *base_mem = AllocMem(pdev, MSPACE_TYPE_DEVRAM, (*stride) * y); + *phys_mem = PA(pdev, *base_mem, pdev->main_mem_slot); + break; + case DEVICE_BITMAP_ALLOCATION_TYPE_VRAM: + *stride = x * depth / 8; + *stride = ALIGN(*stride, 4); + *base_mem = __AllocMem(pdev, MSPACE_TYPE_VRAM, (*stride) * y, FALSE); + *phys_mem = SurfaceToPhysical(pdev, *base_mem); + break; + case DEVICE_BITMAP_ALLOCATION_TYPE_RAM: + /* used only before suspend to sleep (DrvAssertMode(FALSE)) and then released + * and copied back to VRAM */ + *stride = x * depth / 8; + *stride = ALIGN(*stride, 4); + *base_mem = EngAllocMem(0 /* don't zero memory, will be copied over in a bit */, + (*stride) * y, ALLOC_TAG); + *phys_mem = (QXLPHYSICAL)NULL; /* make sure no one uses it */ + break; + default: + PANIC(pdev, "No allocation type"); + } +} + +void QXLGetSurface(PDev *pdev, QXLPHYSICAL *surface_phys, UINT32 x, UINT32 y, UINT32 depth, + INT32 *stride, UINT8 **base_mem, UINT8 allocation_type) +{ + GetSurfaceMemory(pdev, x, y, depth, stride, base_mem, surface_phys, allocation_type); +} + +void QXLDelSurface(PDev *pdev, UINT8 *base_mem, UINT8 allocation_type) +{ + switch (allocation_type) { + case DEVICE_BITMAP_ALLOCATION_TYPE_DEVRAM: + FreeMem(pdev, MSPACE_TYPE_DEVRAM, base_mem); + break; + case DEVICE_BITMAP_ALLOCATION_TYPE_VRAM: + FreeMem(pdev, MSPACE_TYPE_VRAM, base_mem); + break; + case DEVICE_BITMAP_ALLOCATION_TYPE_RAM: + EngFreeMem(base_mem); + break; + default: + PANIC(pdev, "bad allocation type"); + } +} + +typedef struct InternalDelSurface { + UINT32 surface_id; + UINT8 allocation_type; +} InternalDelSurface; + + +static void FreeDelSurface(PDev *pdev, Resource *res) +{ + InternalDelSurface *internal; + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + + internal = (InternalDelSurface *)res->res; + QXLDelSurface(pdev, GetSurfaceInfo(pdev, internal->surface_id)->draw_area.base_mem, + internal->allocation_type); + FreeSurfaceInfo(pdev, internal->surface_id); + FreeMem(pdev, MSPACE_TYPE_DEVRAM, res); + + DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); +} + +#define SURFACEDEL_ALLOC_BASE (sizeof(Resource) + sizeof(InternalDelSurface)) + +void QXLGetDelSurface(PDev *pdev, QXLSurfaceCmd *surface, UINT32 surface_id, UINT8 allocation_type) +{ + Resource *surface_res; + InternalDelSurface *internal; + size_t alloc_size; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + + alloc_size = SURFACEDEL_ALLOC_BASE; + surface_res = AllocMem(pdev, MSPACE_TYPE_DEVRAM, alloc_size); + + surface_res->refs = 1; + surface_res->free = FreeDelSurface; + RESOURCE_TYPE(surface_res, RESOURCE_TYPE_SURFACE); + + internal = (InternalDelSurface *)surface_res->res; + internal->surface_id = surface_id; + internal->allocation_type = allocation_type; + + SurfaceAddRes(pdev, surface, surface_res); + RELEASE_RES(pdev, surface_res); +} + +static void FreePath(PDev *pdev, Resource *res) +{ + QXLPHYSICAL 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, pdev->main_mem_slot); + chunk_phys = chunk->next_chunk; + FreeMem(pdev, MSPACE_TYPE_DEVRAM, chunk); + ONDBG(pdev->num_path_pages--); + } + FreeMem(pdev, MSPACE_TYPE_DEVRAM, 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, MSPACE_TYPE_DEVRAM, size + sizeof(QXLDataChunk)); \ + ONDBG((*(page_counter))++); \ + chunk->next_chunk = PA(pdev, ptr, pdev->main_mem_slot); \ + ((QXLDataChunk *)ptr)->prev_chunk = PA(pdev, chunk, pdev->main_mem_slot); \ + 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(QXLPathSeg) +\ + 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; + QXLPathSeg *seg; + + more = PATHOBJ_bEnum(path, &data); + if (data.count == 0) { + break; + } + + if (end - now < sizeof(QXLPathSeg)) { + size_t alloc_size = MIN(data.count << 3, sizeof(POINTFIX) * PATH_MAX_ALLOC_PONTS); + alloc_size += sizeof(QXLPathSeg); + NEW_DATA_CHUNK(page_counter, alloc_size); + } + seg = (QXLPathSeg*)now; + seg->flags = data.flags; + seg->count = data.count; + now = (UINT8 *)seg->points; + 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 = (int)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, MSPACE_TYPE_DEVRAM, PATH_ALLOC_SIZE); + ONDBG(pdev->num_path_pages++); + res->refs = 1; + res->free = FreePath; + RESOURCE_TYPE(res, RESOURCE_TYPE_PATH); + + 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, QXLPHYSICAL *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, pdev->main_mem_slot); + DrawableAddRes(pdev, drawable, path_res); + RELEASE_RES(pdev, path_res); + return TRUE; +} + + +static void FreeClipRects(PDev *pdev, Resource *res) +{ + QXLPHYSICAL 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, pdev->main_mem_slot); + chunk_phys = chunk->next_chunk; + FreeMem(pdev, MSPACE_TYPE_DEVRAM, chunk); + ONDBG(pdev->num_rects_pages--); + } + FreeMem(pdev, MSPACE_TYPE_DEVRAM, 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(QXLRect) * RECTS_NUM_PREALLOC) +#define RECTS_NUM_ALLOC 20 +#define RECTS_CHUNK_ALLOC_SIZE (sizeof(QXLDataChunk) + sizeof(QXLRect) * RECTS_NUM_ALLOC) + +static Resource *GetClipRects(PDev *pdev, CLIPOBJ *clip) +{ + Resource *res; + QXLClipRects *rects; + QXLDataChunk *chunk; + QXLRect *dest; + QXLRect *dest_end; + int more; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + res = (Resource *)AllocMem(pdev, MSPACE_TYPE_DEVRAM, RECTS_ALLOC_SIZE); + ONDBG(pdev->num_rects_pages++); + res->refs = 1; + res->free = FreeClipRects; + RESOURCE_TYPE(res, RESOURCE_TYPE_CLIP_RECTS); + rects = (QXLClipRects *)res->res; + rects->num_rects = 0; + + chunk = &rects->chunk; + chunk->data_size = 0; + chunk->prev_chunk = 0; + chunk->next_chunk = 0; + + dest = (QXLRect *)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, MSPACE_TYPE_DEVRAM, RECTS_CHUNK_ALLOC_SIZE); + ONDBG(pdev->num_rects_pages++); + chunk->next_chunk = PA(pdev, page, pdev->main_mem_slot); + ((QXLDataChunk *)page)->prev_chunk = PA(pdev, chunk, pdev->main_mem_slot); + chunk = (QXLDataChunk *)page; + chunk->data_size = 0; + chunk->next_chunk = 0; + dest = (QXLRect *)chunk->data; + dest_end = dest + RECTS_NUM_ALLOC; + } + CopyRect(dest, now); + chunk->data_size += sizeof(QXLRect); + } + } 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 = SPICE_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, MSPACE_TYPE_DEVRAM, sizeof(Resource) + sizeof(QXLClipRects) + + sizeof(QXLRect)); + rects_res->refs = 1; + rects_res->free = FreeClipRects; + RESOURCE_TYPE(rects_res, RESOURCE_TYPE_CLIP_RECTS); + rects = (QXLClipRects *)rects_res->res; + rects->num_rects = 1; + rects->chunk.data_size = sizeof(QXLRect); + rects->chunk.prev_chunk = 0; + rects->chunk.next_chunk = 0; + CopyRect((QXLRect *)rects->chunk.data, &clip->rclBounds); + } else { + rects_res = GetClipRects(pdev, clip); + } + + DrawableAddRes(pdev, drawable, rects_res); + RELEASE_RES(pdev, rects_res); + drawable->clip.type = SPICE_CLIP_TYPE_RECTS; + drawable->clip.data = PA(pdev, rects_res->res, pdev->main_mem_slot); + DEBUG_PRINT((pdev, 10, "%s: done\n", __FUNCTION__)); + return TRUE; +} + +#ifndef _WIN64 + +static _inline void fast_memcpy_aligment(void *dest, const void *src, size_t len) +{ + _asm + { + mov ecx, len + mov esi, src + mov edi, dest + + cmp ecx, 128 + jb try_to_copy64 + + prefetchnta [esi] + copy_128: + prefetchnta [esi + 64] + + movdqa xmm0, [esi] + movdqa xmm1, [esi + 16] + movdqa xmm2, [esi + 32] + movdqa xmm3, [esi + 48] + + prefetchnta [esi + 128] + + movntdq [edi], xmm0 + movntdq [edi + 16], xmm1 + movntdq [edi + 32], xmm2 + movntdq [edi + 48], xmm3 + + movdqa xmm0, [esi + 64] + movdqa xmm1, [esi + 80] + movdqa xmm2, [esi + 96] + movdqa xmm3, [esi + 112] + + movntdq [edi + 64], xmm0 + movntdq [edi + 80], xmm1 + movntdq [edi + 96], xmm2 + movntdq [edi + 112], xmm3 + + add edi, 128 + add esi, 128 + sub ecx, 128 + cmp ecx, 128 + jae copy_128 + + try_to_copy64: + cmp ecx, 64 + jb try_to_copy32 + + movdqa xmm0, [esi] + movdqa xmm1, [esi + 16] + movdqa xmm2, [esi + 32] + movdqa xmm3, [esi + 48] + + movntdq [edi], xmm0 + movntdq [edi + 16], xmm1 + movntdq [edi + 32], xmm2 + movntdq [edi + 48], xmm3 + + add edi, 64 + add esi, 64 + sub ecx, 64 + prefetchnta [esi] + + try_to_copy32: + cmp ecx, 32 + jb try_to_copy16 + + movdqa xmm0, [esi] + movdqa xmm1, [esi + 16] + movntdq [edi], xmm0 + movntdq [edi + 16], xmm1 + + add edi, 32 + add esi, 32 + sub ecx, 32 + + try_to_copy16: + cmp ecx, 16 + jb try_to_copy4 + + movdqa xmm0, [esi] + movntdq [edi], xmm0 + + add edi, 16 + add esi, 16 + sub ecx, 16 + + + try_to_copy4: + cmp ecx, 4 + jb try_to_copy_1 + movsd + sub ecx, 4 + jmp try_to_copy4 + + try_to_copy_1: + rep movsb + + sfence + } +} + +static _inline void fast_memcpy_unaligment(void *dest, const void *src, size_t len) +{ + _asm + { + mov ecx, len + mov esi, src + mov edi, dest + + cmp ecx, 128 + jb try_to_copy64 + + prefetchnta [esi] + copy_128: + prefetchnta [esi + 64] + + movdqu xmm0, [esi] + movdqu xmm1, [esi + 16] + movdqu xmm2, [esi + 32] + movdqu xmm3, [esi + 48] + + prefetchnta [esi + 128] + + movntdq [edi], xmm0 + movntdq [edi + 16], xmm1 + movntdq [edi + 32], xmm2 + movntdq [edi + 48], xmm3 + + movdqu xmm0, [esi + 64] + movdqu xmm1, [esi + 80] + movdqu xmm2, [esi + 96] + movdqu xmm3, [esi + 112] + + movntdq [edi + 64], xmm0 + movntdq [edi + 80], xmm1 + movntdq [edi + 96], xmm2 + movntdq [edi + 112], xmm3 + + add edi, 128 + add esi, 128 + sub ecx, 128 + cmp ecx, 128 + jae copy_128 + + try_to_copy64: + cmp ecx, 64 + jb try_to_copy32 + + movdqu xmm0, [esi] + movdqu xmm1, [esi + 16] + movdqu xmm2, [esi + 32] + movdqu xmm3, [esi + 48] + + movntdq [edi], xmm0 + movntdq [edi + 16], xmm1 + movntdq [edi + 32], xmm2 + movntdq [edi + 48], xmm3 + + add edi, 64 + add esi, 64 + sub ecx, 64 + prefetchnta [esi] + + try_to_copy32: + cmp ecx, 32 + jb try_to_copy16 + + movdqu xmm0, [esi] + movdqu xmm1, [esi + 16] + movntdq [edi], xmm0 + movntdq [edi + 16], xmm1 + + add edi, 32 + add esi, 32 + sub ecx, 32 + + try_to_copy16: + cmp ecx, 16 + jb try_to_copy4 + + movdqu xmm0, [esi] + movntdq [edi], xmm0 + + add edi, 16 + add esi, 16 + sub ecx, 16 + + + try_to_copy4: + cmp ecx, 4 + jb try_to_copy_1 + movsd + sub ecx, 4 + jmp try_to_copy4 + + try_to_copy_1: + rep movsb + + sfence + } +} + +#endif + +#ifdef DBG + #define PutBytesAlign __PutBytesAlign +#define PutBytes(pdev, chunk, now, end, src, size, page_counter, alloc_size, use_sse)\ + __PutBytesAlign(pdev, chunk, now, end, src, size, page_counter, alloc_size, 1, use_sse) +#else +#define PutBytesAlign(pdev, chunk, now, end, src, size, page_counter, alloc_size, alignment, use_sse)\ + __PutBytesAlign(pdev, chunk, now, end, src, size, NULL, alloc_size, alignment, use_sse) +#define PutBytes(pdev, chunk, now, end, src, size, page_counter, alloc_size, use_sse)\ + __PutBytesAlign(pdev, chunk, now, end, src, size, NULL, alloc_size, 1, use_sse) +#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, BOOL use_sse) +{ + QXLDataChunk *chunk = *chunk_ptr; + UINT8 *now = *now_ptr; + UINT8 *end = *end_ptr; + int offset; + + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + while (size) { + int cp_size = (int)MIN(end - now, size); + if (!cp_size) { + size_t aligned_size; + ASSERT(pdev, alloc_size > 0); + ASSERT(pdev, BITS_BUF_MAX > alignment); + aligned_size = (int)MIN(alloc_size + alignment - 1, BITS_BUF_MAX); + aligned_size -= aligned_size % alignment; + NEW_DATA_CHUNK(page_counter, aligned_size); + cp_size = (int)MIN(end - now, size); + } +#ifndef _WIN64 + if (use_sse) { + offset = (size_t)now & SSE_MASK; + if (offset) { + offset = SSE_ALIGN - offset; + if (offset >= cp_size) { + RtlCopyMemory(now, src, cp_size); + src += cp_size; + now += cp_size; + chunk->data_size += cp_size; + size -= cp_size; + continue; + } + RtlCopyMemory(now, src, offset); + now += offset; + src += offset; + size -= offset; + cp_size -= offset; + chunk->data_size += offset; + } + + if (((size_t)src & SSE_MASK) == 0) { + fast_memcpy_aligment(now, src, cp_size); + } else { + fast_memcpy_unaligment(now, src, cp_size); + } + } else { + RtlCopyMemory(now, src, cp_size); + } +#else + RtlCopyMemory(now, src, cp_size); +#endif + 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) + +static void ImageKeyPut(PDev *pdev, HSURF hsurf, UINT64 unique, UINT32 key) +{ + ImageKey *image_key; + + if (!unique) { + return; + } + image_key = &pdev->image_key_lookup[IMAGE_KEY_HASH_VAL(hsurf)]; + image_key->hsurf = hsurf; + image_key->unique = unique; + image_key->key = key; +} + +static BOOL ImageKeyGet(PDev *pdev, HSURF hsurf, UINT64 unique, UINT32 *key) +{ + ImageKey *image_key; + BOOL res = FALSE; + + 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; + res = TRUE; + } + return res; +} + +#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; + + 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))) { + break; + } + cache_image = cache_image->next; + } + return cache_image; +} + +static void ImageCacheAdd(PDev *pdev, CacheImage *cache_image) +{ + int key; + + 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; + break; + } + cache_img = &(*cache_img)->next; + } +} + +static CacheImage *AllocCacheImage(PDev* pdev) +{ + RingItem *item; + while (!(item = RingGetTail(pdev, &pdev->cache_image_lru))) { + /* malloc_sem protects release_ring too */ + EngAcquireSemaphore(pdev->malloc_sem); + if (pdev->free_outputs == 0 && + SPICE_RING_IS_EMPTY(pdev->release_ring)) { + WaitForReleaseRing(pdev); + } + FlushReleaseRing(pdev); + EngReleaseSemaphore(pdev->malloc_sem); + } + 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; + QXLPalette 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, MSPACE_TYPE_DEVRAM, palette); + } +} + +static _inline void PaletteCacheRemove(PDev *pdev, InternalPalette *palette) +{ + InternalPalette **internal; + BOOL found = FALSE; + + 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; + found = TRUE; + break; + } + internal = &(*internal)->next; + } + + RingRemove(pdev, &palette->lru_link); + ReleasePalette(pdev, palette); + pdev->num_palettes--; + + if (!found) { + DEBUG_PRINT((pdev, 0, "%s: Error: palette 0x%x isn't in cache \n", __FUNCTION__, palette)); + ASSERT(pdev, FALSE); + } else { + DEBUG_PRINT((pdev, 16, "%s: done\n", __FUNCTION__)); + } +} + +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 void PaletteCacheClear(PDev *pdev) +{ + DEBUG_PRINT((pdev, 1, "%s\n", __FUNCTION__)); + while(pdev->num_palettes) { + ASSERT(pdev, RingGetTail(pdev, &pdev->palette_lru)); + PaletteCacheRemove(pdev, CONTAINEROF(RingGetTail(pdev, &pdev->palette_lru), + InternalPalette, lru_link)); + } +} + +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, QXLBitmap *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, pdev->main_mem_slot); + return; + } + + internal = (InternalPalette *)AllocMem(pdev, MSPACE_TYPE_DEVRAM, sizeof(InternalPalette) + + (color_trans->cEntries << 2)); + internal->refs = 1; + RingItemInit(&internal->lru_link); + bitmap->palette = PA(pdev, &internal->palette, pdev->main_mem_slot); + 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; + QXLPHYSICAL 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, pdev->main_mem_slot); + chunk_phys = chunk->next_chunk; + FreeMem(pdev, MSPACE_TYPE_DEVRAM, chunk); + ONDBG(pdev->num_bits_pages--); + + } + FreeMem(pdev, MSPACE_TYPE_DEVRAM, res); + ONDBG(pdev->num_bits_pages--); + DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); +} + +static _inline QuicImageType GetQuicImageType(UINT8 format) +{ + switch (format) { + case SPICE_BITMAP_FMT_32BIT: + return QUIC_IMAGE_TYPE_RGB32; + case SPICE_BITMAP_FMT_16BIT: + return QUIC_IMAGE_TYPE_RGB16; + case SPICE_BITMAP_FMT_RGBA: + return QUIC_IMAGE_TYPE_RGBA; + case SPICE_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, MSPACE_TYPE_DEVRAM, sizeof(QXLDataChunk) + alloc_size); + new_chank->data_size = 0; + new_chank->prev_chunk = PA(pdev, usr_data->chunk, pdev->main_mem_slot); + 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, pdev->main_mem_slot); + 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; + } + + EngAcquireSemaphore(pdev->quic_data_sem); + + 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, MSPACE_TYPE_DEVRAM, alloc_size); + ONDBG(pdev->num_bits_pages++); + image_res->refs = 1; + image_res->free = FreeQuicImage; + RESOURCE_TYPE(image_res, RESOURCE_TYPE_QUIC_IMAGE); + + internal = (InternalImage *)image_res->res; + SetImageId(internal, cache_me, width, height, format, key); + internal->image.descriptor.type = SPICE_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 = (int)(((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__)); + image_res = NULL; + goto out; + } + + 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)); + + out: + EngReleaseSemaphore(pdev->quic_data_sem); + + return image_res; +} + +static void FreeBitmapImage(PDev *pdev, Resource *res) // todo: defer +{ + InternalImage *internal; + QXLPHYSICAL 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) { + QXLPalette *palette = (QXLPalette *)VA(pdev, internal->image.bitmap.palette, + pdev->main_mem_slot); + 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, pdev->main_mem_slot); + chunk_phys = chunk->next_chunk; + FreeMem(pdev, MSPACE_TYPE_DEVRAM, chunk); + ONDBG(pdev->num_bits_pages--); + + } + + FreeMem(pdev, MSPACE_TYPE_DEVRAM, res); + ONDBG(pdev->num_bits_pages--); + DEBUG_PRINT((pdev, 13, "%s: done\n", __FUNCTION__)); +} + +#ifndef _WIN64 + +static _inline void RestoreFPU(PDev *pdev, UINT8 FPUSave[]) +{ + void *align_addr = (void *)ALIGN((size_t)(FPUSave), SSE_ALIGN); + + _asm + { + mov esi, align_addr + + movdqa xmm0, [esi] + movdqa xmm1, [esi + 16] + movdqa xmm2, [esi + 32] + movdqa xmm3, [esi + 48] + } +} + +static _inline void SaveFPU(PDev *pdev, UINT8 FPUSave[]) +{ + void *align_addr = (void *)ALIGN((size_t)(FPUSave), SSE_ALIGN); + + _asm + { + mov edi, align_addr + + movdqa [edi], xmm0 + movdqa [edi + 16], xmm1 + movdqa [edi + 32], xmm2 + movdqa [edi + 48], xmm3 + } +} + +#endif + +static void FreeSurfaceImage(PDev *pdev, Resource *res) +{ + DEBUG_PRINT((pdev, 12, "%s\n", __FUNCTION__)); + + FreeMem(pdev, MSPACE_TYPE_DEVRAM, res); + + 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; + UINT8 FPUSave[16 * 4 + 15]; + BOOL use_sse = FALSE; + + 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, MSPACE_TYPE_DEVRAM, alloc_size); + ONDBG(pdev->num_bits_pages++); + + image_res->refs = 1; + image_res->free = FreeBitmapImage; + RESOURCE_TYPE(image_res, RESOURCE_TYPE_BITMAP_IMAGE); + + internal = (InternalImage *)image_res->res; + SetImageId(internal, cache_me, width, height, format, key); + internal->image.descriptor.type = SPICE_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, pdev->main_mem_slot); + 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; + +#ifndef _WIN64 + if (have_sse2 && alloc_size >= 1024) { + use_sse = TRUE; + SaveFPU(pdev, FPUSave); + } +#endif + 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, use_sse); + } +#ifndef _WIN64 + if (use_sse) { + RestoreFPU(pdev, FPUSave); + } +#endif + + 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, int high_bits_set, + 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 = murmurhash2a(color_trans->pulXlate, + sizeof(*color_trans->pulXlate) * color_trans->cEntries, + hash_value); + } + + if (format == SPICE_BITMAP_FMT_32BIT && stride == line_size) { + hash_value = murmurhash2ajump3((UINT32 *)row_buf, width * height, hash_value); + } else { + for (row = 0; row < height; row++) { + #ifdef ADAPTIVE_HASH + if (format == SPICE_BITMAP_FMT_32BIT) { + hash_value = murmurhash2ajump3((UINT32 *)row_buf, width, hash_value); + } else { + if (format == SPICE_BITMAP_FMT_4BIT_BE && (width & 0x1)) { + last_byte = row_buf[line_size - 1] & 0xF0; + } else if (format == SPICE_BITMAP_FMT_1BIT_BE && (reminder = width & 0x7)) { + last_byte = row_buf[line_size - 1] & ~((1 << (8 - reminder)) - 1); + } + if (last_byte) { + hash_value = murmurhash2a(row_buf, line_size - 1, hash_value); + hash_value = murmurhash2a(&last_byte, 1, hash_value); + } else { + hash_value = murmurhash2a(row_buf, line_size, hash_value); + } + } + #else + hash_value = murmurhash2a(row_buf, line_size, hash_value); + #endif + row_buf += stride; + } + } + if (high_bits_set) { + hash_value ^= 1; + } + return hash_value; +} + +static _inline UINT32 GetFormatLineSize(INT32 width, ULONG bitmap_format, UINT8 *format) +{ + switch (bitmap_format) { + case BMF_32BPP: + *format = SPICE_BITMAP_FMT_32BIT; + return width << 2; + case BMF_24BPP: + *format = SPICE_BITMAP_FMT_24BIT; + return width * 3; + case BMF_16BPP: + *format = SPICE_BITMAP_FMT_16BIT; + return width << 1; + case BMF_8BPP: + *format = SPICE_BITMAP_FMT_8BIT; + return width; + case BMF_4BPP: + *format = SPICE_BITMAP_FMT_4BIT_BE; + return ALIGN(width, 2) >> 1; + case BMF_1BPP: + *format = SPICE_BITMAP_FMT_1BIT_BE; + return ALIGN(width, 8) >> 3; + default: + return 0; + } +} + +static BOOL CacheSizeTest(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 _inline UINT64 get_unique(SURFOBJ *surf, XLATEOBJ *color_trans) +{ + ULONG pallette_unique = color_trans ? 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_unique) { + return 0; + } else { + return (surf->iUniq | ((UINT64)pallette_unique << 32)); + } +} + +BOOL QXLCheckIfCacheImage(PDev *pdev, SURFOBJ *surf, XLATEOBJ *color_trans) +{ + CacheImage *cache_image; + UINT64 gdi_unique; + UINT32 key; + UINT8 format; + + gdi_unique = get_unique(surf, color_trans); + + if (!ImageKeyGet(pdev, surf->hsurf, gdi_unique, &key)) { + return FALSE; + } + + switch (surf->iBitmapFormat) { + case BMF_32BPP: + format = SPICE_BITMAP_FMT_32BIT; + break; + case BMF_24BPP: + format = SPICE_BITMAP_FMT_24BIT; + break; + case BMF_16BPP: + format = SPICE_BITMAP_FMT_16BIT; + break; + case BMF_8BPP: + format = SPICE_BITMAP_FMT_8BIT; + break; + case BMF_4BPP: + format = SPICE_BITMAP_FMT_4BIT_BE; + break; + case BMF_1BPP: + format = SPICE_BITMAP_FMT_1BIT_BE; + } + + + if ((cache_image = ImageCacheGetByKey(pdev, key, TRUE, format, + surf->sizlBitmap.cx, + surf->sizlBitmap.cy))) { + return TRUE; + } + + return FALSE; +} + +static CacheImage *GetCacheImage(PDev *pdev, SURFOBJ *surf, XLATEOBJ *color_trans, int high_bits_set, UINT32 *hash_key) +{ + CacheImage *cache_image; + UINT64 gdi_unique; + UINT32 key; + UINT8 format; + UINT32 line_size; + + gdi_unique = get_unique(surf, color_trans); + + 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, + high_bits_set, 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))) { + cache_image->hits++; + DEBUG_PRINT((pdev, 11, "%s: ImageCacheGetByKey %u hits %u\n", __FUNCTION__, + key, cache_image->hits)); + return cache_image; + } + + if (CacheSizeTest(pdev, surf)) { + CacheImage *cache_image; + 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; +} + +// TODO: reconsider +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; +} + +static int rgb32_data_has_alpha(int width, int height, int stride, + UINT8 *data, int *all_set_out) +{ + UINT32 *line, *end, alpha; + int has_alpha; + + has_alpha = FALSE; + while (height-- > 0) { + line = (UINT32 *)data; + end = line + width; + data += stride; + while (line != end) { + alpha = *line & 0xff000000U; + if (alpha != 0) { + has_alpha = TRUE; + if (alpha != 0xff000000U) { + *all_set_out = FALSE; + return TRUE; + } + } + line++; + } + } + + *all_set_out = has_alpha; + return has_alpha; +} + +BOOL QXLGetBitmap(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *image_phys, SURFOBJ *surf, + QXLRect *area, XLATEOBJ *color_trans, UINT32 *hash_key, BOOL use_cache, + INT32 *surface_dest) +{ + Resource *image_res; + InternalImage *internal; + CacheImage *cache_image; + UINT32 key; + UINT8 format; + UINT32 line_size; + UINT8 *src; + int high_bits_set; + 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) { + UINT32 alloc_size; + + DEBUG_PRINT((pdev, 9, "%s: copy from device\n", __FUNCTION__)); + + alloc_size = sizeof(Resource) + sizeof(InternalImage); + image_res = AllocMem(pdev, MSPACE_TYPE_DEVRAM, alloc_size); + + ONDBG(pdev->num_bits_pages++); + image_res->refs = 1; + image_res->free = FreeSurfaceImage; + RESOURCE_TYPE(image_res, RESOURCE_TYPE_SURFACE_IMAGE); + + internal = (InternalImage *)image_res->res; + + SetImageId(internal, FALSE, 0, 0, 0, 0); + internal->image.descriptor.type = SPICE_IMAGE_TYPE_SURFACE; + internal->image.descriptor.width = 0; + internal->image.descriptor.height = 0; + *surface_dest = internal->image.surface_image.surface_id = GetSurfaceId(surf); + + *image_phys = PA(pdev, &internal->image, pdev->main_mem_slot); + + DrawableAddRes(pdev, drawable, image_res); + + RELEASE_RES(pdev, image_res); + + return TRUE; + } + + 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; + } + + high_bits_set = FALSE; + if (surf->iBitmapFormat == BMF_32BPP) { + if (rgb32_data_has_alpha(width, height, surf->lDelta, + (UINT8 *)surf->pvScan0 + area->left * 4, + &high_bits_set) && + !high_bits_set) { + return QXLGetAlphaBitmap(pdev, drawable, image_phys, + surf, area, surface_dest); + } + } + + 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 = GetCacheImage(pdev, surf, color_trans, high_bits_set, 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, pdev->main_mem_slot); + 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 (high_bits_set) { + internal->image.descriptor.flags |= QXL_IMAGE_HIGH_BITS_SET; + } + 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, pdev->main_mem_slot); + DrawableAddRes(pdev, drawable, image_res); + RELEASE_RES(pdev, image_res); + return TRUE; +} + +BOOL QXLGetAlphaBitmap(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *image_phys, + SURFOBJ *surf, QXLRect *area, INT32 *surface_dest) +{ + 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, 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)); + + if (surf->iType != STYPE_BITMAP) { + UINT32 alloc_size; + + DEBUG_PRINT((pdev, 9, "%s: copy from device\n", __FUNCTION__)); + + alloc_size = sizeof(Resource) + sizeof(InternalImage); + image_res = AllocMem(pdev, MSPACE_TYPE_DEVRAM, alloc_size); + + ONDBG(pdev->num_bits_pages++); + image_res->refs = 1; + image_res->free = FreeSurfaceImage; + RESOURCE_TYPE(image_res, RESOURCE_TYPE_SURFACE_IMAGE); + + internal = (InternalImage *)image_res->res; + + SetImageId(internal, FALSE, 0, 0, 0, 0); + internal->image.descriptor.type = SPICE_IMAGE_TYPE_SURFACE; + internal->image.descriptor.width = 0; + internal->image.descriptor.height = 0; + *surface_dest = internal->image.surface_image.surface_id = GetSurfaceId(surf); + + *image_phys = PA(pdev, &internal->image, pdev->main_mem_slot); + DrawableAddRes(pdev, drawable, image_res); + RELEASE_RES(pdev, image_res); + + return TRUE; + } + + ASSERT(pdev, surf->iBitmapFormat == BMF_32BPP && surf->iType == STYPE_BITMAP); + + //todo: use GetCacheImage + + // 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, SPICE_BITMAP_FMT_RGBA, + FALSE, 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, SPICE_BITMAP_FMT_RGBA, + surf->sizlBitmap.cx, surf->sizlBitmap.cy)) { + DEBUG_PRINT((pdev, 11, "%s: ImageCacheGetByKey %u hits %u\n", __FUNCTION__, + key, cache_image->hits)); + 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, pdev->main_mem_slot); + image_res = (Resource *)((UINT8 *)internal - sizeof(Resource)); + DrawableAddRes(pdev, drawable, image_res); + return TRUE; + } + } else if (CacheSizeTest(pdev, surf)) { + CacheImage *cache_image; + cache_image = AllocCacheImage(pdev); + ImageCacheRemove(pdev, cache_image); + cache_image->key = key; + cache_image->image = NULL; + cache_image->format = SPICE_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, + SPICE_BITMAP_FMT_RGBA, src, width << 2, key))) { + image_res = GetBitmapImage(pdev, surf, NULL, !!cache_image, width, height, + SPICE_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, pdev->main_mem_slot); + DrawableAddRes(pdev, drawable, image_res); + RELEASE_RES(pdev, image_res); + return TRUE; +} + +BOOL QXLGetBitsFromCache(PDev *pdev, QXLDrawable *drawable, UINT32 hash_key, QXLPHYSICAL *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)) { + cache_image->hits++; + *image_phys = PA(pdev, &internal->image, pdev->main_mem_slot); + image_res = (Resource *)((UINT8 *)internal - sizeof(Resource)); + DrawableAddRes(pdev, drawable, image_res); + return TRUE; + } + return FALSE; +} + +BOOL QXLGetMask(PDev *pdev, QXLDrawable *drawable, QXLQMask *qxl_mask, SURFOBJ *mask, POINTL *pos, + BOOL invers, LONG width, LONG height, INT32 *surface_dest) +{ + QXLRect 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 ? SPICE_MASK_FLAGS_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, + surface_dest)) { + 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, MSPACE_TYPE_DEVRAM, res); +} + +UINT8 *QXLGetBuf(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *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, MSPACE_TYPE_DEVRAM, sizeof(Resource) + size); + ONDBG(pdev->num_buf_pages++); + buf_res->refs = 1; + buf_res->free = FreeBuf; + RESOURCE_TYPE(buf_res, RESOURCE_TYPE_BUF); + + *buf_phys = PA(pdev, buf_res->res, pdev->main_mem_slot); + DrawableAddRes(pdev, drawable, buf_res); + RELEASE_RES(pdev, buf_res); + return buf_res->res; +} + +#ifdef UPDATE_CMD +void UpdateArea(PDev *pdev, RECTL *area, UINT32 surface_id) +{ + QXLCommand *cmd; + QXLOutput *output; + QXLUpdateCmd *updat_cmd; + + DEBUG_PRINT((pdev, 12, "%s UPDATE_CMD\n", __FUNCTION__)); + + output = (QXLOutput *)AllocMem(pdev, sizeof(QXLOutput) + sizeof(QXLUpdateCmd)); + RESOURCE_TYPE(output, RESOURCE_TYPE_UPDATE); + 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; + updat_cmd->surface_id = surface_id; + + EngAcquireSemaphore(pdev->cmd_sem); + WaitForCmdRing(pdev); + cmd = SPICE_RING_PROD_ITEM(pdev->cmd_ring); + cmd->type = QXL_CMD_UPDATE; + cmd->data = PA(pdev, updat_cmd, pdev->main_mem_slot); + PUSH_CMD(pdev); + EngReleaseSemaphore(pdev->cmd_sem); + do { +#ifdef DBG + { + LARGE_INTEGER timeout; // 1 => 100 nanoseconds + timeout.QuadPart = -1 * (1000 * 1000 * 10); //negative => relative // 1s + WAIT_FOR_EVENT(pdev, pdev->display_event, &timeout); + if (*pdev->dev_update_id != pdev->update_id) { + DEBUG_PRINT((pdev, 0, "%s: 0x%lx: timeout\n", __FUNCTION__, pdev)); + } + } +#else + WAIT_FOR_EVENT(pdev, pdev->display_event, NULL); +#endif // DEBUG + mb(); + } while (*pdev->dev_update_id != pdev->update_id); +} + +#else + +void UpdateArea(PDev *pdev, RECTL *area, UINT32 surface_id) +{ + DEBUG_PRINT((pdev, 12, "%s IO\n", __FUNCTION__)); + CopyRect(pdev->update_area, area); + *pdev->update_surface = surface_id; + async_io(pdev, ASYNCABLE_UPDATE_AREA, 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, QXLPoint **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++) { + QXLRasterGlyph *glyph; + UINT8 *line; + UINT8 *end_line; + UINT32 stride; + + if (end - now < sizeof(*glyph)) { + NEW_DATA_CHUNK(&pdev->num_glyphs_pages, PAGE_SIZE); + } + + glyph = (QXLRasterGlyph *)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 = (QXLPoint *)&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 BOOL add_glyphs(PDev *pdev, QXLString *str, ULONG count, GLYPHPOS *glyps, + QXLDataChunk **chunk, UINT8 **now, UINT8 **end, POINTL *delta, + QXLPoint **str_pos) +{ + if (str->flags & SPICE_STRING_FLAGS_RASTER_A1) { + add_rast_glyphs(pdev, str, count, glyps, chunk, now, end, 1, delta, str_pos); + } else if (str->flags & SPICE_STRING_FLAGS_RASTER_A4) { + add_rast_glyphs(pdev, str, count, glyps, chunk, now, end, 4, delta, str_pos); + } + return TRUE; +} + +static void FreeSring(PDev *pdev, Resource *res) +{ + QXLPHYSICAL 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, pdev->main_mem_slot); + chunk_phys = chunk->next_chunk; + FreeMem(pdev, MSPACE_TYPE_DEVRAM, chunk); + ONDBG(pdev->num_glyphs_pages--); + } + + FreeMem(pdev, MSPACE_TYPE_DEVRAM, 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, QXLPHYSICAL *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; + QXLPoint *str_pos; + + DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__)); + + str_res = (Resource *)AllocMem(pdev, MSPACE_TYPE_DEVRAM, TEXT_ALLOC_SIZE); + ONDBG(pdev->num_glyphs_pages++); + str_res->refs = 1; + str_res->free = FreeSring; + RESOURCE_TYPE(str_res, RESOURCE_TYPE_SRING); + + 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) ? SPICE_STRING_FLAGS_RASTER_A4 : + SPICE_STRING_FLAGS_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, pdev->main_mem_slot); + 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, MSPACE_TYPE_DEVRAM, sizeof(QXLOutput) + sizeof(QXLCursorCmd)); + output->num_res = 0; + RESOURCE_TYPE(output, RESOURCE_TYPE_CURSOR); + 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__)); + EngAcquireSemaphore(pdev->cursor_sem); + WaitForCursorRing(pdev); + cmd = SPICE_RING_PROD_ITEM(pdev->cursor_ring); + cmd->type = QXL_CMD_CURSOR; + cmd->data = PA(pdev, cursor_cmd, pdev->main_mem_slot); + PUSH_CURSOR_CMD(pdev); + EngReleaseSemaphore(pdev->cursor_sem); + 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; + BOOL found = FALSE; + + 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; + found = TRUE; + break; + } + DEBUG_PRINT((pdev, 0, "%s: unexpected\n", __FUNCTION__)); + } + internal = &(*internal)->next; + } + + RingRemove(pdev, &cursor->lru_link); + RELEASE_RES(pdev, (Resource *)((UINT8 *)cursor - sizeof(Resource))); + pdev->num_cursors--; + + if (!found) { + DEBUG_PRINT((pdev, 0, "%s: Error: cursor 0x%x isn't in cache \n", __FUNCTION__, cursor)); + ASSERT(pdev, FALSE); + } else { + DEBUG_PRINT((pdev, 16, "%s: done\n", __FUNCTION__)); + } + +} + +static void CursorCacheClear(PDev *pdev) +{ + DEBUG_PRINT((pdev, 1, "%s\n", __FUNCTION__)); + while (pdev->num_cursors) { + ASSERT(pdev, RingGetTail(pdev, &pdev->cursors_lru)); + CursorCacheRemove(pdev, CONTAINEROF(RingGetTail(pdev, &pdev->cursors_lru), + InternalCursor, lru_link)); + } +} + +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) +{ + QXLPHYSICAL 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, pdev->main_mem_slot); + chunk_phys = chunk->next_chunk; + FreeMem(pdev, MSPACE_TYPE_DEVRAM, chunk); + ONDBG(pdev->num_cursor_pages--); + } + + FreeMem(pdev, MSPACE_TYPE_DEVRAM, 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, BOOL *in_cach) +{ + InternalCursor *internal; + QXLCursor *cursor; + Resource *res; + ULONG unique; + UINT8 *src; + UINT8 *src_end; + int line_size; + HSURF bitmap = 0; + SURFOBJ *local_surf = surf; + + DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__)); + *in_cach = FALSE; + 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, pdev->main_mem_slot); + *in_cach = TRUE; + return TRUE; + } + + if (surf->iType != STYPE_BITMAP) { + RECTL dest_rect; + POINTL src_pos; + ASSERT(pdev, surf->iBitmapFormat == BMF_32BPP || surf->iBitmapFormat == BMF_16BPP); + + /* copying the surface to a bitmap */ + + bitmap = (HSURF)EngCreateBitmap(surf->sizlBitmap, surf->lDelta, surf->iBitmapFormat, + 0, NULL); + if (!bitmap) { + 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 (!(local_surf = EngLockSurface(bitmap))) { + DEBUG_PRINT((pdev, 0, "%s: EngLockSurface failed\n", __FUNCTION__)); + goto error; + } + + dest_rect.top = 0; + dest_rect.left = 0; + dest_rect.bottom = surf->sizlBitmap.cy; + dest_rect.right = surf->sizlBitmap.cx; + + src_pos.x = 0; + src_pos.y = 0; + + if (!BitBltFromDev(pdev, surf, local_surf, NULL, NULL, NULL, &dest_rect, src_pos, + NULL, NULL, NULL, 0xcccc)) { + goto error; + } + } + + ASSERT(pdev, sizeof(Resource) + sizeof(InternalCursor) < CURSOR_ALLOC_SIZE); + res = (Resource *)AllocMem(pdev, MSPACE_TYPE_DEVRAM, CURSOR_ALLOC_SIZE); + ONDBG(pdev->num_cursor_pages++); + res->refs = 1; + res->free = FreeCursor; + RESOURCE_TYPE(res, RESOURCE_TYPE_CURSOR); + + 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)local_surf->sizlBitmap.cx; + cursor->header.height = (type == SPICE_CURSOR_TYPE_MONO) ? (UINT16)local_surf->sizlBitmap.cy >> 1 : + (UINT16)local_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 SPICE_CURSOR_TYPE_ALPHA: + case SPICE_CURSOR_TYPE_COLOR32: + line_size = cursor->header.width << 2; + break; + case SPICE_CURSOR_TYPE_MONO: + line_size = ALIGN(cursor->header.width, 8) >> 3; + break; + case SPICE_CURSOR_TYPE_COLOR4: + line_size = ALIGN(cursor->header.width, 2) >> 1; + break; + case SPICE_CURSOR_TYPE_COLOR8: + line_size = cursor->header.width; + break; + case SPICE_CURSOR_TYPE_COLOR16: + line_size = cursor->header.width << 1; + break; + case SPICE_CURSOR_TYPE_COLOR24: + line_size = cursor->header.width * 3; + break; + } + + cursor->data_size = line_size * local_surf->sizlBitmap.cy; + src = local_surf->pvScan0; + src_end = src + (local_surf->lDelta * local_surf->sizlBitmap.cy); + for (; src != src_end; src += local_surf->lDelta) { + PutBytes(pdev, &info->chunk, &info->now, &info->end, src, line_size, + &pdev->num_cursor_pages, PAGE_SIZE, FALSE); + } + + CursorCacheAdd(pdev, internal); + CursorCmdAddRes(pdev, cmd, res); + RELEASE_RES(pdev, res); + cmd->u.set.shape = PA(pdev, &internal->cursor, pdev->main_mem_slot); + DEBUG_PRINT((pdev, 11, "%s: done, data_size %u\n", __FUNCTION__, cursor->data_size)); + + if (local_surf != surf) { + EngUnlockSurface(local_surf); + EngDeleteSurface(bitmap); + } + + return TRUE; +error: + if (bitmap) { + ASSERT(pdev, local_surf != surf); + EngDeleteSurface(bitmap); + } + + return FALSE; +} + +BOOL GetAlphaCursor(PDev *pdev, QXLCursorCmd *cmd, LONG hot_x, LONG hot_y, SURFOBJ *surf) +{ + NewCursorInfo info; + BOOL ret; + BOOL in_cache; + + ASSERT(pdev, surf->iBitmapFormat == BMF_32BPP); + ASSERT(pdev, surf->sizlBitmap.cx > 0 && surf->sizlBitmap.cy > 0); + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + ret = GetCursorCommon(pdev, cmd, hot_x, hot_y, surf, SPICE_CURSOR_TYPE_ALPHA, &info, &in_cache); + DEBUG_PRINT((pdev, 8, "%s: done\n", __FUNCTION__)); + return ret; +} + +BOOL GetMonoCursor(PDev *pdev, QXLCursorCmd *cmd, LONG hot_x, LONG hot_y, SURFOBJ *surf) +{ + NewCursorInfo info; + BOOL ret; + BOOL in_cache; + + 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__)); + + ret = GetCursorCommon(pdev, cmd, hot_x, hot_y, surf, SPICE_CURSOR_TYPE_MONO, &info, &in_cache); + DEBUG_PRINT((pdev, 8, "%s: done\n", __FUNCTION__)); + return ret; +} + +BOOL GetColorCursor(PDev *pdev, QXLCursorCmd *cmd, LONG hot_x, LONG hot_y, SURFOBJ *surf, + SURFOBJ *mask, XLATEOBJ *color_trans) +{ + NewCursorInfo info; + UINT16 type; + BOOL in_cache; + + 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 = SPICE_CURSOR_TYPE_COLOR32; + break; + case BMF_24BPP: + type = SPICE_CURSOR_TYPE_COLOR24; + break; + case BMF_16BPP: + type = SPICE_CURSOR_TYPE_COLOR16; + break; + case BMF_8BPP: + type = SPICE_CURSOR_TYPE_COLOR8; + break; + case BMF_4BPP: + type = SPICE_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, &in_cache)) { + return FALSE; + } + + if (!in_cache) { + int line_size; + UINT8 *src; + UINT8 *src_end; + + if (type == SPICE_CURSOR_TYPE_COLOR8) { + + DEBUG_PRINT((pdev, 8, "%s: SPICE_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, FALSE); + } 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, FALSE); + } + } + info.cursor->data_size += 256 << 2; + } else if (type == SPICE_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, FALSE); + } 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, FALSE); + } + } + info.cursor->data_size += 16 << 2; + } + + ASSERT(pdev, mask->iBitmapFormat == BMF_1BPP); + ASSERT(pdev, mask->iType == STYPE_BITMAP); + + 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, FALSE); + } + } + + 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, MSPACE_TYPE_DEVRAM, sizeof(Resource) + sizeof(InternalCursor)); + ONDBG(pdev->num_cursor_pages++); + res->refs = 1; + res->free = FreeCursor; + RESOURCE_TYPE(res, RESOURCE_TYPE_CURSOR); + + internal = (InternalCursor *)res->res; + internal->hsurf = NULL; + internal->unique = 0; + RingItemInit(&internal->lru_link); + + cursor = &internal->cursor; + cursor->header.type = SPICE_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, pdev->main_mem_slot); + + DEBUG_PRINT((pdev, 8, "%s: done\n", __FUNCTION__)); + return TRUE; +} + +void ReleaseCacheDeviceMemoryResources(PDev *pdev) +{ + DEBUG_PRINT((pdev, 0, "%s \n", __FUNCTION__)); + PaletteCacheClear(pdev); + CursorCacheClear(pdev); +} + +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; + pdev->quic_data_sem = EngCreateSemaphore(); + if (!pdev->quic_data_sem) { + PANIC(pdev, "quic_data_sem creation failed\n"); + } + pdev->io_sem = EngCreateSemaphore(); + if (!pdev->io_sem) { + PANIC(pdev, "io_sem creation failed\n"); + } + + return TRUE; +} + +void ResDestroy(PDev *pdev) +{ + QuicData *usr_data = pdev->quic_data; + quic_destroy(usr_data->quic); + EngDeleteSemaphore(pdev->quic_data_sem); + 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; +} + +#ifndef _WIN64 + +void CheckAndSetSSE2() +{ + _asm + { + mov eax, 0x0000001 + cpuid + and edx, 0x4000000 + mov have_sse2, edx + } + + if (have_sse2) { + have_sse2 = TRUE; + } +} + +#endif diff --git a/xddm/display/res.h b/xddm/display/res.h new file mode 100644 index 0000000..d69986e --- /dev/null +++ b/xddm/display/res.h @@ -0,0 +1,77 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This software is licensed under the GNU General Public License, + version 2 (GPLv2) (see COPYING for details), subject to the + following clarification. + + With respect to binaries built using the Microsoft(R) Windows + Driver Kit (WDK), GPLv2 does not extend to any code contained in or + derived from the WDK ("WDK Code"). As to WDK Code, by using or + distributing such binaries you agree to be bound by the Microsoft + Software License Terms for the WDK. All WDK Code is considered by + the GPLv2 licensors to qualify for the special exception stated in + section 3 of GPLv2 (commonly known as the system library + exception). + + There is NO WARRANTY for this software, express or implied, + including the implied warranties of NON-INFRINGEMENT, TITLE, + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#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, UINT32 surface_id); +void PushDrawable(PDev *pdev, QXLDrawable *drawable); +QXLSurfaceCmd *SurfaceCmd(PDev *pdev, UINT8 type, UINT32 surface_id); +void PushSurfaceCmd(PDev *pdev, QXLSurfaceCmd *surface_cmd); + +QXLPHYSICAL SurfaceToPhysical(PDev *pdev, UINT8 *base_mem); +void QXLGetSurface(PDev *pdev, QXLPHYSICAL *surface_phys, UINT32 x, UINT32 y, UINT32 depth, + INT32 *stride, UINT8 **base_mem, UINT8 allocation_type); +void QXLGetDelSurface(PDev *pdev, QXLSurfaceCmd *surface, UINT32 surface_id, UINT8 allocation_type); +void QXLDelSurface(PDev *pdev, UINT8 *base_mem, UINT8 allocation_type); +BOOL QXLGetPath(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *path_phys, PATHOBJ *path); +BOOL QXLGetMask(PDev *pdev, QXLDrawable *drawable, QXLQMask *qxl_mask, SURFOBJ *mask, POINTL *pos, + BOOL invers, LONG width, LONG height, INT32 *surface_dest); +BOOL QXLGetBrush(PDev *pdev, QXLDrawable *drawable, QXLBrush *qxl_brush, + BRUSHOBJ *brush, POINTL *brush_pos, INT32 *surface_dest, + QXLRect *surface_rect); +BOOL QXLGetBitmap(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *image_phys, SURFOBJ *surf, + QXLRect *area, XLATEOBJ *color_trans, UINT32 *hash_key, BOOL use_cache, + INT32 *surface_dest); +BOOL QXLGetBitsFromCache(PDev *pdev, QXLDrawable *drawable, UINT32 hash_key, QXLPHYSICAL *image_phys); +BOOL QXLGetAlphaBitmap(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *image_phys, SURFOBJ *surf, + QXLRect *area, INT32 *surface_dest); +BOOL QXLCheckIfCacheImage(PDev *pdev, SURFOBJ *surf, XLATEOBJ *color_trans); +UINT8 *QXLGetBuf(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *buf_phys, UINT32 size); +BOOL QXLGetStr(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *str_phys, FONTOBJ *font, STROBJ *str); + +void UpdateArea(PDev *pdev, RECTL *area, UINT32 surface_id); + +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(); +#ifndef _WIN64 +void CheckAndSetSSE2(); +#endif +void EmptyReleaseRing(PDev *pdev); +void InitDeviceMemoryResources(PDev *pdev); +void ReleaseCacheDeviceMemoryResources(PDev *pdev); + +#endif diff --git a/xddm/display/rop.c b/xddm/display/rop.c new file mode 100644 index 0000000..9fb3527 --- /dev/null +++ b/xddm/display/rop.c @@ -0,0 +1,1778 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This software is licensed under the GNU General Public License, + version 2 (GPLv2) (see COPYING for details), subject to the + following clarification. + + With respect to binaries built using the Microsoft(R) Windows + Driver Kit (WDK), GPLv2 does not extend to any code contained in or + derived from the WDK ("WDK Code"). As to WDK Code, by using or + distributing such binaries you agree to be bound by the Microsoft + Software License Terms for the WDK. All WDK Code is considered by + the GPLv2 licensors to qualify for the special exception stated in + section 3 of GPLv2 (commonly known as the system library + exception). + + There is NO WARRANTY for this software, express or implied, + including the implied warranties of NON-INFRINGEMENT, TITLE, + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#include "os_dep.h" +#include "qxldd.h" +#include "utils.h" +#include "res.h" +#include "rop.h" +#include "surface.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, SPICE_ROPD_OP_BLACKNESS}, //0 + {QXL_EFFECT_BLEND, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, SPICE_ROPD_OP_OR | + SPICE_ROPD_INVERS_RES}, //DPon + {QXL_EFFECT_NOP_ON_DUP, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, + SPICE_ROPD_INVERS_BRUSH | SPICE_ROPD_OP_AND}, //DPna + {QXL_EFFECT_OPAQUE, ROP3_BRUSH, ROP3_TYPE_FILL, SPICE_ROPD_INVERS_BRUSH | + SPICE_ROPD_OP_PUT}, //Pn + {QXL_EFFECT_BLACKNESS_ON_DUP, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, + SPICE_ROPD_INVERS_DEST | SPICE_ROPD_OP_AND}, //PDna + {QXL_EFFECT_REVERT_ON_DUP, ROP3_DEST, ROP3_TYPE_INVERS, SPICE_ROPD_OP_INVERS}, //Dn + {QXL_EFFECT_REVERT_ON_DUP, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, SPICE_ROPD_OP_XOR}, //DPx + {QXL_EFFECT_BLACKNESS_ON_DUP, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, + SPICE_ROPD_OP_AND | SPICE_ROPD_INVERS_RES}, //DPan + {QXL_EFFECT_NOP_ON_DUP, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, SPICE_ROPD_OP_AND}, //DPa + {QXL_EFFECT_BLEND, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, SPICE_ROPD_OP_XOR | + SPICE_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, + SPICE_ROPD_INVERS_BRUSH | SPICE_ROPD_OP_OR}, //DPno + {QXL_EFFECT_OPAQUE, ROP3_BRUSH, ROP3_TYPE_FILL, SPICE_ROPD_OP_PUT}, //P + {QXL_EFFECT_BLEND, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, SPICE_ROPD_INVERS_DEST | + SPICE_ROPD_OP_OR}, //PDno + {QXL_EFFECT_NOP_ON_DUP, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, SPICE_ROPD_OP_OR}, //DPo + {QXL_EFFECT_OPAQUE, 0, ROP3_TYPE_WHITENESS, SPICE_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, SPICE_ROPD_OP_OR | + SPICE_ROPD_INVERS_RES}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x04}, //SDPona + //DPon + {QXL_EFFECT_BLEND, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, SPICE_ROPD_OP_OR | + SPICE_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, + SPICE_ROPD_INVERS_BRUSH | + SPICE_ROPD_OP_AND}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x0b}, //PSDnaon + //SPna + {QXL_EFFECT_OPAQUE, ROP3_SRC | ROP3_BRUSH, ROP3_TYPE_OPAQUE, SPICE_ROPD_INVERS_BRUSH | + SPICE_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, SPICE_ROPD_INVERS_BRUSH | SPICE_ROPD_OP_PUT}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0x10}, //PDSona + //DSon + {QXL_EFFECT_BLEND, ROP3_SRC | ROP3_DEST, ROP3_TYPE_BLEND, SPICE_ROPD_OP_OR | + SPICE_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, SPICE_ROPD_INVERS_SRC | + SPICE_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, SPICE_ROPD_INVERS_SRC | + SPICE_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, SPICE_ROPD_INVERS_SRC | + SPICE_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, SPICE_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, SPICE_ROPD_OP_AND | + SPICE_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, SPICE_ROPD_INVERS_DEST | + SPICE_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, SPICE_ROPD_INVERS_DEST | + SPICE_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, SPICE_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, SPICE_ROPD_OP_AND | + SPICE_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, + SPICE_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, SPICE_ROPD_OP_AND | + SPICE_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, + SPICE_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, SPICE_ROPD_OP_XOR | + SPICE_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, + SPICE_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, SPICE_ROPD_OP_XOR | + SPICE_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, + SPICE_ROPD_INVERS_BRUSH | SPICE_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, + SPICE_ROPD_INVERS_SRC | SPICE_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, SPICE_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, SPICE_ROPD_OP_XOR | + SPICE_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, SPICE_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, + SPICE_ROPD_INVERS_BRUSH | + SPICE_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, + SPICE_ROPD_INVERS_DEST | + SPICE_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, + SPICE_ROPD_OP_OR}, //DSo + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xef}, //SDPnoo + {QXL_EFFECT_OPAQUE, ROP3_BRUSH, ROP3_TYPE_FILL, SPICE_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, + SPICE_ROPD_INVERS_SRC | + SPICE_ROPD_OP_OR}, + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xf4}, //PSDnao + //PDno + {QXL_EFFECT_BLEND, ROP3_DEST | ROP3_BRUSH, ROP3_TYPE_FILL, + SPICE_ROPD_INVERS_DEST | + SPICE_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, + SPICE_ROPD_OP_OR}, //DPo + {QXL_EFFECT_BLEND, ROP3_ALL, ROP3_TYPE_ROP3, 0xfb}, //DPSnoo + {QXL_EFFECT_OPAQUE, ROP3_SRC | ROP3_BRUSH, ROP3_TYPE_OPAQUE, SPICE_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, UINT32 surface_id, RECTL *area, CLIPOBJ *clip, BRUSHOBJ *brush, + POINTL *brush_pos, ROP3Info *rop_info, SURFOBJ *mask, POINTL *mask_pos, + BOOL invers_mask) +{ + QXLDrawable *drawable; + UINT32 width; + UINT32 height; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + ASSERT(pdev, pdev && area && brush); + + if (!(drawable = Drawable(pdev, QXL_DRAW_FILL, area, clip, surface_id))) { + return FALSE; + } + + width = area->right - area->left; + height = area->bottom - area->top; + + if (!QXLGetBrush(pdev, drawable, &drawable->u.fill.brush, brush, brush_pos, + &drawable->surfaces_dest[0], &drawable->surfaces_rects[0]) || + !QXLGetMask(pdev, drawable, &drawable->u.fill.mask, mask, mask_pos, invers_mask, + width, height, &drawable->surfaces_dest[1])) { + ReleaseOutput(pdev, drawable->release_info.id); + return FALSE; + } + + drawable->u.fill.rop_descriptor = rop_info->method_data; + + drawable->effect = mask ? QXL_EFFECT_BLEND : rop_info->effect; + + if (mask_pos) { + CopyRectPoint(&drawable->surfaces_rects[1], mask_pos, width, height); + } + + PushDrawable(pdev, drawable); + return TRUE; +} + +static BOOL GetBitmap(PDev *pdev, QXLDrawable *drawable, QXLPHYSICAL *bitmap_phys, SURFOBJ *surf, + QXLRect *area, XLATEOBJ *color_trans, BOOL use_cache, INT32 *surface_dest) +{ + DEBUG_PRINT((pdev, 9, "%s\n", __FUNCTION__)); + if (surf->iType != STYPE_BITMAP) { + UINT32 surface_id; + + ASSERT(pdev, (PDev *)surf->dhpdev == pdev); + surface_id = GetSurfaceId(surf); + if (surface_id == drawable->surface_id) { + DEBUG_PRINT((pdev, 9, "%s copy from self\n", __FUNCTION__)); + *bitmap_phys = 0; + drawable->self_bitmap = TRUE; + drawable->self_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, surface_dest); +} + +static _inline UINT8 GdiScaleModeToQxl(ULONG scale_mode) +{ + return (scale_mode == HALFTONE) ? SPICE_IMAGE_SCALE_MODE_INTERPOLATE : + SPICE_IMAGE_SCALE_MODE_NEAREST; +} + +static BOOL DoOpaque(PDev *pdev, UINT32 surface_id, RECTL *area, CLIPOBJ *clip, SURFOBJ *src, + RECTL *src_rect, XLATEOBJ *color_trans, BRUSHOBJ *brush, POINTL *brush_pos, + UINT16 rop_descriptor, SURFOBJ *mask, POINTL *mask_pos, BOOL invers_mask, + ULONG scale_mode) +{ + QXLDrawable *drawable; + UINT32 width; + UINT32 height; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + ASSERT(pdev, pdev && area && brush && src_rect && src); + + if (!(drawable = Drawable(pdev, QXL_DRAW_OPAQUE, area, clip, surface_id))) { + return FALSE; + } + + drawable->u.opaque.scale_mode = GdiScaleModeToQxl(scale_mode); + CopyRect(&drawable->u.opaque.src_area, src_rect); + + width = area->right - area->left; + height = area->bottom - area->top; + + if (!QXLGetBrush(pdev, drawable, &drawable->u.opaque.brush, brush, brush_pos, + &drawable->surfaces_dest[0], &drawable->surfaces_rects[0]) || + !QXLGetMask(pdev, drawable, &drawable->u.opaque.mask, mask, mask_pos, invers_mask, + width, height, &drawable->surfaces_dest[1]) || + !GetBitmap(pdev, drawable, &drawable->u.opaque.src_bitmap, src, + &drawable->u.opaque.src_area, color_trans, TRUE, + &drawable->surfaces_dest[2])) { + ReleaseOutput(pdev, drawable->release_info.id); + return FALSE; + } + + if (mask_pos) { + CopyRectPoint(&drawable->surfaces_rects[1], mask_pos, width, height); + } + CopyRect(&drawable->surfaces_rects[2], src_rect); + + drawable->u.opaque.rop_descriptor = rop_descriptor; + drawable->effect = mask ? QXL_EFFECT_BLEND : QXL_EFFECT_OPAQUE; + PushDrawable(pdev, drawable); + return TRUE; +} + +static BOOL StreamTest(PDev *pdev, UINT32 surface_id, 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 || + /* Only handle streams on primary surface */ + surface_id != 0) { + 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) { + trace->last_time = now - 1; // asumong mm clock is active so delta t == 0 is + // imposibole. frocing delata t to be at least 1. + if (trace->count < 20) { + trace->count++; + ret = TRUE; + } else { + ret = FALSE; + } + } else { + trace->last_time = now; + trace->count = 0; + 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; + } + trace->count = 0; + RingAdd(pdev, ring, (RingItem *)trace); + + return TRUE; +} + +static BOOL TestSplitClips(PDev *pdev, SURFOBJ *src, RECTL *src_rect, CLIPOBJ *clip, SURFOBJ *mask) +{ + UINT32 width; + UINT32 height; + UINT32 src_space; + UINT32 clip_space = 0; + int more; + + if (!clip || mask) { + return FALSE; + } + + if (src->iType != STYPE_BITMAP) { + return FALSE; + } + + width = src_rect->right - src_rect->left; + height = src_rect->bottom - src_rect->top; + src_space = width * height; + + if (clip->iDComplexity == DC_RECT) { + width = clip->rclBounds.right - clip->rclBounds.left; + height = clip->rclBounds.bottom - clip->rclBounds.top; + clip_space = width * height; + + if ((src_space / clip_space) > 1) { + return TRUE; + } + return FALSE; + } + + if (clip->iMode == TC_RECTANGLES) { + 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); + for(now = buf.rects, end = now + buf.count; now < end; now++) { + width = now->right - now->left; + height = now->bottom - now->top; + clip_space += width * height; + } + } while (more); + + if ((src_space / clip_space) > 1) { + return TRUE; + } + } + return FALSE; +} + +static _inline BOOL DoPartialCopy(PDev *pdev, UINT32 surface_id, SURFOBJ *src, RECTL *src_rect, + RECTL *area_rect, RECTL *clip_rect, XLATEOBJ *color_trans, + ULONG scale_mode, UINT16 rop_descriptor) +{ + QXLDrawable *drawable; + RECTL clip_area; + UINT32 width; + UINT32 height; + + SectRect(area_rect, clip_rect, &clip_area); + if (IsEmptyRect(&clip_area)) { + return TRUE; + } + + width = clip_area.right - clip_area.left; + height = clip_area.bottom - clip_area.top; + + if (!(drawable = Drawable(pdev, QXL_DRAW_COPY, &clip_area, NULL, surface_id))) { + return FALSE; + } + + drawable->effect = QXL_EFFECT_OPAQUE; + drawable->u.copy.scale_mode = GdiScaleModeToQxl(scale_mode); + drawable->u.copy.mask.bitmap = 0; + drawable->u.copy.rop_descriptor = rop_descriptor; + + drawable->u.copy.src_area.top = src_rect->top + (clip_area.top - area_rect->top); + drawable->u.copy.src_area.bottom = drawable->u.copy.src_area.top + clip_area.bottom - + clip_area.top; + drawable->u.copy.src_area.left = src_rect->left + (clip_area.left - area_rect->left); + drawable->u.copy.src_area.right = drawable->u.copy.src_area.left + clip_area.right - + clip_area.left; + + if(!GetBitmap(pdev, drawable, &drawable->u.copy.src_bitmap, src, &drawable->u.copy.src_area, + color_trans, FALSE, &drawable->surfaces_dest[0])) { + ReleaseOutput(pdev, drawable->release_info.id); + return FALSE; + } + CopyRect(&drawable->surfaces_rects[0], src_rect); + PushDrawable(pdev, drawable); + return TRUE; +} + +static BOOL DoCopy(PDev *pdev, UINT32 surface_id, RECTL *area, CLIPOBJ *clip, SURFOBJ *src, + RECTL *src_rect, XLATEOBJ *color_trans, UINT16 rop_descriptor, SURFOBJ *mask, + POINTL *mask_pos, BOOL invers_mask, ULONG scale_mode) +{ + QXLDrawable *drawable; + BOOL use_cache; + UINT32 width; + UINT32 height; + + ASSERT(pdev, pdev && area && src_rect && src); + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + + width = area->right - area->left; + height = area->bottom - area->top; + + if (mask) { + use_cache = TRUE; + } else { + use_cache = StreamTest(pdev, surface_id, src, color_trans, src_rect, area); + } + + if (use_cache && TestSplitClips(pdev, src, src_rect, clip, mask) && + !QXLCheckIfCacheImage(pdev, src, color_trans)) { + if (clip->iDComplexity == DC_RECT) { + if (!DoPartialCopy(pdev, surface_id, src, src_rect, area, &clip->rclBounds, color_trans, + scale_mode, rop_descriptor)) { + return FALSE; + } + } else { + int more; + ASSERT(pdev, clip->iMode == TC_RECTANGLES); + 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); + for(now = buf.rects, end = now + buf.count; now < end; now++) { + if (!DoPartialCopy(pdev, surface_id, src, src_rect, area, now, color_trans, + scale_mode, rop_descriptor)) { + return FALSE; + } + } + } while (more); + } + return TRUE; + } + + if (!(drawable = Drawable(pdev, QXL_DRAW_COPY, area, clip, surface_id))) { + return FALSE; + } + + if (mask) { + drawable->effect = QXL_EFFECT_BLEND; + } else { + drawable->effect = QXL_EFFECT_OPAQUE; + } + + 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, + width, height, &drawable->surfaces_dest[0]) || + !GetBitmap(pdev, drawable, &drawable->u.copy.src_bitmap, src, &drawable->u.copy.src_area, + color_trans, use_cache, &drawable->surfaces_dest[1])) { + ReleaseOutput(pdev, drawable->release_info.id); + return FALSE; + } + + if (mask_pos) { + CopyRectPoint(&drawable->surfaces_rects[0], mask_pos, width, height); + } + CopyRect(&drawable->surfaces_rects[1], src_rect); + + drawable->u.copy.rop_descriptor = rop_descriptor; + PushDrawable(pdev, drawable); + DEBUG_PRINT((pdev, 7, "%s: done\n", __FUNCTION__)); + return TRUE; +} + +static BOOL DoCopyBits(PDev *pdev, UINT32 surface_id, CLIPOBJ *clip, RECTL *area, POINTL *src_pos) +{ + QXLDrawable *drawable; + UINT32 width; + UINT32 height; + + 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, 6, "%s: NOP\n", __FUNCTION__)); + return TRUE; + } + + width = area->right - area->left; + height = area->bottom - area->top; + + if (!(drawable = Drawable(pdev, QXL_COPY_BITS, area, clip, surface_id))) { + return FALSE; + } + + drawable->surfaces_dest[0] = surface_id; + CopyRectPoint(&drawable->surfaces_rects[0], src_pos, width, height); + + CopyPoint(&drawable->u.copy_bits.src_pos, src_pos); + drawable->effect = QXL_EFFECT_OPAQUE; + PushDrawable(pdev, drawable); + return TRUE; +} + +static BOOL DoBlend(PDev *pdev, UINT32 surface_id, 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; + UINT32 width; + UINT32 height; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + ASSERT(pdev, pdev && area && src_rect && src); + + if (!(drawable = Drawable(pdev, QXL_DRAW_BLEND, area, clip, surface_id))) { + return FALSE; + } + + width = area->right - area->left; + height = area->bottom - area->top; + + 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, + width, height, &drawable->surfaces_dest[0]) || + !GetBitmap(pdev, drawable, &drawable->u.blend.src_bitmap, src, &drawable->u.blend.src_area, + color_trans, TRUE, &drawable->surfaces_dest[1])) { + ReleaseOutput(pdev, drawable->release_info.id); + return FALSE; + } + + if (mask_pos) { + CopyRectPoint(&drawable->surfaces_rects[0], mask_pos, width, height); + } + CopyRect(&drawable->surfaces_rects[1], src_rect); + + drawable->u.blend.rop_descriptor = rop_info->method_data; + drawable->effect = mask ? QXL_EFFECT_BLEND : rop_info->effect; + PushDrawable(pdev, drawable); + return TRUE; +} + +static BOOL DoBlackness(PDev *pdev, UINT32 surface_id, RECTL *area, CLIPOBJ *clip, SURFOBJ *mask, + POINTL *mask_pos, BOOL invers_mask) +{ + QXLDrawable *drawable; + UINT32 width; + UINT32 height; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + ASSERT(pdev, pdev && area); + + if (!(drawable = Drawable(pdev, QXL_DRAW_BLACKNESS, area, clip, surface_id))) { + return FALSE; + } + + width = area->right - area->left; + height = area->bottom - area->top; + + if (!QXLGetMask(pdev, drawable, &drawable->u.blackness.mask, mask, mask_pos, invers_mask, + width, height, &drawable->surfaces_dest[0])) { + ReleaseOutput(pdev, drawable->release_info.id); + return FALSE; + } + + if (mask_pos) { + CopyRectPoint(&drawable->surfaces_rects[0], mask_pos, width, height); + } + + drawable->effect = mask ? QXL_EFFECT_BLEND : QXL_EFFECT_OPAQUE; + PushDrawable(pdev, drawable); + return TRUE; +} + +static BOOL DoWhiteness(PDev *pdev, UINT32 surface_id, RECTL *area, CLIPOBJ *clip, SURFOBJ *mask, + POINTL *mask_pos, BOOL invers_mask) +{ + QXLDrawable *drawable; + UINT32 width; + UINT32 height; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + ASSERT(pdev, pdev && area); + + if (!(drawable = Drawable(pdev, QXL_DRAW_WHITENESS, area, clip, surface_id))) { + return FALSE; + } + + width = area->right - area->left; + height = area->bottom - area->top; + + if (!QXLGetMask(pdev, drawable, &drawable->u.whiteness.mask, mask, mask_pos, invers_mask, + width, height, &drawable->surfaces_dest[0])) { + ReleaseOutput(pdev, drawable->release_info.id); + return FALSE; + } + + if (mask_pos) { + CopyRectPoint(&drawable->surfaces_rects[0], mask_pos, width, height); + } + + drawable->effect = mask ? QXL_EFFECT_BLEND : QXL_EFFECT_OPAQUE; + PushDrawable(pdev, drawable); + return TRUE; +} + +static BOOL DoInvers(PDev *pdev, UINT32 surface_id, RECTL *area, CLIPOBJ *clip, SURFOBJ *mask, + POINTL *mask_pos, BOOL invers_mask) +{ + QXLDrawable *drawable; + UINT32 width; + UINT32 height; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + ASSERT(pdev, pdev && area); + + if (!(drawable = Drawable(pdev, QXL_DRAW_INVERS, area, clip, surface_id))) { + return FALSE; + } + + width = area->right - area->left; + height = area->bottom - area->top; + + if (!QXLGetMask(pdev, drawable, &drawable->u.invers.mask, mask, mask_pos, invers_mask, + width, height, &drawable->surfaces_dest[0])) { + ReleaseOutput(pdev, drawable->release_info.id); + return FALSE; + } + + if (mask_pos) { + CopyRectPoint(&drawable->surfaces_rects[0], mask_pos, width, height); + } + + drawable->effect = mask ? QXL_EFFECT_BLEND : QXL_EFFECT_REVERT_ON_DUP; + PushDrawable(pdev, drawable); + return TRUE; +} + +static BOOL DoROP3(PDev *pdev, UINT32 surface_id, 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; + UINT32 width; + UINT32 height; + + DEBUG_PRINT((pdev, 6, "%s\n", __FUNCTION__)); + ASSERT(pdev, pdev && area && brush && src_rect && src); + + if (!(drawable = Drawable(pdev, QXL_DRAW_ROP3, area, clip, surface_id))) { + return FALSE; + } + + width = area->right - area->left; + height = area->bottom - area->top; + + 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, + &drawable->surfaces_dest[0], &drawable->surfaces_rects[0]) || + !QXLGetMask(pdev, drawable, &drawable->u.rop3.mask, mask, mask_pos, invers_mask, + width, height, &drawable->surfaces_dest[1]) || + !GetBitmap(pdev, drawable, &drawable->u.rop3.src_bitmap, src, &drawable->u.rop3.src_area, + color_trans, TRUE, &drawable->surfaces_dest[2])) { + ReleaseOutput(pdev, drawable->release_info.id); + return FALSE; + } + + if (mask_pos) { + CopyRectPoint(&drawable->surfaces_rects[1], mask_pos, width, height); + } + CopyRect(&drawable->surfaces_rects[2], src_rect); + + drawable->u.rop3.rop3 = rop3; + drawable->effect = mask ? QXL_EFFECT_BLEND : QXL_EFFECT_BLEND; //for now + PushDrawable(pdev, drawable); + return TRUE; +} + +BOOL BitBltFromDev(PDev *pdev, SURFOBJ *src, 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; + UINT32 surface_id; + SurfaceInfo *surface; + + surface = (SurfaceInfo *)src->dhsurf; + surface_id = GetSurfaceId(src); + + 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, + surface->draw_area.surf_obj->sizlBitmap.cy); + area.left = MAX(0, src_pos.x); + area.right = MIN(src_pos.x + dest_rect->right - dest_rect->left, + surface->draw_area.surf_obj->sizlBitmap.cx); + + UpdateArea(pdev, &area, surface_id); + + surf_obj = surface->draw_area.surf_obj; + + 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); + } + + return ret; +} + +BOOL _inline __DrvBitBlt(PDev *pdev, UINT32 surface_id, 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, surface_id, dest_rect, clip, brush, brush_pos, rop_info, mask, mask_pos, + invers_mask); + case ROP3_TYPE_OPAQUE: + return DoOpaque(pdev, surface_id, 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, surface_id, 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, surface_id, dest_rect, clip, src, src_rect, color_trans, rop_info, + mask, mask_pos, invers_mask, scale_mode); + case ROP3_TYPE_BLACKNESS: + return DoBlackness(pdev, surface_id, dest_rect, clip, mask, mask_pos, invers_mask); + case ROP3_TYPE_WHITENESS: + return DoWhiteness(pdev, surface_id, dest_rect, clip, mask, mask_pos, invers_mask); + case ROP3_TYPE_INVERS: + return DoInvers(pdev, surface_id, dest_rect, clip, mask, mask_pos, invers_mask); + case ROP3_TYPE_ROP3: + return DoROP3(pdev, surface_id, 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; + UINT32 surface_id; + + ASSERT(pdev, dest->iType != STYPE_BITMAP); + + surface_id = GetSurfaceId(dest); + + if (!PrepareBrush(brush)) { + return QXL_FAILED; + } + + if ((rop3 = rop4 & 0xff) == (second_rop3 = ((rop4 >> 8) & 0xff))) { + return __DrvBitBlt(pdev, surface_id, 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, surface_id, dest_rect, clip, src, src_rect, color_trans, brush, + brush_pos, rop3, mask, mask_pos, FALSE, scale_mode) && + __DrvBitBlt(pdev, surface_id, 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_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, src, dest, mask, clip, color_trans, &area, local_pos, + mask_pos, brush, brush_pos, rop4) ? QXL_SUCCESS : QXL_FAILED; + } + + if (src->iType != STYPE_BITMAP + && GetSurfaceId(src) == GetSurfaceId(dest) && rop4 == 0xcccc) { //SRCCOPY no mask + return DoCopyBits(pdev, GetSurfaceId(src), 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) { + pdev = (PDev *)src->dhpdev; + } else { + pdev = (PDev *)dest->dhpdev; + } + + PUNT_IF_DISABLED(pdev); + + 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) { + pdev = (PDev *)src->dhpdev; + } else { + pdev = (PDev *)dest->dhpdev; + } + + PUNT_IF_DISABLED(pdev); + + 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) { + pdev = (PDev *)src->dhpdev; + } else { + pdev = (PDev *)dest->dhpdev; + } + + pdev = (PDev *)dest->dhpdev; + DEBUG_PRINT((pdev, 3, "%s\n", __FUNCTION__)); + CountCall(pdev, CALL_COUNTER_STRETCH_BLT_ROP); + + PUNT_IF_DISABLED(pdev); + + 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) { + pdev = (PDev *)src->dhpdev; + } else { + pdev = (PDev *)dest->dhpdev; + } + pdev = (PDev *)dest->dhpdev; + + DEBUG_PRINT((pdev, 3, "%s\n", __FUNCTION__)); + CountCall(pdev, CALL_COUNTER_STRETCH_BLT); + PUNT_IF_DISABLED(pdev); + + 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) { + pdev = (PDev *)src->dhpdev; + } else { + pdev = (PDev *)dest->dhpdev; + } + + pdev = (PDev *)dest->dhpdev; + DEBUG_PRINT((pdev, 3, "%s\n", __FUNCTION__)); + + PUNT_IF_DISABLED(pdev); + + 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, GetSurfaceId(dest)))) { + 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->surfaces_rects[0], src_rect); + 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, + &drawable->surfaces_dest[0])) { + 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, + &drawable->surfaces_dest[0])) { + DEBUG_PRINT((pdev, 0, "%s: QXLGetBitmap failed\n", __FUNCTION__)); + ReleaseOutput(pdev, drawable->release_info.id); + return FALSE; + } + } + drawable->u.alpha_blend.alpha_flags = 0; + if (src->iType != STYPE_BITMAP && + bland->BlendFunction.AlphaFormat == AC_SRC_ALPHA) + drawable->u.alpha_blend.alpha_flags |= SPICE_ALPHA_FLAGS_SRC_SURFACE_HAS_ALPHA; + + 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; + } else { + ASSERT(NULL, dest->dhpdev); + pdev = (PDev *)dest->dhpdev; + } + + DEBUG_PRINT((pdev, 3, "%s\n", __FUNCTION__)); + + PUNT_IF_DISABLED(pdev); + + 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, GetSurfaceId(dest)))) { + 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); + CopyRect(&drawable->surfaces_rects[0], src_rect); + if (!QXLGetBitmap(pdev, drawable, &drawable->u.transparent.src_bitmap, src, + &drawable->u.transparent.src_area, color_trans, NULL, TRUE, + &drawable->surfaces_dest[0])) { + 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/xddm/display/rop.h b/xddm/display/rop.h new file mode 100644 index 0000000..b0c7ef5 --- /dev/null +++ b/xddm/display/rop.h @@ -0,0 +1,42 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This software is licensed under the GNU General Public License, + version 2 (GPLv2) (see COPYING for details), subject to the + following clarification. + + With respect to binaries built using the Microsoft(R) Windows + Driver Kit (WDK), GPLv2 does not extend to any code contained in or + derived from the WDK ("WDK Code"). As to WDK Code, by using or + distributing such binaries you agree to be bound by the Microsoft + Software License Terms for the WDK. All WDK Code is considered by + the GPLv2 licensors to qualify for the special exception stated in + section 3 of GPLv2 (commonly known as the system library + exception). + + There is NO WARRANTY for this software, express or implied, + including the implied warranties of NON-INFRINGEMENT, TITLE, + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#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[]; + +BOOL BitBltFromDev(PDev *pdev, SURFOBJ *src, 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); +#endif diff --git a/xddm/display/sources b/xddm/display/sources new file mode 100644 index 0000000..6c1d5c7 --- /dev/null +++ b/xddm/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 \
+ surface.c \
+ driver.rc
+
diff --git a/xddm/display/surface.c b/xddm/display/surface.c new file mode 100644 index 0000000..2cc5895 --- /dev/null +++ b/xddm/display/surface.c @@ -0,0 +1,407 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This software is licensed under the GNU General Public License, + version 2 (GPLv2) (see COPYING for details), subject to the + following clarification. + + With respect to binaries built using the Microsoft(R) Windows + Driver Kit (WDK), GPLv2 does not extend to any code contained in or + derived from the WDK ("WDK Code"). As to WDK Code, by using or + distributing such binaries you agree to be bound by the Microsoft + Software License Terms for the WDK. All WDK Code is considered by + the GPLv2 licensors to qualify for the special exception stated in + section 3 of GPLv2 (commonly known as the system library + exception). + + There is NO WARRANTY for this software, express or implied, + including the implied warranties of NON-INFRINGEMENT, TITLE, + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#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 "qxldd.h" +#include "utils.h" +#include "mspace.h" +#include "res.h" +#include "surface.h" + +static BOOL CreateDrawArea(PDev *pdev, UINT8 *base_mem, ULONG format, UINT32 cx, UINT32 cy, + UINT32 stride, UINT32 surface_id) +{ + SIZEL size; + DrawArea *drawarea; + + size.cx = cx; + size.cy = cy; + + drawarea = &GetSurfaceInfo(pdev, surface_id)->draw_area; + + if (!(drawarea->bitmap = (HSURF)EngCreateBitmap(size, stride, format, 0, base_mem))) { + DEBUG_PRINT((pdev, 0, "%s: EngCreateBitmap failed\n", __FUNCTION__)); + return FALSE; + } + + if (!EngAssociateSurface(drawarea->bitmap, pdev->eng, 0)) { + DEBUG_PRINT((pdev, 0, "%s: EngAssociateSurface failed\n", __FUNCTION__)); + goto error; + } + + if (!(drawarea->surf_obj = EngLockSurface(drawarea->bitmap))) { + DEBUG_PRINT((pdev, 0, "%s: EngLockSurface failed\n", __FUNCTION__)); + goto error; + } + + drawarea->base_mem = base_mem; + + return TRUE; +error: + EngDeleteSurface(drawarea->bitmap); + return FALSE; +} + +static VOID FreeDrawArea(DrawArea *drawarea) +{ + if (drawarea->surf_obj) { + EngUnlockSurface(drawarea->surf_obj); + EngDeleteSurface(drawarea->bitmap); + drawarea->surf_obj = NULL; + } +} + +static void BitmapFormatToDepthAndSurfaceFormat(ULONG format, UINT32 *depth, UINT32 *surface_format) +{ + switch (format) { + case BMF_16BPP: + *surface_format = SPICE_SURFACE_FMT_16_555; + *depth = 16; + break; + case BMF_24BPP: + case BMF_32BPP: + *surface_format = SPICE_SURFACE_FMT_32_xRGB; + *depth = 32; + break; + default: + *depth = 0; + break; + }; +} + +static UINT8 *CreateSurfaceHelper(PDev *pdev, UINT32 surface_id, + UINT32 cx, UINT32 cy, ULONG format, + UINT8 allocation_type, + INT32 *stride, UINT32 *surface_format, + QXLPHYSICAL *phys_mem) +{ + UINT32 depth; + SurfaceInfo *surface_info = GetSurfaceInfo(pdev, surface_id); + UINT8 *base_mem; + int size; + + BitmapFormatToDepthAndSurfaceFormat(format, &depth, surface_format); + ASSERT(pdev, depth != 0); + ASSERT(pdev, stride); + QXLGetSurface(pdev, phys_mem, cx, cy, depth, stride, &base_mem, allocation_type); + DEBUG_PRINT((pdev, 3, + "%s: %d, pm %0lX, fmt %d, d %d, s (%d, %d) st %d\n", + __FUNCTION__, surface_id, (uint64_t)*phys_mem, *surface_format, + depth, cx, cy, *stride)); + size = abs(*stride) * cy; + if (!base_mem) { + DEBUG_PRINT((pdev, 0, "%s: %p: %d: QXLGetSurface failed (%d bytes alloc)\n", + __FUNCTION__, pdev, surface_id, size)); + return NULL; + } + if (!CreateDrawArea(pdev, base_mem, surface_info->bitmap_format, cx, cy, *stride, surface_id)) { + DEBUG_PRINT((pdev, 0, "%s: %p: CreateDrawArea failed (%d)\n", + __FUNCTION__, pdev, surface_id, size)); + // TODO: Why did it fail? nothing in the MSDN + QXLDelSurface(pdev, base_mem, allocation_type); + return NULL; + } + return base_mem; +} + +static void SendSurfaceCreateCommand(PDev *pdev, UINT32 surface_id, SIZEL size, + UINT32 surface_format, INT32 stride, QXLPHYSICAL phys_mem, + int keep_data) +{ + QXLSurfaceCmd *surface; + + surface = SurfaceCmd(pdev, QXL_SURFACE_CMD_CREATE, surface_id); + if (keep_data) { + surface->flags |= QXL_SURF_FLAG_KEEP_DATA; + } + surface->u.surface_create.format = surface_format; + surface->u.surface_create.width = size.cx; + surface->u.surface_create.height = size.cy; + surface->u.surface_create.stride = stride; + surface->u.surface_create.data = phys_mem; + PushSurfaceCmd(pdev, surface); +} + +HBITMAP CreateDeviceBitmap(PDev *pdev, SIZEL size, ULONG format, QXLPHYSICAL *phys_mem, + UINT8 **base_mem, UINT32 surface_id, UINT8 allocation_type) +{ + UINT32 surface_format, depth; + HBITMAP hbitmap; + INT32 stride; + SurfaceInfo *surface_info; + + DEBUG_PRINT((pdev, 9, "%s: %p: %d, (%dx%d), %d\n", __FUNCTION__, pdev, surface_id, + size.cx, size.cy, format)); + surface_info = GetSurfaceInfo(pdev, surface_id); + + if (!(hbitmap = EngCreateDeviceBitmap((DHSURF)surface_info, size, format))) { + DEBUG_PRINT((pdev, 0, "%s: EngCreateDeviceBitmap failed, pdev 0x%lx, surface_id=%d\n", + __FUNCTION__, pdev, surface_id)); + goto out_error1; + } + + if (!EngAssociateSurface((HSURF)hbitmap, pdev->eng, QXL_SURFACE_HOOKS)) { + DEBUG_PRINT((pdev, 0, "%s: EngAssociateSurface failed\n", __FUNCTION__)); + goto out_error2; + } + surface_info->u.pdev = pdev; + surface_info->hbitmap = hbitmap; + surface_info->copy = NULL; + surface_info->size = size; + surface_info->bitmap_format = format; + if ((*base_mem = CreateSurfaceHelper(pdev, surface_id, size.cx, size.cy, format, + allocation_type, &stride, &surface_format, + phys_mem)) == NULL) { + DEBUG_PRINT((pdev, 0, "%s: failed, pdev 0x%lx, surface_id=%d\n", + __FUNCTION__, pdev, surface_id)); + goto out_error2; + } + surface_info->stride = stride; + if (allocation_type != DEVICE_BITMAP_ALLOCATION_TYPE_SURF0) { + SendSurfaceCreateCommand(pdev, surface_id, size, surface_format, -stride, *phys_mem, 0); + } + + return hbitmap; +out_error2: + EngDeleteSurface((HSURF)hbitmap); +out_error1: + return 0; +} + +VOID DeleteDeviceBitmap(PDev *pdev, UINT32 surface_id, UINT8 allocation_type) +{ + DrawArea *drawarea; + + drawarea = &GetSurfaceInfo(pdev,surface_id)->draw_area; + + FreeDrawArea(drawarea); + + if (allocation_type != DEVICE_BITMAP_ALLOCATION_TYPE_SURF0 && + pdev->surfaces_info[surface_id].draw_area.base_mem != NULL) { + + if (allocation_type == DEVICE_BITMAP_ALLOCATION_TYPE_RAM) { + /* server side this surface is already destroyed, just free it here */ + ASSERT(pdev, pdev->surfaces_info[surface_id].draw_area.base_mem == + pdev->surfaces_info[surface_id].copy); + QXLDelSurface(pdev, + pdev->surfaces_info[surface_id].draw_area.base_mem, + allocation_type); + FreeSurfaceInfo(pdev, surface_id); + } else { + QXLSurfaceCmd *surface_cmd; + surface_cmd = SurfaceCmd(pdev, QXL_SURFACE_CMD_DESTROY, surface_id); + QXLGetDelSurface(pdev, surface_cmd, surface_id, allocation_type); + PushSurfaceCmd(pdev, surface_cmd); + } + } +} + +static void CleanupSurfaceInfo(PDev *pdev, UINT32 surface_id, UINT8 allocation_type) +{ + SurfaceInfo *surface_info = GetSurfaceInfo(pdev, surface_id); + + FreeDrawArea(&surface_info->draw_area); + if (surface_info->draw_area.base_mem != NULL) { + QXLDelSurface(pdev, surface_info->draw_area.base_mem, allocation_type); + } +} + +BOOL MoveSurfaceToVideoRam(PDev *pdev, UINT32 surface_id) +{ + QXLSurfaceCmd *surface; + UINT32 surface_format; + UINT32 depth; + int count_used = 0; + int size; + INT32 stride = 0; + QXLPHYSICAL phys_mem; + SurfaceInfo *surface_info = GetSurfaceInfo(pdev, surface_id); + UINT32 cx = surface_info->size.cx; + UINT32 cy = surface_info->size.cy; + UINT8 *base_mem; + + DEBUG_PRINT((pdev, 3, "%s: %d\n", __FUNCTION__, surface_id)); + if ((base_mem = CreateSurfaceHelper(pdev, surface_id, cx, cy, surface_info->bitmap_format, + DEVICE_BITMAP_ALLOCATION_TYPE_VRAM, + &stride, &surface_format, &phys_mem)) == NULL) { + DEBUG_PRINT((pdev, 0, "%s: %p: %d: failed\n", __FUNCTION__, pdev, surface_id)); + return FALSE; + } + size = abs(stride) * cy; + if (!EngModifySurface((HSURF)surface_info->hbitmap, pdev->eng, QXL_SURFACE_HOOKS, + MS_NOTSYSTEMMEMORY, (DHSURF)surface_info, NULL, 0, NULL)) { + DEBUG_PRINT((pdev, 0, "%s: %p: %d: EngModifySurface failed\n", + __FUNCTION__, pdev, surface_id)); + CleanupSurfaceInfo(pdev, surface_id, DEVICE_BITMAP_ALLOCATION_TYPE_VRAM); + return FALSE; + } + DEBUG_PRINT((pdev, 3, "%s: stride = %d, phys_mem = %0lX, base_mem = %p\n", + __FUNCTION__, -stride, (uint64_t)phys_mem, base_mem)); + DEBUG_PRINT((pdev, 3, "%s: copy %d bytes to %d\n", __FUNCTION__, size, surface_id)); + // Everything allocated, nothing can fail (API wise) from this point + RtlCopyMemory(base_mem, surface_info->copy, size); + EngFreeMem(surface_info->copy); + surface_info->copy = NULL; + SendSurfaceCreateCommand(pdev, surface_id, surface_info->size, surface_format, + -stride, phys_mem, 1); + return TRUE; +} + +/* when we return from S3 we need to resend all the surface creation commands. + * Actually moving the memory vram<->guest is not strictly neccessary since vram + * is not reset during the suspend, so contents are not lost */ +int MoveAllSurfacesToVideoRam(PDev *pdev) +{ + UINT32 surface_id; + SurfaceInfo *surface_info; + + /* brute force implementation - alternative is to keep an updated used_surfaces list */ + DEBUG_PRINT((pdev, 3, "%s %p\n", __FUNCTION__, pdev)); + + for (surface_id = 1 ; surface_id < pdev->n_surfaces ; ++surface_id) { + surface_info = GetSurfaceInfo(pdev, surface_id); + if (!surface_info->draw_area.base_mem) { + continue; + } + if (surface_info->u.pdev != pdev) { + DEBUG_PRINT((pdev, 3, "%s: %p: not our pdev (%p)\n", __FUNCTION__, pdev, + surface_info->u.pdev)); + continue; + } + if (surface_info->draw_area.surf_obj) { + DEBUG_PRINT((pdev, 3, "%s: surface_id = %d, surf_obj not empty\n", __FUNCTION__, + surface_id)); + continue; + } + if (surface_info->copy == NULL) { + DEBUG_PRINT((pdev, 3, "%s: %p: %d: no copy buffer, ignored\n", __FUNCTION__, + pdev, surface_id)); + continue; + } + if (!MoveSurfaceToVideoRam(pdev, surface_id)) { + /* Some of the surfaces have not been moved to video ram. + * they will remain managed by GDI. */ + DEBUG_PRINT((pdev, 0, "%s: %p: %d: failed moving to vram\n", __FUNCTION__, + pdev, surface_id)); + } + } + return TRUE; +} + +/* to_surface_id is exclusive */ +static void SendSurfaceRangeCreateCommand(PDev *pdev, UINT32 from_surface_id, UINT32 to_surface_id) +{ + UINT32 surface_id; + + ASSERT(pdev, from_surface_id < to_surface_id); + ASSERT(pdev, to_surface_id <= pdev->n_surfaces); + + for (surface_id = from_surface_id; surface_id < to_surface_id; surface_id++) { + SurfaceInfo *surface_info; + SURFOBJ *surf_obj; + QXLPHYSICAL phys_mem; + UINT32 surface_format; + UINT32 depth; + + surface_info = GetSurfaceInfo(pdev, surface_id); + if (!surface_info->draw_area.base_mem) { + continue; + } + + surf_obj = surface_info->draw_area.surf_obj; + + if (!surf_obj) { + continue; + } + + phys_mem = SurfaceToPhysical(pdev, surface_info->draw_area.base_mem); + BitmapFormatToDepthAndSurfaceFormat(surface_info->bitmap_format, &depth, &surface_format); + + SendSurfaceCreateCommand(pdev, surface_id, surf_obj->sizlBitmap, + surface_format, -surface_info->stride, phys_mem, + /* the surface is still there, tell server not to erase */ + 1); + } +} + +BOOL MoveAllSurfacesToRam(PDev *pdev) +{ + UINT32 surface_id; + SurfaceInfo *surface_info; + SURFOBJ *surf_obj; + UINT8 *copy; + UINT8 *line0; + int size; + QXLPHYSICAL phys_mem; + + for (surface_id = 1 ; surface_id < pdev->n_surfaces ; ++surface_id) { + surface_info = GetSurfaceInfo(pdev, surface_id); + if (!surface_info->draw_area.base_mem) { + continue; + } + surf_obj = surface_info->draw_area.surf_obj; + if (!surf_obj) { + DEBUG_PRINT((pdev, 3, "%s: %d: no surfobj, not copying\n", __FUNCTION__, surface_id)); + continue; + } + size = surf_obj->sizlBitmap.cy * abs(surf_obj->lDelta); + copy = EngAllocMem(0, size, ALLOC_TAG); + DEBUG_PRINT((pdev, 3, "%s: %d: copying #%d to %p (%d)\n", __FUNCTION__, surface_id, size, + copy, surf_obj->lDelta)); + RtlCopyMemory(copy, surface_info->draw_area.base_mem, size); + surface_info->copy = copy; + line0 = surf_obj->lDelta > 0 ? copy : copy + abs(surf_obj->lDelta) * + (surf_obj->sizlBitmap.cy - 1); + if (!EngModifySurface((HSURF)surface_info->hbitmap, + pdev->eng, + 0, /* from the example: used to monitor memory HOOK_COPYBITS | HOOK_BITBLT, */ + 0, /* It's system-memory */ + (DHSURF)surface_info, + line0, + surf_obj->lDelta, + NULL)) { + /* Send a create messsage for this surface - we previously did a destroy all. */ + EngFreeMem(surface_info->copy); + surface_info->copy = NULL; + DEBUG_PRINT((pdev, 0, "%s: %d: EngModifySurface failed, sending create for %d-%d\n", + __FUNCTION__, surface_id, surface_id, pdev->n_surfaces - 1)); + SendSurfaceRangeCreateCommand(pdev, surface_id, pdev->n_surfaces); + return FALSE; + } + QXLDelSurface(pdev, surface_info->draw_area.base_mem, DEVICE_BITMAP_ALLOCATION_TYPE_VRAM); + surface_info->draw_area.base_mem = copy; + FreeDrawArea(&surface_info->draw_area); + } + return TRUE; +} diff --git a/xddm/display/surface.h b/xddm/display/surface.h new file mode 100644 index 0000000..c3e5a47 --- /dev/null +++ b/xddm/display/surface.h @@ -0,0 +1,103 @@ +#ifndef SURFACE_H +#define SURFACE_H + +#include "qxldd.h" + +/* Hooks supported by our surfaces. */ +#ifdef CALL_TEST +#define QXL_SURFACE_HOOKS_CALL_TEST \ + (HOOK_PLGBLT | HOOK_FILLPATH | HOOK_STROKEANDFILLPATH | HOOK_LINETO | \ + HOOK_GRADIENTFILL) +#else +#define QXL_SURFACE_HOOKS_CALL_TEST (0) +#endif + +#define QXL_SURFACE_HOOKS \ + (HOOK_SYNCHRONIZE | HOOK_COPYBITS | \ + HOOK_BITBLT | HOOK_TEXTOUT | HOOK_STROKEPATH | HOOK_STRETCHBLT | \ + HOOK_STRETCHBLTROP | HOOK_TRANSPARENTBLT | HOOK_ALPHABLEND | QXL_SURFACE_HOOKS_CALL_TEST) + + +static _inline UINT32 GetSurfaceIdFromInfo(SurfaceInfo *info) +{ + PDev *pdev; + + pdev = info->u.pdev; + if (info == &pdev->surface0_info) { + return 0; + } + return (UINT32)(info - pdev->surfaces_info); +} + +static _inline SurfaceInfo *GetSurfaceInfo(PDev *pdev, UINT32 id) +{ + if (id == 0) { + return &pdev->surface0_info; + } + return &pdev->surfaces_info[id]; +} + +static _inline UINT32 GetSurfaceId(SURFOBJ *surf) +{ + SurfaceInfo *surface; + + if (!surf || !surf->dhsurf) { + return (UINT32)-1; + } + surface = (SurfaceInfo *)surf->dhsurf; + return GetSurfaceIdFromInfo(surface); +} + +static _inline void FreeSurfaceInfo(PDev *pdev, UINT32 surface_id) +{ + SurfaceInfo *surface; + + if (surface_id == 0) { + return; + } + + DEBUG_PRINT((pdev, 9, "%s: %p: %d\n", __FUNCTION__, pdev, surface_id)); + surface = &pdev->surfaces_info[surface_id]; + if (surface->draw_area.base_mem == NULL) { + DEBUG_PRINT((pdev, 9, "%s: %p: %d: double free. safely ignored\n", __FUNCTION__, + pdev, surface_id)); + return; + } + surface->draw_area.base_mem = NULL; /* Mark as not used */ + surface->u.next_free = pdev->free_surfaces; + pdev->free_surfaces = surface; +} + +static UINT32 GetFreeSurface(PDev *pdev) +{ + UINT32 x, id; + SurfaceInfo *surface; + + ASSERT(pdev, pdev->enabled); + surface = pdev->free_surfaces; + if (surface == NULL) { + id = 0; + } else { + pdev->free_surfaces = surface->u.next_free; + + id = (UINT32)(surface - pdev->surfaces_info); + } + + return id; +} + +enum { + DEVICE_BITMAP_ALLOCATION_TYPE_SURF0, + DEVICE_BITMAP_ALLOCATION_TYPE_DEVRAM, + DEVICE_BITMAP_ALLOCATION_TYPE_VRAM, + DEVICE_BITMAP_ALLOCATION_TYPE_RAM, +}; + +HBITMAP CreateDeviceBitmap(PDev *pdev, SIZEL size, ULONG format, QXLPHYSICAL *phys_mem, + UINT8 **base_mem, UINT32 surface_id, UINT8 allocation_type); +VOID DeleteDeviceBitmap(PDev *pdev, UINT32 surface_id, UINT8 allocation_type); + +int MoveAllSurfacesToVideoRam(PDev *pdev); +BOOL MoveAllSurfacesToRam(PDev *pdev); + +#endif diff --git a/xddm/display/text.c b/xddm/display/text.c new file mode 100644 index 0000000..b0a516a --- /dev/null +++ b/xddm/display/text.c @@ -0,0 +1,128 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This software is licensed under the GNU General Public License, + version 2 (GPLv2) (see COPYING for details), subject to the + following clarification. + + With respect to binaries built using the Microsoft(R) Windows + Driver Kit (WDK), GPLv2 does not extend to any code contained in or + derived from the WDK ("WDK Code"). As to WDK Code, by using or + distributing such binaries you agree to be bound by the Microsoft + Software License Terms for the WDK. All WDK Code is considered by + the GPLv2 licensors to qualify for the special exception stated in + section 3 of GPLv2 (commonly known as the system library + exception). + + There is NO WARRANTY for this software, express or implied, + including the implied warranties of NON-INFRINGEMENT, TITLE, + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#include "os_dep.h" +#include "qxldd.h" +#include "utils.h" +#include "res.h" +#include "rop.h" +#include "surface.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; + UINT32 surface_id; + + if (!(pdev = (PDev *)surf->dhpdev)) { + DEBUG_PRINT((NULL, 0, "%s: err no pdev\n", __FUNCTION__)); + return FALSE; + } + + PUNT_IF_DISABLED(pdev); + + surface_id = GetSurfaceId(surf); + + 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, surface_id))) { + return FALSE; + } + + if (opaque_rect) { + ASSERT(pdev, back_brash && brushs_origin); + if (!QXLGetBrush(pdev, drawable, &drawable->u.text.back_brush, back_brash, brushs_origin, + &drawable->surfaces_dest[0], &drawable->surfaces_rects[0])) { + goto error; + } + CopyRect(&drawable->u.text.back_area, &area); + drawable->u.text.back_mode = SPICE_ROPD_OP_PUT; + drawable->effect = QXL_EFFECT_OPAQUE; + } else { + drawable->u.text.back_brush.type = SPICE_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 = SPICE_BRUSH_TYPE_NONE; + } else if (!QXLGetBrush(pdev, drawable, &drawable->u.text.fore_brush, fore_brush, + brushs_origin, &drawable->surfaces_dest[1], + &drawable->surfaces_rects[1])) { + 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/xddm/display/utils.h b/xddm/display/utils.h new file mode 100644 index 0000000..a8d0de6 --- /dev/null +++ b/xddm/display/utils.h @@ -0,0 +1,123 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This software is licensed under the GNU General Public License, + version 2 (GPLv2) (see COPYING for details), subject to the + following clarification. + + With respect to binaries built using the Microsoft(R) Windows + Driver Kit (WDK), GPLv2 does not extend to any code contained in or + derived from the WDK ("WDK Code"). As to WDK Code, by using or + distributing such binaries you agree to be bound by the Microsoft + Software License Terms for the WDK. All WDK Code is considered by + the GPLv2 licensors to qualify for the special exception stated in + section 3 of GPLv2 (commonly known as the system library + exception). + + There is NO WARRANTY for this software, express or implied, + including the implied warranties of NON-INFRINGEMENT, TITLE, + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#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) ((UINT64)&((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 CopyRectPoint(dest, src, width, height) \ + (dest)->left = (src)->x; \ + (dest)->right = (src)->x + width; \ + (dest)->top = (src)->y; \ + (dest)->bottom = (src)->y + height; + +#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/xddm/include/murmur_hash2a.h b/xddm/include/murmur_hash2a.h new file mode 100644 index 0000000..51da7db --- /dev/null +++ b/xddm/include/murmur_hash2a.h @@ -0,0 +1,152 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This software is licensed under the GNU General Public License, + version 2 (GPLv2) (see COPYING for details), subject to the + following clarification. + + With respect to binaries built using the Microsoft(R) Windows + Driver Kit (WDK), GPLv2 does not extend to any code contained in or + derived from the WDK ("WDK Code"). As to WDK Code, by using or + distributing such binaries you agree to be bound by the Microsoft + Software License Terms for the WDK. All WDK Code is considered by + the GPLv2 licensors to qualify for the special exception stated in + section 3 of GPLv2 (commonly known as the system library + exception). + + There is NO WARRANTY for this software, express or implied, + including the implied warranties of NON-INFRINGEMENT, TITLE, + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +//Some modifications by Red Hat any bug is probably our fault + +//----------------------------------------------------------------------------- +// MurmurHash2A, by Austin Appleby + +// This is a variant of MurmurHash2 modified to use the Merkle-Damgard +// construction. Bulk speed should be identical to Murmur2, small-key speed +// will be 10%-20% slower due to the added overhead at the end of the hash. + +// This variant fixes a minor issue where null keys were more likely to +// collide with each other than expected, and also makes the algorithm +// more amenable to incremental implementations. All other caveats from +// MurmurHash2 still apply. + +#ifndef __MURMUR_HASH2A_H +#define __MURMUR_HASH2A_H + +#include <windef.h> +#include "os_dep.h" + +typedef UINT32 uint32_t; +typedef UINT16 uint16_t; +typedef UINT8 uint8_t; + +#define mmix(h,k) { k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; } + +_inline uint32_t MurmurHash2A(const void * key, uint32_t len, uint32_t seed ) +{ + const uint32_t m = 0x5bd1e995; + const uint32_t r = 24; + uint32_t l = len; + uint32_t t = 0; + + const uint8_t * data = (const uint8_t *)key; + + uint32_t h = seed; + + while (len >= 4) { + uint32_t k = *(uint32_t*)data; + + mmix(h,k); + + data += 4; + len -= 4; + } + + switch (len) { + case 3: t ^= data[2] << 16; + case 2: t ^= data[1] << 8; + case 1: t ^= data[0]; + }; + + mmix(h,t); + mmix(h,l); + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; +} + +_inline uint32_t MurmurHash2AJump3(const uint32_t * key, uint32_t len, uint32_t seed ) +{ + uint32_t m = 0x5bd1e995; + uint32_t r = 24; + uint32_t l = len << 2; + + const uint8_t * data = (const uint8_t *)key; + + uint32_t h = seed; + + while (len >= 4) { + uint32_t k = *(uint32_t*)data; + uint32_t tmp; + + data += 4; + tmp = *(uint32_t *)data; + k = k << 8; + k |= (uint8_t)tmp; + mmix(h,k); + + k = tmp << 8; + k = k & 0xffff0000; + data += 4; + tmp = *(uint32_t *)data; + k |= (uint16_t)(tmp >> 8); + mmix(h,k); + + data += 4; + k = *(uint32_t *)data; + k = k << 8; + k |= (uint8_t)tmp; + mmix(h,k); + + data += 4; + len -= 4; + } + + while (len >= 1) { + uint32_t k = *(uint32_t*)data; + + k = k << 8; + mmix(h,k); + + data += 4; + len--; + } + + h *= m; + mmix(h,l); + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; +} + + +_inline uint32_t murmurhash2a(const void *key, size_t length, uint32_t initval) +{ + return MurmurHash2A(key, length, initval); +} + +_inline uint32_t murmurhash2ajump3(const uint32_t *key, size_t length, uint32_t initval) +{ + return MurmurHash2AJump3(key, length, initval); +} +#endif + diff --git a/xddm/include/os_dep.h b/xddm/include/os_dep.h new file mode 100644 index 0000000..ad229e2 --- /dev/null +++ b/xddm/include/os_dep.h @@ -0,0 +1,41 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This software is licensed under the GNU General Public License, + version 2 (GPLv2) (see COPYING for details), subject to the + following clarification. + + With respect to binaries built using the Microsoft(R) Windows + Driver Kit (WDK), GPLv2 does not extend to any code contained in or + derived from the WDK ("WDK Code"). As to WDK Code, by using or + distributing such binaries you agree to be bound by the Microsoft + Software License Terms for the WDK. All WDK Code is considered by + the GPLv2 licensors to qualify for the special exception stated in + section 3 of GPLv2 (commonly known as the system library + exception). + + There is NO WARRANTY for this software, express or implied, + including the implied warranties of NON-INFRINGEMENT, TITLE, + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#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/xddm/include/qxl_driver.h b/xddm/include/qxl_driver.h new file mode 100644 index 0000000..677ee17 --- /dev/null +++ b/xddm/include/qxl_driver.h @@ -0,0 +1,127 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This software is licensed under the GNU General Public License, + version 2 (GPLv2) (see COPYING for details), subject to the + following clarification. + + With respect to binaries built using the Microsoft(R) Windows + Driver Kit (WDK), GPLv2 does not extend to any code contained in or + derived from the WDK ("WDK Code"). As to WDK Code, by using or + distributing such binaries you agree to be bound by the Microsoft + Software License Terms for the WDK. All WDK Code is considered by + the GPLv2 licensors to qualify for the special exception stated in + section 3 of GPLv2 (commonly known as the system library + exception). + + There is NO WARRANTY for this software, express or implied, + including the implied warranties of NON-INFRINGEMENT, TITLE, + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#ifndef _H_QXL_DRIVER +#define _H_QXL_DRIVER + +#include <spice\qxl_dev.h> +#include <spice\qxl_windows.h> + +#if (WINVER < 0x0501) +#include "wdmhelper.h" +#endif + +enum { + FIRST_AVIL_IOCTL_FUNC = 0x800, + QXL_GET_INFO_FUNC = FIRST_AVIL_IOCTL_FUNC, + QXL_SET_CUSTOM_DISPLAY +}; + +#define IOCTL_QXL_GET_INFO \ + CTL_CODE(FILE_DEVICE_VIDEO, QXL_GET_INFO_FUNC, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define IOCTL_QXL_SET_CUSTOM_DISPLAY \ + CTL_CODE(FILE_DEVICE_VIDEO, QXL_SET_CUSTOM_DISPLAY, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define QXL_DRIVER_INFO_VERSION 3 + +typedef struct MemSlot { + UINT8 generation; + UINT64 start_phys_addr; + UINT64 end_phys_addr; + UINT64 start_virt_addr; + UINT64 end_virt_addr; +} MemSlot; + +typedef struct QXLDriverInfo { + UINT32 version; + QXLCommandRing *cmd_ring; + QXLCursorRing *cursor_ring; + QXLReleaseRing *release_ring; + PUCHAR notify_cmd_port; + PUCHAR notify_cursor_port; + PUCHAR notify_oom_port; + PUCHAR update_area_async_port; + PUCHAR memslot_add_async_port; + PUCHAR create_primary_async_port; + PUCHAR destroy_primary_async_port; + PUCHAR destroy_surface_async_port; + PUCHAR destroy_all_surfaces_async_port; + PUCHAR flush_surfaces_async_port; + PUCHAR flush_release_port; + PEVENT display_event; + PEVENT cursor_event; + PEVENT sleep_event; + PEVENT io_cmd_event; + + UINT32 num_pages; + void *io_pages_virt; + UINT64 io_pages_phys; + + UINT8 *surface0_area; + UINT32 surface0_area_size; + + UINT32 *update_id; + UINT32 *compression_level; + + PUCHAR update_area_port; + QXLRect *update_area; + UINT32 *update_surface; + + UINT32 *mm_clock; + + PUCHAR log_port; + UINT8 *log_buf; + UINT32 *log_level; +#if (WINVER < 0x0501) + PQXLWaitForEvent WaitForEvent; +#endif + UINT8 num_mem_slot; + UINT8 main_mem_slot_id; + UINT8 slot_id_bits; + UINT8 slot_gen_bits; + UINT8 *slots_generation; + UINT64 *ram_slot_start; + UINT64 *ram_slot_end; + MemSlot main_mem_slot; + + PUCHAR destroy_surface_wait_port; + PUCHAR create_primary_port; + PUCHAR destroy_primary_port; + PUCHAR memslot_add_port; + PUCHAR memslot_del_port; + PUCHAR destroy_all_surfaces_port; + + UCHAR pci_revision; + + UINT32 dev_id; + + QXLSurfaceCreate *primary_surface_create; + + UINT32 n_surfaces; + + UINT64 fb_phys; + + UINT8 create_non_primary_surfaces; +} QXLDriverInfo; + +#endif + diff --git a/xddm/include/stdint.h b/xddm/include/stdint.h new file mode 100644 index 0000000..f825d4b --- /dev/null +++ b/xddm/include/stdint.h @@ -0,0 +1,397 @@ +/* ISO C9x 7.18 Integer types <stdint.h> + + * Based on ISO/IEC SC22/WG14 9899 Committee draft (SC22 N2794) + + * + + * THIS SOFTWARE IS NOT COPYRIGHTED + + * + + * Contributor: Danny Smith <danny_r_smith_2001@yahoo.co.nz> + + * + + * This source code is offered for use in the public domain. You may + + * use, modify or distribute it freely. + + * + + * This code is distributed in the hope that it will be useful but + + * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY + + * DISCLAIMED. This includes but is not limited to warranties of + + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + * + + * Date: 2000-12-02 + + */ + + + + + +#ifndef _STDINT_H + +#define _STDINT_H + +#define __need_wint_t + +#define __need_wchar_t + +#include <stddef.h> + + + +#ifdef _WIN32_WCE + +typedef _int64 int64_t; + +typedef unsigned _int64 uint64_t; + +#else + +typedef long long int64_t; + +typedef unsigned long long uint64_t; + +#endif /* _WIN32_WCE */ + + + +/* 7.18.1.1 Exact-width integer types */ + +typedef signed char int8_t; + +typedef unsigned char uint8_t; + +typedef short int16_t; + +typedef unsigned short uint16_t; + +typedef int int32_t; + +typedef unsigned uint32_t; + + + +/* 7.18.1.2 Minimum-width integer types */ + +typedef signed char int_least8_t; + +typedef unsigned char uint_least8_t; + +typedef short int_least16_t; + +typedef unsigned short uint_least16_t; + +typedef int int_least32_t; + +typedef unsigned uint_least32_t; + +#ifndef _WIN32_WCE + +typedef long long int_least64_t; + +typedef unsigned long long uint_least64_t; + +#endif + + + +/* 7.18.1.3 Fastest minimum-width integer types + + * Not actually guaranteed to be fastest for all purposes + + * Here we use the exact-width types for 8 and 16-bit ints. + + */ + +typedef char int_fast8_t; + +typedef unsigned char uint_fast8_t; + +typedef short int_fast16_t; + +typedef unsigned short uint_fast16_t; + +typedef int int_fast32_t; + +typedef unsigned int uint_fast32_t; + +#ifndef _WIN32_WCE + +typedef long long int_fast64_t; + +typedef unsigned long long uint_fast64_t; + +#endif + + + +/* 7.18.1.4 Integer types capable of holding object pointers */ + +#ifndef _WIN64 + +typedef int intptr_t; + +typedef unsigned uintptr_t; + +#endif + +/* 7.18.1.5 Greatest-width integer types */ + +#ifndef _WIN32_WCE + +typedef long long intmax_t; + +typedef unsigned long long uintmax_t; + +#endif + + + +/* 7.18.2 Limits of specified-width integer types */ + +#if !defined ( __cplusplus) || defined (__STDC_LIMIT_MACROS) + + + +/* 7.18.2.1 Limits of exact-width integer types */ + +#define INT8_MIN (-128) + +#define INT16_MIN (-32768) + +#define INT32_MIN (-2147483647 - 1) + +#define INT64_MIN (-9223372036854775807LL - 1) + + + +#define INT8_MAX 127 + +#define INT16_MAX 32767 + +#define INT32_MAX 2147483647 + +#define INT64_MAX 9223372036854775807LL + + + +#define UINT8_MAX 0xff /* 255U */ + +#define UINT16_MAX 0xffff /* 65535U */ + +#define UINT32_MAX 0xffffffff /* 4294967295U */ + +#define UINT64_MAX 0xffffffffffffffffULL /* 18446744073709551615ULL */ + + + +/* 7.18.2.2 Limits of minimum-width integer types */ + +#define INT_LEAST8_MIN INT8_MIN + +#define INT_LEAST16_MIN INT16_MIN + +#define INT_LEAST32_MIN INT32_MIN + +#define INT_LEAST64_MIN INT64_MIN + + + +#define INT_LEAST8_MAX INT8_MAX + +#define INT_LEAST16_MAX INT16_MAX + +#define INT_LEAST32_MAX INT32_MAX + +#define INT_LEAST64_MAX INT64_MAX + + + +#define UINT_LEAST8_MAX UINT8_MAX + +#define UINT_LEAST16_MAX UINT16_MAX + +#define UINT_LEAST32_MAX UINT32_MAX + +#define UINT_LEAST64_MAX UINT64_MAX + + + +/* 7.18.2.3 Limits of fastest minimum-width integer types */ + +#define INT_FAST8_MIN INT8_MIN + +#define INT_FAST16_MIN INT16_MIN + +#define INT_FAST32_MIN INT32_MIN + +#define INT_FAST64_MIN INT64_MIN + + + +#define INT_FAST8_MAX INT8_MAX + +#define INT_FAST16_MAX INT16_MAX + +#define INT_FAST32_MAX INT32_MAX + +#define INT_FAST64_MAX INT64_MAX + + + +#define UINT_FAST8_MAX UINT8_MAX + +#define UINT_FAST16_MAX UINT16_MAX + +#define UINT_FAST32_MAX UINT32_MAX + +#define UINT_FAST64_MAX UINT64_MAX + + + +/* 7.18.2.4 Limits of integer types capable of holding + + object pointers */ + +#define INTPTR_MIN INT32_MIN + +#define INTPTR_MAX INT32_MAX + +#define UINTPTR_MAX UINT32_MAX + + + +/* 7.18.2.5 Limits of greatest-width integer types */ + +#define INTMAX_MIN INT64_MIN + +#define INTMAX_MAX INT64_MAX + +#define UINTMAX_MAX UINT64_MAX + + + +/* 7.18.3 Limits of other integer types */ + +#define PTRDIFF_MIN INT32_MIN + +#define PTRDIFF_MAX INT32_MAX + + + +#define SIG_ATOMIC_MIN INT32_MIN + +#define SIG_ATOMIC_MAX INT32_MAX + + + +#ifndef SIZE_MAX +#define SIZE_MAX UINT32_MAX +#endif + + +#ifndef WCHAR_MIN /* also in wchar.h */ + +#define WCHAR_MIN 0 + +#define WCHAR_MAX 0xffff /* UINT16_MAX */ + +#endif + + + +/* + + * wint_t is unsigned short for compatibility with MS runtime + + */ + +#define WINT_MIN 0 + +#define WINT_MAX 0xffff /* UINT16_MAX */ + + + +#endif /* !defined ( __cplusplus) || defined __STDC_LIMIT_MACROS */ + + + + + +/* 7.18.4 Macros for integer constants */ + +#if !defined ( __cplusplus) || defined (__STDC_CONSTANT_MACROS) + + + +/* 7.18.4.1 Macros for minimum-width integer constants + + + + Accoding to Douglas Gwyn <gwyn@arl.mil>: + + "This spec was changed in ISO/IEC 9899:1999 TC1; in ISO/IEC + + 9899:1999 as initially published, the expansion was required + + to be an integer constant of precisely matching type, which + + is impossible to accomplish for the shorter types on most + + platforms, because C99 provides no standard way to designate + + an integer constant with width less than that of type int. + + TC1 changed this to require just an integer constant + + *expression* with *promoted* type." + +*/ + + + +#define INT8_C(val) ((int8_t) + (val)) + +#define UINT8_C(val) ((uint8_t) + (val##U)) + +#define INT16_C(val) ((int16_t) + (val)) + +#define UINT16_C(val) ((uint16_t) + (val##U)) + + + +#define INT32_C(val) val##L + +#define UINT32_C(val) val##UL + +#define INT64_C(val) val##LL + +#define UINT64_C(val) val##ULL + + + +/* 7.18.4.2 Macros for greatest-width integer constants */ + +#define INTMAX_C(val) INT64_C(val) + +#define UINTMAX_C(val) UINT64_C(val) + + + +#endif /* !defined ( __cplusplus) || defined __STDC_CONSTANT_MACROS */ + + + +#endif + + + diff --git a/xddm/include/wdmhelper.h b/xddm/include/wdmhelper.h new file mode 100644 index 0000000..854a4cc --- /dev/null +++ b/xddm/include/wdmhelper.h @@ -0,0 +1,38 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This software is licensed under the GNU General Public License, + version 2 (GPLv2) (see COPYING for details), subject to the + following clarification. + + With respect to binaries built using the Microsoft(R) Windows + Driver Kit (WDK), GPLv2 does not extend to any code contained in or + derived from the WDK ("WDK Code"). As to WDK Code, by using or + distributing such binaries you agree to be bound by the Microsoft + Software License Terms for the WDK. All WDK Code is considered by + the GPLv2 licensors to qualify for the special exception stated in + section 3 of GPLv2 (commonly known as the system library + exception). + + There is NO WARRANTY for this software, express or implied, + including the implied warranties of NON-INFRINGEMENT, TITLE, + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#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/xddm/miniport/makefile b/xddm/miniport/makefile new file mode 100644 index 0000000..53b9a3d --- /dev/null +++ b/xddm/miniport/makefile @@ -0,0 +1 @@ +!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/xddm/miniport/minimal_snprintf.c b/xddm/miniport/minimal_snprintf.c new file mode 100644 index 0000000..40572c1 --- /dev/null +++ b/xddm/miniport/minimal_snprintf.c @@ -0,0 +1,708 @@ +/* $Id: snprintf.c,v 1.2 2003/12/10 01:35:10 lukem Exp $ */ + +/* + * Copyright Patrick Powell 1995 + * This code is based on code written by Patrick Powell (papowell@astart.com) + * It may be used for any purpose as long as this notice remains intact + * on all source code distributions + */ + +/************************************************************** + * Original: + * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 + * A bombproof version of doprnt (dopr) included. + * Sigh. This sort of thing is always nasty do deal with. Note that + * the version here does not include floating point... + * + * snprintf() is used instead of sprintf() as it does limit checks + * for string length. This covers a nasty loophole. + * + * The other functions are there to prevent NULL pointers from + * causing nast effects. + * + * More Recently: + * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43 + * This was ugly. It is still ugly. I opted out of floating point + * numbers, but the formatter understands just about everything + * from the normal C string format, at least as far as I can tell from + * the Solaris 2.5 printf(3S) man page. + * + * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1 + * Ok, added some minimal floating point support, which means this + * probably requires libm on most operating systems. Don't yet + * support the exponent (e,E) and sigfig (g,G). Also, fmtint() + * was pretty badly broken, it just wasn't being exercised in ways + * which showed it, so that's been fixed. Also, formated the code + * to mutt conventions, and removed dead code left over from the + * original. Also, there is now a builtin-test, just compile with: + * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm + * and run snprintf for results. + * + * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i + * The PGP code was using unsigned hexadecimal formats. + * Unfortunately, unsigned formats simply didn't work. + * + * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8 + * The original code assumed that both snprintf() and vsnprintf() were + * missing. Some systems only have snprintf() but not vsnprintf(), so + * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. + * + * Andrew Tridgell (tridge@samba.org) Oct 1998 + * fixed handling of %.0f + * added test for HAVE_LONG_DOUBLE + * + * Luke Mewburn <lukem@NetBSD.org>, Thu Sep 30 23:28:21 EST 1999 + * cleaned up formatting, autoconf tests + * added long long support + * + **************************************************************/ + +#include "minimal_snprintf.h" + +#define MAX(a,b) ((a)>(b)?(a):(b)) + +static _inline int isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +#if HAVE_LONG_LONG +#define LLONG long long +#else +#define LLONG long +#endif + +static void dopr(char *buffer, size_t maxlen, size_t *retlen, + const char *format, va_list args); +static void fmtstr(char *buffer, size_t * currlen, size_t maxlen, + char *value, int min, int max, int flags); +static void fmtint(char *buffer, size_t * currlen, size_t maxlen, + LLONG value, int base, int min, int max, int flags); +#ifdef SUPPORT_FLOAT +#if HAVE_LONG_DOUBLE +#define LDOUBLE long double +#else +#define LDOUBLE double +#endif + +static void fmtfp(char *buffer, size_t * currlen, size_t maxlen, + LDOUBLE fvalue, int min, int max, int flags); +#endif +static void dopr_outch(char *buffer, size_t * currlen, size_t maxlen, int c); + +/* + * dopr(): poor man's version of doprintf + */ + +/* format read states */ +#define DP_S_DEFAULT 0 +#define DP_S_FLAGS 1 +#define DP_S_MIN 2 +#define DP_S_DOT 3 +#define DP_S_MAX 4 +#define DP_S_MOD 5 +#define DP_S_CONV 6 +#define DP_S_DONE 7 + +/* format flags - Bits */ +#define DP_F_MINUS (1 << 0) +#define DP_F_PLUS (1 << 1) +#define DP_F_SPACE (1 << 2) +#define DP_F_NUM (1 << 3) +#define DP_F_ZERO (1 << 4) +#define DP_F_UP (1 << 5) +#define DP_F_UNSIGNED (1 << 6) + +/* Conversion Flags */ +#define DP_C_SHORT 1 +#define DP_C_LONG 2 +#ifdef SUPPORT_FLOAT +#define DP_C_LDOUBLE 3 +#endif +#define DP_C_LLONG 4 + +#define char_to_int(p) (p - '0') + +static void +dopr(char *buffer, size_t maxlen, size_t *retlen, const char *format, + va_list args) +{ + char ch; + LLONG value; +#ifdef SUPPORT_FLOAT + LDOUBLE fvalue; +#endif + char *strvalue; + int min; + int max; + int state; + int flags; + int cflags; + size_t currlen; + + state = DP_S_DEFAULT; + flags = currlen = cflags = min = 0; + max = -1; + ch = *format++; + + while (state != DP_S_DONE) { + if ((ch == '\0') || (currlen >= maxlen)) + state = DP_S_DONE; + + switch (state) { + case DP_S_DEFAULT: + if (ch == '%') + state = DP_S_FLAGS; + else + dopr_outch(buffer, &currlen, maxlen, ch); + ch = *format++; + break; + case DP_S_FLAGS: + switch (ch) { + case '-': + flags |= DP_F_MINUS; + ch = *format++; + break; + case '+': + flags |= DP_F_PLUS; + ch = *format++; + break; + case ' ': + flags |= DP_F_SPACE; + ch = *format++; + break; + case '#': + flags |= DP_F_NUM; + ch = *format++; + break; + case '0': + flags |= DP_F_ZERO; + ch = *format++; + break; + default: + state = DP_S_MIN; + break; + } + break; + case DP_S_MIN: + if (isdigit((unsigned char) ch)) { + min = 10 * min + char_to_int(ch); + ch = *format++; + } else if (ch == '*') { + min = va_arg(args, int); + ch = *format++; + state = DP_S_DOT; + } else + state = DP_S_DOT; + break; + case DP_S_DOT: + if (ch == '.') { + state = DP_S_MAX; + ch = *format++; + } else + state = DP_S_MOD; + break; + case DP_S_MAX: + if (isdigit((unsigned char) ch)) { + if (max < 0) + max = 0; + max = 10 * max + char_to_int(ch); + ch = *format++; + } else if (ch == '*') { + max = va_arg(args, int); + ch = *format++; + state = DP_S_MOD; + } else + state = DP_S_MOD; + break; + case DP_S_MOD: + switch (ch) { + case 'h': + cflags = DP_C_SHORT; + ch = *format++; + break; + case 'l': + if (*format == 'l') { + cflags = DP_C_LLONG; + format++; + } else + cflags = DP_C_LONG; + ch = *format++; + break; + case 'q': + cflags = DP_C_LLONG; + ch = *format++; + break; +#ifdef SUPPORT_FLOAT + case 'L': + cflags = DP_C_LDOUBLE; + ch = *format++; + break; +#endif + default: + break; + } + state = DP_S_CONV; + break; + case DP_S_CONV: + switch (ch) { + case 'd': + case 'i': + switch (cflags) { + case DP_C_SHORT: + value = va_arg(args, int); + break; + case DP_C_LONG: + value = va_arg(args, long int); + break; + case DP_C_LLONG: + value = va_arg(args, LLONG); + break; + default: + value = va_arg(args, int); + break; + } + fmtint(buffer, &currlen, maxlen, value, 10, + min, max, flags); + break; + case 'X': + flags |= DP_F_UP; + /* FALLTHROUGH */ + case 'x': + case 'o': + case 'u': + flags |= DP_F_UNSIGNED; + switch (cflags) { + case DP_C_SHORT: + value = va_arg(args, unsigned int); + break; + case DP_C_LONG: + value = (LLONG) va_arg(args, + unsigned long int); + break; + case DP_C_LLONG: + value = va_arg(args, unsigned LLONG); + break; + default: + value = (LLONG) va_arg(args, + unsigned int); + break; + } + fmtint(buffer, &currlen, maxlen, value, + ch == 'o' ? 8 : (ch == 'u' ? 10 : 16), + min, max, flags); + break; +#ifdef SUPPORT_FLOAT + case 'f': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg(args, LDOUBLE); + else + fvalue = va_arg(args, double); + /* um, floating point? */ + fmtfp(buffer, &currlen, maxlen, fvalue, min, + max, flags); + break; + case 'E': + flags |= DP_F_UP; + case 'e': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg(args, LDOUBLE); + else + fvalue = va_arg(args, double); + break; + case 'G': + flags |= DP_F_UP; + case 'g': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg(args, LDOUBLE); + else + fvalue = va_arg(args, double); + break; +#endif // SUPPORT_FLOAT + case 'c': + dopr_outch(buffer, &currlen, maxlen, + va_arg(args, int)); + break; + case 's': + strvalue = va_arg(args, char *); + if (max < 0) + max = maxlen; /* ie, no max */ + fmtstr(buffer, &currlen, maxlen, strvalue, + min, max, flags); + break; + case 'p': + value = (long)va_arg(args, void *); + fmtint(buffer, &currlen, maxlen, + value, 16, min, max, flags); + break; + case 'n': +/* XXX */ + if (cflags == DP_C_SHORT) { + short int *num; + num = va_arg(args, short int *); + *num = (short)currlen; + } else if (cflags == DP_C_LONG) { /* XXX */ + long int *num; + num = va_arg(args, long int *); + *num = (long int) currlen; + } else if (cflags == DP_C_LLONG) { /* XXX */ + LLONG *num; + num = va_arg(args, LLONG *); + *num = (LLONG) currlen; + } else { + int *num; + num = va_arg(args, int *); + *num = currlen; + } + break; + case '%': + dopr_outch(buffer, &currlen, maxlen, ch); + break; + case 'w': + /* not supported yet, treat as next char */ + ch = *format++; + break; + default: + /* Unknown, skip */ + break; + } + ch = *format++; + state = DP_S_DEFAULT; + flags = cflags = min = 0; + max = -1; + break; + case DP_S_DONE: + break; + default: + /* hmm? */ + break; /* some picky compilers need this */ + } + } + if (currlen >= maxlen - 1) + currlen = maxlen - 1; + buffer[currlen] = '\0'; + *retlen = currlen; +} + +static void +fmtstr(char *buffer, size_t *currlen, size_t maxlen, char *value, + int min, int max, int flags) +{ + int padlen, strln; /* amount to pad */ + int cnt = 0; + + if (value == 0) { + value = "<NULL>"; + } + for (strln = 0; value[strln]; ++strln) + ; /* strlen */ + padlen = min - strln; + if (padlen < 0) + padlen = 0; + if (flags & DP_F_MINUS) + padlen = -padlen; /* Left Justify */ + + while ((padlen > 0) && (cnt < max)) { + dopr_outch(buffer, currlen, maxlen, ' '); + --padlen; + ++cnt; + } + while (*value && (cnt < max)) { + dopr_outch(buffer, currlen, maxlen, *value++); + ++cnt; + } + while ((padlen < 0) && (cnt < max)) { + dopr_outch(buffer, currlen, maxlen, ' '); + ++padlen; + ++cnt; + } +} +/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ + +static void +fmtint(char *buffer, size_t *currlen, size_t maxlen, LLONG value, int base, + int min, int max, int flags) +{ + int signvalue = 0; + unsigned LLONG uvalue; + char convert[20]; + int place = 0; + int spadlen = 0; /* amount to space pad */ + int zpadlen = 0; /* amount to zero pad */ + int caps = 0; + + if (max < 0) + max = 0; + + uvalue = value; + + if (!(flags & DP_F_UNSIGNED)) { + if (value < 0) { + signvalue = '-'; + uvalue = -value; + } else if (flags & DP_F_PLUS) /* Do a sign (+/i) */ + signvalue = '+'; + else if (flags & DP_F_SPACE) + signvalue = ' '; + } + if (flags & DP_F_UP) + caps = 1; /* Should characters be upper case? */ + + do { + convert[place++] = + (caps ? "0123456789ABCDEF" : "0123456789abcdef") + [uvalue % (unsigned) base]; + uvalue = (uvalue / (unsigned) base); + } while (uvalue && (place < 20)); + if (place == 20) + place--; + convert[place] = 0; + + zpadlen = max - place; + spadlen = min - MAX(max, place) - (signvalue ? 1 : 0); + if (zpadlen < 0) + zpadlen = 0; + if (spadlen < 0) + spadlen = 0; + if (flags & DP_F_ZERO) { + zpadlen = MAX(zpadlen, spadlen); + spadlen = 0; + } + if (flags & DP_F_MINUS) + spadlen = -spadlen; /* Left Justifty */ + +#ifdef DEBUG_SNPRINTF + printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n", + zpadlen, spadlen, min, max, place); +#endif + + /* Spaces */ + while (spadlen > 0) { + dopr_outch(buffer, currlen, maxlen, ' '); + --spadlen; + } + + /* Sign */ + if (signvalue) + dopr_outch(buffer, currlen, maxlen, signvalue); + + /* Zeros */ + if (zpadlen > 0) { + while (zpadlen > 0) { + dopr_outch(buffer, currlen, maxlen, '0'); + --zpadlen; + } + } + /* Digits */ + while (place > 0) + dopr_outch(buffer, currlen, maxlen, convert[--place]); + + /* Left Justified spaces */ + while (spadlen < 0) { + dopr_outch(buffer, currlen, maxlen, ' '); + ++spadlen; + } +} + +#ifdef SUPPORT_FLOAT +static LDOUBLE +abs_val(LDOUBLE value) +{ + LDOUBLE result = value; + + if (value < 0) + result = -value; + + return result; +} + +static LDOUBLE +pow10(int exp) +{ + LDOUBLE result = 1; + + while (exp) { + result *= 10; + exp--; + } + + return result; +} + +static long +round(LDOUBLE value) +{ + long intpart; + + intpart = (long) value; + value = value - intpart; + if (value >= 0.5) + intpart++; + + return intpart; +} + +static void +fmtfp(char *buffer, size_t *currlen, size_t maxlen, LDOUBLE fvalue, + int min, int max, int flags) +{ + int signvalue = 0; + LDOUBLE ufvalue; + char iconvert[20]; + char fconvert[20]; + int iplace = 0; + int fplace = 0; + int padlen = 0; /* amount to pad */ + int zpadlen = 0; + int caps = 0; + long intpart; + long fracpart; + + /* AIX manpage says the default is 0, but Solaris says the default is + * 6, and sprintf on AIX defaults to 6 */ + if (max < 0) + max = 6; + + ufvalue = abs_val(fvalue); + + if (fvalue < 0) + signvalue = '-'; + else if (flags & DP_F_PLUS) /* Do a sign (+/i) */ + signvalue = '+'; + else if (flags & DP_F_SPACE) + signvalue = ' '; + +#if 0 + if (flags & DP_F_UP) + caps = 1; /* Should characters be upper case? */ +#endif + + intpart = (long) ufvalue; + + /* Sorry, we only support 9 digits past the decimal because of our + * conversion method */ + if (max > 9) + max = 9; + + /* We "cheat" by converting the fractional part to integer by + * multiplying by a factor of 10 */ + fracpart = round((pow10(max)) * (ufvalue - intpart)); + + if (fracpart >= pow10(max)) { + intpart++; + fracpart -= (long)pow10(max); + } +#ifdef DEBUG_SNPRINTF + printf("fmtfp: %g %d.%d min=%d max=%d\n", + (double) fvalue, intpart, fracpart, min, max); +#endif + + /* Convert integer part */ + do { + iconvert[iplace++] = + (caps ? "0123456789ABCDEF" + : "0123456789abcdef")[intpart % 10]; + intpart = (intpart / 10); + } while (intpart && (iplace < 20)); + if (iplace == 20) + iplace--; + iconvert[iplace] = 0; + + /* Convert fractional part */ + do { + fconvert[fplace++] = + (caps ? "0123456789ABCDEF" + : "0123456789abcdef")[fracpart % 10]; + fracpart = (fracpart / 10); + } while (fracpart && (fplace < 20)); + if (fplace == 20) + fplace--; + fconvert[fplace] = 0; + + /* -1 for decimal point, another -1 if we are printing a sign */ + padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); + zpadlen = max - fplace; + if (zpadlen < 0) + zpadlen = 0; + if (padlen < 0) + padlen = 0; + if (flags & DP_F_MINUS) + padlen = -padlen; /* Left Justifty */ + + if ((flags & DP_F_ZERO) && (padlen > 0)) { + if (signvalue) { + dopr_outch(buffer, currlen, maxlen, signvalue); + --padlen; + signvalue = 0; + } + while (padlen > 0) { + dopr_outch(buffer, currlen, maxlen, '0'); + --padlen; + } + } + while (padlen > 0) { + dopr_outch(buffer, currlen, maxlen, ' '); + --padlen; + } + if (signvalue) + dopr_outch(buffer, currlen, maxlen, signvalue); + + while (iplace > 0) + dopr_outch(buffer, currlen, maxlen, iconvert[--iplace]); + + +#ifdef DEBUG_SNPRINTF + printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen); +#endif + + /* + * Decimal point. This should probably use locale to find the correct + * char to print out. + */ + if (max > 0) { + dopr_outch(buffer, currlen, maxlen, '.'); + + while (fplace > 0) + dopr_outch(buffer, currlen, maxlen, fconvert[--fplace]); + } + while (zpadlen > 0) { + dopr_outch(buffer, currlen, maxlen, '0'); + --zpadlen; + } + + while (padlen < 0) { + dopr_outch(buffer, currlen, maxlen, ' '); + ++padlen; + } +} +#endif // SUPPORT_FLOAT + +static void +dopr_outch(char *buffer, size_t *currlen, size_t maxlen, int c) +{ + if (*currlen < maxlen) + buffer[(*currlen)++] = (char)c; +} + +int +vsnprintf(char *str, size_t count, const char *fmt, va_list args) +{ + size_t retlen; + + str[0] = 0; + dopr(str, count, &retlen, fmt, args); + return (retlen); +} + +/* VARARGS3 */ +int +snprintf(char *str, size_t count, const char *fmt, ...) +{ + va_list ap; + int rv; + + va_start(ap, fmt); + rv = vsnprintf(str, count, fmt, ap); + va_end(ap); + return (rv); +} diff --git a/xddm/miniport/minimal_snprintf.h b/xddm/miniport/minimal_snprintf.h new file mode 100644 index 0000000..e26f1da --- /dev/null +++ b/xddm/miniport/minimal_snprintf.h @@ -0,0 +1,9 @@ +#ifndef MINIMAL_SNPRINTF_H +#define MINIMAL_SNPRINTF_H + +#include <stdarg.h> + +int snprintf(char *str, size_t count, const char *fmt, ...); +int vsnprintf(char *str, size_t count, const char *fmt, va_list args); + +#endif // MINIMAL_SNPRINTF_H diff --git a/xddm/miniport/qxl.c b/xddm/miniport/qxl.c new file mode 100644 index 0000000..58ba15e --- /dev/null +++ b/xddm/miniport/qxl.c @@ -0,0 +1,1311 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This software is licensed under the GNU General Public License, + version 2 (GPLv2) (see COPYING for details), subject to the + following clarification. + + With respect to binaries built using the Microsoft(R) Windows + Driver Kit (WDK), GPLv2 does not extend to any code contained in or + derived from the WDK ("WDK Code"). As to WDK Code, by using or + distributing such binaries you agree to be bound by the Microsoft + Software License Terms for the WDK. All WDK Code is considered by + the GPLv2 licensors to qualify for the special exception stated in + section 3 of GPLv2 (commonly known as the system library + exception). + + There is NO WARRANTY for this software, express or implied, + including the implied warranties of NON-INFRINGEMENT, TITLE, + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#include "os_dep.h" +#include "qxl.h" +#if (WINVER < 0x0501) +#include "wdmhelper.h" +#endif +#include "minimal_snprintf.h" + +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; + PUCHAR io_port; + + UCHAR pci_revision; + + 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; + UINT8 *vram_start; + + ULONG current_mode; + ULONG n_modes; + ULONG custom_mode; + PVIDEO_MODE_INFORMATION modes; + + PEVENT display_event; + PEVENT cursor_event; + PEVENT sleep_event; + PEVENT io_cmd_event; + + MemSlot *mem_slots; + + char *log_buf; + PUCHAR log_port; + + UINT8 create_non_primary_surfaces; +} QXLExtension; + +#define QXL_ALLOC_TAG '_lxq' + +#define DBG_LEVEL 10 + +#define QXL_MINIPORT_DEBUG_PREFIX "qxlmp: " + +void DebugPrintV(char *log_buf, PUCHAR log_port, const char *message, const char *func, va_list ap) +{ + int n, n_strlen; + + if (log_buf && log_port) { + /* + * TODO: use a shared semaphore with display code. + * In practice this is not a problem, since miniport runs either on ioctls (sync) + * or before display is brought up or when it is brought down. + * Also the worst that can happen is overwriting a message (not seen in practice). + */ + snprintf(log_buf, QXL_LOG_BUF_SIZE, QXL_MINIPORT_DEBUG_PREFIX); + vsnprintf(log_buf + strlen(QXL_MINIPORT_DEBUG_PREFIX), + QXL_LOG_BUF_SIZE - strlen(QXL_MINIPORT_DEBUG_PREFIX), + message, ap); + VideoPortWritePortUchar(log_port, 0); + } else { + VideoDebugPrint((0, (PCHAR)message, ap)); + } +} + +void DebugPrint(QXLExtension *dev, UINT32 level, const char *message, const char *func, ...) +{ + va_list ap; + + if (dev && dev->rom && level > dev->rom->log_level) { + return; + } + va_start(ap, message); + DebugPrintV(dev ? dev->log_buf : NULL, dev ? dev->log_port : 0, message, func, ap); + va_end(ap); +} + +ULONG DriverEntry(PVOID context1, PVOID context2) +{ + VIDEO_HW_INITIALIZATION_DATA init_data; + ULONG ret; + + PAGED_CODE(); + + DEBUG_PRINT((NULL, 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((NULL, 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((NULL, 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((dev, 0, "%s\n", __FUNCTION__)); + + if ((dev->pci_revision == QXL_REVISION_STABLE_V06 && + range->RangeLength < QXL_IO_DESTROY_ALL_SURFACES + 1) + || (dev->pci_revision > QXL_REVISION_STABLE_V06 && + range->RangeLength < QXL_IO_FLUSH_RELEASE + 1) + || !range->RangeInIoSpace) { + DEBUG_PRINT((dev, 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((dev, 0, "%s: get io base failed\n", __FUNCTION__)); + return ERROR_NOT_ENOUGH_MEMORY; + } + + dev->io_base = io_base; + dev->io_port = (PUCHAR)range->RangeStart.LowPart; + dev->log_port = dev->io_port + QXL_IO_LOG; + DEBUG_PRINT((dev, 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((dev, 0, "%s\n", __FUNCTION__)); + + if (rom_size < sizeof(QXLRom) || range->RangeInIoSpace) { + DEBUG_PRINT((dev, 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((dev, 0, "%s: map rom filed\n", __FUNCTION__)); + return error; + } + + if (rom_size < range->RangeLength) { + DEBUG_PRINT((dev, 0, "%s: short rom map\n", __FUNCTION__)); + error = ERROR_NOT_ENOUGH_MEMORY; + goto err; + } + + if (((QXLRom*)rom)->magic != QXL_ROM_MAGIC) { + DEBUG_PRINT((dev, 0, "%s: bad rom magic\n", __FUNCTION__)); + error = ERROR_INVALID_DATA; + goto err; + } + + dev->rom = rom; + dev->rom_size = range->RangeLength; + DEBUG_PRINT((dev, 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((dev, 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((dev, 0, "%s\n", __FUNCTION__)); + + if (ram_size < sizeof(QXLRam) + dev->rom->ram_header_offset || range->RangeInIoSpace) { + DEBUG_PRINT((dev, 0, "%s: bad ram range\n", __FUNCTION__)); + return ERROR_INVALID_DATA; + } + + if (ram_size < dev->rom->num_pages << PAGE_SHIFT) { + DEBUG_PRINT((dev, 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((dev, 0, "%s: map ram filed\n", __FUNCTION__)); + return error; + } + + if (ram_size < range->RangeLength) { + DEBUG_PRINT((dev, 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((dev, 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; + dev->log_buf = dev->ram_header->log_buf; + DEBUG_PRINT((dev, 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((dev, 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) +{ + UINT8 *vram_addr = NULL; + ULONG vram_mapped_size = range->RangeLength; + ULONG io_space = VIDEO_MEMORY_SPACE_MEMORY; + VP_STATUS error; + + PAGED_CODE(); + DEBUG_PRINT((dev, 0, "%s\n", __FUNCTION__)); + + if (range->RangeLength == 0 || range->RangeInIoSpace) { + DEBUG_PRINT((dev, 0, "%s: bad mem range\n", __FUNCTION__)); + return ERROR_INVALID_DATA; + } + + if ((error = VideoPortMapMemory(dev, range->RangeStart, + &vram_mapped_size, + &io_space, + &vram_addr))) { + DEBUG_PRINT((dev, 0, "%s: map vram failed\n", __FUNCTION__)); + return error; + } + + if (vram_mapped_size < range->RangeLength) { + DEBUG_PRINT((dev, 0, "%s: vram shrinked\n", __FUNCTION__)); + VideoPortUnmapMemory(dev, vram_addr, NULL); + return ERROR_NOT_ENOUGH_MEMORY; + } + dev->vram_physical = range->RangeStart; + dev->vram_start = vram_addr; + dev->vram_size = range->RangeLength; + DEBUG_PRINT((dev, 0, "%s: OK, vram 0x%lx size %lu vaddr 0x%lx\n", __FUNCTION__, + (ULONG)range->RangeStart.QuadPart, range->RangeLength, dev->vram_start)); + 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((dev, 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((dev, 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((dev, 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_STABLE) { + DEBUG_PRINT((dev, 0, "%s: bad vendor id 0x%x expectes 0x%x\n", + __FUNCTION__, pci_conf.DeviceID, QXL_DEVICE_ID_STABLE)); + return ERROR_INVALID_PARAMETER; + } + + if (pci_conf.RevisionID < QXL_REVISION_STABLE_V06) { + DEBUG_PRINT((dev, 0, "%s: bad revision 0x%x expectes at least 0x%x\n", + __FUNCTION__, pci_conf.RevisionID, QXL_REVISION_STABLE_V06)); + return ERROR_INVALID_PARAMETER; + } + dev->pci_revision = pci_conf.RevisionID; + + VideoPortZeroMemory(ranges, sizeof(VIDEO_ACCESS_RANGE) * n_ranges); + if ((error = VideoPortGetAccessRanges(dev, 0, NULL, n_ranges, + ranges, NULL, NULL, + NULL)) != NO_ERROR ) { + DEBUG_PRINT((dev, 0, "%s: get access ranges failed status %u\n", __FUNCTION__, error)); + } + + if (conf_info->BusInterruptLevel == 0 && conf_info->BusInterruptVector == 0) { + DEBUG_PRINT((dev, 0, "%s: no interrupt\n", __FUNCTION__)); + error = ERROR_INVALID_DATA; + } + +#ifdef DBG + if (error == NO_ERROR) { + int i; + + DEBUG_PRINT((dev, 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((dev, 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((dev, 0, "%s exit %lu\n", __FUNCTION__, error)); + return error; +} + +#if defined(ALLOC_PRAGMA) +void FillVidModeBPP(VIDEO_MODE_INFORMATION *pMode, ULONG bitsR, ULONG bitsG, ULONG bitsB, + ULONG maskR, ULONG maskG, ULONG maskB); +#pragma alloc_text(PAGE, FillVidModeBPP) +#endif + +/* Fills given video mode BPP related fields */ +void FillVidModeBPP(VIDEO_MODE_INFORMATION *pMode, ULONG bitsR, ULONG bitsG, ULONG bitsB, + ULONG maskR, ULONG maskG, ULONG maskB) +{ + pMode->NumberRedBits = bitsR; + pMode->NumberGreenBits = bitsG; + pMode->NumberBlueBits = bitsB; + pMode->RedMask = maskR; + pMode->GreenMask = maskG; + pMode->BlueMask = maskB; +} + +#if defined(ALLOC_PRAGMA) +VP_STATUS FillVidModeInfo(VIDEO_MODE_INFORMATION *pMode, ULONG xres, ULONG yres, ULONG bpp, ULONG index); +#pragma alloc_text(PAGE, FillVidModeInfo) +#endif +/* Fills given video mode structure */ +VP_STATUS FillVidModeInfo(VIDEO_MODE_INFORMATION *pMode, ULONG xres, ULONG yres, ULONG bpp, ULONG index) +{ + unsigned bytes_pp = (bpp + 7) / 8; + + if (xres <= 0 || yres <= 0) + return ERROR_INVALID_DATA; + + VideoPortZeroMemory(pMode, sizeof(VIDEO_MODE_INFORMATION)); + + /*Common entries*/ + pMode->Length = sizeof(VIDEO_MODE_INFORMATION); + pMode->ModeIndex = index; + pMode->VisScreenWidth = xres; + pMode->VisScreenHeight = yres; + pMode->ScreenStride = (xres * bytes_pp + 3) & ~0x3; /* Pixman requirement */ + pMode->NumberOfPlanes = 1; + pMode->BitsPerPlane = bpp; + pMode->Frequency = 60; + pMode->XMillimeter = 320; + pMode->YMillimeter = 240; + pMode->VideoMemoryBitmapWidth = xres; + pMode->VideoMemoryBitmapHeight = yres; + pMode->DriverSpecificAttributeFlags = 0; + pMode->AttributeFlags = VIDEO_MODE_GRAPHICS | VIDEO_MODE_COLOR; + + /*BPP related entries*/ + switch (bpp) + { + case 16: + FillVidModeBPP(pMode, 5, 5, 5, 0x7C00, 0x3E0, 0x1F); + break; + case 24: + case 32: + FillVidModeBPP(pMode, 8, 8, 8, 0xFF0000, 0xFF00, 0xFF); + break; + default: + return ERROR_INVALID_DATA; + } + + return NO_ERROR; +} + +#if defined(ALLOC_PRAGMA) +VP_STATUS SetVideoModeInfo(QXLExtension *dev, PVIDEO_MODE_INFORMATION video_mode, QXLMode *qxl_mode); +#pragma alloc_text(PAGE, SetVideoModeInfo) +#endif + +VP_STATUS SetVideoModeInfo(QXLExtension *dev, PVIDEO_MODE_INFORMATION video_mode, QXLMode *qxl_mode) +{ + ULONG color_bits; + PAGED_CODE(); + DEBUG_PRINT((dev, 5, "%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((dev, 5, "%s OK\n", __FUNCTION__)); + return NO_ERROR; +} + + +#if defined(ALLOC_PRAGMA) +VP_STATUS InitModes(QXLExtension *dev); +#pragma alloc_text(PAGE, InitModes) +#endif + +void DestroyMemSlots(QXLExtension *dev) +{ + if (dev->mem_slots) { + VideoPortFreePool(dev, dev->mem_slots); + dev->mem_slots = NULL; + } +} + +VP_STATUS InitMemSlots(QXLExtension *dev) +{ +#if (WINVER < 0x0501) //Win2K + error = VideoPortAllocateBuffer(dev, dev->rom->slots_end * sizeof(MemSlot), &dev->mem_slots); + + if(!dev->mem_slots || error != NO_ERROR) { + return ERROR_NOT_ENOUGH_MEMORY; + } +#else + if (!(dev->mem_slots = VideoPortAllocatePool(dev, VpPagedPool, + dev->rom->slots_end * sizeof(MemSlot), + QXL_ALLOC_TAG))) { + DEBUG_PRINT((dev, 0, "%s: alloc mem failed\n", __FUNCTION__)); + return ERROR_NOT_ENOUGH_MEMORY; + } +#endif + VideoPortZeroMemory(dev->mem_slots, dev->rom->slots_end * sizeof(MemSlot)); + + return NO_ERROR; +} + +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((dev, 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((dev, 0, "%s: bad rom size\n", __FUNCTION__)); + return ERROR_INVALID_DATA; + } + + n_modes += 2; +#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((dev, 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 < modes->n_modes; i++) { + error = SetVideoModeInfo(dev, &modes_info[i], &modes->modes[i]); + if (error != NO_ERROR) { + VideoPortFreePool(dev, modes_info); + DEBUG_PRINT((dev, 0, "%s: set video mode failed\n", __FUNCTION__)); + return error; + } + } + + /* 2 dummy modes for custom display resolution */ + /* This is necessary to bypass Windows mode index check, that + would prevent reusing the same index */ + dev->custom_mode = modes->n_modes; + + for (i = dev->custom_mode; i <= dev->custom_mode + 1; ++i) { + memcpy(&modes_info[i], &modes_info[0], sizeof(VIDEO_MODE_INFORMATION)); + modes_info[i].ModeIndex = i; + } + + dev->n_modes = n_modes; + dev->modes = modes_info; + DEBUG_PRINT((dev, 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->io_cmd_event) { + VideoPortDeleteEvent(dev, dev->io_cmd_event); + } + + if (dev->rom) { + VideoPortUnmapMemory(dev, dev->rom, NULL); + } + + if (dev->ram_start) { + VideoPortUnmapMemory(dev, dev->ram_start, NULL); + } + + if (dev->vram_start) { + VideoPortUnmapMemory(dev, dev->vram_start, NULL); + } + + if (dev->modes) { + VideoPortFreePool(dev, dev->modes); + } + + if (dev->mem_slots) { + DestroyMemSlots(dev); + } + + 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; + PEVENT io_cmd_event = NULL; +#if (WINVER >= 0x0501) + VPOSVERSIONINFO sys_info; +#endif + + PAGED_CODE(); + + DEBUG_PRINT((dev_ext, 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((dev_ext, 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((dev_ext, 0, "%s: err configInfo size\n", __FUNCTION__)); + return ERROR_INVALID_PARAMETER; + } + + if (conf_info->AdapterInterfaceType != PCIBus) { + DEBUG_PRINT((dev_ext, 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((dev_ext, 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((dev_ext, 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((dev_ext, 0, "%s: create sleep event failed %lu\n", + __FUNCTION__, status)); + VideoPortDeleteEvent(dev_ext, display_event); + VideoPortDeleteEvent(dev_ext, cursor_event); + return status; + } + + if ((status = VideoPortCreateEvent(dev_ext, 0, NULL, &io_cmd_event)) != NO_ERROR) { + DEBUG_PRINT((dev_ext, 0, "%s: create io_cmd event failed %lu\n", + __FUNCTION__, status)); + VideoPortDeleteEvent(dev_ext, sleep_event); + 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; + dev_ext->io_cmd_event = io_cmd_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 || + (status = InitMemSlots(dev_ext)) != NO_ERROR) { + DEBUG_PRINT((dev_ext, 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((dev_ext, 0, "%s: write QXL ID failed\n", __FUNCTION__)); + } + + DEBUG_PRINT((dev_ext, 0, "%s: exit %lu\n", __FUNCTION__, status)); + return status; +} + +static BOOLEAN CreateMemSlots(QXLExtension *dev_ext) +{ + QXLMemSlot *slot; + UINT8 slot_id = dev_ext->rom->slots_start; + + if (slot_id >= dev_ext->rom->slots_end) { + DEBUG_PRINT((dev_ext, 0, "%s: start_memslot bigger than nmem_slot\n", __FUNCTION__)); + return FALSE; + } + + dev_ext->mem_slots[slot_id].start_phys_addr = dev_ext->ram_physical.QuadPart; + dev_ext->mem_slots[slot_id].end_phys_addr = dev_ext->mem_slots[slot_id].start_phys_addr + + dev_ext->rom->surface0_area_size + + dev_ext->rom->num_pages * PAGE_SIZE; + + dev_ext->mem_slots[slot_id].start_virt_addr = (UINT64)dev_ext->ram_start; + dev_ext->mem_slots[slot_id].end_virt_addr = dev_ext->mem_slots[slot_id].start_virt_addr + + dev_ext->rom->surface0_area_size + + dev_ext->rom->num_pages * PAGE_SIZE; + + dev_ext->ram_header->mem_slot.mem_start = dev_ext->mem_slots[slot_id].start_phys_addr; + dev_ext->ram_header->mem_slot.mem_end = dev_ext->mem_slots[slot_id].end_phys_addr; + + VideoPortWritePortUchar((PUCHAR)dev_ext->io_port + QXL_IO_MEMSLOT_ADD, slot_id); + + dev_ext->mem_slots[slot_id].generation = dev_ext->rom->slot_generation; + + return TRUE; +} + +#if defined(ALLOC_PRAGMA) +void HWReset(QXLExtension *dev_ext); +#pragma alloc_text(PAGE, HWReset) +#endif + +/* called from HWReset or after returning from sleep from SetPowerState, + * when returning from sleep we don't want to do a redundant QXL_IO_RESET */ +static void ResetDeviceWithoutIO(QXLExtension *dev_ext) +{ + dev_ext->ram_header->int_mask = ~0; + CreateMemSlots(dev_ext); +} + +void HWReset(QXLExtension *dev_ext) +{ + PAGED_CODE(); + DEBUG_PRINT((dev_ext, 0, "%s\n", __FUNCTION__)); + VideoPortWritePortUchar((PUCHAR)dev_ext->io_base + QXL_IO_RESET, 0); + ResetDeviceWithoutIO(dev_ext); + DEBUG_PRINT((dev_ext, 0, "%s: done\n", __FUNCTION__)); +} + +BOOLEAN Initialize(PVOID dev_ext) +{ + PAGED_CODE(); + DEBUG_PRINT((dev_ext, 0, "%s: enter\n", __FUNCTION__)); + HWReset(dev_ext); + DEBUG_PRINT((dev_ext, 0, "%s: done\n", __FUNCTION__)); + return TRUE; +} + +VP_STATUS GetPowerState(PVOID dev_extension, + ULONG hw_id, + PVIDEO_POWER_MANAGEMENT pm_stat) +{ + QXLExtension *dev = dev_extension; + PAGED_CODE(); + DEBUG_PRINT((dev, 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((dev, 0, "%s: OK\n", __FUNCTION__)); + return NO_ERROR; + } + break; + default: + DEBUG_PRINT((dev, 0, "%s: unexpected hw_id %lu\n", __FUNCTION__, hw_id)); + } + DEBUG_PRINT((dev, 0, "%s: ERROR_DEVICE_REINITIALIZATION_NEEDED\n", __FUNCTION__)); + return ERROR_DEVICE_REINITIALIZATION_NEEDED; +} + +#ifdef DBG +static void DebugZeroDeviceMemory(QXLExtension *dev_ext) +{ + // don't zero the memory if the ram_start and vram_start are not initialized (a + // device has been installed but the monitor is disabled) + if (dev_ext->ram_start == 0 || dev_ext->vram_start == 0) { + DEBUG_PRINT((dev_ext, 0, "%s: not zeroing memory (addresses not initialized)\n", __FUNCTION__)); + return; + } + VideoPortZeroMemory(dev_ext->ram_start, dev_ext->ram_size); + VideoPortZeroMemory(dev_ext->vram_start, dev_ext->vram_size); +} +#else +static _inline void DebugZeroDeviceMemory(QXLExtension *dev_ext) +{ +} +#endif + +VP_STATUS SetPowerState(PVOID dev_extension, + ULONG hw_id, + PVIDEO_POWER_MANAGEMENT pm_stat) +{ + QXLExtension *dev_ext = dev_extension; + PAGED_CODE(); + DEBUG_PRINT((dev_ext, 0, "%s (%d): %d: %lu\n", __FUNCTION__, dev_ext->rom->id, hw_id, pm_stat->PowerState)); + + switch (hw_id) { + case DISPLAY_ADAPTER_HW_ID: + switch (pm_stat->PowerState) { + case VideoPowerOn: + ResetDeviceWithoutIO(dev_ext); + break; + case VideoPowerStandBy: + break; + case VideoPowerSuspend: + break; + case VideoPowerOff: + DebugZeroDeviceMemory(dev_ext); + break; + case VideoPowerShutdown: + /* Important: you cannot call out to qxldd.dll here or you get a BSOD. */ + break; + case VideoPowerHibernate: + DebugZeroDeviceMemory(dev_ext); + break; + default: + DEBUG_PRINT((dev_ext, 0, "%s: unexpected power state\n", __FUNCTION__)); + return ERROR_DEVICE_REINITIALIZATION_NEEDED; + } + break; + default: + DEBUG_PRINT((dev_ext, 0, "%s: unexpected hw_id %lu\n", __FUNCTION__, hw_id)); + return ERROR_DEVICE_REINITIALIZATION_NEEDED; + } + return NO_ERROR; +} + +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) +{ + QXLExtension *dev = dev_extension; + PAGED_CODE(); + DEBUG_PRINT((dev, 0, "%s: enter\n", __FUNCTION__)); + + switch (enum_info->ChildIndex) { + case 0: + DEBUG_PRINT((dev, 0, "%s: ACPI id %u\n", __FUNCTION__, enum_info->ACPIHwId)); + return ERROR_NO_MORE_DEVICES; + case 1: + DEBUG_PRINT((dev, 0, "%s: Monitor\n", __FUNCTION__)); + /* + *pChildType = Monitor; + todo: handle EDID + return ERROR_MORE_DATA; + */ + return ERROR_NO_MORE_DEVICES; + } + DEBUG_PRINT((dev, 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((dev_ext, 0, "%s\n", __FUNCTION__)); + + inf = dev_ext->modes; + end = inf + dev_ext->n_modes; + for (; inf < end; inf++) { + if (inf->ModeIndex == mode) { + DEBUG_PRINT((dev_ext, 0, "%s: OK mode %lu res %lu-%lu orientation %lu\n", __FUNCTION__, + mode, inf->VisScreenWidth, inf->VisScreenHeight, + inf->DriverSpecificAttributeFlags )); + return inf; + } + } + DEBUG_PRINT((dev_ext, 0, "%s: mod info not found\n", __FUNCTION__)); + return NULL; +} + +static VP_STATUS SetCustomDisplay(QXLExtension *dev_ext, QXLEscapeSetCustomDisplay *custom_display) +{ + /* alternate custom mode index */ + if (dev_ext->custom_mode == (dev_ext->n_modes - 1)) + dev_ext->custom_mode = dev_ext->n_modes - 2; + else + dev_ext->custom_mode = dev_ext->n_modes - 1; + + return FillVidModeInfo(&dev_ext->modes[dev_ext->custom_mode], + custom_display->xres, custom_display->yres, + custom_display->bpp, + dev_ext->custom_mode); +} + +VP_STATUS QXLRegistryCallback( + PVOID HwDeviceExtension, + PVOID Context, + PWSTR ValueName, + PVOID ValueData, + ULONG ValueLength +) +{ + QXLExtension *dev_ext = HwDeviceExtension; + ULONG *key_ret = (ULONG *)Context; + + DEBUG_PRINT((dev_ext, 60, "%s: length %d, first byte %d\n", __FUNCTION__, + ValueLength, (UINT8)ValueData)); + + if (key_ret) { + *key_ret = *(PULONG)ValueData; + } + return NO_ERROR; +} + +static UINT8 check_non_primary_surfaces_registry_key(QXLExtension *dev_ext) +{ + VP_STATUS ret; + ULONG key_ret; + + ret = VideoPortGetRegistryParameters( + dev_ext, + L"DisableSurfaces", + FALSE, + QXLRegistryCallback, + &key_ret); + if (ret == ERROR_INVALID_PARAMETER) { + dev_ext->create_non_primary_surfaces = 1; + DEBUG_PRINT((dev_ext, 0, "%s: CreateNonPrimarySurfaces key doesn't exist, default to 1\n", + __FUNCTION__)); + } else { + dev_ext->create_non_primary_surfaces = 0; + } + return dev_ext->create_non_primary_surfaces; +} + +BOOLEAN StartIO(PVOID dev_extension, PVIDEO_REQUEST_PACKET packet) +{ + QXLExtension *dev_ext = dev_extension; + VP_STATUS error; + + PAGED_CODE(); + DEBUG_PRINT((dev_ext, 0, "%s %d\n", __FUNCTION__, packet->IoControlCode)); + + switch (packet->IoControlCode) { + case IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES: + DEBUG_PRINT((dev_ext, 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((dev_ext, 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((dev_ext, 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; + + dev_ext->current_mode = request_mode; + DEBUG_PRINT((dev_ext, 0, "%s: mode %u\n", __FUNCTION__, request_mode)); + if (!IsValidMode(dev_ext, request_mode)) { + error = ERROR_INVALID_PARAMETER; + goto err; + } + } + break; + case IOCTL_VIDEO_QUERY_CURRENT_MODE: { + PVIDEO_MODE_INFORMATION inf; + + DEBUG_PRINT((dev_ext, 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->current_mode)) == NULL) { + DEBUG_PRINT((dev_ext, 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; + + DEBUG_PRINT((dev_ext, 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; + } + + ASSERT(((PVIDEO_MEMORY)(packet->InputBuffer))->RequestedVirtualAddress == NULL); + mem_info = packet->OutputBuffer; + mem_info->VideoRamBase = mem_info->FrameBufferBase = dev_ext->vram_start; + mem_info->VideoRamLength = mem_info->FrameBufferLength = dev_ext->vram_size; +#if 0 +#ifdef DBG + DEBUG_PRINT((dev, 0, "%s: zap\n", __FUNCTION__)); + VideoPortZeroMemory(mem_info->VideoRamBase, mem_info->VideoRamLength); + DEBUG_PRINT((dev, 0, "%s: zap done\n", __FUNCTION__)); +#endif +#endif + } + break; + case IOCTL_VIDEO_UNMAP_VIDEO_MEMORY: { + DEBUG_PRINT((dev_ext, 0, "%s: IOCTL_VIDEO_UNMAP_VIDEO_MEMORY (do nothing) \n", __FUNCTION__)); + } + break; + case IOCTL_VIDEO_RESET_DEVICE: + DEBUG_PRINT((dev_ext, 0, "%s: IOCTL_VIDEO_RESET_DEVICE\n", __FUNCTION__)); + HWReset(dev_ext); + break; + case IOCTL_QXL_GET_INFO: { + QXLDriverInfo *driver_info; + DEBUG_PRINT((dev_ext, 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->pci_revision = dev_ext->pci_revision; + 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->io_cmd_event = dev_ext->io_cmd_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->update_area_async_port = dev_ext->io_port + QXL_IO_UPDATE_AREA_ASYNC; + driver_info->memslot_add_async_port = dev_ext->io_port + QXL_IO_MEMSLOT_ADD_ASYNC; + driver_info->create_primary_async_port = + dev_ext->io_port + QXL_IO_CREATE_PRIMARY_ASYNC; + driver_info->destroy_primary_async_port = + dev_ext->io_port + QXL_IO_DESTROY_PRIMARY_ASYNC; + driver_info->destroy_surface_async_port = + dev_ext->io_port + QXL_IO_DESTROY_SURFACE_ASYNC; + driver_info->destroy_all_surfaces_async_port = + dev_ext->io_port + QXL_IO_DESTROY_ALL_SURFACES_ASYNC; + driver_info->flush_surfaces_async_port = dev_ext->io_port + QXL_IO_FLUSH_SURFACES_ASYNC; + driver_info->flush_release_port = dev_ext->io_port + QXL_IO_FLUSH_RELEASE; + + driver_info->log_port = dev_ext->io_port + QXL_IO_LOG; + driver_info->log_buf = dev_ext->ram_header->log_buf; + + driver_info->surface0_area = dev_ext->ram_start; + driver_info->surface0_area_size = dev_ext->rom->surface0_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->update_surface = &dev_ext->ram_header->update_surface; + + driver_info->num_pages = dev_ext->rom->num_pages; + driver_info->io_pages_virt = dev_ext->ram_start + driver_info->surface0_area_size; + driver_info->io_pages_phys = dev_ext->ram_physical.QuadPart + + driver_info->surface0_area_size; + + driver_info->main_mem_slot_id = dev_ext->rom->slots_start; + driver_info->num_mem_slot = dev_ext->rom->slots_end; + driver_info->slot_gen_bits = dev_ext->rom->slot_gen_bits; + driver_info->slot_id_bits = dev_ext->rom->slot_id_bits; + driver_info->slots_generation = &dev_ext->rom->slot_generation; + driver_info->ram_slot_start = &dev_ext->ram_header->mem_slot.mem_start; + driver_info->ram_slot_end = &dev_ext->ram_header->mem_slot.mem_end; + driver_info->main_mem_slot = dev_ext->mem_slots[driver_info->main_mem_slot_id]; + +#if (WINVER < 0x0501) + driver_info->WaitForEvent = QXLWaitForEvent; +#endif + driver_info->destroy_surface_wait_port = dev_ext->io_port + QXL_IO_DESTROY_SURFACE_WAIT; + driver_info->destroy_all_surfaces_port = dev_ext->io_port + QXL_IO_DESTROY_ALL_SURFACES; + driver_info->create_primary_port = dev_ext->io_port + QXL_IO_CREATE_PRIMARY; + driver_info->destroy_primary_port = dev_ext->io_port + QXL_IO_DESTROY_PRIMARY; + driver_info->memslot_add_port = dev_ext->io_port + QXL_IO_MEMSLOT_ADD; + driver_info->memslot_del_port = dev_ext->io_port + QXL_IO_MEMSLOT_DEL; + + driver_info->primary_surface_create = &dev_ext->ram_header->create_surface; + + driver_info->n_surfaces = dev_ext->rom->n_surfaces; + + driver_info->fb_phys = dev_ext->vram_physical.QuadPart; + + driver_info->dev_id = dev_ext->rom->id; + + driver_info->create_non_primary_surfaces = check_non_primary_surfaces_registry_key(dev_ext); + } + break; + + case IOCTL_QXL_SET_CUSTOM_DISPLAY: { + QXLEscapeSetCustomDisplay *custom_display; + DEBUG_PRINT((dev_ext, 0, "%s: IOCTL_QXL_SET_CUSTOM_DISPLAY\n", __FUNCTION__)); + + if (packet->InputBufferLength < (packet->StatusBlock->Information = + sizeof(QXLEscapeSetCustomDisplay))) { + error = ERROR_INSUFFICIENT_BUFFER; + goto err; + } + + custom_display = packet->InputBuffer; + DEBUG_PRINT((dev_ext, 0, "%s: %dx%d@%d\n", __FUNCTION__, + custom_display->xres, custom_display->yres, + custom_display->bpp)); + SetCustomDisplay(dev_ext, custom_display); + } + break; + + default: + DEBUG_PRINT((dev_ext, 0, "%s: invalid command 0x%lx\n", __FUNCTION__, packet->IoControlCode)); + error = ERROR_INVALID_FUNCTION; + goto err; + } + packet->StatusBlock->Status = NO_ERROR; + DEBUG_PRINT((dev_ext, 0, "%s: OK\n", __FUNCTION__)); + return TRUE; +err: + packet->StatusBlock->Information = 0; + packet->StatusBlock->Status = error; + DEBUG_PRINT((dev_ext, 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); + } + if (pending & QXL_INTERRUPT_IO_CMD) { + VideoPortSetEvent(dev_ext, dev_ext->io_cmd_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/xddm/miniport/qxl.h b/xddm/miniport/qxl.h new file mode 100644 index 0000000..c7df4b1 --- /dev/null +++ b/xddm/miniport/qxl.h @@ -0,0 +1,41 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This software is licensed under the GNU General Public License, + version 2 (GPLv2) (see COPYING for details), subject to the + following clarification. + + With respect to binaries built using the Microsoft(R) Windows + Driver Kit (WDK), GPLv2 does not extend to any code contained in or + derived from the WDK ("WDK Code"). As to WDK Code, by using or + distributing such binaries you agree to be bound by the Microsoft + Software License Terms for the WDK. All WDK Code is considered by + the GPLv2 licensors to qualify for the special exception stated in + section 3 of GPLv2 (commonly known as the system library + exception). + + There is NO WARRANTY for this software, express or implied, + including the implied warranties of NON-INFRINGEMENT, TITLE, + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#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) DebugPrint arg +#else +#define DEBUG_PRINT(arg) +#endif diff --git a/xddm/miniport/qxl.inf b/xddm/miniport/qxl.inf new file mode 100644 index 0000000..9b17575 --- /dev/null +++ b/xddm/miniport/qxl.inf @@ -0,0 +1,97 @@ +
+; Installation inf for qxl driver
+
+[Version]
+Signature = "$CHICAGO$"
+DriverVer = 08/15/2012,1.4.2.3 +Provider = %RHAT%
+CatalogFile = qxl.cat
+Class = Display
+ClassGUID = {4d36e968-e325-11ce-bfc1-08002be10318}
+
+[DestinationDirs]
+DefaultDestDir = 11 ; system32
+qxl.Miniport = 12 ; drivers
+qxl.Display = 11 ; system32
+
+[Manufacturer]
+%RHAT% = q, NTx86, NTamd64, NTx86.6.0, NTamd64.6.0
+
+; WinXP x86 and up
+[q.NTx86]
+%RHAT% %QXL% = qxl, PCI\VEN_1b36&DEV_0100&SUBSYS_11001af4
+
+; WinXP x64 and up
+[q.NTamd64]
+%RHAT% %QXL% = qxl, PCI\VEN_1b36&DEV_0100&SUBSYS_11001af4
+
+; Vista x86 and up
+[q.NTx86.6.0]
+%RHAT% %QXL% = qxl_vista, PCI\VEN_1b36&DEV_0100&SUBSYS_11001af4
+
+; Vista x64 and up
+[q.NTamd64.6.0]
+%RHAT% %QXL% = qxl_vista, PCI\VEN_1b36&DEV_0100&SUBSYS_11001af4
+
+
+[ControlFlags]
+ExcludeFromSelect = *
+
+[qxl]
+CopyFiles = qxl.Miniport, qxl.Display
+
+[qxl_vista]
+FeatureScore = FC
+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_vista.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_vista.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/xddm/miniport/qxl.rc b/xddm/miniport/qxl.rc new file mode 100644 index 0000000..50abefe --- /dev/null +++ b/xddm/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.4.2.3" +#define VER_PRODUCTNAME_STR "Spice"
+#define VER_PRODUCTVERSION_STR "1.4.2.3" +
+#undef VER_PRODUCTVERSION
+#define VER_PRODUCTVERSION 1,4,2,3 +
+#define VER_COMPANYNAME_STR "Red Hat Inc."
+#define VER_LEGALCOPYRIGHT_STR "© Red Hat Inc. All rights reserved."
+
+#include "common.ver"
diff --git a/xddm/miniport/sources b/xddm/miniport/sources new file mode 100644 index 0000000..8d96e14 --- /dev/null +++ b/xddm/miniport/sources @@ -0,0 +1,29 @@ +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 \
+ minimal_snprintf.c \
+ wdmhelper.c \
+ qxl.rc
+
+MISCFILES=qxl.inf
diff --git a/xddm/miniport/wdmhelper.c b/xddm/miniport/wdmhelper.c new file mode 100644 index 0000000..5af1909 --- /dev/null +++ b/xddm/miniport/wdmhelper.c @@ -0,0 +1,64 @@ +/* + Copyright (C) 2009 Red Hat, Inc. + + This software is licensed under the GNU General Public License, + version 2 (GPLv2) (see COPYING for details), subject to the + following clarification. + + With respect to binaries built using the Microsoft(R) Windows + Driver Kit (WDK), GPLv2 does not extend to any code contained in or + derived from the WDK ("WDK Code"). As to WDK Code, by using or + distributing such binaries you agree to be bound by the Microsoft + Software License Terms for the WDK. All WDK Code is considered by + the GPLv2 licensors to qualify for the special exception stated in + section 3 of GPLv2 (commonly known as the system library + exception). + + There is NO WARRANTY for this software, express or implied, + including the implied warranties of NON-INFRINGEMENT, TITLE, + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +*/ + +#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 + diff --git a/xddm/scripts/buildAll.bat b/xddm/scripts/buildAll.bat new file mode 100644 index 0000000..24c81ea --- /dev/null +++ b/xddm/scripts/buildAll.bat @@ -0,0 +1,75 @@ +REM Copyright Red Hat 2007-2011
+REM Authors: Yan Vugenfirer
+REM Arnon Gilboa
+REM Uri Lublin
+:
+: Set global parameters:
+:
+
+: Use Windows 7 DDK
+if "%DDKVER%"=="" set DDKVER=7600.16385.0
+
+: By default DDK is installed under C:\WINDDK, but it can be installed in different location
+if "%DDKINSTALLROOT%"=="" set DDKINSTALLROOT=C:\WINDDK\
+set BUILDROOT=%DDKINSTALLROOT%%DDKVER%
+set X64ENV=x64
+if "%DDKVER%"=="6000" set X64ENV=amd64
+if "%BUILDCFG%"=="" set BUILDCFG=fre
+
+if not "%1"=="" goto parameters_here
+echo no parameters specified, exiting
+goto :eof
+:parameters_here
+
+:nextparam
+if "%1"=="" goto :eof
+goto %1
+:continue
+shift
+goto nextparam
+
+:fre
+set BUILDCFG=fre
+goto continue
+
+:chk
+set BUILDCFG=chk
+goto continue
+
+:Win7
+set BUILDENV=WIN7
+goto build_it
+
+:Win7_64
+set BUILDENV=%X64ENV% WIN7
+goto build_it
+
+:Vista
+set BUILDENV=Wlh
+goto build_it
+
+:Vista64
+set BUILDENV=%X64ENV% Wlh
+goto build_it
+
+:Win2003
+set BUILDENV=WNET
+goto build_it
+
+:Win200364
+set BUILDENV=%X64ENV% WNET
+goto build_it
+
+:XP
+set BUILDENV=WXP
+goto build_it
+
+:build_it
+set DDKBUILDENV=
+pushd %BUILDROOT%
+call %BUILDROOT%\bin\setenv.bat %BUILDROOT% %BUILDCFG% %BUILDENV%
+popd
+build -cZg
+
+goto continue
+
diff --git a/xddm/scripts/clean.bat b/xddm/scripts/clean.bat new file mode 100644 index 0000000..acc3bc3 --- /dev/null +++ b/xddm/scripts/clean.bat @@ -0,0 +1,6 @@ +REM Copyright Red Hat 2009-2011
+REM Authors: Arnon Gilboa
+:rmdir /S /Q Debug
+rmdir /S /Q Release
+for /d %%a in (obj*) do rd /s /q "%%a"
+del /F *.log *.wrn *.err
|