fresh start

This commit is contained in:
Yaniv Kamay 2009-09-19 21:25:46 +03:00
commit c1b79eb035
244 changed files with 124499 additions and 0 deletions

21
.gitignore vendored Normal file
View 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

0
AUTHORS Normal file
View File

340
COPYING Normal file
View 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.

0
ChangeLog Normal file
View File

7
Makefile.am Normal file
View File

@ -0,0 +1,7 @@
SUBDIRS = common server client
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = spice.pc
DISTCLEANFILES = \
spice.pc

0
NEWS Normal file
View File

10
README Normal file
View 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
View 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
View File

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

82
client/Makefile.am Normal file
View 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

File diff suppressed because it is too large Load Diff

267
client/application.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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(&copy.data.src_bitmap);
localalize_mask(copy.data.mask);
draw_copy(&copy.base.box, &copy.base.clip, &copy.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(&copy.base.box, &copy.base.clip, &copy.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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

190
client/display_channel.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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

View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 = &region.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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 = &region.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
View 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
View 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 = &region.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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 = &region.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 &region)
{
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
View 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 &region);
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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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

View 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)
{
}

View 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
View 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;
}

View 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"

View 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());
}
}

View 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

View 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);
}

View 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
View 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);
}

View 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];
}

View 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