mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2025-12-26 14:41:25 +00:00
fresh start
This commit is contained in:
commit
c1b79eb035
21
.gitignore
vendored
Normal file
21
.gitignore
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
*~
|
||||
*.tar.bz2
|
||||
*.tar.gz
|
||||
aclocal.m4
|
||||
autom4te.cache
|
||||
compile
|
||||
config.guess
|
||||
config.h
|
||||
config.h.in
|
||||
config.log
|
||||
config.status
|
||||
config.sub
|
||||
configure
|
||||
depcomp
|
||||
install-sh
|
||||
libtool
|
||||
ltmain.sh
|
||||
missing
|
||||
Makefile
|
||||
Makefile.in
|
||||
stamp-h1
|
||||
340
COPYING
Normal file
340
COPYING
Normal file
@ -0,0 +1,340 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
|
||||
7
Makefile.am
Normal file
7
Makefile.am
Normal file
@ -0,0 +1,7 @@
|
||||
SUBDIRS = common server client
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = spice.pc
|
||||
|
||||
DISTCLEANFILES = \
|
||||
spice.pc
|
||||
10
README
Normal file
10
README
Normal file
@ -0,0 +1,10 @@
|
||||
Copyright 2009 Red Hat, Inc. and/or its affiliates.
|
||||
|
||||
This program is licensed to you under the GNU General Public License,
|
||||
version 2 or (at your option) any later version published by the Free
|
||||
Software Foundation. See the file COPYING for details.
|
||||
|
||||
There is NO WARRANTY for this software, not even the implied
|
||||
warranties of MERCHANTABILITY, NONINFRINGEMENT, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE.
|
||||
|
||||
170
autogen.sh
Executable file
170
autogen.sh
Executable file
@ -0,0 +1,170 @@
|
||||
#! /bin/sh
|
||||
|
||||
srcdir=`dirname $0`
|
||||
test -z "$srcdir" && srcdir=.
|
||||
|
||||
ORIGDIR=`pwd`
|
||||
cd $srcdir
|
||||
|
||||
# FIXME: can replace this entire script with
|
||||
# the following line if we can require autoconf 2.60:
|
||||
# autoreconf -v --force --install || exit 1
|
||||
|
||||
PACKAGE=spice
|
||||
|
||||
ACLOCAL_FLAGS=""
|
||||
LIBTOOLIZE=${LIBTOOLIZE-libtoolize}
|
||||
LIBTOOLIZE_FLAGS="--copy --force"
|
||||
AUTOHEADER=${AUTOHEADER-autoheader}
|
||||
AUTOMAKE_FLAGS="--add-missing --gnu"
|
||||
AUTOCONF=${AUTOCONF-autoconf}
|
||||
|
||||
# automake 1.8 requires autoconf 2.58
|
||||
# automake 1.7 requires autoconf 2.54
|
||||
automake_min_vers=1.7
|
||||
aclocal_min_vers=$automake_min_vers
|
||||
autoconf_min_vers=2.54
|
||||
libtoolize_min_vers=1.4
|
||||
|
||||
# The awk-based string->number conversion we use needs a C locale to work
|
||||
# as expected. Setting LC_ALL overrides whether the user set LC_ALL,
|
||||
# LC_NUMERIC, or LANG.
|
||||
LC_ALL=C
|
||||
|
||||
ARGV0=$0
|
||||
|
||||
# Allow invocation from a separate build directory; in that case, we change
|
||||
# to the source directory to run the auto*, then change back before running configure
|
||||
srcdir=`dirname $ARGV0`
|
||||
test -z "$srcdir" && srcdir=.
|
||||
|
||||
ORIGDIR=`pwd`
|
||||
|
||||
# Not all echo versions allow -n, so we check what is possible. This test is
|
||||
# based on the one in autoconf.
|
||||
case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
|
||||
*c*,-n*) ECHO_N= ;;
|
||||
*c*,* ) ECHO_N=-n ;;
|
||||
*) ECHO_N= ;;
|
||||
esac
|
||||
|
||||
|
||||
# some terminal codes ...
|
||||
boldface="`tput bold 2>/dev/null || true`"
|
||||
normal="`tput sgr0 2>/dev/null || true`"
|
||||
printbold() {
|
||||
echo $ECHO_N "$boldface"
|
||||
echo "$@"
|
||||
echo $ECHO_N "$normal"
|
||||
}
|
||||
printerr() {
|
||||
echo "$@" >&2
|
||||
}
|
||||
|
||||
|
||||
# Usage:
|
||||
# compare_versions MIN_VERSION ACTUAL_VERSION
|
||||
# returns true if ACTUAL_VERSION >= MIN_VERSION
|
||||
compare_versions() {
|
||||
ch_min_version=$1
|
||||
ch_actual_version=$2
|
||||
ch_status=0
|
||||
IFS="${IFS= }"; ch_save_IFS="$IFS"; IFS="."
|
||||
set $ch_actual_version
|
||||
for ch_min in $ch_min_version; do
|
||||
ch_cur=`echo $1 | sed 's/[^0-9].*$//'`; shift # remove letter suffixes
|
||||
if [ -z "$ch_min" ]; then break; fi
|
||||
if [ -z "$ch_cur" ]; then ch_status=1; break; fi
|
||||
if [ $ch_cur -gt $ch_min ]; then break; fi
|
||||
if [ $ch_cur -lt $ch_min ]; then ch_status=1; break; fi
|
||||
done
|
||||
IFS="$ch_save_IFS"
|
||||
return $ch_status
|
||||
}
|
||||
|
||||
# Usage:
|
||||
# version_check PACKAGE VARIABLE CHECKPROGS MIN_VERSION SOURCE
|
||||
# checks to see if the package is available
|
||||
version_check() {
|
||||
vc_package=$1
|
||||
vc_variable=$2
|
||||
vc_checkprogs=$3
|
||||
vc_min_version=$4
|
||||
vc_source=$5
|
||||
vc_status=1
|
||||
|
||||
vc_checkprog=`eval echo "\\$$vc_variable"`
|
||||
if [ -n "$vc_checkprog" ]; then
|
||||
printbold "using $vc_checkprog for $vc_package"
|
||||
return 0
|
||||
fi
|
||||
|
||||
printbold "checking for $vc_package >= $vc_min_version..."
|
||||
for vc_checkprog in $vc_checkprogs; do
|
||||
echo $ECHO_N " testing $vc_checkprog... "
|
||||
if $vc_checkprog --version < /dev/null > /dev/null 2>&1; then
|
||||
vc_actual_version=`$vc_checkprog --version | head -n 1 | \
|
||||
sed 's/^.*[ ]\([0-9.]*[a-z]*\).*$/\1/'`
|
||||
if compare_versions $vc_min_version $vc_actual_version; then
|
||||
echo "found $vc_actual_version"
|
||||
# set variable
|
||||
eval "$vc_variable=$vc_checkprog"
|
||||
vc_status=0
|
||||
break
|
||||
else
|
||||
echo "too old (found version $vc_actual_version)"
|
||||
fi
|
||||
else
|
||||
echo "not found."
|
||||
fi
|
||||
done
|
||||
if [ "$vc_status" != 0 ]; then
|
||||
printerr "***Error***: You must have $vc_package >= $vc_min_version installed"
|
||||
printerr " to build $PROJECT. Download the appropriate package for"
|
||||
printerr " from your distribution or get the source tarball at"
|
||||
printerr " $vc_source"
|
||||
printerr
|
||||
fi
|
||||
return $vc_status
|
||||
}
|
||||
|
||||
version_check autoconf AUTOCONF $AUTOCONF $autoconf_min_vers \
|
||||
"http://ftp.gnu.org/pub/gnu/autoconf/autoconf-${autoconf_min_vers}.tar.gz" || DIE=1
|
||||
version_check automake AUTOMAKE "$AUTOMAKE automake automake-1.10 automake-1.9 automake-1.8 automake-1.7" $automake_min_vers \
|
||||
"http://ftp.gnu.org/pub/gnu/automake/automake-${automake_min_vers}.tar.gz" || DIE=1
|
||||
ACLOCAL=`echo $AUTOMAKE | sed s/automake/aclocal/`
|
||||
version_check libtool LIBTOOLIZE "$LIBTOOLIZE glibtoolize libtoolize" $libtoolize_min_vers \
|
||||
"http://ftp.gnu.org/pub/gnu/libtool/libtool-${libtool_min_vers}.tar.gz" || DIE=1
|
||||
|
||||
if test -n "$DIE"; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
if test -z "$*"; then
|
||||
echo "$ARGV0: Note: \`./configure' will be run with no arguments."
|
||||
echo " If you wish to pass any to it, please specify them on the"
|
||||
echo " \`$0' command line."
|
||||
echo
|
||||
fi
|
||||
|
||||
do_cmd() {
|
||||
echo "$ARGV0: running \`$@'"
|
||||
$@
|
||||
}
|
||||
|
||||
# Run for top level directory
|
||||
|
||||
printbold "Setting up $PACKAGE toplevel"
|
||||
cd $srcdir
|
||||
do_cmd $LIBTOOLIZE $LIBTOOLIZE_FLAGS
|
||||
do_cmd $ACLOCAL $ACLOCAL_FLAGS
|
||||
do_cmd $AUTOHEADER
|
||||
do_cmd $AUTOMAKE $AUTOMAKE_FLAGS
|
||||
do_cmd $AUTOCONF
|
||||
|
||||
cd $ORIGDIR || exit $?
|
||||
rm -f config.cache
|
||||
|
||||
do_cmd $srcdir/configure --enable-maintainer-mode ${1+"$@"} || exit 1
|
||||
|
||||
8
client/.gitignore
vendored
Normal file
8
client/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
*.la
|
||||
*.lo
|
||||
*.loT
|
||||
*.o
|
||||
.deps
|
||||
.libs
|
||||
Makefile
|
||||
Makefile.in
|
||||
82
client/Makefile.am
Normal file
82
client/Makefile.am
Normal file
@ -0,0 +1,82 @@
|
||||
NULL =
|
||||
|
||||
SUBDIRS = $(red_target)
|
||||
DIST_SUBDIRS = x11 #windows
|
||||
|
||||
RED_COMMON_SRCS = \
|
||||
application.cpp \
|
||||
application.h \
|
||||
audio_channels.h \
|
||||
audio_devices.h \
|
||||
cache.hpp \
|
||||
cairo_canvas.cpp \
|
||||
canvas.cpp \
|
||||
canvas.h \
|
||||
canvas_utils.cpp \
|
||||
red_cairo_canvas.cpp \
|
||||
red_cairo_canvas.h \
|
||||
cmd_line_parser.cpp \
|
||||
cmd_line_parser.h \
|
||||
common.h \
|
||||
cursor_channel.cpp \
|
||||
cursor_channel.h \
|
||||
cursor.cpp \
|
||||
cursor.h \
|
||||
debug.h \
|
||||
display_channel.cpp \
|
||||
display_channel.h \
|
||||
events_loop.h \
|
||||
red_gl_canvas.cpp \
|
||||
red_gl_canvas.h \
|
||||
gl_canvas.cpp \
|
||||
glc.cpp \
|
||||
glz_decoded_image.h \
|
||||
glz_decoder_config.h \
|
||||
glz_decoder.cpp \
|
||||
glz_decoder.h \
|
||||
glz_decoder_window.cpp \
|
||||
glz_decoder_window.h \
|
||||
glz_decode_tmpl.c \
|
||||
inputs_channel.cpp \
|
||||
inputs_channel.h \
|
||||
inputs_handler.h \
|
||||
lz.cpp \
|
||||
monitor.cpp \
|
||||
monitor.h \
|
||||
menu.cpp \
|
||||
menu.h \
|
||||
pixels_source.h \
|
||||
platform.h \
|
||||
playback_channel.cpp \
|
||||
quic.cpp \
|
||||
read_write_mutex.h \
|
||||
record_channel.cpp \
|
||||
red_channel.cpp \
|
||||
red_channel.h \
|
||||
red_client.cpp \
|
||||
red_client.h \
|
||||
red_drawable.h \
|
||||
red_key.h \
|
||||
red_peer.cpp \
|
||||
red_peer.h \
|
||||
red_pixmap_cairo.h \
|
||||
red_pixmap_gl.h \
|
||||
red_pixmap.h \
|
||||
red_types.h \
|
||||
red_window.h \
|
||||
region.cpp \
|
||||
rop3.cpp \
|
||||
screen.cpp \
|
||||
screen.h \
|
||||
screen_layer.cpp \
|
||||
screen_layer.h \
|
||||
shared_cache.hpp \
|
||||
hot_keys.cpp \
|
||||
hot_keys.h \
|
||||
threads.cpp \
|
||||
threads.h \
|
||||
utils.cpp \
|
||||
utils.h \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DIST = $(RED_COMMON_SRCS)
|
||||
1674
client/application.cpp
Normal file
1674
client/application.cpp
Normal file
File diff suppressed because it is too large
Load Diff
267
client/application.h
Normal file
267
client/application.h
Normal file
@ -0,0 +1,267 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_APPLICATION
|
||||
#define _H_APPLICATION
|
||||
|
||||
#include "common.h"
|
||||
#include "threads.h"
|
||||
#include "red_client.h"
|
||||
#include "red_key.h"
|
||||
#include "platform.h"
|
||||
#include "menu.h"
|
||||
#include "hot_keys.h"
|
||||
|
||||
class RedScreen;
|
||||
class Application;
|
||||
class ScreenLayer;
|
||||
class GUILayer;
|
||||
class InputsHandler;
|
||||
class Monitor;
|
||||
class CmdLineParser;
|
||||
class Menu;
|
||||
|
||||
class Event {
|
||||
public:
|
||||
Event() : _refs (1) {}
|
||||
|
||||
virtual void responce(Application& application) = 0;
|
||||
|
||||
Event* ref() { ++_refs; return this;}
|
||||
void unref() {if (--_refs == 0) delete this;}
|
||||
|
||||
protected:
|
||||
virtual ~Event() {}
|
||||
|
||||
AtomicCount _refs;
|
||||
friend class Application;
|
||||
uint32_t _generation;
|
||||
};
|
||||
|
||||
class SyncEvent: public Event {
|
||||
public:
|
||||
SyncEvent();
|
||||
|
||||
void wait();
|
||||
bool success() { return !_err;}
|
||||
|
||||
virtual void do_responce(Application& application) {}
|
||||
|
||||
protected:
|
||||
virtual ~SyncEvent();
|
||||
|
||||
private:
|
||||
virtual void responce(Application& application);
|
||||
|
||||
private:
|
||||
Mutex _mutex;
|
||||
Condition _condition;
|
||||
bool _err;
|
||||
bool _ready;
|
||||
};
|
||||
|
||||
class ConnectedEvent: public Event {
|
||||
public:
|
||||
ConnectedEvent() : Event() {}
|
||||
virtual void responce(Application& application);
|
||||
};
|
||||
|
||||
class DisconnectedEvent: public Event {
|
||||
public:
|
||||
DisconnectedEvent() : Event() {}
|
||||
virtual void responce(Application& application);
|
||||
};
|
||||
|
||||
class CoonnectionError: public Event {
|
||||
public:
|
||||
CoonnectionError(int error_code) : Event(), _error_code (error_code) {}
|
||||
|
||||
virtual void responce(Application& application);
|
||||
|
||||
private:
|
||||
int _error_code;
|
||||
};
|
||||
|
||||
class ErrorEvent: public Event {
|
||||
public:
|
||||
ErrorEvent() : Event() {}
|
||||
virtual void responce(Application& application);
|
||||
};
|
||||
|
||||
struct MonitorInfo {
|
||||
int depth;
|
||||
Point size;
|
||||
Point position;
|
||||
};
|
||||
|
||||
class MonitorsQuery: public SyncEvent {
|
||||
public:
|
||||
MonitorsQuery() {}
|
||||
|
||||
virtual void do_responce(Application& application);
|
||||
|
||||
std::vector<MonitorInfo>& get_monitors() {return _monitors;}
|
||||
|
||||
private:
|
||||
std::vector<MonitorInfo> _monitors;
|
||||
};
|
||||
|
||||
struct KeyInfo {
|
||||
uint32_t _make;
|
||||
uint32_t _break;
|
||||
bool press;
|
||||
};
|
||||
|
||||
enum CanvasOption {
|
||||
CANVAS_OPTION_INVALID,
|
||||
CANVAS_OPTION_CAIRO,
|
||||
#ifdef WIN32
|
||||
CANVAS_OPTION_GDI,
|
||||
#endif
|
||||
#ifdef USE_OGL
|
||||
CANVAS_OPTION_OGL_FBO,
|
||||
CANVAS_OPTION_OGL_PBUFF,
|
||||
#endif
|
||||
};
|
||||
|
||||
class Application : public Platform::EventListener,
|
||||
public Platform::DisplayModeListner,
|
||||
public CommandTarget {
|
||||
public:
|
||||
Application();
|
||||
virtual ~Application();
|
||||
|
||||
int run();
|
||||
void quit(int exit_code);
|
||||
void push_event(Event* event);
|
||||
void set_inputs_handler(InputsHandler& handler);
|
||||
void remove_inputs_handler(InputsHandler& handler);
|
||||
RedScreen* find_screen(int id);
|
||||
RedScreen* get_screen(int id);
|
||||
|
||||
void on_screen_destroyed(int id, bool was_captured);
|
||||
void on_mouse_motion(int dx, int dy, int buttons_state);
|
||||
void on_mouse_position(int x, int y, int buttons_state, int display_id);
|
||||
void on_mouse_down(int button, int buttons_state);
|
||||
void on_mouse_up(int button, int buttons_state);
|
||||
void on_key_down(RedKey key);
|
||||
void on_key_up(RedKey key);
|
||||
void on_deactivate_screen(RedScreen* screen);
|
||||
void on_activate_screen(RedScreen* screen);
|
||||
virtual void on_app_activated();
|
||||
virtual void on_app_deactivated();
|
||||
virtual void on_monitors_change();
|
||||
virtual void on_display_mode_change();
|
||||
void on_connected();
|
||||
void on_disconnecting();
|
||||
|
||||
bool rearrange_monitors(RedScreen& screen);
|
||||
void enter_full_screen();
|
||||
void exit_full_screen();
|
||||
bool toggle_full_screen();
|
||||
void minimize();
|
||||
void show_splash(int screen_id);
|
||||
void hide_splash(int screen_id);
|
||||
void set_title(std::wstring& title);
|
||||
void hide();
|
||||
void show();
|
||||
void external_show();
|
||||
void connect();
|
||||
const PeerConnectionOptMap& get_con_opt_map() {return _peer_con_opt;}
|
||||
uint32_t get_mouse_mode();
|
||||
const std::vector<int>& get_canvas_types() { return _canvas_types;}
|
||||
|
||||
Menu* get_app_menu();
|
||||
virtual void do_command(int command);
|
||||
|
||||
static int main(int argc, char** argv, const char* version_str);
|
||||
|
||||
private:
|
||||
bool set_channels_security(CmdLineParser& parser, bool on, char *val);
|
||||
bool set_enable_channels(CmdLineParser& parser, bool enable, char *val);
|
||||
bool set_canvas_option(CmdLineParser& parser, char *val);
|
||||
bool process_cmd_line(int argc, char** argv);
|
||||
void process_events();
|
||||
int message_loop();
|
||||
void abort();
|
||||
void init_scan_code(int index);
|
||||
void init_korean_scan_code(int index);
|
||||
void init_escape_scan_code(int index);
|
||||
void init_pause_scan_code();
|
||||
void init_key_table();
|
||||
void init_menu();
|
||||
uint32_t get_make_scan_code(RedKey key);
|
||||
uint32_t get_break_scan_code(RedKey key);
|
||||
void unpress_all();
|
||||
bool release_capture();
|
||||
bool do_connect();
|
||||
bool do_disconnect();
|
||||
|
||||
Monitor* find_monitor(int id);
|
||||
Monitor* get_monitor(int id);
|
||||
void init_monitors();
|
||||
void destroy_monitors();
|
||||
void assign_monitors();
|
||||
void restore_monitors();
|
||||
void prepare_monitors();
|
||||
void position_screens();
|
||||
void show_full_screen();
|
||||
void send_key_down(RedKey key);
|
||||
void send_key_up(RedKey key);
|
||||
void send_alt_ctl_del();
|
||||
void send_ctrl_alt_end();
|
||||
void send_command_hotkey(int command);
|
||||
void send_hotkey_key_set(const HotkeySet& key_set);
|
||||
void menu_item_callback(unsigned int item_id);
|
||||
int get_hotkeys_commnad();
|
||||
bool is_key_set_pressed(const HotkeySet& key_set);
|
||||
bool is_cad_pressed();
|
||||
|
||||
static void init_logger();
|
||||
static void init_globals();
|
||||
|
||||
friend class MonitorsQuery;
|
||||
friend class AutoAbort;
|
||||
|
||||
private:
|
||||
RedClient _client;
|
||||
PeerConnectionOptMap _peer_con_opt;
|
||||
std::vector<bool> _enabled_channels;
|
||||
std::vector<RedScreen*> _screens;
|
||||
std::list<Event*> _events;
|
||||
RedScreen* _main_screen;
|
||||
Mutex _events_lock;
|
||||
bool _quitting;
|
||||
bool _active;
|
||||
bool _full_screen;
|
||||
bool _changing_screens;
|
||||
int _exit_code;
|
||||
uint32_t _events_gen;
|
||||
RedScreen* _active_screen;
|
||||
KeyInfo _key_table[REDKEY_NUM_KEYS];
|
||||
HotKeys _hot_keys;
|
||||
CommandsMap _commands_map;
|
||||
std::auto_ptr<GUILayer> _gui_layer;
|
||||
InputsHandler* _inputs_handler;
|
||||
const MonitorsList* _monitors;
|
||||
std::wstring _title;
|
||||
std::vector<int> _canvas_types;
|
||||
AutoRef<Menu> _app_menu;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
101
client/audio_channels.h
Normal file
101
client/audio_channels.h
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_AUDIO_CHANNELS
|
||||
#define _H_AUDIO_CHANNELS
|
||||
|
||||
#include <celt051/celt.h>
|
||||
|
||||
#include "red_channel.h"
|
||||
#include "debug.h"
|
||||
|
||||
class ChannelFactory;
|
||||
|
||||
class WavePlaybackAbstract;
|
||||
class WaveRecordAbstract;
|
||||
class RecordSamplesMessage;
|
||||
|
||||
class PlaybackChannel: public RedChannel {
|
||||
public:
|
||||
PlaybackChannel(RedClient& client, uint32_t id);
|
||||
~PlaybackChannel(void);
|
||||
bool abort(void);
|
||||
|
||||
static ChannelFactory& Factory();
|
||||
|
||||
private:
|
||||
void handle_mode(RedPeer::InMessage* message);
|
||||
void handle_start(RedPeer::InMessage* message);
|
||||
void handle_stop(RedPeer::InMessage* message);
|
||||
void handle_raw_data(RedPeer::InMessage* message);
|
||||
void handle_celt_data(RedPeer::InMessage* message);
|
||||
void null_handler(RedPeer::InMessage* message);
|
||||
void disable();
|
||||
|
||||
void set_data_handler();
|
||||
|
||||
private:
|
||||
WavePlaybackAbstract* _wave_player;
|
||||
uint32_t _mode;
|
||||
uint32_t _frame_bytes;
|
||||
CELTMode *_celt_mode;
|
||||
CELTDecoder *_celt_decoder;
|
||||
bool _playing;
|
||||
uint32_t _frame_count;
|
||||
};
|
||||
|
||||
class RecordChannel: public RedChannel, private Platform::RecordClinet {
|
||||
public:
|
||||
RecordChannel(RedClient& client, uint32_t id);
|
||||
~RecordChannel(void);
|
||||
|
||||
bool abort(void);
|
||||
|
||||
static ChannelFactory& Factory();
|
||||
|
||||
private:
|
||||
void handle_start(RedPeer::InMessage* message);
|
||||
void handle_stop(RedPeer::InMessage* message);
|
||||
|
||||
virtual void on_connect();
|
||||
|
||||
virtual void add_evnet_sorce(EventsLoop::File& evnet_sorce);
|
||||
virtual void remove_evnet_sorce(EventsLoop::File& evnet_sorce);
|
||||
virtual void add_evnet_sorce(EventsLoop::Trigger& evnet_sorce);
|
||||
virtual void remove_evnet_sorce(EventsLoop::Trigger& evnet_sorce);
|
||||
virtual void push_frame(uint8_t *frame);
|
||||
|
||||
void send_start_mark();
|
||||
void release_message(RecordSamplesMessage *message);
|
||||
RecordSamplesMessage * get_message();
|
||||
|
||||
private:
|
||||
WaveRecordAbstract* _wave_recorder;
|
||||
Mutex _messages_lock;
|
||||
std::list<RecordSamplesMessage *> _messages;
|
||||
int _mode;
|
||||
CELTMode *_celt_mode;
|
||||
CELTEncoder *_celt_encoder;
|
||||
uint32_t _frame_bytes;
|
||||
|
||||
static int data_mode;
|
||||
|
||||
friend class RecordSamplesMessage;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
52
client/audio_devices.h
Normal file
52
client/audio_devices.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_AUDIO_DEVICES
|
||||
#define _H_AUDIO_DEVICES
|
||||
|
||||
class WavePlaybackAbstract {
|
||||
public:
|
||||
WavePlaybackAbstract() {}
|
||||
virtual ~WavePlaybackAbstract() {}
|
||||
|
||||
virtual bool write(uint8_t* frame) = 0;
|
||||
virtual bool abort() = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual uint32_t get_delay_ms() = 0;
|
||||
|
||||
enum {
|
||||
FRAME_SIZE = 256,
|
||||
};
|
||||
};
|
||||
|
||||
class WaveRecordAbstract {
|
||||
public:
|
||||
WaveRecordAbstract() {}
|
||||
virtual ~WaveRecordAbstract() {}
|
||||
|
||||
|
||||
virtual void start() = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual bool abort() = 0;
|
||||
|
||||
enum {
|
||||
FRAME_SIZE = 256,
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
121
client/cache.hpp
Normal file
121
client/cache.hpp
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_CACHE
|
||||
#define _H_CACHE
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
/*class Cache::Treat {
|
||||
T* get(T*);
|
||||
void release(T*);
|
||||
const char* name();
|
||||
};*/
|
||||
|
||||
template <class T, class Treat, int HASH_SIZE>
|
||||
class Cache {
|
||||
public:
|
||||
Cache()
|
||||
{
|
||||
memset(_hash, 0, sizeof(_hash));
|
||||
}
|
||||
|
||||
~Cache()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void add(uint64_t id, T* data)
|
||||
{
|
||||
Item** item = &_hash[key(id)];
|
||||
|
||||
while (*item) {
|
||||
if ((*item)->id == id) {
|
||||
THROW("%s id %lu, double insert", Treat::name(), id);
|
||||
}
|
||||
item = &(*item)->next;
|
||||
}
|
||||
*item = new Item(id, data);
|
||||
}
|
||||
|
||||
T* get(uint64_t id)
|
||||
{
|
||||
Item* item = _hash[key(id)];
|
||||
|
||||
while (item && item->id != id) {
|
||||
item = item->next;
|
||||
}
|
||||
|
||||
if (!item) {
|
||||
THROW("%s id %lu, not found", Treat::name(), id);
|
||||
}
|
||||
return Treat::get(item->data);
|
||||
}
|
||||
|
||||
void remove(uint64_t id)
|
||||
{
|
||||
Item** item = &_hash[key(id)];
|
||||
|
||||
while (*item) {
|
||||
if ((*item)->id == id) {
|
||||
Item *rm_item = *item;
|
||||
*item = rm_item->next;
|
||||
delete rm_item;
|
||||
return;
|
||||
}
|
||||
item = &(*item)->next;
|
||||
}
|
||||
THROW("%s id %lu, not found", Treat::name(), id);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
for (int i = 0; i < HASH_SIZE; i++) {
|
||||
while (_hash[i]) {
|
||||
Item *item = _hash[i];
|
||||
_hash[i] = item->next;
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
inline uint32_t key(uint64_t id) {return uint32_t(id) % HASH_SIZE;}
|
||||
|
||||
private:
|
||||
class Item {
|
||||
public:
|
||||
Item(uint64_t in_id, T* data)
|
||||
: id (in_id)
|
||||
, next (NULL)
|
||||
, data (Treat::get(data)) {}
|
||||
|
||||
~Item()
|
||||
{
|
||||
Treat::release(data);
|
||||
}
|
||||
|
||||
uint64_t id;
|
||||
Item* next;
|
||||
T* data;
|
||||
};
|
||||
|
||||
Item* _hash[HASH_SIZE];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
24
client/cairo_canvas.cpp
Normal file
24
client/cairo_canvas.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define CANVAS_ERROR(format, ...) THROW(format, ## __VA_ARGS__)
|
||||
|
||||
#include "../common/cairo_canvas.c"
|
||||
|
||||
251
client/canvas.cpp
Normal file
251
client/canvas.cpp
Normal file
@ -0,0 +1,251 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "canvas.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
Canvas::Canvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
|
||||
GlzDecoderWindow &glz_decoder_window)
|
||||
: _pixmap_cache (pixmap_cache)
|
||||
, _palette_cache (palette_cache)
|
||||
, _glz_decoder(glz_decoder_window, _glz_handler, _glz_debug)
|
||||
{
|
||||
}
|
||||
|
||||
Canvas::~Canvas()
|
||||
{
|
||||
}
|
||||
|
||||
inline void Canvas::access_test(void *ptr, size_t size)
|
||||
{
|
||||
if ((unsigned long)ptr < _base || (unsigned long)ptr + size > _max) {
|
||||
THROW("access violation %p %lu", ptr, size);
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::localalize_ptr(ADDRESS* data)
|
||||
{
|
||||
if (*data) {
|
||||
*data += _base;
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::localalize_image(ADDRESS* in_bitmap)
|
||||
{
|
||||
ImageDescriptor* image;
|
||||
|
||||
ASSERT(*in_bitmap);
|
||||
localalize_ptr(in_bitmap);
|
||||
image = (ImageDescriptor*)GET_ADDRESS(*in_bitmap);
|
||||
switch (image->type) {
|
||||
case IMAGE_TYPE_BITMAP: {
|
||||
BitmapImage *bitmap = (BitmapImage *)image;
|
||||
localalize_ptr(&bitmap->bitmap.data);
|
||||
if (bitmap->bitmap.palette && !(bitmap->bitmap.flags & BITMAP_PAL_FROM_CACHE)) {
|
||||
localalize_ptr(&bitmap->bitmap.palette);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IMAGE_TYPE_LZ_PLT: {
|
||||
LZ_PLTImage *lzImage = (LZ_PLTImage *)image;
|
||||
ASSERT(lzImage->lz_plt.palette);
|
||||
if (!(lzImage->lz_plt.flags & BITMAP_PAL_FROM_CACHE)) {
|
||||
localalize_ptr(&lzImage->lz_plt.palette);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IMAGE_TYPE_LZ_RGB:
|
||||
case IMAGE_TYPE_GLZ_RGB:
|
||||
case IMAGE_TYPE_QUIC:
|
||||
break;
|
||||
case IMAGE_TYPE_FROM_CACHE:
|
||||
break;
|
||||
default:
|
||||
THROW("invalid image type %u", image->type);
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::localalize_brush(Brush& brush)
|
||||
{
|
||||
if (brush.type == BRUSH_TYPE_PATTERN) {
|
||||
localalize_image(&brush.u.pattern.pat);
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::localalize_attr(LineAttr& attr)
|
||||
{
|
||||
if (attr.style_nseg) {
|
||||
localalize_ptr(&attr.style);
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::localalize_mask(QMask& mask)
|
||||
{
|
||||
if (mask.bitmap) {
|
||||
localalize_image(&mask.bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
void Canvas::begin_draw(RedDrawBase& base, int size, size_t min_size)
|
||||
{
|
||||
_base = (unsigned long)&base;
|
||||
_max = _base + size;
|
||||
set_access_params(_base, _base, _max);
|
||||
access_test(&base, min_size);
|
||||
localalize_ptr(&base.clip.data);
|
||||
}
|
||||
|
||||
void Canvas::draw_fill(RedFill& fill, int size)
|
||||
{
|
||||
begin_draw(fill.base, size, sizeof(RedFill));
|
||||
localalize_brush(fill.data.brush);
|
||||
localalize_mask(fill.data.mask);
|
||||
draw_fill(&fill.base.box, &fill.base.clip, &fill.data);
|
||||
}
|
||||
|
||||
void Canvas::draw_text(RedText& text, int size)
|
||||
{
|
||||
begin_draw(text.base, size, sizeof(RedText));
|
||||
localalize_brush(text.data.fore_brush);
|
||||
localalize_brush(text.data.back_brush);
|
||||
localalize_ptr(&text.data.str);
|
||||
draw_text(&text.base.box, &text.base.clip, &text.data);
|
||||
}
|
||||
|
||||
void Canvas::draw_opaque(RedOpaque& opaque, int size)
|
||||
{
|
||||
begin_draw(opaque.base, size, sizeof(RedOpaque));
|
||||
localalize_brush(opaque.data.brush);
|
||||
localalize_image(&opaque.data.src_bitmap);
|
||||
localalize_mask(opaque.data.mask);
|
||||
draw_opaque(&opaque.base.box, &opaque.base.clip, &opaque.data);
|
||||
}
|
||||
|
||||
void Canvas::draw_copy(RedCopy& copy, int size)
|
||||
{
|
||||
begin_draw(copy.base, size, sizeof(RedCopy));
|
||||
localalize_image(©.data.src_bitmap);
|
||||
localalize_mask(copy.data.mask);
|
||||
draw_copy(©.base.box, ©.base.clip, ©.data);
|
||||
}
|
||||
|
||||
void Canvas::draw_transparent(RedTransparent& transparent, int size)
|
||||
{
|
||||
begin_draw(transparent.base, size, sizeof(RedTransparent));
|
||||
localalize_image(&transparent.data.src_bitmap);
|
||||
draw_transparent(&transparent.base.box, &transparent.base.clip, &transparent.data);
|
||||
}
|
||||
|
||||
void Canvas::draw_alpha_blend(RedAlphaBlend& alpha_blend, int size)
|
||||
{
|
||||
begin_draw(alpha_blend.base, size, sizeof(RedAlphaBlend));
|
||||
localalize_image(&alpha_blend.data.src_bitmap);
|
||||
draw_alpha_blend(&alpha_blend.base.box, &alpha_blend.base.clip, &alpha_blend.data);
|
||||
}
|
||||
|
||||
void Canvas::copy_bits(RedCopyBits& copy, int size)
|
||||
{
|
||||
begin_draw(copy.base, size, sizeof(RedCopyBits));
|
||||
copy_bits(©.base.box, ©.base.clip, ©.src_pos);
|
||||
}
|
||||
|
||||
void Canvas::draw_blend(RedBlend& blend, int size)
|
||||
{
|
||||
begin_draw(blend.base, size, sizeof(RedBlend));
|
||||
localalize_image(&blend.data.src_bitmap);
|
||||
localalize_mask(blend.data.mask);
|
||||
draw_blend(&blend.base.box, &blend.base.clip, &blend.data);
|
||||
}
|
||||
|
||||
void Canvas::draw_blackness(RedBlackness& blackness, int size)
|
||||
{
|
||||
begin_draw(blackness.base, size, sizeof(RedBlackness));
|
||||
localalize_mask(blackness.data.mask);
|
||||
draw_blackness(&blackness.base.box, &blackness.base.clip, &blackness.data);
|
||||
}
|
||||
|
||||
void Canvas::draw_whiteness(RedWhiteness& whiteness, int size)
|
||||
{
|
||||
begin_draw(whiteness.base, size, sizeof(RedWhiteness));
|
||||
localalize_mask(whiteness.data.mask);
|
||||
draw_whiteness(&whiteness.base.box, &whiteness.base.clip, &whiteness.data);
|
||||
}
|
||||
|
||||
void Canvas::draw_invers(RedInvers& invers, int size)
|
||||
{
|
||||
begin_draw(invers.base, size, sizeof(RedInvers));
|
||||
localalize_mask(invers.data.mask);
|
||||
draw_invers(&invers.base.box, &invers.base.clip, &invers.data);
|
||||
}
|
||||
|
||||
void Canvas::draw_rop3(RedRop3& rop3, int size)
|
||||
{
|
||||
begin_draw(rop3.base, size, sizeof(RedRop3));
|
||||
localalize_brush(rop3.data.brush);
|
||||
localalize_image(&rop3.data.src_bitmap);
|
||||
localalize_mask(rop3.data.mask);
|
||||
draw_rop3(&rop3.base.box, &rop3.base.clip, &rop3.data);
|
||||
}
|
||||
|
||||
void Canvas::draw_stroke(RedStroke& stroke, int size)
|
||||
{
|
||||
begin_draw(stroke.base, size, sizeof(RedStroke));
|
||||
localalize_brush(stroke.data.brush);
|
||||
localalize_ptr(&stroke.data.path);
|
||||
localalize_attr(stroke.data.attr);
|
||||
draw_stroke(&stroke.base.box, &stroke.base.clip, &stroke.data);
|
||||
}
|
||||
|
||||
void Canvas::bits_cache_put(void *opaque, uint64_t id, cairo_surface_t *surface)
|
||||
{
|
||||
PixmapCache* cache = static_cast<PixmapCache*>(opaque);
|
||||
cache->add(id, surface);
|
||||
}
|
||||
|
||||
cairo_surface_t* Canvas::bits_cache_get(void *opaque, uint64_t id)
|
||||
{
|
||||
PixmapCache* cache = static_cast<PixmapCache*>(opaque);
|
||||
return cache->get(id);
|
||||
}
|
||||
|
||||
void Canvas::palette_cache_put(void *opaque, Palette *palette)
|
||||
{
|
||||
PaletteCache* cache = static_cast<PaletteCache*>(opaque);
|
||||
AutoRef<CachedPalette> cached_palette(new CachedPalette(palette));
|
||||
cache->add(palette->unique, *cached_palette);
|
||||
}
|
||||
|
||||
Palette* Canvas::palette_cache_get(void *opaque, uint64_t id)
|
||||
{
|
||||
PaletteCache* cache = static_cast<PaletteCache*>(opaque);
|
||||
return cache->get(id)->palette();
|
||||
}
|
||||
|
||||
void Canvas::palette_cache_release(Palette* palette)
|
||||
{
|
||||
CachedPalette::unref(palette);
|
||||
}
|
||||
|
||||
void Canvas::glz_decode(void *opaque, uint8_t *data, Palette *plt, void *usr_data)
|
||||
{
|
||||
GlzDecoder* decoder = static_cast<GlzDecoder*>(opaque);
|
||||
decoder->decode(data, plt, usr_data);
|
||||
}
|
||||
|
||||
265
client/canvas.h
Normal file
265
client/canvas.h
Normal file
@ -0,0 +1,265 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_CANVAS
|
||||
#define _H_CANVAS
|
||||
|
||||
#include "common.h"
|
||||
#include "debug.h"
|
||||
#include "cairo.h"
|
||||
#include "red.h"
|
||||
#include "cache.hpp"
|
||||
#include "shared_cache.hpp"
|
||||
#include "canvas_utils.h"
|
||||
#include "glz_decoded_image.h"
|
||||
#include "glz_decoder.h"
|
||||
|
||||
enum CanvasType {
|
||||
CANVAS_TYPE_INVALID,
|
||||
CANVAS_TYPE_CAIRO,
|
||||
CANVAS_TYPE_GL,
|
||||
CANVAS_TYPE_GDI,
|
||||
};
|
||||
|
||||
struct QRegion;
|
||||
|
||||
class PixmapCacheTreat {
|
||||
public:
|
||||
static inline cairo_surface_t *get(cairo_surface_t *surf)
|
||||
{
|
||||
return cairo_surface_reference(surf);
|
||||
}
|
||||
|
||||
static inline void release(cairo_surface_t *surf)
|
||||
{
|
||||
cairo_surface_destroy(surf);
|
||||
}
|
||||
|
||||
static const char* name() { return "pixmap";}
|
||||
};
|
||||
|
||||
typedef SharedCache<cairo_surface_t, PixmapCacheTreat, 1024> PixmapCache;
|
||||
|
||||
class CachedPalette {
|
||||
public:
|
||||
CachedPalette(Palette* palette)
|
||||
: _refs(1)
|
||||
{
|
||||
int size = sizeof(Palette) + palette->num_ents * sizeof(uint32_t);
|
||||
CachedPalette **ptr = (CachedPalette **)new uint8_t[size + sizeof(CachedPalette *)];
|
||||
*ptr = this;
|
||||
_palette = (Palette*)(ptr + 1);
|
||||
memcpy(_palette, palette, size);
|
||||
}
|
||||
|
||||
CachedPalette* ref()
|
||||
{
|
||||
_refs++;
|
||||
return this;
|
||||
}
|
||||
|
||||
void unref()
|
||||
{
|
||||
if (--_refs == 0) {
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
static void unref(Palette *pal)
|
||||
{
|
||||
CachedPalette **ptr = (CachedPalette **)pal;
|
||||
(*(ptr - 1))->unref();
|
||||
}
|
||||
|
||||
Palette* palette() { return _palette;}
|
||||
|
||||
private:
|
||||
~CachedPalette()
|
||||
{
|
||||
delete[] (uint8_t *)((CachedPalette **)_palette - 1);
|
||||
}
|
||||
|
||||
private:
|
||||
int _refs;
|
||||
Palette* _palette;
|
||||
};
|
||||
|
||||
class PaletteCacheTreat {
|
||||
public:
|
||||
static inline CachedPalette* get(CachedPalette* palette)
|
||||
{
|
||||
return palette->ref();
|
||||
}
|
||||
|
||||
static inline void release(CachedPalette* palette)
|
||||
{
|
||||
palette->unref();
|
||||
}
|
||||
|
||||
static const char* name() { return "palette";}
|
||||
};
|
||||
|
||||
typedef Cache<CachedPalette, PaletteCacheTreat, 1024> PaletteCache;
|
||||
|
||||
/* Lz decoder related classes */
|
||||
|
||||
class GlzDecodedSurface: public GlzDecodedImage {
|
||||
public:
|
||||
GlzDecodedSurface(uint64_t id, uint64_t win_head_id, uint8_t *data, int size,
|
||||
int bytes_per_pixel, cairo_surface_t *surface)
|
||||
: GlzDecodedImage(id, win_head_id, data, size, bytes_per_pixel)
|
||||
, _surface (surface)
|
||||
{
|
||||
cairo_surface_reference(_surface);
|
||||
}
|
||||
|
||||
virtual ~GlzDecodedSurface()
|
||||
{
|
||||
cairo_surface_destroy(_surface);
|
||||
}
|
||||
|
||||
private:
|
||||
cairo_surface_t *_surface;
|
||||
};
|
||||
|
||||
class GlzDecodeSurfaceHandler: public GlzDecodeHandler {
|
||||
public:
|
||||
virtual GlzDecodedImage *alloc_image(void *opaque_usr_info, uint64_t image_id,
|
||||
uint64_t image_win_head_id, LzImageType type,
|
||||
int width, int height, int gross_pixels,
|
||||
int n_bytes_per_pixel, bool top_down)
|
||||
{
|
||||
cairo_surface_t *surface = alloc_lz_image_surface((LzDecodeUsrData *)opaque_usr_info,
|
||||
type, width, height, gross_pixels,
|
||||
top_down);
|
||||
uint8_t *data = cairo_image_surface_get_data(surface);
|
||||
if (!top_down) {
|
||||
data = data - (gross_pixels / height) * n_bytes_per_pixel * (height - 1);
|
||||
}
|
||||
|
||||
return (new GlzDecodedSurface(image_id, image_win_head_id, data,
|
||||
gross_pixels, n_bytes_per_pixel, surface));
|
||||
}
|
||||
};
|
||||
|
||||
/* TODO: unite with the window debug callbacks? */
|
||||
class GlzDecoderCanvasDebug: public GlzDecoderDebug {
|
||||
public:
|
||||
virtual void error(const std::string& str)
|
||||
{
|
||||
throw Exception(str);
|
||||
}
|
||||
|
||||
virtual void warn(const std::string& str)
|
||||
{
|
||||
LOG_WARN("%s", str.c_str());
|
||||
}
|
||||
|
||||
virtual void info(const std::string& str)
|
||||
{
|
||||
LOG_INFO("%s", str.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
class Canvas {
|
||||
public:
|
||||
Canvas(PixmapCache& bits_cache, PaletteCache& palette_cache,
|
||||
GlzDecoderWindow &glz_decoder_window);
|
||||
virtual ~Canvas();
|
||||
|
||||
virtual void copy_pixels(const QRegion& region, RedDrawable* dc,
|
||||
const PixmapHeader* pixmap) = 0;
|
||||
virtual void copy_pixels(const QRegion& region, RedDrawable& dc) = 0;
|
||||
virtual void thread_touch() = 0;
|
||||
|
||||
virtual void clear() = 0;
|
||||
|
||||
void draw_fill(RedFill& fill, int size);
|
||||
void draw_text(RedText& text, int size);
|
||||
void draw_opaque(RedOpaque& opaque, int size);
|
||||
void draw_copy(RedCopy& copy, int size);
|
||||
void draw_transparent(RedTransparent& transparent, int size);
|
||||
void draw_alpha_blend(RedAlphaBlend& alpha_blend, int size);
|
||||
void copy_bits(RedCopyBits& copy_bits, int size);
|
||||
void draw_blend(RedBlend& blend, int size);
|
||||
void draw_blackness(RedBlackness& blackness, int size);
|
||||
void draw_whiteness(RedWhiteness& whiteness, int size);
|
||||
void draw_invers(RedInvers& invers, int size);
|
||||
void draw_rop3(RedRop3& rop3, int size);
|
||||
void draw_stroke(RedStroke& stroke, int size);
|
||||
|
||||
#ifdef WIN32
|
||||
virtual void put_image(HDC dc, const PixmapHeader& image,
|
||||
const Rect& dest, const QRegion* clip) = 0;
|
||||
#else
|
||||
virtual void put_image(const PixmapHeader& image, const Rect& dest,
|
||||
const QRegion* clip) = 0;
|
||||
#endif
|
||||
|
||||
virtual CanvasType get_pixmap_type() { return CANVAS_TYPE_INVALID; }
|
||||
|
||||
protected:
|
||||
virtual void set_access_params(ADDRESS delta, unsigned long base, unsigned long max) = 0;
|
||||
virtual void draw_fill(Rect *bbox, Clip *clip, Fill *fill) = 0;
|
||||
virtual void draw_copy(Rect *bbox, Clip *clip, Copy *copy) = 0;
|
||||
virtual void draw_opaque(Rect *bbox, Clip *clip, Opaque *opaque) = 0;
|
||||
virtual void copy_bits(Rect *bbox, Clip *clip, Point *src_pos) = 0;
|
||||
virtual void draw_text(Rect *bbox, Clip *clip, Text *text) = 0;
|
||||
virtual void draw_stroke(Rect *bbox, Clip *clip, Stroke *stroke) = 0;
|
||||
virtual void draw_rop3(Rect *bbox, Clip *clip, Rop3 *rop3) = 0;
|
||||
virtual void draw_blend(Rect *bbox, Clip *clip, Blend *blend) = 0;
|
||||
virtual void draw_blackness(Rect *bbox, Clip *clip, Blackness *blackness) = 0;
|
||||
virtual void draw_whiteness(Rect *bbox, Clip *clip, Whiteness *whiteness) = 0;
|
||||
virtual void draw_invers(Rect *bbox, Clip *clip, Invers *invers) = 0;
|
||||
virtual void draw_transparent(Rect *bbox, Clip *clip, Transparent* transparent) = 0;
|
||||
virtual void draw_alpha_blend(Rect *bbox, Clip *clip, AlphaBlnd* alpha_blend) = 0;
|
||||
|
||||
PixmapCache& pixmap_cache() { return _pixmap_cache;}
|
||||
PaletteCache& palette_cache() { return _palette_cache;}
|
||||
static void bits_cache_put(void *opaque, uint64_t id, cairo_surface_t *surface);
|
||||
static cairo_surface_t* bits_cache_get(void *opaque, uint64_t id);
|
||||
static void palette_cache_put(void *opaque, Palette *palette);
|
||||
static Palette* palette_cache_get(void *opaque, uint64_t id);
|
||||
static void palette_cache_release(Palette* palette);
|
||||
|
||||
GlzDecoder& glz_decoder() {return _glz_decoder;}
|
||||
static void glz_decode(void *opaque, uint8_t *data, Palette *plt, void *usr_data);
|
||||
|
||||
private:
|
||||
void access_test(void* ptr, size_t size);
|
||||
void localalize_ptr(ADDRESS* data);
|
||||
void localalize_image(ADDRESS* in_bitmap);
|
||||
void localalize_brush(Brush& brush);
|
||||
void localalize_attr(LineAttr& attr);
|
||||
void localalize_mask(QMask& mask);
|
||||
void begin_draw(RedDrawBase& base, int size, size_t min_size);
|
||||
|
||||
private:
|
||||
PixmapCache& _pixmap_cache;
|
||||
PaletteCache& _palette_cache;
|
||||
|
||||
GlzDecodeSurfaceHandler _glz_handler;
|
||||
GlzDecoderCanvasDebug _glz_debug;
|
||||
GlzDecoder _glz_decoder;
|
||||
|
||||
unsigned long _base;
|
||||
unsigned long _max;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
25
client/canvas_utils.cpp
Normal file
25
client/canvas_utils.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
#define CANVAS_ERROR(format, ...) THROW(format, ## __VA_ARGS__)
|
||||
|
||||
#include "../common/canvas_utils.c"
|
||||
|
||||
524
client/cmd_line_parser.cpp
Normal file
524
client/cmd_line_parser.cpp
Normal file
@ -0,0 +1,524 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <getopt.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "cmd_line_parser.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define DISABLE_ABBREVIATE
|
||||
|
||||
|
||||
CmdLineParser::Option::Option(int in_id, const std::string& in_name, char in_short_name,
|
||||
OptionType in_type, const std::string& in_help,
|
||||
const std::string& in_arg_name)
|
||||
: id (in_id)
|
||||
, name (in_name)
|
||||
, arg_name (in_arg_name)
|
||||
, type (in_type)
|
||||
, short_name (in_short_name)
|
||||
, help (in_help)
|
||||
, optional (true)
|
||||
, is_set (false)
|
||||
, seperator (0)
|
||||
{
|
||||
}
|
||||
|
||||
CmdLineParser::CmdLineParser(std::string description, bool allow_positional_args)
|
||||
: _description (description)
|
||||
, _short_options ("+")
|
||||
, _argc (0)
|
||||
, _argv (NULL)
|
||||
, _multi_args (NULL)
|
||||
, _multi_next (NULL)
|
||||
, _multi_seperator (0)
|
||||
, _positional_args (allow_positional_args)
|
||||
, _done (false)
|
||||
{
|
||||
}
|
||||
|
||||
CmdLineParser::~CmdLineParser()
|
||||
{
|
||||
Options::iterator iter = _options.begin();
|
||||
for (; iter != _options.end(); ++iter) {
|
||||
delete *iter;
|
||||
}
|
||||
delete[] _multi_args;
|
||||
}
|
||||
|
||||
void CmdLineParser::add_private(int id, const std::string& name, char short_name,
|
||||
OptionType type, const std::string& help,
|
||||
const std::string& arg_name)
|
||||
{
|
||||
if (_argv) {
|
||||
THROW("unexpected");
|
||||
}
|
||||
|
||||
if (find(id)) {
|
||||
THROW("exist");
|
||||
}
|
||||
|
||||
if (name.size() == 0) {
|
||||
THROW("invalid name");
|
||||
}
|
||||
|
||||
if (find(name)) {
|
||||
THROW("name exist");
|
||||
}
|
||||
|
||||
if (short_name != 0) {
|
||||
if (!isalnum(short_name) || short_name == 'W') {
|
||||
THROW("invalid short name");
|
||||
}
|
||||
|
||||
if (find(short_name)) {
|
||||
THROW("short name exist");
|
||||
}
|
||||
}
|
||||
|
||||
if (help.size() == 0) {
|
||||
THROW("invalid help string");
|
||||
}
|
||||
|
||||
if (help.find_first_of('\t') != std::string::npos) {
|
||||
THROW("tab is not allow in help string");
|
||||
}
|
||||
|
||||
_options.push_back(new Option(id, name, short_name, type, help, arg_name));
|
||||
}
|
||||
|
||||
void CmdLineParser::add(int id, const std::string& name, const std::string& help, char short_name)
|
||||
{
|
||||
if (id < OPTION_FIRST_AVILABLE) {
|
||||
THROW("invalid id");
|
||||
}
|
||||
add_private(id, name, short_name, NO_ARGUMENT, help, "");
|
||||
}
|
||||
|
||||
void CmdLineParser::add(int id, const std::string& name, const std::string& help,
|
||||
const std::string& arg_name, bool reqired_arg, char short_name)
|
||||
{
|
||||
if (id < OPTION_FIRST_AVILABLE) {
|
||||
THROW("invalid id");
|
||||
}
|
||||
|
||||
if (arg_name.size() == 0) {
|
||||
THROW("invalid arg name");
|
||||
}
|
||||
|
||||
add_private(id, name, short_name, reqired_arg ? REQUIRED_ARGUMENT : OPTIONAL_ARGUMENT, help,
|
||||
arg_name);
|
||||
}
|
||||
|
||||
void CmdLineParser::set_multi(int id, char seperator)
|
||||
{
|
||||
if (_argv) {
|
||||
THROW("unexpected");
|
||||
}
|
||||
|
||||
if (!ispunct(seperator)) {
|
||||
THROW("invalid seperator");
|
||||
}
|
||||
|
||||
Option* opt = find(id);
|
||||
|
||||
if (!opt) {
|
||||
THROW("not found");
|
||||
}
|
||||
|
||||
if (opt->type == NO_ARGUMENT) {
|
||||
THROW("can't set multi for option without argument");
|
||||
}
|
||||
|
||||
opt->seperator = seperator;
|
||||
}
|
||||
|
||||
void CmdLineParser::set_reqired(int id)
|
||||
{
|
||||
if (_argv) {
|
||||
THROW("unexpected");
|
||||
}
|
||||
|
||||
Option* opt = find(id);
|
||||
|
||||
if (!opt) {
|
||||
THROW("not found");
|
||||
}
|
||||
|
||||
opt->optional = false;
|
||||
}
|
||||
|
||||
CmdLineParser::Option* CmdLineParser::find(int id)
|
||||
{
|
||||
Options::iterator iter = _options.begin();
|
||||
for (; iter != _options.end(); ++iter) {
|
||||
if ((*iter)->id == id) {
|
||||
return *iter;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool CmdLineParser::is_set(int id)
|
||||
{
|
||||
Option *opt = find(id);
|
||||
|
||||
if (!opt) {
|
||||
THROW("not found");
|
||||
}
|
||||
return opt->is_set;
|
||||
}
|
||||
|
||||
CmdLineParser::Option* CmdLineParser::find(const std::string& name)
|
||||
{
|
||||
Options::iterator iter = _options.begin();
|
||||
for (; iter != _options.end(); ++iter) {
|
||||
if ((*iter)->name == name) {
|
||||
return *iter;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CmdLineParser::Option* CmdLineParser::find(char short_name)
|
||||
{
|
||||
if (short_name == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Options::iterator iter = _options.begin();
|
||||
for (; iter != _options.end(); ++iter) {
|
||||
if ((*iter)->short_name == short_name) {
|
||||
return *iter;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CmdLineParser::Option* CmdLineParser::find_missing_opt()
|
||||
{
|
||||
Options::iterator iter = _options.begin();
|
||||
for (; iter != _options.end(); ++iter) {
|
||||
CmdLineParser::Option* opt = *iter;
|
||||
if (!opt->optional && !opt->is_set) {
|
||||
return opt;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CmdLineParser::build()
|
||||
{
|
||||
Options::iterator iter = _options.begin();
|
||||
_long_options.resize(_options.size() + 1);
|
||||
for (int i = 0; iter != _options.end(); ++iter, i++) {
|
||||
CmdLineParser::Option* opt = *iter;
|
||||
struct option& long_option = _long_options[i];
|
||||
long_option.name = opt->name.c_str();
|
||||
switch (opt->type) {
|
||||
case NO_ARGUMENT:
|
||||
long_option.has_arg = no_argument;
|
||||
break;
|
||||
case OPTIONAL_ARGUMENT:
|
||||
long_option.has_arg = optional_argument;
|
||||
break;
|
||||
case REQUIRED_ARGUMENT:
|
||||
long_option.has_arg = required_argument;
|
||||
break;
|
||||
}
|
||||
long_option.flag = &long_option.val;
|
||||
long_option.val = opt->id;
|
||||
if (opt->short_name != 0) {
|
||||
_short_options += opt->short_name;
|
||||
switch (opt->type) {
|
||||
case OPTIONAL_ARGUMENT:
|
||||
_short_options += "::";
|
||||
break;
|
||||
case REQUIRED_ARGUMENT:
|
||||
_short_options += ":";
|
||||
break;
|
||||
case NO_ARGUMENT:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
struct option& long_option = _long_options[_long_options.size() - 1];
|
||||
long_option.flag = 0;
|
||||
long_option.has_arg = 0;
|
||||
long_option.name = NULL;
|
||||
long_option.val = 0;
|
||||
}
|
||||
|
||||
void CmdLineParser::begin(int argc, char** argv)
|
||||
{
|
||||
if (_argv) {
|
||||
THROW("unexpected");
|
||||
}
|
||||
|
||||
if (!argv || argc < 1) {
|
||||
THROW("invalid args");
|
||||
}
|
||||
|
||||
add_private(CmdLineParser::OPTION_HELP, "help", 0, NO_ARGUMENT, "show command help", "");
|
||||
opterr = 0;
|
||||
_argv = argv;
|
||||
_argc = argc;
|
||||
build();
|
||||
}
|
||||
|
||||
char* CmdLineParser::start_multi(char *optarg, char seperator)
|
||||
{
|
||||
if (!optarg) {
|
||||
return NULL;
|
||||
}
|
||||
_multi_args = new char[strlen(optarg) + 1];
|
||||
_multi_seperator = seperator;
|
||||
strcpy(_multi_args, optarg);
|
||||
if ((_multi_next = strchr(_multi_args, _multi_seperator))) {
|
||||
*(_multi_next++) = 0;
|
||||
}
|
||||
return _multi_args;
|
||||
}
|
||||
|
||||
char* CmdLineParser::next_multi()
|
||||
{
|
||||
if (!_multi_next) {
|
||||
_multi_seperator = 0;
|
||||
delete[] _multi_args;
|
||||
_multi_args = NULL;
|
||||
return NULL;
|
||||
}
|
||||
char* ret = _multi_next;
|
||||
if ((_multi_next = strchr(_multi_next, _multi_seperator))) {
|
||||
*(_multi_next++) = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int CmdLineParser::get_option(char** val)
|
||||
{
|
||||
CmdLineParser::Option* opt_obj;
|
||||
|
||||
if (!_argv) {
|
||||
THROW("unexpected");
|
||||
}
|
||||
|
||||
if (_multi_args) {
|
||||
THROW("in multi args mode");
|
||||
}
|
||||
|
||||
if (_done) {
|
||||
THROW("is done");
|
||||
}
|
||||
|
||||
int long_index;
|
||||
|
||||
int opt = getopt_long(_argc, _argv, _short_options.c_str(), &_long_options[0], &long_index);
|
||||
|
||||
switch (opt) {
|
||||
case 0: {
|
||||
if (!(opt_obj = find(_long_options[long_index].val))) {
|
||||
THROW("long option no found");
|
||||
}
|
||||
|
||||
#ifdef DISABLE_ABBREVIATE
|
||||
int name_pos = (opt_obj->type == REQUIRED_ARGUMENT) ? optind - 2 : optind - 1;
|
||||
std::string cmd_name(_argv[name_pos] + 2);
|
||||
if (cmd_name.find(opt_obj->name) != 0) {
|
||||
std::cout << _argv[0] << ": invalid option '--" << cmd_name << "'\n";
|
||||
return OPTION_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (opt_obj->seperator) {
|
||||
*val = start_multi(optarg, opt_obj->seperator);
|
||||
} else {
|
||||
*val = optarg;
|
||||
}
|
||||
opt_obj->is_set = true;
|
||||
return opt_obj->id;
|
||||
}
|
||||
case -1: {
|
||||
*val = NULL;
|
||||
if (!_positional_args && optind != _argc) {
|
||||
std::cout << _argv[0] << ": unexpected positional arguments\n";
|
||||
return OPTION_ERROR;
|
||||
}
|
||||
if ((opt_obj = find_missing_opt())) {
|
||||
std::cout << _argv[0] << ": option --" << opt_obj->name << " is required\n";
|
||||
return OPTION_ERROR;
|
||||
}
|
||||
_done = true;
|
||||
return OPTION_DONE;
|
||||
}
|
||||
case '?':
|
||||
if (optopt >= 255) {
|
||||
opt_obj = find(optopt);
|
||||
ASSERT(opt_obj);
|
||||
|
||||
#ifdef DISABLE_ABBREVIATE
|
||||
std::string cmd_name(_argv[optind - 1] + 2);
|
||||
if (cmd_name.find(opt_obj->name) != 0) {
|
||||
std::cout << _argv[0] << ": invalid option '--" << cmd_name << "'\n";
|
||||
return OPTION_ERROR;
|
||||
}
|
||||
#endif
|
||||
std::cout << _argv[0] << ": option --" << opt_obj->name << " requires an argument\n";
|
||||
} else if (optopt == 0) {
|
||||
std::cout << _argv[0] << ": invalid option '" << _argv[optind - 1] << "'\n";
|
||||
} else if ((opt_obj = find((char)optopt))) {
|
||||
std::cout << _argv[0] << ": option '-" << opt_obj->short_name <<
|
||||
"' requires an argument\n";
|
||||
} else {
|
||||
std::cout << _argv[0] << ": invalid option '-" << char(optopt) << "'\n";
|
||||
}
|
||||
return OPTION_ERROR;
|
||||
default:
|
||||
if (opt > 255 || !(opt_obj = find((char)opt))) {
|
||||
*val = NULL;
|
||||
return OPTION_ERROR;
|
||||
}
|
||||
if (opt_obj->seperator) {
|
||||
*val = start_multi(optarg, opt_obj->seperator);
|
||||
} else {
|
||||
*val = optarg;
|
||||
}
|
||||
opt_obj->is_set = true;
|
||||
return opt_obj->id;
|
||||
}
|
||||
}
|
||||
|
||||
char* CmdLineParser::next_argument()
|
||||
{
|
||||
if (!_argv) {
|
||||
THROW("unexpected");
|
||||
}
|
||||
|
||||
if (_multi_args) {
|
||||
return next_multi();
|
||||
}
|
||||
|
||||
if (!_done) {
|
||||
THROW("not done");
|
||||
}
|
||||
|
||||
if (optind == _argc) {
|
||||
return NULL;
|
||||
}
|
||||
return _argv[optind++];
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
char* basename(char *str)
|
||||
{
|
||||
char *base;
|
||||
if ((base = strrchr(str, '\\'))) {
|
||||
return base;
|
||||
}
|
||||
|
||||
if ((base = strrchr(str, ':'))) {
|
||||
return base;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void CmdLineParser::show_help()
|
||||
{
|
||||
static const int HELP_START_POS = 30;
|
||||
static const int HELP_WIDTH = 80 - HELP_START_POS;
|
||||
|
||||
std::cout << basename(_argv[0]) << " - " << _description.c_str() << "\n\noptions:\n\n";
|
||||
|
||||
Options::iterator iter = _options.begin();
|
||||
for (; iter != _options.end(); ++iter) {
|
||||
CmdLineParser::Option* opt = *iter;
|
||||
std::ostringstream os;
|
||||
|
||||
if (opt->short_name) {
|
||||
os << " -" << opt->short_name << ", ";
|
||||
} else {
|
||||
os << " ";
|
||||
}
|
||||
|
||||
os << "--" << opt->name;
|
||||
|
||||
if (opt->type == OPTIONAL_ARGUMENT) {
|
||||
os << "[=";
|
||||
} else if (opt->type == REQUIRED_ARGUMENT) {
|
||||
os << " <";
|
||||
}
|
||||
|
||||
if (opt->type == OPTIONAL_ARGUMENT || opt->type == REQUIRED_ARGUMENT) {
|
||||
if (opt->seperator) {
|
||||
os << opt->arg_name << opt->seperator << opt->arg_name << "...";
|
||||
} else {
|
||||
os << opt->arg_name;
|
||||
}
|
||||
}
|
||||
|
||||
if (opt->type == OPTIONAL_ARGUMENT) {
|
||||
os << "]";
|
||||
} else if (opt->type == REQUIRED_ARGUMENT) {
|
||||
os << ">";
|
||||
}
|
||||
|
||||
int skip = HELP_START_POS - os.str().size();
|
||||
if (skip < 2) {
|
||||
os << "\n ";
|
||||
} else {
|
||||
while (skip--) {
|
||||
os << " ";
|
||||
}
|
||||
}
|
||||
|
||||
int line_count = 0;
|
||||
std::istringstream is(opt->help);
|
||||
std::string line;
|
||||
std::getline(is, line);
|
||||
do {
|
||||
if (line_count++) {
|
||||
os << " ";
|
||||
}
|
||||
if (line.size() > HELP_WIDTH) {
|
||||
int now = HELP_WIDTH;
|
||||
std::string sub;
|
||||
sub.append(line, 0, now);
|
||||
int last_space;
|
||||
if ((last_space = sub.find_last_of(' ')) != std::string::npos) {
|
||||
now = last_space;
|
||||
sub.resize(now++);
|
||||
}
|
||||
os << sub << "\n";
|
||||
line = line.substr(now, line.size() - now);
|
||||
} else {
|
||||
os << line << "\n";
|
||||
line.clear();
|
||||
}
|
||||
} while (line.size() || std::getline(is, line));
|
||||
|
||||
std::cout << os.str();
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
105
client/cmd_line_parser.h
Normal file
105
client/cmd_line_parser.h
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_CMD_LINE_PARSER
|
||||
#define _H_CMD_LINE_PARSER
|
||||
|
||||
class CmdLineParser {
|
||||
public:
|
||||
|
||||
enum {
|
||||
OPTION_ERROR = -1,
|
||||
OPTION_DONE = 0,
|
||||
OPTION_HELP = 256,
|
||||
OPTION_FIRST_AVILABLE,
|
||||
};
|
||||
|
||||
CmdLineParser(std::string description, bool allow_positional_args);
|
||||
virtual ~CmdLineParser();
|
||||
|
||||
void add(int id, const std::string& name, const std::string& help,
|
||||
char short_name = 0);
|
||||
|
||||
void add(int id, const std::string& name, const std::string& help,
|
||||
const std::string& arg_name, bool reqired_arg, char short_name = 0);
|
||||
void set_multi(int id, char seperator);
|
||||
void set_reqired(int id);
|
||||
|
||||
void begin(int argc, char** argv);
|
||||
int get_option(char** val);
|
||||
char* next_argument();
|
||||
bool is_set(int id);
|
||||
|
||||
void show_help();
|
||||
|
||||
private:
|
||||
class Option;
|
||||
|
||||
enum OptionType {
|
||||
NO_ARGUMENT,
|
||||
OPTIONAL_ARGUMENT,
|
||||
REQUIRED_ARGUMENT,
|
||||
};
|
||||
|
||||
void add_private(int id, const std::string& name, char short_name, OptionType type,
|
||||
const std::string& help, const std::string& arg_name);
|
||||
Option* find(char short_name);
|
||||
Option* find(int id);
|
||||
Option* find(const std::string& name);
|
||||
Option* find_missing_opt();
|
||||
|
||||
void build();
|
||||
|
||||
char* start_multi(char *optarg, char seperator);
|
||||
char* next_multi();
|
||||
|
||||
private:
|
||||
|
||||
class Option {
|
||||
public:
|
||||
Option(int in_id, const std::string& in_name, char in_short_name, OptionType in_type,
|
||||
const std::string& in_help, const std::string& arg_name);
|
||||
|
||||
public:
|
||||
int id;
|
||||
std::string name;
|
||||
std::string arg_name;
|
||||
OptionType type;
|
||||
char short_name;
|
||||
std::string help;
|
||||
bool optional;
|
||||
bool is_set;
|
||||
char seperator;
|
||||
};
|
||||
|
||||
std::string _description;
|
||||
std::vector<struct option> _long_options;
|
||||
std::string _short_options;
|
||||
|
||||
typedef std::list<Option*> Options;
|
||||
Options _options;
|
||||
int _argc;
|
||||
char** _argv;
|
||||
char* _multi_args;
|
||||
char* _multi_next;
|
||||
char _multi_seperator;
|
||||
bool _positional_args;
|
||||
bool _done;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
63
client/common.h
Normal file
63
client/common.h
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_COMMON
|
||||
#define _H_COMMON
|
||||
|
||||
#ifndef _WIN32_WCE
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <exception>
|
||||
#include <list>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <basetsd.h>
|
||||
|
||||
#pragma warning(disable:4355)
|
||||
#pragma warning(disable:4996)
|
||||
#pragma warning(disable:4200)
|
||||
|
||||
#define strcasecmp stricmp
|
||||
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <X11/X.h>
|
||||
#include <GL/glx.h>
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#if __SIZEOF_POINTER__ == 8
|
||||
#define RED64
|
||||
#endif
|
||||
#elif defined(_WIN64)
|
||||
#define RED64
|
||||
#endif
|
||||
|
||||
#include "red_types.h"
|
||||
|
||||
#endif
|
||||
|
||||
113
client/cursor.cpp
Normal file
113
client/cursor.cpp
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "cursor.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
|
||||
CursorData::CursorData(RedCursor& cursor, int data_size)
|
||||
: _atomic (1)
|
||||
, _header (cursor.header)
|
||||
, _data (NULL)
|
||||
, _opaque (NULL)
|
||||
, _local_cursor (NULL)
|
||||
{
|
||||
int expected_size = 0;
|
||||
|
||||
switch (cursor.header.type) {
|
||||
case CURSOR_TYPE_ALPHA:
|
||||
expected_size = (_header.width << 2) * _header.height;
|
||||
break;
|
||||
case CURSOR_TYPE_MONO:
|
||||
expected_size = (ALIGN(_header.width, 8) >> 2) * _header.height;
|
||||
break;
|
||||
case CURSOR_TYPE_COLOR4:
|
||||
expected_size = (ALIGN(_header.width, 2) >> 1) * _header.height;
|
||||
expected_size += (ALIGN(_header.width, 8) >> 3) * _header.height;
|
||||
expected_size += 16 * sizeof(uint32_t);
|
||||
break;
|
||||
case CURSOR_TYPE_COLOR8:
|
||||
expected_size = _header.width * _header.height;
|
||||
expected_size += (ALIGN(_header.width, 8) >> 3) * _header.height;
|
||||
expected_size += 256 * sizeof(uint32_t);
|
||||
break;
|
||||
case CURSOR_TYPE_COLOR16:
|
||||
expected_size = (_header.width << 1) * _header.height;
|
||||
expected_size += (ALIGN(_header.width, 8) >> 3) * _header.height;
|
||||
break;
|
||||
case CURSOR_TYPE_COLOR24:
|
||||
expected_size = (_header.width * 3) * _header.height;
|
||||
expected_size += (ALIGN(_header.width, 8) >> 3) * _header.height;
|
||||
break;
|
||||
case CURSOR_TYPE_COLOR32:
|
||||
expected_size = (_header.width << 2) * _header.height;
|
||||
expected_size += (ALIGN(_header.width, 8) >> 3) * _header.height;
|
||||
break;
|
||||
}
|
||||
|
||||
if (data_size < expected_size) {
|
||||
THROW("access violation 0x%lx %u", (unsigned long)cursor.data, expected_size);
|
||||
}
|
||||
_data = new uint8_t[expected_size];
|
||||
memcpy(_data, cursor.data, expected_size);
|
||||
}
|
||||
|
||||
void CursorData::set_local(LocalCursor* local_cursor)
|
||||
{
|
||||
ASSERT(!_local_cursor);
|
||||
if (local_cursor) {
|
||||
_local_cursor = local_cursor->ref();
|
||||
}
|
||||
}
|
||||
|
||||
CursorData::~CursorData()
|
||||
{
|
||||
if (_local_cursor) {
|
||||
_local_cursor->unref();
|
||||
}
|
||||
delete _opaque;
|
||||
delete[] _data;
|
||||
}
|
||||
|
||||
int LocalCursor::get_size_bits(const CursorHeader& header, int& size)
|
||||
{
|
||||
switch (header.type) {
|
||||
case CURSOR_TYPE_ALPHA:
|
||||
case CURSOR_TYPE_COLOR32:
|
||||
size = (header.width << 2) * header.height;
|
||||
return 32;
|
||||
case CURSOR_TYPE_MONO:
|
||||
size = (ALIGN(header.width, 8) >> 3) * header.height;
|
||||
return 1;
|
||||
case CURSOR_TYPE_COLOR4:
|
||||
size = (ALIGN(header.width, 2) >> 1) * header.height;
|
||||
return 4;
|
||||
case CURSOR_TYPE_COLOR8:
|
||||
size = header.width * header.height;
|
||||
return 8;
|
||||
case CURSOR_TYPE_COLOR16:
|
||||
size = (header.width << 1) * header.height;
|
||||
return 16;
|
||||
case CURSOR_TYPE_COLOR24:
|
||||
size = (header.width * 3) * header.height;
|
||||
return 24;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
73
client/cursor.h
Normal file
73
client/cursor.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_CURSOR_
|
||||
#define _H_CURSOR_
|
||||
|
||||
#include "threads.h"
|
||||
#include "red.h"
|
||||
#include "red_window_p.h"
|
||||
|
||||
class CursorOpaque {
|
||||
public:
|
||||
CursorOpaque() {}
|
||||
virtual ~CursorOpaque() {}
|
||||
};
|
||||
|
||||
class LocalCursor;
|
||||
|
||||
class CursorData {
|
||||
public:
|
||||
CursorData(RedCursor& cursor, int data_size);
|
||||
|
||||
CursorData *ref() { ++_atomic; return this;}
|
||||
void unref() {if (--_atomic == 0) delete this;}
|
||||
void set_opaque(CursorOpaque* opaque) { delete _opaque; _opaque = opaque;}
|
||||
CursorOpaque* get_opaque() { return _opaque;}
|
||||
void set_local(LocalCursor* local_cursor);
|
||||
LocalCursor* get_local() { return _local_cursor;}
|
||||
const CursorHeader& header() const { return _header;}
|
||||
const uint8_t* data() const { return _data;}
|
||||
|
||||
private:
|
||||
~CursorData();
|
||||
|
||||
private:
|
||||
AtomicCount _atomic;
|
||||
CursorHeader _header;
|
||||
uint8_t* _data;
|
||||
CursorOpaque* _opaque;
|
||||
LocalCursor* _local_cursor;
|
||||
};
|
||||
|
||||
class LocalCursor {
|
||||
public:
|
||||
LocalCursor(): _atomic (1) {}
|
||||
virtual ~LocalCursor() {}
|
||||
virtual void set(Window window) {}
|
||||
LocalCursor* ref() { ++_atomic; return this;}
|
||||
void unref() { if (--_atomic == 0) delete this;}
|
||||
|
||||
protected:
|
||||
static int get_size_bits(const CursorHeader& header, int &size);
|
||||
|
||||
private:
|
||||
AtomicCount _atomic;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
661
client/cursor_channel.cpp
Normal file
661
client/cursor_channel.cpp
Normal file
@ -0,0 +1,661 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "cursor_channel.h"
|
||||
#include "cursor.h"
|
||||
#include "red_client.h"
|
||||
#include "application.h"
|
||||
#include "debug.h"
|
||||
#include "utils.h"
|
||||
#include "screen.h"
|
||||
#include "red_pixmap_cairo.h"
|
||||
#include "rect.h"
|
||||
|
||||
static inline uint8_t revers_bits(uint8_t byte)
|
||||
{
|
||||
uint8_t ret = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
int shift = 7 - i * 2;
|
||||
ret |= (byte & (1 << i)) << shift;
|
||||
ret |= (byte & (0x80 >> i)) >> shift;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
class NaitivCursor: public CursorOpaque {
|
||||
public:
|
||||
virtual void draw(RedDrawable& dest, int x, int y, const Rect& area) = 0;
|
||||
};
|
||||
|
||||
class AlphaCursor: public NaitivCursor {
|
||||
public:
|
||||
AlphaCursor(const CursorHeader& header, const uint8_t* data);
|
||||
|
||||
virtual void draw(RedDrawable& dest, int x, int y, const Rect& area);
|
||||
|
||||
private:
|
||||
std::auto_ptr<RedPixmap> _pixmap;
|
||||
};
|
||||
|
||||
class MonoCursor: public NaitivCursor {
|
||||
public:
|
||||
MonoCursor(const CursorHeader& header, const uint8_t* data);
|
||||
|
||||
virtual void draw(RedDrawable& dest, int x, int y, const Rect& area);
|
||||
|
||||
private:
|
||||
std::auto_ptr<RedPixmap> _pixmap;
|
||||
int _height;
|
||||
};
|
||||
|
||||
class UnsupportedCursor: public NaitivCursor {
|
||||
public:
|
||||
UnsupportedCursor(const CursorHeader& header);
|
||||
virtual void draw(RedDrawable& dest, int x, int y, const Rect& area);
|
||||
|
||||
private:
|
||||
int _hot_x;
|
||||
int _hot_y;
|
||||
};
|
||||
|
||||
UnsupportedCursor::UnsupportedCursor(const CursorHeader& header)
|
||||
: _hot_x (header.hot_spot_x)
|
||||
, _hot_y (header.hot_spot_y)
|
||||
{
|
||||
LOG_WARN("Unsupported cursor %hu", header.type);
|
||||
}
|
||||
|
||||
void UnsupportedCursor::draw(RedDrawable& dest, int x, int y, const Rect& area)
|
||||
{
|
||||
Rect dest_area;
|
||||
Rect rect;
|
||||
|
||||
dest_area.left = area.left;
|
||||
dest_area.right = area.right;
|
||||
dest_area.top = area.top;
|
||||
dest_area.bottom = area.bottom;
|
||||
|
||||
rect.left = x + _hot_x - 2;
|
||||
rect.right = rect.left + 8;
|
||||
rect.top = y + _hot_y - 2;
|
||||
rect.bottom = rect.top + 8;
|
||||
rect_sect(rect, dest_area);
|
||||
|
||||
dest.fill_rect(rect, rgb32_make(0xf8, 0xf1, 0xb8));
|
||||
|
||||
rect.left = x + _hot_x - 1;
|
||||
rect.right = rect.left + 6;
|
||||
rect.top = y + _hot_y - 1;
|
||||
rect.bottom = rect.top + 6;
|
||||
rect_sect(rect, dest_area);
|
||||
|
||||
dest.frame_rect(rect, rgb32_make(0, 0, 0));
|
||||
}
|
||||
|
||||
AlphaCursor::AlphaCursor(const CursorHeader& header, const uint8_t* data)
|
||||
: _pixmap (new RedPixmapCairo(header.width, header.height,
|
||||
RedPixmap::ARGB32, true, NULL, NULL))
|
||||
{
|
||||
int stride = _pixmap->get_stride();
|
||||
uint8_t* dest = _pixmap->get_data();
|
||||
int line_size = header.width * sizeof(uint32_t);
|
||||
for (int i = 0; i < header.height; i++, data += line_size, dest += stride) {
|
||||
memcpy(dest, data, line_size);
|
||||
}
|
||||
}
|
||||
|
||||
void AlphaCursor::draw(RedDrawable& dest, int x, int y, const Rect& area)
|
||||
{
|
||||
dest.blend_pixels(*_pixmap, area.left - x, area.top - y, area);
|
||||
}
|
||||
|
||||
MonoCursor::MonoCursor(const CursorHeader& header, const uint8_t* data)
|
||||
: _pixmap (NULL)
|
||||
, _height (header.height)
|
||||
{
|
||||
rgb32_t pallete[2] = { rgb32_make(0x00, 0x00, 0x00), rgb32_make(0xff, 0xff, 0xff)};
|
||||
_pixmap.reset(new RedPixmapCairo(header.width, _height * 2, RedPixmap::A1,
|
||||
true, pallete, NULL));
|
||||
|
||||
int dest_stride = _pixmap->get_stride();
|
||||
uint8_t *dest_line = _pixmap->get_data();
|
||||
int src_stride = ALIGN(header.width, 8) >> 3;
|
||||
const uint8_t* src_line = data;
|
||||
const uint8_t* end_line = src_line + _pixmap->get_height() * src_stride;
|
||||
|
||||
if (_pixmap->is_big_endian_bits()) {
|
||||
for (; src_line < end_line; src_line += src_stride, dest_line += dest_stride) {
|
||||
memcpy(dest_line, src_line, src_stride);
|
||||
}
|
||||
} else {
|
||||
for (; src_line < end_line; src_line += src_stride, dest_line += dest_stride) {
|
||||
for (int i = 0; i < src_stride; i++) {
|
||||
dest_line[i] = revers_bits(src_line[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MonoCursor::draw(RedDrawable& dest, int x, int y, const Rect& area)
|
||||
{
|
||||
dest.combine_pixels(*_pixmap, area.left - x, area.top - y, area, RedDrawable::OP_AND);
|
||||
dest.combine_pixels(*_pixmap, area.left - x, area.top - y + _height, area, RedDrawable::OP_XOR);
|
||||
}
|
||||
|
||||
class ColorCursor: public NaitivCursor {
|
||||
public:
|
||||
ColorCursor(const CursorHeader& header);
|
||||
|
||||
virtual void draw(RedDrawable& dest, int x, int y, const Rect& area);
|
||||
|
||||
protected:
|
||||
void init_pixels(const CursorHeader& header, const uint8_t* _pixels, const uint8_t *and_mask);
|
||||
virtual uint32_t get_pixel_color(const uint8_t *data, int row, int col) = 0;
|
||||
|
||||
private:
|
||||
std::auto_ptr<RedPixmap> _pixmap;
|
||||
std::auto_ptr<RedPixmap> _invers;
|
||||
};
|
||||
|
||||
ColorCursor::ColorCursor(const CursorHeader& header)
|
||||
: _pixmap (new RedPixmapCairo(header.width, header.height,
|
||||
RedPixmap::ARGB32, true, NULL, NULL))
|
||||
, _invers (NULL)
|
||||
{
|
||||
rgb32_t pallete[2] = { rgb32_make(0x00, 0x00, 0x00), rgb32_make(0xff, 0xff, 0xff)};
|
||||
_invers.reset(new RedPixmapCairo(header.width, header.height, RedPixmap::A1,
|
||||
true, pallete, NULL));
|
||||
}
|
||||
|
||||
void ColorCursor::init_pixels(const CursorHeader& header, const uint8_t* pixels,
|
||||
const uint8_t *and_mask)
|
||||
{
|
||||
int mask_stride = ALIGN(header.width, 8) / 8;
|
||||
int invers_stride = _invers->get_stride();
|
||||
int pixmap_stride = _pixmap->get_stride();
|
||||
uint8_t *_pixmap_line = _pixmap->get_data();
|
||||
uint8_t* invers_line = _invers->get_data();
|
||||
bool be_bits = _invers->is_big_endian_bits();
|
||||
memset(invers_line, 0, header.height * invers_stride);
|
||||
for (int i = 0; i < header.height; i++, and_mask += mask_stride, invers_line += invers_stride,
|
||||
_pixmap_line += pixmap_stride) {
|
||||
uint32_t *line_32 = (uint32_t *)_pixmap_line;
|
||||
for (int j = 0; j < header.width; j++) {
|
||||
uint32_t pixel_val = get_pixel_color(pixels, i, j);
|
||||
int and_val = test_bit_be(and_mask, j);
|
||||
if ((pixel_val & 0x00ffffff) == 0 && and_val) {
|
||||
line_32[j] = 0;
|
||||
} else if ((pixel_val & 0x00ffffff) == 0x00ffffff && and_val) {
|
||||
line_32[j] = 0;
|
||||
if (be_bits) {
|
||||
set_bit_be(invers_line, j);
|
||||
} else {
|
||||
set_bit(invers_line, j);
|
||||
}
|
||||
} else {
|
||||
line_32[j] = pixel_val | 0xff000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ColorCursor::draw(RedDrawable& dest, int x, int y, const Rect& area)
|
||||
{
|
||||
dest.blend_pixels(*_pixmap, area.left - x, area.top - y, area);
|
||||
dest.combine_pixels(*_invers, area.left - x, area.top - y, area, RedDrawable::OP_XOR);
|
||||
}
|
||||
|
||||
class ColorCursor32: public ColorCursor {
|
||||
public:
|
||||
ColorCursor32(const CursorHeader& header, const uint8_t* data)
|
||||
: ColorCursor(header)
|
||||
, _src_stride (header.width * sizeof(uint32_t))
|
||||
{
|
||||
init_pixels(header, data, data + _src_stride * header.height);
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t get_pixel_color(const uint8_t *data, int row, int col)
|
||||
{
|
||||
return *((uint32_t *)(data + row * _src_stride) + col);
|
||||
}
|
||||
|
||||
private:
|
||||
int _src_stride;
|
||||
};
|
||||
|
||||
class ColorCursor16: public ColorCursor {
|
||||
public:
|
||||
ColorCursor16(const CursorHeader& header, const uint8_t* data)
|
||||
: ColorCursor(header)
|
||||
, _src_stride (header.width * sizeof(uint16_t))
|
||||
{
|
||||
init_pixels(header, data, data + _src_stride * header.height);
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t get_pixel_color(const uint8_t *data, int row, int col)
|
||||
{
|
||||
uint32_t pix = *((uint16_t*)(data + row * _src_stride) + col);
|
||||
return ((pix & 0x1f) << 3) | ((pix & 0x3e0) << 6) | ((pix & 0x7c00) << 9);
|
||||
}
|
||||
|
||||
private:
|
||||
int _src_stride;
|
||||
};
|
||||
|
||||
class ColorCursor4: public ColorCursor {
|
||||
public:
|
||||
ColorCursor4(const CursorHeader& header, const uint8_t* data)
|
||||
: ColorCursor(header)
|
||||
, _src_stride (ALIGN(header.width, 2) >> 1)
|
||||
, _palette ((uint32_t*)(data + _src_stride * header.height))
|
||||
{
|
||||
init_pixels(header, data, (uint8_t*)(_palette + 16));
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t get_pixel_color(const uint8_t *data, int row, int col)
|
||||
{
|
||||
data += _src_stride * row + (col >> 1);
|
||||
return (col & 1) ? _palette[*data & 0x0f] : _palette[*data >> 4];
|
||||
}
|
||||
|
||||
private:
|
||||
int _src_stride;
|
||||
uint32_t* _palette;
|
||||
};
|
||||
|
||||
class CursorSetEvent: public Event {
|
||||
public:
|
||||
CursorSetEvent(CursorChannel& channel, CursorData *cursor, int x, int y, bool visable)
|
||||
: _channel (channel)
|
||||
, _cursor (cursor->ref())
|
||||
, _x (x)
|
||||
, _y (y)
|
||||
, _visible (visable)
|
||||
{
|
||||
}
|
||||
|
||||
void create_cursor()
|
||||
{
|
||||
CursorData *cursor = *_cursor;
|
||||
CursorOpaque* native_cursor = cursor->get_opaque();
|
||||
|
||||
if (native_cursor) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cursor->header().type) {
|
||||
case CURSOR_TYPE_ALPHA:
|
||||
native_cursor = new AlphaCursor(cursor->header(), cursor->data());
|
||||
break;
|
||||
case CURSOR_TYPE_COLOR32:
|
||||
native_cursor = new ColorCursor32(cursor->header(), cursor->data());
|
||||
break;
|
||||
case CURSOR_TYPE_MONO:
|
||||
native_cursor = new MonoCursor(cursor->header(), cursor->data());
|
||||
break;
|
||||
case CURSOR_TYPE_COLOR4:
|
||||
native_cursor = new ColorCursor4(cursor->header(), cursor->data());
|
||||
break;
|
||||
case CURSOR_TYPE_COLOR8:
|
||||
native_cursor = new UnsupportedCursor(cursor->header());
|
||||
break;
|
||||
case CURSOR_TYPE_COLOR16:
|
||||
native_cursor = new ColorCursor16(cursor->header(), cursor->data());
|
||||
break;
|
||||
case CURSOR_TYPE_COLOR24:
|
||||
native_cursor = new UnsupportedCursor(cursor->header());
|
||||
break;
|
||||
default:
|
||||
THROW("invalid curosr type");
|
||||
}
|
||||
cursor->set_opaque(native_cursor);
|
||||
}
|
||||
|
||||
virtual void responce(Application& application)
|
||||
{
|
||||
CursorData *cursor = *_cursor;
|
||||
create_cursor();
|
||||
Lock lock(_channel._update_lock);
|
||||
|
||||
_channel._hot_pos.x = _x;
|
||||
_channel._hot_pos.y = _y;
|
||||
_channel._cursor_visible = _visible;
|
||||
_channel._cursor_rect.left = _x - cursor->header().hot_spot_x;
|
||||
_channel._cursor_rect.right = _channel._cursor_rect.left + cursor->header().width;
|
||||
_channel._cursor_rect.top = _y - cursor->header().hot_spot_y;
|
||||
_channel._cursor_rect.bottom = _channel._cursor_rect.top + cursor->header().height;
|
||||
|
||||
if (application.get_mouse_mode() == RED_MOUSE_MODE_CLIENT) {
|
||||
RedScreen* screen = _channel.screen();
|
||||
ASSERT(screen);
|
||||
screen->set_cursor(_visible ? cursor : NULL);
|
||||
} else {
|
||||
if (_visible) {
|
||||
_channel.set_rect_area(_channel._cursor_rect);
|
||||
} else {
|
||||
_channel.clear_area();
|
||||
}
|
||||
}
|
||||
|
||||
if (_channel._cursor) {
|
||||
_channel._cursor->unref();
|
||||
}
|
||||
|
||||
_channel._cursor = cursor->ref();
|
||||
}
|
||||
|
||||
private:
|
||||
CursorChannel& _channel;
|
||||
AutoRef<CursorData> _cursor;
|
||||
int _x;
|
||||
int _y;
|
||||
bool _visible;
|
||||
};
|
||||
|
||||
class CursorMoveEvent: public Event {
|
||||
public:
|
||||
CursorMoveEvent(CursorChannel& channel, int x, int y)
|
||||
: _channel (channel)
|
||||
, _x (x)
|
||||
, _y (y)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void responce(Application& application)
|
||||
{
|
||||
_channel._cursor_visible = true;
|
||||
if (application.get_mouse_mode() == RED_MOUSE_MODE_CLIENT) {
|
||||
RedScreen* screen = _channel.screen();
|
||||
ASSERT(screen);
|
||||
screen->set_cursor(_channel._cursor);
|
||||
} else {
|
||||
Lock lock(_channel._update_lock);
|
||||
int dx = _x - _channel._hot_pos.x;
|
||||
int dy = _y - _channel._hot_pos.y;
|
||||
_channel._hot_pos.x += dx;
|
||||
_channel._hot_pos.y += dy;
|
||||
_channel._cursor_rect.left += dx;
|
||||
_channel._cursor_rect.right += dx;
|
||||
_channel._cursor_rect.top += dy;
|
||||
_channel._cursor_rect.bottom += dy;
|
||||
lock.unlock();
|
||||
_channel.set_rect_area(_channel._cursor_rect);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
CursorChannel& _channel;
|
||||
int _x;
|
||||
int _y;
|
||||
};
|
||||
|
||||
class CursorHideEvent: public Event {
|
||||
public:
|
||||
CursorHideEvent(CursorChannel& channel): _channel (channel) {}
|
||||
virtual void responce(Application& application)
|
||||
{
|
||||
_channel._cursor_visible = false;
|
||||
if (application.get_mouse_mode() == RED_MOUSE_MODE_CLIENT) {
|
||||
RedScreen* screen = _channel.screen();
|
||||
ASSERT(screen);
|
||||
screen->set_cursor(NULL);
|
||||
} else {
|
||||
_channel.clear_area();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
CursorChannel& _channel;
|
||||
};
|
||||
|
||||
class CursorRemoveEvent: public Event {
|
||||
public:
|
||||
CursorRemoveEvent(CursorChannel& channel): _channel (channel) {}
|
||||
virtual void responce(Application& application)
|
||||
{
|
||||
_channel._cursor_visible = false;
|
||||
_channel.clear_area();
|
||||
if (_channel._cursor) {
|
||||
_channel._cursor->unref();
|
||||
_channel._cursor = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
CursorChannel& _channel;
|
||||
};
|
||||
|
||||
class CursorHandler: public MessageHandlerImp<CursorChannel, RED_CURSOR_MESSAGES_END> {
|
||||
public:
|
||||
CursorHandler(CursorChannel& channel)
|
||||
: MessageHandlerImp<CursorChannel, RED_CURSOR_MESSAGES_END>(channel) {}
|
||||
};
|
||||
|
||||
class CursorModeEvent: public Event {
|
||||
public:
|
||||
CursorModeEvent(CursorChannel& channel): _channel (channel) {}
|
||||
|
||||
virtual void responce(Application& application)
|
||||
{
|
||||
RedScreen* screen = _channel.screen();
|
||||
if (!screen) {
|
||||
return;
|
||||
}
|
||||
if (application.get_mouse_mode() == RED_MOUSE_MODE_CLIENT) {
|
||||
_channel.clear_area();
|
||||
screen->set_cursor(_channel._cursor_visible ? _channel._cursor : NULL);
|
||||
} else {
|
||||
if (_channel._cursor_visible && _channel._cursor) {
|
||||
_channel.set_rect_area(_channel._cursor_rect);
|
||||
} else {
|
||||
_channel.clear_area();
|
||||
}
|
||||
}
|
||||
screen->relase_inputs();
|
||||
}
|
||||
|
||||
private:
|
||||
CursorChannel& _channel;
|
||||
};
|
||||
|
||||
CursorModeTrigger::CursorModeTrigger(CursorChannel& channel)
|
||||
: _channel (channel)
|
||||
{
|
||||
}
|
||||
|
||||
void CursorModeTrigger::on_event()
|
||||
{
|
||||
AutoRef<CursorModeEvent> set_event(new CursorModeEvent(_channel));
|
||||
_channel.get_client().push_event(*set_event);
|
||||
}
|
||||
|
||||
CursorChannel::CursorChannel(RedClient& client, uint32_t id)
|
||||
: RedChannel(client, RED_CHANNEL_CURSOR, id, new CursorHandler(*this))
|
||||
, ScreenLayer(SCREEN_LAYER_CURSOR, false)
|
||||
, _cursor (NULL)
|
||||
, _cursor_trigger (*this)
|
||||
, _cursor_visible (false)
|
||||
{
|
||||
CursorHandler* handler = static_cast<CursorHandler*>(get_message_handler());
|
||||
|
||||
handler->set_handler(RED_MIGRATE, &CursorChannel::handle_migrate, 0);
|
||||
handler->set_handler(RED_SET_ACK, &CursorChannel::handle_set_ack, sizeof(RedSetAck));
|
||||
handler->set_handler(RED_PING, &CursorChannel::handle_ping, sizeof(RedPing));
|
||||
handler->set_handler(RED_WAIT_FOR_CHANNELS, &CursorChannel::handle_wait_for_channels,
|
||||
sizeof(RedWaitForChannels));
|
||||
handler->set_handler(RED_DISCONNECTING, &CursorChannel::handle_disconnect,
|
||||
sizeof(RedDisconnect));
|
||||
handler->set_handler(RED_NOTIFY, &CursorChannel::handle_notify, sizeof(RedNotify));
|
||||
|
||||
handler->set_handler(RED_CURSOR_INIT, &CursorChannel::handle_init, sizeof(RedCursorInit));
|
||||
handler->set_handler(RED_CURSOR_RESET, &CursorChannel::handle_reset, 0);
|
||||
handler->set_handler(RED_CURSOR_SET, &CursorChannel::handle_cursor_set,
|
||||
sizeof(RedCursorSet));
|
||||
handler->set_handler(RED_CURSOR_MOVE, &CursorChannel::handle_cursor_move,
|
||||
sizeof(RedCursorMove));
|
||||
handler->set_handler(RED_CURSOR_HIDE, &CursorChannel::handle_cursor_hide, 0);
|
||||
handler->set_handler(RED_CURSOR_TRAIL, &CursorChannel::handle_cursor_trail,
|
||||
sizeof(RedCursorTrail));
|
||||
handler->set_handler(RED_CURSOR_INVAL_ONE, &CursorChannel::handle_inval_one,
|
||||
sizeof(RedInvalOne));
|
||||
handler->set_handler(RED_CURSOR_INVAL_ALL, &CursorChannel::handle_inval_all, 0);
|
||||
|
||||
get_events_loop().add_trigger(_cursor_trigger);
|
||||
}
|
||||
|
||||
CursorChannel::~CursorChannel()
|
||||
{
|
||||
ASSERT(!_cursor);
|
||||
}
|
||||
|
||||
void CursorChannel::on_connect()
|
||||
{
|
||||
}
|
||||
|
||||
void CursorChannel::on_disconnect()
|
||||
{
|
||||
remove_cursor();
|
||||
_cursor_cache.clear();
|
||||
AutoRef<SyncEvent> sync_event(new SyncEvent());
|
||||
get_client().push_event(*sync_event);
|
||||
(*sync_event)->wait();
|
||||
detach_from_screen(get_client().get_application());
|
||||
}
|
||||
|
||||
void CursorChannel::remove_cursor()
|
||||
{
|
||||
AutoRef<CursorRemoveEvent> event(new CursorRemoveEvent(*this));
|
||||
get_client().push_event(*event);
|
||||
}
|
||||
|
||||
void CursorChannel::copy_pixels(const QRegion& dest_region, RedDrawable& dest_dc)
|
||||
{
|
||||
Lock lock(_update_lock);
|
||||
for (int i = 0; i < (int)dest_region.num_rects; i++) {
|
||||
ASSERT(_cursor && _cursor->get_opaque());
|
||||
((NaitivCursor*)_cursor->get_opaque())->draw(dest_dc, _cursor_rect.left, _cursor_rect.top,
|
||||
dest_region.rects[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void CursorChannel::set_cursor(RedCursor& red_cursor, int data_size, int x, int y, bool visible)
|
||||
{
|
||||
CursorData *cursor;
|
||||
|
||||
if (red_cursor.flags & RED_CURSOR_NONE) {
|
||||
remove_cursor();
|
||||
return;
|
||||
}
|
||||
|
||||
if (red_cursor.flags & RED_CURSOR_FROM_CACHE) {
|
||||
cursor = _cursor_cache.get(red_cursor.header.unique);
|
||||
} else {
|
||||
cursor = new CursorData(red_cursor, data_size);
|
||||
if (red_cursor.flags & RED_CURSOR_CACHE_ME) {
|
||||
ASSERT(red_cursor.header.unique);
|
||||
_cursor_cache.add(red_cursor.header.unique, cursor);
|
||||
}
|
||||
}
|
||||
|
||||
AutoRef<CursorData> cursor_ref(cursor);
|
||||
AutoRef<CursorSetEvent> set_event(new CursorSetEvent(*this, *cursor_ref, x, y, visible));
|
||||
get_client().push_event(*set_event);
|
||||
}
|
||||
|
||||
void CursorChannel::handle_init(RedPeer::InMessage *message)
|
||||
{
|
||||
RedCursorInit *init = (RedCursorInit*)message->data();
|
||||
attach_to_screen(get_client().get_application(), get_id());
|
||||
remove_cursor();
|
||||
_cursor_cache.clear();
|
||||
set_cursor_mode();
|
||||
set_cursor(init->cursor, message->size() - sizeof(RedCursorInit), init->position.x,
|
||||
init->position.y, init->visible != 0);
|
||||
}
|
||||
|
||||
void CursorChannel::handle_reset(RedPeer::InMessage *message)
|
||||
{
|
||||
remove_cursor();
|
||||
detach_from_screen(get_client().get_application());
|
||||
_cursor_cache.clear();
|
||||
}
|
||||
|
||||
void CursorChannel::handle_cursor_set(RedPeer::InMessage* message)
|
||||
{
|
||||
RedCursorSet* set = (RedCursorSet*)message->data();
|
||||
set_cursor(set->cursor, message->size() - sizeof(RedCursorSet), set->postition.x,
|
||||
set->postition.y, set->visible != 0);
|
||||
}
|
||||
|
||||
void CursorChannel::handle_cursor_move(RedPeer::InMessage* message)
|
||||
{
|
||||
RedCursorMove* move = (RedCursorMove*)message->data();
|
||||
AutoRef<CursorMoveEvent> event(new CursorMoveEvent(*this, move->postition.x,
|
||||
move->postition.y));
|
||||
get_client().push_event(*event);
|
||||
}
|
||||
|
||||
void CursorChannel::handle_cursor_hide(RedPeer::InMessage* message)
|
||||
{
|
||||
AutoRef<CursorHideEvent> event(new CursorHideEvent(*this));
|
||||
get_client().push_event(*event);
|
||||
}
|
||||
|
||||
void CursorChannel::handle_cursor_trail(RedPeer::InMessage* message)
|
||||
{
|
||||
RedCursorTrail* trail = (RedCursorTrail*)message->data();
|
||||
DBG(0, "length %u frequency %u", trail->length, trail->frequency)
|
||||
}
|
||||
|
||||
void CursorChannel::handle_inval_one(RedPeer::InMessage* message)
|
||||
{
|
||||
RedInvalOne* inval = (RedInvalOne*)message->data();
|
||||
_cursor_cache.remove(inval->id);
|
||||
}
|
||||
|
||||
void CursorChannel::handle_inval_all(RedPeer::InMessage* message)
|
||||
{
|
||||
_cursor_cache.clear();
|
||||
}
|
||||
|
||||
void CursorChannel::set_cursor_mode()
|
||||
{
|
||||
_cursor_trigger.trigger();
|
||||
}
|
||||
|
||||
class CursorFactory: public ChannelFactory {
|
||||
public:
|
||||
CursorFactory() : ChannelFactory(RED_CHANNEL_CURSOR) {}
|
||||
virtual RedChannel* construct(RedClient& client, uint32_t id)
|
||||
{
|
||||
return new CursorChannel(client, id);
|
||||
}
|
||||
};
|
||||
|
||||
static CursorFactory factory;
|
||||
|
||||
ChannelFactory& CursorChannel::Factory()
|
||||
{
|
||||
return factory;
|
||||
}
|
||||
|
||||
100
client/cursor_channel.h
Normal file
100
client/cursor_channel.h
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_CURSOR_CHANNEL
|
||||
#define _H_CURSOR_CHANNEL
|
||||
|
||||
#include "red_channel.h"
|
||||
#include "cache.hpp"
|
||||
#include "cursor.h"
|
||||
#include "screen_layer.h"
|
||||
|
||||
class ChannelFactory;
|
||||
class CursorChannel;
|
||||
|
||||
class CursorCacheTreat {
|
||||
public:
|
||||
static inline CursorData* get(CursorData* cursor)
|
||||
{
|
||||
return cursor->ref();
|
||||
}
|
||||
|
||||
static inline void release(CursorData* cursor)
|
||||
{
|
||||
cursor->unref();
|
||||
}
|
||||
|
||||
static const char* name() { return "cursor";}
|
||||
};
|
||||
|
||||
typedef Cache<CursorData, CursorCacheTreat, 1024> CursorCache;
|
||||
|
||||
class CursorModeTrigger: public EventsLoop::Trigger {
|
||||
public:
|
||||
CursorModeTrigger(CursorChannel& channel);
|
||||
virtual void on_event();
|
||||
|
||||
private:
|
||||
CursorChannel& _channel;
|
||||
};
|
||||
|
||||
class CursorChannel: public RedChannel, public ScreenLayer {
|
||||
public:
|
||||
CursorChannel(RedClient& client, uint32_t id);
|
||||
virtual ~CursorChannel();
|
||||
|
||||
static ChannelFactory& Factory();
|
||||
void set_cursor_mode();
|
||||
|
||||
protected:
|
||||
virtual void on_connect();
|
||||
virtual void on_disconnect();
|
||||
|
||||
private:
|
||||
void set_cursor(RedCursor& red_cursor, int data_size, int x, int y, bool visible);
|
||||
void remove_cursor();
|
||||
|
||||
virtual void copy_pixels(const QRegion& dest_region, RedDrawable& dest_dc);
|
||||
|
||||
void handle_init(RedPeer::InMessage* message);
|
||||
void handle_reset(RedPeer::InMessage* message);
|
||||
void handle_cursor_set(RedPeer::InMessage* message);
|
||||
void handle_cursor_move(RedPeer::InMessage* message);
|
||||
void handle_cursor_hide(RedPeer::InMessage* message);
|
||||
void handle_cursor_trail(RedPeer::InMessage* message);
|
||||
void handle_inval_one(RedPeer::InMessage* message);
|
||||
void handle_inval_all(RedPeer::InMessage* message);
|
||||
|
||||
friend class CursorSetEvent;
|
||||
friend class CursorMoveEvent;
|
||||
friend class CursorHideEvent;
|
||||
friend class CursorRemoveEvent;
|
||||
friend class CursorModeTrigger;
|
||||
friend class CursorModeEvent;
|
||||
|
||||
private:
|
||||
CursorCache _cursor_cache;
|
||||
CursorData* _cursor;
|
||||
CursorModeTrigger _cursor_trigger;
|
||||
Point _hot_pos;
|
||||
Rect _cursor_rect;
|
||||
Mutex _update_lock;
|
||||
bool _cursor_visible;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
101
client/debug.h
Normal file
101
client/debug.h
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_DEBUG
|
||||
#define _H_DEBUG
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sstream>
|
||||
|
||||
#include <log4cpp/Category.hh>
|
||||
#include <log4cpp/convenience.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
#define ON_PANIC() ::abort()
|
||||
|
||||
#ifdef RED_DEBUG
|
||||
|
||||
#ifdef WIN32
|
||||
#define ASSERTBREAK DebugBreak()
|
||||
#else
|
||||
#define ASSERTBREAK ::abort()
|
||||
#endif
|
||||
|
||||
#define ASSERT(x) if (!(x)) { \
|
||||
printf("%s: ASSERT %s failed\n", __FUNCTION__, #x); \
|
||||
ASSERTBREAK; \
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define ASSERT(cond)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
static inline std::string pretty_func_to_func_name(const std::string& f_name)
|
||||
{
|
||||
std::string name(f_name);
|
||||
std::string::size_type end_pos = f_name.find('(');
|
||||
if (end_pos == std::string::npos) {
|
||||
return f_name;
|
||||
}
|
||||
std::string::size_type start = f_name.rfind(' ', end_pos);
|
||||
if (start == std::string::npos) {
|
||||
return f_name;
|
||||
}
|
||||
end_pos -= ++start;
|
||||
return name.substr(start, end_pos);
|
||||
}
|
||||
|
||||
#define FUNC_NAME pretty_func_to_func_name(__PRETTY_FUNCTION__).c_str()
|
||||
#else
|
||||
#define FUNC_NAME __FUNCTION__
|
||||
#endif
|
||||
|
||||
#define LOGGER_SECTION(section) LOG4CPP_LOGGER(section)
|
||||
|
||||
LOG4CPP_LOGGER("spice")
|
||||
|
||||
#define LOG(type, format, ...) { \
|
||||
std::string log_message; \
|
||||
string_printf(log_message, "%s: " format, FUNC_NAME, ## __VA_ARGS__); \
|
||||
LOG4CPP_##type(logger, log_message.c_str()); \
|
||||
}
|
||||
|
||||
#define LOG_INFO(format, ...) LOG(INFO, format, ## __VA_ARGS__)
|
||||
#define LOG_WARN(format, ...) LOG(WARN, format, ## __VA_ARGS__)
|
||||
#define LOG_ERROR(format, ...) LOG(ERROR, format, ## __VA_ARGS__)
|
||||
|
||||
#define PANIC(format, ...) { \
|
||||
LOG(FATAL, format, ## __VA_ARGS__); \
|
||||
ON_PANIC(); \
|
||||
}
|
||||
|
||||
#define DBGLEVEL 1000
|
||||
|
||||
#define DBG(level, format, ...) { \
|
||||
if (level <= DBGLEVEL) { \
|
||||
LOG(DEBUG, format, ## __VA_ARGS__); \
|
||||
} \
|
||||
}
|
||||
|
||||
#endif // _H_DEBUG
|
||||
|
||||
1483
client/display_channel.cpp
Normal file
1483
client/display_channel.cpp
Normal file
File diff suppressed because it is too large
Load Diff
190
client/display_channel.h
Normal file
190
client/display_channel.h
Normal file
@ -0,0 +1,190 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_DISPLAY_CHANNEL
|
||||
#define _H_DISPLAY_CHANNEL
|
||||
|
||||
#include "common.h"
|
||||
#include "canvas.h"
|
||||
#include "region.h"
|
||||
#include "red_channel.h"
|
||||
#include "cairo.h"
|
||||
#include "cache.hpp"
|
||||
#include "screen_layer.h"
|
||||
#include "events_loop.h"
|
||||
#ifdef USE_OGL
|
||||
#include "red_pixmap_gl.h"
|
||||
#endif
|
||||
#include "glz_decoder_window.h"
|
||||
|
||||
class RedScreen;
|
||||
class ChannelFactory;
|
||||
class VideoStream;
|
||||
class DisplayChannel;
|
||||
|
||||
class StreamsTrigger: public EventsLoop::Trigger {
|
||||
public:
|
||||
StreamsTrigger(DisplayChannel& channel);
|
||||
|
||||
virtual void on_event();
|
||||
|
||||
private:
|
||||
DisplayChannel& _channel;
|
||||
};
|
||||
|
||||
#ifdef USE_OGL
|
||||
class GLInterruptRecreate: public EventsLoop::Trigger {
|
||||
public:
|
||||
GLInterruptRecreate(DisplayChannel& channel);
|
||||
virtual void trigger();
|
||||
virtual void on_event();
|
||||
|
||||
private:
|
||||
DisplayChannel& _channel;
|
||||
Mutex _lock;
|
||||
Condition _cond;
|
||||
};
|
||||
#endif
|
||||
|
||||
class InterruptUpdate: public EventsLoop::Trigger {
|
||||
public:
|
||||
InterruptUpdate(DisplayChannel& channel);
|
||||
|
||||
virtual void on_event();
|
||||
|
||||
private:
|
||||
DisplayChannel& _channel;
|
||||
};
|
||||
|
||||
|
||||
class DisplayChannel: public RedChannel, public ScreenLayer {
|
||||
public:
|
||||
DisplayChannel(RedClient& client, uint32_t id,
|
||||
PixmapCache& pixmap_cache, GlzDecoderWindow& glz_window);
|
||||
virtual ~DisplayChannel();
|
||||
|
||||
virtual void copy_pixels(const QRegion& dest_region, RedDrawable& dest_dc);
|
||||
virtual void copy_pixels(const QRegion& dest_region, const PixmapHeader &dest);
|
||||
#ifdef USE_OGL
|
||||
virtual void recreate_ogl_context();
|
||||
virtual void recreate_ogl_context_interrupt();
|
||||
virtual void pre_migrate();
|
||||
virtual void post_migrate();
|
||||
#endif
|
||||
virtual void update_interrupt();
|
||||
|
||||
static ChannelFactory& Factory();
|
||||
|
||||
protected:
|
||||
virtual void on_connect();
|
||||
virtual void on_disconnect();
|
||||
|
||||
private:
|
||||
void set_draw_handlers();
|
||||
void clear_draw_handlers();
|
||||
bool create_cairo_canvas(int width, int height, int depth);
|
||||
#ifdef USE_OGL
|
||||
bool create_ogl_canvas(int width, int height, int depth, bool recreate,
|
||||
RenderType rendertype);
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
bool create_gdi_canvas(int width, int height, int depth);
|
||||
#endif
|
||||
void destroy_canvas();
|
||||
void create_canvas(const std::vector<int>& canvas_type, int width, int height,
|
||||
int depth);
|
||||
void destroy_strams();
|
||||
|
||||
void handle_mode(RedPeer::InMessage* message);
|
||||
void handle_mark(RedPeer::InMessage* message);
|
||||
void handle_reset(RedPeer::InMessage* message);
|
||||
|
||||
void handle_inval_list(RedPeer::InMessage* message);
|
||||
void handle_inval_all_pixmaps(RedPeer::InMessage* message);
|
||||
void handle_inval_palette(RedPeer::InMessage* message);
|
||||
void handle_inval_all_palettes(RedPeer::InMessage* message);
|
||||
void handle_copy_bits(RedPeer::InMessage* message);
|
||||
void handle_stream_create(RedPeer::InMessage* message);
|
||||
void handle_stream_data(RedPeer::InMessage* message);
|
||||
void handle_stream_clip(RedPeer::InMessage* message);
|
||||
void handle_stream_destroy(RedPeer::InMessage* message);
|
||||
void handle_stream_destroy_all(RedPeer::InMessage* message);
|
||||
|
||||
void handle_draw_fill(RedPeer::InMessage* message);
|
||||
void handle_draw_opaque(RedPeer::InMessage* message);
|
||||
void handle_draw_copy(RedPeer::InMessage* message);
|
||||
void handle_draw_blend(RedPeer::InMessage* message);
|
||||
void handle_draw_blackness(RedPeer::InMessage* message);
|
||||
void handle_draw_whiteness(RedPeer::InMessage* message);
|
||||
void handle_draw_invers(RedPeer::InMessage* message);
|
||||
void handle_draw_rop3(RedPeer::InMessage* message);
|
||||
void handle_draw_stroke(RedPeer::InMessage* message);
|
||||
void handle_draw_text(RedPeer::InMessage* message);
|
||||
void handle_draw_transparent(RedPeer::InMessage* message);
|
||||
void handle_draw_alpha_blend(RedPeer::InMessage* message);
|
||||
|
||||
void on_streams_trigger();
|
||||
virtual void on_update_completion(uint64_t mark);
|
||||
void streams_time();
|
||||
void activate_streams_timer();
|
||||
void stream_update_request(uint32_t update_time);
|
||||
|
||||
static void set_clip_rects(const Clip& clip, uint32_t& num_clip_rects, Rect*& clip_rects,
|
||||
unsigned long addr_offset, uint8_t *min, uint8_t *max);
|
||||
static void streams_timer_callback(void* opaque, TimerID timer);
|
||||
static void reset_timer_callback(void* opaque, TimerID timer);
|
||||
|
||||
private:
|
||||
std::auto_ptr<Canvas> _canvas;
|
||||
PixmapCache& _pixmap_cache;
|
||||
PaletteCache _palette_cache;
|
||||
GlzDecoderWindow& _glz_window;
|
||||
bool _mark;
|
||||
int _x_res;
|
||||
int _y_res;
|
||||
int _depth;
|
||||
#ifdef USE_OGL
|
||||
RenderType _rendertype;
|
||||
#endif
|
||||
|
||||
#ifndef RED64
|
||||
Mutex _mark_lock;
|
||||
#endif
|
||||
uint64_t _update_mark;
|
||||
Mutex _streams_lock;
|
||||
|
||||
Mutex _timer_lock;
|
||||
TimerID _streams_timer;
|
||||
uint32_t _next_timer_time;
|
||||
|
||||
std::vector<VideoStream*> _streams;
|
||||
VideoStream* _active_streams;
|
||||
StreamsTrigger _streams_trigger;
|
||||
#ifdef USE_OGL
|
||||
GLInterruptRecreate _gl_interrupt_recreate;
|
||||
#endif
|
||||
InterruptUpdate _interrupt_update;
|
||||
friend class SetModeEvent;
|
||||
friend class ActivateTimerEvent;
|
||||
friend class VideoStream;
|
||||
friend class StreamsTrigger;
|
||||
friend class GLInterupt;
|
||||
friend void streams_timer_callback(void* opaque, TimerID timer);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
86
client/events_loop.h
Normal file
86
client/events_loop.h
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_EVENTS_LOOP
|
||||
#define _H_EVENTS_LOOP
|
||||
|
||||
#include "common.h"
|
||||
#include "events_loop_p.h"
|
||||
|
||||
class EventSource;
|
||||
|
||||
class EventsLoop: public EventsLoop_p {
|
||||
public:
|
||||
class Trigger;
|
||||
class Socket;
|
||||
class File;
|
||||
|
||||
EventsLoop();
|
||||
virtual ~EventsLoop();
|
||||
|
||||
void add_trigger(Trigger& trigger);
|
||||
void remove_trigger(Trigger& trigger);
|
||||
void add_socket(Socket& socket);
|
||||
void remove_socket(Socket& socket);
|
||||
void add_file(File& file);
|
||||
void remove_file(File& file);
|
||||
void run();
|
||||
// FIXME: temporary - need to adjust the loop for the main thread
|
||||
void run_once(int timeout_milli = INFINITE);
|
||||
};
|
||||
|
||||
class EventSource {
|
||||
public:
|
||||
virtual ~EventSource() {}
|
||||
virtual void on_event() = 0;
|
||||
|
||||
private:
|
||||
virtual void action() {on_event();}
|
||||
|
||||
friend class EventsLoop;
|
||||
};
|
||||
|
||||
class EventsLoop::Trigger: public EventSource, private Trigger_p {
|
||||
public:
|
||||
Trigger();
|
||||
virtual ~Trigger();
|
||||
virtual void trigger();
|
||||
virtual void reset();
|
||||
|
||||
private:
|
||||
virtual void action();
|
||||
|
||||
friend class EventsLoop;
|
||||
};
|
||||
|
||||
class EventsLoop::Socket: public EventSource {
|
||||
protected:
|
||||
virtual int get_socket() = 0;
|
||||
|
||||
friend class EventsLoop;
|
||||
};
|
||||
|
||||
|
||||
class EventsLoop::File: public EventSource {
|
||||
protected:
|
||||
virtual int get_fd() = 0;
|
||||
|
||||
friend class EventsLoop;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
24
client/gdi_canvas.cpp
Normal file
24
client/gdi_canvas.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define CANVAS_ERROR(format, ...) THROW(format, ## __VA_ARGS__)
|
||||
|
||||
#include "../common/gdi_canvas.c"
|
||||
|
||||
21
client/gl_canvas.cpp
Normal file
21
client/gl_canvas.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include "common.h"
|
||||
#include "../common/gl_canvas.c"
|
||||
|
||||
21
client/glc.cpp
Normal file
21
client/glc.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include "common.h"
|
||||
#include "../common/glc.c"
|
||||
|
||||
335
client/glz_decode_tmpl.c
Normal file
335
client/glz_decode_tmpl.c
Normal file
@ -0,0 +1,335 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// External defines: PLT, RGBX/PLTXX/ALPHA, TO_RGB32.
|
||||
// If PLT4/1 and TO_RGB32 are defined, we need CAST_PLT_DISTANCE (
|
||||
// because then the number of pixels differ from the units used in the compression)
|
||||
|
||||
/*
|
||||
For each output pixel type the following macros are defined:
|
||||
OUT_PIXEL - the output pixel type
|
||||
COPY_PIXEL(p, out) - assignes 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.
|
||||
*/
|
||||
|
||||
#if !defined(LZ_RGB_ALPHA)
|
||||
#define COPY_PIXEL(p, out) (*(out++) = p)
|
||||
#define COPY_REF_PIXEL(ref, out) (*(out++) = *(ref++))
|
||||
#endif
|
||||
|
||||
// decompressing plt to plt
|
||||
#ifdef LZ_PLT
|
||||
#ifndef TO_RGB32
|
||||
#define OUT_PIXEL one_byte_pixel_t
|
||||
#define FNAME(name) glz_plt_##name
|
||||
#define COPY_COMP_PIXEL(in, out) {(out)->a = *(in++); out++;}
|
||||
#else // TO_RGB32
|
||||
#define OUT_PIXEL rgb32_pixel_t
|
||||
#define COPY_PLT_ENTRY(ent, out) {\
|
||||
(out)->b = ent; (out)->g = (ent >> 8); (out)->r = (ent >> 16); (out)->pad = 0;}
|
||||
#ifdef PLT8
|
||||
#define FNAME(name) glz_plt8_to_rgb32_##name
|
||||
#define COPY_COMP_PIXEL(in, out, palette) { \
|
||||
uint32_t rgb = palette->ents[*(in++)]; \
|
||||
COPY_PLT_ENTRY(rgb, out); \
|
||||
out++; \
|
||||
}
|
||||
#elif defined(PLT4_BE)
|
||||
#define FNAME(name) glz_plt4_be_to_rgb32_##name
|
||||
#define COPY_COMP_PIXEL(in, out, palette){ \
|
||||
uint8_t byte = *(in++); \
|
||||
uint32_t rgb = palette->ents[((byte >> 4) & 0x0f) % (palette->num_ents)]; \
|
||||
COPY_PLT_ENTRY(rgb, out); \
|
||||
out++; \
|
||||
rgb = palette->ents[(byte & 0x0f) % (palette->num_ents)]; \
|
||||
COPY_PLT_ENTRY(rgb, out); \
|
||||
out++; \
|
||||
}
|
||||
#define CAST_PLT_DISTANCE(dist) (dist*2)
|
||||
#elif defined(PLT4_LE)
|
||||
#define FNAME(name) glz_plt4_le_to_rgb32_##name
|
||||
#define COPY_COMP_PIXEL(in, out, palette){ \
|
||||
uint8_t byte = *(in++); \
|
||||
uint32_t rgb = palette->ents[(byte & 0x0f) % (palette->num_ents)]; \
|
||||
COPY_PLT_ENTRY(rgb, out); \
|
||||
out++; \
|
||||
rgb = palette->ents[((byte >> 4) & 0x0f) % (palette->num_ents)]; \
|
||||
COPY_PLT_ENTRY(rgb, out); \
|
||||
out++; \
|
||||
}
|
||||
#define CAST_PLT_DISTANCE(dist) (dist*2)
|
||||
#elif defined(PLT1_BE) // TODO store palette entries for direct access
|
||||
#define FNAME(name) glz_plt1_be_to_rgb32_##name
|
||||
#define COPY_COMP_PIXEL(in, out, palette){ \
|
||||
uint8_t byte = *(in++); \
|
||||
int i; \
|
||||
uint32_t fore = palette->ents[1]; \
|
||||
uint32_t back = palette->ents[0]; \
|
||||
for (i = 7; i >= 0; i--) \
|
||||
{ \
|
||||
if ((byte >> i) & 1) { \
|
||||
COPY_PLT_ENTRY(fore, out); \
|
||||
} else { \
|
||||
COPY_PLT_ENTRY(back, out); \
|
||||
} \
|
||||
out++; \
|
||||
} \
|
||||
}
|
||||
#define CAST_PLT_DISTANCE(dist) (dist*8)
|
||||
#elif defined(PLT1_LE)
|
||||
#define FNAME(name) glz_plt1_le_to_rgb32_##name
|
||||
#define COPY_COMP_PIXEL(in, out, palette){ \
|
||||
uint8_t byte = *(in++); \
|
||||
int i; \
|
||||
uint32_t fore = palette->ents[1]; \
|
||||
uint32_t back = palette->ents[0]; \
|
||||
for (i = 0; i < 8; i++) \
|
||||
{ \
|
||||
if ((byte >> i) & 1) { \
|
||||
COPY_PLT_ENTRY(fore, out); \
|
||||
} else { \
|
||||
COPY_PLT_ENTRY(back, out); \
|
||||
} \
|
||||
out++; \
|
||||
} \
|
||||
}
|
||||
#define CAST_PLT_DISTANCE(dist) (dist*8)
|
||||
#endif // PLT Type
|
||||
#endif // TO_RGB32
|
||||
#endif
|
||||
|
||||
#ifdef LZ_RGB16
|
||||
#ifndef TO_RGB32
|
||||
#define OUT_PIXEL rgb16_pixel_t
|
||||
#define FNAME(name) glz_rgb16_##name
|
||||
#define COPY_COMP_PIXEL(in, out) {*out = (*(in++)) << 8; *out |= *(in++); out++;}
|
||||
#else
|
||||
#define OUT_PIXEL rgb32_pixel_t
|
||||
#define FNAME(name) glz_rgb16_to_rgb32_##name
|
||||
#define COPY_COMP_PIXEL(in, out) {out->r = *(in++); out->b= *(in++); \
|
||||
out->g = (((out->r) << 6) | ((out->b) >> 2)) & ~0x07; \
|
||||
out->g |= (out->g >> 5); \
|
||||
out->r = ((out->r << 1) & ~0x07) | ((out->r >> 4) & 0x07) ; \
|
||||
out->b = (out->b << 3) | ((out->b >> 2) & 0x07); \
|
||||
out->pad = 0; \
|
||||
out++; \
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef LZ_RGB24
|
||||
#define OUT_PIXEL rgb24_pixel_t
|
||||
#define FNAME(name) glz_rgb24_##name
|
||||
#define COPY_COMP_PIXEL(in, out) { \
|
||||
out->b = *(in++); \
|
||||
out->g = *(in++); \
|
||||
out->r = *(in++); \
|
||||
out++; \
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LZ_RGB32
|
||||
#define OUT_PIXEL rgb32_pixel_t
|
||||
#define FNAME(name) glz_rgb32_##name
|
||||
#define COPY_COMP_PIXEL(in, out) { \
|
||||
out->b = *(in++); \
|
||||
out->g = *(in++); \
|
||||
out->r = *(in++); \
|
||||
out->pad = 0; \
|
||||
out++; \
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LZ_RGB_ALPHA
|
||||
#define OUT_PIXEL rgb32_pixel_t
|
||||
#define FNAME(name) glz_rgb_alpha_##name
|
||||
#define COPY_PIXEL(p, out) {out->pad = p.pad; out++;}
|
||||
#define COPY_REF_PIXEL(ref, out) {out->pad = ref->pad; out++; ref++;}
|
||||
#define COPY_COMP_PIXEL(in, out) {out->pad = *(in++); out++;}
|
||||
#endif
|
||||
|
||||
// TODO: seperate into routines that decode to dist,len. and to a routine that
|
||||
// actualy copies the data.
|
||||
|
||||
/* returns num of bytes read from in buf.
|
||||
size should be in PIXEL */
|
||||
static size_t FNAME(decode)(GlzDecoderWindow &window, uint8_t* in_buf,
|
||||
uint8_t *out_buf, int size,
|
||||
DecodedImageWinId image_win_id, Palette *plt,
|
||||
GlzDecoderDebug &debug_calls)
|
||||
{
|
||||
uint8_t *ip = in_buf;
|
||||
OUT_PIXEL *out_pix_buf = (OUT_PIXEL *)out_buf;
|
||||
OUT_PIXEL *op = out_pix_buf;
|
||||
OUT_PIXEL *op_limit = out_pix_buf + size;
|
||||
|
||||
uint32_t ctrl = *(ip++);
|
||||
int loop = true;
|
||||
|
||||
do {
|
||||
if (ctrl >= MAX_COPY) { // reference (dictionary/RLE)
|
||||
OUT_PIXEL *ref = op;
|
||||
uint32_t len = ctrl >> 5;
|
||||
uint8_t pixel_flag = (ctrl >> 4) & 0x01;
|
||||
uint32_t pixel_ofs = (ctrl & 0x0f);
|
||||
uint8_t image_flag;
|
||||
uint32_t image_dist;
|
||||
|
||||
/* retrieving the referenced images, the offset of the first pixel,
|
||||
and the match length */
|
||||
|
||||
uint8_t code;
|
||||
//len--; // TODO: why do we do this?
|
||||
|
||||
if (len == 7) { // match length is bigger than 7
|
||||
do {
|
||||
code = *(ip++);
|
||||
len += code;
|
||||
} while (code == 255); // remaining of len
|
||||
}
|
||||
code = *(ip++);
|
||||
pixel_ofs += (code << 4);
|
||||
|
||||
code = *(ip++);
|
||||
image_flag = (code >> 6) & 0x03;
|
||||
if (!pixel_flag) { // short pixel offset
|
||||
image_dist = code & 0x3f;
|
||||
for (int i = 0; i < image_flag; i++) {
|
||||
code = *(ip++);
|
||||
image_dist += (code << (6 + (8 * i)));
|
||||
}
|
||||
} else {
|
||||
pixel_flag = (code >> 5) & 0x01;
|
||||
pixel_ofs += (code & 0x1f) << 12;
|
||||
image_dist = 0;
|
||||
for (int i = 0; i < image_flag; i++) {
|
||||
code = *(ip++);
|
||||
image_dist += (code << 8 * i);
|
||||
}
|
||||
|
||||
|
||||
if (pixel_flag) { // very long pixel offset
|
||||
code = *(ip++);
|
||||
pixel_ofs += code << 17;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(LZ_PLT) || defined(LZ_RGB_ALPHA)
|
||||
len += 2; // length is biased by 2 (fixing bias)
|
||||
#elif defined(LZ_RGB16)
|
||||
len += 1; // length is biased by 1 (fixing bias)
|
||||
#endif
|
||||
if (!image_dist) {
|
||||
pixel_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)
|
||||
pixel_ofs = CAST_PLT_DISTANCE(pixel_ofs);
|
||||
len = CAST_PLT_DISTANCE(len);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (!image_dist) { // reference is inside the same image
|
||||
ref -= pixel_ofs;
|
||||
GLZ_ASSERT(debug_calls, ref + len <= op_limit);
|
||||
GLZ_ASSERT(debug_calls, ref >= out_pix_buf);
|
||||
} else {
|
||||
ref = (OUT_PIXEL *)window.get_ref_pixel(image_win_id, image_dist,
|
||||
pixel_ofs);
|
||||
}
|
||||
|
||||
GLZ_ASSERT(debug_calls, op + len <= op_limit);
|
||||
|
||||
/* copying the match*/
|
||||
|
||||
if (ref == (op - 1)) { // run (this will never be called in PLT4/1_TO_RGB because the
|
||||
// number of pixel copied is larger then one...
|
||||
/* optimize copy for a run */
|
||||
OUT_PIXEL b = *ref;
|
||||
for (; len; --len) {
|
||||
COPY_PIXEL(b, op);
|
||||
GLZ_ASSERT(debug_calls, op <= op_limit);
|
||||
}
|
||||
} else {
|
||||
for (; len; --len) {
|
||||
COPY_REF_PIXEL(ref, op);
|
||||
GLZ_ASSERT(debug_calls, 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))
|
||||
GLZ_ASSERT(debug_calls, op + CAST_PLT_DISTANCE(ctrl) <= op_limit);
|
||||
#else
|
||||
GLZ_ASSERT(debug_calls, op + ctrl <= op_limit);
|
||||
#endif
|
||||
|
||||
#if defined(TO_RGB32) && defined(LZ_PLT)
|
||||
GLZ_ASSERT(debug_calls, plt);
|
||||
COPY_COMP_PIXEL(ip, op, plt);
|
||||
#else
|
||||
COPY_COMP_PIXEL(ip, op);
|
||||
#endif
|
||||
GLZ_ASSERT(debug_calls, op <= op_limit);
|
||||
|
||||
for (--ctrl; ctrl; ctrl--) {
|
||||
#if defined(TO_RGB32) && defined(LZ_PLT)
|
||||
GLZ_ASSERT(debug_calls, plt);
|
||||
COPY_COMP_PIXEL(ip, op, plt);
|
||||
#else
|
||||
COPY_COMP_PIXEL(ip, op);
|
||||
#endif
|
||||
GLZ_ASSERT(debug_calls, op <= op_limit);
|
||||
}
|
||||
} // END REF/COPY
|
||||
|
||||
if (LZ_EXPECT_CONDITIONAL(op < op_limit)) {
|
||||
ctrl = *(ip++);
|
||||
} else {
|
||||
loop = false;
|
||||
}
|
||||
} while (LZ_EXPECT_CONDITIONAL(loop));
|
||||
|
||||
return (ip - in_buf);
|
||||
}
|
||||
#undef LZ_PLT
|
||||
#undef PLT8
|
||||
#undef PLT4_BE
|
||||
#undef PLT4_LE
|
||||
#undef PLT1_BE
|
||||
#undef PLT1_LE
|
||||
#undef LZ_RGB16
|
||||
#undef LZ_RGB24
|
||||
#undef LZ_RGB32
|
||||
#undef LZ_RGB_ALPHA
|
||||
#undef TO_RGB32
|
||||
#undef OUT_PIXEL
|
||||
#undef FNAME
|
||||
#undef COPY_PIXEL
|
||||
#undef COPY_REF_PIXEL
|
||||
#undef COPY_COMP_PIXEL
|
||||
#undef COPY_PLT_ENTRY
|
||||
#undef CAST_PLT_DISTANCE
|
||||
|
||||
65
client/glz_decoded_image.h
Normal file
65
client/glz_decoded_image.h
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_GLZ_DECODED_IMAGE
|
||||
#define _H_GLZ_DECODED_IMAGE
|
||||
|
||||
#include "common.h"
|
||||
#include "glz_decoder_config.h"
|
||||
|
||||
/*
|
||||
This class represents an image the lz window holds. It is created after the decoding of the
|
||||
image is completed, and destroyed when it exits the window.
|
||||
*/
|
||||
|
||||
class GlzDecodedImage {
|
||||
public:
|
||||
|
||||
GlzDecodedImage(uint64_t id, uint64_t win_head_id, uint8_t *data, int size,
|
||||
int bytes_per_pixel)
|
||||
: _id (id)
|
||||
, _win_head_id (win_head_id)
|
||||
, _data (data)
|
||||
, _bytes_per_pixel (bytes_per_pixel)
|
||||
, _size (size) {}
|
||||
|
||||
virtual ~GlzDecodedImage() {}
|
||||
uint8_t *get_data() {return _data;}
|
||||
uint8_t *get_pixel_ref(int offset); // pallete pix_id = byte count
|
||||
uint64_t get_id() {return _id;}
|
||||
uint64_t get_window_head_id() {return _win_head_id;}
|
||||
int get_size() {return _size;}
|
||||
|
||||
protected:
|
||||
uint64_t _id;
|
||||
uint64_t _win_head_id;
|
||||
uint8_t *_data;
|
||||
int _bytes_per_pixel; // if image is with palette pixel=byte
|
||||
int _size; // number of pixels
|
||||
};
|
||||
|
||||
inline uint8_t* GlzDecodedImage::get_pixel_ref(int offset)
|
||||
{
|
||||
if (!_data) {
|
||||
return NULL;
|
||||
} else {
|
||||
return (_data + (offset * _bytes_per_pixel));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
290
client/glz_decoder.cpp
Normal file
290
client/glz_decoder.cpp
Normal file
@ -0,0 +1,290 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "glz_decoder_config.h"
|
||||
#include "glz_decoder.h"
|
||||
|
||||
GlzDecoder::GlzDecoder(GlzDecoderWindow &images_window,
|
||||
GlzDecodeHandler &usr_handler, GlzDecoderDebug &debug_calls)
|
||||
: _images_window (images_window)
|
||||
, _usr_handler (usr_handler)
|
||||
, _debug_calls (debug_calls)
|
||||
{
|
||||
}
|
||||
|
||||
GlzDecoder::~GlzDecoder()
|
||||
{
|
||||
}
|
||||
|
||||
void GlzDecoder::decode_header()
|
||||
{
|
||||
int magic;
|
||||
int version;
|
||||
uint8_t tmp;
|
||||
int stride;
|
||||
|
||||
magic = decode_32();
|
||||
if (magic != LZ_MAGIC) {
|
||||
_debug_calls.warn(std::string("bad magic\n"));
|
||||
}
|
||||
|
||||
version = decode_32();
|
||||
if (version != LZ_VERSION) {
|
||||
_debug_calls.warn(std::string("bad version\n"));
|
||||
}
|
||||
|
||||
tmp = *(_in_now++);
|
||||
|
||||
_image.type = (LzImageType)(tmp & LZ_IMAGE_TYPE_MASK);
|
||||
_image.top_down = (tmp >> LZ_IMAGE_TYPE_LOG) ? true : false;
|
||||
_image.width = decode_32();
|
||||
_image.height = decode_32();
|
||||
stride = decode_32();
|
||||
|
||||
if (IS_IMAGE_TYPE_PLT[_image.type]) {
|
||||
_image.gross_pixels = stride * PLT_PIXELS_PER_BYTE[_image.type] * _image.height;
|
||||
} else {
|
||||
_image.gross_pixels = _image.width * _image.height;
|
||||
}
|
||||
|
||||
_image.id = decode_64();
|
||||
_image.win_head_dist = decode_32();
|
||||
}
|
||||
|
||||
inline uint32_t GlzDecoder::decode_32()
|
||||
{
|
||||
uint32_t word = 0;
|
||||
word |= *(_in_now++);
|
||||
word <<= 8;
|
||||
word |= *(_in_now++);
|
||||
word <<= 8;
|
||||
word |= *(_in_now++);
|
||||
word <<= 8;
|
||||
word |= *(_in_now++);
|
||||
return word;
|
||||
}
|
||||
|
||||
inline uint64_t GlzDecoder::decode_64()
|
||||
{
|
||||
uint64_t long_word = decode_32();
|
||||
long_word <<= 32;
|
||||
long_word |= decode_32();
|
||||
return long_word;
|
||||
}
|
||||
|
||||
// TODO: the code is historically c based. Consider transforming to c++ and use templates
|
||||
// - but be sure it won't make it slower!
|
||||
|
||||
/*
|
||||
* Give hints to the compiler for branch prediction optimization.
|
||||
*/
|
||||
#if defined(__GNUC__) && (__GNUC__ > 2)
|
||||
#define LZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1))
|
||||
#define LZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0))
|
||||
#else
|
||||
#define LZ_EXPECT_CONDITIONAL(c) (c)
|
||||
#define LZ_UNEXPECT_CONDITIONAL(c) (c)
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define ATTR_PACKED __attribute__ ((__packed__))
|
||||
#else
|
||||
#define ATTR_PACKED
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
#endif
|
||||
|
||||
|
||||
/* the palette images will be treated as one byte pixels. Their width should be transformed
|
||||
accordingly.
|
||||
*/
|
||||
typedef struct ATTR_PACKED one_byte_pixel_t {
|
||||
uint8_t a;
|
||||
} one_byte_pixel_t;
|
||||
|
||||
typedef struct 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 {
|
||||
uint8_t b;
|
||||
uint8_t g;
|
||||
uint8_t r;
|
||||
} rgb24_pixel_t;
|
||||
|
||||
typedef uint16_t rgb16_pixel_t;
|
||||
|
||||
#ifndef __GNUC__
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
#undef ATTR_PACKED
|
||||
|
||||
#define LZ_PLT
|
||||
#include "glz_decode_tmpl.c"
|
||||
|
||||
#define LZ_PLT
|
||||
#define PLT8
|
||||
#define TO_RGB32
|
||||
#include "glz_decode_tmpl.c"
|
||||
|
||||
#define LZ_PLT
|
||||
#define PLT4_BE
|
||||
#define TO_RGB32
|
||||
#include "glz_decode_tmpl.c"
|
||||
|
||||
#define LZ_PLT
|
||||
#define PLT4_LE
|
||||
#define TO_RGB32
|
||||
#include "glz_decode_tmpl.c"
|
||||
|
||||
#define LZ_PLT
|
||||
#define PLT1_BE
|
||||
#define TO_RGB32
|
||||
#include "glz_decode_tmpl.c"
|
||||
|
||||
#define LZ_PLT
|
||||
#define PLT1_LE
|
||||
#define TO_RGB32
|
||||
#include "glz_decode_tmpl.c"
|
||||
|
||||
|
||||
#define LZ_RGB16
|
||||
#include "glz_decode_tmpl.c"
|
||||
#define LZ_RGB16
|
||||
#define TO_RGB32
|
||||
#include "glz_decode_tmpl.c"
|
||||
|
||||
#define LZ_RGB24
|
||||
#include "glz_decode_tmpl.c"
|
||||
|
||||
#define LZ_RGB32
|
||||
#include "glz_decode_tmpl.c"
|
||||
|
||||
#define LZ_RGB_ALPHA
|
||||
#include "glz_decode_tmpl.c"
|
||||
|
||||
#undef LZ_UNEXPECT_CONDITIONAL
|
||||
#undef LZ_EXPECT_CONDITIONAL
|
||||
|
||||
typedef size_t (*decode_function)(GlzDecoderWindow &window, uint8_t* in_buf,
|
||||
uint8_t *out_buf, int size,
|
||||
DecodedImageWinId image_win_id, Palette *plt,
|
||||
GlzDecoderDebug &debug_calls);
|
||||
|
||||
// ordered according to LZ_IMAGE_TYPE
|
||||
const decode_function DECODE_TO_RGB32[] = {
|
||||
NULL,
|
||||
glz_plt1_le_to_rgb32_decode,
|
||||
glz_plt1_be_to_rgb32_decode,
|
||||
glz_plt4_le_to_rgb32_decode,
|
||||
glz_plt4_be_to_rgb32_decode,
|
||||
glz_plt8_to_rgb32_decode,
|
||||
glz_rgb16_to_rgb32_decode,
|
||||
glz_rgb32_decode,
|
||||
glz_rgb32_decode,
|
||||
glz_rgb32_decode
|
||||
};
|
||||
|
||||
const decode_function DECODE_TO_SAME[] = {
|
||||
NULL,
|
||||
glz_plt_decode,
|
||||
glz_plt_decode,
|
||||
glz_plt_decode,
|
||||
glz_plt_decode,
|
||||
glz_plt_decode,
|
||||
glz_rgb16_decode,
|
||||
glz_rgb24_decode,
|
||||
glz_rgb32_decode,
|
||||
glz_rgb32_decode
|
||||
};
|
||||
|
||||
void GlzDecoder::decode(uint8_t *data, Palette *palette, void *opaque_usr_info)
|
||||
{
|
||||
int out_size;
|
||||
DecodedImageWinId image_window_id;
|
||||
GlzDecodedImage *decoded_image;
|
||||
size_t n_in_bytes_decoded;
|
||||
int bytes_per_pixel;
|
||||
LzImageType decoded_type;
|
||||
|
||||
_in_start = data;
|
||||
_in_now = data;
|
||||
|
||||
decode_header();
|
||||
|
||||
#ifdef GLZ_DECODE_TO_RGB32
|
||||
out_size = _image.gross_pixels << 2;
|
||||
bytes_per_pixel = 4;
|
||||
|
||||
if (_image.type == LZ_IMAGE_TYPE_RGBA) {
|
||||
decoded_type = LZ_IMAGE_TYPE_RGBA;
|
||||
} else {
|
||||
decoded_type = LZ_IMAGE_TYPE_RGB32;
|
||||
}
|
||||
|
||||
#else
|
||||
if (IS_IMAGE_TYPE_PLT[_image.type]) {
|
||||
GLZ_ASSERT(_debug_calls, !(_image.gross_pixels % PLT_PIXELS_PER_BYTE[_image.type]));
|
||||
out_size = _image.gross_pixels / PLT_PIXELS_PER_BYTE[_image.type];
|
||||
} else {
|
||||
out_size = _image.gross_pixels * RGB_BYTES_PER_PIXEL[_image.type];
|
||||
}
|
||||
bytes_per_pixel = RGB_BYTES_PER_PIXEL[_image.type];
|
||||
decoded_type = _image.type;
|
||||
#endif
|
||||
|
||||
|
||||
image_window_id = _images_window.pre_decode(_image.id, _image.id - _image.win_head_dist);
|
||||
|
||||
decoded_image = _usr_handler.alloc_image(opaque_usr_info, _image.id,
|
||||
_image.id - _image.win_head_dist,
|
||||
decoded_type, _image.width, _image.height,
|
||||
_image.gross_pixels, bytes_per_pixel,
|
||||
_image.top_down);
|
||||
|
||||
_image.data = decoded_image->get_data();
|
||||
|
||||
// decode_by_type
|
||||
#ifdef GLZ_DECODE_TO_RGB32
|
||||
n_in_bytes_decoded = DECODE_TO_RGB32[_image.type](_images_window, _in_now, _image.data,
|
||||
_image.gross_pixels, image_window_id,
|
||||
palette, _debug_calls);
|
||||
#else
|
||||
n_in_bytes_decoded = DECODE_TO_SAME[_image.type](_images_window, _in_now, _image.data,
|
||||
IS_IMAGE_TYPE_PLT[_image.type] ?
|
||||
_image.gross_pixels /
|
||||
PLT_PIXELS_PER_BYTE[_image.type] :
|
||||
_image.gross_pixels,
|
||||
image_window_id, palette, _debug_calls);
|
||||
#endif
|
||||
|
||||
_in_now += n_in_bytes_decoded;
|
||||
|
||||
if (_image.type == LZ_IMAGE_TYPE_RGBA) {
|
||||
glz_rgb_alpha_decode(_images_window, _in_now, _image.data,
|
||||
_image.gross_pixels, image_window_id, palette, _debug_calls);
|
||||
}
|
||||
|
||||
_images_window.post_decode(decoded_image);
|
||||
}
|
||||
|
||||
86
client/glz_decoder.h
Normal file
86
client/glz_decoder.h
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_GLZ_DECODER
|
||||
#define _H_GLZ_DECODER
|
||||
|
||||
#include "lz_common.h"
|
||||
#include "glz_decoder_config.h"
|
||||
#include "glz_decoder_window.h"
|
||||
#include "draw.h"
|
||||
|
||||
|
||||
|
||||
class GlzDecodeHandler {
|
||||
public:
|
||||
GlzDecodeHandler() {}
|
||||
virtual ~GlzDecodeHandler() {}
|
||||
|
||||
/* Called by the decoder before the image decoding is starts. The
|
||||
user of the decoder should create GlzDecodedImage instance.
|
||||
If resources should be released when the image exits the Glz window,
|
||||
it should be handled in the instance dtor.
|
||||
|
||||
opaque_usr_info: the data sent when GlzDecoder::decode was called
|
||||
gross_pixels : number of pixels when considering the whole stride*/
|
||||
virtual GlzDecodedImage *alloc_image(void *opaque_usr_info, uint64_t image_id,
|
||||
uint64_t image_win_head_id, LzImageType type,
|
||||
int width, int height, int gross_pixels,
|
||||
int n_bytes_per_pixel, bool top_down) = 0;
|
||||
};
|
||||
|
||||
/*
|
||||
This class implements the lz decoding algorithm
|
||||
*/
|
||||
|
||||
class GlzDecoder {
|
||||
public:
|
||||
|
||||
GlzDecoder(GlzDecoderWindow &images_window, GlzDecodeHandler &usr_handler,
|
||||
GlzDecoderDebug &debug_calls);
|
||||
virtual ~GlzDecoder();
|
||||
|
||||
/* Decodes the data and afterwards calls GlzDecodeHandler::handle_decoded_image */
|
||||
void decode(uint8_t *data, Palette *palette, void *opaque_usr_info);
|
||||
|
||||
private:
|
||||
void decode_header();
|
||||
uint32_t decode_32();
|
||||
uint64_t decode_64();
|
||||
|
||||
private:
|
||||
GlzDecoderWindow &_images_window;
|
||||
GlzDecodeHandler &_usr_handler;
|
||||
GlzDecoderDebug &_debug_calls;
|
||||
|
||||
uint8_t *_in_now;
|
||||
uint8_t *_in_start;
|
||||
|
||||
struct {
|
||||
uint64_t id;
|
||||
LzImageType type;
|
||||
int width;
|
||||
int height;
|
||||
int gross_pixels;
|
||||
bool top_down;
|
||||
int win_head_dist;
|
||||
uint8_t *data;
|
||||
} _image;
|
||||
};
|
||||
|
||||
#endif // _H_GLZ_DECODER
|
||||
|
||||
85
client/glz_decoder_config.h
Normal file
85
client/glz_decoder_config.h
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_GLZ_DECODER_CONFIG
|
||||
#define _H_GLZ_DECODER_CONFIG
|
||||
|
||||
#include <exception>
|
||||
#include <sstream>
|
||||
|
||||
#include "lz_common.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __GNUC__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#else
|
||||
|
||||
#include <stddef.h>
|
||||
#include <basetsd.h>
|
||||
|
||||
|
||||
typedef UINT64 uint64_t;
|
||||
typedef UINT32 uint32_t;
|
||||
typedef UINT16 uint16_t;
|
||||
typedef UINT8 uint8_t;
|
||||
|
||||
#endif //__GNUC__
|
||||
|
||||
#define MIN(x, y) (((x) <= (y)) ? (x) : (y))
|
||||
#define MAX(x, y) (((x) >= (y)) ? (x) : (y))
|
||||
|
||||
class GlzException: public std::exception {
|
||||
public:
|
||||
GlzException(const std::string& str) : _mess (str) {}
|
||||
virtual ~GlzException() throw () {}
|
||||
virtual const char* what() const throw () {return _mess.c_str();}
|
||||
|
||||
private:
|
||||
std::string _mess;
|
||||
};
|
||||
|
||||
class GlzDecoderDebug {
|
||||
public:
|
||||
virtual ~GlzDecoderDebug() {}
|
||||
virtual void error(const std::string& str) = 0;
|
||||
virtual void warn(const std::string& str) = 0;
|
||||
virtual void info(const std::string& str) = 0;
|
||||
};
|
||||
|
||||
#ifdef RED_DEBUG
|
||||
|
||||
#define GLZ_ASSERT(debug, x) { \
|
||||
if (!(x)) { \
|
||||
std::ostringstream os; \
|
||||
os << __FUNCTION__ << ": ASSERT " << #x << " failed\n"; \
|
||||
(debug).error(os.str()); \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
|
||||
#define GLZ_ASSERT(debug, x)
|
||||
|
||||
#endif
|
||||
|
||||
#define GLZ_DECODE_TO_RGB32
|
||||
|
||||
#endif //_H_GLZ_DECODER_CONFIG
|
||||
|
||||
358
client/glz_decoder_window.cpp
Normal file
358
client/glz_decoder_window.cpp
Normal file
@ -0,0 +1,358 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "glz_decoder_window.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define INIT_IMAGES_CAPACITY 100
|
||||
#define WIN_OVERFLOW_FACTOR 1.5
|
||||
#define WIN_REALLOC_FACTOR 1.5
|
||||
|
||||
GlzDecoderWindow::GlzDecoderWindow(int pixels_capacity, GlzDecoderDebug &debug_calls)
|
||||
: _pixels_capacity (pixels_capacity)
|
||||
, _aborting (false)
|
||||
, _debug_calls (debug_calls)
|
||||
{
|
||||
if (_pixels_capacity > LZ_MAX_WINDOW_SIZE) {
|
||||
std::string erro_str;
|
||||
string_printf(erro_str, "Glz Window capacity exceeds the limit %d",
|
||||
_pixels_capacity);
|
||||
_debug_calls.error(erro_str);
|
||||
}
|
||||
|
||||
_images_capacity = INIT_IMAGES_CAPACITY;
|
||||
_images = new GlzDecodedImage*[_images_capacity];
|
||||
if (!_images) {
|
||||
_debug_calls.error(std::string("failed allocating images\n"));
|
||||
}
|
||||
|
||||
memset(_images, 0, sizeof(GlzDecodedImage*) * _images_capacity);
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
GlzDecoderWindow::~GlzDecoderWindow()
|
||||
{
|
||||
clear();
|
||||
delete _images;
|
||||
}
|
||||
|
||||
DecodedImageWinId GlzDecoderWindow::pre_decode(uint64_t image_id, uint64_t relative_head_id)
|
||||
{
|
||||
DecodedImageWinId image_win_id = pre_decode_update_window(image_id, relative_head_id);
|
||||
pre_decode_finalize();
|
||||
return image_win_id;
|
||||
}
|
||||
|
||||
void GlzDecoderWindow::post_decode(GlzDecodedImage *image)
|
||||
{
|
||||
post_decode_intialize();
|
||||
post_decode_update_window(image);
|
||||
}
|
||||
|
||||
/* index: the physical index in the images array. Note that it can't change between waits since
|
||||
the realloc mutex should be read locked.
|
||||
No starvation for the realloc mutex can occure, since the image we wait for is located before us,
|
||||
hence, when it arrives - no realloc is needed. */
|
||||
void GlzDecoderWindow::wait_for_image(int index)
|
||||
{
|
||||
Lock lock(_new_image_mutex);
|
||||
GlzDecodedImage *image = _images[index]; // can be performed without locking the _win_mutex,
|
||||
// since it is called after pre and the rw mutex is // locked, hence, physical chnages to the window are
|
||||
// not allowed. In addtion the reading of the image ptr
|
||||
// is atomic, thus, even if the value changes we are
|
||||
// not affected.
|
||||
|
||||
while (!image) {
|
||||
if (_aborting) {
|
||||
THROW("aborting");
|
||||
}
|
||||
_new_image_cond.wait(lock);
|
||||
image = _images[index];
|
||||
}
|
||||
}
|
||||
|
||||
void GlzDecoderWindow::abort()
|
||||
{
|
||||
Lock lock1(_win_modifiers_mutex);
|
||||
Lock lock2(_new_image_mutex);
|
||||
_aborting = true;
|
||||
_new_image_cond.notify_all();
|
||||
_release_image_cond.notify_all();
|
||||
_win_alloc_cond.notify_all();
|
||||
}
|
||||
|
||||
void GlzDecoderWindow::clear()
|
||||
{
|
||||
Lock lock(_win_modifiers_mutex);
|
||||
release_images();
|
||||
init();
|
||||
}
|
||||
|
||||
void GlzDecoderWindow::set_pixels_capacity(int pixels_capacity)
|
||||
{
|
||||
Lock lock(_win_modifiers_mutex);
|
||||
|
||||
if (pixels_capacity > LZ_MAX_WINDOW_SIZE) {
|
||||
std::string erro_str;
|
||||
string_printf(erro_str, "Glz Window capacity exceeds the limit %d",
|
||||
pixels_capacity);
|
||||
_debug_calls.error(erro_str);
|
||||
}
|
||||
_pixels_capacity = pixels_capacity;
|
||||
}
|
||||
|
||||
void GlzDecoderWindow::init()
|
||||
{
|
||||
_missing_list.clear();
|
||||
// The window is never empty: the head is in the missing list or in the window.
|
||||
// The first missing image is 0.
|
||||
_missing_list.push_front(0);
|
||||
_head_idx = 0;
|
||||
_tail_image_id = 0;
|
||||
_n_images = 1;
|
||||
_n_pixels = 0;
|
||||
}
|
||||
|
||||
void GlzDecoderWindow::release_images()
|
||||
{
|
||||
for (int i = 0; i < _n_images; i++) {
|
||||
int idx = (_head_idx + i) % _images_capacity;
|
||||
if (_images[idx]) {
|
||||
delete _images[idx];
|
||||
_images[idx] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline bool GlzDecoderWindow::is_empty()
|
||||
{
|
||||
return (!_n_images);
|
||||
}
|
||||
|
||||
/* aprroximated overflow. Considers only the size that currently occupies the window and
|
||||
not the size of the missing images. TODO: consider other measures */
|
||||
inline bool GlzDecoderWindow::will_overflow(uint64_t image_id, uint64_t relative_head_id)
|
||||
{
|
||||
if (image_id <= _tail_image_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_n_pixels > (WIN_OVERFLOW_FACTOR * _pixels_capacity)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_missing_list.empty() && (_missing_list.front() < relative_head_id)) {
|
||||
// two non overlapping windows
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
DecodedImageWinId GlzDecoderWindow::pre_decode_update_window(uint64_t image_id,
|
||||
uint64_t relative_head_id)
|
||||
{
|
||||
Lock lock(_win_modifiers_mutex);
|
||||
int realloc_size;
|
||||
|
||||
while (will_overflow(image_id, relative_head_id)) {
|
||||
if (_aborting) {
|
||||
THROW("aborting");
|
||||
}
|
||||
_release_image_cond.wait(lock);
|
||||
}
|
||||
|
||||
// The following conditions prevent starvation in case thread (1) should realloc,
|
||||
// thread (2) is during decode and waits for a previous image and
|
||||
/// thread (3) should decode this the previous image and needs to enter pre decode although
|
||||
// (1) is in there.
|
||||
// We must give priority to older images in the window.
|
||||
// The condition should be checked over again in case a later image entered the window
|
||||
// and the realocation was already performed
|
||||
while ((realloc_size = calc_realloc_size(image_id))) {
|
||||
if (_aborting) {
|
||||
THROW("aborting");
|
||||
}
|
||||
|
||||
if (_win_alloc_rw_mutex.try_write_lock()) {
|
||||
realloc(realloc_size);
|
||||
_win_alloc_rw_mutex.write_unlock();
|
||||
break;
|
||||
} else {
|
||||
_win_alloc_cond.wait(lock);
|
||||
}
|
||||
}
|
||||
|
||||
if (image_id > _tail_image_id) { // not in missing list
|
||||
add_pre_decoded_image(image_id);
|
||||
}
|
||||
|
||||
return calc_image_win_idx(image_id);
|
||||
}
|
||||
|
||||
void GlzDecoderWindow::add_pre_decoded_image(uint64_t image_id)
|
||||
{
|
||||
GLZ_ASSERT(_debug_calls, image_id > _tail_image_id);
|
||||
GLZ_ASSERT(_debug_calls, image_id - _tail_image_id + _n_images < _images_capacity);
|
||||
|
||||
for (uint64_t miss_id = _tail_image_id + 1; miss_id <= image_id; miss_id++) {
|
||||
_missing_list.push_back(miss_id);
|
||||
_n_images++;
|
||||
}
|
||||
_tail_image_id = image_id;
|
||||
}
|
||||
|
||||
inline int GlzDecoderWindow::calc_realloc_size(uint64_t new_tail_image_id)
|
||||
{
|
||||
if (new_tail_image_id <= _tail_image_id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int min_capacity = (int)(_n_images + new_tail_image_id - _tail_image_id);
|
||||
|
||||
if ((min_capacity * WIN_REALLOC_FACTOR) > _images_capacity) {
|
||||
return (int)MAX(min_capacity * WIN_REALLOC_FACTOR, WIN_REALLOC_FACTOR * _images_capacity);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void GlzDecoderWindow::realloc(int size)
|
||||
{
|
||||
GlzDecodedImage **new_images = new GlzDecodedImage*[size];
|
||||
|
||||
if (!new_images) {
|
||||
_debug_calls.error(std::string("failed allocating images array"));
|
||||
}
|
||||
memset(new_images, 0, sizeof(GlzDecodedImage*) * size);
|
||||
|
||||
for (int i = 0; i < _n_images; i++) {
|
||||
new_images[i] = _images[(i + _head_idx) % _images_capacity];
|
||||
}
|
||||
delete _images;
|
||||
|
||||
_images = new_images;
|
||||
_head_idx = 0;
|
||||
_images_capacity = size;
|
||||
}
|
||||
|
||||
void GlzDecoderWindow::pre_decode_finalize()
|
||||
{
|
||||
_win_alloc_rw_mutex.read_lock();
|
||||
}
|
||||
|
||||
void GlzDecoderWindow::post_decode_intialize()
|
||||
{
|
||||
_win_alloc_rw_mutex.read_unlock();
|
||||
}
|
||||
|
||||
void GlzDecoderWindow::post_decode_update_window(GlzDecodedImage *image)
|
||||
{
|
||||
Lock lock(_win_modifiers_mutex);
|
||||
add_decoded_image(image);
|
||||
narrow_window(image);
|
||||
_win_alloc_cond.notify_all();
|
||||
}
|
||||
|
||||
void GlzDecoderWindow::add_decoded_image(GlzDecodedImage *image)
|
||||
{
|
||||
Lock lock(_new_image_mutex);
|
||||
GLZ_ASSERT(_debug_calls, image->get_id() <= _tail_image_id);
|
||||
_images[calc_image_win_idx(image->get_id())] = image;
|
||||
_n_pixels += image->get_size();
|
||||
_new_image_cond.notify_all();
|
||||
}
|
||||
|
||||
/* Important observations:
|
||||
1) When an image is added, if it is not the first missing image, it is not removed
|
||||
immediately from the missing list (in order not to store another pointer to
|
||||
the missing list inside the window ,or otherwise, to go over the missing list
|
||||
and look for the image).
|
||||
2) images that weren't removed from the missing list in their addition time, will be removed when
|
||||
preliminary images will be added.
|
||||
3) The first item in the missing list is always really missing.
|
||||
4) The missing list is always ordered by image id (see add_pre_decoded_image)
|
||||
*/
|
||||
void GlzDecoderWindow::narrow_window(GlzDecodedImage *last_added)
|
||||
{
|
||||
uint64_t new_head_image_id;
|
||||
|
||||
GLZ_ASSERT(_debug_calls, !_missing_list.empty());
|
||||
|
||||
if (_missing_list.front() != last_added->get_id()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_missing_list.pop_front(); // removing the last added image from the missing list
|
||||
|
||||
/* maintaing the missing list: removing front images that already arrived */
|
||||
while (!_missing_list.empty()) {
|
||||
int front_win_idx = calc_image_win_idx(_missing_list.front());
|
||||
if (_images[front_win_idx] == NULL) { // still missing
|
||||
break;
|
||||
} else {
|
||||
_missing_list.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
/* removing unnecessary image from the window's head*/
|
||||
if (_missing_list.empty()) {
|
||||
new_head_image_id = _images[
|
||||
calc_image_win_idx(_tail_image_id)]->get_window_head_id();
|
||||
} else {
|
||||
// there must be at least one image in the window since narrow_window is called
|
||||
// from post decode
|
||||
GLZ_ASSERT(_debug_calls, _images[calc_image_win_idx(_missing_list.front() - 1)]);
|
||||
new_head_image_id = _images[
|
||||
calc_image_win_idx(_missing_list.front() - 1)]->get_window_head_id();
|
||||
}
|
||||
|
||||
remove_head(new_head_image_id);
|
||||
}
|
||||
|
||||
inline void GlzDecoderWindow::remove_head(uint64_t new_head_image_id)
|
||||
{
|
||||
GLZ_ASSERT(_debug_calls, _images[_head_idx]);
|
||||
|
||||
int n_images_remove = (int)(new_head_image_id - _images[_head_idx]->get_id());
|
||||
|
||||
if (!n_images_remove) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < n_images_remove; i++) {
|
||||
int index = (_head_idx + i) % _images_capacity;
|
||||
_n_pixels -= _images[index]->get_size();
|
||||
delete _images[index];
|
||||
_images[index] = NULL;
|
||||
}
|
||||
|
||||
_n_images -= n_images_remove;
|
||||
_head_idx = (_head_idx + n_images_remove) % _images_capacity;
|
||||
|
||||
_release_image_cond.notify_all();
|
||||
}
|
||||
|
||||
/* NOTE: can only be used when the window is locked. (i.e, not inside get_ref_pixel) */
|
||||
inline int GlzDecoderWindow::calc_image_win_idx(uint64_t image_id)
|
||||
{
|
||||
return (int)((_head_idx + _n_images - 1 - (_tail_image_id - image_id)) % _images_capacity);
|
||||
}
|
||||
|
||||
127
client/glz_decoder_window.h
Normal file
127
client/glz_decoder_window.h
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_GLZ_DECODER_WINDOW
|
||||
#define _H_GLZ_DECODER_WINDOW
|
||||
|
||||
#include "glz_decoder_config.h"
|
||||
#include "glz_decoded_image.h"
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "read_write_mutex.h"
|
||||
|
||||
typedef int DecodedImageWinId;
|
||||
|
||||
/*
|
||||
The class represents the lz window of images, which is shared among the glz decoders
|
||||
*/
|
||||
|
||||
class GlzDecoderWindow {
|
||||
public:
|
||||
GlzDecoderWindow(int pixels_capacity, GlzDecoderDebug &debug_calls);
|
||||
virtual ~GlzDecoderWindow();
|
||||
|
||||
DecodedImageWinId pre_decode(uint64_t image_id, uint64_t relative_head_id);
|
||||
|
||||
void post_decode(GlzDecodedImage *image);
|
||||
|
||||
/* NOTE - get_ref_pixel should be called only after pre_decode was called and
|
||||
before post decode was called */
|
||||
uint8_t *get_ref_pixel(DecodedImageWinId decoded_image_win_id, int dist_from_ref_image,
|
||||
int pixel_offset);
|
||||
|
||||
void abort();
|
||||
|
||||
/* NOTE - clear mustn't be called if the window is currently used by a decoder*/
|
||||
void clear();
|
||||
|
||||
void set_pixels_capacity(int pixels_capacity);
|
||||
|
||||
private:
|
||||
void wait_for_image(int index);
|
||||
void add_image(GlzDecodedImage *image);
|
||||
uint8_t* get_pixel_after_image_entered(int image_index, int pixel_offset);
|
||||
|
||||
bool will_overflow(uint64_t image_id, uint64_t relative_head_id);
|
||||
bool is_empty();
|
||||
|
||||
DecodedImageWinId pre_decode_update_window(uint64_t image_id, uint64_t relative_head_id);
|
||||
void pre_decode_finalize();
|
||||
void post_decode_intialize();
|
||||
void post_decode_update_window(GlzDecodedImage *image);
|
||||
void add_pre_decoded_image(uint64_t image_id);
|
||||
void add_decoded_image(GlzDecodedImage *image);
|
||||
void narrow_window(GlzDecodedImage *last_added);
|
||||
void remove_head(uint64_t new_head_image_id);
|
||||
int calc_image_win_idx(uint64_t image_id);
|
||||
int calc_realloc_size(uint64_t new_tail_image_id);
|
||||
void realloc(int size);
|
||||
void init();
|
||||
void release_images();
|
||||
|
||||
private:
|
||||
int _pixels_capacity;
|
||||
GlzDecodedImage **_images; // cyclic window
|
||||
int _head_idx; // index in images array (not image id)
|
||||
uint64_t _tail_image_id;
|
||||
int _images_capacity;
|
||||
int _n_images; // _n_images counts all the images in
|
||||
// the window, including the missing ones
|
||||
uint64_t _n_pixels;
|
||||
|
||||
std::list<uint64_t> _missing_list;
|
||||
|
||||
Mutex _win_modifiers_mutex;
|
||||
ReadWriteMutex _win_alloc_rw_mutex;
|
||||
Mutex _new_image_mutex;
|
||||
|
||||
Condition _new_image_cond; // when get_pixel_ref waits for an image
|
||||
Condition _release_image_cond; // when waiting for the window to narrow.
|
||||
Condition _win_alloc_cond;
|
||||
|
||||
bool _aborting;
|
||||
|
||||
GlzDecoderDebug &_debug_calls;
|
||||
};
|
||||
|
||||
inline uint8_t* GlzDecoderWindow::get_pixel_after_image_entered(int image_index,
|
||||
int pixel_offset)
|
||||
{
|
||||
return _images[image_index]->get_pixel_ref(pixel_offset);
|
||||
}
|
||||
|
||||
/* should be called only between pre and post (when realloc mutex is write-locked).
|
||||
Note that it can't use calc_image_win_idx, since the window is not locked for changes
|
||||
(that are not reallocation) during decoding.*/
|
||||
inline uint8_t *GlzDecoderWindow::get_ref_pixel(DecodedImageWinId decoded_image_win_id,
|
||||
int dist_from_ref_image, int pixel_offset)
|
||||
{
|
||||
int ref_image_index = (dist_from_ref_image <= decoded_image_win_id) ?
|
||||
(decoded_image_win_id - dist_from_ref_image) :
|
||||
_images_capacity + (decoded_image_win_id - dist_from_ref_image);
|
||||
|
||||
if (_images[ref_image_index] == NULL) { // reading image is atomic
|
||||
wait_for_image(ref_image_index);
|
||||
}
|
||||
|
||||
// after image entered - it won't leave the window till no decoder needs it
|
||||
return get_pixel_after_image_entered(ref_image_index, pixel_offset);
|
||||
}
|
||||
|
||||
#endif // _H_GLZ_DECODER_WINDOW
|
||||
|
||||
147
client/hot_keys.cpp
Normal file
147
client/hot_keys.cpp
Normal file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "hot_keys.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
|
||||
HotKeysParser::HotKeysParser(const std::string& hotkeys, const CommandsMap& commands_map)
|
||||
{
|
||||
DBG(0, "hotkeys = %s", hotkeys.c_str());
|
||||
|
||||
std::istringstream is(hotkeys);
|
||||
std::string hotkey;
|
||||
while (std::getline(is, hotkey, ',')) {
|
||||
add_hotkey(hotkey, commands_map);
|
||||
}
|
||||
}
|
||||
|
||||
void HotKeysParser::parse_keys(int command_id, const std::string& hotkey)
|
||||
{
|
||||
HotkeySet& keys = _hot_keys[command_id];
|
||||
std::istringstream is(hotkey);
|
||||
std::string key;
|
||||
while (std::getline(is, key, '+')) {
|
||||
add_key(keys, key.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void HotKeysParser::add_key(HotkeySet& keys, const char* key)
|
||||
{
|
||||
ASSERT(key != NULL);
|
||||
|
||||
static const struct {
|
||||
const char* name;
|
||||
RedKey main;
|
||||
RedKey alter;
|
||||
} keyboard[] = {
|
||||
{ "alt", REDKEY_R_ALT, REDKEY_L_ALT },
|
||||
{ "ralt", REDKEY_R_ALT, REDKEY_INVALID },
|
||||
{ "rightalt", REDKEY_R_ALT, REDKEY_INVALID },
|
||||
{ "right-alt", REDKEY_R_ALT, REDKEY_INVALID },
|
||||
{ "lalt", REDKEY_L_ALT, REDKEY_INVALID },
|
||||
{ "leftalt", REDKEY_L_ALT, REDKEY_INVALID },
|
||||
{ "left-alt", REDKEY_L_ALT, REDKEY_INVALID },
|
||||
{ "ctrl", REDKEY_R_CTRL, REDKEY_L_CTRL },
|
||||
{ "rctrl", REDKEY_R_CTRL, REDKEY_INVALID },
|
||||
{ "rightctrl", REDKEY_R_CTRL, REDKEY_INVALID },
|
||||
{ "right-ctrl", REDKEY_R_CTRL, REDKEY_INVALID },
|
||||
{ "lctrl", REDKEY_L_CTRL, REDKEY_INVALID },
|
||||
{ "leftctrl", REDKEY_L_CTRL, REDKEY_INVALID },
|
||||
{ "left-ctrl", REDKEY_L_CTRL, REDKEY_INVALID },
|
||||
{ "shift", REDKEY_R_SHIFT, REDKEY_L_SHIFT },
|
||||
{ "rshift", REDKEY_R_SHIFT, REDKEY_INVALID },
|
||||
{ "rightshift", REDKEY_R_SHIFT, REDKEY_INVALID },
|
||||
{ "right-shift", REDKEY_R_SHIFT, REDKEY_INVALID },
|
||||
{ "lshift", REDKEY_L_SHIFT, REDKEY_INVALID },
|
||||
{ "leftshift", REDKEY_L_SHIFT, REDKEY_INVALID },
|
||||
{ "left-shift", REDKEY_L_SHIFT, REDKEY_INVALID },
|
||||
{ "cmd", REDKEY_RIGHT_CMD, REDKEY_LEFT_CMD },
|
||||
{ "rcmd", REDKEY_RIGHT_CMD, REDKEY_INVALID },
|
||||
{ "rightcmd", REDKEY_RIGHT_CMD, REDKEY_INVALID },
|
||||
{ "right-cmd", REDKEY_RIGHT_CMD, REDKEY_INVALID },
|
||||
{ "lcmd", REDKEY_LEFT_CMD, REDKEY_INVALID },
|
||||
{ "leftcmd", REDKEY_LEFT_CMD, REDKEY_INVALID },
|
||||
{ "left-cmd", REDKEY_LEFT_CMD, REDKEY_INVALID },
|
||||
{ "win", REDKEY_RIGHT_CMD, REDKEY_LEFT_CMD },
|
||||
{ "rwin", REDKEY_RIGHT_CMD, REDKEY_INVALID },
|
||||
{ "rightwin", REDKEY_RIGHT_CMD, REDKEY_INVALID },
|
||||
{ "right-win", REDKEY_RIGHT_CMD, REDKEY_INVALID },
|
||||
{ "lwin", REDKEY_LEFT_CMD, REDKEY_INVALID },
|
||||
{ "leftwin", REDKEY_LEFT_CMD, REDKEY_INVALID },
|
||||
{ "left-win", REDKEY_LEFT_CMD, REDKEY_INVALID },
|
||||
{ "esc", REDKEY_ESCAPE, REDKEY_INVALID },
|
||||
{ "escape", REDKEY_ESCAPE, REDKEY_INVALID },
|
||||
{ "ins", REDKEY_INSERT, REDKEY_INVALID },
|
||||
{ "insert", REDKEY_INSERT, REDKEY_INVALID },
|
||||
{ "del", REDKEY_DELETE, REDKEY_INVALID },
|
||||
{ "delete", REDKEY_DELETE, REDKEY_INVALID },
|
||||
{ "pgup", REDKEY_PAGEUP, REDKEY_INVALID },
|
||||
{ "pageup", REDKEY_PAGEUP, REDKEY_INVALID },
|
||||
{ "pgdn", REDKEY_PAGEDOWN, REDKEY_INVALID },
|
||||
{ "pagedown", REDKEY_PAGEDOWN, REDKEY_INVALID },
|
||||
{ "home", REDKEY_HOME, REDKEY_INVALID },
|
||||
{ "end", REDKEY_END, REDKEY_INVALID },
|
||||
{ "space", REDKEY_SPACE, REDKEY_INVALID },
|
||||
{ "enter", REDKEY_ENTER, REDKEY_INVALID },
|
||||
{ "tab", REDKEY_TAB, REDKEY_INVALID },
|
||||
{ "f1", REDKEY_F1, REDKEY_INVALID },
|
||||
{ "f2", REDKEY_F2, REDKEY_INVALID },
|
||||
{ "f3", REDKEY_F3, REDKEY_INVALID },
|
||||
{ "f4", REDKEY_F4, REDKEY_INVALID },
|
||||
{ "f5", REDKEY_F5, REDKEY_INVALID },
|
||||
{ "f6", REDKEY_F6, REDKEY_INVALID },
|
||||
{ "f7", REDKEY_F7, REDKEY_INVALID },
|
||||
{ "f8", REDKEY_F8, REDKEY_INVALID },
|
||||
{ "f9", REDKEY_F9, REDKEY_INVALID },
|
||||
{ "f10", REDKEY_F10, REDKEY_INVALID },
|
||||
{ "f11", REDKEY_F11, REDKEY_INVALID },
|
||||
{ "f12", REDKEY_F12, REDKEY_INVALID }
|
||||
};
|
||||
|
||||
for (int i = 0; i < (sizeof(keyboard) / sizeof(keyboard[0])); ++i) {
|
||||
if (strcasecmp(key, keyboard[i].name) == 0) {
|
||||
HotkeyKey hotkey;
|
||||
hotkey.main = keyboard[i].main;
|
||||
hotkey.alter = keyboard[i].alter;
|
||||
DBG(0, "keys = %s", keyboard[i].name);
|
||||
keys.push_back(hotkey);
|
||||
return;
|
||||
}
|
||||
}
|
||||
THROW("unknown key name %s", key);
|
||||
}
|
||||
|
||||
void HotKeysParser::add_hotkey(const std::string& hotkey, const CommandsMap& commands_map)
|
||||
{
|
||||
std::string::size_type key_start = hotkey.find('=', 0);
|
||||
if (key_start == std::string::npos) {
|
||||
THROW("unable to parse hot keys");
|
||||
}
|
||||
std::string command_name = hotkey.substr(0, key_start);
|
||||
|
||||
CommandsMap::const_iterator command = commands_map.find(command_name);
|
||||
|
||||
if (commands_map.find(command_name) == commands_map.end()) {
|
||||
THROW("invalid action bname");
|
||||
}
|
||||
int command_id = commands_map.find(command_name)->second;
|
||||
std::string keys = hotkey.substr(key_start + 1);
|
||||
parse_keys(command_id, keys);
|
||||
}
|
||||
|
||||
49
client/hot_keys.h
Normal file
49
client/hot_keys.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_HOT_KEYS
|
||||
#define _H_HOT_KEYS
|
||||
|
||||
#include "common.h"
|
||||
#include "red_key.h"
|
||||
|
||||
typedef std::map<std::string, int> CommandsMap;
|
||||
|
||||
struct HotkeyKey {
|
||||
RedKey main;
|
||||
RedKey alter;
|
||||
};
|
||||
|
||||
typedef std::vector<HotkeyKey> HotkeySet;
|
||||
typedef std::map<int, HotkeySet> HotKeys;
|
||||
|
||||
class HotKeysParser {
|
||||
public:
|
||||
HotKeysParser(const std::string& hotkeys, const CommandsMap& commands_map);
|
||||
const HotKeys& get() { return _hot_keys;}
|
||||
|
||||
private:
|
||||
void add_hotkey(const std::string& hotkey, const CommandsMap& commands_map);
|
||||
void parse_keys(int command_id, const std::string& hotkey);
|
||||
void add_key(HotkeySet& keys, const char* key);
|
||||
|
||||
private:
|
||||
HotKeys _hot_keys;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
36
client/icon.h
Normal file
36
client/icon.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_ICON
|
||||
#define _H_ICON
|
||||
|
||||
class Icon {
|
||||
public:
|
||||
Icon() : _refs (1) {}
|
||||
|
||||
Icon* ref() { _refs++; return this;}
|
||||
void unref() { if (!--_refs) delete this;}
|
||||
|
||||
protected:
|
||||
virtual ~Icon() {}
|
||||
|
||||
private:
|
||||
int _refs;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
360
client/inputs_channel.cpp
Normal file
360
client/inputs_channel.cpp
Normal file
@ -0,0 +1,360 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "inputs_channel.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
#include "red_client.h"
|
||||
#include "application.h"
|
||||
|
||||
#define SYNC_REMOTH_MODIFIRES
|
||||
|
||||
class SetInputsHandlerEvent: public Event {
|
||||
public:
|
||||
SetInputsHandlerEvent(InputsChannel& channel) : _channel (channel) {}
|
||||
|
||||
virtual void responce(Application& application)
|
||||
{
|
||||
application.set_inputs_handler(_channel);
|
||||
}
|
||||
|
||||
private:
|
||||
InputsChannel& _channel;
|
||||
};
|
||||
|
||||
class KeyModifiersEvent: public Event {
|
||||
public:
|
||||
KeyModifiersEvent(InputsChannel& channel) : _channel (channel) {}
|
||||
|
||||
virtual void responce(Application& application)
|
||||
{
|
||||
Lock lock(_channel._update_modifiers_lock);
|
||||
_channel._active_modifiers_event = false;
|
||||
_channel.set_local_modifiers();
|
||||
}
|
||||
|
||||
private:
|
||||
InputsChannel& _channel;
|
||||
};
|
||||
|
||||
class RemoveInputsHandlerEvent: public SyncEvent {
|
||||
public:
|
||||
RemoveInputsHandlerEvent(InputsChannel& channel) : _channel (channel) {}
|
||||
|
||||
virtual void do_responce(Application& application)
|
||||
{
|
||||
application.remove_inputs_handler(_channel);
|
||||
}
|
||||
|
||||
private:
|
||||
InputsChannel& _channel;
|
||||
};
|
||||
|
||||
class MotionMessage: public RedChannel::OutMessage, public RedPeer::OutMessage {
|
||||
public:
|
||||
MotionMessage(InputsChannel& channel);
|
||||
virtual RedPeer::OutMessage& peer_message();
|
||||
virtual void release();
|
||||
|
||||
private:
|
||||
InputsChannel& _channel;
|
||||
};
|
||||
|
||||
MotionMessage::MotionMessage(InputsChannel& channel)
|
||||
: RedChannel::OutMessage()
|
||||
, RedPeer::OutMessage(REDC_INPUTS_MOUSE_MOTION, sizeof(RedcMouseMotion))
|
||||
, _channel (channel)
|
||||
{
|
||||
}
|
||||
|
||||
void MotionMessage::release()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
RedPeer::OutMessage& MotionMessage::peer_message()
|
||||
{
|
||||
_channel.set_motion_event(*(RedcMouseMotion*)data());
|
||||
return *this;
|
||||
}
|
||||
|
||||
class PositionMessage: public RedChannel::OutMessage, public RedPeer::OutMessage {
|
||||
public:
|
||||
PositionMessage(InputsChannel& channel);
|
||||
virtual RedPeer::OutMessage& peer_message();
|
||||
virtual void release();
|
||||
|
||||
private:
|
||||
InputsChannel& _channel;
|
||||
};
|
||||
|
||||
PositionMessage::PositionMessage(InputsChannel& channel)
|
||||
: RedChannel::OutMessage()
|
||||
, RedPeer::OutMessage(REDC_INPUTS_MOUSE_POSITION, sizeof(RedcMousePosition))
|
||||
, _channel (channel)
|
||||
{
|
||||
}
|
||||
|
||||
void PositionMessage::release()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
RedPeer::OutMessage& PositionMessage::peer_message()
|
||||
{
|
||||
_channel.set_position_event(*(RedcMousePosition*)data());
|
||||
return *this;
|
||||
}
|
||||
|
||||
class InputsMessHandler: public MessageHandlerImp<InputsChannel, RED_INPUTS_MESSAGES_END> {
|
||||
public:
|
||||
InputsMessHandler(InputsChannel& channel)
|
||||
: MessageHandlerImp<InputsChannel, RED_INPUTS_MESSAGES_END>(channel) {}
|
||||
};
|
||||
|
||||
InputsChannel::InputsChannel(RedClient& client, uint32_t id)
|
||||
: RedChannel(client, RED_CHANNEL_INPUTS, id, new InputsMessHandler(*this))
|
||||
, _mouse_buttons_state (0)
|
||||
, _mouse_dx (0)
|
||||
, _mouse_dy (0)
|
||||
, _mouse_x (~0)
|
||||
, _mouse_y (~0)
|
||||
, _display_id (-1)
|
||||
, _active_motion (false)
|
||||
, _motion_count (0)
|
||||
, _active_modifiers_event (false)
|
||||
{
|
||||
InputsMessHandler* handler = static_cast<InputsMessHandler*>(get_message_handler());
|
||||
handler->set_handler(RED_MIGRATE, &InputsChannel::handle_migrate, 0);
|
||||
handler->set_handler(RED_SET_ACK, &InputsChannel::handle_set_ack, sizeof(RedSetAck));
|
||||
handler->set_handler(RED_PING, &InputsChannel::handle_ping, sizeof(RedPing));
|
||||
handler->set_handler(RED_WAIT_FOR_CHANNELS, &InputsChannel::handle_wait_for_channels,
|
||||
sizeof(RedWaitForChannels));
|
||||
handler->set_handler(RED_DISCONNECTING, &InputsChannel::handle_disconnect,
|
||||
sizeof(RedDisconnect));
|
||||
handler->set_handler(RED_NOTIFY, &InputsChannel::handle_notify, sizeof(RedNotify));
|
||||
|
||||
handler->set_handler(RED_INPUTS_INIT, &InputsChannel::handle_init, sizeof(RedInputsInit));
|
||||
handler->set_handler(RED_INPUTS_KEY_MODIFAIERS, &InputsChannel::handle_modifaiers,
|
||||
sizeof(RedKeyModifiers));
|
||||
handler->set_handler(RED_INPUTS_MOUSE_MOTION_ACK, &InputsChannel::handle_motion_ack, 0);
|
||||
}
|
||||
|
||||
InputsChannel::~InputsChannel()
|
||||
{
|
||||
}
|
||||
|
||||
void InputsChannel::on_connect()
|
||||
{
|
||||
_motion_count = _mouse_dx = _mouse_dy = _mouse_buttons_state = _modifiers = 0;
|
||||
_mouse_x = _mouse_y = ~0;
|
||||
_display_id = -1;
|
||||
}
|
||||
|
||||
void InputsChannel::on_disconnect()
|
||||
{
|
||||
AutoRef<RemoveInputsHandlerEvent> remove_handler_event(new RemoveInputsHandlerEvent(*this));
|
||||
get_client().push_event(*remove_handler_event);
|
||||
(*remove_handler_event)->wait();
|
||||
}
|
||||
|
||||
void InputsChannel::handle_init(RedPeer::InMessage* message)
|
||||
{
|
||||
RedInputsInit* init = (RedInputsInit*)message->data();
|
||||
_modifiers = init->keyboard_modifiers;
|
||||
AutoRef<SetInputsHandlerEvent> set_handler_event(new SetInputsHandlerEvent(*this));
|
||||
get_client().push_event(*set_handler_event);
|
||||
}
|
||||
|
||||
void InputsChannel::handle_modifaiers(RedPeer::InMessage* message)
|
||||
{
|
||||
RedKeyModifiers* init = (RedKeyModifiers*)message->data();
|
||||
_modifiers = init->modifiers;
|
||||
Lock lock(_update_modifiers_lock);
|
||||
if (_active_modifiers_event) {
|
||||
return;
|
||||
}
|
||||
_active_modifiers_event = true;
|
||||
AutoRef<KeyModifiersEvent> modifiers_event(new KeyModifiersEvent(*this));
|
||||
get_client().push_event(*modifiers_event);
|
||||
}
|
||||
|
||||
void InputsChannel::handle_motion_ack(RedPeer::InMessage* message)
|
||||
{
|
||||
Lock lock(_motion_lock);
|
||||
if (_motion_count < RED_MOTION_ACK_BUNCH) {
|
||||
LOG_WARN("invalid motion count");
|
||||
_motion_count = 0;
|
||||
} else {
|
||||
_motion_count -= RED_MOTION_ACK_BUNCH;
|
||||
}
|
||||
if (!_active_motion && (_mouse_dx || _mouse_dy || _display_id != -1)) {
|
||||
_active_motion = true;
|
||||
_motion_count++;
|
||||
switch (get_client().get_mouse_mode()) {
|
||||
case RED_MOUSE_MODE_CLIENT:
|
||||
post_message(new PositionMessage(*this));
|
||||
break;
|
||||
case RED_MOUSE_MODE_SERVER:
|
||||
post_message(new MotionMessage(*this));
|
||||
break;
|
||||
default:
|
||||
THROW("invalid mouse mode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputsChannel::set_motion_event(RedcMouseMotion& motion)
|
||||
{
|
||||
Lock lock(_motion_lock);
|
||||
motion.buttons_state = _mouse_buttons_state;
|
||||
motion.dx = _mouse_dx;
|
||||
motion.dy = _mouse_dy;
|
||||
_mouse_dx = _mouse_dy = 0;
|
||||
_active_motion = false;
|
||||
}
|
||||
|
||||
void InputsChannel::set_position_event(RedcMousePosition& position)
|
||||
{
|
||||
Lock lock(_motion_lock);
|
||||
position.buttons_state = _mouse_buttons_state;
|
||||
position.x = _mouse_x;
|
||||
position.y = _mouse_y;
|
||||
position.display_id = _display_id;
|
||||
_mouse_x = _mouse_y = ~0;
|
||||
_display_id = -1;
|
||||
_active_motion = false;
|
||||
}
|
||||
|
||||
void InputsChannel::on_mouse_motion(int dx, int dy, int buttons_state)
|
||||
{
|
||||
Lock lock(_motion_lock);
|
||||
_mouse_buttons_state = buttons_state;
|
||||
_mouse_dx += dx;
|
||||
_mouse_dy += dy;
|
||||
if (!_active_motion && _motion_count < RED_MOTION_ACK_BUNCH * 2) {
|
||||
_active_motion = true;
|
||||
_motion_count++;
|
||||
post_message(new MotionMessage(*this));
|
||||
}
|
||||
}
|
||||
|
||||
void InputsChannel::on_mouse_position(int x, int y, int buttons_state, int display_id)
|
||||
{
|
||||
Lock lock(_motion_lock);
|
||||
_mouse_buttons_state = buttons_state;
|
||||
_mouse_x = x;
|
||||
_mouse_y = y;
|
||||
_display_id = display_id;
|
||||
if (!_active_motion && _motion_count < RED_MOTION_ACK_BUNCH * 2) {
|
||||
_active_motion = true;
|
||||
_motion_count++;
|
||||
post_message(new PositionMessage(*this));
|
||||
}
|
||||
}
|
||||
|
||||
void InputsChannel::on_migrate()
|
||||
{
|
||||
_motion_count = _active_motion ? 1 : 0;
|
||||
}
|
||||
|
||||
void InputsChannel::on_mouse_down(int button, int buttons_state)
|
||||
{
|
||||
Message* message;
|
||||
|
||||
message = new Message(REDC_INPUTS_MOUSE_PRESS, sizeof(RedcMouseRelease));
|
||||
RedcMousePress* event = (RedcMousePress*)message->data();
|
||||
event->button = button;
|
||||
event->buttons_state = buttons_state;
|
||||
post_message(message);
|
||||
}
|
||||
|
||||
void InputsChannel::on_mouse_up(int button, int buttons_state)
|
||||
{
|
||||
Message* message;
|
||||
|
||||
message = new Message(REDC_INPUTS_MOUSE_RELEASE, sizeof(RedcMouseRelease));
|
||||
RedcMouseRelease* event = (RedcMouseRelease*)message->data();
|
||||
event->button = button;
|
||||
event->buttons_state = buttons_state;
|
||||
post_message(message);
|
||||
}
|
||||
|
||||
void InputsChannel::on_key_down(uint32_t scan_code)
|
||||
{
|
||||
Message* message = new Message(REDC_INPUTS_KEY_DOWN, sizeof(RedcKeyDown));
|
||||
RedcKeyDown* event = (RedcKeyDown*)message->data();
|
||||
event->code = scan_code;
|
||||
post_message(message);
|
||||
}
|
||||
|
||||
void InputsChannel::on_key_up(uint32_t scan_code)
|
||||
{
|
||||
Message* message = new Message(REDC_INPUTS_KEY_UP, sizeof(RedcKeyUp));
|
||||
RedcKeyUp* event = (RedcKeyUp*)message->data();
|
||||
event->code = scan_code;
|
||||
post_message(message);
|
||||
}
|
||||
|
||||
void InputsChannel::set_local_modifiers()
|
||||
{
|
||||
unsigned int modifiers = 0;
|
||||
|
||||
if (_modifiers & RED_SCROLL_LOCK_MODIFIER) {
|
||||
modifiers |= Platform::SCROLL_LOCK_MODIFIER;
|
||||
}
|
||||
|
||||
if (_modifiers & RED_NUM_LOCK_MODIFIER) {
|
||||
modifiers |= Platform::NUM_LOCK_MODIFIER;
|
||||
}
|
||||
|
||||
if (_modifiers & RED_CAPS_LOCK_MODIFIER) {
|
||||
modifiers |= Platform::CAPS_LOCK_MODIFIER;
|
||||
}
|
||||
|
||||
Platform::set_keyboard_modifiers(_modifiers);
|
||||
}
|
||||
|
||||
void InputsChannel::on_focus_in()
|
||||
{
|
||||
#ifdef SYNC_REMOTH_MODIFIRES
|
||||
Message* message = new Message(REDC_INPUTS_KEY_MODIFAIERS, sizeof(RedcKeyDown));
|
||||
RedcKeyModifiers* modifiers = (RedcKeyModifiers*)message->data();
|
||||
modifiers->modifiers = Platform::get_keyboard_modifiers();
|
||||
post_message(message);
|
||||
#else
|
||||
set_local_modifiers();
|
||||
#endif
|
||||
}
|
||||
|
||||
class InputsFactory: public ChannelFactory {
|
||||
public:
|
||||
InputsFactory() : ChannelFactory(RED_CHANNEL_INPUTS) {}
|
||||
virtual RedChannel* construct(RedClient& client, uint32_t id)
|
||||
{
|
||||
return new InputsChannel(client, id);
|
||||
}
|
||||
};
|
||||
|
||||
static InputsFactory factory;
|
||||
|
||||
ChannelFactory& InputsChannel::Factory()
|
||||
{
|
||||
return factory;
|
||||
}
|
||||
|
||||
76
client/inputs_channel.h
Normal file
76
client/inputs_channel.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_INPUTS_CHANNEL
|
||||
#define _H_INPUTS_CHANNEL
|
||||
|
||||
#include "red_channel.h"
|
||||
#include "inputs_handler.h"
|
||||
|
||||
class ChannelFactory;
|
||||
|
||||
class InputsChannel: public RedChannel, public InputsHandler {
|
||||
public:
|
||||
InputsChannel(RedClient& client, uint32_t id);
|
||||
virtual ~InputsChannel();
|
||||
|
||||
virtual void on_mouse_motion(int dx, int dy, int buttons_state);
|
||||
virtual void on_mouse_position(int x, int y, int buttons_state, int display_id);
|
||||
virtual void on_mouse_down(int button, int buttons_state);
|
||||
virtual void on_mouse_up(int button, int buttons_state);
|
||||
virtual void on_key_down(uint32_t scan_code);
|
||||
virtual void on_key_up(uint32_t scan_code);
|
||||
virtual void on_focus_in();
|
||||
|
||||
static ChannelFactory& Factory();
|
||||
|
||||
protected:
|
||||
virtual void on_connect();
|
||||
virtual void on_disconnect();
|
||||
virtual void on_migrate();
|
||||
|
||||
private:
|
||||
void set_motion_event(RedcMouseMotion& motion_event);
|
||||
void set_position_event(RedcMousePosition& position_event);
|
||||
void set_local_modifiers();
|
||||
|
||||
void handle_init(RedPeer::InMessage* message);
|
||||
void handle_modifaiers(RedPeer::InMessage* message);
|
||||
void handle_motion_ack(RedPeer::InMessage* message);
|
||||
|
||||
private:
|
||||
Mutex _motion_lock;
|
||||
int _mouse_buttons_state;
|
||||
int _mouse_dx;
|
||||
int _mouse_dy;
|
||||
unsigned int _mouse_x;
|
||||
unsigned int _mouse_y;
|
||||
int _display_id;
|
||||
bool _active_motion;
|
||||
int _motion_count;
|
||||
uint32_t _modifiers;
|
||||
Mutex _update_modifiers_lock;
|
||||
bool _active_modifiers_event;
|
||||
|
||||
friend class MotionMessage;
|
||||
friend class PositionMessage;
|
||||
friend class KeyModifiersEvent;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
36
client/inputs_handler.h
Normal file
36
client/inputs_handler.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_INPUTS_HANDLER
|
||||
#define _H_INPUTS_HANDLER
|
||||
|
||||
|
||||
class InputsHandler {
|
||||
public:
|
||||
virtual ~InputsHandler() {}
|
||||
virtual void on_mouse_motion(int dx, int dy, int buttons_state) {}
|
||||
virtual void on_mouse_position(int x, int y, int buttons_state, int display_id) {}
|
||||
virtual void on_mouse_down(int button, int buttons_state) {}
|
||||
virtual void on_mouse_up(int button, int buttons_state) {}
|
||||
virtual void on_key_down(uint32_t scan_code) {}
|
||||
virtual void on_key_up(uint32_t scan_code) {}
|
||||
virtual void on_focus_in() {}
|
||||
virtual void on_focus_out() {}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
20
client/lz.cpp
Normal file
20
client/lz.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "../common/lz.c"
|
||||
|
||||
100
client/menu.cpp
Normal file
100
client/menu.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "menu.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
Menu::Menu(CommandTarget& target, const std::string& name)
|
||||
: _refs (1)
|
||||
, _target (target)
|
||||
, _name (name)
|
||||
{
|
||||
}
|
||||
|
||||
Menu::~Menu()
|
||||
{
|
||||
for (unsigned int i = 0; i < _items.size(); i++) {
|
||||
if (_items[i].type == MENU_ITEM_TYPE_COMMAND) {
|
||||
delete (MenuCommand*)_items[i].obj;
|
||||
} else if (_items[i].type == MENU_ITEM_TYPE_MENU) {
|
||||
((Menu*)_items[i].obj)->unref();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::add_item(MenuItem& item)
|
||||
{
|
||||
int pos = _items.size();
|
||||
_items.resize(pos + 1);
|
||||
_items[pos] = item;
|
||||
}
|
||||
|
||||
void Menu::add_command(const std::string& name, int cmd_id)
|
||||
{
|
||||
MenuCommand* cmd = new MenuCommand(name, cmd_id);
|
||||
MenuItem item;
|
||||
item.type = MENU_ITEM_TYPE_COMMAND;
|
||||
item.obj = cmd;
|
||||
add_item(item);
|
||||
}
|
||||
|
||||
void Menu::add_separator()
|
||||
{
|
||||
MenuItem item;
|
||||
item.type = MENU_ITEM_TYPE_SEPARATOR;
|
||||
item.obj = NULL;
|
||||
add_item(item);
|
||||
}
|
||||
|
||||
void Menu::add_sub(Menu* menu)
|
||||
{
|
||||
ASSERT(menu);
|
||||
MenuItem item;
|
||||
item.type = MENU_ITEM_TYPE_MENU;
|
||||
item.obj = menu->ref();
|
||||
add_item(item);
|
||||
}
|
||||
|
||||
Menu::ItemType Menu::item_type_at(int pos)
|
||||
{
|
||||
if (pos >= (int)_items.size()) {
|
||||
return MENU_ITEM_TYPE_INVALID;
|
||||
}
|
||||
return _items[pos].type;
|
||||
}
|
||||
|
||||
void Menu::command_at(int pos, std::string& name, int& cmd_id)
|
||||
{
|
||||
if (_items[pos].type != MENU_ITEM_TYPE_COMMAND) {
|
||||
THROW("incorrect item type");
|
||||
}
|
||||
MenuCommand* cmd = (MenuCommand*)_items[pos].obj;
|
||||
name = cmd->get_name();
|
||||
cmd_id = cmd->get_cmd_id();
|
||||
}
|
||||
|
||||
Menu* Menu::sub_at(int pos)
|
||||
{
|
||||
if (_items[pos].type != MENU_ITEM_TYPE_MENU) {
|
||||
THROW("incorrect item type");
|
||||
}
|
||||
return ((Menu*)_items[pos].obj)->ref();
|
||||
}
|
||||
|
||||
86
client/menu.h
Normal file
86
client/menu.h
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_MENU
|
||||
#define _H_MENU
|
||||
|
||||
class CommandTarget {
|
||||
public:
|
||||
virtual void do_command(int command) = 0;
|
||||
virtual ~CommandTarget() {}
|
||||
};
|
||||
|
||||
class Menu {
|
||||
public:
|
||||
Menu(CommandTarget& target, const std::string& name);
|
||||
|
||||
enum ItemType {
|
||||
MENU_ITEM_TYPE_INVALID,
|
||||
MENU_ITEM_TYPE_COMMAND,
|
||||
MENU_ITEM_TYPE_MENU,
|
||||
MENU_ITEM_TYPE_SEPARATOR,
|
||||
};
|
||||
|
||||
Menu* ref() { _refs++; return this;}
|
||||
void unref() { if (!--_refs) delete this;}
|
||||
|
||||
const std::string& get_name() { return _name;}
|
||||
CommandTarget& get_target() { return _target;}
|
||||
|
||||
void add_command(const std::string& name, int cmd_id);
|
||||
void add_separator();
|
||||
void add_sub(Menu* sub);
|
||||
|
||||
ItemType item_type_at(int pos);
|
||||
void command_at(int pos, std::string& name, int& cmd_id);
|
||||
Menu* sub_at(int pos);
|
||||
|
||||
private:
|
||||
virtual ~Menu();
|
||||
|
||||
class MenuCommand {
|
||||
public:
|
||||
MenuCommand(const std::string& name, int cmd_id)
|
||||
: _name (name)
|
||||
, _cmd_id (cmd_id)
|
||||
{
|
||||
}
|
||||
|
||||
const std::string& get_name() { return _name;}
|
||||
int get_cmd_id() { return _cmd_id;}
|
||||
|
||||
private:
|
||||
std::string _name;
|
||||
int _cmd_id;
|
||||
};
|
||||
|
||||
struct MenuItem {
|
||||
ItemType type;
|
||||
void *obj;
|
||||
};
|
||||
|
||||
void add_item(MenuItem& item);
|
||||
|
||||
private:
|
||||
int _refs;
|
||||
CommandTarget& _target;
|
||||
std::string _name;
|
||||
std::vector<MenuItem> _items;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
35
client/monitor.cpp
Normal file
35
client/monitor.cpp
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "monitor.h"
|
||||
#include "debug.h"
|
||||
|
||||
uint32_t Monitor::self_monitors_change = 0;
|
||||
|
||||
|
||||
Monitor::Monitor(int id)
|
||||
: _id (id)
|
||||
, _free (true)
|
||||
{
|
||||
}
|
||||
|
||||
bool Monitor::is_self_change()
|
||||
{
|
||||
return self_monitors_change != 0;
|
||||
}
|
||||
|
||||
56
client/monitor.h
Normal file
56
client/monitor.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_MONITOR
|
||||
#define _H_MONITOR
|
||||
|
||||
#include "draw.h"
|
||||
|
||||
class Monitor {
|
||||
public:
|
||||
Monitor(int id);
|
||||
|
||||
int get_id() { return _id;}
|
||||
bool is_free() { return _free;}
|
||||
void set_free() {_free = true;}
|
||||
void set_used() {_free = false;}
|
||||
|
||||
virtual void set_mode(int width, int height) = 0;
|
||||
virtual void restore() = 0;
|
||||
virtual int get_depth() = 0;
|
||||
virtual Point get_position() = 0;
|
||||
virtual Point get_size() const = 0;
|
||||
virtual bool is_out_of_sync() = 0;
|
||||
virtual int get_screen_id() = 0;
|
||||
|
||||
static bool is_self_change();
|
||||
|
||||
protected:
|
||||
virtual ~Monitor() {}
|
||||
|
||||
private:
|
||||
int _id;
|
||||
bool _free;
|
||||
|
||||
protected:
|
||||
static uint32_t self_monitors_change;
|
||||
|
||||
friend class Platform;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
59
client/pixels_source.h
Normal file
59
client/pixels_source.h
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_PIXELS_SOURCE
|
||||
#define _H_PIXELS_SOURCE
|
||||
|
||||
#include "draw.h"
|
||||
|
||||
#define PIXELES_SOURCE_OPAQUE_SIZE (20 * sizeof(void*))
|
||||
|
||||
class PixelsSource {
|
||||
public:
|
||||
PixelsSource();
|
||||
virtual ~PixelsSource();
|
||||
|
||||
virtual Point get_size() = 0;
|
||||
void set_origin(int x, int y) { _origin.x = x; _origin.y = y;}
|
||||
const Point& get_origin() { return _origin;}
|
||||
|
||||
protected:
|
||||
const uint8_t* get_opaque() const { return _opaque;}
|
||||
|
||||
private:
|
||||
Point _origin;
|
||||
uint8_t _opaque[PIXELES_SOURCE_OPAQUE_SIZE];
|
||||
|
||||
friend class RedDrawable;
|
||||
};
|
||||
|
||||
class ImageFromRes: public PixelsSource {
|
||||
public:
|
||||
ImageFromRes(int res_id);
|
||||
virtual ~ImageFromRes();
|
||||
virtual Point get_size();
|
||||
};
|
||||
|
||||
class AlphaImageFromRes: public PixelsSource {
|
||||
public:
|
||||
AlphaImageFromRes(int res_id);
|
||||
virtual ~AlphaImageFromRes();
|
||||
virtual Point get_size();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
158
client/platform.h
Normal file
158
client/platform.h
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_PLATFORM
|
||||
#define _H_PLATFORM
|
||||
|
||||
#include "cursor.h"
|
||||
#include "events_loop.h"
|
||||
|
||||
#define INVALID_TIMER (~TimerID(0))
|
||||
typedef unsigned long TimerID;
|
||||
typedef void (*timer_proc_t)(void* opaque, TimerID timer);
|
||||
|
||||
class WaveRecordAbstract;
|
||||
class WavePlaybackAbstract;
|
||||
class Icon;
|
||||
|
||||
class Monitor;
|
||||
typedef std::list<Monitor*> MonitorsList;
|
||||
|
||||
class Platform {
|
||||
public:
|
||||
static void init();
|
||||
static void wait_events();
|
||||
static bool process_events();
|
||||
static void wakeup();
|
||||
static void msleep(unsigned int millisec);
|
||||
static void yield();
|
||||
static void send_quit_request();
|
||||
static TimerID create_interval_timer(timer_proc_t proc, void* opaque);
|
||||
static bool activate_interval_timer(TimerID timer, unsigned int millisec);
|
||||
static bool deactivate_interval_timer(TimerID timer);
|
||||
static void destroy_interval_timer(TimerID timer);
|
||||
static uint64_t get_monolithic_time();
|
||||
static void get_temp_dir(std::string& path);
|
||||
|
||||
static const MonitorsList& init_monitors();
|
||||
static void destroy_monitors();
|
||||
static bool is_monitors_pos_valid();
|
||||
|
||||
enum ThreadPriority {
|
||||
PRIORITY_INVALID,
|
||||
PRIORITY_TIME_CRITICAL,
|
||||
PRIORITY_HIGH,
|
||||
PRIORITY_ABOVE_NORMAL,
|
||||
PRIORITY_NORMAL,
|
||||
PRIORITY_BELOW_NORMAL,
|
||||
PRIORITY_LOW,
|
||||
PRIORITY_IDLE,
|
||||
};
|
||||
|
||||
static void set_thread_priority(void *thread, ThreadPriority priority);
|
||||
|
||||
class RecordClinet;
|
||||
static WaveRecordAbstract* create_recorder(RecordClinet& client,
|
||||
uint32_t sampels_per_sec,
|
||||
uint32_t bits_per_sample,
|
||||
uint32_t channels);
|
||||
static WavePlaybackAbstract* create_player(uint32_t sampels_per_sec,
|
||||
uint32_t bits_per_sample,
|
||||
uint32_t channels);
|
||||
|
||||
enum {
|
||||
SCROLL_LOCK_MODIFIER_SHIFT,
|
||||
NUM_LOCK_MODIFIER_SHIFT,
|
||||
CAPS_LOCK_MODIFIER_SHIFT,
|
||||
|
||||
SCROLL_LOCK_MODIFIER = (1 << SCROLL_LOCK_MODIFIER_SHIFT),
|
||||
NUM_LOCK_MODIFIER = (1 << NUM_LOCK_MODIFIER_SHIFT),
|
||||
CAPS_LOCK_MODIFIER = (1 << CAPS_LOCK_MODIFIER_SHIFT),
|
||||
};
|
||||
|
||||
static uint32_t get_keyboard_modifiers();
|
||||
static void set_keyboard_modifiers(uint32_t modifiers);
|
||||
|
||||
static LocalCursor* create_local_cursor(CursorData* cursor_data);
|
||||
static LocalCursor* create_inactive_cursor();
|
||||
static LocalCursor* create_default_cursor();
|
||||
|
||||
static Icon* load_icon(int id);
|
||||
|
||||
class EventListener;
|
||||
static void set_event_listener(EventListener* listener);
|
||||
|
||||
class DisplayModeListner;
|
||||
static void set_display_mode_listner(DisplayModeListner* listener);
|
||||
};
|
||||
|
||||
class Platform::EventListener {
|
||||
public:
|
||||
virtual ~EventListener() {}
|
||||
virtual void on_app_activated() = 0;
|
||||
virtual void on_app_deactivated() = 0;
|
||||
virtual void on_monitors_change() = 0;
|
||||
};
|
||||
|
||||
class Platform::RecordClinet {
|
||||
public:
|
||||
virtual ~RecordClinet() {}
|
||||
virtual void add_evnet_sorce(EventsLoop::File& evnet_sorce) = 0;
|
||||
virtual void remove_evnet_sorce(EventsLoop::File& evnet_sorce) = 0;
|
||||
virtual void add_evnet_sorce(EventsLoop::Trigger& evnet_sorce) = 0;
|
||||
virtual void remove_evnet_sorce(EventsLoop::Trigger& evnet_sorce) = 0;
|
||||
virtual void push_frame(uint8_t *frame) = 0;
|
||||
};
|
||||
|
||||
class Platform::DisplayModeListner {
|
||||
public:
|
||||
virtual ~DisplayModeListner() {}
|
||||
virtual void on_display_mode_change() = 0;
|
||||
};
|
||||
|
||||
class NamedPipe {
|
||||
public:
|
||||
typedef unsigned long ListenerRef;
|
||||
typedef unsigned long ConnectionRef;
|
||||
static const ConnectionRef INVALID_CONNECTION = ~0;
|
||||
|
||||
class ConnectionInterface {
|
||||
public:
|
||||
ConnectionInterface() : _opaque (INVALID_CONNECTION) {}
|
||||
virtual ~ConnectionInterface() {}
|
||||
virtual void bind(ConnectionRef conn_ref) = 0;
|
||||
virtual void on_data() = 0;
|
||||
|
||||
protected:
|
||||
ConnectionRef _opaque;
|
||||
};
|
||||
|
||||
class ListenerInterface {
|
||||
public:
|
||||
virtual ~ListenerInterface() {}
|
||||
virtual ConnectionInterface &create() = 0;
|
||||
};
|
||||
|
||||
static ListenerRef create(const char *name, ListenerInterface& listener_interface);
|
||||
static void destroy(ListenerRef listener_ref);
|
||||
static void destroy_connection(ConnectionRef conn_ref);
|
||||
static int32_t read(ConnectionRef conn_ref, uint8_t *buf, int32_t size);
|
||||
static int32_t write(ConnectionRef conn_ref, const uint8_t *buf, int32_t size);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
355
client/playback_channel.cpp
Normal file
355
client/playback_channel.cpp
Normal file
@ -0,0 +1,355 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "red_client.h"
|
||||
#include "audio_channels.h"
|
||||
#include "audio_devices.h"
|
||||
|
||||
//#define WAVE_CAPTURE
|
||||
#ifdef WAVE_CAPTURE
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#define WAVE_BUF_SIZE (1024 * 1024 * 20)
|
||||
|
||||
typedef struct __attribute__ ((__packed__)) ChunkHeader {
|
||||
uint32_t id;
|
||||
uint32_t size;
|
||||
} ChunkHeader;
|
||||
|
||||
typedef struct __attribute__ ((__packed__)) FormatInfo {
|
||||
uint16_t compression_code;
|
||||
uint16_t num_channels;
|
||||
uint32_t sample_rate;
|
||||
uint32_t average_bytes_per_second;
|
||||
uint16_t block_align;
|
||||
uint16_t bits_per_sample;
|
||||
//uint16_t extra_format_bytes;
|
||||
//uint8_t extra[0];
|
||||
} FormatInfo;
|
||||
|
||||
static uint8_t* wave_buf = NULL;
|
||||
static uint8_t* wave_now = NULL;
|
||||
static uint8_t* wave_end = NULL;
|
||||
static bool wave_blocked = false;
|
||||
|
||||
static void write_all(int fd, uint8_t* data, uint32_t size)
|
||||
{
|
||||
while (size) {
|
||||
int n = write(fd, data, size);
|
||||
if (n == -1) {
|
||||
if (errno != EINTR) {
|
||||
throw Exception(fmt("%s: failed") % __FUNCTION__);
|
||||
}
|
||||
} else {
|
||||
data += n;
|
||||
size -= n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void write_wave()
|
||||
{
|
||||
static uint32_t file_id = 0;
|
||||
char file_name[100];
|
||||
ChunkHeader header;
|
||||
FormatInfo format;
|
||||
|
||||
if (wave_buf == wave_now) {
|
||||
return;
|
||||
}
|
||||
|
||||
sprintf(file_name, "/tmp/%u.wav", ++file_id);
|
||||
int fd = open(file_name, O_CREAT | O_TRUNC | O_WRONLY, 0644);
|
||||
if (fd == -1) {
|
||||
DBG(0, fmt("open file %s failed") % file_name);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy((char *)&header.id, "RIFF", 4);
|
||||
header.size = 4;
|
||||
write_all(fd, (uint8_t *)&header, sizeof(header));
|
||||
write_all(fd, (uint8_t *)"WAVE", 4);
|
||||
|
||||
memcpy((char *)&header.id, "fmt ", 4);
|
||||
header.size = sizeof(format);
|
||||
write_all(fd, (uint8_t *)&header, sizeof(header));
|
||||
|
||||
format.compression_code = 1;
|
||||
format.num_channels = 2;
|
||||
format.sample_rate = 44100;
|
||||
format.average_bytes_per_second = format.sample_rate * 4;
|
||||
format.block_align = 4;
|
||||
format.bits_per_sample = 16;
|
||||
write_all(fd, (uint8_t *)&format, sizeof(format));
|
||||
|
||||
memcpy((char *)&header.id, "data", 4);
|
||||
header.size = wave_now - wave_buf;
|
||||
write_all(fd, (uint8_t *)&header, sizeof(header));
|
||||
write_all(fd, wave_buf, header.size);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void init_wave()
|
||||
{
|
||||
if (!wave_buf) {
|
||||
wave_buf = new uint8_t[WAVE_BUF_SIZE];
|
||||
}
|
||||
wave_now = wave_buf;
|
||||
wave_end = wave_buf + WAVE_BUF_SIZE;
|
||||
}
|
||||
|
||||
static void start_wave()
|
||||
{
|
||||
wave_blocked = false;
|
||||
wave_now = wave_buf;
|
||||
}
|
||||
|
||||
static void put_wave_data(uint8_t *data, uint32_t size)
|
||||
{
|
||||
if (wave_blocked || size > wave_end - wave_now) {
|
||||
wave_blocked = true;
|
||||
return;
|
||||
}
|
||||
memcpy((void *)wave_now, (void *)data, size);
|
||||
wave_now += size;
|
||||
}
|
||||
|
||||
static void end_wave()
|
||||
{
|
||||
write_wave();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
class PlaybackHandler: public MessageHandlerImp<PlaybackChannel, RED_PLAYBACK_MESSAGES_END> {
|
||||
public:
|
||||
PlaybackHandler(PlaybackChannel& channel)
|
||||
: MessageHandlerImp<PlaybackChannel, RED_PLAYBACK_MESSAGES_END>(channel) {}
|
||||
};
|
||||
|
||||
PlaybackChannel::PlaybackChannel(RedClient& client, uint32_t id)
|
||||
: RedChannel(client, RED_CHANNEL_PLAYBACK, id, new PlaybackHandler(*this),
|
||||
Platform::PRIORITY_HIGH)
|
||||
, _wave_player (NULL)
|
||||
, _mode (RED_AUDIO_DATA_MODE_INVALD)
|
||||
, _celt_mode (NULL)
|
||||
, _celt_decoder (NULL)
|
||||
, _playing (false)
|
||||
{
|
||||
#ifdef WAVE_CAPTURE
|
||||
init_wave();
|
||||
#endif
|
||||
PlaybackHandler* handler = static_cast<PlaybackHandler*>(get_message_handler());
|
||||
|
||||
handler->set_handler(RED_MIGRATE, &PlaybackChannel::handle_migrate, 0);
|
||||
handler->set_handler(RED_SET_ACK, &PlaybackChannel::handle_set_ack, sizeof(RedSetAck));
|
||||
handler->set_handler(RED_PING, &PlaybackChannel::handle_ping, sizeof(RedPing));
|
||||
handler->set_handler(RED_WAIT_FOR_CHANNELS, &PlaybackChannel::handle_wait_for_channels,
|
||||
sizeof(RedWaitForChannels));
|
||||
handler->set_handler(RED_DISCONNECTING, &PlaybackChannel::handle_disconnect,
|
||||
sizeof(RedDisconnect));
|
||||
handler->set_handler(RED_NOTIFY, &PlaybackChannel::handle_notify, sizeof(RedNotify));
|
||||
|
||||
handler->set_handler(RED_PLAYBACK_MODE, &PlaybackChannel::handle_mode,
|
||||
sizeof(RedPlaybackMode));
|
||||
|
||||
set_capability(RED_PLAYBACK_CAP_CELT_0_5_1);
|
||||
}
|
||||
|
||||
PlaybackChannel::~PlaybackChannel(void)
|
||||
{
|
||||
delete _wave_player;
|
||||
|
||||
if (_celt_decoder) {
|
||||
celt051_decoder_destroy(_celt_decoder);
|
||||
}
|
||||
|
||||
if (_celt_mode) {
|
||||
celt051_mode_destroy(_celt_mode);
|
||||
}
|
||||
}
|
||||
|
||||
bool PlaybackChannel::abort(void)
|
||||
{
|
||||
return (!_wave_player || _wave_player->abort()) && RedChannel::abort();
|
||||
}
|
||||
|
||||
void PlaybackChannel::set_data_handler()
|
||||
{
|
||||
PlaybackHandler* handler = static_cast<PlaybackHandler*>(get_message_handler());
|
||||
|
||||
if (_mode == RED_AUDIO_DATA_MODE_RAW) {
|
||||
handler->set_handler(RED_PLAYBACK_DATA, &PlaybackChannel::handle_raw_data, 0);
|
||||
} else if (_mode == RED_AUDIO_DATA_MODE_CELT_0_5_1) {
|
||||
handler->set_handler(RED_PLAYBACK_DATA, &PlaybackChannel::handle_celt_data, 0);
|
||||
} else {
|
||||
THROW("invalid mode");
|
||||
}
|
||||
}
|
||||
|
||||
void PlaybackChannel::handle_mode(RedPeer::InMessage* message)
|
||||
{
|
||||
RedPlaybackMode* playbacke_mode = (RedPlaybackMode*)message->data();
|
||||
if (playbacke_mode->mode != RED_AUDIO_DATA_MODE_RAW &&
|
||||
playbacke_mode->mode != RED_AUDIO_DATA_MODE_CELT_0_5_1) {
|
||||
THROW("invalid mode");
|
||||
}
|
||||
|
||||
_mode = playbacke_mode->mode;
|
||||
if (_playing) {
|
||||
set_data_handler();
|
||||
return;
|
||||
}
|
||||
|
||||
PlaybackHandler* handler = static_cast<PlaybackHandler*>(get_message_handler());
|
||||
handler->set_handler(RED_PLAYBACK_START, &PlaybackChannel::handle_start,
|
||||
sizeof(RedPlaybackStart));
|
||||
}
|
||||
|
||||
void PlaybackChannel::null_handler(RedPeer::InMessage* message)
|
||||
{
|
||||
}
|
||||
|
||||
void PlaybackChannel::disable()
|
||||
{
|
||||
PlaybackHandler* handler = static_cast<PlaybackHandler*>(get_message_handler());
|
||||
|
||||
handler->set_handler(RED_PLAYBACK_START, &PlaybackChannel::null_handler, 0);
|
||||
handler->set_handler(RED_PLAYBACK_STOP, &PlaybackChannel::null_handler, 0);
|
||||
handler->set_handler(RED_PLAYBACK_MODE, &PlaybackChannel::null_handler, 0);
|
||||
handler->set_handler(RED_PLAYBACK_DATA, &PlaybackChannel::null_handler, 0);
|
||||
}
|
||||
|
||||
void PlaybackChannel::handle_start(RedPeer::InMessage* message)
|
||||
{
|
||||
PlaybackHandler* handler = static_cast<PlaybackHandler*>(get_message_handler());
|
||||
RedPlaybackStart* start = (RedPlaybackStart*)message->data();
|
||||
|
||||
handler->set_handler(RED_PLAYBACK_START, NULL, 0);
|
||||
handler->set_handler(RED_PLAYBACK_STOP, &PlaybackChannel::handle_stop, 0);
|
||||
|
||||
#ifdef WAVE_CAPTURE
|
||||
start_wave();
|
||||
#endif
|
||||
if (!_wave_player) {
|
||||
// for now support only one setting
|
||||
int celt_mode_err;
|
||||
|
||||
if (start->format != RED_AUDIO_FMT_S16) {
|
||||
THROW("unexpected format");
|
||||
}
|
||||
int bits_per_sample = 16;
|
||||
int frame_size = 256;
|
||||
_frame_bytes = frame_size * start->channels * bits_per_sample / 8;
|
||||
try {
|
||||
_wave_player = Platform::create_player(start->frequency, bits_per_sample,
|
||||
start->channels);
|
||||
} catch (...) {
|
||||
LOG_WARN("create player failed");
|
||||
//todo: support disconnecting singel channel
|
||||
disable();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(_celt_mode = celt051_mode_create(start->frequency, start->channels,
|
||||
frame_size, &celt_mode_err))) {
|
||||
THROW("create celt mode failed %d", celt_mode_err);
|
||||
}
|
||||
|
||||
if (!(_celt_decoder = celt051_decoder_create(_celt_mode))) {
|
||||
THROW("create celt decoder");
|
||||
}
|
||||
}
|
||||
_playing = true;
|
||||
_frame_count = 0;
|
||||
set_data_handler();
|
||||
}
|
||||
|
||||
void PlaybackChannel::handle_stop(RedPeer::InMessage* message)
|
||||
{
|
||||
PlaybackHandler* handler = static_cast<PlaybackHandler*>(get_message_handler());
|
||||
|
||||
handler->set_handler(RED_PLAYBACK_STOP, NULL, 0);
|
||||
handler->set_handler(RED_PLAYBACK_DATA, NULL, 0);
|
||||
handler->set_handler(RED_PLAYBACK_START, &PlaybackChannel::handle_start,
|
||||
sizeof(RedPlaybackStart));
|
||||
|
||||
#ifdef WAVE_CAPTURE
|
||||
end_wave();
|
||||
#endif
|
||||
_wave_player->stop();
|
||||
_playing = false;
|
||||
}
|
||||
|
||||
void PlaybackChannel::handle_raw_data(RedPeer::InMessage* message)
|
||||
{
|
||||
RedPlaybackPacket* packet = (RedPlaybackPacket*)message->data();
|
||||
uint8_t* data = (uint8_t*)(packet + 1);
|
||||
uint32_t size = message->size() - sizeof(*packet);
|
||||
#ifdef WAVE_CAPTURE
|
||||
put_wave_data(data, size);
|
||||
return;
|
||||
#endif
|
||||
if (size != _frame_bytes) {
|
||||
//for now throw on unexpected size (based on current server imp).
|
||||
// will probably be replaced by supporting flexible data size in the player imp
|
||||
THROW("unexpected frame size");
|
||||
}
|
||||
if ((_frame_count++ % 1000) == 0) {
|
||||
get_client().set_mm_time(packet->time - _wave_player->get_delay_ms());
|
||||
}
|
||||
_wave_player->write(data);
|
||||
}
|
||||
|
||||
void PlaybackChannel::handle_celt_data(RedPeer::InMessage* message)
|
||||
{
|
||||
RedPlaybackPacket* packet = (RedPlaybackPacket*)message->data();
|
||||
uint8_t* data = (uint8_t*)(packet + 1);
|
||||
uint32_t size = message->size() - sizeof(*packet);
|
||||
celt_int16_t pcm[256 * 2];
|
||||
|
||||
if (celt051_decode(_celt_decoder, data, size, pcm) != CELT_OK) {
|
||||
THROW("celt decode failed");
|
||||
}
|
||||
#ifdef WAVE_CAPTURE
|
||||
put_wave_data(pcm, _frame_bytes);
|
||||
return;
|
||||
#endif
|
||||
if ((_frame_count++ % 1000) == 0) {
|
||||
get_client().set_mm_time(packet->time - _wave_player->get_delay_ms());
|
||||
}
|
||||
_wave_player->write((uint8_t *)pcm);
|
||||
}
|
||||
|
||||
class PlaybackFactory: public ChannelFactory {
|
||||
public:
|
||||
PlaybackFactory() : ChannelFactory(RED_CHANNEL_PLAYBACK) {}
|
||||
virtual RedChannel* construct(RedClient& client, uint32_t id)
|
||||
{
|
||||
return new PlaybackChannel(client, id);
|
||||
}
|
||||
};
|
||||
|
||||
static PlaybackFactory factory;
|
||||
|
||||
ChannelFactory& PlaybackChannel::Factory()
|
||||
{
|
||||
return factory;
|
||||
}
|
||||
|
||||
20
client/quic.cpp
Normal file
20
client/quic.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "../common/quic.c"
|
||||
|
||||
120
client/read_write_mutex.h
Normal file
120
client/read_write_mutex.h
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_READ_WRITE_MUTEX
|
||||
#define _H_READ_WRITE_MUTEX
|
||||
|
||||
#include "threads.h"
|
||||
|
||||
class ReadWriteMutex {
|
||||
public:
|
||||
ReadWriteMutex()
|
||||
{
|
||||
_state.read_count = 0;
|
||||
_state.write = false;
|
||||
_state.write_waiting = false;
|
||||
}
|
||||
|
||||
virtual ~ReadWriteMutex()
|
||||
{
|
||||
}
|
||||
|
||||
void read_lock()
|
||||
{
|
||||
Lock lock(_state_mutex);
|
||||
|
||||
while (_state.write || _state.write_waiting) {
|
||||
_read_cond.wait(lock);
|
||||
}
|
||||
|
||||
++_state.read_count;
|
||||
}
|
||||
|
||||
bool try_read_lock()
|
||||
{
|
||||
Lock lock(_state_mutex);
|
||||
if (_state.write || _state.write_waiting) {
|
||||
return false;
|
||||
} else {
|
||||
++_state.read_count;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void read_unlock()
|
||||
{
|
||||
Lock lock(_state_mutex);
|
||||
--_state.read_count;
|
||||
|
||||
if (!_state.read_count) { // last reader
|
||||
_state.write_waiting = false;
|
||||
release_waiters();
|
||||
}
|
||||
}
|
||||
|
||||
void write_lock()
|
||||
{
|
||||
Lock lock(_state_mutex);
|
||||
|
||||
while (_state.read_count || _state.write) {
|
||||
_state.write_waiting = true;
|
||||
_write_cond.wait(lock);
|
||||
}
|
||||
_state.write = true;
|
||||
}
|
||||
|
||||
bool try_write_lock()
|
||||
{
|
||||
Lock lock(_state_mutex);
|
||||
|
||||
if (_state.read_count || _state.write) {
|
||||
return false;
|
||||
} else {
|
||||
_state.write = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void write_unlock()
|
||||
{
|
||||
Lock lock(_state_mutex);
|
||||
_state.write = false;
|
||||
_state.write_waiting = false;
|
||||
release_waiters();
|
||||
}
|
||||
|
||||
private:
|
||||
void release_waiters()
|
||||
{
|
||||
_write_cond.notify_one();
|
||||
_read_cond.notify_all();
|
||||
}
|
||||
|
||||
private:
|
||||
struct {
|
||||
unsigned int read_count;
|
||||
bool write;
|
||||
bool write_waiting;
|
||||
} _state;
|
||||
|
||||
Mutex _state_mutex;
|
||||
Condition _read_cond;
|
||||
Condition _write_cond;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
280
client/record_channel.cpp
Normal file
280
client/record_channel.cpp
Normal file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "red_client.h"
|
||||
#include "audio_channels.h"
|
||||
#include "audio_devices.h"
|
||||
|
||||
#define NUM_SAMPLES_MESSAGES 4
|
||||
|
||||
|
||||
static uint32_t get_mm_time()
|
||||
{
|
||||
return uint32_t(Platform::get_monolithic_time() / (1000 * 1000));
|
||||
}
|
||||
|
||||
class RecordSamplesMessage: public RedChannel::OutMessage {
|
||||
public:
|
||||
RecordSamplesMessage(RecordChannel& channel);
|
||||
virtual ~RecordSamplesMessage();
|
||||
|
||||
virtual RedPeer::OutMessage& peer_message() { return *_massage;}
|
||||
virtual void release();
|
||||
|
||||
private:
|
||||
RecordChannel& _channel;
|
||||
RedPeer::OutMessage *_massage;
|
||||
};
|
||||
|
||||
RecordSamplesMessage::RecordSamplesMessage(RecordChannel& channel)
|
||||
: _channel (channel)
|
||||
, _massage (new Message(REDC_RECORD_DATA, sizeof(RedcRecordPacket) + 4096))
|
||||
{
|
||||
}
|
||||
|
||||
RecordSamplesMessage::~RecordSamplesMessage()
|
||||
{
|
||||
delete _massage;
|
||||
}
|
||||
|
||||
void RecordSamplesMessage::release()
|
||||
{
|
||||
_channel.release_message(this);
|
||||
}
|
||||
|
||||
int RecordChannel::data_mode = RED_AUDIO_DATA_MODE_CELT_0_5_1;
|
||||
|
||||
class RecordHandler: public MessageHandlerImp<RecordChannel, REDC_RECORD_MESSAGES_END> {
|
||||
public:
|
||||
RecordHandler(RecordChannel& channel)
|
||||
: MessageHandlerImp<RecordChannel, REDC_RECORD_MESSAGES_END>(channel) {}
|
||||
};
|
||||
|
||||
RecordChannel::RecordChannel(RedClient& client, uint32_t id)
|
||||
: RedChannel(client, RED_CHANNEL_RECORD, id, new RecordHandler(*this))
|
||||
, _wave_recorder (NULL)
|
||||
, _mode (RED_AUDIO_DATA_MODE_INVALD)
|
||||
, _celt_mode (NULL)
|
||||
, _celt_encoder (NULL)
|
||||
{
|
||||
for (int i = 0; i < NUM_SAMPLES_MESSAGES; i++) {
|
||||
_messages.push_front(new RecordSamplesMessage(*this));
|
||||
}
|
||||
|
||||
RecordHandler* handler = static_cast<RecordHandler*>(get_message_handler());
|
||||
|
||||
handler->set_handler(RED_MIGRATE, &RecordChannel::handle_migrate, 0);
|
||||
handler->set_handler(RED_SET_ACK, &RecordChannel::handle_set_ack, sizeof(RedSetAck));
|
||||
handler->set_handler(RED_PING, &RecordChannel::handle_ping, sizeof(RedPing));
|
||||
handler->set_handler(RED_WAIT_FOR_CHANNELS, &RecordChannel::handle_wait_for_channels,
|
||||
sizeof(RedWaitForChannels));
|
||||
handler->set_handler(RED_DISCONNECTING, &RecordChannel::handle_disconnect,
|
||||
sizeof(RedDisconnect));
|
||||
handler->set_handler(RED_NOTIFY, &RecordChannel::handle_notify, sizeof(RedNotify));
|
||||
|
||||
handler->set_handler(RED_RECORD_START, &RecordChannel::handle_start, sizeof(RedRecordStart));
|
||||
|
||||
set_capability(RED_RECORD_CAP_CELT_0_5_1);
|
||||
}
|
||||
|
||||
RecordChannel::~RecordChannel(void)
|
||||
{
|
||||
while (!_messages.empty()) {
|
||||
RecordSamplesMessage *mes;
|
||||
mes = *_messages.begin();
|
||||
_messages.pop_front();
|
||||
delete mes;
|
||||
}
|
||||
delete _wave_recorder;
|
||||
|
||||
if (_celt_encoder) {
|
||||
celt051_encoder_destroy(_celt_encoder);
|
||||
}
|
||||
if (_celt_mode) {
|
||||
celt051_mode_destroy(_celt_mode);
|
||||
}
|
||||
}
|
||||
|
||||
bool RecordChannel::abort(void)
|
||||
{
|
||||
return (!_wave_recorder || _wave_recorder->abort()) && RedChannel::abort();
|
||||
}
|
||||
|
||||
void RecordChannel::on_connect()
|
||||
{
|
||||
Message* message = new Message(REDC_RECORD_MODE, sizeof(RedcRecordMode));
|
||||
RedcRecordMode *mode = (RedcRecordMode *)message->data();
|
||||
mode->time = get_mm_time();
|
||||
mode->mode = _mode = test_capability(RED_RECORD_CAP_CELT_0_5_1) ? RecordChannel::data_mode :
|
||||
RED_AUDIO_DATA_MODE_RAW;
|
||||
post_message(message);
|
||||
}
|
||||
|
||||
void RecordChannel::send_start_mark()
|
||||
{
|
||||
Message* message = new Message(REDC_RECORD_START_MARK, sizeof(RedcRecordStartMark));
|
||||
RedcRecordStartMark *start_mark = (RedcRecordStartMark *)message->data();
|
||||
start_mark->time = get_mm_time();
|
||||
post_message(message);
|
||||
}
|
||||
|
||||
void RecordChannel::handle_start(RedPeer::InMessage* message)
|
||||
{
|
||||
RecordHandler* handler = static_cast<RecordHandler*>(get_message_handler());
|
||||
RedRecordStart* start = (RedRecordStart*)message->data();
|
||||
|
||||
handler->set_handler(RED_RECORD_START, NULL, 0);
|
||||
handler->set_handler(RED_RECORD_STOP, &RecordChannel::handle_stop, 0);
|
||||
ASSERT(!_wave_recorder && !_celt_mode && !_celt_encoder);
|
||||
|
||||
// for now support only one setting
|
||||
if (start->format != RED_AUDIO_FMT_S16) {
|
||||
THROW("unexpected format");
|
||||
}
|
||||
|
||||
int bits_per_sample = 16;
|
||||
try {
|
||||
_wave_recorder = Platform::create_recorder(*this, start->frequency,
|
||||
bits_per_sample,
|
||||
start->channels);
|
||||
} catch (...) {
|
||||
LOG_WARN("create recorder failed");
|
||||
return;
|
||||
}
|
||||
|
||||
int frame_size = 256;
|
||||
int celt_mode_err;
|
||||
_frame_bytes = frame_size * bits_per_sample * start->channels / 8;
|
||||
if (!(_celt_mode = celt051_mode_create(start->frequency, start->channels, frame_size,
|
||||
&celt_mode_err))) {
|
||||
THROW("create celt mode failed %d", celt_mode_err);
|
||||
}
|
||||
|
||||
if (!(_celt_encoder = celt051_encoder_create(_celt_mode))) {
|
||||
THROW("create celt encoder failed");
|
||||
}
|
||||
|
||||
send_start_mark();
|
||||
_wave_recorder->start();
|
||||
}
|
||||
|
||||
void RecordChannel::handle_stop(RedPeer::InMessage* message)
|
||||
{
|
||||
RecordHandler* handler = static_cast<RecordHandler*>(get_message_handler());
|
||||
handler->set_handler(RED_RECORD_START, &RecordChannel::handle_start, sizeof(RedRecordStart));
|
||||
handler->set_handler(RED_RECORD_STOP, NULL, 0);
|
||||
if (!_wave_recorder) {
|
||||
return;
|
||||
}
|
||||
ASSERT(_celt_mode && _celt_encoder);
|
||||
_wave_recorder->stop();
|
||||
celt051_encoder_destroy(_celt_encoder);
|
||||
_celt_encoder = NULL;
|
||||
celt051_mode_destroy(_celt_mode);
|
||||
_celt_mode = NULL;
|
||||
delete _wave_recorder;
|
||||
_wave_recorder = NULL;
|
||||
}
|
||||
|
||||
RecordSamplesMessage* RecordChannel::get_message()
|
||||
{
|
||||
Lock lock(_messages_lock);
|
||||
if (_messages.empty()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RecordSamplesMessage* ret = *_messages.begin();
|
||||
_messages.pop_front();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void RecordChannel::release_message(RecordSamplesMessage *message)
|
||||
{
|
||||
Lock lock(_messages_lock);
|
||||
_messages.push_front(message);
|
||||
}
|
||||
|
||||
void RecordChannel::add_evnet_sorce(EventsLoop::File& evnet_sorce)
|
||||
{
|
||||
get_events_loop().add_file(evnet_sorce);
|
||||
}
|
||||
|
||||
void RecordChannel::remove_evnet_sorce(EventsLoop::File& evnet_sorce)
|
||||
{
|
||||
get_events_loop().remove_file(evnet_sorce);
|
||||
}
|
||||
|
||||
void RecordChannel::add_evnet_sorce(EventsLoop::Trigger& evnet_sorce)
|
||||
{
|
||||
get_events_loop().add_trigger(evnet_sorce);
|
||||
}
|
||||
|
||||
void RecordChannel::remove_evnet_sorce(EventsLoop::Trigger& evnet_sorce)
|
||||
{
|
||||
get_events_loop().remove_trigger(evnet_sorce);
|
||||
}
|
||||
|
||||
#define FRAME_SIZE 256
|
||||
#define CELT_BIT_RATE (64 * 1024)
|
||||
#define CELT_COMPRESSED_FRAME_BYTES (FRAME_SIZE * CELT_BIT_RATE / 44100 / 8)
|
||||
|
||||
void RecordChannel::push_frame(uint8_t *frame)
|
||||
{
|
||||
RecordSamplesMessage *message;
|
||||
ASSERT(_frame_bytes == FRAME_SIZE * 4);
|
||||
if (!(message = get_message())) {
|
||||
DBG(0, "blocked");
|
||||
return;
|
||||
}
|
||||
uint8_t celt_buf[CELT_COMPRESSED_FRAME_BYTES];
|
||||
int n;
|
||||
|
||||
if (_mode == RED_AUDIO_DATA_MODE_CELT_0_5_1) {
|
||||
n = celt051_encode(_celt_encoder, (celt_int16_t *)frame, NULL, celt_buf,
|
||||
CELT_COMPRESSED_FRAME_BYTES);
|
||||
if (n < 0) {
|
||||
THROW("celt encode failed");
|
||||
}
|
||||
frame = celt_buf;
|
||||
} else {
|
||||
n = _frame_bytes;
|
||||
}
|
||||
RedPeer::OutMessage& peer_message = message->peer_message();
|
||||
peer_message.resize(n + sizeof(RedcRecordPacket));
|
||||
RedcRecordPacket* packet = (RedcRecordPacket*)peer_message.data();
|
||||
packet->time = get_mm_time();
|
||||
memcpy(packet->data, frame, n);
|
||||
post_message(message);
|
||||
}
|
||||
|
||||
class RecordFactory: public ChannelFactory {
|
||||
public:
|
||||
RecordFactory() : ChannelFactory(RED_CHANNEL_RECORD) {}
|
||||
virtual RedChannel* construct(RedClient& client, uint32_t id)
|
||||
{
|
||||
return new RecordChannel(client, id);
|
||||
}
|
||||
};
|
||||
|
||||
static RecordFactory factory;
|
||||
|
||||
ChannelFactory& RecordChannel::Factory()
|
||||
{
|
||||
return factory;
|
||||
}
|
||||
|
||||
201
client/red_cairo_canvas.cpp
Normal file
201
client/red_cairo_canvas.cpp
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include <stdint.h>
|
||||
#include "red_window.h"
|
||||
#include "red_cairo_canvas.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
#include "region.h"
|
||||
#include "red_pixmap_cairo.h"
|
||||
|
||||
CCanvas::CCanvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
|
||||
GlzDecoderWindow &glz_decoder_window)
|
||||
: Canvas (pixmap_cache, palette_cache, glz_decoder_window)
|
||||
, _canvas (NULL)
|
||||
, _pixmap (0)
|
||||
{
|
||||
}
|
||||
|
||||
CCanvas::~CCanvas()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
void CCanvas::destroy()
|
||||
{
|
||||
if (_canvas) {
|
||||
cairo_t *cairo = canvas_get_cairo(_canvas);
|
||||
canvas_destroy(_canvas);
|
||||
cairo_destroy(cairo);
|
||||
_canvas = NULL;
|
||||
}
|
||||
destroy_pixmap();
|
||||
}
|
||||
|
||||
void CCanvas::clear()
|
||||
{
|
||||
if (_canvas) {
|
||||
canvas_clear(_canvas);
|
||||
}
|
||||
}
|
||||
|
||||
void CCanvas::destroy_pixmap()
|
||||
{
|
||||
delete _pixmap;
|
||||
_pixmap = NULL;
|
||||
}
|
||||
|
||||
void CCanvas::create_pixmap(int width, int height, RedWindow *win)
|
||||
{
|
||||
_pixmap = new RedPixmapCairo(width, height, RedPixmap::RGB32, true, NULL, win);
|
||||
}
|
||||
|
||||
void CCanvas::copy_pixels(const QRegion& region, RedDrawable& dest_dc)
|
||||
{
|
||||
for (int i = 0; i < (int)region.num_rects; i++) {
|
||||
Rect* r = ®ion.rects[i];
|
||||
dest_dc.copy_pixels(*_pixmap, r->left, r->top, *r);
|
||||
}
|
||||
}
|
||||
|
||||
void CCanvas::copy_pixels(const QRegion& region, RedDrawable* dest_dc, const PixmapHeader* pixmap)
|
||||
{
|
||||
copy_pixels(region, *dest_dc);
|
||||
}
|
||||
|
||||
void CCanvas::set_mode(int width, int height, int depth, RedWindow *win)
|
||||
{
|
||||
cairo_surface_t *cairo_surface;
|
||||
cairo_t *cairo;
|
||||
|
||||
destroy();
|
||||
create_pixmap(width, height, win);
|
||||
cairo_surface = cairo_image_surface_create_for_data(_pixmap->get_data(), CAIRO_FORMAT_RGB24,
|
||||
width, height, _pixmap->get_stride());
|
||||
if (cairo_surface_status(cairo_surface) != CAIRO_STATUS_SUCCESS) {
|
||||
THROW("create surface failed, %s",
|
||||
cairo_status_to_string(cairo_surface_status(cairo_surface)));
|
||||
}
|
||||
|
||||
cairo = cairo_create(cairo_surface);
|
||||
cairo_surface_destroy(cairo_surface);
|
||||
if (cairo_status(cairo) != CAIRO_STATUS_SUCCESS) {
|
||||
THROW("create cairo failed, %s", cairo_status_to_string(cairo_status(cairo)));
|
||||
}
|
||||
if (!(_canvas = canvas_create(cairo, depth,
|
||||
&pixmap_cache(), bits_cache_put, bits_cache_get,
|
||||
&palette_cache(), palette_cache_put, palette_cache_get,
|
||||
palette_cache_release,
|
||||
&glz_decoder(),
|
||||
glz_decode))) {
|
||||
THROW("create canvas failed");
|
||||
}
|
||||
}
|
||||
|
||||
void CCanvas::set_access_params(ADDRESS delta, unsigned long base, unsigned long max)
|
||||
{
|
||||
canvas_set_access_params(_canvas, delta, base, max);
|
||||
}
|
||||
|
||||
void CCanvas::draw_fill(Rect *bbox, Clip *clip, Fill *fill)
|
||||
{
|
||||
canvas_draw_fill(_canvas, bbox, clip, fill);
|
||||
}
|
||||
|
||||
void CCanvas::draw_text(Rect *bbox, Clip *clip, Text *text)
|
||||
{
|
||||
canvas_draw_text(_canvas, bbox, clip, text);
|
||||
}
|
||||
|
||||
void CCanvas::draw_opaque(Rect *bbox, Clip *clip, Opaque *opaque)
|
||||
{
|
||||
canvas_draw_opaque(_canvas, bbox, clip, opaque);
|
||||
}
|
||||
|
||||
void CCanvas::draw_copy(Rect *bbox, Clip *clip, Copy *copy)
|
||||
{
|
||||
canvas_draw_copy(_canvas, bbox, clip, copy);
|
||||
}
|
||||
|
||||
void CCanvas::draw_transparent(Rect *bbox, Clip *clip, Transparent* transparent)
|
||||
{
|
||||
canvas_draw_transparent(_canvas, bbox, clip, transparent);
|
||||
}
|
||||
|
||||
void CCanvas::draw_alpha_blend(Rect *bbox, Clip *clip, AlphaBlnd* alpha_blend)
|
||||
{
|
||||
canvas_draw_alpha_blend(_canvas, bbox, clip, alpha_blend);
|
||||
}
|
||||
|
||||
void CCanvas::copy_bits(Rect *bbox, Clip *clip, Point *src_pos)
|
||||
{
|
||||
canvas_copy_bits(_canvas, bbox, clip, src_pos);
|
||||
}
|
||||
|
||||
void CCanvas::draw_blend(Rect *bbox, Clip *clip, Blend *blend)
|
||||
{
|
||||
canvas_draw_blend(_canvas, bbox, clip, blend);
|
||||
}
|
||||
|
||||
void CCanvas::draw_blackness(Rect *bbox, Clip *clip, Blackness *blackness)
|
||||
{
|
||||
canvas_draw_blackness(_canvas, bbox, clip, blackness);
|
||||
}
|
||||
|
||||
void CCanvas::draw_whiteness(Rect *bbox, Clip *clip, Whiteness *whiteness)
|
||||
{
|
||||
canvas_draw_whiteness(_canvas, bbox, clip, whiteness);
|
||||
}
|
||||
|
||||
void CCanvas::draw_invers(Rect *bbox, Clip *clip, Invers *invers)
|
||||
{
|
||||
canvas_draw_invers(_canvas, bbox, clip, invers);
|
||||
}
|
||||
|
||||
void CCanvas::draw_rop3(Rect *bbox, Clip *clip, Rop3 *rop3)
|
||||
{
|
||||
canvas_draw_rop3(_canvas, bbox, clip, rop3);
|
||||
}
|
||||
|
||||
void CCanvas::draw_stroke(Rect *bbox, Clip *clip, Stroke *stroke)
|
||||
{
|
||||
canvas_draw_stroke(_canvas, bbox, clip, stroke);
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
void CCanvas::put_image(HDC dc, const PixmapHeader& image, const Rect& dest, const QRegion* clip)
|
||||
{
|
||||
canvas_put_image(_canvas, dc, &dest, image.data, image.width, image.height, image.stride,
|
||||
clip);
|
||||
}
|
||||
|
||||
#else
|
||||
void CCanvas::put_image(const PixmapHeader& image, const Rect& dest, const QRegion* clip)
|
||||
{
|
||||
canvas_put_image(_canvas, &dest, image.data, image.width, image.height, image.stride,
|
||||
clip);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
CanvasType CCanvas::get_pixmap_type()
|
||||
{
|
||||
return CANVAS_TYPE_CAIRO;
|
||||
}
|
||||
|
||||
77
client/red_cairo_canvas.h
Normal file
77
client/red_cairo_canvas.h
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_CCANVAS
|
||||
#define _H_CCANVAS
|
||||
|
||||
#include "canvas.h"
|
||||
#include "cairo_canvas.h"
|
||||
|
||||
class RedPixmap;
|
||||
|
||||
class CCanvas: public Canvas {
|
||||
public:
|
||||
CCanvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
|
||||
GlzDecoderWindow &glz_decoder_window);
|
||||
virtual ~CCanvas();
|
||||
|
||||
virtual void set_mode(int x, int y, int bits, RedWindow *win);
|
||||
virtual void clear();
|
||||
virtual void thread_touch() {}
|
||||
virtual void copy_pixels(const QRegion& region, RedDrawable* dc,
|
||||
const PixmapHeader* pixmap);
|
||||
virtual void copy_pixels(const QRegion& region, RedDrawable& dc);
|
||||
#ifdef WIN32
|
||||
virtual void put_image(HDC dc, const PixmapHeader& image,
|
||||
const Rect& dest, const QRegion* clip);
|
||||
#else
|
||||
virtual void put_image(const PixmapHeader& image, const Rect& dest,
|
||||
const QRegion* clip);
|
||||
#endif
|
||||
|
||||
virtual CanvasType get_pixmap_type();
|
||||
|
||||
protected:
|
||||
virtual void set_access_params(ADDRESS delta, unsigned long base, unsigned long max);
|
||||
virtual void draw_fill(Rect *bbox, Clip *clip, Fill *fill);
|
||||
virtual void draw_copy(Rect *bbox, Clip *clip, Copy *copy);
|
||||
virtual void draw_opaque(Rect *bbox, Clip *clip, Opaque *opaque);
|
||||
virtual void copy_bits(Rect *bbox, Clip *clip, Point *src_pos);
|
||||
virtual void draw_text(Rect *bbox, Clip *clip, Text *text);
|
||||
virtual void draw_stroke(Rect *bbox, Clip *clip, Stroke *stroke);
|
||||
virtual void draw_rop3(Rect *bbox, Clip *clip, Rop3 *rop3);
|
||||
virtual void draw_blend(Rect *bbox, Clip *clip, Blend *blend);
|
||||
virtual void draw_blackness(Rect *bbox, Clip *clip, Blackness *blackness);
|
||||
virtual void draw_whiteness(Rect *bbox, Clip *clip, Whiteness *whiteness);
|
||||
virtual void draw_invers(Rect *bbox, Clip *clip, Invers *invers);
|
||||
virtual void draw_transparent(Rect *bbox, Clip *clip, Transparent* transparent);
|
||||
virtual void draw_alpha_blend(Rect *bbox, Clip *clip, AlphaBlnd* alpha_blend);
|
||||
|
||||
private:
|
||||
void create_pixmap(int width, int height, RedWindow *win);
|
||||
void destroy_pixmap();
|
||||
void destroy();
|
||||
|
||||
private:
|
||||
CairoCanvas* _canvas;
|
||||
RedPixmap *_pixmap;
|
||||
unsigned long _base;
|
||||
unsigned long _max;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
714
client/red_channel.cpp
Normal file
714
client/red_channel.cpp
Normal file
@ -0,0 +1,714 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "red_channel.h"
|
||||
#include "red_client.h"
|
||||
#include "application.h"
|
||||
#include "debug.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "openssl/rsa.h"
|
||||
#include "openssl/evp.h"
|
||||
#include "openssl/x509.h"
|
||||
|
||||
|
||||
RedChannelBase::RedChannelBase(uint8_t type, uint8_t id, const ChannelCaps& common_caps,
|
||||
const ChannelCaps& caps)
|
||||
: RedPeer()
|
||||
, _type (type)
|
||||
, _id (id)
|
||||
, _common_caps (common_caps)
|
||||
, _caps (caps)
|
||||
{
|
||||
}
|
||||
|
||||
RedChannelBase::~RedChannelBase()
|
||||
{
|
||||
}
|
||||
|
||||
void RedChannelBase::link(uint32_t connection_id, const std::string& password)
|
||||
{
|
||||
RedLinkHeader header;
|
||||
RedLinkMess link_mess;
|
||||
RedLinkReply* reply;
|
||||
uint32_t link_res;
|
||||
uint32_t i;
|
||||
|
||||
EVP_PKEY *pubkey;
|
||||
int nRSASize;
|
||||
BIO *bioKey;
|
||||
RSA *rsa;
|
||||
|
||||
header.magic = RED_MAGIC;
|
||||
header.size = sizeof(link_mess);
|
||||
header.major_version = RED_VERSION_MAJOR;
|
||||
header.minor_version = RED_VERSION_MINOR;
|
||||
link_mess.connection_id = connection_id;
|
||||
link_mess.channel_type = _type;
|
||||
link_mess.channel_id = _id;
|
||||
link_mess.num_common_caps = get_common_caps().size();
|
||||
link_mess.num_channel_caps = get_caps().size();
|
||||
link_mess.caps_offset = sizeof(link_mess);
|
||||
header.size += (link_mess.num_common_caps + link_mess.num_channel_caps) * sizeof(uint32_t);
|
||||
send((uint8_t*)&header, sizeof(header));
|
||||
send((uint8_t*)&link_mess, sizeof(link_mess));
|
||||
|
||||
for (i = 0; i < _common_caps.size(); i++) {
|
||||
send((uint8_t*)&_common_caps[i], sizeof(uint32_t));
|
||||
}
|
||||
|
||||
for (i = 0; i < _caps.size(); i++) {
|
||||
send((uint8_t*)&_caps[i], sizeof(uint32_t));
|
||||
}
|
||||
|
||||
recive((uint8_t*)&header, sizeof(header));
|
||||
|
||||
if (header.magic != RED_MAGIC) {
|
||||
THROW_ERR(SPICEC_ERROR_CODE_CONNECT_FAILED, "bad magic");
|
||||
}
|
||||
|
||||
if (header.major_version != RED_VERSION_MAJOR) {
|
||||
THROW_ERR(SPICEC_ERROR_CODE_VERSION_MISMATCH,
|
||||
"version mismatch: expect %u got %u",
|
||||
RED_VERSION_MAJOR,
|
||||
header.major_version);
|
||||
}
|
||||
|
||||
AutoArray<uint8_t> reply_buf(new uint8_t[header.size]);
|
||||
recive(reply_buf.get(), header.size);
|
||||
|
||||
reply = (RedLinkReply *)reply_buf.get();
|
||||
|
||||
if (reply->error != RED_ERR_OK) {
|
||||
THROW_ERR(SPICEC_ERROR_CODE_CONNECT_FAILED, "connect error %u", reply->error);
|
||||
}
|
||||
|
||||
uint32_t num_caps = reply->num_channel_caps + reply->num_common_caps;
|
||||
if ((uint8_t *)(reply + 1) > reply_buf.get() + header.size ||
|
||||
(uint8_t *)reply + reply->caps_offset + num_caps * sizeof(uint32_t) >
|
||||
reply_buf.get() + header.size) {
|
||||
THROW_ERR(SPICEC_ERROR_CODE_CONNECT_FAILED, "access violation");
|
||||
}
|
||||
|
||||
uint32_t *caps = (uint32_t *)((uint8_t *)reply + reply->caps_offset);
|
||||
|
||||
_remote_common_caps.clear();
|
||||
for (i = 0; i < reply->num_common_caps; i++, caps++) {
|
||||
_remote_common_caps.resize(i + 1);
|
||||
_remote_common_caps[i] = *caps;
|
||||
}
|
||||
|
||||
_remote_caps.clear();
|
||||
for (i = 0; i < reply->num_channel_caps; i++, caps++) {
|
||||
_remote_caps.resize(i + 1);
|
||||
_remote_caps[i] = *caps;
|
||||
}
|
||||
|
||||
bioKey = BIO_new(BIO_s_mem());
|
||||
if (bioKey != NULL) {
|
||||
BIO_write(bioKey, reply->pub_key, RED_TICKET_PUBKEY_BYTES);
|
||||
pubkey = d2i_PUBKEY_bio(bioKey, NULL);
|
||||
rsa = pubkey->pkey.rsa;
|
||||
nRSASize = RSA_size(rsa);
|
||||
AutoArray<unsigned char> bufEncrypted(new unsigned char[nRSASize]);
|
||||
|
||||
/*
|
||||
The use of RSA encryption limit the potential maximum password length.
|
||||
for RSA_PKCS1_OAEP_PADDING it is RSA_size(rsa) - 41.
|
||||
*/
|
||||
if (RSA_public_encrypt(password.length() + 1, (unsigned char *)password.c_str(),
|
||||
(uint8_t *)bufEncrypted.get(),
|
||||
rsa, RSA_PKCS1_OAEP_PADDING) > 0 ) {
|
||||
send((uint8_t*)bufEncrypted.get(), nRSASize);
|
||||
} else {
|
||||
THROW("could not encrypt password");
|
||||
}
|
||||
|
||||
memset(bufEncrypted.get(), 0, nRSASize);
|
||||
} else {
|
||||
THROW("Could not initiate BIO");
|
||||
}
|
||||
|
||||
BIO_free(bioKey);
|
||||
|
||||
recive((uint8_t*)&link_res, sizeof(link_res));
|
||||
if (link_res != RED_ERR_OK) {
|
||||
int error_code = (link_res == RED_ERR_PERMISSION_DENIED) ?
|
||||
SPICEC_ERROR_CODE_CONNECT_FAILED : SPICEC_ERROR_CODE_CONNECT_FAILED;
|
||||
THROW_ERR(error_code, "connect failed %u", link_res);
|
||||
}
|
||||
}
|
||||
|
||||
void RedChannelBase::connect(const ConnectionOptions& options, uint32_t connection_id,
|
||||
uint32_t ip, std::string password)
|
||||
{
|
||||
if (options.allow_unsecure()) {
|
||||
try {
|
||||
RedPeer::connect_unsecure(ip, options.unsecure_port);
|
||||
link(connection_id, password);
|
||||
return;
|
||||
} catch (...) {
|
||||
if (!options.allow_secure()) {
|
||||
throw;
|
||||
}
|
||||
RedPeer::close();
|
||||
}
|
||||
}
|
||||
ASSERT(options.allow_secure());
|
||||
RedPeer::connect_secure(options, ip);
|
||||
link(connection_id, password);
|
||||
}
|
||||
|
||||
void RedChannelBase::connect(const ConnectionOptions& options, uint32_t connection_id,
|
||||
const char* host, std::string password)
|
||||
{
|
||||
connect(options, connection_id, host_by_name(host), password);
|
||||
}
|
||||
|
||||
void RedChannelBase::set_capability(ChannelCaps& caps, uint32_t cap)
|
||||
{
|
||||
uint32_t word_index = cap / 32;
|
||||
|
||||
if (caps.size() < word_index + 1) {
|
||||
caps.resize(word_index + 1);
|
||||
}
|
||||
caps[word_index] |= 1 << (cap % 32);
|
||||
}
|
||||
|
||||
void RedChannelBase::set_common_capability(uint32_t cap)
|
||||
{
|
||||
set_capability(_common_caps, cap);
|
||||
}
|
||||
|
||||
void RedChannelBase::set_capability(uint32_t cap)
|
||||
{
|
||||
set_capability(_caps, cap);
|
||||
}
|
||||
|
||||
bool RedChannelBase::test_capability(const ChannelCaps& caps, uint32_t cap)
|
||||
{
|
||||
uint32_t word_index = cap / 32;
|
||||
|
||||
if (caps.size() < word_index + 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (caps[word_index] & (1 << (cap % 32))) != 0;
|
||||
}
|
||||
|
||||
bool RedChannelBase::test_common_capability(uint32_t cap)
|
||||
{
|
||||
return test_capability(_remote_common_caps, cap);
|
||||
}
|
||||
|
||||
bool RedChannelBase::test_capability(uint32_t cap)
|
||||
{
|
||||
return test_capability(_remote_caps, cap);
|
||||
}
|
||||
|
||||
SendTrigger::SendTrigger(RedChannel& channel)
|
||||
: _channel (channel)
|
||||
{
|
||||
}
|
||||
|
||||
void SendTrigger::on_event()
|
||||
{
|
||||
_channel.on_send_trigger();
|
||||
}
|
||||
|
||||
void AbortTrigger::on_event()
|
||||
{
|
||||
THROW("abort");
|
||||
}
|
||||
|
||||
RedChannel::RedChannel(RedClient& client, uint8_t type, uint8_t id,
|
||||
RedChannel::MessageHandler* handler,
|
||||
Platform::ThreadPriority worker_priority)
|
||||
: RedChannelBase(type, id, ChannelCaps(), ChannelCaps())
|
||||
, _client (client)
|
||||
, _state (PASSIVE_STATE)
|
||||
, _action (WAIT_ACTION)
|
||||
, _error (SPICEC_ERROR_CODE_SUCCESS)
|
||||
, _wait_for_threads (true)
|
||||
, _socket_in_loop (false)
|
||||
, _worker (NULL)
|
||||
, _worker_priority (worker_priority)
|
||||
, _message_handler (handler)
|
||||
, _outgoing_message (NULL)
|
||||
, _incomming_header_pos (0)
|
||||
, _incomming_message (NULL)
|
||||
, _message_ack_count (0)
|
||||
, _message_ack_window (0)
|
||||
, _send_trigger (*this)
|
||||
, _disconnect_stamp (0)
|
||||
, _disconnect_reason (RED_ERR_OK)
|
||||
{
|
||||
_loop.add_trigger(_send_trigger);
|
||||
_loop.add_trigger(_abort_trigger);
|
||||
}
|
||||
|
||||
RedChannel::~RedChannel()
|
||||
{
|
||||
ASSERT(_state == TERMINATED_STATE || _state == PASSIVE_STATE);
|
||||
delete _worker;
|
||||
}
|
||||
|
||||
void* RedChannel::worker_main(void *data)
|
||||
{
|
||||
try {
|
||||
RedChannel* channel = static_cast<RedChannel*>(data);
|
||||
channel->set_state(DISCONNECTED_STATE);
|
||||
Platform::set_thread_priority(NULL, channel->get_worker_priority());
|
||||
channel->run();
|
||||
} catch (Exception& e) {
|
||||
LOG_ERROR("unhandle exception: %s", e.what());
|
||||
} catch (std::exception& e) {
|
||||
LOG_ERROR("unhandle exception: %s", e.what());
|
||||
} catch (...) {
|
||||
LOG_ERROR("unhandled exception");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void RedChannel::post_message(RedChannel::OutMessage* message)
|
||||
{
|
||||
Lock lock(_outgoing_lock);
|
||||
_outgoing_messages.push_back(message);
|
||||
lock.unlock();
|
||||
_send_trigger.trigger();
|
||||
}
|
||||
|
||||
RedPeer::CompundInMessage *RedChannel::recive()
|
||||
{
|
||||
CompundInMessage *message = RedChannelBase::recive();
|
||||
on_message_recived();
|
||||
return message;
|
||||
}
|
||||
|
||||
RedChannel::OutMessage* RedChannel::get_outgoing_message()
|
||||
{
|
||||
if (_state != CONNECTED_STATE || _outgoing_messages.empty()) {
|
||||
return NULL;
|
||||
}
|
||||
RedChannel::OutMessage* message = _outgoing_messages.front();
|
||||
_outgoing_messages.pop_front();
|
||||
return message;
|
||||
}
|
||||
|
||||
class AutoMessage {
|
||||
public:
|
||||
AutoMessage(RedChannel::OutMessage* message) : _message (message) {}
|
||||
~AutoMessage() {if (_message) _message->release();}
|
||||
void set(RedChannel::OutMessage* message) { _message = message;}
|
||||
RedChannel::OutMessage* get() { return _message;}
|
||||
RedChannel::OutMessage* release();
|
||||
|
||||
private:
|
||||
RedChannel::OutMessage* _message;
|
||||
};
|
||||
|
||||
RedChannel::OutMessage* AutoMessage::release()
|
||||
{
|
||||
RedChannel::OutMessage* ret = _message;
|
||||
_message = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void RedChannel::start()
|
||||
{
|
||||
ASSERT(!_worker);
|
||||
_worker = new Thread(RedChannel::worker_main, this);
|
||||
Lock lock(_state_lock);
|
||||
while (_state == PASSIVE_STATE) {
|
||||
_state_cond.wait(lock);
|
||||
}
|
||||
}
|
||||
|
||||
void RedChannel::set_state(int state)
|
||||
{
|
||||
Lock lock(_state_lock);
|
||||
_state = state;
|
||||
_state_cond.notify_all();
|
||||
}
|
||||
|
||||
void RedChannel::connect()
|
||||
{
|
||||
Lock lock(_action_lock);
|
||||
|
||||
if (_state != DISCONNECTED_STATE && _state != PASSIVE_STATE) {
|
||||
return;
|
||||
}
|
||||
_action = CONNECT_ACTION;
|
||||
_action_cond.notify_one();
|
||||
}
|
||||
|
||||
void RedChannel::disconnect()
|
||||
{
|
||||
clear_outgoing_messages();
|
||||
|
||||
Lock lock(_action_lock);
|
||||
if (_state != CONNECTING_STATE && _state != CONNECTED_STATE) {
|
||||
return;
|
||||
}
|
||||
_action = DISCONNECT_ACTION;
|
||||
RedPeer::disconnect();
|
||||
_action_cond.notify_one();
|
||||
}
|
||||
|
||||
void RedChannel::clear_outgoing_messages()
|
||||
{
|
||||
Lock lock(_outgoing_lock);
|
||||
while (!_outgoing_messages.empty()) {
|
||||
RedChannel::OutMessage* message = _outgoing_messages.front();
|
||||
_outgoing_messages.pop_front();
|
||||
message->release();
|
||||
}
|
||||
}
|
||||
|
||||
void RedChannel::run()
|
||||
{
|
||||
for (;;) {
|
||||
Lock lock(_action_lock);
|
||||
if (_action == WAIT_ACTION) {
|
||||
_action_cond.wait(lock);
|
||||
}
|
||||
int action = _action;
|
||||
_action = WAIT_ACTION;
|
||||
lock.unlock();
|
||||
switch (action) {
|
||||
case CONNECT_ACTION:
|
||||
try {
|
||||
get_client().get_sync_info(get_type(), get_id(), _sync_info);
|
||||
on_connecting();
|
||||
set_state(CONNECTING_STATE);
|
||||
ConnectionOptions con_options(_client.get_connection_options(get_type()),
|
||||
_client.get_port(),
|
||||
_client.get_sport());
|
||||
RedChannelBase::connect(con_options, _client.get_connection_id(),
|
||||
_client.get_host(), _client.get_password());
|
||||
on_connect();
|
||||
set_state(CONNECTED_STATE);
|
||||
_loop.add_socket(*this);
|
||||
_socket_in_loop = true;
|
||||
on_event();
|
||||
_loop.run();
|
||||
} catch (RedPeer::DisconnectedException&) {
|
||||
_error = SPICEC_ERROR_CODE_SUCCESS;
|
||||
} catch (Exception& e) {
|
||||
LOG_WARN("%s", e.what());
|
||||
_error = e.get_error_code();
|
||||
} catch (std::exception& e) {
|
||||
LOG_WARN("%s", e.what());
|
||||
_error = SPICEC_ERROR_CODE_ERROR;
|
||||
}
|
||||
if (_socket_in_loop) {
|
||||
_socket_in_loop = false;
|
||||
_loop.remove_socket(*this);
|
||||
}
|
||||
if (_outgoing_message) {
|
||||
_outgoing_message->release();
|
||||
_outgoing_message = NULL;
|
||||
}
|
||||
_incomming_header_pos = 0;
|
||||
delete _incomming_message;
|
||||
_incomming_message = NULL;
|
||||
case DISCONNECT_ACTION:
|
||||
close();
|
||||
on_disconnect();
|
||||
set_state(DISCONNECTED_STATE);
|
||||
_client.on_channel_disconnected(*this);
|
||||
continue;
|
||||
case QUIT_ACTION:
|
||||
set_state(TERMINATED_STATE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RedChannel::abort()
|
||||
{
|
||||
clear_outgoing_messages();
|
||||
Lock lock(_action_lock);
|
||||
if (_state == TERMINATED_STATE) {
|
||||
if (_wait_for_threads) {
|
||||
_wait_for_threads = false;
|
||||
_worker->join();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
_action = QUIT_ACTION;
|
||||
_action_cond.notify_one();
|
||||
lock.unlock();
|
||||
RedPeer::disconnect();
|
||||
_abort_trigger.trigger();
|
||||
|
||||
for (;;) {
|
||||
Lock state_lock(_state_lock);
|
||||
if (_state == TERMINATED_STATE) {
|
||||
break;
|
||||
}
|
||||
uint64_t timout = 1000 * 1000 * 100; // 100ms
|
||||
if (!_state_cond.timed_wait(state_lock, timout)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (_wait_for_threads) {
|
||||
_wait_for_threads = false;
|
||||
_worker->join();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RedChannel::send_messages()
|
||||
{
|
||||
if (_outgoing_message) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
Lock lock(_outgoing_lock);
|
||||
AutoMessage message(get_outgoing_message());
|
||||
if (!message.get()) {
|
||||
return;
|
||||
}
|
||||
RedPeer::OutMessage& peer_message = message.get()->peer_message();
|
||||
uint32_t n = send(peer_message);
|
||||
if (n != peer_message.message_size()) {
|
||||
_outgoing_message = message.release();
|
||||
_outgoing_pos = n;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RedChannel::on_send_trigger()
|
||||
{
|
||||
send_messages();
|
||||
}
|
||||
|
||||
void RedChannel::on_message_recived()
|
||||
{
|
||||
if (_message_ack_count && !--_message_ack_count) {
|
||||
post_message(new Message(REDC_ACK, 0));
|
||||
_message_ack_count = _message_ack_window;
|
||||
}
|
||||
}
|
||||
|
||||
void RedChannel::on_message_complition(uint64_t serial)
|
||||
{
|
||||
Lock lock(*_sync_info.lock);
|
||||
*_sync_info.message_serial = serial;
|
||||
_sync_info.condition->notify_all();
|
||||
}
|
||||
|
||||
void RedChannel::recive_messages()
|
||||
{
|
||||
for (;;) {
|
||||
uint32_t n = RedPeer::recive((uint8_t*)&_incomming_header, sizeof(RedDataHeader));
|
||||
if (n != sizeof(RedDataHeader)) {
|
||||
_incomming_header_pos = n;
|
||||
return;
|
||||
}
|
||||
std::auto_ptr<CompundInMessage> message(new CompundInMessage(_incomming_header.serial,
|
||||
_incomming_header.type,
|
||||
_incomming_header.size,
|
||||
_incomming_header.sub_list));
|
||||
n = RedPeer::recive(message->data(), message->compund_size());
|
||||
if (n != message->compund_size()) {
|
||||
_incomming_message = message.release();
|
||||
_incomming_message_pos = n;
|
||||
return;
|
||||
}
|
||||
on_message_recived();
|
||||
_message_handler->handle_message(*message.get());
|
||||
on_message_complition(message->serial());
|
||||
}
|
||||
}
|
||||
|
||||
void RedChannel::on_event()
|
||||
{
|
||||
if (_outgoing_message) {
|
||||
RedPeer::OutMessage& peer_message = _outgoing_message->peer_message();
|
||||
_outgoing_pos += send(peer_message.base() + _outgoing_pos,
|
||||
peer_message.message_size() - _outgoing_pos);
|
||||
if (_outgoing_pos == peer_message.message_size()) {
|
||||
_outgoing_message->release();
|
||||
_outgoing_message = NULL;
|
||||
}
|
||||
}
|
||||
send_messages();
|
||||
|
||||
if (_incomming_header_pos) {
|
||||
_incomming_header_pos += RedPeer::recive(((uint8_t*)&_incomming_header) +
|
||||
_incomming_header_pos,
|
||||
sizeof(RedDataHeader) - _incomming_header_pos);
|
||||
if (_incomming_header_pos != sizeof(RedDataHeader)) {
|
||||
return;
|
||||
}
|
||||
_incomming_header_pos = 0;
|
||||
_incomming_message = new CompundInMessage(_incomming_header.serial,
|
||||
_incomming_header.type,
|
||||
_incomming_header.size,
|
||||
_incomming_header.sub_list);
|
||||
_incomming_message_pos = 0;
|
||||
}
|
||||
|
||||
if (_incomming_message) {
|
||||
_incomming_message_pos += RedPeer::recive(_incomming_message->data() +
|
||||
_incomming_message_pos,
|
||||
_incomming_message->compund_size() -
|
||||
_incomming_message_pos);
|
||||
if (_incomming_message_pos != _incomming_message->compund_size()) {
|
||||
return;
|
||||
}
|
||||
std::auto_ptr<CompundInMessage> message(_incomming_message);
|
||||
_incomming_message = NULL;
|
||||
on_message_recived();
|
||||
_message_handler->handle_message(*message.get());
|
||||
on_message_complition(message->serial());
|
||||
}
|
||||
recive_messages();
|
||||
}
|
||||
|
||||
void RedChannel::send_migrate_flush_mark()
|
||||
{
|
||||
if (_outgoing_message) {
|
||||
RedPeer::OutMessage& peer_message = _outgoing_message->peer_message();
|
||||
send(peer_message.base() + _outgoing_pos, peer_message.message_size() - _outgoing_pos);
|
||||
_outgoing_message->release();
|
||||
_outgoing_message = NULL;
|
||||
}
|
||||
Lock lock(_outgoing_lock);
|
||||
for (;;) {
|
||||
AutoMessage message(get_outgoing_message());
|
||||
if (!message.get()) {
|
||||
break;
|
||||
}
|
||||
send(message.get()->peer_message());
|
||||
}
|
||||
lock.unlock();
|
||||
std::auto_ptr<RedPeer::OutMessage> message(new RedPeer::OutMessage(REDC_MIGRATE_FLUSH_MARK, 0));
|
||||
send(*message);
|
||||
}
|
||||
|
||||
void RedChannel::handle_migrate(RedPeer::InMessage* message)
|
||||
{
|
||||
DBG(0, "channel type %u id %u", get_type(), get_id());
|
||||
_socket_in_loop = false;
|
||||
_loop.remove_socket(*this);
|
||||
RedMigrate* migrate = (RedMigrate*)message->data();
|
||||
if (migrate->flags & RED_MIGRATE_NEED_FLUSH) {
|
||||
send_migrate_flush_mark();
|
||||
}
|
||||
std::auto_ptr<RedPeer::CompundInMessage> data_message;
|
||||
if (migrate->flags & RED_MIGRATE_NEED_DATA_TRANSFER) {
|
||||
data_message.reset(recive());
|
||||
}
|
||||
_client.migrate_channel(*this);
|
||||
if (migrate->flags & RED_MIGRATE_NEED_DATA_TRANSFER) {
|
||||
if (data_message->type() != RED_MIGRATE_DATA) {
|
||||
THROW("expect RED_MIGRATE_DATA");
|
||||
}
|
||||
std::auto_ptr<RedPeer::OutMessage> message(new RedPeer::OutMessage(REDC_MIGRATE_DATA,
|
||||
data_message->size()));
|
||||
memcpy(message->data(), data_message->data(), data_message->size());
|
||||
send(*message);
|
||||
}
|
||||
_loop.add_socket(*this);
|
||||
_socket_in_loop = true;
|
||||
on_migrate();
|
||||
set_state(CONNECTED_STATE);
|
||||
on_event();
|
||||
}
|
||||
|
||||
void RedChannel::handle_set_ack(RedPeer::InMessage* message)
|
||||
{
|
||||
RedSetAck* ack = (RedSetAck*)message->data();
|
||||
_message_ack_window = _message_ack_count = ack->window;
|
||||
Message *responce = new Message(REDC_ACK_SYNC, sizeof(uint32_t));
|
||||
*(uint32_t *)responce->data() = ack->generation;
|
||||
post_message(responce);
|
||||
}
|
||||
|
||||
void RedChannel::handle_ping(RedPeer::InMessage* message)
|
||||
{
|
||||
RedPing *ping = (RedPing *)message->data();
|
||||
Message *pong = new Message(REDC_PONG, sizeof(RedPing));
|
||||
*(RedPing *)pong->data() = *ping;
|
||||
post_message(pong);
|
||||
}
|
||||
|
||||
void RedChannel::handle_disconnect(RedPeer::InMessage* message)
|
||||
{
|
||||
RedDisconnect *disconnect = (RedDisconnect *)message->data();
|
||||
_disconnect_stamp = disconnect->time_stamp;
|
||||
_disconnect_reason = disconnect->reason;
|
||||
}
|
||||
|
||||
void RedChannel::handle_notify(RedPeer::InMessage* message)
|
||||
{
|
||||
RedNotify *notify = (RedNotify *)message->data();
|
||||
const char *sevirity;
|
||||
const char *visibility;
|
||||
const char *message_str = "";
|
||||
const char *message_prefix = "";
|
||||
|
||||
static const char* sevirity_strings[] = {"info", "warn", "error"};
|
||||
static const char* visibility_strings[] = {"!", "!!", "!!!"};
|
||||
|
||||
|
||||
if (notify->severty > RED_NOTIFY_SEVERITY_ERROR) {
|
||||
THROW("bad sevirity");
|
||||
}
|
||||
sevirity = sevirity_strings[notify->severty];
|
||||
|
||||
if (notify->visibilty > RED_NOTIFY_VISIBILITY_HIGH) {
|
||||
THROW("bad visibilty");
|
||||
}
|
||||
visibility = visibility_strings[notify->visibilty];
|
||||
|
||||
|
||||
if (notify->message_len) {
|
||||
if ((message->size() - sizeof(*notify) < notify->message_len + 1)) {
|
||||
THROW("access violation");
|
||||
}
|
||||
message_str = (char *)(notify + 1);
|
||||
if (message_str[notify->message_len] != 0) {
|
||||
THROW("invalid message");
|
||||
}
|
||||
message_prefix = ": ";
|
||||
}
|
||||
|
||||
|
||||
LOG_INFO("remote channel %u:%u %s%s #%u%s%s",
|
||||
get_type(), get_id(),
|
||||
sevirity, visibility,
|
||||
notify->what,
|
||||
message_prefix, message_str);
|
||||
}
|
||||
|
||||
void RedChannel::handle_wait_for_channels(RedPeer::InMessage* message)
|
||||
{
|
||||
RedWaitForChannels *wait = (RedWaitForChannels *)message->data();
|
||||
if (message->size() < sizeof(*wait) + wait->wait_count * sizeof(wait->wait_list[0])) {
|
||||
THROW("access violation");
|
||||
}
|
||||
_client.wait_for_channels(wait->wait_count, wait->wait_list);
|
||||
}
|
||||
|
||||
296
client/red_channel.h
Normal file
296
client/red_channel.h
Normal file
@ -0,0 +1,296 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_REDCHANNEL
|
||||
#define _H_REDCHANNEL
|
||||
|
||||
#include "common.h"
|
||||
#include "utils.h"
|
||||
#include "threads.h"
|
||||
#include "red_peer.h"
|
||||
#include "platform.h"
|
||||
#include "events_loop.h"
|
||||
|
||||
enum {
|
||||
PASSIVE_STATE,
|
||||
DISCONNECTED_STATE,
|
||||
CONNECTING_STATE,
|
||||
CONNECTED_STATE,
|
||||
TERMINATED_STATE,
|
||||
};
|
||||
|
||||
enum {
|
||||
WAIT_ACTION,
|
||||
CONNECT_ACTION,
|
||||
DISCONNECT_ACTION,
|
||||
QUIT_ACTION,
|
||||
};
|
||||
|
||||
class RedClient;
|
||||
class RedChannel;
|
||||
|
||||
typedef std::vector<uint32_t> ChannelCaps;
|
||||
|
||||
class RedChannelBase: public RedPeer {
|
||||
public:
|
||||
RedChannelBase(uint8_t type, uint8_t id, const ChannelCaps& common_caps,
|
||||
const ChannelCaps& caps);
|
||||
|
||||
virtual ~RedChannelBase();
|
||||
|
||||
uint8_t get_type() { return _type;}
|
||||
uint8_t get_id() { return _id;}
|
||||
|
||||
void connect(const ConnectionOptions& options, uint32_t connection_id, uint32_t ip,
|
||||
std::string password);
|
||||
void connect(const ConnectionOptions& options, uint32_t connection_id, const char *host,
|
||||
std::string password);
|
||||
|
||||
const ChannelCaps& get_common_caps() { return _common_caps;}
|
||||
const ChannelCaps& get_caps() {return _caps;}
|
||||
|
||||
protected:
|
||||
void set_common_capability(uint32_t cap);
|
||||
void set_capability(uint32_t cap);
|
||||
bool test_common_capability(uint32_t cap);
|
||||
bool test_capability(uint32_t cap);
|
||||
|
||||
private:
|
||||
void set_capability(ChannelCaps& caps, uint32_t cap);
|
||||
bool test_capability(const ChannelCaps& caps, uint32_t cap);
|
||||
void link(uint32_t connection_id, const std::string& password);
|
||||
|
||||
private:
|
||||
uint8_t _type;
|
||||
uint8_t _id;
|
||||
|
||||
ChannelCaps _common_caps;
|
||||
ChannelCaps _caps;
|
||||
|
||||
ChannelCaps _remote_common_caps;
|
||||
ChannelCaps _remote_caps;
|
||||
};
|
||||
|
||||
class SendTrigger: public EventsLoop::Trigger {
|
||||
public:
|
||||
SendTrigger(RedChannel& channel);
|
||||
|
||||
virtual void on_event();
|
||||
|
||||
private:
|
||||
RedChannel& _channel;
|
||||
};
|
||||
|
||||
class AbortTrigger: public EventsLoop::Trigger {
|
||||
public:
|
||||
virtual void on_event();
|
||||
};
|
||||
|
||||
struct SyncInfo {
|
||||
Mutex* lock;
|
||||
Condition* condition;
|
||||
uint64_t* message_serial;
|
||||
};
|
||||
|
||||
class RedChannel: public RedChannelBase {
|
||||
public:
|
||||
friend class RedCannel;
|
||||
class MessageHandler;
|
||||
class OutMessage;
|
||||
|
||||
RedChannel(RedClient& client, uint8_t type, uint8_t id, MessageHandler* handler,
|
||||
Platform::ThreadPriority worker_priority = Platform::PRIORITY_NORMAL);
|
||||
virtual ~RedChannel();
|
||||
void start();
|
||||
|
||||
virtual void connect();
|
||||
virtual void disconnect();
|
||||
virtual bool abort();
|
||||
|
||||
virtual CompundInMessage *recive();
|
||||
|
||||
virtual void post_message(RedChannel::OutMessage* message);
|
||||
int get_connection_error() { return _error;}
|
||||
Platform::ThreadPriority get_worker_priority() { return _worker_priority;}
|
||||
|
||||
protected:
|
||||
RedClient& get_client() { return _client;}
|
||||
EventsLoop& get_events_loop() { return _loop;}
|
||||
MessageHandler* get_message_handler() { return _message_handler.get();}
|
||||
virtual void on_connecting() {}
|
||||
virtual void on_connect() {}
|
||||
virtual void on_disconnect() {}
|
||||
virtual void on_migrate() {}
|
||||
void handle_migrate(RedPeer::InMessage* message);
|
||||
void handle_set_ack(RedPeer::InMessage* message);
|
||||
void handle_ping(RedPeer::InMessage* message);
|
||||
void handle_wait_for_channels(RedPeer::InMessage* message);
|
||||
void handle_disconnect(RedPeer::InMessage* message);
|
||||
void handle_notify(RedPeer::InMessage* message);
|
||||
|
||||
private:
|
||||
void set_state(int state);
|
||||
void run();
|
||||
void send_migrate_flush_mark();
|
||||
void send_messages();
|
||||
void recive_messages();
|
||||
void on_send_trigger();
|
||||
virtual void on_event();
|
||||
void on_message_recived();
|
||||
void on_message_complition(uint64_t serial);
|
||||
|
||||
static void* worker_main(void *);
|
||||
|
||||
RedChannel::OutMessage* get_outgoing_message();
|
||||
void clear_outgoing_messages();
|
||||
|
||||
private:
|
||||
RedClient& _client;
|
||||
int _state;
|
||||
int _action;
|
||||
int _error;
|
||||
bool _wait_for_threads;
|
||||
bool _socket_in_loop;
|
||||
|
||||
Thread* _worker;
|
||||
Platform::ThreadPriority _worker_priority;
|
||||
std::auto_ptr<MessageHandler> _message_handler;
|
||||
Mutex _state_lock;
|
||||
Condition _state_cond;
|
||||
Mutex _action_lock;
|
||||
Condition _action_cond;
|
||||
SyncInfo _sync_info;
|
||||
|
||||
Mutex _outgoing_lock;
|
||||
std::list<RedChannel::OutMessage*> _outgoing_messages;
|
||||
RedChannel::OutMessage* _outgoing_message;
|
||||
uint32_t _outgoing_pos;
|
||||
|
||||
RedDataHeader _incomming_header;
|
||||
uint32_t _incomming_header_pos;
|
||||
RedPeer::CompundInMessage* _incomming_message;
|
||||
uint32_t _incomming_message_pos;
|
||||
|
||||
uint32_t _message_ack_count;
|
||||
uint32_t _message_ack_window;
|
||||
|
||||
EventsLoop _loop;
|
||||
SendTrigger _send_trigger;
|
||||
AbortTrigger _abort_trigger;
|
||||
|
||||
uint64_t _disconnect_stamp;
|
||||
uint64_t _disconnect_reason;
|
||||
|
||||
friend class SendTrigger;
|
||||
};
|
||||
|
||||
|
||||
class RedChannel::OutMessage {
|
||||
public:
|
||||
OutMessage() {}
|
||||
virtual ~OutMessage() {}
|
||||
|
||||
virtual RedPeer::OutMessage& peer_message() = 0;
|
||||
virtual void release() = 0;
|
||||
};
|
||||
|
||||
class Message: public RedChannel::OutMessage, public RedPeer::OutMessage {
|
||||
public:
|
||||
Message(uint32_t type, uint32_t size)
|
||||
: RedChannel::OutMessage()
|
||||
, RedPeer::OutMessage(type, size)
|
||||
{
|
||||
}
|
||||
|
||||
virtual RedPeer::OutMessage& peer_message() { return *this;}
|
||||
virtual void release() {delete this;}
|
||||
};
|
||||
|
||||
|
||||
class RedChannel::MessageHandler {
|
||||
public:
|
||||
MessageHandler() {}
|
||||
virtual ~MessageHandler() {}
|
||||
virtual void handle_message(RedPeer::CompundInMessage& message) = 0;
|
||||
};
|
||||
|
||||
|
||||
template <class HandlerClass, unsigned int end_message>
|
||||
class MessageHandlerImp: public RedChannel::MessageHandler {
|
||||
public:
|
||||
MessageHandlerImp(HandlerClass& obj);
|
||||
virtual ~MessageHandlerImp() {}
|
||||
virtual void handle_message(RedPeer::CompundInMessage& message);
|
||||
typedef void (HandlerClass::*Handler)(RedPeer::InMessage* message);
|
||||
void set_handler(unsigned int id, Handler handler, size_t mess_size);
|
||||
|
||||
private:
|
||||
HandlerClass& _obj;
|
||||
struct HandlerInfo {
|
||||
Handler handler;
|
||||
size_t mess_size;
|
||||
};
|
||||
|
||||
HandlerInfo _handlers[end_message];
|
||||
};
|
||||
|
||||
template <class HandlerClass, unsigned int end_message>
|
||||
MessageHandlerImp<HandlerClass, end_message>::MessageHandlerImp(HandlerClass& obj)
|
||||
: _obj (obj)
|
||||
{
|
||||
memset(_handlers, 0, sizeof(_handlers));
|
||||
}
|
||||
|
||||
template <class HandlerClass, unsigned int end_message>
|
||||
void MessageHandlerImp<HandlerClass, end_message>::handle_message(RedPeer::CompundInMessage&
|
||||
message)
|
||||
{
|
||||
if (message.type() >= end_message || !_handlers[message.type()].handler) {
|
||||
THROW("bad message type %d", message.type());
|
||||
}
|
||||
if (message.size() < _handlers[message.type()].mess_size) {
|
||||
THROW("bad message size, type %d size %d expected %d",
|
||||
message.type(),
|
||||
message.size(),
|
||||
_handlers[message.type()].mess_size);
|
||||
}
|
||||
if (message.sub_list()) {
|
||||
RedSubMessageList *sub_list;
|
||||
sub_list = (RedSubMessageList *)(message.data() + message.sub_list());
|
||||
for (int i = 0; i < sub_list->size; i++) {
|
||||
RedSubMessage *sub = (RedSubMessage *)(message.data() + sub_list->sub_messages[i]);
|
||||
//todo: test size
|
||||
RedPeer::InMessage sub_message(sub->type, sub->size, (uint8_t *)(sub + 1));
|
||||
(_obj.*_handlers[sub_message.type()].handler)(&sub_message);
|
||||
}
|
||||
}
|
||||
(_obj.*_handlers[message.type()].handler)(&message);
|
||||
}
|
||||
|
||||
template <class HandlerClass, unsigned int end_message>
|
||||
void MessageHandlerImp<HandlerClass, end_message>::set_handler(unsigned int id, Handler handler,
|
||||
size_t mess_size)
|
||||
{
|
||||
if (id >= end_message) {
|
||||
THROW("bad handler id");
|
||||
}
|
||||
_handlers[id].handler = handler;
|
||||
_handlers[id].mess_size = mess_size;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
811
client/red_client.cpp
Normal file
811
client/red_client.cpp
Normal file
@ -0,0 +1,811 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include <math.h>
|
||||
#include "red_client.h"
|
||||
#include "application.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
|
||||
Migrate::Migrate(RedClient& client)
|
||||
: _client (client)
|
||||
, _running (false)
|
||||
, _aborting (false)
|
||||
, _connected (false)
|
||||
, _thread (NULL)
|
||||
, _pending_con (0)
|
||||
{
|
||||
}
|
||||
|
||||
Migrate::~Migrate()
|
||||
{
|
||||
ASSERT(!_thread);
|
||||
delete_channels();
|
||||
}
|
||||
|
||||
void Migrate::delete_channels()
|
||||
{
|
||||
while (!_channels.empty()) {
|
||||
MigChannels::iterator iter = _channels.begin();
|
||||
delete *iter;
|
||||
_channels.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
void Migrate::clear_channels()
|
||||
{
|
||||
Lock lock(_lock);
|
||||
ASSERT(!_running);
|
||||
delete_channels();
|
||||
}
|
||||
|
||||
void Migrate::add_channel(MigChannel* channel)
|
||||
{
|
||||
Lock lock(_lock);
|
||||
_channels.push_back(channel);
|
||||
}
|
||||
|
||||
void Migrate::swap_peer(RedChannelBase& other)
|
||||
{
|
||||
DBG(0, "channel type %u id %u", other.get_type(), other.get_id());
|
||||
try {
|
||||
Lock lock(_lock);
|
||||
MigChannels::iterator iter = _channels.begin();
|
||||
|
||||
if (_running) {
|
||||
THROW("swap and running");
|
||||
}
|
||||
|
||||
if (!_connected) {
|
||||
THROW("not connected");
|
||||
}
|
||||
|
||||
for (; iter != _channels.end(); ++iter) {
|
||||
MigChannel* curr = *iter;
|
||||
if (curr->get_type() == other.get_type() && curr->get_id() == other.get_id()) {
|
||||
if (!curr->is_valid()) {
|
||||
THROW("invalid");
|
||||
}
|
||||
other.swap(curr);
|
||||
curr->set_valid(false);
|
||||
if (!--_pending_con) {
|
||||
lock.unlock();
|
||||
_client.set_target(_host.c_str(), _port, _sport);
|
||||
abort();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
THROW("no channel");
|
||||
} catch (...) {
|
||||
abort();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void Migrate::connect_one(MigChannel& channel, const RedPeer::ConnectionOptions& options,
|
||||
uint32_t connection_id)
|
||||
{
|
||||
if (_aborting) {
|
||||
DBG(0, "aborting");
|
||||
THROW("aborting");
|
||||
}
|
||||
channel.connect(options, connection_id, _host.c_str(), _password);
|
||||
++_pending_con;
|
||||
channel.set_valid(true);
|
||||
}
|
||||
|
||||
void Migrate::run()
|
||||
{
|
||||
uint32_t connection_id;
|
||||
|
||||
DBG(0, "");
|
||||
try {
|
||||
RedPeer::ConnectionOptions con_opt(_client.get_connection_options(RED_CHANNEL_MAIN),
|
||||
_port, _port);
|
||||
MigChannels::iterator iter = _channels.begin();
|
||||
connection_id = _client.get_connection_id();
|
||||
connect_one(**iter, con_opt, connection_id);
|
||||
for (++iter; iter != _channels.end(); ++iter) {
|
||||
con_opt = RedPeer::ConnectionOptions(
|
||||
_client.get_connection_options((*iter)->get_type()),
|
||||
_port, _sport);
|
||||
connect_one(**iter, con_opt, connection_id);
|
||||
}
|
||||
_connected = true;
|
||||
DBG(0, "connected");
|
||||
} catch (...) {
|
||||
close_channels();
|
||||
}
|
||||
|
||||
Lock lock(_lock);
|
||||
_cond.notify_one();
|
||||
if (_connected) {
|
||||
Message* message = new Message(REDC_MIGRATE_CONNECTED, 0);
|
||||
_client.post_message(message);
|
||||
} else {
|
||||
Message* message = new Message(REDC_MIGRATE_CONNECT_ERROR, 0);
|
||||
_client.post_message(message);
|
||||
}
|
||||
_running = false;
|
||||
}
|
||||
|
||||
void* Migrate::worker_main(void *data)
|
||||
{
|
||||
Migrate* mig = (Migrate*)data;
|
||||
mig->run();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Migrate::start(const RedMigrationBegin* migrate)
|
||||
{
|
||||
DBG(0, "");
|
||||
abort();
|
||||
_host.assign(migrate->host);
|
||||
_port = migrate->port ? migrate->port : -1;
|
||||
_sport = migrate->sport ? migrate->sport : -1;
|
||||
_password = _client._password;
|
||||
Lock lock(_lock);
|
||||
_running = true;
|
||||
lock.unlock();
|
||||
_thread = new Thread(Migrate::worker_main, this);
|
||||
}
|
||||
|
||||
void Migrate::disconnect_channels()
|
||||
{
|
||||
MigChannels::iterator iter = _channels.begin();
|
||||
|
||||
for (; iter != _channels.end(); ++iter) {
|
||||
(*iter)->disconnect();
|
||||
(*iter)->set_valid(false);
|
||||
}
|
||||
}
|
||||
|
||||
void Migrate::close_channels()
|
||||
{
|
||||
MigChannels::iterator iter = _channels.begin();
|
||||
|
||||
for (; iter != _channels.end(); ++iter) {
|
||||
(*iter)->close();
|
||||
(*iter)->set_valid(false);
|
||||
(*iter)->enable();
|
||||
}
|
||||
}
|
||||
|
||||
bool Migrate::abort()
|
||||
{
|
||||
Lock lock(_lock);
|
||||
if (_aborting) {
|
||||
return false;
|
||||
}
|
||||
_aborting = true;
|
||||
for (;;) {
|
||||
disconnect_channels();
|
||||
if (!_running) {
|
||||
break;
|
||||
}
|
||||
uint64_t timout = 1000 * 1000 * 10; /*10ms*/
|
||||
_cond.timed_wait(lock, timout);
|
||||
}
|
||||
close_channels();
|
||||
_pending_con = 0;
|
||||
_connected = false;
|
||||
_aborting = false;
|
||||
if (_thread) {
|
||||
_thread->join();
|
||||
delete _thread;
|
||||
_thread = NULL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#define AGENT_TIMEOUT (1000 * 30)
|
||||
|
||||
void agent_timer_proc(void *opaque, TimerID timer)
|
||||
{
|
||||
Platform::deactivate_interval_timer(timer);
|
||||
THROW_ERR(SPICEC_ERROR_CODE_AGENT_TIMEOUT, "vdagent timeout");
|
||||
}
|
||||
|
||||
class MainChannelLoop: public MessageHandlerImp<RedClient, RED_MESSAGES_END> {
|
||||
public:
|
||||
MainChannelLoop(RedClient& client): MessageHandlerImp<RedClient, RED_MESSAGES_END>(client) {}
|
||||
};
|
||||
|
||||
RedClient::RedClient(Application& application)
|
||||
: RedChannel(*this, RED_CHANNEL_MAIN, 0, new MainChannelLoop(*this))
|
||||
, _application (application)
|
||||
, _connection_id (0)
|
||||
, _mouse_mode (RED_MOUSE_MODE_SERVER)
|
||||
, _notify_disconnect (false)
|
||||
, _aborting (false)
|
||||
, _agent_connected (false)
|
||||
, _agent_mon_config_sent (false)
|
||||
, _agent_msg (new VDAgentMessage)
|
||||
, _agent_msg_data (NULL)
|
||||
, _agent_msg_pos (0)
|
||||
, _agent_tokens (0)
|
||||
, _agent_timer (Platform::create_interval_timer(agent_timer_proc, NULL))
|
||||
, _migrate (*this)
|
||||
, _glz_window (0, _glz_debug)
|
||||
{
|
||||
MainChannelLoop* message_loop = static_cast<MainChannelLoop*>(get_message_handler());
|
||||
|
||||
message_loop->set_handler(RED_MIGRATE, &RedClient::handle_migrate, 0);
|
||||
message_loop->set_handler(RED_SET_ACK, &RedClient::handle_set_ack, sizeof(RedSetAck));
|
||||
message_loop->set_handler(RED_PING, &RedClient::handle_ping, sizeof(RedPing));
|
||||
message_loop->set_handler(RED_WAIT_FOR_CHANNELS, &RedClient::handle_wait_for_channels,
|
||||
sizeof(RedWaitForChannels));
|
||||
message_loop->set_handler(RED_DISCONNECTING, &RedClient::handle_disconnect,
|
||||
sizeof(RedDisconnect));
|
||||
message_loop->set_handler(RED_NOTIFY, &RedClient::handle_notify, sizeof(RedNotify));
|
||||
|
||||
message_loop->set_handler(RED_MIGRATE_BEGIN, &RedClient::handle_migrate_begin,
|
||||
sizeof(RedMigrationBegin));
|
||||
message_loop->set_handler(RED_MIGRATE_CANCEL, &RedClient::handle_migrate_cancel, 0);
|
||||
message_loop->set_handler(RED_INIT, &RedClient::handle_init, sizeof(RedInit));
|
||||
message_loop->set_handler(RED_CHANNELS_LIST, &RedClient::handle_channels,
|
||||
sizeof(RedChannels));
|
||||
message_loop->set_handler(RED_MOUSE_MODE, &RedClient::handle_mouse_mode,
|
||||
sizeof(RedMouseMode));
|
||||
message_loop->set_handler(RED_MULTI_MEDIA_TIME, &RedClient::handle_mm_time,
|
||||
sizeof(RedMultiMediaTime));
|
||||
|
||||
message_loop->set_handler(RED_AGENT_CONNECTED, &RedClient::handle_agent_connected, 0);
|
||||
message_loop->set_handler(RED_AGENT_DISCONNECTED, &RedClient::handle_agent_disconnected,
|
||||
sizeof(RedAgentDisconnect));
|
||||
message_loop->set_handler(RED_AGENT_DATA, &RedClient::handle_agent_data, 0);
|
||||
message_loop->set_handler(RED_AGENT_TOKEN, &RedClient::handle_agent_tokens,
|
||||
sizeof(RedAgentTokens));
|
||||
if (_agent_timer == INVALID_TIMER) {
|
||||
THROW("invalid agent timer");
|
||||
}
|
||||
start();
|
||||
}
|
||||
|
||||
RedClient::~RedClient()
|
||||
{
|
||||
ASSERT(_channels.empty());
|
||||
Platform::destroy_interval_timer(_agent_timer);
|
||||
delete _agent_msg;
|
||||
}
|
||||
|
||||
void RedClient::init(const char* host, int port, int sport, const char *password,
|
||||
bool auto_display_res)
|
||||
{
|
||||
_host = host;
|
||||
_port = port;
|
||||
_sport = sport;
|
||||
_auto_display_res = auto_display_res;
|
||||
|
||||
if (password != NULL) {
|
||||
_password = password;
|
||||
} else {
|
||||
_password = "";
|
||||
}
|
||||
}
|
||||
|
||||
void RedClient::set_target(const char* host, uint16_t port, uint16_t sport)
|
||||
{
|
||||
_port = port;
|
||||
_sport = sport;
|
||||
_host.assign(host);
|
||||
}
|
||||
|
||||
void RedClient::push_event(Event* event)
|
||||
{
|
||||
_application.push_event(event);
|
||||
}
|
||||
|
||||
void RedClient::on_connecting()
|
||||
{
|
||||
_notify_disconnect = true;
|
||||
}
|
||||
|
||||
void RedClient::on_connect()
|
||||
{
|
||||
push_event(new ConnectedEvent());
|
||||
_migrate.add_channel(new MigChannel(RED_CHANNEL_MAIN, 0, get_common_caps(),
|
||||
get_caps()));
|
||||
}
|
||||
|
||||
void RedClient::on_disconnect()
|
||||
{
|
||||
_migrate.abort();
|
||||
_connection_id = 0;
|
||||
Platform::deactivate_interval_timer(_agent_timer);
|
||||
_agent_mon_config_sent = false;
|
||||
delete[] _agent_msg_data;
|
||||
_agent_msg_data = NULL;
|
||||
_agent_msg_pos = 0;
|
||||
_agent_tokens = 0;
|
||||
}
|
||||
|
||||
void RedClient::delete_channels()
|
||||
{
|
||||
Lock lock(_channels_lock);
|
||||
Channels::iterator iter = _channels.begin();
|
||||
while (!_channels.empty()) {
|
||||
RedChannel *channel = *_channels.begin();
|
||||
_channels.pop_front();
|
||||
delete channel;
|
||||
}
|
||||
}
|
||||
|
||||
RedPeer::ConnectionOptions::Type RedClient::get_connection_options(uint32_t channel_type)
|
||||
{
|
||||
return _con_opt_map[channel_type];
|
||||
}
|
||||
|
||||
void RedClient::connect()
|
||||
{
|
||||
//todo wait for disconnect state
|
||||
if (_connection_id || !abort_channels()) {
|
||||
return;
|
||||
}
|
||||
_pixmap_cache.clear();
|
||||
_glz_window.clear();
|
||||
memset(_sync_info, 0, sizeof(_sync_info));
|
||||
_aborting = false;
|
||||
_migrate.clear_channels();
|
||||
delete_channels();
|
||||
enable();
|
||||
|
||||
_con_opt_map.clear();
|
||||
PeerConnectionOptMap::const_iterator iter = _application.get_con_opt_map().begin();
|
||||
PeerConnectionOptMap::const_iterator end = _application.get_con_opt_map().end();
|
||||
for (; iter != end; iter++) {
|
||||
_con_opt_map[(*iter).first] = (*iter).second;
|
||||
}
|
||||
RedChannel::connect();
|
||||
}
|
||||
|
||||
void RedClient::disconnect()
|
||||
{
|
||||
_migrate.abort();
|
||||
RedChannel::disconnect();
|
||||
}
|
||||
|
||||
void RedClient::disconnect_channels()
|
||||
{
|
||||
Lock lock(_channels_lock);
|
||||
Channels::iterator iter = _channels.begin();
|
||||
for (; iter != _channels.end(); ++iter) {
|
||||
(*iter)->RedPeer::disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
void RedClient::on_channel_disconnected(RedChannel& channel)
|
||||
{
|
||||
Lock lock(_notify_lock);
|
||||
if (_notify_disconnect) {
|
||||
_notify_disconnect = false;
|
||||
int connection_error = channel.get_connection_error();
|
||||
if (connection_error == SPICEC_ERROR_CODE_SUCCESS) {
|
||||
LOG_INFO("disconneted");
|
||||
push_event(new DisconnectedEvent());
|
||||
} else {
|
||||
push_event(new CoonnectionError(connection_error));
|
||||
}
|
||||
}
|
||||
disconnect_channels();
|
||||
RedPeer::disconnect();
|
||||
}
|
||||
|
||||
bool RedClient::abort_channels()
|
||||
{
|
||||
Lock lock(_channels_lock);
|
||||
Channels::iterator iter = _channels.begin();
|
||||
|
||||
for (; iter != _channels.end(); ++iter) {
|
||||
if (!(*iter)->abort()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RedClient::abort()
|
||||
{
|
||||
if (!_aborting) {
|
||||
Lock lock(_sync_lock);
|
||||
_aborting = true;
|
||||
_sync_condition.notify_all();
|
||||
}
|
||||
_pixmap_cache.abort();
|
||||
_glz_window.abort();
|
||||
if (RedChannel::abort() && abort_channels()) {
|
||||
delete_channels();
|
||||
_migrate.abort();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void RedClient::handle_migrate_begin(RedPeer::InMessage* message)
|
||||
{
|
||||
DBG(0, "");
|
||||
RedMigrationBegin* migrate = (RedMigrationBegin*)message->data();
|
||||
//add mig channels
|
||||
_migrate.start(migrate);
|
||||
}
|
||||
|
||||
void RedClient::handle_migrate_cancel(RedPeer::InMessage* message)
|
||||
{
|
||||
_migrate.abort();
|
||||
}
|
||||
|
||||
ChannelFactory* RedClient::find_factory(uint32_t type)
|
||||
{
|
||||
Factorys::iterator iter = _factorys.begin();
|
||||
for (; iter != _factorys.end(); ++iter) {
|
||||
if ((*iter)->type() == type) {
|
||||
return *iter;
|
||||
}
|
||||
}
|
||||
LOG_WARN("no factory for %u", type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void RedClient::create_channel(uint32_t type, uint32_t id)
|
||||
{
|
||||
ChannelFactory* factory = find_factory(type);
|
||||
if (!factory) {
|
||||
return;
|
||||
}
|
||||
RedChannel* channel = factory->construct(*this, id);
|
||||
ASSERT(channel);
|
||||
Lock lock(_channels_lock);
|
||||
_channels.push_back(channel);
|
||||
channel->start();
|
||||
channel->connect();
|
||||
_migrate.add_channel(new MigChannel(type, id, channel->get_common_caps(), channel->get_caps()));
|
||||
}
|
||||
|
||||
void RedClient::send_agent_monitors_config()
|
||||
{
|
||||
AutoRef<MonitorsQuery > qury(new MonitorsQuery());
|
||||
push_event(*qury);
|
||||
(*qury)->wait();
|
||||
if (!(*qury)->success()) {
|
||||
THROW(" monitors query failed");
|
||||
}
|
||||
|
||||
double min_distance = HUGE;
|
||||
int dx = 0;
|
||||
int dy = 0;
|
||||
int i;
|
||||
|
||||
std::vector<MonitorInfo>& monitors = (*qury)->get_monitors();
|
||||
std::vector<MonitorInfo>::iterator iter = monitors.begin();
|
||||
for (; iter != monitors.end(); iter++) {
|
||||
double distance = sqrt(pow((double)(*iter).position.x, 2) + pow((double)(*iter).position.y,
|
||||
2));
|
||||
if (distance < min_distance) {
|
||||
min_distance = distance;
|
||||
dx = -(*iter).position.x;
|
||||
dy = -(*iter).position.y;
|
||||
}
|
||||
}
|
||||
|
||||
Message* message = new Message(REDC_AGENT_DATA,sizeof(VDAgentMessage) +
|
||||
sizeof(VDAgentMonitorsConfig) +
|
||||
monitors.size() * sizeof(VDAgentMonConfig));
|
||||
VDAgentMessage* msg = (VDAgentMessage*)message->data();
|
||||
msg->protocol = VD_AGENT_PROTOCOL;
|
||||
msg->type = VD_AGENT_MONITORS_CONFIG;
|
||||
msg->opaque = 0;
|
||||
msg->size = sizeof(VDAgentMonitorsConfig) + monitors.size() * sizeof(VDAgentMonConfig);
|
||||
|
||||
VDAgentMonitorsConfig* mon_config = (VDAgentMonitorsConfig*)msg->data;
|
||||
mon_config->num_of_monitors = monitors.size();
|
||||
mon_config->flags = 0;
|
||||
if (Platform::is_monitors_pos_valid()) {
|
||||
mon_config->flags = VD_AGENT_CONFIG_MONITORS_FLAG_USE_POS;
|
||||
}
|
||||
for (iter = monitors.begin(), i = 0; iter != monitors.end(); iter++, i++) {
|
||||
mon_config->monitors[i].depth = (*iter).depth;
|
||||
mon_config->monitors[i].width = (*iter).size.x;
|
||||
mon_config->monitors[i].height = (*iter).size.y;
|
||||
mon_config->monitors[i].x = (*iter).position.x + dx;
|
||||
mon_config->monitors[i].y = (*iter).position.y + dy;
|
||||
}
|
||||
ASSERT(_agent_tokens)
|
||||
_agent_tokens--;
|
||||
post_message(message);
|
||||
_agent_mon_config_sent = true;
|
||||
}
|
||||
|
||||
#define MIN_DISPLAY_PIXMAP_CACHE (1024 * 1024 * 20)
|
||||
#define MAX_DISPLAY_PIXMAP_CACHE (1024 * 1024 * 80)
|
||||
#define MIN_MEM_FOR_OTHERS (1024 * 1024 * 40)
|
||||
|
||||
// tmp till the pci mem will be shared by the qxls
|
||||
#define MIN_GLZ_WINDOW_SIZE (1024 * 1024 * 12)
|
||||
#define MAX_GLZ_WINDOW_SIZE MIN((LZ_MAX_WINDOW_SIZE * 4), 1024 * 1024 * 64)
|
||||
|
||||
void RedClient::calc_pixmap_cach_and_glz_window_size(uint32_t display_channels_hint,
|
||||
uint32_t pci_mem_hint)
|
||||
{
|
||||
#ifdef WIN32
|
||||
display_channels_hint = MAX(1, display_channels_hint);
|
||||
int max_cache_size = display_channels_hint * MAX_DISPLAY_PIXMAP_CACHE;
|
||||
int min_cache_size = display_channels_hint * MIN_DISPLAY_PIXMAP_CACHE;
|
||||
|
||||
MEMORYSTATUSEX mem_status;
|
||||
mem_status.dwLength = sizeof(mem_status);
|
||||
|
||||
if (!GlobalMemoryStatusEx(&mem_status)) {
|
||||
THROW("get mem status failed %u", GetLastError());
|
||||
}
|
||||
|
||||
//ullTotalPageFile is physical memory plus the size of the page file, minus a small overhead
|
||||
uint64_t free_mem = mem_status.ullAvailPageFile;
|
||||
if (free_mem < (min_cache_size + MIN_MEM_FOR_OTHERS + MIN_GLZ_WINDOW_SIZE)) {
|
||||
THROW_ERR(SPICEC_ERROR_CODE_NOT_ENOUGH_MEMORY, "low memory condition");
|
||||
}
|
||||
free_mem -= MIN_MEM_FOR_OTHERS;
|
||||
_glz_window_size = MIN(MAX_GLZ_WINDOW_SIZE, pci_mem_hint / 2);
|
||||
_glz_window_size = (int)MIN(free_mem / 3, _glz_window_size);
|
||||
_glz_window_size = MAX(MIN_GLZ_WINDOW_SIZE, _glz_window_size);
|
||||
free_mem -= _glz_window_size;
|
||||
_pixmap_cache_size = MIN(free_mem, mem_status.ullAvailVirtual);
|
||||
_pixmap_cache_size = MIN(free_mem, max_cache_size);
|
||||
#else
|
||||
//for now
|
||||
_glz_window_size = (int)MIN(MAX_GLZ_WINDOW_SIZE, pci_mem_hint / 2);
|
||||
_glz_window_size = MAX(MIN_GLZ_WINDOW_SIZE, _glz_window_size);
|
||||
_pixmap_cache_size = MAX_DISPLAY_PIXMAP_CACHE;
|
||||
#endif
|
||||
|
||||
_pixmap_cache_size /= 4;
|
||||
_glz_window_size /= 4;
|
||||
}
|
||||
|
||||
void RedClient::on_display_mode_change()
|
||||
{
|
||||
#ifdef USE_OGL
|
||||
Lock lock(_channels_lock);
|
||||
Channels::iterator iter = _channels.begin();
|
||||
for (; iter != _channels.end(); ++iter) {
|
||||
if ((*iter)->get_type() == RED_CHANNEL_DISPLAY) {
|
||||
((DisplayChannel *)(*iter))->recreate_ogl_context();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void RedClient::set_mouse_mode(uint32_t supported_modes, uint32_t current_mode)
|
||||
{
|
||||
if (current_mode != _mouse_mode) {
|
||||
_mouse_mode = current_mode;
|
||||
Lock lock(_channels_lock);
|
||||
Channels::iterator iter = _channels.begin();
|
||||
for (; iter != _channels.end(); ++iter) {
|
||||
if ((*iter)->get_type() == RED_CHANNEL_CURSOR) {
|
||||
((CursorChannel *)(*iter))->set_cursor_mode();
|
||||
}
|
||||
}
|
||||
}
|
||||
// FIXME: use configured mouse mode (currently, use client mouse mode if supported by server)
|
||||
if ((supported_modes & RED_MOUSE_MODE_CLIENT) && (current_mode != RED_MOUSE_MODE_CLIENT)) {
|
||||
Message* message = new Message(REDC_MOUSE_MODE_REQUEST, sizeof(RedcMouseModeRequest));
|
||||
RedcMouseModeRequest* mouse_mode_request = (RedcMouseModeRequest*)message->data();
|
||||
mouse_mode_request->mode = RED_MOUSE_MODE_CLIENT;
|
||||
post_message(message);
|
||||
}
|
||||
}
|
||||
|
||||
void RedClient::handle_init(RedPeer::InMessage* message)
|
||||
{
|
||||
RedInit *init = (RedInit *)message->data();
|
||||
_connection_id = init->session_id;
|
||||
set_mm_time(init->multi_media_time);
|
||||
calc_pixmap_cach_and_glz_window_size(init->display_channels_hint, init->ram_hint);
|
||||
_glz_window.set_pixels_capacity(_glz_window_size);
|
||||
set_mouse_mode(init->supported_mouse_modes, init->current_mouse_mode);
|
||||
_agent_tokens = init->agent_tokens;
|
||||
_agent_connected = !!init->agent_connected;
|
||||
if (_agent_connected) {
|
||||
Message* msg = new Message(REDC_AGENT_START, sizeof(RedcAgentStart));
|
||||
RedcAgentStart* agent_start = (RedcAgentStart *)msg->data();
|
||||
agent_start->num_tokens = ~0;
|
||||
post_message(msg);
|
||||
}
|
||||
if (_auto_display_res) {
|
||||
Platform::activate_interval_timer(_agent_timer, AGENT_TIMEOUT);
|
||||
if (_agent_connected) {
|
||||
send_agent_monitors_config();
|
||||
}
|
||||
} else {
|
||||
post_message(new Message(REDC_ATTACH_CHANNELS, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void RedClient::handle_channels(RedPeer::InMessage* message)
|
||||
{
|
||||
RedChannels *init = (RedChannels *)message->data();
|
||||
RedChannelInit* channels = init->channels;
|
||||
for (unsigned int i = 0; i < init->num_of_channels; i++) {
|
||||
create_channel(channels[i].type, channels[i].id);
|
||||
}
|
||||
}
|
||||
|
||||
void RedClient::handle_mouse_mode(RedPeer::InMessage* message)
|
||||
{
|
||||
RedMouseMode *mouse_mode = (RedMouseMode *)message->data();
|
||||
set_mouse_mode(mouse_mode->supported_modes, mouse_mode->current_mode);
|
||||
}
|
||||
|
||||
void RedClient::handle_mm_time(RedPeer::InMessage* message)
|
||||
{
|
||||
RedMultiMediaTime *mm_time = (RedMultiMediaTime *)message->data();
|
||||
set_mm_time(mm_time->time);
|
||||
}
|
||||
|
||||
void RedClient::handle_agent_connected(RedPeer::InMessage* message)
|
||||
{
|
||||
DBG(0, "");
|
||||
_agent_connected = true;
|
||||
Message* msg = new Message(REDC_AGENT_START, sizeof(RedcAgentStart));
|
||||
RedcAgentStart* agent_start = (RedcAgentStart *)msg->data();
|
||||
agent_start->num_tokens = ~0;
|
||||
post_message(msg);
|
||||
if (_auto_display_res && !_agent_mon_config_sent) {
|
||||
send_agent_monitors_config();
|
||||
}
|
||||
}
|
||||
|
||||
void RedClient::handle_agent_disconnected(RedPeer::InMessage* message)
|
||||
{
|
||||
DBG(0, "");
|
||||
_agent_connected = false;
|
||||
}
|
||||
|
||||
void RedClient::on_agent_reply(VDAgentReply* reply)
|
||||
{
|
||||
switch (reply->error) {
|
||||
case VD_AGENT_SUCCESS:
|
||||
break;
|
||||
case VD_AGENT_ERROR:
|
||||
THROW_ERR(SPICEC_ERROR_CODE_AGENT_ERROR, "vdagent error");
|
||||
default:
|
||||
THROW("unknown vdagent error");
|
||||
}
|
||||
switch (reply->type) {
|
||||
case VD_AGENT_MONITORS_CONFIG:
|
||||
post_message(new Message(REDC_ATTACH_CHANNELS, 0));
|
||||
Platform::deactivate_interval_timer(_agent_timer);
|
||||
break;
|
||||
default:
|
||||
THROW("unexpected vdagent reply type");
|
||||
}
|
||||
}
|
||||
|
||||
void RedClient::handle_agent_data(RedPeer::InMessage* message)
|
||||
{
|
||||
uint32_t msg_size = message->size();
|
||||
uint8_t* msg_pos = message->data();
|
||||
uint32_t n;
|
||||
|
||||
DBG(0, "");
|
||||
while (msg_size) {
|
||||
if (_agent_msg_pos < sizeof(VDAgentMessage)) {
|
||||
n = MIN(sizeof(VDAgentMessage) - _agent_msg_pos, msg_size);
|
||||
memcpy((uint8_t*)_agent_msg + _agent_msg_pos, msg_pos, n);
|
||||
_agent_msg_pos += n;
|
||||
msg_size -= n;
|
||||
msg_pos += n;
|
||||
if (_agent_msg_pos == sizeof(VDAgentMessage)) {
|
||||
if (_agent_msg->protocol != VD_AGENT_PROTOCOL) {
|
||||
THROW("Invalid protocol %u", _agent_msg->protocol);
|
||||
}
|
||||
_agent_msg_data = new uint8_t[_agent_msg->size];
|
||||
}
|
||||
}
|
||||
if (_agent_msg_pos >= sizeof(VDAgentMessage)) {
|
||||
n = MIN(sizeof(VDAgentMessage) + _agent_msg->size - _agent_msg_pos, msg_size);
|
||||
memcpy(_agent_msg_data + _agent_msg_pos - sizeof(VDAgentMessage), msg_pos, n);
|
||||
_agent_msg_pos += n;
|
||||
msg_size -= n;
|
||||
msg_pos += n;
|
||||
}
|
||||
if (_agent_msg_pos == sizeof(VDAgentMessage) + _agent_msg->size) {
|
||||
switch (_agent_msg->type) {
|
||||
case VD_AGENT_REPLY: {
|
||||
on_agent_reply((VDAgentReply*)_agent_msg_data);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
DBG(0, "Unsupported message type %u size %u", _agent_msg->type, _agent_msg->size);
|
||||
}
|
||||
delete[] _agent_msg_data;
|
||||
_agent_msg_data = NULL;
|
||||
_agent_msg_pos = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RedClient::handle_agent_tokens(RedPeer::InMessage* message)
|
||||
{
|
||||
RedAgentTokens *token = (RedAgentTokens *)message->data();
|
||||
_agent_tokens += token->num_tokens;
|
||||
}
|
||||
|
||||
void RedClient::migrate_channel(RedChannel& channel)
|
||||
{
|
||||
DBG(0, "channel type %u id %u", channel.get_type(), channel.get_id());
|
||||
_migrate.swap_peer(channel);
|
||||
}
|
||||
|
||||
void RedClient::get_sync_info(uint8_t channel_type, uint8_t channel_id, SyncInfo& info)
|
||||
{
|
||||
info.lock = &_sync_lock;
|
||||
info.condition = &_sync_condition;
|
||||
info.message_serial = &_sync_info[channel_type][channel_id];
|
||||
}
|
||||
|
||||
void RedClient::wait_for_channels(int wait_list_size, RedWaitForChannel* wait_list)
|
||||
{
|
||||
for (int i = 0; i < wait_list_size; i++) {
|
||||
if (wait_list[i].channel_type >= RED_CHANNEL_END) {
|
||||
THROW("invalid channel type %u", wait_list[i].channel_type);
|
||||
}
|
||||
uint64_t& sync_cell = _sync_info[wait_list[i].channel_type][wait_list[i].channel_id];
|
||||
#ifndef RED64
|
||||
Lock lock(_sync_lock);
|
||||
#endif
|
||||
if (sync_cell >= wait_list[i].message_serial) {
|
||||
continue;
|
||||
}
|
||||
#ifdef RED64
|
||||
Lock lock(_sync_lock);
|
||||
#endif
|
||||
for (;;) {
|
||||
if (sync_cell >= wait_list[i].message_serial) {
|
||||
break;
|
||||
}
|
||||
if (_aborting) {
|
||||
THROW("aborting");
|
||||
}
|
||||
_sync_condition.wait(lock);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RedClient::set_mm_time(uint32_t time)
|
||||
{
|
||||
Lock lock(_mm_clock_lock);
|
||||
_mm_clock_last_update = Platform::get_monolithic_time();
|
||||
_mm_time = time;
|
||||
}
|
||||
|
||||
uint32_t RedClient::get_mm_time()
|
||||
{
|
||||
Lock lock(_mm_clock_lock);
|
||||
return uint32_t((Platform::get_monolithic_time() - _mm_clock_last_update) / 1000 / 1000 +
|
||||
_mm_time);
|
||||
}
|
||||
|
||||
void RedClient::register_channel_factory(ChannelFactory& factory)
|
||||
{
|
||||
_factorys.push_back(&factory);
|
||||
}
|
||||
|
||||
234
client/red_client.h
Normal file
234
client/red_client.h
Normal file
@ -0,0 +1,234 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_REDCLIENT
|
||||
#define _H_REDCLIENT
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "common.h"
|
||||
#include "red_peer.h"
|
||||
#include "red_channel.h"
|
||||
#include "display_channel.h"
|
||||
#include "inputs_channel.h"
|
||||
#include "cursor_channel.h"
|
||||
#include "audio_channels.h"
|
||||
#include "red.h"
|
||||
#include "vd_agent.h"
|
||||
|
||||
class Application;
|
||||
class Event;
|
||||
|
||||
class MigChannel: public RedChannelBase {
|
||||
public:
|
||||
MigChannel(uint32_t type, uint32_t id, const ChannelCaps& common_caps, const ChannelCaps& caps)
|
||||
: RedChannelBase(type, id, common_caps, caps)
|
||||
, _valid(false) {}
|
||||
bool is_valid() { return _valid;}
|
||||
void set_valid(bool val) { _valid = val;}
|
||||
|
||||
private:
|
||||
bool _valid;
|
||||
};
|
||||
|
||||
class Migrate {
|
||||
public:
|
||||
Migrate(RedClient& client);
|
||||
~Migrate();
|
||||
|
||||
void start(const RedMigrationBegin* migrate);
|
||||
bool abort();
|
||||
void add_channel(MigChannel* channel);
|
||||
void clear_channels();
|
||||
void swap_peer(RedChannelBase& other);
|
||||
|
||||
private:
|
||||
void connect_one(MigChannel& channel, const RedPeer::ConnectionOptions& options,
|
||||
uint32_t connection_id);
|
||||
void disconnect_channels();
|
||||
void close_channels();
|
||||
void delete_channels();
|
||||
void run();
|
||||
static void* worker_main(void *data);
|
||||
|
||||
private:
|
||||
RedClient& _client;
|
||||
typedef std::list<MigChannel*> MigChannels;
|
||||
MigChannels _channels;
|
||||
bool _running;
|
||||
bool _aborting;
|
||||
bool _connected;
|
||||
std::string _password;
|
||||
std::string _host;
|
||||
int _port;
|
||||
int _sport;
|
||||
Thread* _thread;
|
||||
Mutex _lock;
|
||||
Condition _cond;
|
||||
int _pending_con;
|
||||
};
|
||||
|
||||
class ChannelFactory {
|
||||
public:
|
||||
ChannelFactory(uint32_t type) : _type (type) {}
|
||||
virtual ~ChannelFactory() {}
|
||||
|
||||
uint32_t type() { return _type;}
|
||||
virtual RedChannel* construct(RedClient& client, uint32_t id) = 0;
|
||||
|
||||
private:
|
||||
uint32_t _type;
|
||||
};
|
||||
|
||||
class GlzDecoderWindowDebug: public GlzDecoderDebug {
|
||||
public:
|
||||
virtual void error(const std::string& str)
|
||||
{
|
||||
throw Exception(str);
|
||||
}
|
||||
|
||||
virtual void warn(const std::string& str)
|
||||
{
|
||||
LOG_WARN("%s", str.c_str());
|
||||
}
|
||||
|
||||
virtual void info(const std::string& str)
|
||||
{
|
||||
LOG_INFO("%s", str.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map< int, RedPeer::ConnectionOptions::Type> PeerConnectionOptMap;
|
||||
|
||||
class RedClient: public RedChannel {
|
||||
public:
|
||||
friend class RedChannel;
|
||||
friend class Migrate;
|
||||
|
||||
RedClient(Application& application);
|
||||
~RedClient();
|
||||
|
||||
void init(const char* host, int port, int sport, const char *password, bool auto_display_res);
|
||||
|
||||
void register_channel_factory(ChannelFactory& factory);
|
||||
|
||||
virtual void connect();
|
||||
virtual void disconnect();
|
||||
virtual bool abort();
|
||||
|
||||
void push_event(Event* event);
|
||||
|
||||
void set_target(const char* host, uint16_t port, uint16_t sport);
|
||||
const char* get_password() { return _password.c_str();}
|
||||
const char* get_host() { return _host.c_str();}
|
||||
int get_port() { return _port;}
|
||||
int get_sport() { return _sport;}
|
||||
virtual uint32_t get_connection_id() { return _connection_id;}
|
||||
uint32_t get_mouse_mode() { return _mouse_mode;}
|
||||
Application& get_application() { return _application;}
|
||||
bool is_auto_display_res() { return _auto_display_res;}
|
||||
RedPeer::ConnectionOptions::Type get_connection_options(uint32_t channel_type);
|
||||
void get_sync_info(uint8_t channel_type, uint8_t channel_id, SyncInfo& info);
|
||||
void wait_for_channels(int wait_list_size, RedWaitForChannel* wait_list);
|
||||
PixmapCache& get_pixmap_cache() {return _pixmap_cache;}
|
||||
uint64_t get_pixmap_cache_size() { return _pixmap_cache_size;}
|
||||
void on_display_mode_change();
|
||||
|
||||
GlzDecoderWindow& get_glz_window() {return _glz_window;}
|
||||
int get_glz_window_size() { return _glz_window_size;}
|
||||
|
||||
void set_mm_time(uint32_t time);
|
||||
uint32_t get_mm_time();
|
||||
|
||||
protected:
|
||||
virtual void on_connecting();
|
||||
virtual void on_connect();
|
||||
virtual void on_disconnect();
|
||||
|
||||
private:
|
||||
void on_channel_disconnected(RedChannel& channel);
|
||||
void migrate_channel(RedChannel& channel);
|
||||
void send_agent_monitors_config();
|
||||
void calc_pixmap_cach_and_glz_window_size(uint32_t display_channels_hint,
|
||||
uint32_t pci_mem_hint);
|
||||
void set_mouse_mode(uint32_t supported_modes, uint32_t current_mode);
|
||||
|
||||
void handle_migrate_begin(RedPeer::InMessage* message);
|
||||
void handle_migrate_cancel(RedPeer::InMessage* message);
|
||||
void handle_init(RedPeer::InMessage* message);
|
||||
void handle_channels(RedPeer::InMessage* message);
|
||||
void handle_mouse_mode(RedPeer::InMessage* message);
|
||||
void handle_mm_time(RedPeer::InMessage* message);
|
||||
void handle_agent_connected(RedPeer::InMessage* message);
|
||||
void handle_agent_disconnected(RedPeer::InMessage* message);
|
||||
void handle_agent_data(RedPeer::InMessage* message);
|
||||
void handle_agent_tokens(RedPeer::InMessage* message);
|
||||
|
||||
void on_agent_reply(VDAgentReply* reply);
|
||||
|
||||
ChannelFactory* find_factory(uint32_t type);
|
||||
void create_channel(uint32_t type, uint32_t id);
|
||||
void disconnect_channels();
|
||||
void delete_channels();
|
||||
bool abort_channels();
|
||||
|
||||
private:
|
||||
Application& _application;
|
||||
|
||||
std::string _password;
|
||||
std::string _host;
|
||||
int _port;
|
||||
int _sport;
|
||||
uint32_t _connection_id;
|
||||
uint32_t _mouse_mode;
|
||||
Mutex _notify_lock;
|
||||
bool _notify_disconnect;
|
||||
bool _auto_display_res;
|
||||
bool _aborting;
|
||||
|
||||
bool _agent_connected;
|
||||
bool _agent_mon_config_sent;
|
||||
VDAgentMessage* _agent_msg;
|
||||
uint8_t* _agent_msg_data;
|
||||
uint32_t _agent_msg_pos;
|
||||
uint32_t _agent_tokens;
|
||||
TimerID _agent_timer;
|
||||
|
||||
PeerConnectionOptMap _con_opt_map;
|
||||
Migrate _migrate;
|
||||
Mutex _channels_lock;
|
||||
typedef std::list<ChannelFactory*> Factorys;
|
||||
Factorys _factorys;
|
||||
typedef std::list<RedChannel*> Channels;
|
||||
Channels _channels;
|
||||
PixmapCache _pixmap_cache;
|
||||
uint64_t _pixmap_cache_size;
|
||||
Mutex _sync_lock;
|
||||
Condition _sync_condition;
|
||||
uint64_t _sync_info[RED_CHANNEL_END][256];
|
||||
|
||||
GlzDecoderWindowDebug _glz_debug;
|
||||
GlzDecoderWindow _glz_window;
|
||||
int _glz_window_size; // in pixels
|
||||
|
||||
Mutex _mm_clock_lock;
|
||||
uint64_t _mm_clock_last_update;
|
||||
uint32_t _mm_time;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
66
client/red_drawable.h
Normal file
66
client/red_drawable.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_RED_DRAWABLE
|
||||
#define _H_RED_DRAWABLE
|
||||
|
||||
#include "pixels_source.h"
|
||||
|
||||
typedef uint32_t rgb32_t;
|
||||
|
||||
static inline rgb32_t rgb32_make(uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
return (rgb32_t(r) << 16) | (rgb32_t(g) << 8) | b;
|
||||
}
|
||||
|
||||
static inline uint8_t rgb32_get_red(rgb32_t color)
|
||||
{
|
||||
return color >> 16;
|
||||
}
|
||||
|
||||
static inline uint8_t rgb32_get_green(rgb32_t color)
|
||||
{
|
||||
return color >> 8;
|
||||
}
|
||||
|
||||
static inline uint8_t rgb32_get_blue(rgb32_t color)
|
||||
{
|
||||
return color;
|
||||
}
|
||||
|
||||
class RedDrawable: public PixelsSource {
|
||||
public:
|
||||
RedDrawable() {}
|
||||
virtual ~RedDrawable() {}
|
||||
|
||||
enum CombineOP {
|
||||
OP_COPY,
|
||||
OP_AND,
|
||||
OP_XOR,
|
||||
};
|
||||
|
||||
void copy_pixels(const PixelsSource& src, int src_x, int src_y, const Rect& dest);
|
||||
void blend_pixels(const PixelsSource& src, int src_x, int src_y, const Rect& dest);
|
||||
void combine_pixels(const PixelsSource& src, int src_x, int src_y, const Rect& dest,
|
||||
CombineOP op);
|
||||
void fill_rect(const Rect& rect, rgb32_t color);
|
||||
void frame_rect(const Rect& rect, rgb32_t color);
|
||||
void erase_rect(const Rect& rect, rgb32_t color);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
174
client/red_gdi_canvas.cpp
Normal file
174
client/red_gdi_canvas.cpp
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include <stdint.h>
|
||||
#include "red_gdi_canvas.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
#include "region.h"
|
||||
#include "red_pixmap_gdi.h"
|
||||
|
||||
GDICanvas::GDICanvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
|
||||
GlzDecoderWindow &glz_decoder_window)
|
||||
: Canvas (pixmap_cache, palette_cache, glz_decoder_window)
|
||||
, _canvas (NULL)
|
||||
, _pixmap (0)
|
||||
{
|
||||
}
|
||||
|
||||
GDICanvas::~GDICanvas()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
void GDICanvas::destroy()
|
||||
{
|
||||
if (_canvas) {
|
||||
_canvas = NULL;
|
||||
}
|
||||
destroy_pixmap();
|
||||
}
|
||||
|
||||
void GDICanvas::clear()
|
||||
{
|
||||
if (_canvas) {
|
||||
gdi_canvas_clear(_canvas);
|
||||
}
|
||||
}
|
||||
|
||||
void GDICanvas::destroy_pixmap()
|
||||
{
|
||||
delete _pixmap;
|
||||
_pixmap = NULL;
|
||||
}
|
||||
|
||||
void GDICanvas::create_pixmap(int width, int height)
|
||||
{
|
||||
_pixmap = new RedPixmapGdi(width, height, RedPixmap::RGB32, true, NULL);
|
||||
}
|
||||
|
||||
void GDICanvas::copy_pixels(const QRegion& region, RedDrawable& dest_dc)
|
||||
{
|
||||
for (int i = 0; i < (int)region.num_rects; i++) {
|
||||
Rect* r = ®ion.rects[i];
|
||||
dest_dc.copy_pixels(*_pixmap, r->left, r->top, *r);
|
||||
}
|
||||
}
|
||||
|
||||
void GDICanvas::copy_pixels(const QRegion& region, RedDrawable* dest_dc, const PixmapHeader* pixmap)
|
||||
{
|
||||
copy_pixels(region, *dest_dc);
|
||||
}
|
||||
|
||||
void GDICanvas::set_mode(int width, int height, int depth)
|
||||
{
|
||||
destroy();
|
||||
create_pixmap(width, height);
|
||||
if (!(_canvas = gdi_canvas_create(_pixmap->get_dc(),
|
||||
&_pixmap->get_mutex(),
|
||||
depth, &pixmap_cache(), bits_cache_put,
|
||||
bits_cache_get, &palette_cache(),
|
||||
palette_cache_put, palette_cache_get,
|
||||
palette_cache_release,
|
||||
&glz_decoder(),
|
||||
glz_decode))) {
|
||||
THROW("create canvas failed");
|
||||
}
|
||||
}
|
||||
|
||||
void GDICanvas::set_access_params(ADDRESS delta, unsigned long base, unsigned long max)
|
||||
{
|
||||
gdi_canvas_set_access_params(_canvas, delta, base, max);
|
||||
}
|
||||
|
||||
void GDICanvas::draw_fill(Rect *bbox, Clip *clip, Fill *fill)
|
||||
{
|
||||
gdi_canvas_draw_fill(_canvas, bbox, clip, fill);
|
||||
}
|
||||
|
||||
void GDICanvas::draw_text(Rect *bbox, Clip *clip, Text *text)
|
||||
{
|
||||
gdi_canvas_draw_text(_canvas, bbox, clip, text);
|
||||
}
|
||||
|
||||
void GDICanvas::draw_opaque(Rect *bbox, Clip *clip, Opaque *opaque)
|
||||
{
|
||||
gdi_canvas_draw_opaque(_canvas, bbox, clip, opaque);
|
||||
}
|
||||
|
||||
void GDICanvas::draw_copy(Rect *bbox, Clip *clip, Copy *copy)
|
||||
{
|
||||
gdi_canvas_draw_copy(_canvas, bbox, clip, copy);
|
||||
}
|
||||
|
||||
void GDICanvas::draw_transparent(Rect *bbox, Clip *clip, Transparent* transparent)
|
||||
{
|
||||
gdi_canvas_draw_transparent(_canvas, bbox, clip, transparent);
|
||||
}
|
||||
|
||||
void GDICanvas::draw_alpha_blend(Rect *bbox, Clip *clip, AlphaBlnd* alpha_blend)
|
||||
{
|
||||
gdi_canvas_draw_alpha_blend(_canvas, bbox, clip, alpha_blend);
|
||||
}
|
||||
|
||||
void GDICanvas::copy_bits(Rect *bbox, Clip *clip, Point *src_pos)
|
||||
{
|
||||
gdi_canvas_copy_bits(_canvas, bbox, clip, src_pos);
|
||||
}
|
||||
|
||||
void GDICanvas::draw_blend(Rect *bbox, Clip *clip, Blend *blend)
|
||||
{
|
||||
gdi_canvas_draw_blend(_canvas, bbox, clip, blend);
|
||||
}
|
||||
|
||||
void GDICanvas::draw_blackness(Rect *bbox, Clip *clip, Blackness *blackness)
|
||||
{
|
||||
gdi_canvas_draw_blackness(_canvas, bbox, clip, blackness);
|
||||
}
|
||||
|
||||
void GDICanvas::draw_whiteness(Rect *bbox, Clip *clip, Whiteness *whiteness)
|
||||
{
|
||||
gdi_canvas_draw_whiteness(_canvas, bbox, clip, whiteness);
|
||||
}
|
||||
|
||||
void GDICanvas::draw_invers(Rect *bbox, Clip *clip, Invers *invers)
|
||||
{
|
||||
gdi_canvas_draw_invers(_canvas, bbox, clip, invers);
|
||||
}
|
||||
|
||||
void GDICanvas::draw_rop3(Rect *bbox, Clip *clip, Rop3 *rop3)
|
||||
{
|
||||
gdi_canvas_draw_rop3(_canvas, bbox, clip, rop3);
|
||||
}
|
||||
|
||||
void GDICanvas::draw_stroke(Rect *bbox, Clip *clip, Stroke *stroke)
|
||||
{
|
||||
gdi_canvas_draw_stroke(_canvas, bbox, clip, stroke);
|
||||
}
|
||||
|
||||
void GDICanvas::put_image(HDC dc, const PixmapHeader& image, const Rect& dest, const QRegion* clip)
|
||||
{
|
||||
gdi_canvas_put_image(_canvas, dc, &dest, image.data, image.width, image.height, image.stride,
|
||||
clip);
|
||||
}
|
||||
|
||||
CanvasType GDICanvas::get_pixmap_type()
|
||||
{
|
||||
return CANVAS_TYPE_GDI;
|
||||
}
|
||||
|
||||
77
client/red_gdi_canvas.h
Normal file
77
client/red_gdi_canvas.h
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_GDICANVAS
|
||||
#define _H_GDICANVAS
|
||||
|
||||
#include "canvas.h"
|
||||
#include "gdi_canvas.h"
|
||||
#include "red_pixmap_gdi.h"
|
||||
|
||||
class RedPixmap;
|
||||
|
||||
|
||||
class GDICanvas: public Canvas {
|
||||
public:
|
||||
GDICanvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
|
||||
GlzDecoderWindow &glz_decoder_window);
|
||||
virtual ~GDICanvas();
|
||||
|
||||
virtual void set_mode(int x, int y, int bits);
|
||||
virtual void clear();
|
||||
virtual void thread_touch() {}
|
||||
virtual void copy_pixels(const QRegion& region, RedDrawable* dc,
|
||||
const PixmapHeader* pixmap);
|
||||
virtual void copy_pixels(const QRegion& region, RedDrawable& dc);
|
||||
virtual void put_image(HDC dc, const PixmapHeader& image, const Rect& dest,
|
||||
const QRegion* clip);
|
||||
|
||||
virtual CanvasType get_pixmap_type();
|
||||
|
||||
protected:
|
||||
virtual void set_access_params(ADDRESS delta, unsigned long base, unsigned long max);
|
||||
virtual void draw_fill(Rect *bbox, Clip *clip, Fill *fill);
|
||||
virtual void draw_copy(Rect *bbox, Clip *clip, Copy *copy);
|
||||
virtual void draw_opaque(Rect *bbox, Clip *clip, Opaque *opaque);
|
||||
virtual void copy_bits(Rect *bbox, Clip *clip, Point *src_pos);
|
||||
virtual void draw_text(Rect *bbox, Clip *clip, Text *text);
|
||||
virtual void draw_stroke(Rect *bbox, Clip *clip, Stroke *stroke);
|
||||
virtual void draw_rop3(Rect *bbox, Clip *clip, Rop3 *rop3);
|
||||
virtual void draw_blend(Rect *bbox, Clip *clip, Blend *blend);
|
||||
virtual void draw_blackness(Rect *bbox, Clip *clip, Blackness *blackness);
|
||||
virtual void draw_whiteness(Rect *bbox, Clip *clip, Whiteness *whiteness);
|
||||
virtual void draw_invers(Rect *bbox, Clip *clip, Invers *invers);
|
||||
virtual void draw_transparent(Rect *bbox, Clip *clip, Transparent* transparent);
|
||||
virtual void draw_alpha_blend(Rect *bbox, Clip *clip, AlphaBlnd* alpha_blend);
|
||||
|
||||
private:
|
||||
void create_pixmap(int width, int height);
|
||||
void destroy_pixmap();
|
||||
void destroy();
|
||||
|
||||
private:
|
||||
GdiCanvas* _canvas;
|
||||
RedPixmapGdi *_pixmap;
|
||||
RedPixmapGdi *_helper_pixmap;
|
||||
HDC _dc;
|
||||
HBITMAP _prev_bitmap;
|
||||
unsigned long _base;
|
||||
unsigned long _max;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
219
client/red_gl_canvas.cpp
Normal file
219
client/red_gl_canvas.cpp
Normal file
@ -0,0 +1,219 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include <stdint.h>
|
||||
#include "red_gl_canvas.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
#include "region.h"
|
||||
#include "red_pixmap_gl.h"
|
||||
#include <GL/glx.h>
|
||||
|
||||
GCanvas::GCanvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
|
||||
GlzDecoderWindow &glz_decoder_window)
|
||||
: Canvas(pixmap_cache, palette_cache, glz_decoder_window)
|
||||
, _canvas (NULL)
|
||||
, _pixmap (0)
|
||||
, _textures_lost (false)
|
||||
{
|
||||
}
|
||||
|
||||
GCanvas::~GCanvas()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
void GCanvas::destroy()
|
||||
{
|
||||
if (_canvas) {
|
||||
gl_canvas_destroy(_canvas, _textures_lost);
|
||||
_canvas = NULL;
|
||||
}
|
||||
destroy_pixmap();
|
||||
}
|
||||
|
||||
void GCanvas::clear()
|
||||
{
|
||||
if (_canvas) {
|
||||
gl_canvas_clear(_canvas);
|
||||
}
|
||||
}
|
||||
|
||||
void GCanvas::destroy_pixmap()
|
||||
{
|
||||
delete _pixmap;
|
||||
_pixmap = NULL;
|
||||
}
|
||||
|
||||
void GCanvas::create_pixmap(int width, int height, RedWindow *win,
|
||||
RenderType rendertype)
|
||||
{
|
||||
_pixmap = new RedPixmapGL(width, height, RedPixmap::RGB32, true, NULL,
|
||||
win, rendertype);
|
||||
}
|
||||
|
||||
void GCanvas::copy_pixels(const QRegion& region, RedDrawable& dest_dc)
|
||||
{
|
||||
for (int i = 0; i < (int)region.num_rects; i++) {
|
||||
Rect* r = ®ion.rects[i];
|
||||
dest_dc.copy_pixels(*_pixmap, r->left, r->top, *r);
|
||||
}
|
||||
}
|
||||
|
||||
void GCanvas::copy_pixels(const QRegion& region, RedDrawable* dest_dc, const PixmapHeader* pixmap)
|
||||
{
|
||||
copy_pixels(region, *dest_dc);
|
||||
}
|
||||
|
||||
void GCanvas::set_mode(int width, int height, int depth, RedWindow *win,
|
||||
RenderType rendertype)
|
||||
{
|
||||
destroy();
|
||||
|
||||
create_pixmap(width, height, win, rendertype);
|
||||
if (!(_canvas = gl_canvas_create(NULL, width, height, depth,
|
||||
&pixmap_cache(),
|
||||
bits_cache_put,
|
||||
bits_cache_get,
|
||||
&palette_cache(),
|
||||
palette_cache_put,
|
||||
palette_cache_get,
|
||||
palette_cache_release,
|
||||
&glz_decoder(),
|
||||
glz_decode))) {
|
||||
THROW("create canvas failed");
|
||||
}
|
||||
}
|
||||
|
||||
void GCanvas::set_access_params(ADDRESS delta, unsigned long base, unsigned long max)
|
||||
{
|
||||
gl_canvas_set_access_params(_canvas, delta, base, max);
|
||||
}
|
||||
|
||||
void GCanvas::draw_fill(Rect *bbox, Clip *clip, Fill *fill)
|
||||
{
|
||||
gl_canvas_draw_fill(_canvas, bbox, clip, fill);
|
||||
_pixmap->update_texture(bbox);
|
||||
}
|
||||
|
||||
void GCanvas::draw_text(Rect *bbox, Clip *clip, Text *text)
|
||||
{
|
||||
gl_canvas_draw_text(_canvas, bbox, clip, text);
|
||||
_pixmap->update_texture(bbox);
|
||||
}
|
||||
|
||||
void GCanvas::draw_opaque(Rect *bbox, Clip *clip, Opaque *opaque)
|
||||
{
|
||||
gl_canvas_draw_opaque(_canvas, bbox, clip, opaque);
|
||||
_pixmap->update_texture(bbox);
|
||||
}
|
||||
|
||||
void GCanvas::draw_copy(Rect *bbox, Clip *clip, Copy *copy)
|
||||
{
|
||||
gl_canvas_draw_copy(_canvas, bbox, clip, copy);
|
||||
_pixmap->update_texture(bbox);
|
||||
}
|
||||
|
||||
void GCanvas::draw_transparent(Rect *bbox, Clip *clip, Transparent* transparent)
|
||||
{
|
||||
gl_canvas_draw_transparent(_canvas, bbox, clip, transparent);
|
||||
_pixmap->update_texture(bbox);
|
||||
}
|
||||
|
||||
void GCanvas::draw_alpha_blend(Rect *bbox, Clip *clip, AlphaBlnd* alpha_blend)
|
||||
{
|
||||
gl_canvas_draw_alpha_blend(_canvas, bbox, clip, alpha_blend);
|
||||
_pixmap->update_texture(bbox);
|
||||
}
|
||||
|
||||
void GCanvas::copy_bits(Rect *bbox, Clip *clip, Point *src_pos)
|
||||
{
|
||||
gl_canvas_copy_pixels(_canvas, bbox, clip, src_pos);
|
||||
_pixmap->update_texture(bbox);
|
||||
}
|
||||
|
||||
void GCanvas::draw_blend(Rect *bbox, Clip *clip, Blend *blend)
|
||||
{
|
||||
gl_canvas_draw_blend(_canvas, bbox, clip, blend);
|
||||
_pixmap->update_texture(bbox);
|
||||
}
|
||||
|
||||
void GCanvas::draw_blackness(Rect *bbox, Clip *clip, Blackness *blackness)
|
||||
{
|
||||
gl_canvas_draw_blackness(_canvas, bbox, clip, blackness);
|
||||
_pixmap->update_texture(bbox);
|
||||
}
|
||||
|
||||
void GCanvas::draw_whiteness(Rect *bbox, Clip *clip, Whiteness *whiteness)
|
||||
{
|
||||
gl_canvas_draw_whiteness(_canvas, bbox, clip, whiteness);
|
||||
_pixmap->update_texture(bbox);
|
||||
}
|
||||
|
||||
void GCanvas::draw_invers(Rect *bbox, Clip *clip, Invers *invers)
|
||||
{
|
||||
gl_canvas_draw_invers(_canvas, bbox, clip, invers);
|
||||
_pixmap->update_texture(bbox);
|
||||
}
|
||||
|
||||
void GCanvas::draw_rop3(Rect *bbox, Clip *clip, Rop3 *rop3)
|
||||
{
|
||||
gl_canvas_draw_rop3(_canvas, bbox, clip, rop3);
|
||||
_pixmap->update_texture(bbox);
|
||||
}
|
||||
|
||||
void GCanvas::draw_stroke(Rect *bbox, Clip *clip, Stroke *stroke)
|
||||
{
|
||||
gl_canvas_draw_stroke(_canvas, bbox, clip, stroke);
|
||||
_pixmap->update_texture(bbox);
|
||||
}
|
||||
|
||||
void GCanvas::put_image(const PixmapHeader& image, const Rect& dest,
|
||||
const QRegion* clip)
|
||||
{
|
||||
gl_canvas_put_image(_canvas, &dest, image.data, image.width, image.height,
|
||||
image.stride, clip);
|
||||
_pixmap->update_texture(&dest);
|
||||
}
|
||||
|
||||
CanvasType GCanvas::get_pixmap_type()
|
||||
{
|
||||
return CANVAS_TYPE_GL;
|
||||
}
|
||||
|
||||
void GCanvas::textures_lost()
|
||||
{
|
||||
_textures_lost = true;
|
||||
_pixmap->textures_lost();
|
||||
}
|
||||
|
||||
void GCanvas::touch_context()
|
||||
{
|
||||
_pixmap->touch_context();
|
||||
}
|
||||
|
||||
void GCanvas::pre_gl_copy()
|
||||
{
|
||||
_pixmap->pre_copy();
|
||||
}
|
||||
|
||||
void GCanvas::post_gl_copy()
|
||||
{
|
||||
_pixmap->past_copy();
|
||||
}
|
||||
|
||||
79
client/red_gl_canvas.h
Normal file
79
client/red_gl_canvas.h
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_GCANVAS
|
||||
#define _H_GCANVAS
|
||||
|
||||
#include "canvas.h"
|
||||
#include "cairo_canvas.h"
|
||||
#include "gl_canvas.h"
|
||||
#include "red_pixmap_gl.h"
|
||||
#include "red_window.h"
|
||||
|
||||
class RedPixmapGL;
|
||||
|
||||
class GCanvas: public Canvas {
|
||||
public:
|
||||
GCanvas(PixmapCache& pixmap_cache, PaletteCache& palette_cache,
|
||||
GlzDecoderWindow &glz_decoder_window);
|
||||
virtual ~GCanvas();
|
||||
|
||||
void set_mode(int width, int height, int depth, RedWindow *win,
|
||||
RenderType rendertype);
|
||||
void clear();
|
||||
void thread_touch() {}
|
||||
void copy_pixels(const QRegion& region, RedDrawable* dc,
|
||||
const PixmapHeader* pixmap);
|
||||
void copy_pixels(const QRegion& region, RedDrawable& dc);
|
||||
void put_image(const PixmapHeader& image, const Rect& dest,
|
||||
const QRegion* clip);
|
||||
|
||||
void set_access_params(ADDRESS delta, unsigned long base, unsigned long max);
|
||||
void draw_fill(Rect *bbox, Clip *clip, Fill *fill);
|
||||
void draw_copy(Rect *bbox, Clip *clip, Copy *copy);
|
||||
void draw_opaque(Rect *bbox, Clip *clip, Opaque *opaque);
|
||||
void copy_bits(Rect *bbox, Clip *clip, Point *src_pos);
|
||||
void draw_text(Rect *bbox, Clip *clip, Text *text);
|
||||
void draw_stroke(Rect *bbox, Clip *clip, Stroke *stroke);
|
||||
void draw_rop3(Rect *bbox, Clip *clip, Rop3 *rop3);
|
||||
void draw_blend(Rect *bbox, Clip *clip, Blend *blend);
|
||||
void draw_blackness(Rect *bbox, Clip *clip, Blackness *blackness);
|
||||
void draw_whiteness(Rect *bbox, Clip *clip, Whiteness *whiteness);
|
||||
void draw_invers(Rect *bbox, Clip *clip, Invers *invers);
|
||||
void draw_transparent(Rect *bbox, Clip *clip, Transparent* transparent);
|
||||
void draw_alpha_blend(Rect *bbox, Clip *clip, AlphaBlnd* alpha_blend);
|
||||
|
||||
virtual void textures_lost();
|
||||
virtual CanvasType get_pixmap_type();
|
||||
virtual void touch_context();
|
||||
virtual void pre_gl_copy();
|
||||
virtual void post_gl_copy();
|
||||
|
||||
private:
|
||||
void create_pixmap(int width, int height, RedWindow *win,
|
||||
RenderType rendertype);
|
||||
void destroy_pixmap();
|
||||
void destroy();
|
||||
|
||||
private:
|
||||
GLCanvas* _canvas;
|
||||
RedPixmapGL *_pixmap;
|
||||
bool _textures_lost;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
150
client/red_key.h
Normal file
150
client/red_key.h
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_RED_KEY
|
||||
#define _H_RED_KEY
|
||||
|
||||
enum RedKey {
|
||||
REDKEY_INVALID,
|
||||
REDKEY_ESCAPE,
|
||||
REDKEY_1,
|
||||
REDKEY_2,
|
||||
REDKEY_3,
|
||||
REDKEY_4,
|
||||
REDKEY_5,
|
||||
REDKEY_6,
|
||||
REDKEY_7,
|
||||
REDKEY_8,
|
||||
REDKEY_9,
|
||||
REDKEY_0,
|
||||
REDKEY_MINUS,
|
||||
REDKEY_EQUALS,
|
||||
REDKEY_BACKSPACE,
|
||||
REDKEY_TAB,
|
||||
REDKEY_Q,
|
||||
REDKEY_W,
|
||||
REDKEY_E,
|
||||
REDKEY_R,
|
||||
REDKEY_T,
|
||||
REDKEY_Y,
|
||||
REDKEY_U,
|
||||
REDKEY_I,
|
||||
REDKEY_O,
|
||||
REDKEY_P,
|
||||
REDKEY_L_BRACKET,
|
||||
REDKEY_R_BRACKET,
|
||||
REDKEY_ENTER,
|
||||
REDKEY_L_CTRL,
|
||||
REDKEY_A,
|
||||
REDKEY_S,
|
||||
REDKEY_D,
|
||||
REDKEY_F,
|
||||
REDKEY_G,
|
||||
REDKEY_H,
|
||||
REDKEY_J,
|
||||
REDKEY_K,
|
||||
REDKEY_L,
|
||||
REDKEY_SEMICOLON,
|
||||
REDKEY_QUOTE,
|
||||
|
||||
REDKEY_BACK_QUOTE,
|
||||
REDKEY_L_SHIFT,
|
||||
REDKEY_BACK_SLASH,
|
||||
REDKEY_Z,
|
||||
REDKEY_X,
|
||||
REDKEY_C,
|
||||
REDKEY_V,
|
||||
REDKEY_B,
|
||||
REDKEY_N,
|
||||
REDKEY_M,
|
||||
REDKEY_COMMA,
|
||||
REDKEY_PERIOD,
|
||||
REDKEY_SLASH,
|
||||
REDKEY_R_SHIFT,
|
||||
REDKEY_PAD_MULTIPLY,
|
||||
REDKEY_L_ALT,
|
||||
REDKEY_SPACE,
|
||||
REDKEY_CAPS_LOCK,
|
||||
REDKEY_F1,
|
||||
REDKEY_F2,
|
||||
REDKEY_F3,
|
||||
REDKEY_F4,
|
||||
REDKEY_F5,
|
||||
REDKEY_F6,
|
||||
REDKEY_F7,
|
||||
REDKEY_F8,
|
||||
REDKEY_F9,
|
||||
REDKEY_F10,
|
||||
REDKEY_NUM_LOCK,
|
||||
REDKEY_SCROLL_LOCK,
|
||||
REDKEY_PAD_7,
|
||||
REDKEY_PAD_8,
|
||||
REDKEY_PAD_9,
|
||||
REDKEY_PAD_MINUS,
|
||||
REDKEY_PAD_4,
|
||||
REDKEY_PAD_5,
|
||||
REDKEY_PAD_6,
|
||||
REDKEY_PAD_PLUS,
|
||||
REDKEY_PAD_1,
|
||||
REDKEY_PAD_2,
|
||||
REDKEY_PAD_3,
|
||||
REDKEY_PAD_0,
|
||||
REDKEY_PAD_POINT,
|
||||
|
||||
REDKEY_EUROPEAN = 0x56,
|
||||
REDKEY_F11,
|
||||
REDKEY_F12,
|
||||
|
||||
REDKEY_JAPANESE_HIRAGANA_KATAKANA = 0x70,
|
||||
REDKEY_JAPANESE_BACKSLASH = 0x73,
|
||||
REDKEY_JAPANESE_HENKAN = 0x79,
|
||||
REDKEY_JAPANESE_MUHENKAN = 0x7B,
|
||||
REDKEY_JAPANESE_YEN = 0x7D,
|
||||
|
||||
REDKEY_KOREAN_HANGUL_HANJA = 0xf1,
|
||||
REDKEY_KOREAN_HANGUL = 0xf2,
|
||||
|
||||
REDKEY_ESCAPE_BASE = 0x100,
|
||||
REDKEY_PAD_ENTER = REDKEY_ESCAPE_BASE + 0x1c,
|
||||
REDKEY_R_CTRL = REDKEY_ESCAPE_BASE + 0x1d,
|
||||
REDKEY_FAKE_L_SHIFT = REDKEY_ESCAPE_BASE + 0x2a,
|
||||
REDKEY_PAD_DIVIDE = REDKEY_ESCAPE_BASE + 0x35,
|
||||
REDKEY_FAKE_R_SHIFT = REDKEY_ESCAPE_BASE + 0x36,
|
||||
REDKEY_CTRL_PRINT_SCREEN = REDKEY_ESCAPE_BASE + 0x37,
|
||||
REDKEY_R_ALT = REDKEY_ESCAPE_BASE + 0x38,
|
||||
REDKEY_CTRL_BREAK = REDKEY_ESCAPE_BASE + 0x46,
|
||||
REDKEY_HOME = REDKEY_ESCAPE_BASE + 0x47,
|
||||
REDKEY_UP = REDKEY_ESCAPE_BASE + 0x48,
|
||||
REDKEY_PAGEUP = REDKEY_ESCAPE_BASE + 0x49,
|
||||
REDKEY_LEFT = REDKEY_ESCAPE_BASE + 0x4b,
|
||||
REDKEY_RIGHT = REDKEY_ESCAPE_BASE + 0x4d,
|
||||
REDKEY_END = REDKEY_ESCAPE_BASE + 0x4f,
|
||||
REDKEY_DOWN = REDKEY_ESCAPE_BASE + 0x50,
|
||||
REDKEY_PAGEDOWN = REDKEY_ESCAPE_BASE + 0x51,
|
||||
REDKEY_INSERT = REDKEY_ESCAPE_BASE + 0x52,
|
||||
REDKEY_DELETE = REDKEY_ESCAPE_BASE + 0x53,
|
||||
REDKEY_LEFT_CMD = REDKEY_ESCAPE_BASE + 0x5b,
|
||||
REDKEY_RIGHT_CMD = REDKEY_ESCAPE_BASE + 0x5c,
|
||||
REDKEY_MENU = REDKEY_ESCAPE_BASE + 0x5d,
|
||||
|
||||
REDKEY_PAUSE,
|
||||
|
||||
REDKEY_NUM_KEYS
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
426
client/red_peer.cpp
Normal file
426
client/red_peer.cpp
Normal file
@ -0,0 +1,426 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#define SHUT_RDWR SD_BOTH
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#define INVALID_SOCKET -1
|
||||
#define SOCKET_ERROR -1
|
||||
#define closesocket(sock) ::close(sock)
|
||||
#endif
|
||||
#include "red.h"
|
||||
#include "red_peer.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
#include "platform_utils.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
int inet_aton(const char *ip, struct in_addr *in_addr)
|
||||
{
|
||||
unsigned long addr = inet_addr(ip);
|
||||
|
||||
if (addr == INADDR_NONE) {
|
||||
return 0;
|
||||
}
|
||||
in_addr->S_un.S_addr = addr;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define SHUTDOWN_ERR WSAESHUTDOWN
|
||||
#define INTERRUPTED_ERR WSAEINTR
|
||||
#define WOULDBLOCK_ERR WSAEWOULDBLOCK
|
||||
#define sock_error() WSAGetLastError()
|
||||
#define sock_err_message(err) sys_err_to_str(err)
|
||||
#else
|
||||
#define SHUTDOWN_ERR EPIPE
|
||||
#define INTERRUPTED_ERR EINTR
|
||||
#define WOULDBLOCK_ERR EAGAIN
|
||||
#define sock_error() errno
|
||||
#define sock_err_message(err) strerror(err)
|
||||
#endif
|
||||
|
||||
static void ssl_error()
|
||||
{
|
||||
ERR_print_errors_fp(stderr);
|
||||
THROW_ERR(SPICEC_ERROR_CODE_SSL_ERROR, "SSL Error");
|
||||
}
|
||||
|
||||
RedPeer::RedPeer()
|
||||
: _peer (INVALID_SOCKET)
|
||||
, _shut (false)
|
||||
, _ctx (NULL)
|
||||
, _ssl (NULL)
|
||||
{
|
||||
}
|
||||
|
||||
RedPeer::~RedPeer()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void RedPeer::cleanup()
|
||||
{
|
||||
if (_ssl) {
|
||||
SSL_free(_ssl);
|
||||
_ssl = NULL;
|
||||
}
|
||||
|
||||
if (_ctx) {
|
||||
SSL_CTX_free(_ctx);
|
||||
_ctx = NULL;
|
||||
}
|
||||
|
||||
if (_peer != INVALID_SOCKET) {
|
||||
closesocket(_peer);
|
||||
_peer = INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t RedPeer::host_by_name(const char* host)
|
||||
{
|
||||
struct addrinfo *result = NULL;
|
||||
struct sockaddr_in *addr;
|
||||
uint32_t return_value;
|
||||
int rc;
|
||||
|
||||
rc = getaddrinfo(host, NULL, NULL, &result);
|
||||
if (rc != 0 || result == NULL) {
|
||||
THROW_ERR(SPICEC_ERROR_CODE_GETHOSTBYNAME_FAILED, "cannot resolve host address %s", host);
|
||||
}
|
||||
|
||||
addr = (sockaddr_in *)result->ai_addr;
|
||||
return_value = addr->sin_addr.s_addr;
|
||||
|
||||
freeaddrinfo(result);
|
||||
|
||||
DBG(0, "%s = %u", host, return_value);
|
||||
return ntohl(return_value);
|
||||
}
|
||||
|
||||
void RedPeer::connect_unsecure(uint32_t ip, int port)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int no_delay;
|
||||
|
||||
ASSERT(_ctx == NULL && _ssl == NULL && _peer == INVALID_SOCKET);
|
||||
try {
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(ip);
|
||||
|
||||
Lock lock(_lock);
|
||||
if ((_peer = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {
|
||||
int err = sock_error();
|
||||
THROW_ERR(SPICEC_ERROR_CODE_SOCKET_FAILED, "failed to create socket: %s (%d)",
|
||||
sock_err_message(err), err);
|
||||
}
|
||||
|
||||
no_delay = 1;
|
||||
if (setsockopt(_peer, IPPROTO_TCP, TCP_NODELAY, (const char*)&no_delay, sizeof(no_delay)) ==
|
||||
SOCKET_ERROR) {
|
||||
LOG_WARN("set TCP_NODELAY failed");
|
||||
}
|
||||
|
||||
LOG_INFO("Connecting %s %d", inet_ntoa(addr.sin_addr), port);
|
||||
lock.unlock();
|
||||
if (::connect(_peer, (struct sockaddr *)&addr, sizeof(sockaddr_in)) == SOCKET_ERROR) {
|
||||
int err = sock_error();
|
||||
closesocket(_peer);
|
||||
THROW_ERR(SPICEC_ERROR_CODE_CONNECT_FAILED, "failed to connect: %s (%d)",
|
||||
sock_err_message(err), err);
|
||||
}
|
||||
_serial = 0;
|
||||
} catch (...) {
|
||||
Lock lock(_lock);
|
||||
cleanup();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void RedPeer::connect_unsecure(const char* host, int port)
|
||||
{
|
||||
connect_unsecure(host_by_name(host), port);
|
||||
}
|
||||
|
||||
// todo: use SSL_CTX_set_cipher_list, SSL_CTX_load_verify_location etc.
|
||||
void RedPeer::connect_secure(const ConnectionOptions& options, uint32_t ip)
|
||||
{
|
||||
connect_unsecure(ip, options.secure_port);
|
||||
ASSERT(_ctx == NULL && _ssl == NULL && _peer != INVALID_SOCKET);
|
||||
|
||||
try {
|
||||
SSL_METHOD *ssl_method = TLSv1_method();
|
||||
|
||||
_ctx = SSL_CTX_new(ssl_method);
|
||||
if (_ctx == NULL) {
|
||||
ssl_error();
|
||||
}
|
||||
|
||||
_ssl = SSL_new(_ctx);
|
||||
if (!_ssl) {
|
||||
THROW("create ssl failed");
|
||||
}
|
||||
|
||||
BIO* sbio = BIO_new_socket(_peer, BIO_NOCLOSE);
|
||||
if (!sbio) {
|
||||
THROW("alloc new socket bio failed");
|
||||
}
|
||||
|
||||
SSL_set_bio(_ssl, sbio, sbio);
|
||||
|
||||
int return_code = SSL_connect(_ssl);
|
||||
if (return_code <= 0) {
|
||||
SSL_get_error(_ssl, return_code);
|
||||
ssl_error();
|
||||
}
|
||||
} catch (...) {
|
||||
Lock lock(_lock);
|
||||
cleanup();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void RedPeer::connect_secure(const ConnectionOptions& options, const char* host)
|
||||
{
|
||||
connect_secure(options, host_by_name(host));
|
||||
}
|
||||
|
||||
void RedPeer::shutdown()
|
||||
{
|
||||
if (_peer != INVALID_SOCKET) {
|
||||
if (_ssl) {
|
||||
SSL_shutdown(_ssl);
|
||||
}
|
||||
::shutdown(_peer, SHUT_RDWR);
|
||||
}
|
||||
_shut = true;
|
||||
}
|
||||
|
||||
void RedPeer::disconnect()
|
||||
{
|
||||
Lock lock(_lock);
|
||||
shutdown();
|
||||
}
|
||||
|
||||
void RedPeer::close()
|
||||
{
|
||||
Lock lock(_lock);
|
||||
if (_peer != INVALID_SOCKET) {
|
||||
if (_ctx) {
|
||||
SSL_free(_ssl);
|
||||
_ssl = NULL;
|
||||
SSL_CTX_free(_ctx);
|
||||
_ctx = NULL;
|
||||
}
|
||||
|
||||
closesocket(_peer);
|
||||
_peer = INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
|
||||
void RedPeer::swap(RedPeer* other)
|
||||
{
|
||||
Lock lock(_lock);
|
||||
SOCKET temp_peer = _peer;
|
||||
SSL_CTX *temp_ctx = _ctx;
|
||||
SSL *temp_ssl = _ssl;
|
||||
|
||||
_peer = other->_peer;
|
||||
other->_peer = temp_peer;
|
||||
|
||||
if (_ctx) {
|
||||
_ctx = other->_ctx;
|
||||
_ssl = other->_ssl;
|
||||
|
||||
other->_ctx = temp_ctx;
|
||||
other->_ssl = temp_ssl;
|
||||
}
|
||||
|
||||
if (_shut) {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t RedPeer::recive(uint8_t *buf, uint32_t size)
|
||||
{
|
||||
uint8_t *pos = buf;
|
||||
while (size) {
|
||||
int now;
|
||||
if (_ctx == NULL) {
|
||||
if ((now = recv(_peer, (char *)pos, size, 0)) <= 0) {
|
||||
int err = sock_error();
|
||||
if (now == SOCKET_ERROR && err == WOULDBLOCK_ERR) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (now == 0 || err == SHUTDOWN_ERR) {
|
||||
throw RedPeer::DisconnectedException();
|
||||
}
|
||||
|
||||
if (err == INTERRUPTED_ERR) {
|
||||
continue;
|
||||
}
|
||||
THROW_ERR(SPICEC_ERROR_CODE_RECV_FAILED, "%s (%d)", sock_err_message(err), err);
|
||||
}
|
||||
size -= now;
|
||||
pos += now;
|
||||
} else {
|
||||
if ((now = SSL_read(_ssl, pos, size)) <= 0) {
|
||||
int ssl_error = SSL_get_error(_ssl, now);
|
||||
|
||||
if (ssl_error == SSL_ERROR_WANT_READ) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (ssl_error == SSL_ERROR_SYSCALL) {
|
||||
int err = sock_error();
|
||||
if (now == -1) {
|
||||
if (err == WOULDBLOCK_ERR) {
|
||||
break;
|
||||
}
|
||||
if (err == INTERRUPTED_ERR) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (now == 0 || (now == -1 && err == SHUTDOWN_ERR)) {
|
||||
throw RedPeer::DisconnectedException();
|
||||
}
|
||||
THROW_ERR(SPICEC_ERROR_CODE_SEND_FAILED, "%s (%d)", sock_err_message(err), err);
|
||||
} else if (ssl_error == SSL_ERROR_ZERO_RETURN) {
|
||||
throw RedPeer::DisconnectedException();
|
||||
}
|
||||
THROW_ERR(SPICEC_ERROR_CODE_RECV_FAILED, "ssl error %d", ssl_error);
|
||||
}
|
||||
size -= now;
|
||||
pos += now;
|
||||
}
|
||||
}
|
||||
return pos - buf;
|
||||
}
|
||||
|
||||
RedPeer::CompundInMessage* RedPeer::recive()
|
||||
{
|
||||
RedDataHeader header;
|
||||
std::auto_ptr<CompundInMessage> message;
|
||||
|
||||
recive((uint8_t*)&header, sizeof(RedDataHeader));
|
||||
message.reset(new CompundInMessage(header.serial, header.type, header.size, header.sub_list));
|
||||
recive(message->data(), message->compund_size());
|
||||
return message.release();
|
||||
}
|
||||
|
||||
uint32_t RedPeer::send(uint8_t *buf, uint32_t size)
|
||||
{
|
||||
uint8_t *pos = buf;
|
||||
while (size) {
|
||||
int now;
|
||||
|
||||
if (_ctx == NULL) {
|
||||
if ((now = ::send(_peer, (char *)pos, size, 0)) == SOCKET_ERROR) {
|
||||
int err = sock_error();
|
||||
if (err == WOULDBLOCK_ERR) {
|
||||
break;
|
||||
}
|
||||
if (err == SHUTDOWN_ERR) {
|
||||
throw RedPeer::DisconnectedException();
|
||||
}
|
||||
if (err == INTERRUPTED_ERR) {
|
||||
continue;
|
||||
}
|
||||
THROW_ERR(SPICEC_ERROR_CODE_SEND_FAILED, "%s (%d)", sock_err_message(err), err);
|
||||
}
|
||||
size -= now;
|
||||
pos += now;
|
||||
} else {
|
||||
if ((now = SSL_write(_ssl, pos, size)) <= 0) {
|
||||
int ssl_error = SSL_get_error(_ssl, now);
|
||||
|
||||
if (ssl_error == SSL_ERROR_WANT_WRITE) {
|
||||
break;
|
||||
}
|
||||
if (ssl_error == SSL_ERROR_SYSCALL) {
|
||||
int err = sock_error();
|
||||
if (now == -1) {
|
||||
if (err == WOULDBLOCK_ERR) {
|
||||
break;
|
||||
}
|
||||
if (err == INTERRUPTED_ERR) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (now == 0 || (now == -1 && err == SHUTDOWN_ERR)) {
|
||||
throw RedPeer::DisconnectedException();
|
||||
}
|
||||
THROW_ERR(SPICEC_ERROR_CODE_SEND_FAILED, "%s (%d)", sock_err_message(err), err);
|
||||
} else if (ssl_error == SSL_ERROR_ZERO_RETURN) {
|
||||
throw RedPeer::DisconnectedException();
|
||||
}
|
||||
THROW_ERR(SPICEC_ERROR_CODE_SEND_FAILED, "ssl error %d", ssl_error);
|
||||
}
|
||||
size -= now;
|
||||
pos += now;
|
||||
}
|
||||
}
|
||||
return pos - buf;
|
||||
}
|
||||
|
||||
uint32_t RedPeer::send(RedPeer::OutMessage& message)
|
||||
{
|
||||
message.header().serial = ++_serial;
|
||||
return send(message.base(), message.message_size());
|
||||
}
|
||||
|
||||
RedPeer::OutMessage::OutMessage(uint32_t type, uint32_t size)
|
||||
: _data (new uint8_t[size + sizeof(RedDataHeader)])
|
||||
, _size (size)
|
||||
{
|
||||
header().type = type;
|
||||
header().size = size;
|
||||
}
|
||||
|
||||
RedPeer::OutMessage::~OutMessage()
|
||||
{
|
||||
delete[] _data;
|
||||
}
|
||||
|
||||
void RedPeer::OutMessage::resize(uint32_t size)
|
||||
{
|
||||
if (size <= _size) {
|
||||
header().size = size;
|
||||
return;
|
||||
}
|
||||
uint32_t type = header().type;
|
||||
delete[] _data;
|
||||
_data = NULL;
|
||||
_size = 0;
|
||||
_data = new uint8_t[size + sizeof(RedDataHeader)];
|
||||
_size = size;
|
||||
header().type = type;
|
||||
header().size = size;
|
||||
}
|
||||
|
||||
182
client/red_peer.h
Normal file
182
client/red_peer.h
Normal file
@ -0,0 +1,182 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_REDPEER
|
||||
#define _H_REDPEER
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock.h>
|
||||
#else
|
||||
typedef int SOCKET;
|
||||
#endif
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "red.h"
|
||||
#include "events_loop.h"
|
||||
#include "threads.h"
|
||||
|
||||
class RedPeer: protected EventsLoop::Socket {
|
||||
public:
|
||||
RedPeer();
|
||||
virtual ~RedPeer();
|
||||
|
||||
class InMessage;
|
||||
class CompundInMessage;
|
||||
class OutMessage;
|
||||
class DisconnectedException {};
|
||||
|
||||
class ConnectionOptions {
|
||||
public:
|
||||
|
||||
enum Type {
|
||||
CON_OP_INVALID,
|
||||
CON_OP_SECURE,
|
||||
CON_OP_UNSECURE,
|
||||
CON_OP_BOTH,
|
||||
};
|
||||
|
||||
ConnectionOptions(Type in_type, int in_port, int in_sport)
|
||||
: type (in_type)
|
||||
, unsecure_port (in_port)
|
||||
, secure_port (in_sport)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~ConnectionOptions() {}
|
||||
|
||||
bool allow_secure() const
|
||||
{
|
||||
return (type == CON_OP_BOTH || type == CON_OP_SECURE) && secure_port != -1;
|
||||
}
|
||||
|
||||
bool allow_unsecure() const
|
||||
{
|
||||
return (type == CON_OP_BOTH || type == CON_OP_UNSECURE) && unsecure_port != -1;
|
||||
}
|
||||
|
||||
public:
|
||||
Type type;
|
||||
int unsecure_port;
|
||||
int secure_port;
|
||||
};
|
||||
|
||||
void connect_unsecure(uint32_t ip, int port);
|
||||
void connect_unsecure(const char* host, int port);
|
||||
|
||||
void connect_secure(const ConnectionOptions& options, uint32_t ip);
|
||||
void connect_secure(const ConnectionOptions& options, const char* host);
|
||||
|
||||
void disconnect();
|
||||
void swap(RedPeer* other);
|
||||
void close();
|
||||
void enable() { _shut = false;}
|
||||
|
||||
virtual CompundInMessage* recive();
|
||||
uint32_t send(OutMessage& message);
|
||||
|
||||
uint32_t recive(uint8_t* buf, uint32_t size);
|
||||
uint32_t send(uint8_t* buf, uint32_t size);
|
||||
|
||||
static uint32_t host_by_name(const char *host);
|
||||
|
||||
protected:
|
||||
virtual void on_event() {}
|
||||
virtual int get_socket() { return _peer;}
|
||||
|
||||
private:
|
||||
void shutdown();
|
||||
void cleanup();
|
||||
|
||||
private:
|
||||
SOCKET _peer;
|
||||
Mutex _lock;
|
||||
bool _shut;
|
||||
uint64_t _serial;
|
||||
|
||||
SSL_CTX *_ctx;
|
||||
SSL *_ssl;
|
||||
};
|
||||
|
||||
class RedPeer::InMessage {
|
||||
public:
|
||||
InMessage(uint16_t type, uint32_t size, uint8_t* data)
|
||||
: _type (type)
|
||||
, _size (size)
|
||||
, _data (data)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~InMessage() {}
|
||||
|
||||
uint16_t type() { return _type;}
|
||||
uint8_t* data() { return _data;}
|
||||
virtual uint32_t size() { return _size;}
|
||||
|
||||
protected:
|
||||
uint16_t _type;
|
||||
uint32_t _size;
|
||||
uint8_t* _data;
|
||||
};
|
||||
|
||||
class RedPeer::CompundInMessage: public RedPeer::InMessage {
|
||||
public:
|
||||
CompundInMessage(uint64_t _serial, uint16_t type, uint32_t size, uint32_t sub_list)
|
||||
: InMessage(type, size, new uint8_t[size])
|
||||
, _serial (_serial)
|
||||
, _sub_list (sub_list)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~CompundInMessage() { delete[] _data;}
|
||||
|
||||
uint64_t serial() { return _serial;}
|
||||
uint32_t sub_list() { return _sub_list;}
|
||||
|
||||
virtual uint32_t size() { return _sub_list ? _sub_list : _size;}
|
||||
uint32_t compund_size() {return _size;}
|
||||
|
||||
private:
|
||||
uint64_t _serial;
|
||||
uint32_t _sub_list;
|
||||
};
|
||||
|
||||
class RedPeer::OutMessage {
|
||||
public:
|
||||
OutMessage(uint32_t type, uint32_t size);
|
||||
virtual ~OutMessage();
|
||||
|
||||
RedDataHeader& header() { return *(RedDataHeader *)_data;}
|
||||
uint8_t* data() { return _data + sizeof(RedDataHeader);}
|
||||
void resize(uint32_t size);
|
||||
|
||||
private:
|
||||
uint32_t message_size() { return header().size + sizeof(RedDataHeader);}
|
||||
uint8_t* base() { return _data;}
|
||||
|
||||
private:
|
||||
uint8_t* _data;
|
||||
uint32_t _size;
|
||||
|
||||
friend class RedPeer;
|
||||
friend class RedChannel;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
53
client/red_pixmap.h
Normal file
53
client/red_pixmap.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_RED_PIXMAP
|
||||
#define _H_RED_PIXMAP
|
||||
|
||||
#include "red_drawable.h"
|
||||
|
||||
class RedPixmap: public RedDrawable {
|
||||
public:
|
||||
enum Format {
|
||||
ARGB32,
|
||||
RGB32,
|
||||
A1,
|
||||
};
|
||||
|
||||
RedPixmap(int width, int height, Format format, bool top_bottom,
|
||||
rgb32_t* pallete);
|
||||
virtual ~RedPixmap();
|
||||
|
||||
virtual Point get_size() { Point pt = {_width, _height}; return pt;}
|
||||
|
||||
int get_width() { return _width;}
|
||||
int get_height() { return _height;}
|
||||
int get_stride() { return _stride;}
|
||||
uint8_t* get_data() { return _data;}
|
||||
bool is_big_endian_bits();
|
||||
|
||||
protected:
|
||||
Format _format;
|
||||
int _width;
|
||||
int _height;
|
||||
int _stride;
|
||||
bool _top_bottom;
|
||||
uint8_t* _data;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
32
client/red_pixmap_cairo.h
Normal file
32
client/red_pixmap_cairo.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_RED_PIXMAP_CAIRO
|
||||
#define _H_RED_PIXMAP_CAIRO
|
||||
|
||||
#include "red_pixmap.h"
|
||||
#include "red_window.h"
|
||||
|
||||
class RedPixmapCairo: public RedPixmap {
|
||||
public:
|
||||
RedPixmapCairo(int width, int height, Format format, bool top_bottom,
|
||||
rgb32_t *pallete, RedWindow *win);
|
||||
~RedPixmapCairo();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
36
client/red_pixmap_gdi.h
Normal file
36
client/red_pixmap_gdi.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_RED_PIXMAP_CAIRO
|
||||
#define _H_RED_PIXMAP_CAIRO
|
||||
|
||||
#include "red_pixmap.h"
|
||||
|
||||
class Mutex;
|
||||
|
||||
class RedPixmapGdi: public RedPixmap {
|
||||
public:
|
||||
RedPixmapGdi(int width, int height, Format format, bool top_bottom,
|
||||
rgb32_t *pallete);
|
||||
HDC get_dc();
|
||||
void *get_memptr();
|
||||
~RedPixmapGdi();
|
||||
Mutex& get_mutex();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
50
client/red_pixmap_gl.h
Normal file
50
client/red_pixmap_gl.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_RED_PIXMAP_GL
|
||||
#define _H_RED_PIXMAP_GL
|
||||
|
||||
#include "red_pixmap.h"
|
||||
#include "red_window.h"
|
||||
|
||||
enum RenderType {
|
||||
RENDER_TYPE_PBUFF,
|
||||
RENDER_TYPE_FBO,
|
||||
};
|
||||
|
||||
class RedPixmapGL: public RedPixmap {
|
||||
public:
|
||||
RedPixmapGL(int width, int height, Format format, bool top_bottom,
|
||||
rgb32_t *pallete, RedWindow *win, RenderType rendertype);
|
||||
|
||||
void textures_lost();
|
||||
void touch_context();
|
||||
void update_texture(const Rect *bbox);
|
||||
void pre_copy();
|
||||
void past_copy();
|
||||
~RedPixmapGL();
|
||||
|
||||
private:
|
||||
RedGlContext _glcont;
|
||||
GLint _prev_tex;
|
||||
GLint _prev_matrix_mode;
|
||||
bool _tex_enabled;
|
||||
bool _textures_lost;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
38
client/red_types.h
Normal file
38
client/red_types.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_RED_TYPES
|
||||
#define _H_RED_TYPES
|
||||
|
||||
struct PixmapHeader {
|
||||
uint8_t* data;
|
||||
int width;
|
||||
int height;
|
||||
int stride;
|
||||
};
|
||||
|
||||
struct IconHeader {
|
||||
int width;
|
||||
int height;
|
||||
uint8_t* pixmap;
|
||||
uint8_t* mask;
|
||||
};
|
||||
|
||||
class RedDrawable;
|
||||
|
||||
#endif
|
||||
|
||||
193
client/red_window.h
Normal file
193
client/red_window.h
Normal file
@ -0,0 +1,193 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_RED_WINDOW
|
||||
#define _H_RED_WINDOW
|
||||
|
||||
#include "red_drawable.h"
|
||||
#include "red.h"
|
||||
#include "red_key.h"
|
||||
#include "red_window_p.h"
|
||||
#include "cursor.h"
|
||||
|
||||
class Menu;
|
||||
class Icon;
|
||||
|
||||
class RedWindow: public RedDrawable, private RedWindow_p {
|
||||
public:
|
||||
class Listener;
|
||||
|
||||
enum {
|
||||
DEFAULT_SCREEN = -1,
|
||||
};
|
||||
|
||||
RedWindow(RedWindow::Listener& listener, int screen_id = DEFAULT_SCREEN);
|
||||
virtual ~RedWindow();
|
||||
|
||||
void move_and_resize(int x, int y, int width, int height);
|
||||
void resize(int width, int height);
|
||||
void move(int x, int y);
|
||||
void position_after(RedWindow *after);
|
||||
void raise();
|
||||
void show(int screen_id);
|
||||
void external_show();
|
||||
void hide();
|
||||
void minimize();
|
||||
void activate();
|
||||
void set_title(std::wstring& title);
|
||||
void set_icon(Icon *icon);
|
||||
|
||||
enum Type {
|
||||
TYPE_INVALID,
|
||||
TYPE_NORMAL,
|
||||
TYPE_FULLSCREEN,
|
||||
};
|
||||
void set_type(Type type) { _type = type;}
|
||||
Point get_position();
|
||||
virtual Point get_size();
|
||||
bool get_mouse_anchor_point(Point& pt);
|
||||
|
||||
void set_mouse_position(int x, int y);
|
||||
void set_cursor(LocalCursor* local_cursor);
|
||||
void hide_cursor();
|
||||
void show_cursor();
|
||||
void cupture_mouse();
|
||||
void release_mouse();
|
||||
void start_key_interception();
|
||||
void stop_key_interception();
|
||||
void set_menu(Menu* menu);
|
||||
|
||||
#ifdef USE_OGL
|
||||
void untouch_context();
|
||||
RedGlContext create_context_gl();
|
||||
RedPbuffer create_pbuff(int width, int height);
|
||||
void set_render_pbuff(RedPbuffer pbuff);
|
||||
void set_render_fbo(GLuint fbo);
|
||||
void set_gl_context(RedGlContext context);
|
||||
#endif
|
||||
|
||||
int get_screen_num();
|
||||
|
||||
void set_type_gl();
|
||||
void unset_type_gl();
|
||||
|
||||
static void init();
|
||||
|
||||
Listener& get_listener() { return _listener;}
|
||||
|
||||
private:
|
||||
void on_focus_in();
|
||||
void on_focus_out();
|
||||
void on_pointer_enter();
|
||||
void on_pointer_leave();
|
||||
|
||||
void do_start_key_interception();
|
||||
void do_stop_key_interception();
|
||||
|
||||
private:
|
||||
Listener& _listener;
|
||||
Point _window_size;
|
||||
Type _type;
|
||||
LocalCursor* _local_cursor;
|
||||
bool _cursor_visible;
|
||||
bool _focused;
|
||||
bool _pointer_in_window;
|
||||
bool _key_interception;
|
||||
Menu* _menu;
|
||||
|
||||
friend class RedWindow_p;
|
||||
};
|
||||
|
||||
class RedWindow::Listener {
|
||||
public:
|
||||
virtual ~Listener() {}
|
||||
virtual void on_exposed_rect(const Rect& area) = 0;
|
||||
virtual void on_mouse_motion(int x, int y, unsigned int buttons_state) = 0;
|
||||
virtual void on_key_press(RedKey key) = 0;
|
||||
virtual void on_key_release(RedKey key) = 0;
|
||||
virtual void on_button_press(RedButton button, unsigned int buttons_state) = 0;
|
||||
virtual void on_button_release(RedButton button, unsigned int buttons_state) = 0;
|
||||
virtual void on_deactivate() = 0;
|
||||
virtual void on_activate() = 0;
|
||||
virtual void on_pointer_enter() = 0;
|
||||
virtual void on_pointer_leave() = 0;
|
||||
virtual void on_start_key_interception() = 0;
|
||||
virtual void on_stop_key_interception() = 0;
|
||||
virtual void enter_modal_loop() = 0;
|
||||
virtual void exit_modal_loop() = 0;
|
||||
|
||||
virtual void pre_migrate() { }
|
||||
virtual void post_migrate() { }
|
||||
};
|
||||
|
||||
/*class REGION {
|
||||
void get_bbox(Rect& bbox) const;
|
||||
bool contains_point(int x, int y) const;
|
||||
|
||||
};*/
|
||||
|
||||
template <class REGION>
|
||||
static bool find_anchor_point(const REGION& region, Point& pt)
|
||||
{
|
||||
static const unsigned int lookup_size = 20;
|
||||
unsigned int width;
|
||||
unsigned int height;
|
||||
Rect bbox;
|
||||
|
||||
region.get_bbox(bbox);
|
||||
width = bbox.right - bbox.left;
|
||||
height = bbox.bottom - bbox.top;
|
||||
|
||||
int div = 2;
|
||||
|
||||
for (;;) {
|
||||
unsigned int h_unit;
|
||||
unsigned int v_unit;
|
||||
|
||||
if ((v_unit = height / div) * 2 < lookup_size || (h_unit = width / div) * 2 < lookup_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned int y = v_unit; y < v_unit * div; y += v_unit * 2) {
|
||||
for (unsigned int x = h_unit; x < h_unit * div; x += h_unit * 2) {
|
||||
if (!region.contains_point(bbox.left + x, bbox.top + y)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Rect r;
|
||||
r.left = bbox.left + x - lookup_size / 2;
|
||||
r.right = r.left + lookup_size;
|
||||
r.top = bbox.top + y - lookup_size / 2;
|
||||
r.bottom = r.top + lookup_size;
|
||||
|
||||
if (!region.contains_point(r.left, r.top) ||
|
||||
!region.contains_point(r.right, r.top) ||
|
||||
!region.contains_point(r.left, r.bottom) ||
|
||||
!region.contains_point(r.right, r.bottom)) {
|
||||
continue;
|
||||
}
|
||||
pt.x = bbox.left + x;
|
||||
pt.y = bbox.top + y;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
div *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
20
client/region.cpp
Normal file
20
client/region.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "../common/region.c"
|
||||
|
||||
20
client/rop3.cpp
Normal file
20
client/rop3.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "../common/rop3.c"
|
||||
|
||||
777
client/screen.cpp
Normal file
777
client/screen.cpp
Normal file
@ -0,0 +1,777 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "screen.h"
|
||||
#include "application.h"
|
||||
#include "screen_layer.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
#include "monitor.h"
|
||||
#include "red_pixmap_cairo.h"
|
||||
#include "resource.h"
|
||||
#include "icon.h"
|
||||
|
||||
class UpdateEvent: public Event {
|
||||
public:
|
||||
UpdateEvent(int screen) : _screen (screen) {}
|
||||
|
||||
virtual void responce(Application& application)
|
||||
{
|
||||
RedScreen* screen = application.find_screen(_screen);
|
||||
if (screen) {
|
||||
screen->update();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int _screen;
|
||||
};
|
||||
|
||||
void periodic_update_proc(void *opaque, TimerID timer)
|
||||
{
|
||||
RedScreen* screen = (RedScreen*)opaque;
|
||||
|
||||
screen->periodic_update();
|
||||
}
|
||||
|
||||
RedScreen::RedScreen(Application& owner, int id, const std::wstring& name, int width, int height)
|
||||
: _owner (owner)
|
||||
, _id (id)
|
||||
, _refs (1)
|
||||
, _window (*this)
|
||||
, _active (false)
|
||||
, _captured (false)
|
||||
, _full_screen (false)
|
||||
, _out_of_sync (false)
|
||||
, _frame_area (false)
|
||||
, _cursor_visible (true)
|
||||
, _periodic_update (false)
|
||||
, _key_interception (false)
|
||||
, _update_by_timer (true)
|
||||
, _forec_update_timer (0)
|
||||
, _update_timer (INVALID_TIMER)
|
||||
, _composit_area (NULL)
|
||||
, _update_mark (1)
|
||||
, _monitor (NULL)
|
||||
, _default_cursor (NULL)
|
||||
, _active_cursor (NULL)
|
||||
, _inactive_cursor (NULL)
|
||||
, _pointer_location (POINTER_OUTSIDE_WINDOW)
|
||||
, _pixel_format_index (0)
|
||||
, _update_interrupt_trigger (NULL)
|
||||
{
|
||||
region_init(&_dirty_region);
|
||||
set_name(name);
|
||||
_size.x = width;
|
||||
_size.y = height;
|
||||
_origin.x = _origin.y = 0;
|
||||
create_composit_area();
|
||||
_window.resize(_size.x, _size.y);
|
||||
save_position();
|
||||
_update_timer = Platform::create_interval_timer(periodic_update_proc, this);
|
||||
if (_update_timer == INVALID_TIMER) {
|
||||
THROW("create timer failed");
|
||||
}
|
||||
if ((_default_cursor = Platform::create_default_cursor()) == NULL) {
|
||||
THROW("create default cursor failed");
|
||||
}
|
||||
if ((_inactive_cursor = Platform::create_inactive_cursor()) == NULL) {
|
||||
THROW("create inactive cursor failed");
|
||||
}
|
||||
_window.set_cursor(_default_cursor);
|
||||
AutoRef<Menu> menu(_owner.get_app_menu());
|
||||
_window.set_menu(*menu);
|
||||
AutoRef<Icon> icon(Platform::load_icon(RED_ICON_RES_ID));
|
||||
_window.set_icon(*icon);
|
||||
}
|
||||
|
||||
RedScreen::~RedScreen()
|
||||
{
|
||||
bool captured = is_captured();
|
||||
relase_inputs();
|
||||
destroy_composit_area();
|
||||
Platform::destroy_interval_timer(_update_timer);
|
||||
_owner.on_screen_destroyed(_id, captured);
|
||||
region_destroy(&_dirty_region);
|
||||
if (_default_cursor) {
|
||||
_default_cursor->unref();
|
||||
}
|
||||
if (_active_cursor) {
|
||||
_active_cursor->unref();
|
||||
}
|
||||
if (_inactive_cursor) {
|
||||
_inactive_cursor->unref();
|
||||
}
|
||||
}
|
||||
|
||||
void RedScreen::show(bool activate, RedScreen* pos)
|
||||
{
|
||||
_window.position_after((pos) ? &pos->_window : NULL);
|
||||
show();
|
||||
if (activate) {
|
||||
_window.activate();
|
||||
}
|
||||
}
|
||||
|
||||
RedScreen* RedScreen::ref()
|
||||
{
|
||||
_refs++;
|
||||
return this;
|
||||
}
|
||||
|
||||
void RedScreen::unref()
|
||||
{
|
||||
if (!--_refs) {
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
void RedScreen::destroy_composit_area()
|
||||
{
|
||||
if (_composit_area) {
|
||||
delete _composit_area;
|
||||
_composit_area = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void RedScreen::create_composit_area()
|
||||
{
|
||||
destroy_composit_area();
|
||||
_composit_area = new RedPixmapCairo(_size.x, _size.y, RedPixmap::RGB32,
|
||||
false, NULL, NULL);
|
||||
}
|
||||
|
||||
void RedScreen::adjust_window_rect(int x, int y)
|
||||
{
|
||||
_window.move_and_resize(x, y, _size.x, _size.y);
|
||||
}
|
||||
|
||||
void RedScreen::set_mode(int width, int height, int depth)
|
||||
{
|
||||
RecurciveLock lock(_update_lock);
|
||||
_size.x = width;
|
||||
_size.y = height;
|
||||
create_composit_area();
|
||||
if (_full_screen) {
|
||||
bool cuptur = _owner.rearrange_monitors(*this);
|
||||
__show_full_screen();
|
||||
if (cuptur) {
|
||||
capture_inputs();
|
||||
}
|
||||
} else {
|
||||
_window.resize(_size.x, _size.y);
|
||||
}
|
||||
notify_new_size();
|
||||
}
|
||||
|
||||
void RedScreen::set_name(const std::wstring& name)
|
||||
{
|
||||
if (!name.empty()) {
|
||||
wstring_printf(_name, name.c_str(), _id);
|
||||
}
|
||||
_window.set_title(_name);
|
||||
}
|
||||
|
||||
void RedScreen::attach_layer(ScreenLayer& layer)
|
||||
{
|
||||
RecurciveLock lock(_update_lock);
|
||||
int order = layer.z_order();
|
||||
|
||||
if ((int)_layes.size() < order + 1) {
|
||||
_layes.resize(order + 1);
|
||||
}
|
||||
if (_layes[order]) {
|
||||
THROW("layer in use");
|
||||
}
|
||||
layer.set_screen(this);
|
||||
_layes[order] = &layer;
|
||||
ref();
|
||||
lock.unlock();
|
||||
layer.invalidate();
|
||||
}
|
||||
|
||||
void RedScreen::detach_layer(ScreenLayer& layer)
|
||||
{
|
||||
RecurciveLock lock(_update_lock);
|
||||
int order = layer.z_order();
|
||||
|
||||
if ((int)_layes.size() < order + 1 || _layes[order] != &layer) {
|
||||
THROW("not found");
|
||||
}
|
||||
QRegion layer_area;
|
||||
region_clone(&layer_area, &layer.area());
|
||||
_layes[order]->set_screen(NULL);
|
||||
_layes[order] = NULL;
|
||||
lock.unlock();
|
||||
invalidate(layer_area);
|
||||
region_destroy(&layer_area);
|
||||
unref();
|
||||
}
|
||||
|
||||
void RedScreen::composit_to_screen(RedDrawable& win_dc, const QRegion& region)
|
||||
{
|
||||
for (int i = 0; i < (int)region.num_rects; i++) {
|
||||
Rect* r = ®ion.rects[i];
|
||||
win_dc.copy_pixels(*_composit_area, r->left, r->top, *r);
|
||||
}
|
||||
}
|
||||
|
||||
void RedScreen::notify_new_size()
|
||||
{
|
||||
for (int i = 0; i < (int)_layes.size(); i++) {
|
||||
if (!_layes[i]) {
|
||||
continue;
|
||||
}
|
||||
_layes[i]->on_size_changed();
|
||||
}
|
||||
}
|
||||
|
||||
inline void RedScreen::begin_update(QRegion& direct_rgn, QRegion& composit_rgn,
|
||||
QRegion& frame_rgn)
|
||||
{
|
||||
region_init(&composit_rgn);
|
||||
RecurciveLock lock(_update_lock);
|
||||
region_clone(&direct_rgn, &_dirty_region);
|
||||
region_clear(&_dirty_region);
|
||||
_update_mark++;
|
||||
lock.unlock();
|
||||
|
||||
QRegion rect_rgn;
|
||||
Rect r;
|
||||
r.top = r.left = 0;
|
||||
r.right = _size.x;
|
||||
r.bottom = _size.y;
|
||||
region_init(&rect_rgn);
|
||||
region_add(&rect_rgn, &r);
|
||||
|
||||
if (_frame_area) {
|
||||
region_clone(&frame_rgn, &direct_rgn);
|
||||
region_exclude(&frame_rgn, &rect_rgn);
|
||||
}
|
||||
region_and(&direct_rgn, &rect_rgn);
|
||||
region_destroy(&rect_rgn);
|
||||
|
||||
for (int i = _layes.size() - 1; i >= 0; i--) {
|
||||
ScreenLayer* layer;
|
||||
|
||||
if (!(layer = _layes[i])) {
|
||||
continue;
|
||||
}
|
||||
layer->begin_update(direct_rgn, composit_rgn);
|
||||
}
|
||||
}
|
||||
|
||||
inline void RedScreen::update_done()
|
||||
{
|
||||
for (unsigned int i = 0; i < _layes.size(); i++) {
|
||||
ScreenLayer* layer;
|
||||
|
||||
if (!(layer = _layes[i])) {
|
||||
continue;
|
||||
}
|
||||
layer->on_update_completion(_update_mark - 1);
|
||||
}
|
||||
}
|
||||
|
||||
inline void RedScreen::update_composit(QRegion& composit_rgn)
|
||||
{
|
||||
erase_background(*_composit_area, composit_rgn);
|
||||
for (int i = 0; i < (int)_layes.size(); i++) {
|
||||
ScreenLayer* layer;
|
||||
|
||||
if (!(layer = _layes[i])) {
|
||||
continue;
|
||||
}
|
||||
QRegion& dest_region = layer->composit_area();
|
||||
region_or(&composit_rgn, &dest_region);
|
||||
layer->copy_pixels(dest_region, *_composit_area);
|
||||
}
|
||||
}
|
||||
|
||||
inline void RedScreen::draw_direct(RedDrawable& win_dc, QRegion& direct_rgn, QRegion& composit_rgn,
|
||||
QRegion& frame_rgn)
|
||||
{
|
||||
erase_background(win_dc, direct_rgn);
|
||||
|
||||
if (_frame_area) {
|
||||
erase_background(win_dc, frame_rgn);
|
||||
region_destroy(&frame_rgn);
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)_layes.size(); i++) {
|
||||
ScreenLayer* layer;
|
||||
|
||||
if (!(layer = _layes[i])) {
|
||||
continue;
|
||||
}
|
||||
QRegion& dest_region = layer->direct_area();
|
||||
layer->copy_pixels(dest_region, win_dc);
|
||||
}
|
||||
}
|
||||
|
||||
void RedScreen::periodic_update()
|
||||
{
|
||||
bool need_update;
|
||||
RecurciveLock lock(_update_lock);
|
||||
if (is_dirty()) {
|
||||
need_update = true;
|
||||
} else {
|
||||
if (!_forec_update_timer) {
|
||||
Platform::deactivate_interval_timer(_update_timer);
|
||||
_periodic_update = false;
|
||||
}
|
||||
need_update = false;
|
||||
}
|
||||
lock.unlock();
|
||||
|
||||
if (need_update) {
|
||||
if (update_by_interrupt()) {
|
||||
interrupt_update();
|
||||
} else {
|
||||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RedScreen::activate_timer()
|
||||
{
|
||||
RecurciveLock lock(_update_lock);
|
||||
if (_periodic_update) {
|
||||
return;
|
||||
}
|
||||
_periodic_update = true;
|
||||
lock.unlock();
|
||||
if (!Platform::activate_interval_timer(_update_timer, 1000 / 30)) {
|
||||
LOG_WARN("failed");
|
||||
}
|
||||
}
|
||||
|
||||
void RedScreen::update()
|
||||
{
|
||||
if (is_out_of_sync()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QRegion direct_rgn;
|
||||
QRegion composit_rgn;
|
||||
QRegion frame_rgn;
|
||||
|
||||
begin_update(direct_rgn, composit_rgn, frame_rgn);
|
||||
update_composit(composit_rgn);
|
||||
draw_direct(_window, direct_rgn, composit_rgn, frame_rgn);
|
||||
composit_to_screen(_window, composit_rgn);
|
||||
update_done();
|
||||
region_destroy(&direct_rgn);
|
||||
region_destroy(&composit_rgn);
|
||||
|
||||
if (_update_by_timer) {
|
||||
activate_timer();
|
||||
}
|
||||
}
|
||||
|
||||
bool RedScreen::_invalidate(const Rect& rect, bool urgent, uint64_t& update_mark)
|
||||
{
|
||||
RecurciveLock lock(_update_lock);
|
||||
bool update_triger = !is_dirty() && (urgent || !_periodic_update);
|
||||
region_add(&_dirty_region, &rect);
|
||||
update_mark = _update_mark;
|
||||
return update_triger;
|
||||
}
|
||||
|
||||
uint64_t RedScreen::invalidate(const Rect& rect, bool urgent)
|
||||
{
|
||||
uint64_t update_mark;
|
||||
if (_invalidate(rect, urgent, update_mark)) {
|
||||
if (!urgent && _update_by_timer) {
|
||||
activate_timer();
|
||||
} else {
|
||||
if (update_by_interrupt()) {
|
||||
interrupt_update();
|
||||
} else {
|
||||
AutoRef<UpdateEvent> update_event(new UpdateEvent(_id));
|
||||
_owner.push_event(*update_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
return update_mark;
|
||||
}
|
||||
|
||||
void RedScreen::invalidate(const QRegion ®ion)
|
||||
{
|
||||
Rect *r = region.rects;
|
||||
Rect *end = r + region.num_rects;
|
||||
while (r != end) {
|
||||
invalidate(*r++, false);
|
||||
}
|
||||
}
|
||||
|
||||
inline void RedScreen::erase_background(RedDrawable& dc, const QRegion& composit_rgn)
|
||||
{
|
||||
for (int i = 0; i < (int)composit_rgn.num_rects; i++) {
|
||||
dc.fill_rect(composit_rgn.rects[i], 0);
|
||||
}
|
||||
}
|
||||
|
||||
void RedScreen::reset_mouse_pos()
|
||||
{
|
||||
_window.set_mouse_position(_mouse_anchor_point.x, _mouse_anchor_point.y);
|
||||
}
|
||||
|
||||
void RedScreen::capture_inputs()
|
||||
{
|
||||
if (_captured || !_window.get_mouse_anchor_point(_mouse_anchor_point)) {
|
||||
return;
|
||||
}
|
||||
if (_owner.get_mouse_mode() == RED_MOUSE_MODE_SERVER) {
|
||||
_window.hide_cursor();
|
||||
reset_mouse_pos();
|
||||
_window.cupture_mouse();
|
||||
}
|
||||
#ifndef NO_KEY_GRAB
|
||||
_window.start_key_interception();
|
||||
#endif
|
||||
_captured = true;
|
||||
}
|
||||
|
||||
void RedScreen::relase_inputs()
|
||||
{
|
||||
if (!_captured) {
|
||||
return;
|
||||
}
|
||||
#ifndef NO_KEY_GRAB
|
||||
_window.stop_key_interception();
|
||||
#endif
|
||||
_captured = false;
|
||||
_window.release_mouse();
|
||||
if (_owner.get_mouse_mode() == RED_MOUSE_MODE_SERVER) {
|
||||
_window.set_cursor(_default_cursor);
|
||||
}
|
||||
}
|
||||
|
||||
void RedScreen::set_cursor(CursorData* cursor)
|
||||
{
|
||||
if (cursor) {
|
||||
if (_active_cursor) {
|
||||
_active_cursor->unref();
|
||||
}
|
||||
if (!cursor->get_local()) {
|
||||
AutoRef<LocalCursor> cur(Platform::create_local_cursor(cursor));
|
||||
if (*cur == NULL) {
|
||||
THROW("create local cursor failed");
|
||||
}
|
||||
cursor->set_local(*cur);
|
||||
_active_cursor = (*cur)->ref();
|
||||
} else {
|
||||
_active_cursor = (cursor->get_local())->ref();
|
||||
}
|
||||
}
|
||||
_cursor_visible = !!cursor;
|
||||
update_active_cursor();
|
||||
}
|
||||
|
||||
void RedScreen::update_active_cursor()
|
||||
{
|
||||
if (_owner.get_mouse_mode() == RED_MOUSE_MODE_CLIENT &&
|
||||
_pointer_location == POINTER_IN_ACTIVE_AREA) {
|
||||
if (_cursor_visible && _active_cursor) {
|
||||
_window.set_cursor(_active_cursor);
|
||||
} else {
|
||||
_window.hide_cursor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RedScreen::on_mouse_motion(int x, int y, unsigned int buttons_state)
|
||||
{
|
||||
switch (_owner.get_mouse_mode()) {
|
||||
case RED_MOUSE_MODE_CLIENT:
|
||||
if (!_frame_area) {
|
||||
_owner.on_mouse_position(x, y, buttons_state, _id);
|
||||
} else if (x >= 0 && x < _size.x && y >= 0 && y < _size.y) {
|
||||
_owner.on_mouse_position(x, y, buttons_state, _id);
|
||||
if (_pointer_location != POINTER_IN_ACTIVE_AREA) {
|
||||
_pointer_location = POINTER_IN_ACTIVE_AREA;
|
||||
update_active_cursor();
|
||||
}
|
||||
} else if (_pointer_location != POINTER_IN_FRAME_AREA) {
|
||||
_pointer_location = POINTER_IN_FRAME_AREA;
|
||||
_window.set_cursor(_inactive_cursor);
|
||||
}
|
||||
break;
|
||||
case RED_MOUSE_MODE_SERVER:
|
||||
if (_captured && (x != _mouse_anchor_point.x || y != _mouse_anchor_point.y)) {
|
||||
_owner.on_mouse_motion(x - _mouse_anchor_point.x,
|
||||
y - _mouse_anchor_point.y,
|
||||
buttons_state);
|
||||
reset_mouse_pos();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
THROW("invalid mouse mode");
|
||||
}
|
||||
}
|
||||
|
||||
void RedScreen::on_button_press(RedButton button, unsigned int buttons_state)
|
||||
{
|
||||
if (_owner.get_mouse_mode() == RED_MOUSE_MODE_CLIENT &&
|
||||
_pointer_location != POINTER_IN_ACTIVE_AREA) {
|
||||
return;
|
||||
}
|
||||
if (!mouse_is_captured()) {
|
||||
if (_owner.get_mouse_mode() == RED_MOUSE_MODE_SERVER && button != REDC_MOUSE_LBUTTON) {
|
||||
return;
|
||||
}
|
||||
capture_inputs();
|
||||
if (_owner.get_mouse_mode() == RED_MOUSE_MODE_SERVER) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
_owner.on_mouse_down(button, buttons_state);
|
||||
}
|
||||
|
||||
void RedScreen::on_button_release(RedButton button, unsigned int buttons_state)
|
||||
{
|
||||
if (!mouse_is_captured()) {
|
||||
if (_owner.get_mouse_mode() == RED_MOUSE_MODE_SERVER) {
|
||||
return;
|
||||
}
|
||||
capture_inputs();
|
||||
}
|
||||
_owner.on_mouse_up(button, buttons_state);
|
||||
}
|
||||
|
||||
void RedScreen::on_key_press(RedKey key)
|
||||
{
|
||||
_owner.on_key_down(key);
|
||||
}
|
||||
|
||||
void RedScreen::on_key_release(RedKey key)
|
||||
{
|
||||
_owner.on_key_up(key);
|
||||
}
|
||||
|
||||
void RedScreen::on_deactivate()
|
||||
{
|
||||
relase_inputs();
|
||||
_active = false;
|
||||
_owner.on_deactivate_screen(this);
|
||||
}
|
||||
|
||||
void RedScreen::on_activate()
|
||||
{
|
||||
_active = true;
|
||||
_owner.on_activate_screen(this);
|
||||
}
|
||||
|
||||
void RedScreen::on_pointer_enter()
|
||||
{
|
||||
if (!_frame_area) {
|
||||
_pointer_location = POINTER_IN_ACTIVE_AREA;
|
||||
update_active_cursor();
|
||||
}
|
||||
}
|
||||
|
||||
void RedScreen::on_pointer_leave()
|
||||
{
|
||||
_pointer_location = POINTER_OUTSIDE_WINDOW;
|
||||
}
|
||||
|
||||
void RedScreen::enter_modal_loop()
|
||||
{
|
||||
_forec_update_timer++;
|
||||
activate_timer();
|
||||
}
|
||||
|
||||
void RedScreen::exit_modal_loop()
|
||||
{
|
||||
ASSERT(_forec_update_timer > 0)
|
||||
_forec_update_timer--;
|
||||
}
|
||||
|
||||
void RedScreen::pre_migrate()
|
||||
{
|
||||
for (int i = 0; i < (int)_layes.size(); i++) {
|
||||
if (!_layes[i]) {
|
||||
continue;
|
||||
}
|
||||
_layes[i]->pre_migrate();
|
||||
}
|
||||
}
|
||||
|
||||
void RedScreen::post_migrate()
|
||||
{
|
||||
for (int i = 0; i < (int)_layes.size(); i++) {
|
||||
if (!_layes[i]) {
|
||||
continue;
|
||||
}
|
||||
_layes[i]->post_migrate();
|
||||
}
|
||||
}
|
||||
|
||||
void RedScreen::exit_full_screen()
|
||||
{
|
||||
if (!_full_screen) {
|
||||
return;
|
||||
}
|
||||
RecurciveLock lock(_update_lock);
|
||||
_window.hide();
|
||||
region_clear(&_dirty_region);
|
||||
_window.set_type(RedWindow::TYPE_NORMAL);
|
||||
adjust_window_rect(_save_pos.x, _save_pos.y);
|
||||
_origin.x = _origin.y = 0;
|
||||
_window.set_origin(0, 0);
|
||||
show();
|
||||
_full_screen = false;
|
||||
_out_of_sync = false;
|
||||
_frame_area = false;
|
||||
}
|
||||
|
||||
void RedScreen::save_position()
|
||||
{
|
||||
_save_pos = _window.get_position();
|
||||
}
|
||||
|
||||
void RedScreen::__show_full_screen()
|
||||
{
|
||||
if (!_monitor) {
|
||||
hide();
|
||||
return;
|
||||
}
|
||||
Point position = _monitor->get_position();
|
||||
Point monitor_size = _monitor->get_size();
|
||||
_frame_area = false;
|
||||
region_clear(&_dirty_region);
|
||||
_window.set_type(RedWindow::TYPE_FULLSCREEN);
|
||||
_window.move_and_resize(position.x, position.y, monitor_size.x, monitor_size.y);
|
||||
|
||||
if (!(_out_of_sync = _monitor->is_out_of_sync())) {
|
||||
ASSERT(monitor_size.x >= _size.x);
|
||||
ASSERT(monitor_size.y >= _size.y);
|
||||
_origin.x = (monitor_size.x - _size.x) / 2;
|
||||
_origin.y = (monitor_size.y - _size.y) / 2;
|
||||
_frame_area = monitor_size.x != _size.x || monitor_size.y != _size.y;
|
||||
} else {
|
||||
_origin.x = _origin.y = 0;
|
||||
}
|
||||
_window.set_origin(_origin.x, _origin.y);
|
||||
show();
|
||||
}
|
||||
|
||||
void RedScreen::show_full_screen()
|
||||
{
|
||||
if (_full_screen) {
|
||||
return;
|
||||
}
|
||||
RecurciveLock lock(_update_lock);
|
||||
hide();
|
||||
save_position();
|
||||
_full_screen = true;
|
||||
__show_full_screen();
|
||||
}
|
||||
|
||||
void RedScreen::minimize()
|
||||
{
|
||||
_window.minimize();
|
||||
}
|
||||
|
||||
void RedScreen::position_full_screen(const Point& position)
|
||||
{
|
||||
if (!_full_screen) {
|
||||
return;
|
||||
}
|
||||
|
||||
_window.move(position.x, position.y);
|
||||
}
|
||||
|
||||
void RedScreen::hide()
|
||||
{
|
||||
_window.hide();
|
||||
}
|
||||
|
||||
void RedScreen::show()
|
||||
{
|
||||
RecurciveLock lock(_update_lock);
|
||||
_window.show(_monitor ? _monitor->get_screen_id() : 0);
|
||||
}
|
||||
|
||||
void RedScreen::activate()
|
||||
{
|
||||
_window.activate();
|
||||
}
|
||||
|
||||
void RedScreen::external_show()
|
||||
{
|
||||
DBG(0, "Entry");
|
||||
_window.external_show();
|
||||
}
|
||||
|
||||
void RedScreen::on_exposed_rect(const Rect& area)
|
||||
{
|
||||
if (is_out_of_sync()) {
|
||||
_window.fill_rect(area, rgb32_make(0xff, 0xff, 0xff));
|
||||
return;
|
||||
}
|
||||
invalidate(area, false);
|
||||
}
|
||||
|
||||
int RedScreen::get_screen_id()
|
||||
{
|
||||
return _monitor ? _monitor->get_screen_id() : 0;
|
||||
}
|
||||
|
||||
#ifdef USE_OGL
|
||||
void RedScreen::untouch_context()
|
||||
{
|
||||
_window.untouch_context();
|
||||
}
|
||||
|
||||
bool RedScreen::need_recreate_context_gl()
|
||||
{
|
||||
if (_full_screen) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void RedScreen::set_update_interrupt_trigger(EventsLoop::Trigger *trigger)
|
||||
{
|
||||
_update_interrupt_trigger = trigger;
|
||||
}
|
||||
|
||||
bool RedScreen::update_by_interrupt()
|
||||
{
|
||||
return _update_interrupt_trigger != NULL;
|
||||
}
|
||||
|
||||
void RedScreen::interrupt_update()
|
||||
{
|
||||
_update_interrupt_trigger->trigger();
|
||||
}
|
||||
|
||||
void RedScreen::set_type_gl()
|
||||
{
|
||||
_window.set_type_gl();
|
||||
}
|
||||
|
||||
void RedScreen::unset_type_gl()
|
||||
{
|
||||
_window.unset_type_gl();
|
||||
}
|
||||
|
||||
181
client/screen.h
Normal file
181
client/screen.h
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_SCREEN
|
||||
#define _H_SCREEN
|
||||
|
||||
#include "common.h"
|
||||
#include "region.h"
|
||||
#include "red_key.h"
|
||||
#include "GL/gl.h"
|
||||
|
||||
#include "red_window.h"
|
||||
#include "platform.h"
|
||||
#include "events_loop.h"
|
||||
#include "threads.h"
|
||||
|
||||
class Application;
|
||||
class ScreenLayer;
|
||||
class Monitor;
|
||||
|
||||
enum {
|
||||
SCREEN_LAYER_DISPLAY,
|
||||
SCREEN_LAYER_CURSOR,
|
||||
SCREEN_LAYER_GUI,
|
||||
};
|
||||
|
||||
class RedScreen: public RedWindow::Listener {
|
||||
public:
|
||||
RedScreen(Application& owner, int id, const std::wstring& name, int width, int height);
|
||||
|
||||
RedScreen* ref();
|
||||
void unref();
|
||||
|
||||
void attach_layer(ScreenLayer& layer);
|
||||
void detach_layer(ScreenLayer& layer);
|
||||
void set_mode(int width, int height, int depth);
|
||||
void set_name(const std::wstring& name);
|
||||
uint64_t invalidate(const Rect& rect, bool urgent);
|
||||
void invalidate(const QRegion ®ion);
|
||||
void relase_inputs();
|
||||
void capture_inputs();
|
||||
bool is_captured() { return _captured;}
|
||||
bool intercepts_sys_key() { return _key_interception;}
|
||||
Point get_size() { return _size;}
|
||||
bool has_monitor() { return _monitor != 0;}
|
||||
void set_monitor(Monitor *monitor) { _monitor = monitor;}
|
||||
Monitor* get_monitor() { return _monitor;}
|
||||
RedWindow* get_window() { return &_window;}
|
||||
void set_cursor(CursorData* cursor);
|
||||
void exit_full_screen();
|
||||
void minimize();
|
||||
void show(bool activate, RedScreen* pos);
|
||||
void show_full_screen();
|
||||
void position_full_screen(const Point& position);
|
||||
void hide();
|
||||
void show();
|
||||
void activate();
|
||||
void external_show();
|
||||
|
||||
int get_id() { return _id;}
|
||||
int get_screen_id();
|
||||
|
||||
#ifdef USE_OGL
|
||||
void untouch_context();
|
||||
bool need_recreate_context_gl();
|
||||
#endif
|
||||
void set_update_interrupt_trigger(EventsLoop::Trigger *trigger);
|
||||
bool update_by_interrupt();
|
||||
void interrupt_update();
|
||||
void set_type_gl();
|
||||
void unset_type_gl();
|
||||
|
||||
void update();
|
||||
|
||||
private:
|
||||
friend void periodic_update_proc(void *opaque, TimerID timer);
|
||||
friend class UpdateEvent;
|
||||
|
||||
virtual ~RedScreen();
|
||||
void create_composit_area();
|
||||
|
||||
void destroy_composit_area();
|
||||
void erase_background(RedDrawable& dc, const QRegion& region);
|
||||
void notify_new_size();
|
||||
void adjust_window_rect(int x, int y);
|
||||
void save_position();
|
||||
bool is_out_of_sync() { return _out_of_sync;}
|
||||
void __show_full_screen();
|
||||
|
||||
bool _invalidate(const Rect& rect, bool urgent, uint64_t& update_mark);
|
||||
void begin_update(QRegion& direct_rgn, QRegion& composit_rgn, QRegion& frame_rgn);
|
||||
void update_composit(QRegion& composit_rgn);
|
||||
void draw_direct(RedDrawable& win_dc, QRegion& direct_rgn, QRegion& composit_rgn,
|
||||
QRegion& frame_rgn);
|
||||
void activate_timer();
|
||||
void update_done();
|
||||
void periodic_update();
|
||||
bool is_dirty() {return !region_is_empty(&_dirty_region);}
|
||||
void composit_to_screen(RedDrawable& win_dc, const QRegion& region);
|
||||
|
||||
void reset_mouse_pos();
|
||||
bool mouse_is_captured() { return _captured;}
|
||||
void update_active_cursor();
|
||||
|
||||
virtual void on_exposed_rect(const Rect& area);
|
||||
virtual void on_mouse_motion(int x, int y, unsigned int buttons_state);
|
||||
virtual void on_key_press(RedKey key);
|
||||
virtual void on_key_release(RedKey key);
|
||||
virtual void on_button_press(RedButton button, unsigned int buttons_state);
|
||||
virtual void on_button_release(RedButton button, unsigned int buttons_state);
|
||||
virtual void on_deactivate();
|
||||
virtual void on_activate();
|
||||
virtual void on_pointer_enter();
|
||||
virtual void on_pointer_leave();
|
||||
virtual void on_start_key_interception() { _key_interception = true;}
|
||||
virtual void on_stop_key_interception() { _key_interception = false;}
|
||||
virtual void enter_modal_loop();
|
||||
virtual void exit_modal_loop();
|
||||
|
||||
virtual void pre_migrate();
|
||||
virtual void post_migrate();
|
||||
|
||||
private:
|
||||
Application& _owner;
|
||||
int _id;
|
||||
int _refs;
|
||||
std::wstring _name;
|
||||
RedWindow _window;
|
||||
std::vector<ScreenLayer*> _layes;
|
||||
QRegion _dirty_region;
|
||||
RecurciveMutex _update_lock;
|
||||
bool _active;
|
||||
bool _captured;
|
||||
bool _full_screen;
|
||||
bool _out_of_sync;
|
||||
bool _frame_area;
|
||||
bool _cursor_visible;
|
||||
bool _periodic_update;
|
||||
bool _key_interception;
|
||||
bool _update_by_timer;
|
||||
int _forec_update_timer;
|
||||
TimerID _update_timer;
|
||||
RedDrawable* _composit_area;
|
||||
uint64_t _update_mark;
|
||||
|
||||
Point _size;
|
||||
Point _origin;
|
||||
Point _mouse_anchor_point;
|
||||
Point _save_pos;
|
||||
Monitor* _monitor;
|
||||
|
||||
LocalCursor* _default_cursor;
|
||||
LocalCursor* _active_cursor;
|
||||
LocalCursor* _inactive_cursor;
|
||||
|
||||
enum PointerLocation {
|
||||
POINTER_IN_ACTIVE_AREA,
|
||||
POINTER_IN_FRAME_AREA,
|
||||
POINTER_OUTSIDE_WINDOW,
|
||||
};
|
||||
PointerLocation _pointer_location;
|
||||
int _pixel_format_index;
|
||||
EventsLoop::Trigger *_update_interrupt_trigger;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
200
client/screen_layer.cpp
Normal file
200
client/screen_layer.cpp
Normal file
@ -0,0 +1,200 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "screen_layer.h"
|
||||
#include "utils.h"
|
||||
#include "screen.h"
|
||||
#include "application.h"
|
||||
#include "debug.h"
|
||||
|
||||
class AttacheLayerEvent: public SyncEvent {
|
||||
public:
|
||||
AttacheLayerEvent(ScreenLayer& layer, int screen_id) : _layer (layer), _screen_id (screen_id) {}
|
||||
|
||||
virtual void do_responce(Application& application);
|
||||
|
||||
private:
|
||||
ScreenLayer& _layer;
|
||||
int _screen_id;
|
||||
};
|
||||
|
||||
void AttacheLayerEvent::do_responce(Application& application)
|
||||
{
|
||||
AutoRef<RedScreen> screen(application.get_screen(_screen_id));
|
||||
(*screen)->attach_layer(_layer);
|
||||
}
|
||||
|
||||
class DetacheLayerEvent: public SyncEvent {
|
||||
public:
|
||||
DetacheLayerEvent(ScreenLayer& _layer) : _layer (_layer) {}
|
||||
|
||||
virtual void do_responce(Application& application);
|
||||
|
||||
private:
|
||||
ScreenLayer& _layer;
|
||||
};
|
||||
|
||||
void DetacheLayerEvent::do_responce(Application& application)
|
||||
{
|
||||
_layer.screen()->detach_layer(_layer);
|
||||
}
|
||||
|
||||
ScreenLayer::ScreenLayer(int z_order, bool opaque)
|
||||
: _screen (NULL)
|
||||
, _z_order (z_order)
|
||||
, _opaque (opaque)
|
||||
, _using_ogl (false)
|
||||
{
|
||||
region_init(&_area);
|
||||
region_init(&_direct_area);
|
||||
region_init(&_composit_area);
|
||||
}
|
||||
|
||||
ScreenLayer::~ScreenLayer()
|
||||
{
|
||||
ASSERT(!_screen);
|
||||
region_destroy(&_area);
|
||||
region_destroy(&_direct_area);
|
||||
region_destroy(&_composit_area);
|
||||
}
|
||||
|
||||
uint64_t ScreenLayer::invalidate_rect(const Rect& r, bool urgent)
|
||||
{
|
||||
return _screen->invalidate(r, urgent);
|
||||
}
|
||||
|
||||
uint64_t ScreenLayer::invalidate(const Rect& r, bool urgent)
|
||||
{
|
||||
if (!_screen) {
|
||||
return 0;
|
||||
}
|
||||
return invalidate_rect(r, urgent);
|
||||
}
|
||||
|
||||
void ScreenLayer::invalidate(const QRegion& region)
|
||||
{
|
||||
if (!_screen) {
|
||||
return;
|
||||
}
|
||||
Rect *r = region.rects;
|
||||
Rect *end = r + region.num_rects;
|
||||
while (r != end) {
|
||||
invalidate_rect(*r++, false);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenLayer::invalidate()
|
||||
{
|
||||
invalidate(_area);
|
||||
}
|
||||
|
||||
void ScreenLayer::set_area(const QRegion& area)
|
||||
{
|
||||
Lock lock(_area_lock);
|
||||
invalidate();
|
||||
region_destroy(&_area);
|
||||
region_clone(&_area, &area);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void ScreenLayer::clear_area()
|
||||
{
|
||||
Lock lock(_area_lock);
|
||||
invalidate();
|
||||
region_clear(&_area);
|
||||
}
|
||||
|
||||
void ScreenLayer::set_rect_area(const Rect& r)
|
||||
{
|
||||
Lock lock(_area_lock);
|
||||
invalidate();
|
||||
region_clear(&_area);
|
||||
region_add(&_area, &r);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void ScreenLayer::offset_area(int dx, int dy)
|
||||
{
|
||||
Lock lock(_area_lock);
|
||||
invalidate();
|
||||
region_offset(&_area, dx, dy);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void ScreenLayer::add_rect_area(const Rect& r)
|
||||
{
|
||||
Lock lock(_area_lock);
|
||||
region_add(&_area, &r);
|
||||
}
|
||||
|
||||
void ScreenLayer::remove_rect_area(const Rect& r)
|
||||
{
|
||||
Lock lock(_area_lock);
|
||||
invalidate();
|
||||
region_remove(&_area, &r);
|
||||
}
|
||||
|
||||
void ScreenLayer::begin_update(QRegion& direct_rgn, QRegion& composit_rgn)
|
||||
{
|
||||
Lock lock(_area_lock);
|
||||
region_destroy(&_direct_area);
|
||||
region_clone(&_direct_area, &_area);
|
||||
region_and(&_direct_area, &direct_rgn);
|
||||
|
||||
region_destroy(&_composit_area);
|
||||
region_clone(&_composit_area, &_area);
|
||||
region_and(&_composit_area, &composit_rgn);
|
||||
|
||||
region_exclude(&direct_rgn, &_direct_area);
|
||||
if (_opaque) {
|
||||
region_exclude(&composit_rgn, &_composit_area);
|
||||
} else {
|
||||
region_or(&composit_rgn, &_direct_area);
|
||||
region_or(&_composit_area, &_direct_area);
|
||||
region_clear(&_direct_area);
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenLayer::attach_to_screen(Application& applicaion, int screen_id)
|
||||
{
|
||||
if (_screen) {
|
||||
return;
|
||||
}
|
||||
AutoRef<AttacheLayerEvent> event(new AttacheLayerEvent(*this, screen_id));
|
||||
applicaion.push_event(*event);
|
||||
(*event)->wait();
|
||||
if (!(*event)->success()) {
|
||||
THROW("attach failed");
|
||||
}
|
||||
ASSERT(_screen);
|
||||
}
|
||||
|
||||
void ScreenLayer::detach_from_screen(Application& applicaion)
|
||||
{
|
||||
if (!_screen) {
|
||||
return;
|
||||
}
|
||||
AutoRef<DetacheLayerEvent> event(new DetacheLayerEvent(*this));
|
||||
applicaion.push_event(*event);
|
||||
(*event)->wait();
|
||||
if (!(*event)->success()) {
|
||||
THROW("detach failed");
|
||||
}
|
||||
ASSERT(!_screen);
|
||||
}
|
||||
|
||||
79
client/screen_layer.h
Normal file
79
client/screen_layer.h
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_SCREEN_LAYER
|
||||
#define _H_SCREEN_LAYER
|
||||
|
||||
#include "threads.h"
|
||||
#include "region.h"
|
||||
|
||||
class RedScreen;
|
||||
class Application;
|
||||
|
||||
class ScreenLayer {
|
||||
public:
|
||||
ScreenLayer(int z_order, bool opaque);
|
||||
virtual ~ScreenLayer();
|
||||
|
||||
void attach_to_screen(Application& applicaion, int screen_id);
|
||||
void detach_from_screen(Application& applicaion);
|
||||
|
||||
void set_screen(RedScreen* screen) { _screen = screen;}
|
||||
RedScreen* screen() { return _screen; }
|
||||
int z_order() { return _z_order;}
|
||||
void set_area(const QRegion& area);
|
||||
void offset_area(int dx, int dy);
|
||||
void clear_area();
|
||||
void set_rect_area(const Rect& r);
|
||||
void add_rect_area(const Rect& r);
|
||||
void remove_rect_area(const Rect& r);
|
||||
void begin_update(QRegion& direct_rgn, QRegion& composit_rgn);
|
||||
void invalidate();
|
||||
uint64_t invalidate(const Rect& r, bool urgent = false);
|
||||
void invalidate(const QRegion& r);
|
||||
|
||||
virtual void copy_pixels(const QRegion& dest_region, RedDrawable& dest_dc) {}
|
||||
|
||||
void set_using_ogl(bool val) {_using_ogl = val;}
|
||||
bool using_ogl() {return _using_ogl;}
|
||||
virtual void on_size_changed() {}
|
||||
|
||||
virtual void pre_migrate() { }
|
||||
virtual void post_migrate() { }
|
||||
|
||||
QRegion& area() { return _area;}
|
||||
QRegion& direct_area() { return _direct_area;}
|
||||
QRegion& composit_area() { return _composit_area;}
|
||||
|
||||
virtual void on_update_completion(uint64_t mark) {}
|
||||
|
||||
private:
|
||||
uint64_t invalidate_rect(const Rect& r, bool urgent);
|
||||
|
||||
private:
|
||||
RedScreen* _screen;
|
||||
int _z_order;
|
||||
bool _opaque;
|
||||
bool _using_ogl;
|
||||
Mutex _area_lock;
|
||||
QRegion _area;
|
||||
QRegion _direct_area;
|
||||
QRegion _composit_area;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
152
client/shared_cache.hpp
Normal file
152
client/shared_cache.hpp
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_SHARED_CACHE
|
||||
#define _H_SHARED_CACHE
|
||||
|
||||
#include "utils.h"
|
||||
#include "threads.h"
|
||||
|
||||
/*class SharedCache::Treat {
|
||||
T* get(T*);
|
||||
void release(T*);
|
||||
const char* name();
|
||||
};*/
|
||||
|
||||
template <class T, class Treat, int HASH_SIZE>
|
||||
class SharedCache {
|
||||
public:
|
||||
SharedCache()
|
||||
: _aborting (false)
|
||||
{
|
||||
memset(_hash, 0, sizeof(_hash));
|
||||
}
|
||||
|
||||
~SharedCache()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void add(uint64_t id, T* data)
|
||||
{
|
||||
Lock lock(_lock);
|
||||
Item** item = &_hash[key(id)];
|
||||
|
||||
while (*item) {
|
||||
if ((*item)->id == id) {
|
||||
(*item)->refs++;
|
||||
return;
|
||||
}
|
||||
item = &(*item)->next;
|
||||
}
|
||||
*item = new Item(id, data);
|
||||
_new_item_cond.notify_all();
|
||||
}
|
||||
|
||||
T* get(uint64_t id)
|
||||
{
|
||||
Lock lock(_lock);
|
||||
Item* item = _hash[key(id)];
|
||||
|
||||
for (;;) {
|
||||
if (!item) {
|
||||
if (_aborting) {
|
||||
THROW("%s aborting", Treat::name());
|
||||
}
|
||||
_new_item_cond.wait(lock);
|
||||
item = _hash[key(id)];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item->id != id) {
|
||||
item = item->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
return Treat::get(item->data);
|
||||
}
|
||||
}
|
||||
|
||||
void remove(uint64_t id)
|
||||
{
|
||||
Lock lock(_lock);
|
||||
Item** item = &_hash[key(id)];
|
||||
|
||||
while (*item) {
|
||||
if ((*item)->id == id) {
|
||||
if (!--(*item)->refs) {
|
||||
Item *rm_item = *item;
|
||||
*item = rm_item->next;
|
||||
delete rm_item;
|
||||
}
|
||||
return;
|
||||
}
|
||||
item = &(*item)->next;
|
||||
}
|
||||
THROW("%s id %lu, not found", Treat::name(), id);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
Lock lock(_lock);
|
||||
for (int i = 0; i < HASH_SIZE; i++) {
|
||||
while (_hash[i]) {
|
||||
Item *item = _hash[i];
|
||||
_hash[i] = item->next;
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void abort()
|
||||
{
|
||||
Lock lock(_lock);
|
||||
_aborting = true;
|
||||
_new_item_cond.notify_all();
|
||||
}
|
||||
|
||||
private:
|
||||
inline uint32_t key(uint64_t id) {return uint32_t(id) % HASH_SIZE;}
|
||||
|
||||
private:
|
||||
class Item {
|
||||
public:
|
||||
Item(uint64_t in_id, T* data)
|
||||
: id (in_id)
|
||||
, refs (1)
|
||||
, next (NULL)
|
||||
, data (Treat::get(data)) {}
|
||||
|
||||
~Item()
|
||||
{
|
||||
Treat::release(data);
|
||||
}
|
||||
|
||||
uint64_t id;
|
||||
int refs;
|
||||
Item* next;
|
||||
T* data;
|
||||
};
|
||||
|
||||
Item* _hash[HASH_SIZE];
|
||||
Mutex _lock;
|
||||
Condition _new_item_cond;
|
||||
bool _aborting;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
125
client/threads.cpp
Normal file
125
client/threads.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "threads.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
#ifdef WIN32
|
||||
#include <sys/timeb.h>
|
||||
#endif
|
||||
|
||||
Thread::Thread(thread_main_t thread_main, void* opaque)
|
||||
{
|
||||
int r = pthread_create(&_thread, NULL, thread_main, opaque);
|
||||
if (r) {
|
||||
THROW("failed %d", r);
|
||||
}
|
||||
}
|
||||
|
||||
void Thread::join()
|
||||
{
|
||||
pthread_join(_thread, NULL);
|
||||
}
|
||||
|
||||
static inline void rel_time(struct timespec& time, uint64_t delta_nano)
|
||||
{
|
||||
#ifdef WIN32
|
||||
struct _timeb now;
|
||||
_ftime_s(&now);
|
||||
time.tv_sec = now.time;
|
||||
time.tv_nsec = now.millitm * 1000 * 1000;
|
||||
#else
|
||||
clock_gettime(CLOCK_MONOTONIC, &time);
|
||||
#endif
|
||||
delta_nano += (uint64_t)time.tv_sec * 1000 * 1000 * 1000;
|
||||
delta_nano += time.tv_nsec;
|
||||
time.tv_sec = long(delta_nano / (1000 * 1000 * 1000));
|
||||
time.tv_nsec = long(delta_nano % (1000 * 1000 * 1000));
|
||||
}
|
||||
|
||||
void Lock::timed_lock(uint64_t timout_nano)
|
||||
{
|
||||
struct timespec time;
|
||||
int r;
|
||||
|
||||
rel_time(time, timout_nano);
|
||||
if ((r = pthread_mutex_timedlock(_mutex.get(), &time))) {
|
||||
_locked = false;
|
||||
if (r != ETIMEDOUT) {
|
||||
THROW("failed %d", r);
|
||||
}
|
||||
return;
|
||||
}
|
||||
_locked = true;
|
||||
}
|
||||
|
||||
Condition::Condition()
|
||||
{
|
||||
#ifdef WIN32
|
||||
pthread_cond_init(&_condition, NULL);
|
||||
#else
|
||||
pthread_condattr_t attr;
|
||||
pthread_condattr_init(&attr);
|
||||
int r;
|
||||
if ((r = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC))) {
|
||||
THROW("set clock failed %d", r);
|
||||
}
|
||||
pthread_cond_init(&_condition, &attr);
|
||||
pthread_condattr_destroy(&attr);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Condition::timed_wait(Lock& lock, uint64_t nano)
|
||||
{
|
||||
struct timespec time;
|
||||
rel_time(time, nano);
|
||||
int r = pthread_cond_timedwait(&_condition, lock.get(), &time);
|
||||
if (r) {
|
||||
if (r != ETIMEDOUT) {
|
||||
THROW("failed %d", r);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Mutex::Mutex(Type type)
|
||||
{
|
||||
pthread_mutexattr_t attr;
|
||||
|
||||
pthread_mutexattr_init(&attr);
|
||||
if (type == NORMAL) {
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
|
||||
} else if (type == RECURSIVE) {
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
} else {
|
||||
THROW("invalid type %d", type);
|
||||
}
|
||||
|
||||
int r;
|
||||
if ((r = pthread_mutex_init(&_mutex, &attr))) {
|
||||
THROW("int failed %d", r);
|
||||
}
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
}
|
||||
|
||||
Mutex::~Mutex()
|
||||
{
|
||||
pthread_mutex_destroy(&_mutex);
|
||||
}
|
||||
|
||||
135
client/threads.h
Normal file
135
client/threads.h
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_THREADS
|
||||
#define _H_THREADS
|
||||
|
||||
#include "pthread.h"
|
||||
#include "atomic_count.h"
|
||||
|
||||
class Thread {
|
||||
public:
|
||||
typedef void* (*thread_main_t)(void*);
|
||||
|
||||
Thread(thread_main_t thread_main, void* opaque);
|
||||
void join();
|
||||
|
||||
private:
|
||||
pthread_t _thread;
|
||||
};
|
||||
|
||||
class Mutex {
|
||||
public:
|
||||
enum Type {
|
||||
NORMAL,
|
||||
RECURSIVE,
|
||||
};
|
||||
|
||||
Mutex(Type = NORMAL);
|
||||
virtual ~Mutex();
|
||||
|
||||
private:
|
||||
friend class Lock;
|
||||
pthread_mutex_t* get() {return &_mutex;}
|
||||
|
||||
private:
|
||||
pthread_mutex_t _mutex;
|
||||
};
|
||||
|
||||
class Lock {
|
||||
public:
|
||||
Lock(Mutex& mutex)
|
||||
: _locked (true)
|
||||
, _mutex (mutex)
|
||||
{
|
||||
pthread_mutex_lock(_mutex.get());
|
||||
}
|
||||
|
||||
Lock(Mutex& mutex, uint64_t timout_nano)
|
||||
: _mutex (mutex)
|
||||
{
|
||||
if (!pthread_mutex_trylock(_mutex.get())) {
|
||||
_locked = true;
|
||||
return;
|
||||
}
|
||||
timed_lock(timout_nano);
|
||||
}
|
||||
|
||||
~Lock()
|
||||
{
|
||||
unlock();
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
if (_locked) {
|
||||
pthread_mutex_unlock(_mutex.get());
|
||||
_locked = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_locked() { return _locked;}
|
||||
|
||||
private:
|
||||
friend class Condition;
|
||||
pthread_mutex_t* get() {return _mutex.get();}
|
||||
void timed_lock(uint64_t timout_nano);
|
||||
|
||||
private:
|
||||
bool _locked;
|
||||
Mutex& _mutex;
|
||||
};
|
||||
|
||||
class RecurciveMutex: public Mutex {
|
||||
public:
|
||||
RecurciveMutex() : Mutex(Mutex::RECURSIVE) {}
|
||||
};
|
||||
|
||||
typedef Lock RecurciveLock;
|
||||
class Condition {
|
||||
public:
|
||||
Condition();
|
||||
|
||||
~Condition()
|
||||
{
|
||||
pthread_cond_destroy(&_condition);
|
||||
}
|
||||
|
||||
void notify_one()
|
||||
{
|
||||
pthread_cond_signal(&_condition);
|
||||
}
|
||||
|
||||
void notify_all()
|
||||
{
|
||||
pthread_cond_broadcast(&_condition);
|
||||
}
|
||||
|
||||
void wait(Lock& lock)
|
||||
{
|
||||
pthread_cond_wait(&_condition, lock.get());
|
||||
}
|
||||
|
||||
bool timed_wait(Lock& lock, uint64_t nano);
|
||||
|
||||
private:
|
||||
pthread_cond_t _condition;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
39
client/utils.cpp
Normal file
39
client/utils.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
void string_printf(std::string& str, const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
string_vprintf(str, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void wstring_printf(std::wstring& str, const wchar_t* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
wstring_vprintf(str, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
151
client/utils.h
Normal file
151
client/utils.h
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_UTILS
|
||||
#define _H_UTILS
|
||||
|
||||
#include "common.h"
|
||||
#include "red_error_codes.h"
|
||||
|
||||
#define MIN(x, y) (((x) <= (y)) ? (x) : (y))
|
||||
#define MAX(x, y) (((x) >= (y)) ? (x) : (y))
|
||||
#define ALIGN(a, b) (((a) + ((b) - 1)) & ~((b) - 1))
|
||||
|
||||
class Exception: public std::exception {
|
||||
public:
|
||||
Exception(const std::string& str, int error_code) : _mess (str), _erro_code (error_code) {}
|
||||
Exception(const std::string& str) : _mess (str) { _erro_code = SPICEC_ERROR_CODE_ERROR;}
|
||||
virtual ~Exception() throw () {}
|
||||
virtual const char* what() const throw () {return _mess.c_str();}
|
||||
int get_error_code() {return _erro_code;}
|
||||
|
||||
private:
|
||||
std::string _mess;
|
||||
int _erro_code;
|
||||
};
|
||||
|
||||
#define THROW(format, ...) { \
|
||||
std::string exption_string; \
|
||||
string_printf(exption_string, format, ## __VA_ARGS__ ); \
|
||||
throw Exception(exption_string); \
|
||||
}
|
||||
|
||||
#define THROW_ERR(err, format, ...) { \
|
||||
std::string exption_string; \
|
||||
string_printf(exption_string, format, ## __VA_ARGS__ ); \
|
||||
throw Exception(exption_string, err); \
|
||||
}
|
||||
|
||||
|
||||
template <class T>
|
||||
class AutoRef {
|
||||
public:
|
||||
AutoRef() : _obj (NULL) {}
|
||||
AutoRef(T* obj) : _obj (obj) {}
|
||||
~AutoRef() { if (_obj) _obj->unref();}
|
||||
T* operator * () {return _obj;}
|
||||
|
||||
void reset(T* obj)
|
||||
{
|
||||
if (_obj) {
|
||||
_obj->unref();
|
||||
}
|
||||
_obj = obj;
|
||||
}
|
||||
|
||||
T* release()
|
||||
{
|
||||
T* ret = _obj;
|
||||
_obj = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
T* _obj;
|
||||
};
|
||||
|
||||
static inline int test_bit(const void* addr, int bit)
|
||||
{
|
||||
return !!(((uint32_t*)addr)[bit >> 5] & (1 << (bit & 0x1f)));
|
||||
}
|
||||
|
||||
static inline int test_bit_be(const void* addr, int bit)
|
||||
{
|
||||
return !!(((uint8_t*)addr)[bit >> 3] & (0x80 >> (bit & 0x07)));
|
||||
}
|
||||
|
||||
static inline void set_bit(const void* addr, int bit)
|
||||
{
|
||||
((uint8_t*)addr)[bit >> 5] |= (0x1 << (bit & 0x1f));
|
||||
}
|
||||
|
||||
static inline void set_bit_be(const void* addr, int bit)
|
||||
{
|
||||
((uint8_t*)addr)[bit >> 3] |= (0x80 >> (bit & 0x07));
|
||||
}
|
||||
|
||||
void string_vprintf(std::string& str, const char* format, va_list ap);
|
||||
void string_printf(std::string& str, const char *format, ...);
|
||||
void wstring_vprintf(std::wstring& str, const wchar_t* format, va_list ap);
|
||||
void wstring_printf(std::wstring& str, const wchar_t *format, ...);
|
||||
|
||||
template<class T>
|
||||
class FreeObject {
|
||||
public:
|
||||
void operator () (T p) { delete p;}
|
||||
};
|
||||
|
||||
template<class T, class FreeRes = FreeObject<T> >
|
||||
class _AutoRes {
|
||||
public:
|
||||
_AutoRes() : _res (NULL) {}
|
||||
_AutoRes(T* res) : _res (res) {}
|
||||
~_AutoRes() { set(NULL); }
|
||||
|
||||
void set(T* res) {if (_res) _free_res(_res); _res = res; }
|
||||
T* get() {return _res;}
|
||||
T* operator -> () { return _res;}
|
||||
T* release() {T* tmp = _res; _res = NULL; return tmp; }
|
||||
bool valid() { return !!_res; }
|
||||
|
||||
private:
|
||||
_AutoRes(const _AutoRes&);
|
||||
_AutoRes& operator = (const _AutoRes&);
|
||||
|
||||
private:
|
||||
T* _res;
|
||||
FreeRes _free_res;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class AutoArray {
|
||||
public:
|
||||
AutoArray() : _array (NULL) {}
|
||||
AutoArray(T* array) : _array (array) {}
|
||||
~AutoArray() { delete[] _array;}
|
||||
|
||||
void set(T* array) { delete[] _array; _array = array;}
|
||||
T* get() {return _array;}
|
||||
T* release() {T* tmp = _array; _array = NULL; return tmp; }
|
||||
T& operator [] (int i) {return _array[i];}
|
||||
|
||||
private:
|
||||
T* _array;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
42
client/windows/atomic_count.h
Normal file
42
client/windows/atomic_count.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_ATOMIC_COUNT
|
||||
#define _H_ATOMIC_COUNT
|
||||
|
||||
class AtomicCount {
|
||||
public:
|
||||
AtomicCount(uint32_t count = 0) : _count (count) {}
|
||||
|
||||
uint32_t operator ++ ()
|
||||
{
|
||||
return InterlockedIncrement(&_count);
|
||||
}
|
||||
|
||||
uint32_t operator -- ()
|
||||
{
|
||||
return InterlockedDecrement(&_count);
|
||||
}
|
||||
|
||||
operator uint32_t () { return _count;}
|
||||
|
||||
private:
|
||||
LONG _count;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
158
client/windows/events_loop_p.cpp
Normal file
158
client/windows/events_loop_p.cpp
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "events_loop.h"
|
||||
#include "debug.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
EventsLoop::EventsLoop()
|
||||
{
|
||||
}
|
||||
|
||||
EventsLoop::~EventsLoop()
|
||||
{
|
||||
}
|
||||
|
||||
void EventsLoop::run()
|
||||
{
|
||||
for (;;) {
|
||||
run_once();
|
||||
}
|
||||
}
|
||||
|
||||
void EventsLoop::run_once(int timeout_milli)
|
||||
{
|
||||
DWORD wait_res = WaitForMultipleObjects(_handles.size(), &_handles[0], FALSE, timeout_milli);
|
||||
if (wait_res == WAIT_FAILED) {
|
||||
THROW("wait failed");
|
||||
}
|
||||
int event_index = wait_res - WAIT_OBJECT_0;
|
||||
if (event_index < 0 || event_index >= (int)_events.size()) {
|
||||
THROW("invalid event id");
|
||||
}
|
||||
_events[event_index]->action();
|
||||
}
|
||||
|
||||
void EventsLoop::add_socket(Socket& socket)
|
||||
{
|
||||
HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
if (!event) {
|
||||
THROW("create event failed");
|
||||
}
|
||||
if (WSAEventSelect(socket.get_socket(), event, FD_READ | FD_WRITE | FD_CLOSE) == SOCKET_ERROR) {
|
||||
CloseHandle(event);
|
||||
THROW("event select failed");
|
||||
}
|
||||
int size = _events.size();
|
||||
_events.resize(size + 1);
|
||||
_handles.resize(size + 1);
|
||||
_events[size] = &socket;
|
||||
_handles[size] = event;
|
||||
}
|
||||
|
||||
void EventsLoop::remove_socket(Socket& socket)
|
||||
{
|
||||
int size = _events.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (_events[i] == &socket) {
|
||||
if (WSAEventSelect(socket.get_socket(), NULL, 0) == SOCKET_ERROR) {
|
||||
THROW("event select failed");
|
||||
}
|
||||
u_long arg = 0;
|
||||
if (ioctlsocket(socket.get_socket(), FIONBIO, &arg) == SOCKET_ERROR) {
|
||||
THROW("set blocking mode failed");
|
||||
}
|
||||
CloseHandle(_handles[i]);
|
||||
for (i++; i < size; i++) {
|
||||
_events[i - 1] = _events[i];
|
||||
_handles[i - 1] = _handles[i];
|
||||
}
|
||||
_events.resize(size - 1);
|
||||
_handles.resize(size - 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
THROW("socket not found");
|
||||
}
|
||||
|
||||
void EventsLoop::add_trigger(Trigger& trigger)
|
||||
{
|
||||
int size = _events.size();
|
||||
_events.resize(size + 1);
|
||||
_handles.resize(size + 1);
|
||||
_events[size] = &trigger;
|
||||
_handles[size] = trigger.get_handle();
|
||||
}
|
||||
|
||||
void EventsLoop::remove_trigger(Trigger& trigger)
|
||||
{
|
||||
int size = _events.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (_events[i] == &trigger) {
|
||||
for (i++; i < size; i++) {
|
||||
_events[i - 1] = _events[i];
|
||||
_handles[i - 1] = _handles[i];
|
||||
}
|
||||
_events.resize(size - 1);
|
||||
_handles.resize(size - 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
THROW("trigger not found");
|
||||
}
|
||||
|
||||
EventsLoop::Trigger::Trigger()
|
||||
{
|
||||
if (!(event = CreateEvent(NULL, FALSE, FALSE, NULL))) {
|
||||
THROW("create event failed");
|
||||
}
|
||||
}
|
||||
|
||||
EventsLoop::Trigger::~Trigger()
|
||||
{
|
||||
CloseHandle(event);
|
||||
}
|
||||
|
||||
void EventsLoop::Trigger::trigger()
|
||||
{
|
||||
if (!SetEvent(event)) {
|
||||
THROW("set event failed");
|
||||
}
|
||||
}
|
||||
|
||||
void EventsLoop::Trigger::reset()
|
||||
{
|
||||
if (!ResetEvent(event)) {
|
||||
THROW("set event failed");
|
||||
}
|
||||
}
|
||||
|
||||
void EventsLoop::Trigger::action()
|
||||
{
|
||||
on_event();
|
||||
}
|
||||
|
||||
void EventsLoop::add_file(File& file)
|
||||
{
|
||||
}
|
||||
|
||||
void EventsLoop::remove_file(File& file)
|
||||
{
|
||||
}
|
||||
|
||||
42
client/windows/events_loop_p.h
Normal file
42
client/windows/events_loop_p.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_EVENTS_LOOP_P
|
||||
#define _H_EVENTS_LOOP_P
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class EventSource;
|
||||
|
||||
class EventsLoop_p {
|
||||
public:
|
||||
std::vector<EventSource*> _events;
|
||||
std::vector<HANDLE> _handles;
|
||||
};
|
||||
|
||||
class Trigger_p {
|
||||
public:
|
||||
HANDLE get_handle() { return event;}
|
||||
|
||||
public:
|
||||
HANDLE event;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
124
client/windows/main.cpp
Normal file
124
client/windows/main.cpp
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include <fstream>
|
||||
#include <windows.h>
|
||||
extern "C" {
|
||||
#include "pthread.h"
|
||||
}
|
||||
|
||||
//#define OPEN_CONSOLE
|
||||
#ifdef OPEN_CONSOLE
|
||||
#include <io.h>
|
||||
#include <conio.h>
|
||||
#endif
|
||||
|
||||
#include "application.h"
|
||||
#include "debug.h"
|
||||
#include "utils.h"
|
||||
|
||||
HINSTANCE instance = NULL;
|
||||
|
||||
static void init_winsock()
|
||||
{
|
||||
WSADATA wsaData;
|
||||
int res;
|
||||
|
||||
if ((res = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0) {
|
||||
THROW("WSAStartup failed %d", res);
|
||||
}
|
||||
}
|
||||
|
||||
char* version_string = "???";
|
||||
static char _version_string[40];
|
||||
|
||||
static void init_version_string()
|
||||
{
|
||||
DWORD handle;
|
||||
DWORD verrsion_inf_size = GetFileVersionInfoSizeA(__argv[0], &handle);
|
||||
if (verrsion_inf_size == 0) {
|
||||
return;
|
||||
}
|
||||
AutoArray<uint8_t> info_buf (new uint8_t[verrsion_inf_size]);
|
||||
if (!GetFileVersionInfoA(__argv[0], handle, verrsion_inf_size, info_buf.get())) {
|
||||
return;
|
||||
}
|
||||
UINT size;
|
||||
VS_FIXEDFILEINFO *file_info;
|
||||
if (!VerQueryValueA(info_buf.get(), "\\", (VOID**)&file_info, &size) ||
|
||||
size < sizeof(VS_FIXEDFILEINFO)) {
|
||||
return;
|
||||
}
|
||||
sprintf(_version_string, "%d.%d.%d.%d",
|
||||
file_info->dwFileVersionMS >> 16,
|
||||
file_info->dwFileVersionMS & 0x0ffff,
|
||||
file_info->dwFileVersionLS >> 16,
|
||||
file_info->dwFileVersionLS & 0x0ffff);
|
||||
version_string = _version_string;
|
||||
}
|
||||
|
||||
int WINAPI WinMain(HINSTANCE hInstance,
|
||||
HINSTANCE hPrevInstance,
|
||||
LPSTR lpCmdLine,
|
||||
int nCmdShow)
|
||||
{
|
||||
int exit_val;
|
||||
|
||||
instance = hInstance;
|
||||
|
||||
try {
|
||||
init_version_string();
|
||||
#ifdef OPEN_CONSOLE
|
||||
AllocConsole();
|
||||
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
int hConHandle = _open_osfhandle((intptr_t)h, _O_TEXT);
|
||||
FILE * fp = _fdopen(hConHandle, "w");
|
||||
*stdout = *fp;
|
||||
|
||||
h = GetStdHandle(STD_INPUT_HANDLE);
|
||||
hConHandle = _open_osfhandle((intptr_t)h, _O_TEXT);
|
||||
fp = _fdopen(hConHandle, "r");
|
||||
*stdin = *fp;
|
||||
|
||||
h = GetStdHandle(STD_ERROR_HANDLE);
|
||||
hConHandle = _open_osfhandle((intptr_t)h, _O_TEXT);
|
||||
fp = _fdopen(hConHandle, "w");
|
||||
*stderr = *fp;
|
||||
#endif
|
||||
pthread_win32_process_attach_np();
|
||||
init_winsock();
|
||||
exit_val = Application::main(__argc, __argv, version_string);
|
||||
LOG_INFO("Spice client terminated (exitcode = %d)", exit_val);
|
||||
} catch (Exception& e) {
|
||||
LOG_ERROR("unhandle exception: %s", e.what());
|
||||
exit_val = e.get_error_code();
|
||||
} catch (std::exception& e) {
|
||||
LOG_ERROR("unhandle exception: %s", e.what());
|
||||
exit_val = SPICEC_ERROR_CODE_ERROR;
|
||||
} catch (...) {
|
||||
LOG_ERROR("unhandled exception");
|
||||
exit_val = SPICEC_ERROR_CODE_ERROR;
|
||||
}
|
||||
log4cpp::Category::shutdown();
|
||||
#ifdef OPEN_CONSOLE
|
||||
_getch();
|
||||
#endif
|
||||
pthread_win32_process_detach_np();
|
||||
return exit_val;
|
||||
}
|
||||
|
||||
20
client/windows/my_getopt.cpp
Normal file
20
client/windows/my_getopt.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "../../common/win/my_getopt-1.5/my_getopt.c"
|
||||
|
||||
227
client/windows/named_pipe.cpp
Normal file
227
client/windows/named_pipe.cpp
Normal file
@ -0,0 +1,227 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "named_pipe.h"
|
||||
#include "utils.h"
|
||||
#include "debug.h"
|
||||
|
||||
PipeBuffer::PipeBuffer(HANDLE pipe)
|
||||
: _handler (NULL)
|
||||
, _pipe (pipe)
|
||||
, _start (0)
|
||||
, _end (0)
|
||||
, _pending (false)
|
||||
{
|
||||
ZeroMemory(&_overlap, sizeof(_overlap));
|
||||
_overlap.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
_event_handle = _overlap.hEvent;
|
||||
WinPlatform::add_event(*this);
|
||||
}
|
||||
|
||||
PipeBuffer::~PipeBuffer()
|
||||
{
|
||||
WinPlatform::remove_event(*this);
|
||||
CloseHandle(_event_handle);
|
||||
}
|
||||
|
||||
DWORD PipeBuffer::get_overlapped_bytes()
|
||||
{
|
||||
DWORD bytes = 0;
|
||||
|
||||
if (!GetOverlappedResult(_pipe, &_overlap, &bytes, FALSE) || bytes == 0) {
|
||||
_pending = false;
|
||||
_handler->on_data();
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
int32_t PipeReader::read(uint8_t *buf, int32_t size)
|
||||
{
|
||||
ASSERT(buf && size >= 0);
|
||||
|
||||
if (_start < _end) {
|
||||
int32_t bytes_read = 0;
|
||||
bytes_read = MIN(_end - _start, (uint32_t)size);
|
||||
CopyMemory(buf, _data + _start, bytes_read);
|
||||
_start += bytes_read;
|
||||
if (_start == _end) {
|
||||
_start = _end = 0;
|
||||
}
|
||||
return bytes_read;
|
||||
}
|
||||
if (_pending) {
|
||||
return 0;
|
||||
}
|
||||
if (!ReadFile(_pipe, _data + _end, sizeof(_data) - _end, NULL, &_overlap) &&
|
||||
GetLastError() != ERROR_IO_PENDING) {
|
||||
DBG(0, "ReadFile() failed %u", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
_pending = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PipeReader::on_event()
|
||||
{
|
||||
ASSERT(_pending);
|
||||
DWORD bytes = get_overlapped_bytes();
|
||||
|
||||
if (!bytes) {
|
||||
return;
|
||||
}
|
||||
_end += bytes;
|
||||
_pending = false;
|
||||
_handler->on_data();
|
||||
}
|
||||
|
||||
int32_t PipeWriter::write(const uint8_t *buf, int32_t size)
|
||||
{
|
||||
int32_t bytes_written = 0;
|
||||
ASSERT(buf && size >= 0);
|
||||
|
||||
if (!_pending && _start == _end) {
|
||||
_start = _end = 0;
|
||||
}
|
||||
if (_end < sizeof(_data)) {
|
||||
bytes_written = MIN(sizeof(_data) - _end, (uint32_t)size);
|
||||
CopyMemory(_data + _end, buf, bytes_written);
|
||||
_end += bytes_written;
|
||||
}
|
||||
if (!_pending && _start < _end) {
|
||||
if (!WriteFile(_pipe, _data + _start, _end - _start, NULL, &_overlap) &&
|
||||
GetLastError() != ERROR_IO_PENDING) {
|
||||
DBG(0, "WriteFile() failed %u", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
_pending = true;
|
||||
}
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
void PipeWriter::on_event()
|
||||
{
|
||||
ASSERT(_pending);
|
||||
DWORD bytes = get_overlapped_bytes();
|
||||
if (!bytes) {
|
||||
return;
|
||||
}
|
||||
_start += bytes;
|
||||
_pending = false;
|
||||
if (_start == sizeof(_data)) {
|
||||
_handler->on_data();
|
||||
}
|
||||
}
|
||||
|
||||
WinConnection::WinConnection(HANDLE pipe)
|
||||
: _pipe (pipe)
|
||||
, _writer (pipe)
|
||||
, _reader (pipe)
|
||||
{
|
||||
}
|
||||
|
||||
WinConnection::~WinConnection()
|
||||
{
|
||||
if (!DisconnectNamedPipe(_pipe)) {
|
||||
DBG(0, "DisconnectNamedPipe failed %d", GetLastError());
|
||||
}
|
||||
CloseHandle(_pipe);
|
||||
}
|
||||
|
||||
int32_t WinConnection::read(uint8_t *buf, int32_t size)
|
||||
{
|
||||
return _reader.read(buf, size);
|
||||
}
|
||||
|
||||
int32_t WinConnection::write(const uint8_t *buf, int32_t size)
|
||||
{
|
||||
return _writer.write(buf, size);
|
||||
}
|
||||
|
||||
void WinConnection::set_handler(NamedPipe::ConnectionInterface* handler)
|
||||
{
|
||||
_reader.set_handler(handler);
|
||||
_writer.set_handler(handler);
|
||||
}
|
||||
|
||||
WinListener::WinListener(const char *name, NamedPipe::ListenerInterface &listener_interface)
|
||||
: _listener_interface (listener_interface)
|
||||
, _pipe (0)
|
||||
{
|
||||
_pipename = new TCHAR[PIPE_MAX_NAME_LEN];
|
||||
swprintf_s(_pipename, PIPE_MAX_NAME_LEN, L"%s%S", PIPE_PREFIX, name);
|
||||
ZeroMemory(&_overlap, sizeof(_overlap));
|
||||
_overlap.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
_event_handle = _overlap.hEvent;
|
||||
WinPlatform::add_event(*this);
|
||||
create_pipe();
|
||||
}
|
||||
|
||||
WinListener::~WinListener()
|
||||
{
|
||||
CancelIo(_pipe);
|
||||
WinPlatform::remove_event(*this);
|
||||
CloseHandle(_event_handle);
|
||||
delete[] _pipename;
|
||||
}
|
||||
|
||||
void WinListener::on_event()
|
||||
{
|
||||
DWORD bytes;
|
||||
|
||||
if (!GetOverlappedResult(_pipe, &_overlap, &bytes, FALSE)) {
|
||||
DBG(0, "GetOverlappedResult() failed %u", GetLastError());
|
||||
return;
|
||||
}
|
||||
DBG(0, "Pipe connected 0x%p", _pipe);
|
||||
WinConnection *con = new WinConnection(_pipe);
|
||||
NamedPipe::ConnectionInterface &con_interface = _listener_interface.create();
|
||||
con->set_handler(&con_interface);
|
||||
con_interface.bind((NamedPipe::ConnectionRef)con);
|
||||
create_pipe();
|
||||
}
|
||||
|
||||
void WinListener::create_pipe()
|
||||
{
|
||||
_pipe = CreateNamedPipe(_pipename, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
||||
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
|
||||
PIPE_UNLIMITED_INSTANCES, PIPE_BUF_SIZE, PIPE_BUF_SIZE,
|
||||
PIPE_TIMEOUT, NULL);
|
||||
if (_pipe == INVALID_HANDLE_VALUE) {
|
||||
THROW("CreateNamedPipe() failed %u", GetLastError());
|
||||
}
|
||||
if (ConnectNamedPipe(_pipe, &_overlap)) {
|
||||
THROW("ConnectNamedPipe() is not pending");
|
||||
}
|
||||
switch (GetLastError()) {
|
||||
case ERROR_IO_PENDING:
|
||||
DBG(0, "Pipe waits for connection");
|
||||
break;
|
||||
case ERROR_PIPE_CONNECTED: {
|
||||
DBG(0, "Pipe already connected");
|
||||
WinConnection *con = new WinConnection(_pipe);
|
||||
NamedPipe::ConnectionInterface &con_interface = _listener_interface.create();
|
||||
con->set_handler(&con_interface);
|
||||
con_interface.bind((NamedPipe::ConnectionRef)con);
|
||||
create_pipe();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
THROW("ConnectNamedPipe() failed %u", GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
94
client/windows/named_pipe.h
Normal file
94
client/windows/named_pipe.h
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_NAMED_PIPE
|
||||
#define _H_NAMED_PIPE
|
||||
|
||||
#include <windows.h>
|
||||
#include "platform.h"
|
||||
#include "win_platform.h"
|
||||
|
||||
#define PIPE_TIMEOUT 5000
|
||||
#define PIPE_BUF_SIZE 8192
|
||||
#define PIPE_MAX_NAME_LEN 256
|
||||
#define PIPE_PREFIX TEXT("\\\\.\\pipe\\")
|
||||
|
||||
class WinConnection;
|
||||
|
||||
class PipeBuffer: public EventOwner {
|
||||
public:
|
||||
PipeBuffer(HANDLE pipe);
|
||||
~PipeBuffer();
|
||||
void set_handler(NamedPipe::ConnectionInterface* handler) { _handler = handler;}
|
||||
DWORD get_overlapped_bytes();
|
||||
|
||||
protected:
|
||||
NamedPipe::ConnectionInterface *_handler;
|
||||
OVERLAPPED _overlap;
|
||||
HANDLE _pipe;
|
||||
uint32_t _start;
|
||||
uint32_t _end;
|
||||
uint8_t _data[PIPE_BUF_SIZE];
|
||||
bool _pending;
|
||||
};
|
||||
|
||||
class PipeReader: public PipeBuffer {
|
||||
public:
|
||||
PipeReader(HANDLE pipe) : PipeBuffer(pipe) {}
|
||||
int32_t read(uint8_t *buf, int32_t size);
|
||||
void on_event();
|
||||
};
|
||||
|
||||
class PipeWriter: public PipeBuffer {
|
||||
public:
|
||||
PipeWriter(HANDLE pipe) : PipeBuffer(pipe) {}
|
||||
int32_t write(const uint8_t *buf, int32_t size);
|
||||
void on_event();
|
||||
};
|
||||
|
||||
class WinConnection {
|
||||
public:
|
||||
WinConnection(HANDLE pipe);
|
||||
~WinConnection();
|
||||
int32_t read(uint8_t *buf, int32_t size);
|
||||
int32_t write(const uint8_t *buf, int32_t size);
|
||||
void set_handler(NamedPipe::ConnectionInterface* handler);
|
||||
|
||||
private:
|
||||
HANDLE _pipe;
|
||||
PipeWriter _writer;
|
||||
PipeReader _reader;
|
||||
};
|
||||
|
||||
class WinListener: public EventOwner {
|
||||
public:
|
||||
WinListener(const char *name, NamedPipe::ListenerInterface &listener_interface);
|
||||
~WinListener();
|
||||
void on_event();
|
||||
|
||||
private:
|
||||
void create_pipe();
|
||||
|
||||
private:
|
||||
TCHAR *_pipename;
|
||||
NamedPipe::ListenerInterface &_listener_interface;
|
||||
OVERLAPPED _overlap;
|
||||
HANDLE _pipe;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
108
client/windows/pixels_source.cpp
Normal file
108
client/windows/pixels_source.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "pixels_source.h"
|
||||
#include "pixels_source_p.h"
|
||||
#include "platform_utils.h"
|
||||
#include "threads.h"
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
static Point get_bitmap_size(HDC dc)
|
||||
{
|
||||
BITMAP bitmap_info;
|
||||
Point size;
|
||||
|
||||
GetObject(GetCurrentObject(dc, OBJ_BITMAP), sizeof(bitmap_info), &bitmap_info);
|
||||
|
||||
size.x = bitmap_info.bmWidth;
|
||||
size.y = bitmap_info.bmHeight;
|
||||
return size;
|
||||
}
|
||||
|
||||
PixelsSource::PixelsSource()
|
||||
{
|
||||
ASSERT(sizeof(_opaque) >= sizeof(PixelsSource_p));
|
||||
_origin.x = _origin.y = 0;
|
||||
memset(_opaque, 0, sizeof(_opaque));
|
||||
PixelsSource_p* p_data = (PixelsSource_p*)_opaque;
|
||||
p_data->_mutex = new Mutex();
|
||||
}
|
||||
|
||||
PixelsSource::~PixelsSource()
|
||||
{
|
||||
PixelsSource_p* p_data = (PixelsSource_p*)_opaque;
|
||||
delete p_data->_mutex;
|
||||
}
|
||||
|
||||
struct ResImage_p {
|
||||
PixelsSource_p source_p;
|
||||
HBITMAP prev_bitmap;
|
||||
};
|
||||
|
||||
|
||||
ImageFromRes::ImageFromRes(int res_id)
|
||||
{
|
||||
AutoDC dc(create_compatible_dc());
|
||||
((ResImage_p*)get_opaque())->prev_bitmap = (HBITMAP)SelectObject(dc.get(),
|
||||
get_bitmap_res(res_id));
|
||||
((ResImage_p*)get_opaque())->source_p.dc = dc.release();
|
||||
}
|
||||
|
||||
ImageFromRes::~ImageFromRes()
|
||||
{
|
||||
HDC dc = ((ResImage_p*)get_opaque())->source_p.dc;
|
||||
if (dc) {
|
||||
HGDIOBJ bitmap = SelectObject(dc, ((ResImage_p*)get_opaque())->prev_bitmap);
|
||||
DeleteObject(bitmap);
|
||||
DeleteDC(dc);
|
||||
}
|
||||
}
|
||||
|
||||
Point ImageFromRes::get_size()
|
||||
{
|
||||
ResImage_p* p_data = (ResImage_p*)get_opaque();
|
||||
Lock lock(*p_data->source_p._mutex);
|
||||
return get_bitmap_size(p_data->source_p.dc);
|
||||
}
|
||||
|
||||
AlphaImageFromRes::AlphaImageFromRes(int res_id)
|
||||
{
|
||||
AutoDC dc(create_compatible_dc());
|
||||
((ResImage_p*)get_opaque())->prev_bitmap = (HBITMAP)SelectObject(dc.get(),
|
||||
get_alpha_bitmap_res(res_id));
|
||||
((ResImage_p*)get_opaque())->source_p.dc = dc.release();
|
||||
}
|
||||
|
||||
AlphaImageFromRes::~AlphaImageFromRes()
|
||||
{
|
||||
HDC dc = ((ResImage_p*)get_opaque())->source_p.dc;
|
||||
if (dc) {
|
||||
HGDIOBJ bitmap = SelectObject(dc, ((ResImage_p*)get_opaque())->prev_bitmap);
|
||||
DeleteObject(bitmap);
|
||||
DeleteDC(dc);
|
||||
}
|
||||
}
|
||||
|
||||
Point AlphaImageFromRes::get_size()
|
||||
{
|
||||
ResImage_p* p_data = (ResImage_p*)get_opaque();
|
||||
Lock lock(*p_data->source_p._mutex);
|
||||
return get_bitmap_size(p_data->source_p.dc);
|
||||
}
|
||||
|
||||
29
client/windows/pixels_source_p.h
Normal file
29
client/windows/pixels_source_p.h
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_PIXELE_SOURSR_P
|
||||
#define _H_PIXELE_SOURSR_P
|
||||
|
||||
class Mutex;
|
||||
|
||||
struct PixelsSource_p {
|
||||
HDC dc;
|
||||
Mutex* _mutex;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
786
client/windows/platform.cpp
Normal file
786
client/windows/platform.cpp
Normal file
@ -0,0 +1,786 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "platform.h"
|
||||
#include "win_platform.h"
|
||||
#include "utils.h"
|
||||
#include "threads.h"
|
||||
#include "debug.h"
|
||||
#include "monitor.h"
|
||||
#include "record.h"
|
||||
#include "playback.h"
|
||||
#include "cursor.h"
|
||||
#include "named_pipe.h"
|
||||
|
||||
#define WM_USER_WAKEUP WM_USER
|
||||
#define NUM_TIMERS 100
|
||||
|
||||
int gdi_handlers = 0;
|
||||
extern HINSTANCE instance;
|
||||
|
||||
class DefaultEventListener: public Platform::EventListener {
|
||||
public:
|
||||
virtual void on_app_activated() {}
|
||||
virtual void on_app_deactivated() {}
|
||||
virtual void on_monitors_change() {}
|
||||
};
|
||||
|
||||
static DefaultEventListener default_event_listener;
|
||||
static Platform::EventListener* event_listener = &default_event_listener;
|
||||
static HWND paltform_win;
|
||||
static HANDLE main_tread;
|
||||
|
||||
struct Timer {
|
||||
TimerID id;
|
||||
timer_proc_t proc;
|
||||
void* opaque;
|
||||
Timer *next;
|
||||
};
|
||||
|
||||
Timer timers[NUM_TIMERS];
|
||||
Timer* free_timers = NULL;
|
||||
Mutex timers_lock;
|
||||
|
||||
static void free_timer(Timer* timer)
|
||||
{
|
||||
Lock lock(timers_lock);
|
||||
timer->proc = NULL;
|
||||
timer->next = free_timers;
|
||||
free_timers = timer;
|
||||
}
|
||||
|
||||
static void init_timers()
|
||||
{
|
||||
for (int i = 0; i < NUM_TIMERS; i++) {
|
||||
timers[i].id = i;
|
||||
free_timer(&timers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static Timer* alloc_timer()
|
||||
{
|
||||
Timer* timer;
|
||||
|
||||
Lock lock(timers_lock);
|
||||
if (!(timer = free_timers)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
free_timers = free_timers->next;
|
||||
return timer;
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK PlatformWinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (message) {
|
||||
case WM_TIMER: {
|
||||
TimerID id = wParam - 1;
|
||||
ASSERT(id < NUM_TIMERS);
|
||||
Timer* timer = &timers[id];
|
||||
timer->proc(timer->opaque, id);
|
||||
break;
|
||||
}
|
||||
case WM_USER_WAKEUP: {
|
||||
break;
|
||||
}
|
||||
case WM_ACTIVATEAPP:
|
||||
if (wParam) {
|
||||
event_listener->on_app_activated();
|
||||
} else {
|
||||
event_listener->on_app_deactivated();
|
||||
}
|
||||
break;
|
||||
case WM_DISPLAYCHANGE:
|
||||
event_listener->on_monitors_change();
|
||||
break;
|
||||
default:
|
||||
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void create_message_wind()
|
||||
{
|
||||
WNDCLASSEX wclass;
|
||||
ATOM class_atom;
|
||||
HWND window;
|
||||
|
||||
const LPCWSTR class_name = L"spicec_platform_wclass";
|
||||
|
||||
wclass.cbSize = sizeof(WNDCLASSEX);
|
||||
wclass.style = 0;
|
||||
wclass.lpfnWndProc = PlatformWinProc;
|
||||
wclass.cbClsExtra = 0;
|
||||
wclass.cbWndExtra = 0;
|
||||
wclass.hInstance = instance;
|
||||
wclass.hIcon = NULL;
|
||||
wclass.hCursor = NULL;
|
||||
wclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
|
||||
wclass.lpszMenuName = NULL;
|
||||
wclass.lpszClassName = class_name;
|
||||
wclass.hIconSm = NULL;
|
||||
|
||||
if ((class_atom = RegisterClassEx(&wclass)) == 0) {
|
||||
THROW("register class failed");
|
||||
}
|
||||
|
||||
if (!(window = CreateWindow(class_name, L"", 0, 0, 0, 0, 0, NULL, NULL, instance, NULL))) {
|
||||
THROW("create message window failed");
|
||||
}
|
||||
|
||||
paltform_win = window;
|
||||
}
|
||||
|
||||
void Platform::send_quit_request()
|
||||
{
|
||||
ASSERT(GetCurrentThread() == main_tread);
|
||||
PostQuitMessage(0);
|
||||
}
|
||||
|
||||
static std::vector<HANDLE> events;
|
||||
static std::vector<EventOwner*> events_owners;
|
||||
|
||||
void WinPlatform::add_event(EventOwner& event_owner)
|
||||
{
|
||||
ASSERT(main_tread == GetCurrentThread());
|
||||
int size = events.size();
|
||||
if (size == MAXIMUM_WAIT_OBJECTS - 1) {
|
||||
THROW("reached maximum allowed events to wait for");
|
||||
}
|
||||
events.resize(size + 1);
|
||||
events_owners.resize(size + 1);
|
||||
events[size] = event_owner.get_event_handle();
|
||||
events_owners[size] = &event_owner;
|
||||
}
|
||||
|
||||
void WinPlatform::remove_event(EventOwner& event_owner)
|
||||
{
|
||||
ASSERT(main_tread == GetCurrentThread());
|
||||
int size = events.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (events_owners[i] == &event_owner) {
|
||||
for (i++; i < size; i++) {
|
||||
events[i - 1] = events[i];
|
||||
events_owners[i - 1] = events_owners[i];
|
||||
}
|
||||
events.resize(size - 1);
|
||||
events_owners.resize(size - 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
THROW("event owner not found");
|
||||
}
|
||||
|
||||
void Platform::wait_events()
|
||||
{
|
||||
if (!events.size()) {
|
||||
if (!WaitMessage()) {
|
||||
THROW("wait failed %d", GetLastError());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD r = MsgWaitForMultipleObjectsEx(events.size(), &events[0], INFINITE, QS_ALLINPUT, 0);
|
||||
if (r == WAIT_OBJECT_0 + events.size()) {
|
||||
return;
|
||||
}
|
||||
if (r >= WAIT_OBJECT_0 && r <= WAIT_OBJECT_0 + events.size() - 1) {
|
||||
events_owners[r - WAIT_OBJECT_0]->on_event();
|
||||
} else if (r == WAIT_FAILED) {
|
||||
THROW("wait multiple failed %d", GetLastError());
|
||||
} else {
|
||||
THROW("unexpected wait return %u", r);
|
||||
}
|
||||
}
|
||||
|
||||
NamedPipe::ListenerRef NamedPipe::create(const char *name, ListenerInterface& listener_interface)
|
||||
{
|
||||
return (ListenerRef)(new WinListener(name, listener_interface));
|
||||
}
|
||||
|
||||
void NamedPipe::destroy(ListenerRef listener_ref)
|
||||
{
|
||||
delete (WinListener *)listener_ref;
|
||||
}
|
||||
|
||||
void NamedPipe::destroy_connection(ConnectionRef conn_ref)
|
||||
{
|
||||
delete (WinConnection *)conn_ref;
|
||||
}
|
||||
|
||||
int32_t NamedPipe::read(ConnectionRef conn_ref, uint8_t *buf, int32_t size)
|
||||
{
|
||||
return ((WinConnection *)conn_ref)->read(buf, size);
|
||||
}
|
||||
|
||||
int32_t NamedPipe::write(ConnectionRef conn_ref, const uint8_t *buf, int32_t size)
|
||||
{
|
||||
return ((WinConnection *)conn_ref)->write(buf, size);
|
||||
}
|
||||
|
||||
void Platform::wakeup()
|
||||
{
|
||||
if (!PostMessage(paltform_win, WM_USER_WAKEUP, 0, 0)) {
|
||||
THROW("post failed %d", GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
bool Platform::process_events()
|
||||
{
|
||||
MSG msg;
|
||||
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
||||
if (msg.message == WM_QUIT) {
|
||||
return true;
|
||||
}
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Platform::msleep(unsigned int msec)
|
||||
{
|
||||
Sleep(msec);
|
||||
}
|
||||
|
||||
void Platform::yield()
|
||||
{
|
||||
Sleep(0);
|
||||
}
|
||||
|
||||
void Platform::set_thread_priority(void* thread, Platform::ThreadPriority in_priority)
|
||||
{
|
||||
ASSERT(thread == NULL);
|
||||
int priority;
|
||||
|
||||
switch (in_priority) {
|
||||
case PRIORITY_TIME_CRITICAL:
|
||||
priority = THREAD_PRIORITY_TIME_CRITICAL;
|
||||
break;
|
||||
case PRIORITY_HIGH:
|
||||
priority = THREAD_PRIORITY_HIGHEST;
|
||||
break;
|
||||
case PRIORITY_ABOVE_NORMAL:
|
||||
priority = THREAD_PRIORITY_ABOVE_NORMAL;
|
||||
break;
|
||||
case PRIORITY_NORMAL:
|
||||
priority = THREAD_PRIORITY_NORMAL;
|
||||
break;
|
||||
case PRIORITY_BELOW_NORMAL:
|
||||
priority = THREAD_PRIORITY_BELOW_NORMAL;
|
||||
break;
|
||||
case PRIORITY_LOW:
|
||||
priority = THREAD_PRIORITY_LOWEST;
|
||||
break;
|
||||
case PRIORITY_IDLE:
|
||||
priority = THREAD_PRIORITY_IDLE;
|
||||
break;
|
||||
default:
|
||||
THROW("invalid priority %d", in_priority);
|
||||
}
|
||||
SetThreadPriority(GetCurrentThread(), priority);
|
||||
}
|
||||
|
||||
void Platform::set_event_listener(EventListener* listener)
|
||||
{
|
||||
event_listener = listener ? listener : &default_event_listener;
|
||||
}
|
||||
|
||||
TimerID Platform::create_interval_timer(timer_proc_t proc, void* opaque)
|
||||
{
|
||||
Timer* timer = alloc_timer();
|
||||
if (!timer) {
|
||||
return INVALID_TIMER;
|
||||
}
|
||||
timer->proc = proc;
|
||||
timer->opaque = opaque;
|
||||
return timer->id;
|
||||
}
|
||||
|
||||
bool Platform::activate_interval_timer(TimerID timer, unsigned int millisec)
|
||||
{
|
||||
if (timer >= NUM_TIMERS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SetTimer(paltform_win, timer + 1, millisec, NULL)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Platform::deactivate_interval_timer(TimerID timer)
|
||||
{
|
||||
if (timer >= NUM_TIMERS) {
|
||||
return false;
|
||||
}
|
||||
KillTimer(paltform_win, timer + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Platform::destroy_interval_timer(TimerID timer)
|
||||
{
|
||||
if (timer == INVALID_TIMER) {
|
||||
return;
|
||||
}
|
||||
ASSERT(timer < NUM_TIMERS);
|
||||
KillTimer(paltform_win, timer + 1);
|
||||
free_timer(&timers[timer]);
|
||||
}
|
||||
|
||||
uint64_t Platform::get_monolithic_time()
|
||||
{
|
||||
return uint64_t(GetTickCount()) * 1000 * 1000;
|
||||
}
|
||||
|
||||
void Platform::get_temp_dir(std::string& path)
|
||||
{
|
||||
DWORD len = GetTempPathA(0, NULL);
|
||||
if (len <= 0) {
|
||||
throw Exception("get temp patch failed");
|
||||
}
|
||||
char* tmp_path = new char[len + 1];
|
||||
GetTempPathA(len, tmp_path);
|
||||
path = tmp_path;
|
||||
delete[] tmp_path;
|
||||
}
|
||||
|
||||
class WinMonitor: public Monitor {
|
||||
public:
|
||||
WinMonitor(int id, const wchar_t* name, const wchar_t* string);
|
||||
|
||||
virtual void set_mode(int width, int height);
|
||||
virtual void restore();
|
||||
virtual int get_depth() { return _depth;}
|
||||
virtual Point get_position();
|
||||
virtual Point get_size() const { Point size = {_width, _height}; return size;}
|
||||
virtual bool is_out_of_sync() { return _out_of_sync;}
|
||||
virtual int get_screen_id() { return 0;}
|
||||
|
||||
protected:
|
||||
virtual ~WinMonitor();
|
||||
|
||||
private:
|
||||
void update_position();
|
||||
bool change_display_settings(int width, int height, int depth);
|
||||
bool best_display_setting(uint32_t width, uint32_t height, uint32_t depth);
|
||||
|
||||
private:
|
||||
std::wstring _dev_name;
|
||||
std::wstring _dev_string;
|
||||
bool _active;
|
||||
Point _position;
|
||||
int _width;
|
||||
int _height;
|
||||
int _depth;
|
||||
bool _out_of_sync;
|
||||
};
|
||||
|
||||
WinMonitor::WinMonitor(int id, const wchar_t* name, const wchar_t* string)
|
||||
: Monitor(id)
|
||||
, _dev_name (name)
|
||||
, _dev_string (string)
|
||||
, _active (false)
|
||||
, _out_of_sync (false)
|
||||
{
|
||||
update_position();
|
||||
}
|
||||
|
||||
WinMonitor::~WinMonitor()
|
||||
{
|
||||
restore();
|
||||
}
|
||||
|
||||
void WinMonitor::update_position()
|
||||
{
|
||||
DEVMODE mode;
|
||||
mode.dmSize = sizeof(DEVMODE);
|
||||
mode.dmDriverExtra = 0;
|
||||
EnumDisplaySettings(_dev_name.c_str(), ENUM_CURRENT_SETTINGS, &mode);
|
||||
_position.x = mode.dmPosition.x;
|
||||
_position.y = mode.dmPosition.y;
|
||||
_width = mode.dmPelsWidth;
|
||||
_height = mode.dmPelsHeight;
|
||||
_depth = mode.dmBitsPerPel;
|
||||
}
|
||||
|
||||
Point WinMonitor::get_position()
|
||||
{
|
||||
update_position();
|
||||
return _position;
|
||||
}
|
||||
|
||||
bool WinMonitor::change_display_settings(int width, int height, int depth)
|
||||
{
|
||||
DEVMODE mode;
|
||||
mode.dmSize = sizeof(DEVMODE);
|
||||
mode.dmDriverExtra = 0;
|
||||
mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
|
||||
mode.dmPelsWidth = width;
|
||||
mode.dmPelsHeight = height;
|
||||
mode.dmBitsPerPel = depth;
|
||||
|
||||
return ChangeDisplaySettingsEx(_dev_name.c_str(), &mode, NULL, CDS_FULLSCREEN, NULL)
|
||||
== DISP_CHANGE_SUCCESSFUL;
|
||||
}
|
||||
|
||||
bool WinMonitor::best_display_setting(uint32_t width, uint32_t height, uint32_t depth)
|
||||
{
|
||||
DEVMODE mode;
|
||||
DWORD mode_id = 0;
|
||||
uint32_t mod_waste = ~0;
|
||||
DWORD mod_width;
|
||||
DWORD mod_height;
|
||||
DWORD mod_depth;
|
||||
DWORD mod_frequency;
|
||||
|
||||
mode.dmSize = sizeof(DEVMODE);
|
||||
mode.dmDriverExtra = 0;
|
||||
while (EnumDisplaySettings(_dev_name.c_str(), mode_id++, &mode)) {
|
||||
// Workaround for
|
||||
// Lenovo T61p, Nvidia Quadro FX 570M and
|
||||
// Lenovo T61, Nvidia Quadro NVS 140M
|
||||
//
|
||||
// with dual monitors configuration
|
||||
//
|
||||
// we get strange values from EnumDisplaySettings 640x480x4 frequency 1
|
||||
// and calling ChangeDisplaySettingsEx with that configuration result with
|
||||
// machine that is stucked for a long period of time
|
||||
if (mode.dmDisplayFrequency == 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mode.dmPelsWidth >= width && mode.dmPelsHeight >= height) {
|
||||
bool replace = false;
|
||||
uint32_t curr_waste = mode.dmPelsWidth * mode.dmPelsHeight - width * height;
|
||||
if (curr_waste < mod_waste) {
|
||||
replace = true;
|
||||
} else if (curr_waste == mod_waste) {
|
||||
if (mod_depth == mode.dmBitsPerPel) {
|
||||
replace = mode.dmDisplayFrequency > mod_frequency;
|
||||
} else if (mod_depth != depth && mode.dmBitsPerPel > mod_depth) {
|
||||
replace = true;
|
||||
}
|
||||
}
|
||||
if (replace) {
|
||||
mod_waste = curr_waste;
|
||||
mod_width = mode.dmPelsWidth;
|
||||
mod_height = mode.dmPelsHeight;
|
||||
mod_depth = mode.dmBitsPerPel;
|
||||
mod_frequency = mode.dmDisplayFrequency;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mod_waste == ~0) {
|
||||
return false;
|
||||
}
|
||||
mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
|
||||
mode.dmPelsWidth = mod_width;
|
||||
mode.dmPelsHeight = mod_height;
|
||||
mode.dmBitsPerPel = mod_depth;
|
||||
mode.dmDisplayFrequency = mod_frequency;
|
||||
|
||||
return ChangeDisplaySettingsEx(_dev_name.c_str(), &mode, NULL, CDS_FULLSCREEN, NULL)
|
||||
== DISP_CHANGE_SUCCESSFUL;
|
||||
}
|
||||
|
||||
void WinMonitor::set_mode(int width, int height)
|
||||
{
|
||||
update_position();
|
||||
if (width == _width && height == _height) {
|
||||
_out_of_sync = false;
|
||||
return;
|
||||
}
|
||||
self_monitors_change++;
|
||||
if (!change_display_settings(width, height, 32) && !best_display_setting(width, height, 32)) {
|
||||
_out_of_sync = true;
|
||||
} else {
|
||||
_out_of_sync = false;
|
||||
}
|
||||
self_monitors_change--;
|
||||
_active = true;
|
||||
update_position();
|
||||
}
|
||||
|
||||
void WinMonitor::restore()
|
||||
{
|
||||
if (_active) {
|
||||
_active = false;
|
||||
self_monitors_change++;
|
||||
ChangeDisplaySettingsEx(_dev_name.c_str(), NULL, NULL, 0, NULL);
|
||||
self_monitors_change--;
|
||||
}
|
||||
}
|
||||
|
||||
static MonitorsList monitors;
|
||||
|
||||
const MonitorsList& Platform::init_monitors()
|
||||
{
|
||||
ASSERT(monitors.empty());
|
||||
|
||||
int id = 0;
|
||||
DISPLAY_DEVICE device_info;
|
||||
DWORD device_id = 0;
|
||||
device_info.cb = sizeof(device_info);
|
||||
while (EnumDisplayDevices(NULL, device_id, &device_info, 0)) {
|
||||
if ((device_info.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) &&
|
||||
!(device_info.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER)) {
|
||||
monitors.push_back(new WinMonitor(id++, device_info.DeviceName,
|
||||
device_info.DeviceString));
|
||||
}
|
||||
device_id++;
|
||||
}
|
||||
return monitors;
|
||||
}
|
||||
|
||||
void Platform::destroy_monitors()
|
||||
{
|
||||
while (!monitors.empty()) {
|
||||
Monitor* monitor = monitors.front();
|
||||
monitors.pop_front();
|
||||
delete monitor;
|
||||
}
|
||||
}
|
||||
|
||||
bool Platform::is_monitors_pos_valid()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void Platform::init()
|
||||
{
|
||||
main_tread = GetCurrentThread();
|
||||
create_message_wind();
|
||||
init_timers();
|
||||
}
|
||||
|
||||
WaveRecordAbstract* Platform::create_recorder(RecordClinet& client,
|
||||
uint32_t sampels_per_sec,
|
||||
uint32_t bits_per_sample,
|
||||
uint32_t channels)
|
||||
{
|
||||
return new WaveRecorder(client, sampels_per_sec, bits_per_sample, channels);
|
||||
}
|
||||
|
||||
WavePlaybackAbstract* Platform::create_player(uint32_t sampels_per_sec,
|
||||
uint32_t bits_per_sample,
|
||||
uint32_t channels)
|
||||
{
|
||||
return new WavePlayer(sampels_per_sec, bits_per_sample, channels);
|
||||
}
|
||||
|
||||
static void toggle_modifier(int key)
|
||||
{
|
||||
INPUT inputs[2];
|
||||
memset(inputs, 0, sizeof(inputs));
|
||||
inputs[0].type = inputs[1].type = INPUT_KEYBOARD;
|
||||
inputs[0].ki.wVk = inputs[1].ki.wVk = key;
|
||||
inputs[1].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
SendInput(2, inputs, sizeof(INPUT));
|
||||
}
|
||||
|
||||
uint32_t Platform::get_keyboard_modifiers()
|
||||
{
|
||||
uint32_t modifiers = 0;
|
||||
if ((GetKeyState(VK_SCROLL) & 1)) {
|
||||
modifiers |= SCROLL_LOCK_MODIFIER;
|
||||
}
|
||||
if ((GetKeyState(VK_NUMLOCK) & 1)) {
|
||||
modifiers |= NUM_LOCK_MODIFIER;
|
||||
}
|
||||
if ((GetKeyState(VK_CAPITAL) & 1)) {
|
||||
modifiers |= CAPS_LOCK_MODIFIER;
|
||||
}
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
void Platform::set_keyboard_modifiers(uint32_t modifiers)
|
||||
{
|
||||
if (((modifiers >> SCROLL_LOCK_MODIFIER_SHIFT) & 1) != (GetKeyState(VK_SCROLL) & 1)) {
|
||||
toggle_modifier(VK_SCROLL);
|
||||
}
|
||||
|
||||
if (((modifiers >> Platform::NUM_LOCK_MODIFIER_SHIFT) & 1) != (GetKeyState(VK_NUMLOCK) & 1)) {
|
||||
toggle_modifier(VK_NUMLOCK);
|
||||
}
|
||||
|
||||
if (((modifiers >> CAPS_LOCK_MODIFIER_SHIFT) & 1) != (GetKeyState(VK_CAPITAL) & 1)) {
|
||||
toggle_modifier(VK_CAPITAL);
|
||||
}
|
||||
}
|
||||
|
||||
class WinBaseLocalCursor: public LocalCursor {
|
||||
public:
|
||||
WinBaseLocalCursor() : _handle (0) {}
|
||||
void set(Window window) { SetCursor(_handle);}
|
||||
|
||||
protected:
|
||||
HCURSOR _handle;
|
||||
};
|
||||
|
||||
class WinLocalCursor: public WinBaseLocalCursor {
|
||||
public:
|
||||
WinLocalCursor(CursorData* cursor_data);
|
||||
~WinLocalCursor();
|
||||
|
||||
private:
|
||||
bool _shared;
|
||||
};
|
||||
|
||||
WinLocalCursor::WinLocalCursor(CursorData* cursor_data)
|
||||
: _shared (false)
|
||||
{
|
||||
const CursorHeader& header = cursor_data->header();
|
||||
const uint8_t* data = cursor_data->data();
|
||||
int cur_size;
|
||||
int bits = get_size_bits(header, cur_size);
|
||||
if (!bits) {
|
||||
THROW("invalid curosr type");
|
||||
}
|
||||
if (header.type == CURSOR_TYPE_MONO) {
|
||||
_handle = CreateCursor(NULL, header.hot_spot_x, header.hot_spot_y,
|
||||
header.width, header.height, data, data + cur_size);
|
||||
return;
|
||||
}
|
||||
ICONINFO icon;
|
||||
icon.fIcon = FALSE;
|
||||
icon.xHotspot = header.hot_spot_x;
|
||||
icon.yHotspot = header.hot_spot_y;
|
||||
icon.hbmColor = icon.hbmMask = NULL;
|
||||
HDC hdc = GetDC(NULL);
|
||||
|
||||
switch (header.type) {
|
||||
case CURSOR_TYPE_ALPHA:
|
||||
case CURSOR_TYPE_COLOR32:
|
||||
case CURSOR_TYPE_COLOR16: {
|
||||
BITMAPV5HEADER bmp_hdr;
|
||||
ZeroMemory(&bmp_hdr, sizeof(bmp_hdr));
|
||||
bmp_hdr.bV5Size = sizeof(bmp_hdr);
|
||||
bmp_hdr.bV5Width = header.width;
|
||||
bmp_hdr.bV5Height = -header.height;
|
||||
bmp_hdr.bV5Planes = 1;
|
||||
bmp_hdr.bV5BitCount = bits;
|
||||
bmp_hdr.bV5Compression = BI_BITFIELDS;
|
||||
if (bits == 32) {
|
||||
bmp_hdr.bV5RedMask = 0x00FF0000;
|
||||
bmp_hdr.bV5GreenMask = 0x0000FF00;
|
||||
bmp_hdr.bV5BlueMask = 0x000000FF;
|
||||
} else if (bits == 16) {
|
||||
bmp_hdr.bV5RedMask = 0x00007C00;
|
||||
bmp_hdr.bV5GreenMask = 0x000003E0;
|
||||
bmp_hdr.bV5BlueMask = 0x0000001F;
|
||||
}
|
||||
if (header.type == CURSOR_TYPE_ALPHA) {
|
||||
bmp_hdr.bV5AlphaMask = 0xFF000000;
|
||||
}
|
||||
void* bmp_pixels = NULL;
|
||||
icon.hbmColor = CreateDIBSection(hdc, (BITMAPINFO *)&bmp_hdr, DIB_RGB_COLORS, &bmp_pixels,
|
||||
NULL, 0);
|
||||
memcpy(bmp_pixels, data, cur_size);
|
||||
icon.hbmMask = CreateBitmap(header.width, header.height, 1, 1,
|
||||
(header.type == CURSOR_TYPE_ALPHA) ? NULL :
|
||||
(CONST VOID *)(data + cur_size));
|
||||
break;
|
||||
}
|
||||
case CURSOR_TYPE_COLOR4: {
|
||||
BITMAPINFO* bmp_info;
|
||||
bmp_info = (BITMAPINFO *)new uint8_t[sizeof(BITMAPINFO) + (sizeof(RGBQUAD) << bits)];
|
||||
ZeroMemory(bmp_info, sizeof(BITMAPINFO));
|
||||
bmp_info->bmiHeader.biSize = sizeof(bmp_info->bmiHeader);
|
||||
bmp_info->bmiHeader.biWidth = header.width;
|
||||
bmp_info->bmiHeader.biHeight = -header.height;
|
||||
bmp_info->bmiHeader.biPlanes = 1;
|
||||
bmp_info->bmiHeader.biBitCount = bits;
|
||||
bmp_info->bmiHeader.biCompression = BI_RGB;
|
||||
memcpy(bmp_info->bmiColors, data + cur_size, sizeof(RGBQUAD) << bits);
|
||||
icon.hbmColor = CreateDIBitmap(hdc, &bmp_info->bmiHeader, CBM_INIT, data,
|
||||
bmp_info, DIB_RGB_COLORS);
|
||||
icon.hbmMask = CreateBitmap(header.width, header.height, 1, 1,
|
||||
(CONST VOID *)(data + cur_size + (sizeof(uint32_t) << bits)));
|
||||
delete[] (uint8_t *)bmp_info;
|
||||
break;
|
||||
}
|
||||
case CURSOR_TYPE_COLOR24:
|
||||
case CURSOR_TYPE_COLOR8:
|
||||
default:
|
||||
LOG_WARN("unsupported cursor type %d", header.type);
|
||||
_handle = LoadCursor(NULL, IDC_ARROW);
|
||||
_shared = true;
|
||||
ReleaseDC(NULL, hdc);
|
||||
return;
|
||||
}
|
||||
|
||||
ReleaseDC(NULL, hdc);
|
||||
|
||||
if (icon.hbmColor && icon.hbmMask) {
|
||||
_handle = CreateIconIndirect(&icon);
|
||||
}
|
||||
if (icon.hbmMask) {
|
||||
DeleteObject(icon.hbmMask);
|
||||
}
|
||||
if (icon.hbmColor) {
|
||||
DeleteObject(icon.hbmColor);
|
||||
}
|
||||
}
|
||||
|
||||
WinLocalCursor::~WinLocalCursor()
|
||||
{
|
||||
if (_handle && !_shared) {
|
||||
DestroyCursor(_handle);
|
||||
}
|
||||
}
|
||||
|
||||
LocalCursor* Platform::create_local_cursor(CursorData* cursor_data)
|
||||
{
|
||||
return new WinLocalCursor(cursor_data);
|
||||
}
|
||||
|
||||
class WinInactiveCursor: public WinBaseLocalCursor {
|
||||
public:
|
||||
WinInactiveCursor() { _handle = LoadCursor(NULL, IDC_NO);}
|
||||
};
|
||||
|
||||
LocalCursor* Platform::create_inactive_cursor()
|
||||
{
|
||||
return new WinInactiveCursor();
|
||||
}
|
||||
|
||||
class WinDefaultCursor: public WinBaseLocalCursor {
|
||||
public:
|
||||
WinDefaultCursor() { _handle = LoadCursor(NULL, IDC_ARROW);}
|
||||
};
|
||||
|
||||
LocalCursor* Platform::create_default_cursor()
|
||||
{
|
||||
return new WinDefaultCursor();
|
||||
}
|
||||
|
||||
void Platform::set_display_mode_listner(DisplayModeListner* listener)
|
||||
{
|
||||
}
|
||||
|
||||
Icon* Platform::load_icon(int id)
|
||||
{
|
||||
HICON icon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(id));
|
||||
if (!icon) {
|
||||
return NULL;
|
||||
}
|
||||
return new WinIcon(icon);
|
||||
}
|
||||
|
||||
151
client/windows/platform_utils.cpp
Normal file
151
client/windows/platform_utils.cpp
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include <map>
|
||||
#include "platform_utils.h"
|
||||
#include "utils.h"
|
||||
#include "threads.h"
|
||||
|
||||
void string_vprintf(std::string& str, const char* format, va_list ap)
|
||||
{
|
||||
int buf_size = 256;
|
||||
for (;;) {
|
||||
AutoArray<char> buf(new char[buf_size]);
|
||||
int r = vsnprintf_s(buf.get(), buf_size, buf_size - 1, format, ap);
|
||||
if (r != -1) {
|
||||
str = buf.get();
|
||||
return;
|
||||
}
|
||||
buf_size *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
void wstring_vprintf(std::wstring& str, const wchar_t* format, va_list ap)
|
||||
{
|
||||
int buf_size = 256;
|
||||
for (;;) {
|
||||
AutoArray<wchar_t> buf(new wchar_t[buf_size]);
|
||||
int r = vswprintf(buf.get(), buf_size, format, ap);
|
||||
if (r != -1) {
|
||||
str = buf.get();
|
||||
return;
|
||||
}
|
||||
buf_size *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
HDC create_compatible_dc()
|
||||
{
|
||||
HDC dc = CreateCompatibleDC(NULL);
|
||||
if (!dc) {
|
||||
THROW("create compatible DC failed");
|
||||
}
|
||||
return dc;
|
||||
}
|
||||
|
||||
HBITMAP get_bitmap_res(int id)
|
||||
{
|
||||
HBITMAP bitmap = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(id));
|
||||
if (!bitmap) {
|
||||
THROW("get bitmpa #%d failed", id);
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
HBITMAP get_alpha_bitmap_res(int id)
|
||||
{
|
||||
AutoGDIObject bitmap(LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(id), IMAGE_BITMAP, 0, 0,
|
||||
LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_SHARED));
|
||||
if (!bitmap.valid()) {
|
||||
THROW("get bitmpa #%d failed", id);
|
||||
}
|
||||
|
||||
BITMAP src_info;
|
||||
GetObject(bitmap.get(), sizeof(src_info), &src_info);
|
||||
if (src_info.bmBitsPixel != 32 || src_info.bmPlanes != 1) {
|
||||
THROW("invalid format #%d ", id);
|
||||
}
|
||||
|
||||
LONG src_size = src_info.bmHeight * src_info.bmWidthBytes;
|
||||
AutoArray<uint8_t> src_pixels(new uint8_t[src_size]);
|
||||
LONG ncopy = GetBitmapBits((HBITMAP)bitmap.get(), src_size, src_pixels.get());
|
||||
if (ncopy != src_size) {
|
||||
THROW("get vitmap bits failed, %u", GetLastError());
|
||||
}
|
||||
|
||||
AutoDC auto_dc(create_compatible_dc());
|
||||
BITMAPINFO dest_info;
|
||||
uint8_t *dest;
|
||||
dest_info.bmiHeader.biSize = sizeof(dest_info.bmiHeader);
|
||||
dest_info.bmiHeader.biWidth = src_info.bmWidth;
|
||||
dest_info.bmiHeader.biHeight = -src_info.bmHeight;
|
||||
dest_info.bmiHeader.biPlanes = 1;
|
||||
dest_info.bmiHeader.biBitCount = 32;
|
||||
dest_info.bmiHeader.biCompression = BI_RGB;
|
||||
dest_info.bmiHeader.biSizeImage = 0;
|
||||
dest_info.bmiHeader.biXPelsPerMeter = dest_info.bmiHeader.biYPelsPerMeter = 0;
|
||||
dest_info.bmiHeader.biClrUsed = 0;
|
||||
dest_info.bmiHeader.biClrImportant = 0;
|
||||
|
||||
HBITMAP ret = CreateDIBSection(auto_dc.get(), &dest_info, 0, (VOID **)&dest, NULL, 0);
|
||||
if (!ret) {
|
||||
THROW("create bitmap failed, %u", GetLastError());
|
||||
}
|
||||
|
||||
uint8_t* src_line = src_pixels.get();
|
||||
for (int i = 0; i < src_info.bmHeight; i++, src_line += src_info.bmWidthBytes) {
|
||||
uint8_t* src = src_line;
|
||||
for (int j = 0; j < src_info.bmWidth; j++) {
|
||||
dest[3] = src[3];
|
||||
double alpha = (double)dest[3] / 0xff;
|
||||
dest[2] = (uint8_t)(alpha * src[2]);
|
||||
dest[1] = (uint8_t)(alpha * src[1]);
|
||||
dest[0] = (uint8_t)(alpha * src[0]);
|
||||
src += 4;
|
||||
dest += 4;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static std::map<int, const char*> errors_map;
|
||||
static Mutex errors_map_mutex;
|
||||
|
||||
const char* sys_err_to_str(int error)
|
||||
{
|
||||
Lock lock(errors_map_mutex);
|
||||
if (errors_map.find(error) == errors_map.end()) {
|
||||
LPSTR msg;
|
||||
if (!FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPSTR)&msg, 0, NULL)) {
|
||||
const int BUF_SIZE = 20;
|
||||
msg = new char[BUF_SIZE];
|
||||
_snprintf(msg, BUF_SIZE, "errno %d", error);
|
||||
} else {
|
||||
char *new_line;
|
||||
if ((new_line = strrchr(msg, '\r'))) {
|
||||
*new_line = 0;
|
||||
}
|
||||
}
|
||||
errors_map[error] = msg;
|
||||
}
|
||||
return errors_map[error];
|
||||
}
|
||||
|
||||
84
client/windows/platform_utils.h
Normal file
84
client/windows/platform_utils.h
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
Copyright (C) 2009 Red Hat, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _H_PLATFORM_UTILS
|
||||
#define _H_PLATFORM_UTILS
|
||||
|
||||
#define mb() __asm {lock add [esp], 0}
|
||||
|
||||
template<class T, class FreeRes = FreeObject<T>, T invalid = NULL >
|
||||
class AutoRes {
|
||||
public:
|
||||
AutoRes() : res (invalid) {}
|
||||
AutoRes(T inRes) : res (inRes) {}
|
||||
~AutoRes() { set(invalid); }
|
||||
|
||||
void set(T inRes) {if (res != invalid) free_res(res); res = inRes; }
|
||||
T get() {return res;}
|
||||
T release() {T tmp = res; res = invalid; return tmp; }
|
||||
bool valid() { return res != invalid; }
|
||||
|
||||
private:
|
||||
AutoRes(const AutoRes&);
|
||||
AutoRes& operator = (const AutoRes&);
|
||||
|
||||
private:
|
||||
T res;
|
||||
FreeRes free_res;
|
||||
};
|
||||
|
||||
class Delete_DC {
|
||||
public:
|
||||
void operator () (HDC dc) { DeleteDC(dc);}
|
||||
};
|
||||
|
||||
typedef AutoRes<HDC, Delete_DC> AutoDC;
|
||||
|
||||
class Delete_Object {
|
||||
public:
|
||||
void operator () (HGDIOBJ obj) { DeleteObject(obj);}
|
||||
};
|
||||
typedef AutoRes<HGDIOBJ, Delete_Object> AutoGDIObject;
|
||||
|
||||
class DeleteOGLContext {
|
||||
public:
|
||||
void operator () (HGLRC ctx) { wglDeleteContext(ctx);}
|
||||
};
|
||||
|
||||
typedef AutoRes<HGLRC, DeleteOGLContext> AutoOGLCtx;
|
||||
|
||||
HDC create_compatible_dc();
|
||||
HBITMAP get_bitmap_res(int id);
|
||||
HBITMAP get_alpha_bitmap_res(int id);
|
||||
|
||||
class WindowDC {
|
||||
public:
|
||||
WindowDC(HWND window) : _window (window), _dc (GetDC(window)) {}
|
||||
~WindowDC() { ReleaseDC(_window, _dc);}
|
||||
HDC operator * () { return _dc;}
|
||||
|
||||
private:
|
||||
HWND _window;
|
||||
HDC _dc;
|
||||
};
|
||||
|
||||
typedef AutoRes<HDC, Delete_DC> AutoReleaseDC;
|
||||
|
||||
const char* sys_err_to_str(int error);
|
||||
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user