Compare commits

...

No commits in common. "before-split" and "master" have entirely different histories.

130 changed files with 15647 additions and 11472 deletions

9
.gitignore vendored
View File

@ -1,9 +0,0 @@
*.la
*.lo
*.loT
*.o
.deps
.dirstamp
.libs
Makefile
Makefile.in

52
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,52 @@
image: fedora:latest
before_script:
- >
dnf install git libtool make libasan gawk
python3 python3-pyparsing glib-networking
meson ninja-build gdk-pixbuf2-devel
glib2-devel pixman-devel openssl-devel libjpeg-devel
libcacard-devel cyrus-sasl-devel lz4-devel opus-devel
gstreamer1-devel gstreamer1-plugins-base-devel
-y
- git clone ${CI_REPOSITORY_URL/spice-common.git/spice-protocol.git}
- meson --buildtype=release spice-protocol build-spice-protocol --prefix=/usr --werror
- ninja -C build-spice-protocol install
makecheck:
script:
- >
CFLAGS='-O2 -pipe -g -fsanitize=address -fno-omit-frame-pointer -Wframe-larger-than=40920'
LDFLAGS='-fsanitize=address -lasan'
./autogen.sh --enable-extra-checks
- make
- make check || (cat tests/test-suite.log && exit 1)
meson-makecheck:
script:
- >
CFLAGS='-O2 -pipe -g -fsanitize=address -fno-omit-frame-pointer -Wframe-larger-than=40920'
LDFLAGS='-fsanitize=address -lasan'
meson build -Dextra-checks=true || (cat build/meson-logs/meson-log.txt && exit 1)
- ninja -C build
- (cd build && meson test) || (cat build/meson-logs/testlog.txt && exit 1)
make-win:
script:
- >
dnf install mingw64-gcc mingw64-pkg-config mingw64-pixman mingw64-openssl
mingw64-opus mingw64-glib2 mingw64-glib-networking mingw64-gdk-pixbuf
'wine-core(x86-64)'
-y
- mkdir spice-protocol/build
- (cd spice-protocol/build && mingw64-meson --werror && ninja install)
- NOCONFIGURE=yes ./autogen.sh
- export WINEPATH='Z:\usr\x86_64-w64-mingw32\sys-root\mingw\bin'
- >
PYTHON=python3
CFLAGS='-O2 -pipe -g -fsanitize=address -fno-omit-frame-pointer -Wframe-larger-than=40920'
LDFLAGS='-fsanitize=address -lasan'
mingw64-configure --enable-extra-checks
- make
- export LANG=en_US.UTF-8
- make LOG_COMPILER=wine check || (cat tests/test-suite.log && exit 1)

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "common/recorder"]
path = common/recorder
url = https://github.com/c3d/recorder.git

3
.gitpublish Normal file
View File

@ -0,0 +1,3 @@
[gitpublishprofile "default"]
to = spice-devel@lists.freedesktop.org
prefix = PATCH spice-common

502
COPYING Normal file
View File

@ -0,0 +1,502 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 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.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, 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 library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.
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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.
If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be 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.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.
9. 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 Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.
11. 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 Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.
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.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. 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 library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; 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.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

@ -1,76 +1,33 @@
if OS_WIN32
SUBDIRS = win
endif
NULL =
ACLOCAL_AMFLAGS = -I m4
noinst_LTLIBRARIES = libspice-common.la
libspice_common_la_SOURCES = \
bitops.h \
canvas_utils.c \
canvas_utils.h \
draw.h \
lines.c \
lines.h \
lz.c \
lz.h \
lz_common.h \
lz_config.h \
marshaller.c \
marshaller.h \
mem.c \
mem.h \
messages.h \
mutex.h \
pixman_utils.c \
pixman_utils.h \
quic.c \
quic.h \
quic_config.h \
rect.h \
region.c \
region.h \
ring.h \
rop3.c \
rop3.h \
spice_common.h \
ssl_verify.c \
ssl_verify.h \
backtrace.c \
backtrace.h \
$(NULL)
SUBDIRS = python_modules common docs
if SUPPORT_GL
libspice_common_la_SOURCES += \
gl_utils.h \
glc.h \
glc.c \
ogl_ctx.h \
ogl_ctx.c \
$(NULL)
if ENABLE_TESTS
SUBDIRS += tests
endif
INCLUDES = \
$(GL_CFLAGS) \
$(PIXMAN_CFLAGS) \
$(PROTOCOL_CFLAGS) \
$(VISIBILITY_HIDDEN_CFLAGS) \
$(WARN_CFLAGS) \
-std=gnu99 \
$(NULL)
EXTRA_DIST = \
canvas_base.c \
canvas_base.h \
gdi_canvas.c \
gdi_canvas.h \
gl_canvas.c \
gl_canvas.h \
sw_canvas.c \
sw_canvas.h \
lz_compress_tmpl.c \
lz_decompress_tmpl.c \
quic_family_tmpl.c \
quic_rgb_tmpl.c \
quic_tmpl.c \
meson.build \
meson_options.txt \
spice_codegen.py \
spice.proto \
$(NULL)
DISTCLEANFILES = *.pyc
MAINTAINERCLEANFILES = \
$(srcdir)/INSTALL \
$(srcdir)/aclocal.m4 \
$(srcdir)/autoscan.log \
$(srcdir)/build-aux \
$(srcdir)/config.h.in \
$(srcdir)/m4/libtool.m4 \
$(srcdir)/m4/ltoptions.m4 \
$(srcdir)/m4/ltsugar.m4 \
$(srcdir)/m4/ltversion.m4 \
$(srcdir)/m4/lt~obsolete.m4 \
`find "$(srcdir)" -type f -name Makefile.in -print` \
$(NULL)
-include $(top_srcdir)/git.mk

17
autogen.sh Executable file
View File

@ -0,0 +1,17 @@
#!/bin/sh
set -e # exit on errors
srcdir=`dirname $0`
test -z "$srcdir" && srcdir=.
olddir=`pwd`
cd "$srcdir"
mkdir -p m4
autoreconf --verbose --force --install
cd "$olddir"
if [ -z "$NOCONFIGURE" ]; then
"$srcdir"/configure --enable-maintainer-mode --enable-python-checks ${1+"$@"}
fi

View File

@ -1,91 +0,0 @@
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2009 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef BITOPS_H
#define BITOPS_H
#include <spice/macros.h>
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
static inline int spice_bit_find_msb(unsigned int val)
{
int ret;
asm ("bsrl %1,%0\n\t"
"jnz 1f\n\t"
"movl $-1,%0\n"
"1:"
: "=r"(ret) : "r"(val));
return ret + 1;
}
#elif defined(WIN32) && !defined(_WIN64)
static INLINE int spice_bit_find_msb(uint32_t val)
{
uint32_t r;
__asm {
bsr eax, val
jnz found
mov eax, -1
found:
mov r, eax
}
return r + 1;
}
#else
static INLINE int spice_bit_find_msb(unsigned int val)
{
signed char index = 31;
if(val == 0) {
return 0;
}
do {
if(val & 0x80000000) {
break;
}
val <<= 1;
} while(--index >= 0);
return index+1;
}
#endif
static INLINE int spice_bit_next_pow2(unsigned int val)
{
if ((val & (val - 1)) == 0) {
return val;
}
return 1 << spice_bit_find_msb(val);
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,299 +0,0 @@
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2009 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "canvas_utils.h"
#include <spice/macros.h>
#ifdef __GNUC__
#include <stdlib.h>
#include <stdio.h>
#endif
#include "mem.h"
#ifdef WIN32
static int gdi_handlers = 0;
#endif
#ifndef CANVAS_ERROR
#define CANVAS_ERROR(format, ...) { \
printf("%s: " format "\n", __FUNCTION__, ## __VA_ARGS__); \
abort(); \
}
#endif
static void release_data(pixman_image_t *image, void *release_data)
{
PixmanData *data = (PixmanData *)release_data;
#ifdef WIN32
if (data->bitmap) {
DeleteObject((HBITMAP)data->bitmap);
CloseHandle(data->mutex);
gdi_handlers--;
}
#endif
free(data->data);
free(data);
}
static PixmanData *
pixman_image_add_data(pixman_image_t *image)
{
PixmanData *data;
data = (PixmanData *)pixman_image_get_destroy_data(image);
if (data == NULL) {
data = (PixmanData *)calloc(1, sizeof(PixmanData));
if (data == NULL) {
CANVAS_ERROR("out of memory");
}
pixman_image_set_destroy_function(image,
release_data,
data);
}
return data;
}
void
spice_pixman_image_set_format(pixman_image_t *image,
pixman_format_code_t format)
{
PixmanData *data;
data = pixman_image_add_data(image);
data->format = format;
}
pixman_format_code_t
spice_pixman_image_get_format(pixman_image_t *image)
{
PixmanData *data;
data = (PixmanData *)pixman_image_get_destroy_data(image);
if (data != NULL &&
data->format != 0)
return data->format;
CANVAS_ERROR("Unknown pixman image type");
}
static INLINE pixman_image_t *__surface_create_stride(pixman_format_code_t format, int width, int height,
int stride)
{
uint8_t *data;
uint8_t *stride_data;
pixman_image_t *surface;
PixmanData *pixman_data;
data = (uint8_t *)spice_malloc_n(abs(stride), height);
if (stride < 0) {
stride_data = data + (-stride) * (height - 1);
} else {
stride_data = data;
}
surface = pixman_image_create_bits(format, width, height, (uint32_t *)stride_data, stride);
if (surface == NULL) {
free(data);
CANVAS_ERROR("create surface failed, out of memory");
}
pixman_data = pixman_image_add_data(surface);
pixman_data->data = data;
pixman_data->format = format;
return surface;
}
#ifdef WIN32
pixman_image_t *surface_create(HDC dc, pixman_format_code_t format,
int width, int height, int top_down)
#else
pixman_image_t * surface_create(pixman_format_code_t format, int width, int height, int top_down)
#endif
{
#ifdef WIN32
/*
* Windows xp allow only 10,000 of gdi handlers, considering the fact that
* we limit here the number to 5000, we dont use atomic operations to sync
* this calculation against the other canvases (in case of multiple
* monitors), in worst case there will be little more than 5000 gdi
* handlers.
*/
if (dc && gdi_handlers < 5000) {
uint8_t *data;
uint8_t *src;
struct {
BITMAPINFO inf;
RGBQUAD palette[255];
} bitmap_info;
int nstride;
pixman_image_t *surface;
PixmanData *pixman_data;
HBITMAP bitmap;
HANDLE mutex;
memset(&bitmap_info, 0, sizeof(bitmap_info));
bitmap_info.inf.bmiHeader.biSize = sizeof(bitmap_info.inf.bmiHeader);
bitmap_info.inf.bmiHeader.biWidth = width;
bitmap_info.inf.bmiHeader.biHeight = (!top_down) ? height : -height;
bitmap_info.inf.bmiHeader.biPlanes = 1;
switch (format) {
case PIXMAN_a8r8g8b8:
case PIXMAN_x8r8g8b8:
bitmap_info.inf.bmiHeader.biBitCount = 32;
nstride = width * 4;
break;
case PIXMAN_x1r5g5b5:
case PIXMAN_r5g6b5:
bitmap_info.inf.bmiHeader.biBitCount = 16;
nstride = SPICE_ALIGN(width * 2, 4);
break;
case PIXMAN_a8:
bitmap_info.inf.bmiHeader.biBitCount = 8;
nstride = SPICE_ALIGN(width, 4);
break;
case PIXMAN_a1:
bitmap_info.inf.bmiHeader.biBitCount = 1;
nstride = SPICE_ALIGN(width, 32) / 8;
break;
default:
CANVAS_ERROR("invalid format");
}
bitmap_info.inf.bmiHeader.biCompression = BI_RGB;
mutex = CreateMutex(NULL, 0, NULL);
if (!mutex) {
CANVAS_ERROR("Unable to CreateMutex");
}
bitmap = CreateDIBSection(dc, &bitmap_info.inf, 0, (VOID **)&data, NULL, 0);
if (!bitmap) {
CloseHandle(mutex);
CANVAS_ERROR("Unable to CreateDIBSection");
}
if (top_down) {
src = data;
} else {
src = data + nstride * (height - 1);
nstride = -nstride;
}
surface = pixman_image_create_bits(format, width, height, (uint32_t *)src, nstride);
if (surface == NULL) {
CloseHandle(mutex);
DeleteObject(bitmap);
CANVAS_ERROR("create surface failed, out of memory");
}
pixman_data = pixman_image_add_data(surface);
pixman_data->format = format;
pixman_data->bitmap = bitmap;
pixman_data->mutex = mutex;
gdi_handlers++;
return surface;
} else {
#endif
if (top_down) {
pixman_image_t *surface;
PixmanData *data;
surface = pixman_image_create_bits(format, width, height, NULL, 0);
data = pixman_image_add_data(surface);
data->format = format;
return surface;
} else {
// NOTE: we assume here that the lz decoders always decode to RGB32.
int stride = 0;
switch (format) {
case PIXMAN_a8r8g8b8:
case PIXMAN_x8r8g8b8:
stride = width * 4;
break;
case PIXMAN_x1r5g5b5:
case PIXMAN_r5g6b5:
stride = SPICE_ALIGN(width * 2, 4);
break;
case PIXMAN_a8:
stride = SPICE_ALIGN(width, 4);
break;
case PIXMAN_a1:
stride = SPICE_ALIGN(width, 32) / 8;
break;
default:
CANVAS_ERROR("invalid format");
}
stride = -stride;
return __surface_create_stride(format, width, height, stride);
}
#ifdef WIN32
}
#endif
}
#ifdef WIN32
pixman_image_t *surface_create_stride(HDC dc, pixman_format_code_t format, int width, int height,
int stride)
#else
pixman_image_t *surface_create_stride(pixman_format_code_t format, int width, int height,
int stride)
#endif
{
#ifdef WIN32
if (dc) {
if (abs(stride) == (width * 4)) {
return surface_create(dc, format, width, height, (stride > 0));
}
}
#endif
return __surface_create_stride(format, width, height, stride);
}
pixman_image_t *alloc_lz_image_surface(LzDecodeUsrData *canvas_data,
pixman_format_code_t pixman_format, int width,
int height, int gross_pixels, int top_down)
{
int stride;
pixman_image_t *surface = NULL;
stride = (gross_pixels / height) * (PIXMAN_FORMAT_BPP (pixman_format) / 8);
if (!top_down) {
stride = -stride;
}
surface = surface_create_stride(
#ifdef WIN32
canvas_data->dc,
#endif
pixman_format, width, height, stride);
canvas_data->out_surface = surface;
return surface;
}

161
common/Makefile.am Normal file
View File

@ -0,0 +1,161 @@
NULL =
# Avoid need for python(pyparsing) by end users
CLIENT_MARSHALLERS = \
generated_client_demarshallers.c \
generated_client_marshallers.c \
generated_client_marshallers.h \
$(NULL)
SERVER_MARSHALLERS = \
generated_server_demarshallers.c \
generated_server_marshallers.c \
generated_server_marshallers.h \
$(NULL)
BUILT_SOURCES = $(CLIENT_MARSHALLERS) $(SERVER_MARSHALLERS)
noinst_LTLIBRARIES = libspice-common.la libspice-common-server.la libspice-common-client.la
libspice_common_la_SOURCES = \
agent.c \
agent.h \
backtrace.c \
backtrace.h \
canvas_utils.c \
canvas_utils.h \
demarshallers.h \
draw.h \
lines.c \
lines.h \
log.c \
log.h \
lz.c \
lz.h \
lz_common.h \
lz_config.h \
macros.h \
marshaller.c \
marshaller.h \
mem.c \
mem.h \
messages.h \
pixman_utils.c \
pixman_utils.h \
quic.c \
quic.h \
quic_config.h \
rect.h \
region.c \
region.h \
ring.h \
rop3.c \
rop3.h \
snd_codec.c \
snd_codec.h \
udev.c \
udev.h \
utils.c \
utils.h \
verify.h \
recorder.h \
$(NULL)
if ENABLE_RECORDER
libspice_common_la_SOURCES += \
recorder/recorder.c \
recorder/recorder.h \
recorder/recorder_ring.c \
recorder/recorder_ring.h \
$(NULL)
endif
if ENABLE_AGENT_INTERFACE
libspice_common_la_SOURCES += \
agent_interface.c \
agent_interface.h \
$(NULL)
endif
# These 2 files are not build as part of spice-common
# build system, but modules using spice-common will build
# them with the appropriate options. We need to let automake
# know that these are source files so that it can properly
# track these files dependencies
EXTRA_libspice_common_la_SOURCES = \
sw_canvas.c \
sw_canvas.h \
$(NULL)
libspice_common_client_la_SOURCES = \
client_marshallers.h \
ssl_verify.c \
ssl_verify.h \
$(CLIENT_MARSHALLERS) \
$(NULL)
libspice_common_client_la_LIBADD = \
$(GIO2_LIBS) \
$(NULL)
libspice_common_server_la_SOURCES = \
$(SERVER_MARSHALLERS) \
$(NULL)
AM_CPPFLAGS = \
-I$(top_srcdir) \
-I$(top_builddir) \
$(SPICE_COMMON_CFLAGS) \
$(PROTOCOL_CFLAGS) \
$(NULL)
libspice_common_la_LIBADD = \
$(SPICE_COMMON_LIBS) \
$(UDEV_LIBS) \
$(NULL)
MARSHALLERS_DEPS = \
$(top_srcdir)/python_modules/__init__.py \
$(top_srcdir)/python_modules/codegen.py \
$(top_srcdir)/python_modules/demarshal.py \
$(top_srcdir)/python_modules/marshal.py \
$(top_srcdir)/python_modules/ptypes.py \
$(top_srcdir)/python_modules/spice_parser.py \
$(top_srcdir)/spice_codegen.py \
$(NULL)
# Note despite being autogenerated these are not part of CLEANFILES, they are
# actually a part of EXTRA_DIST, to avoid the need for pyparser by end users
generated_client_demarshallers.c generated_messages.h: $(top_srcdir)/spice.proto $(MARSHALLERS_DEPS)
$(AM_V_GEN)$(PYTHON) $(top_srcdir)/spice_codegen.py --generate-demarshallers --client --include common/messages.h \
--generated-declaration-file generated_messages.h $< $@ >/dev/null
generated_client_marshallers.c generated_client_marshallers.h: $(top_srcdir)/spice.proto $(MARSHALLERS_DEPS)
$(AM_V_GEN)$(PYTHON) $(top_srcdir)/spice_codegen.py --generate-marshallers -P --include common/client_marshallers.h --client \
--generate-header $< $@ >/dev/null
generated_server_demarshallers.c: $(top_srcdir)/spice.proto $(MARSHALLERS_DEPS)
$(AM_V_GEN)$(PYTHON) $(top_srcdir)/spice_codegen.py --generate-demarshallers --server --include common/messages.h $< $@ >/dev/null
STRUCTS = -M String -M Rect -M Point -M DisplayBase -M Fill -M Opaque -M Copy -M Blend -M Blackness -M Whiteness -M Invers -M Rop3 -M Stroke -M Text -M Transparent -M AlphaBlend -M Composite
generated_server_marshallers.c generated_server_marshallers.h: $(top_srcdir)/spice.proto $(MARSHALLERS_DEPS)
$(AM_V_GEN)$(PYTHON) $(top_srcdir)/spice_codegen.py --generate-marshallers $(STRUCTS) --server --include common/messages.h \
--generate-header $< $@ >/dev/null
EXTRA_DIST = \
$(CLIENT_MARSHALLERS) \
$(SERVER_MARSHALLERS) \
generated_messages.h \
meson.build \
canvas_base.c \
canvas_base.h \
lz_compress_tmpl.c \
lz_decompress_tmpl.c \
quic_family_tmpl.c \
quic_tmpl.c \
$(NULL)
GITIGNOREFILES = \
generated_messages.h \
$(NULL)
-include $(top_srcdir)/git.mk

361
common/agent.c Normal file
View File

@ -0,0 +1,361 @@
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2020 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "agent.h"
#ifdef _WIN32
// Windows is always little endian
# define FIX_ENDIAN16(x) (x) = (x)
# define FIX_ENDIAN32(x) (x) = (x)
# define FIX_ENDIAN64(x) (x) = (x)
#else
# include <glib.h>
# define FIX_ENDIAN16(x) (x) = GUINT16_FROM_LE(x)
# define FIX_ENDIAN32(x) (x) = GUINT32_FROM_LE(x)
# define FIX_ENDIAN64(x) (x) = GUINT64_FROM_LE(x)
#endif
#include <spice/start-packed.h>
typedef struct SPICE_ATTR_PACKED {
uint16_t v;
} uint16_unaligned_t;
typedef struct SPICE_ATTR_PACKED {
uint32_t v;
} uint32_unaligned_t;
typedef struct SPICE_ATTR_PACKED {
uint64_t v;
} uint64_unaligned_t;
#include <spice/end-packed.h>
static const int agent_message_min_size[] =
{
-1, /* Does not exist */
sizeof(VDAgentMouseState), /* VD_AGENT_MOUSE_STATE */
sizeof(VDAgentMonitorsConfig), /* VD_AGENT_MONITORS_CONFIG */
sizeof(VDAgentReply), /* VD_AGENT_REPLY */
sizeof(VDAgentClipboard), /* VD_AGENT_CLIPBOARD */
sizeof(VDAgentDisplayConfig), /* VD_AGENT_DISPLAY_CONFIG */
sizeof(VDAgentAnnounceCapabilities), /* VD_AGENT_ANNOUNCE_CAPABILITIES */
sizeof(VDAgentClipboardGrab), /* VD_AGENT_CLIPBOARD_GRAB */
sizeof(VDAgentClipboardRequest), /* VD_AGENT_CLIPBOARD_REQUEST */
sizeof(VDAgentClipboardRelease), /* VD_AGENT_CLIPBOARD_RELEASE */
sizeof(VDAgentFileXferStartMessage), /* VD_AGENT_FILE_XFER_START */
sizeof(VDAgentFileXferStatusMessage), /* VD_AGENT_FILE_XFER_STATUS */
sizeof(VDAgentFileXferDataMessage), /* VD_AGENT_FILE_XFER_DATA */
0, /* VD_AGENT_CLIENT_DISCONNECTED */
sizeof(VDAgentMaxClipboard), /* VD_AGENT_MAX_CLIPBOARD */
sizeof(VDAgentAudioVolumeSync), /* VD_AGENT_AUDIO_VOLUME_SYNC */
sizeof(VDAgentGraphicsDeviceInfo), /* VD_AGENT_GRAPHICS_DEVICE_INFO */
};
static AgentCheckResult
agent_message_check_size(const VDAgentMessage *message_header,
const uint32_t *capabilities, uint32_t capabilities_size)
{
if (message_header->protocol != VD_AGENT_PROTOCOL) {
return AGENT_CHECK_WRONG_PROTOCOL_VERSION;
}
if (message_header->type >= SPICE_N_ELEMENTS(agent_message_min_size) ||
agent_message_min_size[message_header->type] < 0) {
return AGENT_CHECK_UNKNOWN_MESSAGE;
}
uint32_t min_size = agent_message_min_size[message_header->type];
if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
switch (message_header->type) {
case VD_AGENT_CLIPBOARD_GRAB:
case VD_AGENT_CLIPBOARD_REQUEST:
case VD_AGENT_CLIPBOARD:
case VD_AGENT_CLIPBOARD_RELEASE:
min_size += 4;
}
}
if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
VD_AGENT_CAP_CLIPBOARD_GRAB_SERIAL)
&& message_header->type == VD_AGENT_CLIPBOARD_GRAB) {
min_size += 4;
}
switch (message_header->type) {
case VD_AGENT_MONITORS_CONFIG:
case VD_AGENT_FILE_XFER_START:
case VD_AGENT_FILE_XFER_DATA:
case VD_AGENT_CLIPBOARD:
case VD_AGENT_CLIPBOARD_GRAB:
case VD_AGENT_AUDIO_VOLUME_SYNC:
case VD_AGENT_ANNOUNCE_CAPABILITIES:
case VD_AGENT_GRAPHICS_DEVICE_INFO:
case VD_AGENT_FILE_XFER_STATUS:
if (message_header->size < min_size) {
return AGENT_CHECK_INVALID_SIZE;
}
break;
case VD_AGENT_MOUSE_STATE:
case VD_AGENT_DISPLAY_CONFIG:
case VD_AGENT_REPLY:
case VD_AGENT_CLIPBOARD_REQUEST:
case VD_AGENT_CLIPBOARD_RELEASE:
case VD_AGENT_MAX_CLIPBOARD:
case VD_AGENT_CLIENT_DISCONNECTED:
if (message_header->size != min_size) {
return AGENT_CHECK_INVALID_SIZE;
}
break;
default:
return AGENT_CHECK_UNKNOWN_MESSAGE;
}
return AGENT_CHECK_NO_ERROR;
}
static void uint16_from_le(uint8_t *_msg, uint32_t size, uint32_t offset)
{
uint32_t i;
uint16_unaligned_t *msg = (uint16_unaligned_t *)(_msg + offset);
/* size % 2 should be 0 - extra bytes are ignored */
for (i = 0; i < size / 2; i++) {
FIX_ENDIAN16(msg[i].v);
}
}
static void uint32_from_le(uint8_t *_msg, uint32_t size, uint32_t offset)
{
uint32_t i;
uint32_unaligned_t *msg = (uint32_unaligned_t *)(_msg + offset);
/* size % 4 should be 0 - extra bytes are ignored */
for (i = 0; i < size / 4; i++) {
FIX_ENDIAN32(msg[i].v);
}
}
static void
agent_message_clipboard_from_le(const VDAgentMessage *message_header, uint8_t *data,
const uint32_t *capabilities, uint32_t capabilities_size)
{
size_t min_size = agent_message_min_size[message_header->type];
uint32_unaligned_t *data_type = (uint32_unaligned_t *) data;
if (VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
VD_AGENT_CAP_CLIPBOARD_SELECTION)) {
min_size += 4;
data_type++;
}
switch (message_header->type) {
case VD_AGENT_CLIPBOARD_REQUEST:
case VD_AGENT_CLIPBOARD:
FIX_ENDIAN32(data_type->v);
break;
case VD_AGENT_CLIPBOARD_GRAB:
uint32_from_le(data, message_header->size - min_size, min_size);
break;
case VD_AGENT_CLIPBOARD_RELEASE:
// empty
break;
}
}
static void
agent_message_file_xfer_from_le(const VDAgentMessage *message_header, uint8_t *data)
{
uint32_unaligned_t *id = (uint32_unaligned_t *)data;
FIX_ENDIAN32(id->v);
id++; // result
switch (message_header->type) {
case VD_AGENT_FILE_XFER_DATA: {
VDAgentFileXferDataMessage *msg = (VDAgentFileXferDataMessage *) data;
FIX_ENDIAN64(msg->size);
break;
}
case VD_AGENT_FILE_XFER_STATUS: {
VDAgentFileXferStatusMessage *msg = (VDAgentFileXferStatusMessage *) data;
FIX_ENDIAN32(msg->result);
// from client/server we don't expect any detail
switch (msg->result) {
case VD_AGENT_FILE_XFER_STATUS_NOT_ENOUGH_SPACE:
if (message_header->size >= sizeof(VDAgentFileXferStatusMessage) +
sizeof(VDAgentFileXferStatusNotEnoughSpace)) {
VDAgentFileXferStatusNotEnoughSpace *err =
(VDAgentFileXferStatusNotEnoughSpace*) msg->data;
FIX_ENDIAN64(err->disk_free_space);
}
break;
case VD_AGENT_FILE_XFER_STATUS_ERROR:
if (message_header->size >= sizeof(VDAgentFileXferStatusMessage) +
sizeof(VDAgentFileXferStatusError)) {
VDAgentFileXferStatusError *err =
(VDAgentFileXferStatusError *) msg->data;
FIX_ENDIAN32(err->error_code);
}
break;
}
break;
}
}
}
static AgentCheckResult
agent_message_graphics_device_info_check_from_le(const VDAgentMessage *message_header,
uint8_t *data)
{
uint8_t *const end = data + message_header->size;
uint32_unaligned_t *u32 = (uint32_unaligned_t *) data;
FIX_ENDIAN32(u32->v);
const uint32_t count = u32->v;
data += 4;
for (size_t i = 0; i < count; ++i) {
if ((size_t) (end - data) < sizeof(VDAgentDeviceDisplayInfo)) {
return AGENT_CHECK_TRUNCATED;
}
uint32_from_le(data, sizeof(VDAgentDeviceDisplayInfo), 0);
VDAgentDeviceDisplayInfo *info = (VDAgentDeviceDisplayInfo *) data;
data += sizeof(VDAgentDeviceDisplayInfo);
if (!info->device_address_len) {
return AGENT_CHECK_INVALID_DATA;
}
if ((size_t) (end - data) < info->device_address_len) {
return AGENT_CHECK_TRUNCATED;
}
info->device_address[info->device_address_len - 1] = 0;
data += info->device_address_len;
}
return AGENT_CHECK_NO_ERROR;
}
static AgentCheckResult
agent_message_monitors_config_from_le(const VDAgentMessage *message_header, uint8_t *message)
{
uint32_from_le(message, sizeof(VDAgentMonitorsConfig), 0);
VDAgentMonitorsConfig *vdata = (VDAgentMonitorsConfig*) message;
vdata->flags &= VD_AGENT_CONFIG_MONITORS_FLAG_USE_POS|
VD_AGENT_CONFIG_MONITORS_FLAG_PHYSICAL_SIZE;
size_t element_size = sizeof(vdata->monitors[0]);
if ((vdata->flags & VD_AGENT_CONFIG_MONITORS_FLAG_PHYSICAL_SIZE) != 0) {
element_size += sizeof(VDAgentMonitorMM);
}
const size_t max_monitors =
(message_header->size - sizeof(*vdata)) / element_size;
if (vdata->num_of_monitors > max_monitors) {
return AGENT_CHECK_TRUNCATED;
}
uint32_from_le(message, sizeof(vdata->monitors[0]) * vdata->num_of_monitors,
sizeof(*vdata));
if ((vdata->flags & VD_AGENT_CONFIG_MONITORS_FLAG_PHYSICAL_SIZE) != 0) {
uint16_from_le(message, sizeof(VDAgentMonitorMM) * vdata->num_of_monitors,
sizeof(*vdata) + sizeof(vdata->monitors[0]) * vdata->num_of_monitors);
}
return AGENT_CHECK_NO_ERROR;
}
AgentCheckResult
agent_check_message(const VDAgentMessage *message_header, uint8_t *message,
const uint32_t *capabilities, uint32_t capabilities_size)
{
AgentCheckResult res;
res = agent_message_check_size(message_header, capabilities, capabilities_size);
if (res != AGENT_CHECK_NO_ERROR) {
return res;
}
switch (message_header->type) {
case VD_AGENT_MOUSE_STATE:
uint32_from_le(message, 3 * sizeof(uint32_t), 0);
break;
case VD_AGENT_REPLY:
case VD_AGENT_DISPLAY_CONFIG:
case VD_AGENT_MAX_CLIPBOARD:
case VD_AGENT_ANNOUNCE_CAPABILITIES:
uint32_from_le(message, message_header->size, 0);
break;
case VD_AGENT_MONITORS_CONFIG:
return agent_message_monitors_config_from_le(message_header, message);
case VD_AGENT_CLIPBOARD:
case VD_AGENT_CLIPBOARD_GRAB:
case VD_AGENT_CLIPBOARD_REQUEST:
case VD_AGENT_CLIPBOARD_RELEASE:
agent_message_clipboard_from_le(message_header, message,
capabilities, capabilities_size);
break;
case VD_AGENT_FILE_XFER_START:
case VD_AGENT_FILE_XFER_STATUS:
case VD_AGENT_FILE_XFER_DATA:
agent_message_file_xfer_from_le(message_header, message);
break;
case VD_AGENT_CLIENT_DISCONNECTED:
break;
case VD_AGENT_GRAPHICS_DEVICE_INFO:
return agent_message_graphics_device_info_check_from_le(message_header, message);
case VD_AGENT_AUDIO_VOLUME_SYNC: {
VDAgentAudioVolumeSync *vdata = (VDAgentAudioVolumeSync *)message;
const size_t max_channels =
(message_header->size - sizeof(*vdata)) / sizeof(vdata->volume[0]);
if (vdata->nchannels > max_channels) {
return AGENT_CHECK_TRUNCATED;
}
uint16_from_le(message, message_header->size - sizeof(*vdata), sizeof(*vdata));
break;
}
default:
return AGENT_CHECK_UNKNOWN_MESSAGE;
}
return AGENT_CHECK_NO_ERROR;
}
void
agent_prepare_filexfer_status(AgentFileXferStatusMessageFull *status, size_t *status_size,
const uint32_t *capabilities, uint32_t capabilities_size)
{
if (*status_size < sizeof(status->common)) {
*status_size = sizeof(status->common);
}
// if there are details but no cap for detail remove it
if (!VD_AGENT_HAS_CAPABILITY(capabilities, capabilities_size,
VD_AGENT_CAP_FILE_XFER_DETAILED_ERRORS)) {
*status_size = sizeof(status->common);
// if detail cap is not provided and error > threshold set to error
if (status->common.result >= VD_AGENT_FILE_XFER_STATUS_NOT_ENOUGH_SPACE) {
status->common.result = VD_AGENT_FILE_XFER_STATUS_ERROR;
}
}
// fix endian
switch (status->common.result) {
case VD_AGENT_FILE_XFER_STATUS_NOT_ENOUGH_SPACE:
FIX_ENDIAN64(status->not_enough_space.disk_free_space);
break;
case VD_AGENT_FILE_XFER_STATUS_ERROR:
FIX_ENDIAN32(status->error.error_code);
break;
}
// header should be done last
FIX_ENDIAN32(status->common.id);
FIX_ENDIAN32(status->common.result);
}

83
common/agent.h Normal file
View File

@ -0,0 +1,83 @@
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2020 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <spice/macros.h>
#include <spice/vd_agent.h>
SPICE_BEGIN_DECLS
#include <spice/start-packed.h>
/* This helper macro is to define a structure in a way compatible with
* Microsoft compiler */
#define SPICE_INNER_FIELD_STATUS_ERROR(type, name) \
struct SPICE_ATTR_PACKED { \
char common_ ## name[sizeof(VDAgentFileXferStatusMessage)]; \
type name; \
}
/**
* Structure to fill with transfer status.
* Fill as much details as you can and call agent_prepare_filexfer_status
* before sending to adjust for capabilities and endianness.
* If any detail are filled the status_size passed to agent_prepare_filexfer_status
* should be updated.
*/
typedef union SPICE_ATTR_PACKED AgentFileXferStatusMessageFull {
VDAgentFileXferStatusMessage common;
SPICE_INNER_FIELD_STATUS_ERROR(VDAgentFileXferStatusNotEnoughSpace, not_enough_space);
SPICE_INNER_FIELD_STATUS_ERROR(VDAgentFileXferStatusError, error);
} AgentFileXferStatusMessageFull;
#undef SPICE_INNER_FIELD_STATUS_ERROR
#include <spice/end-packed.h>
/**
* Prepare AgentFileXferStatusMessageFull to
* be sent to network.
* Avoid protocol incompatibilities and endian issues
*/
void
agent_prepare_filexfer_status(AgentFileXferStatusMessageFull *status, size_t *status_size,
const uint32_t *capabilities, uint32_t capabilities_size);
/**
* Possible results checking a message.
* Beside AGENT_CHECK_NO_ERROR all other conditions are errors.
*/
typedef enum AgentCheckResult {
AGENT_CHECK_NO_ERROR,
AGENT_CHECK_WRONG_PROTOCOL_VERSION,
AGENT_CHECK_UNKNOWN_MESSAGE,
AGENT_CHECK_INVALID_SIZE,
AGENT_CHECK_TRUNCATED,
AGENT_CHECK_INVALID_DATA,
} AgentCheckResult;
/**
* Check message from network and fix endianness
* Returns AGENT_CHECK_NO_ERROR if message is valid.
* message buffer size should be message_header->size.
*/
AgentCheckResult
agent_check_message(const VDAgentMessage *message_header, uint8_t *message,
const uint32_t *capabilities, uint32_t capabilities_size);
SPICE_END_DECLS

510
common/agent_interface.c Normal file
View File

@ -0,0 +1,510 @@
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2019 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdlib.h>
#include <sys/time.h>
#include <string.h>
#include <glib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/eventfd.h>
#include <errno.h>
#include <poll.h>
#include <common/agent_interface.h>
typedef struct sockaddr SA;
static GThread *recorder_comm_thr;
static bool agent_terminated = false;
static int terminate_efd = -1;
static FILE *communication_f = NULL;
#define NB_MAX_RECORDERS 16
static recorder_info *recorders[NB_MAX_RECORDERS];
static uint32_t nb_recorders = 0;
static forward_quality_cb_t forward_quality_cb;
static void *forward_quality_cb_data;
static on_connect_cb_t on_connect_cb;
static void *on_connect_cb_data;
static uintptr_t recorder_tick(void);
#ifndef RECORDER_HZ
#define RECORDER_HZ 1000000
#endif // RECORDER_HZ
static GMutex mutex_socket;
static int agent_initialize_communication(int socket)
{
uint32_t i;
int ret = -1;
FILE *socket_f;
g_mutex_lock(&mutex_socket);
if (communication_f != NULL) {
g_warning("A client is already connected, rejecting the connection.");
goto unlock;
}
socket_f = fdopen(socket, "w+b");
fprintf(socket_f, "Recorders: ");
for (i = 0; i < nb_recorders; i++) {
g_debug("Sending %s", recorders[i]->name);
fprintf(socket_f, "%s;", recorders[i]->name);
}
fprintf(socket_f, "\n");
fflush(socket_f);
for (i = 0; i < nb_recorders; i++) {
char enable;
if (read(socket, &enable, sizeof(enable)) != sizeof(enable)) {
g_warning("Invalid read on the client socket");
goto unlock;
}
if (enable != '0' && enable != '1') {
g_critical("Invalid enable-value received for recorder '%s': %u",
recorders[i]->name, enable);
goto unlock;
}
if (enable == '0') {
continue;
}
recorders[i]->trace = 1;
g_info("Enable recorder '%s'", recorders[i]->name);
}
if (on_connect_cb && on_connect_cb(on_connect_cb_data)) {
goto unlock;
}
communication_f = socket_f;
ret = 0;
unlock:
g_mutex_unlock(&mutex_socket);
return ret;
}
static void agent_finalize_communication(int socket)
{
uint32_t i;
g_info("Communication socket closed.");
g_mutex_lock(&mutex_socket);
g_assert(socket == fileno(communication_f));
fclose(communication_f);
communication_f = NULL;
for (i = 0; i < nb_recorders; i++) {
recorders[i]->trace = 0;
}
g_mutex_unlock(&mutex_socket);
}
static void forward_quality(const char *quality)
{
if (!forward_quality_cb) {
g_warning("Quality: No callback set, dropping the message (%s).", quality);
return;
}
g_info("Quality: Forwarding '%s'", quality);
forward_quality_cb(forward_quality_cb_data, quality);
}
static int agent_process_communication(int socket)
{
static char msg_in[128];
static long unsigned int len = 0;
g_assert(socket == fileno(communication_f));
int nbytes = read(socket, msg_in + len, 1);
if (nbytes < 0 && errno == EINTR) {
return 0;
}
if (nbytes <= 0) {
agent_finalize_communication(socket);
return -1; // socket closed
}
if (msg_in[len] == '\0') {
// process quality indicator
forward_quality(msg_in);
len = 0;
return 0;
}
len += nbytes;
if (len >= sizeof(msg_in) - 1) {
msg_in[sizeof(msg_in) - 1] = '\0';
g_warning("Invalid message received (too long?): %s", msg_in);
len = 0;
}
return 0;
}
static int make_socket(guint port)
{
struct sockaddr_in servaddr;
int listen_socket = socket(AF_INET, SOCK_STREAM, 0);
if (listen_socket == -1) {
g_critical("socket creation failed");
return -1;
}
int enable = 1;
if (setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) {
g_critical("setsockopt(SO_REUSEADDR) failed");
close(listen_socket);
return -1;
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
if (bind(listen_socket, (SA *) &servaddr, sizeof(servaddr)) != 0) {
g_critical("socket bind failed");
close(listen_socket);
return -1;
}
return listen_socket;
}
static gpointer handle_communications(gpointer user_data)
{
struct pollfd fds[3];
int nb_fd = 0;
int listen_socket;
int i;
guint port = GPOINTER_TO_UINT(user_data);
listen_socket = make_socket(port);
if (listen_socket < 0) {
return NULL;
}
g_debug("Listening!");
if ((listen(listen_socket, 1)) != 0) {
g_critical("listen failed: %m");
return NULL;
}
fds[0].fd = terminate_efd;
fds[0].events = POLLIN;
fds[1].fd = listen_socket;
fds[1].events = POLLIN;
nb_fd = 2;
while (!agent_terminated) {
/* Block until input arrives on one or more active sockets. */
int ret = poll(fds, nb_fd, -1);
if (ret < 0) {
g_critical("poll failed: %m");
break;
}
/* Service all the sockets with input pending. */
for (i = 0; i < nb_fd; i++) {
int fd = fds[i].fd;
if (fd == terminate_efd) {
if (fds[i].revents & POLLIN) {
g_assert(agent_terminated);
break;
}
} else if (fd == listen_socket) {
if (fds[i].revents & ~POLLIN) {
g_critical("server socket closed");
break;
}
if (!(fds[i].revents & POLLIN)) {
continue;
}
/* Connection request on original socket. */
int new_fd = accept(listen_socket, NULL, NULL);
if (new_fd < 0) {
g_critical("accept failed: %m");
break;
}
if (nb_fd == 3) {
close(new_fd);
g_warning("Too many clients accepted ...");
continue;
}
g_debug("Agent Interface: client connected!");
if (agent_initialize_communication(new_fd)) {
close(new_fd);
g_warning("Initialization failed ...");
continue;
}
fds[nb_fd].fd = new_fd;
fds[nb_fd].events = POLLIN;
nb_fd++;
/* fds array modified, restart the poll. */
break;
} else {
if (!(fds[i].revents & POLLIN)) {
continue;
}
/* Data arriving on an already-connected socket. */
if (agent_process_communication(fd) < 0) {
nb_fd--;
}
}
}
}
close(terminate_efd);
close(listen_socket);
g_info("Agent interface thread: bye!");
return NULL;
}
static void recorder_deregister(void);
static void recorder_initialization(unsigned int port)
{
GError *error = NULL;
terminate_efd = eventfd(0, 0);
if (terminate_efd == -1) {
g_critical("eventfd failed: %m");
return;
}
recorder_comm_thr = g_thread_try_new("smart_agent_interface",
handle_communications,
GUINT_TO_POINTER((guint) port), &error);
if (error) {
g_assert(!recorder_comm_thr);
g_critical("Error: Could not start the agent interface thread: %s", error->message);
g_error_free(error);
return;
}
atexit(recorder_deregister);
}
static void recorder_interrupt_communications(void)
{
agent_terminated = true;
uint64_t msg = 1;
ssize_t s = write(terminate_efd, &msg, sizeof(uint64_t));
if (s != sizeof(uint64_t)) {
g_warning("failed to send recorder thread termination event: %m");
}
}
static void recorder_deregister(void)
{
if (recorder_comm_thr) {
recorder_interrupt_communications();
g_thread_join(recorder_comm_thr);
recorder_comm_thr = NULL;
}
}
void recorder_activate(recorder_info *recorder)
{
if (nb_recorders >= NB_MAX_RECORDERS) {
g_critical("Too many recorders configured (nb max: %d)", NB_MAX_RECORDERS);
return;
}
recorders[nb_recorders] = recorder;
nb_recorders++;
}
static void do_send_entry(FILE *dest, recorder_info *info, recorder_entry *entry, va_list args)
{
fprintf(dest, "Name: %s\nFunction: %s\nTime: %lu\n",
info->name, entry->where, entry->timestamp);
vfprintf(dest, entry->format, args);
fprintf(dest, "\n\n");
fflush(dest);
}
static void recorder_trace_entry(recorder_info *info, recorder_entry *entry, ...)
// ----------------------------------------------------------------------------
// Show a recorder entry when a trace is enabled
// ----------------------------------------------------------------------------
{
va_list args;
if (strchr(entry->format, '\n') != NULL) {
g_critical("Agent records cannot contain '\n' char ... (%s)", entry->where);
return;
}
// send info/entry to the socket
g_mutex_lock(&mutex_socket);
if (communication_f == NULL) {
g_mutex_unlock(&mutex_socket);
return;
}
va_start(args, entry);
do_send_entry(communication_f, info, entry, args);
va_end(args);
if (g_strcmp0(g_getenv("SPICE_AGENT_LOG_RECORDS"), "1") == 0) {
va_start(args, entry);
do_send_entry(stderr, info, entry, args);
va_end(args);
}
g_mutex_unlock(&mutex_socket);
}
void agent_interface_start(unsigned int port)
{
g_info("Launch on port %u", port);
recorder_initialization(port);
}
void agent_interface_set_forward_quality_cb(forward_quality_cb_t cb, void *data)
{
g_debug("Received forward_quality callback");
forward_quality_cb = cb;
forward_quality_cb_data = data;
}
void agent_interface_set_on_connect_cb(on_connect_cb_t cb, void *data)
{
g_debug("Received on_connect callback");
on_connect_cb = cb;
on_connect_cb_data = data;
}
void recorder_append(recorder_info *rec,
const char *where,
const char *format,
uintptr_t a0,
uintptr_t a1,
uintptr_t a2,
uintptr_t a3)
// ----------------------------------------------------------------------------
// Enter a record entry in ring buffer with given set of args
// ----------------------------------------------------------------------------
{
recorder_entry entry;
if (!rec->trace) {
return;
}
entry.format = format;
entry.timestamp = recorder_tick();
entry.where = where;
recorder_trace_entry(rec, &entry, a0, a1, a2, a3);
}
void recorder_append2(recorder_info *rec,
const char *where,
const char *format,
uintptr_t a0,
uintptr_t a1,
uintptr_t a2,
uintptr_t a3,
uintptr_t a4,
uintptr_t a5,
uintptr_t a6,
uintptr_t a7)
// ----------------------------------------------------------------------------
// Enter a double record (up to 8 args)
// ----------------------------------------------------------------------------
{
recorder_entry entry;
if (!rec->trace) {
return;
}
entry.format = format;
entry.timestamp = recorder_tick();
entry.where = where;
recorder_trace_entry(rec, &entry, a0, a1, a2, a3, a4, a5, a6, a7);
}
// ============================================================================
//
// Support functions
//
// ============================================================================
static uintptr_t recorder_tick(void)
// ----------------------------------------------------------------------------
// Return the "ticks" as stored in the recorder
// ----------------------------------------------------------------------------
{
struct timeval t;
gettimeofday(&t, NULL);
return t.tv_sec * RECORDER_HZ + t.tv_usec / (1000000 / RECORDER_HZ);
}

564
common/agent_interface.h Normal file
View File

@ -0,0 +1,564 @@
#pragma once
// *****************************************************************************
// This software is licensed under the GNU Lesser General Public License v2+
// (C) 2017-2019, Christophe de Dinechin <christophe@dinechin.org>
// *****************************************************************************
// This file was part of Recorder
//
// Recorder is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// Recorder 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Recorder, in a file named COPYING.
// If not, see <https://www.gnu.org/licenses/>.
// *****************************************************************************
/* This file is based on Recorder's recorder.h file, that describes a general-
* purpose instrumentation interface. agent_interface.h is a trimmed-down
* version of it. */
#include <stdarg.h>
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
static inline void
recorder_dump_on_common_signals(unsigned add, unsigned remove)
{
}
// ============================================================================
//
// Recorder data structures
//
// ============================================================================
typedef struct recorder_entry
/// ---------------------------------------------------------------------------
/// Entry in the flight recorder.
///----------------------------------------------------------------------------
/// Notice that the arguments are stored as "intptr_t" because that type
/// is guaranteed to be the same size as a pointer. This allows us to
/// properly align recorder entries to powers of 2 for efficiency.
/// Also read explanations of \ref _recorder_double and \ref _recorder_float
/// below regarding how to use floating-point with the recorder.
{
const char *format; ///< Printf-style format for record + file/line
uintptr_t timestamp; ///< Time at which record took place
const char *where; ///< Source code function
uintptr_t args[4]; ///< Four arguments, for a total of 8 fields
} recorder_entry;
/// A global counter indicating the order of entries across recorders.
/// this is incremented atomically for each record() call.
/// It must be exposed because all XYZ_record() implementations need to
/// touch the same shared variable in order to provide a global order.
extern uintptr_t recorder_order;
typedef struct recorder_info
///----------------------------------------------------------------------------
/// A linked list of the activated recorders
///----------------------------------------------------------------------------
{
intptr_t trace; ///< Trace this recorder
const char * name; ///< Name of this parameter / recorder
const char * description;///< Description of what is recorded
recorder_entry data[0]; ///< Data for this recorder
} recorder_info;
// ============================================================================
//
// Adding data to a recorder
//
// ============================================================================
extern void recorder_append(recorder_info *rec,
const char *where,
const char *format,
uintptr_t a0,
uintptr_t a1,
uintptr_t a2,
uintptr_t a3);
extern void recorder_append2(recorder_info *rec,
const char *where,
const char *format,
uintptr_t a0,
uintptr_t a1,
uintptr_t a2,
uintptr_t a3,
uintptr_t a4,
uintptr_t a5,
uintptr_t a6,
uintptr_t a7);
extern void recorder_append3(recorder_info *rec,
const char *where,
const char *format,
uintptr_t a0,
uintptr_t a1,
uintptr_t a2,
uintptr_t a3,
uintptr_t a4,
uintptr_t a5,
uintptr_t a6,
uintptr_t a7,
uintptr_t a8,
uintptr_t a9,
uintptr_t a10,
uintptr_t a11);
/// Activate a recorder (during construction time)
extern void recorder_activate(recorder_info *recorder);
// ============================================================================
//
// Declaration of recorders and tweaks
//
// ============================================================================
#define RECORDER_DECLARE(Name) \
/* ----------------------------------------------------------------*/ \
/* Declare a recorder with the given name (for use in headers) */ \
/* ----------------------------------------------------------------*/ \
extern recorder_info * const recorder_info_ptr_for_##Name; \
extern struct recorder_info_for_##Name recorder_info_for_##Name
// ============================================================================
//
// Definition of recorders and tweaks
//
// ============================================================================
#define RECORDER(Name, Size, Info) RECORDER_DEFINE(Name,Size,Info)
#define RECORDER_DEFINE(Name, Size, Info) \
/*!----------------------------------------------------------------*/ \
/*! Define a recorder type with Size elements */ \
/*!----------------------------------------------------------------*/ \
/*! \param Name is the C name fo the recorder. \
*! \param Size is the number of entries in the circular buffer. \
*! \param Info is a description of the recorder for help. */ \
\
/* The entry in linked list for this type */ \
struct recorder_info_for_##Name \
{ \
recorder_info info; \
recorder_entry data[Size]; \
} \
recorder_info_for_##Name = \
{ \
{ \
0, #Name, Info, {} \
}, \
{} \
}; \
recorder_info * const recorder_info_ptr_for_##Name = \
&recorder_info_for_##Name.info; \
\
RECORDER_CONSTRUCTOR \
static void recorder_activate_##Name(void) \
/* ----------------------------------------------------------------*/ \
/* Activate recorder before entering main() */ \
/* ----------------------------------------------------------------*/ \
{ \
recorder_activate(RECORDER_INFO(Name)); \
} \
\
/* Purposefully generate compile error if macro not followed by ; */ \
extern void recorder_activate(recorder_info *recorder)
typedef struct SpiceDummyTweak {
intptr_t tweak_value;
} SpiceDummyTweak;
typedef struct SpiceEmptyStruct {
char dummy[0];
} SpiceEmptyStruct;
#define RECORDER_TWEAK_DECLARE(rec) \
extern const SpiceDummyTweak spice_recorder_tweak_ ## rec
#define RECORDER_TWEAK_DEFINE(rec, value, comment) \
const SpiceDummyTweak spice_recorder_tweak_ ## rec = { (value) }
#define RECORDER_TWEAK(rec) \
((spice_recorder_tweak_ ## rec).tweak_value)
#define RECORDER_TRACE(rec) \
(sizeof(struct recorder_info_for_ ## rec) != sizeof(SpiceEmptyStruct))
// ============================================================================
//
// Access to recorder and tweak info
//
// ============================================================================
#define RECORDER_INFO(Name) (recorder_info_ptr_for_##Name)
// ============================================================================
//
// Recording stuff
//
// ============================================================================
#define record(Name, ...) RECORD_MACRO(Name, __VA_ARGS__)
#define RECORD(Name,...) RECORD_MACRO(Name, __VA_ARGS__)
#define RECORD_MACRO(Name, ...) \
RECORD_(RECORD,RECORD_COUNT_(__VA_ARGS__),Name,__VA_ARGS__)
#define RECORD_(RECORD,RCOUNT,Name,...) \
RECORD__(RECORD,RCOUNT,Name,__VA_ARGS__)
#define RECORD__(RECORD,RCOUNT,Name,...) \
RECORD##RCOUNT(Name,__VA_ARGS__)
#define RECORD_COUNT_(...) RECORD_COUNT__(Dummy,##__VA_ARGS__,_X,_X,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,_1,_0)
#define RECORD_COUNT__(Dummy,_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_N,...) _N
#define RECORD_0(Name, Format) \
recorder_append(RECORDER_INFO(Name), \
RECORDER_SOURCE_FUNCTION, \
RECORDER_SOURCE_LOCATION \
Format, 0, 0, 0, 0)
#define RECORD_1(Name, Format, a) \
recorder_append(RECORDER_INFO(Name), \
RECORDER_SOURCE_FUNCTION, \
RECORDER_SOURCE_LOCATION \
Format, \
RECORDER_ARG(a), 0, 0, 0)
#define RECORD_2(Name, Format, a,b) \
recorder_append(RECORDER_INFO(Name), \
RECORDER_SOURCE_FUNCTION, \
RECORDER_SOURCE_LOCATION \
Format, \
RECORDER_ARG(a), \
RECORDER_ARG(b), 0, 0)
#define RECORD_3(Name, Format, a,b,c) \
recorder_append(RECORDER_INFO(Name), \
RECORDER_SOURCE_FUNCTION, \
RECORDER_SOURCE_LOCATION \
Format, \
RECORDER_ARG(a), \
RECORDER_ARG(b), \
RECORDER_ARG(c), 0)
#define RECORD_4(Name, Format, a,b,c,d) \
recorder_append(RECORDER_INFO(Name), \
RECORDER_SOURCE_FUNCTION, \
RECORDER_SOURCE_LOCATION \
Format, \
RECORDER_ARG(a), \
RECORDER_ARG(b), \
RECORDER_ARG(c), \
RECORDER_ARG(d))
#define RECORD_5(Name, Format, a,b,c,d,e) \
recorder_append2(RECORDER_INFO(Name), \
RECORDER_SOURCE_FUNCTION, \
RECORDER_SOURCE_LOCATION \
Format, \
RECORDER_ARG(a), \
RECORDER_ARG(b), \
RECORDER_ARG(c), \
RECORDER_ARG(d), \
RECORDER_ARG(e), 0, 0, 0)
#define RECORD_6(Name, Format, a,b,c,d,e,f) \
recorder_append2(RECORDER_INFO(Name), \
RECORDER_SOURCE_FUNCTION, \
RECORDER_SOURCE_LOCATION \
Format, \
RECORDER_ARG(a), \
RECORDER_ARG(b), \
RECORDER_ARG(c), \
RECORDER_ARG(d), \
RECORDER_ARG(e), \
RECORDER_ARG(f), 0, 0)
#define RECORD_7(Name, Format, a,b,c,d,e,f,g) \
recorder_append2(RECORDER_INFO(Name), \
RECORDER_SOURCE_FUNCTION, \
RECORDER_SOURCE_LOCATION \
Format, \
RECORDER_ARG(a), \
RECORDER_ARG(b), \
RECORDER_ARG(c), \
RECORDER_ARG(d), \
RECORDER_ARG(e), \
RECORDER_ARG(f), \
RECORDER_ARG(g), 0)
#define RECORD_8(Name, Format, a,b,c,d,e,f,g,h) \
recorder_append2(RECORDER_INFO(Name), \
RECORDER_SOURCE_FUNCTION, \
RECORDER_SOURCE_LOCATION \
Format, \
RECORDER_ARG(a), \
RECORDER_ARG(b), \
RECORDER_ARG(c), \
RECORDER_ARG(d), \
RECORDER_ARG(e), \
RECORDER_ARG(f), \
RECORDER_ARG(g), \
RECORDER_ARG(h))
#define RECORD_9(Name, Format, a,b,c,d,e,f,g,h,i) \
recorder_append3(RECORDER_INFO(Name), \
RECORDER_SOURCE_FUNCTION, \
RECORDER_SOURCE_LOCATION \
Format, \
RECORDER_ARG(a), \
RECORDER_ARG(b), \
RECORDER_ARG(c), \
RECORDER_ARG(d), \
RECORDER_ARG(e), \
RECORDER_ARG(f), \
RECORDER_ARG(g), \
RECORDER_ARG(h), \
RECORDER_ARG(i), 0,0,0)
#define RECORD_10(Name, Format, a,b,c,d,e,f,g,h,i,j) \
recorder_append3(RECORDER_INFO(Name), \
RECORDER_SOURCE_FUNCTION, \
RECORDER_SOURCE_LOCATION \
Format, \
RECORDER_ARG(a), \
RECORDER_ARG(b), \
RECORDER_ARG(c), \
RECORDER_ARG(d), \
RECORDER_ARG(e), \
RECORDER_ARG(f), \
RECORDER_ARG(g), \
RECORDER_ARG(h), \
RECORDER_ARG(i), \
RECORDER_ARG(j), 0,0)
#define RECORD_11(Name, Format, a,b,c,d,e,f,g,h,i,j,k) \
recorder_append3(RECORDER_INFO(Name), \
RECORDER_SOURCE_FUNCTION, \
RECORDER_SOURCE_LOCATION \
Format, \
RECORDER_ARG(a), \
RECORDER_ARG(b), \
RECORDER_ARG(c), \
RECORDER_ARG(d), \
RECORDER_ARG(e), \
RECORDER_ARG(f), \
RECORDER_ARG(g), \
RECORDER_ARG(h), \
RECORDER_ARG(i), \
RECORDER_ARG(j), \
RECORDER_ARG(k),0)
#define RECORD_12(Name,Format,a,b,c,d,e,f,g,h,i,j,k,l) \
recorder_append3(RECORDER_INFO(Name), \
RECORDER_SOURCE_FUNCTION, \
RECORDER_SOURCE_LOCATION \
Format, \
RECORDER_ARG(a), \
RECORDER_ARG(b), \
RECORDER_ARG(c), \
RECORDER_ARG(d), \
RECORDER_ARG(e), \
RECORDER_ARG(f), \
RECORDER_ARG(g), \
RECORDER_ARG(h), \
RECORDER_ARG(i), \
RECORDER_ARG(j), \
RECORDER_ARG(k), \
RECORDER_ARG(l))
#define RECORD_X(Name, ...) RECORD_TOO_MANY_ARGS(printf(__VA_ARGS__))
// Some ugly macro drudgery to make things easy to use. Adjust type.
#ifdef __cplusplus
#define RECORDER_ARG(arg) _recorder_arg(arg)
#else // !__cplusplus
#if defined(__GNUC__) && !defined(__clang__)
# if __GNUC__ <= 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 9)
# define RECORDER_WITHOUT_GENERIC
# endif
#endif // __GNUC__
#ifdef RECORDER_WITHOUT_GENERIC
#define RECORDER_ARG(arg) ((uintptr_t) (arg))
#else // !RECORDER_WITHOUT_GENERIC
#define RECORDER_ARG(arg) \
_Generic(arg, \
unsigned char: _recorder_unsigned, \
unsigned short: _recorder_unsigned, \
unsigned: _recorder_unsigned, \
unsigned long: _recorder_unsigned, \
unsigned long long:_recorder_unsigned, \
char: _recorder_char, \
signed char: _recorder_signed, \
signed short: _recorder_signed, \
signed: _recorder_signed, \
signed long: _recorder_signed, \
signed long long: _recorder_signed, \
float: _recorder_float, \
double: _recorder_double, \
default: _recorder_pointer)(arg)
#endif // RECORDER_WITHOUT_GENERIC
#endif // __cplusplus
// ============================================================================
//
// Timing information
//
// ============================================================================
#define RECORD_TIMING_BEGIN(rec) \
do { RECORD(rec, "begin");
#define RECORD_TIMING_END(rec, op, name, value) \
RECORD(rec, "end" op name); \
} while (0)
// ============================================================================
//
// Support macros
//
// ============================================================================
#define RECORDER_SOURCE_FUNCTION __func__ /* Works in C99 and C++11 */
#define RECORDER_SOURCE_LOCATION __FILE__ ":" RECORDER_STRING(__LINE__) ":"
#define RECORDER_STRING(LINE) RECORDER_STRING_(LINE)
#define RECORDER_STRING_(LINE) #LINE
#ifdef __GNUC__
#define RECORDER_CONSTRUCTOR __attribute__((constructor))
#else
#define RECORDER_CONSTRUCTOR
#endif
#ifdef __cplusplus
}
#endif // __cplusplus
// ============================================================================
//
// Utility: Convert floating point values for vararg format
//
// ============================================================================
//
// The recorder stores only uintptr_t in recorder entries. Integer types
// are promoted, pointer types are converted. Floating point values
// are converted a floating point type of the same size as uintptr_t,
// i.e. float are converted to double on 64-bit platforms, and conversely.
#ifdef __cplusplus
#include <string>
// In C++, we don't use _Generic but actual overloading
template <class inttype>
static inline uintptr_t _recorder_arg(inttype i)
{
return (uintptr_t) i;
}
static inline uintptr_t _recorder_arg(const std::string &arg)
{
return (uintptr_t) arg.c_str();
}
#define _recorder_float _recorder_arg
#define _recorder_double _recorder_arg
#else // !__cplusplus
static inline uintptr_t _recorder_char(char c)
// ----------------------------------------------------------------------------
// Necessary because of the way generic selections work
// ----------------------------------------------------------------------------
{
return c;
}
static inline uintptr_t _recorder_unsigned(uintptr_t i)
// ----------------------------------------------------------------------------
// Necessary because of the way generic selections work
// ----------------------------------------------------------------------------
{
return i;
}
static inline uintptr_t _recorder_signed(intptr_t i)
// ----------------------------------------------------------------------------
// Necessary because of the way generic selections work
// ----------------------------------------------------------------------------
{
return (uintptr_t) i;
}
static inline uintptr_t _recorder_pointer(const void *i)
// ----------------------------------------------------------------------------
// Necessary because of the way generic selections work
// ----------------------------------------------------------------------------
{
return (uintptr_t) i;
}
#endif // __cplusplus
static inline uintptr_t _recorder_float(float f)
// ----------------------------------------------------------------------------
// Convert floating point number to intptr_t representation for recorder
// ----------------------------------------------------------------------------
{
if (sizeof(float) == sizeof(intptr_t)) {
union { float f; uintptr_t i; } u;
u.f = f;
return u.i;
} else {
union { double d; uintptr_t i; } u;
u.d = (double) f;
return u.i;
}
}
static inline uintptr_t _recorder_double(double d)
// ----------------------------------------------------------------------------
// Convert double-precision floating point number to intptr_t representation
// ----------------------------------------------------------------------------
{
if (sizeof(double) == sizeof(intptr_t)) {
union { double d; uintptr_t i; } u;
u.d = d;
return u.i;
} else {
// Better to lose precision than not store any data
union { float f; uintptr_t i; } u;
u.f = d;
return u.i;
}
}
// ============================================================================
// Agent-Interface specific definitions
// ============================================================================
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// launch the Agent-Interface server socket
extern void agent_interface_start(unsigned int port);
//
typedef void (*forward_quality_cb_t)(void *, const char *);
extern void agent_interface_set_forward_quality_cb(forward_quality_cb_t cb, void *data);
// set a callback function triggered when a new client connects to the socket
typedef int (*on_connect_cb_t)(void *);
extern void agent_interface_set_on_connect_cb(on_connect_cb_t cb, void *data);
#ifdef __cplusplus
}
#endif // __cplusplus

View File

@ -22,18 +22,20 @@
*/
#include <config.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#if !defined(WIN32) || defined(__MINGW32__)
#include "backtrace.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#ifndef __MINGW32__
#include <sys/wait.h>
#endif
#include "spice_common.h"
#define GSTACK_PATH "/usr/bin/gstack"
#if HAVE_EXECINFO_H
@ -75,7 +77,6 @@ static int spice_backtrace_gstack(void)
/* CHILD */
char parent[16];
seteuid(0);
close(STDIN_FILENO);
close(STDOUT_FILENO);
dup2(pipefd[1],STDOUT_FILENO);
@ -131,3 +132,4 @@ void spice_backtrace(void)
spice_backtrace_backtrace();
}
}
#endif

View File

@ -16,8 +16,8 @@
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef BACKTRACE_H
#define BACKTRACE_H
#ifndef H_SPICE_COMMON_BACKTRACE
#define H_SPICE_COMMON_BACKTRACE
#include <spice/macros.h>
@ -31,4 +31,4 @@ void spice_backtrace(void);
SPICE_END_DECLS
#endif // BACKTRACE_H
#endif // H_SPICE_COMMON_BACKTRACE

File diff suppressed because it is too large Load Diff

View File

@ -16,24 +16,17 @@
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _H_CANVAS_BASE
#define _H_CANVAS_BASE
#ifndef H_SPICE_COMMON_CANVAS_BASE
#define H_SPICE_COMMON_CANVAS_BASE
#ifndef SPICE_CANVAS_INTERNAL
#error "This header shouldn't be included directly"
#endif
#include <spice/macros.h>
#include "pixman_utils.h"
#include "lz.h"
#include "region.h"
#include "draw.h"
#ifdef WIN32
#include <windows.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
SPICE_BEGIN_DECLS
typedef void (*spice_destroy_fn_t)(void *data);
@ -64,7 +57,7 @@ typedef struct {
} SpiceImageCacheOps;
struct _SpiceImageCache {
SpiceImageCacheOps *ops;
const SpiceImageCacheOps *ops;
};
typedef struct {
@ -73,7 +66,7 @@ typedef struct {
} SpiceImageSurfacesOps;
struct _SpiceImageSurfaces {
SpiceImageSurfacesOps *ops;
const SpiceImageSurfacesOps *ops;
};
typedef struct {
@ -137,6 +130,7 @@ typedef struct {
void (*draw_text)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceText *text);
void (*draw_stroke)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke);
void (*draw_rop3)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3);
void (*draw_composite)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceComposite *composite);
void (*draw_blend)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend);
void (*draw_blackness)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness);
void (*draw_whiteness)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness);
@ -144,9 +138,6 @@ typedef struct {
void (*draw_transparent)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceTransparent* transparent);
void (*draw_alpha_blend)(SpiceCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlend* alpha_blend);
void (*put_image)(SpiceCanvas *canvas,
#ifdef WIN32
HDC dc,
#endif
const SpiceRect *dest, const uint8_t *src_data,
uint32_t src_width, uint32_t src_height, int src_stride,
const QRegion *clip);
@ -310,18 +301,13 @@ typedef struct {
void (*copy_region)(SpiceCanvas *canvas,
pixman_region32_t *dest_region,
int dx, int dy);
pixman_image_t *(*get_image)(SpiceCanvas *canvas);
pixman_image_t *(*get_image)(SpiceCanvas *canvas, int force_opaque);
} SpiceCanvasOps;
void spice_canvas_set_usr_data(SpiceCanvas *canvas, void *data, spice_destroy_fn_t destroy_fn);
void *spice_canvas_get_usr_data(SpiceCanvas *canvas);
struct _SpiceCanvas {
SpiceCanvasOps *ops;
};
#ifdef __cplusplus
}
#endif
SPICE_END_DECLS
#endif

180
common/canvas_utils.c Normal file
View File

@ -0,0 +1,180 @@
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2009 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include "canvas_utils.h"
#include "mem.h"
typedef struct PixmanData {
uint8_t *data;
pixman_format_code_t format;
} PixmanData;
static void release_data(SPICE_GNUC_UNUSED pixman_image_t *image,
void *release_data)
{
PixmanData *data = (PixmanData *)release_data;
free(data->data);
free(data);
}
static PixmanData *
pixman_image_add_data(pixman_image_t *image)
{
PixmanData *data;
data = (PixmanData *)pixman_image_get_destroy_data(image);
if (data == NULL) {
data = (PixmanData *)calloc(1, sizeof(PixmanData));
if (data == NULL) {
spice_error("out of memory");
}
pixman_image_set_destroy_function(image,
release_data,
data);
}
return data;
}
void
spice_pixman_image_set_format(pixman_image_t *image,
pixman_format_code_t format)
{
PixmanData *data;
data = pixman_image_add_data(image);
data->format = format;
}
int spice_pixman_image_get_format(pixman_image_t *image, pixman_format_code_t *format)
{
PixmanData *data;
spice_return_val_if_fail(format != NULL, 0);
data = (PixmanData *)pixman_image_get_destroy_data(image);
if (data != NULL && data->format != 0) {
*format = data->format;
return 1;
}
spice_warn_if_reached();
return 0;
}
pixman_image_t *surface_create_stride(pixman_format_code_t format, int width, int height,
int stride)
{
uint8_t *data;
uint8_t *stride_data;
pixman_image_t *surface;
PixmanData *pixman_data;
data = (uint8_t *)spice_malloc_n(abs(stride), height);
if (stride < 0) {
stride_data = data + (-stride) * (height - 1);
} else {
stride_data = data;
}
surface = pixman_image_create_bits(format, width, height, (uint32_t *)stride_data, stride);
if (surface == NULL) {
free(data);
data = NULL;
spice_error("create surface failed, out of memory");
}
pixman_data = pixman_image_add_data(surface);
pixman_data->data = data;
pixman_data->format = format;
return surface;
}
pixman_image_t * surface_create(pixman_format_code_t format, int width, int height, int top_down)
{
if (top_down) {
pixman_image_t *surface;
PixmanData *data;
surface = pixman_image_create_bits(format, width, height, NULL, 0);
data = pixman_image_add_data(surface);
data->format = format;
return surface;
} else {
// NOTE: we assume here that the lz decoders always decode to RGB32.
int stride = 0;
switch (format) {
case PIXMAN_a8r8g8b8:
case PIXMAN_x8r8g8b8:
#ifdef WORDS_BIGENDIAN
case PIXMAN_b8g8r8a8:
case PIXMAN_b8g8r8x8:
#endif
stride = width * 4;
break;
case PIXMAN_r8g8b8:
#ifdef WORDS_BIGENDIAN
case PIXMAN_b8g8r8:
#endif
// NOTE: LZ4 also decodes to RGB24
stride = SPICE_ALIGN(width * 3, 4);
break;
case PIXMAN_x1r5g5b5:
case PIXMAN_r5g6b5:
stride = SPICE_ALIGN(width * 2, 4);
break;
case PIXMAN_a8:
stride = SPICE_ALIGN(width, 4);
break;
case PIXMAN_a1:
stride = SPICE_ALIGN(width, 32) / 8;
break;
default:
spice_error("invalid format");
}
stride = -stride;
return surface_create_stride(format, width, height, stride);
}
}
pixman_image_t *alloc_lz_image_surface(LzDecodeUsrData *canvas_data,
pixman_format_code_t pixman_format, int width,
int height, int gross_pixels, int top_down)
{
int stride;
pixman_image_t *surface = NULL;
stride = (gross_pixels / height) * (PIXMAN_FORMAT_BPP (pixman_format) / 8);
/* pixman requires strides to be 4-byte aligned */
stride = SPICE_ALIGN(stride, 4);
if (!top_down) {
stride = -stride;
}
surface = surface_create_stride(pixman_format, width, height, stride);
canvas_data->out_surface = surface;
return surface;
}

View File

@ -16,56 +16,29 @@
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _H_CANVAS_UTILS
#define _H_CANVAS_UTILS
#ifdef WIN32
#include <windows.h>
#endif
#ifndef H_SPICE_COMMON_CANVAS_UTILS
#define H_SPICE_COMMON_CANVAS_UTILS
#include <spice/types.h>
#include <spice/macros.h>
#include "pixman_utils.h"
#include "lz.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct PixmanData {
#ifdef WIN32
HBITMAP bitmap;
HANDLE mutex;
#endif
uint8_t *data;
pixman_format_code_t format;
} PixmanData;
SPICE_BEGIN_DECLS
void spice_pixman_image_set_format(pixman_image_t *image,
pixman_format_code_t format);
pixman_format_code_t spice_pixman_image_get_format(pixman_image_t *image);
int spice_pixman_image_get_format(pixman_image_t *image, pixman_format_code_t *format);
#ifdef WIN32
pixman_image_t *surface_create(HDC dc, pixman_format_code_t format,
int width, int height, int top_down);
#else
pixman_image_t *surface_create(pixman_format_code_t format, int width, int height, int top_down);
#endif
#ifdef WIN32
pixman_image_t *surface_create_stride(HDC dc, pixman_format_code_t format, int width, int height,
int stride);
#else
pixman_image_t *surface_create_stride(pixman_format_code_t format, int width, int height,
int stride);
#endif
typedef struct LzDecodeUsrData {
#ifdef WIN32
HDC dc;
#endif
pixman_image_t *out_surface;
} LzDecodeUsrData;
@ -73,8 +46,7 @@ typedef struct LzDecodeUsrData {
pixman_image_t *alloc_lz_image_surface(LzDecodeUsrData *canvas_data,
pixman_format_code_t pixman_format, int width,
int height, int gross_pixels, int top_down);
#ifdef __cplusplus
}
#endif
SPICE_END_DECLS
#endif

View File

@ -1,6 +1,6 @@
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2009 Red Hat, Inc.
Copyright (C) 2010 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@ -16,23 +16,19 @@
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _H_GLCTX
#define _H_GLCTX
#ifndef H_SPICE_COMMON_CLIENT_MARSHALLERS
#define H_SPICE_COMMON_CLIENT_MARSHALLERS
#ifdef __cplusplus
extern "C" {
#endif
#include <spice/protocol.h>
typedef struct OGLCtx OGLCtx;
#include "messages.h"
#include "common/generated_client_marshallers.h"
#include "marshaller.h"
const char *oglctx_type_str(OGLCtx *ctx);
void oglctx_make_current(OGLCtx *ctx);
OGLCtx *pbuf_create(int width, int heigth);
OGLCtx *pixmap_create(int width, int heigth);
void oglctx_destroy(OGLCtx *ctx);
SPICE_BEGIN_DECLS
#ifdef __cplusplus
}
#endif
SpiceMessageMarshallers *spice_message_marshallers_get(void);
SPICE_END_DECLS
#endif

38
common/demarshallers.h Normal file
View File

@ -0,0 +1,38 @@
/*
Copyright (C) 2010 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef H_SPICE_COMMON_DEMARSHALLERS
#define H_SPICE_COMMON_DEMARSHALLERS
#include <stddef.h>
#include <spice/macros.h>
SPICE_BEGIN_DECLS
typedef void (*message_destructor_t)(uint8_t *message);
typedef uint8_t * (*spice_parse_channel_func_t)(uint8_t *message_start, uint8_t *message_end,
uint16_t message_type, int minor,
size_t *size_out,
message_destructor_t *free_message);
spice_parse_channel_func_t
spice_get_server_channel_parser(uint32_t channel, unsigned int *max_message_type);
spice_parse_channel_func_t
spice_get_client_channel_parser(uint32_t channel, unsigned int *max_message_type);
SPICE_END_DECLS
#endif

View File

@ -28,16 +28,15 @@
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _H_SPICE_DRAW
#define _H_SPICE_DRAW
#ifndef H_SPICE_COMMON_DRAW
#define H_SPICE_COMMON_DRAW
#include <spice/macros.h>
#include <spice/types.h>
#include <spice/enums.h>
#include "mem.h"
#ifdef __cplusplus
extern "C" {
#endif
SPICE_BEGIN_DECLS
#define SPICE_GET_ADDRESS(addr) ((void *)(uintptr_t)(addr))
#define SPICE_SET_ADDRESS(addr, val) ((addr) = (uintptr_t)(val))
@ -83,7 +82,7 @@ typedef struct SpiceClipRects {
} SpiceClipRects;
typedef struct SpiceClip {
uint32_t type;
uint8_t type;
SpiceClipRects *rects;
} SpiceClip;
@ -121,7 +120,7 @@ typedef struct SpiceSurface {
typedef struct SpiceQUICData {
uint32_t data_size;
SpiceChunks *data;
} SpiceQUICData, SpiceLZRGBData, SpiceJPEGData;
} SpiceQUICData, SpiceLZRGBData, SpiceJPEGData, SpiceLZ4Data;
typedef struct SpiceLZPLTData {
uint8_t flags;
@ -154,6 +153,7 @@ typedef struct SpiceImage {
SpiceLZRGBData lz_rgb;
SpiceLZPLTData lz_plt;
SpiceJPEGData jpeg;
SpiceLZ4Data lz4;
SpiceZlibGlzRGBData zlib_glz;
SpiceJPEGAlphaData jpeg_alpha;
} u;
@ -224,6 +224,26 @@ typedef struct SpiceRop3 {
SpiceQMask mask;
} SpiceRop3;
/* Given in 16.16 fixed point */
typedef struct SpiceTransform {
uint32_t t00;
uint32_t t01;
uint32_t t02;
uint32_t t10;
uint32_t t11;
uint32_t t12;
} SpiceTransform;
typedef struct SpiceComposite {
uint32_t flags;
SpiceImage *src_bitmap;
SpiceImage *mask_bitmap;
SpiceTransform src_transform;
SpiceTransform mask_transform;
SpicePoint16 src_origin;
SpicePoint16 mask_origin;
} SpiceComposite;
typedef struct SpiceBlackness {
SpiceQMask mask;
} SpiceBlackness, SpiceInvers, SpiceWhiteness;
@ -274,8 +294,12 @@ typedef struct SpiceCursorHeader {
uint16_t hot_spot_y;
} SpiceCursorHeader;
#ifdef __cplusplus
static inline int spice_image_descriptor_is_lossy(const SpiceImageDescriptor *descriptor)
{
return descriptor->type == SPICE_IMAGE_TYPE_JPEG ||
descriptor->type == SPICE_IMAGE_TYPE_JPEG_ALPHA;
}
#endif
#endif /* _H_SPICE_DRAW */
SPICE_END_DECLS
#endif // H_SPICE_COMMON_DRAW

View File

@ -45,9 +45,7 @@ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
******************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <spice/macros.h>
@ -84,7 +82,7 @@ typedef struct lineGC *GCPtr;
#define miWideDash spice_canvas_wide_dash_line
#define miWideLine spice_canvas_wide_line
static INLINE int ICEIL (double x)
static inline int ICEIL (double x)
{
int _cTmp = (int)x;
return ((x == _cTmp) || (x < 0.0)) ? _cTmp : _cTmp + 1;
@ -412,7 +410,7 @@ miStepDash (int dist, /* distance to step */
totallen = 0;
for (i = 0; i < numInDashList; i++)
totallen += pDash[i];
if (totallen <= dist)
if (totallen > 0 && totallen <= dist)
dist = dist % totallen;
while (dist >= pDash[dashIndex]) {
dist -= pDash[dashIndex];
@ -806,14 +804,14 @@ miFillUniqueSpanGroup (GCPtr pGC, SpanGroup * spanGroup, Boolean foreground)
newwidths = xrealloc (newspans->widths,
ysizes[index] * sizeof (int));
if (!newpoints || !newwidths) {
int i;
for (i = 0; i < ylength; i++) {
xfree (yspans[i].points);
xfree (yspans[i].widths);
}
xfree (yspans);
xfree (ysizes);
xfree (newpoints);
xfree (newwidths);
miDisposeSpanGroup (spanGroup);
return;
}
@ -836,8 +834,6 @@ miFillUniqueSpanGroup (GCPtr pGC, SpanGroup * spanGroup, Boolean foreground)
points = (DDXPointRec*)xalloc (count * sizeof (DDXPointRec));
widths = (int *)xalloc (count * sizeof (int));
if (!points || !widths) {
int i;
for (i = 0; i < ylength; i++) {
xfree (yspans[i].points);
xfree (yspans[i].widths);
@ -929,7 +925,7 @@ end of the line, we will find the largest number of Y steps that
satisfies the inequality. In that case, since we are representing
the Y steps as (dy - N), we will actually want to solve for the
smallest N in that equation.
Case 1: X major, starting X coordinate moved by M steps
-2dx <= 2Mdy - 2Ndx - dx - B < 0
@ -977,7 +973,7 @@ steps, so we want the highest N, so we use the < inequality:
= floor((2Mdy + dx + B + 2dx - 1) / 2dx) - 1
= floor((2Mdy + dx + B + 2dx - 1 - 2dx) / 2dx)
= floor((2Mdy + dx + B - 1) / 2dx)
Case 3: Y major, starting X coordinate moved by M steps
-2dy <= 2Ndx - 2Mdy - dy - B < 0
@ -1023,7 +1019,7 @@ Same analysis as Case 4, but we want the smallest number of Y steps
which means the largest N, so we use the <= inequality:
N = floor((2Mdy + dy - B) / 2dx)
Now let's try the Y coordinates, we have the same 4 cases.
Case 5: X major, starting Y coordinate moved by N steps
@ -1068,7 +1064,7 @@ Same derivations as Case 6, but we want the smallest # of X steps
which means the largest M, so use the <= inequality:
M = floor((2Ndx + dx - B) / 2dy)
Case 7: Y major, starting Y coordinate moved by N steps
-2dy <= 2Ndx - 2Mdy - dy - B < 0
@ -1113,7 +1109,7 @@ steps which means the largest M, so we use the < inequality:
= floor((2Ndx + dy + B + 2dy - 1) / 2dy) - 1
= floor((2Ndx + dy + B + 2dy - 1 - 2dy) / 2dy)
= floor((2Ndx + dy + B - 1) / 2dy)
So, our equations are:
1: X major move x1 to x1+M floor((2Mdy + dx - B) / 2dx)
@ -1826,7 +1822,7 @@ miFillRectPolyHelper (GCPtr pGC, Boolean foreground, SpanDataPtr spanData, int x
}
static int
miPolyBuildEdge (double x0, double y0, double k, /* x0 * dy - y0 * dx */
miPolyBuildEdge (SPICE_GNUC_UNUSED double x0, double y0, double k, /* x0 * dy - y0 * dx */
int dx, int dy, int xi, int yi, int left, PolyEdgePtr edge)
{
int x, y, e;
@ -1837,15 +1833,6 @@ miPolyBuildEdge (double x0, double y0, double k, /* x0 * dy - y0 * dx */
dx = -dx;
k = -k;
}
#ifdef NOTDEF
{
double realk, kerror;
realk = x0 * dy - y0 * dx;
kerror = Fabs (realk - k);
if (kerror > .1)
printf ("realk: %g k: %g\n", realk, k);
}
#endif
y = ICEIL (y0);
xady = ICEIL (k) + y * dx;
@ -1970,7 +1957,11 @@ miPolyBuildPoly (PolyVertexPtr vertices,
}
static void
miLineOnePoint (GCPtr pGC, Boolean foreground, SpanDataPtr spanData, int x, int y)
miLineOnePoint (GCPtr pGC,
Boolean foreground,
SPICE_GNUC_UNUSED SpanDataPtr spanData,
int x,
int y)
{
DDXPointRec pt;
int wid;
@ -2430,7 +2421,7 @@ miLineArc (GCPtr pGC,
int xorgi = 0, yorgi = 0;
Spans spanRec;
int n;
PolyEdgeRec edge1, edge2;
PolyEdgeRec edge1 = { 0 }, edge2 = { 0 };
int edgey1, edgey2;
Boolean edgeleft1, edgeleft2;
@ -2501,13 +2492,18 @@ miLineArc (GCPtr pGC,
}
static void
miLineProjectingCap (GCPtr pGC, Boolean foreground,
SpanDataPtr spanData, LineFacePtr face, Boolean isLeft,
double xorg, double yorg, Boolean isInt)
miLineProjectingCap (GCPtr pGC,
Boolean foreground,
SpanDataPtr spanData,
LineFacePtr face,
Boolean isLeft,
SPICE_GNUC_UNUSED double xorg,
SPICE_GNUC_UNUSED double yorg,
Boolean isInt)
{
int xorgi = 0, yorgi = 0;
int lw;
PolyEdgeRec lefts[2], rights[2];
PolyEdgeRec lefts[4], rights[4];
int lefty, righty, topy, bottomy;
PolyEdgePtr left, right;
PolyEdgePtr top, bottom;
@ -2665,7 +2661,7 @@ miWideSegment (GCPtr pGC,
PolyEdgePtr top, bottom;
int lefty, righty, topy, bottomy;
int signdx;
PolyEdgeRec lefts[2], rights[2];
PolyEdgeRec lefts[4], rights[4];
LineFacePtr tface;
int lw = pGC->lineWidth;
@ -2980,9 +2976,9 @@ miWideDashSegment (GCPtr pGC,
double L, l;
double k;
PolyVertexRec vertices[4];
PolyVertexRec saveRight = { 0 }, saveBottom;
PolyVertexRec saveRight = { 0, 0 }, saveBottom;
PolySlopeRec slopes[4];
PolyEdgeRec left[2], right[2];
PolyEdgeRec left[4], right[4];
LineFaceRec lcapFace, rcapFace;
int nleft, nright;
int h;

View File

@ -46,17 +46,17 @@ SOFTWARE.
******************************************************************/
#ifndef LINES_H
#define LINES_H
#ifndef H_SPICE_COMMON_LINES
#define H_SPICE_COMMON_LINES
#include <pixman_utils.h>
#include <stdlib.h>
#include <string.h>
#include <spice/macros.h>
#include "pixman_utils.h"
#include "draw.h"
#ifdef __cplusplus
extern "C" {
#endif
SPICE_BEGIN_DECLS
typedef struct lineGC lineGC;
@ -131,8 +131,6 @@ extern int spice_canvas_clip_spans(pixman_region32_t *clip_region,
int *new_widths,
int sorted);
#ifdef __cplusplus
}
#endif
SPICE_END_DECLS
#endif /* LINES_H */
#endif // H_SPICE_COMMON_LINES

71
common/log.c Normal file
View File

@ -0,0 +1,71 @@
/*
Copyright (C) 2012-2015 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <glib.h>
#include <stdlib.h>
#include <common/recorder.h>
#include "log.h"
#include "backtrace.h"
#define G_LOG_DOMAIN "Spice"
SPICE_CONSTRUCTOR_FUNC(spice_log_init)
{
recorder_dump_on_common_signals(0, 0);
}
G_GNUC_PRINTF(5, 0)
static void spice_logv(const char *log_domain,
GLogLevelFlags log_level,
const char *strloc,
const char *function,
const char *format,
va_list args)
{
GString *log_msg;
log_msg = g_string_new(NULL);
if (strloc && function) {
g_string_append_printf(log_msg, "%s:%s: ", strloc, function);
}
if (format) {
g_string_append_vprintf(log_msg, format, args);
}
g_log(log_domain, log_level, "%s", log_msg->str);
g_string_free(log_msg, TRUE);
if ((log_level & G_LOG_LEVEL_CRITICAL) != 0) {
spice_backtrace();
abort();
}
}
void spice_log(GLogLevelFlags log_level,
const char *strloc,
const char *function,
const char *format,
...)
{
va_list args;
va_start (args, format);
spice_logv (G_LOG_DOMAIN, log_level, strloc, function, format, args);
va_end (args);
}

110
common/log.h Normal file
View File

@ -0,0 +1,110 @@
/*
Copyright (C) 2012 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef H_SPICE_COMMON_LOG
#define H_SPICE_COMMON_LOG
#include <stdarg.h>
#include <stdio.h>
#include <glib.h>
#include <spice/macros.h>
#include "macros.h"
SPICE_BEGIN_DECLS
#ifdef SPICE_LOG_DOMAIN
#error Do not use obsolete SPICE_LOG_DOMAIN macro, is currently unused
#endif
#define SPICE_STRLOC __FILE__ ":" G_STRINGIFY (__LINE__)
void spice_log(GLogLevelFlags log_level,
const char *strloc,
const char *function,
const char *format,
...) G_GNUC_PRINTF(4, 5);
/* FIXME: name is misleading, this aborts.. */
#define spice_return_if_fail(x) G_STMT_START { \
if G_LIKELY(x) { } else { \
spice_critical("condition `%s' failed", #x); \
return; \
} \
} G_STMT_END
/* FIXME: name is misleading, this aborts.. */
#define spice_return_val_if_fail(x, val) G_STMT_START { \
if G_LIKELY(x) { } else { \
spice_critical("condition `%s' failed", #x); \
return (val); \
} \
} G_STMT_END
#define spice_warn_if_reached() G_STMT_START { \
spice_log(G_LOG_LEVEL_WARNING, SPICE_STRLOC, __FUNCTION__, "should not be reached"); \
} G_STMT_END
#define spice_info(...) G_STMT_START { \
spice_log(G_LOG_LEVEL_INFO, SPICE_STRLOC, __FUNCTION__, "" __VA_ARGS__); \
} G_STMT_END
#define spice_debug(...) G_STMT_START { \
spice_log(G_LOG_LEVEL_DEBUG, SPICE_STRLOC, __FUNCTION__, "" __VA_ARGS__); \
} G_STMT_END
#define spice_warning(...) G_STMT_START { \
spice_log(G_LOG_LEVEL_WARNING, SPICE_STRLOC, __FUNCTION__, "" __VA_ARGS__); \
} G_STMT_END
#define spice_critical(...) G_STMT_START { \
spice_log(G_LOG_LEVEL_CRITICAL, SPICE_STRLOC, __FUNCTION__, "" __VA_ARGS__); \
SPICE_UNREACHABLE; \
} G_STMT_END
#define spice_error(...) G_STMT_START { \
spice_log(G_LOG_LEVEL_ERROR, SPICE_STRLOC, __FUNCTION__, "" __VA_ARGS__); \
SPICE_UNREACHABLE; \
} G_STMT_END
#define spice_warn_if_fail(x) G_STMT_START { \
if G_LIKELY(x) { } else { \
spice_warning("condition `%s' failed", #x); \
} \
} G_STMT_END
#define spice_assert(x) G_STMT_START { \
if G_LIKELY(x) { } else { \
spice_error("assertion `%s' failed", #x); \
} \
} G_STMT_END
#if ENABLE_EXTRA_CHECKS
enum { spice_extra_checks = 1 };
#else
enum { spice_extra_checks = 0 };
#endif
#define spice_extra_assert(x) G_STMT_START { \
if (!spice_extra_checks || G_LIKELY(x)) { } else { \
spice_error("assertion `%s' failed", #x); \
} \
} G_STMT_END
SPICE_END_DECLS
#endif // H_SPICE_COMMON_LOG

View File

@ -43,29 +43,16 @@
SOFTWARE.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "lz.h"
#define DEBUG
#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 HASH_LOG 13
#define HASH_SIZE (1 << HASH_LOG)
#define HASH_MASK (HASH_SIZE - 1)
/* Maximum image size, mainly to avoid possible integer overflows */
#define SPICE_MAX_IMAGE_SIZE (1024 * 1024 * 1024 - 1)
typedef struct LzImageSegment LzImageSegment;
struct LzImageSegment {
@ -102,7 +89,6 @@ typedef struct Encoder {
// (2) a pointer to the first byte in the segment that matches the word
HashEntry htab[HASH_SIZE];
uint8_t *io_start;
uint8_t *io_now;
uint8_t *io_end;
size_t io_bytes_count;
@ -113,7 +99,7 @@ typedef struct Encoder {
/****************************************************/
/* functions for managing the pool of image segments*/
/****************************************************/
static INLINE LzImageSegment *lz_alloc_image_seg(Encoder *encoder);
static inline LzImageSegment *lz_alloc_image_seg(Encoder *encoder);
static void lz_reset_image_seg(Encoder *encoder);
static int lz_read_image_segments(Encoder *encoder, uint8_t *first_lines,
unsigned int num_first_lines);
@ -121,7 +107,7 @@ static int lz_read_image_segments(Encoder *encoder, uint8_t *first_lines,
// return a free image segment if one exists. Make allocation if needed. adds it to the
// tail of the image segments lists
static INLINE LzImageSegment *lz_alloc_image_seg(Encoder *encoder)
static inline LzImageSegment *lz_alloc_image_seg(Encoder *encoder)
{
LzImageSegment *ret;
@ -148,7 +134,7 @@ static INLINE LzImageSegment *lz_alloc_image_seg(Encoder *encoder)
}
// adding seg to the head of free segments (lz_reset_image_seg removes it from used ones)
static INLINE void __lz_free_image_seg(Encoder *encoder, LzImageSegment *seg)
static inline void __lz_free_image_seg(Encoder *encoder, LzImageSegment *seg)
{
seg->next = encoder->free_image_segs;
encoder->free_image_segs = seg;
@ -184,7 +170,7 @@ static int lz_read_image_segments(Encoder *encoder, uint8_t *first_lines,
uint8_t* lines = first_lines;
int row;
ASSERT(encoder->usr, !encoder->head_image_segs);
spice_return_val_if_fail(!encoder->head_image_segs, FALSE);
image_seg = lz_alloc_image_seg(encoder);
if (!image_seg) {
@ -224,7 +210,7 @@ error_1:
/**************************************************************************
* Handling encoding and decoding of a byte
***************************************************************************/
static INLINE int more_io_bytes(Encoder *encoder)
static inline int more_io_bytes(Encoder *encoder)
{
uint8_t *io_ptr;
int num_io_bytes = encoder->usr->more_space(encoder->usr, &io_ptr);
@ -234,20 +220,20 @@ static INLINE int more_io_bytes(Encoder *encoder)
return num_io_bytes;
}
static INLINE void encode(Encoder *encoder, uint8_t byte)
static inline void encode(Encoder *encoder, uint8_t byte)
{
if (encoder->io_now == encoder->io_end) {
if (more_io_bytes(encoder) <= 0) {
encoder->usr->error(encoder->usr, "%s: no more bytes\n", __FUNCTION__);
}
ASSERT(encoder->usr, encoder->io_now);
spice_return_if_fail(encoder->io_now);
}
ASSERT(encoder->usr, encoder->io_now < encoder->io_end);
spice_return_if_fail(encoder->io_now < encoder->io_end);
*(encoder->io_now++) = byte;
}
static INLINE void encode_32(Encoder *encoder, unsigned int word)
static inline void encode_32(Encoder *encoder, unsigned int word)
{
encode(encoder, (uint8_t)(word >> 24));
encode(encoder, (uint8_t)(word >> 16) & 0x0000ff);
@ -255,37 +241,32 @@ static INLINE void encode_32(Encoder *encoder, unsigned int word)
encode(encoder, (uint8_t)(word & 0x0000ff));
}
static INLINE void encode_copy_count(Encoder *encoder, uint8_t copy_count)
static inline void encode_copy_count(Encoder *encoder, uint8_t copy_count)
{
encode(encoder, copy_count);
encoder->io_last_copy = encoder->io_now - 1; // io_now cannot be the first byte of the buffer
}
static INLINE void update_copy_count(Encoder *encoder, uint8_t copy_count)
static inline void update_copy_count(Encoder *encoder, uint8_t copy_count)
{
ASSERT(encoder->usr, encoder->io_last_copy);
spice_return_if_fail(encoder->io_last_copy);
*(encoder->io_last_copy) = copy_count;
}
static INLINE void encode_level(Encoder *encoder, uint8_t level_code)
{
*(encoder->io_start) |= level_code;
}
// decrease the io ptr by 1
static INLINE void compress_output_prev(Encoder *encoder)
static inline void compress_output_prev(Encoder *encoder)
{
// io_now cannot be the first byte of the buffer
encoder->io_now--;
// the function should be called only when copy count is written unnecessarily by lz_compress
ASSERT(encoder->usr, encoder->io_now == encoder->io_last_copy)
spice_return_if_fail(encoder->io_now == encoder->io_last_copy);
}
static int encoder_reset(Encoder *encoder, uint8_t *io_ptr, uint8_t *io_ptr_end)
{
ASSERT(encoder->usr, io_ptr <= io_ptr_end);
spice_return_val_if_fail(io_ptr <= io_ptr_end, FALSE);
encoder->io_bytes_count = io_ptr_end - io_ptr;
encoder->io_start = io_ptr;
encoder->io_now = io_ptr;
encoder->io_end = io_ptr_end;
encoder->io_last_copy = NULL;
@ -293,20 +274,20 @@ static int encoder_reset(Encoder *encoder, uint8_t *io_ptr, uint8_t *io_ptr_end)
return TRUE;
}
static INLINE uint8_t decode(Encoder *encoder)
static inline uint8_t decode(Encoder *encoder)
{
if (encoder->io_now == encoder->io_end) {
int num_io_bytes = more_io_bytes(encoder);
if (num_io_bytes <= 0) {
encoder->usr->error(encoder->usr, "%s: no more bytes\n", __FUNCTION__);
}
ASSERT(encoder->usr, encoder->io_now);
spice_assert(encoder->io_now);
}
ASSERT(encoder->usr, encoder->io_now < encoder->io_end);
spice_assert(encoder->io_now < encoder->io_end);
return *(encoder->io_now++);
}
static INLINE uint32_t decode_32(Encoder *encoder)
static inline uint32_t decode_32(Encoder *encoder)
{
uint32_t word = 0;
word |= decode(encoder);
@ -319,7 +300,7 @@ static INLINE uint32_t decode_32(Encoder *encoder)
return word;
}
static INLINE int is_io_to_decode_end(Encoder *encoder)
static inline int is_io_to_decode_end(Encoder *encoder)
{
if (encoder->io_now != encoder->io_end) {
return FALSE;
@ -393,30 +374,23 @@ void lz_destroy(LzContext *lz)
#endif
#ifdef __GNUC__
#define ATTR_PACKED __attribute__ ((__packed__))
#else
#define ATTR_PACKED
#pragma pack(push)
#pragma pack(1)
#endif
#include <spice/start-packed.h>
/* the palette images will be treated as one byte pixels. Their width should be transformed
accordingly.
*/
typedef struct ATTR_PACKED one_byte_pixel_t {
typedef struct SPICE_ATTR_PACKED one_byte_pixel_t {
uint8_t a;
} one_byte_pixel_t;
typedef struct ATTR_PACKED rgb32_pixel_t {
typedef struct SPICE_ATTR_PACKED rgb32_pixel_t {
uint8_t b;
uint8_t g;
uint8_t r;
uint8_t pad;
} rgb32_pixel_t;
typedef struct ATTR_PACKED rgb24_pixel_t {
typedef struct SPICE_ATTR_PACKED rgb24_pixel_t {
uint8_t b;
uint8_t g;
uint8_t r;
@ -424,11 +398,7 @@ typedef struct ATTR_PACKED rgb24_pixel_t {
typedef uint16_t rgb16_pixel_t;
#ifndef __GNUC__
#pragma pack(pop)
#endif
#undef ATTR_PACKED
#include <spice/end-packed.h>
#define MAX_COPY 32
@ -476,6 +446,13 @@ typedef uint16_t rgb16_pixel_t;
#define TO_RGB32
#include "lz_decompress_tmpl.c"
#define LZ_A8
#include "lz_compress_tmpl.c"
#define LZ_A8
#include "lz_decompress_tmpl.c"
#define LZ_A8
#define TO_RGB32
#include "lz_decompress_tmpl.c"
#define LZ_RGB16
#include "lz_compress_tmpl.c"
@ -504,6 +481,44 @@ typedef uint16_t rgb16_pixel_t;
#undef LZ_UNEXPECT_CONDITIONAL
#undef LZ_EXPECT_CONDITIONAL
static void lz_set_sizes(Encoder *encoder, int type, int width, int height, int stride)
{
if (width < 0) {
encoder->usr->error(encoder->usr, "invalid lz width %d\n", width);
}
if (height < 0) {
encoder->usr->error(encoder->usr, "invalid lz height %d\n", height);
}
if (stride < 0) {
encoder->usr->error(encoder->usr, "invalid lz stride %d\n", stride);
}
if (IS_IMAGE_TYPE_PLT[type]) {
if (stride > (width / PLT_PIXELS_PER_BYTE[type])) {
if (((width % PLT_PIXELS_PER_BYTE[type]) == 0) || (
(stride - (width / PLT_PIXELS_PER_BYTE[type])) > 1)) {
encoder->usr->error(encoder->usr, "stride overflows (plt)\n");
}
}
} else {
if (stride != width * RGB_BYTES_PER_PIXEL[type]) {
encoder->usr->error(encoder->usr, "stride != width*bytes_per_pixel (rgb) %d != %d * %d (%d)\n",
stride, width, RGB_BYTES_PER_PIXEL[type],
type);
}
}
// avoid too big images
if ((uint64_t) stride * height > SPICE_MAX_IMAGE_SIZE) {
encoder->usr->error(encoder->usr, "image too large\n");
}
encoder->type = type;
encoder->width = width;
encoder->height = height;
encoder->stride = stride;
}
int lz_encode(LzContext *lz, LzImageType type, int width, int height, int top_down,
uint8_t *lines, unsigned int num_lines, int stride,
uint8_t *io_ptr, unsigned int num_io_bytes)
@ -511,23 +526,7 @@ int lz_encode(LzContext *lz, LzImageType type, int width, int height, int top_do
Encoder *encoder = (Encoder *)lz;
uint8_t *io_ptr_end = io_ptr + num_io_bytes;
encoder->type = type;
encoder->width = width;
encoder->height = height;
encoder->stride = stride;
if (IS_IMAGE_TYPE_PLT[encoder->type]) {
if (encoder->stride > (width / PLT_PIXELS_PER_BYTE[encoder->type])) {
if (((width % PLT_PIXELS_PER_BYTE[encoder->type]) == 0) || (
(encoder->stride - (width / PLT_PIXELS_PER_BYTE[encoder->type])) > 1)) {
encoder->usr->error(encoder->usr, "stride overflows (plt)\n");
}
}
} else {
if (encoder->stride != width * RGB_BYTES_PER_PIXEL[encoder->type]) {
encoder->usr->error(encoder->usr, "stride != width*bytes_per_pixel (rgb)\n");
}
}
lz_set_sizes(encoder, type, width, height, stride);
// assign the output buffer
if (!encoder_reset(encoder, io_ptr, io_ptr_end)) {
@ -571,6 +570,9 @@ int lz_encode(LzContext *lz, LzImageType type, int width, int height, int top_do
case LZ_IMAGE_TYPE_XXXA:
lz_rgb_alpha_compress(encoder);
break;
case LZ_IMAGE_TYPE_A8:
lz_a8_compress(encoder);
break;
case LZ_IMAGE_TYPE_INVALID:
default:
encoder->usr->error(encoder->usr, "bad image type\n");
@ -610,10 +612,15 @@ void lz_decode_begin(LzContext *lz, uint8_t *io_ptr, unsigned int num_io_bytes,
encoder->usr->error(encoder->usr, "bad version\n");
}
encoder->type = (LzImageType)decode_32(encoder);
encoder->width = decode_32(encoder);
encoder->height = decode_32(encoder);
encoder->stride = decode_32(encoder);
int type = decode_32(encoder);
if (type <= LZ_IMAGE_TYPE_INVALID || type > LZ_IMAGE_TYPE_A8) {
encoder->usr->error(encoder->usr, "invalid lz type %d\n", type);
}
int width = decode_32(encoder);
int height = decode_32(encoder);
int stride = decode_32(encoder);
lz_set_sizes(encoder, type, width, height, stride);
*out_top_down = decode_32(encoder);
*out_width = encoder->width;
@ -646,6 +653,7 @@ void lz_decode(LzContext *lz, LzImageType to_type, uint8_t *buf)
if (!encoder->palette) {
encoder->usr->error(encoder->usr,
"a palette is missing (for bpp to rgb decoding)\n");
return;
}
switch (encoder->type) {
case LZ_IMAGE_TYPE_PLT1_BE:
@ -707,7 +715,7 @@ void lz_decode(LzContext *lz, LzImageType to_type, uint8_t *buf)
if (encoder->type == to_type) {
out_size = lz_rgb32_decompress(encoder, (rgb32_pixel_t *)buf, size);
alpha_size = lz_rgb_alpha_decompress(encoder, (rgb32_pixel_t *)buf, size);
ASSERT(encoder->usr, alpha_size == size);
spice_assert(alpha_size == size);
} else {
encoder->usr->error(encoder->usr, "unsupported output format\n");
}
@ -720,6 +728,17 @@ void lz_decode(LzContext *lz, LzImageType to_type, uint8_t *buf)
encoder->usr->error(encoder->usr, "unsupported output format\n");
}
break;
case LZ_IMAGE_TYPE_A8:
if (encoder->type == to_type) {
alpha_size = lz_a8_decompress(encoder, (one_byte_pixel_t *)buf, size);
out_size = alpha_size;
} else if (to_type == LZ_IMAGE_TYPE_RGB32) {
alpha_size = lz_a8_to_rgb32_decompress(encoder, (rgb32_pixel_t *)buf, size);
out_size = alpha_size;
} else {
encoder->usr->error(encoder->usr, "unsupported output format\n");
}
break;
case LZ_IMAGE_TYPE_PLT1_LE:
case LZ_IMAGE_TYPE_PLT1_BE:
case LZ_IMAGE_TYPE_PLT4_LE:
@ -731,8 +750,8 @@ void lz_decode(LzContext *lz, LzImageType to_type, uint8_t *buf)
}
}
ASSERT(encoder->usr, is_io_to_decode_end(encoder));
ASSERT(encoder->usr, out_size == size);
spice_assert(is_io_to_decode_end(encoder));
spice_assert(out_size == size);
if (out_size != size) {
encoder->usr->error(encoder->usr, "bad decode size\n");

View File

@ -3,24 +3,26 @@
dictionary compression for images based on fastlz (http://www.fastlz.org/)
(Distributed under MIT license).
*/
#ifndef __LZ_H
#define __LZ_H
#ifndef H_SPICE_COMMON_LZ
#define H_SPICE_COMMON_LZ
#include <spice/macros.h>
#include "lz_common.h"
#include "lz_config.h"
#include "draw.h"
#include "macros.h"
#ifdef __cplusplus
extern "C" {
#endif
SPICE_BEGIN_DECLS
typedef void *LzContext;
typedef struct LzUsrContext LzUsrContext;
struct LzUsrContext {
void (*error)(LzUsrContext *usr, const char *fmt, ...);
void (*warn)(LzUsrContext *usr, const char *fmt, ...);
void (*info)(LzUsrContext *usr, const char *fmt, ...);
SPICE_GNUC_NORETURN
SPICE_GNUC_PRINTF(2, 3) void (*error)(LzUsrContext *usr, const char *fmt, ...);
SPICE_GNUC_PRINTF(2, 3) void (*warn)(LzUsrContext *usr, const char *fmt, ...);
SPICE_GNUC_PRINTF(2, 3) void (*info)(LzUsrContext *usr, const char *fmt, ...);
void *(*malloc)(LzUsrContext *usr, int size);
void (*free)(LzUsrContext *usr, void *ptr);
int (*more_space)(LzUsrContext *usr, uint8_t **io_ptr); // get the next chunk of the
@ -75,8 +77,6 @@ LzContext *lz_create(LzUsrContext *usr);
void lz_destroy(LzContext *lz);
#ifdef __cplusplus
}
#endif
SPICE_END_DECLS
#endif // __LZ_H
#endif // H_SPICE_COMMON_LZ

View File

@ -20,14 +20,13 @@
/*common header for encoder and decoder*/
#ifndef _LZ_COMMON_H
#define _LZ_COMMON_H
#ifndef H_SPICE_COMMON_LZ_COMMON
#define H_SPICE_COMMON_LZ_COMMON
#ifdef __cplusplus
extern "C" {
#endif
#include <spice/macros.h>
#include "verify.h"
//#define DEBUG
SPICE_BEGIN_DECLS
/* change the max window size will require change in the encoding format*/
#define LZ_MAX_WINDOW_SIZE (1 << 25)
@ -44,26 +43,30 @@ typedef enum {
LZ_IMAGE_TYPE_RGB24,
LZ_IMAGE_TYPE_RGB32,
LZ_IMAGE_TYPE_RGBA,
LZ_IMAGE_TYPE_XXXA
LZ_IMAGE_TYPE_XXXA,
LZ_IMAGE_TYPE_A8
} LzImageType;
#define LZ_IMAGE_TYPE_MASK 0x0f
#define LZ_IMAGE_TYPE_LOG 4 // number of bits required for coding the image type
/* access to the arrays is based on the image types */
static const int IS_IMAGE_TYPE_PLT[] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0};
static const int IS_IMAGE_TYPE_RGB[] = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1};
static const int IS_IMAGE_TYPE_PLT[] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0};
static const int IS_IMAGE_TYPE_RGB[] = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1};
static const int PLT_PIXELS_PER_BYTE[] = {0, 8, 8, 2, 2, 1};
static const int RGB_BYTES_PER_PIXEL[] = {0, 1, 1, 1, 1, 1, 2, 3, 4, 4, 4};
static const int RGB_BYTES_PER_PIXEL[] = {0, 1, 1, 1, 1, 1, 2, 3, 4, 4, 4, 1};
verify(SPICE_N_ELEMENTS(IS_IMAGE_TYPE_PLT) == (LZ_IMAGE_TYPE_A8 + 1));
verify(SPICE_N_ELEMENTS(IS_IMAGE_TYPE_RGB) == (LZ_IMAGE_TYPE_A8 + 1));
verify(SPICE_N_ELEMENTS(PLT_PIXELS_PER_BYTE) == (LZ_IMAGE_TYPE_PLT8 + 1));
verify(SPICE_N_ELEMENTS(RGB_BYTES_PER_PIXEL) == (LZ_IMAGE_TYPE_A8 + 1));
#define LZ_MAGIC (*(uint32_t *)"LZ ")
/* ASCII "LZ " */
#define LZ_MAGIC 0x20205a4c
#define LZ_VERSION_MAJOR 1U
#define LZ_VERSION_MINOR 1U
#define LZ_VERSION ((LZ_VERSION_MAJOR << 16) | (LZ_VERSION_MINOR & 0xffff))
#ifdef __cplusplus
}
#endif
SPICE_END_DECLS
#endif // _LZ_COMMON_H
#endif // H_SPICE_COMMON_LZ_COMMON

View File

@ -40,11 +40,9 @@
SOFTWARE.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define DJB2_START 5381;
#define DJB2_START 5381
#define DJB2_HASH(hash, c) (hash = ((hash << 5) + hash) ^ (c)) //|{hash = ((hash << 5) + hash) + c;}
/*
@ -71,6 +69,21 @@
}
#endif
#ifdef LZ_A8
#define PIXEL one_byte_pixel_t
#define FNAME(name) lz_a8_##name
#define ENCODE_PIXEL(e, pix) encode(e, (pix).a) // gets the pixel and write only the needed bytes
// from the pixel
#define SAME_PIXEL(pix1, pix2) ((pix1).a == (pix2).a)
#define HASH_FUNC(v, p) { \
v = DJB2_START; \
DJB2_HASH(v, p[0].a); \
DJB2_HASH(v, p[1].a); \
DJB2_HASH(v, p[2].a); \
v &= HASH_MASK; \
}
#endif
#ifdef LZ_RGB_ALPHA
//#undef LZ_RGB_ALPHA
#define PIXEL rgb32_pixel_t
@ -90,9 +103,8 @@
#ifdef LZ_RGB16
#define PIXEL rgb16_pixel_t
#define FNAME(name) lz_rgb16_##name
#define GET_r(pix) (((pix) >> 10) & 0x1f)
#define GET_g(pix) (((pix) >> 5) & 0x1f)
#define GET_b(pix) ((pix) & 0x1f)
#define GET_rgb(pix) ((pix) & 0x7fffu)
#define SAME_PIXEL(p1, p2) (GET_rgb(p1) == GET_rgb(p2))
#define ENCODE_PIXEL(e, pix) {encode(e, (pix) >> 8); encode(e, (pix) & 0xff);}
#define HASH_FUNC(v, p) { \
@ -110,20 +122,17 @@
#ifdef LZ_RGB24
#define PIXEL rgb24_pixel_t
#define FNAME(name) lz_rgb24_##name
#define ENCODE_PIXEL(e, pix) {encode(e, (pix).b); encode(e, (pix).g); encode(e, (pix).r);}
#endif
#ifdef LZ_RGB32
#define PIXEL rgb32_pixel_t
#define FNAME(name) lz_rgb32_##name
#define ENCODE_PIXEL(e, pix) {encode(e, (pix).b); encode(e, (pix).g); encode(e, (pix).r);}
#endif
#if defined(LZ_RGB24) || defined(LZ_RGB32)
#define GET_r(pix) ((pix).r)
#define GET_g(pix) ((pix).g)
#define GET_b(pix) ((pix).b)
#define ENCODE_PIXEL(e, pix) {encode(e, (pix).b); encode(e, (pix).g); encode(e, (pix).r);}
#define SAME_PIXEL(p1, p2) ((p1).r == (p2).r && (p1).g == (p2).g && (p1).b == (p2).b)
#define HASH_FUNC(v, p) { \
v = DJB2_START; \
DJB2_HASH(v, p[0].r); \
@ -139,12 +148,6 @@
}
#endif
#if defined(LZ_RGB16) || defined(LZ_RGB24) || defined(LZ_RGB32)
#define SAME_PIXEL(p1, p2) (GET_r(p1) == GET_r(p2) && GET_g(p1) == GET_g(p2) && \
GET_b(p1) == GET_b(p2))
#endif
#define PIXEL_ID(pix_ptr, seg_ptr) (pix_ptr - ((PIXEL *)seg_ptr->lines) + seg_ptr->size_delta)
// when encoding, the ref can be in previous segment, and we should check that it doesn't
@ -177,7 +180,7 @@ static void FNAME(compress_seg)(Encoder *encoder, LzImageSegment *seg, PIXEL *fr
size_t distance;
/* minimum match length */
#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA)
#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA) || defined(LZ_A8)
size_t len = 3;
#elif defined(LZ_RGB16)
size_t len = 2;
@ -199,9 +202,7 @@ static void FNAME(compress_seg)(Encoder *encoder, LzImageSegment *seg, PIXEL *fr
ip += 3;
ref = anchor + 2;
ref_limit = (PIXEL *)(seg->lines_end);
#if defined(LZ_RGB16) || defined(LZ_RGB24) || defined(LZ_RGB32)
len = 3;
#endif
goto match;
}
}
@ -234,7 +235,7 @@ static void FNAME(compress_seg)(Encoder *encoder, LzImageSegment *seg, PIXEL *fr
ip++;
/* minimum match length for rgb16 is 2 and for plt and alpha is 3 */
#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA) || defined(LZ_RGB16)
#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA) || defined(LZ_RGB16) || defined(LZ_A8)
if (!SAME_PIXEL(*ref, *ip)) {
ref++;
ip++;
@ -244,7 +245,7 @@ static void FNAME(compress_seg)(Encoder *encoder, LzImageSegment *seg, PIXEL *fr
ip++;
#endif
#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA)
#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA) || defined(LZ_A8)
if (!SAME_PIXEL(*ref, *ip)) {
ref++;
ip++;
@ -255,7 +256,7 @@ static void FNAME(compress_seg)(Encoder *encoder, LzImageSegment *seg, PIXEL *fr
#endif
/* far, needs at least 5-byte match */
if (distance >= MAX_DISTANCE) {
#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA)
#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA) || defined(LZ_A8)
if (ref >= (ref_limit - 1)) {
goto literal;
}
@ -272,7 +273,7 @@ static void FNAME(compress_seg)(Encoder *encoder, LzImageSegment *seg, PIXEL *fr
ref++;
ip++;
len++;
#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA)
#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA) || defined(LZ_A8)
if (!SAME_PIXEL(*ref, *ip)) {
ref++;
ip++;
@ -310,18 +311,15 @@ match: // RLE or dictionary (both are encoded by distance from ref (-1) a
// TODO: maybe separate a run from the same seg or from different ones in order
// to spare ref < ref_limit and that way we can also perform 8 calls of
// (ref++ != ip++) outside a loop
for (;;) {
while ((ip < ip_bound) && (ref < ref_limit)) {
if (!SAME_PIXEL(*ref, *ip)) {
ref++;
ip++;
break;
} else {
ref++;
ip++;
}
while ((ip < ip_bound) && (ref < ref_limit)) {
if (!SAME_PIXEL(*ref, *ip)) {
ref++;
ip++;
break;
} else {
ref++;
ip++;
}
break;
}
}
@ -393,27 +391,23 @@ match: // RLE or dictionary (both are encoded by distance from ref (-1) a
/* update the hash at match boundary */
#if defined(LZ_RGB16) || defined(LZ_RGB24) || defined(LZ_RGB32)
if (ip > anchor) {
if (ip > anchor)
#endif
HASH_FUNC(hval, ip);
encoder->htab[hval].ref = (uint8_t *)ip;
{
HASH_FUNC(hval, ip);
encoder->htab[hval].ref = (uint8_t *)ip;
encoder->htab[hval].image_seg = seg;
}
ip++;
encoder->htab[hval].image_seg = seg;
#if defined(LZ_RGB16) || defined(LZ_RGB24) || defined(LZ_RGB32)
} else {ip++;
}
#endif
#if defined(LZ_RGB24) || defined(LZ_RGB32)
if (ip > anchor) {
if (ip > anchor)
#endif
HASH_FUNC(hval, ip);
encoder->htab[hval].ref = (uint8_t *)ip;
{
HASH_FUNC(hval, ip);
encoder->htab[hval].ref = (uint8_t *)ip;
encoder->htab[hval].image_seg = seg;
}
ip++;
encoder->htab[hval].image_seg = seg;
#if defined(LZ_RGB24) || defined(LZ_RGB32)
} else {ip++;
}
#endif
/* assuming literal copy */
encode_copy_count(encoder, MAX_COPY - 1);
continue;
@ -513,17 +507,11 @@ static void FNAME(compress)(Encoder *encoder)
#undef PIXEL
#undef ENCODE_PIXEL
#undef SAME_PIXEL
#undef LZ_READU16
#undef HASH_FUNC
#undef BYTES_TO_16
#undef HASH_FUNC_16
#undef GET_r
#undef GET_g
#undef GET_b
#undef GET_CODE
#undef GET_rgb
#undef LZ_PLT
#undef LZ_RGB_ALPHA
#undef LZ_RGB16
#undef LZ_RGB24
#undef LZ_RGB32
#undef HASH_FUNC2
#undef LZ_A8

View File

@ -18,8 +18,8 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __LZ_CONFIG_H
#define __LZ_CONFIG_H
#ifndef H_SPICE_COMMON_LZ_CONFIG
#define H_SPICE_COMMON_LZ_CONFIG
#include <spice/types.h>
#include <spice/macros.h>
@ -36,4 +36,4 @@
#endif // QXLDD
#endif //__GNUC__
#endif //__LZ_CONFIG_H
#endif // H_SPICE_COMMON_LZ_CONFIG

View File

@ -50,18 +50,16 @@
/*
For each output pixel type the following macros are defined:
OUT_PIXEL - the output pixel type
COPY_PIXEL(p, out) - assigns the pixel to the place pointed by out and increases
out. Used in RLE. Need special handling because in alpha we
copy only the pad byte.
OUT_PIXEL - the output pixel type
COPY_PIXEL(p, out) - assigns the pixel to the place pointed by out and increases
out. Used in RLE. Need special handling because in alpha we
copy only the pad byte.
COPY_REF_PIXEL(ref, out) - copies the pixel pointed by ref to the pixel pointed by out.
Increases ref and out.
COPY_COMP_PIXEL(encoder, out) - copies pixel from the compressed buffer to the decompressed
buffer. Increases out.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#if !defined(LZ_RGB_ALPHA)
#define COPY_PIXEL(p, out) (*out++ = p)
@ -153,6 +151,26 @@
#endif // TO_RGB32
#endif
#ifndef CAST_PLT_DISTANCE
#define CAST_PLT_DISTANCE(dist) (dist)
#endif
#ifdef LZ_A8
#ifndef TO_RGB32
#define OUT_PIXEL one_byte_pixel_t
#define FNAME(name) lz_a8_##name
#define COPY_COMP_PIXEL(encoder, out) {out->a = decode(encoder); out++;}
#else // TO_RGB32
#define OUT_PIXEL rgb32_pixel_t
#define FNAME(name) lz_a8_to_rgb32_##name
#define COPY_COMP_PIXEL(encoder, out) { \
(out)->b = (out)->g = (out)->r = 0; \
(out)->pad = decode(encoder); \
(out)++; \
}
#endif
#endif
#ifdef LZ_RGB16
#ifndef TO_RGB32
#define OUT_PIXEL rgb16_pixel_t
@ -204,18 +222,18 @@
static size_t FNAME(decompress)(Encoder *encoder, OUT_PIXEL *out_buf, int size)
{
OUT_PIXEL *op = out_buf;
OUT_PIXEL *op_limit = out_buf + size;
uint32_t ctrl = decode(encoder);
int loop = TRUE;
OUT_PIXEL *const op_limit = out_buf + size;
do {
const OUT_PIXEL *ref = op;
uint32_t len = ctrl >> 5;
uint32_t ofs = (ctrl & 31) << 8; // 5 MSb of distance
for (;;) {
uint32_t ctrl = decode(encoder);
if (ctrl >= MAX_COPY) { // reference (dictionary/RLE)
/* retrieving the reference and the match length */
const OUT_PIXEL *ref = op;
uint32_t len = ctrl >> 5;
uint32_t ofs = (ctrl & 31) << 8; // 5 MSb of distance
uint8_t code;
len--;
//ref -= ofs;
@ -237,7 +255,7 @@ static size_t FNAME(decompress)(Encoder *encoder, OUT_PIXEL *out_buf, int size)
}
}
#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA)
#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA) || defined(LZ_A8)
len += 3; // length is biased by 2 + 1 (fixing bias)
#elif defined(LZ_RGB16)
len += 2; // length is biased by 1 + 1 (fixing bias)
@ -246,17 +264,13 @@ static size_t FNAME(decompress)(Encoder *encoder, OUT_PIXEL *out_buf, int size)
#endif
ofs += 1; // offset is biased by 1 (fixing bias)
#if defined(TO_RGB32)
#if defined(PLT4_BE) || defined(PLT4_LE) || defined(PLT1_BE) || defined(PLT1_LE)
ofs = CAST_PLT_DISTANCE(ofs);
len = CAST_PLT_DISTANCE(len);
#endif
#endif
ref -= ofs;
ASSERT(encoder->usr, op + len <= op_limit);
ASSERT(encoder->usr, ref + len <= op_limit);
ASSERT(encoder->usr, ref >= out_buf);
spice_assert(op + len <= op_limit);
spice_assert(ref + len <= op_limit);
spice_assert(ref >= out_buf);
// TODO: optimize by not calling loop at least 3 times when not PLT_TO_RGB32 (len is
// always >=3). in PLT_TO_RGB32 len >= 3*number_of_pixels_per_byte
@ -267,41 +281,31 @@ static size_t FNAME(decompress)(Encoder *encoder, OUT_PIXEL *out_buf, int size)
// because the number of pixel copied is larger
// then one...
/* optimize copy for a run */
OUT_PIXEL b = *ref;
const OUT_PIXEL b = *ref;
for (; len; --len) {
COPY_PIXEL(b, op);
ASSERT(encoder->usr, op <= op_limit);
spice_extra_assert(op <= op_limit);
}
} else {
for (; len; --len) {
COPY_REF_PIXEL(ref, op);
ASSERT(encoder->usr, op <= op_limit);
spice_extra_assert(op <= op_limit);
}
}
} else { // copy
ctrl++; // copy count is biased by 1
#if defined(TO_RGB32) && (defined(PLT4_BE) || defined(PLT4_LE) || defined(PLT1_BE) || \
defined(PLT1_LE))
ASSERT(encoder->usr, op + CAST_PLT_DISTANCE(ctrl) <= op_limit);
#else
ASSERT(encoder->usr, op + ctrl <= op_limit);
#endif
COPY_COMP_PIXEL(encoder, op);
spice_assert(op + CAST_PLT_DISTANCE(ctrl) <= op_limit);
ASSERT(encoder->usr, op <= op_limit);
for (--ctrl; ctrl; ctrl--) {
do {
COPY_COMP_PIXEL(encoder, op);
ASSERT(encoder->usr, op <= op_limit);
}
spice_extra_assert(op <= op_limit);
} while(--ctrl);
}
if (LZ_EXPECT_CONDITIONAL(op < op_limit)) {
ctrl = decode(encoder);
} else {
loop = FALSE;
if (LZ_UNEXPECT_CONDITIONAL(op >= op_limit)) {
break;
}
} while (LZ_EXPECT_CONDITIONAL(loop));
}
return (op - out_buf);
}
@ -315,6 +319,7 @@ static size_t FNAME(decompress)(Encoder *encoder, OUT_PIXEL *out_buf, int size)
#undef LZ_RGB16
#undef LZ_RGB24
#undef LZ_RGB32
#undef LZ_A8
#undef LZ_RGB_ALPHA
#undef TO_RGB32
#undef OUT_PIXEL

56
common/macros.h Normal file
View File

@ -0,0 +1,56 @@
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2009 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef H_SPICE_COMMON_MACROS
#define H_SPICE_COMMON_MACROS
#include "verify.h"
#ifdef __GNUC__
#define SPICE_CONSTRUCTOR_FUNC(func_name) \
static void __attribute__((constructor)) func_name(void)
#define SPICE_DESTRUCTOR_FUNC(func_name) \
static void __attribute__((destructor)) func_name(void)
#elif defined(_MSC_VER)
#define SPICE_CONSTRUCTOR_FUNC(func_name) \
static void func_name(void); \
static int func_name ## _wrapper(void) { func_name(); return 0; } \
__pragma(section(".CRT$XCU",read)) \
__declspec(allocate(".CRT$XCU")) static int (* _array ## func_name)(void) = func_name ## _wrapper; \
static void func_name(void)
#define SPICE_DESTRUCTOR_FUNC(func_name) \
static void func_name(void); \
static int func_name ## _wrapper(void) { func_name(); return 0; } \
__pragma(section(".CRT$XPU",read)) \
__declspec(allocate(".CRT$XPU")) static int (* _array ## func_name)(void) = func_name ## _wrapper; \
static void func_name(void)
#else
#error Please implement SPICE_CONSTRUCTOR_FUNC and SPICE_DESTRUCTOR_FUNC for this compiler
#endif
#define SPICE_VERIFY(cond) verify_expr(cond, (void)1)
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
#define SPICE_UNREACHABLE __builtin_unreachable()
#elif defined(_MSC_VER)
#define SPICE_UNREACHABLE __assume(0)
#else
#define SPICE_UNREACHABLE for(;;) continue
#endif
#endif // H_SPICE_COMMON_MACROS

View File

@ -15,34 +15,60 @@
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "log.h"
#include "marshaller.h"
#include "mem.h"
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <stdio.h>
#include <spice/start-packed.h>
typedef struct SPICE_ATTR_PACKED {
int16_t val;
} int16_unaligned_t;
typedef struct SPICE_ATTR_PACKED {
uint16_t val;
} uint16_unaligned_t;
typedef struct SPICE_ATTR_PACKED {
int32_t val;
} int32_unaligned_t;
typedef struct SPICE_ATTR_PACKED {
uint32_t val;
} uint32_unaligned_t;
typedef struct SPICE_ATTR_PACKED {
int64_t val;
} int64_unaligned_t;
typedef struct SPICE_ATTR_PACKED {
uint64_t val;
} uint64_unaligned_t;
#include <spice/end-packed.h>
#define write_int8(ptr,v) (*(int8_t *)(ptr) = v)
#define write_uint8(ptr,v) (*(uint8_t *)(ptr) = v)
#ifdef WORDS_BIGENDIAN
#define write_int8(ptr,v) (*((int8_t *)(ptr)) = v)
#define write_uint8(ptr,v) (*((uint8_t *)(ptr)) = v)
#define write_int16(ptr,v) (*((int16_t *)(ptr)) = SPICE_BYTESWAP16((uint16_t)(v)))
#define write_uint16(ptr,v) (*((uint16_t *)(ptr)) = SPICE_BYTESWAP16((uint16_t)(v)))
#define write_int32(ptr,v) (*((int32_t *)(ptr)) = SPICE_BYTESWAP32((uint32_t)(v)))
#define write_uint32(ptr,v) (*((uint32_t *)(ptr)) = SPICE_BYTESWAP32((uint32_t)(v)))
#define write_int64(ptr,v) (*((int64_t *)(ptr)) = SPICE_BYTESWAP64((uint64_t)(v)))
#define write_uint64(ptr,v) (*((uint64_t *)(ptr)) = SPICE_BYTESWAP64((uint64_t)(v)))
#define write_int16(ptr,v) (((uint16_unaligned_t *)(ptr))->val = SPICE_BYTESWAP16((uint16_t)(v)))
#define write_uint16(ptr,v) (((uint16_unaligned_t *)(ptr))->val = SPICE_BYTESWAP16((uint16_t)(v)))
#define write_int32(ptr,v) (((uint32_unaligned_t *)(ptr))->val = SPICE_BYTESWAP32((uint32_t)(v)))
#define write_uint32(ptr,v) (((uint32_unaligned_t *)(ptr))->val = SPICE_BYTESWAP32((uint32_t)(v)))
#define write_int64(ptr,v) (((uint64_unaligned_t *)(ptr))->val = SPICE_BYTESWAP64((uint64_t)(v)))
#define write_uint64(ptr,v) (((uint64_unaligned_t *)(ptr))->val = SPICE_BYTESWAP64((uint64_t)(v)))
#else
#define write_int8(ptr,v) (*((int8_t *)(ptr)) = v)
#define write_uint8(ptr,v) (*((uint8_t *)(ptr)) = v)
#define write_int16(ptr,v) (*((int16_t *)(ptr)) = v)
#define write_uint16(ptr,v) (*((uint16_t *)(ptr)) = v)
#define write_int32(ptr,v) (*((int32_t *)(ptr)) = v)
#define write_uint32(ptr,v) (*((uint32_t *)(ptr)) = v)
#define write_int64(ptr,v) (*((int64_t *)(ptr)) = v)
#define write_uint64(ptr,v) (*((uint64_t *)(ptr)) = v)
#define write_int16(ptr,v) (((int16_unaligned_t *)(ptr))->val = v)
#define write_uint16(ptr,v) (((uint16_unaligned_t *)(ptr))->val = v)
#define write_int32(ptr,v) (((int32_unaligned_t *)(ptr))->val = v)
#define write_uint32(ptr,v) (((uint32_unaligned_t *)(ptr))->val = v)
#define write_int64(ptr,v) (((int64_unaligned_t *)(ptr))->val = v)
#define write_uint64(ptr,v) (((uint64_unaligned_t *)(ptr))->val = v)
#endif
typedef struct {
@ -68,7 +94,6 @@ typedef struct SpiceMarshallerData SpiceMarshallerData;
typedef struct {
SpiceMarshaller *marshaller;
int item_nr;
int is_64bit;
size_t offset;
} MarshallerRef;
@ -80,25 +105,26 @@ struct SpiceMarshaller {
MarshallerRef pointer_ref;
int n_items;
int items_size; /* number of items availible in items */
int items_size; /* number of items available in items */
MarshallerItem *items;
MarshallerItem static_items[N_STATIC_ITEMS];
int num_fd;
int fd[4];
};
struct SpiceMarshallerData {
size_t total_size;
size_t base;
SpiceMarshaller *marshallers;
SpiceMarshaller *last_marshaller;
size_t current_buffer_position;
MarshallerBuffer *current_buffer;
MarshallerItem *current_buffer_item;
MarshallerBuffer *buffers;
SpiceMarshaller static_marshaller;
MarshallerBuffer static_buffer;
// first marshaller and buffer are statically allocated here
SpiceMarshaller marshallers[1];
MarshallerBuffer buffers[1];
};
static void spice_marshaller_init(SpiceMarshaller *m,
@ -111,6 +137,8 @@ static void spice_marshaller_init(SpiceMarshaller *m,
m->n_items = 0;
m->items_size = N_STATIC_ITEMS;
m->items = m->static_items;
m->fd[0] = m->fd[1] = m->fd[2] = m->fd[3] = -1;
m->num_fd = 0;
}
SpiceMarshaller *spice_marshaller_new(void)
@ -120,16 +148,15 @@ SpiceMarshaller *spice_marshaller_new(void)
d = spice_new(SpiceMarshallerData, 1);
d->last_marshaller = d->marshallers = &d->static_marshaller;
d->last_marshaller = d->marshallers;
d->total_size = 0;
d->base = 0;
d->buffers = &d->static_buffer;
d->buffers->next = NULL;
d->current_buffer = d->buffers;
d->current_buffer_position = 0;
d->current_buffer_item = NULL;
m = &d->static_marshaller;
m = d->marshallers;
spice_marshaller_init(m, d);
return m;
@ -160,6 +187,7 @@ void spice_marshaller_reset(SpiceMarshaller *m)
{
SpiceMarshaller *m2, *next;
SpiceMarshallerData *d;
int i;
/* Only supported for root marshaller */
assert(m->data->marshallers == m);
@ -179,6 +207,14 @@ void spice_marshaller_reset(SpiceMarshaller *m)
m->n_items = 0;
m->total_size = 0;
for (i = 0; i < m->num_fd; i++) {
if (m->fd[i] >= 0) {
close(m->fd[i]);
m->fd[i] = -1;
}
}
m->num_fd = 0;
d = m->data;
d->last_marshaller = d->marshallers;
d->total_size = 0;
@ -238,6 +274,11 @@ static size_t remaining_buffer_size(SpiceMarshallerData *d)
return MARSHALLER_BUFFER_SIZE - d->current_buffer_position;
}
static void reserve_space_free_data(uint8_t *data, SPICE_GNUC_UNUSED void *opaque)
{
free(data);
}
uint8_t *spice_marshaller_reserve_space(SpiceMarshaller *m, size_t size)
{
MarshallerItem *item;
@ -276,7 +317,7 @@ uint8_t *spice_marshaller_reserve_space(SpiceMarshaller *m, size_t size)
/* Large item, allocate by itself */
item->data = (uint8_t *)spice_malloc(size);
item->len = size;
item->free_data = (spice_marshaller_item_free_func)free;
item->free_data = reserve_space_free_data;
item->opaque = NULL;
} else {
/* Use next buffer */
@ -310,8 +351,8 @@ void spice_marshaller_unreserve_space(SpiceMarshaller *m, size_t size)
item->len -= size;
}
uint8_t *spice_marshaller_add_ref_full(SpiceMarshaller *m, uint8_t *data, size_t size,
spice_marshaller_item_free_func free_data, void *opaque)
uint8_t *spice_marshaller_add_by_ref_full(SpiceMarshaller *m, uint8_t *data, size_t size,
spice_marshaller_item_free_func free_data, void *opaque)
{
MarshallerItem *item;
SpiceMarshallerData *d;
@ -333,7 +374,7 @@ uint8_t *spice_marshaller_add_ref_full(SpiceMarshaller *m, uint8_t *data, size_t
return data;
}
uint8_t *spice_marshaller_add(SpiceMarshaller *m, uint8_t *data, size_t size)
uint8_t *spice_marshaller_add(SpiceMarshaller *m, const uint8_t *data, size_t size)
{
uint8_t *ptr;
@ -342,18 +383,21 @@ uint8_t *spice_marshaller_add(SpiceMarshaller *m, uint8_t *data, size_t size)
return ptr;
}
uint8_t *spice_marshaller_add_ref(SpiceMarshaller *m, uint8_t *data, size_t size)
uint8_t *spice_marshaller_add_by_ref(SpiceMarshaller *m, const uint8_t *data, size_t size)
{
return spice_marshaller_add_ref_full(m, data, size, NULL, NULL);
/* the cast to no-const here is safe as data is used for writing only if
* free_data pointer is not NULL
*/
return spice_marshaller_add_by_ref_full(m, (uint8_t *) data, size, NULL, NULL);
}
void spice_marshaller_add_ref_chunks(SpiceMarshaller *m, SpiceChunks *chunks)
void spice_marshaller_add_chunks_by_ref(SpiceMarshaller *m, SpiceChunks *chunks)
{
unsigned int i;
for (i = 0; i < chunks->num_chunks; i++) {
spice_marshaller_add_ref(m, chunks->chunk[i].data,
chunks->chunk[i].len);
spice_marshaller_add_by_ref(m, chunks->chunk[i].data,
chunks->chunk[i].len);
}
}
@ -373,13 +417,13 @@ SpiceMarshaller *spice_marshaller_get_submarshaller(SpiceMarshaller *m)
return m2;
}
SpiceMarshaller *spice_marshaller_get_ptr_submarshaller(SpiceMarshaller *m, int is_64bit)
SpiceMarshaller *spice_marshaller_get_ptr_submarshaller(SpiceMarshaller *m)
{
SpiceMarshaller *m2;
uint8_t *p;
int size;
size = is_64bit ? 8 : 4;
size = 4;
p = spice_marshaller_reserve_space(m, size);
memset(p, 0, size);
@ -387,12 +431,11 @@ SpiceMarshaller *spice_marshaller_get_ptr_submarshaller(SpiceMarshaller *m, int
m2->pointer_ref.marshaller = m;
m2->pointer_ref.item_nr = m->n_items - 1;
m2->pointer_ref.offset = m->items[m->n_items - 1].len - size;
m2->pointer_ref.is_64bit = is_64bit;
return m2;
}
uint8_t *lookup_ref(MarshallerRef *ref)
static uint8_t *lookup_ref(MarshallerRef *ref)
{
MarshallerItem *item;
@ -419,7 +462,7 @@ uint8_t *spice_marshaller_linearize(SpiceMarshaller *m, size_t skip_bytes,
/* Only supported for root marshaller */
assert(m->data->marshallers == m);
if (m->n_items == 1) {
if (m->n_items == 1 && m->next == NULL) {
*free_res = FALSE;
if (m->items[0].len <= skip_bytes) {
*len = 0;
@ -492,18 +535,19 @@ void spice_marshaller_flush(SpiceMarshaller *m)
for (m2 = m; m2 != NULL; m2 = m2->next) {
if (m2->pointer_ref.marshaller != NULL && m2->total_size > 0) {
ptr_pos = lookup_ref(&m2->pointer_ref);
if (m2->pointer_ref.is_64bit) {
write_uint64(ptr_pos,
spice_marshaller_get_offset(m2));
} else {
write_uint32(ptr_pos,
spice_marshaller_get_offset(m2));
}
write_uint32(ptr_pos, spice_marshaller_get_offset(m2));
}
}
}
#ifndef WIN32
#ifdef WIN32
// this definition is ABI compatible with WSABUF
struct iovec {
unsigned long iov_len;
void *iov_base;
};
#endif
int spice_marshaller_fill_iovec(SpiceMarshaller *m, struct iovec *vec,
int n_vec, size_t skip_bytes)
{
@ -525,7 +569,7 @@ int spice_marshaller_fill_iovec(SpiceMarshaller *m, struct iovec *vec,
if (v == n_vec) {
return v; /* Not enough space in vec */
}
vec[v].iov_base = (void *)item->data + skip_bytes;
vec[v].iov_base = (uint8_t *)item->data + skip_bytes;
vec[v].iov_len = item->len - skip_bytes;
skip_bytes = 0;
v++;
@ -535,7 +579,6 @@ int spice_marshaller_fill_iovec(SpiceMarshaller *m, struct iovec *vec,
return v;
}
#endif
void *spice_marshaller_add_uint64(SpiceMarshaller *m, uint64_t v)
{
@ -564,7 +607,7 @@ void *spice_marshaller_add_uint32(SpiceMarshaller *m, uint32_t v)
return (void *)ptr;
}
void spice_marshaller_set_uint32(SpiceMarshaller *m, void *ref, uint32_t v)
void spice_marshaller_set_uint32(SPICE_GNUC_UNUSED SpiceMarshaller *m, void *ref, uint32_t v)
{
write_uint32((uint8_t *)ref, v);
}
@ -613,3 +656,26 @@ void *spice_marshaller_add_int8(SpiceMarshaller *m, int8_t v)
write_int8(ptr, v);
return (void *)ptr;
}
void spice_marshaller_add_fd(SpiceMarshaller *m, int fd)
{
spice_assert(m->num_fd < 4);
if (fd != -1) {
m->fd[m->num_fd] = dup(fd);
if (m->fd[m->num_fd] == -1) {
perror("dup");
}
m->num_fd++;
}
}
int spice_marshaller_get_fds(SpiceMarshaller *m, int fd[4])
{
int num_fd = m->num_fd;
memcpy(fd, m->fd, sizeof(m->fd));
m->num_fd = 0;
return num_fd;
}

View File

@ -16,18 +16,20 @@
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _H_MARSHALLER
#define _H_MARSHALLER
#ifndef H_SPICE_COMMON_MARSHALLER
#define H_SPICE_COMMON_MARSHALLER
#include <stdbool.h>
#include <spice/macros.h>
#include <spice/types.h>
#include "mem.h"
#ifndef WIN32
#include <sys/uio.h>
#else
struct iovec;
#endif
#ifdef __cplusplus
extern "C" {
#endif
SPICE_BEGIN_DECLS
typedef struct SpiceMarshaller SpiceMarshaller;
typedef void (*spice_marshaller_item_free_func)(uint8_t *data, void *opaque);
@ -37,11 +39,11 @@ void spice_marshaller_reset(SpiceMarshaller *m);
void spice_marshaller_destroy(SpiceMarshaller *m);
uint8_t *spice_marshaller_reserve_space(SpiceMarshaller *m, size_t size);
void spice_marshaller_unreserve_space(SpiceMarshaller *m, size_t size);
uint8_t *spice_marshaller_add(SpiceMarshaller *m, uint8_t *data, size_t size);
uint8_t *spice_marshaller_add_ref(SpiceMarshaller *m, uint8_t *data, size_t size);
uint8_t *spice_marshaller_add_ref_full(SpiceMarshaller *m, uint8_t *data, size_t size,
spice_marshaller_item_free_func free_data, void *opaque);
void spice_marshaller_add_ref_chunks(SpiceMarshaller *m, SpiceChunks *chunks);
uint8_t *spice_marshaller_add(SpiceMarshaller *m, const uint8_t *data, size_t size);
uint8_t *spice_marshaller_add_by_ref(SpiceMarshaller *m, const uint8_t *data, size_t size);
uint8_t *spice_marshaller_add_by_ref_full(SpiceMarshaller *m, uint8_t *data, size_t size,
spice_marshaller_item_free_func free_data, void *opaque);
void spice_marshaller_add_chunks_by_ref(SpiceMarshaller *m, SpiceChunks *chunks);
void spice_marshaller_flush(SpiceMarshaller *m);
void spice_marshaller_set_base(SpiceMarshaller *m, size_t base);
uint8_t *spice_marshaller_linearize(SpiceMarshaller *m, size_t skip,
@ -51,11 +53,9 @@ size_t spice_marshaller_get_offset(SpiceMarshaller *m);
size_t spice_marshaller_get_size(SpiceMarshaller *m);
size_t spice_marshaller_get_total_size(SpiceMarshaller *m);
SpiceMarshaller *spice_marshaller_get_submarshaller(SpiceMarshaller *m);
SpiceMarshaller *spice_marshaller_get_ptr_submarshaller(SpiceMarshaller *m, int is_64bit);
#ifndef WIN32
SpiceMarshaller *spice_marshaller_get_ptr_submarshaller(SpiceMarshaller *m);
int spice_marshaller_fill_iovec(SpiceMarshaller *m, struct iovec *vec,
int n_vec, size_t skip_bytes);
#endif
void *spice_marshaller_add_uint64(SpiceMarshaller *m, uint64_t v);
void *spice_marshaller_add_int64(SpiceMarshaller *m, int64_t v);
void *spice_marshaller_add_uint32(SpiceMarshaller *m, uint32_t v);
@ -67,8 +67,9 @@ void *spice_marshaller_add_int8(SpiceMarshaller *m, int8_t v);
void spice_marshaller_set_uint32(SpiceMarshaller *m, void *ref, uint32_t v);
#ifdef __cplusplus
}
#endif
void spice_marshaller_add_fd(SpiceMarshaller *m, int fd);
int spice_marshaller_get_fds(SpiceMarshaller *m, int fd[4]);
SPICE_END_DECLS
#endif

View File

@ -15,20 +15,17 @@
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "mem.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifndef MALLOC_ERROR
#define MALLOC_ERROR(format, ...) { \
printf(format "\n", ## __VA_ARGS__); \
abort(); \
}
#define MALLOC_ERROR(...) SPICE_STMT_START { \
spice_error(__VA_ARGS__); \
abort(); \
} SPICE_STMT_END
#endif
size_t spice_strnlen(const char *str, size_t max_len)
@ -46,13 +43,15 @@ size_t spice_strnlen(const char *str, size_t max_len)
char *spice_strdup(const char *str)
{
char *copy;
size_t len;
if (str == NULL) {
return NULL;
}
copy = (char *)spice_malloc(strlen(str) + 1);
strcpy(copy, str);
len = strlen(str) + 1;
copy = (char *)spice_malloc(len);
memcpy(copy, str, len);
return copy;
}
@ -94,8 +93,7 @@ void *spice_malloc(size_t n_bytes)
return mem;
}
MALLOC_ERROR("spice_malloc: panic: unable to allocate %lu bytes\n",
(unsigned long)n_bytes);
MALLOC_ERROR("unable to allocate %lu bytes", (unsigned long)n_bytes);
}
return NULL;
}
@ -111,8 +109,7 @@ void *spice_malloc0(size_t n_bytes)
return mem;
}
MALLOC_ERROR("spice_malloc0: panic: unable to allocate %lu bytes\n",
(unsigned long)n_bytes);
MALLOC_ERROR("unable to allocate %lu bytes", (unsigned long)n_bytes);
}
return NULL;
}
@ -126,8 +123,7 @@ void *spice_realloc(void *mem, size_t n_bytes)
return mem;
}
MALLOC_ERROR("spice_realloc: panic: unable to allocate %lu bytes\n",
(unsigned long)n_bytes);
MALLOC_ERROR("unable to allocate %lu bytes", (unsigned long)n_bytes);
}
free(mem);
@ -140,7 +136,7 @@ void *spice_realloc(void *mem, size_t n_bytes)
void *spice_malloc_n(size_t n_blocks, size_t n_block_bytes)
{
if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) {
MALLOC_ERROR("spice_malloc_n: overflow allocating %lu*%lu bytes",
MALLOC_ERROR("overflow allocating %lu*%lu bytes",
(unsigned long)n_blocks, (unsigned long)n_block_bytes);
}
@ -179,7 +175,7 @@ void *spice_realloc_n(void *mem, size_t n_blocks, size_t n_block_bytes)
if (SIZE_OVERFLOWS (n_blocks, n_block_bytes)) {
MALLOC_ERROR("spice_realloc_n: overflow allocating %lu*%lu bytes",
(unsigned long)n_blocks, (unsigned long)n_block_bytes);
}
}
return spice_realloc(mem, n_blocks * n_block_bytes);
}
@ -295,3 +291,25 @@ size_t spice_buffer_remove(SpiceBuffer *buffer, size_t len)
buffer->offset -= len;
return len;
}
#ifdef SPICE_DEBUG_ALIGNMENT
void spice_alignment_warning(const char *loc, void *p, unsigned sz)
{
static const char *last_loc = NULL;
if (loc != last_loc) {
last_loc = loc;
spice_log(G_LOG_LEVEL_WARNING, loc, __FUNCTION__,
"Misaligned access at %p, alignment %u", p, sz);
}
}
void spice_alignment_debug(const char *loc, void *p, unsigned sz)
{
static const char *last_loc = NULL;
if (loc != last_loc) {
last_loc = loc;
spice_log(G_LOG_LEVEL_DEBUG, loc, __FUNCTION__,
"Expected misaligned access at %p, alignment %u", p, sz);
}
}
#endif // SPICE_DEBUG_ALIGNMENT

View File

@ -16,19 +16,14 @@
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _H_MEM
#define _H_MEM
#ifndef H_SPICE_COMMON_MEM
#define H_SPICE_COMMON_MEM
#include "log.h"
#include <stdlib.h>
#include <spice/macros.h>
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
SPICE_BEGIN_DECLS
#ifdef STDC_HEADERS
# include <stdlib.h>
@ -118,7 +113,7 @@ size_t spice_strnlen(const char *str, size_t max_len);
__p; \
}))
# define _SPICE_RENEW(struct_type, mem, n_structs, func) \
(struct_type *) (__extension__ ({ \
(struct_type *) (__extension__ ({ \
size_t __n = (size_t) (n_structs); \
size_t __s = sizeof (struct_type); \
void *__p = (void *) (mem); \
@ -134,7 +129,6 @@ size_t spice_strnlen(const char *str, size_t max_len);
#else
/* Unoptimized version: always call the _n() function. */
#define _SPICE_NEW(struct_type, n_structs, func) \
((struct_type *) spice_##func##_n ((n_structs), sizeof (struct_type)))
#define _SPICE_RENEW(struct_type, mem, n_structs, func) \
@ -142,6 +136,46 @@ size_t spice_strnlen(const char *str, size_t max_len);
#endif
/* Cast to a type with stricter alignment constraints (to build with clang) */
/* Misaligned cast to a type with stricter alignment */
#ifndef SPICE_DEBUG_ALIGNMENT
#define SPICE_UNALIGNED_CAST(type, value) ((type)(void *)(value))
#define SPICE_ALIGNED_CAST(type, value) ((type)(void *)(value))
#else // SPICE_DEBUG_ALIGNMENT
#define SPICE_ALIGNED_CAST(type, value) \
((type)spice_alignment_check(G_STRLOC, \
(void *)(value), \
__alignof(*((type)0))))
#define SPICE_UNALIGNED_CAST(type, value) \
((type)spice_alignment_weak_check(G_STRLOC, \
(void *)(value), \
__alignof(*((type)0))))
extern void spice_alignment_warning(const char *loc, void *p, unsigned sz);
extern void spice_alignment_debug(const char *loc, void *p, unsigned sz);
static inline void *spice_alignment_check(const char *loc,
void *ptr, unsigned sz)
{
if (G_UNLIKELY(((uintptr_t) ptr & (sz-1U)) != 0))
spice_alignment_warning(loc, ptr, sz);
return ptr;
}
static inline void *spice_alignment_weak_check(const char *loc,
void *ptr, unsigned sz)
{
if (G_UNLIKELY(((uintptr_t) ptr & (sz-1U)) != 0))
spice_alignment_debug(loc, ptr, sz);
return ptr;
}
#endif // SPICE_DEBUG_ALIGNMENT
#define spice_new(struct_type, n_structs) _SPICE_NEW(struct_type, n_structs, malloc)
#define spice_new0(struct_type, n_structs) _SPICE_NEW(struct_type, n_structs, malloc0)
#define spice_renew(struct_type, mem, n_structs) _SPICE_RENEW(struct_type, mem, n_structs, realloc)
@ -156,7 +190,6 @@ void spice_buffer_append(SpiceBuffer *buffer, const void *data, size_t len);
size_t spice_buffer_copy(SpiceBuffer *buffer, void *dest, size_t len);
size_t spice_buffer_remove(SpiceBuffer *buffer, size_t len);
#ifdef __cplusplus
}
#endif
SPICE_END_DECLS
#endif

194
common/meson.build Normal file
View File

@ -0,0 +1,194 @@
#
# libspice-common
#
spice_common_sources = [
'agent.c',
'agent.h',
'backtrace.c',
'backtrace.h',
'canvas_utils.c',
'canvas_utils.h',
'demarshallers.h',
'draw.h',
'lines.c',
'lines.h',
'log.c',
'log.h',
'lz.c',
'lz.h',
'lz_common.h',
'lz_config.h',
'macros.h',
'marshaller.c',
'marshaller.h',
'mem.c',
'mem.h',
'messages.h',
'pixman_utils.c',
'pixman_utils.h',
'quic.c',
'quic.h',
'quic_config.h',
'rect.h',
'region.c',
'region.h',
'ring.h',
'rop3.c',
'rop3.h',
'snd_codec.c',
'snd_codec.h',
'utils.c',
'utils.h',
'udev.c',
'udev.h',
'verify.h',
'recorder.h'
]
if get_option('instrumentation') == 'recorder'
spice_common_sources += [
'recorder/recorder.c',
'recorder/recorder.h',
'recorder/recorder_ring.c',
'recorder/recorder_ring.h'
]
endif
if get_option('instrumentation') == 'agent'
spice_common_sources += [
'agent_interface.c',
'agent_interface.h'
]
endif
spice_common_lib = static_library('spice-common', spice_common_sources,
install : false,
include_directories : spice_common_include,
dependencies : spice_common_deps)
spice_common_dep = declare_dependency(link_with : spice_common_lib,
include_directories : spice_common_include,
dependencies : spice_common_deps)
# client_demarshallers
if spice_common_generate_client_code or spice_common_generate_server_code
codegen_cmd = [python, spice_codegen]
codegen_args = ['--generate-demarshallers',
'--client',
'--include', 'common/messages.h',
'--generated-declaration-file', '@OUTPUT1@',
'@INPUT@', '@OUTPUT0@']
client_demarshallers = custom_target('client_demarshallers',
input : [spice_proto],
output : ['generated_client_demarshallers.c', 'generated_messages.h'],
install : false,
command : [codegen_cmd, codegen_args],
depend_files : [spice_codegen_files, 'messages.h'])
endif
#
# libspice-common-client
#
if spice_common_generate_client_code
# client_marshallers
codegen_args = ['--generate-marshallers',
'--generate-header',
'-P',
'--client',
'--include', 'common/client_marshallers.h',
'@INPUT0@', '@OUTPUT0@']
client_marshallers = custom_target('client_marshallers',
input : [spice_proto, client_demarshallers],
output : ['generated_client_marshallers.c', 'generated_client_marshallers.h'],
install : false,
command : [codegen_cmd, codegen_args],
depend_files : [spice_codegen_files, 'client_marshallers.h'])
spice_common_client_sources = [
client_demarshallers,
client_marshallers,
'client_marshallers.h',
'ssl_verify.c',
'ssl_verify.h',
]
spice_common_client_lib = static_library('spice-common-client', spice_common_client_sources,
install : false,
dependencies : [spice_common_dep, gio2_deps])
spice_common_client_dep = declare_dependency(sources : client_marshallers[1],
link_with : spice_common_client_lib,
dependencies : [spice_common_dep, gio2_deps])
endif
#
# libspice-common-server
#
if spice_common_generate_server_code
structs_args = [
'-M', 'String',
'-M', 'Rect',
'-M', 'Point',
'-M', 'DisplayBase',
'-M', 'Fill',
'-M', 'Opaque',
'-M', 'Copy',
'-M', 'Blend',
'-M', 'Blackness',
'-M', 'Whiteness',
'-M', 'Invers',
'-M', 'Rop3',
'-M', 'Stroke',
'-M', 'Text',
'-M', 'Transparent',
'-M', 'AlphaBlend',
'-M', 'Composite',
]
targets = [
{ 'name' : 'server_demarshallers',
'input' : [ spice_proto, client_demarshallers ],
'output' : ['generated_server_demarshallers.c'],
'codegen_args' : ['--generate-demarshallers',
'--server',
'--include', 'common/messages.h',
'@INPUT0@', '@OUTPUT0@'],
},
{ 'name' : 'server_marshallers',
'input' : [ spice_proto, client_demarshallers ],
'output' : ['generated_server_marshallers.c', 'generated_server_marshallers.h'],
'codegen_args' : ['--generate-marshallers',
'--generate-header',
'--server',
structs_args,
'--include', 'common/messages.h',
'@INPUT0@', '@OUTPUT0@']
},
]
spice_common_server_sources = []
spice_common_server_dep_sources = []
foreach t : targets
target = custom_target(t['name'],
input : t['input'],
output : t['output'],
install : false,
command : [codegen_cmd, t['codegen_args']],
depend_files : [spice_codegen_files, 'messages.h'])
spice_common_server_sources += target
if t['output'].length() > 1
spice_common_server_dep_sources += target[1]
endif
endforeach
spice_common_server_lib = static_library('spice-common-server', spice_common_server_sources,
install : false,
dependencies : spice_common_dep)
spice_common_server_dep = declare_dependency(sources : spice_common_server_dep_sources,
link_with : spice_common_server_lib,
dependencies : spice_common_dep)
endif

73
common/messages.h Normal file
View File

@ -0,0 +1,73 @@
/*
Copyright (C) 2009-2010 Red Hat, Inc.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef H_SPICE_COMMON_MESSAGES
#define H_SPICE_COMMON_MESSAGES
#include <spice/protocol.h>
#include <spice/macros.h>
#ifdef USE_SMARTCARD
#include <libcacard.h>
#endif
#include "draw.h"
SPICE_BEGIN_DECLS
typedef struct SpiceMsgCompressedData {
uint8_t type;
uint32_t uncompressed_size;
uint32_t compressed_size;
uint8_t *compressed_data;
} SpiceMsgCompressedData;
#define SPICE_AGENT_MAX_DATA_SIZE 2048
#ifdef USE_SMARTCARD
typedef struct SpiceMsgSmartcard {
VSCMsgType type;
uint32_t length;
uint32_t reader_id;
uint8_t data[0];
} SpiceMsgSmartcard;
#endif
#include <common/generated_messages.h>
typedef SpiceMsgMainAgentTokens SpiceMsgcMainAgentTokens;
typedef SpiceMsgMainAgentTokens SpiceMsgcMainAgentStart;
typedef SpiceMsgDisplayDrawCopy SpiceMsgDisplayDrawBlend;
typedef SpiceMsgPlaybackMode SpiceMsgcRecordMode;
typedef SpiceMsgPlaybackPacket SpiceMsgcRecordPacket;
SPICE_END_DECLS
#endif // H_SPICE_COMMON_MESSAGES

View File

@ -15,22 +15,26 @@
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "pixman_utils.h"
#include "spice_common.h"
#include <spice/macros.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "mem.h"
/*
* src is used for most OPs, hidden within _equation attribute. For some
* operations (such as "clear" and "noop") src is not used and then we have
* to add SPICE_GNUC_UNUSED, that's just a __attribute__((__unused__)), to
* make GCC happy.
* Also, according to GCC documentation [0], the unused attribute "(...) means
* that the variable is meant to be possibly unused. GCC does not produce a
* warning for this variable.". So, we are safe adding it, even if src is used
* for most OPs.
*/
#define SOLID_RASTER_OP(_name, _size, _type, _equation) \
static void \
solid_rop_ ## _name ## _ ## _size (_type *ptr, int len, _type src) \
solid_rop_ ## _name ## _ ## _size (_type *ptr, int len, SPICE_GNUC_UNUSED _type src) \
{ \
while (len--) { \
_type dst = *ptr; \
@ -206,12 +210,12 @@ void spice_pixman_fill_rect(pixman_image_t *dest,
depth = spice_pixman_image_get_bpp(dest);
/* stride is in bytes, depth in bits */
ASSERT(x >= 0);
ASSERT(y >= 0);
ASSERT(width > 0);
ASSERT(height > 0);
ASSERT(x + width <= pixman_image_get_width(dest));
ASSERT(y + height <= pixman_image_get_height(dest));
spice_assert(x >= 0);
spice_assert(y >= 0);
spice_assert(width > 0);
spice_assert(height > 0);
spice_assert(x + width <= pixman_image_get_width(dest));
spice_assert(y + height <= pixman_image_get_height(dest));
if (pixman_fill(bits,
stride / 4,
@ -231,7 +235,7 @@ void spice_pixman_fill_rect(pixman_image_t *dest,
byte_width = 2 * width;
value = (value & 0xffff) * 0x00010001;
} else {
ASSERT (depth == 32)
spice_assert (depth == 32);
byte_line = ((uint8_t *)bits) + stride * y + x * 4;
byte_width = 4 * width;
}
@ -298,13 +302,13 @@ void spice_pixman_fill_rect_rop(pixman_image_t *dest,
depth = spice_pixman_image_get_bpp(dest);
/* stride is in bytes, depth in bits */
ASSERT(x >= 0);
ASSERT(y >= 0);
ASSERT(width > 0);
ASSERT(height > 0);
ASSERT(x + width <= pixman_image_get_width(dest));
ASSERT(y + height <= pixman_image_get_height(dest));
ASSERT(rop >= 0 && rop < 16);
spice_assert(x >= 0);
spice_assert(y >= 0);
spice_assert(width > 0);
spice_assert(height > 0);
spice_assert(x + width <= pixman_image_get_width(dest));
spice_assert(y + height <= pixman_image_get_height(dest));
spice_assert(rop < 16);
if (depth == 8) {
solid_rop_8_func_t rop_func = solid_rops_8[rop];
@ -358,13 +362,13 @@ void spice_pixman_tile_rect(pixman_image_t *dest,
tile_width = pixman_image_get_width(tile);
tile_height = pixman_image_get_height(tile);
ASSERT(x >= 0);
ASSERT(y >= 0);
ASSERT(width > 0);
ASSERT(height > 0);
ASSERT(x + width <= pixman_image_get_width(dest));
ASSERT(y + height <= pixman_image_get_height(dest));
ASSERT(depth == spice_pixman_image_get_bpp(tile));
spice_assert(x >= 0);
spice_assert(y >= 0);
spice_assert(width > 0);
spice_assert(height > 0);
spice_assert(x + width <= pixman_image_get_width(dest));
spice_assert(y + height <= pixman_image_get_height(dest));
spice_assert(depth == spice_pixman_image_get_bpp(tile));
tile_start_x = (x - offset_x) % tile_width;
if (tile_start_x < 0) {
@ -406,7 +410,7 @@ void spice_pixman_tile_rect(pixman_image_t *dest,
}
}
} else {
ASSERT (depth == 32);
spice_assert (depth == 32);
byte_line = ((uint8_t *)bits) + stride * y + x * 4;
tile_line = ((uint8_t *)tile_bits) + tile_stride * tile_start_y + tile_start_x * 4;
@ -449,14 +453,14 @@ void spice_pixman_tile_rect_rop(pixman_image_t *dest,
tile_width = pixman_image_get_width(tile);
tile_height = pixman_image_get_height(tile);
ASSERT(x >= 0);
ASSERT(y >= 0);
ASSERT(width > 0);
ASSERT(height > 0);
ASSERT(x + width <= pixman_image_get_width(dest));
ASSERT(y + height <= pixman_image_get_height(dest));
ASSERT(rop >= 0 && rop < 16);
ASSERT(depth == spice_pixman_image_get_bpp(tile));
spice_assert(x >= 0);
spice_assert(y >= 0);
spice_assert(width > 0);
spice_assert(height > 0);
spice_assert(x + width <= pixman_image_get_width(dest));
spice_assert(y + height <= pixman_image_get_height(dest));
spice_assert(rop < 16);
spice_assert(depth == spice_pixman_image_get_bpp(tile));
tile_start_x = (x - offset_x) % tile_width;
if (tile_start_x < 0) {
@ -504,7 +508,7 @@ void spice_pixman_tile_rect_rop(pixman_image_t *dest,
} else {
tiled_rop_32_func_t rop_func = tiled_rops_32[rop];
ASSERT (depth == 32);
spice_assert (depth == 32);
byte_line = ((uint8_t *)bits) + stride * y + x * 4;
tile_line = ((uint8_t *)tile_bits) + tile_stride * tile_start_y + tile_start_x * 4;
@ -536,6 +540,11 @@ void spice_pixman_blit(pixman_image_t *dest,
uint8_t *src_line;
int byte_width;
if (!src) {
fprintf(stderr, "missing src!");
return;
}
bits = pixman_image_get_data(dest);
stride = pixman_image_get_stride(dest);
depth = spice_pixman_image_get_bpp(dest);
@ -569,17 +578,17 @@ void spice_pixman_blit(pixman_image_t *dest,
return;
}
ASSERT(src_x >= 0);
ASSERT(src_y >= 0);
ASSERT(dest_x >= 0);
ASSERT(dest_y >= 0);
ASSERT(width > 0);
ASSERT(height > 0);
ASSERT(dest_x + width <= pixman_image_get_width(dest));
ASSERT(dest_y + height <= pixman_image_get_height(dest));
ASSERT(src_x + width <= pixman_image_get_width(src));
ASSERT(src_y + height <= pixman_image_get_height(src));
ASSERT(depth == src_depth);
spice_assert(src_x >= 0);
spice_assert(src_y >= 0);
spice_assert(dest_x >= 0);
spice_assert(dest_y >= 0);
spice_assert(width > 0);
spice_assert(height > 0);
spice_assert(dest_x + width <= pixman_image_get_width(dest));
spice_assert(dest_y + height <= pixman_image_get_height(dest));
spice_assert(src_x + width <= pixman_image_get_width(src));
spice_assert(src_y + height <= pixman_image_get_height(src));
spice_assert(depth == src_depth);
if (pixman_blt(src_bits,
bits,
@ -601,7 +610,7 @@ void spice_pixman_blit(pixman_image_t *dest,
byte_width = width * 2;
src_line = ((uint8_t *)src_bits) + src_stride * src_y + src_x * 2;
} else {
ASSERT (depth == 32);
spice_assert (depth == 32);
byte_line = ((uint8_t *)bits) + stride * dest_y + dest_x * 4;
byte_width = width * 4;
src_line = ((uint8_t *)src_bits) + src_stride * src_y + src_x * 4;
@ -660,17 +669,17 @@ void spice_pixman_blit_rop (pixman_image_t *dest,
return;
}
ASSERT(src_x >= 0);
ASSERT(src_y >= 0);
ASSERT(dest_x >= 0);
ASSERT(dest_y >= 0);
ASSERT(width > 0);
ASSERT(height > 0);
ASSERT(dest_x + width <= pixman_image_get_width(dest));
ASSERT(dest_y + height <= pixman_image_get_height(dest));
ASSERT(src_x + width <= pixman_image_get_width(src));
ASSERT(src_y + height <= pixman_image_get_height(src));
ASSERT(depth == src_depth);
spice_assert(src_x >= 0);
spice_assert(src_y >= 0);
spice_assert(dest_x >= 0);
spice_assert(dest_y >= 0);
spice_assert(width > 0);
spice_assert(height > 0);
spice_assert(dest_x + width <= pixman_image_get_width(dest));
spice_assert(dest_y + height <= pixman_image_get_height(dest));
spice_assert(src_x + width <= pixman_image_get_width(src));
spice_assert(src_y + height <= pixman_image_get_height(src));
spice_assert(depth == src_depth);
if (depth == 8) {
copy_rop_8_func_t rop_func = copy_rops_8[rop];
@ -697,7 +706,7 @@ void spice_pixman_blit_rop (pixman_image_t *dest,
} else {
copy_rop_32_func_t rop_func = copy_rops_32[rop];
ASSERT (depth == 32);
spice_assert (depth == 32);
byte_line = ((uint8_t *)bits) + stride * dest_y + dest_x * 4;
src_line = ((uint8_t *)src_bits) + src_stride * src_y + src_x * 4;
@ -756,17 +765,17 @@ void spice_pixman_blit_colorkey (pixman_image_t *dest,
return;
}
ASSERT(src_x >= 0);
ASSERT(src_y >= 0);
ASSERT(dest_x >= 0);
ASSERT(dest_y >= 0);
ASSERT(width > 0);
ASSERT(height > 0);
ASSERT(dest_x + width <= pixman_image_get_width(dest));
ASSERT(dest_y + height <= pixman_image_get_height(dest));
ASSERT(src_x + width <= pixman_image_get_width(src));
ASSERT(src_y + height <= pixman_image_get_height(src));
ASSERT(depth == spice_pixman_image_get_bpp(src));
spice_assert(src_x >= 0);
spice_assert(src_y >= 0);
spice_assert(dest_x >= 0);
spice_assert(dest_y >= 0);
spice_assert(width > 0);
spice_assert(height > 0);
spice_assert(dest_x + width <= pixman_image_get_width(dest));
spice_assert(dest_y + height <= pixman_image_get_height(dest));
spice_assert(src_x + width <= pixman_image_get_width(src));
spice_assert(src_y + height <= pixman_image_get_height(src));
spice_assert(depth == spice_pixman_image_get_bpp(src));
if (depth == 8) {
byte_line = ((uint8_t *)bits) + stride * dest_y + dest_x;
@ -806,7 +815,7 @@ void spice_pixman_blit_colorkey (pixman_image_t *dest,
src_line += src_stride;
}
} else {
ASSERT (depth == 32);
spice_assert (depth == 32);
byte_line = ((uint8_t *)bits) + stride * dest_y + dest_x * 4;
src_line = ((uint8_t *)src_bits) + src_stride * src_y + src_x * 4;
@ -924,8 +933,7 @@ pixman_format_code_t spice_surface_format_to_pixman(uint32_t surface_format)
case SPICE_SURFACE_FMT_32_ARGB:
return PIXMAN_a8r8g8b8;
default:
printf("Unknown surface format %d\n", surface_format);
abort();
g_error("Unknown surface format %d\n", surface_format);
break;
}
return (pixman_format_code_t)0; /* Not reached */
@ -958,10 +966,12 @@ pixman_format_code_t spice_bitmap_format_to_pixman(int bitmap_format,
case SPICE_BITMAP_FMT_RGBA:
return PIXMAN_a8r8g8b8;
case SPICE_BITMAP_FMT_8BIT_A:
return PIXMAN_a8;
case SPICE_BITMAP_FMT_INVALID:
default:
printf("Unknown bitmap format %d\n", bitmap_format);
abort();
g_error("Unknown bitmap format %d\n", bitmap_format);
return PIXMAN_a8r8g8b8;
}
}
@ -985,25 +995,13 @@ pixman_image_t *spice_bitmap_try_as_pixman(int src_format,
switch (src_format) {
case SPICE_BITMAP_FMT_32BIT:
#ifdef WORDS_BIGENDIAN
pixman_format = PIXMAN_b8g8r8x8;
#else
pixman_format = PIXMAN_x8r8g8b8;
#endif
pixman_format = PIXMAN_LE_x8r8g8b8;
break;
case SPICE_BITMAP_FMT_RGBA:
#ifdef WORDS_BIGENDIAN
pixman_format = PIXMAN_b8g8r8a8;
#else
pixman_format = PIXMAN_a8r8g8b8;
#endif
pixman_format = PIXMAN_LE_a8r8g8b8;
break;
case SPICE_BITMAP_FMT_24BIT:
#ifdef WORDS_BIGENDIAN
pixman_format = PIXMAN_b8g8r8;
#else
pixman_format = PIXMAN_r8g8b8;
#endif
pixman_format = PIXMAN_LE_r8g8b8;
break;
case SPICE_BITMAP_FMT_16BIT:
#ifdef WORDS_BIGENDIAN
@ -1037,26 +1035,6 @@ pixman_image_t *spice_bitmap_try_as_pixman(int src_format,
#define UINT32_FROM_LE(x) (x)
#endif
static INLINE uint32_t rgb_16_555_to_32(uint16_t color)
{
uint32_t 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 uint16_t rgb_32_to_16_555(uint32_t color)
{
return
(((color) >> 3) & 0x001f) |
(((color) >> 6) & 0x03e0) |
(((color) >> 9) & 0x7c00);
}
static void bitmap_32_to_32(uint8_t* dest, int dest_stride,
uint8_t* src, int src_stride,
int width, uint8_t* end)
@ -1078,6 +1056,15 @@ static void bitmap_32_to_32(uint8_t* dest, int dest_stride,
#endif
}
static void bitmap_8_to_8(uint8_t* dest, int dest_stride,
uint8_t* src, int src_stride,
int width, uint8_t* end)
{
for (; src != end; src += src_stride, dest += dest_stride) {
memcpy(dest, src, width);
}
}
static void bitmap_24_to_32(uint8_t* dest, int dest_stride,
uint8_t* src, int src_stride,
int width, uint8_t* end)
@ -1131,7 +1118,7 @@ static void bitmap_8_32_to_32(uint8_t *dest, int dest_stride,
#endif
if (!palette) {
PANIC("No palette");
spice_error("No palette");
return;
}
@ -1177,7 +1164,7 @@ static void bitmap_8_16_to_16_555(uint8_t *dest, int dest_stride,
#endif
if (!palette) {
PANIC("No palette");
spice_error("No palette");
return;
}
@ -1223,7 +1210,7 @@ static void bitmap_4be_32_to_32(uint8_t* dest, int dest_stride,
#endif
if (!palette) {
PANIC("No palette");
spice_error("No palette");
return;
}
@ -1273,7 +1260,7 @@ static void bitmap_4be_16_to_16_555(uint8_t* dest, int dest_stride,
#endif
if (!palette) {
PANIC("No palette");
spice_error("No palette");
return;
}
@ -1310,7 +1297,7 @@ static void bitmap_4be_16_to_16_555(uint8_t* dest, int dest_stride,
}
}
static INLINE int test_bit_be(void* addr, int bit)
static inline int test_bit_be(void* addr, int bit)
{
return !!(((uint8_t*)addr)[bit >> 3] & (0x80 >> (bit & 0x07)));
}
@ -1323,7 +1310,7 @@ static void bitmap_1be_32_to_32(uint8_t* dest, int dest_stride,
uint32_t fore_color;
uint32_t back_color;
ASSERT(palette != NULL);
spice_assert(palette != NULL);
if (!palette) {
return;
@ -1355,7 +1342,7 @@ static void bitmap_1be_16_to_16_555(uint8_t* dest, int dest_stride,
uint16_t fore_color;
uint16_t back_color;
ASSERT(palette != NULL);
spice_assert(palette != NULL);
if (!palette) {
return;
@ -1380,6 +1367,25 @@ static void bitmap_1be_16_to_16_555(uint8_t* dest, int dest_stride,
#ifdef NOT_USED_ATM
static inline uint32_t rgb_16_555_to_32(uint16_t color)
{
uint32_t 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 uint16_t rgb_32_to_16_555(uint32_t color)
{
return
(((color) >> 3) & 0x001f) |
(((color) >> 6) & 0x03e0) |
(((color) >> 9) & 0x7c00);
}
static void bitmap_16_to_32(uint8_t* dest, int dest_stride,
uint8_t* src, int src_stride,
int width, uint8_t* end)
@ -1461,7 +1467,7 @@ pixman_image_t *spice_bitmap_to_pixman(pixman_image_t *dest_image,
dest = (uint8_t *)pixman_image_get_data(dest_image);
dest_stride = pixman_image_get_stride(dest_image);
if (!(flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
ASSERT(height > 0);
spice_assert(height > 0);
dest += dest_stride * (height - 1);
dest_stride = -dest_stride;
}
@ -1472,6 +1478,9 @@ pixman_image_t *spice_bitmap_to_pixman(pixman_image_t *dest_image,
case SPICE_BITMAP_FMT_RGBA:
bitmap_32_to_32(dest, dest_stride, src, src_stride, width, end);
break;
case SPICE_BITMAP_FMT_8BIT_A:
bitmap_8_to_8(dest, dest_stride, src, src_stride, width, end);
break;
case SPICE_BITMAP_FMT_24BIT:
bitmap_24_to_32(dest, dest_stride, src, src_stride, width, end);
break;
@ -1485,7 +1494,7 @@ pixman_image_t *spice_bitmap_to_pixman(pixman_image_t *dest_image,
} else if (palette_surface_format == SPICE_SURFACE_FMT_16_555) {
bitmap_8_16_to_16_555(dest, dest_stride, src, src_stride, width, end, palette);
} else {
PANIC("Unsupported palette format");
spice_error("Unsupported palette format");
}
break;
case SPICE_BITMAP_FMT_4BIT_BE:
@ -1495,7 +1504,7 @@ pixman_image_t *spice_bitmap_to_pixman(pixman_image_t *dest_image,
} else if (palette_surface_format == SPICE_SURFACE_FMT_16_555) {
bitmap_4be_16_to_16_555(dest, dest_stride, src, src_stride, width, end, palette);
} else {
PANIC("Unsupported palette format");
spice_error("Unsupported palette format");
}
break;
case SPICE_BITMAP_FMT_1BIT_BE:
@ -1505,11 +1514,11 @@ pixman_image_t *spice_bitmap_to_pixman(pixman_image_t *dest_image,
} else if (palette_surface_format == SPICE_SURFACE_FMT_16_555) {
bitmap_1be_16_to_16_555(dest, dest_stride, src, src_stride, width, end, palette);
} else {
PANIC("Unsupported palette format");
spice_error("Unsupported palette format");
}
break;
default:
PANIC("Unsupported bitmap format");
spice_error("Unsupported bitmap format");
break;
}

View File

@ -16,20 +16,29 @@
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _H__PIXMAN_UTILS
#define _H__PIXMAN_UTILS
#ifndef H_SPICE_COMMON_PIXMAN_UTILS
#define H_SPICE_COMMON_PIXMAN_UTILS
#include <spice/types.h>
#include <spice/macros.h>
#include <stdlib.h>
#define PIXMAN_DONT_DEFINE_STDINT
#include <pixman.h>
#include "draw.h"
#ifdef __cplusplus
extern "C" {
#ifdef WORDS_BIGENDIAN
# define PIXMAN_LE_x8r8g8b8 PIXMAN_b8g8r8x8
# define PIXMAN_LE_a8r8g8b8 PIXMAN_b8g8r8a8
# define PIXMAN_LE_r8g8b8 PIXMAN_b8g8r8
#else
# define PIXMAN_LE_x8r8g8b8 PIXMAN_x8r8g8b8
# define PIXMAN_LE_a8r8g8b8 PIXMAN_a8r8g8b8
# define PIXMAN_LE_r8g8b8 PIXMAN_r8g8b8
#endif
SPICE_BEGIN_DECLS
/* This lists all possible 2 argument binary raster ops.
* This enum has the same values as the X11 GXcopy type
* and same as the GL constants (GL_AND etc) if you
@ -129,8 +138,6 @@ void spice_pixman_copy_rect(pixman_image_t *image,
int w, int h,
int dest_x, int dest_y);
#ifdef __cplusplus
}
#endif
SPICE_END_DECLS
#endif /* _H__PIXMAN_UTILS */
#endif // H_SPICE_COMMON_PIXMAN_UTILS

File diff suppressed because it is too large Load Diff

View File

@ -16,14 +16,13 @@
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __QUIC_H
#define __QUIC_H
#ifndef H_SPICE_COMMON_QUIC
#define H_SPICE_COMMON_QUIC
#include "quic_config.h"
#include <spice/macros.h>
#include "macros.h"
#ifdef __cplusplus
extern "C" {
#endif
SPICE_BEGIN_DECLS
typedef enum {
QUIC_IMAGE_TYPE_INVALID,
@ -41,9 +40,10 @@ 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, ...);
SPICE_GNUC_NORETURN
SPICE_GNUC_PRINTF(2, 3) void (*error)(QuicUsrContext *usr, const char *fmt, ...);
SPICE_GNUC_PRINTF(2, 3) void (*warn)(QuicUsrContext *usr, const char *fmt, ...);
SPICE_GNUC_PRINTF(2, 3) 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);
@ -63,10 +63,6 @@ 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(void);
#ifdef __cplusplus
}
#endif
SPICE_END_DECLS
#endif

View File

@ -16,15 +16,13 @@
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __QUIC_CONFIG_H
#define __QUIC_CONFIG_H
#ifndef H_SPICE_COMMON_QUIC_CONFIG
#define H_SPICE_COMMON_QUIC_CONFIG
#include <spice/types.h>
#include <spice/macros.h>
#ifdef __cplusplus
extern "C" {
#endif
SPICE_BEGIN_DECLS
#ifdef __GNUC__
#include <string.h>
@ -41,8 +39,6 @@ extern "C" {
#endif // QXLDD
#endif //__GNUC__
#ifdef __cplusplus
}
#endif
SPICE_END_DECLS
#endif

View File

@ -15,9 +15,7 @@
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef QUIC_FAMILY_8BPC
#undef QUIC_FAMILY_8BPC
@ -34,26 +32,19 @@
#define BPC 5
#endif
static unsigned int FNAME(golomb_code_len)(const BYTE n, const unsigned int l)
static inline unsigned int FNAME(golomb_code)(const BYTE n, const unsigned int l)
{
if (n < VNAME(family).nGRcodewords[l]) {
return (n >> l) + 1 + l;
} else {
return VNAME(family).notGRcwlen[l];
}
return VNAME(family).golomb_code[n][l];
}
static void FNAME(golomb_coding)(const BYTE n, const unsigned int l, unsigned int * const codeword,
unsigned int * const codewordlen)
static inline unsigned int FNAME(golomb_code_len)(const BYTE n, const unsigned int l)
{
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];
}
return VNAME(family).golomb_code_len[n][l];
}
static void FNAME(golomb_coding)(Encoder *encoder, const BYTE n, const unsigned int l)
{
encode(encoder, FNAME(golomb_code)(n, l), FNAME(golomb_code_len)(n, l));
}
static unsigned int FNAME(golomb_decoding)(const unsigned int l, const unsigned int bits,
@ -74,13 +65,16 @@ static unsigned int FNAME(golomb_decoding)(const unsigned int l, const unsigned
/* update the bucket using just encoded curval */
static void FNAME(update_model)(CommonState *state, s_bucket * const bucket,
const BYTE curval, unsigned int bpp)
const BYTE curval)
{
SPICE_VERIFY(BPC >= 1);
spice_return_if_fail (bucket != NULL);
const unsigned int bpp = BPC;
COUNTER * const pcounters = bucket->pcounters;
unsigned int i;
unsigned int bestcode;
unsigned int bestcodelen;
//unsigned int bpp = encoder->bpp;
/* update counters, find minimum */
@ -107,9 +101,14 @@ static void FNAME(update_model)(CommonState *state, s_bucket * const bucket,
static s_bucket *FNAME(find_bucket)(Channel *channel, const unsigned int val)
{
ASSERT(channel->encoder->usr, val < (0x1U << BPC));
spice_extra_assert(val < (0x1U << BPC));
return channel->_buckets_ptrs[val];
/* The and (&) here is to avoid buffer overflows in case of garbage or malicious
* attempts. Is much faster then using comparisons and save us from such situations.
* Note that on normal build the check above won't be compiled as this code path
* is pretty hot and would cause speed regressions.
*/
return channel->_buckets_ptrs[val & ((1U << BPC) - 1)];
}
#undef FNAME

662
common/quic_tmpl.c Normal file
View File

@ -0,0 +1,662 @@
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2009 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#define COMPRESS_IMP
#if defined(ONE_BYTE) || defined(FOUR_BYTE)
# define FARGS_DECL(...) (Encoder *encoder, Channel *channel_a, __VA_ARGS__)
# define FARGS_CALL(...) (encoder, channel_a, __VA_ARGS__)
# define UNCOMPRESS_PIX_START(row) do { } while (0)
# define SET_a(pix, val) ((pix)->a = val)
# define GET_a(pix) ((pix)->a)
# define SAME_PIXEL(p1, p2) (GET_a(p1) == GET_a(p2))
# define COPY_PIXEL(dest, src) \
SET_a(dest, GET_a(src));
# define DECLARE_STATE_VARIABLES \
CommonState *state = &channel_a->state
# define DECLARE_CHANNEL_VARIABLES \
BYTE * const correlate_row_a = channel_a->correlate_row
# define APPLY_ALL_COMP(macro, ...) \
macro(a, ## __VA_ARGS__)
#else
# define FARGS_DECL(...) (Encoder *encoder, __VA_ARGS__)
# define FARGS_CALL(...) (encoder, __VA_ARGS__)
# define SAME_PIXEL(p1, p2) \
(GET_r(p1) == GET_r(p2) && GET_g(p1) == GET_g(p2) && \
GET_b(p1) == GET_b(p2))
# define COPY_PIXEL(dest, src) \
SET_r(dest, GET_r(src)); \
SET_g(dest, GET_g(src)); \
SET_b(dest, GET_b(src))
# define DECLARE_STATE_VARIABLES \
CommonState *state = &encoder->rgb_state
# define DECLARE_CHANNEL_VARIABLES \
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
# define APPLY_ALL_COMP(macro, ...) \
macro(r, ## __VA_ARGS__); \
macro(g, ## __VA_ARGS__); \
macro(b, ## __VA_ARGS__)
#endif
#ifdef ONE_BYTE
#undef ONE_BYTE
#define FNAME(name) quic_one_##name
#define PIXEL one_byte_t
#define BPC 8
#endif
#ifdef FOUR_BYTE
#undef FOUR_BYTE
#define FNAME(name) quic_four_##name
#define PIXEL four_bytes_t
#define BPC 8
#endif
#ifdef QUIC_RGB32
#undef QUIC_RGB32
#define PIXEL rgb32_pixel_t
#define FNAME(name) quic_rgb32_##name
#define BPC 8
#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 BPC 8
#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 BPC 5
#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 BPC 5
#undef COMPRESS_IMP
#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 FNAME_DECL(name) FNAME(name) FARGS_DECL
#define FNAME_CALL(name) FNAME(name) FARGS_CALL
#if BPC == 5
# 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_MASK 0x1fU
#elif BPC == 8
# 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_MASK 0xffU
#else
# error BPC must be 5 or 8
#endif
#define _PIXEL_A(channel, curr) ((unsigned int)GET_##channel((curr) - 1))
#define _PIXEL_B(channel, prev) ((unsigned int)GET_##channel(prev))
/* a */
#define DECORRELATE_0(channel, curr, bpc_mask)\
family.xlatU2L[(unsigned)((int)GET_##channel(curr) - (int)_PIXEL_A(channel, curr)) & bpc_mask]
#define CORRELATE_0(channel, curr, correlate, bpc_mask)\
((family.xlatL2U[correlate] + _PIXEL_A(channel, curr)) & bpc_mask)
/* (a+b)/2 */
#define DECORRELATE(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 CORRELATE(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))
#define COMPRESS_ONE_ROW0_0(channel) \
correlate_row_##channel[0] = family.xlatU2L[GET_##channel(cur_row)];\
golomb_coding(encoder, correlate_row_##channel[0], \
find_bucket(channel_##channel, \
correlate_row_##channel[-1])->bestcode)
#define COMPRESS_ONE_ROW0(channel, index) \
correlate_row_##channel[index] = DECORRELATE_0(channel, &cur_row[index], bpc_mask); \
golomb_coding(encoder, correlate_row_##channel[index], \
find_bucket(channel_##channel, \
correlate_row_##channel[index - 1])->bestcode)
#define UPDATE_MODEL_COMP(channel, index) \
update_model(state, find_bucket(channel_##channel, correlate_row_##channel[index - 1]), \
correlate_row_##channel[index])
#define UPDATE_MODEL(index) APPLY_ALL_COMP(UPDATE_MODEL_COMP, index)
#define RLE_PRED_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; \
} \
}
#ifdef COMPRESS_IMP
static void FNAME_DECL(compress_row0_seg)(int i,
const PIXEL * const cur_row,
const int end,
const unsigned int waitmask,
const unsigned int bpc_mask)
{
DECLARE_STATE_VARIABLES;
DECLARE_CHANNEL_VARIABLES;
int stopidx;
spice_assert(end - i > 0);
if (i == 0) {
APPLY_ALL_COMP(COMPRESS_ONE_ROW0_0);
if (state->waitcnt) {
state->waitcnt--;
} else {
state->waitcnt = (tabrand(&state->tabrand_seed) & waitmask);
UPDATE_MODEL(0);
}
stopidx = ++i + state->waitcnt;
} else {
stopidx = i + state->waitcnt;
}
while (stopidx < end) {
for (; i <= stopidx; i++) {
APPLY_ALL_COMP(COMPRESS_ONE_ROW0, i);
}
UPDATE_MODEL(stopidx);
stopidx = i + (tabrand(&state->tabrand_seed) & waitmask);
}
for (; i < end; i++) {
APPLY_ALL_COMP(COMPRESS_ONE_ROW0, i);
}
state->waitcnt = stopidx - end;
}
#undef COMPRESS_ONE_ROW0_0
#undef COMPRESS_ONE_ROW0
static void FNAME_DECL(compress_row0)(const PIXEL *cur_row, unsigned int width)
{
DECLARE_STATE_VARIABLES;
const unsigned int bpc_mask = BPC_MASK;
int pos = 0;
while ((DEFwmimax > (int)state->wmidx) && (state->wmileft <= width)) {
if (state->wmileft) {
FNAME_CALL(compress_row0_seg)(pos, cur_row, pos + state->wmileft,
bppmask[state->wmidx], bpc_mask);
width -= state->wmileft;
pos += state->wmileft;
}
state->wmidx++;
set_wm_trigger(state);
state->wmileft = DEFwminext;
}
if (width) {
FNAME_CALL(compress_row0_seg)(pos, cur_row, pos + width,
bppmask[state->wmidx], bpc_mask);
if (DEFwmimax > (int)state->wmidx) {
state->wmileft -= width;
}
}
spice_assert((int)state->wmidx <= DEFwmimax);
spice_assert(state->wmidx <= 32);
spice_assert(DEFwminext > 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(encoder, correlate_row_##channel[0], \
find_bucket(channel_##channel, correlate_row_##channel[-1])->bestcode)
#define COMPRESS_ONE(channel, index) \
DECORRELATE(channel, &prev_row[index], &cur_row[index],bpc_mask, correlate_row_##channel[index]); \
golomb_coding(encoder, correlate_row_##channel[index], \
find_bucket(channel_##channel, correlate_row_##channel[index - 1])->bestcode)
static void FNAME_DECL(compress_row_seg)(int i,
const PIXEL * const prev_row,
const PIXEL * const cur_row,
const int end,
const unsigned int waitmask,
const unsigned int bpc_mask)
{
DECLARE_STATE_VARIABLES;
DECLARE_CHANNEL_VARIABLES;
int stopidx;
int run_index = 0;
int run_size;
spice_assert(end - i > 0);
if (i == 0) {
APPLY_ALL_COMP(COMPRESS_ONE_0);
if (state->waitcnt) {
state->waitcnt--;
} else {
state->waitcnt = (tabrand(&state->tabrand_seed) & waitmask);
UPDATE_MODEL(0);
}
stopidx = ++i + state->waitcnt;
} else {
stopidx = i + state->waitcnt;
}
for (;;) {
while (stopidx < end) {
for (; i <= stopidx; i++) {
RLE_PRED_IMP;
APPLY_ALL_COMP(COMPRESS_ONE, i);
}
UPDATE_MODEL(stopidx);
stopidx = i + (tabrand(&state->tabrand_seed) & waitmask);
}
for (; i < end; i++) {
RLE_PRED_IMP;
APPLY_ALL_COMP(COMPRESS_ONE, i);
}
state->waitcnt = stopidx - end;
return;
do_run:
run_index = i;
state->waitcnt = stopidx - i;
run_size = 0;
while (SAME_PIXEL(&cur_row[i], &cur_row[i - 1])) {
run_size++;
if (++i == end) {
encode_state_run(encoder, state, run_size);
return;
}
}
encode_state_run(encoder, state, run_size);
stopidx = i + state->waitcnt;
}
}
static void FNAME_DECL(compress_row)(const PIXEL * const prev_row,
const PIXEL * const cur_row,
unsigned int width)
{
DECLARE_STATE_VARIABLES;
const unsigned int bpc_mask = BPC_MASK;
unsigned int pos = 0;
while ((DEFwmimax > (int)state->wmidx) && (state->wmileft <= width)) {
if (state->wmileft) {
FNAME_CALL(compress_row_seg)(pos, prev_row, cur_row,
pos + state->wmileft, bppmask[state->wmidx],
bpc_mask);
width -= state->wmileft;
pos += state->wmileft;
}
state->wmidx++;
set_wm_trigger(state);
state->wmileft = DEFwminext;
}
if (width) {
FNAME_CALL(compress_row_seg)(pos, prev_row, cur_row, pos + width,
bppmask[state->wmidx], bpc_mask);
if (DEFwmimax > (int)state->wmidx) {
state->wmileft -= width;
}
}
spice_assert((int)state->wmidx <= DEFwmimax);
spice_assert(state->wmidx <= 32);
spice_assert(DEFwminext > 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], CORRELATE_0(channel, &cur_row[i], correlate_row_##channel[i], \
bpc_mask)); \
decode_eatbits(encoder, codewordlen);
static void FNAME_DECL(uncompress_row0_seg)(int i,
PIXEL * const cur_row,
const int end,
const unsigned int waitmask,
const unsigned int bpc_mask)
{
DECLARE_STATE_VARIABLES;
DECLARE_CHANNEL_VARIABLES;
int stopidx;
spice_assert(end - i > 0);
if (i == 0) {
unsigned int codewordlen;
UNCOMPRESS_PIX_START(&cur_row[i]);
APPLY_ALL_COMP(UNCOMPRESS_ONE_ROW0_0);
if (state->waitcnt) {
--state->waitcnt;
} else {
state->waitcnt = (tabrand(&state->tabrand_seed) & waitmask);
UPDATE_MODEL(0);
}
stopidx = ++i + state->waitcnt;
} else {
stopidx = i + state->waitcnt;
}
while (stopidx < end) {
for (; i <= stopidx; i++) {
unsigned int codewordlen;
UNCOMPRESS_PIX_START(&cur_row[i]);
APPLY_ALL_COMP(UNCOMPRESS_ONE_ROW0);
}
UPDATE_MODEL(stopidx);
stopidx = i + (tabrand(&state->tabrand_seed) & waitmask);
}
for (; i < end; i++) {
unsigned int codewordlen;
UNCOMPRESS_PIX_START(&cur_row[i]);
APPLY_ALL_COMP(UNCOMPRESS_ONE_ROW0);
}
state->waitcnt = stopidx - end;
}
static void FNAME_DECL(uncompress_row0)(PIXEL * const cur_row,
unsigned int width)
{
DECLARE_STATE_VARIABLES;
const unsigned int bpc_mask = BPC_MASK;
unsigned int pos = 0;
while ((DEFwmimax > (int)state->wmidx) && (state->wmileft <= width)) {
if (state->wmileft) {
FNAME_CALL(uncompress_row0_seg)(pos, cur_row,
pos + state->wmileft,
bppmask[state->wmidx],
bpc_mask);
pos += state->wmileft;
width -= state->wmileft;
}
state->wmidx++;
set_wm_trigger(state);
state->wmileft = DEFwminext;
}
if (width) {
FNAME_CALL(uncompress_row0_seg)(pos, cur_row, pos + width,
bppmask[state->wmidx], bpc_mask);
if (DEFwmimax > (int)state->wmidx) {
state->wmileft -= width;
}
}
spice_assert((int)state->wmidx <= DEFwmimax);
spice_assert(state->wmidx <= 32);
spice_assert(DEFwminext > 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); \
CORRELATE(channel, &prev_row[i], &cur_row[i], correlate_row_##channel[i], bpc_mask, \
&cur_row[i]); \
decode_eatbits(encoder, codewordlen);
static void FNAME_DECL(uncompress_row_seg)(const PIXEL * const prev_row,
PIXEL * const cur_row,
int i,
const int end,
const unsigned int bpc_mask)
{
DECLARE_STATE_VARIABLES;
DECLARE_CHANNEL_VARIABLES;
const unsigned int waitmask = bppmask[state->wmidx];
int stopidx;
int run_index = 0;
int run_end;
spice_assert(end - i > 0);
if (i == 0) {
unsigned int codewordlen;
UNCOMPRESS_PIX_START(&cur_row[i]);
APPLY_ALL_COMP(UNCOMPRESS_ONE_0);
if (state->waitcnt) {
--state->waitcnt;
} else {
state->waitcnt = (tabrand(&state->tabrand_seed) & waitmask);
UPDATE_MODEL(0);
}
stopidx = ++i + state->waitcnt;
} else {
stopidx = i + state->waitcnt;
}
for (;;) {
while (stopidx < end) {
for (; i <= stopidx; i++) {
unsigned int codewordlen;
RLE_PRED_IMP;
UNCOMPRESS_PIX_START(&cur_row[i]);
APPLY_ALL_COMP(UNCOMPRESS_ONE);
}
UPDATE_MODEL(stopidx);
stopidx = i + (tabrand(&state->tabrand_seed) & waitmask);
}
for (; i < end; i++) {
unsigned int codewordlen;
RLE_PRED_IMP;
UNCOMPRESS_PIX_START(&cur_row[i]);
APPLY_ALL_COMP(UNCOMPRESS_ONE);
}
state->waitcnt = stopidx - end;
return;
do_run:
state->waitcnt = stopidx - i;
run_index = i;
run_end = decode_state_run(encoder, state);
if (run_end < 0 || run_end > (end - i)) {
encoder->usr->error(encoder->usr, "wrong RLE\n");
}
run_end += i;
for (; i < run_end; i++) {
UNCOMPRESS_PIX_START(&cur_row[i]);
COPY_PIXEL(&cur_row[i], &cur_row[i - 1]);
}
if (i == end) {
return;
}
stopidx = i + state->waitcnt;
}
}
static void FNAME_DECL(uncompress_row)(const PIXEL * const prev_row,
PIXEL * const cur_row,
unsigned int width)
{
DECLARE_STATE_VARIABLES;
const unsigned int bpc_mask = BPC_MASK;
unsigned int pos = 0;
while ((DEFwmimax > (int)state->wmidx) && (state->wmileft <= width)) {
if (state->wmileft) {
FNAME_CALL(uncompress_row_seg)(prev_row, cur_row, pos,
pos + state->wmileft, bpc_mask);
pos += state->wmileft;
width -= state->wmileft;
}
state->wmidx++;
set_wm_trigger(state);
state->wmileft = DEFwminext;
}
if (width) {
FNAME_CALL(uncompress_row_seg)(prev_row, cur_row, pos,
pos + width, bpc_mask);
if (DEFwmimax > (int)state->wmidx) {
state->wmileft -= width;
}
}
spice_assert((int)state->wmidx <= DEFwmimax);
spice_assert(state->wmidx <= 32);
spice_assert(DEFwminext > 0);
}
#undef PIXEL
#undef FARGS_CALL
#undef FARGS_DECL
#undef FNAME
#undef FNAME_CALL
#undef FNAME_DECL
#undef _PIXEL_A
#undef _PIXEL_B
#undef SAME_PIXEL
#undef RLE_PRED_IMP
#undef UPDATE_MODEL
#undef DECORRELATE_0
#undef DECORRELATE
#undef COMPRESS_ONE_0
#undef COMPRESS_ONE
#undef CORRELATE_0
#undef CORRELATE
#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 SET_a
#undef GET_a
#undef UNCOMPRESS_PIX_START
#undef UPDATE_MODEL_COMP
#undef APPLY_ALL_COMP
#undef DECLARE_STATE_VARIABLES
#undef DECLARE_CHANNEL_VARIABLES
#undef COPY_PIXEL

1
common/recorder Submodule

@ -0,0 +1 @@
Subproject commit d5cbf6caba60d580401c72d0cd18c316cf1be211

106
common/recorder.h Normal file
View File

@ -0,0 +1,106 @@
/*
Copyright (C) 2018 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
/* This file include recorder library headers or if disabled provide
* replacement declarations */
#ifdef ENABLE_RECORDER
#include <common/recorder/recorder.h>
#elif defined(ENABLE_AGENT_INTERFACE)
#include <common/agent_interface.h>
#else
#include <stdio.h>
#include <stdint.h>
#include <spice/macros.h>
/* Replacement declarations.
* There declarations should generate no code (beside when no optimization are
* selected) but catch some possible programming warnings/errors at
* compile/link time like:
* - usage of undeclared recorders;
* - recording formats and arguments;
* - matching RECORD_TIMING_BEGIN and RECORD_TIMING_END.
* The only exceptions are tweaks which just generate a variable to hold the
* value.
*/
typedef struct SpiceEmptyStruct {
char dummy[0];
} SpiceEmptyStruct;
typedef struct SpiceDummyTweak {
intptr_t tweak_value;
} SpiceDummyTweak;
#define RECORDER_DECLARE(rec) \
extern const SpiceEmptyStruct spice_recorder_ ## rec
#define RECORDER(rec, num_rings, comment) \
RECORDER_DEFINE(rec, num_rings, comment)
#define RECORDER_DEFINE(rec, num_rings, comment) \
const SpiceEmptyStruct SPICE_GNUC_UNUSED spice_recorder_ ## rec = {}
#define RECORDER_TRACE(rec) \
(sizeof(spice_recorder_ ## rec) != sizeof(SpiceEmptyStruct))
#define RECORDER_TWEAK_DECLARE(rec) \
extern const SpiceDummyTweak spice_recorder_tweak_ ## rec
#define RECORDER_TWEAK_DEFINE(rec, value, comment) \
const SpiceDummyTweak spice_recorder_tweak_ ## rec = { (value) }
#define RECORDER_TWEAK(rec) \
((spice_recorder_tweak_ ## rec).tweak_value)
#define RECORD(rec, ...) do { \
if (sizeof((spice_recorder_ ## rec).dummy)) printf(__VA_ARGS__); \
} while(0)
#define RECORD_TIMING_BEGIN(rec) \
do { RECORD(rec, "begin");
#define RECORD_TIMING_END(rec, op, name, value) \
RECORD(rec, "end" op name); \
} while(0)
#define record(...) \
RECORD(__VA_ARGS__)
static inline void
recorder_dump_on_common_signals(unsigned add, unsigned remove)
{
}
#endif
#if !defined(ENABLE_AGENT_INTERFACE)
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
/* Stubs for Agent-Interface specific definitions */
static inline void
agent_interface_start(unsigned int port)
{
}
typedef void (*forward_quality_cb_t)(void *, const char *);
static inline void
agent_interface_set_forward_quality_cb(forward_quality_cb_t cb, void *data)
{
}
typedef int (*on_connect_cb_t)(void *);
static inline void
agent_interface_set_on_connect_cb(on_connect_cb_t cb, void *data)
{
}
#ifdef __cplusplus
}
#endif // __cplusplus
#endif

View File

@ -16,17 +16,16 @@
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _H_RECT
#define _H_RECT
#ifndef H_SPICE_COMMON_RECT
#define H_SPICE_COMMON_RECT
#include "draw.h"
#include <spice/macros.h>
#include "draw.h"
#include "log.h"
#ifdef __cplusplus
extern "C" {
#endif
SPICE_BEGIN_DECLS
static INLINE void rect_sect(SpiceRect* r, const SpiceRect* bounds)
static inline void rect_sect(SpiceRect* r, const SpiceRect* bounds)
{
r->left = MAX(r->left, bounds->left);
r->right = MIN(r->right, bounds->right);
@ -37,7 +36,7 @@ static INLINE void rect_sect(SpiceRect* r, const SpiceRect* bounds)
r->bottom = MAX(r->top, r->bottom);
}
static INLINE void rect_offset(SpiceRect* r, int dx, int dy)
static inline void rect_offset(SpiceRect* r, int dx, int dy)
{
r->left += dx;
r->right += dx;
@ -45,24 +44,24 @@ static INLINE void rect_offset(SpiceRect* r, int dx, int dy)
r->bottom += dy;
}
static INLINE int rect_is_empty(const SpiceRect* r)
static inline int rect_is_empty(const SpiceRect* r)
{
return r->top == r->bottom || r->left == r->right;
}
static INLINE int rect_intersects(const SpiceRect* r1, const SpiceRect* r2)
static inline int rect_intersects(const SpiceRect* r1, const SpiceRect* r2)
{
return r1->left < r2->right && r1->right > r2->left &&
r1->top < r2->bottom && r1->bottom > r2->top;
}
static INLINE int rect_is_equal(const SpiceRect *r1, const SpiceRect *r2)
static inline int rect_is_equal(const SpiceRect *r1, const SpiceRect *r2)
{
return r1->top == r2->top && r1->left == r2->left &&
r1->bottom == r2->bottom && r1->right == r2->right;
}
static INLINE void rect_union(SpiceRect *dest, const SpiceRect *r)
static inline void rect_union(SpiceRect *dest, const SpiceRect *r)
{
dest->top = MIN(dest->top, r->top);
dest->left = MIN(dest->left, r->left);
@ -70,15 +69,29 @@ static INLINE void rect_union(SpiceRect *dest, const SpiceRect *r)
dest->right = MAX(dest->right, r->right);
}
static INLINE int rect_is_same_size(const SpiceRect *r1, const SpiceRect *r2)
static inline int rect_is_same_size(const SpiceRect *r1, const SpiceRect *r2)
{
return r1->right - r1->left == r2->right - r2->left &&
r1->bottom - r1->top == r2->bottom - r2->top;
}
#ifdef __cplusplus
static inline int rect_contains(const SpiceRect *big_rect, const SpiceRect *small_rect)
{
return big_rect->left <= small_rect->left && big_rect->right >= small_rect->right &&
big_rect->top <= small_rect->top && big_rect->bottom >= small_rect->bottom;
}
#endif
static inline int rect_get_area(const SpiceRect *r)
{
return (r->right - r->left) * (r->bottom - r->top);
}
static inline void rect_debug(const SpiceRect *r)
{
spice_debug("(%d, %d) (%d, %d)", r->left, r->top, r->right, r->bottom);
}
SPICE_END_DECLS
#ifdef __cplusplus
@ -117,6 +130,21 @@ static inline int rect_is_same_size(const SpiceRect& r1, const SpiceRect& r2)
return rect_is_same_size(&r1, &r2);
}
#endif
static inline int rect_contains(const SpiceRect& big_rect, const SpiceRect& small_rect)
{
return rect_contains(&big_rect, &small_rect);
}
static inline int rect_get_area(const SpiceRect& r)
{
return rect_get_area(&r);
}
static inline void rect_debug(const SpiceRect &r)
{
rect_debug(&r);
}
#endif /* __cplusplus */
#endif

View File

@ -15,9 +15,7 @@
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <string.h>
@ -386,6 +384,17 @@ void region_ret_rects(const QRegion *rgn, SpiceRect *rects, uint32_t num_rects)
}
}
void region_extents(const QRegion *rgn, SpiceRect *r)
{
pixman_box32_t *extents;
extents = pixman_region32_extents((pixman_region32_t *)rgn);
r->left = extents->x1;
r->top = extents->y1;
r->right = extents->x2;
r->bottom = extents->y2;
}
int region_is_equal(const QRegion *rgn1, const QRegion *rgn2)
{
@ -409,7 +418,7 @@ int region_bounds_intersects(const QRegion *rgn1, const QRegion *rgn2)
pixman_box32_t *extents1, *extents2;
extents1 = pixman_region32_extents((pixman_region32_t *)rgn1);
extents2 = pixman_region32_extents((pixman_region32_t *)rgn1);
extents2 = pixman_region32_extents((pixman_region32_t *)rgn2);
return EXTENTCHECK(extents1, extents2);
}
@ -441,6 +450,7 @@ void region_xor(QRegion *rgn, const QRegion *other_rgn)
{
pixman_region32_t intersection;
pixman_region32_init(&intersection);
pixman_region32_copy(&intersection, rgn);
pixman_region32_intersect(&intersection,
&intersection,
@ -510,381 +520,3 @@ void region_dump(const QRegion *rgn, const char *prefix)
rects[i].y2);
}
}
#ifdef REGION_TEST
static int slow_region_test(const QRegion *rgn, const QRegion *other_rgn, int query)
{
pixman_region32_t intersection;
int res;
pixman_region32_init(&intersection);
pixman_region32_intersect(&intersection,
(pixman_region32_t *)rgn,
(pixman_region32_t *)other_rgn);
res = 0;
if (query & REGION_TEST_SHARED &&
pixman_region32_not_empty(&intersection)) {
res |= REGION_TEST_SHARED;
}
if (query & REGION_TEST_LEFT_EXCLUSIVE &&
!pixman_region32_equal(&intersection, (pixman_region32_t *)rgn)) {
res |= REGION_TEST_LEFT_EXCLUSIVE;
}
if (query & REGION_TEST_RIGHT_EXCLUSIVE &&
!pixman_region32_equal(&intersection, (pixman_region32_t *)other_rgn)) {
res |= REGION_TEST_RIGHT_EXCLUSIVE;
}
pixman_region32_fini(&intersection);
return res;
}
static int rect_is_valid(const SpiceRect *r)
{
if (r->top > r->bottom || r->left > r->right) {
printf("%s: invalid rect\n", __FUNCTION__);
return FALSE;
}
return TRUE;
}
static void rect_set(SpiceRect *r, int32_t top, int32_t left, int32_t bottom, int32_t right)
{
r->top = top;
r->left = left;
r->bottom = bottom;
r->right = right;
ASSERT(rect_is_valid(r));
}
static void random_region(QRegion *reg)
{
int i;
int num_rects;
int x, y, w, h;
SpiceRect _r;
SpiceRect *r = &_r;
region_clear(reg);
num_rects = rand() % 20;
for (i = 0; i < num_rects; i++) {
x = rand()%100;
y = rand()%100;
w = rand()%100;
h = rand()%100;
rect_set(r,
x, y,
x+w, y+h);
region_add(reg, r);
}
}
static void test(const QRegion *r1, const QRegion *r2, int *expected)
{
printf("r1 is_empty %s [%s]\n",
region_is_empty(r1) ? "TRUE" : "FALSE",
(region_is_empty(r1) == *(expected++)) ? "OK" : "ERR");
printf("r2 is_empty %s [%s]\n",
region_is_empty(r2) ? "TRUE" : "FALSE",
(region_is_empty(r2) == *(expected++)) ? "OK" : "ERR");
printf("is_equal %s [%s]\n",
region_is_equal(r1, r2) ? "TRUE" : "FALSE",
(region_is_equal(r1, r2) == *(expected++)) ? "OK" : "ERR");
printf("intersects %s [%s]\n",
region_intersects(r1, r2) ? "TRUE" : "FALSE",
(region_intersects(r1, r2) == *(expected++)) ? "OK" : "ERR");
printf("contains %s [%s]\n",
region_contains(r1, r2) ? "TRUE" : "FALSE",
(region_contains(r1, r2) == *(expected++)) ? "OK" : "ERR");
}
enum {
EXPECT_R1_EMPTY,
EXPECT_R2_EMPTY,
EXPECT_EQUAL,
EXPECT_SECT,
EXPECT_CONT,
};
int main(void)
{
QRegion _r1, _r2, _r3;
QRegion *r1 = &_r1;
QRegion *r2 = &_r2;
QRegion *r3 = &_r3;
SpiceRect _r;
SpiceRect *r = &_r;
int expected[5];
int i, j;
region_init(r1);
region_init(r2);
printf("dump r1 empty rgn [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
region_dump(r1, "");
expected[EXPECT_R1_EMPTY] = TRUE;
expected[EXPECT_R2_EMPTY] = TRUE;
expected[EXPECT_EQUAL] = TRUE;
expected[EXPECT_SECT] = FALSE;
expected[EXPECT_CONT] = TRUE;
test(r1, r2, expected);
printf("\n");
region_clone(r3, r1);
printf("dump r3 clone rgn [%s]\n", region_is_valid(r3) ? "VALID" : "INVALID");
region_dump(r3, "");
expected[EXPECT_R1_EMPTY] = TRUE;
expected[EXPECT_R2_EMPTY] = TRUE;
expected[EXPECT_EQUAL] = TRUE;
expected[EXPECT_SECT] = FALSE;
expected[EXPECT_CONT] = TRUE;
test(r1, r3, expected);
region_destroy(r3);
printf("\n");
rect_set(r, 0, 0, 100, 100);
region_add(r1, r);
printf("dump r1 [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
region_dump(r1, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = TRUE;
expected[EXPECT_EQUAL] = FALSE;
expected[EXPECT_SECT] = FALSE;
expected[EXPECT_CONT] = TRUE;
test(r1, r2, expected);
printf("\n");
region_clear(r1);
rect_set(r, 0, 0, 0, 0);
region_add(r1, r);
printf("dump r1 [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
region_dump(r1, "");
expected[EXPECT_R1_EMPTY] = TRUE;
expected[EXPECT_R2_EMPTY] = TRUE;
expected[EXPECT_EQUAL] = TRUE;
expected[EXPECT_SECT] = FALSE;
expected[EXPECT_CONT] = TRUE;
test(r1, r2, expected);
printf("\n");
rect_set(r, -100, -100, 0, 0);
region_add(r1, r);
printf("dump r1 [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
region_dump(r1, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = TRUE;
expected[EXPECT_EQUAL] = FALSE;
expected[EXPECT_SECT] = FALSE;
expected[EXPECT_CONT] = TRUE;
test(r1, r2, expected);
printf("\n");
region_clear(r1);
rect_set(r, -100, -100, 100, 100);
region_add(r1, r);
printf("dump r1 [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
region_dump(r1, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = TRUE;
expected[EXPECT_EQUAL] = FALSE;
expected[EXPECT_SECT] = FALSE;
expected[EXPECT_CONT] = TRUE;
test(r1, r2, expected);
printf("\n");
region_clear(r1);
region_clear(r2);
rect_set(r, 100, 100, 200, 200);
region_add(r1, r);
printf("dump r1 [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
region_dump(r1, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = TRUE;
expected[EXPECT_EQUAL] = FALSE;
expected[EXPECT_SECT] = FALSE;
expected[EXPECT_CONT] = TRUE;
test(r1, r2, expected);
printf("\n");
rect_set(r, 300, 300, 400, 400);
region_add(r1, r);
printf("dump r1 [%s]\n", region_is_valid(r1) ? "VALID" : "INVALID");
region_dump(r1, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = TRUE;
expected[EXPECT_EQUAL] = FALSE;
expected[EXPECT_SECT] = FALSE;
expected[EXPECT_CONT] = TRUE;
test(r1, r2, expected);
printf("\n");
rect_set(r, 500, 500, 600, 600);
region_add(r2, r);
printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
region_dump(r2, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = FALSE;
expected[EXPECT_EQUAL] = FALSE;
expected[EXPECT_SECT] = FALSE;
expected[EXPECT_CONT] = FALSE;
test(r1, r2, expected);
printf("\n");
region_clear(r2);
rect_set(r, 100, 100, 200, 200);
region_add(r2, r);
rect_set(r, 300, 300, 400, 400);
region_add(r2, r);
printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
region_dump(r2, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = FALSE;
expected[EXPECT_EQUAL] = TRUE;
expected[EXPECT_SECT] = TRUE;
expected[EXPECT_CONT] = TRUE;
test(r1, r2, expected);
printf("\n");
region_clear(r2);
rect_set(r, 100, 100, 200, 200);
region_add(r2, r);
printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
region_dump(r2, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = FALSE;
expected[EXPECT_EQUAL] = FALSE;
expected[EXPECT_SECT] = TRUE;
expected[EXPECT_CONT] = TRUE;
test(r1, r2, expected);
printf("\n");
region_clear(r2);
rect_set(r, -2000, -2000, -1000, -1000);
region_add(r2, r);
printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
region_dump(r2, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = FALSE;
expected[EXPECT_EQUAL] = FALSE;
expected[EXPECT_SECT] = FALSE;
expected[EXPECT_CONT] = FALSE;
test(r1, r2, expected);
printf("\n");
region_clear(r2);
rect_set(r, -2000, -2000, 1000, 1000);
region_add(r2, r);
printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
region_dump(r2, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = FALSE;
expected[EXPECT_EQUAL] = FALSE;
expected[EXPECT_SECT] = TRUE;
expected[EXPECT_CONT] = FALSE;
test(r1, r2, expected);
printf("\n");
region_clear(r2);
rect_set(r, 150, 150, 175, 175);
region_add(r2, r);
printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
region_dump(r2, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = FALSE;
expected[EXPECT_EQUAL] = FALSE;
expected[EXPECT_SECT] = TRUE;
expected[EXPECT_CONT] = TRUE;
test(r1, r2, expected);
printf("\n");
region_clear(r2);
rect_set(r, 150, 150, 350, 350);
region_add(r2, r);
printf("dump r2 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
region_dump(r2, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = FALSE;
expected[EXPECT_EQUAL] = FALSE;
expected[EXPECT_SECT] = TRUE;
expected[EXPECT_CONT] = FALSE;
test(r1, r2, expected);
printf("\n");
region_and(r2, r1);
printf("dump r2 and r1 [%s]\n", region_is_valid(r2) ? "VALID" : "INVALID");
region_dump(r2, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = FALSE;
expected[EXPECT_EQUAL] = FALSE;
expected[EXPECT_SECT] = TRUE;
expected[EXPECT_CONT] = FALSE;
test(r2, r1, expected);
printf("\n");
region_clone(r3, r1);
printf("dump r3 clone rgn [%s]\n", region_is_valid(r3) ? "VALID" : "INVALID");
region_dump(r3, "");
expected[EXPECT_R1_EMPTY] = FALSE;
expected[EXPECT_R2_EMPTY] = FALSE;
expected[EXPECT_EQUAL] = TRUE;
expected[EXPECT_SECT] = TRUE;
expected[EXPECT_CONT] = TRUE;
test(r1, r3, expected);
printf("\n");
j = 0;
for (i = 0; i < 1000000; i++) {
int res1, res2, test;
int tests[] = {
REGION_TEST_LEFT_EXCLUSIVE,
REGION_TEST_RIGHT_EXCLUSIVE,
REGION_TEST_SHARED,
REGION_TEST_LEFT_EXCLUSIVE | REGION_TEST_RIGHT_EXCLUSIVE,
REGION_TEST_LEFT_EXCLUSIVE | REGION_TEST_SHARED,
REGION_TEST_RIGHT_EXCLUSIVE | REGION_TEST_SHARED,
REGION_TEST_LEFT_EXCLUSIVE | REGION_TEST_RIGHT_EXCLUSIVE | REGION_TEST_SHARED
};
random_region(r1);
random_region(r2);
for (test = 0; test < 7; test++) {
res1 = region_test(r1, r2, tests[test]);
res2 = slow_region_test(r1, r2, tests[test]);
if (res1 != res2) {
printf ("Error in region_test %d, got %d, expected %d, query=%d\n",
j, res1, res2, tests[test]);
printf ("r1:\n");
region_dump(r1, "");
printf ("r2:\n");
region_dump(r2, "");
}
j++;
}
}
region_destroy(r3);
region_destroy(r1);
region_destroy(r2);
return 0;
}
#endif

View File

@ -16,21 +16,24 @@
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _H_REGION
#define _H_REGION
#ifndef H_SPICE_COMMON_REGION
#define H_SPICE_COMMON_REGION
#include <stdint.h>
#include "draw.h"
#include <pixman_utils.h>
#include <spice/macros.h>
#ifdef __cplusplus
extern "C" {
#endif
#include "draw.h"
#include "pixman_utils.h"
SPICE_BEGIN_DECLS
typedef pixman_region32_t QRegion;
/* the left region is not contained entirely within the right region */
#define REGION_TEST_LEFT_EXCLUSIVE (1 << 0)
/* the right region is not contained entirely within the left region */
#define REGION_TEST_RIGHT_EXCLUSIVE (1 << 1)
/* the regions overlap */
#define REGION_TEST_SHARED (1 << 2)
#define REGION_TEST_ALL \
(REGION_TEST_LEFT_EXCLUSIVE | REGION_TEST_RIGHT_EXCLUSIVE | REGION_TEST_SHARED)
@ -41,6 +44,7 @@ void region_destroy(QRegion *rgn);
void region_clone(QRegion *dest, const QRegion *src);
SpiceRect *region_dup_rects(const QRegion *rgn, uint32_t *num_rects);
void region_ret_rects(const QRegion *rgn, SpiceRect *rects, uint32_t num_rects);
void region_extents(const QRegion *rgn, SpiceRect *r);
int region_test(const QRegion *rgn, const QRegion *other_rgn, int query);
int region_is_valid(const QRegion *rgn);
@ -61,10 +65,9 @@ void region_remove(QRegion *rgn, const SpiceRect *r);
void region_offset(QRegion *rgn, int32_t dx, int32_t dy);
void region_dump(const QRegion *rgn, const char *prefix);
#ifdef __cplusplus
}
#endif
SPICE_END_DECLS
#endif

View File

@ -16,14 +16,12 @@
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _H_RING2
#define _H_RING2
#ifndef H_SPICE_COMMON_RING
#define H_SPICE_COMMON_RING
#include "spice_common.h"
#include "log.h"
#ifdef __cplusplus
extern "C" {
#endif
SPICE_BEGIN_DECLS
typedef struct Ring RingItem;
typedef struct Ring {
@ -48,14 +46,14 @@ static inline int ring_item_is_linked(RingItem *item)
static inline int ring_is_empty(Ring *ring)
{
ASSERT(ring->next != NULL && ring->prev != NULL);
spice_assert(ring->next != NULL && ring->prev != NULL);
return ring == ring->next;
}
static inline void ring_add(Ring *ring, RingItem *item)
{
ASSERT(ring->next != NULL && ring->prev != NULL);
ASSERT(item->next == NULL && item->prev == NULL);
spice_assert(ring->next != NULL && ring->prev != NULL);
spice_assert(item->next == NULL && item->prev == NULL);
item->next = ring->next;
item->prev = ring;
@ -72,54 +70,43 @@ static inline void ring_add_before(RingItem *item, RingItem *pos)
ring_add(pos->prev, item);
}
static inline void __ring_remove(RingItem *item)
{
item->next->prev = item->prev;
item->prev->next = item->next;
item->prev = item->next = 0;
}
static inline void ring_remove(RingItem *item)
{
ASSERT(item->next != NULL && item->prev != NULL);
ASSERT(item->next != item);
spice_assert(item->next != NULL && item->prev != NULL);
spice_assert(item->next != item);
__ring_remove(item);
item->next->prev = item->prev;
item->prev->next = item->next;
item->prev = item->next = NULL;
}
static inline RingItem *ring_get_head(Ring *ring)
{
RingItem *ret;
ASSERT(ring->next != NULL && ring->prev != NULL);
spice_assert(ring->next != NULL && ring->prev != NULL);
if (ring_is_empty(ring)) {
return NULL;
}
ret = ring->next;
return ret;
return ring->next;
}
static inline RingItem *ring_get_tail(Ring *ring)
{
RingItem *ret;
ASSERT(ring->next != NULL && ring->prev != NULL);
spice_assert(ring->next != NULL && ring->prev != NULL);
if (ring_is_empty(ring)) {
return NULL;
}
ret = ring->prev;
return ret;
return ring->prev;
}
static inline RingItem *ring_next(Ring *ring, RingItem *pos)
{
RingItem *ret;
ASSERT(ring->next != NULL && ring->prev != NULL);
ASSERT(pos);
ASSERT(pos->next != NULL && pos->prev != NULL);
spice_assert(ring->next != NULL && ring->prev != NULL);
spice_assert(pos);
spice_assert(pos->next != NULL && pos->prev != NULL);
ret = pos->next;
return (ret == ring) ? NULL : ret;
}
@ -128,9 +115,9 @@ static inline RingItem *ring_prev(Ring *ring, RingItem *pos)
{
RingItem *ret;
ASSERT(ring->next != NULL && ring->prev != NULL);
ASSERT(pos);
ASSERT(pos->next != NULL && pos->prev != NULL);
spice_assert(ring->next != NULL && ring->prev != NULL);
spice_assert(pos);
spice_assert(pos->next != NULL && pos->prev != NULL);
ret = pos->prev;
return (ret == ring) ? NULL : ret;
}
@ -157,16 +144,12 @@ static inline unsigned int ring_get_length(Ring *ring)
RingItem *i;
unsigned int ret = 0;
for (i = ring_get_head(ring);
i != NULL;
i = ring_next(ring, i))
RING_FOREACH(i, ring)
ret++;
return ret;
}
#ifdef __cplusplus
}
#endif
SPICE_END_DECLS
#endif

View File

@ -15,14 +15,9 @@
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include "rop3.h"
#include "spice_common.h"
typedef void (*rop3_with_pattern_handler_t)(pixman_image_t *d, pixman_image_t *s,
SpicePoint *src_pos, pixman_image_t *p,
@ -43,17 +38,21 @@ static rop3_test_handler_t rop3_test_handlers_32[ROP3_NUM_OPS];
static rop3_test_handler_t rop3_test_handlers_16[ROP3_NUM_OPS];
static void default_rop3_with_pattern_handler(pixman_image_t *d, pixman_image_t *s,
SpicePoint *src_pos, pixman_image_t *p,
SpicePoint *pat_pos)
static void default_rop3_with_pattern_handler(SPICE_GNUC_UNUSED pixman_image_t *d,
SPICE_GNUC_UNUSED pixman_image_t *s,
SPICE_GNUC_UNUSED SpicePoint *src_pos,
SPICE_GNUC_UNUSED pixman_image_t *p,
SPICE_GNUC_UNUSED SpicePoint *pat_pos)
{
WARN("not implemented");
spice_critical("not implemented");
}
static void default_rop3_withe_color_handler(pixman_image_t *d, pixman_image_t *s, SpicePoint *src_pos,
uint32_t rgb)
static void default_rop3_withe_color_handler(SPICE_GNUC_UNUSED pixman_image_t *d,
SPICE_GNUC_UNUSED pixman_image_t *s,
SPICE_GNUC_UNUSED SpicePoint *src_pos,
SPICE_GNUC_UNUSED uint32_t rgb)
{
WARN("not implemented");
spice_critical("not implemented");
}
static void default_rop3_test_handler(void)
@ -374,16 +373,10 @@ ROP3_HANDLERS(DPSoo, *src | *pat | *dest, 0xfe);
rop3_test_handlers_32[index] = rop3_test32_##op; \
rop3_test_handlers_16[index] = rop3_test16_##op;
void rop3_init(void)
SPICE_CONSTRUCTOR_FUNC(rop3_global_init)
{
static int need_init = 1;
int i;
if (!need_init) {
return;
}
need_init = 0;
for (i = 0; i < ROP3_NUM_OPS; i++) {
rop3_with_pattern_handlers_32[i] = default_rop3_with_pattern_handler;
rop3_with_pattern_handlers_16[i] = default_rop3_with_pattern_handler;
@ -624,8 +617,8 @@ void do_rop3_with_pattern(uint8_t rop3, pixman_image_t *d, pixman_image_t *s, Sp
int bpp;
bpp = spice_pixman_image_get_bpp(d);
ASSERT (bpp == spice_pixman_image_get_bpp(s));
ASSERT (bpp == spice_pixman_image_get_bpp(p));
spice_assert(bpp == spice_pixman_image_get_bpp(s));
spice_assert(bpp == spice_pixman_image_get_bpp(p));
if (bpp == 32) {
rop3_with_pattern_handlers_32[rop3](d, s, src_pos, p, pat_pos);
@ -640,7 +633,7 @@ void do_rop3_with_color(uint8_t rop3, pixman_image_t *d, pixman_image_t *s, Spic
int bpp;
bpp = spice_pixman_image_get_bpp(d);
ASSERT (bpp == spice_pixman_image_get_bpp(s));
spice_assert(bpp == spice_pixman_image_get_bpp(s));
if (bpp == 32) {
rop3_with_color_handlers_32[rop3](d, s, src_pos, rgb);

View File

@ -16,27 +16,22 @@
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _H_ROP3
#define _H_ROP3
#ifndef H_SPICE_COMMON_ROP3
#define H_SPICE_COMMON_ROP3
#include <stdint.h>
#include <spice/macros.h>
#include "draw.h"
#include "pixman_utils.h"
#ifdef __cplusplus
extern "C" {
#endif
SPICE_BEGIN_DECLS
void do_rop3_with_pattern(uint8_t rop3, pixman_image_t *d, pixman_image_t *s, SpicePoint *src_pos,
pixman_image_t *p, SpicePoint *pat_pos);
void do_rop3_with_color(uint8_t rop3, pixman_image_t *d, pixman_image_t *s, SpicePoint *src_pos,
uint32_t rgb);
void rop3_init(void);
#ifdef __cplusplus
}
#endif
SPICE_END_DECLS
#endif

276
common/snd_codec.c Normal file
View File

@ -0,0 +1,276 @@
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2013 Jeremy White
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
/* snd_codec.c
General purpose sound codec routines for use by Spice.
These routines abstract the work of picking a codec and
encoding and decoding the buffers.
See below for documentation of the public routines.
*/
#include "config.h"
#include <stdio.h>
#include <string.h>
#if HAVE_OPUS
#include <opus.h>
#endif
#include <spice/macros.h>
#include <spice/enums.h>
#include "snd_codec.h"
#include "mem.h"
#include "log.h"
typedef struct SndCodecInternal
{
SpiceAudioDataMode mode;
int frequency;
#if HAVE_OPUS
OpusEncoder *opus_encoder;
OpusDecoder *opus_decoder;
#endif
} SndCodecInternal;
/* Opus support routines */
#if HAVE_OPUS
static void snd_codec_destroy_opus(SndCodecInternal *codec)
{
if (codec->opus_decoder) {
opus_decoder_destroy(codec->opus_decoder);
codec->opus_decoder = NULL;
}
if (codec->opus_encoder) {
opus_encoder_destroy(codec->opus_encoder);
codec->opus_encoder = NULL;
}
}
static SndCodecResult snd_codec_create_opus(SndCodecInternal *codec, int purpose)
{
int opus_error;
if (purpose & SND_CODEC_ENCODE) {
codec->opus_encoder = opus_encoder_create(codec->frequency,
SND_CODEC_PLAYBACK_CHAN,
OPUS_APPLICATION_AUDIO, &opus_error);
if (! codec->opus_encoder) {
g_warning("create opus encoder failed; error %d", opus_error);
goto error;
}
}
if (purpose & SND_CODEC_DECODE) {
codec->opus_decoder = opus_decoder_create(codec->frequency,
SND_CODEC_PLAYBACK_CHAN, &opus_error);
if (! codec->opus_decoder) {
g_warning("create opus decoder failed; error %d", opus_error);
goto error;
}
}
codec->mode = SPICE_AUDIO_DATA_MODE_OPUS;
return SND_CODEC_OK;
error:
snd_codec_destroy_opus(codec);
return SND_CODEC_UNAVAILABLE;
}
static SndCodecResult
snd_codec_encode_opus(SndCodecInternal *codec, uint8_t *in_ptr, int in_size,
uint8_t *out_ptr, int *out_size)
{
int n;
if (in_size != SND_CODEC_OPUS_FRAME_SIZE * SND_CODEC_PLAYBACK_CHAN * 2)
return SND_CODEC_INVALID_ENCODE_SIZE;
n = opus_encode(codec->opus_encoder, (opus_int16 *) in_ptr, SND_CODEC_OPUS_FRAME_SIZE, out_ptr, *out_size);
if (n < 0) {
g_warning("opus_encode failed %d", n);
return SND_CODEC_ENCODE_FAILED;
}
*out_size = n;
return SND_CODEC_OK;
}
static SndCodecResult
snd_codec_decode_opus(SndCodecInternal *codec, uint8_t *in_ptr, int in_size,
uint8_t *out_ptr, int *out_size)
{
int n;
n = opus_decode(codec->opus_decoder, in_ptr, in_size, (opus_int16 *) out_ptr,
*out_size / SND_CODEC_PLAYBACK_CHAN / 2, 0);
if (n < 0) {
g_warning("opus_decode failed %d", n);
return SND_CODEC_DECODE_FAILED;
}
*out_size = n * SND_CODEC_PLAYBACK_CHAN * 2 /* 16 fmt */;
return SND_CODEC_OK;
}
#endif
/*----------------------------------------------------------------------------
** PUBLIC INTERFACE
**--------------------------------------------------------------------------*/
/*
snd_codec_is_capable
Returns true if the current spice implementation can
use the given codec, false otherwise.
mode must be a SPICE_AUDIO_DATA_MODE_XXX enum from spice/enum.h
*/
bool snd_codec_is_capable(SpiceAudioDataMode mode, int frequency)
{
#if HAVE_OPUS
if (mode == SPICE_AUDIO_DATA_MODE_OPUS &&
(frequency == SND_CODEC_ANY_FREQUENCY ||
frequency == 48000 || frequency == 24000 ||
frequency == 16000 || frequency == 12000 ||
frequency == 8000) )
return true;
#endif
return false;
}
/*
snd_codec_create
Create a codec control. Required for most functions in this library.
Parameters:
1. codec Pointer to preallocated codec control
2. mode SPICE_AUDIO_DATA_MODE_XXX enum from spice/enum.h
3. encode TRUE if encoding is desired
4. decode TRUE if decoding is desired
Returns:
SND_CODEC_OK if all went well; a different code if not.
snd_codec_destroy is the obvious partner of snd_codec_create.
*/
SndCodecResult
snd_codec_create(SndCodec *codec, SpiceAudioDataMode mode, int frequency, int purpose)
{
SndCodecResult rc = SND_CODEC_UNAVAILABLE;
SndCodecInternal **c = codec;
*c = spice_new0(SndCodecInternal, 1);
(*c)->frequency = frequency;
#if HAVE_OPUS
if (mode == SPICE_AUDIO_DATA_MODE_OPUS)
rc = snd_codec_create_opus(*c, purpose);
#endif
return rc;
}
/*
snd_codec_destroy
The obvious companion to snd_codec_create
*/
void snd_codec_destroy(SndCodec *codec)
{
SndCodecInternal **c = codec;
if (! c || ! *c)
return;
#if HAVE_OPUS
snd_codec_destroy_opus(*c);
#endif
free(*c);
*c = NULL;
}
/*
snd_codec_frame_size
Returns the size, in frames, of the raw PCM frame buffer
required by this codec. To get bytes, you'll need
to multiply by channels and sample width.
*/
int snd_codec_frame_size(SndCodec codec)
{
#if HAVE_OPUS
SndCodecInternal *c = codec;
if (c && c->mode == SPICE_AUDIO_DATA_MODE_OPUS)
return SND_CODEC_OPUS_FRAME_SIZE;
#endif
return SND_CODEC_MAX_FRAME_SIZE;
}
/*
snd_codec_encode
Encode a block of data to a compressed buffer.
Parameters:
1. codec Pointer to codec control previously allocated + created
2. in_ptr Pointer to uncompressed PCM data
3. in_size Input size
4. out_ptr Pointer to area to write encoded data
5. out_size On input, the maximum size of the output buffer; on
successful return, it will hold the number of bytes
returned.
Returns:
SND_CODEC_OK if all went well
*/
SndCodecResult
snd_codec_encode(SndCodec codec, uint8_t *in_ptr, int in_size, uint8_t *out_ptr, int *out_size)
{
#if HAVE_OPUS
SndCodecInternal *c = codec;
if (c && c->mode == SPICE_AUDIO_DATA_MODE_OPUS)
return snd_codec_encode_opus(c, in_ptr, in_size, out_ptr, out_size);
#endif
return SND_CODEC_ENCODER_UNAVAILABLE;
}
/*
snd_codec_decode
Decode a block of data from a compressed buffer.
Parameters:
1. codec Pointer to codec control previously allocated + created
2. in_ptr Pointer to compressed data
3. in_size Input size
4. out_ptr Pointer to area to write decoded data
5. out_size On input, the maximum size of the output buffer; on
successful return, it will hold the number of bytes
returned.
Returns:
SND_CODEC_OK if all went well
*/
SndCodecResult
snd_codec_decode(SndCodec codec, uint8_t *in_ptr, int in_size, uint8_t *out_ptr, int *out_size)
{
#if HAVE_OPUS
SndCodecInternal *c = codec;
if (c && c->mode == SPICE_AUDIO_DATA_MODE_OPUS)
return snd_codec_decode_opus(c, in_ptr, in_size, out_ptr, out_size);
#endif
return SND_CODEC_DECODER_UNAVAILABLE;
}

69
common/snd_codec.h Normal file
View File

@ -0,0 +1,69 @@
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2013 Jeremy White <jwhite@codeweavers.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef H_SPICE_COMMON_SND_CODEC
#define H_SPICE_COMMON_SND_CODEC
#include <stdbool.h>
#include <spice/enums.h>
#define SND_CODEC_OPUS_FRAME_SIZE 480
#define SND_CODEC_OPUS_PLAYBACK_FREQ 48000
#define SND_CODEC_OPUS_COMPRESSED_FRAME_BYTES 480
#define SND_CODEC_PLAYBACK_CHAN 2
#define SND_CODEC_MAX_FRAME_SIZE SND_CODEC_OPUS_FRAME_SIZE
#define SND_CODEC_MAX_FRAME_BYTES (SND_CODEC_MAX_FRAME_SIZE * SND_CODEC_PLAYBACK_CHAN * 2 /* FMT_S16 */)
#define SND_CODEC_MAX_COMPRESSED_BYTES SND_CODEC_OPUS_COMPRESSED_FRAME_BYTES
#define SND_CODEC_ANY_FREQUENCY -1
#define SND_CODEC_ENCODE 0x0001
#define SND_CODEC_DECODE 0x0002
SPICE_BEGIN_DECLS
typedef enum {
SND_CODEC_OK,
SND_CODEC_UNAVAILABLE,
SND_CODEC_ENCODER_UNAVAILABLE,
SND_CODEC_DECODER_UNAVAILABLE,
SND_CODEC_ENCODE_FAILED,
SND_CODEC_DECODE_FAILED,
SND_CODEC_INVALID_ENCODE_SIZE,
} SndCodecResult;
typedef struct SndCodecInternal * SndCodec;
bool snd_codec_is_capable(SpiceAudioDataMode mode, int frequency);
SndCodecResult snd_codec_create(SndCodec *codec,
SpiceAudioDataMode mode, int frequency, int purpose);
void snd_codec_destroy(SndCodec *codec);
int snd_codec_frame_size(SndCodec codec);
SndCodecResult snd_codec_encode(SndCodec codec, uint8_t *in_ptr, int in_size,
uint8_t *out_ptr, int *out_size);
SndCodecResult snd_codec_decode(SndCodec codec, uint8_t *in_ptr, int in_size,
uint8_t *out_ptr, int *out_size);
SPICE_END_DECLS
#endif

View File

@ -16,12 +16,11 @@
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "mem.h"
#include "ssl_verify.h"
#include "log.h"
#ifndef WIN32
#include <sys/socket.h>
@ -29,21 +28,14 @@
#include <arpa/inet.h>
#endif
#include <ctype.h>
#include <string.h>
#include <gio/gio.h>
#ifndef SPICE_DEBUG
# define SPICE_DEBUG(format, ...)
#endif
#ifdef WIN32
static int inet_aton(const char* ip, struct in_addr* in_addr)
#if OPENSSL_VERSION_NUMBER < 0x10100000 || \
(defined (LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000)
static const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1)
{
unsigned long addr = inet_addr(ip);
if (addr == INADDR_NONE) {
return 0;
}
in_addr->S_un.S_addr = addr;
return 1;
return M_ASN1_STRING_data(asn1);
}
#endif
@ -58,36 +50,41 @@ static int verify_pubkey(X509* cert, const char *key, size_t key_size)
return 0;
if (!cert) {
SPICE_DEBUG("warning: no cert!");
spice_debug("warning: no cert!");
return 0;
}
cert_pubkey = X509_get_pubkey(cert);
if (!cert_pubkey) {
SPICE_DEBUG("warning: reading public key from certificate failed");
spice_debug("warning: reading public key from certificate failed");
goto finish;
}
bio = BIO_new_mem_buf((void*)key, key_size);
if (!bio) {
SPICE_DEBUG("creating BIO failed");
spice_debug("creating BIO failed");
goto finish;
}
orig_pubkey = d2i_PUBKEY_bio(bio, NULL);
if (!orig_pubkey) {
SPICE_DEBUG("reading pubkey from bio failed");
spice_debug("reading pubkey from bio failed");
goto finish;
}
#if OPENSSL_VERSION_NUMBER >= 0x30000000
ret = EVP_PKEY_eq(orig_pubkey, cert_pubkey);
#else
ret = EVP_PKEY_cmp(orig_pubkey, cert_pubkey);
#endif
if (ret == 1)
SPICE_DEBUG("public keys match");
else if (ret == 0)
SPICE_DEBUG("public keys mismatch");
else
SPICE_DEBUG("public keys types mismatch");
if (ret == 1) {
spice_debug("public keys match");
} else if (ret == 0) {
spice_debug("public keys mismatch");
} else {
spice_debug("public keys types mismatch");
}
finish:
if (bio)
@ -162,19 +159,14 @@ static int verify_hostname(X509* cert, const char *hostname)
{
GENERAL_NAMES* subject_alt_names;
int found_dns_name = 0;
struct in_addr addr;
int addr_len = 0;
int cn_match = 0;
X509_NAME* subject;
if (!cert) {
SPICE_DEBUG("warning: no cert!");
return 0;
}
spice_return_val_if_fail(hostname != NULL, 0);
// only IpV4 supported
if (inet_aton(hostname, &addr)) {
addr_len = sizeof(struct in_addr);
if (!cert) {
spice_debug("warning: no cert!");
return 0;
}
/* try matching against:
@ -200,30 +192,56 @@ static int verify_hostname(X509* cert, const char *hostname)
const GENERAL_NAME* name = sk_GENERAL_NAME_value(subject_alt_names, i);
if (name->type == GEN_DNS) {
found_dns_name = 1;
if (_gnutls_hostname_compare((char *)ASN1_STRING_data(name->d.dNSName),
if (_gnutls_hostname_compare((const char *)ASN1_STRING_get0_data(name->d.dNSName),
ASN1_STRING_length(name->d.dNSName),
hostname)) {
SPICE_DEBUG("alt name match=%s", ASN1_STRING_data(name->d.dNSName));
spice_debug("alt name match=%s", ASN1_STRING_get0_data(name->d.dNSName));
GENERAL_NAMES_free(subject_alt_names);
return 1;
}
} else if (name->type == GEN_IPADD) {
int alt_ip_len = ASN1_STRING_length(name->d.iPAddress);
GInetAddress * ip;
const guint8 * ip_binary;
int alt_ip_len;
int ip_len;
found_dns_name = 1;
if ((addr_len == alt_ip_len)&&
!memcmp(ASN1_STRING_data(name->d.iPAddress), &addr, addr_len)) {
SPICE_DEBUG("alt name IP match=%s",
inet_ntoa(*((struct in_addr*)ASN1_STRING_data(name->d.dNSName))));
ip = g_inet_address_new_from_string(hostname);
if (ip == NULL) {
spice_warning("Could not parse hostname: %s", hostname);
continue;
}
ip_len = g_inet_address_get_native_size(ip);
ip_binary = g_inet_address_to_bytes(ip);
alt_ip_len = ASN1_STRING_length(name->d.iPAddress);
if ((ip_len == alt_ip_len) &&
(memcmp(ASN1_STRING_get0_data(name->d.iPAddress), ip_binary, ip_len)) == 0) {
GInetAddress * alt_ip = NULL;
gchar * alt_ip_string = NULL;
alt_ip = g_inet_address_new_from_bytes(ASN1_STRING_get0_data(name->d.iPAddress),
g_inet_address_get_family(ip));
alt_ip_string = g_inet_address_to_string(alt_ip);
spice_debug("alt name IP match=%s", alt_ip_string);
g_free(alt_ip_string);
g_object_unref(alt_ip);
g_object_unref(ip);
GENERAL_NAMES_free(subject_alt_names);
return 1;
}
g_object_unref(ip);
}
}
GENERAL_NAMES_free(subject_alt_names);
}
if (found_dns_name) {
SPICE_DEBUG("warning: SubjectAltName mismatch");
spice_debug("warning: SubjectAltName mismatch");
return 0;
}
@ -244,38 +262,41 @@ static int verify_hostname(X509* cert, const char *hostname)
continue;
}
if (_gnutls_hostname_compare((char*)ASN1_STRING_data(cn_asn1),
if (_gnutls_hostname_compare((const char*)ASN1_STRING_get0_data(cn_asn1),
ASN1_STRING_length(cn_asn1),
hostname)) {
SPICE_DEBUG("common name match=%s", (char*)ASN1_STRING_data(cn_asn1));
spice_debug("common name match=%s", (char*)ASN1_STRING_get0_data(cn_asn1));
cn_match = 1;
break;
}
}
}
if (!cn_match)
SPICE_DEBUG("warning: common name mismatch");
if (!cn_match) {
spice_debug("warning: common name mismatch");
}
return cn_match;
}
X509_NAME* subject_to_x509_name(const char *subject, int *nentries)
static X509_NAME* subject_to_x509_name(const char *subject, int *nentries)
{
X509_NAME* in_subject;
const char *p;
char *key, *val, *k, *v = NULL;
char *key, *val = NULL, *k, *v = NULL;
enum {
KEY,
VALUE
} state;
key = (char*)alloca(strlen(subject));
val = (char*)alloca(strlen(subject));
spice_return_val_if_fail(subject != NULL, NULL);
spice_return_val_if_fail(nentries != NULL, NULL);
key = (char*)alloca(strlen(subject)+1);
in_subject = X509_NAME_new();
if (!in_subject || !key || !val) {
SPICE_DEBUG("failed to allocate");
if (!in_subject || !key) {
spice_debug("failed to allocate");
return NULL;
}
@ -288,7 +309,7 @@ X509_NAME* subject_to_x509_name(const char *subject, int *nentries)
if (*p == '\\') {
++p;
if (*p != '\\' && *p != ',') {
SPICE_DEBUG("Invalid character after \\");
spice_debug("Invalid character after \\");
goto fail;
}
escape = 1;
@ -307,6 +328,7 @@ X509_NAME* subject_to_x509_name(const char *subject, int *nentries)
} else if (*p == '=' && !escape) {
state = VALUE;
*k = 0;
val = k + 1;
v = val;
} else
*k++ = *p;
@ -322,7 +344,7 @@ X509_NAME* subject_to_x509_name(const char *subject, int *nentries)
MBSTRING_UTF8,
(const unsigned char*)val,
-1, -1, 0)) {
SPICE_DEBUG("warning: failed to add entry %s=%s to X509_NAME",
spice_debug("warning: failed to add entry %s=%s to X509_NAME",
key, val);
goto fail;
}
@ -349,89 +371,145 @@ fail:
return NULL;
}
int verify_subject(X509* cert, SpiceOpenSSLVerify* verify)
static int verify_subject(X509* cert, SpiceOpenSSLVerify* verify)
{
X509_NAME *cert_subject = NULL;
X509_NAME* in_subject;
int ret;
int in_entries;
if (!cert) {
SPICE_DEBUG("warning: no cert!");
spice_debug("warning: no cert!");
return 0;
}
cert_subject = X509_get_subject_name(cert);
if (!cert_subject) {
SPICE_DEBUG("warning: reading certificate subject failed");
spice_debug("warning: reading certificate subject failed");
return 0;
}
if (!verify->in_subject) {
verify->in_subject = subject_to_x509_name(verify->subject, &in_entries);
if (!verify->in_subject) {
SPICE_DEBUG("warning: no in_subject!");
return 0;
}
in_subject = subject_to_x509_name(verify->subject, &in_entries);
if (!in_subject) {
spice_debug("warning: no in_subject!");
return 0;
}
/* Note: this check is redundant with the pre-condition in X509_NAME_cmp */
if (X509_NAME_entry_count(cert_subject) != in_entries) {
SPICE_DEBUG("subject mismatch: #entries cert=%d, input=%d",
spice_debug("subject mismatch: #entries cert=%d, input=%d",
X509_NAME_entry_count(cert_subject), in_entries);
X509_NAME_free(in_subject);
return 0;
}
ret = X509_NAME_cmp(cert_subject, verify->in_subject);
ret = X509_NAME_cmp(cert_subject, in_subject);
if (ret == 0)
SPICE_DEBUG("subjects match");
else
SPICE_DEBUG("subjects mismatch");
if (ret == 0) {
spice_debug("subjects match");
} else {
char *p;
spice_debug("subjects mismatch");
p = X509_NAME_oneline(cert_subject, NULL, 0);
spice_debug("cert_subject: %s", p);
free(p);
p = X509_NAME_oneline(in_subject, NULL, 0);
spice_debug("in_subject: %s", p);
free(p);
}
X509_NAME_free(in_subject);
return !ret;
}
static int openssl_verify(int preverify_ok, X509_STORE_CTX *ctx)
{
int depth;
int depth, err;
SpiceOpenSSLVerify *v;
SSL *ssl;
X509* cert;
char buf[256];
unsigned int failed_verifications;
ssl = (SSL*)X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
v = (SpiceOpenSSLVerify*)SSL_get_app_data(ssl);
cert = X509_STORE_CTX_get_current_cert(ctx);
X509_NAME_oneline(X509_get_subject_name(cert), buf, 256);
depth = X509_STORE_CTX_get_error_depth(ctx);
err = X509_STORE_CTX_get_error(ctx);
if (depth > 0) {
if (!preverify_ok) {
SPICE_DEBUG("openssl verify failed at depth=%d", depth);
spice_warning("Error in certificate chain verification: %s (num=%d:depth%d:%s)",
X509_verify_cert_error_string(err), err, depth, buf);
v->all_preverify_ok = 0;
/* if certificate verification failed, we can still authorize the server */
/* if its public key matches the one we hold in the peer_connect_options. */
if (err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN &&
v->verifyop & SPICE_SSL_VERIFY_OP_PUBKEY)
return 1;
if (err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN)
spice_debug("server certificate not being signed by the provided CA");
return 0;
} else
return 1;
}
/* depth == 0 */
cert = X509_STORE_CTX_get_current_cert(ctx);
if (!cert) {
SPICE_DEBUG("failed to get server certificate");
spice_debug("failed to get server certificate");
return 0;
}
if (v->verifyop & SPICE_SSL_VERIFY_OP_PUBKEY &&
verify_pubkey(cert, v->pubkey, v->pubkey_size))
return 1;
failed_verifications = 0;
if (v->verifyop & SPICE_SSL_VERIFY_OP_PUBKEY) {
if (verify_pubkey(cert, v->pubkey, v->pubkey_size))
return 1;
else
failed_verifications |= SPICE_SSL_VERIFY_OP_PUBKEY;
}
if (!v->all_preverify_ok || !preverify_ok)
if (!preverify_ok) {
err = X509_STORE_CTX_get_error(ctx);
depth = X509_STORE_CTX_get_error_depth(ctx);
spice_warning("Error in server certificate verification: %s (num=%d:depth%d:%s)",
X509_verify_cert_error_string(err), err, depth, buf);
return 0;
}
if (!v->all_preverify_ok) {
return 0;
}
if (v->verifyop & SPICE_SSL_VERIFY_OP_HOSTNAME &&
verify_hostname(cert, v->hostname))
return 1;
if (v->verifyop & SPICE_SSL_VERIFY_OP_SUBJECT) {
if (verify_subject(cert, v))
return 1;
else
failed_verifications |= SPICE_SSL_VERIFY_OP_SUBJECT;
} else if (v->verifyop & SPICE_SSL_VERIFY_OP_HOSTNAME) {
if (verify_hostname(cert, v->hostname))
return 1;
else
failed_verifications |= SPICE_SSL_VERIFY_OP_HOSTNAME;
}
if (v->verifyop & SPICE_SSL_VERIFY_OP_SUBJECT &&
verify_subject(cert, v))
return 1;
/* If we reach this code, this means all the tests failed, thus
* verification failed
*/
if (failed_verifications & SPICE_SSL_VERIFY_OP_PUBKEY)
spice_warning("ssl: pubkey verification failed");
if (failed_verifications & SPICE_SSL_VERIFY_OP_HOSTNAME)
spice_warning("ssl: hostname '%s' verification failed", v->hostname);
if (failed_verifications & SPICE_SSL_VERIFY_OP_SUBJECT)
spice_warning("ssl: subject '%s' verification failed", v->subject);
spice_warning("ssl: verification failed");
return 0;
}
@ -473,9 +551,6 @@ void spice_openssl_verify_free(SpiceOpenSSLVerify* verify)
free(verify->subject);
free(verify->hostname);
if (verify->in_subject)
X509_NAME_free(verify->in_subject);
if (verify->ssl)
SSL_set_app_data(verify->ssl, NULL);
free(verify);

View File

@ -16,12 +16,16 @@
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SSL_VERIFY_H
#define SSL_VERIFY_H
#ifndef H_SPICE_COMMON_SSL_VERIFY
#define H_SPICE_COMMON_SSL_VERIFY
#if defined(WIN32) && !defined(__MINGW32__)
#if defined(WIN32)
#include <windows.h>
#include <wincrypt.h>
#ifdef X509_NAME
/* wincrypt.h has already a different define... */
#undef X509_NAME
#endif
#endif
#include <openssl/rsa.h>
@ -29,12 +33,11 @@
#include <openssl/x509.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#undef X509_NAME
#include <openssl/x509v3.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <spice/macros.h>
SPICE_BEGIN_DECLS
typedef enum {
SPICE_SSL_VERIFY_OP_NONE = 0,
@ -51,7 +54,6 @@ typedef struct {
char *pubkey;
size_t pubkey_size;
char *subject;
X509_NAME *in_subject;
} SpiceOpenSSLVerify;
SpiceOpenSSLVerify* spice_openssl_verify_new(SSL *ssl, SPICE_SSL_VERIFY_OP verifyop,
@ -60,7 +62,6 @@ SpiceOpenSSLVerify* spice_openssl_verify_new(SSL *ssl, SPICE_SSL_VERIFY_OP verif
const char *subject);
void spice_openssl_verify_free(SpiceOpenSSLVerify* verify);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // SSL_VERIFY_H
SPICE_END_DECLS
#endif // H_SPICE_COMMON_SSL_VERIFY

View File

@ -15,21 +15,13 @@
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#ifdef __MINGW32__
#undef HAVE_STDLIB_H
#endif
#include <config.h>
#endif
#ifndef SPICE_CANVAS_INTERNAL
#error "This file shouldn't be compiled directly"
#endif
#include <math.h>
#include "sw_canvas.h"
#define CANVAS_USE_PIXMAN
#define CANVAS_SINGLE_INSTANCE
#include "canvas_base.c"
#include "rect.h"
#include "region.h"
@ -83,15 +75,33 @@ static pixman_image_t *canvas_get_pixman_brush(SwCanvas *canvas,
case SPICE_BRUSH_TYPE_NONE:
return NULL;
default:
CANVAS_ERROR("invalid brush type");
spice_warn_if_reached();
return NULL;
}
return NULL;
}
static pixman_image_t *get_image(SpiceCanvas *canvas)
static pixman_image_t *get_image(SpiceCanvas *canvas, int force_opaque)
{
SwCanvas *sw_canvas = (SwCanvas *)canvas;
pixman_format_code_t format;
pixman_image_ref(sw_canvas->image);
spice_pixman_image_get_format(sw_canvas->image, &format);
if (force_opaque && PIXMAN_FORMAT_A (format) != 0) {
uint32_t *data;
int stride;
int width, height;
/* Remove alpha bits from format */
format = (pixman_format_code_t)(((uint32_t)format) & ~(0xf << 12));
data = pixman_image_get_data(sw_canvas->image);
stride = pixman_image_get_stride(sw_canvas->image);
width = pixman_image_get_width(sw_canvas->image);
height = pixman_image_get_height(sw_canvas->image);
return pixman_image_create_bits(format, width, height, data, stride);
} else {
pixman_image_ref(sw_canvas->image);
}
return sw_canvas->image;
}
@ -343,8 +353,8 @@ static void clear_dest_alpha(pixman_image_t *dest,
}
stride = pixman_image_get_stride(dest);
data = (uint32_t *) (
(uint8_t *)pixman_image_get_data(dest) + y * stride + 4 * x);
data = SPICE_ALIGNED_CAST(uint32_t *,
(uint8_t *)pixman_image_get_data(dest) + y * stride + 4 * x);
if ((*data & 0xff000000U) == 0xff000000U) {
spice_pixman_fill_rect_rop(dest,
@ -472,13 +482,13 @@ static void __scale_image(SpiceCanvas *spice_canvas,
pixman_transform_init_scale(&transform, fsx, fsy);
pixman_transform_translate(&transform, NULL,
pixman_int_to_fixed (src_x),
pixman_int_to_fixed (src_y));
pixman_int_to_fixed(src_x),
pixman_int_to_fixed(src_y));
pixman_image_set_transform(src, &transform);
pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE);
ASSERT(scale_mode == SPICE_IMAGE_SCALE_MODE_INTERPOLATE ||
scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST);
spice_return_if_fail(scale_mode == SPICE_IMAGE_SCALE_MODE_INTERPOLATE ||
scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST);
pixman_image_set_filter(src,
(scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST) ?
PIXMAN_FILTER_NEAREST : PIXMAN_FILTER_GOOD,
@ -539,11 +549,13 @@ static void __scale_image_rop(SpiceCanvas *spice_canvas,
pixman_box32_t *rects;
int n_rects, i;
pixman_fixed_t fsx, fsy;
pixman_format_code_t format;
fsx = ((pixman_fixed_48_16_t) src_width * 65536) / dest_width;
fsy = ((pixman_fixed_48_16_t) src_height * 65536) / dest_height;
scaled = pixman_image_create_bits(spice_pixman_image_get_format(src),
spice_return_if_fail(spice_pixman_image_get_format(src, &format));
scaled = pixman_image_create_bits(format,
dest_width,
dest_height,
NULL, 0);
@ -553,13 +565,13 @@ static void __scale_image_rop(SpiceCanvas *spice_canvas,
pixman_transform_init_scale(&transform, fsx, fsy);
pixman_transform_translate(&transform, NULL,
pixman_int_to_fixed (src_x),
pixman_int_to_fixed (src_y));
pixman_int_to_fixed(src_x),
pixman_int_to_fixed(src_y));
pixman_image_set_transform(src, &transform);
pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE);
ASSERT(scale_mode == SPICE_IMAGE_SCALE_MODE_INTERPOLATE ||
scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST);
spice_return_if_fail(scale_mode == SPICE_IMAGE_SCALE_MODE_INTERPOLATE ||
scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST);
pixman_image_set_filter(src,
(scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST) ?
PIXMAN_FILTER_NEAREST : PIXMAN_FILTER_GOOD,
@ -659,7 +671,7 @@ static void __blend_image(SpiceCanvas *spice_canvas,
mask = NULL;
if (overall_alpha != 0xff) {
pixman_color_t color = { 0 };
pixman_color_t color = { 0, 0, 0, 0 };
color.alpha = overall_alpha * 0x101;
mask = pixman_image_create_solid_fill(&color);
}
@ -747,20 +759,20 @@ static void __blend_scale_image(SpiceCanvas *spice_canvas,
pixman_transform_init_scale(&transform, fsx, fsy);
pixman_transform_translate(&transform, NULL,
pixman_int_to_fixed (src_x),
pixman_int_to_fixed (src_y));
pixman_int_to_fixed(src_x),
pixman_int_to_fixed(src_y));
mask = NULL;
if (overall_alpha != 0xff) {
pixman_color_t color = { 0 };
pixman_color_t color = { 0, 0, 0, 0 };
color.alpha = overall_alpha * 0x101;
mask = pixman_image_create_solid_fill(&color);
}
pixman_image_set_transform(src, &transform);
pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE);
ASSERT(scale_mode == SPICE_IMAGE_SCALE_MODE_INTERPOLATE ||
scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST);
spice_return_if_fail(scale_mode == SPICE_IMAGE_SCALE_MODE_INTERPOLATE ||
scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST);
pixman_image_set_filter(src,
(scale_mode == SPICE_IMAGE_SCALE_MODE_NEAREST) ?
PIXMAN_FILTER_NEAREST : PIXMAN_FILTER_GOOD,
@ -895,11 +907,13 @@ static void __colorkey_scale_image(SpiceCanvas *spice_canvas,
pixman_box32_t *rects;
int n_rects, i;
pixman_fixed_t fsx, fsy;
pixman_format_code_t format;
fsx = ((pixman_fixed_48_16_t) src_width * 65536) / dest_width;
fsy = ((pixman_fixed_48_16_t) src_height * 65536) / dest_height;
scaled = pixman_image_create_bits(spice_pixman_image_get_format (src),
spice_return_if_fail(spice_pixman_image_get_format(src, &format));
scaled = pixman_image_create_bits(format,
dest_width,
dest_height,
NULL, 0);
@ -909,8 +923,8 @@ static void __colorkey_scale_image(SpiceCanvas *spice_canvas,
pixman_transform_init_scale(&transform, fsx, fsy);
pixman_transform_translate(&transform, NULL,
pixman_int_to_fixed (src_x),
pixman_int_to_fixed (src_y));
pixman_int_to_fixed(src_x),
pixman_int_to_fixed(src_y));
pixman_image_set_transform(src, &transform);
pixman_image_set_repeat(src, PIXMAN_REPEAT_NONE);
@ -977,9 +991,6 @@ static void colorkey_scale_image_from_surface(SpiceCanvas *spice_canvas,
}
static void canvas_put_image(SpiceCanvas *spice_canvas,
#ifdef WIN32
HDC dc,
#endif
const SpiceRect *dest, const uint8_t *src_data,
uint32_t src_width, uint32_t src_height, int src_stride,
const QRegion *clip)
@ -994,7 +1005,7 @@ static void canvas_put_image(SpiceCanvas *spice_canvas,
src = pixman_image_create_bits(PIXMAN_x8r8g8b8,
src_width,
src_height,
(uint32_t*)src_data,
SPICE_ALIGNED_CAST(uint32_t*,src_data),
src_stride);
@ -1042,7 +1053,7 @@ static void canvas_draw_text(SpiceCanvas *spice_canvas, SpiceRect *bbox,
pixman_region32_t dest_region;
pixman_image_t *str_mask, *brush;
SpiceString *str;
SpicePoint pos;
SpicePoint pos = { 0, 0 };
int depth;
pixman_region32_init_rect(&dest_region,
@ -1064,7 +1075,7 @@ static void canvas_draw_text(SpiceCanvas *spice_canvas, SpiceRect *bbox,
/* Nothing else makes sense for text and we should deprecate it
* and actually it means OVER really */
ASSERT(text->fore_mode == SPICE_ROPD_OP_PUT);
spice_return_if_fail(text->fore_mode == SPICE_ROPD_OP_PUT);
pixman_region32_init_rect(&back_region,
text->back_area.left,
@ -1087,10 +1098,10 @@ static void canvas_draw_text(SpiceCanvas *spice_canvas, SpiceRect *bbox,
} else if (str->flags & SPICE_STRING_FLAGS_RASTER_A4) {
depth = 4;
} else if (str->flags & SPICE_STRING_FLAGS_RASTER_A8) {
WARN("untested path A8 glyphs");
spice_warning("untested path A8 glyphs");
depth = 8;
} else {
WARN("unsupported path vector glyphs");
spice_warning("unsupported path vector glyphs");
pixman_region32_fini (&dest_region);
return;
}
@ -1133,7 +1144,7 @@ static void canvas_read_bits(SpiceCanvas *spice_canvas, uint8_t *dest,
uint8_t *dest_end;
int bpp;
ASSERT(canvas && area);
spice_return_if_fail(canvas && area);
surface = canvas->image;
@ -1170,47 +1181,37 @@ static void canvas_destroy(SpiceCanvas *spice_canvas)
free(canvas);
}
static int need_init = 1;
static SpiceCanvasOps sw_canvas_ops;
static SpiceCanvas *canvas_create_common(pixman_image_t *image,
uint32_t format
uint32_t format,
SpiceImageCache *bits_cache,
#ifdef SW_CANVAS_CACHE
, SpiceImageCache *bits_cache
, SpicePaletteCache *palette_cache
#elif defined(SW_CANVAS_IMAGE_CACHE)
, SpiceImageCache *bits_cache
SpicePaletteCache *palette_cache,
#endif
, SpiceImageSurfaces *surfaces
, SpiceGlzDecoder *glz_decoder
, SpiceJpegDecoder *jpeg_decoder
, SpiceZlibDecoder *zlib_decoder
)
SpiceImageSurfaces *surfaces,
SpiceGlzDecoder *glz_decoder,
SpiceJpegDecoder *jpeg_decoder,
SpiceZlibDecoder *zlib_decoder)
{
SwCanvas *canvas;
if (need_init) {
return NULL;
}
spice_pixman_image_set_format(image,
spice_surface_format_to_pixman (format));
spice_surface_format_to_pixman(format));
canvas = spice_new0(SwCanvas, 1);
canvas_base_init(&canvas->base, &sw_canvas_ops,
pixman_image_get_width (image),
pixman_image_get_height (image),
format
pixman_image_get_width(image),
pixman_image_get_height(image),
format,
bits_cache,
#ifdef SW_CANVAS_CACHE
, bits_cache
, palette_cache
#elif defined(SW_CANVAS_IMAGE_CACHE)
, bits_cache
palette_cache,
#endif
, surfaces
, glz_decoder
, jpeg_decoder
, zlib_decoder
);
surfaces,
glz_decoder,
jpeg_decoder,
zlib_decoder);
canvas->private_data = NULL;
canvas->private_data_size = 0;
@ -1219,78 +1220,37 @@ static SpiceCanvas *canvas_create_common(pixman_image_t *image,
return (SpiceCanvas *)canvas;
}
SpiceCanvas *canvas_create(int width, int height, uint32_t format
#ifdef SW_CANVAS_CACHE
, SpiceImageCache *bits_cache
, SpicePaletteCache *palette_cache
#elif defined(SW_CANVAS_IMAGE_CACHE)
, SpiceImageCache *bits_cache
#endif
, SpiceImageSurfaces *surfaces
, SpiceGlzDecoder *glz_decoder
, SpiceJpegDecoder *jpeg_decoder
, SpiceZlibDecoder *zlib_decoder
)
{
pixman_image_t *image;
image = pixman_image_create_bits(spice_surface_format_to_pixman (format),
width, height, NULL, 0);
return canvas_create_common(image, format
#ifdef SW_CANVAS_CACHE
, bits_cache
, palette_cache
#elif defined(SW_CANVAS_IMAGE_CACHE)
, bits_cache
#endif
, surfaces
, glz_decoder
, jpeg_decoder
, zlib_decoder
);
}
SpiceCanvas *canvas_create_for_data(int width, int height, uint32_t format,
uint8_t *data, int stride
uint8_t *data, int stride,
SpiceImageCache *bits_cache,
#ifdef SW_CANVAS_CACHE
, SpiceImageCache *bits_cache
, SpicePaletteCache *palette_cache
#elif defined(SW_CANVAS_IMAGE_CACHE)
, SpiceImageCache *bits_cache
SpicePaletteCache *palette_cache,
#endif
, SpiceImageSurfaces *surfaces
, SpiceGlzDecoder *glz_decoder
, SpiceJpegDecoder *jpeg_decoder
, SpiceZlibDecoder *zlib_decoder
)
SpiceImageSurfaces *surfaces,
SpiceGlzDecoder *glz_decoder,
SpiceJpegDecoder *jpeg_decoder,
SpiceZlibDecoder *zlib_decoder)
{
pixman_image_t *image;
image = pixman_image_create_bits(spice_surface_format_to_pixman (format),
width, height, (uint32_t *)data, stride);
image = pixman_image_create_bits(spice_surface_format_to_pixman(format),
width, height,
SPICE_ALIGNED_CAST(uint32_t *,data),
stride);
return canvas_create_common(image, format
return canvas_create_common(image, format,
bits_cache,
#ifdef SW_CANVAS_CACHE
, bits_cache
, palette_cache
#elif defined(SW_CANVAS_IMAGE_CACHE)
, bits_cache
palette_cache,
#endif
, surfaces
, glz_decoder
, jpeg_decoder
, zlib_decoder
);
surfaces,
glz_decoder,
jpeg_decoder,
zlib_decoder);
}
void sw_canvas_init(void) //unsafe global function
SPICE_CONSTRUCTOR_FUNC(sw_canvas_global_init) //unsafe global function
{
if (!need_init) {
return;
}
need_init = 0;
canvas_base_init_ops(&sw_canvas_ops);
sw_canvas_ops.draw_text = canvas_draw_text;
sw_canvas_ops.put_image = canvas_put_image;
@ -1323,5 +1283,4 @@ void sw_canvas_init(void) //unsafe global function
sw_canvas_ops.colorkey_scale_image_from_surface = colorkey_scale_image_from_surface;
sw_canvas_ops.copy_region = copy_region;
sw_canvas_ops.get_image = get_image;
rop3_init();
}

View File

@ -16,38 +16,31 @@
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "glc.h"
#ifndef H_SPICE_COMMON_SW_CANVAS
#define H_SPICE_COMMON_SW_CANVAS
#include <stdint.h>
#include <spice/macros.h>
#include "draw.h"
#include "pixman_utils.h"
#include "canvas_base.h"
#include "region.h"
#ifndef SPICE_CANVAS_INTERNAL
#error "This header shouldn't be included directly"
#endif
SPICE_BEGIN_DECLS
#ifndef _H__GL_CANVAS
#define _H__GL_CANVAS
#ifdef __cplusplus
extern "C" {
#endif
SpiceCanvas *gl_canvas_create(int width, int height, uint32_t format
SpiceCanvas *canvas_create_for_data(int width, int height, uint32_t format, uint8_t *data, int stride
, SpiceImageCache *bits_cache
#ifdef SW_CANVAS_CACHE
, SpiceImageCache *bits_cache
, SpicePaletteCache *palette_cache
#elif defined(SW_CANVAS_IMAGE_CACHE)
, SpiceImageCache *bits_cache
#endif
, SpiceImageSurfaces *surfaces
, SpiceGlzDecoder *glz_decoder
, SpiceJpegDecoder *jpeg_decoder
, SpiceZlibDecoder *zlib_decoder
);
void gl_canvas_set_textures_lost(SpiceCanvas *canvas, int textures_lost);
void gl_canvas_init(void);
#ifdef __cplusplus
}
#endif
SPICE_END_DECLS
#endif

78
common/udev.c Normal file
View File

@ -0,0 +1,78 @@
/*
Copyright (C) 2023 Intel Corporation.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include "udev.h"
#ifdef HAVE_UDEV
#include <libudev.h>
#include <stdbool.h>
#include <stdlib.h>
GpuVendor spice_udev_detect_gpu(int gpu_vendor)
{
struct udev *udev;
struct udev_device *drm_dev, *pci_dev;
struct udev_enumerate *udev_enum;
struct udev_list_entry *entry, *devices;
const char *path, *vendor_id;
GpuVendor vendor = VENDOR_GPU_NOTDETECTED;
udev = udev_new();
if (!udev) {
return VENDOR_GPU_UNKNOWN;
}
udev_enum = udev_enumerate_new(udev);
if (udev_enum) {
udev_enumerate_add_match_subsystem(udev_enum, "drm");
udev_enumerate_add_match_sysname(udev_enum, "card[0-9]");
udev_enumerate_scan_devices(udev_enum);
devices = udev_enumerate_get_list_entry(udev_enum);
udev_list_entry_foreach(entry, devices) {
path = udev_list_entry_get_name(entry);
drm_dev = udev_device_new_from_syspath(udev, path);
if (!drm_dev) {
continue;
}
pci_dev = udev_device_get_parent_with_subsystem_devtype(drm_dev,
"pci", NULL);
if (pci_dev) {
vendor_id = udev_device_get_sysattr_value(pci_dev, "vendor");
if (vendor_id && strtol(vendor_id, NULL, 16) == gpu_vendor) {
vendor = VENDOR_GPU_DETECTED;
udev_device_unref(drm_dev);
break;
}
}
udev_device_unref(drm_dev);
}
udev_enumerate_unref(udev_enum);
}
udev_unref(udev);
return vendor;
}
#else
GpuVendor spice_udev_detect_gpu(int gpu_vendor)
{
return VENDOR_GPU_UNKNOWN;
}
#endif

33
common/udev.h Normal file
View File

@ -0,0 +1,33 @@
/*
Copyright (C) 2023 Intel Corporation.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <spice/macros.h>
#define INTEL_VENDOR_ID 0x8086
typedef enum {
VENDOR_GPU_UNKNOWN,
VENDOR_GPU_DETECTED,
VENDOR_GPU_NOTDETECTED,
} GpuVendor;
SPICE_BEGIN_DECLS
GpuVendor spice_udev_detect_gpu(int gpu_vendor);
SPICE_END_DECLS

66
common/utils.c Normal file
View File

@ -0,0 +1,66 @@
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2010, 2011, 2018 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include "utils.h"
/* These 2 functions come from
* libvirt-glib/libvirt-gconfig/libvirt-gconfig-helpers.c
* Copyright (C) 2010, 2011 Red Hat, Inc.
* LGPLv2.1+ licensed */
const char *
spice_genum_get_nick(GType enum_type, gint value)
{
GEnumClass *enum_class;
GEnumValue *enum_value;
g_return_val_if_fail(G_TYPE_IS_ENUM(enum_type), NULL);
enum_class = g_type_class_ref(enum_type);
enum_value = g_enum_get_value(enum_class, value);
g_type_class_unref(enum_class);
if (enum_value != NULL) {
return enum_value->value_nick;
}
g_return_val_if_reached(NULL);
}
int
spice_genum_get_value(GType enum_type, const char *nick,
gint default_value)
{
GEnumClass *enum_class;
GEnumValue *enum_value;
g_return_val_if_fail(G_TYPE_IS_ENUM(enum_type), default_value);
g_return_val_if_fail(nick != NULL, default_value);
enum_class = g_type_class_ref(enum_type);
enum_value = g_enum_get_value_by_nick(enum_class, nick);
g_type_class_unref(enum_class);
if (enum_value != NULL) {
return enum_value->value;
}
g_return_val_if_reached(default_value);
}

57
common/utils.h Normal file
View File

@ -0,0 +1,57 @@
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2010, 2011, 2018 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef H_SPICE_COMMON_UTILS
#define H_SPICE_COMMON_UTILS
#include <glib.h>
#include <glib-object.h>
#include <stdint.h>
G_BEGIN_DECLS
const char *spice_genum_get_nick(GType enum_type, gint value);
int spice_genum_get_value(GType enum_type, const char *nick,
gint default_value);
#define BIT_BYTE(nr) ((nr) / 8)
#define BIT_MASK(nr) (1 << ((nr) % 8))
/**
* set_bitmap - Set a bit in memory
* @nr: the bit to set
* @addr: the address to start counting from
*/
static inline void set_bitmap(uint32_t nr, uint8_t *addr)
{
addr[BIT_BYTE(nr)] |= BIT_MASK(nr);
}
/**
* test_bitmap - Determine whether a bit is set
* @nr: bit number to test
* @addr: Address to start counting from
*/
static inline int test_bitmap(uint32_t nr, const uint8_t *addr)
{
return 1 & (addr[BIT_BYTE(nr)] >> (nr % 8));
}
G_END_DECLS
#endif //H_SPICE_COMMON_UTILS

283
common/verify.h Normal file
View File

@ -0,0 +1,283 @@
/* Compile-time assert-like macros.
Copyright (C) 2005-2006, 2009-2019 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
/* Written by Paul Eggert, Bruno Haible, and Jim Meyering. */
#ifndef _GL_VERIFY_H
#define _GL_VERIFY_H
/* Define _GL_HAVE__STATIC_ASSERT to 1 if _Static_assert works as per C11.
This is supported by GCC 4.6.0 and later, in C mode, and its use
here generates easier-to-read diagnostics when verify (R) fails.
Define _GL_HAVE_STATIC_ASSERT to 1 if static_assert works as per C++11.
This is supported by GCC 6.1.0 and later, in C++ mode.
Use this only with GCC. If we were willing to slow 'configure'
down we could also use it with other compilers, but since this
affects only the quality of diagnostics, why bother? */
#if (4 < __GNUC__ + (6 <= __GNUC_MINOR__) \
&& (201112L <= __STDC_VERSION__ || !defined __STRICT_ANSI__) \
&& !defined __cplusplus)
# define _GL_HAVE__STATIC_ASSERT 1
#endif
#if (6 <= __GNUC__) && defined __cplusplus
# define _GL_HAVE_STATIC_ASSERT 1
#endif
/* FreeBSD 9.1 <sys/cdefs.h>, included by <stddef.h> and lots of other
system headers, defines a conflicting _Static_assert that is no
better than ours; override it. */
#ifndef _GL_HAVE_STATIC_ASSERT
# include <stddef.h>
# undef _Static_assert
#endif
/* Each of these macros verifies that its argument R is nonzero. To
be portable, R should be an integer constant expression. Unlike
assert (R), there is no run-time overhead.
If _Static_assert works, verify (R) uses it directly. Similarly,
_GL_VERIFY_TRUE works by packaging a _Static_assert inside a struct
that is an operand of sizeof.
The code below uses several ideas for C++ compilers, and for C
compilers that do not support _Static_assert:
* The first step is ((R) ? 1 : -1). Given an expression R, of
integral or boolean or floating-point type, this yields an
expression of integral type, whose value is later verified to be
constant and nonnegative.
* Next this expression W is wrapped in a type
struct _gl_verify_type {
unsigned int _gl_verify_error_if_negative: W;
}.
If W is negative, this yields a compile-time error. No compiler can
deal with a bit-field of negative size.
One might think that an array size check would have the same
effect, that is, that the type struct { unsigned int dummy[W]; }
would work as well. However, inside a function, some compilers
(such as C++ compilers and GNU C) allow local parameters and
variables inside array size expressions. With these compilers,
an array size check would not properly diagnose this misuse of
the verify macro:
void function (int n) { verify (n < 0); }
* For the verify macro, the struct _gl_verify_type will need to
somehow be embedded into a declaration. To be portable, this
declaration must declare an object, a constant, a function, or a
typedef name. If the declared entity uses the type directly,
such as in
struct dummy {...};
typedef struct {...} dummy;
extern struct {...} *dummy;
extern void dummy (struct {...} *);
extern struct {...} *dummy (void);
two uses of the verify macro would yield colliding declarations
if the entity names are not disambiguated. A workaround is to
attach the current line number to the entity name:
#define _GL_CONCAT0(x, y) x##y
#define _GL_CONCAT(x, y) _GL_CONCAT0 (x, y)
extern struct {...} * _GL_CONCAT (dummy, __LINE__);
But this has the problem that two invocations of verify from
within the same macro would collide, since the __LINE__ value
would be the same for both invocations. (The GCC __COUNTER__
macro solves this problem, but is not portable.)
A solution is to use the sizeof operator. It yields a number,
getting rid of the identity of the type. Declarations like
extern int dummy [sizeof (struct {...})];
extern void dummy (int [sizeof (struct {...})]);
extern int (*dummy (void)) [sizeof (struct {...})];
can be repeated.
* Should the implementation use a named struct or an unnamed struct?
Which of the following alternatives can be used?
extern int dummy [sizeof (struct {...})];
extern int dummy [sizeof (struct _gl_verify_type {...})];
extern void dummy (int [sizeof (struct {...})]);
extern void dummy (int [sizeof (struct _gl_verify_type {...})]);
extern int (*dummy (void)) [sizeof (struct {...})];
extern int (*dummy (void)) [sizeof (struct _gl_verify_type {...})];
In the second and sixth case, the struct type is exported to the
outer scope; two such declarations therefore collide. GCC warns
about the first, third, and fourth cases. So the only remaining
possibility is the fifth case:
extern int (*dummy (void)) [sizeof (struct {...})];
* GCC warns about duplicate declarations of the dummy function if
-Wredundant-decls is used. GCC 4.3 and later have a builtin
__COUNTER__ macro that can let us generate unique identifiers for
each dummy function, to suppress this warning.
* This implementation exploits the fact that older versions of GCC,
which do not support _Static_assert, also do not warn about the
last declaration mentioned above.
* GCC warns if -Wnested-externs is enabled and verify() is used
within a function body; but inside a function, you can always
arrange to use verify_expr() instead.
* In C++, any struct definition inside sizeof is invalid.
Use a template type to work around the problem. */
/* Concatenate two preprocessor tokens. */
#define _GL_CONCAT(x, y) _GL_CONCAT0 (x, y)
#define _GL_CONCAT0(x, y) x##y
/* _GL_COUNTER is an integer, preferably one that changes each time we
use it. Use __COUNTER__ if it works, falling back on __LINE__
otherwise. __LINE__ isn't perfect, but it's better than a
constant. */
#if defined __COUNTER__ && __COUNTER__ != __COUNTER__
# define _GL_COUNTER __COUNTER__
#else
# define _GL_COUNTER __LINE__
#endif
/* Generate a symbol with the given prefix, making it unique if
possible. */
#define _GL_GENSYM(prefix) _GL_CONCAT (prefix, _GL_COUNTER)
/* Verify requirement R at compile-time, as an integer constant expression
that returns 1. If R is false, fail at compile-time, preferably
with a diagnostic that includes the string-literal DIAGNOSTIC. */
#define _GL_VERIFY_TRUE(R, DIAGNOSTIC) \
(!!sizeof (_GL_VERIFY_TYPE (R, DIAGNOSTIC)))
#ifdef __cplusplus
# if !GNULIB_defined_struct__gl_verify_type
template <int w>
struct _gl_verify_type {
unsigned int _gl_verify_error_if_negative: w;
};
# define GNULIB_defined_struct__gl_verify_type 1
# endif
# define _GL_VERIFY_TYPE(R, DIAGNOSTIC) \
_gl_verify_type<(R) ? 1 : -1>
#elif defined _GL_HAVE__STATIC_ASSERT
# define _GL_VERIFY_TYPE(R, DIAGNOSTIC) \
struct { \
_Static_assert (R, DIAGNOSTIC); \
int _gl_dummy; \
}
#else
# define _GL_VERIFY_TYPE(R, DIAGNOSTIC) \
struct { unsigned int _gl_verify_error_if_negative: (R) ? 1 : -1; }
#endif
/* Verify requirement R at compile-time, as a declaration without a
trailing ';'. If R is false, fail at compile-time, preferably
with a diagnostic that includes the string-literal DIAGNOSTIC.
Unfortunately, unlike C11, this implementation must appear as an
ordinary declaration, and cannot appear inside struct { ... }. */
#ifdef _GL_HAVE__STATIC_ASSERT
# define _GL_VERIFY _Static_assert
#else
# define _GL_VERIFY(R, DIAGNOSTIC) \
extern int (*_GL_GENSYM (_gl_verify_function) (void)) \
[_GL_VERIFY_TRUE (R, DIAGNOSTIC)]
#endif
/* _GL_STATIC_ASSERT_H is defined if this code is copied into assert.h. */
#ifdef _GL_STATIC_ASSERT_H
# if !defined _GL_HAVE__STATIC_ASSERT && !defined _Static_assert
# define _Static_assert(R, DIAGNOSTIC) _GL_VERIFY (R, DIAGNOSTIC)
# endif
# if !defined _GL_HAVE_STATIC_ASSERT && !defined static_assert
# define static_assert _Static_assert /* C11 requires this #define. */
# endif
#endif
/* @assert.h omit start@ */
/* Each of these macros verifies that its argument R is nonzero. To
be portable, R should be an integer constant expression. Unlike
assert (R), there is no run-time overhead.
There are two macros, since no single macro can be used in all
contexts in C. verify_true (R) is for scalar contexts, including
integer constant expression contexts. verify (R) is for declaration
contexts, e.g., the top level. */
/* Verify requirement R at compile-time, as an integer constant expression.
Return 1. This is equivalent to verify_expr (R, 1).
verify_true is obsolescent; please use verify_expr instead. */
#define verify_true(R) _GL_VERIFY_TRUE (R, "verify_true (" #R ")")
/* Verify requirement R at compile-time. Return the value of the
expression E. */
#define verify_expr(R, E) \
(_GL_VERIFY_TRUE (R, "verify_expr (" #R ", " #E ")") ? (E) : (E))
/* Verify requirement R at compile-time, as a declaration without a
trailing ';'. */
#ifdef __GNUC__
# define verify(R) _GL_VERIFY (R, "verify (" #R ")")
#else
/* PGI barfs if R is long. Play it safe. */
# define verify(R) _GL_VERIFY (R, "verify (...)")
#endif
#ifndef __has_builtin
# define __has_builtin(x) 0
#endif
/* Assume that R always holds. This lets the compiler optimize
accordingly. R should not have side-effects; it may or may not be
evaluated. Behavior is undefined if R is false. */
#if (__has_builtin (__builtin_unreachable) \
|| 4 < __GNUC__ + (5 <= __GNUC_MINOR__))
# define assume(R) ((R) ? (void) 0 : __builtin_unreachable ())
#elif 1200 <= _MSC_VER
# define assume(R) __assume (R)
#elif ((defined GCC_LINT || defined lint) \
&& (__has_builtin (__builtin_trap) \
|| 3 < __GNUC__ + (3 < __GNUC_MINOR__ + (4 <= __GNUC_PATCHLEVEL__))))
/* Doing it this way helps various packages when configured with
--enable-gcc-warnings, which compiles with -Dlint. It's nicer
when 'assume' silences warnings even with older GCCs. */
# define assume(R) ((R) ? (void) 0 : __builtin_trap ())
#else
/* Some tools grok NOTREACHED, e.g., Oracle Studio 12.6. */
# define assume(R) ((R) ? (void) 0 : /*NOTREACHED*/ (void) 0)
#endif
/* @assert.h omit end@ */
#endif

81
configure.ac Normal file
View File

@ -0,0 +1,81 @@
AC_PREREQ([2.63])
AC_INIT([spice-common],
[noversion],
[spice-devel@lists.freedesktop.org])
AC_CONFIG_SRCDIR([common/log.h])
AC_CONFIG_MACRO_DIR([m4])
AM_CONFIG_HEADER([config.h])
AC_CONFIG_AUX_DIR([build-aux])
# For automake >= 1.12
m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
# Checks for programs
AM_INIT_AUTOMAKE([1.11 dist-xz no-dist-gzip tar-ustar foreign subdir-objects -Wall -Werror])
AM_MAINTAINER_MODE
AM_SILENT_RULES([yes])
LT_INIT
SPICE_MANUAL
AC_PROG_CC
AC_PROG_CC_C99
if test "x$ac_cv_prog_cc_c99" = xno; then
AC_MSG_ERROR([C99 compiler is required.])
fi
AM_PROG_CC_C_O
AC_CHECK_HEADERS([sys/mman.h regex.h])
AC_CHECK_FUNCS([sigaction drand48 setlinebuf])
AC_SEARCH_LIBS(regcomp, [regex rx])
SPICE_CHECK_SYSDEPS
SPICE_EXTRA_CHECKS
AC_ARG_ENABLE([tests],
AS_HELP_STRING([--enable-tests],
[Enable tests @<:@default=yes@:>@]),
[],
enable_tests="yes")
AM_CONDITIONAL(ENABLE_TESTS, test "x$enable_tests" = "xyes")
AC_ARG_ENABLE([alignment-checks],
AS_HELP_STRING([--enable-alignment-checks],
[Enable runtime checks for cast alignment @<:@default=no@:>@]),
[],
enable_alignment_checks="no")
AS_IF([test "x$enable_alignment_checks" = "xyes"],
[AC_DEFINE([SPICE_DEBUG_ALIGNMENT], 1, [Enable runtime checks for cast alignment])])
SPICE_CHECK_INSTRUMENTATION
# Checks for libraries
PKG_CHECK_MODULES([PROTOCOL], [spice-protocol >= 0.14.2])
SPICE_CHECK_PYTHON_MODULES()
SPICE_CHECK_PIXMAN
SPICE_CHECK_SMARTCARD
SPICE_CHECK_GLIB2
SPICE_CHECK_OPUS
SPICE_CHECK_OPENSSL
SPICE_CHECK_GDK_PIXBUF
SPICE_CHECK_UDEV
SPICE_COMMON_CFLAGS='$(PIXMAN_CFLAGS) $(SMARTCARD_CFLAGS) $(GLIB2_CFLAGS) $(OPUS_CFLAGS) $(OPENSSL_CFLAGS)'
SPICE_COMMON_CFLAGS="$SPICE_COMMON_CFLAGS -DG_LOG_DOMAIN=\\\"Spice\\\""
SPICE_COMMON_LIBS='$(PIXMAN_LIBS) $(GLIB2_LIBS) $(OPUS_LIBS) $(OPENSSL_LIBS)'
AC_SUBST(SPICE_COMMON_CFLAGS)
AC_SUBST(SPICE_COMMON_LIBS)
# The End!
AC_CONFIG_FILES([
Makefile
common/Makefile
python_modules/Makefile
tests/Makefile
docs/Makefile
])
AC_OUTPUT

27
docs/Makefile.am Normal file
View File

@ -0,0 +1,27 @@
NULL =
CLEANFILES =
ASCIIDOC_FILES = \
spice_protocol.txt \
spice_uri_scheme.txt \
$(NULL)
ASCIIDOC_FLAGS = -n -a icons -a toc
EXTRA_DIST = \
$(ASCIIDOC_FILES) \
meson.build \
$(NULL)
if BUILD_HTML_MANUAL
ASCIIDOC_HTML = $(ASCIIDOC_FILES:.txt=.html)
CLEANFILES += $(ASCIIDOC_HTML)
all-local: $(ASCIIDOC_HTML)
.txt.html:
$(AM_V_GEN)$(ASCIIDOC) $(ASCIIDOC_FLAGS) -o $@ $<
endif
-include $(top_srcdir)/git.mk

12
docs/meson.build Normal file
View File

@ -0,0 +1,12 @@
if get_option('manual')
asciidoc = find_program('asciidoc', required : false)
if asciidoc.found()
foreach f: ['spice_protocol.txt', 'spice_uri_scheme.txt']
custom_target('HTML for @0@'.format(f),
input : f,
output : '@BASENAME@.html',
build_by_default : true,
command : [asciidoc, '-n', '-a', 'icons', '-a', 'toc', '-o', '@OUTPUT@', '@INPUT@'])
endforeach
endif
endif

477
docs/spice_protocol.txt Normal file
View File

@ -0,0 +1,477 @@
Spice protocol format file
==========================
Copyright (C) 2016 Red Hat, Inc.
Licensed under a Creative Commons Attribution-Share Alike 3.0
United States License (see http://creativecommons.org/licenses/by-sa/3.0/us/legalcode).
Basic
-----
The spice protocol format file defines the network protocol used by spice.
It resemble the C format.
file ::= <definitions> <protocol> ;
definitions ::= <definition>|<definitions><definition> ;
definition ::= <typedef>|<structure>|<enum>|<flag>|<message>|<channel> ;
protocol ::= "protocol" <identifier> "{" <protocol_channels> "}" ";" ;
protocol_channels ::= <protocol_channel>|<protocol_channels><protocol_channel> ;
protocol_channel ::= <identifier> <identifier> [ "=" <integer> ] ";" ;
integer ::= <hex>|<dec> ;
dec ::= [+-][0-9]+ ;
hex ::= "0x" [0-9a-f]+ ;
identifier ::= [a-z][a-z0-9_]* ;
(here BNF with some regular expression is used).
It's used to generate automatically code to marshal/demarshal the network data.
Example:
channel ExampleChannel {
message {
uint32 dummy;
} Dummy;
};
protocol Example {
ExampleChannel first = 1001;
};
As you can see brackets like C are used and structures looks like C but you
can also see that keywords like `channel`, `protocol`, `message` or some
predefined types like `uint32` are proper of the protocol.
Comments
--------
Both C and C++ style comments are supported
// this is a comment
/* this is a comment too
but can be split in multiple lines */
Base types
----------
All int from 8 to 64 bit (8, 16, 32 and 64) are supported either signed or unsigned.
Also you can pass one unix descriptor.
base_type ::= "int8"|"uint8"|"int16"|"uint16"|"int32"|"uint32"|"int64"|"uint64"|"unix_fd" ;
Example:
int16 x;
Enumerations and flags
----------------------
It's possible to specify enumerations and flags. The difference is that flags are defined as 2 power
values and can be combined. Enumerations and flags must have a size (`8`, `16` or `32`) specified.
enum ::= <enum_type> "{" [ <enumflag_items> ] "}" <attributes> ";" ;
flag ::= <flag_type> "{" [ <enumflag_items> ] "}" <attributes> ";" ;
enum_type ::= "enum8"|"enum16"|"enum32" ;
flag_type ::= "flag8"|"flag16"|"flag32" ;
enumflag_items ::= <enumflag_item>|<enumflag_items><enumflag_item>
enumflag_item ::= <enum_name> [ "=" <integer> ] [ "," ] ;
enum_name ::= [a-z0-9_]* ;
Example:
enum16 Level {
LOW = 0x100,
MEDIUM,
HIGH = 0x1000
};
Variables
---------
As you should already have noted variables are similar to C syntax but there are
some differences.
variable ::= <type> [ "*" ] <identifier> [ "[" <array_size> "]" ] <attributes>;
The `*` specify a pointer. This is quite different from C. For the protocol it
specifies that in the protocol stream a relative offset is put that points to that
variable usually after all defined fields. This happens even on arrays, so for instance
int32 *n;
containing a 0x12345678 `n` value could ended up coded as
04 00 00 00 // 4 as offset
78 56 34 12 // `n`
(little endian). While an array of 2 items defined as
int32 *n[2];
and containing 0x12345678 and 0x9abcdef could end up with
04 00 00 00 // 4 as offset
78 56 34 12 // `n`[0]
ef cd ab 09 // `n`[1]
note that `int32 *n[2]` defined a pointer to an array of 2 items and not
an array of pointers as C.
*WARNING*: You should avoid using pointers on protocol if not necessary as they are complicated
to handle not using autogenerated code and also use more space on the network.
Arrays
------
As seen above the easiest way to define an array size is specifying a constant value.
However there are multiple way to specify the size
array_size ::= <integer>|<identifier>|""|<array_size_image>|<array_size_cstring> ;
array_size_image ::= "image_size" "(" <integer> "," <identifier> ")" ;
array_size_cstring ::= "cstring()" ;
We already seen integer.
Specifying an identifier name instead (should be variable) indicate that the length is specified
in another field, for instance
uint8 name_len;
int8 name[name_len];
allows to put a name of `name_len` len.
The empty value tells that the array will end when the containing message end so if we have
int8 name[];
and the message is
66 6f 6f
possibly the name we want is `foo` (66 6f 6f is the ASCII encoding for `foo`).
TODO: what happen with two [] in the structure ??
TODO: can a [] array not be the last and what happens ??
`image_size` allow to specify an array holding an image, for instance
uint16 width;
uint16 height;
uint8 raw_image[image_size(8, width, height)];
could contain row data in raw_image. The constant `8` is the bit size of the image.
`cstring` allows to specify NUL-terminated sequence so having
int8 name[cstring()];
and the message as
66 6f 6f 00
we'll have the `foo` name. Note that the field does not need to end the message as in `int8 name[]` example.
Structures
----------
The simplest compound type is the structure. As in C is defined as a list of fields (any variable or switch).
But as a protocol definition there are no alignment or padding and every field (beside pointer values) follow each other.
struct ::= "struct" <identifier> "{" [ <fields> ] "}" <attributes> ";" ;
fields ::= <field>|<fields><field> ;
field ::= <variable>|<switch>
Example:
struct Point {
int32 x;
int32 y;
};
Messages
--------
Messages have the same syntax of structure (beside `message`) with the difference that they can
be used directly inside channels.
message ::= "message" <identifier> "{" [ <fields> ] "}" <attributes> ";" ;
Switches
--------
TODO
Type definitions
----------------
Like C type definition allow to short types defining new ones.
typedef ::= "typedef" <identifier> <type> <attributes> ;
note that unlike C name came before the type.
Example:
typedef XCoord int32;
Channels
--------
channel ::= "channel" <identifier> [ ":" <identifier> ] "{" <channel_messages> "}" <attributes> ";" ;
channel_messages ::= <channel_message>|<channel_messages><channel_message> ;
channel_message ::= "server:" | "client:" | "message" <identifier> [ "=" <integer> ] ;
Example:
channel ExampleChannel {
server:
message {
uint32 dummy;
} Dummy;
};
Note that every message is associated with a number which is used in the protocol.
The assignment work in a similar way to enumeration in C beside first message is
assigned 1 value and not 0. So first message (if no integer is specified) is assigned
1, second 2 and so on.
`server:` or `client:` specify the direction of messages following, `server` specify
messages from server while `client` from client. If not specified is assumed from
server.
For each channel you can specify a parent channel. Derived channel inherits all
messages specified in the parent.
Note that messages from parent can be overridden by derived channels.
Protocol
--------
protocol ::= "protocol" <identifier> "{" <protocol_channels> "}" ";" ;
protocol_channels ::= <protocol_channel>|<protocol_channels><protocol_channel> ;
protocol_channel ::= <identifier> <identifier> [ "=" <integer> ] ";" ;
Example:
protocol Example {
ExampleChannel first = 1001;
};
Protocol specify the list of channel supported. Channel have an associated number
assigned in a similar way of channels (incremented from one to the next with
first starting from 0 if not specified).
*NOTE*: Due to the way currently code is generate you should use
small numbers.
Attributes
----------
As you probably noted attributed can be specified for lot of definitions.
They allow to change code generated or specific constraints of the protocol.
attributes ::= ""|<attributes><attribute>|<attribute> ;
attribute ::= <attribute_name> [ "(" <attribute_values> ")" ] ;
attribute_values ::= <attribute_values> "," <attribute_value> | <attribute_value>
attribute_value ::= <integer> | <identifier>
attribute_name ::= @[a-z][a-z0-9_]* ;
Mostly of the attributes have no arguments, other currently have only one
argument.
*NOTE*: Some comments are also written in `spice-common` `python_modules/ptypes.py`
source file.
ctype
~~~~~
Specify the structure type name that the generated marshaller/demarshaller code
will use. By default the name will be converted to CamelCase and prefixed by
`Spice` so for example a structure like
struct Point {
int32 x;
int32 y;
} @ctype(MyPoint);
will be marshalled into a C structure like
struct MyPoint {
int32_t x;
int32_t y;
};
prefix
~~~~~~
This attribute allows to specify the prefix used for generated enumerations (both
protocol enumerations and flags generate C enumerations). By default the enumeration
will use upper case of the enum/flag name prefixed with `SPICE_` and followed by item so
enum32 level {
LOW,
HIGH,
};
will generate
typedef enum SpiceLevel {
SPICE_LEVEL_LOW,
SPICE_LEVEL_HIGH,
SPICE_LEVEL_ENUM_END
} SpiceLevel;
while
enum32 level {
LOW,
HIGH,
} @prefix(LVL_);
will generate
typedef enum SpiceLevel {
LVL_LOW,
LVL_HIGH,
SPICE_LEVEL_ENUM_END
} SpiceLevel;
(note that an automatic `END` enumeration is generated and name is not affected).
end
~~~
This attribute specifies that the data will be appended/embedded in the final C structure.
Example:
struct test {
uint16 len;
uint16 array[len] @end;
};
Output C structure:
struct test {
uint16_t len;
uint16_t array[0];
};
The generated code will allocate the C structure to allow space for extracted array.
*WARNING*: This option is usually confused with with empty size protocol. The
empty protocol array size specify array that extend on the network data while
the `@end` attribute specify to extend the C structure (for instance in the example
the attribute was attached to a `len`-sized array).
to_ptr
~~~~~~
This specifies that the corresponding C structure field contains a pointer to
the data. On marshalling the pointer is used, on demarshalling the data is
allocated in the memory block that holds the returned structure.
The type of this field must be a structure.
Example:
struct test {
uint16 num;
};
struct msg {
test ptr @to_ptr;
};
Output C structure:
struct test {
uint16_t num;
};
struct msg {
struct test *ptr;
};
nocopy
~~~~~~
TODO
as_ptr
~~~~~~
TODO
nomarshal
~~~~~~~~~
Do not generate code for marshalling this variable.
Usually used on last array element to make possible to manually feed data.
Example:
struct Data {
uint32 data_size;
uint8 data[data_size] @nomarshal;
};
zero_terminated
~~~~~~~~~~~~~~~
The field should terminated by zero.
Actually it's not used by python code so it's not enforced and no
code is generated.
marshall
~~~~~~~~
TODO
nonnull
~~~~~~~
This pointer field cannot be NULL. This means that marshaller assume C structure
contain not NULL pointer and demarshaller will fail to demarshall message if offset
is 0.
unique_flag
~~~~~~~~~~~
This flag field should contain just a single flag.
Actually it's not used by python code so it's not enforced and no
code is generated.
deprecated
~~~~~~~~~~
This flag currently apply only to enumerations and flags types and will set
generated C enumeration constant to deprecated
ptr_array
~~~~~~~~~
TODO
outvar
~~~~~~
TODO
anon
~~~~
TODO
chunk
~~~~~
TODO
ifdef
~~~~~
TODO
zero
~~~~
TODO
virtual
~~~~~~~
TODO

131
docs/spice_uri_scheme.txt Normal file
View File

@ -0,0 +1,131 @@
The "spice" URI scheme
======================
This document is inspired by 'The "vnc" URI Scheme' (rfc7869) and
attempts to document a standard Spice URI scheme.
The normative syntax of the Spice URI is defined in the <spice-uri>
rule in the following syntax specification. This specification
uses the Augmented Backus-Naur Form (ABNF) as described in
[RFC5234]. The Spice URI conforms to the generic URI syntax
specified in [RFC3986]. The <userinfo>, <host>, <port>,
<unreserved>, and <pct-encoded> rules are defined in [RFC3986].
spice-uri = spice-scheme "://" [ userinfo "@" ] [ host [ ":" port ] ]
[ "?" [ spice-params ] ]
spice-scheme = "spice" / "spice+unix" / "spice+tls"
spice-params = param "=" value *("&" param "=" value) ["&"]
param = 1*( param-char )
value = *( param-char )
param-char = unreserved / pct-encoded / unreserved-symbols
unreserved-symbols = ":" / "/" / "@" / "!" / "$" / "'"
/ "(" / ")" / "*" / "," / ";"
The "?", "=", and "&" characters are used to delimit Spice parameters
and must be percent-encoded when representing a data octet as
specified in [RFC3986]. Within the <spice-params> portion of a Spice
URI, the <unreserved-symbols> do not have special meaning and need not
be percent-encoded when representing a data octet.
A Spice URI has the general form:
spice-scheme://host:port?param1=value1&param2=value2...
The host information and each parameter value specify information
used in establishing or operating the remote desktop session as
specified in Section "URI Parameters".
URI Parameters
--------------
A description of host information and URI parameters is provided in
this section. Information on the constraints of various data types is
provided in Section "Data Types". All parameters are considered optional;
however, a client will not be able to connect without sufficient
information.
A parameter without a specified default value indicates that no
default value is implied by this URI scheme; however, Spice clients
can apply implementation-dependent default behaviors otherwise
consistent with this document.
The <host> and <port> values in the "spice://" and "spice+tls://" URIs
specify the address of the Spice server on the remote host:
[options="header"]
|=======================================================================
| Name | Type | Description | Default
| host | string | Spice server hostname or IP | none
| port | ushort | Spice server port | none
|=======================================================================
The <host> value in the "spice+unix://" URI specify the UNIX domain
socket path of the Spice server on the local host:
[options="header"]
|=======================================================================
| Name | Type | Description | Default
| host | string | UNIX domain socket path | none
|=======================================================================
The Spice URI parameter values specify remote desktop connection or
session properties, including aspects of client operation, usability,
and security as specified in the table below:
[options="header"]
|=======================================================================
| Name | Type | Description | Default
| port | ushort | Spice server port (legacy) | none
| tls-port | ushort | Spice server TLS port (legacy) | none
| password | string | Spice server password (legacy) | none
| ... | | |
|=======================================================================
Parameter names SHOULD be provided in the case specified in this
document; however, for compatibility, clients SHOULD accept
parameters in a case-insensitive manner. Values SHALL be interpreted
in a case-sensitive manner, unless otherwise noted.
Additional parameters likely to be useful with multiple Spice clients
can be added to the "URI Parameters" registry (at the moment,
discussed and approved on the Spice mailing list). Individual clients
MAY support parameters specific to that client. Spice clients
supporting application-specific parameters SHOULD include a
distinguishing prefix within the parameter name, such as the name of
the application package specified in source code except when precluded
by compatibility constraints. For example:
spice://?com.redhat.spiceclient.MonitorMapping=2&
It can also be expected that clients will maintain backward
compatibility with legacy URI formats and parameters.
Legacy software applications respond to "spice" URIs in different ways
and may fail to behave as expected. It is advisable to test "spice"
URIs with specific applications or consult application-specific
documentation.
Data Types
----------
Spice URIs can be percent-encoded as specified in [RFC3986] and MUST
be decoded. After decoding, the following type constraints and
semantics apply:
string
~~~~~~
Values of "string" type are UTF8-encoded strings as specified in
[RFC3629].
ushort
~~~~~~
The "ushort" values represent unsigned 16-bit integers expressed
in decimal digits with value between 0-65535 inclusive.

File diff suppressed because it is too large Load Diff

View File

@ -1,51 +0,0 @@
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2009 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _H__GDI_CANVAS
#define _H__GDI_CANVAS
#ifndef SPICE_CANVAS_INTERNAL
#error "This header shouldn't be included directly"
#endif
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#include "pixman_utils.h"
#include "canvas_base.h"
#include "region.h"
SpiceCanvas *gdi_canvas_create(int width, int height,
HDC dc, class RecurciveMutex *lock, uint32_t format,
SpiceImageCache *bits_cache,
SpicePaletteCache *palette_cache,
SpiceImageSurfaces *surfaces,
SpiceGlzDecoder *glz_decoder,
SpiceJpegDecoder *jpeg_decoder,
SpiceZlibDecoder *zlib_decoder);
void gdi_canvas_init(void);
#ifdef __cplusplus
}
#endif
#endif

400
git.mk Normal file
View File

@ -0,0 +1,400 @@
# git.mk, a small Makefile to autogenerate .gitignore files
# for autotools-based projects.
#
# Copyright 2009, Red Hat, Inc.
# Copyright 2010,2011,2012,2013 Behdad Esfahbod
# Written by Behdad Esfahbod
#
# Copying and distribution of this file, with or without modification,
# is permitted in any medium without royalty provided the copyright
# notice and this notice are preserved.
#
# The latest version of this file can be downloaded from:
GIT_MK_URL = https://raw.githubusercontent.com/behdad/git.mk/master/git.mk
#
# Bugs, etc, should be reported upstream at:
# https://github.com/behdad/git.mk
#
# To use in your project, import this file in your git repo's toplevel,
# then do "make -f git.mk". This modifies all Makefile.am files in
# your project to -include git.mk. Remember to add that line to new
# Makefile.am files you create in your project, or just rerun the
# "make -f git.mk".
#
# This enables automatic .gitignore generation. If you need to ignore
# more files, add them to the GITIGNOREFILES variable in your Makefile.am.
# But think twice before doing that. If a file has to be in .gitignore,
# chances are very high that it's a generated file and should be in one
# of MOSTLYCLEANFILES, CLEANFILES, DISTCLEANFILES, or MAINTAINERCLEANFILES.
#
# The only case that you need to manually add a file to GITIGNOREFILES is
# when remove files in one of mostlyclean-local, clean-local, distclean-local,
# or maintainer-clean-local make targets.
#
# Note that for files like editor backup, etc, there are better places to
# ignore them. See "man gitignore".
#
# If "make maintainer-clean" removes the files but they are not recognized
# by this script (that is, if "git status" shows untracked files still), send
# me the output of "git status" as well as your Makefile.am and Makefile for
# the directories involved and I'll diagnose.
#
# For a list of toplevel files that should be in MAINTAINERCLEANFILES, see
# Makefile.am.sample in the git.mk git repo.
#
# Don't EXTRA_DIST this file. It is supposed to only live in git clones,
# not tarballs. It serves no useful purpose in tarballs and clutters the
# build dir.
#
# This file knows how to handle autoconf, automake, libtool, gtk-doc,
# gnome-doc-utils, yelp.m4, mallard, intltool, gsettings, dejagnu, appdata,
# appstream, hotdoc.
#
# This makefile provides the following targets:
#
# - all: "make all" will build all gitignore files.
# - gitignore: makes all gitignore files in the current dir and subdirs.
# - .gitignore: make gitignore file for the current dir.
# - gitignore-recurse: makes all gitignore files in the subdirs.
#
# KNOWN ISSUES:
#
# - Recursive configure doesn't work as $(top_srcdir)/git.mk inside the
# submodule doesn't find us. If you have configure.{in,ac} files in
# subdirs, add a proxy git.mk file in those dirs that simply does:
# "include $(top_srcdir)/../git.mk". Add more ..'s to your taste.
# And add those files to git. See vte/gnome-pty-helper/git.mk for
# example.
#
###############################################################################
# Variables user modules may want to add to toplevel MAINTAINERCLEANFILES:
###############################################################################
#
# Most autotools-using modules should be fine including this variable in their
# toplevel MAINTAINERCLEANFILES:
GITIGNORE_MAINTAINERCLEANFILES_TOPLEVEL = \
$(srcdir)/aclocal.m4 \
$(srcdir)/autoscan.log \
$(srcdir)/configure.scan \
`AUX_DIR=$(srcdir)/$$(cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_AUX_DIR:$$1' ./configure.ac); \
test "x$$AUX_DIR" = "x$(srcdir)/" && AUX_DIR=$(srcdir); \
for x in \
ar-lib \
compile \
config.guess \
config.rpath \
config.sub \
depcomp \
install-sh \
ltmain.sh \
missing \
mkinstalldirs \
test-driver \
ylwrap \
; do echo "$$AUX_DIR/$$x"; done` \
`cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_HEADERS:$$1' ./configure.ac | \
head -n 1 | while read f; do echo "$(srcdir)/$$f.in"; done`
#
# All modules should also be fine including the following variable, which
# removes automake-generated Makefile.in files:
GITIGNORE_MAINTAINERCLEANFILES_MAKEFILE_IN = \
`cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_FILES:$$1' ./configure.ac | \
while read f; do \
case $$f in Makefile|*/Makefile) \
test -f "$(srcdir)/$$f.am" && echo "$(srcdir)/$$f.in";; esac; \
done`
#
# Modules that use libtool and use AC_CONFIG_MACRO_DIR() may also include this,
# though it's harmless to include regardless.
GITIGNORE_MAINTAINERCLEANFILES_M4_LIBTOOL = \
`MACRO_DIR=$(srcdir)/$$(cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_MACRO_DIR:$$1' ./configure.ac); \
if test "x$$MACRO_DIR" != "x$(srcdir)/"; then \
for x in \
libtool.m4 \
ltoptions.m4 \
ltsugar.m4 \
ltversion.m4 \
lt~obsolete.m4 \
; do echo "$$MACRO_DIR/$$x"; done; \
fi`
#
# Modules that use gettext and use AC_CONFIG_MACRO_DIR() may also include this,
# though it's harmless to include regardless.
GITIGNORE_MAINTAINERCLEANFILES_M4_GETTEXT = \
`MACRO_DIR=$(srcdir)/$$(cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_MACRO_DIR:$$1' ./configure.ac); \
if test "x$$MACRO_DIR" != "x$(srcdir)/"; then \
for x in \
codeset.m4 \
extern-inline.m4 \
fcntl-o.m4 \
gettext.m4 \
glibc2.m4 \
glibc21.m4 \
iconv.m4 \
intdiv0.m4 \
intl.m4 \
intldir.m4 \
intlmacosx.m4 \
intmax.m4 \
inttypes-pri.m4 \
inttypes_h.m4 \
lcmessage.m4 \
lib-ld.m4 \
lib-link.m4 \
lib-prefix.m4 \
lock.m4 \
longlong.m4 \
nls.m4 \
po.m4 \
printf-posix.m4 \
progtest.m4 \
size_max.m4 \
stdint_h.m4 \
threadlib.m4 \
uintmax_t.m4 \
visibility.m4 \
wchar_t.m4 \
wint_t.m4 \
xsize.m4 \
; do echo "$$MACRO_DIR/$$x"; done; \
fi`
###############################################################################
# Default rule is to install ourselves in all Makefile.am files:
###############################################################################
git-all: git-mk-install
git-mk-install:
@echo "Installing git makefile"
@any_failed=; \
find "`test -z "$(top_srcdir)" && echo . || echo "$(top_srcdir)"`" -name Makefile.am | while read x; do \
if grep 'include .*/git.mk' $$x >/dev/null; then \
echo "$$x already includes git.mk"; \
else \
failed=; \
echo "Updating $$x"; \
{ cat $$x; \
echo ''; \
echo '-include $$(top_srcdir)/git.mk'; \
} > $$x.tmp || failed=1; \
if test x$$failed = x; then \
mv $$x.tmp $$x || failed=1; \
fi; \
if test x$$failed = x; then : else \
echo "Failed updating $$x"; >&2 \
any_failed=1; \
fi; \
fi; done; test -z "$$any_failed"
git-mk-update:
wget $(GIT_MK_URL) -O $(top_srcdir)/git.mk
.PHONY: git-all git-mk-install git-mk-update
###############################################################################
# Actual .gitignore generation:
###############################################################################
$(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk $(top_srcdir)/configure.ac
@echo "git.mk: Generating $@"
@{ \
if test "x$(DOC_MODULE)" = x -o "x$(DOC_MAIN_SGML_FILE)" = x; then :; else \
for x in \
$(DOC_MODULE)-decl-list.txt \
$(DOC_MODULE)-decl.txt \
tmpl/$(DOC_MODULE)-unused.sgml \
"tmpl/*.bak" \
$(REPORT_FILES) \
$(DOC_MODULE).pdf \
xml html \
; do echo "/$$x"; done; \
FLAVOR=$$(cd $(top_srcdir); $(AUTOCONF) --trace 'GTK_DOC_CHECK:$$2' ./configure.ac); \
case $$FLAVOR in *no-tmpl*) echo /tmpl;; esac; \
if echo "$(SCAN_OPTIONS)" | grep -q "\-\-rebuild-types"; then \
echo "/$(DOC_MODULE).types"; \
fi; \
if echo "$(SCAN_OPTIONS)" | grep -q "\-\-rebuild-sections"; then \
echo "/$(DOC_MODULE)-sections.txt"; \
fi; \
if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \
for x in \
$(SETUP_FILES) \
$(DOC_MODULE).types \
; do echo "/$$x"; done; \
fi; \
fi; \
if test "x$(DOC_MODULE)$(DOC_ID)" = x -o "x$(DOC_LINGUAS)" = x; then :; else \
for lc in $(DOC_LINGUAS); do \
for x in \
$(if $(DOC_MODULE),$(DOC_MODULE).xml) \
$(DOC_PAGES) \
$(DOC_INCLUDES) \
; do echo "/$$lc/$$x"; done; \
done; \
for x in \
$(_DOC_OMF_ALL) \
$(_DOC_DSK_ALL) \
$(_DOC_HTML_ALL) \
$(_DOC_MOFILES) \
$(DOC_H_FILE) \
"*/.xml2po.mo" \
"*/*.omf.out" \
; do echo /$$x; done; \
fi; \
if test "x$(HOTDOC)" = x; then :; else \
$(foreach project, $(HOTDOC_PROJECTS),echo "/$(call HOTDOC_TARGET,$(project))"; \
echo "/$(shell $(call HOTDOC_PROJECT_COMMAND,$(project)) --get-conf-path output)" ; \
echo "/$(shell $(call HOTDOC_PROJECT_COMMAND,$(project)) --get-private-folder)" ; \
) \
for x in \
.hotdoc.d \
; do echo "/$$x"; done; \
fi; \
if test "x$(HELP_ID)" = x -o "x$(HELP_LINGUAS)" = x; then :; else \
for lc in $(HELP_LINGUAS); do \
for x in \
$(HELP_FILES) \
"$$lc.stamp" \
"$$lc.mo" \
; do echo "/$$lc/$$x"; done; \
done; \
fi; \
if test "x$(gsettings_SCHEMAS)" = x; then :; else \
for x in \
$(gsettings_SCHEMAS:.xml=.valid) \
$(gsettings__enum_file) \
; do echo "/$$x"; done; \
fi; \
if test "x$(appdata_XML)" = x; then :; else \
for x in \
$(appdata_XML:.xml=.valid) \
; do echo "/$$x"; done; \
fi; \
if test "x$(appstream_XML)" = x; then :; else \
for x in \
$(appstream_XML:.xml=.valid) \
; do echo "/$$x"; done; \
fi; \
if test -f $(srcdir)/po/Makefile.in.in; then \
for x in \
ABOUT-NLS \
po/Makefile.in.in \
po/Makefile.in.in~ \
po/Makefile.in \
po/Makefile \
po/Makevars.template \
po/POTFILES \
po/Rules-quot \
po/stamp-it \
po/stamp-po \
po/.intltool-merge-cache \
"po/*.gmo" \
"po/*.header" \
"po/*.mo" \
"po/*.sed" \
"po/*.sin" \
po/$(GETTEXT_PACKAGE).pot \
intltool-extract.in \
intltool-merge.in \
intltool-update.in \
; do echo "/$$x"; done; \
fi; \
if test -f $(srcdir)/configure; then \
for x in \
autom4te.cache \
configure \
config.h \
stamp-h1 \
libtool \
config.lt \
; do echo "/$$x"; done; \
fi; \
if test "x$(DEJATOOL)" = x; then :; else \
for x in \
$(DEJATOOL) \
; do echo "/$$x.sum"; echo "/$$x.log"; done; \
echo /site.exp; \
fi; \
if test "x$(am__dirstamp)" = x; then :; else \
echo "$(am__dirstamp)"; \
fi; \
if test "x$(findstring libtool,$(LTCOMPILE))" = x -a "x$(findstring libtool,$(LTCXXCOMPILE))" = x -a "x$(GTKDOC_RUN)" = x; then :; else \
for x in \
"*.lo" \
".libs" "_libs" \
; do echo "$$x"; done; \
fi; \
for x in \
.gitignore \
$(GITIGNOREFILES) \
$(CLEANFILES) \
$(PROGRAMS) $(check_PROGRAMS) $(EXTRA_PROGRAMS) \
$(LIBRARIES) $(check_LIBRARIES) $(EXTRA_LIBRARIES) \
$(LTLIBRARIES) $(check_LTLIBRARIES) $(EXTRA_LTLIBRARIES) \
so_locations \
$(MOSTLYCLEANFILES) \
$(TEST_LOGS) \
$(TEST_LOGS:.log=.trs) \
$(TEST_SUITE_LOG) \
$(TESTS:=.test) \
"*.gcda" \
"*.gcno" \
$(DISTCLEANFILES) \
$(am__CONFIG_DISTCLEAN_FILES) \
$(CONFIG_CLEAN_FILES) \
TAGS ID GTAGS GRTAGS GSYMS GPATH tags \
"*.tab.c" \
$(MAINTAINERCLEANFILES) \
$(BUILT_SOURCES) \
$(patsubst %.vala,%.c,$(filter %.vala,$(SOURCES))) \
$(filter %_vala.stamp,$(DIST_COMMON)) \
$(filter %.vapi,$(DIST_COMMON)) \
$(filter $(addprefix %,$(notdir $(patsubst %.vapi,%.h,$(filter %.vapi,$(DIST_COMMON))))),$(DIST_COMMON)) \
Makefile \
Makefile.in \
"*.orig" \
"*.rej" \
"*.bak" \
"*~" \
".*.sw[nop]" \
".dirstamp" \
; do echo "/$$x"; done; \
for x in \
"*.$(OBJEXT)" \
$(DEPDIR) \
; do echo "$$x"; done; \
} | \
sed "s@^/`echo "$(srcdir)" | sed 's/\(.\)/[\1]/g'`/@/@" | \
sed 's@/[.]/@/@g' | \
LC_ALL=C sort | uniq > $@.tmp && \
mv $@.tmp $@;
all: $(srcdir)/.gitignore gitignore-recurse-maybe
gitignore: $(srcdir)/.gitignore gitignore-recurse
gitignore-recurse-maybe:
@for subdir in $(DIST_SUBDIRS); do \
case " $(SUBDIRS) " in \
*" $$subdir "*) :;; \
*) test "$$subdir" = . -o -e "$$subdir/.git" || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) gitignore || echo "Skipping $$subdir");; \
esac; \
done
gitignore-recurse:
@for subdir in $(DIST_SUBDIRS); do \
test "$$subdir" = . -o -e "$$subdir/.git" || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) gitignore || echo "Skipping $$subdir"); \
done
maintainer-clean: gitignore-clean
gitignore-clean:
-rm -f $(srcdir)/.gitignore
.PHONY: gitignore-clean gitignore gitignore-recurse gitignore-recurse-maybe

View File

@ -1,906 +0,0 @@
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2009 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifndef SPICE_CANVAS_INTERNAL
#error "This file shouldn't be compiled directly"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "quic.h"
#include "rop3.h"
#include "region.h"
#define GL_CANVAS
#include "canvas_base.c"
typedef struct GLCanvas GLCanvas;
struct GLCanvas {
CanvasBase base;
GLCCtx glc;
void *private_data;
int private_data_size;
int textures_lost;
};
static inline uint8_t *copy_opposite_image(GLCanvas *canvas, void *data, int stride, int height)
{
uint8_t *ret_data = (uint8_t *)data;
uint8_t *dest;
uint8_t *src;
int i;
if (!canvas->private_data) {
canvas->private_data = spice_malloc_n(height, stride);
if (!canvas->private_data) {
return ret_data;
}
canvas->private_data_size = stride * height;
}
if (canvas->private_data_size < (stride * height)) {
free(canvas->private_data);
canvas->private_data = spice_malloc_n(height, stride);
if (!canvas->private_data) {
return ret_data;
}
canvas->private_data_size = stride * height;
}
dest = (uint8_t *)canvas->private_data;
src = (uint8_t *)data + (height - 1) * stride;
for (i = 0; i < height; ++i) {
memcpy(dest, src, stride);
dest += stride;
src -= stride;
}
return (uint8_t *)canvas->private_data;
}
static pixman_image_t *canvas_surf_to_trans_surf(GLCImage *image,
uint32_t trans_color)
{
int width = image->width;
int height = image->height;
uint8_t *src_line;
uint8_t *end_src_line;
int src_stride;
uint8_t *dest_line;
int dest_stride;
pixman_image_t *ret;
int i;
ret = pixman_image_create_bits(PIXMAN_a8r8g8b8, width, height, NULL, 0);
if (ret == NULL) {
CANVAS_ERROR("create surface failed");
}
src_line = image->pixels;
src_stride = image->stride;
end_src_line = src_line + src_stride * height;
dest_line = (uint8_t *)pixman_image_get_data(ret);
dest_stride = pixman_image_get_stride(ret);
for (; src_line < end_src_line; src_line += src_stride, dest_line += dest_stride) {
for (i = 0; i < width; i++) {
if ((((uint32_t*)src_line)[i] & 0x00ffffff) == trans_color) {
((uint32_t*)dest_line)[i] = 0;
} else {
((uint32_t*)dest_line)[i] = (((uint32_t*)src_line)[i]) | 0xff000000;
}
}
}
return ret;
}
static GLCPath get_path(GLCanvas *canvas, SpicePath *s)
{
GLCPath path = glc_path_create(canvas->glc);
int i;
for (i = 0; i < s->num_segments; i++) {
SpicePathSeg* seg = s->segments[i];
SpicePointFix* point = seg->points;
SpicePointFix* end_point = point + seg->count;
if (seg->flags & SPICE_PATH_BEGIN) {
glc_path_move_to(path, fix_to_double(point->x), fix_to_double(point->y));
point++;
}
if (seg->flags & SPICE_PATH_BEZIER) {
ASSERT((point - end_point) % 3 == 0);
for (; point + 2 < end_point; point += 3) {
glc_path_curve_to(path,
fix_to_double(point[0].x), fix_to_double(point[0].y),
fix_to_double(point[1].x), fix_to_double(point[1].y),
fix_to_double(point[2].x), fix_to_double(point[2].y));
}
} else {
for (; point < end_point; point++) {
glc_path_line_to(path, fix_to_double(point->x), fix_to_double(point->y));
}
}
if (seg->flags & SPICE_PATH_END) {
if (seg->flags & SPICE_PATH_CLOSE) {
glc_path_close(path);
}
}
}
return path;
}
#define SET_GLC_RECT(dest, src) { \
(dest)->x = (src)->left; \
(dest)->y = (src)->top; \
(dest)->width = (src)->right - (src)->left; \
(dest)->height = (src)->bottom - (src)->top; \
}
#define SET_GLC_BOX(dest, src) { \
(dest)->x = (src)->x1; \
(dest)->y = (src)->y1; \
(dest)->width = (src)->x2 - (src)->x1; \
(dest)->height = (src)->y2 - (src)->y1; \
}
static void set_clip(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip)
{
GLCRect rect;
glc_clip_reset(canvas->glc);
switch (clip->type) {
case SPICE_CLIP_TYPE_NONE:
break;
case SPICE_CLIP_TYPE_RECTS: {
uint32_t n = clip->rects->num_rects;
SpiceRect *now = clip->rects->rects;
SpiceRect *end = now + n;
if (n == 0) {
rect.x = rect.y = 0;
rect.width = rect.height = 0;
glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_SET);
break;
} else {
SET_GLC_RECT(&rect, now);
glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_SET);
}
for (now++; now < end; now++) {
SET_GLC_RECT(&rect, now);
glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_OR);
}
break;
}
default:
CANVAS_ERROR("invalid clip type");
}
}
static void set_mask(GLCanvas *canvas, SpiceQMask *mask, int x, int y)
{
pixman_image_t *image;
if (!(image = canvas_get_mask(&canvas->base, mask, NULL))) {
glc_clear_mask(canvas->glc, GLC_MASK_A);
return;
}
glc_set_mask(canvas->glc, x - mask->pos.x, y - mask->pos.y,
pixman_image_get_width(image),
pixman_image_get_height(image),
pixman_image_get_stride(image),
(uint8_t *)pixman_image_get_data(image), GLC_MASK_A);
}
static inline void surface_to_image(GLCanvas *canvas, pixman_image_t *surface, GLCImage *image,
int ignore_stride)
{
int depth = pixman_image_get_depth(surface);
ASSERT(depth == 32 || depth == 24);
image->format = (depth == 24) ? GLC_IMAGE_RGB32 : GLC_IMAGE_ARGB32;
image->width = pixman_image_get_width(surface);
image->height = pixman_image_get_height(surface);
image->stride = pixman_image_get_stride(surface);
image->pixels = (uint8_t *)pixman_image_get_data(surface);
image->pallet = NULL;
if (ignore_stride) {
return;
}
if (image->stride < 0) {
image->stride = -image->stride;
image->pixels = image->pixels - (image->height - 1) * image->stride;
} else {
image->pixels = copy_opposite_image(canvas, image->pixels, image->stride, image->height);
}
}
static void set_brush(GLCanvas *canvas, SpiceBrush *brush)
{
switch (brush->type) {
case SPICE_BRUSH_TYPE_SOLID: {
uint32_t color = brush->u.color;
double r, g, b;
b = (double)(color & canvas->base.color_mask) / canvas->base.color_mask;
color >>= canvas->base.color_shift;
g = (double)(color & canvas->base.color_mask) / canvas->base.color_mask;
color >>= canvas->base.color_shift;
r = (double)(color & canvas->base.color_mask) / canvas->base.color_mask;
glc_set_rgb(canvas->glc, r, g, b);
break;
}
case SPICE_BRUSH_TYPE_PATTERN: {
GLCImage image;
GLCPattern pattern;
pixman_image_t *surface;
surface = canvas_get_image(&canvas->base, brush->u.pattern.pat, FALSE);
surface_to_image(canvas, surface, &image, 0);
pattern = glc_pattern_create(canvas->glc, -brush->u.pattern.pos.x,
-brush->u.pattern.pos.y, &image);
glc_set_pattern(canvas->glc, pattern);
glc_pattern_destroy(pattern);
pixman_image_unref (surface);
}
case SPICE_BRUSH_TYPE_NONE:
return;
default:
CANVAS_ERROR("invalid brush type");
}
}
static void set_op(GLCanvas *canvas, uint16_t rop_decriptor)
{
GLCOp op;
switch (rop_decriptor) {
case SPICE_ROPD_OP_PUT:
op = GLC_OP_COPY;
break;
case SPICE_ROPD_OP_XOR:
op = GLC_OP_XOR;
break;
case SPICE_ROPD_OP_BLACKNESS:
op = GLC_OP_CLEAR;
break;
case SPICE_ROPD_OP_WHITENESS:
op = GLC_OP_SET;
break;
case SPICE_ROPD_OP_PUT | SPICE_ROPD_INVERS_BRUSH:
case SPICE_ROPD_OP_PUT | SPICE_ROPD_INVERS_SRC:
op = GLC_OP_COPY_INVERTED;
break;
case SPICE_ROPD_OP_INVERS:
op = GLC_OP_INVERT;
break;
case SPICE_ROPD_OP_AND:
op = GLC_OP_AND;
break;
case SPICE_ROPD_OP_AND | SPICE_ROPD_INVERS_RES:
op = GLC_OP_NAND;
break;
case SPICE_ROPD_OP_OR:
op = GLC_OP_OR;
break;
case SPICE_ROPD_OP_OR | SPICE_ROPD_INVERS_RES:
op = GLC_OP_NOR;
break;
case SPICE_ROPD_OP_XOR | SPICE_ROPD_INVERS_RES:
op = GLC_OP_EQUIV;
break;
case SPICE_ROPD_OP_AND | SPICE_ROPD_INVERS_DEST:
op = GLC_OP_AND_REVERSE;
break;
case SPICE_ROPD_OP_AND | SPICE_ROPD_INVERS_BRUSH:
case SPICE_ROPD_OP_AND | SPICE_ROPD_INVERS_SRC:
op = GLC_OP_AND_INVERTED;
break;
case SPICE_ROPD_OP_OR | SPICE_ROPD_INVERS_DEST:
op = GLC_OP_OR_REVERSE;
break;
case SPICE_ROPD_OP_OR | SPICE_ROPD_INVERS_BRUSH:
case SPICE_ROPD_OP_OR | SPICE_ROPD_INVERS_SRC:
op = GLC_OP_OR_INVERTED;
break;
default:
WARN("GLC_OP_NOOP");
op = GLC_OP_NOOP;
}
glc_set_op(canvas->glc, op);
}
static void gl_canvas_draw_fill(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceFill *fill)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
GLCRect rect;
set_clip(canvas, bbox, clip);
set_mask(canvas, &fill->mask, bbox->left, bbox->top);
set_brush(canvas, &fill->brush);
set_op(canvas, fill->rop_descriptor);
SET_GLC_RECT(&rect, bbox);
glc_fill_rect(canvas->glc, &rect);
glc_flush(canvas->glc);
}
static void gl_canvas_draw_copy(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceCopy *copy)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
pixman_image_t *surface;
GLCRecti src;
GLCRecti dest;
GLCImage image;
set_clip(canvas, bbox, clip);
set_mask(canvas, &copy->mask, bbox->left, bbox->top);
set_op(canvas, copy->rop_descriptor);
//todo: optimize get_image (use ogl conversion + remove unnecessary copy of 32bpp)
surface = canvas_get_image(&canvas->base, copy->src_bitmap, FALSE);
surface_to_image(canvas, surface, &image, 0);
SET_GLC_RECT(&dest, bbox);
SET_GLC_RECT(&src, &copy->src_area);
glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
pixman_image_unref(surface);
glc_flush(canvas->glc);
}
static void gl_canvas_draw_opaque(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceOpaque *opaque)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
pixman_image_t *surface;
GLCRecti src;
GLCRecti dest;
GLCRect fill_rect;
GLCImage image;
set_clip(canvas, bbox, clip);
set_mask(canvas, &opaque->mask, bbox->left, bbox->top);
glc_set_op(canvas->glc, (opaque->rop_descriptor & SPICE_ROPD_INVERS_SRC) ? GLC_OP_COPY_INVERTED :
GLC_OP_COPY);
surface = canvas_get_image(&canvas->base, opaque->src_bitmap, FALSE);
surface_to_image(canvas, surface, &image, 0);
SET_GLC_RECT(&dest, bbox);
SET_GLC_RECT(&src, &opaque->src_area);
glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
pixman_image_unref(surface);
set_brush(canvas, &opaque->brush);
set_op(canvas, opaque->rop_descriptor & ~SPICE_ROPD_INVERS_SRC);
SET_GLC_RECT(&fill_rect, bbox);
glc_fill_rect(canvas->glc, &fill_rect);
glc_flush(canvas->glc);
}
static void gl_canvas_draw_alpha_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceAlphaBlend *alpha_blend)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
pixman_image_t *surface;
GLCRecti src;
GLCRecti dest;
GLCImage image;
set_clip(canvas, bbox, clip);
glc_clear_mask(canvas->glc, GLC_MASK_A);
glc_set_op(canvas->glc, GLC_OP_COPY);
surface = canvas_get_image(&canvas->base, alpha_blend->src_bitmap, FALSE);
surface_to_image(canvas, surface, &image, 0);
SET_GLC_RECT(&dest, bbox);
SET_GLC_RECT(&src, &alpha_blend->src_area);
glc_draw_image(canvas->glc, &dest, &src, &image, 0, (double)alpha_blend->alpha / 0xff);
pixman_image_unref(surface);
glc_flush(canvas->glc);
}
static void gl_canvas_draw_blend(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlend *blend)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
pixman_image_t *surface;
GLCRecti src;
GLCRecti dest;
GLCImage image;
set_clip(canvas, bbox, clip);
set_mask(canvas, &blend->mask, bbox->left, bbox->top);
set_op(canvas, blend->rop_descriptor);
surface = canvas_get_image(&canvas->base, blend->src_bitmap, FALSE);
SET_GLC_RECT(&dest, bbox);
SET_GLC_RECT(&src, &blend->src_area);
surface_to_image(canvas, surface, &image, 0);
glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
pixman_image_unref(surface);
glc_flush(canvas->glc);
}
static void gl_canvas_draw_transparent(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceTransparent *transparent)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
pixman_image_t *surface;
pixman_image_t *trans_surf;
GLCImage image;
GLCRecti src;
GLCRecti dest;
set_clip(canvas, bbox, clip);
glc_clear_mask(canvas->glc, GLC_MASK_A);
glc_set_op(canvas->glc, GLC_OP_COPY);
surface = canvas_get_image(&canvas->base, transparent->src_bitmap, FALSE);
surface_to_image(canvas, surface, &image, 0);
trans_surf = canvas_surf_to_trans_surf(&image, transparent->true_color);
pixman_image_unref(surface);
surface_to_image(canvas, trans_surf, &image, 1);
SET_GLC_RECT(&dest, bbox);
SET_GLC_RECT(&src, &transparent->src_area);
glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
pixman_image_unref(trans_surf);
glc_flush(canvas->glc);
}
static inline void fill_common(GLCanvas *canvas, SpiceRect *bbox, SpiceClip *clip, SpiceQMask * mask, GLCOp op)
{
GLCRect rect;
set_clip(canvas, bbox, clip);
set_mask(canvas, mask, bbox->left, bbox->top);
glc_set_op(canvas->glc, op);
SET_GLC_RECT(&rect, bbox);
glc_fill_rect(canvas->glc, &rect);
}
static void gl_canvas_draw_whiteness(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceWhiteness *whiteness)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
fill_common(canvas, bbox, clip, &whiteness->mask, GLC_OP_SET);
}
static void gl_canvas_draw_blackness(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceBlackness *blackness)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
fill_common(canvas, bbox, clip, &blackness->mask, GLC_OP_CLEAR);
}
static void gl_canvas_draw_invers(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceInvers *invers)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
fill_common(canvas, bbox, clip, &invers->mask, GLC_OP_INVERT);
}
static void gl_canvas_draw_rop3(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceRop3 *rop3)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
pixman_image_t *d;
pixman_image_t *s;
GLCImage image;
SpicePoint src_pos;
uint8_t *data_opp;
int src_stride;
set_clip(canvas, bbox, clip);
set_mask(canvas, &rop3->mask, bbox->left, bbox->top);
glc_set_op(canvas->glc, GLC_OP_COPY);
image.format = GLC_IMAGE_RGB32;
image.width = bbox->right - bbox->left;
image.height = bbox->bottom - bbox->top;
image.pallet = NULL;
d = pixman_image_create_bits(PIXMAN_x8r8g8b8, image.width, image.height, NULL, 0);
if (d == NULL) {
CANVAS_ERROR("create surface failed");
}
image.pixels = (uint8_t *)pixman_image_get_data(d);
image.stride = pixman_image_get_stride(d);
glc_read_pixels(canvas->glc, bbox->left, bbox->top, &image);
data_opp = copy_opposite_image(canvas, image.pixels,
image.stride,
pixman_image_get_height(d));
memcpy(image.pixels, data_opp,
image.stride * pixman_image_get_height(d));
s = canvas_get_image(&canvas->base, rop3->src_bitmap, FALSE);
src_stride = pixman_image_get_stride(s);
if (src_stride > 0) {
data_opp = copy_opposite_image(canvas, (uint8_t *)pixman_image_get_data(s),
src_stride, pixman_image_get_height(s));
memcpy((uint8_t *)pixman_image_get_data(s), data_opp,
src_stride * pixman_image_get_height(s));
}
if (!rect_is_same_size(bbox, &rop3->src_area)) {
pixman_image_t *scaled_s = canvas_scale_surface(s, &rop3->src_area, image.width,
image.height, rop3->scale_mode);
pixman_image_unref(s);
s = scaled_s;
src_pos.x = 0;
src_pos.y = 0;
} else {
src_pos.x = rop3->src_area.left;
src_pos.y = rop3->src_area.top;
}
if (pixman_image_get_width(s) - src_pos.x < image.width ||
pixman_image_get_height(s) - src_pos.y < image.height) {
CANVAS_ERROR("bad src bitmap size");
}
if (rop3->brush.type == SPICE_BRUSH_TYPE_PATTERN) {
pixman_image_t *p = canvas_get_image(&canvas->base, rop3->brush.u.pattern.pat, FALSE);
SpicePoint pat_pos;
pat_pos.x = (bbox->left - rop3->brush.u.pattern.pos.x) % pixman_image_get_width(p);
pat_pos.y = (bbox->top - rop3->brush.u.pattern.pos.y) % pixman_image_get_height(p);
//for now (bottom-top)
if (pat_pos.y < 0) {
pat_pos.y = pixman_image_get_height(p) + pat_pos.y;
}
pat_pos.y = (image.height + pat_pos.y) % pixman_image_get_height(p);
pat_pos.y = pixman_image_get_height(p) - pat_pos.y;
do_rop3_with_pattern(rop3->rop3, d, s, &src_pos, p, &pat_pos);
pixman_image_unref(p);
} else {
uint32_t color = (canvas->base.color_shift) == 8 ? rop3->brush.u.color :
canvas_16bpp_to_32bpp(rop3->brush.u.color);
do_rop3_with_color(rop3->rop3, d, s, &src_pos, color);
}
pixman_image_unref(s);
GLCRecti dest;
GLCRecti src;
dest.x = bbox->left;
dest.y = bbox->top;
image.pixels = copy_opposite_image(canvas, image.pixels, pixman_image_get_stride(d),
pixman_image_get_height(d));
src.x = src.y = 0;
dest.width = src.width = image.width;
dest.height = src.height = image.height;
glc_draw_image(canvas->glc, &dest, &src, &image, 0, 1);
pixman_image_unref(d);
}
static void gl_canvas_draw_stroke(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceStroke *stroke)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
GLCPath path;
set_clip(canvas, bbox, clip);
glc_clear_mask(canvas->glc, GLC_MASK_A);
set_op(canvas, stroke->fore_mode);
set_brush(canvas, &stroke->brush);
if (stroke->attr.flags & SPICE_LINE_FLAGS_STYLED) {
WARN("SPICE_LINE_FLAGS_STYLED");
}
glc_set_line_width(canvas->glc, 1.0);
path = get_path(canvas, stroke->path);
glc_stroke_path(canvas->glc, path);
glc_path_destroy(path);
}
static void gl_canvas_draw_text(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpiceText *text)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
GLCRect rect;
SpiceString *str;
set_clip(canvas, bbox, clip);
glc_clear_mask(canvas->glc, GLC_MASK_A);
if (!rect_is_empty(&text->back_area)) {
set_brush(canvas, &text->back_brush);
set_op(canvas, text->back_mode);
SET_GLC_RECT(&rect, bbox);
glc_fill_rect(canvas->glc, &rect);
}
str = (SpiceString *)SPICE_GET_ADDRESS(text->str);
set_brush(canvas, &text->fore_brush);
set_op(canvas, text->fore_mode);
if (str->flags & SPICE_STRING_FLAGS_RASTER_A1) {
SpicePoint pos;
pixman_image_t *mask = canvas_get_str_mask(&canvas->base, str, 1, &pos);
_glc_fill_mask(canvas->glc, pos.x, pos.y,
pixman_image_get_width(mask),
pixman_image_get_height(mask),
pixman_image_get_stride(mask),
(uint8_t *)pixman_image_get_data(mask));
pixman_image_unref(mask);
} else if (str->flags & SPICE_STRING_FLAGS_RASTER_A4) {
SpicePoint pos;
pixman_image_t *mask = canvas_get_str_mask(&canvas->base, str, 4, &pos);
glc_fill_alpha(canvas->glc, pos.x, pos.y,
pixman_image_get_width(mask),
pixman_image_get_height(mask),
pixman_image_get_stride(mask),
(uint8_t *)pixman_image_get_data(mask));
pixman_image_unref(mask);
} else if (str->flags & SPICE_STRING_FLAGS_RASTER_A8) {
WARN("untested path A8 glyphs, doing nothing");
if (0) {
SpicePoint pos;
pixman_image_t *mask = canvas_get_str_mask(&canvas->base, str, 8, &pos);
glc_fill_alpha(canvas->glc, pos.x, pos.y,
pixman_image_get_width(mask),
pixman_image_get_height(mask),
pixman_image_get_stride(mask),
(uint8_t *)pixman_image_get_data(mask));
pixman_image_unref(mask);
}
} else {
WARN("untested path vector glyphs, doing nothing");
if (0) {
//draw_vector_str(canvas, str, &text->fore_brush, text->fore_mode);
}
}
glc_flush(canvas->glc);
}
static void gl_canvas_clear(SpiceCanvas *spice_canvas)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
glc_clear(canvas->glc);
glc_flush(canvas->glc);
}
static void gl_canvas_copy_bits(SpiceCanvas *spice_canvas, SpiceRect *bbox, SpiceClip *clip, SpicePoint *src_pos)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
set_clip(canvas, bbox, clip);
glc_clear_mask(canvas->glc, GLC_MASK_A);
glc_set_op(canvas->glc, GLC_OP_COPY);
glc_copy_pixels(canvas->glc, bbox->left, bbox->top, src_pos->x, src_pos->y,
bbox->right - bbox->left, bbox->bottom - bbox->top);
}
static void gl_canvas_read_bits(SpiceCanvas *spice_canvas, uint8_t *dest, int dest_stride, const SpiceRect *area)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
GLCImage image;
ASSERT(dest_stride > 0);
image.format = GLC_IMAGE_RGB32;
image.height = area->bottom - area->top;
image.width = area->right - area->left;
image.pixels = dest;
image.stride = dest_stride;
glc_read_pixels(canvas->glc, area->left, area->top, &image);
}
static void gl_canvas_group_start(SpiceCanvas *spice_canvas, QRegion *region)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
GLCRect *glc_rects;
GLCRect *now, *end;
int num_rect;
pixman_box32_t *rects;
canvas_base_group_start(spice_canvas, region);
rects = pixman_region32_rectangles(region, &num_rect);
glc_rects = spice_new(GLCRect, num_rect);
now = glc_rects;
end = glc_rects + num_rect;
for (; now < end; now++, rects++) {
SET_GLC_BOX(now, rects);
}
glc_mask_rects(canvas->glc, num_rect, glc_rects, GLC_MASK_B);
free(glc_rects);
}
static void gl_canvas_put_image(SpiceCanvas *spice_canvas, const SpiceRect *dest, const uint8_t *src_data,
uint32_t src_width, uint32_t src_height, int src_stride,
const QRegion *clip)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
GLCRecti src;
GLCRecti gldest;
GLCImage image;
uint32_t i;
ASSERT(src_stride <= 0)
glc_clip_reset(canvas->glc);
if (clip) {
int num_rects;
pixman_box32_t *rects = pixman_region32_rectangles((pixman_region32_t *)clip,
&num_rects);
GLCRect rect;
if (num_rects == 0) {
rect.x = rect.y = rect.width = rect.height = 0;
glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_SET);
} else {
SET_GLC_BOX(&rect, rects);
glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_SET);
for (i = 1; i < num_rects; i++) {
SET_GLC_BOX(&rect, rects + i);
glc_clip_rect(canvas->glc, &rect, GLC_CLIP_OP_OR);
}
}
}
SET_GLC_RECT(&gldest, dest);
src.x = src.y = 0;
src.width = src_width;
src.height = src_height;
image.format = GLC_IMAGE_RGB32;
image.width = src_width;
image.height = src_height;
src_stride = -src_stride;
image.stride = src_stride;
image.pixels = (uint8_t *)src_data - (src_height - 1) * src_stride;
image.pallet = NULL;
glc_draw_image(canvas->glc, &gldest, &src, &image, 0, 1);
glc_flush(canvas->glc);
}
static void gl_canvas_group_end(SpiceCanvas *spice_canvas)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
canvas_base_group_end(spice_canvas);
glc_clear_mask(canvas->glc, GLC_MASK_B);
}
static int need_init = 1;
static SpiceCanvasOps gl_canvas_ops;
SpiceCanvas *gl_canvas_create(int width, int height, uint32_t format
#ifdef SW_CANVAS_CACHE
, SpiceImageCache *bits_cache
, SpicePaletteCache *palette_cache
#elif defined(SW_CANVAS_IMAGE_CACHE)
, SpiceImageCache *bits_cache
#endif
, SpiceImageSurfaces *surfaces
, SpiceGlzDecoder *glz_decoder
, SpiceJpegDecoder *jpeg_decoder
, SpiceZlibDecoder *zlib_decoder
)
{
GLCanvas *canvas;
int init_ok;
if (need_init) {
return NULL;
}
canvas = spice_new0(GLCanvas, 1);
if (!(canvas->glc = glc_create(width, height))) {
goto error_1;
}
canvas->private_data = NULL;
init_ok = canvas_base_init(&canvas->base, &gl_canvas_ops,
width, height, format
#ifdef SW_CANVAS_CACHE
, bits_cache
, palette_cache
#elif defined(SW_CANVAS_IMAGE_CACHE)
, bits_cache
#endif
, surfaces
, glz_decoder
, jpeg_decoder
, zlib_decoder
);
if (!init_ok) {
goto error_2;
}
return (SpiceCanvas *)canvas;
error_2:
glc_destroy(canvas->glc, 0);
error_1:
free(canvas);
return NULL;
}
void gl_canvas_set_textures_lost(SpiceCanvas *spice_canvas,
int textures_lost)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
canvas->textures_lost = textures_lost;
}
static void gl_canvas_destroy(SpiceCanvas *spice_canvas)
{
GLCanvas *canvas = (GLCanvas *)spice_canvas;
if (!canvas) {
return;
}
canvas_base_destroy(&canvas->base);
glc_destroy(canvas->glc, canvas->textures_lost);
free(canvas->private_data);
free(canvas);
}
void gl_canvas_init(void) //unsafe global function
{
if (!need_init) {
return;
}
need_init = 0;
canvas_base_init_ops(&gl_canvas_ops);
gl_canvas_ops.draw_fill = gl_canvas_draw_fill;
gl_canvas_ops.draw_copy = gl_canvas_draw_copy;
gl_canvas_ops.draw_opaque = gl_canvas_draw_opaque;
gl_canvas_ops.copy_bits = gl_canvas_copy_bits;
gl_canvas_ops.draw_text = gl_canvas_draw_text;
gl_canvas_ops.draw_stroke = gl_canvas_draw_stroke;
gl_canvas_ops.draw_rop3 = gl_canvas_draw_rop3;
gl_canvas_ops.draw_blend = gl_canvas_draw_blend;
gl_canvas_ops.draw_blackness = gl_canvas_draw_blackness;
gl_canvas_ops.draw_whiteness = gl_canvas_draw_whiteness;
gl_canvas_ops.draw_invers = gl_canvas_draw_invers;
gl_canvas_ops.draw_transparent = gl_canvas_draw_transparent;
gl_canvas_ops.draw_alpha_blend = gl_canvas_draw_alpha_blend;
gl_canvas_ops.put_image = gl_canvas_put_image;
gl_canvas_ops.clear = gl_canvas_clear;
gl_canvas_ops.read_bits = gl_canvas_read_bits;
gl_canvas_ops.group_start = gl_canvas_group_start;
gl_canvas_ops.group_end = gl_canvas_group_end;
gl_canvas_ops.destroy = gl_canvas_destroy;
rop3_init();
}

View File

@ -1,61 +0,0 @@
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2009 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef GL_UTILS_H
#define GL_UTILS_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef RED_DEBUG
#define GLC_ERROR_TEST_FLUSH { \
GLenum gl_err; glFlush(); \
if ((gl_err = glGetError()) != GL_NO_ERROR) { \
printf("%s[%d]: opengl error: %s\n", __FUNCTION__, __LINE__, \
gluErrorString(gl_err)); \
abort(); \
} \
}
#define GLC_ERROR_TEST_FINISH { \
GLenum gl_err; glFinish(); \
if ((gl_err = glGetError()) != GL_NO_ERROR) { \
printf("%s[%d]: opengl error: %s\n", __FUNCTION__, __LINE__, \
gluErrorString(gl_err)); \
abort(); \
} \
}
#else
#define GLC_ERROR_TEST_FLUSH ;
#define GLC_ERROR_TEST_FINISH ;
#endif
#include "bitops.h"
#define find_msb spice_bit_find_msb
#define gl_get_to_power_two spice_bit_next_pow2
#ifdef __cplusplus
}
#endif
#endif

1513
glc.c

File diff suppressed because it is too large Load Diff

167
glc.h
View File

@ -1,167 +0,0 @@
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2009 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _H_GL_CANVASE
#define _H_GL_CANVASE
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void * GLCCtx;
typedef void * GLCPattern;
typedef void * GLCPath;
typedef struct GLCRect {
double x;
double y;
double width;
double height;
} GLCRect;
typedef struct GLCRecti {
int x;
int y;
int width;
int height;
} GLCRecti;
typedef enum {
GLC_IMAGE_RGB32,
GLC_IMAGE_ARGB32,
} GLCImageFormat;
typedef struct GLCPImage {
GLCImageFormat format;
int width;
int height;
int stride;
uint8_t *pixels;
uint32_t *pallet;
} GLCImage;
GLCPattern glc_pattern_create(GLCCtx glc, int x_orign, int y_orign, const GLCImage *image);
void glc_pattern_set(GLCPattern pattern, int x_orign, int y_orign, const GLCImage *image);
void glc_pattern_destroy(GLCPattern pattern);
void glc_path_move_to(GLCPath path, double x, double y);
void glc_path_line_to(GLCPath path, double x, double y);
void glc_path_curve_to(GLCPath path, double p1_x, double p1_y, double p2_x, double p2_y,
double p3_x, double p3_y);
void glc_path_rel_move_to(GLCPath path, double x, double y);
void glc_path_rel_line_to(GLCPath path, double x, double y);
void glc_path_rel_curve_to(GLCPath path, double p1_x, double p1_y, double p2_x, double p2_y,
double p3_x, double p3_y);
void glc_path_close(GLCPath path);
void glc_path_cleare(GLCPath);
GLCPath glc_path_create(GLCCtx glc);
void glc_path_destroy(GLCPath path);
void glc_set_rgb(GLCCtx glc, double red, double green, double blue);
void glc_set_rgba(GLCCtx glc, double red, double green, double blue, double alpha);
void glc_set_pattern(GLCCtx glc, GLCPattern pattern);
typedef enum {
GLC_OP_CLEAR = 0x1500,
GLC_OP_SET = 0x150F,
GLC_OP_COPY = 0x1503,
GLC_OP_COPY_INVERTED = 0x150C,
GLC_OP_NOOP = 0x1505,
GLC_OP_INVERT = 0x150A,
GLC_OP_AND = 0x1501,
GLC_OP_NAND = 0x150E,
GLC_OP_OR = 0x1507,
GLC_OP_NOR = 0x1508,
GLC_OP_XOR = 0x1506,
GLC_OP_EQUIV = 0x1509,
GLC_OP_AND_REVERSE = 0x1502,
GLC_OP_AND_INVERTED = 0x1504,
GLC_OP_OR_REVERSE = 0x150B,
GLC_OP_OR_INVERTED = 0x150D,
} GLCOp;
void glc_set_op(GLCCtx glc, GLCOp op);
void glc_set_alpha_factor(GLCCtx glc, double alpah);
typedef enum {
GLC_FILL_MODE_WINDING_ODD,
GLC_FILL_MODE_WINDING_NONZERO,
} GLCFillMode;
void glc_set_fill_mode(GLCCtx glc, GLCFillMode mode);
void glc_set_line_width(GLCCtx glc, double width);
void glc_set_line_end_cap(GLCCtx glc, int style);
void glc_set_line_join(GLCCtx glc, int style);
void glc_set_miter_limit(GLCCtx glc, int limit);
void glc_set_line_dash(GLCCtx glc, const double *dashes, int num_dashes, double offset);
typedef enum {
GLC_MASK_A,
GLC_MASK_B,
} GLCMaskID;
void glc_set_mask(GLCCtx glc, int x_dest, int y_dest, int width, int height,
int stride, const uint8_t *bitmap, GLCMaskID id);
void glc_mask_rects(GLCCtx glc, int num_rect, GLCRect *rects, GLCMaskID id);
void glc_clear_mask(GLCCtx glc, GLCMaskID id);
typedef enum {
GLC_CLIP_OP_SET,
GLC_CLIP_OP_OR,
GLC_CLIP_OP_AND,
GLC_CLIP_OP_EXCLUDE,
} GLCClipOp;
void glc_clip_rect(GLCCtx glc, const GLCRect *rect, GLCClipOp op);
void glc_clip_path(GLCCtx glc, GLCPath path, GLCClipOp op);
void glc_clip_mask(GLCCtx glc, int x_dest, int y_dest, int width, int height, int stride,
const uint8_t *bitmap, GLCClipOp op);
void glc_clip_reset(GLCCtx glc);
void glc_fill_rect(GLCCtx glc, const GLCRect *rect);
void glc_fill_path(GLCCtx glc, GLCPath path);
void _glc_fill_mask(GLCCtx glc, int x_dest, int y_dest, int width, int height, int stride,
const uint8_t *bitmap);
void glc_fill_alpha(GLCCtx glc, int x_dest, int y_dest, int width, int height, int stride,
const uint8_t *alpha_mask);
void glc_stroke_rect(GLCCtx glc, const GLCRect *rect);
void glc_stroke_path(GLCCtx glc, GLCPath path);
void glc_draw_image(GLCCtx glc, const GLCRecti *dest, const GLCRecti *src, const GLCImage *image,
int scale_mode, double alpha);
void glc_copy_pixels(GLCCtx glc, int x_dest, int y_dest, int x_src, int y_src, int width,
int height);
void glc_read_pixels(GLCCtx glc, int x, int y, GLCImage *image);
void glc_flush(GLCCtx glc);
void glc_clear(GLCCtx glc);
GLCCtx glc_create(int width, int height);
void glc_destroy(GLCCtx glc, int textures_lost);
#ifdef __cplusplus
}
#endif
#endif

0
m4/.gitignore vendored Normal file
View File

49
m4/ax_python_module.m4 Normal file
View File

@ -0,0 +1,49 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_python_module.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_PYTHON_MODULE(modname[, fatal])
#
# DESCRIPTION
#
# Checks for Python module.
#
# If fatal is non-empty then absence of a module will trigger an error.
#
# LICENSE
#
# Copyright (c) 2008 Andrew Collier
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 6
AU_ALIAS([AC_PYTHON_MODULE], [AX_PYTHON_MODULE])
AC_DEFUN([AX_PYTHON_MODULE],[
if test -z $PYTHON;
then
PYTHON="python"
fi
PYTHON_NAME=`basename $PYTHON`
AC_MSG_CHECKING($PYTHON_NAME module: $1)
$PYTHON -c "import $1" 2>/dev/null
if test $? -eq 0;
then
AC_MSG_RESULT(yes)
eval AS_TR_CPP(HAVE_PYMOD_$1)=yes
else
AC_MSG_RESULT(no)
eval AS_TR_CPP(HAVE_PYMOD_$1)=no
#
if test -n "$2"
then
AC_MSG_ERROR(failed to find required module $1)
exit 1
fi
fi
])

6
m4/common.m4 Normal file
View File

@ -0,0 +1,6 @@
dnl This file should be included by spice-common user from
dnl configure.ac to have all required checks in order to use spice-common
m4_define(spice_common_dir,m4_bpatsubst(__file__,[/m4/common\.m4],[]))dnl
dnl Automatically include m4 files in m4 directory
AC_CONFIG_MACRO_DIRS(spice_common_dir[/m4])dnl
SPICE_COMMON(spice_common_dir)

368
m4/spice-deps.m4 Normal file
View File

@ -0,0 +1,368 @@
# SPICE_WARNING(warning)
# SPICE_PRINT_MESSAGES
# ----------------------
# Collect warnings and print them at the end so they are clearly visible.
# ---------------------
AC_DEFUN([SPICE_WARNING],[AS_VAR_APPEND([spice_warnings],["|$1"])])
AC_DEFUN([SPICE_PRINT_MESSAGES],[
ac_save_IFS="$IFS"
IFS="|"
for msg in $spice_warnings; do
IFS="$ac_save_IFS"
AS_VAR_IF([msg],[],,[AC_MSG_WARN([$msg]); echo >&2])
done
IFS="$ac_save_IFS"
])
# SPICE_EXTRA_CHECKS()
# --------------------
# Check for --enable-extra-checks option
# --------------------
AC_DEFUN([SPICE_EXTRA_CHECKS],[
AC_ARG_ENABLE([extra-checks],
AS_HELP_STRING([--enable-extra-checks=@<:@yes/no@:>@],
[Enable expensive checks @<:@default=no@:>@]))
AM_CONDITIONAL(ENABLE_EXTRA_CHECKS, test "x$enable_extra_checks" = "xyes")
AM_COND_IF([ENABLE_EXTRA_CHECKS], AC_DEFINE([ENABLE_EXTRA_CHECKS], 1, [Enable extra checks on code]))
])
# SPICE_CHECK_SYSDEPS()
# ---------------------
# Checks for header files and library functions needed by spice-common.
# ---------------------
AC_DEFUN([SPICE_CHECK_SYSDEPS], [
AC_C_BIGENDIAN
AC_FUNC_ALLOCA
AC_CHECK_HEADERS([arpa/inet.h netinet/in.h stdlib.h sys/socket.h unistd.h])
# Checks for typedefs, structures, and compiler characteristics
AC_C_INLINE
# Checks for library functions
# do not check malloc or realloc, since that cannot be cross-compiled checked
AC_CHECK_FUNCS([floor])
AC_SEARCH_LIBS([hypot], [m], [], [
AC_MSG_ERROR([unable to find the hypot() function])
])
])
# SPICE_CHECK_SMARTCARD
# ---------------------
# Adds a --enable-smartcard switch in order to enable/disable smartcard
# support, and checks if the needed libraries are available. If found, it will
# return the flags to use in the SMARTCARD_CFLAGS and SMARTCARD_LIBS variables, and
# it will define a USE_SMARTCARD preprocessor symbol as well as a HAVE_SMARTCARD
# Makefile conditional.
#----------------------
AC_DEFUN([SPICE_CHECK_SMARTCARD], [
AC_ARG_ENABLE([smartcard],
AS_HELP_STRING([--enable-smartcard=@<:@yes/no/auto@:>@],
[Enable smartcard support @<:@default=auto@:>@]),
[],
[enable_smartcard="auto"])
have_smartcard=no
if test "x$enable_smartcard" != "xno"; then
PKG_CHECK_MODULES([SMARTCARD], [libcacard >= 2.5.1], [have_smartcard=yes], [have_smartcard=no])
if test "x$enable_smartcard" = "xyes" && test "x$have_smartcard" = "xno"; then
AC_MSG_ERROR([smarcard support explicitly requested, but some required packages are not available])
fi
if test "x$have_smartcard" = "xyes"; then
AC_DEFINE(USE_SMARTCARD, [1], [Define if supporting smartcard proxying])
fi
fi
AM_CONDITIONAL(HAVE_SMARTCARD, test "x$have_smartcard" = "xyes")
])
# SPICE_CHECK_OPUS
# ----------------
# Check for the availability of Opus. If found, it will return the flags to use
# in the OPUS_CFLAGS and OPUS_LIBS variables, and it will define a
# HAVE_OPUS preprocessor symbol as well as a HAVE_OPUS Makefile conditional.
# ----------------
AC_DEFUN([SPICE_CHECK_OPUS], [
AC_ARG_ENABLE([opus],
[ --disable-opus Disable Opus audio codec (enabled by default)],,
[enable_opus="auto"])
if test "x$enable_opus" != "xno"; then
PKG_CHECK_MODULES([OPUS], [opus >= 0.9.14], [have_opus=yes], [have_opus=no])
if test "x$enable_opus" = "xauto" && test "x$have_opus" = "xno"; then
AC_MSG_ERROR([Opus could not be detected, explicitly use --disable-opus if that's intentional])
fi
if test "x$enable_opus" = "xyes" && test "x$have_opus" != "xyes"; then
AC_MSG_ERROR([--enable-opus has been specified, but Opus is missing])
fi
fi
AM_CONDITIONAL([HAVE_OPUS], [test "x$have_opus" = "xyes"])
AM_COND_IF([HAVE_OPUS], AC_DEFINE([HAVE_OPUS], [1], [Define if we have OPUS]))
])
# SPICE_CHECK_PIXMAN
# ------------------
# Check for the availability of pixman. If found, it will return the flags to
# use in the PIXMAN_CFLAGS and PIXMAN_LIBS variables.
#-------------------
AC_DEFUN([SPICE_CHECK_PIXMAN], [
PKG_CHECK_MODULES(PIXMAN, pixman-1 >= 0.17.7)
])
# SPICE_CHECK_GLIB2
# -----------------
# Check for the availability of glib2. If found, it will return the flags to
# use in the GLIB2_CFLAGS and GLIB2_LIBS variables.
#------------------
AC_DEFUN([SPICE_CHECK_GLIB2], [
PKG_CHECK_MODULES(GLIB2, glib-2.0 >= 2.38)
PKG_CHECK_MODULES(GIO2, gio-2.0 >= 2.38)
GLIB2_CFLAGS="$GLIB2_CFLAGS -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_38 -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_38"
])
# SPICE_CHECK_GDK_PIXBUF
# ----------------------
# Check for the availability of gdk-pixbuf. If found, it will return the flags to use
# in the GDK_PIXBUF_CFLAGS and GDK_PIXBUF_LIBS variables, and it will define a
# HAVE_GDK_PIXBUF preprocessor symbol as well as a HAVE_GDK_PIXBUF Makefile conditional.
# ----------------
AC_DEFUN([SPICE_CHECK_GDK_PIXBUF], [
PKG_CHECK_MODULES([GDK_PIXBUF], [gdk-pixbuf-2.0 >= 2.26], [have_gdk_pixbuf=yes], [have_gdk_pixbuf=no])
AM_CONDITIONAL([HAVE_GDK_PIXBUF], [test "x$have_gdk_pixbuf" = "xyes"])
AM_COND_IF([HAVE_GDK_PIXBUF], AC_DEFINE([HAVE_GDK_PIXBUF], [1], [Define if gdk-pixbuf was found]))
])
# SPICE_CHECK_PYTHON_MODULES()
# --------------------------
# Adds a --enable-python-checks configure flags as well as checks for the
# availability of the python modules needed by the python scripts generating
# C code from spice.proto. These checks are not needed when building from
# tarballs so they are disabled by default.
#---------------------------
AC_DEFUN([SPICE_CHECK_PYTHON_MODULES], [
AC_ARG_ENABLE([python-checks],
AS_HELP_STRING([--enable-python-checks=@<:@yes/no@:>@],
[Enable checks for Python modules needed to build from git @<:@default=no@:>@]),
[],
[enable_python_checks="no"])
if test "x$enable_python_checks" != "xno"; then
AS_IF([test -n "$PYTHON"], # already set required PYTHON version
[AM_PATH_PYTHON
AX_PYTHON_MODULE([pyparsing], [1])],
[PYTHON=python3
AX_PYTHON_MODULE([pyparsing])
test "$HAVE_PYMOD_PYPARSING" = "yes"],
[AM_PATH_PYTHON([3])],
[AC_MSG_ERROR([Python module pyparsing is required])])
else
AM_PATH_PYTHON
fi
])
# SPICE_CHECK_LZ4
# ---------------
# Adds a --enable-lz4 switch in order to enable/disable LZ4 compression
# support, and checks if the needed libraries are available. If found, it will
# return the flags to use in the LZ4_CFLAGS and LZ4_LIBS variables, and
# it will define a USE_LZ4 preprocessor symbol and a HAVE_LZ4 conditional.
# ---------------
AC_DEFUN([SPICE_CHECK_LZ4], [
AC_ARG_ENABLE([lz4],
AS_HELP_STRING([--enable-lz4=@<:@yes/no/auto@:>@],
[Enable LZ4 compression support @<:@default=auto@:>@]),
[],
[enable_lz4="auto"])
have_lz4="no"
if test "x$enable_lz4" != "xno"; then
# LZ4_compress_default is available in liblz4 >= 129, however liblz has changed
# versioning scheme making the check failing. Rather check for function definition
PKG_CHECK_MODULES([LZ4], [liblz4], [have_lz4="yes"], [have_lz4="no"])
if test "x$have_lz4" = "xyes"; then
# It is necessary to set LIBS and CFLAGS before AC_CHECK_FUNC
old_LIBS="$LIBS"
old_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $LZ4_CFLAGS"
LIBS="$LIBS $LZ4_LIBS"
AC_CHECK_FUNC([LZ4_compress_default], [
AC_DEFINE(USE_LZ4, [1], [Define to build with lz4 support])],
[have_lz4="no"])
AC_CHECK_FUNCS([LZ4_compress_fast_continue])
LIBS="$old_LIBS"
CFLAGS="$old_CFLAGS"
fi
if test "x$enable_lz4" = "xyes" && test "x$have_lz4" = "xno"; then
AC_MSG_ERROR([lz4 support requested but liblz4 >= 129 could not be found])
fi
fi
AM_CONDITIONAL(HAVE_LZ4, test "x$have_lz4" = "xyes")
])
# SPICE_CHECK_GSTREAMER(VAR, version, packages-to-check-for, [action-if-found, [action-if-not-found]])
# ---------------------
# Checks whether the specified GStreamer modules are present and sets the
# corresponding autoconf variables and preprocessor definitions.
# ---------------------
AC_DEFUN([SPICE_CHECK_GSTREAMER], [
AS_VAR_PUSHDEF([have_gst],[have_]m4_tolower([$1]))dnl
AS_VAR_PUSHDEF([gst_inspect],[GST_INSPECT_$2])dnl
PKG_CHECK_MODULES([$1], [$3],
[have_gst="yes"
AC_SUBST(AS_TR_SH([[$1]_CFLAGS]))
AC_SUBST(AS_TR_SH([[$1]_LIBS]))
AS_VAR_APPEND([SPICE_REQUIRES], [" $3"])
AC_DEFINE(AS_TR_SH([HAVE_$1]), [1], [Define if supporting GStreamer $2])
AC_PATH_PROG(gst_inspect, gst-inspect-$2)
AS_IF([test "x$gst_inspect" = x],
SPICE_WARNING([Cannot verify that the required runtime GStreamer $2 elements are present because gst-inspect-$2 is missing]))
$4],
[have_gst="no"
$5])
AS_VAR_POPDEF([gst_inspect])dnl
AS_VAR_POPDEF([have_gst])dnl
])
# SPICE_CHECK_GSTREAMER_ELEMENTS(gst-inspect, package, elements-to-check-for)
# ---------------------
# Checks that the specified GStreamer elements are installed. If not it
# issues a warning and sets missing_gstreamer_elements.
# ---------------------
AC_DEFUN([SPICE_CHECK_GSTREAMER_ELEMENTS], [
AS_IF([test "x$1" != x],
[missing=""
for element in $3
do
AS_VAR_PUSHDEF([cache_var],[spice_cv_prog_${1}_${element}])dnl
AC_CACHE_CHECK([for the $element GStreamer element], cache_var,
[found=no
"$1" $element >/dev/null 2>/dev/null && found=yes
eval "cache_var=$found"])
AS_VAR_COPY(res, cache_var)
AS_IF([test "x$res" = "xno"], [missing="$missing $element"])
AS_VAR_POPDEF([cache_var])dnl
done
AS_IF([test "x$missing" != x],
[SPICE_WARNING([The$missing GStreamer element(s) are missing. You should be able to find them in the $2 package.])
missing_gstreamer_elements="yes"],
[test "x$missing_gstreamer_elements" = x],
[missing_gstreamer_elements="no"])
])
])
# SPICE_CHECK_SASL
# ----------------
# Adds a --with-sasl switch to allow using SASL for authentication.
# Checks whether the required library is available. If it is present,
# it will return the flags to use in SASL_CFLAGS and SASL_LIBS variables,
# and it will define a have_sasl configure variable and a HAVE_SASL preprocessor
# symbol.
# ----------------
AC_DEFUN([SPICE_CHECK_SASL], [
AC_ARG_WITH([sasl],
[AS_HELP_STRING([--with-sasl=@<:@yes/no/auto@:>@],
[use cyrus SASL for authentication @<:@default=auto@:>@])],
[],
[with_sasl="auto"])
have_sasl=no
if test "x$with_sasl" != "xno"; then
PKG_CHECK_MODULES([SASL], [libsasl2], [have_sasl=yes],[have_sasl=no])
if test "x$have_sasl" = "xno" && test "x$with_sasl" = "xyes"; then
AC_MSG_ERROR([Cyrus SASL support requested but libsasl2 could not be found])
fi
if test "x$have_sasl" = "xyes"; then
AC_DEFINE([HAVE_SASL], 1, [whether Cyrus SASL is available for authentication])
fi
fi
])
# SPICE_CHECK_OPENSSL
# -----------------
# Check for the availability of openssl. If found, it will return the flags to
# use in the OPENSSL_CFLAGS and OPENSSL_LIBS variables.
#------------------
AC_DEFUN([SPICE_CHECK_OPENSSL], [
PKG_CHECK_MODULES(OPENSSL, openssl)
])
# SPICE_CHECK_UDEV
# -----------------
# Check for the availability of libudev. If found, it will help to determine
# if a given vendor GPU is available or not.
#------------------
AC_DEFUN([SPICE_CHECK_UDEV], [
PKG_CHECK_MODULES([UDEV], [libudev], [have_udev=yes],[have_udev=no])
if test "x$have_udev" = "xyes"; then
AC_DEFINE([HAVE_UDEV], 1, [whether libudev is available to identify GPU])
fi
])
# SPICE_CHECK_INSTRUMENTATION
# -----------------
# Check for the availability of an instrumentation library.
#------------------
AC_DEFUN([SPICE_CHECK_INSTRUMENTATION], [
AC_ARG_ENABLE([instrumentation],
AS_HELP_STRING([--enable-instrumentation=@<:@recorder/agent/no@:>@],
[Enable instrumentation @<:@default=no@:>@]),
[],
enable_instrumentation="no")
AS_IF([test "$enable_instrumentation" = "recorder"],
AC_DEFINE([ENABLE_RECORDER], [1], [Define if the recorder instrumentation is enabled]))
AS_IF([test "$enable_instrumentation" = "agent"],
AC_DEFINE([ENABLE_AGENT_INTERFACE], [1], [Define if the agent-interface instrumentation is enabled]))
AM_CONDITIONAL([ENABLE_RECORDER],[test "$enable_instrumentation" = "recorder"])
AM_CONDITIONAL([ENABLE_AGENT_INTERFACE],[test "$enable_instrumentation" = "agent"])
])
# SPICE_COMMON
# -----------------
# Define variables in order to use spice-common
# SPICE_COMMON_DIR directory for output libraries
# SPICE_COMMON_CFLAGS CFLAGS to add to use the library
#
# SPICE_PROTOCOL_MIN_VER input (m4) and output (autoconf) SPICE protocol version
# SPICE_PROTOCOL_CFLAGS CFLAGS for SPICE protocol, already automatically included
#
# GLIB2_MIN_VER input (m4) and output (shell) GLib2 minimum version
# GLIB2_MIN_VERSION output (shell) variable like "GLIB_VERSION_1_2" from GLIB2_MIN_VER
#------------------
AC_DEFUN([SPICE_COMMON], [dnl
dnl These add some flags and checks to component using spice-common
dnl The flags are necessary in order to make included header working
AC_REQUIRE([SPICE_EXTRA_CHECKS])dnl
AC_REQUIRE([SPICE_CHECK_INSTRUMENTATION])dnl
dnl Get the required spice protocol version
m4_define([SPICE_PROTOCOL_MIN_VER],m4_ifdef([SPICE_PROTOCOL_MIN_VER],SPICE_PROTOCOL_MIN_VER,[0.14.2]))dnl
m4_define([SPICE_PROTOCOL_MIN_VER],m4_if(m4_version_compare(SPICE_PROTOCOL_MIN_VER,[0.14.2]),[1],SPICE_PROTOCOL_MIN_VER,[0.14.2]))dnl
[SPICE_PROTOCOL_MIN_VER]=SPICE_PROTOCOL_MIN_VER
m4_undefine([SPICE_PROTOCOL_MIN_VER])dnl
PKG_CHECK_MODULES([SPICE_PROTOCOL], [spice-protocol >= $SPICE_PROTOCOL_MIN_VER])
AC_SUBST([SPICE_PROTOCOL_MIN_VER])dnl
dnl Get the required GLib2 version
m4_define([GLIB2_MIN_VER],m4_ifdef([GLIB2_MIN_VER],GLIB2_MIN_VER,[2.38]))dnl
m4_define([GLIB2_MIN_VER],m4_if(m4_version_compare(GLIB2_MIN_VER,[2.38]),[1],GLIB2_MIN_VER,[2.38]))dnl
m4_define([GLIB2_MIN_VERSION],[GLIB_VERSION_]m4_translit(GLIB2_MIN_VER,[.],[_]))dnl
[GLIB2_MIN_VER]=GLIB2_MIN_VER
[GLIB2_MIN_VERSION]=GLIB2_MIN_VERSION
m4_undefine([GLIB2_MIN_VER])dnl
m4_undefine([GLIB2_MIN_VERSION])dnl
PKG_CHECK_MODULES([GLIB2], [glib-2.0 >= $GLIB2_MIN_VER gio-2.0 >= $GLIB2_MIN_VER])
dnl Configuration variables
AC_CONFIG_SUBDIRS([$1])dnl
SPICE_COMMON_CFLAGS='-I${top_srcdir}/$1 -I${top_builddir}/$1 -DG_LOG_DOMAIN=\"Spice\" $(SPICE_PROTOCOL_CFLAGS) $(GLIB2_CFLAGS)'
AC_SUBST([SPICE_COMMON_CFLAGS])dnl
SPICE_COMMON_DIR='${top_builddir}/$1'
AC_SUBST([SPICE_COMMON_DIR])dnl
])

32
m4/spice_manual.m4 Normal file
View File

@ -0,0 +1,32 @@
dnl SPICE_MANUAL
dnl ------------
dnl Check if user wants manuals to be compiled and
dnl if all programs (asciidoc and a2x) are available
dnl ------------
dnl Shell defines:
dnl - have_asciidoc yes or not is asciidoc program is available
dnl Automake macros:
dnl - A2X a2x program or empty
dnl - ASCIIDOC asciidoc program or emtpy
dnl - BUILD_MANUAL if asciidoc and a2x are available
dnl - BUILD_HTML_MANUAL if asciidoc is available (html can be produced)
dnl - BUILD_CHUNKED_MANUAL if a2x is available
AC_DEFUN([SPICE_MANUAL],[
AC_ARG_ENABLE([manual],
AS_HELP_STRING([--enable-manual=@<:@auto/yes/no@:>@],
[Build SPICE manual]),
[],
[enable_manual="auto"])
if test "x$enable_manual" != "xno"; then
AC_PATH_PROG([ASCIIDOC], [asciidoc])
AS_IF([test -z "$ASCIIDOC" && test "x$enable_manual" = "xyes"],
[AC_MSG_ERROR([asciidoc is missing and build of manual was requested])])
AC_PATH_PROG([A2X], [a2x])
AS_IF([test -z "$A2X" && test "x$enable_manual" = "xyes"],
[AC_MSG_ERROR([a2x is missing and build of manual was requested])])
fi
AS_IF([test -n "$ASCIIDOC"], [have_asciidoc=yes], [have_asciidoc=no])
AM_CONDITIONAL([BUILD_MANUAL], [test -n "$ASCIIDOC" || test -n "$A2X"])
AM_CONDITIONAL([BUILD_HTML_MANUAL], [test -n "$ASCIIDOC"])
AM_CONDITIONAL([BUILD_CHUNKED_MANUAL], [test -n "$A2X"])
])

181
meson.build Normal file
View File

@ -0,0 +1,181 @@
#
# project definition
#
project('spice-common', 'c',
meson_version : '>= 0.49.0',
license : 'LGPLv2.1',
default_options : ['warning_level=2'])
if not meson.is_subproject()
warning('This project is only intended to be used as a subproject!')
endif
# some global vars
spice_common_global_cflags = ['-DG_LOG_DOMAIN="Spice"',
'-Wno-unused-parameter']
if get_option('alignment-checks')
spice_common_global_cflags += ['-DSPICE_DEBUG_ALIGNMENT']
endif
spice_common_deps = []
spice_common_include = include_directories('.')
spice_proto = files('spice.proto')
spice_codegen = files('spice_codegen.py')
spice_codegen_files = [spice_codegen]
compiler = meson.get_compiler('c')
spice_common_config_data = configuration_data()
if get_option('extra-checks')
spice_common_config_data.set('ENABLE_EXTRA_CHECKS', '1')
endif
if host_machine.endian() == 'big'
spice_common_config_data.set('WORDS_BIGENDIAN', '1')
endif
if get_option('instrumentation') == 'recorder'
spice_common_config_data.set('ENABLE_RECORDER', '1')
endif
if get_option('instrumentation') == 'agent'
spice_common_config_data.set('ENABLE_AGENT_INTERFACE', '1')
endif
spice_common_generate_code = get_option('generate-code')
spice_common_generate_client_code = spice_common_generate_code == 'all' or spice_common_generate_code == 'client'
spice_common_generate_server_code = spice_common_generate_code == 'all' or spice_common_generate_code == 'server'
#
# check for system headers
#
headers = ['alloca.h',
'arpa/inet.h',
'dlfcn.h',
'inttypes.h',
'netinet/in.h',
'stdlib.h',
'sys/socket.h',
'sys/stat.h',
'sys/types.h',
'unistd.h',
'regex.h',
'sys/mman.h']
foreach header : headers
if compiler.has_header(header)
spice_common_config_data.set('HAVE_@0@'.format(header.underscorify().to_upper()), '1')
endif
endforeach
#
# check for system functions
#
functions = ['alloca',
'sigaction',
'drand48',
'setlinebuf']
foreach func : functions
if compiler.has_function(func)
spice_common_config_data.set('HAVE_@0@'.format(func.underscorify().to_upper()), '1')
endif
endforeach
# FIXME
# check for libm, as workaround for https://github.com/mesonbuild/meson/issues/3740
libm = compiler.find_library('m', required : false)
if libm.found()
spice_common_deps += libm
endif
#
# check for mandatory dependencies
#
glib_version = '2.38'
glib_version_info = '>= @0@'.format(glib_version)
spice_protocol_version = '0.14.2'
spice_protocol_version_req = get_option('spice-protocol-version')
if spice_protocol_version_req.version_compare('> @0@'.format(spice_protocol_version))
spice_protocol_version = spice_protocol_version_req
endif
deps = {'spice-protocol' : '>= @0@'.format(spice_protocol_version),
'glib-2.0' : glib_version_info,
'pixman-1' : '>= 0.17.7',
'openssl' : '>= 1.0.0'}
foreach dep, version : deps
spice_common_deps += dependency(dep, version : version)
endforeach
gio2_deps = dependency('gio-2.0', version : glib_version_info)
#
# Non-mandatory/optional dependencies
#
optional_deps = {'opus' : '>= 0.9.14'}
foreach dep, version : optional_deps
d = dependency(dep, required : get_option(dep), version : version)
if d.found()
spice_common_deps += d
spice_common_config_data.set('HAVE_@0@'.format(dep.underscorify().to_upper()), '1')
endif
endforeach
# Python
if spice_common_generate_client_code or spice_common_generate_server_code
py_module = import('python')
python = py_module.find_installation('python3')
if get_option('python-checks')
foreach module : ['pyparsing']
message('Checking for python module @0@'.format(module))
cmd = run_command(python, '-c', 'import @0@'.format(module), check : false)
if cmd.returncode() != 0
error('Python module @0@ not found'.format(module))
endif
endforeach
endif
endif
# smartcard check
smartcard_dep = dependency('libcacard', required : get_option('smartcard'), version : '>= 2.5.1')
if smartcard_dep.found()
spice_common_deps += smartcard_dep
spice_common_config_data.set('USE_SMARTCARD', '1')
endif
# udev check
udev_dep = dependency('libudev', required : false)
if udev_dep.found()
spice_common_deps += udev_dep
spice_common_config_data.set('HAVE_UDEV', '1')
endif
#
# global C defines
#
glib_encoded_version = 'GLIB_VERSION_@0@'.format(glib_version.underscorify())
spice_common_global_cflags += ['-DGLIB_VERSION_MIN_REQUIRED=@0@'.format(glib_encoded_version),
'-DGLIB_VERSION_MAX_ALLOWED=@0@'.format(glib_encoded_version)]
add_project_arguments(compiler.get_supported_arguments(spice_common_global_cflags),
language : 'c')
#
# Subdirectories
#
subdir('python_modules')
subdir('common')
if get_option('tests')
subdir('tests')
endif
subdir('docs')
#
# write config.h
#
configure_file(output : 'config.h',
configuration : spice_common_config_data)

54
meson_options.txt Normal file
View File

@ -0,0 +1,54 @@
option('alignment-checks',
type : 'boolean',
value : false,
yield : true,
description : 'Enable runtime checks for cast alignment')
option('extra-checks',
type : 'boolean',
value : false,
yield : true,
description : 'Enable extra checks on code')
option('opus',
type : 'feature',
yield : true,
description: 'Enable Opus audio codec')
option('instrumentation',
type : 'combo',
value : 'no',
choices : ['recorder', 'agent', 'no'],
description: 'Enable instrumentation')
option('smartcard',
type : 'feature',
yield : true,
description : 'Enable smartcard support')
option('python-checks',
type : 'boolean',
value : true,
description : 'Enable checks for Python modules needed to build from git')
option('manual',
type : 'boolean',
value : true,
yield : true,
description : 'Build SPICE manual')
option('generate-code',
type : 'combo',
choices : ['all', 'server', 'client', 'none'],
description : 'Which code should be built')
option('tests',
type : 'boolean',
value : true,
yield : true,
description : 'Enable SPICE tests')
option('spice-protocol-version',
type : 'string',
value : '0.1', # not existing low number version
description : 'Minimal requested SPICE protocol version')

View File

@ -1,525 +0,0 @@
/*
Copyright (C) 2009-2010 Red Hat, Inc.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _H_MESSAGES
#define _H_MESSAGES
#include <spice/protocol.h>
#include "draw.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct SpiceMsgData {
uint32_t data_size;
uint8_t data[0];
} SpiceMsgData;
typedef struct SpiceMsgEmpty {
} SpiceMsgEmpty;
typedef struct SpiceMsgInputsInit {
uint32_t keyboard_modifiers;
} SpiceMsgInputsInit;
typedef struct SpiceMsgInputsKeyModifiers {
uint32_t modifiers;
} SpiceMsgInputsKeyModifiers;
typedef struct SpiceMsgMainMultiMediaTime {
uint32_t time;
} SpiceMsgMainMultiMediaTime;
typedef struct SpiceMsgMainMigrationBegin {
uint16_t port;
uint16_t sport;
uint32_t host_size;
uint8_t *host_data;
uint16_t pub_key_type;
uint32_t pub_key_size;
uint8_t *pub_key_data;
uint32_t cert_subject_size;
uint8_t *cert_subject_data;
} SpiceMsgMainMigrationBegin;
typedef struct SpiceMsgMainMigrationSwitchHost {
uint16_t port;
uint16_t sport;
uint32_t host_size;
uint8_t *host_data;
uint32_t cert_subject_size;
uint8_t *cert_subject_data;
} SpiceMsgMainMigrationSwitchHost;
typedef struct SpiceMsgMigrate {
uint32_t flags;
} SpiceMsgMigrate;
typedef struct SpiceResourceID {
uint8_t type;
uint64_t id;
} SpiceResourceID;
typedef struct SpiceResourceList {
uint16_t count;
SpiceResourceID resources[0];
} SpiceResourceList;
typedef struct SpiceMsgSetAck {
uint32_t generation;
uint32_t window;
} SpiceMsgSetAck;
typedef struct SpiceMsgcAckSync {
uint32_t generation;
} SpiceMsgcAckSync;
typedef struct SpiceWaitForChannel {
uint8_t channel_type;
uint8_t channel_id;
uint64_t message_serial;
} SpiceWaitForChannel;
typedef struct SpiceMsgWaitForChannels {
uint8_t wait_count;
SpiceWaitForChannel wait_list[0];
} SpiceMsgWaitForChannels;
typedef struct SpiceChannelId {
uint8_t type;
uint8_t id;
} SpiceChannelId;
typedef struct SpiceMsgMainInit {
uint32_t session_id;
uint32_t display_channels_hint;
uint32_t supported_mouse_modes;
uint32_t current_mouse_mode;
uint32_t agent_connected;
uint32_t agent_tokens;
uint32_t multi_media_time;
uint32_t ram_hint;
} SpiceMsgMainInit;
typedef struct SpiceMsgDisconnect {
uint64_t time_stamp;
uint32_t reason; // SPICE_ERR_?
} SpiceMsgDisconnect;
typedef struct SpiceMsgNotify {
uint64_t time_stamp;
uint32_t severity;
uint32_t visibilty;
uint32_t what;
uint32_t message_len;
uint8_t message[0];
} SpiceMsgNotify;
typedef struct SpiceMsgChannels {
uint32_t num_of_channels;
SpiceChannelId channels[0];
} SpiceMsgChannels;
typedef struct SpiceMsgMainName {
uint32_t name_len;
uint8_t name[0];
} SpiceMsgMainName;
typedef struct SpiceMsgMainUuid {
uint8_t uuid[16];
} SpiceMsgMainUuid;
typedef struct SpiceMsgMainMouseMode {
uint32_t supported_modes;
uint32_t current_mode;
} SpiceMsgMainMouseMode;
typedef struct SpiceMsgPing {
uint32_t id;
uint64_t timestamp;
void *data;
uint32_t data_len;
} SpiceMsgPing;
typedef struct SpiceMsgMainAgentDisconnect {
uint32_t error_code; // SPICE_ERR_?
} SpiceMsgMainAgentDisconnect;
#define SPICE_AGENT_MAX_DATA_SIZE 2048
typedef struct SpiceMsgMainAgentTokens {
uint32_t num_tokens;
} SpiceMsgMainAgentTokens, SpiceMsgcMainAgentTokens, SpiceMsgcMainAgentStart;
typedef struct SpiceMsgcClientInfo {
uint64_t cache_size;
} SpiceMsgcClientInfo;
typedef struct SpiceMsgcMainMouseModeRequest {
uint32_t mode;
} SpiceMsgcMainMouseModeRequest;
typedef struct SpiceCursor {
uint32_t flags;
SpiceCursorHeader header;
uint32_t data_size;
uint8_t *data;
} SpiceCursor;
typedef struct SpiceMsgDisplayMode {
uint32_t x_res;
uint32_t y_res;
uint32_t bits;
} SpiceMsgDisplayMode;
typedef struct SpiceMsgSurfaceCreate {
uint32_t surface_id;
uint32_t width;
uint32_t height;
uint32_t format;
uint32_t flags;
} SpiceMsgSurfaceCreate;
typedef struct SpiceMsgSurfaceDestroy {
uint32_t surface_id;
} SpiceMsgSurfaceDestroy;
typedef struct SpiceMsgDisplayBase {
uint32_t surface_id;
SpiceRect box;
SpiceClip clip;
} SpiceMsgDisplayBase;
typedef struct SpiceMsgDisplayDrawFill {
SpiceMsgDisplayBase base;
SpiceFill data;
} SpiceMsgDisplayDrawFill;
typedef struct SpiceMsgDisplayDrawOpaque {
SpiceMsgDisplayBase base;
SpiceOpaque data;
} SpiceMsgDisplayDrawOpaque;
typedef struct SpiceMsgDisplayDrawCopy {
SpiceMsgDisplayBase base;
SpiceCopy data;
} SpiceMsgDisplayDrawCopy;
typedef struct SpiceMsgDisplayDrawTransparent {
SpiceMsgDisplayBase base;
SpiceTransparent data;
} SpiceMsgDisplayDrawTransparent;
typedef struct SpiceMsgDisplayDrawAlphaBlend {
SpiceMsgDisplayBase base;
SpiceAlphaBlend data;
} SpiceMsgDisplayDrawAlphaBlend;
typedef struct SpiceMsgDisplayCopyBits {
SpiceMsgDisplayBase base;
SpicePoint src_pos;
} SpiceMsgDisplayCopyBits;
typedef SpiceMsgDisplayDrawCopy SpiceMsgDisplayDrawBlend;
typedef struct SpiceMsgDisplayDrawRop3 {
SpiceMsgDisplayBase base;
SpiceRop3 data;
} SpiceMsgDisplayDrawRop3;
typedef struct SpiceMsgDisplayDrawBlackness {
SpiceMsgDisplayBase base;
SpiceBlackness data;
} SpiceMsgDisplayDrawBlackness;
typedef struct SpiceMsgDisplayDrawWhiteness {
SpiceMsgDisplayBase base;
SpiceWhiteness data;
} SpiceMsgDisplayDrawWhiteness;
typedef struct SpiceMsgDisplayDrawInvers {
SpiceMsgDisplayBase base;
SpiceInvers data;
} SpiceMsgDisplayDrawInvers;
typedef struct SpiceMsgDisplayDrawStroke {
SpiceMsgDisplayBase base;
SpiceStroke data;
} SpiceMsgDisplayDrawStroke;
typedef struct SpiceMsgDisplayDrawText {
SpiceMsgDisplayBase base;
SpiceText data;
} SpiceMsgDisplayDrawText;
typedef struct SpiceMsgDisplayInvalOne {
uint64_t id;
} SpiceMsgDisplayInvalOne;
typedef struct SpiceMsgDisplayStreamCreate {
uint32_t surface_id;
uint32_t id;
uint32_t flags;
uint32_t codec_type;
uint64_t stamp;
uint32_t stream_width;
uint32_t stream_height;
uint32_t src_width;
uint32_t src_height;
SpiceRect dest;
SpiceClip clip;
} SpiceMsgDisplayStreamCreate;
typedef struct SpiceMsgDisplayStreamData {
uint32_t id;
uint32_t multi_media_time;
uint32_t data_size;
uint8_t data[0];
} SpiceMsgDisplayStreamData;
typedef struct SpiceMsgDisplayStreamClip {
uint32_t id;
SpiceClip clip;
} SpiceMsgDisplayStreamClip;
typedef struct SpiceMsgDisplayStreamDestroy {
uint32_t id;
} SpiceMsgDisplayStreamDestroy;
typedef struct SpiceMsgCursorInit {
SpicePoint16 position;
uint16_t trail_length;
uint16_t trail_frequency;
uint8_t visible;
SpiceCursor cursor;
} SpiceMsgCursorInit;
typedef struct SpiceMsgCursorSet {
SpicePoint16 position;
uint8_t visible;
SpiceCursor cursor;
} SpiceMsgCursorSet;
typedef struct SpiceMsgCursorMove {
SpicePoint16 position;
} SpiceMsgCursorMove;
typedef struct SpiceMsgCursorTrail {
uint16_t length;
uint16_t frequency;
} SpiceMsgCursorTrail;
typedef struct SpiceMsgcDisplayInit {
uint8_t pixmap_cache_id;
int64_t pixmap_cache_size; //in pixels
uint8_t glz_dictionary_id;
int32_t glz_dictionary_window_size; // in pixels
} SpiceMsgcDisplayInit;
typedef struct SpiceMsgcKeyDown {
uint32_t code;
} SpiceMsgcKeyDown;
typedef struct SpiceMsgcKeyUp {
uint32_t code;
} SpiceMsgcKeyUp;
typedef struct SpiceMsgcKeyModifiers {
uint32_t modifiers;
} SpiceMsgcKeyModifiers;
typedef struct SpiceMsgcMouseMotion {
int32_t dx;
int32_t dy;
uint32_t buttons_state;
} SpiceMsgcMouseMotion;
typedef struct SpiceMsgcMousePosition {
uint32_t x;
uint32_t y;
uint32_t buttons_state;
uint8_t display_id;
} SpiceMsgcMousePosition;
typedef struct SpiceMsgcMousePress {
int32_t button;
int32_t buttons_state;
} SpiceMsgcMousePress;
typedef struct SpiceMsgcMouseRelease {
int32_t button;
int32_t buttons_state;
} SpiceMsgcMouseRelease;
typedef struct SpiceMsgAudioVolume {
uint8_t nchannels;
uint16_t volume[0];
} SpiceMsgAudioVolume;
typedef struct SpiceMsgAudioMute {
uint8_t mute;
} SpiceMsgAudioMute;
typedef struct SpiceMsgPlaybackMode {
uint32_t time;
uint32_t mode; //SPICE_AUDIO_DATA_MODE_?
uint8_t *data;
uint32_t data_size;
} SpiceMsgPlaybackMode, SpiceMsgcRecordMode;
typedef struct SpiceMsgPlaybackStart {
uint32_t channels;
uint32_t format; //SPICE_AUDIO_FMT_?
uint32_t frequency;
uint32_t time;
} SpiceMsgPlaybackStart;
typedef struct SpiceMsgPlaybackPacket {
uint32_t time;
uint8_t *data;
uint32_t data_size;
} SpiceMsgPlaybackPacket, SpiceMsgcRecordPacket;
typedef struct SpiceMsgRecordStart {
uint32_t channels;
uint32_t format; //SPICE_AUDIO_FMT_?
uint32_t frequency;
} SpiceMsgRecordStart;
typedef struct SpiceMsgcRecordStartMark {
uint32_t time;
} SpiceMsgcRecordStartMark;
typedef struct SpiceMsgTunnelInit {
uint16_t max_num_of_sockets;
uint32_t max_socket_data_size;
} SpiceMsgTunnelInit;
typedef uint8_t SpiceTunnelIPv4[4];
typedef struct SpiceMsgTunnelIpInfo {
uint16_t type;
union {
SpiceTunnelIPv4 ipv4;
} u;
uint8_t data[0];
} SpiceMsgTunnelIpInfo;
typedef struct SpiceMsgTunnelServiceIpMap {
uint32_t service_id;
SpiceMsgTunnelIpInfo virtual_ip;
} SpiceMsgTunnelServiceIpMap;
typedef struct SpiceMsgTunnelSocketOpen {
uint16_t connection_id;
uint32_t service_id;
uint32_t tokens;
} SpiceMsgTunnelSocketOpen;
/* connection id must be the first field in msgs directed to a specific connection */
typedef struct SpiceMsgTunnelSocketFin {
uint16_t connection_id;
} SpiceMsgTunnelSocketFin;
typedef struct SpiceMsgTunnelSocketClose {
uint16_t connection_id;
} SpiceMsgTunnelSocketClose;
typedef struct SpiceMsgTunnelSocketData {
uint16_t connection_id;
uint8_t data[0];
} SpiceMsgTunnelSocketData;
typedef struct SpiceMsgTunnelSocketTokens {
uint16_t connection_id;
uint32_t num_tokens;
} SpiceMsgTunnelSocketTokens;
typedef struct SpiceMsgTunnelSocketClosedAck {
uint16_t connection_id;
} SpiceMsgTunnelSocketClosedAck;
typedef struct SpiceMsgcTunnelAddGenericService {
uint32_t type;
uint32_t id;
uint32_t group;
uint32_t port;
uint64_t name;
uint64_t description;
union {
SpiceMsgTunnelIpInfo ip;
} u;
} SpiceMsgcTunnelAddGenericService;
typedef struct SpiceMsgcTunnelRemoveService {
uint32_t id;
} SpiceMsgcTunnelRemoveService;
/* connection id must be the first field in msgs directed to a specific connection */
typedef struct SpiceMsgcTunnelSocketOpenAck {
uint16_t connection_id;
uint32_t tokens;
} SpiceMsgcTunnelSocketOpenAck;
typedef struct SpiceMsgcTunnelSocketOpenNack {
uint16_t connection_id;
} SpiceMsgcTunnelSocketOpenNack;
typedef struct SpiceMsgcTunnelSocketData {
uint16_t connection_id;
uint8_t data[0];
} SpiceMsgcTunnelSocketData;
typedef struct SpiceMsgcTunnelSocketFin {
uint16_t connection_id;
} SpiceMsgcTunnelSocketFin;
typedef struct SpiceMsgcTunnelSocketClosed {
uint16_t connection_id;
} SpiceMsgcTunnelSocketClosed;
typedef struct SpiceMsgcTunnelSocketClosedAck {
uint16_t connection_id;
} SpiceMsgcTunnelSocketClosedAck;
typedef struct SpiceMsgcTunnelSocketTokens {
uint16_t connection_id;
uint32_t num_tokens;
} SpiceMsgcTunnelSocketTokens;
#ifdef __cplusplus
}
#endif
#endif /* _H_SPICE_PROTOCOL */

44
mutex.h
View File

@ -1,44 +0,0 @@
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2009 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _H_MUTEX
#define _H_MUTEX
#ifdef __cplusplus
extern "C" {
#endif
#ifdef _WIN32
#include <windows.h>
typedef CRITICAL_SECTION mutex_t;
#define MUTEX_INIT(mutex) InitializeCriticalSection(&mutex)
#define MUTEX_LOCK(mutex) EnterCriticalSection(&mutex)
#define MUTEX_UNLOCK(mutex) LeaveCriticalSection(&mutex)
#else
#include <pthread.h>
typedef pthread_mutex_t mutex_t;
#define MUTEX_INIT(mutex) pthread_mutex_init(&mutex, NULL);
#define MUTEX_LOCK(mutex) pthread_mutex_lock(&mutex)
#define MUTEX_UNLOCK(mutex) pthread_mutex_unlock(&mutex)
#endif
#ifdef __cplusplus
}
#endif
#endif // _H_MUTEX

251
ogl_ctx.c
View File

@ -1,251 +0,0 @@
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2009 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <X11/Xlib.h>
#include <GL/glx.h>
#include "ogl_ctx.h"
#include "spice_common.h"
enum {
OGLCTX_TYPE_PBUF,
OGLCTX_TYPE_PIXMAP,
};
struct OGLCtx {
int type;
Display *x_display;
GLXContext glx_context;
GLXDrawable drawable;
};
typedef struct OGLPixmapCtx {
OGLCtx base;
Pixmap pixmap;
} OGLPixmapCtx;
const char *oglctx_type_str(OGLCtx *ctx)
{
static const char *pbuf_str = "pbuf";
static const char *pixmap_str = "pixmap";
static const char *invalid_str = "invalid";
switch (ctx->type) {
case OGLCTX_TYPE_PBUF:
return pbuf_str;
case OGLCTX_TYPE_PIXMAP:
return pixmap_str;
default:
return invalid_str;
}
}
void oglctx_make_current(OGLCtx *ctx)
{
if (!glXMakeCurrent(ctx->x_display, ctx->drawable, ctx->glx_context)) {
printf("%s: failed\n", __FUNCTION__);
}
}
OGLCtx *pbuf_create(int width, int heigth)
{
OGLCtx *ctx;
Display *x_display;
int num_configs;
GLXFBConfig *fb_config;
GLXPbuffer glx_pbuf;
GLXContext glx_context;
const int glx_attributes[] = { GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_ALPHA_SIZE, 8,
GLX_STENCIL_SIZE, 4,
0 };
int pbuf_attrib[] = { GLX_PRESERVED_CONTENTS, True,
GLX_PBUFFER_WIDTH, width,
GLX_PBUFFER_HEIGHT, heigth,
GLX_LARGEST_PBUFFER, False,
0, 0 };
if (!(ctx = calloc(1, sizeof(*ctx)))) {
printf("%s: alloc pbuf failed\n", __FUNCTION__);
return NULL;
}
if (!(x_display = XOpenDisplay(NULL))) {
printf("%s: open display failed\n", __FUNCTION__);
goto error_1;
}
if (!(fb_config = glXChooseFBConfig(x_display, 0, glx_attributes, &num_configs)) ||
!num_configs) {
printf("%s: choose fb config failed\n", __FUNCTION__);
goto error_2;
}
if (!(glx_pbuf = glXCreatePbuffer(x_display, fb_config[0], pbuf_attrib))) {
goto error_3;
}
if (!(glx_context = glXCreateNewContext(x_display, fb_config[0], GLX_RGBA_TYPE, NULL, True))) {
printf("%s: create context failed\n", __FUNCTION__);
goto error_4;
}
XFree(fb_config);
ctx->type = OGLCTX_TYPE_PBUF;
ctx->drawable = glx_pbuf;
ctx->glx_context = glx_context;
ctx->x_display = x_display;
return ctx;
error_4:
glXDestroyPbuffer(x_display, glx_pbuf);
error_3:
XFree(fb_config);
error_2:
XCloseDisplay(x_display);
error_1:
free(ctx);
return NULL;
}
OGLCtx *pixmap_create(int width, int heigth)
{
Display *x_display;
int num_configs;
GLXFBConfig *fb_config;
GLXPixmap glx_pixmap;
GLXContext glx_context;
Pixmap pixmap;
int screen;
Window root_window;
OGLPixmapCtx *pix;
const int glx_attributes[] = { GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_ALPHA_SIZE, 8,
GLX_STENCIL_SIZE, 4,
0 };
if (!(pix = calloc(1, sizeof(*pix)))) {
printf("%s: alloc pix failed\n", __FUNCTION__);
return NULL;
}
if (!(x_display = XOpenDisplay(NULL))) {
printf("%s: open display failed\n", __FUNCTION__);
goto error_1;
}
screen = DefaultScreen(x_display);
root_window = RootWindow(x_display, screen);
if (!(fb_config = glXChooseFBConfig(x_display, 0, glx_attributes, &num_configs)) ||
!num_configs) {
printf("%s: choose fb config failed\n", __FUNCTION__);
goto error_2;
}
if (!(pixmap = XCreatePixmap(x_display, root_window, width, heigth, 32 /*use fb config*/))) {
printf("%s: create x pixmap failed\n", __FUNCTION__);
goto error_3;
}
if (!(glx_pixmap = glXCreatePixmap(x_display, fb_config[0], pixmap, NULL))) {
printf("%s: create glx pixmap failed\n", __FUNCTION__);
goto error_4;
}
if (!(glx_context = glXCreateNewContext(x_display, fb_config[0], GLX_RGBA_TYPE, NULL, True))) {
printf("%s: create context failed\n", __FUNCTION__);
goto error_5;
}
XFree(fb_config);
pix->base.type = OGLCTX_TYPE_PIXMAP;
pix->base.x_display = x_display;
pix->base.drawable = glx_pixmap;
pix->base.glx_context = glx_context;
pix->pixmap = pixmap;
return &pix->base;
error_5:
glXDestroyPixmap(x_display, glx_pixmap);
error_4:
XFreePixmap(x_display, pixmap);
error_3:
XFree(fb_config);
error_2:
XCloseDisplay(x_display);
error_1:
free(pix);
return NULL;
}
void oglctx_destroy(OGLCtx *ctx)
{
if (!ctx) {
return;
}
// test is current ?
glXDestroyContext(ctx->x_display, ctx->glx_context);
switch (ctx->type) {
case OGLCTX_TYPE_PBUF:
glXDestroyPbuffer(ctx->x_display, ctx->drawable);
break;
case OGLCTX_TYPE_PIXMAP:
glXDestroyPixmap(ctx->x_display, ctx->drawable);
XFreePixmap(ctx->x_display, ((OGLPixmapCtx *)ctx)->pixmap);
break;
default:
PANIC("invalid ogl ctx type");
}
XCloseDisplay(ctx->x_display);
free(ctx);
}

View File

@ -0,0 +1,16 @@
NULL =
PYTHON_MODULES = \
__init__.py \
codegen.py \
demarshal.py \
marshal.py \
ptypes.py \
spice_parser.py \
$(NULL)
EXTRA_DIST = meson.build $(PYTHON_MODULES)
DISTCLEANFILES = *.pyc __pycache__/*.pyc
-include $(top_srcdir)/git.mk

View File

376
python_modules/codegen.py Normal file
View File

@ -0,0 +1,376 @@
from io import StringIO
def camel_to_underscores(s, upper = False):
res = ""
for i in range(len(s)):
c = s[i]
if i > 0 and c.isupper():
res = res + "_"
if upper:
res = res + c.upper()
else:
res = res + c.lower()
return res
def underscores_to_camel(s):
res = ""
do_upper = True
for i in range(len(s)):
c = s[i]
if c == "_":
do_upper = True
else:
if do_upper:
res = res + c.upper()
else:
res = res + c
do_upper = False
return res
proto_prefix = "Temp"
def set_prefix(prefix):
global proto_prefix
global proto_prefix_upper
global proto_prefix_lower
proto_prefix = prefix
proto_prefix_upper = prefix.upper()
proto_prefix_lower = prefix.lower()
def prefix_underscore_upper(*args):
s = proto_prefix_upper
for arg in args:
s = s + "_" + arg
return s
def prefix_underscore_lower(*args):
s = proto_prefix_lower
for arg in args:
s = s + "_" + arg
return s
def prefix_camel(*args):
s = proto_prefix
for arg in args:
s = s + underscores_to_camel(arg)
return s
def increment_identifier(idf):
v = idf[-1:]
if v.isdigit():
return idf[:-1] + str(int(v) + 1)
return idf + "2"
def sum_array(array):
if len(array) == 0:
return 0
return " + ".join(array)
class CodeWriter:
def __init__(self):
self.out = StringIO()
self.contents = [self.out]
self.indentation = 0
self.at_line_start = True
self.indexes = ["i", "j", "k", "ii", "jj", "kk"]
self.current_index = 0
self.generated = {}
self.vars = []
self.has_error_check = False
self.options = {}
self.function_helper_writer = None
self.index_type = 'uint32_t'
def set_option(self, opt, value = True):
self.options[opt] = value
def has_option(self, opt):
return opt in self.options
def set_is_generated(self, kind, name):
if kind not in self.generated:
v = {}
self.generated[kind] = v
else:
v = self.generated[kind]
v[name] = 1
def is_generated(self, kind, name):
if kind not in self.generated:
return False
v = self.generated[kind]
return name in v
def getvalue(self):
strs = [writer.getvalue() for writer in self.contents]
return "".join(strs)
def get_subwriter(self):
writer = CodeWriter()
self.contents.append(writer)
self.out = StringIO()
self.contents.append(self.out)
writer.indentation = self.indentation
writer.at_line_start = self.at_line_start
writer.index_type = self.index_type
writer.generated = self.generated
writer.options = self.options
writer.public_suffix = self.public_suffix
return writer
def write(self, s):
# Ensure its a unicode string
s = str(s)
if len(s) == 0:
return
if self.at_line_start:
self.out.write(" " * self.indentation)
self.at_line_start = False
self.out.write(s)
return self
def newline(self):
self.out.write("\n")
self.at_line_start = True
return self
def writeln(self, s):
self.write(s)
self.newline()
return self
def label(self, s):
self.indentation = self.indentation - 1
self.write(s + ":")
self.indentation = self.indentation + 1
self.newline()
def statement(self, s):
self.write(s)
self.write(";")
self.newline()
return self
def assign(self, var, val):
self.write("%s = %s" % (var, val))
self.write(";")
self.newline()
return self
def increment(self, var, val):
self.write("%s += %s" % (var, val))
self.write(";")
self.newline()
return self
def comment(self, str):
self.write("/* " + str + " */")
return self
def todo(self, str):
self.comment("TODO: *** %s ***" % str).newline()
return self
def error_check(self, check, label = "error"):
self.has_error_check = True
with self.block("if (SPICE_UNLIKELY(%s))" % check):
if self.has_option("print_error"):
self.statement('printf("%%s: Caught error - %s", __PRETTY_FUNCTION__)' % check)
if self.has_option("assert_on_error"):
self.statement("assert(0)")
self.statement("goto %s" % label)
def indent(self):
self.indentation += 4
def unindent(self):
self.indentation -= 4
if self.indentation < 0:
self.indentation = 0
def begin_block(self, prefix= "", comment = ""):
if len(prefix) > 0:
self.write(prefix)
if self.at_line_start:
self.write("{")
else:
self.write(" {")
if len(comment) > 0:
self.write(" ")
self.comment(comment)
self.newline()
self.indent()
def end_block(self, semicolon=False, newline=True):
self.unindent()
if self.at_line_start:
self.write("}")
else:
self.write(" }")
if semicolon:
self.write(";")
if newline:
self.newline()
class Block:
def __init__(self, writer, semicolon, newline):
self.writer = writer
self.semicolon = semicolon
self.newline = newline
def __enter__(self):
return self.writer.get_subwriter()
def __exit__(self, exc_type, exc_value, traceback):
self.writer.end_block(self.semicolon, self.newline)
class PartialBlock:
def __init__(self, writer, scope, semicolon, newline):
self.writer = writer
self.scope = scope
self.semicolon = semicolon
self.newline = newline
def __enter__(self):
return self.scope
def __exit__(self, exc_type, exc_value, traceback):
self.writer.end_block(self.semicolon, self.newline)
class NoBlock:
def __init__(self, scope):
self.scope = scope
def __enter__(self):
return self.scope
def __exit__(self, exc_type, exc_value, traceback):
pass
def block(self, prefix= "", comment = "", semicolon=False, newline=True):
self.begin_block(prefix, comment)
return self.Block(self, semicolon, newline)
def partial_block(self, scope, semicolon=False, newline=True):
return self.PartialBlock(self, scope, semicolon, newline)
def no_block(self, scope):
return self.NoBlock(scope)
def optional_block(self, scope):
if scope != None:
return self.NoBlock(scope)
return self.block()
def for_loop(self, index, limit):
return self.block("for (%s = 0; %s < %s; %s++)" % (index, index, limit, index))
def while_loop(self, expr):
return self.block("while (%s)" % (expr))
def if_block(self, check, elseif=False, newline=True):
s = "if (%s)" % (check)
if elseif:
s = " else " + s
self.begin_block(s, "")
return self.Block(self, False, newline)
def variable_defined(self, name):
for n in self.vars:
if n == name:
return True
return False
def variable_def(self, ctype, *names):
for n in names:
# Strip away initialization
i = n.find("=")
if i != -1:
n = n[0:i]
self.vars.append(n.strip())
# only add space for non-pointer types
if ctype[-1] == "*":
ctype = ctype[:-1].rstrip()
self.writeln("%s *%s;"%(ctype, ", *".join(names)))
else:
self.writeln("%s %s;"%(ctype, ", ".join(names)))
return self
def function_helper(self):
if self.function_helper_writer != None:
writer = self.function_helper_writer.get_subwriter()
self.function_helper_writer.newline()
else:
writer = self.get_subwriter()
return writer
def function(self, name, return_type, args, static = False):
self.has_error_check = False
self.function_helper_writer = self.get_subwriter()
if static:
self.write("static ")
self.write(return_type)
self.write(" %s(%s)"% (name, args)).newline()
self.begin_block()
self.function_variables_writer = self.get_subwriter()
self.function_variables = {}
return self.function_variables_writer
def macro(self, name, args, define):
self.write("#define %s(%s) %s" % (name, args, define)).newline()
def ifdef(self, name):
indentation = self.indentation
self.indentation = 0;
self.write("#ifdef %s" % (name)).newline()
self.indentation = indentation
def ifdef_else(self, name):
indentation = self.indentation
self.indentation = 0;
self.write("#else /* %s */" % (name)).newline()
self.indentation = indentation
def endif(self, name):
indentation = self.indentation
self.indentation = 0;
self.write("#endif /* %s */" % (name)).newline()
self.indentation = indentation
def add_function_variable(self, ctype, name):
if name in self.function_variables:
assert(self.function_variables[name] == ctype)
else:
self.function_variables[name] = ctype
self.function_variables_writer.variable_def(ctype, name)
def pop_index(self):
index = self.indexes[self.current_index]
self.current_index = self.current_index + 1
self.add_function_variable(self.index_type, index)
return index
def push_index(self):
assert self.current_index > 0
self.current_index = self.current_index - 1
class Index:
def __init__(self, writer, val):
self.writer = writer
self.val = val
def __enter__(self):
return self.val
def __exit__(self, exc_type, exc_value, traceback):
self.writer.push_index()
def index(self, no_block = False):
if no_block:
return self.no_block(None)
val = self.pop_index()
return self.Index(self, val)

1237
python_modules/demarshal.py Normal file

File diff suppressed because it is too large Load Diff

430
python_modules/marshal.py Normal file
View File

@ -0,0 +1,430 @@
from . import ptypes
from . import codegen
import re
import os
def write_includes(writer):
writer.header.writeln("#include <spice/protocol.h>")
writer.header.writeln('#include "common/marshaller.h"')
writer.header.newline()
if writer.header.has_option("dest_file"):
src = os.path.basename(writer.header.options["dest_file"])
else:
src = "generated_headers.h"
src = re.sub(r'(?i)[^a-z0-9]+', '_', src)
src = src.upper()
if src.endswith("_H"):
src = "_H_"+src[:-2]
writer.header.writeln("#ifndef %s" % src)
writer.header.writeln("#define %s" % src)
writer.header.newline()
writer.header.writeln("SPICE_BEGIN_DECLS")
writer.header.newline()
writer.writeln("#include <string.h>")
writer.writeln("#include <assert.h>")
writer.writeln("#include <stdlib.h>")
writer.writeln("#include <stdio.h>")
writer.writeln("#include <spice/protocol.h>")
writer.writeln("#include <spice/macros.h>")
writer.writeln('#include "common/marshaller.h"')
writer.newline()
writer.writeln("#ifdef _MSC_VER")
writer.writeln("#pragma warning(disable:4101)")
writer.writeln("#pragma warning(disable:4018)")
writer.writeln("#endif")
writer.newline()
class MarshallingSource:
def __init__(self):
pass
def child_at_end(self, t):
return RootMarshallingSource(self, t.c_type(), t.sizeof())
def child_sub(self, containee):
return SubMarshallingSource(self, containee)
def declare(self, writer):
return writer.optional_block(self.reuse_scope)
def is_toplevel(self):
return self.parent_src == None and not self.is_helper
class RootMarshallingSource(MarshallingSource):
def __init__(self, parent_src, c_type, sizeof, pointer = None):
self.is_helper = False
self.reuse_scope = None
self.parent_src = parent_src
if parent_src:
self.base_var = codegen.increment_identifier(parent_src.base_var)
else:
self.base_var = "src"
self.c_type = c_type
self.sizeof = sizeof
self.pointer = pointer
assert pointer != None
def get_self_ref(self):
return self.base_var
def get_ref(self, member):
return self.base_var + "->" + member
def declare(self, writer):
if self.reuse_scope:
scope = self.reuse_scope
else:
writer.begin_block()
scope = writer.get_subwriter()
scope.variable_def("const " + self.c_type + " *", self.base_var)
if not self.reuse_scope:
scope.newline()
writer.assign(self.base_var, "(const %s *)%s" % (self.c_type, self.pointer))
writer.newline()
if self.reuse_scope:
return writer.no_block(self.reuse_scope)
else:
return writer.partial_block(scope)
class SubMarshallingSource(MarshallingSource):
def __init__(self, parent_src, containee):
self.reuse_scope = None
self.parent_src = parent_src
self.base_var = parent_src.base_var
self.containee = containee
self.name = containee.name
self.is_helper = False
def get_self_ref(self):
if self.containee.has_attr("to_ptr"):
return "%s" % self.parent_src.get_ref(self.name)
else:
return "&%s" % self.parent_src.get_ref(self.name)
def get_ref(self, member):
if self.containee.has_attr("to_ptr"):
return self.parent_src.get_ref(self.name) + "->" + member
else:
return self.parent_src.get_ref(self.name) + "." + member
def write_marshal_ptr_function(writer, target_type, is_helper=True):
if target_type.is_array():
marshal_function = "spice_marshall_array_%s" % target_type.element_type.primitive_type()
else:
marshal_function = "spice_marshall_%s" % target_type.name
if writer.is_generated("marshaller", marshal_function):
return marshal_function
writer.set_is_generated("marshaller", marshal_function)
names = target_type.get_pointer_names(False)
names_args = ""
if len(names) > 0:
n = [", SpiceMarshaller **%s_out" % name for name in names]
names_args = "".join(n)
header = writer.header
if is_helper:
writer = writer.function_helper()
writer.header = header
writer.out_prefix = ""
if target_type.is_array():
scope = writer.function(marshal_function, "SPICE_GNUC_UNUSED static void",
"SpiceMarshaller *m, const %s_t *ptr, unsigned count" % target_type.element_type.primitive_type() + names_args)
else:
scope = writer.function(marshal_function, "void", "SpiceMarshaller *m, const %s *ptr" % target_type.c_type() + names_args)
header.writeln("void " + marshal_function + "(SpiceMarshaller *m, const %s *msg" % target_type.c_type() + names_args + ");")
scope.variable_def("SPICE_GNUC_UNUSED SpiceMarshaller *", "m2")
for n in names:
writer.assign("*%s_out" % n, "NULL")
writer.newline()
if target_type.is_struct():
src = RootMarshallingSource(None, target_type.c_type(), target_type.sizeof(), "ptr")
src.reuse_scope = scope
write_container_marshaller(writer, target_type, src)
elif target_type.is_array() and target_type.element_type.is_primitive():
with writer.index() as index:
with writer.for_loop(index, "count") as array_scope:
writer.statement("spice_marshaller_add_%s(m, *ptr++)" % (target_type.element_type.primitive_type()))
else:
writer.todo("Unsuppored pointer marshaller type")
writer.end_block()
return marshal_function
def get_array_size(array, container_src):
if array.is_constant_length():
return array.size
elif array.is_identifier_length():
return container_src.get_ref(array.size)
elif array.is_remaining_length():
raise NotImplementedError("remaining size array sizes marshalling not supported")
elif array.is_image_size_length():
bpp = array.size[1]
width = array.size[2]
rows = array.size[3]
width_v = container_src.get_ref(width)
rows_v = container_src.get_ref(rows)
if bpp == 8:
return "((uint64_t) %s * %s)" % (width_v, rows_v)
elif bpp == 1:
return "((((uint64_t) %s + 7U) / 8U ) * %s)" % (width_v, rows_v)
else:
return "((((uint64_t) %s * %s + 7U) / 8U ) * %s)" % (bpp, width_v, rows_v)
else:
raise NotImplementedError("TODO array size type not handled yet: %s" % array)
def write_array_marshaller(writer, member, array, container_src, scope):
element_type = array.element_type
if array.is_remaining_length():
writer.comment("Remaining data must be appended manually").newline()
return
nelements = get_array_size(array, container_src)
element = "%s__element" % member.name
if not scope.variable_defined(element):
if array.has_attr("ptr_array"):
type_formart = "%s * const *"
else:
type_formart = "const %s * "
scope.variable_def(type_formart % element_type.c_type(), element)
element_array = element
if array.has_attr("ptr_array"):
element = "*" + element
writer.assign(element_array, container_src.get_ref(member.name))
with writer.index() as index:
with writer.for_loop(index, nelements) as array_scope:
if element_type.is_primitive():
writer.statement("spice_marshaller_add_%s(m, *%s)" % (element_type.primitive_type(), element))
elif element_type.is_struct():
src2 = RootMarshallingSource(container_src, element_type.c_type(), element_type.sizeof(), element)
src2.reuse_scope = array_scope
write_container_marshaller(writer, element_type, src2)
else:
writer.todo("array element unhandled type").newline()
writer.statement("%s++" % element_array)
def write_pointer_marshaller(writer, member, src):
t = member.member_type
ptr_func = write_marshal_ptr_function(writer, t.target_type)
submarshaller = "spice_marshaller_get_ptr_submarshaller(m)"
if member.has_attr("marshall"):
rest_args = ""
if t.target_type.is_array():
rest_args = ", %s" % get_array_size(t.target_type, src)
writer.assign("m2", submarshaller)
if t.has_attr("nonnull"):
writer.statement("%s(m2, %s%s)" % (ptr_func, src.get_ref(member.name), rest_args))
else:
with writer.if_block("%s != NULL" % src.get_ref(member.name)) as block:
writer.statement("%s(m2, %s%s)" % (ptr_func, src.get_ref(member.name), rest_args))
else:
writer.assign("*%s_out" % (writer.out_prefix + member.name), submarshaller)
def write_switch_marshaller(writer, container, switch, src, scope):
var = container.lookup_member(switch.variable)
var_type = var.member_type
saved_out_prefix = writer.out_prefix
first = True
for c in switch.cases:
check = c.get_check(src.get_ref(switch.variable), var_type)
m = c.member
writer.out_prefix = saved_out_prefix
if m.has_attr("outvar"):
writer.out_prefix = "%s_%s" % (m.attributes["outvar"][0], writer.out_prefix)
with writer.if_block(check, not first, False) as block:
t = m.member_type
if switch.has_attr("anon"):
if t.is_struct():
src2 = src.child_sub(m)
else:
src2 = src
else:
if t.is_struct():
src2 = src.child_sub(switch).child_sub(m)
else:
src2 = src.child_sub(switch)
src2.reuse_scope = block
if t.is_struct():
write_container_marshaller(writer, t, src2)
elif t.is_pointer():
write_pointer_marshaller(writer, m, src2)
elif t.is_primitive():
if m.has_attr("zero"):
writer.statement("spice_marshaller_add_%s(m, 0)" % (t.primitive_type()))
else:
writer.statement("spice_marshaller_add_%s(m, %s)" % (t.primitive_type(), src2.get_ref(m.name)))
#TODO validate e.g. flags and enums
elif t.is_array():
write_array_marshaller(writer, m, t, src2, scope)
else:
writer.todo("Can't handle type %s" % m.member_type)
first = False
writer.newline()
def write_member_marshaller(writer, container, member, src, scope):
if member.has_attr("outvar"):
writer.out_prefix = "%s_%s" % (member.attributes["outvar"][0], writer.out_prefix)
if member.has_attr("virtual"):
writer.comment("Don't marshall @virtual %s" % member.name).newline()
return
if member.has_attr("nomarshal"):
writer.comment("Don't marshall @nomarshal %s" % member.name).newline()
return
if member.is_switch():
write_switch_marshaller(writer, container, member, src, scope)
return
t = member.member_type
if t.is_pointer():
write_pointer_marshaller(writer, member, src)
elif t.is_primitive():
if member.has_attr("zero"):
writer.statement("spice_marshaller_add_%s(m, 0)" % (t.primitive_type()))
else:
writer.statement("spice_marshaller_add_%s(m, %s)" % (t.primitive_type(), src.get_ref(member.name)))
elif t.is_array():
write_array_marshaller(writer, member, t, src, scope)
elif t.is_struct():
src2 = src.child_sub(member)
writer.comment(member.name)
write_container_marshaller(writer, t, src2)
else:
raise NotImplementedError("TODO can't handle parsing of %s" % t)
def write_container_marshaller(writer, container, src):
saved_out_prefix = writer.out_prefix
with src.declare(writer) as scope:
for m in container.members:
writer.out_prefix = saved_out_prefix
write_member_marshaller(writer, container, m, src, scope)
def write_message_marshaller(writer, message, private):
if message.has_attr("ifdef"):
writer.ifdef(message.attributes["ifdef"][0])
writer.header.ifdef(message.attributes["ifdef"][0])
writer.out_prefix = ""
function_name = "spice_marshall_" + message.c_name()
if writer.is_generated("marshaller", function_name):
return function_name
writer.set_is_generated("marshaller", function_name)
names = message.get_pointer_names(False)
names_args = ""
if len(names) > 0:
n = [", SpiceMarshaller **%s_out" % name for name in names]
names_args = "".join(n)
if private:
message_name = message.c_name()
if (not message_name.startswith("msgc_")):
#small bug above, checks for startswith("msg") which
#matches "msgc" and appends "msg_" if this fails causing
#inconsistencies
message_name = "msg_" + message_name
writer.header.writeln("void (*" + message_name + ")(SpiceMarshaller *m, const %s *msg" % message.c_type() + names_args + ");")
else:
writer.header.writeln("void " + function_name + "(SpiceMarshaller *m, const %s *msg" % message.c_type() + names_args + ");")
scope = writer.function(function_name,
"static void" if private else "void",
"SPICE_GNUC_UNUSED SpiceMarshaller *m, SPICE_GNUC_UNUSED const %s *msg" % message.c_type() + names_args)
scope.variable_def("SPICE_GNUC_UNUSED SpiceMarshaller *", "m2")
for n in names:
writer.assign("*%s_out" % n, "NULL")
# fix warnings about unused variables by not creating body if no members to parse
if any(x.is_fixed_nw_size() for x in message.members):
src = RootMarshallingSource(None, message.c_type(), message.sizeof(), "msg")
src.reuse_scope = scope
write_container_marshaller(writer, message, src)
writer.end_block()
if message.has_attr("ifdef"):
writer.endif(message.attributes["ifdef"][0])
writer.header.endif(message.attributes["ifdef"][0])
writer.newline()
return function_name
def write_protocol_marshaller(writer, proto, is_server, private_marshallers):
functions = {}
if private_marshallers:
writer.header.begin_block("typedef struct")
for c in proto.channels:
channel = c.channel_type
if channel.has_attr("ifdef"):
writer.ifdef(channel.attributes["ifdef"][0])
writer.header.ifdef(channel.attributes["ifdef"][0])
if is_server:
messages = channel.client_messages
else:
messages = channel.server_messages
for m in messages:
message = m.message_type
f = write_message_marshaller(writer, message, private_marshallers)
if channel.has_attr("ifdef") and f not in functions:
functions[f] = channel.attributes["ifdef"][0]
elif message.has_attr("ifdef") and f not in functions:
functions[f] = message.attributes["ifdef"][0]
else:
functions[f] = True
if channel.has_attr("ifdef"):
writer.endif(channel.attributes["ifdef"][0])
writer.header.endif(channel.attributes["ifdef"][0])
if private_marshallers:
writer.header.end_block(newline=False)
writer.header.writeln(" SpiceMessageMarshallers;")
writer.header.newline()
writer.header.statement("SpiceMessageMarshallers *spice_message_marshallers_get" + writer.public_suffix+"(void)")
writer.header.newline()
scope = writer.function("spice_message_marshallers_get" + writer.public_suffix,
"SpiceMessageMarshallers *",
"void")
writer.writeln("static SpiceMessageMarshallers marshallers = {0};").newline()
for f in sorted(functions.keys()):
member = f[len("spice_marshall_"):]
if not member.startswith("msg"):
member = "msg_" + member
if functions[f] != True:
writer.ifdef(functions[f])
writer.assign("marshallers.%s" % member, f)
if functions[f] != True:
writer.endif(functions[f])
writer.newline()
writer.statement("return &marshallers")
writer.end_block()
writer.newline()
def write_trailer(writer):
writer.header.newline()
writer.header.writeln("SPICE_END_DECLS")
writer.header.newline()
writer.header.writeln("#endif")

View File

@ -0,0 +1,6 @@
spice_codegen_files += files('codegen.py',
'demarshal.py',
'__init__.py',
'marshal.py',
'ptypes.py',
'spice_parser.py')

1191
python_modules/ptypes.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,159 @@
try:
from pyparsing import Literal, CaselessLiteral, Word, OneOrMore, ZeroOrMore, \
Forward, delimitedList, Group, Optional, Combine, alphas, nums, restOfLine, cStyleComment, \
alphanums, ParseException, ParseResults, Keyword, StringEnd, replaceWith
except ImportError:
print("Module pyparsing not found.")
exit(1)
from . import ptypes
import sys
cvtInt = lambda toks: int(toks[0])
def parseVariableDef(toks):
t = toks[0][0]
pointer = toks[0][1]
name = toks[0][2]
array_size = toks[0][3]
attributes = toks[0][4]
if array_size != None:
t = ptypes.ArrayType(t, array_size)
if pointer != None:
t = ptypes.PointerType(t)
return ptypes.Member(name, t, attributes)
bnf = None
def SPICE_BNF():
global bnf
if not bnf:
# punctuation
colon = Literal(":").suppress()
lbrace = Literal("{").suppress()
rbrace = Literal("}").suppress()
lbrack = Literal("[").suppress()
rbrack = Literal("]").suppress()
lparen = Literal("(").suppress()
rparen = Literal(")").suppress()
equals = Literal("=").suppress()
comma = Literal(",").suppress()
semi = Literal(";").suppress()
# primitive types
int8_ = Keyword("int8").setParseAction(replaceWith(ptypes.int8))
uint8_ = Keyword("uint8").setParseAction(replaceWith(ptypes.uint8))
int16_ = Keyword("int16").setParseAction(replaceWith(ptypes.int16))
uint16_ = Keyword("uint16").setParseAction(replaceWith(ptypes.uint16))
int32_ = Keyword("int32").setParseAction(replaceWith(ptypes.int32))
uint32_ = Keyword("uint32").setParseAction(replaceWith(ptypes.uint32))
int64_ = Keyword("int64").setParseAction(replaceWith(ptypes.int64))
uint64_ = Keyword("uint64").setParseAction(replaceWith(ptypes.uint64))
unix_fd_ = Keyword("unix_fd").setParseAction(replaceWith(ptypes.unix_fd))
# keywords
enum32_ = Keyword("enum32").setParseAction(replaceWith(32))
enum16_ = Keyword("enum16").setParseAction(replaceWith(16))
enum8_ = Keyword("enum8").setParseAction(replaceWith(8))
flags32_ = Keyword("flags32").setParseAction(replaceWith(32))
flags16_ = Keyword("flags16").setParseAction(replaceWith(16))
flags8_ = Keyword("flags8").setParseAction(replaceWith(8))
channel_ = Keyword("channel")
server_ = Keyword("server")
client_ = Keyword("client")
protocol_ = Keyword("protocol")
typedef_ = Keyword("typedef")
struct_ = Keyword("struct")
message_ = Keyword("message")
image_size_ = Keyword("image_size")
cstring_ = Keyword("cstring")
switch_ = Keyword("switch")
default_ = Keyword("default")
case_ = Keyword("case")
identifier = Word( alphas, alphanums + "_" )
enumname = Word( alphanums + "_" )
integer = ( Combine( CaselessLiteral("0x") + Word( nums+"abcdefABCDEF" ) ) |
Word( nums+"+-", nums ) ).setName("int").setParseAction(cvtInt)
typename = identifier.copy().setParseAction(lambda toks : ptypes.TypeRef(str(toks[0])))
# This is just normal "types", i.e. not channels or messages
typeSpec = Forward()
attributeValue = integer ^ identifier
attribute = Group(Combine ("@" + identifier) + Optional(lparen + delimitedList(attributeValue) + rparen))
attributes = Group(ZeroOrMore(attribute))
arraySizeSpecImage = Group(image_size_ + lparen + integer + comma + identifier + comma + identifier + rparen)
arraySizeSpecCString = Group(cstring_ + lparen + rparen)
arraySizeSpec = lbrack + Optional(identifier ^ integer ^ arraySizeSpecImage ^ arraySizeSpecCString, default="") + rbrack
variableDef = Group(typeSpec + Optional("*", default=None) + identifier + Optional(arraySizeSpec, default=None) + attributes - semi) \
.setParseAction(parseVariableDef)
switchCase = Group(Group(OneOrMore(default_.setParseAction(replaceWith(None)) + colon | Group(case_.suppress() + Optional("!", default="") + identifier) + colon)) + variableDef) \
.setParseAction(lambda toks: ptypes.SwitchCase(toks[0][0], toks[0][1]))
switchBody = Group(switch_ + lparen + delimitedList(identifier,delim='.', combine=True) + rparen + lbrace + Group(OneOrMore(switchCase)) + rbrace + identifier + attributes - semi) \
.setParseAction(lambda toks: ptypes.Switch(toks[0][1], toks[0][2], toks[0][3], toks[0][4]))
messageBody = structBody = Group(lbrace + ZeroOrMore(variableDef | switchBody) + rbrace)
structSpec = Group(struct_ + identifier + structBody + attributes).setParseAction(lambda toks: ptypes.StructType(toks[0][1], toks[0][2], toks[0][3]))
# have to use longest match for type, in case a user-defined type name starts with a keyword type, like "channel_type"
typeSpec << ( structSpec ^ int8_ ^ uint8_ ^ int16_ ^ uint16_ ^
int32_ ^ uint32_ ^ int64_ ^ uint64_ ^ unix_fd_ ^
typename).setName("type")
flagsBody = enumBody = Group(lbrace + delimitedList(Group (enumname + Optional(equals + integer, default=None) + attributes)) + Optional(comma) + rbrace)
messageSpec = Group(message_ + messageBody + attributes).setParseAction(lambda toks: ptypes.MessageType(None, toks[0][1], toks[0][2])) | typename
channelParent = Optional(colon + typename, default=None)
channelMessage = Group(messageSpec + identifier + Optional(equals + integer, default=None) + semi) \
.setParseAction(lambda toks: ptypes.ChannelMember(toks[0][1], toks[0][0], toks[0][2]))
channelBody = channelParent + Group(lbrace + ZeroOrMore( server_ + colon | client_ + colon | channelMessage) + rbrace)
enum_ = (enum32_ | enum16_ | enum8_)
flags_ = (flags32_ | flags16_ | flags8_)
enumDef = Group(enum_ + identifier + enumBody + attributes - semi).setParseAction(lambda toks: ptypes.EnumType(toks[0][0], toks[0][1], toks[0][2], toks[0][3]))
flagsDef = Group(flags_ + identifier + flagsBody + attributes - semi).setParseAction(lambda toks: ptypes.FlagsType(toks[0][0], toks[0][1], toks[0][2], toks[0][3]))
messageDef = Group(message_ + identifier + messageBody + attributes - semi).setParseAction(lambda toks: ptypes.MessageType(toks[0][1], toks[0][2], toks[0][3]))
channelDef = Group(channel_ + identifier + channelBody + attributes - semi).setParseAction(lambda toks: ptypes.ChannelType(toks[0][1], toks[0][2], toks[0][3], toks[0][4]))
structDef = Group(struct_ + identifier + structBody + attributes - semi).setParseAction(lambda toks: ptypes.StructType(toks[0][1], toks[0][2], toks[0][3]))
typedefDef = Group(typedef_ + identifier + typeSpec + attributes - semi).setParseAction(lambda toks: ptypes.TypeAlias(toks[0][1], toks[0][2], toks[0][3]))
definitions = typedefDef | structDef | enumDef | flagsDef | messageDef | channelDef
protocolChannel = Group(typename + identifier + Optional(equals + integer, default=None) + semi) \
.setParseAction(lambda toks: ptypes.ProtocolMember(toks[0][1], toks[0][0], toks[0][2]))
protocolDef = Group(protocol_ + identifier + Group(lbrace + ZeroOrMore(protocolChannel) + rbrace) + semi) \
.setParseAction(lambda toks: ptypes.ProtocolType(toks[0][1], toks[0][2]))
bnf = ZeroOrMore (definitions) + protocolDef + StringEnd()
singleLineComment = "//" + restOfLine
bnf.ignore( singleLineComment )
bnf.ignore( cStyleComment )
return bnf
def parse(filename):
try:
bnf = SPICE_BNF()
types = bnf.parseFile(filename)
except ParseException as err:
print(err.line, file=sys.stderr)
print(" "*(err.column-1) + "^", file=sys.stderr)
print(err, file=sys.stderr)
return None
for t in types:
t.resolve()
t.register()
protocol = types[-1]
return protocol

View File

@ -1,765 +0,0 @@
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2009 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#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

View File

@ -1,635 +0,0 @@
/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
Copyright (C) 2009 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#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

1398
spice.proto Normal file

File diff suppressed because it is too large Load Diff

366
spice_codegen.py Executable file
View File

@ -0,0 +1,366 @@
#!/usr/bin/env python
import os
import sys
from optparse import OptionParser
import traceback
from python_modules import spice_parser
from python_modules import ptypes
from python_modules import codegen
from python_modules import demarshal
from python_modules import marshal
def write_channel_enums(writer, channel, client, describe):
messages = list(filter(lambda m : m.channel == channel, \
channel.client_messages if client else channel.server_messages))
if len(messages) == 0:
return
if client:
prefix = [ "MSGC" ]
else:
prefix = [ "MSG" ]
if channel.member_name:
prefix.append(channel.member_name.upper())
if not describe:
writer.begin_block("enum")
else:
writer.begin_block("static const value_string %s_vs[] = " % (codegen.prefix_underscore_lower(*[x.lower() for x in prefix])))
i = 0
prefix.append(None) # To be replaced with name
for m in messages:
prefix[-1] = m.name.upper()
enum = codegen.prefix_underscore_upper(*prefix)
if describe:
writer.writeln("{ %s, \"%s %s\" }," % (enum, "Client" if client else "Server", m.name.upper()))
else:
if m.value == i:
writer.writeln("%s," % enum)
i = i + 1
else:
writer.writeln("%s = %s," % (enum, m.value))
i = m.value + 1
if describe:
writer.writeln("{ 0, NULL }");
else:
if channel.member_name:
prefix[-1] = prefix[-2]
prefix[-2] = "END"
writer.newline()
writer.writeln("%s" % (codegen.prefix_underscore_upper(*prefix)))
writer.end_block(semicolon=True)
writer.newline()
def write_channel_type_enum(writer, describe=False):
i = 0
if describe:
writer.begin_block("static const value_string channel_types_vs[] =")
else:
writer.begin_block("enum")
for c in proto.channels:
enum = codegen.prefix_underscore_upper("CHANNEL", c.name.upper())
if describe:
writer.writeln("{ %s, \"%s\" }," % (enum, c.name.upper()))
else:
if c.value == i:
writer.writeln("%s," % enum)
i = i + 1
else:
writer.writeln("%s = %s," % (enum, c.value))
i = c.value + 1
writer.newline()
if describe:
writer.writeln("{ 0, NULL }")
else:
writer.writeln("SPICE_END_CHANNEL")
writer.end_block(semicolon=True)
writer.newline()
def write_enums(writer, describe=False):
writer.writeln("#ifndef _H_SPICE_ENUMS")
writer.writeln("#define _H_SPICE_ENUMS")
writer.newline()
# Define enums
for t in ptypes.get_named_types():
if isinstance(t, ptypes.EnumBaseType):
t.c_define(writer)
if describe:
t.c_describe(writer)
write_channel_type_enum(writer)
if (describe):
write_channel_type_enum(writer, True)
for c in ptypes.get_named_types():
if not isinstance(c, ptypes.ChannelType):
continue
write_channel_enums(writer, c, False, False)
if describe:
write_channel_enums(writer, c, False, describe)
write_channel_enums(writer, c, True, False)
if describe:
write_channel_enums(writer, c, True, describe)
writer.writeln("#endif /* _H_SPICE_ENUMS */")
def write_content(dest_file, content, keep_identical_file):
if keep_identical_file:
try:
with open(dest_file, 'rb') as f:
old_content = f.read()
if content == old_content:
print("No changes to %s" % dest_file)
return
except IOError:
pass
with open(dest_file, 'wb') as f:
f.write(bytes(content, 'UTF-8'))
print("Wrote %s" % dest_file)
parser = OptionParser(usage="usage: %prog [options] <protocol_file> <destination file>")
parser.add_option("-e", "--generate-enums",
action="store_true", dest="generate_enums", default=False,
help="Generate enums")
parser.add_option("-w", "--generate-wireshark-dissector",
action="store_true", dest="generate_dissector", default=False,
help="Generate Wireshark dissector definitions")
parser.add_option("-d", "--generate-demarshallers",
action="store_true", dest="generate_demarshallers", default=False,
help="Generate demarshallers")
parser.add_option("-m", "--generate-marshallers",
action="store_true", dest="generate_marshallers", default=False,
help="Generate message marshallers")
parser.add_option("-P", "--private-marshallers",
action="store_true", dest="private_marshallers", default=False,
help="Generate private message marshallers")
parser.add_option("-M", "--generate-struct-marshaller",
action="append", dest="struct_marshallers",
help="Generate struct marshallers")
parser.add_option("-a", "--assert-on-error",
action="store_true", dest="assert_on_error", default=False,
help="Assert on error")
parser.add_option("-H", "--header",
action="store_true", dest="header", default=False,
help="Generate header")
parser.add_option("-p", "--print-error",
action="store_true", dest="print_error", default=False,
help="Print errors")
parser.add_option("-s", "--server",
action="store_true", dest="server", default=False,
help="Print errors")
parser.add_option("-c", "--client",
action="store_true", dest="client", default=False,
help="Print errors")
parser.add_option("-k", "--keep-identical-file",
action="store_true", dest="keep_identical_file", default=False,
help="Print errors")
parser.add_option("-i", "--include",
action="append", dest="includes", metavar="FILE",
help="Include FILE in generated code")
parser.add_option("--suffix", dest="suffix",
help="set public symbol suffix", default="")
parser.add_option("--license", dest="license",
help="license to use for generated file(s) (LGPL/BSD)", default="LGPL")
parser.add_option("--generate-header",
action="store_true", dest="generate_header", default=False,
help="Generate also the header")
parser.add_option("--generated-declaration-file", dest="generated_declaration_file", metavar="FILE",
help="Name of the file to generate declarations")
(options, args) = parser.parse_args()
if len(args) == 0:
parser.error("No protocol file specified")
if len(args) == 1:
parser.error("No destination file specified")
proto_file = args[0]
dest_file = args[1]
proto = spice_parser.parse(proto_file)
if proto == None:
exit(1)
codegen.set_prefix(proto.name)
writer = codegen.CodeWriter()
writer.header = codegen.CodeWriter()
if options.generate_header:
filename = os.path.splitext(dest_file)[0] + '.h'
writer.header.set_option("dest_file", filename)
else:
writer.header.set_option("dest_file", dest_file)
writer.set_option("source", os.path.basename(proto_file))
if options.license == "LGPL":
license = """/*
Copyright (C) 2013 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
"""
elif options.license == "BSD":
license = """/*
Copyright (C) 2013 Red Hat, Inc.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
"""
else:
print("Invalid license specified: %s" % options.license, file=sys.stderr)
sys.exit(1)
all_structures = {}
def generate_declaration(t, writer_top):
writer = codegen.CodeWriter()
try:
c_type = t.c_type()
t.generate_c_declaration(writer)
value = writer.getvalue().strip()
if not value:
return
if c_type in all_structures:
assert all_structures[c_type] == value, """Structure %s redefinition
previous:
%s
---
current:
%s
---""" % (c_type, all_structures[c_type], value)
else:
all_structures[c_type] = value
t.generate_c_declaration(writer_top)
except:
print('type %s' % t, file=sys.stderr)
print(writer.getvalue(), file=sys.stderr)
traceback.print_exc(file=sys.stderr)
sys.exit(1)
def generate_declarations():
writer = codegen.CodeWriter()
writer.public_suffix = options.suffix
writer.write(license)
# all types
for t in ptypes.get_named_types():
if isinstance(t, ptypes.StructType):
generate_declaration(t, writer)
if isinstance(t, ptypes.ChannelType):
for m in t.client_messages + t.server_messages:
generate_declaration(m.message_type, writer)
content = writer.getvalue()
write_content(options.generated_declaration_file, content,
options.keep_identical_file)
if options.generated_declaration_file:
generate_declarations()
writer.public_suffix = options.suffix
writer.writeln("/* this is a file autogenerated by spice_codegen.py */")
writer.write(license)
writer.header.writeln("/* this is a file autogenerated by spice_codegen.py */")
writer.header.write(license)
if not options.generate_enums:
writer.writeln("#include <config.h>")
if options.assert_on_error:
writer.set_option("assert_on_error")
if options.print_error:
writer.set_option("print_error")
if options.includes:
for i in options.includes:
writer.header.writeln('#include "%s"' % i)
writer.writeln('#include "%s"' % i)
if options.generate_enums or options.generate_dissector:
write_enums(writer, options.generate_dissector)
if options.generate_demarshallers:
if not options.server and not options.client:
print("Must specify client and/or server", file=sys.stderr)
sys.exit(1)
demarshal.write_includes(writer)
if options.server:
demarshal.write_protocol_parser(writer, proto, False)
if options.client:
demarshal.write_protocol_parser(writer, proto, True)
if options.generate_marshallers or (options.struct_marshallers and len(options.struct_marshallers) > 0):
marshal.write_includes(writer)
if options.generate_marshallers:
if not options.server and not options.client:
print("Must specify client and/or server", file=sys.stderr)
sys.exit(1)
if options.server:
marshal.write_protocol_marshaller(writer, proto, False, options.private_marshallers)
if options.client:
marshal.write_protocol_marshaller(writer, proto, True, options.private_marshallers)
if options.struct_marshallers:
for structname in options.struct_marshallers:
t = ptypes.lookup_type(structname)
marshal.write_marshal_ptr_function(writer, t, False)
if options.generate_marshallers or (options.struct_marshallers and len(options.struct_marshallers) > 0):
marshal.write_trailer(writer)
if options.header:
content = writer.header.getvalue()
else:
content = writer.getvalue()
write_content(dest_file, content, options.keep_identical_file)
if options.generate_header:
content = writer.header.getvalue()
filename = writer.header.options["dest_file"]
write_content(filename, content, options.keep_identical_file)
sys.exit(0)

Some files were not shown because too many files have changed in this diff Show More