summaryrefslogtreecommitdiff
path: root/xddm
diff options
context:
space:
mode:
Diffstat (limited to 'xddm')
-rw-r--r--xddm/COPYING339
-rwxr-xr-xxddm/build.bat45
-rw-r--r--xddm/dirs4
-rw-r--r--xddm/display/brush.c400
-rw-r--r--xddm/display/driver.c1590
-rw-r--r--xddm/display/driver.rc29
-rw-r--r--xddm/display/makefile1
-rw-r--r--xddm/display/mspace.c2437
-rw-r--r--xddm/display/mspace.h150
-rw-r--r--xddm/display/pointer.c144
-rw-r--r--xddm/display/quic.c1738
-rw-r--r--xddm/display/quic.h68
-rw-r--r--xddm/display/quic_config.h54
-rw-r--r--xddm/display/quic_family_tmpl.c118
-rw-r--r--xddm/display/quic_rgb_tmpl.c766
-rw-r--r--xddm/display/quic_tmpl.c636
-rw-r--r--xddm/display/qxldd.h548
-rw-r--r--xddm/display/res.c3387
-rw-r--r--xddm/display/res.h77
-rw-r--r--xddm/display/rop.c1778
-rw-r--r--xddm/display/rop.h42
-rw-r--r--xddm/display/sources34
-rw-r--r--xddm/display/surface.c407
-rw-r--r--xddm/display/surface.h103
-rw-r--r--xddm/display/text.c128
-rw-r--r--xddm/display/utils.h123
-rw-r--r--xddm/include/murmur_hash2a.h152
-rw-r--r--xddm/include/os_dep.h41
-rw-r--r--xddm/include/qxl_driver.h127
-rw-r--r--xddm/include/stdint.h397
-rw-r--r--xddm/include/wdmhelper.h38
-rw-r--r--xddm/miniport/makefile1
-rw-r--r--xddm/miniport/minimal_snprintf.c708
-rw-r--r--xddm/miniport/minimal_snprintf.h9
-rw-r--r--xddm/miniport/qxl.c1311
-rw-r--r--xddm/miniport/qxl.h41
-rw-r--r--xddm/miniport/qxl.inf97
-rw-r--r--xddm/miniport/qxl.rc29
-rw-r--r--xddm/miniport/sources29
-rw-r--r--xddm/miniport/wdmhelper.c64
-rw-r--r--xddm/scripts/buildAll.bat75
-rw-r--r--xddm/scripts/clean.bat6
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 = &delta;
+ } 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